Skip to content

xexe

· xieyuheng

可执行文件不可避免 ​

我们希望能有简单的 flat 汇编器, 直接编译出来 code + data, 并且用相对当前位置的转跳 offset, 来处理其中指针的相互引用。

但是这是不可能的,原因有二。

首先,现代操作系统要求 code segment 与 data segment 分别 mmap, 因为 code segment 与 data segment 的权限不同, code segment 部分被强制要求是不可写的, 而 data segment 被要求是可写的。 可以使用一个连续的 buffer 来同时保存 code segment 与 data segment, 但是要知道二者的边界才能分别设置权限, 因此,可执行文件中至少要包含这个边界信息。

其次,除了 code segment 与 data segment 之间的相互引用, 还有 code 或 data 到 c 函数的引用,这些函数在加载时才能知道地。 因此可执行文件中至少要包含 relocation table。

可执行文件的设计 ​

c
// - why: every field is 8 bytes,
//   because arm and risc-v require 8 bytes alignment.

// - why: the size of code + data + space segment must be less than 2G,
//   so that relative addressing can work.

struct __attribute__((packed)) xexe_header_t {
  uint8_t magic[8]; // "xexe\0\0\0\0" magic number
  uint8_t machine[8]; // "x86-64\0\0"
  uint64_t version;

  uint64_t code_file_offset;
  uint64_t code_size;
  uint64_t entry_code_segment_offset;

  uint64_t data_file_offset;
  uint64_t data_size;

  uint64_t space_size;

  uint64_t string_table_file_offset;
  uint64_t string_table_size;

  uint64_t label_table_file_offset;
  uint64_t label_table_size;

  uint64_t relocation_table_file_offset;
  uint64_t relocation_table_size;
};

固定的三个 segment:

  • code segment -- 保存可执代码。
  • data segment -- 保存编译时数据。
  • space segment -- 为运行时数据预留空间,可执行文件中只需要记录 size。

string table -- 记录可执行文件中出现的所有字符串。 一段连续的区域,保存 \0 结尾的 c string。

c
// - note: string in string table must be unique,
//   so that we can compare string by pointer.

typedef uint64_t xexe_string_t; // offset to string table

label table

保存 code、data、space 三个 segment 之中,label 到地址的映射。

c
typedef uint64_t xexe_segment_kind_t;

#define XEXE_CODE_SEGMENT 0
#define XEXE_DATA_SEGMENT 1
#define XEXE_SPACE_SEGMENT 2

struct __attribute__((packed)) xexe_label_entry_t {
  xexe_string_t name;
  xexe_segment_kind_t segment_kind;
  uint64_t segment_offset;
};

relocation table

记录信息以明确在 load 时如何把数据回填到 segment 中留空的位置。

c
struct __attribute__((packed)) xexe_relocation_entry_t {
  xexe_string_t type;
  xexe_string_t name;
  xexe_segment_kind_t segment_kind;
  uint64_t segment_offset;
};

type 和 name 决定了如何在 load 过程中获得数据以回填到 segment 中。

汇编语言 ​

汇编语言在于控制。

通过 exe 格式,来控制 loader 的行为,也是控制中的一部分。

通过 (relocation <type> <name>) 来创建 xexe_relocation_entry_t。