最近抽空在看自制操作系统相关的书籍,比如《自己动手写操作系统》、《Orange’S:一个操作系统的实现》、《一个64位操作系统的设计与实现》、《30天自制操作系统》等等,只有《30天自制操作系统》是可以完全在Windows下编译、链接、生成镜像的(使用的自制的非标准工具),其它几个全部都是在虚拟机中安装Linux系统,在Windows下编写源码,Linux下进行源码的编译链接,然后生成镜像。这就导致需要在Windows与Linux之间来回切换。

笔者尝试改写书中Makefile,用于MinGW环境下完全编译链接,生成镜像文件,遇到过太多坑。最主要的问题是MinGW中的GCC不支持elf文件格式,而是只能生成PE格式,LD也只能链接PE格式。但是我们需要链接成平面(Flat)二进制或者ELF格式,有一个–oformat binary选项,但是使用它会遇到的问题是:

1cannot perform PE operations on non PE output file

网上很多资料都是建议构建一个交叉编译的GCC套件。

笔者后面写的《 在MinGW中构建GCC交叉编译器》一文有介绍使用MinGW构建GCC交叉编译器。

就在快要放弃的时候,遇到一个玩具操作系统示例,它就是完全在MinGW下编译、链接生成镜像的,这让我顿时来了兴趣,下面以一个实例来说明。

新建一个目录,在其中创建一个main.c,内容如下:

1void start()
2{
3loop:
4	goto loop;
5}

再创建一个Makefile文件,内容如下:

 1CC := gcc
 2RM := rm
 3CFLAGS	:= -Wall -fno-builtin -nostdlib -ffreestanding -nostdinc -m32 -fno-pic
 4
 5all: main.bin
 6
 7main.exe: main.o
 8	$(CC) $(CFLAGS) -o main.exe main.o
 9
10main.bin: main.exe
11	objcopy -O binary main.exe main.bin
12
13%.o: %.c
14	$(CC) $(CFLAGS) -o $@ -c $<
15
16clean:
17	$(RM) *.o *.exe *.bin

在MinGW控制台中执行make命令,可以看到完全正常编译链接,没有任何错误:

使用命令

1ndisasm main.bin > a.txt

反汇编来看一下:

可以看到生成了相应的代码,jmp short 0x3就是一直在死循环。

还有一种使用ld的脚本命令作为参数来编译链接,建立一个link.ld文件,内容如下:

 1OUTPUT_FORMAT("pei-i386")
 2ENTRY(_start)
 3phys = 0x00020000;
 4SECTIONS
 5{
 6  .text phys : AT(phys) {
 7    code = .;
 8    *(.text)
 9    *(.rodata)
10    . = ALIGN(4096);
11  }
12  .data : AT(phys + (data - code))
13  {
14    data = .;
15    *(.data)
16    . = ALIGN(4096);
17  }
18  .bss : AT(phys + (bss - code))
19  {
20    bss = .;
21    *(.bss)
22    . = ALIGN(4096);
23  }
24  end = .;
25}

然后修改Makefile中的生成命令:

1$(CC) $(CFLAGS) -o main.exe main.o -T link.ld

注意输出格式是"pei-i386",而不是ld支持的格式。ld支持的格式为:

再看一下生成的汇编代码:

可以看到,两种方式生成的自己编写的代码是完全一样的,只是后面自动填充的代码不一样。

由于ld默认的入口函数为start所以C文件中也是定义的start函数,不能直接使用main函数作为入口,否则会导致链接失败:

这里有一个技巧,就是不直接使用ld命令来链接,而是让gcc来调用,然后使用objcopy来生成我们想要的平面二进制文件或者ELF文件,当然,如果你想要生成32位elf文件,可以将objcopy的参数改为elf32-i386,比如:

1objcopy -O elf32-i386 main.exe main.elf

然后可以通过命令objdump -f main.elf查看刚才生成的文件格式:

1$ objdump -f main.elf
2main.elf:     file format elf32-i386
3architecture: i386, flags 0x00000112:
4EXEC_P, HAS_SYMS, D_PAGED
5start address 0x00020000

原来MinGW环境下要这么玩,终于可以完全在MinGW下编写自己的操作系统了,希望本文对你有所帮助!