Build GCC Cross Compiler
Table of Contents
1. Build GCC Cross Compiler
1.1. What Is Cross Build
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 的步骤为:
- build binutils
- build gcc (stage 1)
- build libc
- build gcc (stage 2)
之所以需要编译两次 gcc, 主要是因为 gcc 代码不仅包含 gcc, 还包含许多其它的 lib,例如 libatomic, libstdc++ 等, 这些库或多或少会依赖 libc.
因此需要
- 通过 stage 1 用 host gcc 编译出一个单独的 cross gcc
- 然后用编译出来的 cross gcc 编译 target libc
- 最后通过 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 取决于:
- 编译时 `–prefix` 参数
- 编译时的 `-with-as` 参数
- 运行时的 `-B` 参数
- 运行时的 `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
主要包含:
- `enable-languages=c++` 新增的 libstdc++
- libgcc (如果 stage1 没有编译 libgcc)
1.7. disable-bootstrap
默认情况下编译 gcc 时会通过 3 个 stage 确定 gcc 编译的没有问题, 例如现在需要用 gcc 4 编译 gcc 9:
- 使用 gcc 4 编译出一个 gcc 9 (a)
- 使用 a 编译出另一个 gcc 9 (b)
- 使用 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