ELF应用程序二进制接口

ELF应用程序二进制接口

荣涛
2021年11月2日

文档修改日志

日期修改内容修改人备注
2021年11月2日创建荣涛摘自https://docs.oracle.com/
2021年11月2日添加R_AARCH64_xx荣涛摘自binutils-gdb
2021年11月4日init_module荣涛init_module(2)

ELF 应用程序二进制接口

通用 ELF 格式由 System V 应用程序二进制接口定义。该参考文档包含通用版本中的信息,并使用 Oracle Solaris 中的扩展内容进行了扩充。

目标文件格式

本章介绍由汇编程序和链接编辑器生成的目标文件的可执行链接格式 (Excutable and Linking Format, ELF)。存在三种重要类型的目标文件。

  • 可重定位目标文件包含代码节和数据节。此文件适合与其他可重定位目标文件链接,从而创建动态可执行文件、共享目标文件或其他可重定位目标文件。
  • 动态可执行文件包含可随时执行的程序。此文件指定了 exec(2) 创建程序的进程映像的方式。此文件通常在运行时绑定到共享目标文件以创建进程映像。
  • 共享目标文件文件包含适用于进行其他链接的代码和数据。链接编辑器可将此文件与其他可重定位目标文件和共享目标文件一起处理,以创建其他目标文件。运行时链接程序会将此文件与动态可执行文件和其他共享目标文件合并,以创建进程映像。

程序可以使用由 ELF 访问库 libelf 提供的函数处理目标文件。有关 libelf 内容的说明,请参阅 elf(3ELF)。/usr/demo/ELF 目录下的 pkg:/solaris/source/demo/system 软件包中提供了使用 libelf 的源代码样例。

文件格式

目标文件既参与程序链接,又参与程序执行。为了方便和提高效率,目标文件格式提供了文件内容的平行视图,以反映这些活动的不同需要。下图显示了目标文件的结构。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UCB3HR5k-1636806374038)(_v_images/20211102110206867_13981.png)]

ELF 头位于目标文件的起始位置,其中包含用于说明文件结构的指南。

注 - 只有 ELF 头在文件中具有固定位置。由于 ELF 格式的灵活性,不要求头表、节或段具有指定的顺序。但是,此图是 Oracle Solaris OS 中使用的典型布局。

节表示 ELF 文件中可以处理的最小不可分割单位。段是节的集合。段表示可由 exec(2) 或运行时链接程序映射到内存映像的最小独立单元。

节包含链接视图的批量目标文件信息。此数据包括指令、数据、符号表和重定位信息。本章的第一部分提供了各节的说明。本章的第二部分讨论了文件的各段及程序执行视图。

程序头表(如果存在)指示系统如何创建进程映像。用于生成进程映像、可执行文件和共享目标文件的文件必须具有程序头表。可重定位目标文件无需程序头表。

节头表包含说明文件各节的信息。每节在表中有一个与之对应的项。每一项都指定了节名称和节大小之类的信息。链接编辑过程中使用的文件必须具有节头表。

数据表示形式

目标文件格式支持 8 位字节、32 位体系结构和 64 位体系结构的各种处理器。不过,数据表示形式最好可扩展为更大或更小的体系结构。下表列出了 32 位数据类型和 64 位数据类型。

目标文件表示格式与计算机无关的一些控制数据。此格式可提供目标文件的通用标识和解释。目标文件中的其余数据使用目标处理器的编码,无论在什么计算机上创建该文件都是如此。

表1-1 ELF 32 位数据类型

名称大小对齐目的
Elf32_Addr44无符号程序地址
Elf32_Half22无符号中整数
Elf32_Off44无符号文件偏移
Elf32_Sword44有符号整数
Elf32_Word44无符号整数
unsigned char11无符号小整数

表1-2 ELF 64 位数据类型

名称大小对齐目的
Elf64_Addr84无符号程序地址
Elf64_Half22无符号中整数
Elf64_Off88无符号文件偏移
Elf64_Sword44有符号整数
Elf64_Word44无符号整数
Elf64_XWord88无符号长整数
Elf64_SxWord88有符号长整数
unsigned char11无符号小整数

目标文件格式定义的所有数据结构都遵循相关类的自然大小和对齐规则。数据结构可以包含显式填充,以确保 4 字节目标的 4 字节对齐,从而强制结构大小为 4 的倍数,依此类推。数据在文件的开头也会适当对齐。例如,包含 Elf32_Addr 成员的结构在文件中与 4 字节边界对齐。同样,包含 Elf64_Addr 成员的结构与 8 字节边界对齐。

注 - 为便于移植,ELF 不使用位字段。

ELF 头

目标文件中的一些控制结构可以增大,因为 ELF 头包含这些控制结构的实际大小。如果目标文件格式发生变化,则程序可能会遇到大于或小于预期大小的控制结构。因此,程序可能会忽略额外的信息。这些忽略的信息的处理方式取决于上下文,如果定义了扩展内容,则会指定处理方式。

ELF 头具有以下结构。请参见 elf.h

#define EI_NIDENT (16)

typedef struct
{
  unsigned char	e_ident[EI_NIDENT];	/* Magic number and other info */
  Elf32_Half	e_type;			/* Object file type */
  Elf32_Half	e_machine;		/* Architecture */
  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;

typedef struct
{
  unsigned char	e_ident[EI_NIDENT];	/* Magic number and other info */
  Elf64_Half	e_type;			/* Object file type */
  Elf64_Half	e_machine;		/* Architecture */
  Elf64_Word	e_version;		/* Object file version */
  Elf64_Addr	e_entry;		/* Entry point virtual address */
  Elf64_Off	e_phoff;		/* Program header table file offset */
  Elf64_Off	e_shoff;		/* Section header table file offset */
  Elf64_Word	e_flags;		/* Processor-specific flags */
  Elf64_Half	e_ehsize;		/* ELF header size in bytes */
  Elf64_Half	e_phentsize;		/* Program header table entry size */
  Elf64_Half	e_phnum;		/* Program header table entry count */
  Elf64_Half	e_shentsize;		/* Section header table entry size */
  Elf64_Half	e_shnum;		/* Section header table entry count */
  Elf64_Half	e_shstrndx;		/* Section header string table index */
} Elf64_Ehdr;

e_ident

将文件标记为目标文件的初始字节。这些字节提供与计算机无关的数据,用于解码和解释文件的内容。ELF 标识中提供了完整说明。

e_type

标识目标文件类型,如下表中所列。

名称含义
ET_NONE0无文件类型
ET_REL1可重定位文件
ET_EXEC2可执行文件
ET_DYN3共享目标文件
ET_CORE4核心文件
ET_LOOS0xfe00特定于操作系统
ET_HIOS0xfeff特定于操作系统
ET_LOPROC0xff00特定于处理器
ET_HIPROC0xffff特定于处理器
/* Legal values for e_type (object file type).  */

#define ET_NONE     0       /* No file type */                                                                              
#define ET_REL      1       /* Relocatable file */
#define ET_EXEC     2       /* Executable file */
#define ET_DYN      3       /* Shared object file */
#define ET_CORE     4       /* Core file */
#define ET_NUM      5       /* Number of defined types */
#define ET_LOOS     0xfe00      /* OS-specific range start */
#define ET_HIOS     0xfeff      /* OS-specific range end */
#define ET_LOPROC   0xff00      /* Processor-specific range start */
#define ET_HIPROC   0xffff      /* Processor-specific range end */

虽然未指定核心文件内容,但类型 ET_CORE 保留用于标记文件。从 ET_LOPROC 到 ET_HIPROC 之间的值(包括这两个值)保留用于特定于处理器的语义。其他值保留供将来使用。

e_machine

指定独立文件所需的体系结构。下表中列出了相关体系结构。

/* Legal values for e_machine (architecture).  */

#define EM_NONE		 0	/* No machine */
#define EM_M32		 1	/* AT&T WE 32100 */
#define EM_SPARC	 2	/* SUN SPARC */
#define EM_386		 3	/* Intel 80386 */
#define EM_68K		 4	/* Motorola m68k family */
#define EM_88K		 5	/* Motorola m88k family */
#define EM_IAMCU	 6	/* Intel MCU */
#define EM_860		 7	/* Intel 80860 */
#define EM_MIPS		 8	/* MIPS R3000 big-endian */
#define EM_S370		 9	/* IBM System/370 */
#define EM_MIPS_RS3_LE	10	/* MIPS R3000 little-endian */
				/* reserved 11-14 */
#define EM_PARISC	15	/* HPPA */
				/* reserved 16 */
#define EM_VPP500	17	/* Fujitsu VPP500 */
#define EM_SPARC32PLUS	18	/* Sun's "v8plus" */
#define EM_960		19	/* Intel 80960 */
#define EM_PPC		20	/* PowerPC */
#define EM_PPC64	21	/* PowerPC 64-bit */
#define EM_S390		22	/* IBM S390 */
#define EM_SPU		23	/* IBM SPU/SPC */
				/* reserved 24-35 */
#define EM_V800		36	/* NEC V800 series */
#define EM_FR20		37	/* Fujitsu FR20 */
#define EM_RH32		38	/* TRW RH-32 */
#define EM_RCE		39	/* Motorola RCE */
#define EM_ARM		40	/* ARM */
#define EM_FAKE_ALPHA	41	/* Digital Alpha */
#define EM_SH		42	/* Hitachi SH */
#define EM_SPARCV9	43	/* SPARC v9 64-bit */
#define EM_TRICORE	44	/* Siemens Tricore */
#define EM_ARC		45	/* Argonaut RISC Core */
#define EM_H8_300	46	/* Hitachi H8/300 */
#define EM_H8_300H	47	/* Hitachi H8/300H */
#define EM_H8S		48	/* Hitachi H8S */
#define EM_H8_500	49	/* Hitachi H8/500 */
#define EM_IA_64	50	/* Intel Merced */
#define EM_MIPS_X	51	/* Stanford MIPS-X */
#define EM_COLDFIRE	52	/* Motorola Coldfire */
#define EM_68HC12	53	/* Motorola M68HC12 */
#define EM_MMA		54	/* Fujitsu MMA Multimedia Accelerator */
#define EM_PCP		55	/* Siemens PCP */
#define EM_NCPU		56	/* Sony nCPU embeeded RISC */
#define EM_NDR1		57	/* Denso NDR1 microprocessor */
#define EM_STARCORE	58	/* Motorola Start*Core processor */
#define EM_ME16		59	/* Toyota ME16 processor */
#define EM_ST100	60	/* STMicroelectronic ST100 processor */
#define EM_TINYJ	61	/* Advanced Logic Corp. Tinyj emb.fam */
#define EM_X86_64	62	/* AMD x86-64 architecture */
#define EM_PDSP		63	/* Sony DSP Processor */
#define EM_PDP10	64	/* Digital PDP-10 */
#define EM_PDP11	65	/* Digital PDP-11 */
#define EM_FX66		66	/* Siemens FX66 microcontroller */
#define EM_ST9PLUS	67	/* STMicroelectronics ST9+ 8/16 mc */
#define EM_ST7		68	/* STmicroelectronics ST7 8 bit mc */
#define EM_68HC16	69	/* Motorola MC68HC16 microcontroller */
#define EM_68HC11	70	/* Motorola MC68HC11 microcontroller */
#define EM_68HC08	71	/* Motorola MC68HC08 microcontroller */
#define EM_68HC05	72	/* Motorola MC68HC05 microcontroller */
#define EM_SVX		73	/* Silicon Graphics SVx */
#define EM_ST19		74	/* STMicroelectronics ST19 8 bit mc */
#define EM_VAX		75	/* Digital VAX */
#define EM_CRIS		76	/* Axis Communications 32-bit emb.proc */
#define EM_JAVELIN	77	/* Infineon Technologies 32-bit emb.proc */
#define EM_FIREPATH	78	/* Element 14 64-bit DSP Processor */
#define EM_ZSP		79	/* LSI Logic 16-bit DSP Processor */
#define EM_MMIX		80	/* Donald Knuth's educational 64-bit proc */
#define EM_HUANY	81	/* Harvard University machine-independent object files */
#define EM_PRISM	82	/* SiTera Prism */
#define EM_AVR		83	/* Atmel AVR 8-bit microcontroller */
#define EM_FR30		84	/* Fujitsu FR30 */
#define EM_D10V		85	/* Mitsubishi D10V */
#define EM_D30V		86	/* Mitsubishi D30V */
#define EM_V850		87	/* NEC v850 */
#define EM_M32R		88	/* Mitsubishi M32R */
#define EM_MN10300	89	/* Matsushita MN10300 */
#define EM_MN10200	90	/* Matsushita MN10200 */
#define EM_PJ		91	/* picoJava */
#define EM_OPENRISC	92	/* OpenRISC 32-bit embedded processor */
#define EM_ARC_COMPACT	93	/* ARC International ARCompact */
#define EM_XTENSA	94	/* Tensilica Xtensa Architecture */
#define EM_VIDEOCORE	95	/* Alphamosaic VideoCore */
#define EM_TMM_GPP	96	/* Thompson Multimedia General Purpose Proc */
#define EM_NS32K	97	/* National Semi. 32000 */
#define EM_TPC		98	/* Tenor Network TPC */
#define EM_SNP1K	99	/* Trebia SNP 1000 */
#define EM_ST200	100	/* STMicroelectronics ST200 */
#define EM_IP2K		101	/* Ubicom IP2xxx */
#define EM_MAX		102	/* MAX processor */
#define EM_CR		103	/* National Semi. CompactRISC */
#define EM_F2MC16	104	/* Fujitsu F2MC16 */
#define EM_MSP430	105	/* Texas Instruments msp430 */
#define EM_BLACKFIN	106	/* Analog Devices Blackfin DSP */
#define EM_SE_C33	107	/* Seiko Epson S1C33 family */
#define EM_SEP		108	/* Sharp embedded microprocessor */
#define EM_ARCA		109	/* Arca RISC */
#define EM_UNICORE	110	/* PKU-Unity & MPRC Peking Uni. mc series */
#define EM_EXCESS	111	/* eXcess configurable cpu */
#define EM_DXP		112	/* Icera Semi. Deep Execution Processor */
#define EM_ALTERA_NIOS2 113	/* Altera Nios II */
#define EM_CRX		114	/* National Semi. CompactRISC CRX */
#define EM_XGATE	115	/* Motorola XGATE */
#define EM_C166		116	/* Infineon C16x/XC16x */
#define EM_M16C		117	/* Renesas M16C */
#define EM_DSPIC30F	118	/* Microchip Technology dsPIC30F */
#define EM_CE		119	/* Freescale Communication Engine RISC */
#define EM_M32C		120	/* Renesas M32C */
				/* reserved 121-130 */
#define EM_TSK3000	131	/* Altium TSK3000 */
#define EM_RS08		132	/* Freescale RS08 */
#define EM_SHARC	133	/* Analog Devices SHARC family */
#define EM_ECOG2	134	/* Cyan Technology eCOG2 */
#define EM_SCORE7	135	/* Sunplus S+core7 RISC */
#define EM_DSP24	136	/* New Japan Radio (NJR) 24-bit DSP */
#define EM_VIDEOCORE3	137	/* Broadcom VideoCore III */
#define EM_LATTICEMICO32 138	/* RISC for Lattice FPGA */
#define EM_SE_C17	139	/* Seiko Epson C17 */
#define EM_TI_C6000	140	/* Texas Instruments TMS320C6000 DSP */
#define EM_TI_C2000	141	/* Texas Instruments TMS320C2000 DSP */
#define EM_TI_C5500	142	/* Texas Instruments TMS320C55x DSP */
#define EM_TI_ARP32	143	/* Texas Instruments App. Specific RISC */
#define EM_TI_PRU	144	/* Texas Instruments Prog. Realtime Unit */
				/* reserved 145-159 */
#define EM_MMDSP_PLUS	160	/* STMicroelectronics 64bit VLIW DSP */
#define EM_CYPRESS_M8C	161	/* Cypress M8C */
#define EM_R32C		162	/* Renesas R32C */
#define EM_TRIMEDIA	163	/* NXP Semi. TriMedia */
#define EM_QDSP6	164	/* QUALCOMM DSP6 */
#define EM_8051		165	/* Intel 8051 and variants */
#define EM_STXP7X	166	/* STMicroelectronics STxP7x */
#define EM_NDS32	167	/* Andes Tech. compact code emb. RISC */
#define EM_ECOG1X	168	/* Cyan Technology eCOG1X */
#define EM_MAXQ30	169	/* Dallas Semi. MAXQ30 mc */
#define EM_XIMO16	170	/* New Japan Radio (NJR) 16-bit DSP */
#define EM_MANIK	171	/* M2000 Reconfigurable RISC */
#define EM_CRAYNV2	172	/* Cray NV2 vector architecture */
#define EM_RX		173	/* Renesas RX */
#define EM_METAG	174	/* Imagination Tech. META */
#define EM_MCST_ELBRUS	175	/* MCST Elbrus */
#define EM_ECOG16	176	/* Cyan Technology eCOG16 */
#define EM_CR16		177	/* National Semi. CompactRISC CR16 */
#define EM_ETPU		178	/* Freescale Extended Time Processing Unit */
#define EM_SLE9X	179	/* Infineon Tech. SLE9X */
#define EM_L10M		180	/* Intel L10M */
#define EM_K10M		181	/* Intel K10M */
				/* reserved 182 */
#define EM_AARCH64	183	/* ARM AARCH64 */
				/* reserved 184 */
#define EM_AVR32	185	/* Amtel 32-bit microprocessor */
#define EM_STM8		186	/* STMicroelectronics STM8 */
#define EM_TILE64	187	/* Tileta TILE64 */
#define EM_TILEPRO	188	/* Tilera TILEPro */
#define EM_MICROBLAZE	189	/* Xilinx MicroBlaze */
#define EM_CUDA		190	/* NVIDIA CUDA */
#define EM_TILEGX	191	/* Tilera TILE-Gx */
#define EM_CLOUDSHIELD	192	/* CloudShield */
#define EM_COREA_1ST	193	/* KIPO-KAIST Core-A 1st gen. */
#define EM_COREA_2ND	194	/* KIPO-KAIST Core-A 2nd gen. */
#define EM_ARC_COMPACT2	195	/* Synopsys ARCompact V2 */
#define EM_OPEN8	196	/* Open8 RISC */
#define EM_RL78		197	/* Renesas RL78 */
#define EM_VIDEOCORE5	198	/* Broadcom VideoCore V */
#define EM_78KOR	199	/* Renesas 78KOR */
#define EM_56800EX	200	/* Freescale 56800EX DSC */
#define EM_BA1		201	/* Beyond BA1 */
#define EM_BA2		202	/* Beyond BA2 */
#define EM_XCORE	203	/* XMOS xCORE */
#define EM_MCHP_PIC	204	/* Microchip 8-bit PIC(r) */
				/* reserved 205-209 */
#define EM_KM32		210	/* KM211 KM32 */
#define EM_KMX32	211	/* KM211 KMX32 */
#define EM_EMX16	212	/* KM211 KMX16 */
#define EM_EMX8		213	/* KM211 KMX8 */
#define EM_KVARC	214	/* KM211 KVARC */
#define EM_CDP		215	/* Paneve CDP */
#define EM_COGE		216	/* Cognitive Smart Memory Processor */
#define EM_COOL		217	/* Bluechip CoolEngine */
#define EM_NORC		218	/* Nanoradio Optimized RISC */
#define EM_CSR_KALIMBA	219	/* CSR Kalimba */
#define EM_Z80		220	/* Zilog Z80 */
#define EM_VISIUM	221	/* Controls and Data Services VISIUMcore */
#define EM_FT32		222	/* FTDI Chip FT32 */
#define EM_MOXIE	223	/* Moxie processor */
#define EM_AMDGPU	224	/* AMD GPU */
				/* reserved 225-242 */
#define EM_RISCV	243	/* RISC-V */

#define EM_BPF		247	/* Linux BPF -- in-kernel virtual machine */

#define EM_NUM		248

其他值保留供将来使用。特定于处理器的 ELF 名称通过使用计算机名来进行区分。例如,为 e_flags 定义的标志使用前缀 EF_。EM_XYZ 计算机的名为 WIDGET 的标志称为 EF_XYZ_WIDGET。

e_version

标识目标文件版本,如下表中所列。

/* Legal values for e_version (version).  */

#define EV_NONE     0       /* Invalid ELF version */                                                                       
#define EV_CURRENT  1       /* Current version */
#define EV_NUM      2

值 1 表示原始文件格式。EV_CURRENT 的值可根据需要进行更改,以反映当前版本号。

e_entry

虚拟地址,系统首先将控制权转移到该地址,进而启动进程。如果文件没有关联的入口点,则此成员值为零。

e_phoff

程序头表的文件偏移(以字节为单位)。如果文件没有程序头表,则此成员值为零。

e_shoff

节头表的文件偏移(以字节为单位)。如果文件没有节头表,则此成员值为零。

e_flags

与文件关联的特定于处理器的标志。标志名称采用 EF_machine_flag 形式。对于 x86,此成员目前为零。下表中列出了 SPARC 标志。

/* Processor specific flags for the ELF header e_flags field.  */
#define EF_ARM_RELEXEC      0x01
#define EF_ARM_HASENTRY     0x02
#define EF_ARM_INTERWORK    0x04
#define EF_ARM_APCS_26      0x08
#define EF_ARM_APCS_FLOAT   0x10
#define EF_ARM_PIC      0x20
#define EF_ARM_ALIGN8       0x40 /* 8-bit structure alignment is in use */
#define EF_ARM_NEW_ABI      0x80
#define EF_ARM_OLD_ABI      0x100
#define EF_ARM_SOFT_FLOAT   0x200
#define EF_ARM_VFP_FLOAT    0x400
#define EF_ARM_MAVERICK_FLOAT   0x800

#define EF_ARM_ABI_FLOAT_SOFT   0x200   /* NB conflicts with EF_ARM_SOFT_FLOAT */                                           
#define EF_ARM_ABI_FLOAT_HARD   0x400   /* NB conflicts with EF_ARM_VFP_FLOAT */

e_ehsize

ELF 头的大小(以字节为单位)。

e_phentsize

文件的程序头表中某一项的大小(以字节为单位)。所有项的大小都相同。

e_phnum

程序头表中的项数。e_phentsize 和 e_phnum 的积指定了表的大小(以字节为单位)。如果文件没有程序头表,则 e_phnum 值为零。

如果程序头的数量大于或等于 PN_XNUM (0xffff),则此成员的值为 PN_XNUM (0xffff)。程序头表的实际项数包含在节头中索引为 0 的 sh_info 字段中。否则,初始节头项的 sh_info 成员值为零。请参见表 12-6表 12-7

e_shentsize

节头的大小(以字节为单位)。节头是节头表中的一项。所有项的大小都相同。

e_shnum

节头表中的项数。e_shentsize 和 e_shnum 的积指定了节头表的大小(以字节为单位)。如果文件没有节头表,则 e_shnum 值为零。

如果节数大于或等于 SHN_LORESERVE (0xff00),则 e_shnum 值为零。节头表的实际项数包含在节头中索引为 0 的 sh_size 字段中。否则,初始节头项的 sh_size 成员值为零。请参见表 12-6表 12-7

e_shstrndx

section header string table index。

与节名称字符串表关联的项的节头表索引。如果文件没有节名称字符串表,则此成员值为 SHN_UNDEF。

如果节名称字符串表的节索引大于或等于 SHN_LORESERVE (0xff00),则此成员值为 SHN_XINDEX (0xffff),节名称字符串表的实际节索引包含在节头中索引为 0 的 sh_link 字段中。否则,初始节头项的 sh_link 成员值为零。请参见 表 12-6表 12-7

ELF 标识

ELF 提供了一个目标文件框架,用于支持多个处理器、多种数据编码和多类计算机。要支持此目标文件系列,文件的初始字节应指定解释文件的方式。这些字节与发出查询的处理器以及文件的其余内容无关。

ELF 头和目标文件的初始字节对应于 e_ident 成员。

表1-3 ELF 标识索引

名称目的
EI_MAG00文件标识
EI_MAG11文件标识
EI_MAG22文件标识
EI_MAG33文件标识
EI_CLASS4文件类
EI_DATA5数据编码
EI_VERSION6文件版本
EI_OSABI7操作系统/ABI 标识
EI_ABIVERSION8ABI 版本
EI_PAD9填充字节的开头
EI_NIDENT16e_ident[] 的大小
#define EI_MAG0		0		/* File identification byte 0 index */
#define EI_MAG1		1		/* File identification byte 1 index */
#define EI_MAG2		2		/* File identification byte 2 index */
#define EI_MAG3		3		/* File identification byte 3 index */
#define EI_CLASS	4		/* File class byte index */
#define EI_DATA		5		/* Data encoding byte index */
#define EI_VERSION	6		/* File version byte index */
#define EI_OSABI	7		/* OS ABI identification */
#define EI_ABIVERSION	8		/* ABI version */
#define EI_PAD		9		/* Byte index of padding bytes */
#define EI_NIDENT (16)

这些索引可访问值为以下各项的字节。

EI_MAG0 - EI_MAG3

4 字节魔数,用于将文件标识为 ELF 目标文件,如下表中所列。

#define ELFMAG0     0x7f        /* Magic number byte 0 */
#define ELFMAG1     'E'     /* Magic number byte 1 */
#define ELFMAG2     'L'     /* Magic number byte 2 */
#define ELFMAG3     'F'     /* Magic number byte 3 */

EI_CLASS

字节 e_ident[EI_CLASS] 用于标识文件的类或容量,如下表中所列。

#define ELFCLASSNONE    0       /* Invalid class */
#define ELFCLASS32  1       /* 32-bit objects */
#define ELFCLASS64  2       /* 64-bit objects */
#define ELFCLASSNUM 3

文件格式设计用于在各种大小的计算机之间进行移植,而不会将最大计算机的大小强加给最小的计算机。文件类定义目标文件容器的数据结构所使用的基本类型。包含在目标文件各节中的数据可以遵循其他编程模型。

类 ELFCLASS32 支持文件和虚拟地址空间最高为 4 GB 的计算机。该类使用表 12-1 中定义的基本类型。

类 ELFCLASS64 保留用于 64 位体系结构,如 64 位 SPARC 和 x64。该类使用表 12-2 中定义的基本类型。

EI_DATA

字节 e_ident[EI_DATA] 用于指定目标文件中特定于处理器的数据的数据编码,如下表中所列。

#define ELFDATANONE 0       /* Invalid data encoding */
#define ELFDATA2LSB 1       /* 2's complement, little endian */                                                             
#define ELFDATA2MSB 2       /* 2's complement, big endian */
#define ELFDATANUM  3

EI_VERSION

字节 e_ident[EI_VERSION] 用于指定 ELF 头版本号。当前,该值必须为 EV_CURRENT

EI_OSABI

字节 e_ident[EI_OSABI] 用于标识操作系统以及目标文件所面向的 ABI。其他 ELF 结构中的一些字段包含的标志和值具有特定于操作系统或 ABI 的含义。这些字段的解释由此字节的值确定。

#define ELFOSABI_NONE		0	/* UNIX System V ABI */
#define ELFOSABI_SYSV		0	/* Alias.  */
#define ELFOSABI_HPUX		1	/* HP-UX */
#define ELFOSABI_NETBSD		2	/* NetBSD.  */
#define ELFOSABI_GNU		3	/* Object uses GNU ELF extensions.  */
#define ELFOSABI_LINUX		ELFOSABI_GNU /* Compatibility alias.  */
#define ELFOSABI_SOLARIS	6	/* Sun Solaris.  */
#define ELFOSABI_AIX		7	/* IBM AIX.  */
#define ELFOSABI_IRIX		8	/* SGI Irix.  */
#define ELFOSABI_FREEBSD	9	/* FreeBSD.  */
#define ELFOSABI_TRU64		10	/* Compaq TRU64 UNIX.  */
#define ELFOSABI_MODESTO	11	/* Novell Modesto.  */
#define ELFOSABI_OPENBSD	12	/* OpenBSD.  */
#define ELFOSABI_ARM_AEABI	64	/* ARM EABI */
#define ELFOSABI_ARM		97	/* ARM */
#define ELFOSABI_STANDALONE	255	/* Standalone (embedded) application */

EI_ABIVERSION

字节 e_ident[EI_ABIVERSION] 用于标识目标文件所面向的 ABI 的版本。此字段用于区分 ABI 的各个不兼容版本。此版本号的解释依赖于 EI_OSABI 字段标识的 ABI。如果没有为对应于处理器的 EI_OSABI 字段指定值,或者没有为 EI_OSABI 字节的特定值所确定的 ABI 指定版本值,则会使用值零来表示未指定值。

EI_PAD

该值用于标记 e_ident 中未使用字节的起始位置。这些字节会保留并设置为零。读取目标文件的程序应忽略这些值。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RCskZDUS-1636806374041)(_v_images/20211102110206867_13981.png)]

使用目标文件的节头表,可以定位文件的所有节。节头表是 Elf32_ShdrElf64_Shdr 结构的数组。节头表索引是此数组的下标。ELF 头的

  • e_shoff 成员表示从文件的起始位置到节头表的字节偏移。
  • e_shnum 成员表示节头表包含的项数。
  • e_shentsize 成员表示每一项的大小(以字节为单位)。

如果节数大于或等于 SHN_LORESERVE (0xff00),则 e_shnum 值为SHN_UNDEF (0)。节头表的实际项数包含在节头中索引为 0 的 sh_size 字段中。否则,初始项的 sh_size 成员值为零。

如果上下文中限制了索引大小,则会保留部分节头表索引。例如,符号表项的 st_shndx 成员以及 ELF 头的 e_shnum 和 e_shstrndx 成员。在这类上下文中,保留的值不表示目标文件中的实际各节。同样在这类上下文中,转义值表示会在其他位置(较大字段中)找到实际节索引。

表1-4 ELF 特殊节索引

/* Special section indices.  */
#define SHN_UNDEF   0       /* Undefined section */
#define SHN_LORESERVE   0xff00      /* Start of reserved indices */                                                         
#define SHN_LOPROC  0xff00      /* Start of processor-specific */
#define SHN_BEFORE  0xff00      /* Order section before all others (Solaris).  */
#define SHN_AFTER   0xff01      /* Order section after all others (Solaris).  */
#define SHN_HIPROC  0xff1f      /* End of processor-specific */
#define SHN_LOOS    0xff20      /* Start of OS-specific */
#define SHN_HIOS    0xff3f      /* End of OS-specific */
#define SHN_ABS     0xfff1      /* Associated symbol is absolute */
#define SHN_COMMON  0xfff2      /* Associated symbol is common */
#define SHN_XINDEX  0xffff      /* Index is in extra table.  */
#define SHN_HIRESERVE   0xffff      /* End of reserved indices */

注 - 虽然索引 0 保留为未定义的值,但节头表包含对应于索引 0 的项。即,如果 ELF 头的 e_shnum 成员表示文件在节头表中具有 6 项,则这些节的索引为 0 到 5。初始项的内容会在本节的后面指定。

SHN_UNDEF

未定义、缺少、无关或无意义的节引用。例如,定义的相对于节编号 SHN_UNDEF 的符号即是未定义符号。

SHN_LORESERVE

保留索引的范围的下界。

SHN_LOPROC - SHN_HIPROC

此范围内包含的值(包括这两个值)保留用于特定于处理器的语义。

SHN_LOOS - SHN_HIOS

此范围内包含的值(包括这两个值)保留用于特定于操作系统的语义。

SHN_LOSUNW - SHN_HISUNW

此范围内包含的值(包括这两个值)保留用于特定于 Sun 的语义。

SHN_SUNW_IGNORE

此节索引用于在可重定位目标文件中提供临时符号定义。保留供 dtrace(1M) 内部使用。

SHN_BEFORE, SHN_AFTER

SHF_LINK_ORDERSHF_ORDERED 节标志一起提供初始和最终节排序。请参见表 12-8

SHN_AMD64_LCOMMON

特定于 x64 的通用块标签。此标签与 SHN_COMMON 类似,但用于标识较大的通用块。

SHN_ABS

对应引用的绝对值。例如,相对于节编号 SHN_ABS 而定义的符号具有绝对值,不受重定位影响

case SHN_ABS:
	/* Don't need to do anything */
	pr_debug("Absolute symbol: 0x%08lx\n",
	       (long)sym[i].st_value);
	break;

SHN_COMMON

相对于此节而定义的符号为通用符号,如 FORTRAN COMMON 或未分配的 C 外部变量。这些符号有时称为暂定符号。

case SHN_COMMON:
	/* Ignore common symbols */
	if (!strncmp(name, "__gnu_lto", 9))
		break;

	/* We compiled with -fno-common.  These are not
	   supposed to happen.  */
	pr_debug("Common symbol: %s\n", name);
	pr_warn("%s: please compile with -fno-common\n",
	       mod->name);
	ret = -ENOEXEC;
	break;

SHN_XINDEX

转义值,用于表示实际节头索引过大,无法放入包含字段。节头索引可在特定于显示节索引的结构的其他位置中找到。

SHN_HIRESERVE

保留索引的范围的上界。系统保留了 SHN_LORESERVE 和 SHN_HIRESERVE 之间的索引(包括这两个值)。这些值不会引用节头表。节头表不包含对应于保留索引的项。

节包含目标文件中的所有信息,但 ELF 头、程序头表和节头表除外。此外,目标文件中的各节还满足多个条件:

  • 目标文件中的每一节仅有一个说明该节的节头。可能会有节头存在但节不存在的情况。
  • 每一节在文件中占用可能为空的一系列相邻字节。
  • 文件中的各节不能重叠。文件中的字节不能位于多个节中。
  • 目标文件可以包含非活动空间。各种头和节可能不会包括目标文件中的每个字节。非活动数据的内容未指定。

节头

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rzs4QxSj-1636806374045)(_v_images/20211102110206867_13981.png)]
节头具有以下结构:

/* Section header.  */
typedef struct
{
  Elf32_Word    sh_name;        /* Section name (string tbl index) */
  Elf32_Word    sh_type;        /* Section type */
  Elf32_Word    sh_flags;       /* Section flags */
  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;                                                                                                               

typedef struct
{
  Elf64_Word    sh_name;        /* Section name (string tbl index) */
  Elf64_Word    sh_type;        /* Section type */
  Elf64_Xword   sh_flags;       /* Section flags */
  Elf64_Addr    sh_addr;        /* Section virtual addr at execution */
  Elf64_Off sh_offset;      /* Section file offset */
  Elf64_Xword   sh_size;        /* Section size in bytes */
  Elf64_Word    sh_link;        /* Link to another section */
  Elf64_Word    sh_info;        /* Additional section information */
  Elf64_Xword   sh_addralign;       /* Section alignment */
  Elf64_Xword   sh_entsize;     /* Entry size if section holds table */
} Elf64_Shdr;

sh_name

节的名称。此成员值是节头字符串表节的索引,用于指定以空字符结尾的字符串的位置。表 12-10 中列出了节名称及其说明。

sh_type

用于将节的内容和语义分类。 下面列出了节类型及其说明。

表1-5 ELF 节类型 sh_type

/* 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 */
SHT_NULL

将节头标识为无效。此节头没有关联的节。节头的其他成员具有未定义的值。

SHT_PROGBITS

标识由程序定义的信息,这些信息的格式和含义完全由程序确定。

SHT_SYMTAB、SHT_DYNSYM、SHT_SUNW_LDYNSYM

标识符号表。通常,SHT_SYMTAB 节会提供用于链接编辑的符号。作为完整的符号表,该表可能包含许多对于动态链接不必要的符号。因此,目标文件还可以包含一个 SHT_DYNSYM 节,其中包含一组尽可能少的动态链接符号,从而节省空间。

SHT_DYNSYM 还可以使用 SHT_SUNW_LDYNSYM 节进行扩充。此附加节为运行时环境提供局部函数符号,但对于动态链接来说不是必需的。当不可分配的 SHT_SYMTAB 不可用或已从文件中剥离时,调试器通过使用此节可在运行时上下文中产生精确的栈跟踪。此节还可以为运行时环境提供其他符号信息,以便与 dladdr(3C) 一起使用。

如果 SHT_SUNW_LDYNSYM 节和 SHT_DYNSYM 节同时存在,链接编辑器会将这两者的数据区域紧邻彼此放置。SHT_SUNW_LDYNSYM 节位于 SHT_DYNSYM 节的前面。这种放置方式可以使两个表看起来像是一个更大的连续符号表,其中包含 SHT_SYMTAB 中的缩减符号集合。

有关详细信息,请参见符号表节。

SHT_STRTAB、SHT_DYNSTR

标识字符串表。目标文件可以有多个字符串表节。有关详细信息,请参见字符串表节。

SHT_RELA

标识包含显式加数的重定位项,如 32 位目标文件类的 Elf32_Rela 类型。目标文件可以有多个重定位节。有关详细信息,请参见重定位节。

SHT_HASH

标识符号散列表。动态链接的目标文件必须包含符号散列表。当前,目标文件只能有一个散列表,但此限制在将来可能会放宽。有关详细信息,请参见散列表节。

SHT_DYNAMIC

标识动态链接的信息。当前,目标文件只能有一个动态节。有关详细信息,请参见动态节。

SHT_NOTE

标识以某种方法标记文件的信息。有关详细信息,请参见注释节。

SHT_NOBITS

标识在文件中不占用任何空间,但在其他方面与 SHT_PROGBITS 类似的节。虽然此节不包含任何字节,但 sh_offset 成员包含概念性文件偏移。

if (shdr->sh_type != SHT_NOBITS)
    memcpy(dest, (void *)shdr->sh_addr, shdr->sh_size);
SHT_REL

标识不包含显式加数的重定位项,如 32 位目标文件类的 Elf32_Rel 类型。目标文件可以有多个重定位节。有关详细信息,请参见重定位节。

SHT_SHLIB

标识具有未指定的语义的保留节。包含此类型的节的程序不符合 ABI。

SHT_INIT_ARRAY

标识包含指针数组的节,这些指针指向初始化函数。数组中的每个指针都被视为不返回任何值的无参数过程。有关详细信息,请参见初始化节和终止节。

SHT_FINI_ARRAY

标识包含指针数组的节,这些指针指向终止函数。数组中的每个指针都被视为不返回任何值的无参数过程。有关详细信息,请参见初始化节和终止节。

SHT_PREINIT_ARRAY

标识包含指针数组的节,这些指针指向在其他所有初始化函数之前调用的函数。数组中的每个指针都被视为不返回任何值的无参数过程。有关详细信息,请参见初始化节和终止节。

SHT_GROUP

标识节组。节组标识一组相关的节,这些节必须作为一个单元由链接编辑器进行处理。SHT_GROUP 类型的节只能出现在可重定位目标文件中。

一些节会出现在相关的组中。例如,内置函数的外部定义除了要求包含可执行指令的节外,可能还会要求其他信息。此附加信息可以是包含引用的字面值的只读数据节、一个或多个调试信息节或其他信息节。

组节之间可以存在内部引用。但是,如果删除了其中某节,或者将其中某节替换为另一个目标文件中的副本,则这些引用将没有意义。因此,应将这些组作为一个单元包括在链接目标文件中或从中忽略。

SHT_GROUP 类型的节可定义这样分组的一组节:包含方目标文件的其中一个符号表中的某个符号名称为节组提供签名。SHT_GROUP 节的节头会指定标识符号项。sh_link 成员包含含有该项的符号表节的节头索引。sh_info 成员包含标识项的符号表索引。节头的 sh_flags 成员值为零。节名 (sh_name) 未指定。

SHT_GROUP 节的节数据是 Elf32_Word 项的数组。第一项是一个标志字。其余项是一系列节头索引。

当前定义了以下标志:

/* Section group handling.  */
#define GRP_COMDAT  0x1     /* Mark group as COMDAT.  */

GRP_COMDAT 是一个 COMDAT 组。该组可以与另一个目标文件中的 COMDAT 组重复,其中,重复的定义是具有相同的组签名。在这类情况下,链接编辑器将仅保留其中一个重复组。其余组的成员会被丢弃。

SHT_GROUP 节中的节头索引标识构成该组的节。这些节必须在其 sh_flags 节头成员中设置 SHF_GROUP 标志。如果链接编辑器决定删除节组,则它将删除组的所有成员。

为了便于删除组,并且不保留未使用的引用,同时仅对符号表进行最少的处理,请遵循以下规则:

  • 必须使用符号表各项中包含的 STB_GLOBAL 或 STB_WEAK 绑定和节索引 SHN_UNDEF,才能从组外的节中引用组成该组的节。包含引用的目标文件中定义的相同符号必须具有独立于该引用的符号表项。组外的各节不能引用对组节中包含的地址具有 STB_LOCAL 绑定的符号,包括 STT_SECTION 类型的符号。
  • 不允许从组外对组成该组的节进行非符号引用。例如,不能在 sh_link 或 sh_info 成员中使用组成员的节头索引。
  • 如果丢弃了某个组的成员,则可以删除相对于该组的某一节定义的符号表项。如果此符号表项包含在不属于该组的符号表节中,则会进行此删除。
SHT_SYMTAB_SHNDX

标识包含扩展节索引的节,扩展节索引与符号表关联。如果符号表引用的任何节头索引包含转义值 SHN_XINDEX,则需要关联的 SHT_SYMTAB_SHNDX。

SHT_SYMTAB_SHNDX 节是 Elf32_Word 值的数组。对于关联的符号表项中的每一项,此数组都包含对应的一项。这些值表示针对其定义符号表项的节头索引。仅当对应符号表项的 st_shndx 字段包含转义值 SHN_XINDEX 时,匹配的 Elf32_Word 才会包含实际的节头索引。否则,该项必须为 SHN_UNDEF (0)。

SHT_LOOS – SHT_HIOS

此范围内包含的值(包括这两个值)保留用于特定于操作系统的语义。

SHT_LOSUNW – SHT_HISUNW

此范围内包含的值(包括这两个值)保留用于 Oracle Solaris OS 语义。

SHT_SUNW_capchain

收集功能系列成员的索引数组。该数组的第一个元素是链版本号。此元素的后面是以 0 结尾的功能符号索引链。每个以 0 结尾的索引组都表示一个功能系列。每个系列的第一个元素是功能前置符号。后面的元素指向系列成员。有关详细信息,请参见功能节。

SHT_SUNW_capinfo

将符号表项与功能要求及其前置功能符号关联起来的索引数组。定义符号功能的目标文件包含 SHT_SUNW_cap 节。SHT_SUNW_cap 节头信息指向关联的 SHT_SUNW_capinfo 节。SHT_SUNW_capinfo 节头信息指向关联的符号表节。有关详细信息,请参见功能节。

SHT_SUNW_symsort

由相邻的 SHT_SUNW_LDYNSYM 节和 SHT_DYNSYM 节构成的动态符号表的索引数组。这些索引相对于 SHT_SUNW_LDYNSYM 节的开头。这些索引引用包含内存地址的符号。该索引已进行排序,以使索引按照地址递增的顺序引用符号。

SHT_SUNW_tlssort

由相邻的 SHT_SUNW_LDYNSYM 节和 SHT_DYNSYM 节构成的动态符号表的索引数组。这些索引相对于 SHT_SUNW_LDYNSYM 节的开头。这些索引引用线程局部存储符号。请参见第 14 章。该索引已进行排序,以使索引按照偏移递增的顺序引用符号。

SHT_SUNW_LDYNSYM

非全局符号的动态符号表。请参见前面的 SHT_SYMTAB、SHT_DYNSYM、SHT_SUNW_LDYNSYM 说明。

SHT_SUNW_dof

保留供 dtrace(1M) 内部使用。

SHT_SUNW_cap

指定功能要求。

参考链接:功能节

SHT_SUNW_SIGNATURE

标识模块验证签名。

SHT_SUNW_ANNOTATE

注释节的处理遵循处理节的所有缺省规则。仅当注释节位于不可分配的内存中时,才会发生异常。如果未设置节头标志 SHF_ALLOC,则链接编辑器将在不给出任何提示的情况下忽略针对此节的所有未满足的重定位。

SHT_SUNW_DEBUGSTR、SHT_SUNW_DEBUG

标识调试信息。使用链接编辑器的 -z strip-class 选项,或者在链接编辑之后使用 strip(1),可以将此类型的节从目标文件中剥离。

SHT_SUNW_move

标识用于处理部分初始化的符号的数据。有关详细信息,请参见移动节。

SHT_SUNW_COMDAT

标识允许将相同数据的多个副本减少为单个副本的节。

COMDAT 节由其节名称 (sh_name) 唯一标识。如果链接编辑器遇到节名称相同的 SHT_SUNW_COMDAT 类型的多个节,则将保留第一个节,而丢弃其余的节。任何应用于丢弃的 SHT_SUNW_COMDAT 节的重定位都会被忽略。在丢弃的节中定义的任何符号都会被删除。

此外,使用 -xF 选项调用编译器时,链接编辑器还支持用于对节重新排序的节命名约定。如果将函数放入名为 .sectname% funcname 的 SHT_SUNW_COMDAT 节中,则最后保留的几个 SHT_SUNW_COMDAT 节都将并入名为 .sectname 的节中。此方法可用于将 SHT_SUNW_COMDAT 节放入作为最终目标位置的 .text、.data 或其他任何节。

SHT_SUNW_syminfo

标识其他符号信息。有关详细信息,请参见Syminfo 表节。

SHT_SUNW_verdef

标识此文件定义的细分版本。有关详细信息,请参见版本定义章节。

SHT_SUNW_verneed

标识此文件所需的细分依赖性。有关详细信息,请参见版本依赖性节。

SHT_SUNW_versym

标识用于说明符号与文件提供的版本定义之间关系的表。有关详细信息,请参见版本符号节。

SHT_LOPROC - SHT_HIPROC

此范围内包含的值(包括这两个值)保留用于特定于处理器的语义。

SHT_SPARC_GOTDATA

标识特定于 SPARC 的数据,使用相对于 GOT 的寻址引用这些数据。即,相对于指定给符号 GLOBAL_OFFSET_TABLE 的地址的偏移。对于 64 位 SPARC,此节中的数据必须在链接编辑时绑定到偏离 GOT 地址不超过 {±} 2^32 字节的位置。

SHT_AMD64_UNWIND

标识特定于 x64 的数据,其中包含对应于栈展开的展开函数表的各项。

SHT_LOUSER

指定保留用于应用程序的索引范围的下界。

SHT_HIUSER

指定保留用于应用程序的索引范围的上界。应用程序可以使用 SHT_LOUSER 和 SHT_HIUSER 之间的节类型,而不会与当前或将来系统定义的节类型产生冲突。

其他节类型值保留。如前所述,即使索引 0 (SHN_UNDEF) 标记了未定义的节引用,仍会存在对应于该索引的节头。下表显示了这些值。

sh_flags

节可支持用于说明杂项属性的 1 位标志。表 12-8 中列出了标志定义。

/* Legal values for sh_flags (section flags).  */

#define SHF_WRITE        (1 << 0)   /* Writable */                                                                          
#define SHF_ALLOC        (1 << 1)   /* Occupies memory during execution */
#define SHF_EXECINSTR        (1 << 2)   /* Executable */
#define SHF_MERGE        (1 << 4)   /* Might be merged */
#define SHF_STRINGS      (1 << 5)   /* Contains nul-terminated strings */
#define SHF_INFO_LINK        (1 << 6)   /* `sh_info' contains SHT index */
#define SHF_LINK_ORDER       (1 << 7)   /* Preserve order after combining */
#define SHF_OS_NONCONFORMING (1 << 8)   /* Non-standard OS specific handling
                       required */
#define SHF_GROUP        (1 << 9)   /* Section is member of a group.  */
#define SHF_TLS          (1 << 10)  /* Section hold thread-local data.  */
#define SHF_COMPRESSED       (1 << 11)  /* Section with compressed data. */
#define SHF_MASKOS       0x0ff00000 /* OS-specific.  */
#define SHF_MASKPROC         0xf0000000 /* Processor-specific */
#define SHF_ORDERED      (1 << 30)  /* Special ordering requirement
                       (Solaris).  */
#define SHF_EXCLUDE      (1U << 31) /* Section is excluded unless
                       referenced or allocated (Solaris).*/
#define SHF_MIPS_GPREL      0x10000000 /* Must be in global data area.  */                                                  
#define SHF_MIPS_MERGE      0x20000000
#define SHF_MIPS_ADDR       0x40000000
#define SHF_MIPS_STRINGS    0x80000000
#define SHF_MIPS_NOSTRIP    0x08000000
#define SHF_MIPS_LOCAL      0x04000000
#define SHF_MIPS_NAMES      0x02000000
#define SHF_MIPS_NODUPE     0x01000000
#define SHF_PARISC_SHORT    0x20000000 /* Section with short addressing. */                                                 
#define SHF_PARISC_HUGE     0x40000000 /* Section far from gp.  */
#define SHF_PARISC_SBP      0x80000000 /* Static branch prediction code. */
/* ARM-specific values for sh_flags */
#define SHF_ARM_ENTRYSECT   0x10000000 /* Section contains an entry point */                                                
#define SHF_ARM_COMDEF      0x80000000 /* Section may be multiply defined
                          in the input to a link step.  */
/* Processor specific flags for the Shdr sh_flags field.  */
#define SHF_IA_64_SHORT     0x10000000  /* section near gp */                                                               
#define SHF_IA_64_NORECOV   0x20000000  /* spec insns w/o recovery */

如果在 sh_flags 中设置了标志位,则该节的此属性处于启用状态。否则,此属性处于禁用状态,或者不适用。未定义的属性会保留并设置为零。

SHF_WRITE

标识在进程执行过程中应可写的节。

SHF_ALLOC

标识在进程执行过程中占用内存的节。一些控制节不位于目标文件的内存映像中。对于这些节,此属性处于禁用状态。

SHF_EXECINSTR

标识包含可执行计算机指令的节。

SHF_MERGE

标识可以将其中包含的数据合并以消除重复的节。除非还设置了 SHF_STRINGS 标志,否则该节中的数据元素大小一致。每个元素的大小在节头的 sh_entsize 字段中指定。如果还设置了 SHF_STRINGS 标志,则数据元素会包含以空字符结尾的字符串。每个字符的大小在节头的 sh_entsize 字段中指定。

SHF_MERGE 节标志可用于标记可重定位目标文件中的 SHT_PROGBITS 节。请参见表 12-8。此标志表示该节可以与其他目标文件中的兼容节合并。这类合并有可能减小通过这些可重定位目标文件生成的任何可执行文件或共享目标文件的大小。减小文件大小还有助于改善生成的目标文件的运行时性能。

带有 SHF_MERGE 标志的节表示该节遵循以下特征:

  • 该节为只读。包含该节的程序在运行时绝对不可能修改该节的数据。
  • 从单独的重定位记录可以访问该节中的每一项。生成访问这些项的代码时,程序代码无法针对该节中的项的相对位置做出任何假设。
  • 如果该节还设置了 SHF_STRINGS 标志,那么该节只能包含以空字符结尾的字符串。空字符只能作为字符串结束符,而不能出现在任何字符串的中间位置。

SHF_MERGE 是一个可选标志,用于表示进行优化的可能性。允许链接编辑器执行优化,或忽略优化。任一情况下,链接编辑器都会创建一个有效的输出目标文件。当前,链接编辑器仅对包含使用 SHF_STRINGS 标志进行标记的字符串数据的节执行节合并。

同时设置了 SHF_STRINGS 节标志和 SHF_MERGE 标志时,该节中的字符串就可以与其他兼容节中的字符串合并。链接编辑器使用与用于压缩 SHT_STRTAB 字符串表(.strtab 和 .dynstr)的字符串压缩算法相同的字符串压缩算法来合并此类节。

  • 重复字符串会缩减为一个。
  • 会消除尾部字符串。例如,如果输入节包含字符串 “bigdog” 和 “dog”,那么将消除较小的 “dog”,并使用较大的字符串的尾部表示较小的字符串。

目前,链接编辑器仅对由没有特殊对齐限制的单字节大小字符组成的字符串执行字符串合并。具体来说,必须具备以下节特征。

  • sh_entsize 必须为 0 或 1。不支持包含宽字符的节。
  • 仅合并字节对齐的节,其中 sh_addralign 为 0 或 1。
SHF_STRINGS

标识包含以空字符结尾的字符串的节。每个字符的大小在节头的 sh_entsize 字段中指定。

SHF_INFO_LINK

此节头的 sh_info 字段包含节头表索引。

SHF_LINK_ORDER

此节向链接编辑器中添加特殊排序要求。如果此节头的 sh_link 字段引用其他节(链接到的节),则会应用这些要求。如果将此节与输出文件中的其他节合并,则此节将按照相同的相对顺序(相对于这些节)显示。同样,链接到的节也将按照相同的相对顺序(相对于与其合并的节)显示。链接到的节必须是未排序的,因而不能指定 SHF_LINK_ORDER 或 SHF_ORDERED。

特殊的 sh_link 值 SHN_BEFORE 和 SHN_AFTER(请参见表 12-4)表示,已排序的节将分别位于要排序的集合中其他所有各节之前或之后。如果已排序集合中的多个节包含这些特殊值之一,则会保持输入文件链接行的顺序。

此标志的一个典型用法是生成按地址顺序引用文本或数据节的表。

如果缺少 sh_link 排序信息,则合并到输出文件一个节内的单个输入文件中的节是相邻的。这些节会与输入文件中的节一样进行相对排序。构成多个输入文件的节按照链接行顺序显示。

SHF_OS_NONCONFORMING

此节除了要求标准链接规则之外,还要求特定于操作系统的特殊处理,以避免不正确的行为。如果此节具有 sh_type 值,或者对于这些字段包含特定于操作系统范围内的 sh_flags 位,并且链接编辑器无法识别这些值,则包含此节的目标文件会由于出错而被拒绝。

SHF_GROUP

此节是节组的一个成员(可能是唯一的成员)。此节必须由 SHT_GROUP 类型的节引用。只能对可重定位目标文件中包含的节设置 SHF_GROUP 标志。有关详细信息,请参见组节。

SHF_TLS

此节包含线程局部存储。进程中的每个线程都包含此数据的一个不同实例。有关详细信息,请参见第 14 章

SHF_MASKOS

此掩码中包括的所有位都保留用于特定于操作系统的语义。

SHF_AMD64_LARGE

x64 的缺省编译模型仅用于 32 位位移。此位移限制了节的大小,并最终限制段的大小不得超过 2 GB。此属性标志用于标识可包含超过 2 GB 数据的节。此标志允许链接使用不同代码模型的目标文件。

不包含 SHF_AMD64_LARGE 属性标志的 x64 目标文件节可以由使用小代码模型的目标文件任意引用。包含此标志的节只能由使用较大代码模型的目标文件引用。例如,x64 中间代码模型目标文件可以引用包含此属性标志的节和不包含此属性标志的节中的数据。但是,x64 小代码模型目标文件只能引用不包含此标志的节中的数据。

SHF_ORDERED

SHF_ORDERED 是 SHF_LINK_ORDER 所提供的旧版本功能,并且已被 SHF_LINK_ORDER 取代。SHF_ORDERED 提供两种不同的功能。首先,可以指定输出节,其次,链接编辑器具有特殊排序要求。

SHF_ORDERED 节的 sh_link 字段可以构成节的链接列表。此列表以包含指向其自身的 sh_link 的最终节结束。此列表中的所有节都将指定给具有此列表中最终节的名称的输出节。

如果已排序节的 sh_info 项在相同输入文件中是有效节,则将基于由 sh_info 项所指向的节的输出文件内的相对排序,对已排序的节进行排序。sh_info 项指向的节必须尚未排序,因而不能指定 SHF_LINK_ORDER 或 SHF_ORDERED。

特殊的 sh_info 值 SHN_BEFORE 和 SHN_AFTER(请参见表 12-4)表示,已排序的节将分别位于要排序的集合中其他所有各节之前或之后。如果已排序集合中的多个节包含这些特殊值之一,则会保持输入文件链接行的顺序。

如果缺少 sh_info 排序信息,则合并到输出文件一个节内的单个输入文件中的各节是相邻的。这些节会与输入文件中的节一样进行相对排序。构成多个输入文件的节按照链接行顺序显示。

SHF_EXCLUDE

此节不包括在可执行文件或共享目标文件的链接编辑的输入中。如果还设置了 SHF_ALLOC 标志,或者存在针对此节的重定位,则会忽略此标志。

SHF_MASKPROC

此掩码中包括的所有位都保留用于特定于处理器的语义。

sh_addr

如果节显示在进程的内存映像中,则此成员会指定节的第一个字节所在的地址。否则,此成员值为零。

sh_offset

从文件的起始位置到节中第一个字节的字节偏移。对于 SHT_NOBITS 节,此成员表示文件中的概念性偏移,因为该节在文件中不占用任何空间。

sh_size

节的大小(以字节为单位)。除非节类型为 SHT_NOBITS,否则该节将在文件中占用 sh_size 个字节。SHT_NOBITS 类型的节大小可以不为零,但该节在文件中不占用任何空间。

sh_link

节头表索引链接,其解释依赖于节类型。 表 12-9说明了相应的值。

sh_info

额外信息,其解释依赖于节类型。表 12-9说明了相应的值。如果此节头的 sh_flags 字段包含属性 SHF_INFO_LINK,则此成员表示节头表索引。

ELF sh_link 和 sh_info 解释
sh_typesh_linksh_info
SHT_DYNAMIC关联的字符串表的节头索引。0
SHT_HASH关联的符号表的节头索引。0
SHT_REL,SHT_RELA关联的符号表的节头索引。如果sh_flags成员包含SHF_INFO_LINK标志,则为应用重定位的节的节头索引,否则为0。另请参见表12-10和重定位节。
SHT_SYMTAB,SHT_DYNSYM关联的字符串表的节头索引。比上一个局部符号STB_LOCAL的符号表索引大一。
SHT_GROUP关联的符号表的节头索引。关联的符号表中项的符号表索引。指定的符号表项的名称用于提供节组的签名。
SHT_SYMTAB_SHNDX关联的符号表的节头索引。0
SHT_SUNW_cap如果符号功能存在,则为关联的SHT_SUNW_capinfo表的节头索引,否则为0。如果任何功能引用指定的字符串,则为关联的字符串表的节头索引,否则为0。
SHT_SUNW_capinfo关联的符号表的节头索引。对于动态目标文件,为关联的SHT_SUNW_capchain表的节头索引,否则为0。
SHT_SUNW_symsort关联的符号表的节头索引。0
SHT_SUNW_tlssort关联的符号表的节头索引。0
SHT_SUNW_LDYNSYM关联的字符串表的节头索引。此索引是与SHT_DYNSYM节所使用的字符串表相同的字符串表。比上一个局部符号STB_LOCAL的符号表索引大一。由于SHT_SUNW_LDYNSYM仅包含局部符号,因此sh_info等于表中的符号数。
SHT_SUNW_move关联的符号表的节头索引。0
SHT_SUNW_COMDAT00
SHT_SUNW_syminfo关联的符号表的节头索引。关联的.dynamic节的节头索引。
SHT_SUNW_verdef关联的字符串表的节头索引。节中版本定义的数量。
SHT_SUNW_verneed关联的字符串表的节头索引。节中版本依赖性的数量。
SHT_SUNW_versym关联的符号表的节头索引。0

sh_addralign

一些节具有地址对齐约束。例如,如果某节包含双字,则系统必须确保整个节双字对齐。在此情况下,sh_addr 的值在以 sh_addralign 的值为模数进行取模时,同余数必须等于 0。当前,仅允许使用 0 和 2 的正整数幂。值 0 和 1 表示节没有对齐约束。

sh_entsize

一些节包含固定大小的项的表,如符号表。对于这样的节,此成员会指定每一项的大小(以字节为单位)。如果节不包含固定大小的项的表,则此成员值为零。

for (i = 0; i < info->hdr->e_shnum; i++)
    info->sechdrs[i].sh_entsize = ~0UL;

特殊节

包含程序和控制信息的各种节。下表中的各节由系统使用,并且具有指明的类型和属性。
具有点 (.) 前缀的节名为系统而保留,但如果这些节的现有含义符合要求,则应用程序也可以使用这些节。应用程序可以使用不带前缀的名称,以避免与系统节产生冲突。使用目标文件格式,可以定义非保留的节。一个目标文件可以包含多个同名的节。

保留用于处理器体系结构的节名称通过在节名称前加上体系结构名称的缩写而构成。该名称应来自用于 e_machine 的体系结构名称。例如,.Foo.psect 是根据 FOO 体系结构定义的 psect 节。

现有扩展使用其历史名称。

表 1-10 ELF 特殊节

名称类型属性
.bssSHT_NOBITSSHF_ALLOC+SHF_WRITE
.commentSHT_PROGBITS
.data、.data1SHT_PROGBITSSHF_ALLOC+SHF_WRITE
.dynamicSHT_DYNAMICSHF_ALLOC+SHF_WRITE
.dynstrSHT_STRTABSHF_ALLOC
.dynsymSHT_DYNSYMSHF_ALLOC
.eh_frame_hdrSHT_AMD64_UNWINDSHF_ALLOC
.eh_frameSHT_AMD64_UNWINDSHF_ALLOC+SHF_WRITE
.finiSHT_PROGBITSSHF_ALLOC+SHF_EXECINSTR
.fini_arraySHT_FINI_ARRAYSHF_ALLOC+SHF_WRITE
.gotSHT_PROGBITS请参见全局偏移表(特定于处理器)
.hashSHT_HASHSHF_ALLOC
.initSHT_PROGBITSSHF_ALLOC+SHF_EXECINSTR
.init_arraySHT_INIT_ARRAYSHF_ALLOC+SHF_WRITE
.interpSHT_PROGBITS请参见程序的解释程序
.noteSHT_NOTE
.lbssSHT_NOBITSSHF_ALLOC+SHF_WRITE+SHF_AMD64_LARGE
.ldata、.ldata1SHT_PROGBITSSHF_ALLOC+SHF_WRITE+SHF_AMD64_LARGE
.lrodata、.lrodata1SHT_PROGBITSSHF_ALLOC+SHF_AMD64_LARGE
.pltSHT_PROGBITS请参见过程链接表(特定于处理器)
.preinit_arraySHT_PREINIT_ARRAYSHF_ALLOC+SHF_WRITE
.relaSHT_RELA
.relnameSHT_REL请参见重定位节
.relanameSHT_RELA请参见重定位节
.rodata、.rodata1SHT_PROGBITSSHF_ALLOC
.shstrtabSHT_STRTAB
.strtabSHT_STRTAB请参阅此表后面的说明。
.symtabSHT_SYMTAB请参见符号表节
.symtab_shndxSHT_SYMTAB_SHNDX请参见符号表节
.tbssSHT_NOBITSSHF_ALLOC+SHF_WRITE+SHF_TLS
.tdata、.tdata1SHT_PROGBITSSHF_ALLOC+SHF_WRITE+SHF_TLS
.textSHT_PROGBITSSHF_ALLOC+SHF_EXECINSTR
.SUNW_bssSHT_NOBITSSHF_ALLOC+SHF_WRITE
.SUNW_capSHT_SUNW_capSHF_ALLOC
.SUNW_capchainSHT_SUNW_capchainSHF_ALLOC
.SUNW_capinfoSHT_SUNW_capinfoSHF_ALLOC
.SUNW_heapSHT_PROGBITSSHF_ALLOC+SHF_WRITE
.SUNW_ldynsymSHT_SUNW_LDYNSYMSHF_ALLOC
.SUNW_dynsymsortSHT_SUNW_symsortSHF_ALLOC
.SUNW_dymtlssortSHT_SUNW_tlssortSHF_ALLOC
.SUNW_moveSHT_SUNW_moveSHF_ALLOC
.SUNW_relocSHT_REL,SHT_RELASHF_ALLOC
.SUNW_syminfoSHT_SUNW_syminfoSHF_ALLOC
.SUNW_versionSHT_SUNW_verdef,SHT_SUNW_verneed,SHT_SUNW_versymSHF_ALLOC

.bss

构成程序的内存映像的未初始化数据。根据定义,系统在程序开始运行时会将数据初始化为零。如节类型 SHT_NOBITS 所指明的那样,此节不会占用任何文件空间。

.comment

注释信息,通常由编译系统的组件提供。此节可以由 mcs(1) 进行处理。

.data、.data1

构成程序的内存映像的已初始化数据。

.dynamic

动态链接信息。有关详细信息,请参见动态节。

.dynstr

进行动态链接所需的字符串,通常是表示与符号表各项关联的名称的字符串。

.dynsym

动态链接符号表。有关详细信息,请参见符号表节。

.eh_frame_hdr、.eh_frame

用于展开栈的调用帧信息。

.fini

可执行指令,用于构成包含此节的可执行文件或共享目标文件的单个终止函数。有关详细信息,请参见初始化和终止例程。

.fini_array

函数指针数组,用于构成包含此节的可执行文件或共享目标文件的单个终止数组。有关详细信息,请参见初始化和终止例程。

.got

全局偏移表。有关详细信息,请参见全局偏移表(特定于处理器)。

.hash

符号散列表。有关详细信息,请参见散列表节。

.init

可执行指令,用于构成包含此节的可执行文件或共享目标文件的单个初始化函数。有关详细信息,请参见初始化和终止例程。

.init_array

函数指针数组,用于构成包含此节的可执行文件或共享目标文件的单个初始化数组。有关详细信息,请参见初始化和终止例程。

.interp

程序的解释程序的路径名。有关详细信息,请参见程序的解释程序。

.lbss

特定于 x64 的未初始化的数据。此数据与 .bss 类似,但用于大小超过 2 GB 的节。

.ldata, .ldata1

特定于 x64 的已初始化数据。此数据与 .data 类似,但用于大小超过 2 GB 的节。

.lrodata, .lrodata1

特定于 x64 的只读数据。此数据与 .rodata 类似,但用于大小超过 2 GB 的节。

.note

注释节中说明了该格式的信息。

.plt

过程链接表。有关详细信息,请参见过程链接表(特定于处理器)。

.preinit_array

函数指针数组,用于构成包含此节的可执行文件或共享目标文件的单个预初始化数组。有关详细信息,请参见初始化和终止例程。

.rela

不适用于特定节的重定位。此节的用途之一是用于寄存器重定位。有关详细信息,请参见寄存器符号。

.relname、.relaname

重定位信息,如重定位节中所述。如果文件具有包括重定位的可装入段,则此节的属性将包括 SHF_ALLOC 位。否则,该位会处于禁用状态。通常,name 由应用重定位的节提供。因此,.text 的重定位节的名称通常为 .rel.text 或 .rela.text。

.rodata、.rodata1

通常构成进程映像中的非可写段的只读数据。有关详细信息,请参见程序头。

.shstrtab

节名称。

.strtab

字符串,通常是表示与符号表各项关联的名称的字符串。如果文件具有包括符号字符串表的可装入段,则此节的属性将包括 SHF_ALLOC 位。否则,该位会处于禁用状态。

.symtab

符号表,如符号表节中所述。如果文件具有包括符号表的可装入段,则此节的属性将包括 SHF_ALLOC 位。否则,该位会处于禁用状态。

.symtab_shndx

此节包含特殊符号表的节索引数组,如 .symtab 所述。如果关联的符号表节包括 SHF_ALLOC 位,则此节的属性也将包括该位。否则,该位会处于禁用状态。

.tbss

此节包含构成程序的内存映像的未初始化线程局部数据。根据定义,为每个新执行流实例化数据时,系统都会将数据初始化为零。如节类型 SHT_NOBITS 所指明的那样,此节不会占用任何文件空间。有关详细信息,请参见第 14 章。

.tdata, .tdata1

这些节包含已初始化的线程局部数据,这些数据构成程序的内存映像。对于每个新执行流,系统会对其内容的副本进行实例化。有关详细信息,请参见第 14 章。

.text

程序的文本或可执行指令。

.SUNW_bss

共享目标文件的部分初始化数据,这些数据构成程序的内存映像。数据会在运行时进行初始化。如节类型 SHT_NOBITS 所指明的那样,此节不会占用任何文件空间。

.SUNW_cap

功能要求。有关详细信息,请参见功能节。

.SUNW_capchain

功能链表。有关详细信息,请参见功能节。

.SUNW_capinfo

功能符号信息。有关详细信息,请参见功能节。

.SUNW_heap

从 dldump(3C) 中创建的动态可执行文件的堆。

.SUNW_dynsymsort

.SUNW_ldynsym – .dynsym 组合符号表中符号的索引数组。该索引进行排序,以按照地址递增的顺序引用符号。不表示变量或函数的符号不包括在内。对于冗余全局符号和弱符号,仅保留弱符号。有关详细信息,请参见符号排序节。

.SUNW_dyntlssort

.SUNW_ldynsym – .dynsym 组合符号表中线程局部存储符号的索引数组。该索引进行排序,以按照偏移递增的顺序引用符号。不表示 TLS 变量的符号不包括在内。对于冗余全局符号和弱符号,仅保留弱符号。有关详细信息,请参见符号排序节。

.SUNW_ldynsym

扩充 .dynsym 节。此节包含局部函数符号,以在完整的 .symtab 节不可用时在上下文中使用。链接编辑器始终将 .SUNW_ldynsym 节的数据放置在紧邻 .dynsym 节之前。这两个节始终使用相同的 .dynstr 字符串表节。这种放置和组织方式使两个符号表可以被视为一个更大的符号表。请参见符号表节。

.SUNW_move

部分初始化数据的附加信息。有关详细信息,请参见移动节。

.SUNW_reloc

重定位信息,如重定位节中所述。此节是多个重定位节的串联,用于为引用各个重定位记录提供更好的临近性。由于仅有重定位记录的偏移有意义,因此节的 sh_info 值为零。

.SUNW_syminfo

其他符号表信息。有关详细信息,请参见Syminfo 表节。

.SUNW_version

版本控制信息。有关详细信息,请参见版本控制节。

散列表节SHT_HASH

散列表由用于符号表访问的 Elf32_Word 或 Elf64_Word 目标文件组成。SHT_HASH 节提供了此散列表。与散列关联的符号表在散列表节头的 sh_link 项中指定。下图中使用了标签来帮助说明散列表的结构,但这些标签不属于规范的一部分。

图 1-4 符号散列表

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-G0RnO8nH-1636806374047)(_v_images/20211102133644651_6326.png)]

bucket 数组包含 nbucket 项,chain 数组包含 nchain 项。索引从 0 开始。bucket 和 chain 都包含符号表索引。链表的各项与符号表对应。符号表的项数应等于 nchain,因此符号表索引也可选择链表的各项。

接受符号名称的散列函数会返回一个值,用于计算 bucket 索引。因此,如果散列函数为某个名称返回值 x,则 bucket [x% nbucket]将会计算出索引 y。此索引为符号表和链表的索引。如果符号表项不是需要的名称,则 chain[y] 将使用相同的散列值计算出符号表的下一项。

在所选符号表项具有需要的名称或者 chain 项包含值 STN_UNDEF 之前,可以遵循 chain 链接。

散列函数如下所示:

unsigned long
elf_Hash(const unsigned char *name)
{
    unsigned long h = 0, g;

    while (*name)
    {
         h = (h << 4) + *name++;
         if (g = h & 0xf0000000)
              h ^= g >> 24;
               h &= ~g;
    }
    return h;
}

移动节

通常在 ELF 文件中,已初始化的数据变量会保留在目标文件中。如果数据变量很大,并且仅包含少量的已初始化(非零)元素,则整个变量仍会保留在目标文件中。

包含大型部分初始化数据变量(如 FORTRAN COMMON 块)的目标文件可能会产生较大的磁盘空间开销。SHT_SUNW_move 节提供了一种压缩这些数据变量的机制。此压缩机制可减小关联目标文件的磁盘空间大小。

SHT_SUNW_move 节包含多个类型为 ELF32_Move 或 Elf64_Move 的项。使用这些项,可以将数据变量定义为暂定项目 (.bss)。这些项目在目标文件中不占用任何空间,但在运行时会构成目标文件的内存映像。移动记录可确定如何初始化内存映像的数据,从而构造完整的数据变量。

ELF32_Move 和 Elf64_Move 项的定义如下:

/* Move records.  */
typedef struct
{
  Elf32_Xword m_value;      /* Symbol value.  */
  Elf32_Word m_info;        /* Size and index.  */
  Elf32_Word m_poffset;     /* Symbol offset.  */
  Elf32_Half m_repeat;      /* Repeat count.  */
  Elf32_Half m_stride;      /* Stride info.  */
} Elf32_Move;

typedef struct
{
  Elf64_Xword m_value;      /* Symbol value.  */
  Elf64_Xword m_info;       /* Size and index.  */
  Elf64_Xword m_poffset;    /* Symbol offset.  */
  Elf64_Half m_repeat;      /* Repeat count.  */
  Elf64_Half m_stride;      /* Stride info.  */
} Elf64_Move;                                                                                                               

/* Macro to construct move records.  */
#define ELF32_M_SYM(info)   ((info) >> 8)
#define ELF32_M_SIZE(info)  ((unsigned char) (info))
#define ELF32_M_INFO(sym, size) (((sym) << 8) + (unsigned char) (size))

#define ELF64_M_SYM(info)   ELF32_M_SYM (info)
#define ELF64_M_SIZE(info)  ELF32_M_SIZE (info)
#define ELF64_M_INFO(sym, size) ELF32_M_INFO (sym, size)

这些结构的元素如下:

m_value

初始化值,即移到内存映像中的值。

m_info

符号表索引(与应用初始化相关)以及初始化的偏移的大小(以字节为单位)。成员的低 8 位定义大小,该大小可以是 1、2、4 或 8。高位字节定义符号索引。

m_poffset

相对于应用初始化的关联符号的偏移。

m_repeat

重复计数。

m_stride

幅度计数。该值表示在执行重复初始化时应跳过的单元数。单元是由 m_info 定义的初始化目标文件的大小。m_stride 值为零表示连续对单元执行初始化。

以下数据定义以前在目标文件中会占用 0x8000 个字节:

typedef struct {
    int     one;
    char    two;
} Data;

Data move[0x1000] = {
    {0, 0},       {1, '1'},     {0, 0},
    {0xf, 'F'},   {0xf, 'F'},   {0, 0},
    {0xe, 'E'},   {0, 0},       {0xe, 'E'}
};

SHT_SUNW_move 节可用于说明此数据。数据项在 .bss 节中定义。使用相应的移动项初始化数据项中的非零元素。

$ elfdump -s data | fgrep move
      [17]  0x00020868 0x00008000  OBJT GLOB 0   .bss       move
$ elfdump -m data

Move Section:  .SUNW_move
    symndx     offset   size repeat stride            value  with respect to
      [17]       0x44      4      1      1       0x45000000  move
      [17]       0x40      4      1      1              0xe  move
      [17]       0x34      4      1      1       0x45000000  move
      [17]       0x30      4      1      1              0xe  move
      [17]       0x1c      4      2      1       0x46000000  move
      [17]       0x18      4      2      1              0xf  move
      [17]        0xc      4      1      1       0x31000000  move
      [17]        0x8      4      1      1              0x1  move

可重定位目标文件提供的移动节可串联并在链接编辑器所创建的目标文件中输出。但是,在以下条件下链接编辑器将处理移动项,并将其内容扩展到传统的数据项中。
输出文件为静态可执行文件。
移动项的大小大于移动数据会扩展到的符号的大小。
-z nopartial 选项有效。

注释节

供应商或系统工程师可能需要使用特殊信息标记目标文件,以便其他程序可根据此信息检查一致性或兼容性。为此,可使用 SHT_NOTE 类型的节和 PT_NOTE 类型的程序头元素。

节和程序头元素中的注释信息包含任意数量的项,如下图所示。对于 64 位目标文件和 32 位目标文件,每一项都是一个目标处理器格式的 4 字节字的数组。图 12-6 中所示的标签用于帮助说明注释信息的结构,但不属于规范的一部分。

图 1-5 注释信息
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ULsDIGYe-1636806374048)(_v_images/20211102134752851_11519.png)]

namesz 和 name

名称中的前 namesz 个字节包含表示项的所有者或创建者的字符(以空字符结尾)。不存在用于避免名称冲突的正式机制。根据约定,供应商使用其各自的名称(如 “XYZ Computer Company”)作为标识符。如果不存在 name,则 namesz 值为零。如有必要,可使用填充确保描述符 4 字节对齐。namesz 中不包括这种填充方式。

descsz 和 desc

desc 中的前 descsz 个字节包含注释描述符。如果不存在描述符,则 descsz 值为零。如有必要,可使用填充确保下一个注释项 4 字节对齐。descsz 中不包括这种填充方式。

type

提供对描述符的解释。每个创建者可控制其各自的类型。单个 type 值可以存在多种解释。程序必须同时识别名称和 type 才能理解描述符。类型当前必须为非负数。

下图中所示的注释段包含两项。

图 1-6 注释段示例
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QGpEEvVb-1636806374049)(_v_images/20211102134905947_29594.png)]

注 - 系统会保留没有名称 (namesz == 0) 以及名称长度为零 (name[0] == ‘\0’) 的注释信息,但当前不定义任何类型。其他所有名称必须至少有一个非空字符。

重定位节

重定位是连接符号引用与符号定义的过程。例如,程序调用函数时,关联的调用指令必须在执行时将控制权转移到正确的目标地址。可重定位文件必须包含说明如何修改其节内容的信息。通过此信息,可执行文件和共享目标文件可包含进程的程序映像的正确信息。重定位项即是这些数据。

重定位项可具有以下结构。请参见 elf.h。

/* Relocation table entry without addend (in section of type SHT_REL).  */

typedef struct
{
  Elf32_Addr    r_offset;       /* Address */
  Elf32_Word    r_info;         /* Relocation type and symbol index */
} Elf32_Rel;

/* I have seen two different definitions of the Elf64_Rel and
   Elf64_Rela structures, so we'll leave them out until Novell (or
   whoever) gets their act together.  */
/* The following, at least, is used on Sparc v9, MIPS, and Alpha.  */

typedef struct
{
  Elf64_Addr    r_offset;       /* Address */
  Elf64_Xword   r_info;         /* Relocation type and symbol index */
} Elf64_Rel;

/* Relocation table entry with addend (in section of type SHT_RELA).  */

typedef struct
{
  Elf32_Addr    r_offset;       /* Address */
  Elf32_Word    r_info;         /* Relocation type and symbol index */
  Elf32_Sword   r_addend;       /* Addend */
} Elf32_Rela;

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

r_offset

此成员指定应用重定位操作的位置。不同的目标文件对于此成员的解释会稍有不同。

对于可重定位文件,该值表示节偏移。重定位节说明如何修改文件中的其他节。重定位偏移会在第二节中指定一个存储单元。

对于可执行文件或共享目标文件,该值表示受重定位影响的存储单元的虚拟地址。此信息使重定位项对于运行时链接程序更为有用。

虽然为了使相关程序可以更有效地访问,不同目标文件的成员的解释会发生变化,但重定位类型的含义保持相同。

r_info

此成员指定必须对其进行重定位的符号表索引以及要应用的重定位类型。例如,调用指令的重定位项包含所调用的函数的符号表索引。如果索引是未定义的符号索引 STN_UNDEF,则重定位将使用零作为符号值。

重定位类型特定于处理器。重定位项的重定位类型或符号表索引是将 ELF32_R_TYPE 或 ELF32_R_SYM 分别应用于项的 r_info 成员所得的结果。

/* How to extract and insert information held in the r_info field.  */

#define ELF32_R_SYM(val)        ((val) >> 8)
#define ELF32_R_TYPE(val)       ((val) & 0xff)
#define ELF32_R_INFO(sym, type)     (((sym) << 8) + ((type) & 0xff))
                                                                                                                            
#define ELF64_R_SYM(i)          ((i) >> 32)
#define ELF64_R_TYPE(i)         ((i) & 0xffffffff)
#define ELF64_R_INFO(sym,type)      ((((Elf64_Xword) (sym)) << 32) + (type))

对于 64 位 SPARC 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

此成员指定常量加数,用于计算将存储在可重定位字段中的值。

Rela 项包含显式加数。Rel 类型的项会在要修改的位置中存储一个隐式加数。32 位 SPARC 仅使用 Elf32_Rela 重定位项。64 位 SPARC 和 64 位 x86 仅使用 Elf64_Rela 重定位项。因此,r_addend 成员用作重定位加数。x86 仅使用 Elf32_Rel 重定位项。要重定位的字段包含该加数。在所有情况下,加数和计算所得的结果使用相同的字节顺序。

重定位节可以引用其他两个节:符号表(由 sh_link 节头项标识)和要修改的节(由 sh_info 节头项标识)。节中指定了这些关系。如果可重定位目标文件中存在重定位节,则需要 sh_info 项,但对于可执行文件和共享目标文件,该项是可选的。重定位偏移满足执行重定位的要求。

在所有情况下,r_offset 值都会指定受影响存储单元的第一个字节的偏移或虚拟地址。重定位类型可指定要更改的位以及计算这些位的值的方法。

重定位计算

以下表示法用于说明重定位计算。

  • A: 用于计算可重定位字段的值的加数。
  • B: 执行过程中将共享目标文件装入内存的基本地址。通常,生成的共享目标文件的基本虚拟地址为 0。但是,共享目标文件的执行地址不相同。请参见程序头。
  • G: 执行过程中,重定位项的符号地址所在的全局偏移表中的偏移。请参见全局偏移表(特定于处理器)。
  • GOT: 全局偏移表的地址。请参见全局偏移表(特定于处理器)。
  • L: 符号的过程链接表项的节偏移或地址。请参见过程链接表(特定于处理器)。
  • P: 使用 r_offset 计算出的重定位的存储单元的节偏移或地址。
  • S: 索引位于重定位项中的符号的值。
  • Z: 索引位于重定位项中的符号的大小。

SPARC: 重定位

在 SPARC 平台上,重定位项应用于字节 (byte8)、半字 (half16)、字 (word32) 和扩展字 (xword64)。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dh9TvQ56-1636806374049)(_v_images/20211102135432275_15317.png)]

重定位字段 (disp19, disp22, disp30) 的 dispn 系列都是字对齐、带符号扩展的 PC 相对位移。全部将值编码为其最低有效位都位于字的位置 0,仅在分配给值的位数方面有所不同。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QSG4FHWc-1636806374050)(_v_images/20211102135444666_23474.png)]

d2/disp8 和 d2/disp14 变体使用两个非连续位字段 d2 和 disp n 对 16 位和 10 位位移值进行编码。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0f4LFVKm-1636806374050)(_v_images/20211102135455314_27746.png)]

重定位字段 (imm5, imm6, imm7, imm10, imm13 , imm22) 的 immn 系列表示无符号整型常数。全部将值编码为其最低有效位都位于字的位置 0,仅在分配给值的位数方面有所不同。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZrGwB3QN-1636806374051)(_v_images/20211102135511315_7085.png)]

重定位字段 (simm10, simm11, simm13, simm22) 的 simmn 系列表示带符号整型常数。全部将值编码为其最低有效位都位于字的位置 0,仅在分配给值的位数方面有所不同。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Kfc4jaI1-1636806374051)(_v_images/20211102135531670_4904.png)]

SPARC: 重定位类型

下表中的字段名称可确定重定位类型是否会检查 overflow。计算出的重定位值可以大于预期的字段,并且重定位类型可以验证 (V) 值是适合结果还是将结果截断 (T)。例如,V-simm13 表示计算出的值不能包含 simm13 字段外有意义的非零位。

表 1-13 SPARC: ELF 重定位类型

名称字段计算
R_SPARC_NONE0
R_SPARC_81V-byte8S+A
R_SPARC_162V-half16S+A
R_SPARC_323V-word32S+A
R_SPARC_DISP84V-byte8S+A-P
R_SPARC_DISP165V-half16S+A-P
R_SPARC_DISP326V-disp32S+A-P
R_SPARC_WDISP307V-disp30(S+A-P)>>2
R_SPARC_WDISP228V-disp22(S+A-P)>>2
R_SPARC_HI229T-imm22(S+A)>>10
R_SPARC_2210V-imm22S+A
R_SPARC_1311V-simm13S+A
R_SPARC_LO1012T-simm13(S+A)&0x3ff
R_SPARC_GOT1013T-simm13G&0x3ff
R_SPARC_GOT1314V-simm13G
R_SPARC_GOT2215T-simm22G>>10
R_SPARC_PC1016T-simm13(S+A-P)&0x3ff
R_SPARC_PC2217V-disp22(S+A-P)>>10
R_SPARC_WPLT3018V-disp30(L+A-P)>>2
R_SPARC_COPY19请参阅此表后面的说明。
R_SPARC_GLOB_DAT20V-word32S+A
R_SPARC_JMP_SLOT21请参阅此表后面的说明。
R_SPARC_RELATIVE22V-word32B+A
R_SPARC_UA3223V-word32S+A
R_SPARC_PLT3224V-word32L+A
R_SPARC_HIPLT2225T-imm22(L+A)>>10
R_SPARC_LOPLT1026T-simm13(L+A)&0x3ff
R_SPARC_PCPLT3227V-word32L+A-P
R_SPARC_PCPLT2228V-disp22(L+A-P)>>10
R_SPARC_PCPLT1029V-simm13(L+A-P)&0x3ff
R_SPARC_1030V-simm10S+A
R_SPARC_1131V-simm11S+A
R_SPARC_HH2234V-imm22(S+A)>>42
R_SPARC_HM1035T-simm13((S+A)>>32)&0x3ff
R_SPARC_LM2236T-imm22(S+A)>>10
R_SPARC_PC_HH2237V-imm22(S+A-P)>>42
R_SPARC_PC_HM1038T-simm13((S+A-P)>>32)&0x3ff
R_SPARC_PC_LM2239T-imm22(S+A-P)>>10
R_SPARC_WDISP1640V-d2/disp14(S+A-P)>>2
R_SPARC_WDISP1941V-disp19(S+A-P)>>2
R_SPARC_743V-imm7S+A
R_SPARC_544V-imm5S+A
R_SPARC_645V-imm6S+A
R_SPARC_HIX2248V-imm22((S+A)^0xffffffffffffffff)>>10
R_SPARC_LOX1049T-simm13((S+A)&0x3ff)
R_SPARC_H4450V-imm22(S+A)>>22
R_SPARC_M4451T-imm10((S+A)>>12)&0x3ff
R_SPARC_L4452T-imm13(S+A)&0xfff
R_SPARC_REGISTER53V-word32S+A
R_SPARC_UA1655V-half16S+A
R_SPARC_GOTDATA_HIX2280V-imm22((S+A-GOT)>>10)^((S+A-GOT)>>31)
R_SPARC_GOTDATA_LOX1081T-imm13((S+A-GOT)&0x3ff)
R_SPARC_GOTDATA_OP_HIX2282T-imm22(G>>10)^(G>>31)
R_SPARC_GOTDATA_OP_LOX1083T-imm13(G&0x3ff)
R_SPARC_GOTDATA_OP84Word32请参阅此表后面的说明。
R_SPARC_SIZE3286V-word32Z+A
R_SPARC_WDISP1088V-d2/disp8(S+A-P)>>2

注 - 其他重定位类型可用于线程局部存储引用。这些重定位类型将在第 14 章中介绍。

一些重定位类型的语义不只是简单的计算:

R_SPARC_GOT10

与 R_SPARC_LO10 类似,不同的是此重定位指向符号的 GOT 项的地址。此外,R_SPARC_GOT10 还指示链接编辑器创建全局偏移表。

R_SPARC_GOT13

与 R_SPARC_13 类似,不同的是此重定位指向符号的 GOT 项的地址。此外,R_SPARC_GOT13 还指示链接编辑器创建全局偏移表。

R_SPARC_GOT22

与 R_SPARC_22 类似,不同的是此重定位指向符号的 GOT 项的地址。此外,R_SPARC_GOT22 还指示链接编辑器创建全局偏移表。

R_SPARC_WPLT30

与 R_SPARC_WDISP30 类似,不同的是此重定位指向符号的过程链接表项的地址。此外,R_SPARC_WPLT30 还指示链接编辑器创建过程链接表。

R_SPARC_COPY

由链接编辑器为动态可执行文件创建,用于保留只读文本段。此重定位偏移成员指向可写段中的位置。符号表索引指定应在当前目标文件和共享目标文件中同时存在的符号。执行过程中,运行时链接程序将与共享目标文件的符号关联的数据复制到偏移所指定的位置。请参见复制重定位。

R_SPARC_GLOB_DAT

与 R_SPARC_32 类似,不同的是此重定位会将 GOT 项设置为所指定符号的地址。使用特殊重定位类型,可以确定符号和 GOT 项之间的对应关系。

R_SPARC_JMP_SLOT

由链接编辑器为动态目标文件创建,用于提供延迟绑定。此重定位偏移成员可指定过程链接表项的位置。运行时链接程序会修改过程链接表项,以将控制权转移到指定的符号地址。

R_SPARC_RELATIVE

由链接编辑器为动态目标文件创建。此重定位偏移成员可指定共享目标文件中包含表示相对地址的值的位置。运行时链接程序通过将装入共享目标文件的虚拟地址与相对地址相加,计算对应的虚拟地址。此类型的重定位项必须为符号表索引指定值零。

R_SPARC_UA32

与 R_SPARC_32 类似,不同的是此重定位指向未对齐的字。必须将要重定位的字作为任意对齐的四个独立字节进行处理,而不是作为根据体系结构要求对齐的字进行处理。

R_SPARC_LM22

与 R_SPARC_HI22 类似,不同的是此重定位会进行截断而不是验证。

R_SPARC_PC_LM22

与 R_SPARC_PC22 类似,不同的是此重定位会进行截断而不是验证。

R_SPARC_HIX22

与 R_SPARC_LOX10 一起用于可执行文件,这些可执行文件在 64 位地址空间中的上限为 4 GB。与 R_SPARC_HI22 类似,但会提供链接值的补码。

R_SPARC_LOX10

与 R_SPARC_HIX22 一起使用。与 R_SPARC_LO10 类似,但始终设置链接值的位 10 到 12。

R_SPARC_L44

与 R_SPARC_H44 和 R_SPARC_M44 重定位类型一起使用,以生成 44 位的绝对寻址模型。

R_SPARC_REGISTER

用于初始化寄存器符号。此重定位偏移成员包含要初始化的寄存器编号。对于此寄存器必须存在对应的寄存器符号。该符号必须为 SHN_ABS 类型。

R_SPARC_GOTDATA_OP_HIX22、R_SPARC_GOTDATA_OP_LOX10 和 R_SPARC_GOTDATA_OP

这些重定位类型用于代码转换。

64 位 SPARC: 重定位类型

重定位计算中使用的以下表示法是特定于 64 位 SPARC 的。

O: 用于计算重定位字段的值的辅助加数。此加数通过应用 ELF64_R_TYPE_DATA 宏从 r_info 字段中提取。

下表中列出的重定位类型是扩展或修改针对 32 位 SPARC 定义的重定位类型所得的。请参见SPARC: 重定位类型。

表 1-14 64 位 SPARC: ELF 重定位类型

名称字段计算
R_SPARC_HI229V-imm22(S+A)>>10
R_SPARC_GLOB_DAT20V-xword64S+A
R_SPARC_RELATIVE22V-xword64B+A
R_SPARC_6432V-xword64S+A
R_SPARC_OLO1033V-simm13((S+A)&0x3ff)+O
R_SPARC_DISP6446V-xword64S+A-P
R_SPARC_PLT6447V-xword64L+A
R_SPARC_REGISTER53V-xword64S+A
R_SPARC_UA6454V-xword64S+A
R_SPARC_H3485V-imm22(S+A)>>12
R_SPARC_SIZE6487V-xword64Z+A

以下重定位类型的语义不只是简单的计算:

R_SPARC_OLO10

与 R_SPARC_LO10 类似,不同的是会添加额外的偏移,以充分利用 13 位带符号的直接字段。

x86: 重定位

在 x86 上,重定位项应用于字 (word32) 和扩展字 (xword64)。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z3L7ChAl-1636806374052)(_v_images/20211102140134492_24656.png)]

word32 指定一个占用 4 个字节的 32 位字段,此字段以任意字节对齐。这些值使用与 x86 体系结构中的其他字值相同的字节顺序。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KhsMwz8R-1636806374052)(_v_images/20211102140147168_21246.png)]

32 位 x86: 重定位类型

下表中列出的重定位类型是针对 32 位 x86 定义的。

表 1-15 32 位 x86: ELF 重定位类型

名称字段计算
R_386_NONE0
R_386_321word32S+A
R_386_PC322word32S+A-P
R_386_GOT323word32G+A
R_386_PLT324word32L+A-P
R_386_COPY5请参阅此表后面的说明。
R_386_GLOB_DAT6word32S
R_386_JMP_SLOT7word32S
R_386_RELATIVE8word32B+A
R_386_GOTOFF9word32S+A-GOT
R_386_GOTPC10word32GOT+A-P
R_386_32PLT11word32L+A
R_386_1620word16S+A
R_386_PC1621word16S+A-P
R_386_822word8S+A
R_386_PC823word8S+A-P
R_386_SIZE3238word32Z+A

注 - 其他重定位类型可用于线程局部存储引用。这些重定位类型将在第 14 章中介绍。

一些重定位类型的语义不只是简单的计算:

R_386_GOT32

计算 GOT 的基本地址与符号的 GOT 项之间的距离。此重定位还指示链接编辑器创建全局偏移表。

R_386_PLT32

计算符号的过程链接表项的地址,并指示链接编辑器创建一个过程链接表。

R_386_COPY

由链接编辑器为动态可执行文件创建,用于保留只读文本段。此重定位偏移成员指向可写段中的位置。符号表索引指定应在当前目标文件和共享目标文件中同时存在的符号。执行过程中,运行时链接程序将与共享目标文件的符号关联的数据复制到偏移所指定的位置。请参见复制重定位。

R_386_GLOB_DAT

用于将 GOT 项设置为所指定符号的地址。使用特殊重定位类型,可以确定符号和 GOT 项之间的对应关系。

R_386_JMP_SLOT

由链接编辑器为动态目标文件创建,用于提供延迟绑定。此重定位偏移成员可指定过程链接表项的位置。运行时链接程序会修改过程链接表项,以将控制权转移到指定的符号地址。

R_386_RELATIVE

由链接编辑器为动态目标文件创建。此重定位偏移成员可指定共享目标文件中包含表示相对地址的值的位置。运行时链接程序通过将装入共享目标文件的虚拟地址与相对地址相加,计算对应的虚拟地址。此类型的重定位项必须为符号表索引指定值零。

R_386_GOTOFF

计算符号的值与 GOT 的地址之间的差值。此重定位还指示链接编辑器创建全局偏移表。

R_386_GOTPC

与 R_386_PC32 类似,不同的是它在其计算中会使用 GOT 的地址。此重定位中引用的符号通常是 GLOBAL_OFFSET_TABLE,该符号还指示链接编辑器创建全局偏移表。

x64: 重定位类型

下表中列出的重定位是针对 x64 定义的。

表 1-16 x64: ELF 重定位类型

名称字段计算
R_AMD64_NONE0
R_AMD64_641word64S+A
R_AMD64_PC322word32S+A-P
R_AMD64_GOT323word32G+A
R_AMD64_PLT324word32L+A-P
R_AMD64_COPY5请参阅此表后面的说明。
R_AMD64_GLOB_DAT6word64S
R_AMD64_JUMP_SLOT7word64S
R_AMD64_RELATIVE8word64B+A
R_AMD64_GOTPCREL9word32G+GOT+A-P
R_AMD64_3210word32S+A
R_AMD64_32S11word32S+A
R_AMD64_1612word16S+A
R_AMD64_PC1613word16S+A-P
R_AMD64_814word8S+A
R_AMD64_PC815word8S+A-P
R_AMD64_PC6424word64S+A-P
R_AMD64_GOTOFF6425word64S+A-GOT
R_AMD64_GOTPC3226word32GOT+A+P
R_AMD64_SIZE3232word32Z+A
R_AMD64_SIZE6433word64Z+A

注 - 其他重定位类型可用于线程局部存储引用。这些重定位类型将在第 14 章中介绍。

大多数重定位类型的特殊语义与用于 x86 的语义相同。一些重定位类型的语义不只是简单的计算:

R_AMD64_GOTPCREL

此重定位类型具有与 R_AMD64_GOT32 或等效 R_386_GOTPC 重定位类型不同的语义。x64 体系结构提供了相对于指令指针的寻址模式。因此,可以使用单个指令从 GOT 装入地址。

针对 R_AMD64_GOTPCREL 重定位类型进行的计算提供了 GOT 中指定了符号地址的位置与应用重定位的位置之间的差值。

R_AMD64_32

计算出的值会截断为 32 位。链接编辑器可验证为重定位生成的值是否会使用零扩展为初始的 64 位值。

R_AMD64_32S

计算出的值会截断为 32 位。链接编辑器可验证为重定位生成的值是否会使用符号扩展为初始的 64 位值。

R_AMD64_8、R_AMD64_16、R_AMD64_PC16 和 R_AMD64_PC8

这些重定位类型不适用于 x64 ABI,在此列出是为了说明。R_AMD64_8 重定位类型会将计算出的值截断为 8 位。R_AMD64_16 重定位类型会将所计算的值截断为 16 位。

x86_64

#define R_X86_64_NONE		0	/* No reloc */
#define R_X86_64_64		1	/* Direct 64 bit  */
#define R_X86_64_PC32		2	/* PC relative 32 bit signed */
#define R_X86_64_GOT32		3	/* 32 bit GOT entry */
#define R_X86_64_PLT32		4	/* 32 bit PLT address */
#define R_X86_64_COPY		5	/* Copy symbol at runtime */
#define R_X86_64_GLOB_DAT	6	/* Create GOT entry */
#define R_X86_64_JUMP_SLOT	7	/* Create PLT entry */
#define R_X86_64_RELATIVE	8	/* Adjust by program base */
#define R_X86_64_GOTPCREL	9	/* 32 bit signed PC relative
#define R_X86_64_32		10	/* Direct 32 bit zero extended */
#define R_X86_64_32S		11	/* Direct 32 bit sign extended */
#define R_X86_64_16		12	/* Direct 16 bit zero extended */
#define R_X86_64_PC16		13	/* 16 bit sign extended pc relative */
#define R_X86_64_8		14	/* Direct 8 bit sign extended  */
#define R_X86_64_PC8		15	/* 8 bit sign extended pc relative */
#define R_X86_64_DTPMOD64	16	/* ID of module containing symbol */
#define R_X86_64_DTPOFF64	17	/* Offset in module's TLS block */
#define R_X86_64_TPOFF64	18	/* Offset in initial TLS block */
#define R_X86_64_TLSGD		19	/* 32 bit signed PC relative offset
#define R_X86_64_TLSLD		20	/* 32 bit signed PC relative offset
#define R_X86_64_DTPOFF32	21	/* Offset in TLS block */
#define R_X86_64_GOTTPOFF	22	/* 32 bit signed PC relative offset
#define R_X86_64_TPOFF32	23	/* Offset in initial TLS block */
#define R_X86_64_PC64		24	/* PC relative 64 bit */
#define R_X86_64_GOTOFF64	25	/* 64 bit offset to GOT */
#define R_X86_64_GOTPC32	26	/* 32 bit signed pc relative
#define R_X86_64_GOT64		27	/* 64-bit GOT entry offset */
#define R_X86_64_GOTPCREL64	28	/* 64-bit PC relative offset
#define R_X86_64_GOTPC64	29	/* 64-bit PC relative offset to GOT */
#define R_X86_64_GOTPLT64	30 	/* like GOT64, says PLT entry needed */
#define R_X86_64_PLTOFF64	31	/* 64-bit GOT relative offset
#define R_X86_64_SIZE32		32	/* Size of symbol plus 32-bit addend */
#define R_X86_64_SIZE64		33	/* Size of symbol plus 64-bit addend */
#define R_X86_64_GOTPC32_TLSDESC 34	/* GOT offset for TLS descriptor.  */
#define R_X86_64_TLSDESC_CALL   35	/* Marker for call through TLS
#define R_X86_64_TLSDESC        36	/* TLS descriptor.  */
#define R_X86_64_IRELATIVE	37	/* Adjust indirectly by program base */
#define R_X86_64_RELATIVE64	38	/* 64-bit adjust by program base */
					/* 39 Reserved was R_X86_64_PC32_BND */
					/* 40 Reserved was R_X86_64_PLT32_BND */
#define R_X86_64_GOTPCRELX	41	/* Load from 32 bit signed pc relative
#define R_X86_64_REX_GOTPCRELX	42	/* Load from 32 bit signed pc relative
#define R_X86_64_NUM		43

TODO

arm64(aarch64)

#define R_AARCH64_NONE            0	/* No relocation.  */
#define R_AARCH64_P32_ABS32		  1	/* Direct 32 bit.  */
#define R_AARCH64_P32_COPY		180	/* Copy symbol at runtime.  */
#define R_AARCH64_P32_GLOB_DAT		181	/* Create GOT entry.  */
#define R_AARCH64_P32_JUMP_SLOT		182	/* Create PLT entry.  */
#define R_AARCH64_P32_RELATIVE		183	/* Adjust by program base.  */
#define R_AARCH64_P32_TLS_DTPMOD	184	/* Module number, 32 bit.  */
#define R_AARCH64_P32_TLS_DTPREL	185	/* Module-relative offset, 32 bit.  */
#define R_AARCH64_P32_TLS_TPREL		186	/* TP-relative offset, 32 bit.  */
#define R_AARCH64_P32_TLSDESC		187	/* TLS Descriptor.  */
#define R_AARCH64_P32_IRELATIVE		188	/* STT_GNU_IFUNC relocation. */
#define R_AARCH64_ABS64         257	/* Direct 64 bit. */
#define R_AARCH64_ABS32         258	/* Direct 32 bit.  */
#define R_AARCH64_ABS16		259	/* Direct 16-bit.  */
#define R_AARCH64_PREL64	260	/* PC-relative 64-bit.	*/
#define R_AARCH64_PREL32	261	/* PC-relative 32-bit.	*/
#define R_AARCH64_PREL16	262	/* PC-relative 16-bit.	*/
#define R_AARCH64_MOVW_UABS_G0	263	/* Dir. MOVZ imm. from bits 15:0.  */
#define R_AARCH64_MOVW_UABS_G0_NC 264	/* Likewise for MOVK; no check.  */
#define R_AARCH64_MOVW_UABS_G1	265	/* Dir. MOVZ imm. from bits 31:16.  */
#define R_AARCH64_MOVW_UABS_G1_NC 266	/* Likewise for MOVK; no check.  */
#define R_AARCH64_MOVW_UABS_G2	267	/* Dir. MOVZ imm. from bits 47:32.  */
#define R_AARCH64_MOVW_UABS_G2_NC 268	/* Likewise for MOVK; no check.  */
#define R_AARCH64_MOVW_UABS_G3	269	/* Dir. MOV{K,Z} imm. from 63:48.  */
#define R_AARCH64_MOVW_SABS_G0	270	/* Dir. MOV{N,Z} imm. from 15:0.  */
#define R_AARCH64_MOVW_SABS_G1	271	/* Dir. MOV{N,Z} imm. from 31:16.  */
#define R_AARCH64_MOVW_SABS_G2	272	/* Dir. MOV{N,Z} imm. from 47:32.  */
#define R_AARCH64_LD_PREL_LO19	273	/* PC-rel. LD imm. from bits 20:2.  */
#define R_AARCH64_ADR_PREL_LO21	274	/* PC-rel. ADR imm. from bits 20:0.  */
#define R_AARCH64_ADR_PREL_PG_HI21 275	/* Page-rel. ADRP imm. from 32:12.  */
#define R_AARCH64_ADR_PREL_PG_HI21_NC 276 /* Likewise; no overflow check.  */
#define R_AARCH64_ADD_ABS_LO12_NC 277	/* Dir. ADD imm. from bits 11:0.  */
#define R_AARCH64_LDST8_ABS_LO12_NC 278	/* Likewise for LD/ST; no check. */
#define R_AARCH64_TSTBR14	279	/* PC-rel. TBZ/TBNZ imm. from 15:2.  */
#define R_AARCH64_CONDBR19	280	/* PC-rel. cond. br. imm. from 20:2. */
#define R_AARCH64_JUMP26	282	/* PC-rel. B imm. from bits 27:2.  */
#define R_AARCH64_CALL26	283	/* Likewise for CALL.  */
#define R_AARCH64_LDST16_ABS_LO12_NC 284 /* Dir. ADD imm. from bits 11:1.  */
#define R_AARCH64_LDST32_ABS_LO12_NC 285 /* Likewise for bits 11:2.  */
#define R_AARCH64_LDST64_ABS_LO12_NC 286 /* Likewise for bits 11:3.  */
#define R_AARCH64_MOVW_PREL_G0	287	/* PC-rel. MOV{N,Z} imm. from 15:0.  */
#define R_AARCH64_MOVW_PREL_G0_NC 288	/* Likewise for MOVK; no check.  */
#define R_AARCH64_MOVW_PREL_G1	289	/* PC-rel. MOV{N,Z} imm. from 31:16. */
#define R_AARCH64_MOVW_PREL_G1_NC 290	/* Likewise for MOVK; no check.  */
#define R_AARCH64_MOVW_PREL_G2	291	/* PC-rel. MOV{N,Z} imm. from 47:32. */
#define R_AARCH64_MOVW_PREL_G2_NC 292	/* Likewise for MOVK; no check.  */
#define R_AARCH64_MOVW_PREL_G3	293	/* PC-rel. MOV{N,Z} imm. from 63:48. */
#define R_AARCH64_LDST128_ABS_LO12_NC 299 /* Dir. ADD imm. from bits 11:4.  */
#define R_AARCH64_MOVW_GOTOFF_G0 300	/* GOT-rel. off. MOV{N,Z} imm. 15:0. */
#define R_AARCH64_MOVW_GOTOFF_G0_NC 301	/* Likewise for MOVK; no check.  */
#define R_AARCH64_MOVW_GOTOFF_G1 302	/* GOT-rel. o. MOV{N,Z} imm. 31:16.  */
#define R_AARCH64_MOVW_GOTOFF_G1_NC 303	/* Likewise for MOVK; no check.  */
#define R_AARCH64_MOVW_GOTOFF_G2 304	/* GOT-rel. o. MOV{N,Z} imm. 47:32.  */
#define R_AARCH64_MOVW_GOTOFF_G2_NC 305	/* Likewise for MOVK; no check.  */
#define R_AARCH64_MOVW_GOTOFF_G3 306	/* GOT-rel. o. MOV{N,Z} imm. 63:48.  */
#define R_AARCH64_GOTREL64	307	/* GOT-relative 64-bit.  */
#define R_AARCH64_GOTREL32	308	/* GOT-relative 32-bit.  */
#define R_AARCH64_GOT_LD_PREL19	309	/* PC-rel. GOT off. load imm. 20:2.  */
#define R_AARCH64_LD64_GOTOFF_LO15 310	/* GOT-rel. off. LD/ST imm. 14:3.  */
#define R_AARCH64_ADR_GOT_PAGE	311	/* P-page-rel. GOT off. ADRP 32:12.  */
#define R_AARCH64_LD64_GOT_LO12_NC 312	/* Dir. GOT off. LD/ST imm. 11:3.  */
#define R_AARCH64_LD64_GOTPAGE_LO15 313	/* GOT-page-rel. GOT off. LD/ST 14:3 */
#define R_AARCH64_TLSGD_ADR_PREL21 512	/* PC-relative ADR imm. 20:0.  */
#define R_AARCH64_TLSGD_ADR_PAGE21 513	/* page-rel. ADRP imm. 32:12.  */
#define R_AARCH64_TLSGD_ADD_LO12_NC 514	/* direct ADD imm. from 11:0.  */
#define R_AARCH64_TLSGD_MOVW_G1	515	/* GOT-rel. MOV{N,Z} 31:16.  */
#define R_AARCH64_TLSGD_MOVW_G0_NC 516	/* GOT-rel. MOVK imm. 15:0.  */
#define R_AARCH64_TLSLD_ADR_PREL21 517	/* Like 512; local dynamic model.  */
#define R_AARCH64_TLSLD_ADR_PAGE21 518	/* Like 513; local dynamic model.  */
#define R_AARCH64_TLSLD_ADD_LO12_NC 519	/* Like 514; local dynamic model.  */
#define R_AARCH64_TLSLD_MOVW_G1	520	/* Like 515; local dynamic model.  */
#define R_AARCH64_TLSLD_MOVW_G0_NC 521	/* Like 516; local dynamic model.  */
#define R_AARCH64_TLSLD_LD_PREL19 522	/* TLS PC-rel. load imm. 20:2.  */
#define R_AARCH64_TLSLD_MOVW_DTPREL_G2 523 /* TLS DTP-rel. MOV{N,Z} 47:32.  */
#define R_AARCH64_TLSLD_MOVW_DTPREL_G1 524 /* TLS DTP-rel. MOV{N,Z} 31:16.  */
#define R_AARCH64_TLSLD_MOVW_DTPREL_G1_NC 525 /* Likewise; MOVK; no check.  */
#define R_AARCH64_TLSLD_MOVW_DTPREL_G0 526 /* TLS DTP-rel. MOV{N,Z} 15:0.  */
#define R_AARCH64_TLSLD_MOVW_DTPREL_G0_NC 527 /* Likewise; MOVK; no check.  */
#define R_AARCH64_TLSLD_ADD_DTPREL_HI12 528 /* DTP-rel. ADD imm. from 23:12. */
#define R_AARCH64_TLSLD_ADD_DTPREL_LO12 529 /* DTP-rel. ADD imm. from 11:0.  */
#define R_AARCH64_TLSLD_ADD_DTPREL_LO12_NC 530 /* Likewise; no ovfl. check.  */
#define R_AARCH64_TLSLD_LDST8_DTPREL_LO12 531 /* DTP-rel. LD/ST imm. 11:0.  */
#define R_AARCH64_TLSLD_LDST8_DTPREL_LO12_NC 532 /* Likewise; no check.  */
#define R_AARCH64_TLSLD_LDST16_DTPREL_LO12 533 /* DTP-rel. LD/ST imm. 11:1.  */
#define R_AARCH64_TLSLD_LDST16_DTPREL_LO12_NC 534 /* Likewise; no check.  */
#define R_AARCH64_TLSLD_LDST32_DTPREL_LO12 535 /* DTP-rel. LD/ST imm. 11:2.  */
#define R_AARCH64_TLSLD_LDST32_DTPREL_LO12_NC 536 /* Likewise; no check.  */
#define R_AARCH64_TLSLD_LDST64_DTPREL_LO12 537 /* DTP-rel. LD/ST imm. 11:3.  */
#define R_AARCH64_TLSLD_LDST64_DTPREL_LO12_NC 538 /* Likewise; no check.  */
#define R_AARCH64_TLSIE_MOVW_GOTTPREL_G1 539 /* GOT-rel. MOV{N,Z} 31:16.  */
#define R_AARCH64_TLSIE_MOVW_GOTTPREL_G0_NC 540 /* GOT-rel. MOVK 15:0.  */
#define R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21 541 /* Page-rel. ADRP 32:12.  */
#define R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC 542 /* Direct LD off. 11:3.  */
#define R_AARCH64_TLSIE_LD_GOTTPREL_PREL19 543 /* PC-rel. load imm. 20:2.  */
#define R_AARCH64_TLSLE_MOVW_TPREL_G2 544 /* TLS TP-rel. MOV{N,Z} 47:32.  */
#define R_AARCH64_TLSLE_MOVW_TPREL_G1 545 /* TLS TP-rel. MOV{N,Z} 31:16.  */
#define R_AARCH64_TLSLE_MOVW_TPREL_G1_NC 546 /* Likewise; MOVK; no check.  */
#define R_AARCH64_TLSLE_MOVW_TPREL_G0 547 /* TLS TP-rel. MOV{N,Z} 15:0.  */
#define R_AARCH64_TLSLE_MOVW_TPREL_G0_NC 548 /* Likewise; MOVK; no check.  */
#define R_AARCH64_TLSLE_ADD_TPREL_HI12 549 /* TP-rel. ADD imm. 23:12.  */
#define R_AARCH64_TLSLE_ADD_TPREL_LO12 550 /* TP-rel. ADD imm. 11:0.  */
#define R_AARCH64_TLSLE_ADD_TPREL_LO12_NC 551 /* Likewise; no ovfl. check.  */
#define R_AARCH64_TLSLE_LDST8_TPREL_LO12 552 /* TP-rel. LD/ST off. 11:0.  */
#define R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC 553 /* Likewise; no ovfl. check. */
#define R_AARCH64_TLSLE_LDST16_TPREL_LO12 554 /* TP-rel. LD/ST off. 11:1.  */
#define R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC 555 /* Likewise; no check.  */
#define R_AARCH64_TLSLE_LDST32_TPREL_LO12 556 /* TP-rel. LD/ST off. 11:2.  */
#define R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC 557 /* Likewise; no check.  */
#define R_AARCH64_TLSLE_LDST64_TPREL_LO12 558 /* TP-rel. LD/ST off. 11:3.  */
#define R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC 559 /* Likewise; no check.  */
#define R_AARCH64_TLSDESC_LD_PREL19 560	/* PC-rel. load immediate 20:2.  */
#define R_AARCH64_TLSDESC_ADR_PREL21 561 /* PC-rel. ADR immediate 20:0.  */
#define R_AARCH64_TLSDESC_ADR_PAGE21 562 /* Page-rel. ADRP imm. 32:12.  */
#define R_AARCH64_TLSDESC_LD64_LO12 563	/* Direct LD off. from 11:3.  */
#define R_AARCH64_TLSDESC_ADD_LO12 564	/* Direct ADD imm. from 11:0.  */
#define R_AARCH64_TLSDESC_OFF_G1 565	/* GOT-rel. MOV{N,Z} imm. 31:16.  */
#define R_AARCH64_TLSDESC_OFF_G0_NC 566	/* GOT-rel. MOVK imm. 15:0; no ck.  */
#define R_AARCH64_TLSDESC_LDR	567	/* Relax LDR.  */
#define R_AARCH64_TLSDESC_ADD	568	/* Relax ADD.  */
#define R_AARCH64_TLSDESC_CALL	569	/* Relax BLR.  */
#define R_AARCH64_TLSLE_LDST128_TPREL_LO12 570 /* TP-rel. LD/ST off. 11:4.  */
#define R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC 571 /* Likewise; no check.  */
#define R_AARCH64_TLSLD_LDST128_DTPREL_LO12 572 /* DTP-rel. LD/ST imm. 11:4. */
#define R_AARCH64_TLSLD_LDST128_DTPREL_LO12_NC 573 /* Likewise; no check.  */
#define R_AARCH64_COPY         1024	/* Copy symbol at runtime.  */
#define R_AARCH64_GLOB_DAT     1025	/* Create GOT entry.  */
#define R_AARCH64_JUMP_SLOT    1026	/* Create PLT entry.  */
#define R_AARCH64_RELATIVE     1027	/* Adjust by program base.  */
#define R_AARCH64_TLS_DTPMOD   1028	/* Module number, 64 bit.  */
#define R_AARCH64_TLS_DTPREL   1029	/* Module-relative offset, 64 bit.  */
#define R_AARCH64_TLS_TPREL    1030	/* TP-relative offset, 64 bit.  */
#define R_AARCH64_TLSDESC      1031	/* TLS Descriptor.  */
#define R_AARCH64_IRELATIVE	1032	/* STT_GNU_IFUNC relocation.  */
//binutils-gdb: elfcpp/aarch64.h
enum
{
  // Null relocation codes
  R_AARCH64_NONE = 0,		// None
  R_AARCH64_withdrawn = 256,	// Treat as R_AARCH64_NONE

  // Static relocations
  R_AARCH64_ABS64 = 257,	// S + A
  R_AARCH64_ABS32 = 258,	// S + A
  R_AARCH64_ABS16 = 259,	// S + A
  R_AARCH64_PREL64 = 260,	// S + A - P
  R_AARCH64_PREL32 = 261,	// S + A - P
  R_AARCH64_PREL16 = 262,	// S + A - P
  R_AARCH64_MOVW_UABS_G0 = 263,		// S + A
  R_AARCH64_MOVW_UABS_G0_NC = 264,	// S + A
  R_AARCH64_MOVW_UABS_G1 = 265,		// S + A
  R_AARCH64_MOVW_UABS_G1_NC = 266,	// S + A
  R_AARCH64_MOVW_UABS_G2 = 267,		// S + A
  R_AARCH64_MOVW_UABS_G2_NC = 268,	// S + A
  R_AARCH64_MOVW_UABS_G3 = 269,		// S + A
  R_AARCH64_MOVW_SABS_G0 = 270,		// S + A
  R_AARCH64_MOVW_SABS_G1 = 271,		// S + A
  R_AARCH64_MOVW_SABS_G2 = 272,		// S + A
  R_AARCH64_LD_PREL_LO19 = 273,		// S + A - P
  R_AARCH64_ADR_PREL_LO21 = 274,	// S + A - P
  R_AARCH64_ADR_PREL_PG_HI21 = 275,	// Page(S+A) - Page(P)
  R_AARCH64_ADR_PREL_PG_HI21_NC = 276,	// Page(S+A) - Page(P)
  R_AARCH64_ADD_ABS_LO12_NC = 277,	// S + A
  R_AARCH64_LDST8_ABS_LO12_NC = 278,	// S + A
  R_AARCH64_TSTBR14 = 279,		// S + A - P
  R_AARCH64_CONDBR19 = 280,		// S + A - P
  R_AARCH64_JUMP26 = 282,		// S + A - P
  R_AARCH64_CALL26 = 283,		// S + A - P
  R_AARCH64_LDST16_ABS_LO12_NC = 284,	// S + A
  R_AARCH64_LDST32_ABS_LO12_NC = 285,	// S + A
  R_AARCH64_LDST64_ABS_LO12_NC = 286,	// S + A
  R_AARCH64_MOVW_PREL_G0 = 287,		// S + A - P
  R_AARCH64_MOVW_PREL_G0_NC = 288,	// S + A - P
  R_AARCH64_MOVW_PREL_G1 = 289,		// S + A - P
  R_AARCH64_MOVW_PREL_G1_NC = 290,	// S + A - P
  R_AARCH64_MOVW_PREL_G2 = 291,		// S + A - P
  R_AARCH64_MOVW_PREL_G2_NC = 292,	// S + A - P
  R_AARCH64_MOVW_PREL_G3 = 293,		// S + A - P
  R_AARCH64_LDST128_ABS_LO12_NC = 299,	// S + A
  R_AARCH64_MOVW_GOTOFF_G0 = 300,	// G(GDAT(S+A))-GOT
  R_AARCH64_MOVW_GOTOFF_G0_NC = 301,	// G(GDAT(S+A))-GOT
  R_AARCH64_MOVW_GOTOFF_G1 = 302,	// G(GDAT(S+A))-GOT
  R_AARCH64_MOVW_GOTOFF_G1_NC = 303,	// G(GDAT(S+A))-GOT
  R_AARCH64_MOVW_GOTOFF_G2 = 304,	// G(GDAT(S+A))-GOT
  R_AARCH64_MOVW_GOTOFF_G2_NC = 305,	// G(GDAT(S+A))-GOT
  R_AARCH64_MOVW_GOTOFF_G3 = 306,	// G(GDAT(S+A))-GOT
  R_AARCH64_GOTREL64 = 307,		// S + A - GOT
  R_AARCH64_GOTREL32 = 308,		// S + A - GOT
  R_AARCH64_GOT_LD_PREL19 = 309,	// G(GDAT(S+A))-P
  R_AARCH64_LD64_GOTOFF_LO15 = 310,	// G(GDAT(S+A))-GOT
  R_AARCH64_ADR_GOT_PAGE = 311,		// Page(G(GDAT(S+A)))-Page(P)
  R_AARCH64_LD64_GOT_LO12_NC = 312,	// G(GDAT(S+A))
  R_AARCH64_LD64_GOTPAGE_LO15 = 313,	// G(GDAT(S+A))-Page(GOT)

  // Relocations for thread-local storage
  R_AARCH64_TLSGD_ADR_PREL21 = 512,		// G(GTLSIDX(S,A)) - P
  R_AARCH64_TLSGD_ADR_PAGE21 = 513,		// Page(G(GTLSIDX(S,A)))-Page(P)
  R_AARCH64_TLSGD_ADD_LO12_NC = 514,		// G(GTLSICX(S,A))
  R_AARCH64_TLSGD_MOVW_G1 = 515,		// G(GTLSIDX(S,A)) - GOT
  R_AARCH64_TLSGD_MOVW_G0_NC = 516,		// G(GTLSIDX(S,A)) - GOT

  R_AARCH64_TLSLD_ADR_PREL21 = 517,		// G(GLDM(S)) - P
  R_AARCH64_TLSLD_ADR_PAGE21 = 518,		// Page(G(GLDM(S))) - Page(P)
  R_AARCH64_TLSLD_ADD_LO12_NC = 519,		// G(GLDM(S))
  R_AARCH64_TLSLD_MOVW_G1 = 520,		// G(GLDM(S)) - GOT
  R_AARCH64_TLSLD_MOVW_G0_NC = 521,		// G(GLDM(S)) - GOT
  R_AARCH64_TLSLD_LD_PREL19 = 522,		// G(GLDM(S)) - P
  R_AARCH64_TLSLD_MOVW_DTPREL_G2 = 523,		// DTPREL(S+A)
  R_AARCH64_TLSLD_MOVW_DTPREL_G1 = 524,		// DTPREL(S+A)
  R_AARCH64_TLSLD_MOVW_DTPREL_G1_NC = 525,	// DTPREL(S+A)
  R_AARCH64_TLSLD_MOVW_DTPREL_G0 = 526,		// DTPREL(S+A)
  R_AARCH64_TLSLD_MOVW_DTPREL_G0_NC = 527,	// DTPREL(S+A)
  R_AARCH64_TLSLD_ADD_DTPREL_HI12 = 528,	// DTPREL(S+A)
  R_AARCH64_TLSLD_ADD_DTPREL_LO12 = 529,	// DTPREL(S+A)
  R_AARCH64_TLSLD_ADD_DTPREL_LO12_NC = 530,	// DTPREL(S+A)
  R_AARCH64_TLSLD_LDST8_DTPREL_LO12 = 531,	// DTPREL(S+A)
  R_AARCH64_TLSLD_LDST8_DTPREL_LO12_NC = 532,	// DTPREL(S+A)
  R_AARCH64_TLSLD_LDST16_DTPREL_LO12 = 533,	// DTPREL(S+A)
  R_AARCH64_TLSLD_LDST16_DTPREL_LO12_NC = 534,	// DTPREL(S+A)
  R_AARCH64_TLSLD_LDST32_DTPREL_LO12 = 535,	// DTPREL(S+A)
  R_AARCH64_TLSLD_LDST32_DTPREL_LO12_NC = 536,	// DTPREL(S+A)
  R_AARCH64_TLSLD_LDST64_DTPREL_LO12 = 537,	// DTPREL(S+A)
  R_AARCH64_TLSLD_LDST64_DTPREL_LO12_NC = 538,	// DTPREL(S+A)
  R_AARCH64_TLSIE_MOVW_GOTTPREL_G1 = 539,	// G(GTPREL(S+A)) - GOT
  R_AARCH64_TLSIE_MOVW_GOTTPREL_G0_NC = 540,	// G(GTPREL(S+A)) - GOT
  R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21 = 541,	// Page(G(GTPREL(S+A)))-Page(P)
  R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC = 542,	// G(GTPREL(S+A))
  R_AARCH64_TLSIE_LD_GOTTPREL_PREL19 = 543,	// G(GTPREL(S+A)) - P
  R_AARCH64_TLSLE_MOVW_TPREL_G2 = 544,		// TPREL(S+A)
  R_AARCH64_TLSLE_MOVW_TPREL_G1 = 545,		// TPREL(S+A)
  R_AARCH64_TLSLE_MOVW_TPREL_G1_NC = 546,	// TPREL(S+A)
  R_AARCH64_TLSLE_MOVW_TPREL_G0 = 547,		// TPREL(S+A)
  R_AARCH64_TLSLE_MOVW_TPREL_G0_NC = 548,	// TPREL(S+A)
  R_AARCH64_TLSLE_ADD_TPREL_HI12 = 549,		// TPREL(S+A)
  R_AARCH64_TLSLE_ADD_TPREL_LO12 = 550,		// TPREL(S+A)
  R_AARCH64_TLSLE_ADD_TPREL_LO12_NC = 551,	// TPREL(S+A)
  R_AARCH64_TLSLE_LDST8_TPREL_LO12 = 552,	// TPREL(S+A)
  R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC = 553,	// TPREL(S+A)
  R_AARCH64_TLSLE_LDST16_TPREL_LO12 = 554,	// TPREL(S+A)
  R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC = 555,	// TPREL(S+A)
  R_AARCH64_TLSLE_LDST32_TPREL_LO12 = 556,	// TPREL(S+A)
  R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC = 557,	// TPREL(S+A)
  R_AARCH64_TLSLE_LDST64_TPREL_LO12 = 558,	// TPREL(S+A)
  R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC = 559,	// TPREL(S+A)
  R_AARCH64_TLSDESC_LD_PREL19 = 560,	// G(GTLSDESC(S+A)) - P
  R_AARCH64_TLSDESC_ADR_PREL21 = 561,	// G(GTLSDESC(S+A)) - P
  R_AARCH64_TLSDESC_ADR_PAGE21 = 562,	// Page(G(GTLSDESC(S+A)))-Page(P)
  R_AARCH64_TLSDESC_LD64_LO12 = 563,	// G(GTLSDESC(S+A))
  R_AARCH64_TLSDESC_ADD_LO12 = 564,	// G(GTLSDESC(S+A))
  R_AARCH64_TLSDESC_OFF_G1 = 565,	// G(GTLSDESC(S+A)) - GOT
  R_AARCH64_TLSDESC_OFF_G0_NC = 566,	// G(GTLSDESC(S+A)) - GOT
  R_AARCH64_TLSDESC_LDR = 567,		// None
  R_AARCH64_TLSDESC_ADD = 568,		// None
  R_AARCH64_TLSDESC_CALL = 569,		// None
  R_AARCH64_TLSLE_LDST128_TPREL_LO12 = 570,	// TPREL(S+A)
  R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC = 571,	// TPREL(S+A)
  R_AARCH64_TLSLD_LDST128_DTPREL_LO12 = 572,	// DTPREL(S+A)
  R_AARCH64_TLSLD_LDST128_DTPREL_LO12_NC = 573,	// DTPREL(S+A)

  // Dynamic relocations
  R_AARCH64_COPY = 1024,
  R_AARCH64_GLOB_DAT = 1025,		// S + A
  R_AARCH64_JUMP_SLOT = 1026,		// S + A
  R_AARCH64_RELATIVE = 1027,		// Delta(S) + A
  // Note (shenhan): the following 2 relocs are different from elf spec from
  // arm.  In elf docs, TLS_DTPMOD64 is defined as 1029, TLS_DTPREL64 1028.
  // While actually the bfd linker (and the dynamic linker) treates TLS_DTPMOD64
  // as 1028, TLS_DTPREL64 1029.  See binutils-gdb/include/elf/aarch64.h.
  R_AARCH64_TLS_DTPMOD64 = 1028,	// LDM(S)
  R_AARCH64_TLS_DTPREL64 = 1029,	// DTPREL(S+A)
  R_AARCH64_TLS_TPREL64 = 1030,		// TPREL(S+A)
  R_AARCH64_TLSDESC = 1031,		// TLSDESC(S+A)
  R_AARCH64_IRELATIVE = 1032,		// Indirect(Delta(S) + A)
};

TODO

字符串表节

字符串表节包含以空字符结尾的字符序列,通常称为字符串。目标文件使用这些字符串表示符号和节的名称。可以将字符串作为字符串表节的索引进行引用。

第一个字节(索引零)包含空字符。同样,字符串表的最后一个字节也包含空字符,从而确保所有字符串都以空字符结尾。根据上下文,索引为零的字符串不会指定任何名称或指定空名称。

允许使用空字符串表节。节头的 sh_size 成员值为零。对于空字符串表,非零索引无效。

节头的 sh_name 成员包含节头字符串表的节索引。节头字符串表由 ELF 头的 e_shstrndx 成员指定。下图显示了具有 25 个字节的字符串表,并且其字符串与各种索引关联。

图 1-7 ELF 字符串表

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7VtfN5S3-1636806374053)(_v_images/20211102141350994_19890.png)]

下表显示了上图所示的字符串表中的字符串。

表 1-17 ELF 字符串表索引

索引字符串
0
1name
7Variable
11able
16able
24空字符串

如示例所示,字符串表索引可以指向节中的任何字节。一个字符串可以出现多次。可以存在对子字符串的引用。一个字符串可以多次引用。另外,还允许使用未引用的字符串。

符号表节

目标文件的符号表包含定位和重定位程序的符号定义和符号引用所需的信息。符号表索引是此数组的下标。索引 0 指定表中的第一项并用作未定义的符号索引。请参见表 12-21

符号表项具有以下格式。请参见 elf.h。

/* Symbol table entry.  */

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;                                                                                                                 

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;

st_name

目标文件的符号字符串表的索引,其中包含符号名称的字符表示形式。如果该值为非零,则表示指定符号名称的字符串表索引。否则,符号表项没有名称。

st_value

关联符号的值。根据上下文,该值可以是绝对值或地址。
不同目标文件类型的符号表的各项对于 st_value 成员的解释稍有不同。

  • 在可重定位文件中,st_value 包含节索引为 SHN_COMMON 的符号的对齐约束。
  • 在可重定位文件中,st_value 包含所定义符号的节偏移。st_value 表示从 st_shndx 所标识的节的起始位置的偏移。
  • 在可执行文件和共享目标文件中,st_value 包含虚拟地址。为使这些文件的符号更适用于运行时链接程序,节偏移(文件解释)会替换为与节编号无关的虚拟地址(内存解释)。

尽管符号表值对于不同的目标文件具有类似含义,但通过适当的程序可以有效地访问数据。

st_size

许多符号具有关联大小。例如,数据目标文件的大小是目标文件中包含的字节数。如果符号没有大小或大小未知,则此成员值为零。

st_info

符号的类型和绑定属性。表 12-18 中显示了值和含义的列表。以下代码说明了如何处理这些值。请参见 sys/elf.h。

/* How to extract and insert information held in the st_info field.  */

#define ELF32_ST_BIND(val)      (((unsigned char) (val)) >> 4)
#define ELF32_ST_TYPE(val)      ((val) & 0xf)
#define ELF32_ST_INFO(bind, type)   (((bind) << 4) + ((type) & 0xf))

/* Both Elf32_Sym and Elf64_Sym use the same one-byte st_info field.  */
#define ELF64_ST_BIND(val)      ELF32_ST_BIND (val)
#define ELF64_ST_TYPE(val)      ELF32_ST_TYPE (val)
#define ELF64_ST_INFO(bind, type)   ELF32_ST_INFO ((bind), (type))

st_other

符号的可见性。 表 12-20中显示了值和含义的列表。以下代码说明了如何处理 32 位目标文件和 64 位目标文件的值。其他位设置为零,并且未定义任何含义。

/* How to extract and insert information held in the st_other field.  */

#define ELF32_ST_VISIBILITY(o)  ((o) & 0x03)                                                                                 

/* For ELF64 the definitions are the same.  */
#define ELF64_ST_VISIBILITY(o)  ELF32_ST_VISIBILITY (o)

st_shndx

所定义的每一个符号表项都与某节有关。此成员包含相关节头表索引。部分节索引会表示特殊含义。请参见表 12-4

如果此成员包含 SHN_XINDEX,则实际节头索引会过大而无法放入此字段中。实际值包含在 SHT_SYMTAB_SHNDX 类型的关联节中。

根据符号的 st_info 字段确定的符号绑定可确定链接可见性和行为。

ELF 符号绑定

表 1-18 ELF 符号绑定 ELF32_ST_BIND 和 ELF64_ST_BIND

名称
STB_LOCAL0
STB_GLOBAL1
STB_WEAK2
STB_LOOS10
STB_HIOS12
STB_LOPROC13
STB_HIPROC15
STB_LOCAL

局部符号。这些符号在包含其定义的目标文件的外部不可见。名称相同的局部符号可存在于多个文件中而不会相互干扰。

STB_GLOBAL

全局符号。这些符号对于合并的所有目标文件都可见。一个文件的全局符号定义满足另一个文件对相同全局符号的未定义引用。

STB_WEAK

弱符号。这些符号与全局符号类似,但其定义具有较低的优先级。

STB_LOOS - STB_HIOS

此范围内包含的值(包括这两个值)保留用于特定于操作系统的语义。

STB_LOPROC - STB_HIPROC

此范围内包含的值(包括这两个值)保留用于特定于处理器的语义。

全局符号和弱符号主要在以下两个方面不同:

  • 链接编辑器合并多个可重定位目标文件时,不允许多次定义相同名称的 STB_GLOBAL 符号。但是,如果存在已定义的全局符号,则出现相同名称的弱符号不会导致错误。链接编辑器会接受全局定义,并忽略弱定义。
    • 同样,如果存在通用符号,则出现相同名称的弱符号也不会导致错误。链接编辑器将使用通用定义,并忽略弱定义。通用符号具有包含 SHN_COMMON 的 st_shndx 字段。请参见符号解析。
  • 链接编辑器搜索归档库时,将会提取包含未定义全局符号或暂定全局符号的定义的归档成员。此成员的定义可以是全局符号或弱符号。
    • 缺省情况下,链接编辑器不会提取归档成员来解析未定义的弱符号。未解析的弱符号的值为零。使用 -z weakextract 可覆盖此缺省行为。使用此选项,弱引用可提取归档成员。

注 - 弱符号主要适用于系统软件。建议不要在应用程序中使用弱符号。

在每个符号表中,具有 STB_LOCAL 绑定的所有符号都优先于弱符号和全局符号。如节中所述,符号表节的 sh_info 节头成员包含第一个非局部符号的符号表索引。

根据符号的 st_info 字段确定的符号类型用于对关联实体进行一般等级划分。

ELF 符号类型

表 1-19 ELF 符号类型 ELF32_ST_TYPE 和 ELF64_ST_TYPE

名称
STT_NOTYPE0
STT_OBJECT1
STT_FUNC2
STT_SECTION3
STT_FILE4
STT_COMMON5
STT_TLS6
STT_LOOS10
STT_HIOS12
STT_LOPROC13
STT_SPARC_REGISTER13
STT_HIPROC15
STT_NOTYPE

未指定符号类型。

STT_OBJECT

此符号与变量、数组等数据目标文件关联。

STT_FUNC

此符号与函数或其他可执行代码关联。

STT_SECTION

此符号与节关联。此类型的符号表各项主要用于重定位,并且通常具有 STB_LOCAL 绑定。

STT_FILE

通常,符号的名称会指定与目标文件关联的源文件的名称。文件符号具有 STB_LOCAL 绑定和节索引 SHN_ABS。此符号(如果存在)位于文件的其他 STB_LOCAL 符号前面。

符号索引为 1 的 SHT_SYMTAB 是表示目标文件的 STT_FILE 符号。通常,此符号后跟文件的 STT_SECTION 符号。这些节符号又后跟已降为局部符号的任何全局符号。

STT_COMMON

此符号标记未初始化的通用块。此符号的处理与 STT_OBJECT 的处理完全相同。

STT_TLS

此符号指定线程局部存储实体。定义后,此符号可为符号指明指定的偏移,而不是实际地址。

线程局部存储重定位只能引用 STT_TLS 类型的符号。从可分配节中引用 STT_TLS 类型的符号只能通过使用特殊线程局部存储重定位来实现。有关详细信息,请参见第 14 章。从非可分配节中引用 STT_TLS 类型的符号没有此限制。

STT_LOOS - STT_HIOS

此范围内包含的值(包括这两个值)保留用于特定于操作系统的语义。

STT_LOPROC - STT_HIPROC

此范围内包含的值(包括这两个值)保留用于特定于处理器的语义。

符号的可见性根据其 st_other 字段确定。此可见性可以在可重定位目标文件中指定。此可见性定义了在符号成为可执行文件或共享目标文件的一部分后访问该符号的方式。

ELF 符号可见性

表 1-20 ELF 符号可见性

名称
STV_DEFAULT0
STV_INTERNAL1
STV_HIDDEN2
STV_PROTECTED3
STV_EXPORTED4
STV_SINGLETON5
STV_ELIMINATE6
STV_DEFAULT

具有 STV_DEFAULT 属性的符号的可见性与符号的绑定类型指定的可见性相同。全局符号和弱符号在其定义组件(可执行文件或共享目标文件)外部可见。局部符号处于隐藏状态。另外,还可以替换全局符号和弱符号。可以在另一个组件中通过定义相同的名称插入这些符号。

STV_PROTECTED

如果当前组件中定义的符号在其他组件中可见,但不能被替换,则该符号处于受保护状态。定义组件中对这类符号的任何引用都必须解析为该组件中的定义。即使在另一个组件中存在按缺省规则插入的符号定义,也必须进行此解析。具有 STB_LOCAL 绑定的符号将没有 STV_PROTECTED 可见性。

STV_HIDDEN

如果当前组件中定义的符号的名称对于其他组件不可见,则该符号处于隐藏状态。必须对这类符号进行保护。此属性用于控制组件的外部接口。由这样的符号命名的目标文件仍可以在另一个组件中引用(如果将目标文件的地址传到外部)。

如果可执行文件或共享目标文件中包括可重定位目标文件,则该目标文件中包含的隐藏符号将会删除或转换为使用 STB_LOCAL 绑定。

STV_INTERNAL

此可见性属性当前被保留。

STV_EXPORTED

此可见性属性确保符号保持为全局。不能使用任何其他符号可见性技术对此可见性进行降级或消除。具有 STB_LOCAL 绑定的符号将没有 STV_EXPORTED 可见性。

STV_SINGLETON

此可见性属性确保符号保持为全局,并且符号定义的一个实例绑定到一个进程中的所有引用。不能使用任何其他符号可见性技术对此可见性进行降级或消除。具有 STB_LOCAL 绑定的符号将没有 STV_SINGLETON 可见性。不能直接绑定到 STV_SINGLETON。

STV_ELIMINATE

此可见性属性扩展 STV_HIDDEN。当前组件中定义为要消除的符号对其他组件不可见。该符号未写入使用该组件的动态可执行文件或共享目标文件的任何符号表中。

STV_SINGLETON 可见性属性可以影响链接编辑过程中可执行文件或共享目标文件中的符号的解析。从一个进程中的任意引用只能绑定到单件的一个实例。

STV_SINGLETON 可以与 STV_DEFAULT 可见性属性组合,其中 STV_SINGLETON 优先级较高。STV_EXPORT 可以与 STV_DEFAULT 可见性属性组合,其中 STV_EXPORT 优先级较高。STV_SINGLETON 或 STV_EXPORT 可见性不能与任何其他可见性属性组合。链接编辑会将此类事件视为致命错误。

在链接编辑过程中,其他可见性属性不会影响可执行文件或共享目标文件中符号的解析。这样的解析由绑定类型控制。一旦链接编辑器选定了其解析,这些属性即会强加两种要求。两种要求都基于以下事实,即所链接的代码中的引用可能已优化,从而可利用这些属性。

  • 所有非缺省可见性属性在应用于符号引用时都表示,在链接的目标文件中必须提供满足该引用的定义。如果在链接的目标文件中没有定义此类型的符号引用,则该引用必须具有 STB_WEAK 绑定。在此情况下,引用将解析为零。
  • 如果任何名称的引用或定义是具有非缺省可见性属性的符号,则该可见性属性将传播给链接的目标文件中的解析符号。如果针对符号的不同实例指定不同的可见性属性,则最具约束的可见性属性将传播给链接的目标文件中的解析符号。这些属性按最低到最高约束进行排序,依次为 STV_PROTECTED、STV_HIDDEN 和 STV_INTERNAL。

如果符号的值指向节中的特定位置,则符号的节索引成员 st_shndx 会包含节头表的索引。节在重定位过程中移动时,符号的值也会更改。符号的引用仍然指向程序中的相同位置。一些特殊节索引值会具有其他语义

SHN_ABS

此符号具有不会由于重定位而发生更改的绝对值。

SHN_COMMON 和 SHN_AMD64_LCOMMON

此符号标记尚未分配的通用块。与节的 sh_addralign 成员类似,符号的值也会指定对齐约束。链接编辑器在值为 st_value 的倍数的地址位置为符号分配存储空间。符号的大小会指明所需的字节数。

SHN_UNDEF

此节表索引表示未定义符号。链接编辑器将此目标文件与用于定义所表示的符号的另一目标文件合并时,此文件中对该符号的引用将与该定义绑定。

如之前所述,索引 0 (STN_UNDEF) 的符号表项会保留。此项具有下表中列出的值。

ELF 符号表项:索引 0
名称
st_name0无名称
st_value0零值
st_size0无大小
st_info0无类型,局部绑定
st_other0
st_shndxSHN_UNDEF无节

符号表布局和约定

按照以下顺序将符号写入符号表。

  • 任何符号表中的索引 0 用于表示未定义的符号。符号表中的第一项始终完全为零。因此符号类型为 STT_NOTYPE。
  • 如果符号表包含任何局部符号,符号表中的第二项是提供文件名的 STT_FILE 符号。
  • STT_SECTION 类型的节符号。
  • STT_REGISTER 类型的寄存器符号。
  • 缩减到局部作用域的全局符号。
  • 对于提供局部符号的每个输入文件,提供输入文件名称的 STT_FILE 符号,后跟相关符号。
  • 在符号表中,全局符号紧跟局部符号。第一个全局符号由符号表 sh_info 值标识。局部符号和全局符号始终以这种方式保持彼此独立,不能混合。

这些符号表是 Oracle Solaris OS 中的特殊内容。

  • .symtab (SHT_SYMTAB): 此符号表包含说明关联的 ELF 文件的每个符号。此符号表通常是不可分配的,因此在进程的内存映像中不可用。通过使用 mapfile 和 ELIMINATE 关键字可以从 .symtab 中消除全局符号。请参见删除符号和SYMBOL_SCOPE / SYMBOL_VERSION 指令。
  • .dynsym (SHT_DYNSYM): 此表包含 .symtab 表中支持动态链接所需的符号的子集。此符号表可供分配,因此在进程的内存映像中可用。.dynsym 表以标准 NULL 符号开始,后跟文件全局符号。STT_FILE 符号通常不包含在此符号表中。如果重定位项需要,可能会包含 STT_SECTION 符号。
  • .SUNW_ldynsym (SHT_SUNW_LDYNSYM): 扩充 .dynsym 表中包含的信息的可选符号表。.SUNW_ldynsym 表包含局部函数符号。此符号表可供分配,因此在进程的内存映像中可用。当不可分配的 .symtab 不可用,或已从文件中剥离时,调试器通过使用此节可在运行时上下文中产生精确的栈跟踪。此节还可以为运行时环境提供其他符号信息,以便与 dladdr(3C) 一起使用。

仅当 .dynsym 表存在时,才存在 .SUNW_ldynsym 表。当 .SUNW_ldynsym 节和 .dynsym 节同时存在时,链接编辑器会将其数据区域紧邻彼此放置,其中 .SUNW_ldynsym 放置在前面。这种放置方式可以使两个表看起来像是一个更大的连续符号表。此符号表遵从先前枚举的标准布局规则。

.SUNW_ldynsym 表可以通过使用链接编辑器的 -z noldynsym 选项进行消除。

符号排序节

https://docs.oracle.com/cd/E26926_01/html/E25910/chapter6-79797.html#scrolltoc

TODO

寄存器符号

https://docs.oracle.com/cd/E26926_01/html/E25910/chapter6-79797.html#scrolltoc

TODO

Syminfo 表节

https://docs.oracle.com/cd/E26926_01/html/E25910/chapter7-17.html#scrolltoc

syminfo 节包含多个类型为 Elf32_Syminfo 或 Elf64_Syminfo 的项。.SUNW_syminfo 节中包含与关联符号表 (sh_link) 中的每一项对应的项。

如果目标文件中存在此节,则可通过采用关联符号表的符号索引,并使用该索引在此节中查找对应的 Elf32_Syminfo 项或 Elf64_Syminfo 项,从而找到其他符号信息。关联的符号表和 Syminfo 表的项数将始终相同。

索引 0 用于存储 Syminfo 表的当前版本,即 SYMINFO_CURRENT。由于符号表项 0 始终保留用于 UNDEF 符号表项,因此该用法不会造成任何冲突。

Syminfo 项具有以下格式。请参见 elf.h。

/* The syminfo section if available contains additional information about
   every dynamic symbol.  */

typedef struct
{
  Elf32_Half si_boundto;        /* Direct bindings, symbol bound to */
  Elf32_Half si_flags;          /* Per symbol flags */
} Elf32_Syminfo;

typedef struct
{
  Elf64_Half si_boundto;        /* Direct bindings, symbol bound to */
  Elf64_Half si_flags;          /* Per symbol flags */
} Elf64_Syminfo;

/* Syminfo version values.  */
#define SYMINFO_NONE        0
#define SYMINFO_CURRENT     1
#define SYMINFO_NUM     2

si_boundto

.dynamic 节中某项的索引,由 sh_info 字段标识,该字段用于扩充 Syminfo 标志。例如,DT_NEEDED 项标识与 Syminfo 项关联的动态目标文件。以下各项是 si_boundto 的保留值。

名称含义
SYMINFO_BT_SELF0xffff符号与自身绑定。
SYMINFO_BT_PARENT0xfffe符号与父级绑定。父级是指导致此动态目标文件被装入的第一个目标文件。
SYMINFO_BT_NONE0xfffd符号没有任何特殊的符号绑定。
SYMINFO_BT_EXTERN0xfffc符号定义是外部的。
/* Possible values for si_boundto.  */
#define SYMINFO_BT_SELF     0xffff  /* Symbol bound to self */
#define SYMINFO_BT_PARENT   0xfffe  /* Symbol bound to parent */
#define SYMINFO_BT_LOWRESERVE   0xff00  /* Beginning of reserved entries */

si_flags

此位字段可以设置标志,如下表所示。

名称含义
SYMINFO_FLG_DIRECT0x01符号引用与包含定义的目标文件直接关联。
SYMINFO_FLG_FILTER0x02符号定义可用作标准过滤器。
SYMINFO_FLG_COPY0x04符号定义通过副本重定位生成。
SYMINFO_FLG_LAZYLOAD0x08符号引用应延迟装入的目标文件。
SYMINFO_FLG_DIRECTBIND0x10符号引用应与定义直接绑定。
SYMINFO_FLG_NOEXTDIRECT0x20不允许将外部引用与此符号定义直接绑定。
SYMINFO_FLG_AUXILIARY0x40符号定义可用作辅助过滤器。
SYMINFO_FLG_INTERPOSE0x80符号定义可用作插入项。此属性仅适用于动态可执行文件。
SYMINFO_FLG_CAP0x100符号与功能相关联。
SYMINFO_FLG_DEFERRED0x200符号不应包含在BIND_NOW重定位中。
/* Possible bitmasks for si_flags.  */
#define SYMINFO_FLG_DIRECT  0x0001  /* Direct bound symbol */
#define SYMINFO_FLG_PASSTHRU    0x0002  /* Pass-thru symbol for translator */
#define SYMINFO_FLG_COPY    0x0004  /* Symbol is a copy-reloc */
#define SYMINFO_FLG_LAZYLOAD    0x0008  /* Symbol bound to object to be lazy
                       loaded */

版本控制节

链接编辑器创建的目标文件可以包含以下两种类型的版本控制信息:

  • 版本定义,用于提供全局符号关联,并使用类型为 SHT_SUNW_verdef 和 SHT_SUNW_versym 的节实现。
  • 版本依赖性,用于表示其他目标文件依赖性的版本定义要求,并使用类型为 SHT_SUNW_verneedSHT_SUNW_versym 的节实现。

sys/link.h 中定义了这些节的组成结构。包含版本控制信息的节名为 .SUNW_version。

版本定义章节

此节由 SHT_SUNW_verdef 类型定义。如果此节存在,则必须同时存在 SHT_SUNW_versym 节。这两种结构在文件中提供符号与版本定义之间的关联。请参见创建版本定义。此节中的元素具有以下结构:

/* Version definition sections.  */

typedef struct
{
  Elf32_Half    vd_version;     /* Version revision */
  Elf32_Half    vd_flags;       /* Version information */
  Elf32_Half    vd_ndx;         /* Version Index */
  Elf32_Half    vd_cnt;         /* Number of associated aux entries */
  Elf32_Word    vd_hash;        /* Version name hash value */
  Elf32_Word    vd_aux;         /* Offset in bytes to verdaux array */
  Elf32_Word    vd_next;        /* Offset in bytes to next verdef                                                            
                       entry */
} Elf32_Verdef;

typedef struct
{
  Elf64_Half    vd_version;     /* Version revision */
  Elf64_Half    vd_flags;       /* Version information */
  Elf64_Half    vd_ndx;         /* Version Index */
  Elf64_Half    vd_cnt;         /* Number of associated aux entries */
  Elf64_Word    vd_hash;        /* Version name hash value */
  Elf64_Word    vd_aux;         /* Offset in bytes to verdaux array */
  Elf64_Word    vd_next;        /* Offset in bytes to next verdef
                       entry */
} Elf64_Verdef;

vd_version

此成员标识该结构的版本,如下表中所列。

名称含义
VER_DEF_NONE0无效版本。
VER_DEF_CURRENT>=1当前版本。

值 1 表示原始节格式。扩展要求使用更大数字的新版本。VER_DEF_CURRENT 的值可根据需要进行更改,以反映当前版本号。

vd_flags

此成员包含特定于版本定义的信息,如下表中所列。

名称含义
VER_FLG_BASE0x1文件的版本定义。
VER_FLG_WEAK0x2弱版本标识符。

对文件应用版本定义或符号自动缩减后,基版本定义将始终存在。基版本可为文件保留的符号提供缺省版本。弱版本定义 (weak version definition) 没有与版本关联的符号。请参见创建弱版本定义

vd_ndx

版本索引。每个版本定义都有一个唯一的索引,用于将 SHT_SUNW_versym 项与相应的版本定义关联。

vd_cnt

Elf32_Verdaux 数组中的元素数目。

vd_hash

版本定义名称的散列值。该值是通过使用散列表节中介绍的同一散列函数生成的。

vd_aux

从此 Elf32_Verdef 项的开头到版本定义名称的 Elf32_Verdaux 数组的字节偏移。该数组中的第一个元素必须存在。此元素指向该结构定义的版本定义字符串。也可以存在其他元素。元素数目由 vd_cnt 值表示。这些元素表示此版本定义的依赖项。每种依赖项都会具有各自的版本定义结构。

vd_next

从此 Elf32_Verdef 结构的开头到下一个 Elf32_Verdef 项的字节偏移。

vda_name

以空字符结尾的字符串的字符串表偏移,用于提供版本定义的名称。

vda_next

从此 Elf32_Verdaux 项的开头到下一个 Elf32_Verdaux 项的字节偏移。

版本依赖性节

版本依赖性节由 SHT_SUNW_verneed 类型定义。此节通过指明动态依赖项所需的版本定义,对文件的动态依赖性要求进行补充。仅当依赖项包含版本定义时,才会在此节中进行记录。此节中的元素具有以下结构:

/* Version dependency section.  */

typedef struct
{
  Elf32_Half    vn_version;     /* Version of structure */
  Elf32_Half    vn_cnt;         /* Number of associated aux entries */
  Elf32_Word    vn_file;        /* Offset of filename for this
                       dependency */
  Elf32_Word    vn_aux;         /* Offset in bytes to vernaux array */
  Elf32_Word    vn_next;        /* Offset in bytes to next verneed
                       entry */
} Elf32_Verneed;

typedef struct
{
  Elf64_Half    vn_version;     /* Version of structure */
  Elf64_Half    vn_cnt;         /* Number of associated aux entries */
  Elf64_Word    vn_file;        /* Offset of filename for this
                       dependency */
  Elf64_Word    vn_aux;         /* Offset in bytes to vernaux array */
  Elf64_Word    vn_next;        /* Offset in bytes to next verneed
                       entry */
} Elf64_Verneed;

/* Auxiliary needed version information.  */

typedef struct
{
  Elf32_Word    vna_hash;       /* Hash value of dependency name */
  Elf32_Half    vna_flags;      /* Dependency specific information */
  Elf32_Half    vna_other;      /* Unused */
  Elf32_Word    vna_name;       /* Dependency name string offset */
  Elf32_Word    vna_next;       /* Offset in bytes to next vernaux
                       entry */
} Elf32_Vernaux;

typedef struct
{
  Elf64_Word    vna_hash;       /* Hash value of dependency name */
  Elf64_Half    vna_flags;      /* Dependency specific information */
  Elf64_Half    vna_other;      /* Unused */
  Elf64_Word    vna_name;       /* Dependency name string offset */
  Elf64_Word    vna_next;       /* Offset in bytes to next vernaux
                       entry */
} Elf64_Vernaux;

TODO

版本符号节

https://docs.oracle.com/cd/E26926_01/html/E25910/chapter6-54676.html#scrolltoc

TODO

程序装入和动态链接

本章介绍用于创建运行程序的目标文件信息和系统操作。此处介绍的大多数信息适用于所有系统。特定于某处理器的信息位于带有相应标记的各节中。

可执行文件和共享目标文件静态表示应用程序。要执行这类程序,系统可使用这些文件创建动态程序表示形式(即进程映像)。进程映像具有包含其文本、数据、栈等内容的段。提供了以下主要节。

  • 程序头,其中介绍了直接参与程序执行的目标文件结构。主数据结构是一种程序头表,用于定位文件中的段映像,并包含创建程序内存映像所需的其他信息。
  • 程序装入(特定于处理器),其中介绍了用于将程序装入内存的信息。
  • 运行时链接程序,其中介绍了用于指定和解析进程映像的目标文件之间的符号引用的信息。

程序头

可执行文件或共享目标文件的程序头表是一个结构数组。每种结构都描述了系统准备程序执行所需的段或其他信息。目标文件段包含一个或多个节,如段内容中所述。

程序头仅对可执行文件和共享目标文件有意义。文件使用 ELF 头的 e_phentsize 和 e_phnum 成员来指定各自的程序头大小。

程序头具有以下结构。请参见 elf.h。

/* Program segment header.  */

typedef struct
{
  Elf32_Word    p_type;         /* Segment type */
  Elf32_Off p_offset;       /* Segment file offset */
  Elf32_Addr    p_vaddr;        /* Segment virtual address */
  Elf32_Addr    p_paddr;        /* Segment physical address */
  Elf32_Word    p_filesz;       /* Segment size in file */
  Elf32_Word    p_memsz;        /* Segment size in memory */
  Elf32_Word    p_flags;        /* Segment flags */
  Elf32_Word    p_align;        /* Segment alignment */
} Elf32_Phdr;                                                                                                                

typedef struct
{
  Elf64_Word    p_type;         /* Segment type */
  Elf64_Word    p_flags;        /* Segment flags */
  Elf64_Off p_offset;       /* Segment file offset */
  Elf64_Addr    p_vaddr;        /* Segment virtual address */
  Elf64_Addr    p_paddr;        /* Segment physical address */
  Elf64_Xword   p_filesz;       /* Segment size in file */
  Elf64_Xword   p_memsz;        /* Segment size in memory */
  Elf64_Xword   p_align;        /* Segment alignment */
} Elf64_Phdr;

p_type

此数组元素描述的段类型或解释此数组元素的信息的方式。表 13-1 中指定了类型值及其含义。

p_offset

相对段的第一个字节所在文件的起始位置的偏移。

p_vaddr

段的第一个字节在内存中的虚拟地址。

p_paddr

段在与物理寻址相关的系统中的物理地址。由于此系统忽略了应用程序的物理地址,因此该成员对于可执行文件和共享目标文件具有未指定的内容。

p_filesz

段的文件映像中的字节数,可以为零。

p_memsz

段的内存映像中的字节数,可以为零。

p_flags

与段相关的标志。表 13-2 中指定了类型值及其含义。

p_align

可装入的进程段必须具有 p_vaddr 和 p_offset 的同余值(以页面大小为模数)。此成员可提供一个值,用于在内存和文件中根据该值对齐各段。值 0 和 1 表示无需对齐。另外,p_align 应为 2 的正整数幂,并且 p_vaddr 应等于 p_offset(以 p_align 为模数)。请参见程序装入(特定于处理器)

示例

$ readelf -l /usr/bin/ls

Elf file type is DYN (Shared object file)
Entry point 0x5e00
There are 10 program headers, starting at offset 64

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  PHDR           0x0000000000000040 0x0000000000000040 0x0000000000000040
                 0x0000000000000230 0x0000000000000230  R      0x8
  INTERP         0x0000000000000270 0x0000000000000270 0x0000000000000270
                 0x000000000000001c 0x000000000000001c  R      0x1
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
  LOAD           0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x000000000001f628 0x000000000001f628  R E    0x200000
  LOAD           0x000000000001ff70 0x000000000021ff70 0x000000000021ff70
                 0x00000000000012d8 0x0000000000002588  RW     0x200000
  DYNAMIC        0x00000000000209d8 0x00000000002209d8 0x00000000002209d8
                 0x0000000000000210 0x0000000000000210  RW     0x8
  NOTE           0x0000000000000290 0x0000000000000290 0x0000000000000290
                 0x0000000000000020 0x0000000000000020  R      0x8
  NOTE           0x00000000000002b0 0x00000000000002b0 0x00000000000002b0
                 0x0000000000000044 0x0000000000000044  R      0x4
  GNU_EH_FRAME   0x000000000001bdd4 0x000000000001bdd4 0x000000000001bdd4
                 0x00000000000008f4 0x00000000000008f4  R      0x4
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RW     0x10
  GNU_RELRO      0x000000000001ff70 0x000000000021ff70 0x000000000021ff70
                 0x0000000000001090 0x0000000000001090  R      0x1

ELF 段类型

表 2-1 ELF 段类型

名称
PT_NULL0
PT_LOAD1
PT_DYNAMIC2
PT_INTERP3
PT_NOTE4
PT_SHLIB5
PT_PHDR6
PT_TLS7
PT_LOOS0x60000000
PT_SUNW_UNWIND0x6464e550
PT_SUNW_EH_FRAME0x6474e550
PT_LOSUNW0x6ffffffa
PT_SUNWBSS0x6ffffffa
PT_SUNWSTACK0x6ffffffb
PT_SUNWDTRACE0x6ffffffc
PT_SUNWCAP0x6ffffffd
PT_HISUNW0x6fffffff
PT_HIOS0x6fffffff
PT_LOPROC0x70000000
PT_HIPROC0x7fffffff
/* Legal values for p_type (segment type).  */

#define PT_NULL     0       /* Program header table entry unused */
#define PT_LOAD     1       /* Loadable program segment */
#define PT_DYNAMIC  2       /* Dynamic linking information */
#define PT_INTERP   3       /* Program interpreter */
#define PT_NOTE     4       /* Auxiliary information */
#define PT_SHLIB    5       /* Reserved */
#define PT_PHDR     6       /* Entry for header table itself */
#define PT_TLS      7       /* Thread-local storage segment */
#define PT_NUM      8       /* Number of defined types */
#define PT_LOOS     0x60000000  /* Start of OS-specific */
#define PT_GNU_EH_FRAME 0x6474e550  /* GCC .eh_frame_hdr segment */
#define PT_GNU_STACK    0x6474e551  /* Indicates stack executability */
#define PT_GNU_RELRO    0x6474e552  /* Read-only after relocation */
#define PT_LOSUNW   0x6ffffffa
#define PT_SUNWBSS  0x6ffffffa  /* Sun Specific segment */
#define PT_SUNWSTACK    0x6ffffffb  /* Stack segment */
#define PT_HISUNW   0x6fffffff
#define PT_HIOS     0x6fffffff  /* End of OS-specific */
#define PT_LOPROC   0x70000000  /* Start of processor-specific */
#define PT_HIPROC   0x7fffffff  /* End of processor-specific */

PT_NULL

未使用。没有定义成员值。使用此类型,程序头表可以包含忽略的项。

PT_LOAD

指定可装入段,通过 p_filesz 和 p_memsz 进行描述。文件中的字节会映射到内存段的起始位置。如果段的内存大小 (p_memsz) 大于文件大小 (p_filesz),则将多余字节的值定义为 0这些字节跟在段的已初始化区域后面。文件大小不能大于内存大小。程序头表中的可装入段的各项按升序显示,并基于 p_vaddr 成员进行排列。

PT_DYNAMIC

指定动态链接信息。请参见动态节。

PT_INTERP

指定要作为解释程序调用的以空字符结尾的路径名的位置和大小。对于动态可执行文件,必须设置此类型。此类型可出现在共享目标文件中。此类型不能在一个文件中多次出现。此类型(如果存在)必须位于任何可装入段的各项的前面。有关详细信息,请参见程序的解释程序。

PT_NOTE

指定辅助信息的位置和大小。有关详细信息,请参见注释节。

PT_SHLIB

保留类型,但具有未指定的语义。

PT_PHDR

指定程序头表在文件及程序内存映像中的位置和大小。此段类型不能在一个文件中多次出现。此外,仅当程序头表是程序内存映像的一部分时,才可以出现此段。此类型(如果存在)必须位于任何可装入段的各项的前面。有关详细信息,请参见程序的解释程序。

PT_TLS

指定线程局部存储模板。有关详细信息,请参见线程局部存储节。

PT_LOOS - PT_HIOS

此范围内包含的值保留用于特定于操作系统的语义。

PT_SUNW_UNWIND

此段包含栈扩展表。

PT_SUNW_EH_FRAME

此段包含栈扩展表。PT_SUNW_EH_FRAME 与 PT_SUNW_EH_UNWIND 等效。

PT_LOSUNW - PT_HISUNW

此范围内包含的值(包括这两个值)保留用于特定于 Sun 的语义。

PT_SUNWBSS

与 PT_LOAD 元素相同的属性,用于描述 .SUNW_bss 节。

PT_SUNWSTACK

描述进程栈。只能存在一个 PT_SUNWSTACK 元素。仅访问权限(如 p_flags 字段中所定义)有意义。

PT_SUNWDTRACE

保留供 dtrace(1M) 内部使用。

PT_SUNWCAP

指定功能要求。有关详细信息,请参见功能节。

PT_LOPROC - PT_HIPROC

此范围内包含的值(包括这两个值)保留用于特定于处理器的语义。

注 - 除非在其他位置具体要求,否则所有程序头的段类型都是可选的。文件的程序头表只能包含与其内容相关的那些元素。

基本地址

可执行文件和共享目标文件都有一个基本地址,该地址是与程序目标文件的内存映像关联的最低虚拟地址。基本地址的其中一种用途是在动态链接过程中重定位程序的内存映像。

可执行文件或共享目标文件的基本地址是在执行过程中通过以下三个值计算得出的:内存装入地址、最大页面大小和程序可装入段的最低虚拟地址。程序头中的虚拟地址可能并不表示程序内存映像的实际虚拟地址。请参见程序装入(特定于处理器)。

要计算基本地址,首先需要确定与 PT_LOAD 段的最低 p_vaddr 值关联的内存地址。然后,将内存地址截断为最大页面大小的最接近倍数,从而获取基本地址。根据装入内存的文件的类型,内存地址可能与 p_vaddr 值不匹配。

段权限

系统要装入的程序必须至少包含一个可装入段,即使文件格式并不要求此限制也是如此。系统创建可装入段的内存映像时,将会授予如 p_flags 成员中所指定的访问权限。PF_MASKPROC 掩码中包括的所有位都保留用于特定于处理器的语义。

表 2-2 ELF 段标志

名称含义
PF_X0x1执行
PF_W0x2
PF_R0x4
PF_MASKPROC0xf0000000未指定

如果权限位是 0,则会拒绝该位的访问类型。实际内存权限取决于内存管理单元,该单元可随系统的不同而变化。尽管所有标志组合均有效,但系统仍可授予比请求更多的访问权限。不过,如果不显式指定写权限,则段在任何情况下都不会具有该权限。下表列出了确切的标志解释及允许的标志解释。

表 2-3 ELF 段权限

标志确切解释允许解释
0拒绝所有访问拒绝所有访问
PF_X1仅执行读、执行
PF_W2只写读、写、执行
PF_W+PF_X3写、执行读、写、执行
PF_R4只读读、执行
PF_R+PF_X5读、执行读、执行
PF_R+PF_W6读、写读、写、执行
PF_R+PF_W+PF_X7读、写、执行读、写、执行

段内容

目标文件段由一节或多节组成,但此事实对程序头是透明的。另外,无论文件段包含一节还是包含多节,对程序装入都没有实际意义。但是,必须存在各种数据以便执行程序、进行动态链接等操作。下图使用一般术语说明了段内容。段中各节的顺序和成员关系可能会有所变化。

文本段包含只读指令和数据。数据段包含可写数据和指令。有关所有特殊节的列表,请参见表 12-10

PT_DYNAMIC 程序头元素指向 .dynamic 节。.got 和 .plt 节还包含与位置无关的代码和动态链接的相关信息。

.plt 可以位于文本或数据段中,具体取决于处理器。有关详细消息,请参见全局偏移表(特定于处理器)过程链接表(特定于处理器)

类型为 SHT_NOBITS 的节不占用文件空间,但构成段的内存映像。通常,这些未初始化的数据驻留在段尾,从而使 p_memsz 大于关联程序头元素中的 p_filesz。

程序装入(特定于处理器)

https://docs.oracle.com/cd/E26926_01/html/E25910/chapter6-34713.html#scrolltoc

系统创建或扩充进程映像时,系统会以逻辑方式将文件的段复制到虚拟内存段。系统以物理方式读取文件的时间和可能性取决于程序的执行行为、系统负载等。

除非进程在执行过程中引用了逻辑页,否则进程不需要物理页。进程通常会保留许多页面不对其进行引用。因此,延迟物理读取可以提高系统性能。要实际达到这种效率,可执行文件和共享目标文件必须具有文件偏移和虚拟地址同余(以页面大小为模数)的段映像。

32 位段的虚拟地址和文件偏移对模数 64 K (0x10000) 同余。64 位段的虚拟地址和文件偏移对模数 1 MB (0x100000) 同余。通过将各段与最大页面大小对齐,无论物理页大小如何,文件都适合进行换页。

缺省情况下,64 位 SPARC 程序与 0x100000000 的起始地址链接。整个程序位于 4 GB 以上的地址空间内,包括其文本、数据、堆、栈和共享目标文件依赖项。这有助于确保 64 位程序正确,因为如果程序截断其任何指针,则程序在其最低有效的 4 GB 地址空间中将出现错误。尽管 64 位程序在 4 GB 以上的地址空间内进行链接,但您仍可以使用 mapfile 和链接编辑器的 -M 选项,链接 4 GB 以下的地址空间内的程序。请参见 /usr/lib/ld/sparcv9/map.below4G。

下图显示了 SPARC 版本的可执行文件。

图 2-1 SPARC: 可执行文件(64 K 对齐)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-x8rZCb7E-1636806374054)(_v_images/20211102150040127_464.png)]

表 2-4 SPARC: ELF 程序头段(64 K 对齐)

成员文本数据
p_typePT_LOADPT_LOAD
p_offset0x00x4000
p_vaddr0x100000x24000
p_paddr未指定未指定
p_filesize0x3a820x4f5
p_memsz0x3a820x10a4
p_flagsPF_R+PF_XPF_R+PF_W+PF_X
p_align0x100000x10000

下图显示了 x86 版本的可执行文件。

图 2-2 32 位 x86: 可执行文件(64 K 对齐)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NTEPcEgz-1636806374054)(_v_images/20211102150144604_25460.png)]

下表定义了上图中可装入段的各元素。

表 2-5 32 位 x86: ELF 程序头段(64 K 对齐)

成员文本数据
p_typePT_LOADPT_LOAD
p_offset0x00x4000
p_vaddr0x80500000x8064000
p_paddr未指定未指定
p_filesize0x32fd0x3a0
p_memsz0x32fd0xdc4
p_flagsPF_R+PF_XPF_R+PF_W+PF_X
p_align0x100000x10000

https://docs.oracle.com/cd/E26926_01/html/E25910/chapter6-34713.html#scrolltoc

TODO

程序的解释程序

启动动态链接的动态可执行文件或共享目标文件可以包含一个 PT_INTERP 程序头元素。在 exec(2) 过程中,系统将从 PT_INTERP 段检索路径名,并通过解释程序文件段创建初始进程映像。解释程序负责从系统接收控制并为应用程序提供环境。

在 Oracle Solaris OS 中,解释程序称为运行时链接程序,即 ld.so.1(1)

运行时链接程序

创建启动动态链接的动态目标文件时,链接编辑器将向可执行文件中添加一个类型为 PT_INTERP 的程序头元素。该元素指示系统将运行时链接程序作为程序的解释程序进行调用。exec(2) 和运行时链接程序进行协作,为程序创建进程映像。

链接编辑器可为可执行文件和共享目标文件构造协助运行时链接程序运行的各种数据。这些数据位于可装入段中,从而使数据在执行过程中可用。这些段包括:

  • 类型为 SHT_DYNAMIC 的 .dynamic 节,其中包含各种数据。位于该节起始位置的结构包含其他动态链接信息的地址。
  • 类型为 SHT_PROGBITS 的 .got 和 .plt 节,其中分别包含以下两个表:全局偏移表和过程链接表。以下各节说明了运行时链接程序如何使用和更改这些表,以便为目标文件创建内存映像。
  • 类型为 SHT_HASH 的 .hash 节,其中包含符号散列表。

共享目标文件可以占用虚拟内存地址,这些虚拟内存地址与文件的程序头表中记录的地址不同。运行时链接程序会重定位内存映像,从而在应用程序获取控制权之前更新绝对地址。

动态节

如果目标文件参与动态链接,则其程序头表将包含一个类型为 PT_DYNAMIC 的元素。此段包含 .dynamic 节。特殊符号 _DYNAMIC 用于标记包含以下结构的数组的节。请参见 sys/link.h。

/* Dynamic section entry.  */

typedef struct
{
  Elf32_Sword   d_tag;          /* Dynamic entry type */
  union
    {
      Elf32_Word d_val;         /* Integer value */
      Elf32_Addr d_ptr;         /* Address value */
    } d_un;
} Elf32_Dyn;                                                                                                                 

typedef struct
{
  Elf64_Sxword  d_tag;          /* Dynamic entry type */
  union
    {
      Elf64_Xword d_val;        /* Integer value */
      Elf64_Addr d_ptr;         /* Address value */
    } d_un;
} Elf64_Dyn;

对于此类型的每个目标文件,d_tag 将控制 d_un 的解释。

d_val

这些目标文件表示具有各种解释的整数值。

d_ptr

这些目标文件表示程序虚拟地址。在执行过程中,文件虚拟地址可能与内存虚拟地址不匹配。对动态结构中包含的地址进行解释时,运行时链接程序会根据原始文件值和内存基本地址来计算实际地址。为确保一致性,文件不应包含用于更正动态结构中的地址的重定位项。

通常,每个动态标记的值决定了 d_un 联合的解释。借助此约定,第三方工具可进行更简单的动态标记解释。值为偶数的标记表示使用 d_ptr 的动态节项。值为奇数的标记表示使用 d_val 的动态节项,或该标记既不使用 d_ptr,也不使用 d_val。值包含在以下特殊兼容性范围中的标记不遵循这些规则。第三方工具必须逐项明确处理这些例外范围。

  • 值小于特定值 DT_ENCODING 的标记。
  • 值位于 DT_LOOS 和 DT_SUNW_ENCODING 之间的标记。
  • 值位于 DT_HIOS 和 DT_LOPROC 之间的标记。

下表概述了可执行文件和共享目标文件的标记要求。如果某标记带有强制标志,则动态链接数组必须包含此类型的项。同样,可选表示该标记的项可以出现但不是必需的。

表 13-8 ELF 动态数组标记

名称d_un可执行文件共享目标文件
DT_NULL0已忽略强制强制
DT_NEEDED1d_val可选可选
DT_PLTRELSZ2d_val可选可选
DT_PLTGOT3d_ptr可选可选
DT_HASH4d_ptr强制强制
DT_STRTAB5d_ptr强制强制
DT_SYMTAB6d_ptr强制强制
DT_RELA7d_ptr强制可选
DT_RELASZ8d_val强制可选
DT_RELAENT9d_val强制可选
DT_STRSZ10d_val强制强制
DT_SYMENT11d_val强制强制
DT_INIT12d_ptr可选可选
DT_FINI13d_ptr可选可选
DT_SONAME14d_val已忽略可选
DT_RPATH15d_val可选可选
DT_SYMBOLIC16已忽略已忽略可选
DT_REL17d_ptr强制可选
DT_RELSZ18d_val强制可选
DT_RELENT19d_val强制可选
DT_PLTREL20d_val可选可选
DT_DEBUG21d_ptr可选已忽略
DT_TEXTREL22已忽略可选可选
DT_JMPREL23d_ptr可选可选
DT_BIND_NOW24已忽略可选可选
DT_INIT_ARRAY25d_ptr可选可选
DT_FINI_ARRAY26d_ptr可选可选
DT_INIT_ARRAYSZ27d_val可选可选
DT_FINI_ARRAYSZ28d_val可选可选
DT_RUNPATH29d_val可选可选
DT_FLAGS30d_val可选可选
DT_ENCODING32未指定未指定未指定
DT_PREINIT_ARRAY32d_ptr可选已忽略
DT_PREINIT_ARRAYSZ33d_val可选已忽略
DT_MAXPOSTAGS34未指定未指定未指定
DT_LOOS0x6000000d未指定未指定未指定
DT_SUNW_AUXILIARY0x6000000dd_ptr未指定可选
DT_SUNW_RTLDINF0x6000000ed_ptr可选可选
DT_SUNW_FILTER0x6000000ed_ptr未指定可选
DT_SUNW_CAP0x60000010d_ptr可选可选
DT_SUNW_SYMTAB0x60000011d_ptr可选可选
DT_SUNW_SYMSZ0x60000012d_val可选可选
DT_SUNW_ENCODING0x60000013未指定未指定未指定
DT_SUNW_SORTENT0x60000013d_val可选可选
DT_SUNW_SYMSORT0x60000014d_ptr可选可选
DT_SUNW_SYMSORTSZ0x60000015d_val可选可选
DT_SUNW_TLSSORT0x60000016d_ptr可选可选
DT_SUNW_TLSSORTSZ0x60000017d_val可选可选
DT_SUNW_CAPINFO0x60000018d_ptr可选可选
DT_SUNW_STRPAD0x60000019d_val可选可选
DT_SUNW_CAPCHAIN0x6000001ad_ptr可选可选
DT_SUNW_LDMACH0x6000001bd_val可选可选
DT_SUNW_CAPCHAINENT0x6000001dd_val可选可选
DT_SUNW_CAPCHAINSZ0x6000001fd_val可选可选
DT_HIOS0x6ffff000未指定未指定未指定
DT_VALRNGLO0x6ffffd00未指定未指定未指定
DT_CHECKSUM0x6ffffdf8d_val可选可选
DT_PLTPADSZ0x6ffffdf9d_val可选可选
DT_MOVEENT0x6ffffdfad_val可选可选
DT_MOVESZ0x6ffffdfbd_val可选可选
DT_POSFLAG_10x6ffffdfdd_val可选可选
DT_SYMINSZ0x6ffffdfed_val可选可选
DT_SYMINENT0x6ffffdffd_val可选可选
DT_VALRNGHI0x6ffffdff未指定未指定未指定
DT_ADDRRNGLO0x6ffffe00未指定未指定未指定
DT_CONFIG0x6ffffefad_ptr可选可选
DT_DEPAUDIT0x6ffffefbd_ptr可选可选
DT_AUDIT0x6ffffefcd_ptr可选可选
DT_PLTPAD0x6ffffefdd_ptr可选可选
DT_MOVETAB0x6ffffefed_ptr可选可选
DT_SYMINFO0x6ffffeffd_ptr可选可选
DT_ADDRRNGHI0x6ffffeff未指定未指定未指定
DT_RELACOUNT0x6ffffff9d_val可选可选
DT_RELCOUNT0x6ffffffad_val可选可选
DT_FLAGS_10x6ffffffbd_val可选可选
DT_VERDEF0x6ffffffcd_ptr可选可选
DT_VERDEFNUM0x6ffffffdd_val可选可选
DT_VERNEED0x6ffffffed_ptr可选可选
DT_VERNEEDNUM0x6fffffffd_val可选可选
DT_LOPROC0x70000000未指定未指定未指定
DT_SPARC_REGISTER0x70000001d_val可选可选
DT_AUXILIARY0x7ffffffdd_val未指定可选
DT_USED0x7ffffffed_val可选可选
DT_FILTER0x7fffffffd_val未指定可选
DT_HIPROC0x7fffffff未指定未指定未指定
#define DT_NULL		0		/* Marks end of dynamic section */
#define DT_NEEDED	1		/* Name of needed library */
#define DT_PLTRELSZ	2		/* Size in bytes of PLT relocs */
#define DT_PLTGOT	3		/* Processor defined value */
#define DT_HASH		4		/* Address of symbol hash table */
#define DT_STRTAB	5		/* Address of string table */
#define DT_SYMTAB	6		/* Address of symbol table */
#define DT_RELA		7		/* Address of Rela relocs */
#define DT_RELASZ	8		/* Total size of Rela relocs */
#define DT_RELAENT	9		/* Size of one Rela reloc */
#define DT_STRSZ	10		/* Size of string table */
#define DT_SYMENT	11		/* Size of one symbol table entry */
#define DT_INIT		12		/* Address of init function */
#define DT_FINI		13		/* Address of termination function */
#define DT_SONAME	14		/* Name of shared object */
#define DT_RPATH	15		/* Library search path (deprecated) */
#define DT_SYMBOLIC	16		/* Start symbol search here */
#define DT_REL		17		/* Address of Rel relocs */
#define DT_RELSZ	18		/* Total size of Rel relocs */
#define DT_RELENT	19		/* Size of one Rel reloc */
#define DT_PLTREL	20		/* Type of reloc in PLT */
#define DT_DEBUG	21		/* For debugging; unspecified */
#define DT_TEXTREL	22		/* Reloc might modify .text */
#define DT_JMPREL	23		/* Address of PLT relocs */
#define DT_RUNPATH	29		/* Library search path */
#define DT_FLAGS	30		/* Flags for the object being loaded */
#define DT_ENCODING	32		/* Start of encoded range */
#define DT_PREINIT_ARRAY 32		/* Array with addresses of preinit fct*/
#define DT_PREINIT_ARRAYSZ 33		/* size in bytes of DT_PREINIT_ARRAY */
#define DT_SYMTAB_SHNDX	34		/* Address of SYMTAB_SHNDX section */
#define DT_LOOS		0x6000000d	/* Start of OS-specific */
#define DT_HIOS		0x6ffff000	/* End of OS-specific */
#define DT_LOPROC	0x70000000	/* Start of processor-specific */
#define DT_HIPROC	0x7fffffff	/* End of processor-specific */
#define DT_VALRNGLO	0x6ffffd00
#define DT_GNU_PRELINKED 0x6ffffdf5	/* Prelinking timestamp */
#define DT_GNU_CONFLICTSZ 0x6ffffdf6	/* Size of conflict section */
#define DT_GNU_LIBLISTSZ 0x6ffffdf7	/* Size of library list */
#define DT_CHECKSUM	0x6ffffdf8
#define DT_PLTPADSZ	0x6ffffdf9
#define DT_MOVEENT	0x6ffffdfa
#define DT_MOVESZ	0x6ffffdfb
#define DT_FEATURE_1	0x6ffffdfc	/* Feature selection (DTF_*).  */
#define DT_POSFLAG_1	0x6ffffdfd	/* Flags for DT_* entries, effecting*/
#define DT_SYMINSZ	0x6ffffdfe	/* Size of syminfo table (in bytes) */
#define DT_SYMINENT	0x6ffffdff	/* Entry size of syminfo */
#define DT_VALRNGHI	0x6ffffdff
#define DT_VALTAGIDX(tag)	(DT_VALRNGHI - (tag))	/* Reverse order! */
#define DT_VALNUM 12
#define DT_ADDRRNGLO	0x6ffffe00
#define DT_GNU_HASH	0x6ffffef5	/* GNU-style hash table.  */
#define DT_TLSDESC_PLT	0x6ffffef6
#define DT_TLSDESC_GOT	0x6ffffef7
#define DT_GNU_CONFLICT	0x6ffffef8	/* Start of conflict section */
#define DT_GNU_LIBLIST	0x6ffffef9	/* Library list */
#define DT_CONFIG	0x6ffffefa	/* Configuration information.  */
#define DT_DEPAUDIT	0x6ffffefb	/* Dependency auditing.  */
#define DT_AUDIT	0x6ffffefc	/* Object auditing.  */
#define DT_SYMINFO	0x6ffffeff	/* Syminfo table.  */
#define DT_ADDRRNGHI	0x6ffffeff
#define DT_ADDRTAGIDX(tag)	(DT_ADDRRNGHI - (tag))	/* Reverse order! */
#define DT_ADDRNUM 11
#define DT_VERSYM	0x6ffffff0
#define DT_RELACOUNT	0x6ffffff9
#define DT_RELCOUNT	0x6ffffffa
#define DT_FLAGS_1	0x6ffffffb	/* State flags, see DF_1_* below.  */
#define DT_VERSIONTAGIDX(tag)	(DT_VERNEEDNUM - (tag))	/* Reverse order! */
#define DT_VERSIONTAGNUM 16
#define DT_AUXILIARY    0x7ffffffd      /* Shared object to load before self */
#define DT_FILTER       0x7fffffff      /* Shared object to get values from */
#define DT_EXTRATAGIDX(tag)	((Elf32_Word)-((Elf32_Sword) (tag) <<1>>1)-1)
#define DT_EXTRANUM	3
#define DT_SPARC_REGISTER	0x70000001
#define DT_SPARC_NUM		2
#define DT_MIPS_RLD_VERSION  0x70000001	/* Runtime linker interface version */
#define DT_MIPS_TIME_STAMP   0x70000002	/* Timestamp */
#define DT_MIPS_ICHECKSUM    0x70000003	/* Checksum */
#define DT_MIPS_IVERSION     0x70000004	/* Version string (string tbl index) */
#define DT_MIPS_FLAGS	     0x70000005	/* Flags */
#define DT_MIPS_BASE_ADDRESS 0x70000006	/* Base address */
#define DT_MIPS_MSYM	     0x70000007
#define DT_MIPS_CONFLICT     0x70000008	/* Address of CONFLICT section */
#define DT_MIPS_LIBLIST	     0x70000009	/* Address of LIBLIST section */
#define DT_MIPS_LOCAL_GOTNO  0x7000000a	/* Number of local GOT entries */
#define DT_MIPS_CONFLICTNO   0x7000000b	/* Number of CONFLICT entries */
#define DT_MIPS_LIBLISTNO    0x70000010	/* Number of LIBLIST entries */
#define DT_MIPS_SYMTABNO     0x70000011	/* Number of DYNSYM entries */
#define DT_MIPS_UNREFEXTNO   0x70000012	/* First external DYNSYM */
#define DT_MIPS_GOTSYM	     0x70000013	/* First GOT entry in DYNSYM */
#define DT_MIPS_HIPAGENO     0x70000014	/* Number of GOT page table entries */
#define DT_MIPS_RLD_MAP	     0x70000016	/* Address of run time loader map.  */
#define DT_MIPS_DELTA_CLASS  0x70000017	/* Delta C++ class definition.  */
#define DT_MIPS_DELTA_CLASS_NO    0x70000018 /* Number of entries in*/
#define DT_MIPS_DELTA_INSTANCE    0x70000019 /* Delta C++ class instances.  */
#define DT_MIPS_DELTA_INSTANCE_NO 0x7000001a /* Number of entries in*/
#define DT_MIPS_DELTA_RELOC  0x7000001b /* Delta relocations.  */
#define DT_MIPS_DELTA_RELOC_NO 0x7000001c /* Number of entries in*/
#define DT_MIPS_DELTA_SYM    0x7000001d /* Delta symbols that Delta*/
#define DT_MIPS_DELTA_SYM_NO 0x7000001e /* Number of entries in*/
#define DT_MIPS_DELTA_CLASSSYM 0x70000020 /* Delta symbols that hold the*/
#define DT_MIPS_DELTA_CLASSSYM_NO 0x70000021 /* Number of entries in*/
#define DT_MIPS_CXX_FLAGS    0x70000022 /* Flags indicating for C++ flavor.  */
#define DT_MIPS_PIXIE_INIT   0x70000023
#define DT_MIPS_SYMBOL_LIB   0x70000024
#define DT_MIPS_LOCALPAGE_GOTIDX 0x70000025
#define DT_MIPS_LOCAL_GOTIDX 0x70000026
#define DT_MIPS_HIDDEN_GOTIDX 0x70000027
#define DT_MIPS_PROTECTED_GOTIDX 0x70000028
#define DT_MIPS_OPTIONS	     0x70000029 /* Address of .options.  */
#define DT_MIPS_INTERFACE    0x7000002a /* Address of .interface.  */
#define DT_MIPS_DYNSTR_ALIGN 0x7000002b
#define DT_MIPS_INTERFACE_SIZE 0x7000002c /* Size of the .interface section. */
#define DT_MIPS_RLD_TEXT_RESOLVE_ADDR 0x7000002d /* Address of rld_text_rsolve*/
#define DT_MIPS_PERF_SUFFIX  0x7000002e /* Default suffix of dso to be added*/
#define DT_MIPS_COMPACT_SIZE 0x7000002f /* (O32)Size of compact rel section. */
#define DT_MIPS_GP_VALUE     0x70000030 /* GP value for aux GOTs.  */
#define DT_MIPS_AUX_DYNAMIC  0x70000031 /* Address of aux .dynamic.  */
#define DT_MIPS_PLTGOT	     0x70000032
#define DT_MIPS_RWPLT        0x70000034
#define DT_MIPS_RLD_MAP_REL  0x70000035
#define DT_MIPS_XHASH	     0x70000036
#define DT_MIPS_NUM	     0x37
#define DT_ALPHA_PLTRO		(DT_LOPROC + 0)
#define DT_ALPHA_NUM		1
#define DT_PPC_GOT		(DT_LOPROC + 0)
#define DT_PPC_OPT		(DT_LOPROC + 1)
#define DT_PPC_NUM		2
#define DT_PPC64_GLINK  (DT_LOPROC + 0)
#define DT_PPC64_OPD	(DT_LOPROC + 1)
#define DT_PPC64_OPDSZ	(DT_LOPROC + 2)
#define DT_PPC64_OPT	(DT_LOPROC + 3)
#define DT_PPC64_NUM    4
#define DT_AARCH64_VARIANT_PCS	(DT_LOPROC + 5)
#define DT_AARCH64_NUM		6
#define DT_IA_64_PLT_RESERVE	(DT_LOPROC + 0)
#define DT_IA_64_NUM		1
#define DT_NIOS2_GP             0x70000002 /* Address of _gp.  */

DT_NULL

标记 _DYNAMIC 数组的结尾。

DT_NEEDED

以空字符结尾的字符串的 DT_STRTAB 字符串表偏移,用于提供所需依赖项的名称。动态数组可以包含多个此类型的项。尽管这些项与其他类型的项的关系不重要,但其相对顺序却很重要。请参见共享目标文件依赖项。

DT_PLTRELSZ

与过程链接表关联的重定位项的总大小(以字节为单位)。请参见过程链接表(特定于处理器)。

DT_PLTGOT

与过程链接表或全局偏移表关联的地址。请参见过程链接表(特定于处理器)和全局偏移表(特定于处理器)。

DT_HASH

符号散列表的地址。该表引用 DT_SYMTAB 元素指示的符号表。请参见散列表节。

DT_STRTAB

字符串表的地址。运行时链接程序所需的符号名称、依赖项名称和其他字符串位于该表中。请参见字符串表节。

DT_SYMTAB

符号表的地址。请参见符号表节。

DT_RELA

重定位表的地址。请参见重定位节。

目标文件可以有多个重定位节。为可执行文件或共享目标文件创建重定位表时,链接编辑器会连接这些节以形成一个表。尽管这些节在目标文件中可以保持独立,但运行时链接程序将看到一个表。运行时链接程序为可执行文件创建进程映像或将共享目标文件添加到进程映像中时,运行时链接程序将会读取该重定位表并执行关联操作。

此元素要求同时存在 DT_RELASZ 和 DT_RELAENT 元素。如果文件必须重定位,则可以存在 DT_RELA 或 DT_REL。

DT_RELASZ

DT_RELA 重定位表的总大小(以字节为单位)。

DT_RELAENT

DT_RELA 重定位项的大小(以字节为单位)。

DT_STRSZ

DT_STRTAB 字符串表的总大小(以字节为单位)。

DT_SYMENT

DT_SYMTAB 符号项的大小(以字节为单位)。

DT_INIT

初始化函数的地址。请参见初始化节和终止节。

DT_FINI

终止函数的地址。请参见初始化节和终止节。

DT_SONAME

以空字符结尾的字符串的 DT_STRTAB 字符串表偏移,用于标识共享目标文件的名称。请参见记录共享目标文件名称。

DT_RPATH

以空字符结尾的库搜索路径字符串的 DT_STRTAB 字符串表偏移。此元素的用途已被 DT_RUNPATH 取代。请参见运行时链接程序搜索的目录。

DT_SYMBOLIC

表示目标文件包含在其链接编辑过程中应用的符号绑定。此元素的用途已被 DF_SYMBOLIC 标志取代。请参见使用 -B symbolic 选项。

DT_REL

与 DT_RELA 类似,但其表中包含隐式加数。此元素要求同时存在 DT_RELSZ 和 DT_RELENT 元素。

DT_RELSZ

DT_REL 重定位表的总大小(以字节为单位)。

DT_RELENT

DT_REL 重定位项的大小(以字节为单位)。

DT_PLTREL

表示过程链接表指向的重定位项的类型(DT_REL 或 DT_RELA)。过程链接表中的所有重定位都必须使用相同的重定位项。请参见过程链接表(特定于处理器)。此元素要求同时存在 DT_JMPREL 元素。

DT_DEBUG

用于调试。

DT_TEXTREL

表示一个或多个重定位项可能会要求修改非可写段,并且运行时链接程序可以相应地进行准备。此元素的用途已被 DF_TEXTREL 标志取代。请参见与位置无关的代码。

DT_JMPREL

与过程链接表单独关联的重定位项的地址。请参见过程链接表(特定于处理器)。通过分隔这些重定位项,运行时链接程序可在装入启用了延迟绑定的目标文件时忽略这些项。此元素要求同时存在 DT_PLTRELSZ 和 DT_PLTREL 元素。

DT_POSFLAG_1

应用于紧邻的 DT_ 元素的各种状态标志。请参见下表。

表 2-11 ELF 动态位置标志 DT_POSFLAG_1

名称含义
DF_P1_LAZYLOAD0x1标识延迟装入的依赖项。
DF_P1_GROUPPERM0x2标识组依赖性。
DF_P1_LAZYLOAD

将以下 DT_NEEDED 项标识为要延迟装入的目标文件。此标志通过使用链接编辑器的 -z lazyload 选项记录在目标文件中。请参见延迟装入动态依赖项。

DF_P1_GROUPPERM

将以下 DT_NEEDED 项标识为要作为组装入的目标文件。此标志通过使用链接编辑器的 -z groupperm 选项记录在目标文件中。请参见隔离组。

DT_BIND_NOW

表示在将控制权返回给程序之前,必须处理此目标文件的所有重定位项。通过环境或 dlopen(3C) 指定时,提供的此项优先于使用延迟绑定的指令。此元素的用途已被 DF_BIND_NOW 标志取代。请参见执行重定位的时间。

DT_INIT_ARRAY

初始化函数的指针数组的地址。此元素要求同时存在 DT_INIT_ARRAYSZ 元素。请参见初始化节和终止节。

DT_FINI_ARRAY

终止函数的指针数组的地址。此元素要求同时存在 DT_FINI_ARRAYSZ 元素。请参见初始化节和终止节。

DT_INIT_ARRAYSZ

DT_INIT_ARRAY 数组的总大小(以字节为单位)。

DT_FINI_ARRAYSZ

DT_FINI_ARRAY 数组的总大小(以字节为单位)。

DT_RUNPATH

以空字符结尾的库搜索路径字符串的 DT_STRTAB 字符串表偏移。请参见运行时链接程序搜索的目录。

DT_FLAGS

特定于此目标文件的标志值。请参见下表。

表 2-9 ELF 动态标志 DT_FLAGS

名称含义
DF_ORIGIN0x1要求$ORIGIN处理
DF_SYMBOLIC0x2要求符号解析
DF_TEXTREL0x4存在文本重定位项
DF_BIND_NOW0x8要求非延迟绑定
DF_STATIC_TLS0x10目标文件使用静态线程局部存储方案

DF_ORIGIN

表示目标文件要求 $ORIGIN 处理。请参见查找关联的依赖项

DF_SYMBOLIC

表示目标文件包含在其链接编辑过程中应用的符号绑定。请参见使用-B symbolic 选项

DF_TEXTREL

表示一个或多个重定位项可能会要求修改非可写段,并且运行时链接程序可以相应地进行准备。请参见与位置无关的代码

DF_BIND_NOW

表示在将控制权返回给程序之前,必须处理此目标文件的所有重定位项。通过环境或 dlopen(3C) 指定时,提供的此项优先于使用延迟绑定的指令。请参见执行重定位的时间

DF_STATIC_TLS

表示目标文件包含使用静态线程局部存储方案的代码。在通过 dlopen(3C) 或延迟装入而动态装入的目标文件中,不能使用静态线程局部存储。

DT_ENCODING

大于或等于 DT_ENCODING、小于或等于 DT_LOOS 的动态标记值遵循 d_un 联合的解释规则。

DT_PREINIT_ARRAY

预初始化函数的指针数组的地址。此元素要求同时存在 DT_PREINIT_ARRAYSZ 元素。仅在可执行文件中处理该数组。如果该数组包含在共享目标文件中,则会被忽略。请参见初始化节和终止节。

DT_PREINIT_ARRAYSZ

DT_PREINIT_ARRAY 数组的总大小(以字节为单位)。

DT_MAXPOSTAGS

正动态数组标记值的数量。

DT_LOOS - DT_HIOS

此范围内包含的值(包括这两个值)保留用于特定于操作系统的语义。所有这类值都遵循 d_un 联合的解释规则。

DT_SUNW_AUXILIARY

以空字符结尾的字符串的 DT_STRTAB 字符串表偏移,用于逐符号指定一个或多个辅助 filtee。请参见生成辅助过滤器。

DT_SUNW_RTLDINF

保留供运行时链接程序内部使用。

DT_SUNW_FILTER

以空字符结尾的字符串的 DT_STRTAB 字符串表偏移,用于逐符号指定一个或多个标准 filtee。请参见生成标准过滤器。

DT_SUNW_CAP

功能节的地址。请参见功能节。

DT_SUNW_SYMTAB

符号表的地址,其中包含用于扩充 DT_SYMTAB 所提供的符号的局部函数符号。这些符号始终在紧邻 DT_SYMTAB 所提供的符号之前的位置。请参见符号表节。

DT_SUNW_SYMSZ

DT_SUNW_SYMTAB 和 DT_SYMTAB 提供的符号表的组合大小。

DT_SUNW_ENCODING

大于或等于 DT_SUNW_ENCODING、小于或等于 DT_HIOS 的动态标记值遵循 d_un 联合的解释规则。

DT_SUNW_SORTENT

DT_SUNW_SYMSORT 和 DT_SUNW_TLSSORT 符号排序项的大小(以字节为单位)。

DT_SUNW_SYMSORT

符号表索引数组的地址,这些索引提供对 DT_SUNW_SYMTAB 所引用的符号表中函数和变量符号的排序访问。请参见符号排序节。

DT_SUNW_SYMSORTSZ

DT_SUNW_SYMSORT 数组的总大小(以字节为单位)。

DT_SUNW_TLSSORT

符号表索引数组的地址,这些索引提供对 DT_SUNW_SYMTAB 所引用的符号表中线程局部符号的排序访问。请参见符号排序节。

DT_SUNW_TLSSORTSZ

DT_SUNW_TLSSORT 数组的总大小(以字节为单位)。

DT_SUNW_CAPINFO

符号表索引数组的地址,这些索引提供符号与其功能要求之间的关联。请参见功能节。

DT_SUNW_STRPAD

动态字符串表末尾未使用的保留空间的总大小(以字节为单位)。如果目标文件中不存在 DT_SUNW_STRPAD,则没有保留空间可用。

DT_SUNW_CAPCHAIN

功能系列索引数组的地址。每个索引系列都以 0 项结尾。

DT_SUNW_LDMACH

生成目标文件的链接编辑器的计算机体系结构。DT_SUNW_LDMACH 使用与 ELF 头的 e_machine 字段相同的 EM_ 整数值。请参见ELF 头。DT_SUNW_LDMACH 用于标识生成目标文件的链接编辑器的类(32 位或 64 位)和平台。此信息不会用于运行时链接程序,而仅用于说明目的。

DT_SUNW_CAPCHAINENT

DT_SUNW_CAPCHAIN 项的大小(以字节为单位)。

DT_SUNW_CAPCHAINSZ

DT_SUNW_CAPCHAIN 链的总大小(以字节为单位)。

DT_SYMINFO

符号信息表的地址。此元素要求同时存在 DT_SYMINENT 和 DT_SYMINSZ 元素。请参见Syminfo 表节。

DT_SYMINENT

DT_SYMINFO 信息项的大小(以字节为单位)。

DT_SYMINSZ

DT_SYMINFO 表的总大小(以字节为单位)。

DT_VERDEF

版本定义表的地址。该表中的元素包含字符串表 DT_STRTAB 的索引。此元素要求同时存在 DT_VERDEFNUM 元素。请参见版本定义章节。

DT_VERDEFNUM

DT_VERDEF 表中的项数。

DT_VERNEED

版本依赖性表的地址。该表中的元素包含字符串表 DT_STRTAB 的索引。此元素要求同时存在 DT_VERNEEDNUM 元素。请参见版本依赖性节。

DT_VERNEEDNUM

DT_VERNEEDNUM 表中的项数。

DT_RELACOUNT

表示 RELATIVE 重定位计数,该计数是通过串联所有 Elf32_Rela 或 Elf64_Rela 重定位项生成的。请参见组合重定位节。

DT_RELCOUNT

表示 RELATIVE 重定位计数,该计数是通过串联所有 Elf32_Rel 重定位项生成的。请参见组合重定位节。

DT_AUXILIARY

以空字符结尾的字符串的 DT_STRTAB 字符串表偏移,用于指定一个或多个辅助 filtee。请参见生成辅助过滤器。

DT_FILTER

以空字符结尾的字符串的 DT_STRTAB 字符串表偏移,用于指定一个或多个标准 filtee。请参见生成标准过滤器。

DT_CHECKSUM

目标文件中选定的节的简单校验和。请参见 gelf_checksum(3ELF)。

DT_MOVEENT

DT_MOVETAB 移动项的大小(以字节为单位)。

DT_MOVESZ

DT_MOVETAB 表的总大小(以字节为单位)。

DT_MOVETAB

移动表的地址。此元素要求同时存在 DT_MOVEENT 和 DT_MOVESZ 元素。请参见移动节。

DT_CONFIG

以空字符结尾的字符串的 DT_STRTAB 字符串表偏移,用于定义配置文件。该配置文件仅在可执行文件中有意义,并且通常是特定于此目标文件的。请参见配置缺省搜索路径。

DT_DEPAUDIT

以空字符结尾的字符串的 DT_STRTAB 字符串表偏移,用于定义一个或多个审计库。请参见运行时链接程序审计接口。

DT_AUDIT

以空字符结尾的字符串的 DT_STRTAB 字符串表偏移,用于定义一个或多个审计库。请参见运行时链接程序审计接口。

DT_FLAGS_1

特定于此目标文件的标志值。请参见下表。

名称含义
DF_1_NOW0x1执行完整的重定位处理。
DF_1_GLOBAL0x2未使用。
DF_1_GROUP0x4表示目标文件是组的成员。
DF_1_NODELETE0x8不能从进程中删除目标文件。
DF_1_LOADFLTR0x10确保立即装入filtee。
DF_1_INITFIRST0x20首先进行目标文件初始化。
DF_1_NOOPEN0x40目标文件不能用于dlopen(3C)。
DF_1_ORIGIN0x80要求$ORIGIN处理。
DF_1_DIRECT0x100已启用直接绑定。
DF_1_INTERPOSE0x400目标文件是插入项。
DF_1_NODEFLIB0x800忽略缺省的库搜索路径。
DF_1_NODUMP0x1000不能使用dldump(3C)转储目标文件。
DF_1_CONFALT0x2000目标文件是配置替代项。
DF_1_ENDFILTEE0x4000filtee终止过滤器搜索。
DF_1_DISPRELDNE0x8000已执行位移重定位。
DF_1_DISPRELPND0x10000位移重定位暂挂。
DF_1_NODIRECT0x20000目标文件包含非直接绑定。
DF_1_IGNMULDEF0x40000内部使用。
DF_1_NOKSYMS0x80000内部使用。
DF_1_NOHDR0x100000内部使用。
DF_1_EDITED0x200000目标文件在最初生成后已被修改。
DF_1_NORELOC0x400000内部使用。
DF_1_SYMINTPOSE0x800000存在各个符号插入项。
DF_1_GLOBAUDIT0x1000000建立全局审核。
DF_1_SINGLETON0x2000000存在单件符号。
DF_1_NOW

表示在将控制权返回给程序之前,必须处理此目标文件的所有重定位项。通过环境或 dlopen(3C) 指定时,提供的此标志优先于使用延迟绑定的指令。请参见执行重定位的时间

DF_1_GROUP

表示目标文件是组的成员。此标志通过链接编辑器的 -B group 选项记录在目标文件中。请参见目标文件分层结构

DF_1_NODELETE

表示不能从进程中删除目标文件。如果使用 dlopen(3C) 通过直接或依赖性方式将目标文件装入进程,则无法使用 dlopen(3C) 卸载该目标文件。此标志通过使用链接编辑器的 -z nodelete 选项记录在目标文件中。

DF_1_LOADFLTR

仅对过滤器有意义。表示立即处理所有关联 filtee。此标志通过使用链接编辑器的 -z loadfltr 选项记录在目标文件中。请参见filtee 处理

DF_1_INITFIRST

表示在装入其他任何目标文件之前首先运行此目标文件的初始化节。此标志仅适用于专用系统库,并通过使用链接编辑器的 -z initfirst 选项记录在目标文件中。

DF_1_NOOPEN

表示无法使用 dlopen(3C) 将目标文件添加到正在运行的进程。此标志通过使用链接编辑器的 -z nodlopen 选项记录在目标文件中。

DF_1_ORIGIN

表示目标文件要求 $ORIGIN 处理。请参见查找关联的依赖项

DF_1_DIRECT

表示目标文件应使用直接绑定信息。请参见第 9 章

DF_1_INTERPOSE

表示目标文件符号表将在除主装入目标文件(通常为可执行文件)外的所有符号之前插入。此标志通过使用链接编辑器的 -z interpose 选项进行记录。请参见运行时插入

DF_1_NODEFLIB

表示此目标文件的依赖性搜索会忽略所有缺省的库搜索路径。此标志通过使用链接编辑器的 -z nodefaultlib 选项记录在目标文件中。请参见运行时链接程序搜索的目录。

DF_1_NODUMP

表示此目标文件不通过 dldump(3C) 进行转储。此选项的替代选项包括没有重定位项的目标文件,这些目标文件可能会包括在使用 crle(1) 生成的替代目标文件中。此标志通过使用链接编辑器的 -z nodump 选项记录在目标文件中。

DF_1_CONFALT

将此目标文件标识为 crle(1) 生成的配置替代目标文件。此标志可触发运行时链接程序来搜索配置文件 $ORIGIN/ld.config.app-name。

DF_1_ENDFILTEE

仅对 filtee 有意义。终止对其他任何 filtee 的过滤器搜索。此标志通过使用链接编辑器的 -z endfiltee 选项记录在目标文件中。请参见减少 filtee 搜索。

DF_1_DISPRELDNE

表示此目标文件应用了位移重定位。由于位移重定位记录在应用重定位后被丢弃,因此该目标文件中将不再存在这些记录。请参见位移重定位。

DF_1_DISPRELPND

表示此目标文件暂挂了位移重定位。由于此目标文件中存在位移重定位,因此可在运行时完成重定位。请参见位移重定位。

DF_1_NODIRECT

表示此目标文件包含无法直接绑定的符号。请参见SYMBOL_SCOPE / SYMBOL_VERSION 指令。

DF_1_IGNMULDEF

保留供内核运行时链接程序内部使用。

DF_1_NOKSYMS

保留供内核运行时链接程序内部使用。

DF_1_NOHDR

保留供内核运行时链接程序内部使用。

DF_1_EDITED

表示此目标文件在最初由链接编辑器构造后,已被编辑或被修改。此标志用于警告调试器,某个目标文件在最初生成后进行了更改。

DF_1_NORELOC

保留供内核运行时链接程序内部使用。

DF_1_SYMINTPOSE

表示目标文件包含应在除主装入目标文件(通常为可执行文件)外的所有符号之前插入的各个符号。使用 mapfile 和 INTERPOSE 关键字生成目标文件时记录此标志。请参见SYMBOL_SCOPE / SYMBOL_VERSION 指令。

DF_1_GLOBAUDIT

表示动态可执行文件要求全局审计。请参见记录全局审计程序。

DF_1_SINGLETON

表示目标文件定义或引用 singleton 符号。请参见SYMBOL_SCOPE / SYMBOL_VERSION 指令。

DT_VALRNGLO-DT_VALRNGHI

此范围内包含的值(包括这两个值)使用动态结构的 d_un.d_val 字段。

DT_ADDRRNGLO - DT_ADDRRNGHI

此范围内包含的值(包括这两个值)使用动态结构的 d_un.d_ptr 字段。如果生成 ELF 目标文件后对其进行了任何调整,则必须相应地更新这些项。

DT_SPARC_REGISTER

DT_SYMTAB 符号表中 STT_SPARC_REGISTER 符号的索引。该符号表中的每个 STT_SPARC_REGISTER 符号都存在一个动态项。请参见寄存器符号。

DT_LOPROC - DT_HIPROC

此范围内包含的值(包括这两个值)保留用于特定于处理器的语义。

除动态数组末尾的 DT_NULL 元素以及 DT_NEEDED 和 DT_POSFLAG_1 元素的相对顺序以外,各项可以采用任何顺序显示。未显示在该表中的标记值为保留值。

GOT全局偏移表(特定于处理器)

通常,与位置无关的代码不能包含绝对虚拟地址。全局偏移表在专用数据中包含绝对地址。因此这些地址可用,并且不会破坏程序文本的位置独立性和共享性。程序使用与位置无关的地址来引用其 GOT 并提取绝对值。此方法可将与位置无关的引用重定向到绝对位置。

最初,GOT 包含其重定位项所需的信息。系统为可装入目标文件创建内存段后,运行时链接程序将会处理这些重定位项。某些重定位项的类型可以为 R_xxxx_GLOB_DAT,用于引用 GOT。

运行时链接程序可确定关联符号值,计算其绝对地址以及将相应的内存表各项设置为正确的值。尽管链接编辑器创建目标文件时绝对地址未知,但运行时链接程序知道所有内存段的地址,因此可以计算其中包含的符号的绝对地址。

如果程序要求直接访问某符号的绝对地址,则该符号将具有一个 GOT 项。由于可执行文件和共享目标文件具有不同的 GOT,因此一个符号的地址可以出现在多个表中。运行时链接程序在向进程映像中的任何代码授予控制权之前,将首先处理所有的 GOT 重定位项。此处理操作可确保绝对地址在执行过程中可用。

表项零保留用于存储动态结构(使用符号 _DYNAMIC 引用)的地址。使用此符号,运行时链接程序等程序可在尚未处理其重定位项的情况下查找各自的动态结构。此方法对于运行时链接程序尤其重要,因为它必须对自身进行初始化,而不依赖于其他程序来重定位其内存映像。

系统可为不同程序中的同一共享目标文件选择不同的内存段地址。系统甚至可以为同一程序的不同执行方式选择不同的库地址。但是,一旦建立进程映像,内存段即不会更改各地址。只要存在进程,其内存段就会位于固定的虚拟地址。

GOT 的格式和解释是特定于处理器的。符号 GLOBAL_OFFSET_TABLE 可用于访问该表。此符号可以位于 .got 节的中间,以提供地址数组的负下标和非负下标。对于 32 位代码,符号类型是 Elf32_Addr 数组;对于 64 位代码,符号类型是 Elf64_Addr 数组。

extern  Elf32_Addr  _GLOBAL_OFFSET_TABLE_[];
extern  Elf64_Addr  _GLOBAL_OFFSET_TABLE_[];

PLT过程链接表(特定于处理器)

https://docs.oracle.com/cd/E26926_01/html/E25910/chapter6-1235.html#scrolltoc

全局偏移表可将与位置无关的地址计算结果转换为绝对位置。同样,过程链接表也可将与位置无关的函数调用转换为绝对位置。链接编辑器无法解析不同动态目标文件之间的执行传输(如函数调用)。因此,链接编辑器会安排程序将控制权转移给过程链接表中的各项。这样,运行时链接程序就会重定向各项,而不会破坏程序文本的位置独立性和共享性。可执行文件和共享目标文件包含不同的过程链接表。

32 位 SPARC: 过程链接表

TODO

64 位 SPARC: 过程链接表

TODO

32 位 x86: 过程链接表

对于 32 位 x86 动态目标文件,过程链接表位于共享文本中,但使用专用全局偏移表中的地址。运行时链接程序可确定目标的绝对地址,并相应地修改全局偏移表的内存映像。这样,运行时链接程序就会重定向各项,而不会破坏程序文本的位置独立性和共享性。可执行文件和共享目标文件包含不同的过程链接表。

表 13-14 32 位 x86: 绝对过程链接表示例

.PLT0:
    pushl   got_plus_4
    jmp     *got_plus_8
    nop;    nop
    nop;    nop
.PLT1:
    jmp     *name1_in_GOT
    pushl   $offset
    jmp     .PLT0@PC
.PLT2:
    jmp     *name2_in_GOT
    pushl   $offset
    jmp     .PLT0@PC

表 13-15 32 位 x86: 与位置无关的过程链接表示例

.PLT0:
    pushl   4(%ebx)
    jmp     *8(%ebx)
    nop;    nop
    nop;    nop
.PLT1:
    jmp     *name1@GOT(%ebx)
    pushl   $offset
    jmp     .PLT0@PC
.PLT2:
    jmp     *name2@GOT(%ebx)
    pushl   $offset
    jmp     .PLT0@PC

注 - 如前面的示例所示,对于绝对代码和与位置无关的代码,过程链接表指令会使用不同的操作数寻址模式。但是,它们的运行时链接程序接口却相同。

以下步骤介绍了运行时链接程序和程序如何通过过程链接表和全局偏移表来协作解析符号引用。

  1. 初始创建程序的内存映像时,运行时链接程序会将全局偏移表中的第二项和第三项设置为特殊值。以下步骤说明了这些值。
  2. 如果过程链接表与位置无关,则全局偏移表的地址必须位于 %ebx 中。进程映像中的每个共享目标文件都有各自的过程链接表,并且控制权仅转移给位于同一目标文件内的过程链接表项。因此,调用函数在调用过程链接表项之前,必须首先设置全局偏移表基本寄存器。
  3. 例如,该程序会调用 name1,以将控制权转移给标签 .PLT1。
  4. 第一条指令会跳至全局偏移表项中对应于 name1 的地址。最初,全局偏移表包含以下 pushl 指令的地址,而不是 name1 的实际地址。
  5. 该程序将在栈中推送一个重定位偏移 (offset)。该重定位偏移是重定位表中一个 32 位的非负字节偏移。指定的重定位项的类型为 R_386_JMP_SLOT,其偏移指定了前面的 jmp 指令中使用的全局偏移表项。该重定位项还包含符号表索引,以供运行时链接程序用于获取引用的符号 name1。
  6. 推送该重定位偏移后,程序将跳至过程链接表中的第一项 .PLT0。pushl 指令会在栈中推送全局偏移表的第二项(got_plus_4 或 4(%ebx))的值,从而为运行时链接程序提供一个字的标识信息。然后,程序将跳至全局偏移表的第三项(got_plus_8 或 8(%ebx))中的地址,以继续跳至运行时链接程序。
  7. 运行时链接程序将展开栈、检查指定的重定位项、获取符号的值、在全局偏移项表中存储 name1 的实际地址并跳至目标。
  8. 过程链接表项的后续执行结果会直接传输给 name1,而不会再次调用运行时链接程序。位于 .PLT1 的 jmp 指令将跳至 name1,而不是对 pushl 指令失败。

LD_BIND_NOW 环境变量可更改动态链接行为。如果其值不为空,则运行时链接程序会在将控制权转移给程序之前处理 R_386_JMP_SLOT 重定位项。

x64: 过程链接表

对于 x64 动态目标文件,过程链接表位于共享文本中,但使用专用全局偏移表中的地址。运行时链接程序可确定目标的绝对地址,并相应地修改全局偏移表的内存映像。这样,运行时链接程序就会重定向各项,而不会破坏程序文本的位置独立性和共享性。可执行文件和共享目标文件包含不同的过程链接表。

表 13-16 x64: 过程链接表示例

.PLT0:
    pushq   GOT+8(%rip)                         # GOT[1]
    jmp     *GOT+16(%rip)                       # GOT[2]
    nop;    nop
    nop;    nop
.PLT1:
    jmp     *name1@GOTPCREL(%rip)               # 16 bytes from .PLT0
    pushq   $index1
    jmp     .PLT0
.PLT2:
    jmp     *name2@GOTPCREL(%rip)               # 16 bytes from .PLT1
    pushl   $index2
    jmp     .PLT0

以下步骤介绍了运行时链接程序和程序如何通过过程链接表和全局偏移表来协作解析符号引用。

  1. 初始创建程序的内存映像时,运行时链接程序会将全局偏移表中的第二项和第三项设置为特殊值。以下步骤说明了这些值。
  2. 进程映像中的每个共享目标文件都有各自的过程链接表,并且控制权仅转移给位于同一目标文件内的过程链接表项。
  3. 例如,该程序会调用 name1,以将控制权转移给标签 .PLT1。
  4. 第一条指令会跳至全局偏移表项中对应于 name1 的地址。最初,全局偏移表包含以下 pushq 指令的地址,而不是 name1 的实际地址。
  5. 该程序将在栈中推送一个重定位索引 (index1)。该重定位索引是重定位表中一个 32 位的非负索引。重定位表由 DT_JUMPREL 动态节项标识。指定的重定位项的类型为 R_AMD64_JMP_SLOT,其偏移指定了前面的 jmp 指令中使用的全局偏移表项。该重定位项还包含符号表索引,以供运行时链接程序用于获取引用的符号 name1。
  6. 推送该重定位索引后,程序将跳至过程链接表中的第一项 .PLT0。pushq 指令会在栈中推送全局偏移表的第二项 (GOT+8) 的值,从而为运行时链接程序提供一个字的标识信息。然后,程序将跳至第三个全局偏移表项 (GOT+16) 中的地址,以继续跳至运行时链接程序。
  7. 运行时链接程序将展开栈、检查指定的重定位项、获取符号的值、在全局偏移项表中存储 name1 的实际地址并跳至目标。
  8. 过程链接表项的后续执行结果会直接传输给 name1,而不会再次调用运行时链接程序。位于 .PLT1 的 jmp 指令将跳至 name1,而不是对 pushq 指令失败。

LD_BIND_NOW 环境变量可更改动态链接行为。如果其值不为空,则运行时链接程序会在将控制权转移给程序之前处理 R_AMD64_JMP_SLOT 重定位项。

线程局部存储

https://docs.oracle.com/cd/E26926_01/html/E25910/chapter8-1.html#scrolltoc

编译环境支持声明线程局部数据。此类数据有时称为线程特定数据或线程专用数据,但更多时候以首字母缩略词 TLS 表示。通过将变量声明为线程局部变量,编译器可自动安排针对每个线程分配这些变量。

提供对此功能的内置支持有三个目的。

  • 提供生成 POSIX 接口的基础,此接口用于分配线程特定数据。
  • 提供一种方便高效的机制,以便应用程序和库直接使用线程局部变量。
  • 执行循环并行优化时,编译器可以根据需要分配 TLS。

C/C++ 编程接口

__thread int i;
__thread char *p;
__thread struct state s;

线程局部存储节

编译时分配的线程局部数据的独立副本必须与各个执行线程关联。要提供此数据,要使用 TLS 节指定大小和初始内容。编译环境在 SHF_TLS 标志标识的节中分配 TLS。这些节根据存储的声明方式提供已初始化的 TLS 和未初始化的 TLS。

  • 已初始化的线程局部变量分配在 .tdata.tdata1 节中。此初始化可能需要重定位。
  • 未初始化的线程局部变量定义为 COMMON 符号。最终分配在 .tbss 节中进行。

在分配了任何已初始化的节后会立即分配未初始化的节,并进行填充以便正确对齐。合并的节一起构成 TLS 模板,每次创建新线程时,都会使用此模板分配 TLS。此模板的已初始化部分称为 TLS 初始化映像。所有因已初始化的线程局部变量而生成的重定位将应用于此模板。当新线程需要初始值时,将使用重定位的值。

TLS 符号的符号类型为 STT_TLS。这些符号被指定了相对于 TLS 模板开头的偏移。与这些符号关联的实际虚拟地址与此无关。地址仅指向模板,而不指向每个数据项的每线程副本。在动态可执行文件和共享目标文件中,对于已定义符号,STT_TLS 符号的 st_value 字段包含指定的 TLS 偏移。对于未定义的符号,此字段包含零。

定义了多个重定位以支持访问 TLS。请参见SPARC: 线程局部存储的重定位类型、32 位 x86: 线程局部存储的重定位类型和x64: 线程局部存储的重定位类型。通常,TLS 重定位引用 STT_TLS 类型的符号。TLS 重定位还可以引用与 GOT 项关联的局部节符号。在这种情况下,指定的 TLS 偏移存储在关联的 GOT 项中。

对于根据 TLS 项进行的重定位,重定位地址在 TLS 模板的末尾编码为负偏移。计算该偏移时,首先将模板大小舍入到 32 位目标文件中最接近的 8 字节边界,然后舍入为 64 位目标文件中最接近的 16 字节边界。此舍入操作确保静态 TLS 模板合理对齐以便可用于任何用途。

在动态可执行文件和共享目标文件中,PT_TLS 程序项用于描述 TLS 模板。此模板包含以下成员:

表 14-1 ELF PT_TLS 程序头项

成员
p_offsetTLS初始化映像的文件偏移
p_vaddrTLS初始化映像的虚拟内存地址
p_paddr0
p_fileszTLS初始化映像的大小
p_memszTLS模板的总大小
p_flagsPF_R
p_alignTLS模板的对齐方式

线程局部存储的运行时分配

https://docs.oracle.com/cd/E26926_01/html/E25910/gentextid-22613.html#scrolltoc

在程序的生命周期中,会在三个时间创建 TLS。

  • 程序启动时。
  • 创建新线程时。
  • 程序启动后装入共享目标文件之后,线程第一次引用 TLS 块时。

运行时线程局部数据存储的布局如图 14-1 中所示。

图 14-1 线程局部存储的运行时存储布局
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OKmzJNSi-1636806374055)(_v_images/20211102155227032_17532.png)]

TODO

参考链接

附录


Copyright (C) CESTC Com.
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值