《程序员的自我修养》第3章---目标文件里有什么
第3章 目標文件里有什么
3.1 目標文件的格式:
編譯器編譯源代碼后生成的文件叫做 “目標文件”。
目標文件從結構上講,它是已經編譯后的可執行文件格式,只是還沒有經過鏈接的過程,其中可能有些符號或有些地址還沒有被調整。其實它本身就是按照可執行文件格式存儲的,只是跟真正的可執行文件在結構上稍有不同。
現在PC平臺流行的 “可執行文件格式”(Executable)主要是Windows下的PE(Portable Executable)和Linux的ELF(Executable Linkable Format),它們都是 COFF(Common File Format)格式的變種。
目標文件就是源代碼編譯后但未進行鏈接的那些中間文件(Windows的 .obj 和 Linux下的 .o),它跟可執行文件的內容與結構很相似,所以一般跟可執行文件一起采用一種格式存儲。
從廣義上可以將目標文件與可執行文件看成是一種類型的文件,在Windows下統稱它們為 PE-COFF 文件格式,在Linux下它們統稱為 ELF文件。
不只是“可執行文件”按照ELF文件格式存儲,“動態鏈接庫”以及“靜態鏈接庫”文件也都按照ELF格式存儲。
動態鏈接庫:DLL,Dynamic Linking Library。Windows下的 .dll 和 Linux下的 .so 文件;
靜態鏈接庫:Static Linking Library。Windows下的 .lib 和 Linux下的 .a 文件;
靜態鏈接庫稍有不同,它是把很多文件捆綁在一起形成一個文件,再加上一些索引,你可以簡單的把它理解為一個包含很多目標文件的文件包。
ELF文件標準里面把系統中采用ELF格式的文件歸為以下4類:
實例:Linux下的 .o,Windows下的.obj;
說明:可重定位文件包含了代碼和數據,可以被用來鏈接成“可執行文件”或“共享目標文件(.so)”,靜態鏈接庫也可以歸為這一類;
實例:例如 /bin/bash,Windows下的 .exe;
說明:可執行文件包含了可以直接執行的程序,它的代表就是ELF可執行文件,它們一般沒有擴展名;
實例:Linux下的 .so,Windows下的.dll;
說明:共享目標文件包含了代碼和數據,可以在在以下兩種情況下使用:
① 第一種是鏈接器可以使用這種文件跟其他的可重定位文件和其他的共享目標文件鏈接,產生新的目標文件(.o);
② 第二種是動態鏈接器可以將幾個共享目標文件與可執行文件結合,作為進程映像的一部分來運行(所謂的“動態鏈接”);
實例:Linux下的core dump;
說明:當進程意外終止時,系統可以將 該進程的地址空間的內容及終止時的一些其他信息 轉儲到核心轉儲文件(core文件)。
我們可以在Linux下使用 file命令查看響應的文件格式:
# gcc -E demo.c -o demo.i // # gcc -S demo.i -o demo.s //生成匯編文件 # gcc -c demo.s -o demo.o //生成目標文件 # gcc demo.c -o demo //生成可執行文件# file demo.c demo.c: C source, ASCII text# file demo.i demo.i: C source, ASCII text# file demo.s demo.s: assembler source, ASCII text# file demo.o demo.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped# file demo demo: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=9bee5fbd55540c9ab45e68ac1af1569a9abde37c, for GNU/Linux 3.2.0, not stripped目標文件與可執行文件格式的小歷史:
COFF 是由 Unix System V Release 3 首先提出并且使用的格式規范,后來微軟公司基于COFF格式指定了PE格式標準,并將其用于Windows NT系統。
System V Release 4 在 COFF 的基礎上引入了 ELF格式,目前流行的Linux系統也以ELF作為基本可執行文件格式。
這也是為什么目前PE和ELF如此相似的主要原因,因為它們都是源于同一種可執行文件格式COFF。
3.2 目標文件是什么樣的:
目標文件中的內容包括:編譯后的機器指令代碼、數據,以及用于鏈接時所需要的一些信息,例如符號表、調試信息、字符串等。
目標文件將這些信息按不同的屬性,以“段”(Segment)的方式存儲,例如代碼段、數據段。
例如上圖中的ELF文件:
ELF文件的開頭是一個 “文件頭”,它描述了整個文件的文件屬性,包括文件是否可執行、是靜態鏈接還是動態鏈接及入口地址(如果是可執行文件)、目標硬件、目標操作系統等信息;
文件頭還包括一個“段表”(Section Table),段表其實是一個描述文件中各個段的數組,段表描述了文件中各個段在文件中的偏移位置及段的屬性等,從段表里面可以得到每個段的所有信息。
.bss段只是為未初始化的全局變量和靜態局部變量預留位置而已,它并沒有內容,所以它在文件中也不占據空間。
總體來說,程序源代碼被編譯以后主要分成兩種段: 程序指令 和 程序數據。
代碼段(.text)屬于程序指令,而數據段(.data) 和 .bss端屬于程序數據。
為什么要將代碼段和數據段分開存放,而不是簡單的混雜放在一個段里面?因為將它們分開存放有以下幾點好處:
當程序被裝載后,數據和指令分別被映射到兩個虛擬內存區域。由于數據區域對于進程來說是可讀寫的,而指令區域對于進程來說是只讀的,所以這兩個虛擬內存區域的權限可以被分別設置為讀寫和只讀,用以防止程序的指令被有意或無意的改寫;
對于現代CPU來說,它們有著極為強大的緩存體系(Cache)。CPU的緩存一般都被設計成“數據緩存”和“指令緩存”分離,而程序的“指令區”和“數據區”分離有利于提高程序的局部性,因此程序的指令和數據分開存放對CPU的緩存命令率提升有好處;
當系統中運行著多個程序的副本時,它們的指令都是一樣的,所以內存中只需要保存一份該程序的指令部分,特別是在有動態鏈接的系統中,可以節省大量的內存。
3.3 挖掘 SimpleSection.o :
示例程序 SimipleSection.c :
int printf(const char* format, ...);int global_init_var = 64; int global_uninit_var;void func1(int i) {printf("%d\n", i); }int main(void) {static int static_var = 85;static int static_var2;int a = 1;int b;func1(static_var + static_var2 + a + b);return a; }使用gcc生成目標文件:
[linux] gcc -c SimpleSection.c //生成目標文件 SimpleSection.o使用 objdump -h 命令可查看.o目標文件的ELF頭中內容:
(ELF文件頭中的內容描述了整個文件的基本屬性,例如:ELF文件版本、體系結構、起始地址等)
.o目標文件中的各個段在ELF文件中的結構如下圖所示:
<br.>
3.3.1 代碼段:
.text代碼段通過 objdump -s -d 命令進行反匯編后,可以看出與C源程序的代碼內容完全一致。
3.3.2 數據段和只讀數據段:
.data段 保存的是 已初始化的全局靜態變量和已初始化的局部靜態變量。
使用 objdump -s 可以看到目標文件中的 .data段的內容:
[linux] objdump -s SimpleSection.oSimpleSection.o: 文件格式 elf64-x86-64Contents of section .data:0000 40000000 55000000 @...U...x86 CPU的字節序是小端序,高地址存放低字節,所以 0x40 是第一個字節,十進制為64,即對應的是源代碼中的 global_init_var 變量,40000000長度為4個字節,32比特,剛好對應一個int型;
55000000第一個字節 0x50 = 85,對應源代碼中的 static_var。
使用 size 命令可以看到 SimpleSection.o 目標文件中 .data 段的大小為 8個字節:
[linux] size SimpleSection.otext data bss dec hex filename219 8 4 231 e7 SimpleSection.o.rodata段存放的是只讀數據,一般是程序里const關鍵字修飾的只讀變量和 字符串常量。
有時候編譯器會把 字符串常量 放在 .data段,而不會放在 .rodata段。這種行為因編譯器而異。
3.3.3 BSS段:
使用 size 命令可以看到 .bss 段的大小為4,但是使用 objdump -s 命令卻發現目標文件中沒有 .bss 段的內容。
因為 .bss段中存放的是未初始化的全局變量和未初始化的局部靜態變量,它們當前是沒有初始化值的,所以沒有必要在ELF文件中開辟空間為其存放值,更確切的說法是 “.bss段為它們預留了空間”。
另外,SimpleSection.c中有兩個變量 global_uninit_var 和 static_var2都屬于未初始化的靜態變量,為什么目標文件 SimpleSection.o 中的 .bss段的大小是 4字節而不是 8字節?
這與編譯器有關,有些編譯器會將 “全局未初始化變量”存放在 .bss段,有些則不放。
3.4 ELF文件結構描述:
!!! 重要:ELF文件結構:
文件頭:(ELF Header)
用于描述整個ELF文件的基本屬性,例如:ELF文件版本、目標機器型號、程序入口地址等;
段表:(Section Header Table)
段表是一個結構體數組,用于描述ELF文件中的所有段的信息,包括每個段的段名、段長度、偏移量、讀寫權限等屬性。
3.4.1 文件頭:
ELF Header :
描述整個文件的基本屬性,例如:ELF文件版本、目標機器型號、程序入口地址 等。
使用 objdump -x 就能看到 ELF“文件頭”中的所有信息:
[linux] objdump -x SimpleSection.oSimpleSection.o: 文件格式 elf64-x86-64 SimpleSection.o 體系結構:i386:x86-64, 標志 0x00000011: HAS_RELOC, HAS_SYMS 起始地址 0x0000000000000000節: Idx Name Size VMA LMA File off Algn0 .text 0000005f 0000000000000000 0000000000000000 00000040 2**0CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE1 .data 00000008 0000000000000000 0000000000000000 000000a0 2**2CONTENTS, ALLOC, LOAD, DATA2 .bss 00000004 0000000000000000 0000000000000000 000000a8 2**2ALLOC3 .rodata 00000004 0000000000000000 0000000000000000 000000a8 2**0CONTENTS, ALLOC, LOAD, READONLY, DATA4 .comment 0000002b 0000000000000000 0000000000000000 000000ac 2**0CONTENTS, READONLY5 .note.GNU-stack 00000000 0000000000000000 0000000000000000 000000d7 2**0CONTENTS, READONLY6 .note.gnu.property 00000020 0000000000000000 0000000000000000 000000d8 2**3CONTENTS, ALLOC, LOAD, READONLY, DATA7 .eh_frame 00000058 0000000000000000 0000000000000000 000000f8 2**3CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA SYMBOL TABLE: 0000000000000000 l df *ABS* 0000000000000000 SimpleSection.c 0000000000000000 l d .text 0000000000000000 .text 0000000000000000 l d .data 0000000000000000 .data 0000000000000000 l d .bss 0000000000000000 .bss 0000000000000000 l d .rodata 0000000000000000 .rodata 0000000000000004 l O .data 0000000000000004 static_var.1920 0000000000000000 l O .bss 0000000000000004 static_var2.1921 0000000000000000 l d .note.GNU-stack 0000000000000000 .note.GNU-stack 0000000000000000 l d .note.gnu.property 0000000000000000 .note.gnu.property 0000000000000000 l d .eh_frame 0000000000000000 .eh_frame 0000000000000000 l d .comment 0000000000000000 .comment 0000000000000000 g O .data 0000000000000004 global_init_var 0000000000000004 O *COM* 0000000000000004 global_uninit_var 0000000000000000 g F .text 0000000000000028 func1 0000000000000000 *UND* 0000000000000000 _GLOBAL_OFFSET_TABLE_ 0000000000000000 *UND* 0000000000000000 printf 0000000000000028 g F .text 0000000000000037 mainRELOCATION RECORDS FOR [.text]: OFFSET TYPE VALUE 0000000000000017 R_X86_64_PC32 .rodata-0x0000000000000004 0000000000000021 R_X86_64_PLT32 printf-0x0000000000000004 000000000000003d R_X86_64_PC32 .data 0000000000000043 R_X86_64_PC32 .bss-0x0000000000000004 0000000000000056 R_X86_64_PLT32 func1-0x0000000000000004RELOCATION RECORDS FOR [.eh_frame]: OFFSET TYPE VALUE 0000000000000020 R_X86_64_PC32 .text 0000000000000040 R_X86_64_PC32 .text+0x0000000000000028文件頭的內容存放在 /usr/include/elf.h 頭文件中的 Elf32_Ehdr 結構體中:
typedef struct {unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ //Magic, Class, Data, Version, OS/ABI, AB VersionElf32_Half e_type; /* Object file type */ //ELF文件類型: ET_REL=1, 可重定位文件(.o); ET_EXEC=2, 可執行文件; ET_DYN=3, 共享目標文件(.so);Elf32_Half e_machine; /* Architecture */ //該ELF文件支持的運行平臺,ELF文件格式被設計成可以在多個平臺下使用,但不意味著同一個ELF文件可以在多個平臺下運行,而是不同平臺下的ELF文件都遵循同一套ELF標準。以“EM_”開頭: EM_M32: AT&T; EMP_SPARC: SPARC; EM_386: Intel x86; EM_860: Intel 80860; EM_68K: Motorola 68000; EM_88K: Motorola 88000.Elf32_Word e_version; /* Object file version */Elf32_Addr e_entry; /* Entry point virtual address */Elf32_Off e_phoff; /* Program header table file offset */Elf32_Off e_shoff; /* Section header table file offset */Elf32_Word e_flags; /* Processor-specific flags */Elf32_Half e_ehsize; /* ELF header size in bytes */Elf32_Half e_phentsize; /* Program header table entry size */Elf32_Half e_phnum; /* Program header table entry count */Elf32_Half e_shentsize; /* Section header table entry size */Elf32_Half e_shnum; /* Section header table entry count */Elf32_Half e_shstrndx; /* Section header string table index */ } Elf32_Ehdr;使用 readelf -h 命令查看 SimpleSection.o目標文件中的內容:
readelf -h SimpleSection.o ELF 頭:Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 類別: ELF64數據: 2 補碼,小端序 (little endian)Version: 1 (current)OS/ABI: UNIX - System VABI 版本: 0類型: REL (可重定位文件)系統架構: Advanced Micro Devices X86-64版本: 0x1入口點地址: 0x0程序頭起點: 0 (bytes into file)Start of section headers: 1184 (bytes into file)標志: 0x0Size of this header: 64 (bytes)Size of program headers: 0 (bytes)Number of program headers: 0Size of section headers: 64 (bytes)Number of section headers: 14Section header string table index: 13其中,Elf32_Ehdr結構體中的e_ident 成員對應了 readelf輸出結果中的 Class、Data、Version、OS/ABI 和 ABI Version 這5個參數,其中還包括 Magic魔數,其余的 Elf32_Ehdr結構體中的成員與 readelf輸出結果的參數一一對應。
3.4.2 段表:
段表用于描述ELF文件中的各個段(.text, .data, .bss 等)的信息,例如:每個段的 段名、段的長度、在文件中的偏移量、讀寫權限、其他屬性。
段表的結構:
它是一個以 Elf32_Shdr 結構體為元素的數組,數組元素個數等于段的個數,每個 Elf32_Shdr 結構體對應一個段,又稱為 “段描述符”(Section Descriptor)。
Elf32_Shdr 結構體的結構:(用于描述一個段的屬性)
typedef struct {Elf32_Word sh_name; /* Section name (string tbl index) */ //段的名字,如: .text, .data, .rodata, .bss 等Elf32_Word sh_type; /* Section type */ //段的類型,以“SHT_”開頭,如: SHT_NULL:無效段; SHT_PROBITS:程序段、代碼段、數據段都是這種類型; SHT_SYMTAB:符號表; SHT_RELA:字符串表; SHT_DNYSYM:動態鏈接的符號表;Elf32_Word sh_flags; /* Section flags */ //段的標志位:SHF_WRITE:表示該段在進程空間中可寫; SHF_ALLOC:表示該段需要在進程虛擬空間中分配空間,代碼段、數據段都需要這個標志位; SHF_EXCINSTR:表示該段可執行,一般指代碼段;Elf32_Addr sh_addr; /* Section virtual addr at execution */Elf32_Off sh_offset; /* Section file offset */Elf32_Word sh_size; /* Section size in bytes */Elf32_Word sh_link; /* Link to another section */Elf32_Word sh_info; /* Additional section information */Elf32_Word sh_addralign; /* Section alignment */Elf32_Word sh_entsize; /* Entry size if section holds table */ } Elf32_Shdr;3.5 鏈接的接口 ---- 符號:
鏈接過程的本質就是要把多個不同的目標文件相互“粘”在一起,組成一個整體。
為了使不同的目標文件之間能夠相互粘合,這些目標文件之間需要有固定的規則才行。
在鏈接中,目標文件之間相互拼合實際上是目標文件回見對 地址 的引用,即對函數和變量地址的引用。
例如目標文件B引用了目標文件A中的函數foo(),那么就稱目標文件A中 “定義”(Define) 了函數foo(),稱目標文件B中 “引用”(Reference) 了目標文件A中的函數foo()。
“定義”和“引用”這兩個概念也同樣適用于變量。
每個函數和變量都要有自己獨特的名字,才能避免鏈接過程中不同變量和函數之間的混淆。
在鏈接中,將函數和變量統稱為 “符號”(Symbol),函數名和變量名就是 “符號表”(Symbol Name)。
我們可以將符號看作是鏈接中的粘合劑,整個鏈接過程正是基于符號才能夠正確完成。
鏈接過程中很關鍵的一部分就是符號的管理,每一個目標文件都有一個相應的 “符號表”(Symbol Table),表中記錄目標文件所用到的所有符號。
每個定義的符號有一個對應的值,稱為 “符號值”(Symbol Value),對于變量和函數來說,符號值就是它們的地址。
符號表中的符號可以分為以下幾類:
對于鏈接過程來說,最值得關注的就是 “全局符號”,因為鏈接過程只關心全局符號的相互粘合,局部符號、段名、行號等都是次要的,它們對于目標文件來說是不可見的,在鏈接過程中也是無關緊要的。
可用于查看ELF文件符號的工具:
readelf、objdump、nm 等。
使用 nm命令列出 SimpleSection.o目標文件中的所有符號:
(大寫表示全局,如T;小寫表示局部,如d)
使用 objdump -x命令查看SimpleSection.o目標文件中的符號:
[linux] objdump -x SimpleSection.oSYMBOL TABLE: 0000000000000000 l df *ABS* 0000000000000000 SimpleSection.c 0000000000000000 l d .text 0000000000000000 .text 0000000000000000 l d .data 0000000000000000 .data 0000000000000000 l d .bss 0000000000000000 .bss 0000000000000000 l d .rodata 0000000000000000 .rodata 0000000000000004 l O .data 0000000000000004 static_var.1920 0000000000000000 l O .bss 0000000000000004 static_var2.1921 0000000000000000 l d .note.GNU-stack 0000000000000000 .note.GNU-stack 0000000000000000 l d .note.gnu.property 0000000000000000 .note.gnu.property 0000000000000000 l d .eh_frame 0000000000000000 .eh_frame 0000000000000000 l d .comment 0000000000000000 .comment 0000000000000000 g O .data 0000000000000004 global_init_var 0000000000000004 O *COM* 0000000000000004 global_uninit_var 0000000000000000 g F .text 0000000000000028 func1 0000000000000000 *UND* 0000000000000000 _GLOBAL_OFFSET_TABLE_ 0000000000000000 *UND* 0000000000000000 printf 0000000000000028 g F .text 0000000000000037 main使用 readelf -s 查看ELF文件中的符號:
[linux] readelf -s SimpleSection.oSymbol table '.symtab' contains 18 entries:Num: Value Size Type Bind Vis Ndx Name0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND 1: 0000000000000000 0 FILE LOCAL DEFAULT ABS SimpleSection.c2: 0000000000000000 0 SECTION LOCAL DEFAULT 1 3: 0000000000000000 0 SECTION LOCAL DEFAULT 3 4: 0000000000000000 0 SECTION LOCAL DEFAULT 4 5: 0000000000000000 0 SECTION LOCAL DEFAULT 5 6: 0000000000000004 4 OBJECT LOCAL DEFAULT 3 static_var.19207: 0000000000000000 4 OBJECT LOCAL DEFAULT 4 static_var2.19218: 0000000000000000 0 SECTION LOCAL DEFAULT 7 9: 0000000000000000 0 SECTION LOCAL DEFAULT 8 10: 0000000000000000 0 SECTION LOCAL DEFAULT 9 11: 0000000000000000 0 SECTION LOCAL DEFAULT 6 12: 0000000000000000 4 OBJECT GLOBAL DEFAULT 3 global_init_var13: 0000000000000004 4 OBJECT GLOBAL DEFAULT COM global_uninit_var14: 0000000000000000 40 FUNC GLOBAL DEFAULT 1 func115: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND _GLOBAL_OFFSET_TABLE_16: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND printf17: 0000000000000028 55 FUNC GLOBAL DEFAULT 1 mainNum : 表示符號表數組的下標
Value : 符號值
Size : 符號大小
Type : 符號類型
Bind : 綁定信息
Vis : 目前在C/C++中未使用,暫時忽略
Ndx : 表示該符號所屬的段
Name : 符號名稱
3.5.1 ELF符號表結構:
ELF符號表示一個結構體數組,數組中的每個元素對應一個 Elf32_Sym結構體,一個結構體對應一個符號:
typedef struct {Elf32_Word st_name; /* Symbol name (string tbl index) */ //符號名Elf32_Addr st_value; /* Symbol value */ //符號對應的值,可能是一個地址,可能是一個絕對值,這取決于不同的符號Elf32_Word st_size; /* Symbol size */unsigned char st_info; /* Symbol type and binding */unsigned char st_other; /* Symbol visibility */Elf32_Section st_shndx; /* Section index */ } Elf32_Sym;3.5.4 extern “C” :
C++為了與C兼容,在符號管理上,C++有一個用來聲明或定義一個C的符號的 extern "C" 關鍵字:
extern "C" {int func(int);int var; }用 extern "C" { } 大括號括起來的部分的代碼,C++編譯器會將其當作C語言代碼來處理,此時C++的名稱修飾機制(name-manling)將不會起作用。
9. Linux下用于查看二進制文件的幾個實用工具:
9.1 file命令:查看文件格式(源文件、匯編文件、ELF文件)
例如:
[linux] file SimpleSection.oSimpleSection.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped9.2 objdump命令:查看目標文件內部結構
objdump命令有點像那種快速查看之類的工具,就是以一種可閱讀的格式讓你更多的了解 二進制文件 可能帶有的附加信息。
對于一個只想讓自己的程序跑起來的程序員,這個命令沒有更多意義;對于想進一步了解系統的程序員,應該掌握這個工具。
objdump -h, --[section-]headers Display the contents of the section headers//查看目標文件的“文件頭”,可以看到目標文件的代碼段、數據段的大小、屬性等信息objdump -x, --all-headers Display the contents of all headers//“-h”只是查看目標文件的“文件頭”信息,內容精簡;“-x”用于查看目標文件的所有信息,內容豐富復雜objdump -s, --full-contents Display the full contents of all sections requested//將所有的段內容以十六進制的方式打印出來objdump -d, --disassemble Display assembler contents of executable sections//將所有包含指令的段進行反匯編(將目標文件由機器語言格式 反匯編 成為匯編語言格式,在這里是將ELF格式文件反匯編成為匯編語言文件)objdump -r, --reloc Display the relocation entries in the file//查看ELF文件的重定位表,即查看每個重定位入口9.3 size命令:查看ELF文件的代碼段、數據段和BSS段的長度
例如:
[linux] size SimpleSection.otext data bss dec hex filename219 8 4 231 e7 SimpleSection.o9.4 readelf命令:查看ELF文件內容
理解:
objdump -h SimpleSection.o 命令看到的東西更傾向于文件的 “內容”、“結構”;
readelf -h SimpleSection.o 命令看到的東西更傾向于文件的 “屬性”;
readelf -h --file-header Display the ELF file header//查看ELF文件的“文件頭”信息readelf -s --syms Display the symbol table--symbols An alias for --syms//查看ELF文件的“符號表”信息readelf -S --section-headers Display the sections' header--sections An alias for --section-headers//查看ELF文件的“段表頭”信息9.5 nm命令:
“nm” 是 names的縮寫,nm命令用于列出某些文件中的符號(函數、全局變量等)。
例如:
nm SimpleSection.o 0000000000000000 T func1 0000000000000000 D global_init_varU _GLOBAL_OFFSET_TABLE_ 0000000000000004 C global_uninit_var 0000000000000028 T mainU printf 0000000000000004 d static_var.1920 0000000000000000 b static_var2.1921總結
以上是生活随笔為你收集整理的《程序员的自我修养》第3章---目标文件里有什么的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: yum没有被启用的仓库
- 下一篇: 图形识别的应用和发展