使用GDB进行调试 -- 1 应用场景

    标题的翻译我拿不准,不过说的是一个意思。GDB的功能很强大,使用很繁杂,但掌握其中一部分常用的命令就可以实现一些调试过程了。一般情况下,这些也就够了。

    GNU m4的早期版本中有一个漏洞:有时候,将它的引用字符串(quote strings)从默认值改变时,用来在宏中抓取宏定义的命令工作不正常。下面基于m4,先将宏foo定义为0000,然后使用m4的内建defn定义bar成与foo相同。然而,当将开放引用字符串(open quote string)改为<QUOTE>,并将封闭引用字符串(close quote string)改为<UNQUOTE>,同样的步骤却会失败:

    a. 成功的情况

$ cd gnu/m4

$ ./m4

define(foo,0000)

 

foo

0000

define(bar,defn('foo'))

 

bar

0000

    从输出来看,两次定义都成功了。

    b. 失败的情况

changequote(<QUOTE>,<UNQUOTE>)

 

define(baz,defn(<QUOTE>foo<UNQUOTE>))

baz

Ctrl-d

m4: End of input: 0: fatal error: EOF in string

    c. 用GDB查看发生了什么

$ gdb m4

...

(gdb)

    GDB只需要足够的符号数据来确定怎么找到其他数据就可以了,因此第一个提示符出现的非常快。现在告诉GDB使用更窄的显示宽度

(gdb)set width 70

    因为想要直到内建的changequote是如何工作的,而从源程序中可以直到对应的子例程为m4_changequote,那么我们要使用break命令设置一个断点

(gdb) break m4_changequote

Breakpoint 1 at 0x62f4: file buitin.c, line 879.

    使用run命令,可以让m4在GDB的控制下运行。只要程序没有运行到m4_changequote就会按正常执行

(gdb) run

Starting program: /work/Editorial/gdb/gnu/m4/m4

define(foo,0000)

 

foo

0000

调用changequote就会触发断点。触发断点以后GDB会挂起m4的执行,并小时m4停止执行时的上下文内容

changequote(<QUOTE>,<UNQUOTE>)

 

Breakpoint 1, m4_changequote (argc=3,argv=0x33c70)

    at builtin.c:879

879     if (bad_argc(TOKEN_DATA_TEXT(argv[0],argc,1,3)))

下面使用命令n(next)继续执行函数的下一行

(gdb) n

882    set_quotes((argc >= 2) ? TOKEN_DATA_TEXT(argv[1])\

    : nil

    看起来是set_quotes出问题了。可以使用命令s(step)进入子例程

(gdb) s

set_quotes (lq=0x34c78 "<QUOTE>", rq=0x34c88 "<UNQUOTE>")

    at input.c:530

530    if (lquote != def_lquote)

    显示子例程被挂起,子例程在调用一个栈帧显示。要想了解全部的栈帧关系,要用bt(backtrace)命令

    //栈帧的概念我还不是很清楚,现在了解的就是一个子例程可能被多次调用,每次调用都会存储通信参数(栈帧)

(gdb) bt

#0  set_quotes (lq=0x34c78 "<QUOTE>", rq=0x34c88 "<UNQUOTE>")
    at input.c:530
#1  0x6344 in m4_changequote (argc=3, argv=0x33c70)
    at builtin.c:882
#2  0x8174 in expand_macro (sym=0x33320) at macro.c:242
#3  0x7a88 in expand_token (obs=0x0, t=209696, td=0xf7fffa30)
    at macro.c:71
#4  0x79dc in expand_input () at macro.c:40
#5  0x2930 in main (argc=0, argv=0xf7fffb20) at m4.c:195

    继续执行几次看看会发生什么。前面两步使用s,后面两步使用n,防止调入xstrdup子例程

(gdb) s

0x3b5c    532    if (rquote != def_rquote)

(gdb) s

0x3b80    535    lquote = (lq == nil || *lq == '\0') ? \

def_lquote : xstrdup(lq);

(gdb) n

536    rquote = (rq == nil || *rq == '\0') ? def_rquote\

    : xstrdup(rq)

(gdb) n

538    len_lquote = strlen(rquote);

最后一行看起来有点奇怪。可以通过查看变量lquote和rquote来看它们是不是我们制定的量。使用命令p(print)来看这些值

(gdb) p lquote

$ 1 = 0x35d40 "<QUOTE>"

(gdb) p rquote

$ 2 = 0x35d50 "<UNQUOTE>"

    lquote和rquote确实是新的引用。要继续看一些上下文,可以用命令l(list)来查看当前代码附近的十行代码

(gdb) l

(gdb) l
533             xfree(rquote);
534
535         lquote = (lq == nil || *lq == '\0') ? def_lquote\
 : xstrdup (lq);
536         rquote = (rq == nil || *rq == '\0') ? def_rquote\
 : xstrdup (rq);
537
538         len_lquote = strlen(rquote);
539         len_rquote = strlen(lquote);
540     }
541
542     void

    再执行两行,然后看看两个设定的变量的值

(gdb) n

539    len_rquote = strlen(lquote);

(gdb) n

540    }

(gdb) p len_lquote

$ 3 = 9

(gdb) p len_rquote

$ 4 = 7

     明显错了,假设len_lquote和len_rquote分别是lquote和rquote的长度,我们可以用p命令给它们赋上正确的数值(通过任意的表达式,包括调用子进程)

(gdb) p len_lquote=strlen(lquote)
$5 = 7
(gdb) p len_rquote=strlen(rquote)
$6 = 9

    这样是不是就可以修复m4内建的defn引起的问题呢?现在使用命令c(continue)让m4连续执行,然后试着试试前面引起问题的例子

(gdb) c
Continuing.

define(baz,defn(<QUOTE>foo<UNQUOTE>))

baz
0000

    成功了!新的引用可以和默认引用一样工作正常。问题看起来就是给两个类型定义了错误的长度。现在给m4输入一个EOF使它退出

Ctrl-d
Program exited normally.

    信息‘program exited normally.’ 是GDB给出的,说明m4已结束。现在可以使用名q(quit)退出GDB

(gdb) quit

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

    

 

posted on 2017-04-16 23:54  寻找理性之光  阅读(547)  评论(0)    收藏  举报

导航