【问题标题】:Generate raw binary from C code in Linux在 Linux 中从 C 代码生成原始二进制文件
【发布时间】:2017-07-19 09:11:33
【问题描述】:

我从头开始为 x86 架构实现一个简单的操作系统只是为了好玩。我为从磁盘加载内核并进入 32 位模式的引导加载程序实现了汇编代码。加载的内核代码是用 C 编写的,因此为了执行,我们的想法是从 C 代码生成原始二进制文件。

首先,我使用了这些命令:

$gcc -ffreestanding -c kernel.c -o kernel.o -m32

$ld -o kernel.bin -Ttext 0x1000 kernel.o --oformat binary -m elf_i386

但是,它没有生成任何返回这些错误的二进制文件:

kernel.o: In function 'main':
kernel.c:(.text+0xc): undefined reference to '_GLOBAL_OFFSET_TABLE_'

为了清楚起见,kernel.c 代码是:

/* kernel.c */

void main ()
{
   char *video_memory = (char *) 0xb8000 ;
   *video_memory = 'X';
}

然后我跟着这个教程:http://wiki.osdev.org/GCC_Cross-Compiler 为我自己的目标实现我自己的交叉编译器。它适用于我的目的,但是使用命令ndisasm 进行反汇编我获得了以下代码:

00000000  55                push ebp
00000001  89E5              mov ebp,esp
00000003  83EC10            sub esp,byte +0x10
00000006  C745FC00800B00    mov dword [ebp-0x4],0xb8000
0000000D  8B45FC            mov eax,[ebp-0x4]
00000010  C60058            mov byte [eax],0x58
00000013  90                nop
00000014  C9                leave
00000015  C3                ret
00000016  0000              add [eax],al
00000018  1400              adc al,0x0
0000001A  0000              add [eax],al
0000001C  0000              add [eax],al
0000001E  0000              add [eax],al
00000020  017A52            add [edx+0x52],edi
00000023  0001              add [ecx],al
00000025  7C08              jl 0x2f
00000027  011B              add [ebx],ebx
00000029  0C04              or al,0x4
0000002B  0488              add al,0x88
0000002D  0100              add [eax],eax
0000002F  001C00            add [eax+eax],bl
00000032  0000              add [eax],al
00000034  1C00              sbb al,0x0
00000036  0000              add [eax],al
00000038  C8FFFFFF          enter 0xffff,0xff
0000003C  16                push ss
0000003D  0000              add [eax],al
0000003F  0000              add [eax],al
00000041  41                inc ecx
00000042  0E                push cs
00000043  088502420D05      or [ebp+0x50d4202],al
00000049  52                push edx
0000004A  C50C04            lds ecx,[esp+eax]
0000004D  0400              add al,0x0
0000004F  00                db 0x00

如您所见,前9行(除了我不知道为什么插入的NOP)是我的main函数的汇编翻译。从第 10 行到最后,有很多我不知道为什么会出现在这里的代码。

最后,我有两个问题:

1) 为什么会生成该代码?

2) 有没有一种方法可以从 C 语言中生成原始机器代码而不需要那些无用的东西?

【问题讨论】:

  • 您正在查看未启用优化时生成的低效代码。编译 C 时,您可以尝试传递 -O3 。生成的代码的第一部分是典型的堆栈帧序言,然后它在堆栈上为局部变量分配空间。
  • 插入优化选项当然不会产生栈帧prologue,但还是会产生RET后与main函数不匹配的代码。
  • 函数后面的东西很可能是异常处理信息。我没有仔细看。它实际上不是代码,而是数据。您可以尝试使用 -fno-exceptions 使用 GCC 进行构建,看看会发生什么
  • @Olaf,虽然 C 没有,但 GCC 通常仍会在对象中创建 .eh_frame 部分。我通常使用链接器脚本丢弃.eh_frame 部分和comment 部分(以及构建说明)。
  • 您使用的是什么 GCC?我试过你的代码,它工作正常 - 二进制是 21 个字节。还将 main() 重命名为 _start() 以消除警告。

标签: c linux gcc x86 kernel


【解决方案1】:

先给几个提示:

  • 避免将您的起始例程命名为main。它令人困惑(对于读者和编译器来说都是如此;当您不将-ffreestanding 传递给gcc 时,它会非常具体地处理main)。使用 startbegin_of_my_kernel 之类的其他内容...

  • 使用gcc -v 编译以了解您的特定编译器在做什么。

  • 您可能应该向您的编译器询问一些优化和所有警告,因此至少将-O -Wall传递给gcc

    李>
  • 您可能想查看生成的汇编代码,因此使用gcc -S -O -Wall -fverbose-asm kernel.c 获取kernel.s 汇编文件并查看它

  • 正如Michael Petch 评论的那样,您可能想要传递-fno-exceptions

  • 您可能需要一些linker script 和/或一些crt0 的手写汇编程序

  • 你应该阅读一些关于linkers & loaders


 kernel.c:(.text+0xc): undefined reference to '_GLOBAL_OFFSET_TABLE_'

这闻起来像是与position-independent-code 有关的东西。我的猜测:尝试使用explicit -fno-pic or -fno-pie 进行编译

(在一些 Linux 发行版上,他们的gcc 可能配置了一些-fpic 默认启用)

PS。如果您想要 x86 32 位二进制文​​件,请不要忘记将 -m32 添加到 gcc

【讨论】:

  • 我是你的支持者。我还在上一条评论中提供了一个使用 OBJCOPY 而不是链接器脚本的示例。链接器脚本当然是我的偏好,但给猫剥皮的方法总是不止一种。
  • 感谢您的建议。使用 -fno-pic 选项,我可以直接使用我的 gcc 进行编译,而无需使用我制作的交叉编译器 gcc。但是,即使传递选项 -fno-exceptions,如果我从二进制文件中反汇编,我在 RET 之后也会有相同的无用代码。使用@Michael Petch 提出的程序,它运行良好!也谢谢你
  • @gyro91 :如果您打算长期使用玩具操作系统,我强烈建议您坚持使用交叉编译器。从长远来看,它将为您省去麻烦和悲伤。无用的代码实际上是被 NDISASM 解释为指令的数据,因为在二进制文件中,它无法正确区分代码和已经混在一起的数据。
  • @Michael Petch:实际上我正在使用交叉编译器。我按照你的建议使用了 OBJCOPY,效果很好!
猜你喜欢
  • 2013-10-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-04
  • 1970-01-01
  • 1970-01-01
  • 2011-02-17
  • 2014-06-21
相关资源
最近更新 更多