本页目录

链接器脚本与重定位

覆盖: ld script 语法 → relocation types → GOT/PLT relocation → linker relaxation → LLD vs GNU ld vs gold → R_AARCH64_ADR_GOT_PAGE 适用: binutils ld, LLD, 静态/动态链接

重定位: 链接器的工作

编译时 (gcc -c):
  .o 中有 RELA entries → 记录了"这个地址需要填充, 目标符号是这个"
  例: R_X86_64_PLT32: 这个 4-byte offset 需要填入符号的 PLT 地址

链接时 (ld):
  遍历所有 .o 的 RELA → 解析符号 → 填充地址
  如果是动态符号 → 生成 .rela.plt (运行时由 ld.so 解析)

常见重定位类型

// x86-64 (AMD64):
R_X86_64_64:        直接 64-bit 绝对地址
R_X86_64_PC32:      32-bit PC 相对偏移 (call/jmp target)
R_X86_64_PLT32:     类似 PC32 但目标在 PLT 中
R_X86_64_GOTPCREL:  GOT 条目的 PC 相对偏移
R_X86_64_COPY:      将共享库中的变量复制到主程序 .bss

// ARM64 (AArch64):
R_AARCH64_ABS64:    绝对地址
R_AARCH64_ADR_PREL_PG_HI21:  PC 相对, page 粒度 (ADR)
R_AARCH64_ADD_ABS_LO12_NC:   页内偏移 (ADD)
R_AARCH64_LDST64_ABS_LO12_NC: 加载/存储, 页内偏移
R_AARCH64_ADR_GOT_PAGE:       GOT 条目的 PC 相对, page 粒度 + 加载

ld script: 链接器脚本

/* 默认 ld script: /usr/lib/ldscripts/elf_x86_64.x */

SECTIONS
{
    /* 起始: 代码段 (Read + Execute) */
    . = 0x400000 + SIZEOF_HEADERS;  /* 非 PIE */
    .text : { *(.text.unlikely .text.*_unlikely .text.unlikely.*)
              *(.text.exit .text.exit.*)
              *(.text.startup .text.startup.*)
              *(.text.hot .text.hot.*)
              *(.text .stub .text.* .gnu.linkonce.t.*) }

    /* 只读数据 */
    .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
    . = ALIGN(0x1000);
    __init_array_start = .;
    .init_array : { *(.init_array) }
    __init_array_end = .;

    /* 可读写段 (下一页开始) */
    . = ALIGN(0x1000);
    .data : { *(.data .data.* .gnu.linkonce.d.*) }
    .bss : { *(.bss .bss.* .gnu.linkonce.b.*) }
}

LLD vs GNU ld vs gold

GNU ld (BFD)goldLLD (LLVM)
速度基准2-3x faster3-5x faster
内存更低
LTO支持 (GCC LTO)支持 (GCC LTO)支持 (ThinLTO)
链接器脚本完整支持部分大部分
默认传统不维护未来

Linker Relaxation

某些重定位可以通过"放松"优化:
  RISC-V / ARM64 支持:
    如: R_RISCV_CALL → 链接时能看到目标距离 → 用更短的指令序列
    → call (32-bit offset) → jal (20-bit offset) 如果距离够
    → 代码更短 + 更快

  x86 不支持 relaxation (指令固定大小, 无可优化)

调试

# 查看重定位表
readelf -r file.o

# 查看默认 ld script
ld --verbose | less

# 链接过程追踪
ld -Map output.map -o prog *.o  # 写详细映射文件
gcc -Wl,--trace -o prog *.o     # 打印每个链接的文件

# 未定义的符号
ld -o prog *.o 2>&1 | grep undefined

参考

  • 文档⁠: info ld, ELF spec (System V ABI, Chapter 4)
  • 源码⁠: binutils/ld/, lld/ELF/
  • LWN: "Linker relaxation"

关键词: relocation, R_X86_64_PLT32, R_AARCH64_ADR_GOT_PAGE, ld script, LLD, gold, linker relaxation