Relocation

僕をメンターだと言ってくるのでシステムズには詳しくないのかなと思いつつレビューコメントを書いたら、Good work!とかすぐに言っちゃうポジティブガイになってた。これ、いつも真摯でいいなと思ってる講師を無意識にトレースしてたわ。

私の先生上山瑞が辛抱強く慎重に私に教えてくれて本当に感謝しています!

为什么需要 Relocation?

Relocation 是符号定义连接符号引用的过程。例如,当程序调用函数时,相关联的调用指令必须在执行时将控制转移到正确的目标地址。Relocatable 文件必须具有必需的“重定位条目”,因为它们包含描述如何修改其部分内容的信息,从而允许可执行文件和共享对象文件保存进程程序映像的正确信息。

HOWTO

AVR

Dylan McKay 实现的老版本 lld/lib/ReaderWriter/ELF/AVR AVRRelocationHandler 是模仿的 MipsRelocationHandler,但 NewLLD 架构变化很大,无法直接复制粘贴老版本的代码,而且例如 R_AVR_CALLR_AVR_16_PMR_AVR_LO8_LDI_PM 等 unrecognized reloc 在老版本中还没有被支持,此时也不能“模仿” Mips 的 R_MIPS_CALL16 或其他架构的 reloc Type,所以需要仔细阅读 binutils 的 elf32-XXX 关于 reloc_howto_type 实现方法。

binutils 的 elf32-XXX.c 有一个巨大的 reloc_howto_type elf_XXX_howto_table[],例如 Mips 的:

static reloc_howto_type elf_mips_howto_table_rel[] =                                
{                                                                                   
  ...
  /* 32 bit relocation.  */                                                     
  HOWTO (R_MIPS_32,     /* type */
     0,         /* rightshift */
     2,         /* size (0 = byte, 1 = short, 2 = long) */
     32,            /* bitsize */
     FALSE,         /* pc_relative */
     0,         /* bitpos */
     complain_overflow_dont, /* complain_on_overflow */
     _bfd_mips_elf_generic_reloc, /* special_function */
     "R_MIPS_32",       /* name */
     TRUE,          /* partial_inplace */
     0xffffffff,        /* src_mask */
     0xffffffff,        /* dst_mask */
     FALSE),        /* pcrel_offset */
  ...
}
  
右移0(不用移位),位大小32,源、目标掩码0xffffffff;对应到 NewLLD 这样的(R_MIPS_32)Type 的 relocateOne 这样实现:write32<E>(Loc, Val) 简单到“认为自己把问题想简单了”?!

新しいELFリンカーは古いものとはまったく異なり、新しいものは以前よりもずっと簡単です。 リンカーに新しいアーキテクチャを追加するほうがはるかに簡単です。
上山瑞(Rui Ueyama)

那来一个复杂些的:

  /* 16 bit PC relative reference.  Note that the ABI document has a typo
     and claims R_MIPS_PC16 to be not rightshifted, rendering it useless.
     We do the right thing here.  */
  HOWTO (R_MIPS_PC16,       /* type */
     2,         /* rightshift */
     2,         /* size (0 = byte, 1 = short, 2 = long) */
     16,            /* bitsize */
     TRUE,          /* pc_relative */
     0,         /* bitpos */
     complain_overflow_signed, /* complain_on_overflow */
     _bfd_mips_elf_generic_reloc, /* special_function */
     "R_MIPS_PC16",     /* name */
     TRUE,          /* partial_inplace */
     0x0000ffff,        /* src_mask */
     0x0000ffff,        /* dst_mask */
     TRUE),         /* pcrel_offset */
  
右移2,位大小16,和PC相关,源、目标掩码0x0000ffff;对应的:
template <endianness E, uint8_t BSIZE, uint8_t SHIFT>
static void applyMipsPcReloc(uint8_t *Loc, uint32_t Type, uint64_t V) {
  uint32_t Mask = 0xffffffff >> (32 - BSIZE);
  uint32_t Instr = read32<E>(Loc);
  if (SHIFT > 0)
    checkAlignment<(1 << SHIFT)>(Loc, V, Type);
  checkInt<BSIZE + SHIFT>(Loc, V, Type);
  write32<E>(Loc, (Instr & ~Mask) | ((V >> SHIFT) & Mask));
}
  
那么 R_AVR_CALL 就可以仿造 applyMipsPcReloc<E, 16, 2>(Loc, Type, Val) 的实现:
template <uint8_t BSIZE, uint8_t SHIFT>
static void applyAVRReloc(uint8_t *Loc, uint32_t Type, uint64_t V) {
  uint32_t Mask = 0xffffffff >> (32 - BSIZE);
  uint32_t Instr = read32le(Loc);
  if (SHIFT > 0)
    checkAlignment<(1 << SHIFT)>(Loc, V, Type);
  checkInt<BSIZE + SHIFT>(Loc, V, Type);
  write32le(Loc, (Instr & ~Mask) | ((V >> SHIFT) & Mask));
}
  
希望我没有把复杂问题想简单了:applyAVRReloc<23, 1>(Loc, Type, Val)

其他架构

ARM、AArch64(arm64)比 AVR 复杂很多:

  1. GOT
  2. PLT
  3. TLS
  4. Trunks

参考资料