在 Gentoo Linux 下使用 crossdev 建立自己的 toolchain

Gentoo Linux 除了是一套非常強大、易於客製化的 Linux 系統以外,他亦可以讓你輕鬆的建 構不同平台的 toolchain,本文將介紹 Gentoo Linux 的 crossdev 套件,以及如何透過 crossdev 命令建立出不同平台的 toolchain.

Toolchain 中譯為 交叉工具鏈 ,是在進行嵌入式開發必備的工具,包含了以下幾部份:

  • binutils

    用來生成二進制(binary)文件的基本工具

  • gcc

    GNU C/C++ 編譯器

  • gdb

    GNU 除錯工具

  • glibc/ulibc/newlib

    系統相關 C 語言函式庫

  • linux headers

    僅在編譯 Linux 相關平台所需要,為 Linux Kernel 相關的 header files(頭文件)

在非 Gentoo Linux 的平台,一般我們都使用 crosstool-ng 來協助產生需要的開發環境, 但是對於 Gentoo Linux 而言,使用內建的 crossdev 套件即可以快速構件所需要的開發環 境。

要使用 crossdev,首先我們當然是要安裝他囉,在 Gentoo 下使用 emerge 命令進行安裝

Gentoo kernel # emerge sys-devel/crossdev

安裝完成後,我們可以使用 crossdev -t help 查看 crossdev 支援哪些平台

Gentoo kernel # crossdev -t help
Supported Architectures:
   - alpha                                     - arm / armeb / aarch64
   - hppa (parisc)                             - ia64
   - i386 / i486 / i586 / i686 (x86)           - m68k
   - mips / mipsel / mips64 / mips64el
   - powerpc (ppc) / powerpc64 (ppc64)
   - sparc / sparc64                           - s390 / s390x
   - sh / sh[1-5] / sh64                       - x86_64 (amd64)
Supported C Libraries:
   - glibc (gnu)
   - klibc       [prob wont work]
   - musl
   - newlib      [bare metal/no operating system]
   - uclibc      [not all arches are ported]
Special Targets:
   - avr      http://www.nongnu.org/avr-libc/
   - bfin     http://blackfin.uclinux.org/
   - h8300    http://h8300-hms.sourceforge.net/
   - mingw32  http://www.mingw.org/
   - mingw64  http://mingw-w64.sourceforge.net/
   - msp430   http://mspgcc.sourceforge.net/
   - nds32    http://github.com/nds32
   - nios2    http://www.altera.com/products/ip/processors/nios2/ni2-index.html
   - xc16x    http://www.infineon.com/
   - ee / iop / dvp (ps2) [Playstation 2 targets]
   - ppu / spu (cell) [Cell/Playstation 3 targets]
Softfloat toolchains:
   Include 'softfloat' in the 'vendor' field
   e.g. armeb-softfloat-linux-uclibc  powerpc-booya_softfloat-linux-gnu

由上面的命令結果可以知道,基本上主流的 CPU 架構都支援,因此我們可以放心的學習使用 crossdev 建構不同平台的 toolchain.

假設我們今天要建構的 toolchain 是針對 ARMv7a 並且為 HardFloat ,執行於 Linux 環境的話,要怎樣作呢?在 Gentoo 下,使用以下命令 (其中 -S 代表選用 stable 版的套件)

Gentoo kernel # crossdev -S -t armv7a-hardfloat-linux-gnueabi

假設一切順利,你會看到 Gentoo 開始進行編譯,並把完整的 toolchain 編譯出來,不過通 常第一次執行的時候都會遇到如下錯誤 :(

* please convert /etc/portage/profile/package.use.mask to a directory
* If you file a bug, please attach the following logfiles:
* /var/log/portage/cross-armv7a-hardfloat-linux-gnueabi-info.log

這是因為 Gentoo 的 crossdev 必須在 /etc/portage/package.* 皆為 資料夾 的情況 才可以使用,因此你可以手動將那些檔案都變成資料夾,或是使用以下 腳本 幫你自動處理:

#!/bin/bash
PROFILE_DIR="/etc/portage"

if [ ! -e ${PROFILE_DIR} ]; then
    mkdir ${PROFILE_DIR};
fi

for PACK_DIR in package.accept_keywords package.keywords package.use package.unmask package.mask; do
    CUR_DIR="${PROFILE_DIR}/${PACK_DIR}"
    if [ ! -e ${CUR_DIR} ]; then
        mkdir ${CUR_DIR}
    fi

    if [ -e ${CUR_DIR} -a ! -d ${CUR_DIR} ]; then
        mv ${CUR_DIR} ${CUR_DIR}.moving
        mkdir ${CUR_DIR}
        mv ${CUR_DIR}.moving ${CUR_DIR}/monolithic
    fi
done

echo "Completed!"

除此之外,由於 crossdev 會依據你建立的新的 toolchain,產生出相對應的 ebuild 檔案,因 此建議使用者擁有自己的 Local overlay。那我們要怎樣去建立 Local overlay 呢?首先 編輯 /etc/portage/make.conf 添加以下資訊

PORTDIR_OVERLAY="${PORTDIR_OVERLAY} /usr/local/portage"

接著建立 /usr/local/portage 資料夾

Gentoo kernel # mkdir -p /usr/local/portage

這樣當你使用 crossdev 建立新的 toolchain,就會在這個 Local overlay 看到相對應的 ebuild 被建立出來,如下

Gentoo kernel # tree -L 2 /usr/local/portage/
/usr/local/portage/ <b>
├── cross-armv7a-hardfloat-linux-gnueabi <b>
│   ├── binutils -> /usr/portage/sys-devel/binutils <lb>
│   ├── gcc -> /usr/portage/sys-devel/gcc <lb>
│   ├── gdb -> /usr/portage/sys-devel/gdb <lb>
│   ├── glibc -> /usr/portage/sys-libs/glibc <lb>
│   └── linux-headers -> /usr/portage/sys-kernel/linux-headers <lb>
├── metadata <b>
│   └── layout.conf
└── profiles <b>
    └── categories

8 directories, 2 files

建立其他平台的 toolchain

在上面我們講解到了如何使用 crossdev 建立 ARMv7a HardFloat 的 Linux toolchain, 那如果我們的目標是裸版 (baremetal)呢?

針對裸版(baremetal)開發,我們的 toolchain 名稱就不會是 <arch>-linux-gnueabi 這 樣的組成,並且我們基本上只會使用 stage4 來進行編譯,在 crossdev 中,可以使用 -s 參數來指定要編譯的 stage,針對裸版 (baremetal) 的平台,我們基本上都會使用 --stage4 來建立 toolchain。

Stage Options:
    -s0, --stage0            Build just binutils
    -s1, --stage1            Also build a bare C compiler (no C library/
                                C++/shared GCC libs/C++ exceptions/etc...)
    -s2, --stage2            Also build kernel headers
    -s3, --stage3            Also build the C library
    -s4, --stage4            Also build a full compiler [default]
                                (shared libs GCC/various lang frontends/etc...)

以下列出幾個不同平台的 toolchain 建立命令:

  • MSP430

    Gentoo msp430 # crossdev -S -s4 -t msp430
    
  • ARM Cortex-M

    Gentoo stm32 # crossdev -S -s4 -t arm-none-eabi
    
  • Ubuntu 的 arm-linux-gnueabihf

    Gentoo stm32 # crossdev -S USE="-fortran -mudflap -nls -openmp multilib" EXTRA_ECONF="--with-cpu=cortex-a8 --with-float=hard" -t arm-linux-gnueabihf
    

測試我們建立的 toolchain

建立完 toolchain 後,我們可以用簡單的程式搭配 QEMU 來測試我們的 toolchain 是否能正常 工作,此處以 armv7a-hardfloat-linux-gnueabi- 作為範例,首先建立以下檔案並命名為 hello.c

#include <stdio.h>
int main()
{
        printf("Hello cross-compiling world!\n");
        return 0;
}

由於我們建立的 toolhcain 是針對 armv7a-hardfloat-linux-gnueabi 所建立,因此我們 會獲得 armv7a-hardfloat-linux-gnueabi-gcc 這個 gcc 命令,使用他來進行編譯

coldnew@Rosia ~ $ armv7a-hardfloat-linux-gnueabi-gcc hello.c -static -o hello

我們可以先使用 file 命令來檢查是否真的編譯為 ARMv7a 平台的程式

coldnew@Rosia ~ $ file hello
hello: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, for GNU/Linux 2.6.32, not stripped

或是用 qemu-arm 直接執行這個 ELF 格式的程式

coldnew@Rosia ~ $ qemu-arm hello
Hello cross-compiling world!