📄 elf.txt
字号:
R_386_JMP_SLOT 7 word32 S
R_386_RELATIVE 8 word32 B + A
R_386_GOTOFF 9 word32 S + A - GOT
R_386_GOTPC 10 word32 GOT + A - P
有的重定位类型有不同于简单计算的语义。
* R_386_GOT32
这种重定位类型计算全局偏移表基地址到符号的全局偏移表
入口之间的间隔。这样另外通知了 link editor 建立一个全局偏移表 。
* R_386_PLT32
这种重定位类型计算符号的过程链接表入口地址,并另外通知链接器建立一个
过程链接表。
* R_386_COPY
链接器创建该重定位类型用于动态链接。它的偏移成员涉及一个可写段中的一个
位置。符号表索引指定一个可能存在于当前 object file 或在一个shared object
中的符号。在执行过程中,动态链接器把和 shared object 符号相关的数据
拷贝到该偏移所指定的位置。
* R_386_GLOB_DAT
这种重定位类型用于设置一个全局偏移表入口为指定符号的地址。该特定的重定位
类型允许你决定符号和全局偏移表入口之间的一致性。
* R_386_JMP_SLOT {*}
链接器创建该重定位类型用于动态链接。其偏移成员给出了一个过程链接表入口的
位置。动态链接器修改该过程链接表入口以便向特定的符号地址传递控制。
[参阅第二部分中的 "Procedure Linkage Table(过程链接表)"]
* R_386_RELATIVE
链接器创建该重定位类型用于动态链接。其偏移成员给出了包含表达相关地址值
的一个 shared object 中的位置。动态链接器计算相应的虚拟地址(把该
shared object 装载地址和相对地址相加)。该类型的重定位入口必须为
符号表索引指定为 0 。
* R_386_GOTOFF
这种重定位类型计算符号值和全局偏移表地址之间的不同。另外还通知链接器
建立全局偏移表(GOT)。
* R_386_GOTPC
这种重定位类型类似于 R_386_PC32 ,不同的是它在计算中使用全局偏移表。
这种重定位中引用的符号通常是 _GLOBAL_OFFSET_TABLE_ ,该符号通知了
链接器建立全局偏移表(GOT)。
________________________________________________________________
2. PROGRAM LOADING AND DYNAMIC LINKING
程序装入和动态链接
________________________________________________________________
======================== Introduction(介绍) =========================
第二部分描述了 object file 信息和创建运行程序的系统行为。其中部分信息
适合所有的系统,其他信息是和特定处理器相关的。
可执行和共享的 object file 静态的描绘了程序。为了执行这样的程序,系统
用这些文件创建动态的程序表现,或进程映像。一个进程映像有用于保存其代码、
数据、堆栈等等的段。这个部分的主要章节讨论如下的内容。
* 程序头(Program header)。该章节补充第一部分,描述和程序运行相关的
object file 结构。即文件中主要的数据结构、程序头表、定位段映像,也
包含了为该程序创建内存映像所需要的信息。
* 载入程序(Program loading)。在给定一个 object file 时,系统为了
让它运行必须将它载入内存。
* 动态链接(Dynamic linking)。在载入了程序之后,系统必须通过解决组
成该进程的 object file之间的符号引用问题来完成进程映像的过程。
注意:指定了处理器范围的 ELF 常量是有命名约定的。比如,DT_ , PT_ ,
用于特定处理器扩展名,组合了处理器的名称(如 DT_M32_SPECIAL )。
没有使用这种约定但是预先存在的处理器扩展名是允许的。
Pre-existing Extensions
(预先存在的扩展名)
=======================
DT_JMP_REL
====================== Program Header(程序头) ======================
一个可执行的或共享的 object file 的程序头表是一个结构数组,每一个
结构描述一个段或其他系统准备执行该程序所需要的信息。一个 object file
段包含一个或多个部分(就象下面的“段目录”所描述的那样)。程序头仅仅对于
可执行或共享的 object file 有意义。一个文件使用 ELF 头的 e_phentsize
和 e_phnum 成员来指定其拥有的程序头大小。[参阅 第一部分中的 "ELF 头"]
+ Figure 2-1: Program Header
typedef struct {
Elf32_Word p_type;
Elf32_Off p_offset;
Elf32_Addr p_vaddr;
Elf32_Addr p_paddr;
Elf32_Word p_filesz;
Elf32_Word p_memsz;
Elf32_Word p_flags;
Elf32_Word p_align;
} Elf32_Phdr;
* p_type
该成员指出了这个数组的元素描述了什么类型的段,或怎样解释该数组元素的信息。
类型值和含义如下所述。
* p_offset
该成员给出了该段的驻留位置相对于文件开始处的偏移。
* p_vaddr
该成员给出了该段在内存中的首字节地址。
* p_paddr
在物理地址定位有关联的系统中,该成员是为该段的物理地址而保留的。由于
System V 忽略了应用程序的物理地址定位,该成员对于可执行文件和共享的
object 而言是未指定内容的。
* p_filesz
该成员给出了文件映像中该段的字节数;它可能是 0 。
* p_memsz
该成员给出了内存映像中该段的字节数;它可能是 0 。
* p_flags
该成员给出了和该段相关的标志。定义的标志值如下所述。
* p_align
就象在后面“载入程序”部分中所说的那样,可载入的进程段必须有合适的
p_vaddr 、 p_offset 值,取页面大小的模。该成员给出了该段在内存和
文件中排列值。 0 和 1 表示不需要排列。否则, p_align 必须为正的 2 的幂,
并且 p_vaddr 应当等于 p_offset 模 p_align 。
某些入口描述了进程段;其他的则提供补充信息并且无益于进程映像。已经
定义的入口可以以任何顺序出现,除非是下面明确声明的。后面是段类型值;
其他的值保留以便将来用于其他用途。
+ Figure 2-2: Segment Types, p_type
Name Value
==== =====
PT_NULL 0
PT_LOAD 1
PT_DYNAMIC 2
PT_INTERP 3
PT_NOTE 4
PT_SHLIB 5
PT_PHDR 6
PT_LOPROC 0x70000000
PT_HIPROC 0x7fffffff
* PT_NULL
该数组元素未使用;其他的成员值是未定义的。这种类型让程序头表忽略入口。
* PT_LOAD
该数组元素指定一个可载入的段,由 p_filesz 和 p_memsz 描述。文件中
字节被映射到内存段中。如果该段的内存大小( p_memsz )比文件大小( p_filesz )
要大,则多出的字节将象段初始化区域那样保持为 0 。文件的大小不会比内存大小值大。
在程序头表中,可载入段入口是以 p_vaddr 的升序排列的。
* PT_DYNAMIC
该数组元素指定动态链接信息。参阅 后面的“动态部分”以获得更多信息。
* PT_INTERP
该数组元素指定一个 null-terminated 路径名的位置和大小(作为解释程序)。
这种段类型仅仅对可执行文件有意义(尽管它可能发生在一个共享 object 上);
它在一个文件中只能出现一次。如果它出现,它必须先于任何一个可载入段入口。
参阅 后面的“程序解释器”(Program Interpreter)以获得更多的信息。
* PT_NOTE
该数组元素指定辅助信息的位置和大小。参阅 后面的“注意部分”以获得细节。
* PT_SHLIB
该段类型保留且具有未指定的语义。具有一个这种类型数组元素的程序并不
遵守 ABI 。
* PT_PHDR
该数组元素(如果出现),指定了程序头表本身的位置和大小(包括在文件中
和在该程序的内存映像中)。更进一步来说,它仅仅在该程序头表是程序内存映像
的一部分时才有效。如果它出现,它必须先于任何可载入段入口。参阅 后面的
“程序解释器”(Program Interpreter)以获得更多的信息。
* PT_LOPROC through PT_HIPROC
该范围中的值保留用于特定处理器的语义。
注意:除非在别处的特殊要求,所有的程序头的段类型是可选的。也就是说,
一个文件的程序头表也许仅仅包含和其内容相关的元素。
Base Address(基地址)
可执行和共享的 object file 有一个基地址,该基地址是与程序的 object file
在内存中映像相关的最低虚拟地址。基地址的用途之一是在动态链接过程中重定位
该程序的内存映像。
一个可执行的 object file 或 一个共享的 object file 的基地址是在
执行的时候从三个值计算而来的:内存载入地址、页面大小的最大值 和 程序可
载入段的最低虚拟地址。就象在“程序载入”中所描述的那样,程序头中的虚拟地址
也许和程序的内存映像中实际的虚拟地址并不相同。为了计算基地址,必须确定与
PT_LOAD 段 p_vaddr 的最小值相关的内存地址。获得基地址的方法是将内存
地址截去最大页面大小的最接近的整数倍。由于依赖载入内存中的文件类型,
该内存地址和 p_vaddr 值可能匹配也可能不匹配。
就象在第一部分中 "Section" 中描述的那样, .bss section 具有 SHT_NOBITS
的类型。尽管在文件中不占用空间,它在段的内存映像中起作用。通常,没有初始化
的数据驻留在段尾,因此使得在相关的程序头元素中的 p_memsz 比 p_filesz 大。
Note Section(注解部分)
有的时候供应商或系统设计者需要用特定的信息标记一个
object file 以便其他程序检查其兼容的一致性,等等此类。 SHT_NOTE
类型的 section 和 PT_NOTE 类型的程序头元素能够被用于此目的。 section
和程序头中的注解信息包含了任意数目的入口,每一个入口的格式都是对应于特定
处理器格式的 4-字节数组。下面的标签有助于解释注释信息的组织形式,但是这些
标签不是规格说明的一部分。
+ Figure 2-3: Note Information
namesz
descsz
type
name ...
desc ...
* namesz and name
名字中 namesz 的第一个字节包含了一个 null-terminated 字符
表达了该入口的拥有者或始发者。没有正式的机制来避免名字冲突。从
惯例来说,供应商使用他们自己的名称,比如 "XYZ Computer Company" ,
作为标志。如果没有提供名字, namesz 值为 0 。 如果有必要,确定
描述信息4-字节对齐。 这样的填充信息并不包含在namesz 中。
* descsz and desc
desc 中 descsz 的首字节包含了注解描述符。ABI 不会在一个描述符内容中
放入任何系统参数。如果没有描述符, descsz 将为 0 。 如果有必要,确定
描述信息4-字节对齐。 这样的填充信息并不包含在descsz中。
* type
该 word 给出了描述符的解释。每一个创造着(originator) 控制着自己的类型;
对于单单一个类型值的多种解释是可能存在的。因此,一个程序必须辨认出该名字
和其类型以便理解一个描述符。这个时候的类型必须是非负的。ABI 没有定义
描述符的含义。
为了举例说明,下面的解释段包含两个入口。
+ Figure 2-4: Example Note Segment
+0 +1 +2 +3
-------------------
namesz 7
descsz 0 No descriptor
type 1
name X Y Z spc
C o \0 pad
namesz 7
descsz 8
type 3
name X Y Z spc
C o \0 pad
desc word0
word1
注意:系统保留的注解信息没有名字 (namesz==0) ,有一个零长度的名字
(name[0]=='\0') 现在还没有类型为其定义。所有其他的名字必须至少有
一个非空的字符。
注意:注解信息是可选的。注解信息的出现并不影响一个程序的 ABI 一致性,
前提是该信息不影响程序的执行行为。否则,该程序将不遵循 ABI 并将出现
未定义的行为。
===================== Program Loading(程序载入) =====================
当创建或增加一个进程映像的时候,系统在理论上将拷贝一个文件的段到一个虚拟
的内存段。系统什么时候实际地读文件依赖于程序的执行行为,系统载入等等。一个
进程仅仅在执行时需要引用逻辑页面的时候才需要一个物理页面,实际上进程通常会
留下许多未引用的页面。因此推迟物理上的读取常常可以避免这些情况,改良系统的
特性。为了在实践中达到这种效果,可执行的和共享的 object file 必须具有
合适于页面大小取模值的文件偏移和虚拟地址这样条件的段映像。
虚拟地址和文件偏移在 SYSTEM V 结构的段中是模 4KB(0x1000) 或大
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -