1. 背景
長期以來只知道 ELF 是一種廣泛使用的文件格式規范,常指動態庫、bin等,一直沒動力深入研究。出于業務需求,我花了好些天仔細分析 RTOS Bin 的 Section 和 Symbol 。也是趁著這機會,查閱大量資料,完善了知識脈絡。
但是可以預見,業務結束一段時間后,由于不常用,我不可避免會漸漸遺忘。從入門到遺忘,相信只要經歷過深度學習的小伙伴都會有的痛苦掙扎。為了在將來需要時喚醒記憶,我決定通過這文章打下錨點,也希望此文能幫到更多人更快入門。
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)是必須的,但運行時卻是可選的,例如運行需要段信息,而鏈接只有節信息。
在上圖中,程序頭表(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
$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 節)
符號表不屬于文件結構,但因為非常常用,也單獨拎出來講。
以一段簡單的代碼舉例子:
#includeintmain(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 段的作用,如下:
從上圖可以看出,代碼放在 text 段,已經初始化的變量放在 data 段,未初始化的變量放在 bss 段。詳細下文再描述,在此章節,我們需要知道一個概念:
所有變量、函數等都是一個符號,還有字符串、固定的數據等,在編譯的時候會按功能、是否初始化等分類放到特定的節中。
例如 .bss 節記錄了所有未初始化的變量,.text 節保存了所有的二進制代碼,.rodata 節保存了所有只讀的數據。
所以我們編譯(不含鏈接)出來的 ".o" 目標文件,其實就是對單個 ".c/.cpp" 源碼的符號、數據按不同節分類放好。
這是編譯,那么鏈接成可執行程序的時候呢?鏈接雖然涉及多個 ".o",其實也就把相同的節合并,然后歸類到段中放好。
此節也就很粗獷的描述,砍掉了所有枝葉,只是為了更好的更直觀的理解主干,實際情況肯定更復雜。
我們編譯、鏈接的時候,其實可以主動限制某些符號、數據放到哪些節、哪些段的,也可以自己新建個節、段,這屬于另一個大的課題了。大多時候我們也用不上,采用默認的即可。
下面,我們來看一些關鍵的節及其內容。
3.2.2 .text
前文一直有帶出來,.text 節實際就是我們說的代碼段,保存了一系列的可執行二進制指令。
.text 節的內容是會占 ROM 空間,并在運行時拷貝到 RAM。
readelf -x .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
.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) 一文有張圖畫出了虛擬內存的空間分布情況,如下:
我們關注最底下3個區間,其實跟 ELF 的內容是能對應上的。用一張新圖來表示兩者的關系:
可執行文件的 代碼段、數據段等會拷貝到內存中,BSS 段雖然沒數據,但也記錄了有哪些變量,會拷貝到內存可寫區域,而動態庫是 map 到 mmap 區的。
審核編輯:劉清
-
處理器
+關注
關注
68文章
19178瀏覽量
229201 -
BIN文件
+關注
關注
0文章
26瀏覽量
8272 -
elf
+關注
關注
0文章
12瀏覽量
2174
發布評論請先 登錄
相關推薦
評論