从0创建一个OS (十二) 创建gcc交叉编译器
本节将学习如何为特定目标平台创建专属编译器
关键字: 交叉编译器
目标: 创建一个可以用来建立我们的kernel的开发环境
理论基础
鉴于以后我们将使用C语言编辑我们的操作系统核,因此需要对开发环境进行新的布置,也就是创建一个交叉编译器.
为什么要搞一个交叉编译器?
事实上类Unix系统的编译环境已经足够完善,但其缺点是太完善了,我们在编译时不知道到底使用了什么样的库文件,因此我们需要创建一个交叉编译器,只包含标准C语言库文件.
搞一个交叉编译器需要什么东西?
在生成交叉编译器时,我们需要从网上下载2个源码,对这两个源码进行编译.
binutils
gcc
尽量选择最新版本.本节使用的是binutils2.32和gcc9.2.0. 英文原文教程使用的是binutils2.24和gcc4.9.1.
生成交叉编译器
我使用的是ubuntu16.04环境,在多次尝试之后总结了以下生成交叉编译器的步骤.
接下来的操作务必在同一个Terminal中完成.如果过程中不甚关闭Terminal,需要从头开始.
- 首先需要进入超级管理员模式,不然会报很多权限不足的错误.
sudo su
- 确定你的本机gcc位置,使用
which gcc
指令.我的gcc位置为/usr/bin/gcc
. - 使用以下指令,完成对环境变量的暂时修改(退出该Termial之后环境变量会恢复)
# 注意这里的gcc-5是我打开/usr/bin,搜索gcc看到的gcc版本,需要根据自身的环境进行调整 export CC=/usr/bin/gcc-5 export LD=/usr/bin/gcc-5 # PREFIX是最终编译完成的交叉编译器总的存储位置,可以修改为你想要存储的地方 # TARGET是交叉编译器的目标平台,这里选择386平台 # PATH即交叉编译器目录下的bin中含有所有可执行文件 export PREFIX="/usr/local/i386elfgcc" export TARGET=i386-elf export PATH="$PREFIX/bin:$PATH"
- 创建临时文件夹src, 将下载到的binutils和gcc放到该文件夹中
mkdir /tmp/src cd /tmp/src
- 先编译binutils
tar xf binutils-2.32.tar.gz mkdir binutils-build cd binutils-build ../binutils-2.32/configure --target=$TARGET --enable-interwork --enable-multilib --disable-nls --disable-werror --prefix=$PREFIX 2>&1 | tee configure.log make all install 2>&1 | tee make.log
- 再编译gcc
cd .. tar xf gcc-9.2.0.tar.bz2 mkdir gcc-build cd gcc-build # 如果这一步报错说什么gmp\mpfr等库出现问题,使用以下解决办法 # 解决办法:在gcc的根目录下,在terminal中运行 ./contrib/download_prerequisites # 如果有vpn请开vpn,下载会加速 # 运行之后进入gcc-build文件夹重新configure ../gcc-9.2.0/configure --target=$TARGET --prefix="$PREFIX" --disable-nls --disable-libssp --enable-languages=c --without-headers make all-gcc make all-target-libgcc make install-gcc make install-target-libgcc
编译完成的结果:
------ update 2021.4.9 -------------
重新编译安装后,发现/usr/local/i386elfgcc/bin中的所有程序都变为没有了i386-elf前缀,暂时没有找到原因,可能是gcc-10.2.0和binutils-2.36的搭配导致i386不被支持了。
- 修改环境变量,使我们能够直接在terminal中使用交叉编译器进行编译,而无需每次使用时都要切换到
/usr/local/i386elfgcc/bin
目录下.
sudo vim /etc/profile # 在最后加上 export PATH="$PATH:/usr/local/i386elfgcc/bin" # 然后在terminal中输入 source /etc/profile # 重启termial即可
测试
正常情况下如果上面的每一步都不报错,交叉编译器算是生成成功了,这里给出一个示例,跟下一节的内容由部分重合.
首先新建一个c代码文件,test.c
,在文件中输入如下源码:
int my_function() { return 0xBABA; }
再在该文件目录下打开terminal,输入
# 如果出现了i386-elf-gcc无法识别的错误,就直接使用gcc -fno-pie -m32替代i386-elf-gcc
i386-elf-gcc -ffreestanding -c test.c -o test.o
# 如果出现了i386-elf-gcc无法识别的错误,就直接使用objdump -m i386 代替i386-elf-objdump
i386-elf-objdump -d test.o
如果显示如下则表示我们本节的创建gcc交叉编译器成功.
2020.4.4更新
如果发现:
Configuring for a x86_64-pc-linux-gnuoldld host.
Invalid configurationx86_64-pc-linux-gnuoldld': machine
x86_64-pc’ not recognized
Invalid configurationx86_64-pc-linux-gnuoldld': machine
x86_64-pc’ not recognized
Unrecognized host system name x86_64-pc-linux-gnuoldld.
就参考这里is-there-another-version-of-the-binutils-for-x86-64