【问题标题】:use gcc to directly compile to machine code without linking使用gcc直接编译成机器码,无需链接
【发布时间】:2018-12-12 07:28:08
【问题描述】:

我想让 gcc 为我将 c 代码编译成 x86-32 linux 二进制代码,但周围没有任何库。 我只想在开始时指定一个地址,它应该假设它已经在那里加载。然后,我将手动从输出中构建一个 elf 文件并设置所有内容。

我知道如何使用 NASM 做这样的事情,但我有一些更复杂的想法,我不想只使用汇编程序。我不需要任何库,我将使用带有内联 asm 的纯系统调用。我也不在乎它是否会失去一些便携性。

我试了一下,但找不到办法。 有人不仅可以为我提供正确的设置,还可以提供一些关于编译和链接器参数的背景信息吗? 我尝试搜索 gcc 手册,但发现它非常混乱。

【问题讨论】:

  • gcc -c 编译 + 汇编但不链接。但听起来你真正需要的是一个链接器脚本来生成一个平面二进制文件而不是一个 ELF 可执行文件。或 objcopy 将链接的可执行文件的一部分复制到平面二进制文件。
  • 我尝试了 objcopy,但它只是给了我一些虚假信息。我什至无法从 objcopy 给我的文件中的输出文件中找到操作码。另外,我需要地址匹配,或者只有位置无关的代码。所以是的,听起来我确实需要一些方法来告诉链接器我想要什么
  • 存在链接器脚本来告诉链接器您想要什么。请参阅wiki.osdev.org/Bare_Bones#Linking_the_Kernel,或链接器脚本的许多 Stack Overflow 答案。如果您不想完全使用 PIC,那么是的,您需要将代码链接到静态可执行文件中。

标签: c linux gcc x86


【解决方案1】:

我想让 gcc 为我将 c 代码编译成 x86-32 linux 二进制代码,但周围没有任何库。

这意味着您编写独立 C 代码。 (当标准库可用时,您有一个 托管 环境;如果没有,则有一个 独立 环境。)

编译例如foo.c 到一个可执行文件 foo,确保它有一个 _start() 函数,然后使用

gcc -march=i686 -mtune=generic -m32 -ffreestanding -nostdlib -nostartfiles foo.c -o foo

GNU 工具链使用_start 符号的地址来编码ELF 文件中可执行文件的起始地址。

This answer 是 x86-64 的实际示例。对于 x86-32(或任何其他架构),您需要调整 SYSCALL_ 宏。


在评论中,OP 解释说他们想要二进制 blob,而不是 ELF 可执行文件。

在这种情况下,最好告诉编译器生成一个position independent executable。例如,“blob.c”:

void do_something(int arg)
{
    /* Do something with arg, perhaps a syscall,
       or inline assembly? */
}

void loop_something(int from, int to)
{
    int  arg;

    if (from <= to)
        for (arg = from; arg <= to; arg++)
            do_something(arg);
    else
        for (arg = from; arg <= to; arg--)
            do_something(arg);
}

void _start(void)
{
    loop_something(2, 5);
    do_something(6);
    loop_something(5, 2);
    do_something(1);
}    

我确实建议将除 _start 之外的所有函数声明为 static,以避免任何全局偏移表 (GOT) 或过程链接表 (PLT) 引用(如 &lt;__x86.get_pc_thunk.bx&gt; 调用)。

使用例如将其编译为与位置无关的可执行文件

gcc -march=i686 -mtune=generic -m32 -O2 -fPIE -ffreestanding -nostdlib -nostartfiles blob.c -o blob

剥离它,

strip --strip-all blob

并转储二进制文件的内容:

objdump -fd blob

在这个输出中,有两行很重要:

start address 0x08048120

告诉_start符号的地址,并且

080480e0 <.text>:

以十六进制表示代码的偏移量。后者减去前者(0x08048120 - 0x080480e0 = 0x40 = 64)得到起始符号的偏移量。

最后,将代码转储到原始二进制文件“blob.raw”中

objcopy -O binary -j .text blob blob.raw

【讨论】:

  • 指向正确的方向,但不是我想要的。我不希望它输出可执行文件。我希望它输出一个二进制 blob,只有当它以某种方式映射到指定地址并手动跳转到时,它才会起作用。我不想要一个精灵。编辑:澄清:我想要一些我可以使用十六进制编辑器手动组合的东西
  • 另外,我不需要任何系统调用宏,因为我将使用内联 asm 并手动调用 int 80
  • @Boldar:这正是系统调用宏的含义,内联汇编。
  • @Boldar:我添加了一个示例,说明如何从 C 源文件生成包含已编译位置无关的可执行代码的平面原始二进制文件。
  • 谢谢,这或多或少是我想要的。最后一个奖励:我可以强制执行特定的功能顺序吗?例如。当我有你的例子中的代码时,我可以告诉 gcc 我希望 _start 函数成为第一个输出吗?或者,gcc如何确定输出中函数的顺序?
猜你喜欢
  • 2016-02-12
  • 2019-10-15
  • 2015-01-30
  • 2019-01-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-16
  • 2013-03-01
相关资源
最近更新 更多