【问题标题】:Compiling a flat library?编译平面库?
【发布时间】:2021-08-08 15:32:04
【问题描述】:

由于没有更好的术语,我想将一组函数编译成一个平面库。有很多函数,比如

// add.c
int add (int a, int b) {
    return a + b;
}

// multiply.c
int multiply (int a, int b) {
    int result = 0;

    if (a >= 0)
        for (; a > 0; --a) result = add(result, b);
    else
        for (; a < 0; ++a) result = add(result, b);

    return result;
}

// double.c
int two = 2;
int double_ (int x) {
    return multiply(x, two);
}

编译后的二进制文件应该有

  1. 没有main__start 入口点(它是一个库,而不是可执行文件),
  2. 只有指令和数据,没有标题,
  3. 与位置无关的代码,
  4. 没有外部依赖项(我没有使用任何外部库,但 GCC 似乎总是包含我不需要的标准库内容),并且
  5. 几乎没有填充(即没有过多的空字节用于页面/扇区对齐)

为了能够从二进制文件外部调用函数,我需要知道它们从二进制文件开头的偏移量,在二进制文件的开头有一个跳转表。


使用 GCC 第 3 点和第 4 点可能可以通过 -fPIC-nostdlib 来实现。如果这些函数彼此独立,我可以通过简单地单独编译文件并手动连接它们来实现 5. 这也会给我函数偏移量,但这里的函数不是相互独立的,所以我依靠 GCC用最小的填充将函数缝合在一起。对于第 2 点,可能有一些 objcopy --oformat binary 技巧或类似的东西。但我不知道如何让第 1 点起作用。到目前为止,我在网上找到的每一个指南都用于编译自定义/“hello world”内核,所有这些内核都是可执行文件并具有入口点。如果我不提供入口点ld 抱怨找不到符号__start。此外,我不知道如何获取已编译二进制文件的函数偏移量或如何告诉 GCC 包含一个跳转表(两者中的任何一个都可能)。

关于如何编译上面的示例以使编译的二进制文件满足点 1 到 5 并且可以从二进制文件外部调用(通过偏移量或通过开头的跳转表)有什么想法吗?

【问题讨论】:

    标签: gcc


    【解决方案1】:

    在意识到我的需求有点像为存储空间很小的嵌入式设备编译东西后,我查看了如何为嵌入式设备编译固件,发现可以使用linker scripts 微调链接器。我最终编写了自己的链接器脚本,如下所示:

    // File: link.ld
    OUTPUT(test.bin);
    OUTPUT_FORMAT(binary);
    
    SECTIONS {
        .text 0 : {
            add.o(.text);
            multiply.o(.text);
            double.o(.text);
        }
        /DISCARD/ : {
            *(*)
        }
    }
    

    现在,我可以编译我的源代码了

    gcc -c -fPIC -nostartfiles -nostdlib add.c multiply.c double.c
    ld -M -T link.ld
    

    第一行编译 (-c) 源代码为位置无关 (-fPIC) 目标文件 (*.c -> *.o) 没有标准库 (-nostartfiles -nostdlib),第二行基本上采用目标文件的.text 部分并将它们连接起来,并打印出(-M)输出文件的部分布局,包括所有符号的偏移量。

    【讨论】:

      猜你喜欢
      • 2016-08-17
      • 1970-01-01
      • 2013-03-06
      • 2019-07-22
      • 2014-09-28
      • 2012-03-04
      • 2016-06-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多