Linux可执行文件ELF文件结构
目标文件格式
编译器编译源代码后生成的文件叫做目标文件,而目标文件经过编译器链接之后得到的就是可执行文件。那么目标文件到底是什么?它和可执行文件又有什么区别?链接到底又做了什么呢?接下来,我们将探索一下目标文件的本质。
目前,PC平台流行的 可执行文件格式(Executable) 主要包含如下两种,它们都是 COFF(Common File Format) 格式的变种。
- Windows下的 PE(Portable Executable)
- Linux下的 ELF(Executable Linkable Format)
目标文件就是源代码经过编译后但未进行链接的那些中间文件(Windows的.obj和Linux的.o),它与可执行文件的格式非常相似,所以一般跟可执行文件格式一起采用同一种格式存储。在Windows下采用PE-COFF文件格式;Linux下采用ELF文件格式。
事实上,除了可执行文件外,动态链接库(Dynamic Linking Library)、静态链接库(Static Linking Library) 均采用可执行文件格式存储。它们在Window下均按照PE-COFF格式存储;Linux下均按照ELF格式存储。只是文件名后缀不同而已。
- 动态链接库:Windows的.dll、Linux的.so
- 静态链接库:Windows的.lib、Linux的.a
本文将以Linux ELF文件为例进行介绍。
ELF文件结构
如图所示,为ELF文件的基本结构,其主要由四部分组成:
- ELF Header
- ELF Program Header Table (或称Program Headers、程序头),可选,当目标为.o文件时无此字段
- ELF Section Header Table (或称Section Headers、节头表)
- ELF Sections
1.ELF Header
我们可以使用readelf -h来查看ELF Header。readelf 和 objdump 的源码位于 binutils 源码包中,下载地址点这里,对于 elf 文件格式的读取以及解析参考 binutils/readelf.c,include/elf/external.h的实现即可。
64位ELF Header
root@DESKTOP-90KJ7MS:/usr/src/bluetooth/bluez_ins/lib# readelf -h libexpat.so
ELF Header:Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00Class: ELF64Data: 2's complement, little endianVersion: 1 (current)OS/ABI: UNIX - System VABI Version: 0Type: DYN (Shared object file)Machine: AArch64Version: 0x1Entry point address: 0x37e0Start of program headers: 64 (bytes into file)Start of section headers: 709336 (bytes into file)Flags: 0x0Size of this header: 64 (bytes)Size of program headers: 56 (bytes)Number of program headers: 7Size of section headers: 64 (bytes)Number of section headers: 35Section header string table index: 34
typedef struct {unsigned char e_ident[16]; /* ELF "magic number" */unsigned char e_type[2]; /* Identifies object file type */unsigned char e_machine[2]; /* Specifies required architecture */unsigned char e_version[4]; /* Identifies object file version */unsigned char e_entry[8]; /* Entry point virtual address */unsigned char e_phoff[8]; /* Program header table file offset */unsigned char e_shoff[8]; /* Section header table file offset */unsigned char e_flags[4]; /* Processor-specific flags */unsigned char e_ehsize[2]; /* ELF header size in bytes */unsigned char e_phentsize[2]; /* Program header table entry size */unsigned char e_phnum[2]; /* Program header table entry count */unsigned char e_shentsize[2]; /* Section header table entry size */unsigned char e_shnum[2]; /* Section header table entry count */unsigned char e_shstrndx[2]; /* Section header string table index */
} Elf64_External_Ehdr;
32位ELF Header
root@DESKTOP-90KJ7MS:/usr/src/helloworld# readelf -h hello
ELF Header:Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00Class: ELF32Data: 2's complement, little endianVersion: 1 (current)OS/ABI: UNIX - System VABI Version: 0Type: EXEC (Executable file)Machine: ARMVersion: 0x1Entry point address: 0x1030cStart of program headers: 52 (bytes into file)Start of section headers: 6872 (bytes into file)Flags: 0x5000200, Version5 EABI, soft-float ABISize of this header: 52 (bytes)Size of program headers: 32 (bytes)Number of program headers: 9Size of section headers: 40 (bytes)Number of section headers: 29Section header string table index: 28
root@DESKTOP-90KJ7MS:/usr/src/helloworld# readelf -h hello.o
ELF Header:Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00Class: ELF32Data: 2's complement, little endianVersion: 1 (current)OS/ABI: UNIX - System VABI Version: 0Type: REL (Relocatable file)Machine: ARMVersion: 0x1Entry point address: 0x0Start of program headers: 0 (bytes into file)Start of section headers: 552 (bytes into file)Flags: 0x5000000, Version5 EABISize of this header: 52 (bytes)Size of program headers: 0 (bytes)Number of program headers: 0Size of section headers: 40 (bytes)Number of section headers: 12Section header string table index: 11
typedef struct {unsigned char e_ident[16]; /* ELF "magic number" */unsigned char e_type[2]; /* Identifies object file type */unsigned char e_machine[2]; /* Specifies required architecture */unsigned char e_version[4]; /* Identifies object file version */unsigned char e_entry[4]; /* Entry point virtual address */unsigned char e_phoff[4]; /* Program header table file offset */unsigned char e_shoff[4]; /* Section header table file offset */unsigned char e_flags[4]; /* Processor-specific flags */unsigned char e_ehsize[2]; /* ELF header size in bytes */unsigned char e_phentsize[2]; /* Program header table entry size */unsigned char e_phnum[2]; /* Program header table entry count */unsigned char e_shentsize[2]; /* Section header table entry size */unsigned char e_shnum[2]; /* Section header table entry count */unsigned char e_shstrndx[2]; /* Section header string table index */
} Elf32_External_Ehdr;
ELF文件结构示意图中定义的Elf_Ehdr的各个成员的含义与readelf具有对应关系。如下表所示:
成员 | 含义 |
---|---|
e_ident | Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 |
Class: ELF32、ELF64 | |
Data:2’s complement, little end | |
Version: 1 (current) | |
OS/ABI: UNIX - System V | |
ABI Version: 0 | |
e_type | Type: REL、EXEC、DYN ELF文件类型 |
e_machine | Machine: ARM、AARCH64 |
e_version | Version: 0x1 ELF版本号。一般为常数1 |
e_entry | Entry point address: 0x0 入口地址,规定ELF程序的入口虚拟地址,操作系统在加载完该程序后从这个地址开始执行进程的指令。可重定位指令一般没有入口地址,则该值为0 |
e_phoff | Start of program headers: 0(bytes into file) |
e_shoff | Start of section headers: 672 (bytes into file) Section Header Table 在文件中的偏移 |
e_word | Flags: 0x0 ELF标志位,用来标识一些ELF文件平台相关的属性。 |
e_ehsize | Size of this header: 64 (bytes) ELF Header本身的大小 |
e_phentsize | Size of program headers: 0 (bytes) |
e_phnum | Number of program headers: 0 |
e_shentsize | Size of section headers: 64 (bytes) 单个Section Header大小 |
e_shnum | Number of section headers: 13 Section Header的数量 |
e_shstrndx | Section header string table index: 10 Section Header字符串表在Section Header Table中的索引 |
ELF魔数
每种可执行文件的格式的开头几个字节都是很特殊的,特别是开头4个字节,通常被称为魔数(Magic Number)。通过对魔数的判断可以确定文件的格式和类型。如:ELF的可执行文件格式的头4个字节为0x7F、e、l、f;Java的可执行文件格式的头4个字节为c、a、f、e;如果被执行的是Shell脚本或perl、python等解释型语言的脚本,那么它的第一行往往是#!/bin/sh或#!/usr/bin/perl或#!/usr/bin/python,此时前两个字节#和!就构成了魔数,系统一旦判断到这两个字节,就对后面的字符串进行解析,以确定具体的解释程序路径。
ELF文件类型
ELF文件主要有三种类型,可以通过ELF Header中的e_type成员进行区分。
- 可重定位文件(Relocatable File):REL。一般为.o文件。可以被链接成可执行文件或共享目标文件。静态链接库属于可重定位文件。
- 可执行文件(Executable File):EXEC。可以直接执行的程序。
- 共享目标文件(Shared Object File):DYN。一般为.so文件。有两种情况可以使用。
链接器将其与其他可重定位文件、共享目标文件链接成新的目标文件;
动态链接器将其与其他共享目标文件、结合一个可执行文件,创建进程映像。
2. ELF Program Header Table
程序加载的时候,会以段(Segment)作为管理单位,比如虚拟内存地址映射。
注意:段(Segment)与节(Section)的区别。很多地方对两者有所混淆。段是程序执行的必要组成,当多个目标文件链接成一个可执行文件时,会将相同权限的节合并到一个段中。相比而言,节的粒度更小。下面是Section到Segement映射:
Program Headers:Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg AlignEXIDX 0x00049c 0x0001049c 0x0001049c 0x00008 0x00008 R 0x4PHDR 0x000034 0x00010034 0x00010034 0x00120 0x00120 R 0x4INTERP 0x000154 0x00010154 0x00010154 0x00013 0x00013 R 0x1[Requesting program interpreter: /lib/ld-linux.so.3]LOAD 0x000000 0x00010000 0x00010000 0x004a8 0x004a8 R E 0x10000LOAD 0x000f10 0x00020f10 0x00020f10 0x00118 0x0011c RW 0x10000DYNAMIC 0x000f18 0x00020f18 0x00020f18 0x000e8 0x000e8 RW 0x4NOTE 0x000168 0x00010168 0x00010168 0x00044 0x00044 R 0x4GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x10GNU_RELRO 0x000f10 0x00020f10 0x00020f10 0x000f0 0x000f0 R 0x1
Section to Segment mapping:Segment Sections...00 .ARM.exidx0102 .interp03 .interp .note.gnu.build-id .note.ABI-tag .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rel.dyn .rel.plt .init .plt .text .fini .rodata .ARM.exidx .eh_frame04 .init_array .fini_array .dynamic .got .data .bss05 .dynamic06 .note.gnu.build-id .note.ABI-tag0708 .init_array .fini_array .dynamic
typedef struct {unsigned char p_type[4]; /* Identifies program segment type */unsigned char p_offset[4]; /* Segment file offset */unsigned char p_vaddr[4]; /* Segment virtual address */unsigned char p_paddr[4]; /* Segment physical address */unsigned char p_filesz[4]; /* Segment size in file */unsigned char p_memsz[4]; /* Segment size in memory */unsigned char p_flags[4]; /* Segment flags */unsigned char p_align[4]; /* Segment alignment, file & memory */
} Elf32_External_Phdr;
3. ELF Section Header Table
ELF 节头表是一个节头数组。每一个节头都描述了其所对应的节的信息,如节名、节大小、在文件中的偏移、读写权限等。编译器、链接器、装载器都是通过节头表来定位和访问各个节的属性的。
我们可以使用readelf -S工具来查看节头表。
root@DESKTOP-90KJ7MS:/usr/src/helloworld# readelf -S hello
There are 29 section headers, starting at offset 0x1ad8:Section Headers:[Nr] Name Type Addr Off Size ES Flg Lk Inf Al[ 0] NULL 00000000 000000 000000 00 0 0 0[ 1] .interp PROGBITS 00010154 000154 000013 00 A 0 0 1[ 2] .note.gnu.build-i NOTE 00010168 000168 000024 00 A 0 0 4[ 3] .note.ABI-tag NOTE 0001018c 00018c 000020 00 A 0 0 4[ 4] .gnu.hash GNU_HASH 000101ac 0001ac 00002c 04 A 5 0 4[ 5] .dynsym DYNSYM 000101d8 0001d8 000050 10 A 6 1 4[ 6] .dynstr STRTAB 00010228 000228 000041 00 A 0 0 1[ 7] .gnu.version VERSYM 0001026a 00026a 00000a 02 A 5 0 2[ 8] .gnu.version_r VERNEED 00010274 000274 000020 00 A 6 1 4[ 9] .rel.dyn REL 00010294 000294 000008 08 A 5 0 4[10] .rel.plt REL 0001029c 00029c 000020 08 AI 5 21 4[11] .init PROGBITS 000102bc 0002bc 00000c 00 AX 0 0 4[12] .plt PROGBITS 000102c8 0002c8 000044 04 AX 0 0 4[13] .text PROGBITS 0001030c 00030c 000174 00 AX 0 0 4[14] .fini PROGBITS 00010480 000480 000008 00 AX 0 0 4[15] .rodata PROGBITS 00010488 000488 000011 00 A 0 0 4[16] .ARM.exidx ARM_EXIDX 0001049c 00049c 000008 00 AL 13 0 4[17] .eh_frame PROGBITS 000104a4 0004a4 000004 00 A 0 0 4[18] .init_array INIT_ARRAY 00020f10 000f10 000004 04 WA 0 0 4[19] .fini_array FINI_ARRAY 00020f14 000f14 000004 04 WA 0 0 4[20] .dynamic DYNAMIC 00020f18 000f18 0000e8 08 WA 6 0 4[21] .got PROGBITS 00021000 001000 000020 04 WA 0 0 4[22] .data PROGBITS 00021020 001020 000008 00 WA 0 0 4[23] .bss NOBITS 00021028 001028 000004 00 WA 0 0 1[24] .comment PROGBITS 00000000 001028 00002b 01 MS 0 0 1[25] .ARM.attributes ARM_ATTRIBUTES 00000000 001053 00002a 00 0 0 1[26] .symtab SYMTAB 00000000 001080 000660 10 27 80 4[27] .strtab STRTAB 00000000 0016e0 0002f2 00 0 0 1[28] .shstrtab STRTAB 00000000 0019d2 000105 00 0 0 1
Key to Flags:W (write), A (alloc), X (execute), M (merge), S (strings), I (info),L (link order), O (extra OS processing required), G (group), T (TLS),C (compressed), x (unknown), o (OS specific), E (exclude),y (purecode), p (processor specific)
ELF文件结构示意图中定义的Elf_Shdr的各个成员的含义与readelf具有对应关系。如下表所示:
成员 | 含义 |
---|---|
sh_name | 节名 节名是一个字符串,保存在一个名为.shstrtab的字符串表(可通过Section Header索引到)。sh_name的值实际上是其节名字符串在.shstrtab中的偏移值 |
sh_type | 节类型 |
sh_flags | 节标志位 |
sh_addr | 节地址:节的虚拟地址 如果该节可以被加载,则sh_addr为该节被加载后在进程地址空间中的虚拟地址;否则sh_addr为0 |
sh_offset | 节偏移 如果该节存在于文件中,则表示该节在文件中的偏移;否则无意义,如sh_offset对于BSS 节来说是没有意义的 |
sh_size | 节大小 |
sh_link、sh_info | 节链接信息 |
sh_addralign | 节地址对齐方式 |
sh_entsize | 节项大小 有些节包含了一些固定大小的项,如符号表,其包含的每个符号所在的大小都一样的,对于这种节,sh_entsize表示每个项的大小。如果为0,则表示该节不包含固定大小的项。 |
节类型(sh_type)相关常量以SHT_开头,上述readelf -S命令执行的结果省略了该前缀。常见的节类型如下表所示:
常量 | 值 | 含义 |
---|---|---|
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 | 表示该节在文件中没有内容。如.bss节 |
SHT_REL | 9 | 该节包含了重定位信息 |
SHT_SHLIB | 10 | 保留 |
SHT_DNYSYM | 11 | 动态链接的符号表 |
节标志位(sh_flag)
节标志位表示该节在进程虚拟地址空间中的属性。如是否可写、是否可执行等。相关常量以SHF_开头。常见的节标志位如下表所示:
常量 | 值 | 含义 |
---|---|---|
SHF_WRITE | 1 | 表示该节在进程空间中可写 |
SHF_ALLOC | 2 | 表示该节在进程空间中需要分配空间。有些包含指示或控制信息的节不需要在进程空间中分配空间,就不会有这个标志。 |
SHF_EXECINSTR | 4 | 表示该节在进程空间中可以被执行 |
节链接信息(sh_link、sh_info)
如果节的类型是与链接相关的(无论是动态链接还是静态链接),如重定位表、符号表、等,则sh_link、sh_info两个成员所包含的意义如下所示。其他类型的节,这两个成员没有意义。
sh_type | sh_link | sh_info |
---|---|---|
SHT_DYNAMIC | 该节所使用的字符串表在节头表中的下标 | 0 |
SHT_HASH | 该节所使用的符号表在节头表中的下标 | 0 |
SHT_REL | 该节所使用的相应符号表在节头表中的下标 | 该重定位表所作用的节在节头表中的下标 |
SHT_RELA | 该节所使用的相应符号表在节头表中的下标 | 该重定位表所作用的节在节头表中的下标 |
SHT_SYMTAB | 操作系统相关 | 操作系统相关 |
SHT_DYNSYM | 操作系统相关 | 操作系统相关 |
other | SHN_UNDEF | 0 |
4.ELF Sections
节的分类
上述ELF Section Header Table部分已经简单介绍了节类型。接下来我们来介绍详细一些比较重要的节。
.text节
.text节是保存了程序代码指令的代码节。一段可执行程序,如果存在Phdr,则.text节就会存在于text段中。由于.text节保存了程序代码,所以节类型为SHT_PROGBITS。
.rodata节
rodata节保存了只读的数据,如一行C语言代码中的字符串。由于.rodata节是只读的,所以只能存在于一个可执行文件的只读段中。因此,只能在text段(不是data段)中找到.rodata节。由于.rodata节是只读的,所以节类型为SHT_PROGBITS。
.plt节(过程链接表)
.plt节也称为过程链接表(Procedure Linkage Table),其包含了动态链接器调用从共享库导入的函数所必需的相关代码。由于.plt节保存了代码,所以节类型为SHT_PROGBITS。
.data节
.data节存在于data段中,其保存了初始化的全局变量等数据。由于.data节保存了程序的变量数据,所以节类型为SHT_PROGBITS。
.bss节
.bss节存在于data段中,占用空间不超过4字节,仅表示这个节本省的空间。.bss节保存了未进行初始化的全局数据。程序加载时数据被初始化为0,在程序执行期间可以进行赋值。由于.bss节未保存实际的数据,所以节类型为SHT_NOBITS。
.got.plt节(全局偏移表-过程链接表)
.got节保存了全局偏移表。.got节和.plt节一起提供了对导入的共享库函数的访问入口,由动态链接器在运行时进行修改。由于.got.plt节与程序执行有关,所以节类型为SHT_PROGBITS。
.dynsym节(动态链接符号表)
.dynsym节保存在text段中。其保存了从共享库导入的动态符号表。节类型为SHT_DYNSYM。
.dynstr节(动态链接字符串表)
.dynstr保存了动态链接字符串表,表中存放了一系列字符串,这些字符串代表了符号名称,以空字符作为终止符。
.rel.*节(重定位表)
重定位表保存了重定位相关的信息,这些信息描述了如何在链接或运行时,对ELF目标文件的某部分或者进程镜像进行补充或修改。由于重定位表保存了重定位相关的数据,所以节类型为SHT_REL。
.hash节
.hash节也称为.gnu.hash,其保存了一个用于查找符号的散列表。
.symtab节(符号表)
.symtab节是一个ElfN_Sym的数组,保存了符号信息。节类型为SHT_SYMTAB。
.strtab节(字符串表)
.strtab节保存的是符号字符串表,表中的内容会被.symtab的ElfN_Sym结构中的st_name引用。节类型为SHT_STRTAB。
.ctors节和.dtors节
.ctors(构造器)节和.dtors(析构器)节分别保存了指向构造函数和析构函数的函数指针,构造函数是在main函数执行之前需要执行的代码;析构函数是在main函数之后需要执行的代码。
符号表
节的分类中我们介绍了.dynsym节和.symtab节,两者都是符号表。那么它们到底有什么区别呢?存在什么关系呢?
符号是对某些类型的数据或代码(如全局变量或函数)的符号引用,函数名或变量名就是符号名。例如,printf()函数会在动态链接符号表.dynsym中存有一个指向该函数的符号项(以Elf_Sym数据结构表示)。在大多数共享库和动态链接可执行文件中,存在两个符号表。即.dynsym和.symtab。
.dynsym保存了引用来自外部文件符号的全局符号。如printf库函数。.dynsym保存的符号是.symtab所保存符合的子集,.symtab中还保存了可执行文件的本地符号。如全局变量,代码中定义的本地函数等。
既然.dynsym是.symtab的子集,那为何要同时存在两个符号表呢?
通过readelf -S命令可以查看可执行文件的输出,一部分节标志位(sh_flags)被标记为了A(ALLOC)、WA(WRITE/ALLOC)、AX(ALLOC/EXEC)。其中,.dynsym被标记为ALLOC,而.symtab则没有标记。
ALLOC表示有该标记的节会在运行时分配并装载进入内存,而.symtab不是在运行时必需的,因此不会被装载到内存中。.dynsym保存的符号只能在运行时被解析,因此是运行时动态链接器所需的唯一符号。.dynsym对于动态链接可执行文件的执行是必需的,而.symtab只是用来进行调试和链接的。
上图所示为通过符号表索引字符串表的示意图。符号表中的每一项都是一个Elf_Sym结构,对应可以在字符串表中索引得到一个字符串。该数据结构中成员的含义如下表所示:
成员 含义
st_name 符号名。该值为该符号名在字符串表中的偏移地址。
st_value 符号对应的值。存放符号的值(可能是地址或位置偏移量)。
st_size 符号的大小。
st_other 0
st_shndx 符号所在的节
st_info 符号类型及绑定属性
使用readelf工具我们也能够看到符号表的相关信息。
root@DESKTOP-90KJ7MS:/usr/src/helloworld# readelf -s helloSymbol table '.dynsym' contains 5 entries:Num: Value Size Type Bind Vis Ndx Name0: 00000000 0 NOTYPE LOCAL DEFAULT UND1: 00000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__2: 00000000 0 FUNC GLOBAL DEFAULT UND puts@GLIBC_2.4 (2)3: 00000000 0 FUNC GLOBAL DEFAULT UND abort@GLIBC_2.4 (2)4: 00000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.4 (2)Symbol table '.symtab' contains 102 entries:Num: Value Size Type Bind Vis Ndx Name0: 00000000 0 NOTYPE LOCAL DEFAULT UND1: 00010154 0 SECTION LOCAL DEFAULT 12: 00010168 0 SECTION LOCAL DEFAULT 23: 0001018c 0 SECTION LOCAL DEFAULT 34: 000101ac 0 SECTION LOCAL DEFAULT 45: 000101d8 0 SECTION LOCAL DEFAULT 56: 00010228 0 SECTION LOCAL DEFAULT 67: 0001026a 0 SECTION LOCAL DEFAULT 78: 00010274 0 SECTION LOCAL DEFAULT 89: 00010294 0 SECTION LOCAL DEFAULT 910: 0001029c 0 SECTION LOCAL DEFAULT 1011: 000102bc 0 SECTION LOCAL DEFAULT 1112: 000102c8 0 SECTION LOCAL DEFAULT 1213: 0001030c 0 SECTION LOCAL DEFAULT 1314: 00010480 0 SECTION LOCAL DEFAULT 1415: 00010488 0 SECTION LOCAL DEFAULT 1516: 0001049c 0 SECTION LOCAL DEFAULT 1617: 000104a4 0 SECTION LOCAL DEFAULT 1718: 00020f10 0 SECTION LOCAL DEFAULT 1819: 00020f14 0 SECTION LOCAL DEFAULT 1920: 00020f18 0 SECTION LOCAL DEFAULT 2021: 00021000 0 SECTION LOCAL DEFAULT 2122: 00021020 0 SECTION LOCAL DEFAULT 2223: 00021028 0 SECTION LOCAL DEFAULT 2324: 00000000 0 SECTION LOCAL DEFAULT 2425: 00000000 0 SECTION LOCAL DEFAULT 2526: 00000000 0 FILE LOCAL DEFAULT ABS /usr/lib/gcc-cross/arm-li27: 0001018c 0 NOTYPE LOCAL DEFAULT 3 $d28: 0001030c 0 NOTYPE LOCAL DEFAULT 13 $a29: 0001049c 0 NOTYPE LOCAL DEFAULT 16 $d30: 0001033c 0 NOTYPE LOCAL DEFAULT 13 $d31: 00010488 0 NOTYPE LOCAL DEFAULT 15 $d32: 00021020 0 NOTYPE LOCAL DEFAULT 22 $d33: 00000000 0 FILE LOCAL DEFAULT ABS /usr/lib/gcc-cross/arm-li34: 00010348 0 NOTYPE LOCAL DEFAULT 13 $a35: 00010348 0 FUNC LOCAL DEFAULT 13 call_weak_fn36: 00010364 0 NOTYPE LOCAL DEFAULT 13 $d37: 000102bc 0 NOTYPE LOCAL DEFAULT 11 $a38: 00010480 0 NOTYPE LOCAL DEFAULT 14 $a39: 00000000 0 FILE LOCAL DEFAULT ABS /usr/lib/gcc-cross/arm-li40: 000102c4 0 NOTYPE LOCAL DEFAULT 11 $a41: 00010484 0 NOTYPE LOCAL DEFAULT 14 $a42: 00000000 0 FILE LOCAL DEFAULT ABS crtstuff.c43: 0001036c 0 NOTYPE LOCAL DEFAULT 13 $a44: 0001036c 0 FUNC LOCAL DEFAULT 13 deregister_tm_clones45: 0001038c 0 NOTYPE LOCAL DEFAULT 13 $d46: 00010398 0 NOTYPE LOCAL DEFAULT 13 $a47: 00010398 0 FUNC LOCAL DEFAULT 13 register_tm_clones48: 000103c4 0 NOTYPE LOCAL DEFAULT 13 $d49: 00021024 0 NOTYPE LOCAL DEFAULT 22 $d50: 000103d0 0 NOTYPE LOCAL DEFAULT 13 $a51: 000103d0 0 FUNC LOCAL DEFAULT 13 __do_global_dtors_aux52: 000103f4 0 NOTYPE LOCAL DEFAULT 13 $d53: 00021028 1 OBJECT LOCAL DEFAULT 23 completed.1017254: 00020f14 0 NOTYPE LOCAL DEFAULT 19 $d55: 00020f14 0 OBJECT LOCAL DEFAULT 19 __do_global_dtors_aux_fin56: 000103f8 0 NOTYPE LOCAL DEFAULT 13 $a57: 000103f8 0 FUNC LOCAL DEFAULT 13 frame_dummy58: 00020f10 0 NOTYPE LOCAL DEFAULT 18 $d59: 00020f10 0 OBJECT LOCAL DEFAULT 18 __frame_dummy_init_array_60: 00021028 0 NOTYPE LOCAL DEFAULT 23 $d61: 00000000 0 FILE LOCAL DEFAULT ABS hello.c62: 0001048c 0 NOTYPE LOCAL DEFAULT 15 $d63: 000103fc 0 NOTYPE LOCAL DEFAULT 13 $a64: 00010418 0 NOTYPE LOCAL DEFAULT 13 $d65: 00000000 0 FILE LOCAL DEFAULT ABS elf-init.oS66: 0001041c 0 NOTYPE LOCAL DEFAULT 13 $a67: 00010474 0 NOTYPE LOCAL DEFAULT 13 $d68: 0001047c 0 NOTYPE LOCAL DEFAULT 13 $a69: 00000000 0 FILE LOCAL DEFAULT ABS crtstuff.c70: 000104a4 0 NOTYPE LOCAL DEFAULT 17 $d71: 000104a4 0 OBJECT LOCAL DEFAULT 17 __FRAME_END__72: 00000000 0 FILE LOCAL DEFAULT ABS73: 00020f14 0 NOTYPE LOCAL DEFAULT 18 __init_array_end74: 00020f18 0 OBJECT LOCAL DEFAULT 20 _DYNAMIC75: 00020f10 0 NOTYPE LOCAL DEFAULT 18 __init_array_start76: 00021000 0 OBJECT LOCAL DEFAULT 21 _GLOBAL_OFFSET_TABLE_77: 000102c8 0 NOTYPE LOCAL DEFAULT 12 $a78: 000102d8 0 NOTYPE LOCAL DEFAULT 12 $d79: 000102dc 0 NOTYPE LOCAL DEFAULT 12 $a80: 0001047c 4 FUNC GLOBAL DEFAULT 13 __libc_csu_fini81: 00021020 0 NOTYPE WEAK DEFAULT 22 data_start82: 00021028 0 NOTYPE GLOBAL DEFAULT 23 __bss_start__83: 0002102c 0 NOTYPE GLOBAL DEFAULT 23 _bss_end__84: 00021028 0 NOTYPE GLOBAL DEFAULT 22 _edata85: 00010480 0 FUNC GLOBAL HIDDEN 14 _fini86: 0002102c 0 NOTYPE GLOBAL DEFAULT 23 __bss_end__87: 00021020 0 NOTYPE GLOBAL DEFAULT 22 __data_start88: 00000000 0 FUNC GLOBAL DEFAULT UND puts@@GLIBC_2.489: 00000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@@GLIBC_90: 00000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__91: 00021024 0 OBJECT GLOBAL HIDDEN 22 __dso_handle92: 00010488 4 OBJECT GLOBAL DEFAULT 15 _IO_stdin_used93: 0001041c 96 FUNC GLOBAL DEFAULT 13 __libc_csu_init94: 0002102c 0 NOTYPE GLOBAL DEFAULT 23 _end95: 0001030c 0 FUNC GLOBAL DEFAULT 13 _start96: 0002102c 0 NOTYPE GLOBAL DEFAULT 23 __end__97: 00021028 0 NOTYPE GLOBAL DEFAULT 23 __bss_start98: 000103fc 32 FUNC GLOBAL DEFAULT 13 main99: 00021028 0 OBJECT GLOBAL HIDDEN 22 __TMC_END__100: 00000000 0 FUNC GLOBAL DEFAULT UND abort@@GLIBC_2.4101: 000102bc 0 FUNC GLOBAL HIDDEN 11 _init
字符串表
类似于符号表,在大多数共享库和动态链接可执行文件中,也存在两个字符串表。即.dynstr和.strtab,分别对应于.dynsym和symtab。此外,还有一个.shstrtab的节头字符串表,用于保存节头表中用到的字符串,可通过sh_name进行索引。
ELF文件中所有字符表的结构基本一致,如上图所示。
重定位表
重定位就是将符号定义和符号引用进行连接的过程。可重定位文件需要包含描述如何修改节内容的相关信息,从而使可执行文件和共享目标文件能够保存进程的程序镜像所需要的正确信息。
重定位表是进行重定位的重要依据。我们可以使用objdump工具查看目标文件的重定位表:
root@DESKTOP-90KJ7MS:/usr/src/helloworld# readelf -r helloRelocation section '.rel.dyn' at offset 0x294 contains 1 entry:Offset Info Type Sym.Value Sym. Name
0002101c 00000115 R_ARM_GLOB_DAT 00000000 __gmon_start__Relocation section '.rel.plt' at offset 0x29c contains 4 entries:Offset Info Type Sym.Value Sym. Name
0002100c 00000216 R_ARM_JUMP_SLOT 00000000 puts@GLIBC_2.4
00021010 00000416 R_ARM_JUMP_SLOT 00000000 __libc_start_main@GLIBC_2.4
00021014 00000116 R_ARM_JUMP_SLOT 00000000 __gmon_start__
00021018 00000316 R_ARM_JUMP_SLOT 00000000 abort@GLIBC_2.4
重定位表是一个Elf_Rel类型的数组结构,每一项对应一个需要进行重定位的项。 其成员含义如下表所示:
成员 含义
r_offset 重定位入口的偏移。
对于可重定位文件来说,这个值是该重定位入口所要修正的位置的第一个字节相对于节起始的偏移
对于可执行文件或共享对象文件来说,这个值是该重定位入口所要修正的位置的第一个字节的虚拟地址
r_info 重定位入口的类型和符号
因为不同处理器的指令系统不一样,所以重定位所要修正的指令地址格式也不一样。每种处理器都有自己的一套重定位入口的类型。
对于可执行文件和共享目标文件来说,它们的重定位入口是动态链接类型的。
重定位是目标文件链接成为可执行文件的关键。我们将在后面的进行介绍。
相关文章:

Linux可执行文件ELF文件结构
目标文件格式 编译器编译源代码后生成的文件叫做目标文件,而目标文件经过编译器链接之后得到的就是可执行文件。那么目标文件到底是什么?它和可执行文件又有什么区别?链接到底又做了什么呢?接下来,我们将探索一下目标…...
RAG:大模型微调的革命性增强——检索增强生成技术深度解析
RAG:大模型微调的革命性增强——检索增强生成技术深度解析 当大模型遇到知识瓶颈,RAG(检索增强生成)为模型装上"外部记忆库",让静态知识库与动态生成能力完美融合。本文将深入拆解RAG的技术原理、微调策略及…...

DisplayPort 2.0协议介绍(1)
最近开始学习DisplayPort 2.0协议,相比于DP1.4a,最主要的是速率提升到了10Gbps/lane,还有就是128b/132b编码方式的修改。至于速率13.5Gbps和20Gbps还只是可选项,在DP2.1协议才成为必须支持选项。 那在实现技术细节上有哪些变化呢…...

I2C通信讲解
I2C总线发展史 怎么在一条串口线上连接多个设备呢? 由于速度同步线是由主机实时发出的,所以主机可以按需求修改通信速度,这样在一条线上可以挂接不同速度的器件,单片机和性能差的器件通信,就输出较慢的脉冲信号&#x…...
【信息系统项目管理师-选择真题】2025上半年(第一批)综合知识答案和详解
更多内容请见: 备考信息系统项目管理师-专栏介绍和目录 文章目录 【第1题】【第2题】【第3题】【第4题】【第5题】【第6题】【第7题】【第8题】【第9题】【第10题】【第11题】【第12题】【第13题】【第14题】【第15题】【第16题】【第17题】【第18题】【第19题】【第20题】【第…...
ABP VNext 在 Kubernetes 中的零停机蓝绿发布
ABP VNext 在 Kubernetes 中的零停机蓝绿发布 🚀 📚 目录 ABP VNext 在 Kubernetes 中的零停机蓝绿发布 🚀📌 一、前提准备 ℹ️🧱 二、项目结构与目标 🎯🐳 三、多阶段 Dockerfile 构建 &#…...
linux 故障处置通用流程-36计-14-27
014:查看系统主要日志 查看以下日志: 主要查以下关键字 error/NIC/fs /"link down"/Oout of memory" /var/log/messages /var/log/dmesg 015:主机通讯是否延迟 执行命令: #ping 网关_IP #ping 关联主机_IP …...
https和http有什么区别-http各个版本有什么区别
http和 https的区别 HTTP(超文本传输协议)和 HTTPS(安全超文本传输协议)是两种用于在网络上传输数据的协议,它们的主要区别在于安全性: HTTP(Hypertext Transfer Protocol)&#x…...

基于回归算法的心理健康预测(EDA + 预测)
心理健康涵盖情感、心理与社会福祉,影响认知、情绪和行为模式,决定压力应对、人际交往及健康决策,且在生命各阶段(从童年至成年)均至关重要。心理健康与身体健康同为整体健康的核心要素:抑郁会增加糖尿病、…...
React Native开发鸿蒙运动健康类应用的项目实践记录
项目名称:HarmonyFitness - 基于React Native的鸿蒙运动健康应用 技术栈:React Native 0.72.5 TypeScript HarmonyOS API ArkTS原生模块 一、环境搭建与项目初始化 双环境配置 React Native环境: npx re…...

【新品解读】一板多能,AXRF49 定义新一代 RFSoC FPGA 开发平台
“硬件系统庞杂、调试周期长” “高频模拟前端不稳定,影响采样精度” “接收和发射链路难以同步,难以扩展更多通道” “数据流量大,处理与存储跟不上” 这些是大部分客户在构建多通道、高频宽的射频采样链路时,面临的主要问题。…...

贪心算法应用:线性规划贪心舍入问题详解
贪心算法应用:线性规划贪心舍入问题详解 贪心算法是一种在每一步选择中都采取当前状态下最优的选择,从而希望导致结果是全局最优的算法策略。在线性规划问题中,贪心算法特别是贪心舍入技术有着广泛的应用。下面我将全面详细地讲解这一主题。…...

YOLO在C#中的完整训练、验证与部署方案
YOLO在C#中的完整训练、验证与部署方案 C# 在 YOLO 部署上优势明显(高性能、易集成),但训练能力较弱,通常需结合 Python 实现。若项目对开发效率要求高且不依赖 C# 生态,建议全程使用 Python;若需深度集成…...
洛谷题目:P2761 软件补丁问题 (本题简单)
个人介绍: 题目传送门: P2761 软件补丁问题 - 洛谷 (luogu.com.cn) 前言: 这道题是一个典型的状态搜索问题,核心目标就是利用给定d额多个补丁程序,将包含若干错误的软件修复成没有错误的状态,并且要使得修复过程当中的总耗时最少。下面是小亦为大家阐述滴思路: 1、状态…...

智慧园区数字孪生全链交付方案:降本增效30%,多案例实践驱动全周期交付
在智慧园区建设浪潮中,数字孪生技术正成为破解传统园区管理难题的核心引擎。通过构建与物理园区1:1映射的数字模型,实现数据集成、状态同步与智能决策,智慧园区数字孪生全链交付方案已在多个项目中验证其降本增效价值——某物流园区通过该方案…...

【OpenGL学习】(四)统一着色和插值着色
文章目录 【OpenGL学习】(四)统一着色和插值着色统一着色(Flat/Uniform Shading)插值着色(Interpolated Shading) 【OpenGL学习】(四)统一着色和插值着色 着色器介绍: h…...
42、响应处理-【源码分析】-浏览器与PostMan内容协商完全适配
42、响应处理源码分析浏览器与PostMan内容协商完全适配 要实现浏览器与PostMan在内容协商上的完全适配,需要在Spring Boot应用中自定义内容协商策略,确保服务器能根据浏览器和PostMan的请求头正确返回合适格式的数据。以下是详细的步骤: ### …...

在 CentOS 上安装 Docker 和 Docker Compose 并配置使用国内镜像源
在 CentOS 上安装 Docker 和 Docker Compose 并配置使用国内镜像源,可以加速镜像下载速度。以下是详细的步骤: 一、安装 Docker 移除旧版本的 Docker(如果有): sudo yum remove docker \docker-client \docker-client…...
Java Lambda表达式深度解析:从入门到实战
简介 Lambda表达式是Java 8引入的最重要特性之一,它极大地简化了Java代码的编写方式,使函数式编程风格在Java中成为可能。本文将全面介绍Lambda表达式的概念、语法、应用场景以及与相关特性的配合使用,帮助开发者掌握这一强大的编程工具。 一、Lambda表达式基础 1.1 什么…...

Docker慢慢学
1、Docker DeskTop 2、N8N下载 docker run -p 8888:5678 n8nio/n8n 3、Kafka kafka依赖zookeeper,先启动zookeeper docker pull zookeeper docker run -d --name zookeeper -p 2181:2181 -e ALLOW_ANONYMOUS_LOGINyes zookeeper 启动kafka docker pull confluentinc/cp…...

cursor-free-vip使用
一、项目简介 Cursor-Free-VIP 是一个开源项目,旨在帮助用户免费使用 Cursor AI 的高级功能。它通过自动注册 Cursor 账号、重置机器 ID 和完成 Auth 验证等操作,解决 Cursor AI 中常见的限制提示。 二、系统准备 1…cursor需要更新到最新的版本 三、…...

使用SSH tunnel访问内网的MySQL
文章目录 环境背景方法参考 注:本文是使用SSH tunnel做端口转发的一个示例。有关SSH端口转发,可参考我的几篇文档 https://blog.csdn.net/duke_ding2/article/details/106878081https://blog.csdn.net/duke_ding2/article/details/135627263https://blo…...

Redis持久化模式RDB与AOF
RDB持久化 RDB也被叫做Redis数据快照。简单来说就是把内存中的所有数据记录到磁盘中。当Redis实例故障重启后重磁盘中读取快照文件进行数据恢复(快照文件默认保存在当前运行目录); 演示Redis正常停机自动执行一次RDB操作 配置Redis触发RDB机制 RDB其它配置也可在red…...
【JS进阶】ES5 实现继承的几种方式
ES5 实现继承的几种方式 1. 原型链继承(Prototype Chaining) function Parent(name) {this.name name || Parent;this.colors [red, blue]; }Parent.prototype.sayName function() {console.log(this.name); };function Child() {}// 关键ÿ…...

【数据结构】树形结构--二叉树(二)
【数据结构】树形结构--二叉树(二) 一.二叉树的实现1.求二叉树结点的个数2.求二叉树叶子结点的个数3.求二叉树第k层结点的个数4.求二叉树的深度(高度)5.在二叉树中查找值为x的结点6.判断二叉树是否为完全二叉树7.二叉树的销毁 一.…...
JavaScript性能优化实战:深入探讨JavaScript性能瓶颈与优化技巧
引言:为什么JavaScript性能至关重要 在现代Web开发中,JavaScript已成为构建交互式应用程序的核心技术。随着单页应用(SPA)和复杂前端架构的普及,JavaScript代码的性能直接影响用户体验、转化率甚至搜索引擎排名。研究表明,页面加载时间每增加1秒,转化率可能下降7%,而性能…...
在 CentOS 上将 Ansible 项目推送到 GitHub 的完整指南
1. 安装 Git 在 CentOS 中使用 yum 安装 Git,Git 是管理代码版本控制的工具: sudo yum install git -y 2. 配置 Git 用户信息 设置你的 Git 用户名和邮箱,这些信息会出现在你每次提交的记录中: git config --global user.nam…...

深度学习题目1
梯度下降法的正确步骤是什么? a.计算预测值和真实值之间的误差 b.重复迭代,直至得到网络权重的最佳值 c.把输入传入网络,得到输出值 d.用随机值初始化权重和偏差 e.对每一个产生误差的神经元,调整相应的(权重ÿ…...
Spring @Scheduled vs XXL-JOB vs DolphinScheduler vs Airflow:任务调度框架全景对比
引言 从单机定时任务到分布式工作流调度,不同场景需要选择匹配的调度框架。 本文对比 Spring Scheduled、XXL-JOB、DolphinScheduler (海豚调度器)和 Apache Airflow 的核心差异,助你避免过度设计或功能不足。 一、核心定位与适用…...

【Oracle】锁
个人主页:Guiat 归属专栏:Oracle 文章目录 1. 锁基础概述1.1 锁的概念与作用1.2 锁的工作原理1.3 Oracle锁的分类 2. 行级锁 (Row-Level Locks)2.1 行级锁的基本概念2.1.1 TX锁(事务锁)2.1.2 行级锁的工作机制 2.2 行级锁的类型2.…...