处理符号和调试信息
  现在,让我们回到信号和调试信息处理。我没有详细地学习这部分,所以只是大体地说一说。
  首先,我们是否可以不使用调试信息和信号地址来调试呢?答案是可以。因为正如我们看到过的那样,所有的低级指令是对CPU寄存器和内存地址来操作的,不是源程序层面的信息。因此,这个到源程序的链接只是为了方便用户。没有调试信息的时候,你看程序的方式像是处理器(和内核)看到的一样:二进制(汇编)指令和内存字节。GDB不需要进一步的信息来把二进制信息翻译成CPU指令:
  (gdb) x/10x $pc # heXadecimal representation
  0x402c60:   0x56415741  0x54415541  0x55f48949  0x4853fd89
  0x402c70:   0x03a8ec81  0x8b480000  0x8b48643e  0x00282504
  0x402c80:   0x89480000  0x03982484
  (gdb) x/10i $pc # Instruction representation
  => 0x402c60:    push   %r15
  0x402c62:   push   %r14
  0x402c64:   push   %r13
  0x402c66:   push   %r12
  0x402c68:   mov    %rsi,%r12
  0x402c6b:   push   %rbp
  0x402c6c:   mov    %edi,%ebp
  0x402c6e:   push   %rbx
  0x402c6f:   sub    $0x3a8,%rsp
  0x402c76:   mov    (%rsi),%rdi
  现在,如果我们加上调试信息,GDB能够把符号名称和地址配对:
  (gdb) $pc
  $1 = (void (*)()) 0x402c60 <main>
  你可以通过 nm -a $file 来获取ELF二进制的符号列表:
  nm -a /usr/lib/debug/usr/bin/ls.debug | grep " main"
  0000000000402c60 T main
  GDB还会能够展示堆栈跟踪信息(稍后会详细说),但是只有感兴趣的那部分:
  (gdb) where
  #0  write ()
  #1  0x0000003d492769e3 in _IO_new_file_write ()
  #2  0x0000003d49277e4c in new_do_write ()
  #3  _IO_new_do_write ()
  #4  0x0000003d49278223 in _IO_new_file_overflow ()
  #5  0x00000000004085bb in print_current_files ()
  #6  0x000000000040431b in main ()
  我们现在有了PC地址和相应的函数,是这样。在一个函数中,你将需要对着汇编来调试!
  现在让我们加入调试信息:是DWARF规范下的gcc -g选项。我不是特别熟悉这个规范,但我知道它提供的:
  地址到代码行和行到地址的配对
  数据类型的定义,包括typedef和structure
  本地变量和函数参数以及它们的类型
  $ dwarfdump /usr/lib/debug/usr/bin/ls.debug | grep 402ce4
  0x00402ce4  [1289, 0] NS
  $ addr2line -e /usr/lib/debug/usr/bin/ls.debug  0x00402ce4
  /usr/src/debug/coreutils-8.21/src/ls.c:1289
  试一试dwarfdump来查看二进制文件里嵌入的信息。addr2line也能用到这些信息:
  很多源代码层的调试命令会依赖于这些信息,比如next命令,这会在下一行的地址设置一个断点,那个print命令会依赖于变量的类型来输出(char、int、float,而不是二进制或十六进制)。
  后总结
  我们已经见过调试器内部的好多方面了,所以我只会后说几点:
  这个堆栈跟踪信息也是通过当前的帧是向上“解开(unwinded)”的($sp和$bp/#fp),每个堆栈帧处理一次。函数的名称和参数以及本地变量名可以在调试信息中找到。
  监视点(&amp;amp;amp;lt;code&amp;amp;amp;gt;watchpoints)是通过处理器的帮助(如果有)实现的:在寄存器里标记哪些地址应该被监控,然后它会在那内存被读写的时候引发一个异常。如果不支持这项功能,或者你请求的断点超过了处理器所支持的……那么调试器会回到“手动”监视:一个指令一个指令地执行这个程序,并检查是否当前的操作到达了一个监视点的地址。是的,这很慢!
  反向调试也可以这样进行,记录每个操作的效果,并反向执行。
  条件断点是正常的断点,除非在内部,调试器在将控制权交给用户前检查当前的情况。如果当前的情况不满足,程序将会默默地继续运行。
  还可以玩gdb gdb,或者更好的(好多了)gdb --pid $(pid of gdb),因为把两个调试器放到同一个终端里是疯狂的:-)。还可以调试系统:
  qemu-system-i386 -gdb tcp::1234
  gdb --pid $(pidof qemu-system-i386)
  gdb /boot/vmlinuz --exec "target remote localhost:1234"
  但我会在另一篇文章里提到!