由coreDump引发的一次探讨

昨天面试的时候被问到这么一个问题:“如果你开发的程序在Linux上运行的时候发生了段错误之类的问题,你会用什么方法解决?”...心想这不就是在考我coredump嘛,吧啦吧啦说了一通。对方追问:“如果你编译的时候忘记加上-g调试选项了,你又该如何定位段错误的位置呢?”...额,就这么被问住了。最后草草作答,但我一直觉得:即便没有符号信息,只要我们获取到了出错位置的内存地址就应该有办法定位到错误的。这个猜测对么?如果对,应该又是怎么做呢?


首先借用一下C语言结构体里的成员数组和指针 | | 酷 壳 - CoolShell中段错误的例子:

crash.c

root@k8s:~/test# gcc -o crash_noDebug crash.c

crash.c: In function ‘main’:

crash.c:15:10: warning: format not a string literal and no format arguments [-Wformat-security]

  printf(f.a->s);

          ^

root@k8s:~/test# ulimit -c

0

root@k8s:~/test# ulimit -c unlimited

root@k8s:~/test# ulimit -c

unlimited

root@k8s:~/test# ./crash_noDebug

Segmentation fault (core dumped)

root@k8s:~/test# gdb crash_noDebug core

GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1

Copyright (C) 2016 Free Software Foundation, Inc.

License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software: you are free to change and redistribute it.

There is NO WARRANTY, to the extent permitted by law.  Type "show copying"

and "show warranty" for details.

This GDB was configured as "x86_64-linux-gnu".

Type "show configuration" for configuration details.

For bug reporting instructions, please see:

<http://www.gnu.org/software/gdb/bugs/>.

Find the GDB manual and other documentation resources online at:

<http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".

Type "apropos word" to search for commands related to "word"...

Reading symbols from crash_noDebug...(no debugging symbols found)...done.

[New LWP 18077]

Core was generated by `./crash_noDebug'.

Program terminated with signal SIGSEGV, Segmentation fault.

#0  strchrnul () at ../sysdeps/x86_64/strchr.S:32

32      ../sysdeps/x86_64/strchr.S: No such file or directory.

(gdb) where

#0  strchrnul () at ../sysdeps/x86_64/strchr.S:32

#1  0x00007f1ce8cb2208 in __find_specmb (format=0x4 <error: Cannot access memory at address 0x4>) at printf-parse.h:108

#2  _IO_vfprintf_internal (s=0x7f1ce902a620 <_IO_2_1_stdout_>, format=0x4 <error: Cannot access memory at address 0x4>,

    ap=ap@entry=0x7ffccc8407e8) at vfprintf.c:1312

#3  0x00007f1ce8cba899 in __printf (format=<optimized out>) at printf.c:33

#4  0x000000000040055f in main ()

(gdb)

虽然没有符号表直接映射出代码出错的位置,但是最后#4 0x000000000040055f 的这个地址应该还是很有价值的。我暂且做了一个假设,假设加-g选项与否并不影响代码段的内存位置。那么,如果成立的话,只要重新尝试用-g选项编译一下程序,然后通过某种方法定位到0x000000000040055f 地址所对应的符号信息,应该就能够成功解题了。为了验证这个猜测,我重新coredump了一下带有符号信息的程序。

root@k8s:~/test# gcc -o crash_withDebug -g crash.c

crash.c: In function ‘main’:

crash.c:15:10: warning: format not a string literal and no format arguments [-Wformat-security]

  printf(f.a->s);

          ^

root@k8s:~/test# ./crash_withDebug

Segmentation fault (core dumped)

root@k8s:~/test# gdb crash_withDebug core

GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1

Copyright (C) 2016 Free Software Foundation, Inc.

License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software: you are free to change and redistribute it.

There is NO WARRANTY, to the extent permitted by law.  Type "show copying"

and "show warranty" for details.

This GDB was configured as "x86_64-linux-gnu".

Type "show configuration" for configuration details.

For bug reporting instructions, please see:

<http://www.gnu.org/software/gdb/bugs/>.

Find the GDB manual and other documentation resources online at:

<http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".

Type "apropos word" to search for commands related to "word"...

Reading symbols from crash_withDebug...done.

[New LWP 23657]

Core was generated by `./crash_withDebug'.

Program terminated with signal SIGSEGV, Segmentation fault.

#0  strchrnul () at ../sysdeps/x86_64/strchr.S:32

32      ../sysdeps/x86_64/strchr.S: No such file or directory.

(gdb) where

#0  strchrnul () at ../sysdeps/x86_64/strchr.S:32

#1  0x00007ffabb54e208 in __find_specmb (format=0x4 <error: Cannot access memory at address 0x4>) at printf-parse.h:108

#2  _IO_vfprintf_internal (s=0x7ffabb8c6620 <_IO_2_1_stdout_>, format=0x4 <error: Cannot access memory at address 0x4>,

    ap=ap@entry=0x7ffe3a0c4708) at vfprintf.c:1312

#3  0x00007ffabb556899 in __printf (format=<optimized out>) at printf.c:33

#4  0x000000000040055f in main (argc=1, argv=0x7ffe3a0c48e8) at crash.c:15

(gdb)

得到段错误发生的位置是crash.c的第15行,内存地址亦是0x000000000040055f, 这大概侧面印证了此前那个假设的正确性。当然正常的项目中程序的coredump恐怕不总是这样容易再现的,所以不能寄望于追加-g选项后,运行得到coredump文件再次调试获得出错位置。那么,如果只有0x000000000040055f这个内存地址信息,我们该如何定位代码位置呢?这个问题先留在这里,我想系统的学习和整理一下编译、内存等相关的知识,到时候应该自然而然能够得出答案吧!

C语言的编译过程如下,

编译过程

那么问题来了,gcc的-g选项具体作用在上述的哪一步呢?如果能知道符号表(Symbol table)是个什么东西就不难推测出-g实际作用于编译过程,因为所谓的符号表实际是汇编代码中的追加的一些符号信息。

root@k8s:~/test# gcc -S crash.i -o crash_noDebug.S

crash.c: In function ‘main’:

crash.c:15:10: warning: format not a string literal and no format arguments [-Wformat-security]

  printf(f.a->s);

          ^

root@k8s:~/test# gcc -S crash.i -g -o crash_withDebug.S

crash.c: In function ‘main’:

crash.c:15:10: warning: format not a string literal and no format arguments [-Wformat-security]

  printf(f.a->s);

          ^

root@k8s:~/test# diff crash_noDebug.S crash_withDebug.S

2a3

> .Ltext0:

6a8,9

>      .file 1 "crash.c"

>      .loc 1 12 0

15a19

>      .loc 1 13 0

16a21

>      .loc 1 14 0

20a26

>      .loc 1 15 0

26a33

>      .loc 1 17 0

27a35

>      .loc 1 18 0

33a42,380

> .Letext0:

>      .section        .debug_info,"",@progbits

> .Ldebug_info0:

...略...

>      .section        .debug_line,"",@progbits

> .Ldebug_line0:

>      .section        .debug_str,"MS",@progbits,1

> .LASF3:

>      .string "unsigned int"

> .LASF13:

>      .string "/root/test"

> .LASF0:

>      .string "long unsigned int"

> .LASF8:

>      .string "char"

> .LASF12:

>      .string "crash.c"

> .LASF1:

>      .string "unsigned char"

> .LASF14:

>      .string "main"

> .LASF6:

>      .string "long int"

> .LASF9:

>      .string "argc"

> .LASF11:

>      .string "GNU C11 5.4.0 20160609 -mtune=generic -march=x86-64 -g -fstack-protector-strong"

> .LASF2:

>      .string "short unsigned int"

> .LASF4:

>      .string "signed char"

> .LASF5:

>      .string "short int"

> .LASF7:

>      .string "sizetype"

> .LASF10:

>      .string "argv"


最后就是借助一些Linux二进制文件分析工具的力量来找到0x000000000040055f这个地址对应的代码段是哪里的问题了。常用的一些工具如nm、objdump、readelf之类,其中:

nm:专门用来列出二进制文件中的符号信息的。无法详细定位到目标地址的内容,Pass。

objdump:用以显示目标文件的各色信息,比如可以反汇编得到.text段信息。再合适不过了。

root@k8s:~/test# objdump -d -j .text crash_withDebug

crash_withDebug:    file format elf64-x86-64

Disassembly of section .text:

0000000000400526 <main>:

  400526:      55                      push  %rbp

  400527:      48 89 e5                mov    %rsp,%rbp

  40052a:      48 83 ec 20            sub    $0x20,%rsp

  40052e:      89 7d ec                mov    %edi,-0x14(%rbp)

  400531:      48 89 75 e0            mov    %rsi,-0x20(%rbp)

  400535:      48 c7 45 f0 00 00 00    movq  $0x0,-0x10(%rbp)

  40053c:      00

  40053d:      48 8b 45 f0            mov    -0x10(%rbp),%rax

  400541:      48 83 c0 04            add    $0x4,%rax

  400545:      48 85 c0                test  %rax,%rax

  400548:      74 15                  je    40055f <main+0x39>

  40054a:      48 8b 45 f0            mov    -0x10(%rbp),%rax

  40054e:      48 83 c0 04            add    $0x4,%rax

  400552:      48 89 c7                mov    %rax,%rdi

  400555:      b8 00 00 00 00          mov    $0x0,%eax

  40055a:      e8 a1 fe ff ff          callq  400400 <printf@plt>

  40055f:      b8 00 00 00 00          mov    $0x0,%eax

  400564:      c9                      leaveq

  400565:      c3                      retq

  400566:      66 2e 0f 1f 84 00 00    nopw  %cs:0x0(%rax,%rax,1)


readelf:显示ELF格式文件(如可执行二进制、o目标文件、共享库以及coredump文件)的信息。尝试使用-s选项打印符号信息,与nm一样无法准确定位目标地址内容,Pass。


很后悔当时没向面试官询问一下正确答案应该是什么,从目前的调查结果来看,

·通过原有coredump确定段错误内存地址

·追加-g选项重新编译可执行程序

·借助objdump查看代码段符号与地址对应关系

这个思路基本能粗略的定位到出错位置,至于是否还有更方便或者高效的方法本人就不甚明了。长路漫漫啊...

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容