跳过正文

GCC 交叉编译工具链

·6210 字
Cgo Go Compile Gcc
目录

交叉编译是指编译器能生成和它执行环境不同的 CPU 架构的二进制,例如在 arm64 机器上编译出在 x86_64 机器上运行的二进制。

本文分别以常用的 ubuntu aarch64 和 fedora 40 x86_64 编译环境为例,介绍这两个问题的解决方案。

target triplet
#

格式:“cpu-vendor-os”, 其中 os 可以是 system 或 kernel-system。

  • cpu:一般使用 uname -m 命令来获取,如 arm64 CPU 使用的是 aarch64, arm32 CPU 使用的是 arm。
  • vendor:一般为 pc(windows 硬件)、apple(苹果硬件)、unknown(未知、通用);
  • kernel:linux、none(不带内核的裸机程序)
  • system:
    • gnu:通用的,使用 GNU ABI,链接 glibc;
    • gnueabi:一般是 arm32 在用,使用 GNU EAPI,链接 glibc;
    • gnueabihf:一般是 arm32 在用,使用 GNU EABI(嵌入式 ABI 接口),链接 glibc,使用硬件浮点 (hard-float);
    • musl:链接一个专为嵌入式系统设计的轻量级 C 库 musl ,目标是提供与 glibc 相似的功能,但占用更少的资源。常用的场景是实现 libc 库的静态链接, 以解决运行时机器上的 glibc 缺失或版本太老的问题;
    • elf:不适用内核和操作系统的裸机程序;

triplet 示例:

## arm64(ARMv8 及以上处理器,默认硬件浮点计算)

aarch64-apple-darwin

aarch64-unknown-linux-gnu # 使用 GNU ABI 和链接 glibc 《-- 主流
aarch64-unknown-linux-gnu_ilp32
aarch64-unknown-linux-musl
aarch64-unknown-none # 不使用内核和 glibc,如逻辑 elf 程序

aarch64-none-elf:用于裸机(无操作系统)的 64 位 ARM 开发,遵循 ELF 格式。

## arm32 (ARMv7 及以前的处理器)
arm-unknown-linux-gnueabi   # 链接 glibc
arm-unknown-linux-gnueabihf # hf 表示硬件浮点 hard-float 《-- 主流
arm-unknown-linux-musleabi  # 链接 musl
arm-unknown-linux-musleabihf

x86_64-unknown-linux-gnu  《-- 主流
x86_64-unknown-linux-gnux32
x86_64-unknown-linux-musl
x86_64-unknown-linux-none

gcc 工具链一般是只支持编译生成特定架构的二进制,而不能生成其它架构的二进制。例如 Host 架构是 aarch64,则 gcc 或 aarch64-unknown-linux-gnu-gcc 命令只能编译生成 aarch64 架构的二进制。

Ubuntu、RedHat 系统支持安装多套交叉编译工具链,它们使用不同的 target-triplet 和目录结构来区分:

  • 如 x86_64-unknown-linux-gnu-gcc 能生成 x86_64 架构的二进制,它也会自动调用 x86_64-unknown-linux-gnu- 前缀的其它命令。
  • /usr/x86_64-unknown-linux-gnu 作为该交叉编译工具链的第二目录层次,下面的 bin、lib 等保存该架构的 binutils 命令、头文件、libc 标准库、gcc 命令和库等。

对于 autoconf、make、go build 等命令,可以通过 CC 等环境变量来指定使用的工具链架构前缀。

当交叉编译时,configure 会查找带有 target triplet 前缀的工具,如 compilers, linkers, assemblers,如指定运行编译产物的 –host=aarch64-linux-gnu 时,使用的 C 编译器是 aarch64-linux-gnu-gcc,pkg-config 是 aarch64-linux-gnu-pkg-config,这样可以避免找到了错误类型的工具链。

Host 架构编译工具链
#

以 Ubuntu 23.10 aarch64-linux-gnu 为例,目录结构特点:

  1. 不再提供 /lib64 和 /usr/lib64 目录。
  2. /bin, /sbin, /lib 都指向 /usr 下对应目录。
  3. libc 等标准库位于 /usr/lib/ 目录下,而非以前的 /usr/lib 目录。
  4. /usr/lib 和 /usr/libexec 都是 Host 架构库,其它架构的库位于 /usr/ 目录下。
root@lima-dev2:~# ls -l /
total 68
...
lrwxrwxrwx   1 root root     7 Mar  7  2024 bin -> usr/bin
lrwxrwxrwx   1 root root     7 Mar  7  2024 lib -> usr/lib
lrwxrwxrwx   1 root root     8 Mar  7  2024 sbin -> usr/sbin
...

root@lima-dev2:~# ls -l /usr/
total 88
drwxr-xr-x   2 root root 36864 Oct 14 18:09 bin
drwxr-xr-x   2 root root  4096 Oct 10  2023 games
drwxr-xr-x  45 root root  4096 Oct  8 12:27 include
drwxr-xr-x  90 root root  4096 Oct 14 18:09 lib
drwxr-xr-x  12 root root  4096 Oct 14 18:09 libexec
drwxr-xr-x  11 root root  4096 Jun 20 12:28 local
drwxr-xr-x   2 root root 20480 Oct  9 13:37 sbin
drwxr-xr-x 135 root root  4096 Oct 11 15:57 share
drwxr-xr-x   7 root root  4096 Jul 14 06:21 src
drwxr-xr-x   8 root root  4096 Oct 14 18:09 x86_64-linux-gnu # 其它架构(交叉编译工具链)的根目录

# Host 架构的 libc 等标准库不再放到 `/usr/lib` 目录下,
# 而是放到 Host 架构的 `/usr/lib/aarch64-linux-gnu` 目录下:
root@lima-dev2:~# ls -l /usr/lib/libc*
ls: cannot access '/usr/lib/libc*': No such file or directory

root@lima-dev2:~# ls -l /usr/lib/aarch64-linux-gnu/libc.so.6
-rwxr-xr-x 1 root root 1722984 May  1 02:11 /usr/lib/aarch64-linux-gnu/libc.so.6

Fedorat 40 Host 架构 x86_64-redhat-linux 的差异:

  • 还提供 /lib64 目录,但是指向 /usr/lib64;
  • 标准库 libc 位于 /usr/lib64 目录下;
  • 其它架构的标准库位于 /usr/aarch64-redhat-linux/sys-root/fc40/usr/lib64/ 目录下
    • 需要单独安装包 sysroot-aarch64-fc40-glibc-2.39-33.fc40.noarch
[root@d8e784ed1d22 ~]# ls -l /{bin,sbin,lib,lib64}
lrwxrwxrwx 1 root root 7 Jan 24  2024 /bin -> usr/bin
lrwxrwxrwx 1 root root 7 Jan 24  2024 /lib -> usr/lib
lrwxrwxrwx 1 root root 9 Jan 24  2024 /lib64 -> usr/lib64
lrwxrwxrwx 1 root root 8 Jan 24  2024 /sbin -> usr/sbin

[root@d8e784ed1d22 ~]# ls -l /usr/lib64/libc.*
-rw-r--r-- 1 root root 6847798 Dec 23 00:00 /usr/lib64/libc.a
-rw-r--r-- 1 root root     253 Dec 23 00:00 /usr/lib64/libc.so
-rwxr-xr-x 1 root root 2449104 Dec 23 00:00 /usr/lib64/libc.so.6


[root@d8e784ed1d22 ~]# ls /usr/aarch64-linux-gnu/bin/
ar  as  ld  ld.bfd  nm  objcopy  objdump  ranlib  readelf  strip
[root@d8e784ed1d22 ~]# ls /usr/aarch64-linux-gnu/sys-root/
[root@d8e784ed1d22 ~]# ls /usr/aarch64-redhat-linux/sys-root/fc40/usr/
include/ lib/     lib64/

[root@d8e784ed1d22 ~]# ls -l /usr/aarch64-redhat-linux/sys-root/fc40/usr/lib64/libc.*
-rw-r--r-- 1 root root 6411932 Dec 23 00:00 /usr/aarch64-redhat-linux/sys-root/fc40/usr/lib64/libc.a
-rw-r--r-- 1 root root     265 Dec 23 00:00 /usr/aarch64-redhat-linux/sys-root/fc40/usr/lib64/libc.so
-rw-r--r-- 1 root root 2364952 Dec 23 00:00 /usr/aarch64-redhat-linux/sys-root/fc40/usr/lib64/libc.so.6

编译工具链相关包
#

  1. cpp/cpp-13: 预处理器
  2. gcc/gcc-13/g++/libgcc-13-dev:编译器、GCC 相关的头文件和库
  3. binutils:汇编器、链接器和二进制工具
  4. libc6/libc6-dev/linux-libc-dev:GNU C 库、标准头文件、内核 glibc 头文件
  5. pkg-config: 架构相关的编译参数自动配置

cpp:预处理器
#

相关包: cpp-13、cpp

提供 /usr/bin/cpp 和 /usr/bin/aarch64-linux-gnu-cpp 命令:

root@lima-dev2:~# dpkg -L cpp
/usr
/usr/bin
/usr/bin/aarch64-linux-gnu-cpp  # 预处理器
/usr/bin/cpp

GCC 编译器实际使用的是内置的预处理器(gcc -E)而非外置的 cpp 命令,外置的 cpp 更多是历史兼容性场景。

gcc:编译器
#

相关包:

  1. gcc:提供不带版本后缀的编译器二进制,如 aarch64-linux-gnu-gcc
  2. gcc-13: 提供带版本后缀的编译器,如 aarch64-linux-gnu-gcc-13
  3. libgcc-13-dev:编译器自身的头文件和库文件,提供了一些编译器自身内建的函数实现,以及 C 程序 main 函数前后的逻辑。
alizj@lima-dev2:~$ apt list --installed |grep gcc |grep arm64
gcc-13-base/mantic,now 13.2.0-4ubuntu3 arm64 [installed,automatic]
gcc-13/mantic,now 13.2.0-4ubuntu3 arm64 [installed,automatic]
gcc/mantic,now 4:13.2.0-1ubuntu1 arm64 [installed,automatic]
libgcc-13-dev/mantic,now 13.2.0-4ubuntu3 arm64 [installed,automatic]
libgcc-s1/mantic,now 13.2.0-4ubuntu3 arm64 [installed,automatic]

安装的内容:

  1. /usr/bin/gcc 和 /usr/bin/aarch64-linux-gnu-* 等二进制;
  2. /usr/lib/gcc/aarch64-linux-gnu/13/usr/libexec/gcc/aarch64-linux-gnu/13: 包含 GCC 自身的头文件、库文件和调用的二进制。

gcc 包:提供不带版本后缀的编译器二进制:

root@lima-dev2:~# dpkg -L gcc
# 带 targt 前缀,但是不带版本后缀的 host 架构编译器工具
/usr/bin/aarch64-linux-gnu-gcc
/usr/bin/aarch64-linux-gnu-gcc-ar
/usr/bin/aarch64-linux-gnu-gcc-nm
/usr/bin/aarch64-linux-gnu-gcc-ranlib
/usr/bin/aarch64-linux-gnu-gcov
/usr/bin/aarch64-linux-gnu-gcov-dump
/usr/bin/aarch64-linux-gnu-gcov-tool
/usr/bin/aarch64-linux-gnu-lto-dump
# 不带 target 前缀的 host 架构编译器工具,链接到上面带 target 前缀的命令
/usr/bin/gcc
/usr/bin/gcc-ar
/usr/bin/gcc-nm
/usr/bin/gcc-ranlib
/usr/bin/gcov
/usr/bin/gcov-dump
/usr/bin/gcov-tool
/usr/bin/lto-dump
# gcc 自身使用的动态库
/usr/lib
/usr/lib/gcc
/usr/lib/gcc/aarch64-linux-gnu
/usr/lib/gcc/aarch64-linux-gnu/13
/usr/lib/gcc/aarch64-linux-gnu/13/libgomp.spec
/usr/lib/gcc/aarch64-linux-gnu/13/libhwasan_preinit.o
/usr/lib/gcc/aarch64-linux-gnu/13/libitm.spec
/usr/lib/gcc/aarch64-linux-gnu/13/libsanitizer.spec
/usr/lib/gcc/aarch64-linux-gnu/13/plugin
/usr/lib/gcc/aarch64-linux-gnu/13/plugin/libcc1plugin.so.0.0.0
/usr/lib/gcc/aarch64-linux-gnu/13/plugin/libcp1plugin.so.0.0.0
/usr/lib/gcc/aarch64-linux-gnu/13/libcc1.so
/usr/lib/gcc/aarch64-linux-gnu/13/liblto_plugin.so
/usr/lib/gcc/aarch64-linux-gnu/13/plugin/libcc1plugin.so
/usr/lib/gcc/aarch64-linux-gnu/13/plugin/libcc1plugin.so.0
/usr/lib/gcc/aarch64-linux-gnu/13/plugin/libcp1plugin.so
/usr/lib/gcc/aarch64-linux-gnu/13/plugin/libcp1plugin.so.0
# gcc 内部调用的二进制
/usr/libexec
/usr/libexec/gcc
/usr/libexec/gcc/aarch64-linux-gnu
/usr/libexec/gcc/aarch64-linux-gnu/13
/usr/libexec/gcc/aarch64-linux-gnu/13/collect2   # 链接器 wrapper
/usr/libexec/gcc/aarch64-linux-gnu/13/liblto_plugin.so
/usr/libexec/gcc/aarch64-linux-gnu/13/lto-wrapper
/usr/libexec/gcc/aarch64-linux-gnu/13/lto1

gcc-13 包提供带版本后缀的编译器工具,gcc 实际链接到 aarch64-linux-gnu-gcc-13:

alizj@lima-dev2:~$ dpkg -L gcc-13
/.
/usr
/usr/bin
/usr/bin/aarch64-linux-gnu-gcc-13
/usr/bin/aarch64-linux-gnu-gcc-ar-13
/usr/bin/aarch64-linux-gnu-gcc-nm-13
/usr/bin/aarch64-linux-gnu-gcc-ranlib-13
/usr/bin/aarch64-linux-gnu-gcov-13
/usr/bin/aarch64-linux-gnu-gcov-dump-13
/usr/bin/aarch64-linux-gnu-gcov-tool-13
/usr/bin/aarch64-linux-gnu-lto-dump-13
/usr/bin/gcc-13
/usr/bin/gcc-ar-13
/usr/bin/gcc-nm-13
/usr/bin/gcc-ranlib-13
/usr/bin/gcov-13
/usr/bin/gcov-dump-13
/usr/bin/gcov-tool-13
/usr/bin/lto-dump-13

# gcc 实际链接到 aarch64-linux-gnu-gcc-13
root@lima-dev2:~# which gcc
/usr/bin/gcc
root@lima-dev2:~# ls -l /usr/bin/gcc
lrwxrwxrwx 1 root root 6 Aug 12  2023 /usr/bin/gcc -> gcc-13
root@lima-dev2:~# ls -l /usr/bin/gcc-13
lrwxrwxrwx 1 root root 24 Sep 24  2023 /usr/bin/gcc-13 -> aarch64-linux-gnu-gcc-13

libgcc-13-dev 包提供了 gcc 提供的内建函数实现的动态库、静态库以及对应头文件,它们会被链接到 C 程序中,提供了 main 函数前后逻辑:

alizj@lima-dev2:~$ dpkg -L libgcc-13-dev
/.
/usr
/usr/lib
/usr/lib/gcc
/usr/lib/gcc/aarch64-linux-gnu
/usr/lib/gcc/aarch64-linux-gnu/13
# GCC 编译程序时链接的库文件
/usr/lib/gcc/aarch64-linux-gnu/13/crtbegin.o  # C 启动例程,调用 main 函数
/usr/lib/gcc/aarch64-linux-gnu/13/crtbeginS.o
/usr/lib/gcc/aarch64-linux-gnu/13/crtbeginT.o
/usr/lib/gcc/aarch64-linux-gnu/13/crtend.o    # C 结束例程,main 返回后执行
/usr/lib/gcc/aarch64-linux-gnu/13/crtendS.o
/usr/lib/gcc/aarch64-linux-gnu/13/crtfastmath.o
# GCC 相关头文件
/usr/lib/gcc/aarch64-linux-gnu/13/include
/usr/lib/gcc/aarch64-linux-gnu/13/include/acc_prof.h
/usr/lib/gcc/aarch64-linux-gnu/13/include/arm_acle.h
/usr/lib/gcc/aarch64-linux-gnu/13/include/arm_bf16.h
/usr/lib/gcc/aarch64-linux-gnu/13/include/arm_fp16.h
/usr/lib/gcc/aarch64-linux-gnu/13/include/arm_neon.h
/usr/lib/gcc/aarch64-linux-gnu/13/include/arm_sve.h
/usr/lib/gcc/aarch64-linux-gnu/13/include/backtrace-supported.h
/usr/lib/gcc/aarch64-linux-gnu/13/include/backtrace.h
/usr/lib/gcc/aarch64-linux-gnu/13/include/float.h
/usr/lib/gcc/aarch64-linux-gnu/13/include/gcov.h
/usr/lib/gcc/aarch64-linux-gnu/13/include/iso646.h
/usr/lib/gcc/aarch64-linux-gnu/13/include/limits.h
/usr/lib/gcc/aarch64-linux-gnu/13/include/omp.h
/usr/lib/gcc/aarch64-linux-gnu/13/include/openacc.h
/usr/lib/gcc/aarch64-linux-gnu/13/include/sanitizer
/usr/lib/gcc/aarch64-linux-gnu/13/include/sanitizer/asan_interface.h
/usr/lib/gcc/aarch64-linux-gnu/13/include/sanitizer/common_interface_defs.h
/usr/lib/gcc/aarch64-linux-gnu/13/include/sanitizer/hwasan_interface.h
/usr/lib/gcc/aarch64-linux-gnu/13/include/sanitizer/lsan_interface.h
/usr/lib/gcc/aarch64-linux-gnu/13/include/sanitizer/tsan_interface.h
/usr/lib/gcc/aarch64-linux-gnu/13/include/stdalign.h
/usr/lib/gcc/aarch64-linux-gnu/13/include/stdarg.h
/usr/lib/gcc/aarch64-linux-gnu/13/include/stdatomic.h
/usr/lib/gcc/aarch64-linux-gnu/13/include/stdbool.h
/usr/lib/gcc/aarch64-linux-gnu/13/include/stddef.h
/usr/lib/gcc/aarch64-linux-gnu/13/include/stdfix.h
/usr/lib/gcc/aarch64-linux-gnu/13/include/stdint-gcc.h
/usr/lib/gcc/aarch64-linux-gnu/13/include/stdint.h
/usr/lib/gcc/aarch64-linux-gnu/13/include/stdnoreturn.h
/usr/lib/gcc/aarch64-linux-gnu/13/include/syslimits.h
/usr/lib/gcc/aarch64-linux-gnu/13/include/unwind.h
/usr/lib/gcc/aarch64-linux-gnu/13/include/varargs.h

binutils:汇编&链接和二进制工具
#

提供二进制工具:

  1. as:汇编器,将 .s 汇编源文件转换为 ELF 格式的目标(机器码)文件;
  2. ld/ld.bfd/ld.gold :链接器, 包含 bfd(默认) 和 gold 两种类型, gold 是较新、较快的 ELF only 链接器;
  3. gprof/gprofng/addr2line
  4. ar:静态库管理
  5. elfedit/readelf/ranlib
  6. objcopy/objdump/nm/addr2line
  7. size/strings/strip

安装的包:

alizj@lima-dev2:~$ apt list --installed binutils*
binutils-aarch64-linux-gnu/mantic,now 2.41-5ubuntu1 arm64 [installed,automatic]
binutils-common/mantic,now 2.41-5ubuntu1 arm64 [installed,automatic]
binutils-dev/mantic,now 2.41-5ubuntu1 arm64 [installed]
binutils/mantic,now 2.41-5ubuntu1 arm64 [installed,automatic]

包内容:

  1. 汇编器 as 和链接器 ld,以及带 target 前缀的工具,如 aarch64-linux-gnu-ar,aarch64-linux-gnu-ld;
  2. /usr/lib/aarch64-linux-gnu/ldscripts 目录下的各种链接脚本;
alizj@lima-dev2:~$ dpkg -L binutils |sort
/.
/etc
/usr
/usr/bin
/usr/bin/addr2line
/usr/bin/ar
/usr/bin/as
/usr/bin/c++filt
/usr/bin/dwp
/usr/bin/elfedit
/usr/bin/gold
#...
/usr/bin/ld
/usr/bin/ld.bfd
/usr/bin/ld.gold
/usr/bin/nm
/usr/bin/objcopy
/usr/bin/objdump
/usr/bin/ranlib
/usr/bin/readelf
/usr/bin/size
/usr/bin/strings
/usr/bin/strip

/usr/lib
/usr/lib/aarch64-linux-gnu
/usr/lib/aarch64-linux-gnu/gprofng
/usr/lib/aarch64-linux-gnu/gprofng/libgp-collectorAPI.a
/usr/lib/compat-ld
/usr/lib/compat-ld/ld
/usr/lib/gold-ld
/usr/lib/gold-ld/ld

alizj@lima-dev2:~$ dpkg -L binutils-aarch64-linux-gnu
/.
/usr
/usr/bin
/usr/bin/aarch64-linux-gnu-addr2line
/usr/bin/aarch64-linux-gnu-ar
/usr/bin/aarch64-linux-gnu-as
/usr/bin/aarch64-linux-gnu-c++filt
/usr/bin/aarch64-linux-gnu-dwp
/usr/bin/aarch64-linux-gnu-elfedit
/usr/bin/aarch64-linux-gnu-gp-archive
/usr/bin/aarch64-linux-gnu-gp-collect-app
/usr/bin/aarch64-linux-gnu-gp-display-html
/usr/bin/aarch64-linux-gnu-gp-display-src
/usr/bin/aarch64-linux-gnu-gp-display-text
#...
/usr/lib/aarch64-linux-gnu/bfd-plugins/libdep.so
# 各种架构的链接脚本
/usr/lib/aarch64-linux-gnu/ldscripts
/usr/lib/aarch64-linux-gnu/ldscripts/aarch64elf.x
/usr/lib/aarch64-linux-gnu/ldscripts/armelfb_linux_eabi.xu
/usr/lib/aarch64-linux-gnu/ldscripts/armelfb_linux_eabi.xw
/usr/lib/aarch64-linux-gnu/ldscripts/armelfb_linux_eabi.xwe

alizj@lima-dev2:~$ dpkg -L binutils-dev
/.
/usr
/usr/include
/usr/include/ansidecl.h
/usr/include/bfd.h
/usr/include/bfdlink.h
/usr/include/collectorAPI.h
/usr/include/ctf-api.h
/usr/include/ctf.h
/usr/include/diagnostics.h
/usr/include/dis-asm.h
/usr/include/libcollector.h
/usr/include/libfcollector.h
/usr/include/plugin-api.h
/usr/include/sframe-api.h
/usr/include/sframe.h
/usr/include/symcat.h
/usr/lib
/usr/lib/aarch64-linux-gnu
/usr/lib/aarch64-linux-gnu/libbfd.a
/usr/lib/aarch64-linux-gnu/libctf-nobfd.a
/usr/lib/aarch64-linux-gnu/libctf.a
/usr/lib/aarch64-linux-gnu/libgprofng.a
/usr/lib/aarch64-linux-gnu/libopcodes.a
/usr/lib/aarch64-linux-gnu/libsframe.a

binutils 提供两种类型链接器 bfd 和 gold,默认使用 bfd:

alizj@lima-dev2:~$ dpkg -S /usr/bin/ld*
binutils: /usr/bin/ld
binutils: /usr/bin/ld.bfd
binutils: /usr/bin/ld.gold
libc-bin: /usr/bin/ld.so
libc-bin: /usr/bin/ldd

alizj@lima-dev2:~$ ls -l /usr/bin/ld
lrwxrwxrwx 1 root root 20 Sep  5  2023 /usr/bin/ld -> aarch64-linux-gnu-ld
alizj@lima-dev2:~$ ls -l /usr/bin/aarch64-linux-gnu-ld
lrwxrwxrwx 1 root root 24 Sep  5  2023 /usr/bin/aarch64-linux-gnu-ld -> aarch64-linux-gnu-ld.bfd
alizj@lima-dev2:~$ ls -l /usr/bin/aarch64-linux-gnu-ld.bfd
-rwxr-xr-x 1 root root 1710520 Sep  5  2023 /usr/bin/aarch64-linux-gnu-ld.bfd
alizj@lima-dev2:~$

gcc 和 binutils 都提供了 ar/nm 等二进制工具,但前缀不同:

root@lima-dev2:~# dpkg -S /usr/bin/aarch64-linux-gnu-*nm*
gcc: /usr/bin/aarch64-linux-gnu-gcc-nm
gcc-13: /usr/bin/aarch64-linux-gnu-gcc-nm-13
binutils-aarch64-linux-gnu: /usr/bin/aarch64-linux-gnu-nm

系统默认使用 binutils 提供的版本:

root@lima-dev2:~# which ar
/usr/bin/ar
root@lima-dev2:~# ls -l /usr/bin/ar
lrwxrwxrwx 1 root root 20 Sep  5  2023 /usr/bin/ar -> aarch64-linux-gnu-ar
root@lima-dev2:~# dpkg -S /usr/bin/aarch64-linux-gnu-ar
binutils-aarch64-linux-gnu: /usr/bin/aarch64-linux-gnu-ar

libc6 库
#

libc 涉及的包:

  1. libc6:GNU libc 库;
  2. libc6-dev: libc 库标准头文件
  3. libc-bin:locale、tzselect 等国际化支持,以及 ldd 动态连接器和配置;
  4. libc-dev-bin:仅提供 gencat 命令;
  5. libc-devtools:提供 memusage、mtrace、sprof 等不常用的内存分析命令;
  6. linux-libc-dev:提供给 libc 使用的内核头文件;

libc6 库是架构相关的,放到对应的 target triplet 目录下,如 /lib/aarch64-linux-gnu 或 /lib/x86_64-linux-gnu。

libc6 库提供了 glibc 相关的动态库:

root@lima-dev2:/home/alizj.linux# dpkg -L libc6
/.
/etc
# libc 动态加载配置
/etc/ld.so.conf.d
/etc/ld.so.conf.d/aarch64-linux-gnu.conf

/lib
# 架构相关的 libc 库目录
/lib/aarch64-linux-gnu
/lib/aarch64-linux-gnu/ld-linux-aarch64.so.1
/lib/aarch64-linux-gnu/libBrokenLocale.so.1
/lib/aarch64-linux-gnu/libanl.so.1
# GNU glibc 库
/lib/aarch64-linux-gnu/libc.so.6
/lib/aarch64-linux-gnu/libc_malloc_debug.so.0
/lib/aarch64-linux-gnu/libdl.so.2
/lib/aarch64-linux-gnu/libm.so.6
/lib/aarch64-linux-gnu/libmemusage.so
/lib/aarch64-linux-gnu/libmvec.so.1
/lib/aarch64-linux-gnu/libnsl.so.1
/lib/aarch64-linux-gnu/libnss_compat.so.2
/lib/aarch64-linux-gnu/libnss_dns.so.2
/lib/aarch64-linux-gnu/libnss_files.so.2
/lib/aarch64-linux-gnu/libnss_hesiod.so.2
/lib/aarch64-linux-gnu/libpcprofile.so
/lib/aarch64-linux-gnu/libpthread.so.0
/lib/aarch64-linux-gnu/libresolv.so.2
/lib/aarch64-linux-gnu/librt.so.1
/lib/aarch64-linux-gnu/libthread_db.so.1
/lib/aarch64-linux-gnu/libutil.so.1

libc-dev:

  1. 标准头文件位于 /usr/include/aarch64-linux-gnu 和 /usr/include 目录下;
  2. 标准库文件位于 /usr/lib/aarch64-linux-gnu;
  3. 提供了 libc.a 静态库;
alizj@lima-dev2:~$ dpkg -L libc6-dev
/.
/usr
/usr/include
/usr/include/aarch64-linux-gnu
/usr/include/aarch64-linux-gnu/a.out.h
/usr/include/aarch64-linux-gnu/bits
/usr/include/aarch64-linux-gnu/bits/a.out.h
/usr/include/aarch64-linux-gnu/bits/argp-ldbl.h
/usr/include/aarch64-linux-gnu/bits/atomic_wide_counter.h
/usr/include/aarch64-linux-gnu/bits/wchar2.h
/usr/include/aarch64-linux-gnu/bits/wctype-wchar.h
/usr/include/aarch64-linux-gnu/bits/wordsize.h
/usr/include/aarch64-linux-gnu/bits/xopen_lim.h
/usr/include/aarch64-linux-gnu/fpu_control.h
/usr/include/aarch64-linux-gnu/gnu
/usr/include/aarch64-linux-gnu/gnu/lib-names-lp64.h
/usr/include/aarch64-linux-gnu/gnu/lib-names.h
/usr/include/aarch64-linux-gnu/gnu/libc-version.h
/usr/include/aarch64-linux-gnu/gnu/stubs-lp64.h
/usr/include/aarch64-linux-gnu/gnu/stubs.h
/usr/include/aarch64-linux-gnu/ieee754.h
/usr/include/aarch64-linux-gnu/sys
/usr/include/aarch64-linux-gnu/sys/acct.h
/usr/include/aarch64-linux-gnu/sys/auxv.h
/usr/include/aarch64-linux-gnu/sys/bitypes.h
/usr/include/aarch64-linux-gnu/sys/cdefs.h
/usr/include/aarch64-linux-gnu/sys/dir.h
#...
/usr/include/aio.h
/usr/include/aliases.h
/usr/include/alloca.h
/usr/include/fcntl.h
/usr/include/features-time64.h
/usr/include/features.h
/usr/include/wchar.h
/usr/include/wctype.h
/usr/include/wordexp.h

/usr/lib
/usr/lib/aarch64-linux-gnu
/usr/lib/aarch64-linux-gnu/Mcrt1.o
/usr/lib/aarch64-linux-gnu/Scrt1.o
/usr/lib/aarch64-linux-gnu/audit
/usr/lib/aarch64-linux-gnu/audit/sotruss-lib.so
/usr/lib/aarch64-linux-gnu/crt1.o
/usr/lib/aarch64-linux-gnu/crti.o
/usr/lib/aarch64-linux-gnu/crtn.o
/usr/lib/aarch64-linux-gnu/gcrt1.o
/usr/lib/aarch64-linux-gnu/grcrt1.o
/usr/lib/aarch64-linux-gnu/libBrokenLocale.a
/usr/lib/aarch64-linux-gnu/libanl.a
/usr/lib/aarch64-linux-gnu/libc.a
/usr/lib/aarch64-linux-gnu/libc.so
/usr/lib/aarch64-linux-gnu/libc_nonshared.a
/usr/lib/aarch64-linux-gnu/libdl.a
/usr/lib/aarch64-linux-gnu/libg.a
/usr/lib/aarch64-linux-gnu/libm-2.38.a
/usr/lib/aarch64-linux-gnu/libm.a
/usr/lib/aarch64-linux-gnu/libm.so
/usr/lib/aarch64-linux-gnu/libmcheck.a
/usr/lib/aarch64-linux-gnu/libmvec.a
/usr/lib/aarch64-linux-gnu/libpthread.a
/usr/lib/aarch64-linux-gnu/libpthread_nonshared.a
/usr/lib/aarch64-linux-gnu/libresolv.a
/usr/lib/aarch64-linux-gnu/librt.a
/usr/lib/aarch64-linux-gnu/libutil.a
/usr/lib/aarch64-linux-gnu/rcrt1.o
/usr/lib/aarch64-linux-gnu/libBrokenLocale.so
/usr/lib/aarch64-linux-gnu/libanl.so
/usr/lib/aarch64-linux-gnu/libc_malloc_debug.so
/usr/lib/aarch64-linux-gnu/libmvec.so
/usr/lib/aarch64-linux-gnu/libnss_compat.so
/usr/lib/aarch64-linux-gnu/libnss_hesiod.so
/usr/lib/aarch64-linux-gnu/libresolv.so
/usr/lib/aarch64-linux-gnu/libthread_db.so

libc-bin:

  • ld.so:动态库加载器;
  • ldd:打印动态库依赖;
  • 国际化等命令:iconv、locale、localedef、tzselect;
  • /usr/lib/locale:locale 文件目录;
root@lima-dev2:/home/alizj.linux# dpkg -L libc-bin
/.
/etc
/etc/bindresvport.blacklist
/etc/gai.conf
/etc/ld.so.conf
/etc/ld.so.conf.d
/etc/ld.so.conf.d/libc.conf # libc 动态库加载配置

/sbin
/sbin/ldconfig  # 动态加载配置命令
/sbin/ldconfig.real

/usr
/usr/bin
/usr/bin/getconf
/usr/bin/getent
/usr/bin/iconv
/usr/bin/ldd    # 打印动态库对象的依赖
/usr/bin/locale # 国际化
/usr/bin/localedef
/usr/bin/pldd
/usr/bin/tzselect
/usr/bin/zdump

/usr/lib
/usr/lib/locale
/usr/lib/locale/C.utf8
/usr/lib/locale/C.utf8/LC_ADDRESS
/usr/lib/locale/C.utf8/LC_COLLATE
/usr/lib/locale/C.utf8/LC_CTYPE
#...

/var
/var/cache
/var/cache/ldconfig  # 动态加载缓存

/usr/bin/ld.so  # 动态加载器

linux-libc-dev: glibc 使用的内核头文件

root@lima-dev2:/home/alizj.linux# dpkg -L linux-libc-dev
/.
/usr
/usr/include
/usr/include/aarch64-linux-gnu  # 架构相关的头文件
/usr/include/aarch64-linux-gnu/asm
/usr/include/aarch64-linux-gnu/asm/auxvec.h
/usr/include/aarch64-linux-gnu/asm/bitsperlong.h
/usr/include/aarch64-linux-gnu/asm/bpf_perf_event.h
/usr/include/aarch64-linux-gnu/asm/byteorder.h
/usr/include/aarch64-linux-gnu/asm/errno.h
/usr/include/aarch64-linux-gnu/asm/fcntl.h
/usr/include/aarch64-linux-gnu/asm/hwcap.h
/usr/include/aarch64-linux-gnu/asm/ioctl.h
/usr/include/aarch64-linux-gnu/asm/ioctls.h
/usr/include/aarch64-linux-gnu/asm/ipcbuf.h
#...
/usr/include/aarch64-linux-gnu/asm/unistd.h

/usr/include/asm-generic  # 架构无关(通用)的头文件
/usr/include/asm-generic/auxvec.h
/usr/include/asm-generic/bitsperlong.h
/usr/include/asm-generic/bpf_perf_event.h
#...
/usr/include/asm-generic/unistd.h

/usr/include/drm
/usr/include/drm/amdgpu_drm.h
/usr/include/drm/armada_drm.h
/usr/include/drm/drm.h
#...
/usr/include/linux  # 通用内核头文件
/usr/include/linux/acct.h
#...
/usr/include/linux/xilinx-v4l2-controls.h
/usr/include/linux/zorro.h
/usr/include/linux/zorro_ids.h
#...
/usr/include/misc
/usr/include/misc/cxl.h
/usr/include/misc/fastrpc.h
/usr/include/misc/ocxl.h
/usr/include/misc/pvpanic.h
/usr/include/misc/uacce
/usr/include/misc/uacce/hisi_qm.h
/usr/include/misc/uacce/uacce.h
/usr/include/misc/xilinx_sdfec.h
#...
/usr/include/mtd
/usr/include/mtd/inftl-user.h
#...
/usr/include/rdma
/usr/include/rdma/bnxt_re-abi.h
/usr/include/rdma/cxgb4-abi.h
#...
/usr/include/scsi
/usr/include/scsi/cxlflash_ioctl.h
/usr/include/scsi/fc
/usr/include/scsi/fc/fc_els.h
/usr/include/scsi/fc/fc_fs.h
/usr/include/scsi/fc/fc_gs.h
/usr/include/scsi/fc/fc_ns.h
/usr/include/scsi/scsi_bsg_fc.h
/usr/include/scsi/scsi_bsg_mpi3mr.h
/usr/include/scsi/scsi_bsg_ufs.h
/usr/include/scsi/scsi_netlink.h
/usr/include/scsi/scsi_netlink_fc.h
#...
/usr/include/sound
/usr/include/sound/asequencer.h
/usr/include/sound/asoc.h
#...
/usr/include/video
/usr/include/video/edid.h
/usr/include/video/sisfb.h
/usr/include/video/uvesafb.h
#...
/usr/include/xen
/usr/include/xen/evtchn.h
/usr/include/xen/gntalloc.h
/usr/include/xen/gntdev.h
/usr/include/xen/privcmd.h

libc 库类型
#

libc 库封装了所有 POSTIX 系统调用,是与 Kernel 的接口,在编译 glibc 时,需要提供对应版本的内核头文件。

libc 库有多种实现类型:

  1. glibc:通用;
  2. musl libc:也是通用的 libc,但是更轻量,在轻量化容器镜像中得到广泛应用:https://musl.libc.org/
  3. uClibc/uClibc-ng:嵌入式优化的 libc 库:https://uclibc-ng.org/
  4. newlib:嵌入式优化的 libc 库:https://sourceware.org/newlib/

安装交叉编译工具链
#

ubuntu aarch64 -> x86_64 交叉静态编译
#

根据经验,ubuntu 对多架构交叉编译的支持比 centos、fedora 更成熟,后者需要 hack gcc 交叉编译工具链的 sysroot 参数。

在 ubuntu aarch64 架构机器上交叉编译出 x86_64 架构静态二进制方案:

# arm64 虚机上,为 CGO 程序交叉编译生成 amd64 程序报错
$ GOOS=linux GOARCH=amd64 CGO_ENABLED=1 go build -o myagent ./cmd/
runtime/cgo
gcc: error: unrecognized command-line option '-m64'

# 这是由于安装的 ubuntu 安装的 gcc 只支持编译生成 arm64 架构的二进制,而不支持交叉编译生成 amd64 架构的二进制。

# 启用多架构
$ sudo dpkg --add-architecture amd64

# 添加 amd64 架构源
$ cat /etc/apt/sources.list
deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports noble main restricted universe multiverse
deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports noble-updates main restricted universe multiverse
deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports noble-security main restricted universe multiverse
deb [arch=amd64] http://archive.ubuntu.com/ubuntu noble main restricted universe multiverse
deb [arch=amd64] http://archive.ubuntu.com/ubuntu noble-updates main restricted universe multiverse
deb [arch=amd64] http://archive.ubuntu.com/ubuntu noble-security main restricted universe multiverse

# 安装 amd64 架构的 libc 库
sudo apt update
sudo apt install libc6:amd64

# 安装可以生成 amd64 架构的 gcc 交叉编译工具链
$ sudo apt install gcc-x86-64-linux-gnu

$ file /usr/bin/x86_64-linux-gnu-gcc
/usr/bin/x86_64-linux-gnu-gcc: symbolic link to x86_64-linux-gnu-gcc-13

$ file /usr/bin/x86_64-linux-gnu-gcc-13
/usr/bin/x86_64-linux-gnu-gcc-13: ELF 64-bit LSB executable, ARM aarch64, version 1 (GNU/Linux), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, BuildID[sha1]=ad41a19a0955b7e6770ad00ff5cd9b97999b2f0e, for GNU/Linux 3.7.0, stripped

# 启用 CGO 的情况下,交叉编译静态链接成功
$ CC=x86_64-linux-gnu-gcc GOOS=linux GOARCH=amd64 CGO_ENABLED=1 go build -tags 'osusergo netgo sqlite_omit_load_extension no_dynamic_plugins' -o myagent -ldflags=" -extldflags '-static'" ./cmd/

fedora 40 x86_64 -> aarch64 交叉静态编译
#

类似的,以 fedora40 x86_64 编译环境为例(CentOS、RHEL 类似),如果要编译出 arm64 架构的静态二进制,需要先安装 gcc-aarch64 编译工具链,在 go build 时使用 CC 环境变量来指定使用该工具链。

同时还要安装 sysroot-aarch64-fc40-glibc 包,它为交叉编译提供了 glibc 标准库头文件和静态 glibc 库支持(大坑,卡了很久才解决),通过 CGO_LDFLAGS 环境变量的 –sysroot 参数传递给交叉编译工具链,参考:

yum install -y glibc-devel glibc-static gcc

# 在 x86_64 环境中,可以直接编译出 x86_64 架构二进制
GOOS=linux GOARCH=amd64 CGO_ENABLED=1 go build -tags 'osusergo netgo sqlite_omit_load_extension no_dynamic_plugins' -o myagent -ldflags=" -extldflags '-static'" ./cmd/

# 交叉编译 arm64 架构的二进制失败,这是由于  x86_64 gcc 不支持汇编 arm64 指令
[root@d8e784ed1d22 bp-agent]# GOOS=linux GOARCH=arm64 CGO_ENABLED=1 go build -tags 'osusergo netgo sqlite_omit_load_extension no_dynamic_plugins' -buildmode=exe -o bp-agent -ldflags=" -extldflags '-static'" ./cmd/
# runtime/cgo
gcc_arm64.S: Assembler messages:
gcc_arm64.S:30: Error: no such instruction: `stp x29,x30,[sp,'
gcc_arm64.S:34: Error: operand size mismatch for `mov'
gcc_arm64.S:36: Error: no such instruction: `stp x19,x20,[sp,'
gcc_arm64.S:39: Error: no such instruction: `stp x21,x22,[sp,'
gcc_arm64.S:42: Error: no such instruction: `stp x23,x24,[sp,'
gcc_arm64.S:45: Error: no such instruction: `stp x25,x26,[sp,'
gcc_arm64.S:48: Error: no such instruction: `stp x27,x28,[sp,'
gcc_arm64.S:52: Error: operand size mismatch for `mov'
gcc_arm64.S:53: Error: operand size mismatch for `mov'
gcc_arm64.S:54: Error: operand size mismatch for `mov'
gcc_arm64.S:56: Error: no such instruction: `blr x20'
gcc_arm64.S:57: Error: no such instruction: `blr x19'
gcc_arm64.S:59: Error: no such instruction: `ldp x27,x28,[sp,'
gcc_arm64.S:62: Error: no such instruction: `ldp x25,x26,[sp,'
gcc_arm64.S:65: Error: no such instruction: `ldp x23,x24,[sp,'
gcc_arm64.S:68: Error: no such instruction: `ldp x21,x22,[sp,'
gcc_arm64.S:71: Error: no such instruction: `ldp x19,x20,[sp,'
gcc_arm64.S:74: Error: no such instruction: `ldp x29,x30,[sp],'

# 安装支持交叉编译的 arm64 架构的编译工具链
yum install -y gcc-aarch64-linux-gnu binutils-aarch64-linux-gnu gcc-c++-aarch64-linux-gnu;

# 编译失败,提示找不到 stdlib.h 库
[root@d8e784ed1d22 bp-agent]# CC=aarch64-linux-gnu-gcc GOOS=linux GOARCH=arm64 CGO_ENABLED=1 go build -tags 'osusergo netgo sqlite_omit_load_extension no_dynamic_plugins' -o myagent -ldflags=" -extldflags '-static'" ./cmd/
# runtime/cgo
_cgo_export.c:3:10: fatal error: stdlib.h: No such file or directory
    3 | #include <stdlib.h>
      |          ^~~~~~~~~~
compilation terminated.

# 查看 aarch64-linux-gnu-gcc 编译器配置参数
[root@d8e784ed1d22 bp-agent]# aarch64-linux-gnu-gcc -v
Using built-in specs.
COLLECT_GCC=aarch64-linux-gnu-gcc
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/aarch64-linux-gnu/14/lto-wrapper
Target: aarch64-linux-gnu
Configured with: ../gcc-14.1.1-20240508/configure --bindir=/usr/bin --build=x86_64-redhat-linux-gnu --datadir=/usr/share --disable-decimal-float --disable-dependency-tracking --disable-gold --disable-libgcj --disable-libgomp --disable-libmpx --disable-libquadmath --disable-libssp --disable-libunwind-exceptions --disable-shared --disable-silent-rules --disable-sjlj-exceptions --disable-threads --with-ld=/usr/bin/aarch64-linux-gnu-ld --enable-__cxa_atexit --enable-checking=release --enable-gnu-unique-object --enable-initfini-array --enable-languages=c,c++ --enable-linker-build-id --enable-lto --enable-nls --enable-obsolete --enable-plugin --enable-targets=all --exec-prefix=/usr --host=x86_64-redhat-linux-gnu --includedir=/usr/include --infodir=/usr/share/info --libexecdir=/usr/libexec --localstatedir=/var --mandir=/usr/share/man --prefix=/usr --program-prefix=aarch64-linux-gnu- --sbindir=/usr/sbin --sharedstatedir=/var/lib --sysconfdir=/etc --target=aarch64-linux-gnu --with-bugurl=http://bugzilla.redhat.com/bugzilla/ --with-gcc-major-version-only --with-isl --with-newlib --with-plugin-ld=/usr/bin/aarch64-linux-gnu-ld --with-sysroot=/usr/aarch64-linux-gnu/sys-root --with-system-libunwind --with-system-zlib --without-headers --enable-gnu-indirect-function --with-linker-hash-style=gnu
Thread model: single
Supported LTO compression algorithms: zlib zstd
gcc version 14.1.1 20240507 (Red Hat Cross 14.1.1-1) (GCC)

# --with-sysroot=/usr/aarch64-linux-gnu/sys-root 目录为空:
[root@d8e784ed1d22 bp-agent]# ls /usr/aarch64-linux-gnu/bin/
ar  as  ld  ld.bfd  nm  objcopy  objdump  ranlib  readelf  strip
[root@d8e784ed1d22 bp-agent]# ls /usr/aarch64-linux-gnu/sys-root/
[root@d8e784ed1d22 bp-agent]#

# 安装 aarch64 对应的 sysroot 包 sysroot-aarch64-fc40-glibc.noarch,它提供了交叉编译所需的 glibc 头文件和静态库
[root@d8e784ed1d22 bp-agent]# yum install sysroot-aarch64-fc40-glibc.noarch
[root@d8e784ed1d22 bp-agent]# ls /usr/aarch64-redhat-linux/sys-root/fc40/usr/
include  lib  lib64

# 使用 CGO_CFLAGS 环境变量为交叉编译 gcc 指定 --sysroot 参数,来使用安装的 aarch64 的 glibc 头文件和静态库,
# 同时使用 CC 环境变量指定使用该工具链来编译 CGO 链接的 C 程序
[root@d8e784ed1d22 bp-agent]# CC=aarch64-linux-gnu-gcc CGO_CFLAGS="-g -O2 --sysroot=/usr/aarch64-redhat-linux/sys-root/fc40" CGO_LDFLAGS="-g -O2 --sysroot=/usr/aarch64-redhat-linux/sys-root/fc40"  GOOS=linux GOARCH=arm64 CGO_ENABLED=1 go build -x -tags 'osusergo netgo sqlite_omit_load_extension no_dynamic_plugins' -o myagent -ldflags=" -extldflags '-static'" ./cmd/

[root@d8e784ed1d22 bp-agent]# file myagent
myagent: ELF 64-bit LSB executable, ARM aarch64, version 1 (GNU/Linux), statically linked, BuildID[sha1]=90ac14fe0ac828b0defb3ae4e3c328a589948a8c, for GNU/Linux 3.7.0, with debug_info, not stripped

[root@d8e784ed1d22 bp-agent]# ldd myagent
        not a dynamic executable

到此为止,我们已经使用发行版 glibc 库+ 交叉编译工具链方案,完美解决了为 CGO 程序生成静态链接可执行程序的问题,实现了在 x86_64 架构的编译机上同时编译出 x86_64 和 aarch64 架构的静态链接二进制的目标。

后文将介绍另外一种交叉编译+静态链接的方案,它使用轻量化的 musl libc 库(而非 glibc 库)和下载开源的交叉编译工具链,来生成更轻量、更小巧的静态链接可执行程序。类似方案在云原生容器场景(如 alpine 镜像)中得到广泛应用。

Makefile 环境变量和交叉编译
#

参考: 20240524-makefile-manual.md

交叉编译 pkg-config
#

pkg-config 是一个管理编译和链接参数的工具,可以简化处理外部依赖库的工作。

pkg-config 是架构相关的,需要安装对应目录架构的 pkg-config 包,后续使用 target triplet prefix 来区分:

  • pkgconf-pkg-config.x86_64
  • pkgconf-pkg-config.aarch64
alizj@lima-dev2:~$ ls -l /usr/bin/pkg-config
lrwxrwxrwx 1 root root 7 Apr 24  2023 /usr/bin/pkg-config -> pkgconf
alizj@lima-dev2:~$ dpkg -S /usr/bin/pkgconf
pkgconf-bin: /usr/bin/pkgconf
alizj@lima-dev2:~$ dpkg -S /usr/bin/aarch64-linux-gnu-pkg-config
pkgconf:arm64: /usr/bin/aarch64-linux-gnu-pkg-config

可以使用 pkg-config 获取所安装的库提供的 pkg-config 文件,从中自动生成使用编译和链接这个库时所需要的 编译和链接所需的标志

除了用 -I 和 -L 手动指定路径外,还可以使用一些包提供的 pkg-config 机制和命令来自动查找要链接的库(含顺序)和头文件目录:

  • 它使用库开发者发布的 .pc 格式文件来自动生成 -I、-L 和 -l 参数。
  • 库开发者使用 pkg-config –cflags –libs xxx 命令,生成 gcc 的 -L 或 -l 参数(可以与 gcc、makefile 或 Autotools、CMake 等集成)
  • 主流构构建工具,如 CMake/autotools/meson/bazel 都支持 pkg-config 来配置和管理动态链接库;

示例:

CFLAGS="`pkg-config --cflags apophenia glib-2.0` -g -Wall -std=gnu11 -O3"
LDLIBS="`pkg-config --libs apophenia glib-2.0`"
# 等效为
CFLAGS="-I/home/b/root/include -g -Wall-std=gnu11 -O3"
LDLIBS="-L/home/b/root/lib -lweirdlib"

# 两者结合使用
gcc `pkg-config --cflags --libs gsl libxml-2.0` -o specific specific.c
# 等效为
gcc -I/usr/include/libxml2 -lgsl -lgslcblas -lm -lxml2 -o specific specific.c

# 其他示例:
CFLAGS=-I/usr/local/include
LDLIBS=-L/usr/local/lib -Wl,-R/usr/local/lib.

示例: Makefile 中使用 pkg-config,假设依赖 glib 库:

# Compiler and flags
CC = gcc
CFLAGS = -Wall -Iinclude $(shell pkg-config --cflags glib-2.0)  # 生成编译所需的参数,如 -Idir

# Directories
SRC_DIR = src
LIB_DIR = lib
EXT_LIB_DIR = ext_lib

# Source files
SRC_FILES = $(wildcard $(SRC_DIR)/*.c)
LIB_FILES = $(wildcard $(LIB_DIR)/*.c)

# Object files
OBJ_FILES = $(SRC_FILES:.c=.o) $(LIB_FILES:.c=.o)

# External libraries
EXT_LIBS = -L$(EXT_LIB_DIR) -lotherlib $(shell pkg-config --libs glib-2.0) # 生成链接所需参数,如 -Ldir -llib

# Output executable
OUTPUT = myprogram

# Default target
all: $(OUTPUT)

# Link object files
$(OUTPUT): $(OBJ_FILES)
	$(CC) -o $@ $^ $(EXT_LIBS)

# Compile source files
%.o: %.c
	$(CC) $(CFLAGS) -c -o $@ $<

# Clean up
clean:
	rm -f $(SRC_DIR)/*.o $(LIB_DIR)/*.o $(OUTPUT)

.PHONY: all clean

交叉编译配置参数 autoconf
#

对于 autoconf 项目,执行 ./configure 脚本时需要指定交叉编译参数:

  1. –build=build-type:the type of system on which the package is being configured and compiled. It defaults to the result of running config.guess.
  2. –host=host-type:the type of system on which the package runs. By default it is the same as the build machine. Specifying it enables the cross-compilation mode.
  3. –target=target-type:the type of system for which any compiler tools in the package produce code (rarely needed). By default, it is the same as host.
# --prefix 指定安装路径
./configure --build=i686-pc-linux-gnu --host=m68k-coff --prefix=/path/to/install
make
make install

When cross-compiling, configure will warn about any tools (compilers, linkers, assemblers) whose name is not prefixed with the host type. This is an aid to users performing cross-compilation. Continuing the example above, if a cross-compiler named cc is used with a native pkg-config, then libraries found by pkg-config will likely cause subtle build failures; but using the names m68k-coff-cc and m68k-coff-pkg-config avoids any confusion. Avoiding the warning is as simple as creating the correct symlinks naming the cross tools.

configure recognizes short aliases for many system types; for example, ‘decstation’ can be used instead of ‘mips-dec-ultrix4.2’. configure runs a script called config.sub to canonicalize system type aliases.

This section deliberately omits the description of the obsolete interface; see Hosts and Cross-Compilation.

打包的应用程序,在运行 configure 脚本时,可以指定 –prefix 选项来指定安装路径,这样即使没有系统路径安装权限,也可以在自己指定的目录来安装二进制:

# 配置、编译和安装
./configure --prefix=$HOME/root && make && make instal

# 后续使用
export PATH=$HOME/root/bin:$PATH
export LDLIBS=-L$(HOME)/root/lib #(plus the other flags, like -lgsl -lm ...)
export CFLAGS=-I$(HOME)/root/include #(plus -g -Wall -O3 ...)

参考
#

  1. https://www.gnu.org/software/autoconf/manual/autoconf-2.68/html_node/Specifying-Target-Triplets.html
  2. rustc –print target-list
  3. LLVM 定义的 triple:https://llvm.org/doxygen/classllvm_1_1Triple.html

相关文章

Go CGO 程序静态编译链接
·2347 字
Cgo Go Compile Gcc
本文先介绍 Go CGO 的概念和应用场景,以项目用到的 mattn/go-sqlite3 为例,介绍 CGO 程序的静态链接实现方案,其中涉及到动态链接的问题分析、 ubunut/centos 系统的静态编译环境搭建,CGO 静态编译遇到的问题和解决方案,最终生成最小化系统环境依赖的静态链接二进制。
链接器 ld
··6277 字
Gnu Gcc Ld
使用 linux bpftrace 进行内核和应用性能分析
·9685 字
Bpftrace Kernel Ebpf Performance Tool
介绍 bpftrace 工具的使用方式、局限性和问题。
使用 linux perf 进行内核和应用性能分析
·5365 字
Perf Kernel Performance Tool
介绍 perf 工具的使用方式、局限性和问题。