以下內容為<<linux內核編程>>筆記
鏈接程序
找出所有引用的外部模塊並鏈接起來,這些外部模塊或函數庫一般來自於開發者,操作系統和C運行庫。
鏈接程序取出這些函數庫,修訂指針位置(重定位),並交叉引用模塊中的符號解析,最終產生一個可執行模塊。符號可以是全局的也可以是局部的。全局符號可以在模塊內部定義,或由另一模塊外部引用。
靜態庫是在鏈接時被找到並復制的,而動態庫和共享庫是在運行時才裝載的,並讓所有的進程共享。linux提供的系統調用dlopen(),dlsym(),dlclose(),用於加載/打開共享庫,查找庫中的符號,然後關閉共享庫
ELF二進制目標文件
可執行ELF目標文件包括:ELF頭,程序頭表(用於加載的節),第1節,第2節。。。。節頭表(可選)
1.ELF文件頭
typedef struct elf32_hdr{ unsigned char e_ident[EI_NIDENT]; //標識該文件是否為ELF文件 Elf32_Half e_type; //指定目標文件類型,例如可執行文件,重定位文件,共享的目標文件 Elf32_Half e_machine; //被編譯文件所在系統的體系結構 Elf32_Word e_version; //目標文件的版本 Elf32_Addr e_entry; /* Entry point */ //程序的起始地址 Elf32_Off e_phoff; //保存程序頭表在文件中的偏移量 Elf32_Off e_shoff; //保存節頭表在文件中的偏移量 Elf32_Word e_flags; //保存於特定與處理器的標志 Elf32_Half e_ehsize; //字段保存ELF頭的大小 Elf32_Half e_phentsize; //保存程序頭表中的每一項的大小 Elf32_Half e_phnum; //程序頭中表項的個數 Elf32_Half e_shentsize; //節頭表中每一項的大小 Elf32_Half e_shnum; //保存節頭中項的數量,表明該文件中有多少節 Elf32_Half e_shstrndx; //保存節頭中節字符串的索引 } Elf32_Ehdr; typedef struct elf32_hdr{ unsigned char e_ident[EI_NIDENT]; //標識該文件是否為ELF文件 Elf32_Half e_type; //指定目標文件類型,例如可執行文件,重定位文件,共享的目標文件 Elf32_Half e_machine; //被編譯文件所在系統的體系結構 Elf32_Word e_version; //目標文件的版本 Elf32_Addr e_entry; /* Entry point */ //程序的起始地址 Elf32_Off e_phoff; //保存程序頭表在文件中的偏移量 Elf32_Off e_shoff; //保存節頭表在文件中的偏移量 Elf32_Word e_flags; //保存於特定與處理器的標志 Elf32_Half e_ehsize; //字段保存ELF頭的大小 Elf32_Half e_phentsize; //保存程序頭表中的每一項的大小 Elf32_Half e_phnum; //程序頭中表項的個數 Elf32_Half e_shentsize; //節頭表中每一項的大小 Elf32_Half e_shnum; //保存節頭中項的數量,表明該文件中有多少節 Elf32_Half e_shstrndx; //保存節頭中節字符串的索引 } Elf32_Ehdr;
2 節頭表
typedef struct elf32_shdr { Elf32_Word sh_name; //包含節名 Elf32_Word sh_type; //包含節的內容 Elf32_Word sh_flags; //各種屬性的內容 Elf32_Addr sh_addr; //節在內存映像中的地址 Elf32_Off sh_offset; //保存ELF文件中這一節中初始字節的偏移量 Elf32_Word sh_size; //包含節的大小 Elf32_Word sh_link; //表鏈接的索引 Elf32_Word sh_info; //包含附加信息 Elf32_Word sh_addralign; //包含地址對其的約束 Elf32_Word sh_entsize; //節中每項的大小 } Elf32_Shdr; typedef struct elf32_shdr { Elf32_Word sh_name; //包含節名 Elf32_Word sh_type; //包含節的內容 Elf32_Word sh_flags; //各種屬性的內容 Elf32_Addr sh_addr; //節在內存映像中的地址 Elf32_Off sh_offset; //保存ELF文件中這一節中初始字節的偏移量 Elf32_Word sh_size; //包含節的大小 Elf32_Word sh_link; //表鏈接的索引 Elf32_Word sh_info; //包含附加信息 Elf32_Word sh_addralign; //包含地址對其的約束 Elf32_Word sh_entsize; //節中每項的大小 } Elf32_Shdr;
3 非可執行ELF文件的節
bss 為初始化的數據
.data 已初始化的數據
.hash 符號散列表
.init 初始化代碼
.symtab 符號表
.text 可執行的指令
.plt 過程鏈接表
.rodata 只讀數據
.dynamic 動態鏈接信息
等等
4 程序頭表
typedef struct elf64_phdr { Elf64_Word p_type; //描述該段的類型 Elf64_Word p_flags; //以p_type而定 Elf64_Off p_offset; //<SPAN style="FONT-FAMILY: Arial, Helvetica, sans-serif">該段的開始相對於文件開始的偏移量</SPAN> Elf64_Addr p_vaddr; //段虛擬地址 Elf64_Addr p_paddr; //段的虛擬地址 Elf64_Xword p_filesz; //文件映像中該段的字節數 Elf64_Xword p_memsz; //內存映像中該段的字節數 Elf64_Xword p_align; //描述要對齊的段在內存中如何對齊,該值是2的整數次冪 typedef struct elf64_phdr { Elf64_Word p_type; //描述該段的類型 Elf64_Word p_flags; //以p_type而定 Elf64_Off p_offset; //該段的開始相對於文件開始的偏移量 Elf64_Addr p_vaddr; //段虛擬地址 Elf64_Addr p_paddr; //段的虛擬地址 Elf64_Xword p_filesz; //文件映像中該段的字節數 Elf64_Xword p_memsz; //內存映像中該段的字節數 Elf64_Xword p_align; //描述要對齊的段在內存中如何對齊,該值是2的整數次冪 [cpp] view plaincopyprint?} Elf64_Phdr; } Elf64_Phdr;
通過這些信息,系統函數exec()和鏈接程序合作,為可執行程序在內存中創建進程映像,該過程如下:
1. 將可執行文件的段加入內存
2. 加載所有需要的共享庫
3. 需要時重定向可執行文件及其共享對象
4. 將控制權交給程序