可重定位文件結構分析


可重定位文件結構分析
1. 文件頭
使用命令readelf –h vmlinux查看elf文件頭:
[mszsdtcf49][~/ws/arm_elf_linux/relocate_elf_reader]$ readelf -h vmlinux.o
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: REL (Relocatable file)
Machine: AArch64
Version: 0x1
Entry point address: 0x0
Start of program headers: 0 (bytes into file)
Start of section headers: 818885464 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 0 (bytes)
Number of program headers: 0
Size of section headers: 64 (bytes)
Number of section headers: 0 (201200)
Section header string table index: 65535 (201196)
[mszsdtcf49][~/ws/arm_elf_linux/relocate_elf_reader]$

文件的最開始幾個字節給出如何解釋文件的提示信息。這些信息獨立於處理器,也獨立於文件中的其余內容。ELF Header 部分可以用以下的數據結構表示:
typedef struct {
unsigned char e_ident[EI_NIDENT]; /* Id bytes */
Elf64_Quarter e_type; /* file type */
Elf64_Quarter e_machine; /* machine type */
Elf64_Half e_version; /* version number */
Elf64_Addr e_entry; /* entry point */
Elf64_Off e_phoff; /* Program hdr offset */
Elf64_Off e_shoff; /* Section hdr offset */
Elf64_Half e_flags; /* Processor flags */
Elf64_Quarter e_ehsize; /* sizeof ehdr */
Elf64_Quarter e_phentsize; /* Program header entry size */
Elf64_Quarter e_phnum; /* Number of program headers */
Elf64_Quarter e_shentsize; /* Section header entry size */
Elf64_Quarter e_shnum; /* Number of section headers */
Elf64_Quarter e_shstrndx; /* String table index */
} Elf64_Ehdr;
來源:https://github.com/torvalds/linux/blob/master/include/uapi/linux/elf.h#L222

ELF Header大小比較小,只占用64個byte空間,每個字段意義與readelf輸出的對應。

圖片來源:https://bbs.pediy.com/thread-255670.htm

2. 節(section)
節區頭部表
ELF 頭部中,e_shoff 成員給出從文件頭到節區頭部表格的偏移字節數; e_shnum 給出表格中條目數目; e_shentsize 給出每個項目的字節數。從這些信息中可以確切地定 位節區的具體位置、長度。

每個節區頭部數據結構描述:
typedef struct {
Elf64_Half sh_name; /* section name */
Elf64_Half sh_type; /* section type */
Elf64_Xword sh_flags; /* section flags */
Elf64_Addr sh_addr; /* virtual address */
Elf64_Off sh_offset; /* file offset */
Elf64_Xword sh_size; /* section size */
Elf64_Half sh_link; /* link to another */
Elf64_Half sh_info; /* misc info */
Elf64_Xword sh_addralign; /* memory alignment */
Elf64_Xword sh_entsize; /* table entry size */
} Elf64_Shdr;

如何從Elf64_Ehdr 找到Elf64_Shdr?
shdrs = (Elf64_Shdr *)(ehdr->e_shoff + (void *)ehdr);

第一個section不描述任何數據,但是保存了節區數量以及節區使用的符號表位置,其內容如下:
[mszsdtcf49][~/ws/arm_elf_linux/relocate_elf_reader]$ ./relocate_elf -n 0 vmlinux.o
The file:vmlinux.o size is 833088504.
shdr->sh_name = 0
shdr->sh_type = 0
shdr->sh_flags = 0
shdr->sh_addr = 0
shdr->sh_offset= 0
shdr->sh_size = 0x311f0 // section個數
shdr->sh_link = 0x311ec // section的symbol存放在哪個section中
shdr->sh_info = 0
shdr->sh_addralign = 0
shdr->sh_entsize = 0
[mszsdtcf49][~/ws/arm_elf_linux/relocate_elf_reader]$

ehdr與shdr的關系如下圖:

 

 


圖片來源:http://chuquan.me/2018/05/21/elf-introduce/

3. section類型
節包含對象文件中的所有信息,但ELF頭,程序頭表和節頭表除外。此外,目標文件的節滿足幾個條件。
• 對象文件中的每個節都只有一個描述它的節頭。可能存在不包含節的節標題。
• 每個節在文件中占用一個連續的字節序列(可能為空)。
• 文件中的節可能不會重疊。文件中的任何字節都不能駐留在多個部分中。
• 目標文件可能具有無效空間。各種標頭和節可能不會``覆蓋''目標文件中的每個字節。未指定非活動數據的內容。

3.1 section結構體
section的類型存放在section table中的sh_type中,elf.h中列出的取值可能如下:
/* Legal values for sh_type (section type). */

#define SHT_NULL 0 /* Section header table entry unused */
#define SHT_PROGBITS 1 /* Program data */
#define SHT_SYMTAB 2 /* Symbol table */
#define SHT_STRTAB 3 /* String table */
#define SHT_RELA 4 /* Relocation entries with addends */
#define SHT_HASH 5 /* Symbol hash table */
#define SHT_DYNAMIC 6 /* Dynamic linking information */
#define SHT_NOTE 7 /* Notes */
#define SHT_NOBITS 8 /* Program space with no data (bss) */
#define SHT_REL 9 /* Relocation entries, no addends */
#define SHT_SHLIB 10 /* Reserved */
#define SHT_DYNSYM 11 /* Dynamic linker symbol table */
#define SHT_INIT_ARRAY 14 /* Array of constructors */
#define SHT_FINI_ARRAY 15 /* Array of destructors */
#define SHT_PREINIT_ARRAY 16 /* Array of pre-constructors */
#define SHT_GROUP 17 /* Section group */
#define SHT_SYMTAB_SHNDX 18 /* Extended section indeces */
#define SHT_NUM 19 /* Number of defined types. */
#define SHT_LOOS 0x60000000 /* Start OS-specific. */
#define SHT_GNU_ATTRIBUTES 0x6ffffff5 /* Object attributes. */
#define SHT_GNU_HASH 0x6ffffff6 /* GNU-style hash table. */
#define SHT_GNU_LIBLIST 0x6ffffff7 /* Prelink library list */
#define SHT_CHECKSUM 0x6ffffff8 /* Checksum for DSO content. */
#define SHT_LOSUNW 0x6ffffffa /* Sun-specific low bound. */
#define SHT_SUNW_move 0x6ffffffa
#define SHT_SUNW_COMDAT 0x6ffffffb
#define SHT_SUNW_syminfo 0x6ffffffc
#define SHT_GNU_verdef 0x6ffffffd /* Version definition section. */
#define SHT_GNU_verneed 0x6ffffffe /* Version needs section. */
#define SHT_GNU_versym 0x6fffffff /* Version symbol table. */
#define SHT_HISUNW 0x6fffffff /* Sun-specific high bound. */
#define SHT_HIOS 0x6fffffff /* End OS-specific type */
#define SHT_LOPROC 0x70000000 /* Start of processor-specific */
#define SHT_HIPROC 0x7fffffff /* End of processor-specific */
#define SHT_LOUSER 0x80000000 /* Start of application-specific */
#define SHT_HIUSER 0x8fffffff /* End of application-specific */

常用的section type解釋如下:
SHT_NULL 此值將節標題標記為無效;它沒有關聯的部分。節標題的其他成員具有未定義的值。
SHT_PROGBITS 該部分保存由程序定義的信息,其格式和含義僅由程序確定。
SHT_SYMTAB和SHT_DYNSYM 這些部分包含一個符號表。當前,一個目標文件可能每種類型只有一個節,但是將來可能會放寬此限制。通常, SHT_SYMTAB提供用於鏈接編輯的符號,盡管它也可以用於動態鏈接。作為完整的符號表,它可能包含許多動態鏈接不需要的符號。因此,目標文件也可能包含SHT_DYNSYM節,該節保留最少的動態鏈接符號集,以節省空間。
SHT_STRTAB 該部分包含一個字符串表。一個目標文件可能具有多個字符串表節。
SHT_RELA 本節包含帶有顯式加數的重定位條目,例如對於32位類的目標文件,類型為Elf32_Rela。一個目標文件可能具有多個重定位部分。
SHT_HASH 該部分包含一個符號哈希表。當前,一個目標文件可能只有一個哈希表,但是將來可能會放寬此限制。
SHT_DYNAMIC 本節包含用於動態鏈接的信息。當前,一個目標文件可能只有一個動態節,但是將來可能會放寬此限制。
SHT_NOTE 本節包含以某種方式標記文件的信息。
SHT_NOBITS 此類型的節在文件中不占空間,但與SHT_PROGBITS相似。盡管本節不包含任何字節,但該sh_offset成員包含概念性文件偏移量。
SHT_REL 本節包含沒有明確加數的重定位條目,例如 32位類目標文件的Elf32_Rel類型。一個目標文件可能具有多個重定位部分。
SHT_SHLIB 此節類型是保留的,但具有未指定的語義。
來源:http://osr5doc.xinuos.com/en/topics/ELF_secthead.html

• SHT_SYMTAB、SHT_DYNSYM → Symbol table entry
如果節的類型是SHT_SYMTAB或者SHT_DYNSYM,那么該節的內容使用Elf32_Sym和Elf64_Sym描述。
• SHT_RELA → Relocation table entry with addend
如果節的類型是SHT_RELA,那么該節的內容使用Elf32_Rela和Elf64_Rela描述。
• SHT_DYNAMIC → Dynamic section entry
如果節的類型是SHT_DYNAMIC,那么該節的內容使用Elf32_Dyn和Elf64_Dyn描述。

3.2 Symbol
Elf64_Shdr中,如果sh_type描述的section取值為SHT_SYMTAB、SHT_DYNSYM,,那么該section為一個Elf64_Sym[] 類型symbol tab。
/* Symbol table entry. */
typedef struct
{
Elf64_Word   st_name;   /* Symbol name (string tbl index) */
unsigned char  st_info;   /* Symbol type and binding */
unsigned char  st_other;  /* Symbol visibility */
Elf64_Section  st_shndx;  /* Section index */
Elf64_Addr   st_value;  /* Symbol value */
Elf64_Xword   st_size;   /* Symbol size */
} Elf64_Sym;

可重定位文件的符號表包含定位和重新定位程序的符號定義和引用所需的信息。符號表索引是該數組的下標。索引0既指定表中的第一個條目,又用作未定義的符號索引。
st_name 目標文件的符號字符串表的索引,其中包含符號名稱的字符表示。如果該值非零,則表示給出符號名稱的字符串表索引。否則,符號表條目將沒有名稱。
st_value 相關符號的值。根據上下文,這可以是絕對值,地址等。請參見“符號值”。

st_size 許多符號都有關聯的大小。例如,數據對象的大小是對象中包含的字節數。如果符號沒有大小或未知大小,則該成員保持0。
st_info 符號的類型和綁定屬性。表7-19中顯示了值和含義的列表。以下代碼顯示了如何操作在中定義的值sys/elf.h:
#define ELF32_ST_BIND(info) ((info) >> 4)
#define ELF32_ST_TYPE(info) ((info) & 0xf)
#define ELF32_ST_INFO(bind, type) (((bind)<<4)+((type)&0xf))
#define ELF64_ST_BIND(info) ((info) >> 4)
#define ELF64_ST_TYPE(info) ((info) & 0xf)
#define ELF64_ST_INFO(bind, type) (((bind)<<4)+((type)&0xf))
st_other 符號的可見性。表7-21中顯示了值和含義的列表。以下代碼顯示了如何操作32位和64位對象的值。其他位包含0,沒有定義的含義。
#define ELF32_ST_VISIBILITY(o) ((o)&0x3)
#define ELF64_ST_VISIBILITY(o) ((o)&0x3)
st_shndx 每個符號表條目都是相對於某個部分定義的。該成員保存相關節頭表的索引。如表7-12所示,某些部分的索引指示特殊含義。

來源:https://docs.oracle.com/cd/E19683-01/816-1386/chapter6-79797/index.html
特定section index
在ehdr中會記錄所有的section,通過index來查找section,這里的st_shndx記錄的也是section數組的index,但是一些節頭表索引是保留的;可重定位文件也沒有這些特殊索引的部分。
Name Value
SHN_UNDEF 0
SHN_LORESERVE 0xff00
SHN_LOPROC 0xff00
SHN_HIPROC 0xff1f
SHN_ABS 0xfff1
SHN_COMMON 0xfff2
SHN_HIRESERVE 0xffff


SHN_UNDEF 通常表示該符號在本文件中未定義 (外部符號),Linux linker 會為該符號做地址重定位。
SHN_LORESERVE 此值指定保留索引范圍的下限。
SHN_LOPROC到SHN_HIPROC 此包含范圍內的值保留用於特定於處理器的語義。
SHN_ABS 表示該符號包含一個絕對的 (absolute) 值 (往往是一個地址),不受重定位影響。(例如FILE類型,__bss_start symbol類型)
SHN_COMMON 相對於本節定義的符號是通用符號,例如FORTRAN,COMMON或未分配的C外部變量。(表示該符號是一個 common 符號,通常未初始化的全局變量就是該類型的符號。)
SHN_HIRESERVE 此值指定保留索引范圍的上限。系統在SHN_LORESERVE和SHN_HIRESERVE之間(包括兩端)保留索引 ;這些值不引用節標題表。節標題表不包含保留索引的條目。

3.3 strtab
.strtab節保存的是符號字符串表,表中的內容會被.symtab的Elf64_Sym結構中的st_name引用。節類型為SHT_STRTAB。
當shdrs[n].sh_type == SHT_SYMTAB或者SHT_DYNSYM時,該section的sh_link指向strtab.
if (shdrs[i].sh_type == SHT_SYMTAB || shdrs[i].sh_type == SHT_DYNSYM) {
const char *shname = shstrtab + shdrs[i].sh_name;
Elf64_Sym *syms = base + shdrs[i].sh_offset;
size_t entries = shdrs[i].sh_size / shdrs[i].sh_entsize;
const char *strtab = base + shdrs[shdrs[i].sh_link].sh_offset;
do_elf_section_info(&shdrs[i]);
printf("shname=%s, syms=%px, strtab=%px\n", shname, syms, strtab);
print_syms(shdrs, shstrtab, shname, syms, entries, strtab);
}

 

 


來源:http://chuquan.me/2018/05/21/elf-introduce/

通過section .strtab可以找到strtab,然后根據section .symtab的name來查找對應symbol的name。

3.4 重定位表(rel/rela)
重定位是將符號引用與符號定義連接在一起的過程。例如,當程序調用函數時,相關的調用指令必須在執行時將控制權轉移到正確的目標地址。可重定位文件必須具有描述如何修改其節內容的信息,從而使可執行文件和共享目標文件可以保存進程程序映像的正確信息。
typedef struct
{
Elf64_Addr r_offset;
Elf64_Xword r_info;
} Elf64_Rel;

typedef struct
{
Elf64_Addr r_offset; /* Address */
Elf64_Xword r_info; /* Relocation type and symbol index */
Elf64_Sxword r_addend; /* Addend */
} Elf64_Rela;

關於rel/rela字段的解釋如下:
r_offset 該成員提供了應用重定位操作的位置。不同的對象文件對此成員的解釋略有不同。

對於可重定位文件,該值指示節偏移量(_moucnt_loc中的offset)。重定位部分本身描述了如何修改文件中的另一部分。重定位偏移量指定第二部分中的存儲單元。

對於可執行文件或共享庫,該值指示受重定位影響的存儲單元的虛擬地址。此信息使重定位條目對運行時鏈接程序更加有用。

盡管對於不同的對象文件,成員的解釋有所不同,以允許相關程序進行有效訪問,但是重定位類型的含義保持不變。

r_info 該成員既提供符號表索引(必須針對該索引進行重定位),也提供要應用的重定位類型。例如,調用指令的重定位條目將保存被調用函數的符號表索引。如果索引是STN_UNDEF,即未定義的符號索引,則重定位使用0作為符號值。

重定位類型是特定於處理器的。重定位條目的重定位類型或符號表索引是分別將ELF32_R_TYPE或ELF32_R_SYM應用於條目的r_info成員的結果:

#define ELF64_R_SYM(info) ((info)>>32)
#define ELF64_R_TYPE(info) ((Elf64_Word)(info))
#define ELF64_R_INFO(sym, type) (((Elf64_Xword)(sym)<<32)+\
(Elf64_Xword)(type))

對於Elf64_Rel和Elf64_Rela結構,該r_info字段進一步細分為8位類型標識符和24位類型相關數據字段:

#define ELF64_R_TYPE_DATA(info) (((Elf64_Xword)(info)<<32)>>40)
#define ELF64_R_TYPE_ID(info) (((Elf64_Xword)(info)<<56)>>56)
#define ELF64_R_TYPE_INFO(data, type) (((Elf64_Xword)(data)<<8)+\
(Elf64_Xword)(type))

r_addend 該成員指定用於計算要存儲到可重定位字段中的值的常數加數。
來源:https://docs.oracle.com/cd/E19683-01/816-1386/6m7qcoblj/index.html#chapter6-54839

以mcount_loc 為例,從vmlinux.o中讀出來的rela_mcount_loc section信息如下:
[*] Offset Info Addend Type Sym. Value Sym. Name (section)
0000000000000000 0000000600000101 00007100 OTHERS 0000000000000000 $x.2 (.rela__mcount_loc)
0000000000000008 0000000600000101 00007138 OTHERS 0000000000000000 $x.2 (.rela__mcount_loc)
0000000000000010 0000000600000101 000071f4 OTHERS 0000000000000000 $x.2 (.rela__mcount_loc)
Offset: 存放在mcount_loc中的位置.
Info: 前32bit為描述的section index,后32bit為type.
Addend: 相對section的偏移量.

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM