我想让gcc将我的C代码编译成x86-32 linux二进制代码,但是周围没有任何库。 我只想在开始处指定一个地址,并且应该假定它已被加载到那里。然后,我将手动从输出中手动构建一个elf文件并设置所有内容。
我知道如何使用NASM来做类似的事情,但是在我不想只使用汇编程序的地方,我有一些更复杂的想法。我不需要任何库,我将在嵌入式asm中使用纯syscall。我也不在乎是否会失去某些可移植性。
我尝试了一下,但是找不到一种方法。 有人不仅可以为我提供正确的设置,还可以为我提供一些有关编译和链接器参数的背景信息? 我尝试搜索gcc手册,但发现它很混乱。
答案 0 :(得分:4)
我想让gcc将我的C代码编译成x86-32 linux二进制代码,但周围没有任何库。
这意味着您编写独立 C代码。 (当标准库可用时,您将拥有一个 hosted 环境;否则,您将拥有一个 freestanding 环境。)
编译例如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)引用(例如<__x86.get_pc_thunk.bx>
调用)。 / p>
例如使用
将其编译为与位置无关的可执行文件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