Linux C目标文件
LinuxC目標文件
宗旨:技術的學習是有限的,分享的精神是無限的。
一、目標文件格式(ELF格式)
編譯器編譯源代碼后生成的文件叫做目標文件。目標文件是已經編譯后的可執行文件,只是還沒有經過鏈接的過程。
PC平臺流行的可執行文件格式:windows下的PE和Linux下的ELF。
動態鏈接庫和靜態鏈接庫也是按照可執行文件存儲的。
1、ELF文件歸于4類:
| ELF文講類型 | 說明 | 舉例 |
| 可重定位文件 | 目標文件.o | Linux的.o, windows下的.obj |
| 可執行文件 | 直接可執行的文件 | /bin/bash ??windows的.exe |
| 共享目標文件 | ? | .so?? DLL |
| 核心轉儲文件 | 進程意外終止 | core dump |
Linux下的file命令查看相應的文件格式:
2、目標文件
編譯后的機器指令代碼、數據、符號表、調試信息、字符串等。
一般目標文件將這些文件信息按不同的屬性,以“節”的形式存儲。
機器指令放在代碼段.text,已初始化全局變量和靜態變量放在數據段里.data,未初始化全局變量和靜態變量放在數據段里.bss。.bss只是為變量預留的位置而已,并沒有內容,不占據空間。
??
二、剖析目標文件section.o
// section.c: #include<stdio.h>int init_var = 84; int uninit_var;void fun(int i) {printf(" %d \n", i); }int main(void) {static int static_var = 85;static int static_var2;int a = 1;int b;fun(init_var + uninit_var + a + b);return 0; }gcc? -c ?section.c生成section.o——只編譯不鏈接?
objdump? -h? section.o// ELF文件的各個段的基本信息打印出來。
除了最基本的代碼段、數據段、BSS段,還有三個段:只讀數據段(.rodata)、注釋信息段(.comment)和堆棧提示段(.note.GNU-stack)。
段的屬性:最容易理解的就是段的長度Size和段所在的位置File off(偏移量)。每個段第二行的“CONTENTS”表示該段在文件中存在——BSS段沒有“CONTENTS “,實際上在ELF中不存在;”note.GNU-stack“有“CONTENTS”但大小為0,奇怪。
?
size命令查看ELF文件中的代碼段、數據段和BSS段長度。
root@colinux:~/mystudy# size section.o
?? text??? data????bss???? dec???? hex filename
? ?88??????8?????? 4???? 100?????64 section.o
?
1、代碼段
???????? objdump的“-s”十六進制方式打印,“-d”反匯編。提取出代碼段的內容:
?
“Contents of section.text”就是.text的數據以十六進制方式打印出來的內容,0x58字節,與size命令的長度符合。對照反匯編結果,.text包含兩個函數,fun()和main()。.text的第一個字節就是”0x55”就是fun()函數的第一條“push?? %ebp”指令,而最后一個字節0xc3正是main()函數的最后一條指令“ret”。
2、數據段和只讀數據段
???????? .data段保存的是初始化的全局變量和靜態變量,section.c中有這樣兩個變量init_var和static_var,都是int型,剛好8字節。所以.data的大小是8字節。
Contents of section .data:
?0000 54000000 55000000??????????????????? T...U...???????
Contents of section .rodata:
?0000 20256420 0a00???????????????????????? %d ..??
.data前四個字節,0x54、0x00、0x00、0x00 —— 0x54 = 84;——大端機
?
3、BSS段
???????? .bss段保存的是未化的全局變量和靜態變量,section.c中有這樣兩個變量uninit_var和static_var2。但是通過size命令看到.bss只有4字節。通過符號表(后面說)看到,只有static_var2被放入了.bss段,uninit_var沒有。與不同的語言和不同的編譯器有關。
?
4、其他段? ? ? ??
| 常用段名 | 說明 |
| .rodata | 只讀數據,如字符串常量,const只讀變量 |
| .comment | 編譯器版本信息, |
| .debug | 調試信息 |
| .dynamic | 動態鏈接信息 |
| .hash | 符號哈希表 |
| .line | 行號表 |
| .note | 額外的編譯器信息:公司名,發布版本號等 |
| .strtab | 字符串表 |
| .symtab | 符號表 |
| .shstrtab | 段名表 |
| .plt?? .got | 動態鏈接的跳轉表和全局入口表 |
| .init? .fini | 程序初始化與終結代碼段 |
?
三、ELF文件結構
???????? 提取重要的結構:ELFHeader(ELF文件頭)、.text、.data、.bss、其他段、段表、字符串表、符號表等。
ELF文件頭——描述了整個文件的文件屬性:是否可執行、是靜態還是動態連接及入口地址、目標硬件、目標操作系統等信息。
段表——所有段的信息:段名、段的長度、在文件中的偏移、讀寫權限及段的其他屬性。
?
1、文件頭(readelf命令)?
ELF文件頭定義:ELF魔數、文件機器字節長度、文件存儲方式、版本、運行平臺、ABI版本、ELF重定位類型、硬件平臺、硬件平臺版本、入口地址、程序頭入口地址和長度、段表的位置和長度及段的數量等。
ELF文件頭結構及相關常數被定義在”/usr/include/elf.h”,32位“ELF32_Ehdr”
#define EI_NIDENT(16)typedef struct {unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */Elf32_Half e_type; /* Objectfile type */Elf32_Half e_machine; /*Architecture */Elf32_Word e_version; /* Object file version */Elf32_Addr e_entry; /* Entrypoint virtual address */Elf32_Off e_phoff; /* Programheader table file offset */Elf32_Off e_shoff; /* Sectionheader table file offset */Elf32_Word e_flags; /*Processor-specific flags */Elf32_Half e_ehsize; /* ELFheader size in bytes */Elf32_Half e_phentsize; /* Programheader table entry size */Elf32_Half e_phnum; /* Program header table entrycount */Elf32_Half e_shentsize; /* Sectionheader table entry size */Elf32_Half e_shnum; /* Sectionheader table entry count */Elf32_Half e_shstrndx; /* Sectionheader string table index */ } Elf32_Ehdr;結構與readelf輸出的ELF文件頭信息相比:只有e_ident對應了readelf輸出中的“Class ?Data? Version?OS/ABI? ABI Version”5個參數,剩下的參數一一對應。
ELF魔數:Magic:??7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 ?
16字節對應了Elf32_Ehdr的e_ident這個16字節成員。這16字節被ELF標準規定來標識ELF文件的平臺屬性:如字長、字節序、版本等。16字節含義:
前4字節是所有ELF文件都必須相同的標識碼:0x7F、0X45、0X4C、0X46(這四個字節就是ELF文件的魔數)。幾乎所有的可執行文件的開始的幾個字節都是魔數,如a.out最開始兩個字節是0x01、0x07;PE最開始兩個字節是0x4d、0x5a。這個魔數用來確認文件類型。第5個字節用來標識ELF文件類的,0x01表示是32位的,0x02表示64位的。第6字節是字節序,規定ELF文件是大端的還是小端的。第7字節規定ELF文件的主版本號,一般是1.后面的九個字節ELF標準沒有定義,一般寫0。
e_type文件類型:ET_REL—— 1 ——可重定位文件,一般是.o文件;ET_EXEC—— 2 —— 可執行文件; ET_DYN —— 2 ——共享目標文件,一般是.so文件。
e_machine機器類型:ELF文件的平臺屬性,EM_386 —— 3 —— x86
?
2、段表
ELF的段結構就是由段表決定的,編譯器、連接器和裝載器都是靠段表來定位和訪問各個段的屬性的。段表在ELF文件中的位置由ELF文件頭Elf32_Ehdr結構中的” e_shoff” 成員決定。section.o中,段表位于偏移0x104(260字節)處。
前面用”objdump -h”查看ELF文件中的段,此命令只是把ELF文件中的關鍵段顯示出來了,省略了其他輔助性的段:符號表、字符串表、重定位表等。
readelf -S命令
段表是以“Elf32_Shdr”結構體為元素的數組,數組元素的個數等于段的個數,每個“Elf32_Shdr”結構體對應一個段。section.o:11個元素的數組。/usr/include/elf.h:
typedef struct {Elf32_Word sh_name; /* Section name (string tblindex) */Elf32_Word sh_type; /* Section type */Elf32_Word sh_flags; /* Section flags */Elf32_Addr sh_addr; /* Section virtual addr atexecution */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 sectioninformation */Elf32_Word sh_addralign; /* Section alignment */Elf32_Word sh_entsize; /* Entry size if section holdstable */ } Elf32_Shdr;總結section.o段表的位置
| ? | 起始地址 | 大小 |
| ELF Header? e_shoff = 0x104 | 0 | 0x34 |
| .text | 0x34 | 0x52 |
| .data | 0x88 | 0x08 |
| .rodata | 0x90 | 0x06 |
| .comment | 0x96 | 0x1d |
| .shstrtab | 0xB3 | 0x51 |
| Section Table | 0x104 | 0x1b8 |
| .symtab | 0x2bc | 0xf0 |
| .rel.text | 0x3fc | 0x28 |
長度為0x424 = 1060,這個長度正好是section.o文件的大小。
段的類型(sh_type):段的名字只有在編譯和鏈接的過程中有意義。SHT_NULL – 0 – 無效段, SHT_PROGBITS– 1 – 程序段, SHT_SYMTAB – 2 – 表示該段的內容為符號表, SHT_STRTAB – 3 – 字符串表, SHT_RELA –4 – 重定位表, SHT_HASH – 5 – 符號表的哈希表,SHT_DYNAMIC – 6 – 動態鏈接信息, SHT_NOTE – 7 – 提示性信息, SHT_NOBITS– 8 –該段在文件中沒內容, SHT_REL – 9 –該段包含了重定位信息,SHT_SHLIB – 10 – 保留,SHT_DNYSYM – 11 – 動態鏈接的符號表。
段的標志位(sh_flag):表示該段在進程虛擬地址空間中的屬性,可寫可執行等。SHF_WRITE – 1 – 該段在進程空間中可寫; SHF_ALLOC– 2 – 在進程空間中要分配空間; SHF_EXECINSTR– 4 –該段在進程空間中可以被執行,一般指代碼段。
???????? 段的鏈接信息(sh_link、sh_info):
| sh_type | sh_link | sh_info |
| SHT_DYNAMIC | 字符串表在段表的下標 | 0 |
| SHT_HASH | 符號表在段表中的下標 | 0 |
| SHT_REL | 相應符號表在段表中的下標 | 該重定位表所作用的段在段表中的下標 |
| SHT_RELA | ||
| SHT_SYMTAB | 操作系統相關 | 操作系統相關 |
| SHTDYNSYM | ||
| other | SHN_UNDEF | 0 |
?
3、重定位表
???????? section.o中有一個“rel.text”的段,類型是“SHT_REL”——重定位表。
代碼段和數據段中那些對絕對地址的引用的位置——相應的重定位表。section.o中的“rel.text”就是對“.text”段的重定位表——printf函數的調用;而“.data”段沒有對絕對地址的引用,只包含了幾個常量,故沒有“.rel.data”。
?
4、字符串表——段名,變量名等
?
四、鏈接的接口——符號
???????? 可以使用很多工具查看ELF文件的符號表,readelf,objdump,nm等;
1、ELF符號表結構
???????? ELF符號表是文件中的一個段“.symtab”
/* Symbol table entry. */ typedef struct {Elf32_Word st_name; /* Symbol name (string tblindex) */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;
總結
以上是生活随笔為你收集整理的Linux C目标文件的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 人工智能专家系统的简单实现
- 下一篇: Linux下获取文件编译的时间