当我们引用函数 invoker 时:
形象地比喻:
举例说明:
假设我们有一个 C 程序,其中定义了一个函数 invoker:
void invoker() {
// 函数体
}
编译器在编译这个文件时,会生成一个 .o 文件,其中包含:
当链接器将多个 .o 文件链接成一个可执行文件时,会将所有的符号表合并,并创建一个全局的哈希表。这样,当程序运行时,动态链接器就可以通过哈希表快速找到所需的符号。
总结:
符号表、字符串表和哈希表是 ELF 文件中非常重要的组成部分,它们共同保证了程序的正确链接和执行。理解它们之间的关系有助于我们更好地理解编译链接的过程。
struct Elf64_Phdr {
PT p_type; //段的类型,如 PT_LOAD 表示可加载段
PF p_flags; //段的属性,例如可读、可写、可执行
Elf64_Off p_offset; // 段在文件中的偏移
Elf64_Addr p_vaddr; //段在虚拟内存中的起始地址
Elf64_Addr p_paddr; //段在物理内存中的起始地址(一般与虚拟地址相同)
Elf64_Xword p_filesz; //段在文件中的大小
Elf64_Xword p_memsz; //段在内存中的大小
Elf64_Xword p_align; //段的对齐要求
if (p_offset >= 0 && p_filesz > 0 && (p_offset + p_filesz) <= std::mem::size() && p_filesz <= std::mem::size())
u8 p_data[p_filesz] @ p_offset [[sealed]];
};
enum SHT : Elf32_Word {
NULL = 0x00, // 空段,通常作为占位符。
PROGBITS = 0x01, // 程序数据,包含可执行代码、已初始化数据或其他程序数据。
SYMTAB = 0x02, // 符号表,包含定义和使用的符号信息。
STRTAB = 0x03, // 字符串表,包含其他段使用的字符串。
RELA = 0x04, // 带有附加信息的重定位条目,用于动态链接。
HASH = 0x05, // 符号哈希表,用于高效的符号查找。
DYNAMIC = 0x06, // 动态链接信息,用于加载和链接共享库。
NOTE = 0x07, // 包含任意形式的注释数据
NOBITS = 0x08, // 没有内容的段,但在文件中占位。
REL = 0x09, // 不带附加信息的重定位条目。
SHLIB = 0x0A, // 预留给共享库使用
DYNSYM = 0x0B, // 动态符号表,用于动态链接。
UNKNOWN12 = 0x0C,
UNKNOWN13 = 0x0D,
INIT_ARRAY = 0x0E, //程序初始化或终止时调用的函数指针数组
FINI_ARRAY = 0x0F, //程序初始化或终止时调用的函数指针数组
PREINIT_ARRAY = 0x10, //程序初始化或终止时调用的函数指针数组
GROUP = 0x11, //段分组信息。
SYMTAB_SHNDX = 0x12, //符号表中节名称的索引。
GNU_INCREMENTAL_INPUTS = 0x6FFF4700,
GNU_ATTRIBUTES = 0x6FFFFFF5,
GNU_HASH = 0x6FFFFFF6,
GNU_LIBLIST = 0x6FFFFFF7,
CHECKSUM = 0x6FFFFFF8,
SUNW_move = 0x6FFFFFFA,
SUNW_COMDAT = 0x6FFFFFFB,
SUNW_syminfo = 0x6FFFFFFC,
GNU_verdef = 0x6FFFFFFD,
GNU_verneed = 0x6FFFFFFE,
GNU_versym = 0x6FFFFFFF,
ARM_EXIDX = 0x70000001,
ARM_PREEMPTMAP = 0x70000002,
ARM_ATTRIBUTES = 0x70000003,
ARM_DEBUGOVERLAY = 0x70000004,
ARM_OVERLAYSECTION = 0x70000005,
};
enum PT : Elf32_Word {
NULL = 0x00, // 无效段,通常用作占位符
LOAD = 0x01, // 可加载段。这个段会被加载到内存中,并可以被执行或读写。代码段和数据段通常都是 LOAD 类型。
DYNAMIC = 0x02, // 动态链接信息段。包含了动态链接器所需的信息,如动态符号表、重定位信息等
INTERP = 0x03, // 解释器段。指定了动态链接器的路径。
NOTE = 0x04, // 附注段。包含了一些额外的信息,比如构建信息、版权信息等。
SHLIB = 0x05, // 预留给共享库使用。
PHDR = 0x06, // 程序头表段。包含了程序头表本身的信息。
TLS = 0x07, // 线程局部存储段。用于存储线程私有的数据。
LOOS = 0x60000000, // 系统特定的段类型,范围从 LOOS 到 HIOS。
HIOS = 0x6FFFFFFF,
GNU_EH_FRAME = PT::LOOS + 0x474E550, //用于异常处理的段。
GNU_STACK = PT::LOOS + 0x474E551, //指示栈的增长方向和属性。
GNU_RELRO = PT::LOOS + 0x474E552, // 只读重定位段。
GNU_PROPERTY = PT::LOOS + 0x474E553, //属性段。包含一些额外的属性信息。
SUNWBSS = 0x6FFFFFFA, //SunOS 系统特定的段类型。
SUNWSTACK = 0x6FFFFFFB, //SunOS 系统特定的段类型。
ARM_ARCHEXT = 0x70000000, // ARM 架构特定的段类型。
ARM_UNWIND = 0x70000001, // ARM 架构特定的段类型。
};