Build GCC Cross Compiler

Table of Contents

1. Build GCC Cross Compiler

1.1. What Is Cross Build

https://splichal.eu/scripts/sphinx/gccint/_build/html/source-tree-structure-and-build-system/configure-terms-and-history.html

configure gcc 时可以指定三个参数:

  • build

    在什么机器上 build

  • host

    build 出来的 gcc 在什么机器上运行

  • target

    build 出来的 gcc 产生的程序在什么机器上运行

native build 指 `build = host = target` cross build 指 `build = host != target`

还有一种 canadian build 指 `build != host != target`

1.2. Build

build gcc cross compiler 的步骤为:

  1. build binutils
  2. build gcc (stage 1)
  3. build libc
  4. build gcc (stage 2)

之所以需要编译两次 gcc, 主要是因为 gcc 代码不仅包含 gcc, 还包含许多其它的 lib,例如 libatomic, libstdc++ 等, 这些库或多或少会依赖 libc.

因此需要

  1. 通过 stage 1 用 host gcc 编译出一个单独的 cross gcc
  2. 然后用编译出来的 cross gcc 编译 target libc
  3. 最后通过 stage 2 用 cross gcc 编译 target libatomic 等

1.3. binutils

stamps/build-binutils-newlib: $(BINUTILS_SRCDIR) $(BINUTILS_SRC_GIT) stamps/check-write-permission
    rm -rf $@ $(notdir $@)
    mkdir $(notdir $@)
    cd $(notdir $@) && $</configure \
        --target=$(NEWLIB_TUPLE) \
        $(CONFIGURE_HOST) \
        --prefix=$(INSTALL_DIR) \
         \
        --disable-werror \
        $(BINUTILS_TARGET_FLAGS) \
        --disable-gdb \
        --disable-sim \
        --disable-libdecnumber \
        --disable-readline \
        $(WITH_ISA_SPEC)
    $(MAKE) -C $(notdir $@)
    $(MAKE) -C $(notdir $@) install
    mkdir -p $(dir $@) && touch $@

install 后 `prefix` 目录为:

├── bin
│   ├── riscv64-unknown-elf-addr2line
│   ├── riscv64-unknown-elf-ar
│   ├── riscv64-unknown-elf-as
│   ├── riscv64-unknown-elf-c++filt
│   ├── riscv64-unknown-elf-elfedit
│   ├── riscv64-unknown-elf-gprof
│   ├── riscv64-unknown-elf-ld
│   ├── riscv64-unknown-elf-ld.bfd
│   ├── riscv64-unknown-elf-nm
│   ├── riscv64-unknown-elf-objcopy
│   ├── riscv64-unknown-elf-objdump
│   ├── riscv64-unknown-elf-ranlib
│   ├── riscv64-unknown-elf-readelf
│   ├── riscv64-unknown-elf-size
│   ├── riscv64-unknown-elf-strings
│   └── riscv64-unknown-elf-strip
├── lib
│   └── bfd-plugins
│       └── libdep.so
├── riscv64-unknown-elf
│   ├── bin
│   │   ├── ar
│   │   ├── as
│   │   ├── ld
│   │   ├── ld.bfd
│   │   ├── nm
│   │   ├── objcopy
│   │   ├── objdump
│   │   ├── ranlib
│   │   ├── readelf
│   │   └── strip
│   └── lib

由于 Makefile 里设置了 `PATH := \((INSTALL_DIR)/bin:\)(PATH)`, 所以 stage 1 可以找从 bin 中找到编译 libgcc 需要的 riscv64-unknown-elf-as.

`riscv64-unknown-elf/bin` 下的 as 与 `bin/riscv64-unknown-elf-as` 完全相同, 前者是给 gcc driver 使用, 后者倾向于独立使用.

1.4. stage 1

stamps/build-gcc-linux-stage1: $(GCC_SRCDIR) $(GCC_SRC_GIT) stamps/build-binutils-linux \
                               stamps/build-linux-headers
    cd $(notdir $@) && $</configure \
        --target=$(LINUX_TUPLE) \
        $(CONFIGURE_HOST) \
        --prefix=$(INSTALL_DIR) \
        --with-newlib \
        --without-headers \
        --disable-shared \
        --disable-threads \
        --with-system-zlib \
        --enable-tls \
        --enable-languages=c \
        --disable-libatomic \
        --disable-libmudflap \
        --disable-libssp \
        --disable-libquadmath \
        --disable-libgomp \
        --disable-nls \
        --disable-bootstrap \
        --src=$(gccsrcdir)
    # 这里 make all-gcc 而没是 make all-target, 所以 target 相关的例如 libatomic 不会被编译
    $(MAKE) -C $(notdir $@) inhibit-libc=true all-gcc
    $(MAKE) -C $(notdir $@) inhibit-libc=true install-gcc
    $(MAKE) -C $(notdir $@) inhibit-libc=true all-target-libgcc
    $(MAKE) -C $(notdir $@) inhibit-libc=true install-target-libgcc

编译 libgcc 并不需要 libc, 所以 stage1 时就已经可以编译 libgcc 了. 包含 libgcc 的 stage1 gcc 配合 binutils 已经可以编译出不使用 libc 的可执行程序了.

编译 libgcc 时需要保证 binutils (例如 riscv64-unknown-elf-as) 在 PATH中, gcc 会使用 `build/gcc/as` 这个脚本找到 PATH 中的 riscv64-unknown-elf-as. 这个 as 只在编译时使用, make install 后生成的 riscv64-unknown-elf-gcc 运行时如何找到 as 取决于:

  1. 编译时 `–prefix` 参数
  2. 编译时的 `-with-as` 参数
  3. 运行时的 `-B` 参数
  4. 运行时的 `GCC_EXEC_PREFIX` 环境变量

通过 `gcc –print-prog-name=as` 及 `gcc –print-search-dirs` 可以确认找到的哪个 as.

另外, 虽然 sysroot 中也有 `as`, 但看起来 gcc 只会考虑 sysroot 的 include 和 lib, 并不会考虑 bin

install 后 `prefix` 目录新增文件:

├── bin
│   ├── riscv64-unknown-elf-c++
│   ├── riscv64-unknown-elf-c++filt
│   ├── riscv64-unknown-elf-cpp
│   ├── riscv64-unknown-elf-g++
│   ├── riscv64-unknown-elf-gcc
│   ├── riscv64-unknown-elf-gcc-12.2.0
│   ├── riscv64-unknown-elf-gcc-ar
│   ├── riscv64-unknown-elf-gcc-nm
│   ├── riscv64-unknown-elf-gcc-ranlib
│   ├── riscv64-unknown-elf-gcov
│   ├── riscv64-unknown-elf-gcov-dump
│   ├── riscv64-unknown-elf-gcov-tool
│   ├── riscv64-unknown-elf-gprof
│   ├── riscv64-unknown-elf-lto-dump
├── lib
│   └── gcc
│       └── riscv64-unknown-elf
│           └── 12.2.0
│               ├── include
│               │   ├── float.h
│               │   ├── iso646.h
│               │   ├── stdalign.h
│               │   ├── stdarg.h
│               │   ├── stdatomic.h
│               │   ├── stdbool.h
│               │   ├── stddef.h
│               │   ├── stdfix.h
│               │   ├── stdint-gcc.h
│               │   ├── stdint.h
│               │   ├── stdnoreturn.h
│               │   ├── tgmath.h
│               │   └── varargs.h
│               ├── include-fixed
│               │   ├── limits.h
│               │   ├── README
│               │   └── syslimits.h
│               ├── install-tools
│               │   ├── fixinc_list
│               │   ├── gsyslimits.h
│               │   ├── include
│               │   │   ├── limits.h
│               │   │   └── README
│               │   ├── macro_list
│               │   └── mkheaders.conf
│               └── plugin
├── libexec
│   └── gcc
│       └── riscv64-unknown-elf
│           └── 12.2.0
│               ├── cc1
│               ├── cc1plus
│               ├── collect2
│               ├── install-tools
│               │   ├── fixincl
│               │   ├── fixinc.sh
│               │   ├── mkheaders
│               │   └── mkinstalldirs
│               ├── liblto_plugin.la
│               ├── liblto_plugin.so
│               ├── lto1
│               ├── lto-wrapper
│               └── plugin
│                   └── gengtype

1.5. libc

stamps/build-newlib: $(NEWLIB_SRCDIR) $(NEWLIB_SRC_GIT) stamps/build-gcc-newlib-stage1
    rm -rf $@ $(notdir $@)
    mkdir $(notdir $@)
    cd $(notdir $@) && $</configure \
        --target=$(NEWLIB_TUPLE) \
        $(CONFIGURE_HOST) \
        --prefix=$(INSTALL_DIR) \
        --enable-newlib-io-long-double \
        --enable-newlib-io-long-long \
        --enable-newlib-io-c99-formats \
        --enable-newlib-register-fini \
        CFLAGS_FOR_TARGET="-O2 -D_POSIX_MODE -ffunction-sections -fdata-sections $(CFLAGS_FOR_TARGET)" \
        CXXFLAGS_FOR_TARGET="-O2 -D_POSIX_MODE -ffunction-sections -fdata-sections $(CXXFLAGS_FOR_TARGET)"
    $(MAKE) -C $(notdir $@)
    $(MAKE) -C $(notdir $@) install

由于提前已经设置了 PATH, 所以编译 libc 时可以找到需要的 riscv64-unknown-elf-xxx 和 stage1 riscv64-unknown-elf-gcc

新增的文件为:

.
├── riscv64-unknown-elf
│   ├── include
│   │   ├── alloca.h
│   │   ├── _ansi.h
│   │   ├── argz.h
│   │   ├── ar.h
│   │   ├── assert.h
│   │   ├── bits
│   │   ├── complex.h
│   │   ├── cpio.h
│   │   ├── ctype.h
│   │   ├── devctl.h
│   │   ├── dirent.h
│   │   ├── elf.h
....
│   │   ├── unistd.h
│   │   ├── utime.h
│   │   ├── utmp.h
│   │   ├── wchar.h
│   │   ├── wctype.h
│   │   └── wordexp.h
│   └── lib
│       ├── crt0.o
│       ├── libc.a
│       ├── libg.a
│       ├── libgloss.a
│       ├── libm.a
│       ├── libnosys.a
│       ├── libsemihost.a
│       ├── libsim.a
│       ├── nano.specs
│       ├── nosys.specs
│       ├── semihost.specs
│       └── sim.specs

1.6. stage 2

stamps/build-gcc-linux-stage2: $(GCC_SRCDIR) $(GCC_SRC_GIT) $(addprefix stamps/build-glibc-linux-,$(GLIBC_MULTILIB_NAMES)) \
                               stamps/build-glibc-linux-headers
    cd $(notdir $@) && $</configure \
        --target=$(LINUX_TUPLE) \
        $(CONFIGURE_HOST) \
        --prefix=$(INSTALL_DIR) \
        --with-sysroot=$(SYSROOT) \
        --with-pkgversion="$(GCCPKGVER)" \
        --with-system-zlib \
        --enable-shared \
        --enable-tls \
        --enable-languages=c,c++,fortran \
        --disable-libmudflap \
        --disable-libssp \
        --disable-libquadmath \
        --disable-libsanitizer \
        --disable-nls \
        --disable-bootstrap \
        --src=$(gccsrcdir)
    # libc 已经在 sysroot 中, 可以 make all-target 了
    $(MAKE) -C $(notdir $@)
    $(MAKE) -C $(notdir $@) install
    cp -a $(INSTALL_DIR)/$(LINUX_TUPLE)/lib* $(SYSROOT)

新增的文件:

.
├── lib
│   ├── gcc
│   │   └── riscv64-unknown-elf
│   │       └── 12.2.0
│   │           ├── crtbegin.o
│   │           ├── crtend.o
│   │           ├── crti.o
│   │           ├── crtn.o
│   │           ├── libgcc.a
│   │           ├── libgcov.a
├── riscv64-unknown-elf
│   ├── include
│   │   ├── c++
│   │   │   └── 12.2.0
│   │   │       ├── algorithm
│   │   │       ├── any
│   │   │       ├── array
│   │   │       ├── atomic
│   │   │       ├── backward
│   │   │       │   ├── auto_ptr.h
│   │   │       │   ├── backward_warning.h
│   │   │       │   ├── binders.h
│   │   │       │   ├── hash_fun.h
│   │   │       │   ├── hash_map
│   │   │       │   ├── hash_set
│   │   │       │   ├── hashtable.h
│   │   │       │   └── strstream
│   │   │       ├── barrier
│   │   │       ├── bit
│   │   │       ├── bits
│   │   │       │   ├── algorithmfwd.h
....
│   │   │       ├── valarray
│   │   │       ├── variant
│   │   │       ├── vector
│   │   │       └── version
│   └── lib
│       ├── libstdc++.a
│       ├── libstdc++.a-gdb.py
│       ├── libstdc++.la
│       ├── libsupc++.a
│       ├── libsupc++.la

主要包含:

  1. `enable-languages=c++` 新增的 libstdc++
  2. libgcc (如果 stage1 没有编译 libgcc)

1.7. disable-bootstrap

默认情况下编译 gcc 时会通过 3 个 stage 确定 gcc 编译的没有问题, 例如现在需要用 gcc 4 编译 gcc 9:

  1. 使用 gcc 4 编译出一个 gcc 9 (a)
  2. 使用 a 编译出另一个 gcc 9 (b)
  3. 使用 b 再编译一个 gcc 9 (c), 并确认一下 b 和 c 是相同的

在交叉编译时, b 通常不能在当前平台运行, 所以交叉编译时通常通过 disable-bootstrap 只执行 stage 1

1.8. build native gcc for riscv

# build riscv cross compiler at /opt/gcc-riscv
cd <path-to-riscv-gnu-toolchain>
./configure --prefix=/opt/gcc-riscv --with-arch=rv64gc --with-abi=lp64d
make linux -j 20

# install zlib for /opt/gcc-riscv
cd <path-to-zlib>
CC=riscv64-unknown-linux-gnu-gcc ./configure --prefix=/opt/gcc-riscv/riscv64-unknown-linux-gnu
make install

# build bintuils/stage1/glibc
cd <path-to-riscv-gnu-toolchain>
make clean
./configure --prefix=/opt/gcc-riscv-native --with-arch=rv64gc --with-abi=lp64d
make stamps/build-binutils-linux -j 20
make stamps/build-gcc-linux-stage1 -j 20
make stamps/build-glibc-linux-headers
make stamps/build-glibc-linux-rv64gc-lp64d -j 20

# build stage2 with host
export PATH=$PATH:/opt/gcc-riscv/bin/
./configure --prefix=/opt/gcc-riscv-native --with-host=riscv64-unknown-linux-gnu --with-arch=rv64gc --with-abi=lp64d
make stamps/build-gcc-linux-stage2 -j 20

# rebuild binutils with host
make stamps/build-binutils-linux -j 20

Backlinks

GCC (GCC > Build GCC Cross Compiler): Build GCC Cross Compiler

Author: [email protected]
Date: 2022-03-22 Tue 13:39
Last updated: 2023-09-21 Thu 18:56

知识共享许可协议