Alpine系统中静态编译程序

 

搭建编译环境

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
# 因为在Debian下静态编译程序可能会因为GLIBC无法静态链接而失败,所以用Alpine系统的MUSL
docker pull alpine:latest
docker run -it --name alpine alpine:latest /bin/sh
docker exec -it --name alpine /bin/sh

# 将Alpine系统的官方源替换为国内源,系统的 edge 为滚动开发版本不建议日常使用
sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories

# 将Alpine系统升级为最新稳定版,以从 v3.17 升级为例
apk -U upgrade # 先将软件包升级到最新的可用版本
sed -i 's/v3.17/latest-stable/g' /etc/apk/repositories # 更换新版本的源,latest-stable 为最新稳定版
apk update && apk upgrade --available # --available 用于强制升级所有包,即使它们具有相同的版本号
# 注意:/etc 下的一些配置将不会覆盖,而是将新的文件重命名为 ***.apk-new

# Alpine系统包的搜索:https://pkgs.alpinelinux.org/packages ,包内文件查看:https://pkgs.alpinelinux.org/contents
主 源:https://mirrors.ustc.edu.cn/alpine/edge/main/aarch64/
社区源:https://mirrors.ustc.edu.cn/alpine/edge/community/aarch64/

# 可以在该页面中的 【Build log】 里面查看该软件的编译日志
https://pkgs.alpinelinux.org/package/edge/main/aarch64/dnsmasq

# build-base包内含:binutils file gcc g++ make libc-dev fortify-headers patch

# 编译中的一些常用的变量解释,因为GCC中库的链接顺序是从右往左进行,所以要把最基础实现的库放在最后
CC C compiler command
CFLAGS C compiler flags
LDFLAGS linker flags, e.g. -L<lib dir>
LIBS libraries to pass to the linker, e.g. -l<library>
CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I<include dir>

静态编译DNSMASQ

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# 配置基本的开发环境
apk add wget gcc make musl-dev linux-headers

# 静态编译仅具有默认功能的DNSMASQ
wget https://dnsmasq.org/dnsmasq-2.90.tar.gz
tar -xzvf dnsmasq-2.90.tar.gz && cd dnsmasq-2.90
make LDFLAGS="-static"

# strip前后的体积变化
strip /root/dnsmasq-2.90/src/dnsmasq
-rwxr-xr-x 1 root root 1.3M May 28 09:04 src/dnsmasq
-rwxr-xr-x 1 root root 520.6K May 28 09:09 src/dnsmasq

# 默认静态编译DNSMASQ具有的功能
~/dnsmasq-2.90 # src/dnsmasq -v
Dnsmasq version Copyright (c) 2000-2024 Simon Kelley
Compile time options: IPv6 GNU-getopt no-DBus no-UBus no-i18n no-IDN DHCP DHCPv6 no-Lua TFTP no-conntrack ipset no-nftset auth no-cryptohash no-DNSSEC loop-detect inotify dumpfile

------------------------------------------------------------------
# 要动态编译具有全功能的DNSMASQ需要安装如下依赖
apk add nettle-dev libidn2-dev lua5.1-dev dbus-dev libintl gettext-dev libnetfilter_conntrack-dev nftables-dev

# 动态编译命令如下
make LIBS="-lintl" CFLAGS="-fstack-clash-protection -Wformat -Werror=format-security -flto=auto" \
COPTS="-DHAVE_DBUS -DHAVE_IDN -DHAVE_LIBIDN2 -DHAVE_LUASCRIPT -DHAVE_CONNTRACK -DHAVE_NFTSET -DHAVE_DNSSEC" all-i18n

# 动态编译后具有全功能的DNSMASQ
~/dnsmasq-2.90 # src/dnsmasq -v
Dnsmasq version Copyright (c) 2000-2024 Simon Kelley
Compile time options: IPv6 GNU-getopt DBus no-UBus i18n IDN2 DHCP DHCPv6 Lua TFTP conntrack ipset nftset auth cryptohash DNSSEC loop-detect inotify dumpfile

------------------------------------------------------------------
# 要静态编译具有多功能的DNSMASQ不仅需安装动态编译所需的开发包,还需安装其静态库
apk add nettle-dev libidn2-dev lua5.1-dev dbus-dev libintl gettext-dev libnetfilter_conntrack-dev nftables-dev \
nettle-static nftables-static libidn2-static gettext-static libnfnetlink-static libnetfilter_conntrack-static \
libmnl-dev libmnl-static libunistring-dev libunistring-static gmp-dev gmp-static libnftnl-dev

# 静态编译命令如下(由于-DHAVE_DBUS缺静态库所以功能缺失,而-DHAVE_NFTSET则静态编译过不了)
make LIBS="-lintl -lnftnl -lmnl -lunistring" LDFLAGS="-static" \
CFLAGS="-fstack-clash-protection -Wformat -Werror=format-security -flto=auto" \
COPTS="-DHAVE_IDN -DHAVE_LIBIDN2 -DHAVE_LUASCRIPT -DHAVE_CONNTRACK -DHAVE_DNSSEC" all-i18n

# 静态编译后具有高级功能的DNSMASQ(因缺 dbus-static 遂 no-DBus,而NFTSET静态会报错遂 no-nftset)
~/dnsmasq-2.90 # src/dnsmasq -v
Dnsmasq version Copyright (c) 2000-2024 Simon Kelley
Compile time options: IPv6 GNU-getopt no-DBus no-UBus i18n IDN2 DHCP DHCPv6 Lua TFTP conntrack ipset no-nftset auth cryptohash DNSSEC loop-detect inotify dumpfile


# 拷贝容器中编译好的程序到本地
docker cp alpine:/root/dnsmasq-2.90/src/dnsmasq /root/

静态编译HOSTAPD

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
36
37
38
39
40
41
42
43
44
# 安装编译所需的工具及库
apk add wget gcc make pkgconf libc-dev linux-headers
apk add libnl3-dev openssl-dev libnl3-static openssl-libs-static

# 下载源码
wget https://w1.fi/releases/hostapd-2.10.tar.gz
tar -xzvf hostapd-2.10.tar.gz
cd hostapd-2.10/hostapd/
wget -O .config https://github.com/openwrt/openwrt/raw/main/package/network/services/hostapd/files/hostapd-full.config

# 如果需要静态编译,添加如下内容到 hostapd-2.10/hostapd/Makefile 的最前面
LDFLAGS += -static
LIBS += -l:libnl-3.a -l:libnl-genl-3.a -l:libssl.a -l:libcrypto.a

# 开始编译
make hostapd hostapd_cli \
CONFIG_ACS=y CONFIG_DRIVER_NL80211=y CONFIG_DRIVER_WEXT= CONFIG_TLS=openssl \
CONFIG_IEEE80211N=y CONFIG_IEEE80211AC=y CONFIG_IEEE80211AX=y \
CONFIG_SAE=y CONFIG_OWE=y CONFIG_SUITEB192=y CONFIG_AP=y CONFIG_MESH=y


# 使用strip给软件减肥后再查看生成的程序相关情况
strip /root/hostapd-2.10/hostapd/hostapd
strip /root/hostapd-2.10/hostapd/hostapd_cli
------------------------------------------------------------------
-rwxr-xr-x 1 root root 23.0M May 28 07:56 hostapd
-rwxr-xr-x 1 root root 5.2M May 28 08:05 hostapd
-rwxr-xr-x 1 root root 867.6K May 28 07:56 hostapd_cli
-rwxr-xr-x 1 root root 193.9K May 28 08:05 hostapd_cli

/root/hostapd-2.10/hostapd # ldd hostapd
/lib/ld-musl-x86_64.so.1: hostapd: Not a valid dynamic program
/root/hostapd-2.10/hostapd # file hostapd
hostapd: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, BuildID[sha1]=2fb3aac78b451790584157bd3321c7778afb66c0, stripped
/root/hostapd-2.10/hostapd # ldd hostapd_cli
/lib/ld-musl-x86_64.so.1: hostapd_cli: Not a valid dynamic program
/root/hostapd-2.10/hostapd # file hostapd_cli
hostapd_cli: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, BuildID[sha1]=555369dfded47e00982fe380b9beaaf6b4d48a8d, stripped
------------------------------------------------------------------

# 拷贝容器中编译好的程序到本地
docker cp alpine:/root/hostapd-2.10/hostapd/hostapd /root/
docker cp alpine:/root/hostapd-2.10/hostapd/hostapd_cli /root/

静态编译USBIP

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
36
37
38
39
# 安装编译所需的工具及库
apk add build-base autoconf automake libtool eudev-dev linux-headers

# 下载解压源码
wget https://www.kernel.org/pub/linux/kernel/v6.x/linux-6.12.17.tar.gz
tar -xzvf linux-6.12.17.tar.gz
cp -r linux-6.12.17/tools/usb/usbip/ ~ && cd ~/usbip

# 去除报错及生成 configure 脚本
sed -i 's,-Wall -Werror -Wextra,,' configure.ac
./autogen.sh

# 开启静态和开启动态必须同时设置为静开动关才能静态编译
./configure --prefix=$HOME/usbip_build \
--enable-static=yes --enable-shared=no --with-usbids-dir="." \
CFLAGS="-static -no-pie" LDFLAGS="-L/usr/lib" LIBS="-l:libudev.a -l:libc.a"

# 详细显示编译信息及安装到指定位置
make V=s && make install

# 使用strip给两个程序减肥
strip src/usbip src/usbipd

# 将生成的程序拷贝到指定位置后,获取 usb.ids 到 usbip 程序所在目录(上面with-usbids-dir中指定)
wget https://github.com/vcrhonek/hwdata/raw/refs/heads/master/usb.ids

# 如果生成的程序虽然不依赖动态库,但它指定了动态链接器(例如:/lib/ld-musl-x86_64.so.1)
# 如果仅 CFLAGS="-static" 无效,可再添加参数 CFLAGS="-no-pie" 使其不依赖依赖动态链接器
/root/usbip # ldd src/usbipd
/lib/ld-musl-x86_64.so.1 (0x7fb5ea957000)
/root/usbip # file src/usbipd
/mnt/d/usbipd: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-musl-x86_64.so.1, stripped

# PIE依赖动态链接器(如ld-*.so)完成运行时重定位,与传统可执行文件的区别:PIE需通过动态链接器加载,而传统文件可能直接使用固定地址‌
/root/usbip # ldd src/usbipd
/lib/ld-musl-x86_64.so.1: src/usbipd: Not a valid dynamic program
/root/usbip # file src/usbipd
src/usbipd: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, stripped