readelf nm objdump 命令详解

讲解上面三条命令之前要先了解一下ELF格式文件https://blog.csdn.net/u014608280/article/details/81983055

一、readelf

 readelf命令可以查看ELF文件的详细信息。

选项

-a
--all 显示全部信息,等价于 -h -l -S -s -r -d -V -A -I. 

-h
--file-header 显示elf文件开始的文件头信息. 

-l
--program-headers 
--segments 显示程序头(段头)信息(如果有的话)。 

-S
--section-headers 
--sections 显示节头信息(如果有的话)。 

-g
--section-groups 显示节组信息(如果有的话)。 

-t
--section-details 显示节的详细信息(-S的)。 

-s
--syms       
--symbols 显示符号表段中的项(如果有的话)。 

-e
--headers 显示全部头信息,等价于: -h -l -S 

-n
--notes 显示note段(内核注释)的信息。 

-r
--relocs 显示可重定位段的信息。 

-u
--unwind 显示unwind段信息。当前只支持IA64 ELF的unwind段信息。 

-d
--dynamic 显示动态段的信息。 

-V
--version-info 显示版本段的信息。 

-A
--arch-specific 显示CPU构架信息。 

-D
--use-dynamic 使用动态段中的符号表显示符号,而不是使用符号段。 

-x <number or name>
--hex-dump=<number or name> 以16进制方式显示指定段内内容。number指定段表中段的索引,或字符串指定文件中的段名。 

-w[liaprmfFsoR] or
--debug-dump[=line,=info,=abbrev,=pubnames,=aranges,=macro,=frames,=frames-interp,=str,=loc,=Ranges] 显示调试段中指定的内容。 

-I
--histogram 显示符号的时候,显示bucket list长度的柱状图。 

-v
--version 显示readelf的版本信息。 

-H
--help 显示readelf所支持的命令行选项。 

-W
--wide 宽行输出。 

@file 可以将选项集中到一个文件中,然后使用这个@file选项载入。

查看ELF文件头信息

从输出结果我们可以得到:ELF魔数、文件机器字节长度、数据存储方式、版本、运行平台、ABI版本、ELF重定位类型、硬件平台、硬件平台版本、入口地址、程序头入口和长度、段表的位置和长度及段数量等。

ELF文件中有很多各式各样的段,段表就是保存这些段的基本属性的结构。段表是ELF文件中除了文件头以外最重要的结构。

虽然用objdump -h 命令也可以查看ELF文件中包含的段,但是只能查看关键的段,其他辅助的段会省略掉。我们可以用readelf -S命令查看详细的段表结构。

对比一下

objdump -h 

.text(代码段) 、.data(数据段)、.bss是最基本的段,.rodata(只读数据段)、.comment(注释信息段)、.note.GNU-stack(堆栈提示段)。Name 为段名称、Size 是段的长度,File off 为段所在的位置。第二行表示段的各种属性CONTENTS表示改段在文件中存在。

这里提一下另外一个命令size,可以查看目标文件中主要段的大小

readelf -S

段表就是有11个元素的数组,Name为段名,Type为段的类型(详细见附表1),Address为段虚拟地址,Offset为段偏移,Size为段的长度,Entsize为Section Entry Size项的长度(有些段包含了一些固定大小的项,比如符号表,它包含的每个符号所占的大小都是一样的。对于这种段,Entsize表示每个项的大小),Flage为段的标志位(详细见附表2),Link和Info为段的连接信息,Align为段地址对齐。

ELF文件中还有一个重要的是符号表,用readelf -s 查看

Value为符号相对应的值,Size为符号的大小(对于包含数据的符号,即为该数据类型的大小),Type为符号类型(见符号类型表),Bind绑定信息(LOCAL 表示局部符号,对目标文件外部不可见;GLOBAL表示全局符号,外部可见;WEAK 表示弱引用),Ndx表示为符号所在的段(ABS表示该符号包含一个绝对值,COMMON 表示一般为为初始化的全局符号,UNDEF表示改符号未定义,在本目标文件中被引用但是定义在其他目标文件中)

二、objdump

objdump 查看目标文件或者可执行的目标文件

常用参数:

  • -f 显示文件头信息
  • -D 反汇编所有section (-d反汇编特定section)
  • -h 显示目标文件各个section的头部摘要信息
  • -x 显示所有可用的头信息,包括符号表、重定位入口。-x 等价于 -a -f -h -r -t 同时指定。
  • -i 显示对于 -b 或者 -m 选项可用的架构和目标格式列表。
  • -r 显示文件的重定位入口。如果和-d或者-D一起使用,重定位部分以反汇编后的格式显示出来。
  • -R 显示文件的动态重定位入口,仅仅对于动态目标文件有意义,比如某些共享库。
  • -s 将所有段的内容以十六进制的方式打印出来
  • -S 尽可能反汇编出源代码,尤其当编译的时候指定了-g这种调试参数时,效果比较明显。隐含了-d参数。
  • -t 显示文件的符号表入口。类似于nm -s提供的信息

objdump  -d   查看每个段的汇编

输出太长只截取了一部分

objdump -t 查看符号信息

三、nm

nm -s   

 

输出符号含义说明:

A 该符号的值是绝对的,在以后的链接过程中,不允许进行改变。这样的符号值,常常出现在中断向量表中,例如用符号来表示各个中断向量函数在中断向量表中的位置。
B 该符号的值出现在非初始化数据段(bss)中。例如,在一个文件中定义全局static int test。则该符号test的类型为b,位于bss section中。其值表示该符号在bss段中的偏移。一般而言,bss段分配于RAM中
C 该符号为common。common symbol是未初始话数据段。该符号没有包含于一个普通section中。只有在链接过程中才进行分配。符号的值表示该符号需要的字节数。例如在一个c文件中,定义int test,并且该符号在别的地方会被引用,则该符号类型即为C。否则其类型为B。
D 该符号位于初始话数据段中。一般来说,分配到data section中。例如定义全局int baud_table[5] = {9600, 19200, 38400, 57600, 115200},则会分配于初始化数据段中。
G 该符号也位于初始化数据段中。主要用于small object提高访问small data object的一种方式。
I 该符号是对另一个符号的间接引用。
N 该符号是一个debugging符号。
R 该符号位于只读数据区。例如定义全局const int test[] = {123, 123};则test就是一个只读数据区的符号。注意在cygwin下如果使用gcc直接编译成MZ格式时,源文件中的test对应_test,并且其符号类型为D,即初始化数据段中。但是如果使用m6812-elf-gcc这样的交叉编译工具,源文件中的test对应目标文件的test,即没有添加下划线,并且其符号类型为R。一般而言,位于rodata section。值得注意的是,如果在一个函数中定义const char *test = “abc”, const char test_int = 3。使用nm都不会得到符号信息,但是字符串“abc”分配于只读存储器中,test在rodata section中,大小为4。
S 符号位于非初始化数据区,用于small object。
T 该符号位于代码区text section。
U 该符号在当前文件中是未定义的,即该符号的定义在别的文件中。例如,当前文件调用另一个文件中定义的函数,在这个被调用的函数在当前就是未定义的;但是在定义它的文件中类型是T。但是对于全局变量来说,在定义它的文件中,其符号类型为C,在使用它的文件中,其类型为U。
V 该符号是一个weak object。
W The symbol is a weak symbol that has not been specifically tagged as a weak object symbol.
- 该符号是a.out格式文件中的stabs symbol。
? 该符号类型没有定义

附表1 段的类型 

 

附表2 段的标志位

以及系统保留段的属性

2、符号类型表

猜你喜欢

转载自blog.csdn.net/u014608280/article/details/81948141