分享
 
 
 

gdb (GNU 调试器):基础

王朝other·作者佚名  2008-05-18
窄屏简体版  字體: |||超大  

就调试本机可执行文件(即不是 Java* 或 perl 等)而言,使用 gdb 就对了。gdb 可用于源代码级调试,以及跟踪没有源代码的程序或检查某个终止的程序留下的核心文件。遗憾的是,当您从来没有使用过它,或者已经有一段时间没有使用它时,使用它来做这些工作可能很困难。图 1 展示了使用 gdb 来进行调试所需的每个命令。

Command Description

file load program

b set breakpoint

r run

c continue

s step (line)

si step (machine instruction)

n next (step over function call)

finish run until function returns

i r show all registers

i r show specific register

l list source

p display value

set args set command line arguments

图 1

要将 gdb 用作源代码级调试器,请确保在包括调试符号的情况下编译程序;这就是 gcc 的 -g 选项。对于启动 gdb,您可以通过输入 gdb programname(此例中是 gdb simple),或者通过单独运行 gdb 本身并使用 file 命令加载可执行文件来达到目的。

要设置基本的断点,您可以在某个函数名称或行号上中断。例如,b 27 将在当前文件的第 27 行上设置了一个断点。有两种使用函数名称的方式:b main 在函数 main 中的第一行可执行代码上中断,b *main 在 main 的入口地址上设置一个断点(如果打算单步调试函数的每条指令,这样是很有用的)。

一旦设置了第一个断点,可使用 run 或 r 来启动程序并运行到第一个断点。还可以不带任何断点运行程序,如果您不知道程序是在何处崩溃的,这样将很有帮助。当您命中一个断点 c 或 continue 时,程序将恢复执行,直至命中下一个断点。

step“单步”调试源代码行。Step instruction (si) 单步调试机器代码行(当您单步调试优化过的代码时,si 指令可能特别有用,这将在后面介绍)。 next 工作起来就像 step,但是它不跟踪进入函数调用(如果的确错误地跟踪进入了函数调用,可使用 finish 来完成该函数,然后在它返回的地方中断)。

单独的 info register(i r)本身显示所有寄存器的值(浮点值除外),不过您可以指定一个寄存器名称。在 31 位系统上,通用寄存器被命名为 gpr0、gpr1、gpr2,等等;在 64 位系统上,它们被命名为 r0、r1、r2,等等。浮点寄存器遵循相同的命名约定:在 31 位系统上是 fpr0、fpr1、fpr2,等等;在 64 位系统上是 f0、f1、f2,等等。

“l”列出程序当前停止位置周围的源代码。您还可以指定开始列出代码的行号或要列出的函数名称。print 允许您打印程序中任何变量的值。 print 的一个最好的优点在于,它会为您取出一个结构中的所有值,或允许您直接引用该结构的一部分:

Breakpoint 1, main () at simple.c:30

30 boink.boik = &r1;

(gdb) print boink

 $3 = {boik = 0x0}

(gdb) print boink.boik

 $4 = (int *) 0x0

最后,set args 为程序设置命令行参数。您也可以在执行 run 时指定命令行参数,但是 set args 将使参数在 run 的多次执行中都有效。

gdb Post Mortem

当程序意外地终止时,内核会尝试产生一个核心文件,以图判断发生了什么错误。然而,核心文件通常不是在默认设置值下产生的。这可以使用 ulimit 命令来改变。ulimit -c unlimited 帮助确保您获得应用程序的完整核心文件。

虽然核心文件当前仅提供多线程应用程序中的有限的值,不过 2.5 版的开发内核已开始处理这个问题。预计 2.6 版的内核中会提供一些理想的线程改进。

Command Description

file load program

core load core file

bt back trace

where same as back trace

i f frame information

up move up stack

down move down stack

frame jump to frame

disassem display function’s machine code

i locals display local variable values

图 2

图 2 突出显示了一系列便利的 post mortem 命令。

(gdb) file simple

Reading symbols from simple...done.

(gdb) core core

Core was generated by `./simple’.

Program terminated with signal 11, Segmentation fault.

Reading symbols from /lib/libc.so.6...done.

Loaded symbols for /lib/libc.so.6

Reading symbols from /lib/ld.so.1...done.

Loaded symbols for /lib/ld.so.1

#0 0x400ab738 in memcpy () from /lib/libc.so.6

(gdb) where

#0 0x400ab738 in memcpy () from /lib/libc.so.6

#1 0x40066e in main () at simple.c:34

#2 0x40041eb8 in __libc_start_main () from /lib/libc.so.6

#3 0x4004ac in _start ()

(gdb) i f

Stack level 0, frame at 0x7ffff7a0:

pswa = 0x400ab738 in memcpy; saved pswa 0x0

(FRAMELESS), called by frame at 0x7ffff7a0

Arglist at 0x7ffff7a0, args:

Locals at 0x7ffff7a0, Previous frame’s sp is 0x0

(gdb) up

#1 0x40066e in main () at simple.c:34

34 memcpy (doink.boik, boink.boik, sizeof(boink.boik));

(gdb) i locals

doink = {boik = 0x4019a0}

boink = {boik = 0x0}

(gdb) ptype boink.boik

type = int *

(gdb) print *boink.boik

Cannot access memory at address 0x0

(gdb) print *doink.boik

 $1 = 4

图 3

图 3 简要显示了一个核心程序的完整运行过程。同样,我们使用了 simple 程序。 但不是手动加载程序和核心文件,而是从命令行调入:

gdb simple core

在加载符号之后,gdb 将指出程序在何处终止。注意当前帧 #0 包含前一节中计算的地址。gdb 将在 31 位系统上截去高位,仅显示指令地址。 还要注意帧 #1 包含 gpr14 中的返回地址。

接着往下看,i f 提供了关于当前堆栈帧的信息。在堆栈帧中往上移到 main,这就是我们离开该帧的地方(即调用 memcpy 的地方)。简单的 i locals 提供了传递给 memcpy 的变量的值,其中一个变量 boink.boik 的值为 0x0。使用 ptype 来检查变量类型,这样将确认它是一个整型指针,并且如果目的是为了拷贝内容到其中,它就不应该是 0x0。最后一个选项是使用 print,通过一个星号(*)来解除指针引用,以便接收值。

处理优化过的代码

先前,我曾提到当您在源代码级调试优化过的代码时,gdb 可能变得有点棘手。编译器优化一些代码的执行顺序以最大化性能。图 4 显示了这样一个例子。您可以看到行号如何从 32 切换到 30 然后又切换回 32。

(gdb) break main

Breakpoint 1 at 0x800007a8: file simple.c, line 32.

(gdb) r

Starting program: /home/grundym/foo/simple

Breakpoint 1, main () at simple.c:32

32 do_one_thing(&doink);

(gdb) s

30 doink.boik = &r1;

(gdb)

32 do_one_thing(&doink);

(gdb)

do_one_thing (pnum_times=0x1fffffff690) at simple.c:47

47 for (i = 0; i < 4; i++) {

图 4

如何处理这种情况呢?使用 si 和 ni(next instruction;它类似 si,但是会跳过子例程调用)将非常有帮助。 在这个层次上,很好理解 zArchitecture 是有所帮助的。

(gdb) break *main

Breakpoint 1 at 0x80000794: file simple.c, line 27.

(gdb) display /i  $pswa

(gdb) r

Starting program: /home/grundym/foo/simple

Breakpoint 1, main () at simple.c:27

27 {

1: x/i  $pswa 0x80000794 : EB AF F0 50 00 24 stmg %r10,%r15,80(%r15)

(gdb) si

0x8000079a 27 {

1: x/i  $pswa 0x8000079a : B9 04 00 1F lgr %r1,%r15

(gdb)

0x8000079e 27 {

1: x/i  $pswa 0x8000079e : A7 FB FF 58 aghi %r15,-168

(gdb)

0x800007a2 in main () at simple.c:27

27 {

1: x/i  $pswa 0x800007a2 : E3 10 F0 00 00 24 stg %r1,0(%r15)

(gdb)

32 do_one_thing(&doink);

1: x/i  $pswa 0x800007a8 : 41 C0 F0 A0 la %r12,160(%r15)

(gdb)

30 doink.boik = &r1;

1: x/i  $pswa 0x800007ac : C0 10 00 00 08 C2 larl %r1,0x80001930

(gdb)

0x800007b2 30 doink.boik = &r1;

1: x/i  $pswa 0x800007b2 : E3 10 F0 A0 00 24 stg %r1,160(%r15)

(gdb)

32 do_one_thing(&doink);

1: x/i  $pswa 0x800007b8 : B9 04 00 2C lgr %r2,%r12

(gdb)

0x800007bc 32 do_one_thing(&doink);

1: x/i  $pswa 0x800007bc : C0 E5 FF FF FF 68 brasl %r14,0x8000068c

(gdb)

do_one_thing (pnum_times=0x1fffffff7f8) at simple.c:44

44 {

1: x/i  $pswa 0x8000068c : EB BF F0 58 00 24 stmg %r11,%r15,88(%r15)

(gdb)

图 5

图 5 显示了为调试而对程序进行的设置。首先在 main()的地址处设置一个断点,然后设置一个 display。display 是一个表达式,它在每次代码停止执行时打印有关信息。在此例中,display 被设置为显示当前指令地址处的指令。/i 是打印为反汇编代码的格式,而当前指令指针在值/寄存器(value/register) $pswa 中。

单步调试代码,可以明显看出每条机器指令都与一行 c 代码相关联。 前四行与第 27 行(即函数 main 的开头)相关联。 前四行是典型的函数引入操作,它们保存寄存器、堆栈指针并调整堆栈。当关联的行号变为 32 时,我们就设置好了对 do_one_thing() 的函数调用。

当 display 在工作时,它显示 x /i 作为实际数据显示之前的命令。x 是检查内存的命令。/i 是以指令格式来格式化;/x 将以 16 进制格式来格式化;而 /a 将以 16 进制来格式化。然而,您应该在尽可能的地方把该值看作是地址,并解析符号名称。

display /i  $pswa

display /x  $pswm

display /a  $r0

display /a  $r1

display /a  $r2

display /a  $r3

display /a  $r4

display /a  $r5

display /a  $r6

display /a  $r7

display /a  $r8

display /a  $r9

display /a  $r10

display /a  $r11

display /a  $r12

display /a  $r13

display /a  $r12

display /a  $r14

display /a  $r15

display /10i  $pswa

图 6

当在指令级工作时,设置一些显示可能是有所帮助的。您可以将所有 display 命令放在一个文件中,并在命令行上使用 -x 选项来指定它。图 6 包含了工作在汇编程序级时通常使用的 display 命令。

Breakpoint 1, main () at simple.c:27

27 {

20: x/10i  $pswa

0x80000794 : EB AF F0 50 00 24 stmg %r10,%r15,80(%r15)

0x8000079a : B9 04 00 1F lgr %r1,%r15

0x8000079e : A7 FB FF 58 aghi %r15,-168

0x800007a2 : E3 10 F0 00 00 24 stg %r1,0(%r15)

0x800007a8 : 41 C0 F0 A0 la %r12,160(%r15)

0x800007ac : C0 10 00 00 08 C2 larl %r1,0x80001930

0x800007b2 : E3 10 F0 A0 00 24 stg %r1,160(%r15)

0x800007b8 : B9 04 00 2C lgr %r2,%r12

0x800007bc : C0 E5 FF FF FF 68 brasl %r14,0x8000068c

0x800007c2 : E3 10 F0 A0 00 04 lg %r1,160(%r15)

19: /a  $r15 = 0x1fffffff698

18: /a  $r14 = 0x10000057b04 <__libc_start_main+260>

17: /a  $r12 = 0x10000019108 <__curbrk+280>

16: /a  $r13 = 0x8006c9be

15: /a  $r12 = 0x10000019108 <__curbrk+280>

14: /a  $r11 = 0x1fffffff7f8

13: /a  $r10 = 0x80000418 <_init>

12: /a  $r9 = 0x100000198f8 <_dl_debug_mask>

11: /a  $r8 = 0x1000017bee0

10: /a  $r7 = 0x1

9: /a  $r6 = 0x2

8: /a  $r5 = 0x100001803d8

7: /a  $r4 = 0x1fffffff808

6: /a  $r3 = 0x1fffffff7f8

5: /a  $r2 = 0x1

4: /a  $r1 = 0x80000794

3: /a  $r0 = 0x1ff00000000

2: /x  $pswm = 0x705c00180000000

1: x/i  $pswa 0x80000794 : EB AF F0 50 00 24 stmg %r10,%r15,80(%r15)

(gdb)

图 7

这个命令打印全部 PSW 值、所有通用寄存器和从当前指令地址开始的下 10 行机器代码。图 7 显示了当我们在 main() 处中断时的结果。可以看到,在其中一些寄存器所指向的地方,/a 格式解析是如何使得理解正在发生的事情更加容易的。

结束语

对于一些可用于 Linux 应用程序调试的基本工具以及调试过程本身,本文中的信息应该为您提供了有用的入门信息。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有