【发布时间】: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() 以消除警告。