我想让 gcc 将我的 c 代码编译成 x86-32 linux 二进制代码,但周围没有任何库。我只想在开始时指定一个地址,它应该假设它已经加载到那里。然后我将手动从输出中构建一个 elf 文件并设置所有内容。
我知道如何使用 NASM 做这样的事情,但我有一些更复杂的事情,我不想只使用汇编程序。我不需要任何库,我将使用带有内联 asm 的纯系统调用。如果它失去一些便携性,我也不太在意。
我尝试了一些,但找不到办法做到这一点。有人不仅可以为我提供正确的设置,还可以为我提供有关编译和链接器参数的一些背景知识吗?我尝试搜索 gcc 手册,但发现它非常混乱。
我想让 gcc 将我的 c 代码编译成 x86-32 linux 二进制代码,但周围没有任何库。
这意味着您编写独立的C 代码。(当标准库可用时,您就有了一个托管环境;如果没有,则是一个独立的环境。)
要将例如 foo.c 编译为可执行文件 foo,请确保它具有_start()函数,然后使用
gcc -march=i686 -mtune=generic -m32 -ffreestanding -nostdlib -nostartfiles foo.c -o foo
Run Code Online (Sandbox Code Playgroud)
GNU 工具链使用_start符号的地址来编码 ELF 文件中可执行文件的起始地址。
此答案是 x86-64 的实际示例。对于 x86-32(或任何其他架构),您需要调整SYSCALL_宏。
在评论中,OP 解释说他们想要一个二进制 blob,而不是一个 ELF 可执行文件。
在这种情况下,最好告诉编译器生成位置无关的可执行文件。例如,'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);
}
Run Code Online (Sandbox Code Playgroud)
我建议声明除_startas之外的所有函数static,以避免任何全局偏移表 (GOT) 或过程链接表 (PLT) 引用(如<__x86.get_pc_thunk.bx>调用)。
使用例如将其编译为与位置无关的可执行文件
gcc -march=i686 -mtune=generic -m32 -O2 -fPIE -ffreestanding -nostdlib -nostartfiles blob.c -o blob
Run Code Online (Sandbox Code Playgroud)
剥它,
strip --strip-all blob
Run Code Online (Sandbox Code Playgroud)
并转储二进制文件的内容:
objdump -fd blob
Run Code Online (Sandbox Code Playgroud)
在此输出中,有两行重要的行:
start address 0x08048120
Run Code Online (Sandbox Code Playgroud)
它告诉_start符号的地址,和
080480e0 <.text>:
Run Code Online (Sandbox Code Playgroud)
它以十六进制表示代码的偏移量。后者减去前者(0x08048120 - 0x080480e0 = 0x40 = 64)得到起始符号的偏移量。
最后,使用以下命令将代码转储到原始二进制文件“blob.raw”中
objcopy -O binary -j .text blob blob.raw
Run Code Online (Sandbox Code Playgroud)