<acronym id="s8ci2"><small id="s8ci2"></small></acronym>
<rt id="s8ci2"></rt><rt id="s8ci2"><optgroup id="s8ci2"></optgroup></rt>
<acronym id="s8ci2"></acronym>
<acronym id="s8ci2"><center id="s8ci2"></center></acronym>
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,堆,?!罚╤ttps://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 區的。

審核編輯:彭靜
聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 程序
    +關注

    關注

    114

    文章

    3635

    瀏覽量

    79706
  • 代碼
    +關注

    關注

    30

    文章

    4574

    瀏覽量

    67076
  • 深度學習
    +關注

    關注

    73

    文章

    5257

    瀏覽量

    120054
  • elf
    elf
    +關注

    關注

    0

    文章

    10

    瀏覽量

    2144

原文標題:搞懂 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-16 16:43
    帶你<b class='flag-5'>深入了解</b>光耦

    深入了解示波器入門手冊

    深入了解示波器入門手冊
    發表于 03-27 17:43 ?240次下載
    <b class='flag-5'>深入了解</b>示波器入門手冊

    深入了解電感與磁珠的異同

    模擬電子的相關知識學習教材資料——深入了解電感與磁珠的異同
    發表于 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 ?18次下載

    深入了解安全光柵

    深入了解安全光柵
    的頭像 發表于 06-25 13:53 ?833次閱讀
    <b class='flag-5'>深入了解</b>安全光柵

    深入了解 GaN 技術

    深入了解 GaN 技術
    的頭像 發表于 12-06 17:28 ?5456次閱讀
    <b class='flag-5'>深入了解</b> GaN 技術
    亚洲欧美日韩精品久久_久久精品AⅤ无码中文_日本中文字幕有码在线播放_亚洲视频高清不卡在线观看
    <acronym id="s8ci2"><small id="s8ci2"></small></acronym>
    <rt id="s8ci2"></rt><rt id="s8ci2"><optgroup id="s8ci2"></optgroup></rt>
    <acronym id="s8ci2"></acronym>
    <acronym id="s8ci2"><center id="s8ci2"></center></acronym>