readelf与objdump

14 分钟阅读

readelf

查看头信息(-h)

$ readelf -h mytest
ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x400790
  Start of program headers:          64 (bytes into file)
  ......

查看段信息(-S)

$ readelf -S mytest
There are 36 section headers, starting at offset 0x49e8:
Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .interp           PROGBITS         0000000000400238  00000238
       000000000000001c  0000000000000000   A       0     0     1
  [ 2] .note.ABI-tag     NOTE             0000000000400254  00000254
       0000000000000020  0000000000000000   A       0     0     4
  [ 3] .note.gnu.build-i NOTE             0000000000400274  00000274
       0000000000000024  0000000000000000   A       0     0     4
  [ 4] .gnu.hash         GNU_HASH         0000000000400298  00000298
       0000000000000030  0000000000000000   A       5     0     8
  ......

查看符号(-s)

$ readelf -s mytest
Symbol table '.dynsym' contains 14 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
     1: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
     2: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _Jv_RegisterClasses
     3: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _ZNSt8ios_base4InitC1Ev@GLIBCXX_3.4
     ......

查看依赖库(-d)

$ readelf -d mytest
Dynamic section at offset 0xe18 contains 25 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libstdc++.so.6]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 0x000000000000000c (INIT)               0x4006d0
 0x000000000000000d (FINI)               0x4009d4
 0x0000000000000019 (INIT_ARRAY)         0x600df8
 0x000000000000001b (INIT_ARRAYSZ)       16 (bytes)
 0x000000000000001a (FINI_ARRAY)         0x600e08
 ......

objdump

命令

  • objdump -s test.so 显示section的完整内容(所有section)
  • objdump --section=.text -s test.so 显示test.so中的.text段的内容
  • objdump -d test.so 反汇编出具有特定指令的section (精简)
  • objdump -D test.so 反汇编出所有的section (全集)
  • objdump -S test.so 反汇编(尽可能输出源代码,一般-g下回比较明显有用)

显示反汇编代码和地址 -d/-D参数效果如下:

0000000000400d06 <_Z13test_functionv>:
  400d06:    55                       push   %rbp
  400d07:    48 89 e5                 mov    %rsp,%rbp
  400d0a:    be 34 16 40 00           mov    $0x401634,%esi
  400d0f:    bf c0 30 60 00           mov    $0x6030c0,%edi
  400d14:    e8 37 fe ff ff           callq  400b50 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
  400d19:    be c0 0b 40 00           mov    $0x400bc0,%esi
  400d1e:    48 89 c7                 mov    %rax,%rdi
  400d21:    e8 7a fe ff ff           callq  400ba0 <_ZNSolsEPFRSoS_E@plt>
  400d26:    90                       nop
  400d27:    5d                       pop    %rbp
  400d28:    c3                       retq

-S参数效果如下:

0000000000400d06 <_Z13test_functionv>:
#include <iostream>

using namespace std;

void test_function()
{
  400d06:    55                       push   %rbp
  400d07:    48 89 e5                 mov    %rsp,%rbp
  cout << "test function" << endl;
  400d0a:    be 34 16 40 00           mov    $0x401634,%esi
  400d0f:    bf c0 30 60 00           mov    $0x6030c0,%edi
  400d14:    e8 37 fe ff ff           callq  400b50 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
  400d19:    be c0 0b 40 00           mov    $0x400bc0,%esi
  400d1e:    48 89 c7                 mov    %rax,%rdi
  400d21:    e8 7a fe ff ff           callq  400ba0 <_ZNSolsEPFRSoS_E@plt>
}
  400d26:    90                       nop
  400d27:    5d                       pop    %rbp
  400d28:    c3                       retq

内核挂死分析

当程序跑飞,出现coredump的时候,用dmseg显示出最后的信息。然后使用objdump反汇编得到最后的调用栈。

00258000-00273000 r-xp 00000000 08:01 157 /lib/ld-2.10.1.so
00273000-00274000 r--p 0001a000 08:01 157 /lib/ld-2.10.1.so
00274000-00275000 rw-p 0001b000 08:01 157 /lib/ld-2.10.1.so
003b4000-003b7000 r-xp 00000000 08:01 13105 /lib/libSegFault.so
003b7000-003b8000 r--p 00002000 08:01 13105 /lib/libSegFault.so
003b8000-003b9000 rw-p 00003000 08:01 13105 /lib/libSegFault.so
00c76000-00c77000 r-xp 00000000 00:00 0 [vdso]
00e0d000-00e29000 r-xp 00000000 08:01 4817 /lib/libgcc_s.so.1
00e29000-00e2a000 r--p 0001b000 08:01 4817 /lib/libgcc_s.so.1
00e2a000-00e2b000 rw-p 0001c000 08:01 4817 /lib/libgcc_s.so.1
00e73000-00fb1000 r-xp 00000000 08:01 1800 /lib/tls/i686/cmov/libc-2.10.1.so
00fb1000-00fb2000 ---p 0013e000 08:01 1800 /lib/tls/i686/cmov/libc-2.10.1.so
00fb2000-00fb4000 r--p 0013e000 08:01 1800 /lib/tls/i686/cmov/libc-2.10.1.so
00fb4000-00fb5000 rw-p 00140000 08:01 1800 /lib/tls/i686/cmov/libc-2.10.1.so
00fb5000-00fb8000 rw-p 00000000 00:00 0
08048000-08049000 r-xp 00000000 08:01 303895 /home/panfeng/segfault/segfault3
08049000-0804a000 r--p 00000000 08:01 303895 /home/panfeng/segfault/segfault3
0804a000-0804b000 rw-p 00001000 08:01 303895 /home/panfeng/segfault/segfault3
09432000-09457000 rw-p 00000000 00:00 0 [heap]
b78cf000-b78d1000 rw-p 00000000 00:00 0
b78df000-b78e1000 rw-p 00000000 00:00 0
bfb67000-bfb7c000 rw-p 00000000 00:00 0 [stack]

程序挂死分析

参考一次segfault错误的排除过程

加上-g选项挂死的程序,在dmesg中得到的信息如下:

a.out[2374]: segfault at 7f0ed0bfbf70 ip 00007f0edd646fe7 sp 00007f0ed3603978 error 4 a.out[7f0edd514000+1b6000]

at、ip、sp后面都是跟的地址,对这三个地址分别执行:

addr2line -e a.out xxxxxxxx

可以得到这地址对应代码行。

其中error 4,也就是100b,表示信息如下:

bit2:值为1表示是用户态程序内存访问越界,值为0表示是内核态程序内存访问越界
bit1: 值为1表示是写操作导致内存访问越界,值为0表示是读操作导致内存访问越界
bit0: 值为1表示没有足够的权限访问非法地址的内容,值为0表示访问的非法地址根本没有对应的页面,也就是无效地址

库挂死分析

dmesg或者cat /var/log/message可以看到如下信息:

xxxxx.o[2374]: segfault at7f0ed0bfbf70 ip 00007f0edd646fe7 sp 00007f0ed3603978 error 4 inlibc-2.17.so[7f0edd514000+1b6000]

其中:

  • at 7f0ed0bfbf70,表示出错的地址,用处不大;
  • ip 00007f0edd646fe7,表示出错时指令地址
  • sp 00007f0ed3603978,表示堆栈指针
  • inlibc-2.17.so[7f0edd514000+1b6000],表明so加载地址,已经大小

通过加载地址减去指令地址(00007f0edd646fe7 - 7f0edd514000),得到相对地址132fe7;通过反汇编如下:

[root@localhost]# objdump -tT libc-2.17.so | grep 132

000000000008284fl     F .text  000000000000001b              _L_unlock_1325
0000000000082ebfl     F .text  000000000000001c              _L_lock_11322
000000000010b952l     F .text  000000000000001b              _L_unlock_132
000000000010ce62l     F .text  000000000000001b              _L_unlock_132
00000000001132e0l     F .text  00000000000001cf              ruserok2_sa
00000000000f1320l     F .text  00000000000001ce              __ecvt_r
00000000000bf370l     F .text  0000000000000132              __statfs_link_max
0000000000132080l     F .text  0000000000000068              __nss_gshadow_lookup
0000000000132f50l     F .text  0000000000000fd9              __strncmp_sse42  <<<<<<<<<
00000000001320f0l     F .text  00000000000000a5              __strchr_sse42
0000000000132020l     F .text  000000000000005e              __nss_aliases_lookup
00000000001321a0l     F .text  0000000000000da9              __strcmp_sse42
00000000001153b0g     F .text  0000000000000132              setnetgrent
00000000000f1320g     F .text  00000000000001ce              ecvt_r
0000000000112b50g     F .text  0000000000000132              ether_ntohost

找到__strncmp_sse42,通过objdump -S libc-2.17.so,找到具体错误行:

0x00007ffff732efe0 <+144>: mov    %edx,%r8d
0x00007ffff732efe3 <+147>: xchg   %eax,%ecx
0x00007ffff732efe4 <+148>: xchg   %rsi,%rdi
0x00007ffff732efe7<+151>: movdqa (%rdi),%xmm2  <<<<<<<<<<<<<<<<<<<<<<<
0x00007ffff732efeb <+155>: movdqa(%rsi),%xmm1
0x00007ffff732efef <+159>: lea    0xf(%rax),%r9
0x00007ffff732eff3 <+163>: sub    %rcx,%r9
0x00007ffff732eff6 <+166>: lea    0x4d4c3(%rip),%r10

表明第二个参数错误。