我有一个不是使用调试符号构建的可执行文件的核心转储.
@H_502_2@我可以恢复argv内容,看看命令行是什么?
@H_502_2@如果我运行gdb,我可以看到一个回溯,我可以导航到main()框架.有一次,有没有办法恢复argv,而不知道它的确切地址?
@H_502_2@我在x86_x64(Intel Xeon cpu)上运行一个CEntOS Linux发行版/内核,
@H_502_2@我有希望的一个原因是核心转储似乎显示了部分争议.
@H_502_2@(程序是postgres,当我加载核心文件时,gdb打印一个消息,其中包含postgres数据库用户名,客户端OP地址和查询的前10个字符))
解决方法
在x86_64上,参数以%rdi,%rsi等寄存器(
calling convention)传递.
@H_502_2@因此,当您进入主框架时,您应该能够:
(gdb) p $rdi # == argc (gdb) p (char**) $rsi # == argv (gdb) set $argv = (char**)$rsi (gdb) set $i = 0 (gdb) while $argv[$i] > print $argv[$i++] > end@H_502_2@不幸的是,当您切换帧时,GDB通常不会恢复$rdi和$rsi.所以这个例子不行:
cat t.c #include <stdlib.h> int bar() { abort(); } int foo() { return bar(); } int main() { foo(); return 0; } gcc t.c && ./a.out Aborted (core dumped) gdb -q ./a.out core Core was generated by `./a.out'. Program terminated with signal 6,Aborted. #0 0x00007fdc8284aa75 in *__GI_raise (sig=<optimized out>) at ../nptl/sysdeps/unix/sysv/linux/raise.c:64 64 ../nptl/sysdeps/unix/sysv/linux/raise.c: No such file or directory. in ../nptl/sysdeps/unix/sysv/linux/raise.c (gdb) bt #0 0x00007fdc8284aa75 in *__GI_raise (sig=<optimized out>) at ../nptl/sysdeps/unix/sysv/linux/raise.c:64 #1 0x00007fdc8284e5c0 in *__GI_abort () at abort.c:92 #2 0x000000000040052d in bar () #3 0x000000000040053b in foo () #4 0x000000000040054b in main () (gdb) fr 4 #4 0x000000000040054b in main () (gdb) p $rdi $1 = 5524 ### clearly not the right value@H_502_2@所以你必须再工作一些… @H_502_2@您可以做的是使用知识如何在process startup设置Linux堆栈,结合GDB将恢复堆栈指针的事实:
(gdb) set backtrace past-main (gdb) bt #0 0x00007ffff7a8da75 in *__GI_raise (sig=<optimized out>) at ../nptl/sysdeps/unix/sysv/linux/raise.c:64 #1 0x00007ffff7a915c0 in *__GI_abort () at abort.c:92 #2 0x000000000040052d in bar () #3 0x000000000040053b in foo () #4 0x0000000000400556 in main () #5 0x00007ffff7a78c4d in __libc_start_main (main=<optimized out>,argc=<optimized out>,ubp_av=<optimized out>,init=<optimized out>,fini=<optimized out>,rtld_fini=<optimized out>,stack_end=0x7fffffffdad8) at libc-start.c:226 #6 0x0000000000400469 in _start () (gdb) frame 6 (gdb) disas Dump of assembler code for function _start: 0x0000000000400440 <+0>: xor %ebp,%ebp 0x0000000000400442 <+2>: mov %rdx,%r9 0x0000000000400445 <+5>: pop %rsi 0x0000000000400446 <+6>: mov %rsp,%rdx 0x0000000000400449 <+9>: and $0xfffffffffffffff0,%rsp 0x000000000040044d <+13>: push %rax 0x000000000040044e <+14>: push %rsp 0x000000000040044f <+15>: mov $0x400560,%r8 0x0000000000400456 <+22>: mov $0x400570,%rcx 0x000000000040045d <+29>: mov $0x40053d,%rdi 0x0000000000400464 <+36>: callq 0x400428 <__libc_start_main@plt> => 0x0000000000400469 <+41>: hlt 0x000000000040046a <+42>: nop 0x000000000040046b <+43>: nop End of assembler dump.@H_502_2@所以现在我们预期原始的%rsp为$rsp 8(一个POP,两个PUSH),但是由于在指令0x0000000000400449中完成的对齐,它可能在$rsp 16 @H_502_2@我们来看看有什么?
(gdb) x/8gx $rsp+8 0x7fffbe5d5e98: 0x000000000000001c 0x0000000000000004 0x7fffbe5d5ea8: 0x00007fffbe5d6eb8 0x00007fffbe5d6ec0 0x7fffbe5d5eb8: 0x00007fffbe5d6ec4 0x00007fffbe5d6ec8 0x7fffbe5d5ec8: 0x0000000000000000 0x00007fffbe5d6ecf@H_502_2@这看起来很有希望:4(怀疑argc),其次是4个非空指针,后跟NULL. @H_502_2@我们来看看是否平静下来:
(gdb) x/s 0x00007fffbe5d6eb8 0x7fffbe5d6eb8: "./a.out" (gdb) x/s 0x00007fffbe5d6ec0 0x7fffbe5d6ec0: "foo" (gdb) x/s 0x00007fffbe5d6ec4 0x7fffbe5d6ec4: "bar" (gdb) x/s 0x00007fffbe5d6ec8 0x7fffbe5d6ec8: "bazzzz"@H_502_2@的确,这就是我调用二进制文件的方式.作为最后的理智检查,0x00007fffbe5d6ecf看起来像环境的一部分?
(gdb) x/s 0x00007fffbe5d6f3f 0x7fffbe5d6f3f: "SSH_AGENT_PID=2874"@H_502_2@是的,这就是环境的开始(或结束). @H_502_2@所以你有它. @H_502_2@最终注意事项:如果GDB没有打印< optimize out>很多,我们可以从帧#5中恢复argc和argv.在GDB和GCC两方面都有工作,使GDB打印少得多的“优化出”… @H_502_2@另外,当加载核心时,我的GDB打印:
Core was generated by `./a.out foo bar bazzzz'.@H_502_2@否定了整个运动的需要.但是,这仅适用于短命令行,而上述解决方案将适用于任何命令行.