2024年1月1日发(作者:)
如何使用MAP文件找到程序崩溃的原因
作者 Wouter Dhondt 翻译 冯亦成(fengyc@)
[译者] 在我们调试程序的时候,习惯于不停的Step in, 可是如果我们发现Debug版的exe可以完全正常运行,而Release版却经常莫名其妙Crash。那该怎么办??没有关系,这篇文章就是帮你解决这个问题的:) 当然,你如果希望全面提高你的Debug能力,不妨去读一下John Robbins的"Debugging Applications"一书,不过你要读英文版,中文版翻译的太烂了!
导言
编写整洁的程序是一回事。而当用户通知你的程序崩溃了,你知道在增加程序新属性之前最好先修正这些错误,如果你足够幸运的话,用户会给你提供一个崩溃地址,要解决这个问题还是要有很长的路要走。有了崩溃地址,你怎么确定到底是在什么出了错呢?
创建MAP文件
首先,你需要MAP文件。如果你没有MAP文件,那你几乎是不可能通过崩溃地址找到你程序出错的具体代码行。那么先让我教你怎么创建合适的MAP文件。为此我们创建了一个新工程(MAPFILE):我在VC++6.0中创建了应用Win32 Application选项的新工程,并且选择'typical "Hello Word!" application',这样可以使得生成的MAP文件能够满足我下边解说的需要。
当生成新工程后,我们调整release版的工程设置信息。在C/C++属性页,设置Debug Info的值为"Line Numbers Only"。
很多人都忘了这一步,但是如果你想得到合适的MAP文件,你就需要设置这个选项,这不会对你的release程序造成任何影响。下一步是Link属性页,你需要选择"Generate
mapfile"选项。在Project Options编辑框中输入/MAPINFO:LINES和/MAPINFO:EXPORTS开关。
现在你可以编译和链接你的工程了,链接之后,你可以在你的中间目录中找到.map文件(和exe文件在一起)。
阅读MAP文件
在上面这些无趣的工作之后,接下去就是很有趣的部分:怎么读MAP文件。我们通过一个崩溃实例来介绍怎么读MAP文件。那么我们先得让程序崩溃,于是我在InitInstance()函数的最后增加了下边的两行代码:
char* pEmpty = NULL;
*pEmpty = 'x'; // 第119行
我相信你能够找到其它代码使得你的程序崩溃。现在重新编译且链接工程。如果你运行你的程序,程序将崩溃,并且得到怎样的消息:'The instruction at "0x004011a1" referenced
memory at "0x00000000"。0x00000000内存不能写。
现在,可以用Notepad或者类似的编辑工具打开MAP文件。MAP文件如下所示:
在MAP文件的头部包含了模块名称,表示工程链接时刻的时间戳,以及首选加载地址(一般是0x00400000,除非是dll)。文件头之后就是一些section信息,是由链接程序把各种OBJ和LIB文件的section信息组织起来的。
MAPFILE
Timestamp is 3df6394d (Tue Dec 10 19:58:21 2002)
Preferred load address is 00400000
Start Length Name Class
0001:00000000 000038feH .text CODE
0002:00000000 000000f4H .idata$5 DATA
0002:000000f8 00000394H .rdata DATA
0002:0000048c 00000028H .idata$2 DATA
0002:000004b4 00000014H .idata$3 DATA
0002:000004c8 000000f4H .idata$4 DATA
0002:000005bc 0000040aH .idata$6 DATA
0002:000009c6 00000000H .edata DATA
0003:00000000 00000004H .CRT$XCA DATA
0003:00000004 00000004H .CRT$XCZ DATA
0003:00000008 00000004H .CRT$XIA DATA
0003:0000000c 00000004H .CRT$XIC DATA
0003:00000010 00000004H .CRT$XIZ DATA
0003:00000014 00000004H .CRT$XPA DATA
0003:00000018 00000004H .CRT$XPZ DATA
0003:0000001c 00000004H .CRT$XTA DATA
0003:00000020 00000004H .CRT$XTZ DATA
0003:00000030 00002490H .data DATA
0003:000024c0 000005fcH .bss DATA
0004:00000000 00000250H .rsrc$01 DATA
0004:00000250 00000720H .rsrc$02 DATA
在section信息之后,你看到的时公共函数信息。注意下边的"public"部分,如果你有静态C函数,那它不会在"public"部分出现。幸运的是,在line numbers部分仍会反映静态函数的信息。"public"函数信息的最重要的部分是函数名称和Rva+Base栏的信息,Rva+Base
信息是函数的起始地址。
Address Publics by Value Rva+Base Lib:Object
0001:00000000 _WinMain@16 00401000 f
0001:000000c0 ?MyRegisterClass@@YAGPAUHINSTANCE__@@@Z 004010c0 f
0001:00000150 ?InitInstance@@YAHPAUHINSTANCE__@@H@Z 00401150 f
0001:000001b0 ?WndProc@@YGJPAUHWND__@@IIJ@Z 004011b0 f
0001:00000310 ?About@@YGJPAUHWND__@@IIJ@Z 00401310 f
0001:00000350 _WinMainCRTStartup 00401350 f LIBC:
0001:00000446 __amsg_exit 00401446 f LIBC:
0001:0000048f __cinit 0040148f f LIBC:
0001:000004bc _exit 004014bc f LIBC:
0001:000004cd __exit 004014cd f LIBC:
0001:00000591 __XcptFilter 00401591 f LIBC:
0001:00000715 __wincmdln 00401715 f LIBC:
//SNIPPED FOR BETTER READING
0003:00002ab4 __FPinit 00408ab4
0003:00002ab8 __acmdln 00408ab8
entry point at 0001:00000350
Static symbols
0001:000035d0 LeadUp1 004045d0 f LIBC:
0001:000035fc LeadUp2 004045fc f LIBC:
//SNIPPED FOR BETTER READING
0001:00000577 __initterm 00401577 f LIBC:
0001:0000046b _fast_error_exit 0040146b f LIBC:
Public函数部分之后是line信息部分(你设置了Link属性页中使用了/MAPINFO:LINES并且在C/C++属性页中选择"Line numbers")。在这之后就是export信息了,只要你的程序有输出函数并且在link属性页包含了/MAPINFO:EXPORTS,你就可得到export信息。
Line numbers for .(F:) segment .text
24 0001:00000000 30 0001:00000004 31 0001:0000001b 32 0001:00000027
35 0001:0000002d 53 0001:00000041 40 0001:00000047 43 0001:00000050
45 0001:00000077 47 0001:00000088 48 0001:0000008f 52 0001:000000ad
53 0001:000000b3 71 0001:000000c0 80 0001:000000c3 81 0001:000000c8
82 0001:000000ff 86 0001:00000114 88 0001:00000135 89 0001:00000145
102 0001:00000150 108 0001:00000155 110 0001:00000188 122 0001:0000018d
115 0001:0000018e 116 0001:0000019a 119 0001:000001a1 121 0001:000001a8
122 0001:000001ae 135 0001:000001b0 143 0001:000001cc 172 0001:000001ee
175 0001:0000020d 149 0001:00000216 157 0001:0000022c 175 0001:00000248
154 0001:00000251 174 0001:0000025f 175 0001:00000261 151 0001:0000026a
174 0001:00000287 175 0001:00000289 161 0001:00000294 164 0001:000002a8
165 0001:000002b6 166 0001:000002d8 174 0001:000002e7 175 0001:000002e9
169 0001:000002f2 174 0001:000002fa 175 0001:000002fc 179 0001:00000310
186 0001:0000031e 193 0001:0000032e 194 0001:00000330 188 0001:00000333
183 0001:00000344 194 0001:00000349
现在我们来定位代码中哪里发生崩溃。首先我们先要确定是哪个函数包含了崩溃地址。浏览"Rva+Base"栏,查找到第一个地址比崩溃地址大的函数,那么该函数的上一个函数就是发生崩溃的函数了。在我们的例子中,崩溃地址是0x004011a1,这个地址位于0x00401150 和
0x004011b0之间,这样我们就知道崩溃函数是?InitInstance@@YAHPAUHINSTANCE__@@H@Z。任何以问号开头的函数名都是C++
decorated name。你可以把C++ decorated name作为命令行参数传递给Platform SDK的程序,就可以得到原始函数名称了。在大多数情况下你不需要这样做,通过观察C++ decorated name我们就可以知道原始函数名称(这里,函数名称就是InitInstance())
以上是bug跟踪的重要一步。但是我们可以做得更好:我们可以找到是哪一行代码导致崩溃!我们需要做一些基本的十六进制计算,因此需要一个计算器。首先计算下边的值:崩溃地址 – 首选加载地址 - 0x1000。
地址就是相对于第一个code section的偏移量,因此需要做这样的计算。减去首选加载地址可以得到相对于文件起始位置的偏移量(逻辑上),但是为什么还要减去0x1000? 由于Line numbers中的地址是相对于code section的起始位置的偏移量。二进制代码的第一部分是Portable Executable (PE),这部分有0x1000字节长度。因此,在我们的例子中,崩溃地址(即相对于code section的偏移量)应该为0x004011a1 - 0x00400000 - 0x1000 = 0x1a1
现在我们查看MAP文件的line information section部分,每一行都是像30 0001:00000004这个样子。第一个数字是行数,第二个数字是这一行代码相对于code section的偏移值。如果我们要找崩溃代码的行数,我们只要使用与刚才定位崩溃函数相同的方式:找到第一个比我们计算得到的崩溃地址大的偏移量,那么发生崩溃的就是上一个偏移。在我们的例子中,0x1a1 在0x1a8 之前,那我们就可以确定崩溃发生在文件的119行。
保持对MAP文件的跟踪
每个发布版本都有自己相应的MAP文件。在发布exe文件时包含MAP文件不是一个坏主意。这样,你可以确保当前exe拥有合适的MAP文件。你可以让系统里的每个exe都有相应的MAP文件,但是我们都知道这样最后可能导致一些问题,MAP文件中不包含任何你要让用户知道的信息,对用户来说一点用处都没有。不过,当发生程序崩溃后,如果你没有了MAP文件,你至少可以让用户提供MAP文件。
本文来自CSDN博客,转载请标明出处:/wangweixing2000/archive/2007/04/20/
这个是现在记得, 跟上一篇对比.
对比objdump与readelf
objdump和readelf都可以用来查看二进制文件的一些内部信息. 区别在于objdump
借助BFD而更加通用一些, 可以应付不同文件格式, readelf则并不借助BFD,
而是直接读取ELF格式文件的信息, 按readelf手册页上所说, 得到的信息也略细致一些.
几个功能对比.
1. 反汇编代码
查看源代码被翻译成的汇编代码, 大概有3种方法,
1) 通过编译器直接从源文件生成, 如gcc -S
2) 对目标代码反汇编, 一种是静态反汇编, 就是使用objdump
3) 另外一种就是对运行时的代码反汇编, 一般通过gdb
readelf并不提供反汇编功能.
objdump可以指定反汇编哪个节, 一般只有对包含指令的节反汇编才有意义. 而对于一些
其他的类型的节, objdump也可以将特殊节的数据以解析后的形式呈现出来,
例如对于.plt, 输出如下:
[qtl@courier lib]$ objdump -d -j .plt
: file format elf32-i386
Disassembly of section .plt:
000003a4 <__gmon_start__@plt-0x10>:
3a4: ff b3 04 00 00 00 pushl 0x4(%ebx)
3aa: ff a3 08 00 00 00 jmp *0x8(%ebx)
3b0: 00 00 add %al,(%eax)
...
000003b4 <__gmon_start__@plt>:
3b4: ff a3 0c 00 00 00 jmp *0xc(%ebx)
3ba: 68 00 00 00 00 push $0x0
3bf: e9 e0 ff ff ff jmp 3a4 <_init+0x18>
000003c4
3c4: ff a3 10 00 00 00 jmp *0x10(%ebx)
3ca: 68 08 00 00 00 push $0x8
3cf: e9 d0 ff ff ff jmp 3a4 <_init+0x18>
000003d4
3d4: ff a3 14 00 00 00 jmp *0x14(%ebx)
3da: 68 10 00 00 00 push $0x10
3df: e9 c0 ff ff ff jmp 3a4 <_init+0x18>
000003e4
3e4: ff a3 18 00 00 00 jmp *0x18(%ebx)
3ea: 68 18 00 00 00 push $0x18
3ef: e9 b0 ff ff ff jmp 3a4 <_init+0x18>
000003f4 <__cxa_finalize@plt>:
3f4: ff a3 1c 00 00 00 jmp *0x1c(%ebx)
3fa: 68 20 00 00 00 push $0x20
3ff: e9 a0 ff ff ff jmp 3a4 <_init+0x18>
2. 显示relocation节的条目
-r参数显示elf文件的类型为REL的节的信息, 使用-S参数可以列出elf文件的
所有节的信息, 其中也就包括了REL节.
对于可重定位文件两者显示条目一致, 最重要的offset和type以及都有.
下面是两者输出的对比.
[qtl@courier lib]$ readelf -r bar.o
Relocation section '.' at offset 0x4bc contains 6 entries:
Offset Info Type Sym. Name
00000008 00000b02 R_386_PC32 00000000 ___pc_
0000000e 00000c0a R_386_GOTPC 00000000 _GLOBAL_OFFSET_TABLE_
00000025 00000d04 R_386_PLT32 00000000 cos
0000002e 00000e03 R_386_GOT32 00000000 stdout
00000044 00000509 R_386_GOTOFF 00000000 .rodata
00000050 00000f04 R_386_PLT32 00000000 fprintf
[qtl@courier lib]$ objdump -r bar.o
bar.o: file format elf32-i386
RELOCATION RECORDS FOR [.text]:
OFFSET TYPE VALUE
00000008 R_386_PC32 ___pc_
0000000e R_386_GOTPC _GLOBAL_OFFSET_TABLE_
00000025 R_386_PLT32 cos
0000002e R_386_GOT32 stdout
00000044 R_386_GOTOFF .rodata
00000050 R_386_PLT32 fprintf
对于共享库,
[qtl@courier lib]$ readelf -r
Relocation section '.' at offset 0x334 contains 6 entries:
Offset Info Type Sym. Name
00001608 00000008 R_386_RELATIVE
00001704 00000008 R_386_RELATIVE
000016d4 00000106 R_386_GLOB_DAT 00000000 __gmon_start__
000016d8 00000206 R_386_GLOB_DAT 00000000 _Jv_RegisterClasses
000016dc 00000606 R_386_GLOB_DAT 00000000 stdout
000016e0 00000706 R_386_GLOB_DAT 00000000 __cxa_finalize
Relocation section '.' at offset 0x364 contains 5 entries:
Offset Info Type Sym. Name
000016f0 00000107 R_386_JUMP_SLOT 00000000 __gmon_start__
000016f4 00000307 R_386_JUMP_SLOT 00000000 cos
000016f8 00000407 R_386_JUMP_SLOT 00000000 fwrite
000016fc 00000507 R_386_JUMP_SLOT 00000000 fprintf
00001700 00000707 R_386_JUMP_SLOT 00000000 __cxa_finalize
[qtl@courier lib]$ objdump -R
: file format elf32-i386
DYNAMIC RELOCATION RECORDS
OFFSET TYPE VALUE
00001608 R_386_RELATIVE *ABS*
00001704 R_386_RELATIVE *ABS*
000016d4 R_386_GLOB_DAT __gmon_start__
000016d8 R_386_GLOB_DAT _Jv_RegisterClasses
000016dc R_386_GLOB_DAT stdout
000016e0 R_386_GLOB_DAT __cxa_finalize
000016f0 R_386_JUMP_SLOT __gmon_start__
000016f4 R_386_JUMP_SLOT cos
000016f8 R_386_JUMP_SLOT fwrite
000016fc R_386_JUMP_SLOT fprintf
00001700 R_386_JUMP_SLOT __cxa_finalize
有上面可以看出, readelf的显示分节, 而objdump则将两个节合在一起. readelf的
显示更加清晰一些.
3. 显示动态重定位条目(或者可以认为是动态链接相关的重定位条目)
(按objdump的man page说明, 只对dynamic object有效, 如某些类型的共享库)
readelf和objdump等价的命令为readelf -D -r file和objdump -R file.
对readelf使用-r和-D -r的区别, 对于共享库在于数据的呈现方式略有不同. 这两种
都将数据解析后呈现出来. 前者显示的是相对于基地址的偏移, 后者则显示绝对偏移量.
前者显示条目数, 后者显示字节数.
两者输出对比:
[qtl@courier lib]$ readelf -D -r
'REL' relocation section at offset 0x334 contains 48 bytes:
Offset Info Type Sym. Name
00001608 00000008 R_386_RELATIVE
00001704 00000008 R_386_RELATIVE
000016d4 00000106 R_386_GLOB_DAT 00000000 __gmon_start__
000016d8 00000206 R_386_GLOB_DAT 00000000 _Jv_RegisterClasses
000016dc 00000606 R_386_GLOB_DAT 00000000 stdout
000016e0 00000706 R_386_GLOB_DAT 00000000 __cxa_finalize
'PLT' relocation section at offset 0x364 contains 40 bytes:
Offset Info Type Sym. Name
000016f0 00000107 R_386_JUMP_SLOT 00000000 __gmon_start__
000016f4 00000307 R_386_JUMP_SLOT 00000000 cos
000016f8 00000407 R_386_JUMP_SLOT 00000000 fwrite
000016fc 00000507 R_386_JUMP_SLOT 00000000 fprintf
00001700 00000707 R_386_JUMP_SLOT 00000000 __cxa_finalize
[qtl@courier lib]$ objdump -R
: file format elf32-i386
DYNAMIC RELOCATION RECORDS
OFFSET TYPE VALUE
00001608 R_386_RELATIVE *ABS*
00001704 R_386_RELATIVE *ABS*
000016d4 R_386_GLOB_DAT __gmon_start__
000016d8 R_386_GLOB_DAT _Jv_RegisterClasses
000016dc R_386_GLOB_DAT stdout
000016e0 R_386_GLOB_DAT __cxa_finalize
000016f0 R_386_JUMP_SLOT __gmon_start__
000016f4 R_386_JUMP_SLOT cos
000016f8 R_386_JUMP_SLOT fwrite
000016fc R_386_JUMP_SLOT fprintf
00001700 R_386_JUMP_SLOT __cxa_finalize
另外有必要说明的是如果对可重定位文件(.o文件)应用这两个命令是无效的,
错误提示如下:
[qtl@courier lib]$ readelf -D -r bar.o
There are no dynamic relocations in this file.
[qtl@courier lib]$ objdump -R bar.o
bar.o: file format elf32-i386
objdump: bar.o: not a dynamic object
objdump: bar.o: Invalid operation
4. 显示节信息: readelf -S和objdump -h
对于可重定位文件, objdump -h不能显示.rel开头的节和.shstrtab, .symtab, .strtab.
而readelf的显示有一个.group节, 其内容为节的group, 可以用-g参数查看.
输出如下:
[qtl@courier lib]$ readelf -S bar.o
There are 13 section headers, starting at offset 0x150:
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .group GROUP 00000000 000034 000008 04 11 11 4
[ 2] .text PROGBITS 00000000 00003c 00005c 00 AX 0 0 4
[ 3] . REL 00000000 0004bc 000030 08 11 2 4
[ 4] .data PROGBITS 00000000 000098 000000 00 WA 0 0 4
[ 5] .bss NOBITS 00000000 000098 000000 00 WA 0 0 4
[ 6] .rodata PROGBITS 00000000 000098 00000e 00 A 0 0 1
[ 7] .comment PROGBITS 00000000 0000a6 00002e 00 0 0 1
[ 8] .text.___ PROGBITS 00000000 0000d4 000004 00 AXG 0 0 1
[ 9] .-stack PROGBITS 00000000 0000d8 000000 00 0 0 1
[10] .shstrtab STRTAB 00000000 0000d8 000075 00 0 0 1
[11] .symtab SYMTAB 00000000 000358 000110 10 12 10 4
[12] .strtab STRTAB 00000000 000468 000053 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings)
I (info), L (link order), G (group), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)
[qtl@courier lib]$ objdump -h bar.o
bar.o: file format elf32-i386
Sections:
Idx Name Size VMA LMA File off Algn
0 ___pc_ 00000008 00000000 00000000 00000034 2**2
CONTENTS, READONLY, EXCLUDE, GROUP,
LINK_ONCE_DISCARD
1 .text 0000005c 00000000 00000000 0000003c 2**2
CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
2 .data 00000000 00000000 00000000 00000098 2**2
CONTENTS, ALLOC, LOAD, DATA
3 .bss 00000000 00000000 00000000 00000098 2**2
ALLOC
4 .rodata 0000000e 00000000 00000000 00000098 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
5 .comment 0000002e 00000000 00000000 000000a6 2**0
CONTENTS, READONLY
6 .text.___pc_ 00000004 00000000 00000000 000000d4 2**0
CONTENTS, ALLOC, LOAD, READONLY, CODE
7 .-stack 00000000 00000000 00000000 000000d8 2**0
CONTENTS, READONLY
对于共享库, objdump -h仍然不能显示.shstrtab, .symtab, .strtab三个节, 另外还有
一个区别在于readelf从一个NULL类型的节开始, 而objdump的输出去掉了这个空的节.
[qtl@courier lib]$ readelf -S
There are 27 section headers, starting at offset 0x8f0:
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] . GNU_HASH 000000b4 0000b4 000048 04 A 2 0 4
[ 2] .dynsym DYNSYM 000000fc 0000fc 000110 10 A 3 1 4
[ 3] .dynstr STRTAB 0000020c 00020c 0000b3 00 A 0 0 1
[ 4] .n VERSYM 000002c0 0002c0 000022 02 A 2 0 2
[ 5] .n_r VERNEED 000002e4 0002e4 000050 00 A 3 2 4
[ 6] . REL 00000334 000334 000030 08 A 2 0 4
[ 7] . REL 00000364 000364 000028 08 A 2 9 4
[ 8] .init PROGBITS 0000038c 00038c 000017 00 AX 0 0 4
[ 9] .plt PROGBITS 000003a4 0003a4 000060 04 AX 0 0 4
[10] .text PROGBITS 00000410 000410 0001a4 00 AX 0 0 16
[11] .fini PROGBITS 000005b4 0005b4 00001c 00 AX 0 0 4
[12] .rodata PROGBITS 000005d0 0005d0 00001d 00 A 0 0 1
[13] .eh_frame PROGBITS 000005f0 0005f0 000004 00 A 0 0 4
[14] .ctors PROGBITS 000015f4 0005f4 000008 00 WA 0 0 4
[15] .dtors PROGBITS 000015fc 0005fc 000008 00 WA 0 0 4
[16] .jcr PROGBITS 00001604 000604 000004 00 WA 0 0 4
[17] . PROGBITS 00001608 000608 000004 00 WA 0 0 4
[18] .dynamic DYNAMIC 0000160c 00060c 0000c8 08 WA 3 0 4
[19] .got PROGBITS 000016d4 0006d4 000010 04 WA 0 0 4
[20] . PROGBITS 000016e4 0006e4 000020 04 WA 0 0 4
[21] .data PROGBITS 00001704 000704 000004 00 WA 0 0 4
[22] .bss NOBITS 00001708 000708 000010 00 WA 0 0 4
[23] .comment PROGBITS 00000000 000708 000114 00 0 0 1
[24] .shstrtab STRTAB 00000000 00081c 0000d2 00 0 0 1
[25] .symtab SYMTAB 00000000 000d28 0003d0 10 26 45 4
[26] .strtab STRTAB 00000000 0010f8 0001d7 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings)
I (info), L (link order), G (group), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)
[qtl@courier lib]$ objdump -h
: file format elf32-i386
Sections:
Idx Name Size VMA LMA File off Algn
0 . 00000048 000000b4 000000b4 000000b4 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
1 .dynsym 00000110 000000fc 000000fc 000000fc 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
2 .dynstr 000000b3 0000020c 0000020c 0000020c 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
3 .n 00000022 000002c0 000002c0 000002c0 2**1
CONTENTS, ALLOC, LOAD, READONLY, DATA
4 .n_r 00000050 000002e4 000002e4 000002e4 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
5 . 00000030 00000334 00000334 00000334 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
6 . 00000028 00000364 00000364 00000364 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
7 .init 00000017 0000038c 0000038c 0000038c 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
8 .plt 00000060 000003a4 000003a4 000003a4 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
9 .text 000001a4 00000410 00000410 00000410 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
10 .fini 0000001c 000005b4 000005b4 000005b4 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
11 .rodata 0000001d 000005d0 000005d0 000005d0 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
12 .eh_frame 00000004 000005f0 000005f0 000005f0 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
13 .ctors 00000008 000015f4 000015f4 000005f4 2**2
CONTENTS, ALLOC, LOAD, DATA
14 .dtors 00000008 000015fc 000015fc 000005fc 2**2
CONTENTS, ALLOC, LOAD, DATA
15 .jcr 00000004 00001604 00001604 00000604 2**2
CONTENTS, ALLOC, LOAD, DATA
16 . 00000004 00001608 00001608 00000608 2**2
CONTENTS, ALLOC, LOAD, DATA
17 .dynamic 000000c8 0000160c 0000160c 0000060c 2**2
CONTENTS, ALLOC, LOAD, DATA
18 .got 00000010 000016d4 000016d4 000006d4 2**2
CONTENTS, ALLOC, LOAD, DATA
19 . 00000020 000016e4 000016e4 000006e4 2**2
CONTENTS, ALLOC, LOAD, DATA
20 .data 00000004 00001704 00001704 00000704 2**2
CONTENTS, ALLOC, LOAD, DATA
21 .bss 00000010 00001708 00001708 00000708 2**2
ALLOC
22 .comment 00000114 00000000 00000000 00000708 2**0
CONTENTS, READONLY
5. 显示.dynamic节信息
只readelf -d有对应的功能, objdump没有. 另外需要注意, 看重定位文件不需要动态
链接(加载), 所以没有.dynamic节. 对于共享库文件输出如下:
[qtl@courier lib]$ readelf -d
Dynamic section at offset 0x60c contains 21 entries:
Tag Type Name/Value
0x00000001 (NEEDED) Shared library: [.6]
0x00000001 (NEEDED) Shared library: [.6]
0x0000000c (INIT) 0x38c
0x0000000d (FINI) 0x5b4
0x6ffffef5 (GNU_HASH) 0xb4
0x00000005 (STRTAB) 0x20c
0x00000006 (SYMTAB) 0xfc
0x0000000a (STRSZ) 179 (bytes)
0x0000000b (SYMENT) 16 (bytes)
0x00000003 (PLTGOT) 0x16e4
0x00000002 (PLTRELSZ) 40 (bytes)
0x00000014 (PLTREL) REL
0x00000017 (JMPREL) 0x364
0x00000011 (REL) 0x334
0x00000012 (RELSZ) 48 (bytes)
0x00000013 (RELENT) 8 (bytes)
0x6ffffffe (VERNEED) 0x2e4
0x6fffffff (VERNEEDNUM) 2
0x6ffffff0 (VERSYM) 0x2c0
0x6ffffffa (RELCOUNT) 2
0x00000000 (NULL) 0x0
6. 显示程序段信息
第二个readelf支持而objdump没有的功能. 命令参数为readelf -l.
同样, 对于可重定位文件而言没有段. 这一点也可以从ELF头中看到, 命令为readelf -h.
[qtl@courier lib]$ readelf -l
Elf file type is DYN (Shared object file)
Entry point 0x410
There are 4 program headers, starting at offset 52
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x000000 0x00000000 0x00000000 0x005f4 0x005f4 R E 0x1000
LOAD 0x0005f4 0x000015f4 0x000015f4 0x00114 0x00124 RW 0x1000
DYNAMIC 0x00060c 0x0000160c 0x0000160c 0x000c8 0x000c8 RW 0x4
GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x4
Section to Segment mapping:
00 . .dynsym .dynstr .n .n_r .
. .init .plt .text .fini .rodata .eh_frame
01 .ctors .dtors .jcr . .dynamic .got . .data .bss
02 .dynamic
03
7. 以字节(HEX或字符)形式dump某节的内容
readelf -x
objdump -s
后者默认一次dump所有节的内容. 如果只想dump某节的内容, 则用-j
参数指定. readelf一次只能dump某一节的内容. 两者输出如下:
[qtl@courier lib]$ readelf -x .dynamic
Hex dump of section '.dynamic':
0x0000160c 0000007b 00000001 00000071 00000001 ....q.......{...
0x0000161c 000005b4 0000000d 0000038c 0000000c ................
0x0000162c 0000020c 00000005 000000b4 6ffffef5 ...o............
0x0000163c 000000b3 0000000a 000000fc 00000006 ................
0x0000164c 000016e4 00000003 00000010 0000000b ................
0x0000165c 00000011 00000014 00000028 00000002 ....(...........
0x0000166c 00000334 00000011 00000364 00000017 ....d.......4...
0x0000167c 00000008 00000013 00000030 00000012 ....0...........
0x0000168c 00000002 6fffffff 000002e4 6ffffffe ...o.......o....
0x0000169c 00000002 6ffffffa 000002c0 6ffffff0 ...o.......o....
0x000016ac 00000000 00000000 00000000 00000000 ................
0x000016bc 00000000 00000000 00000000 00000000 ................
0x000016cc 00000000 00000000 ........
[qtl@courier lib]$ objdump -s -j .dynamic
: file format elf32-i386
Contents of section .dynamic:
160c 01000000 71000000 01000000 7b000000 ....q.......{...
161c 0c000000 8c030000 0d000000 b4050000 ................
162c f5feff6f b4000000 05000000 0c020000 ...o............
163c 06000000 fc000000 0a000000 b3000000 ................
164c 0b000000 10000000 03000000 e4160000 ................
165c 02000000 28000000 14000000 11000000 ....(...........
166c 17000000 64030000 11000000 34030000 ....d.......4...
167c 12000000 30000000 13000000 08000000 ....0...........
168c feffff6f e4020000 ffffff6f 02000000 ...o.......o....
169c f0ffff6f c0020000 faffff6f 02000000 ...o.......o....
16ac 00000000 00000000 00000000 00000000 ................
16bc 00000000 00000000 00000000 00000000 ................
16cc 00000000 00000000 ........
8. 查看ELF程序头信息
readelf -h提供完整的信息, objdump -f只提供很少的信息.
9. 查看符号信息
readelf -s
objdump -t
两个命令都提供类似nm的信息. 输出如下:
[qtl@courier lib]$ readelf -s
Symbol table '.dynsym' contains 17 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 00000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__
2: 00000000 0 NOTYPE WEAK DEFAULT UND _Jv_RegisterClasses
3: 00000000 38 FUNC GLOBAL DEFAULT UND cos@GLIBC_2.0 (2)
4: 00000000 351 FUNC GLOBAL DEFAULT UND fwrite@GLIBC_2.0 (3)
5: 00000000 36 FUNC GLOBAL DEFAULT UND fprintf@GLIBC_2.0 (3)
6: 00000000 4 OBJECT GLOBAL DEFAULT UND stdout@GLIBC_2.0 (3)
7: 00000000 346 FUNC WEAK DEFAULT UND
__cxa_finalize@GLIBC_2.1.3
(4)
8: 00000520 92 FUNC GLOBAL DEFAULT 10 bar
9: 000004dc 66 FUNC GLOBAL DEFAULT 10 foo
10: 00001718 0 NOTYPE GLOBAL DEFAULT ABS _end
11: 00001708 0 NOTYPE GLOBAL DEFAULT ABS _edata
12: 0000170c 4 OBJECT GLOBAL DEFAULT 22 foo_var
13: 00001708 0 NOTYPE GLOBAL DEFAULT ABS __bss_start
14: 0000038c 0 FUNC GLOBAL DEFAULT 8 _init
15: 000005b4 0 FUNC GLOBAL DEFAULT 11 _fini
16: 00001710 8 OBJECT GLOBAL DEFAULT 22 bar_var
Symbol table '.symtab' contains 61 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 000000b4 0 SECTION LOCAL DEFAULT 1
2: 000000fc
3: 0000020c
4: 000002c0
5: 000002e4
6: 00000334
7: 00000364
8: 0000038c
9: 000003a4
10: 00000410
11: 000005b4
12: 000005d0
13: 000005f0
14: 000015f4
15: 000015fc
16: 00001604
17: 00001608
18: 0000160c
19: 000016d4
20: 000016e4
21: 00001704
22: 00001708
23: 00000000
24: 00000410
25: 00000000
26: 000015f4
27: 000015fc
28: 00001604
29: 00001708
30: 00001704
31: 00000440
32: 000004a0
33: 00000000
34: 000015f8
35: 00001600
36: 000005f0
0 SECTION LOCAL DEFAULT 2
0 SECTION LOCAL DEFAULT 3
0 SECTION LOCAL DEFAULT 4
0 SECTION LOCAL DEFAULT 5
0 SECTION LOCAL DEFAULT 6
0 SECTION LOCAL DEFAULT 7
0 SECTION LOCAL DEFAULT 8
0 SECTION LOCAL DEFAULT 9
0 SECTION LOCAL DEFAULT 10
0 SECTION LOCAL DEFAULT 11
0 SECTION LOCAL DEFAULT 12
0 SECTION LOCAL DEFAULT 13
0 SECTION LOCAL DEFAULT 14
0 SECTION LOCAL DEFAULT 15
0 SECTION LOCAL DEFAULT 16
0 SECTION LOCAL DEFAULT 17
0 SECTION LOCAL DEFAULT 18
0 SECTION LOCAL DEFAULT 19
0 SECTION LOCAL DEFAULT 20
0 SECTION LOCAL DEFAULT 21
0 SECTION LOCAL DEFAULT 22
0 SECTION LOCAL DEFAULT 23
0 FUNC LOCAL DEFAULT 10 call_gmon_start
0 FILE LOCAL DEFAULT ABS crtstuff.c
0 OBJECT LOCAL DEFAULT 14 __CTOR_LIST__
0 OBJECT LOCAL DEFAULT 15 __DTOR_LIST__
0 OBJECT LOCAL DEFAULT 16 __JCR_LIST__
1 OBJECT LOCAL DEFAULT 22 completed.5758
0 OBJECT LOCAL DEFAULT 21 p.5756
0 FUNC LOCAL DEFAULT 10 __do_global_dtors_aux
0 FUNC LOCAL DEFAULT 10 frame_dummy
0 FILE LOCAL DEFAULT ABS crtstuff.c
0 OBJECT LOCAL DEFAULT 14 __CTOR_END__
0 OBJECT LOCAL DEFAULT 15 __DTOR_END__
0 OBJECT LOCAL DEFAULT 13 __FRAME_END__
37: 00001604 0 OBJECT LOCAL DEFAULT 16 __JCR_END__
38: 00000580 0 FUNC LOCAL DEFAULT 10 __do_global_ctors_aux
39: 00000000 0 FILE LOCAL DEFAULT ABS foo.c
40: 00000000 0 FILE LOCAL DEFAULT ABS bar.c
41: 000016e4 0 OBJECT LOCAL HIDDEN ABS _GLOBAL_OFFSET_TABLE_
42: 00001608 0 OBJECT LOCAL HIDDEN 17 __dso_handle
43: 000004d7 0 FUNC LOCAL HIDDEN 10 ___pc_
44: 0000160c 0 OBJECT LOCAL HIDDEN ABS _DYNAMIC
45: 00000520 92 FUNC GLOBAL DEFAULT 10 bar
46: 00000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__
47: 00000000 0 NOTYPE WEAK DEFAULT UND _Jv_RegisterClasses
48: 000005b4 0 FUNC GLOBAL DEFAULT 11 _fini
49: 0000170c 4 OBJECT GLOBAL DEFAULT 22 foo_var
50: 000004dc 66 FUNC GLOBAL DEFAULT 10 foo
51: 00000000 38 FUNC GLOBAL DEFAULT UND cos@@GLIBC_2.0
52: 00000000 351 FUNC GLOBAL DEFAULT UND fwrite@@GLIBC_2.0
53: 00000000 36 FUNC GLOBAL DEFAULT UND fprintf@@GLIBC_2.0
54: 00001708 0 NOTYPE GLOBAL DEFAULT ABS __bss_start
55: 00001718 0 NOTYPE GLOBAL DEFAULT ABS _end
56: 00000000 4 OBJECT GLOBAL DEFAULT UND stdout@@GLIBC_2.0
57: 00001710 8 OBJECT GLOBAL DEFAULT 22 bar_var
58: 00001708 0 NOTYPE GLOBAL DEFAULT ABS _edata
59: 00000000 346 FUNC WEAK DEFAULT UND
__cxa_finalize@@GLIBC_2.1
60: 0000038c 0 FUNC GLOBAL DEFAULT 8 _init
[qtl@courier lib]$ objdump -t
: file format elf32-i386
SYMBOL TABLE:
000000b4 l d . 00000000 .
000000fc l d .dynsym 00000000 .dynsym
0000020c l d .dynstr 00000000 .dynstr
000002c0 l d .n 00000000 .n
000002e4 l d .n_r 00000000 .n_r
00000334 l d . 00000000 .
00000364 l d . 00000000 .
0000038c l d .init 00000000 .init
000003a4 l d .plt 00000000 .plt
00000410 l d .text 00000000 .text
000005b4 l d .fini 00000000 .fini
000005d0 l d .rodata 00000000 .rodata
000005f0 l d .eh_frame 00000000 .eh_frame
000015f4 l d .ctors 00000000 .ctors
000015fc l d .dtors 00000000 .dtors
00001604 l d .jcr 00000000 .jcr
00001608 l d . 00000000 .
0000160c l d .dynamic 00000000 .dynamic
000016d4 l d .got 00000000 .got
000016e4 l d . 00000000 .
00001704 l d .data 00000000 .data
00001708 l d .bss 00000000 .bss
00000000 l d .comment 00000000
00000410 l F .text 00000000
00000000 l df *ABS* 00000000
000015f4 l O .ctors 00000000
000015fc l O .dtors 00000000
00001604 l O .jcr 00000000
00001708 l O .bss 00000001
00001704 l O .data 00000000
00000440 l F .text 00000000
000004a0 l F .text 00000000
00000000 l df *ABS* 00000000
000015f8 l O .ctors 00000000
00001600 l O .dtors 00000000
000005f0 l O .eh_frame 00000000
00001604 l O .jcr 00000000
00000580 l F .text 00000000
00000000 l df *ABS* 00000000
00000000 l df *ABS* 00000000
000016e4 l O *ABS* 00000000
00001608 l O . 00000000
000004d7 l F .text 00000000
0000160c l O *ABS* 00000000
00000520 g F .text 0000005c
00000000 w *UND* 00000000
00000000 w *UND* 00000000
000005b4 g F .fini 00000000
0000170c g O .bss 00000004
000004dc g F .text 00000042
00000000 F *UND* 00000026
00000000 F *UND* 0000015f
00000000 F *UND* 00000024
00001708 g *ABS* 00000000
00001718 g *ABS* 00000000
00000000 O *UND* 00000004
00001710 g O .bss 00000008
.comment
call_gmon_start
crtstuff.c
__CTOR_LIST__
__DTOR_LIST__
__JCR_LIST__
completed.5758
p.5756
__do_global_dtors_aux
frame_dummy
crtstuff.c
__CTOR_END__
__DTOR_END__
__FRAME_END__
__JCR_END__
__do_global_ctors_aux
foo.c
bar.c
.hidden _GLOBAL_OFFSET_TABLE_
.hidden __dso_handle
.hidden ___pc_
.hidden _DYNAMIC
bar
__gmon_start__
_Jv_RegisterClasses
_fini
foo_var
foo
cos@@GLIBC_2.0
fwrite@@GLIBC_2.0
fprintf@@GLIBC_2.0
__bss_start
_end
stdout@@GLIBC_2.0
bar_var
00001708 g *ABS* 00000000 _edata
00000000 w F *UND* 0000015a __cxa_finalize@@GLIBC_2.1.3
0000038c g F .init 00000000 _init
注意readelf同时显示了.dynsym的信息, 而objdump实际上只显示了.symtab部分的信息.
如果需要显示动态部分的符号, 使用-T参数. 输出如下:
[qtl@courier lib]$ objdump -T
: file format elf32-i386
DYNAMIC SYMBOL TABLE:
00000000 w D *UND* 00000000 __gmon_start__
00000000 w D *UND* 00000000 _Jv_RegisterClasses
00000000 DF *UND* 00000026 GLIBC_2.0 cos
00000000 DF *UND* 0000015f GLIBC_2.0 fwrite
00000000 DF *UND* 00000024 GLIBC_2.0 fprintf
00000000 DO *UND* 00000004 GLIBC_2.0 stdout
00000000 w DF *UND* 0000015a GLIBC_2.1.3 __cxa_finalize
00000520 g DF .text 0000005c Base bar
000004dc g DF .text 00000042 Base foo
00001718 g D *ABS* 00000000 Base _end
00001708 g D *ABS* 00000000 Base _edata
0000170c g DO .bss 00000004 Base foo_var
00001708 g D *ABS* 00000000 Base __bss_start
0000038c g DF .init 00000000 Base _init
000005b4 g DF .fini 00000000 Base _fini
00001710 g DO .bss 00000008 Base bar_var
对readelf同时使用-D -s参数无效. 对照手册页说明也没弄清楚-D的主要用途,
在这里可能因为-s已经能够都显示了.
10. 一次全部
两个命令都提供了一个参数, 指定多个其他参数的集合一起显示, 但显示内容略有不同.
readelf -a: -h -l -S -r -s -d -n -V
objdump -x: -a -f -h -p -r -t


发布评论