readelf与objdump
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]
程序挂死分析
加上-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
表明第二个参数错误。