K&R 使用 gdb 调试时,echo 程序的解释令人困惑。怎么了?

Posted

技术标签:

【中文标题】K&R 使用 gdb 调试时,echo 程序的解释令人困惑。怎么了?【英文标题】:K&R Explanation of echo program is confusing, when debugging it using gdb. What's wrong? 【发布时间】:2019-07-07 03:59:28 【问题描述】:

根据 K&R,他对 echo 程序的解释: echo hello world “按照惯例,argv[0] 是调用程序的名称,因此 argc 至少为 1。如果 argc 为 1,则程序名称后没有命令行参数。在上面的示例中,argc 是3、argv[0]、argv[1]、argv[2]分别是"echo"、"hello"、"world"。第一个可选参数是argv[1],最后一个是argv[argc- 1]。”

但是,在调试程序时,使用 gdb,我可以看到以下内容:

(gdb) p argv[0]
$2 = 0x7efff85a "/home/pi/new/a.out"
(gdb) p argv[1]
$3 = 0x0
(gdb) p argv[2]
$4 = 0x7efff86d "LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=00:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc"...
(gdb) p *argv[0]
    $5 = 47 '/'

例 1:

#include <stdio.h>
int main(int argc, char *argv[])

    int i;
    while (--argc>0)
        printf("%s%s",*++argv,(argc>1)?" ":"");
    printf("\n");
    return 0;

对于上面的代码示例,编译执行时如下:

pi@readonly:~/new$ cc -g echo.c
pi@readonly:~/new$ a.out hello world
hello world

使用 gdb 调试:

片段 1:

(gdb) p &*argv
$11 = (char **) 0x7efff744
(gdb) p &**argv
$12 = 0x7efff85a "/home/pi/new/a.out"
(gdb) p argv
$13 = (char **) 0x7efff744
(gdb) p &argv
$14 = (char ***) 0x7efff5e0
(gdb) p &argv[0]
$15 = (char **) 0x7efff744
(gdb) p argv[0]
$16 = 0x7efff85a "/home/pi/new/a.out"

片段 2:

(gdb) p argv[3]
$22 = 0x7efffe5a "_=/usr/bin/gdb"
(gdb) p argv[4]
$23 = 0x7efffe69 "LANG=en_GB.UTF-8"
(gdb) p &argv[3]
$24 = (char **) 0x7efff754

问题:

• 片段 1:

    我理解正确吗,0x7efff85a 是字符串"/home/pi/new/a.out" 的十六进制值?如果是,它是如何获得的?也就是说,是不是通过atoi()等函数得到的?

• 片段 3:

    我理解正确吗,_=/usr/bin/gdb 被转换为0x7efffe5a

    此外,还有一些预定义的值被设置到数组 argv,因为万一没有输入任何内容,它就在那里。我在哪里可以阅读有关此预定义值的信息?它是实现定义的吗?例如,在 Windows 上,使用 Eclipse 或其他 IDE,它会为 argv[] 数组提供不同但预定义的值吗?

【问题讨论】:

不,它们是地址(指针)。查看您要打印的类型。 您需要在 GDB 中使用set args 命令来设置程序的命令行参数。 或在“运行”它时指定它们,即“运行你好世界” argv[0]/home/pi/new/a.out 是因为您没有将程序重命名为 echo。虽然我必须承认 K&R 没有提到路径,或者可能是后来在 argv[0] 的定义中添加的。 如果argc 为1,则无法访问argv[2] 及以上。如果argc 为2 等,则无法访问argv[3]。您从调试器获得的任何此类值都是没有意义的。 【参考方案1】:

片段 1) 0x7efff85a 是指向此程序实例的字符串 "/home/pi/new/a.out" 的指针。

片段 2) 0x7efffe5a 是指向此特定实例的字符串 "_=/usr/bin/gdb" 的指针,但您不应依赖 argv[argc+1] 之后指向任何特定内容。

【讨论】:

【参考方案2】:

注意main的完整定义是:

int main(int argc, char *argv[], char *envp[])

这意味着在最后一个 argv[] 之后跟随环境变量。

【讨论】:

实际上并不能保证是这个意思。但是你说得对,unix 的内部约定是 envp 指向的内存块紧跟 argv 指向的内存块。【参考方案3】:

您正在循环中修改 argv:

while (--argc>0)
        printf("%s%s",*++argv,(argc>1)?" ":"");

所以之后观察它不会给你你的想法!您正在溢出数组并访问环境数组...

要么在循环之前观察 argv,要么至少将其原始值存储在某个地方以在之后观察它...由于您没有告诉我们您创建的确切(所有命令!)gdb 会话是什么,我们无法进一步帮助您.

【讨论】:

以上是关于K&R 使用 gdb 调试时,echo 程序的解释令人困惑。怎么了?的主要内容,如果未能解决你的问题,请参考以下文章

gdb使用_转

GDB 和核心转储问题

使用GDB进行调试 -- 综述

Linux上进程追踪与调试(strace和gdb)

Linux上进程追踪与调试(strace和gdb)

gdb调试coredump文件