一、前言
《linux、osx下搭建操作系统开发环境的完整步骤》一文中讲解了一些基本的搭建方法,并提供了一个nasm汇编编写的简单的系统内核源码。实际开发过程中更多使用的是C语言,就需要有一个配套的C编译器。因为我使用的可执行文件是elf格式,所以我选择的是GCC。但是osx下安装的GCC生成的是osx的可执行文件格式,并不是elf。所以我需要一个能在osx下生成elf的GCC,俗称的交叉编译器。
我的环境:osx 10.8.4 & 10.9
二、安装osx版的gcc
brew install gcc48
推荐下载最新的稳定版gcc。
三、配置编译环境
-
下载gcc源码
根据[参考资料1][1]的建议,最好使用最新的gcc来进行编译,被编译的源码也推荐使用一样版本的。也就是说,用gcc 4.8.1来编译gcc 4.8.1的源码。
下载源码包并解压,得到的目录名称之为「$gcc-4.8.1」。 -
下载依赖项
需要的依赖项有:将它们都解压出来,把解压出来的2、3、4的目录都放到gcc源码目录下。都需要去掉版本号,比如解压出来的目录名为「mpc-1.0.1」,那么现在就是「$gcc-4.8.1/mpc」。1无需这么做,因为它需要单独编译,参考后续的步骤4。
其中GMP源码包是lzip压缩格式,需要下载lzip工具解压(brew安装)。 -
下载 gdb 源码
下载源码包并解压,得到的目录名称之为「$gdb-7.6.1」。 -
设置环境变量
export CC=/usr/local/bin/gcc-4.8 export CXX=/usr/local/bin/g++-4.8 export CPP=/usr/local/bin/cpp-4.8 export LD=/usr/local/bin/gcc-4.8
这些都是brew版gcc4.8.1的软链接。如果不设置,那么会使用系统中默认自带的工具,这些工具版本都很陈旧。比如osx 10.8.4带的/usr/bin/gcc是4.2版本的。
export PREFIX=$HOME/opt/cross export TARGET=i586-elf export PATH="$PREFIX/bin:$PATH"
这些是编译时候使用的选项。
-
编译交叉版的binutils
cd $binutils-x.y.z mkdir build-binutils cd build-binutils ../configure --target=$TARGET --prefix="$PREFIX" --disable-nls make make install
四、编译交叉版的gcc
cd $gcc-4.8.1
mkdir build-gcc
cd build-gcc
../configure --target=$TARGET --prefix="$PREFIX" --disable-nls --enable-languages=c,c++ --without-headers
make all-gcc
make all-target-libgcc
make install-gcc
make install-target-libgcc
完成后,在「~/opt/cross/bin」下就能看到编译好的交叉版的编译套件了,包括「i586-elf-gcc」、「i586-elf-g++」和「i586-elf-ld」等等。可以用「$HOME/opt/cross/bin/$TARGET-gcc –version」来验证一下版本是否正确。
另外,为了方便使用,可以在.bashrc或者.zshrc中调整环境变量:
export PATH="$HOME/opt/cross/bin:$PATH"
五、测试源码
现在咱有了交叉编译器了,试试效果吧:
// kernel.c
#include "multiboot2.h"
#define INFO_REQ_COUNT 2
struct antos_multiboot_header_tag_information_request
{
struct multiboot_header_tag_information_request info_req __attribute__((aligned(MULTIBOOT_TAG_ALIGN)));
multiboot_uint32_t req[INFO_REQ_COUNT];
} __attribute__((packed));
struct antos_multiboot_header
{
struct multiboot_header header __attribute__((aligned(MULTIBOOT_TAG_ALIGN)));
struct antos_multiboot_header_tag_information_request info_req __attribute__((aligned(MULTIBOOT_TAG_ALIGN)));
struct multiboot_header_tag end __attribute__((aligned(MULTIBOOT_TAG_ALIGN)));
} __attribute__((packed));
struct antos_multiboot_header amb =
{
{
MULTIBOOT2_HEADER_MAGIC,
MULTIBOOT_ARCHITECTURE_I386,
sizeof(struct antos_multiboot_header),
-(MULTIBOOT2_HEADER_MAGIC + MULTIBOOT_ARCHITECTURE_I386 + sizeof(struct antos_multiboot_header))
},
{
{
MULTIBOOT_HEADER_TAG_INFORMATION_REQUEST,
MULTIBOOT_HEADER_TAG_OPTIONAL,
sizeof(struct antos_multiboot_header_tag_information_request)
},
MULTIBOOT_TAG_TYPE_BASIC_MEMINFO,
MULTIBOOT_TAG_TYPE_FRAMEBUFFER
},
{
MULTIBOOT_HEADER_TAG_END,
MULTIBOOT_HEADER_TAG_OPTIONAL,
sizeof(struct multiboot_header_tag)
}
};
void breakpoint()
{
asm("xchg %bx, %bx");
}
int _start()
{
breakpoint();
return 0;
}
multiboot2.h头文件是从grub2.0.0的源码里拷贝过来的,主要定义了符合multiboot2规范的数据结构。
编译方法:
~/opt/cross/bin/i586-elf-gcc -c -o kernel.o kernel.c
~/opt/cross/bin/i586-elf-ld -Ttext=0x100000 -o kernel.bin kernel.o
「-Ttext=0x100000」是为了让代码段加载到0x100000,而不是默认的08048074(我的环境中),后者超出我的bochs虚拟机的物理内存空间。
然后用kernel.bin替换之前的虚拟磁盘中的同名文件,再运行bochs虚拟机就能看到熟悉的magic breakpoint了。
六、编译交叉版的 gdb
cd $gdb-7.6.1
mkdir build-$TARGET
cd build-$TARGET
../configure --target=$TARGET --prefix=$PREFIX --disable-nls
make
sudo make install
完成后,在「~/opt/cross/bin」下就能看到编译好的交叉版的i586-elf-gdb 了。
七、参考资料
- GCC Cross-Compiler [1]: http://wiki.osdev.org/GCC_Cross-Compiler “「GCC Cross-Compiler」”
八、版本记录
- v1.0 - 2013-09-07,初始发布。
- v1.1 - 2013-11-04,增加「编译交叉版的gdb」章节。
九、网友补充
以下内容为热心网友补充,供同好参考。(我没有验证。暂记录在这里,后续验证过后我再补入正文。感谢这位网友被我之前老博客系统的验证码刁难了N次后,还依然告知我,非常感谢。)
OSX10.9下使用gcc4.8编译binutils-2.24会报错:nm.c:1687:7: error: ‘sbrk’ is deprecated (declared at /usr/include/unistd.h:582)
需要使用gcc4.9,编译时指定编译参数CFLAGS=-Wno-error=deprecated-declarations
gcc的依赖项GMP/FPMR/MPC也不需要手动下载,在gcc的源码下执行./contrib/download_prerequisites即可