编译,链接和构建

《程序员的自我修养–链接,装载与库》这本书是在读研时才看的,印象很深,现在想想这本书讲的都是程序员,尤其是从事系统编程的必备素养。这里我将平时使用的跟编译,链接和构建应用程序及库相关的知识记录下来,希望以后能温故知新。

编译与链接

Note:

  • ld 这个名字是历史遗留,实际上面这些 ld 并不负责加载程序或库,它们负责在编译阶段生成可执行程序 ELF 时符号和地址的"拼装"
  • 负责运行时链接和加载库的是 /lib32/ld-linux.so.2/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2,它们即是所谓的动态链接器

gcc 的优化级别

-O-O1 是等价的。 -O 会对目标文件大小和执行时间进行优化,但不会进行非常耗时的优化,以下是 -O 打开的优化: (其中红色的是 -Og 关闭的)

  • -fauto-inc-dec
  • -fbranch-count-reg
  • -fcombine-stack-adjustments
  • -fcompare-elim
  • -fcprop-registers
  • -fdce
  • -fdefer-pop
  • -fdelayed-branch
  • -fdse
  • -fforward-propagate
  • -fguess-branch-probability
  • -fif-conversion
  • -fif-conversion2
  • -finline-functions-called-once
  • -fipa-modref
  • -fipa-profile
  • -fipa-reference
  • -fipa-reference-addressable
  • -fmerge-constants
  • -fmove-loop-invariants
  • -fmove-loop-stores
  • -fomit-frame-pointer
  • -freorder-blocks
  • -fshrink-wrap
  • -fshrink-wrap-separate
  • -fsplit-wide-types
  • -fssa-backprop
  • -fssa-phiopt
  • -ftree-bit-ccp
  • -ftree-ccp
  • -ftree-ch
  • -ftree-coalesce-vars
  • -ftree-copy-prop
  • -ftree-dce
  • -ftree-dominator-opts
  • -ftree-dse
  • -ftree-forwprop
  • -ftree-fre
  • -ftree-phiprop
  • -ftree-pta
  • -ftree-scev-cprop
  • -ftree-sink
  • -ftree-slsr
  • -ftree-sra
  • -ftree-ter
  • -funit-at-a-time

动态库都去哪儿呢

  • pkg-config

pkg-config (symbolic link to /usr/bin/pkgconf) 是用来获取系统上安装的库的信息的程序。cmake, meson 这些构建系统底层都是靠它来解析依赖包的。 下面的命令可以查看 pkg-config 工作时所搜索的路径和优先次序, 用户也可以通过环境变量 PKG_CONFIG_PATH 来指定自己想要优先搜索的路径。

1
pkg-config --variable pc_path pkg-config | sed 's/:/\n/g'
1
2
3
4
5
6
/usr/local/lib/x86_64-linux-gnu/pkgconfig
/usr/local/lib/pkgconfig
/usr/local/share/pkgconfig
/usr/lib/x86_64-linux-gnu/pkgconfig
/usr/lib/pkgconfig
/usr/share/pkgconfig

pkg-config 是二进制可执行程序 /usr/bin/pkgconf 的一个符号链接文件,它是 CMake, meson 等构建系统主要使用的系统动态库检测的工具。pkg-config 本质上是在解析 *.pc 文件。下面是常见的 Mesa OpenGL Library 的 .pc 文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
➜  piglit git:(main) ✗ find /usr -name 'gl.pc' -ls
78674 4 -rw-r--r-- 1 root root 205 Jan 3 2023 /usr/lib/x86_64-linux-gnu/pkgconfig/gl.pc
72985 4 -rw-r--r-- 1 root root 362 Jun 7 18:16 /usr/local/lib/x86_64-linux-gnu/pkgconfig/gl.pc
➜ piglit git:(main) ✗ bat /usr/local/lib/x86_64-linux-gnu/pkgconfig/gl.pc
───────┬────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
│ File: /usr/local/lib/x86_64-linux-gnu/pkgconfig/gl.pc
───────┼────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
1 │ prefix=/usr/local
2 │ includedir=${prefix}/include
3 │ libdir=${prefix}/lib/x86_64-linux-gnu
4 │
5 │ glx_tls=yes
6 │
7 │ Name: gl
8 │ Description: Mesa OpenGL Library
9 │ Version: 24.2.0-devel
10 │ Requires.private: x11, xext, xfixes, x11-xcb, xcb, xcb-glx >= 1.8.1, xcb-dri2 >= 1.8, xxf86vm, libdrm >= 2.4.75
11 │ Libs: -L${libdir} -lGL
12 │ Libs.private: -lpthread -pthread -lm
13 │ Cflags: -I${includedir}
───────┴────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
➜ piglit git:(main) ✗ bat /usr/lib/x86_64-linux-gnu/pkgconfig/gl.pc
───────┬────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
│ File: /usr/lib/x86_64-linux-gnu/pkgconfig/gl.pc
───────┼────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
1 │ prefix=/usr
2 │ includedir=${prefix}/include
3 │ libdir=${prefix}/lib/x86_64-linux-gnu
4 │
5 │ Name: GL
6 │ Description: Legacy OpenGL and GLX library and headers.
7 │ Version: 1.2
8 │ Libs: -L${libdir} -lGL
9 │ Cflags: -I${includedir}
───────┴────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
  • lib*-devlib* 的区别
1
2
3
4
5
6
7
8
9
10
11
12
$ dpkg -L libxcb1
/.
/usr
/usr/lib
/usr/lib/x86_64-linux-gnu
/usr/lib/x86_64-linux-gnu/libxcb.so.1.1.0
/usr/share
/usr/share/doc
/usr/share/doc/libxcb1
/usr/share/doc/libxcb1/changelog.Debian.gz
/usr/share/doc/libxcb1/copyright
/usr/lib/x86_64-linux-gnu/libxcb.so.1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ dpkg -L libxcb1-dev
/.
/usr
/usr/include
/usr/include/xcb
/usr/include/xcb/bigreq.h
/usr/include/xcb/xc_misc.h
/usr/include/xcb/xcb.h
/usr/include/xcb/xcbext.h
/usr/include/xcb/xproto.h
/usr/lib
/usr/lib/x86_64-linux-gnu
/usr/lib/x86_64-linux-gnu/libxcb.a
/usr/lib/x86_64-linux-gnu/pkgconfig
/usr/lib/x86_64-linux-gnu/pkgconfig/xcb.pc
/usr/share
/usr/share/doc
/usr/share/doc/libxcb1-dev
/usr/share/doc/libxcb1-dev/copyright
/usr/lib/x86_64-linux-gnu/libxcb.so
/usr/share/doc/libxcb1-dev/changelog.Debian.gz
  • /usr/sbin/ldconfig

Configure Dynamic Linker Run Time Bindings

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/bin/sh

if test $# = 0 \
&& test x"$LDCONFIG_NOTRIGGER" = x \
&& test x"$DPKG_MAINTSCRIPT_PACKAGE" != x \
&& dpkg-trigger --check-supported 2>/dev/null
then
if dpkg-trigger --no-await ldconfig; then
if test x"$LDCONFIG_TRIGGER_DEBUG" != x; then
echo "ldconfig: wrapper deferring update (trigger activated)"
fi
exit 0
fi
fi

exec /sbin/ldconfig.real "$@"

GOT, PLT & PIC

  • GOT Global Offset Table
  • PLT Procedure Linkage Table
  • PIC Position Independent Code

构建系统

  • autotools
  • cmake
  • make
  • meson
  • ninja
  • scons

autotools

flowchart LR
  autoreconf(("autoreconf<br>[Perl script]"))
  autoconf(autoconf)
  aclocal(aclocal)
  automake(automake)
  autoheader(autoheader)
  gettextize(gettextize)
  libtoolize(libtoolize)

  am@{shape: docs, label: "Makefile.am"}
  in@{shape: docs, label: "Makefile.in"}
  mk@{shape: docs, label: "Makefile"}
  ac@{shape: paper-tape, label: "configure.ac"}
  co@{shape: paper-tape, label: "configure<br>[Shell script]"}

  autoreconf --> autoconf
  autoreconf -.-> aclocal
  autoreconf -.-> automake
  autoreconf -.-> autoheader
  autoreconf -.-> gettextize
  autoreconf -.-> libtoolize

  am --> automake --> in
  ac --> autoconf --> co
  in --> co --> mk

autotools 的唯一的可取处就是 GNU 项目广泛使用它。使用它的项目的构建步骤一般是

1
2
3
./autogen.sh
./configure --prefix=$PREFIX
make

当你遇到 ./autogen.sh: 13: autoreconf: not found 这样的错误时,你可能需要安装这些软件包

1
sudo apt-get install autoconf automake libtool gettext

cmake

  • use gold
1
cmake -B build -DCMAKE_BUILD_TYPE=Debug -DCMAKE_EXPORT_COMPILE_COMMANDS=On -DCMAKE_EXE_LINKER_FLAGS=-fuse-ld=gold

meson

meson 的 buildtype 是用来设定编译优化级别 (optimization levels: -O0, -O1, -O2, -O3, -Os) 和是否有调试信息 (debug: -g)。 实际上,meson 提供两个分开的选项分别控制编译优化级别和调试信息

  • -Doptimization (plain|0|2|3|s, plain 指不设置任何 optimization flags)

  • -Ddebug (true|false)

  • 只编译某个 target

1
ninja -C build target
  • meson install --tags tag1,tag2

Installation tags 是专门为打包 (packaging) 设计的,因为打包时开发文件包(头文件),文档包 (mannul) 和二进制包 (shared libraries) 一般是分开的 3 个包。所以 meson install --tags 可以让用户分 3 次安装,每次只安装这个包所需的文件。meson 有几个预定义的 tags (不用用户自己使用 install_tag 关键字去指定 tag 名)

tags files
devel static_library(), install_headers(), .a, .pc
runtime executable(), shared_library(), shared_module(), .so, .dll
man install_man()
doc hotdoc.generate_doc()
i18n i18n.gettext(), files installed into localedir
bin scripts and executables bundled with a library used by end users
bin-devel scripts and executables bundled with a library used by developers
  • use gold
1
meson build --prefix=/usr -D{c,cpp}_args=-fuse-ld=gold -Dflavors=x11-gl,x11-glesv2

参考