从零开始写一个操作系统 —— 2.5 从c语言到机器码

886 阅读3分钟

1.用c语言生成机器码的意义

前面操作系统的开发我们使用到了汇编语言,而使用汇编语言开发带来的问题就是代码量过于庞大。例如我们需要计算从1到100的加和,那么用汇编语言我们需要如下代码:

RESULT	dd	0
mov	ax,	0

SUM:
	inc word[RESULT]
	add ax, word[RESULT]	
	cmp word[RESULT], 101
	jne SUM

同时我们需要时刻注意寄存器的大小与数据是否匹配,使用的内存空间是否与我们所开辟的空间一致。而使用c语言的话,我们只需要如下代码,无需关注寄存器与内存空间的使用,将一切交由编译器来处理:

int result = 0;
for(int i=1;i<=100;i++)
{
	result = result + i
}

2.使用gcc编译器将c语言编译为机器码

在对于普通c语言文件的编译中,我们仅需要简单地使用gcc编译器将其编译为可执行文件即可。如:

gcc script.c -o run

其中发生的具体过程如下:

截屏2021-08-27 22.05.49.png

当文件经过汇编后形成的文件script.o与我们之前的loader.bin二进制文件较为相似,区别在于script.o文件包含了一些在当前系统运行所需的数据结构,这些数据结构不是我们所需要的,所以我们在后期将会删去这些数据结构。我们以一个最简单的script.c文件为例:

int main()
{
	return 0;
}

我们可以直接通过以下步骤获得script.o目标二进制文件:

gcc -mcmodel=large -fno-builtin -m64 -c script.c

其中-mcmodel=large为将内存范围设置为大型范围,该选项将对代码section的地址和大小不进行限制。-fno-builtin不识别不以__builtin_为前缀的内置函数。-m64生成可运行在64位系统上的文件。此时我们在命令行中可以通过objdump命令查看此目标二进制文件的内容。可以看到其中<main>中的55 48 89 e5 ...即是我们所需要的二进制机器码。

bash# objdump -d script.o

test.o:     file format elf64-x86-64


Disassembly of section .text:

0000000000000000 <main>:
   0:	55                   	push   %rbp
   1:	48 89 e5             	mov    %rsp,%rbp
   4:	b8 00 00 00 00       	mov    $0x0,%eax
   9:	5d                   	pop    %rbp
   a:	c3                   	retq

我们可以使用xxd命令查看该二进制目标文件的纯二进制显示:

bash# xxd script.o
00000000: 7f45 4c46 0201 0100 0000 0000 0000 0000  .ELF............
00000010: 0100 3e00 0100 0000 0000 0000 0000 0000  ..>.............
00000020: 0000 0000 0000 0000 1002 0000 0000 0000  ................
00000030: 0000 0000 4000 0000 0000 4000 0b00 0a00  ....@.....@.....
00000040: 5548 89e5 b800 0000 005d c300 4743 433a  UH.......]..GCC:
00000050: 2028 474e 5529 2034 2e38 2e35 2032 3031   (GNU) 4.8.5 201
00000060: 3530 3632 3320 2852 6564 2048 6174 2034  50623 (Red Hat 4
00000070: 2e38 2e35 2d33 3929 0000 0000 0000 0000  .8.5-39)........
00000080: 1400 0000 0000 0000 017a 5200 0178 1001  .........zR..x..
00000090: 1b0c 0708 9001 0000 1c00 0000 1c00 0000  ................
000000a0: 0000 0000 0b00 0000 0041 0e10 8602 430d  .........A....C.
000000b0: 0646 0c07 0800 0000 0000 0000 0000 0000  .F..............
000000c0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
......

我们所需要的机器码的偏移量在40处,然而整个目标文件不只包含了我们所需要的机器码,还包括了非常多的数据结构。所以为了得到我们所需要的存粹的机器码,我们需要使用到objcopy工具:

objcopy -I elf64-x86-64 -R ".eh_frame" -O binary script.o script.bin

此时我们再通过xxd查看script.bin文件:

bash# xxd script.bin
00000000: 5548 89e5 b800 0000 005d c3              UH.......].

这样我们获得的script.bin就是我们所需要的可以运行在我们操作系统上的机器码了。