0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

深入了解 ELF每个结构的细节

Linux阅码场 来源:Linux阅码场 作者:Linux阅码场 2022-10-20 09:04 次阅读

1. 背景

长期以来只知道 ELF 是一种广泛使用的文件格式规范,常指动态库、bin等,一直没动力深入研究。出于业务需求,我花了好些天仔细分析 RTOS Bin 的 Section 和 Symbol 。也是趁着这机会,查阅大量资料,完善了知识脉络。

但是可以预见,业务结束一段时间后,由于不常用,我不可避免会渐渐遗忘。从入门到遗忘,相信只要经历过深度学习的小伙伴都会有的痛苦挣扎。为了在将来需要时唤醒记忆,我决定通过这文章打下锚点,也希望此文能帮到更多人更快入门。

内容:翻译 ELF(v1.2) 通用规范,深入了解 ELF 每个结构的细节,并在最后提供一个实例协助理解。

2. 基础概念

2.1 什么是 ELF 文件

ELF 的全称是 Executable and Linking Format,即“可执行可连接格式”,通俗来说,就是二进制程序。ELF 规定了这二进制程序的组织规范,所有以这规范组织的文件都叫 ELF 文件。ELF 文件有以下四类。

ELF 文件类型 示例
可重定位文件(relocatable file) 例如编译的过程文件 ".o"
共享目标文件(shared object file) 例如 ".so" 库,以及使用动态链接的 bin
可执行文件(executable file) 例如静态链接的文件
core 文件 例如 Linux 上的 coredump 文件

我们通过 file 命令可以识别出来

#test.o:gcc-ctest.c-otest.o
#test-static-link.bin:gcc--statictest.c-otest
#test-dynamic-link.bin:gcctest.c-otest

$filetest.otest-static-link.bintest-dynamic-link.bin
test.o:ELF...relocatable,...
test-dynamic-link.bin:ELF...sharedobject,...
test-static-link.bin:ELF...executable,...

除此之外,我们习惯上叫 ".o" 文件为 目标文件(object file),链接好的可执行文件叫 bin文件

2.2 ELF 文件结构概览

ELF 文件主要的用途有两个,

构建程序,链接成动态库或者bin,一般是目标文件 ".o"

运行程序,一般指链接好的 ".so" 或者 "bin"

这个 ELF 文件用作不同用途,文件结构的解析角度就有点不一样,通俗来说,不同用途对需要哪些数据的要求不一样,例如构建(链接)时 节表头(Section Table Header)是必须的,但运行时却是可选的,例如运行需要段信息,而链接只有节信息。

c5476f66-5010-11ed-a3b6-dac502259ad0.png

在上图中,程序头表(Program Header Table)紧跟在 ELF 文件头(File Header)之后,节头表(Section Header Table)紧跟在节信息之后,但在实际的文件中,这个顺序并不是固定的。在 ELF 文件的各个组成部分中,只有ELF 文件头的位置是固定的,其它内容的位置全都可变

这里有一个潜在的概念,很少看到其他文章明确点出来。Section Header Table 描述了所有节信息表,而 Program Header Table 其实描述的是所有的 段信息表

下一章节会介绍一些关键字段的含义。在这里引入这张图,主要为了引出两个重要的概念 节(Seciton)段(Segment)。由上图可以发现,链接视图有大量的 节(Section),而执行视图有 段(Segment)。那么什么是段,什么又是节?

2.3 节(Section) Vs. 段(Segment)

或许我们听说过 bss段text(代码)段,或者 data(数据)段,但其实我们口头交流的段更多是泛化的 。为什么呢?

我们随便拿个 bin,不妨罗列下 程序头表(Program Header Table):

$readelf-l/bin/ls

ElffiletypeisDYN(Sharedobjectfile)
Entrypoint0x5850
Thereare9programheaders,startingatoffset64

ProgramHeaders:
......

SectiontoSegmentmapping:
......

Program Headers 下面罗列出了所有的段信息,详细如下图所示,共计有 9 个段。

ProgramHeaders:
TypeOffsetVirtAddrPhysAddr
FileSizMemSizFlagsAlign
PHDR0x00000000000000400x00000000000000400x0000000000000040
0x00000000000001f80x00000000000001f8RE0x8
INTERP0x00000000000002380x00000000000002380x0000000000000238
0x000000000000001c0x000000000000001cR0x1
[Requestingprograminterpreter:/lib64/ld-linux-x86-64.so.2]
LOAD0x00000000000000000x00000000000000000x0000000000000000
0x000000000001e6e80x000000000001e6e8RE0x200000
LOAD0x000000000001eff00x000000000021eff00x000000000021eff0
0x00000000000012780x0000000000002570RW0x200000
DYNAMIC0x000000000001fa380x000000000021fa380x000000000021fa38
0x00000000000002000x0000000000000200RW0x8
NOTE0x00000000000002540x00000000000002540x0000000000000254
0x00000000000000440x0000000000000044R0x4
GNU_EH_FRAME0x000000000001b1a00x000000000001b1a00x000000000001b1a0
0x00000000000008840x0000000000000884R0x4
GNU_STACK0x00000000000000000x00000000000000000x0000000000000000
0x00000000000000000x0000000000000000RW0x10
GNU_RELRO0x000000000001eff00x000000000021eff00x000000000021eff0
0x00000000000010100x0000000000001010R0x1

Section to Segment mapping 罗列了 各个段(Segment)包含了哪些节(Section),是的,段是1个或者多个节的集合,详细如下文所示。

SectiontoSegmentmapping:
SegmentSections...
00
01.interp
02.interp.note.ABI-tag.note.gnu.build-id.gnu.hash.dynsym.dynstr.gnu.version.gnu.version_r.rela.dyn.rela.plt.init.plt.plt.got.text.fini.rodata.eh_frame_hdr.eh_frame
03.init_array.fini_array.data.rel.ro.dynamic.got.data.bss
04.dynamic
05.note.ABI-tag.note.gnu.build-id
06.eh_frame_hdr
07
08.init_array.fini_array.data.rel.ro.dynamic.got

总的来说,段是没有名字的,但我们往往把包含 text节 的段叫做 代码(text)段,把含有 data节 的段叫做 数据(data)段。一定程度上在口述时习惯也可以理解为 "段 = 节",例如 bss段rodata段,实际他们指 bss节rodata节。甚至有时候我们说 text段 就狭义的指 text节,完全不用太纠结。

3. 关键的文件结构和节

3.1 一些关键的文件结构

对一个 ELF 格式的文件,有这么几个特殊的结构我们需要关注的。

ELF 文件头(File Header):位于文件最开始,包含了整个文件的结构信息,例如是ELF 幻数,是哪种 ELF 文件,程序头表、节头表的地址等。

程序头表(Program Header Table):描述了所有段的信息

节头表(Section Header Table):描述了所有节的信息

本文不会解释结构体每个元素,而是利用 readelf 工具解读。如果需要详细到每个字节的意义,可以查阅章节1提到的《UnderstandingELF.pdf》

3.1.1 文件头(File Header)

readelf -h 命令可以查看文件头信息,例如:

readelf-h/bin/ls
ELFHeader:
Magic:7f454c46020101000000000000000000
Class:ELF64
Data:2'scomplement,littleendian
Version:1(current)
OS/ABI:UNIX-SystemV
ABIVersion:0
Type:DYN(Sharedobjectfile)
Machine:AdvancedMicroDevicesX86-64
Version:0x1
Entrypointaddress:0x5850
Startofprogramheaders:64(bytesintofile)
Startofsectionheaders:132000(bytesintofile)
Flags:0x0
Sizeofthisheader:64(bytes)
Sizeofprogramheaders:56(bytes)
Numberofprogramheaders:9
Sizeofsectionheaders:64(bytes)
Numberofsectionheaders:28
Sectionheaderstringtableindex:27

解读如下:

字段 含义 备注
Magic 标识是 ELF 文件类型的幻数 值只能为 0x7F + 'E' + 'L' + 'F'
Class 32Bit/64Bit 类型 ELF64 / ELF32
Data 小端/大端编码
Version 文件头版本
OS/ABI 适用的系统 和 ABI
Type 哪种类型的 ELF 文件 DYN:共享目标文件,REL:可重定位文件,EXEC:可执行文件 [1]
Machine 处理器体系架构 常见例如 ARM,X86-64
Version ELF文件的版本
Entry point address 指代程序入口的虚拟地址 一般是 _start(),一系列调用后才到 main()
Start of program headers 程序头表在文件的偏移 单位 Byte
Start of section headers 节头表在文件的偏移 单位 Byte
Flags 处理器特定的标志位
Size of this header ELF 文件头的大小 单位 Byte
Size of program headers 程序头表中每一个表项的大小 单位 Byte
Number of program headers 程序头表中表项的数量,理解为有多少个段
Size of section headers 节头表中每个表项的大小 单位 Byte
Number of section headers 节头表中有表项的数量,理解为有多少个节
Section header string table index 节头表中与节名字表相对应的表项索引信息 [2]

[1]. 文件类型见 2.1 章节
[2]. 段没名字,但节是有名字的,节的名字需要另外保存,对应到 .shstrtab 节

3.1.2 程序头表(Program Header Table)

readelf -l 命令可以查看程序表头的信息。需要注意的是,".o" 文件一般是没有程序表头的。示例如下:

$readelf-l/bin/ls

ElffiletypeisDYN(Sharedobjectfile)
Entrypoint0x5850
Thereare9programheaders,startingatoffset64

ProgramHeaders:
TypeOffsetVirtAddrPhysAddr
FileSizMemSizFlagsAlign
PHDR0x00000000000000400x00000000000000400x0000000000000040
0x00000000000001f80x00000000000001f8RE0x8
INTERP0x00000000000002380x00000000000002380x0000000000000238
0x000000000000001c0x000000000000001cR0x1
[Requestingprograminterpreter:/lib64/ld-linux-x86-64.so.2]
LOAD0x00000000000000000x00000000000000000x0000000000000000
0x000000000001e6e80x000000000001e6e8RE0x200000
LOAD0x000000000001eff00x000000000021eff00x000000000021eff0
0x00000000000012780x0000000000002570RW0x200000
DYNAMIC0x000000000001fa380x000000000021fa380x000000000021fa38
0x00000000000002000x0000000000000200RW0x8
NOTE0x00000000000002540x00000000000002540x0000000000000254
0x00000000000000440x0000000000000044R0x4
GNU_EH_FRAME0x000000000001b1a00x000000000001b1a00x000000000001b1a0
0x00000000000008840x0000000000000884R0x4
GNU_STACK0x00000000000000000x00000000000000000x0000000000000000
0x00000000000000000x0000000000000000RW0x10
GNU_RELRO0x000000000001eff00x000000000021eff00x000000000021eff0
0x00000000000010100x0000000000001010R0x1

SectiontoSegmentmapping:
SegmentSections...
00
01.interp
02.interp.note.ABI-tag.note.gnu.build-id.gnu.hash.dynsym.dynstr.gnu.version.gnu.version_r.rela.dyn.rela.plt.init.plt.plt.got.text.fini.rodata.eh_frame_hdr.eh_frame
03.init_array.fini_array.data.rel.ro.dynamic.got.data.bss
04.dynamic
05.note.ABI-tag.note.gnu.build-id
06.eh_frame_hdr
07
08.init_array.fini_array.data.rel.ro.dynamic.got

Program Headers 罗列了所有段的信息,含义如下表:

字段 含义 备注
Type 段的类型,暗含了如何解析此段的内容 [1]
Offset 本段内容在文件的位置 单位 Byte
FileSiz 本段内容在文件中的大小 单位 Byte
VirtAddr 本段内容的开始位置在进程空间中的虚拟地址
MemSiz 本段内容在进程空间中的大小 单位 Byte
PhysAddr 本段内容的开始位置在进程空间中的物理地址 由于MMU的存在,物理地址不可知,大多时候等于虚拟地址
Flags 段的权限 R/W/E 分别表示 读/写/可执行
Align 大小对齐信息 [3]

[1]. Type 的取值与含义如下

PHDR:此类型的程序头如果存在的话,它表明的是其自身所在的程序头表在文件或内存中的位置和大小。这样的段在文件中可以不存在,只有当所在程序头表所覆盖的段只是整个程序的一部分时,才会出现一次这种表项,而且这种表项一定出现在其它可装载段的表项之前。

INTERP:本段指向了一个以”null”结尾的字符串,这个字符串是一个 ELF 解析器的路径。这种段类型只对可执行程序有意义,当它出现在共享目标文件中时,是一个无意义的多余项。在一个 ELF 文件中它最多只能出现一次,而且必须出现在其它可装载段的表项之前。

LOAD:此类型表明本程序头指向一个可装载的段。段的内容会被从文件中拷贝到内存中。

DYNAMIC:此类型表明本段指明了动态连接的信息。

NOTE:本段指向了一个以”null”结尾的字符串,这个字符串包含一些附加的信息。

[3]. 对于可装载的段来说,其 虚拟地址 和 文件地址 的值至少要向内存页面大小对齐。此数据成员指明本段内容如何在内存和文件中对齐。如果该值为 0 或 1,表明没有对齐要求;否则,此值应该是一个正整数,并且是 2 的幂次数。虚拟地址 和 文件地址 在对此值取模后应该相等。

Section to Segment mapping 展示了段包含哪些节,常关注的是代码段和数据段。下表只是个典型的例子,一个实际的更复杂的代码段可能包含更多的节。

代码段 数据段
.text .data
.rodata .dynamic
.hash .got
.dynsym .bss
.dynstr
.plt
.rel.got

3.1.3 节头表(Section Header Table)

readelf -S 命令可以查看节表头的信息,示例如下:

$readelf-S/bin/ls
Thereare28sectionheaders,startingatoffset0x203a0:

SectionHeaders:
[Nr]NameTypeAddressOffset
SizeEntSizeFlagsLinkInfoAlign
[0]NULL000000000000000000000000
00000000000000000000000000000000000
[1].interpPROGBITS000000000000023800000238
000000000000001c0000000000000000A001
......
00000000000000180000000000000008AX008
[14].textPROGBITS0000000000003e9000003e90
00000000000124d90000000000000000AX0016
......
00000000000003c80000000000000008WA008
[24].dataPROGBITS000000000022000000020000
00000000000002680000000000000000WA0032
[25].bssNOBITS000000000022028000020268
00000000000012e00000000000000000WA0032
......

KeytoFlags:
W(write),A(alloc),X(execute),M(merge),S(strings),I(info),
L(linkorder),O(extraOSprocessingrequired),G(group),T(TLS),
C(compressed),x(unknown),o(OSspecific),E(exclude),
l(large),p(processorspecific)

字段 含义 备注
Name 本节的名字
Size 本节的大小 单位 Byte,如果节类型为 NOBITS,此值仍可能非0,但无意义
Type 本节的类型 [1]
EntSize 如果节是表类型,则表示表项大小 单位 Byte
Address 如果需要映射到虚拟内存,则表示起始地址 单位 Byte
Offset 本节第一个字节所在文件的偏移 单位 Byte
Flags 权限等属性信息
Link 可以查阅章节1提到的《UnderstandingELF.pdf》
Info 可以查阅章节1提到的《UnderstandingELF.pdf》
Align 大小对齐信息

[1]. 节有以下类型

NULL:本节头是一个无效的(非活动的)节头

PROGBITS:本节所含有的信息是由程序定义的,本节内容的格式和含义都由程序来决定。

SYMTAB:所有符号表调试信息,strip 可以干掉,不影响运行

STRTAB:本节是字符串表。

RELA:本节是一个重定位节。

HASH/GNU_HASH:本节包含一张哈希表。

DYNAMIC:本节包含的是动态连接信息。

NOTE:本节包含的信息用于以某种方式来标记本文件。

NOBITS:这一节的内容是空的,节并不占用实际的空间。

REL:本节是一个重定位节。

DYNSYM:动态链接符号表信息

3.1.4 符号表(.symtab 节)

符号表不属于文件结构,但因为非常常用,也单独拎出来讲。

以一段简单的代码举例子:

#include

intmain(intargc,char**argv)
{
printf("helloworld");
return0;
}

编译出 hello 的 bin 之后,我们通过以下命令查看 .symtab 和 .dynsym

readelf-shello

Symboltable'.symtab'contains67entries:
Num:ValueSizeTypeBindVisNdxName
...
51:00000000000000000FUNCGLOBALDEFAULTUNDprintf@@GLIBC_2.2.5
...
61:00000000000006b039FUNCGLOBALDEFAULT14main
...

字段 含义 备注
Num 编号
Value 符号的值 [1]
Size 符号大小 单位 Byte
Type 符号类型 NOTYPE:无类型,OBJECT:数据对象,例如变量、数组,FUNC:函数,FILE:文件符号,SECTION:本符号与一个节相关联,用于重定位
Bind 表示符号的优先级和作用范围 LOCAL:本地符号,在所属".o"文件外无效,GLOBAL:全局符号,WEAK:弱符号,例如__attribute__((weak)) 标注的弱函数
Vis
Ndx 指定相关联的节[2] 数字:节在节头表中的索引,ABS:绝对值常量,常指文件路径,UND:未定义的,常指需要外部链接的函数、变量等
Name 符号名字

[1]. 在".o" 中,如果 Ndx 不是 UND,则此值表示字符在所在节中的偏移量,在可执行文件和共享库文件中,则表示虚拟地址
[2]. 任何一个符号表项的定义都与某一个“节”相联系,因为符号是为节而定义,在节中被引用。

更多符号表的介绍,请看 3.2.6 章节。

3.2 一些关键的节

3.2.1 "编译 + 节" 与 "链接 + 段"

在 《linux 目标文件(*.o) bss,data,text,rodata,堆,栈》(https://blog.csdn.net/sunny04/article/details/40627311) 有张图非常形象的描述了 text、data、bss 段的作用,如下:

c5570066-5010-11ed-a3b6-dac502259ad0.jpg

从上图可以看出,代码放在 text 段,已经初始化的变量放在 data 段,未初始化的变量放在 bss 段。详细下文再描述,在此章节,我们需要知道一个概念:

所有变量、函数等都是一个符号,还有字符串、固定的数据等,在编译的时候会按功能、是否初始化等分类放到特定的节中。

例如 .bss 节记录了所有未初始化的变量,.text 节保存了所有的二进制代码,.rodata 节保存了所有只读的数据。

所以我们编译(不含链接)出来的 ".o" 目标文件,其实就是对单个 ".c/.cpp" 源码的符号、数据按不同节分类放好。

这是编译,那么链接成可执行程序的时候呢?链接虽然涉及多个 ".o",其实也就把相同的节合并,然后归类到段中放好。

c562dabc-5010-11ed-a3b6-dac502259ad0.png

此节也就很粗犷的描述,砍掉了所有枝叶,只是为了更好的更直观的理解主干,实际情况肯定更复杂。

我们编译、链接的时候,其实可以主动限制某些符号、数据放到哪些节、哪些段的,也可以自己新建个节、段,这属于另一个大的课题了。大多时候我们也用不上,采用默认的即可。

下面,我们来看一些关键的节及其内容。

3.2.2 .text

前文一直有带出来,.text 节实际就是我们说的代码段,保存了一系列的可执行二进制指令。

.text 节的内容是会占 ROM 空间,并在运行时拷贝到 RAM。

readelf -x .text 可以以二进制 dump 出来,但没卵用,谁看得懂?此时可以用 objdump -d -j .text 汇编显示出来。

$objdump-d-j.text./main

main:fileformatelf64-x86-64

Disassemblyofsection.text:

......

00000000000006b0
: 6b0:55push%rbp 6b1:4889e5mov%rsp,%rbp 6b4:4883ec10sub$0x10,%rsp 6b8:897dfcmov%edi,-0x4(%rbp) 6bb:488975f0mov%rsi,-0x10(%rbp) 6bf:488d3d9e000000lea0x9e(%rip),%rdi#764<_IO_stdin_used+0x4> 6c6:b800000000mov$0x0,%eax 6cb:e890feffffcallq560 6d0:b800000000mov$0x0,%eax 6d5:c9leaveq 6d6:c3retq 6d7:660f1f84000000nopw0x0(%rax,%rax,1) 6de:0000 ......

上面的 main 汇编仅仅是 printf("hello")。

3.2.3 .bss

.bss 节主要保存了未初始化的变量,这里的未初始化起始也包括初始化为 0 的变量。通常是指用来存放程序中未初始化的全局变量和未初始化的局部静态变量。

.bss 节只在运行时,才会从内存分配空间,因此近乎不占 ROM 空间。

例如下面示例的两个变量都会保存到 bss。

intg_int1;
intg_int2=0;

对上述的两个变量,我们还是用 objdump -d -j .bss 看看:

$objdump-d-j.bss./main

test:fileformatelf64-x86-64


Disassemblyofsection.bss:

0000000000201010<__bss_start>:
201010:0000add%al,(%rax)
...

0000000000201014:
201014:00000000....

0000000000201018:
...

3.2.4 .data

.data 节主要保存了已经初始化的变量,通常是指用来存放程序中已初始化的全局变量和已初始化的静态变量的一块内存区域。

既然已经初始化,在文件中肯定要记录初始化的值,因此 .data 节需要占 ROM 空间的,且相对 .rodata 节来说,.data 节的内容是可变的,因此运行前需要拷贝到内存中可写的区间。

objdump 对查看数据类型的节的确方便,我们继续用 objdump 看看。

//核心变量:int g_int2 = 1
$objdump-d-j.datatest

test:fileformatelf64-x86-64

Disassemblyofsection.data:

0000000000201000<__data_start>:
...

0000000000201008<__dso_handle>:
201008:0810200000000000.......

0000000000201010:
201010:01000000....

代码 int g_int2 = 1 对变量赋了非0的初始值,就不适合放到 .bss 段了,因此放到了 .data 段。从 dump 的结果也可以看到变量 g_int2 以及值 0x1 。

3.2.5 .rodata

.rodata 节的数据是会占 ROM 空间的,且(大多时候)在运行时拷贝到内存中。

.rodata 存的是只读数据,比如字符串常量,全局const变量 和 #define定义的常量。例如:char *p = "123456", 123456 的字符串就存放在 rodata 节 中。还有一个有意思的例子:

printf("thefuncis%s
",__func__);

"the func is %s " 这个格式化打印字符串以及 __func__ 所指代的函数名,也是字符串常量,保存到 .rodata 节中的。

查看字符串类型的节,用 readelf 就方便多了,例如以下代码:

intmain(intargc,char**argv)
{
printf("helloworld");
printf("thisfuncis%s",__func__);
}

执行以下命令可以打印字符串数据,如果需要看二进制数据,例如整型的值,把 -p 改为 -x 即可。

$readelf-p.rodatamain

Stringdumpofsection'.rodata':
[4]helloworld
[10]thisfuncis%s
[20]main

这里有个小 Tips,相同的字符串只会保留 1份,因此下面两段代码效果一样,但保存的 .rodata 数据是不一样的。适当的优化可以节省 ROM 空间。自己琢磨:)。

//代码1
printf("func%s:valis%d
",__func__,val);
printf("func%s:openfailed
",__func__);

//代码2
printf("funcmain:valis%d
",val);
printf("funcmain:openfailed
");

3.2.6 .symtab 和 .dynsym

.symtab 和 .dynsym 都是符号表,例如函数、变量等都是符号,甚至 .dynsym 是 .symtab 的子集,但是他们的作用不太一样。

.symtab,俗称的符号表,记录了所有符号,不管是自己定义的变量、函数,还是未定义需要动态库提供实现的所有符号。在 ”.o" 链接时必须存在,但链接成 bin 后就可去掉节省空间,例如 strip 。去掉符号表之后程序运行能定位到函数地址,但再也不知道这函数名,这也是为什么在程序 crash 时打印的栈里有时候只有地址,有时候有具体函数名。

.dynsym,动态链接才需要的符号表,即可包括对外提供调用的符号,也包括需要外面提供实现的符号。.dynsym 在 ".so" 或者动态链接的 bin 里是必须的。

.symtab 和 .dynsym 是占 ROM 空间的,.dynsym 会加载进内存,但 .symtab 不会。我们也可以通过 strip 去掉符号表,然后通过 file 判断符号表是否已经 striped,例如下面的例子:

$gcctest.c-otest
$filetest
test:ELF64-bitLSBsharedobject,...,notstripped
$striptest
test:ELF64-bitLSBsharedobject,...,stripped

4. 利用工具解析 ELF

在上文的示例中频繁使用 readelf 和 objdump 来读取各种头表和节内容,除了这两个之外,还有一个 nm 工具,3者的功能非常相近。吃多嚼不烂,咱们以 readelf 为主,以 objdump 为辅讲解如何用工具解析 ELF 。

上文已有示例,本文主要做个汇总记录,方便将来查阅。字段的具体含义,可以查看章节3中具体的节解析。

获取 ELF 文件头

readelf-h

获取程序头表(段表)

readelf-l

获取节表(获取有哪些节)

readelf-S

获取符号表(列出函数、变量符号)

#获取所有符号表(含.symtab和.dynsym)
readelf-s

#获取动态符号表
readelf--dyn-syms

获取节内容

#打印节中的字符串,常用于含字符串类型的节,例如.rodata节
readelf-p

#以二进制打印节,常用于非字符串类型的节,例如.bss,.data节
readelf-x

#以汇编打印二进制代码
objdump-d-j

5. ELF 在磁盘 Vs. ELF 加载到内存

在 《完全剖析 - Linux虚拟内存空间管理》(https://cloud.tencent.com/developer/article/1835295) 一文有张图画出了虚拟内存的空间分布情况,如下:

c56da87a-5010-11ed-a3b6-dac502259ad0.png

我们关注最底下3个区间,其实跟 ELF 的内容是能对应上的。用一张新图来表示两者的关系:

c57abf2e-5010-11ed-a3b6-dac502259ad0.png

可执行文件的 代码段、数据段等会拷贝到内存中,BSS 段虽然没数据,但也记录了有哪些变量,会拷贝到内存可写区域,而动态库是 map 到 mmap 区的。

审核编辑:彭静
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 程序
    +关注

    关注

    115

    文章

    3753

    浏览量

    80709
  • 代码
    +关注

    关注

    30

    文章

    4708

    浏览量

    68174
  • 深度学习
    +关注

    关注

    73

    文章

    5456

    浏览量

    120850
  • elf
    elf
    +关注

    关注

    0

    文章

    12

    浏览量

    2163

原文标题:搞懂 ELF - 从入门到遗忘

文章出处:【微信号:LinuxDev,微信公众号:Linux阅码场】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    深入了解示波器

    深入了解示波器
    发表于 11-14 22:32

    专家开讲:深入了解电池技术 ──Part 1

    。笔者不会一一详细介绍所有的电池技术,只选择一些常见或是值得认识的;而在接下来的专栏里,笔者将开始介绍电池分类、常见规格以及专业术语,如果你有特别想知道的电池技术,欢迎留言!扩展阅读:专家开讲:深入了解
    发表于 08-18 09:33

    专家开讲:深入了解电池技术──Part 3

    资深工程师 Ivan Cowie 的「深入了解电池技术」专栏Part 3来啰!这次要介绍的是铅酸电池(lead-acidbatteries)技术。铅酸电池是在1859年由法国物理学家Gaston
    发表于 08-18 09:37

    单片机的深入了解

    项目名称:单片机的深入了解!项目是否开源:否申请开发板数量:1 块申请人团队介绍:我们团队由五个人组成,我们打算开始着手单片机的程序改编,设计一些比较特殊新颖的东西!希望给以支持!
    发表于 10-12 20:00

    深入了解LabVIEW FPGA资料分享

    深入了解LabVIEW FPGA
    发表于 05-27 08:35

    深入了解单片机汇编重要吗?

    不学汇编,只用C语言,能不能深入了解单片机?
    发表于 07-21 10:38

    深入了解主动电扫描阵列(AESA)雷达系统

    深入了解主动电扫描阵列(AESA)雷达系统
    发表于 05-24 06:51

    示波器的深入了解

    示波器的深入了解 引言自然界运行着各种形式的正弦波,比如海浪、地震、声波、爆破、空气中传播的声音,或者身体运转的自然节律。物理世界里,能
    发表于 11-04 11:53 52次下载
    示波器的<b class='flag-5'>深入了解</b>

    深入了解示波器入门手册

    深入了解示波器入门手册
    发表于 03-27 17:43 241次下载
    <b class='flag-5'>深入了解</b>示波器入门手册

    深入了解电路噪声的那些事

    模拟电子的相关知识学习教材资料——深入了解电路噪声的那些事
    发表于 09-27 15:19 0次下载

    深入了解电感与磁珠的异同

    模拟电子的相关知识学习教材资料——深入了解电感与磁珠的异同
    发表于 09-27 15:19 0次下载

    了解IC内部结构吗本文带你深入了解

    本文档的主要内容详细介绍的是IC内部结构了解IC内部结构吗本文带你深入了解
    的头像 发表于 03-09 11:33 1.1w次阅读
    你<b class='flag-5'>了解</b>IC内部<b class='flag-5'>结构</b>吗本文带你<b class='flag-5'>深入了解</b>

    带你深入了解示波器

    带你深入了解示波器
    发表于 02-07 14:26 19次下载

    深入了解安全光栅

    深入了解安全光栅
    的头像 发表于 06-25 13:53 1123次阅读
    <b class='flag-5'>深入了解</b>安全光栅

    深入了解 GaN 技术

    深入了解 GaN 技术
    的头像 发表于 12-06 17:28 6005次阅读
    <b class='flag-5'>深入了解</b> GaN 技术