一、前言

  《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。

三、配置编译环境

  1. 下载gcc源码
    根据[参考资料1][1]的建议,最好使用最新的gcc来进行编译,被编译的源码也推荐使用一样版本的。也就是说,用gcc 4.8.1来编译gcc 4.8.1的源码。
    下载源码包并解压,得到的目录名称之为「$gcc-4.8.1」。

  2. 下载依赖项
    需要的依赖项有:

    1. GNU Binutils
    2. GMP
    3. MPFR
    4. MPC

    将它们都解压出来,把解压出来的2、3、4的目录都放到gcc源码目录下。都需要去掉版本号,比如解压出来的目录名为「mpc-1.0.1」,那么现在就是「$gcc-4.8.1/mpc」。1无需这么做,因为它需要单独编译,参考后续的步骤4。
    其中GMP源码包是lzip压缩格式,需要下载lzip工具解压(brew安装)。

  3. 下载 gdb 源码
    下载源码包并解压,得到的目录名称之为「$gdb-7.6.1」。

  4. 设置环境变量

     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"
    

    这些是编译时候使用的选项。

  5. 编译交叉版的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 了。

七、参考资料

  1. 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即可

本文的pdf版:osx下搭建操作系统开发环境之32位交叉开发工具集(gcc+gdb)v1.1.pdf