前文 《C语言使用MinGW中的GCC生成平面(flat)二进制文件》中有介绍MinGW中使用GCC生成编写操作系统内核所需要的32位平面(flat)二进制文件。但是如果想要在Loader中使用C语言,即编译16位的汇编与C混合代码,就必须使用ELF格式才能转换成16位平面(flat)二进制文件。MinGW默认的GCC只能生成PE文件,不能生成ELF文件,而pei-386是无法转换成16位平面(flat)二进制文件的。

如果在link.ld中使用OUTPUT_FORMAT("pei-i386"),会报错:

1mingw32/bin/ld.exe: unsupported PEI architecture: pei-i386

使用使用OUTPUT_FORMAT("binary"),则会报:

1mingw32/bin/ld.exe: cannot perform PE operations on non PE output file 'loader.bin'

所以为了能使用MinGW编译16位汇编与C语言的混合代码,就必须编译一个交叉编译器。

下面笔者就以最新的GCC 12.1为例,介绍如何通过MinGW来构建GCC的交叉编译器。笔者的MinGW为MSYS2中的MinGW64。

一、下载Binutils

https://ftp.gnu.org/gnu/binutils/下载最新的binutils,目前最新的为 binutils-2.38.tar.xz

二、下载GCC

https://ftp.gnu.org/gnu/gcc/下载最新的GCC,目前最新的为 gcc-12.1.0.tar.gz

三、解压、编译

1.解压

$HOME下新建一个目录,比如src,然后将下载的binutils-2.38.tar.xz以及gcc-12.1.0.tar.gz解压到$HOME/src

2.编译binutils

在编译之前后设置如下环境变量:

1export PREFIX="/opt/cross"
2export TARGET=i686-elf
3export PATH="$PREFIX/bin:$PATH"

如果是想编译成64位的则把TARGET设置为x86_64-elf

然后编译、安装binutils

1cd $HOME/src
2 
3mkdir build-binutils
4cd build-binutils
5../binutils-2.38/configure --target=$TARGET --prefix="$PREFIX" --with-sysroot --disable-nls --disable-werror
6make
7make install

3. 编译、安装GCC

 1cd $HOME/src
 2
 3# $PREFIX/bin目录必须在路径中,不然找不到as汇编器
 4which -- $TARGET-as || echo $TARGET-as is not in the PATH
 5
 6# 进入GCC目录下载必要的依赖
 7cd gcc-12.1.0/
 8./contrib/download_prerequisites
 9cd ..
10
11mkdir build-gcc
12cd build-gcc
13../gcc-12.1.0/configure --target=$TARGET --prefix="$PREFIX" --disable-nls --enable-languages=c,c++ --without-headers
14make all-gcc
15make all-target-libgcc
16make install-gcc
17make install-target-libgcc

注意安装时的提示,后面可能会有用:

 1----------------------------------------------------------------------
 2Libraries have been installed in:
 3   /opt/cross/libexec/gcc/i686-elf/12.1.0
 4
 5If you ever happen to want to link against installed libraries
 6in a given directory, LIBDIR, you must either use libtool, and
 7specify the full pathname of the library, or use the `-LLIBDIR'
 8flag during linking and do at least one of the following:
 9   - add LIBDIR to the `PATH' environment variable
10     during execution
11   - add LIBDIR to the `LD_RUN_PATH' environment variable
12     during linking
13   - use the `-LLIBDIR' linker flag
14
15See any operating system documentation about shared libraries for
16more information, such as the ld(1) and ld.so(8) manual pages.
17----------------------------------------------------------------------

编译完成后就可以在$HOME/opt/cross目录下看到相应的程序了:

使用ld -V查看支持的格式:

可以看到已经支持elf_i386了。Btw:从图中可以看到生成的文件都比较大,像i686-elf-lto-dump.exe达370多M,可以通过使用strip命令删除调试信息来瘦身,在所有生成的exe目录使用:

1strip *.exe

以下是瘦身后的大小情况:

4.编译、安装GDB

下载最新的GDB,目前为gdb-12.1,设置好环境变量后,使用下面的命令进行编译,安装:

1$../gdb-12.1/configure --target=$TARGET --prefix="$PREFIX" --disable-nls --enable-multilib --disable-werror
2$make -j8
3$make install

5.设置路径

为了使用编译好的交叉编译器和调试器,需要添加到路径,编辑~/.bashrc文件,添加一行:

1PATH=$PATH:/opt/cross/bin

如果编译遇到麻烦,或者不想自己编译也可以使用别人编译好的,参见: https://wiki.osdev.org/GCC_Cross-Compiler#Prebuilt Toolchains

有了交叉编译器和调试器,就可以使用MinGW构建16位应用程序了,同时也可以进行调试,有兴趣的话请关注后面的博文。

参考资料: https://wiki.osdev.org/GCC_Cross-Compiler