Funny Little Executable

Funny Little Executable

2024 南京大学《操作系统:设计与实现》
Funny Little Executable

被《计算机系统基础》支配的恐惧?

2024 南京大学《操作系统:设计与实现》
Funny Little Executable

和 ELF 搏斗的每一年

你期末复习的时候吐血了吗?

  • 同学 A: 吐了
  • 同学 B: 吐了
    • “连 CSAPP 这一章都讲得不怎么样”

不只是你们,我们也在吐血

  • 任课教师 A: 第一次打开 ppt 真的有点吐血的感觉
  • 任课教师 B: 根本讲不完,我估计听懂的人也不多
  • 任课教师 C (我自己):太好了,再也不用教了

我们面对的挑战:到底要不要读手册呢?

2024 南京大学《操作系统:设计与实现》
Funny Little Executable

为什么?

反思

  • ELF 不是一个人类友好的 “状态机数据结构描述”
  • 为了性能,彻底违背了可读 (“信息局部性”) 原则

几乎让你去读一个内存数据结构的 core dump

  • 地狱笑话:今天的 core dump 是个 ELF 文件

center

2024 南京大学《操作系统:设计与实现》
Funny Little Executable

但曾经,并不是这样的

UNIX a.out “assembler output”

  • 一个相对平坦的数据结构
struct exec {
    uint32_t  a_midmag;  // Machine ID & Magic
    uint32_t  a_text;    // Text segment size
    uint32_t  a_data;    // Data segment size
    uint32_t  a_bss;     // BSS segment size
    uint32_t  a_syms;    // Symbol table size
    uint32_t  a_entry;   // Entry point
    uint32_t  a_trsize;  // Text reloc table size
    uint32_t  a_drsize;  // Data reloc table size
};
  • 功能太少 (不支持动态链接、调试信息、内存对齐、thread-local……),自然被淘汰
2024 南京大学《操作系统:设计与实现》
Funny Little Executable

换句话说……

支持的特性越多,越不人类友好

  • 听到 “程序头表”、“节头表”,大脑需要额外转译
  • 含义隐晦的 R_X86_64_32, R_X86_64_PLT32
  • 大量的 “指针” (人类无法阅读的偏移量)
    • (我竟然已经被训练成基本可以正常阅读了 😂)

人类友好的方式

  • 越 “平坦”,越容易理解
  • 所有需要的信息都立即可见
2024 南京大学《操作系统:设计与实现》
Funny Little Executable

给你一次重新设计的机会

那就设计一个 FLE 吧

  • Funny (Fluffy) Linkable Executable
  • Friendly Learning Executable ← 🤖 给我的

核心设计思路

  • 一切都对人类直接可读 (所有信息都在局部)
  • 回归链接和加载中的核心概念:代码、符号、重定位
    • 你们会怎么设计?
2024 南京大学《操作系统:设计与实现》
Funny Little Executable

🔥 😭 ✅ ✨ 🫶 😂 😊

代码 (🔢)、符号 (📤)、重定位 (❓)

  • 凑齐这三要素,我们就可以做可执行文件了!
🔢: ff ff ff ff ff ff ff
🔢: ff ff ff ff ff ff ff
📤: _start
🔢: 48 c7 c0 3c 00 00 00
🔢: 48 c7 c7 2a 00 00 00
             ^
             |
          This byte is return code (42).
🔢: 0f 05 ff ff ff ff ff
🔢: ff ff ff ff ff ff ff
❓: i32(unresolved_symbol - 0x4 - 📍)
2024 南京大学《操作系统:设计与实现》
Funny Little Executable

实现 FLE Binutils

实现的工具集

  • exec (加载)
  • objdump/readfle/nm (显示)
  • cc/as (编译)
  • ld (链接)

大部分都复用自 GNU binutils

  • elf_to_fle
2024 南京大学《操作系统:设计与实现》
Funny Little Executable

生成可执行文件 (1):预编译 & 编译

源代码 (.c) → 源代码 (.i)

  • Ctrl-C & Ctrl-V (#include)
  • 字符串替换
  • 今天:我们有过程宏

源代码 (.i) → 汇编代码 (.s)

  • “高级状态机” 到 “低级状态机” 的翻译
  • 最终生成带标注的指令序列
2024 南京大学《操作系统:设计与实现》
Funny Little Executable

生成可执行文件 (2):汇编

汇编代码 (.s) → 目标文件 (.o)

  • 文件 = sections (.text, .data, .rodata.str.1, ...)
    • 对于 ELF,每个 section 有它的权限、内存对齐等信息
  • section 中的三要素
    • 代码 (字节序列)
    • 符号:标记 “当前” 的位置
    • 重定位:暂时不能确定的数值 (链接时确定)
      • Quick Quiz: ELF 中全局和局部符号有什么区别?还有其他类型的符号吗?
2024 南京大学《操作系统:设计与实现》
Funny Little Executable

生成可执行文件 (3):(静态) 链接

多个目标文件 (.o) → 可执行文件 (a.out)

  • 合并所有的 sections
    • 分别合并 .text, .data, .bss 中的代码
    • 把 sections “平铺” 成字节序列
    • 确定所有符号的位置
    • 解析全部重定位
  • 得到一个可执行文件
    • (程序初始内存状态的描述)
2024 南京大学《操作系统:设计与实现》
Funny Little Executable

FLE 程序:加载

把 “字节序列” 搬到内存

  • 没错,就只做这一件事
  • 然后设置正确的 PC,开始运行
mem = mmap.mmap(
    fileno=-1, length=len(bs),
    prot=mmap.PROT_READ | mmap.PROT_WRITE | mmap.PROT_EXEC,
    flags=mmap.MAP_PRIVATE | mmap.MAP_ANONYMOUS,
)
mem.write(bs)
mem.flush()
call_pointer(mem, fle['symbols']['_start'])
2024 南京大学《操作系统:设计与实现》
Funny Little Executable

#! - Shebang

小彩蛋

  • 我们的 FLE 文件是可以直接执行

    #!./exec
    

UNIX 对 # 注释的 “妙用”

  • file.bin:

    #!A B C
    
    • 操作系统会执行 execve(A, ["A", "B C", "file.bin"], envp)
2024 南京大学《操作系统:设计与实现》