Executable Linkable Format

ELF 和 “dl” 没有本质区别

  • 当然,有海量工程实践上的细节
  • 与 99.9% 的程序员无关

“状态机初始状态” → 能够可视化

  • ldd - Print shared object dependencies
    • SEE ALSO 一直是手册里的宝藏
      • 也许有一个更好的 “知识网络”?
    • 指向了 ld.so (8)

更多的细节

对于一个动态链接的二进制文件,execve 后的第一条指令在哪里?

  • 这种问题,再也不需要老师教了
    • What are the first a few steps exected after execve() of a ELF dynamic link binary?
    • How can I compile an ELF binary that use an alternative dynamic loader than the default ld.so?
      • 我们立即可以做一个实验,看 GPT 给我们的答案对不对
    • 从此 “知识” 不再是壁垒和禁区
  • 然后 readelf 的输出就能 “看得懂” 了

重新思考 PLT 的设计

puts@PLT:
  endbr64
  bnd jmpq *GOT[n]  // *offset(%rip)
  • What is the bnd prefix for jmp instruction in x86-64?
    • Intel MPX (一个偶然的机会,立即进入现代操作系统的领域)

一个有趣 (且根本) 的问题

  • 库函数调用看起来 “很浪费”
    • 连续的跳转
  • 为什么不在加载时执行静态链接?
    • 把指令中的立即数替换成跳转地址,这样就避免了查表?
    • (这么做有什么影响?)

代码解决了,数据呢?

如果我自己的共享库要使用数据?

extern FILE *stdout;
extern char *__lib_private;
  • 对于 stdout,无论多少库,都只有一个副本,必须查表
  • 对于 buf,可以直接翻译成 PC 相对寻址

这个问题 GPT-4 答错了

  • -fPIC 默认会为所有 extern 数据增加一层间接访问
    • 此时共享库中只有它的地址,需要重定位
    • 可以通过 __attribute__((visibility("hidden"))) 控制
      • 所以库需要额外的编码规范来组织变量