iptables-yocto-debug-summary
iptables 静态编译 & 跨板子部署调试总结
目标
在 Yocto (i.MX6UL, fsl-imx-fb) 项目中编译 iptables,使其能在不同 glibc 版本的 ARM 板子上运行。
尝试过程
尝试 1:直接加 -static 到全局 LDFLAGS
做法: LDFLAGS += "-static"
报错:
dangerous relocation: unsupported relocation
R_ARM_TLS_LE32 relocation not permitted in shared object
原因: -static 作用到所有链接目标,包括 xtables 扩展模块 (.so)。共享对象不能链接静态 libc.a。
尝试 2:用 sed 去掉 extensions 的 -static
做法: LDFLAGS += "-static" + do_compile_prepend 中 sed 去掉 extensions/GNUmakefile 的 -static
sed -i 's/LDFLAGS .*=.*-static/LDFLAGS = -Wl,-O1.../' ${B}/extensions/GNUmakefile
结果: 编译成功,但 file 显示仍是 dynamically linked。
原因: libtool 消费了 -static 但没有传给 gcc(只链接 .a 不传 -static 给 ld)。
尝试 3:libtool -all-static
做法: 通过 do_configure_append sed 修改 iptables 程序 Makefile 的 LINK 命令,加 -all-static
sed -i 's/$(LDFLAGS) -o/$(LDFLAGS) -all-static -o/' ${B}/iptables/Makefile
结果: 未生效,因 Makefile 结构在 clean 后重建时变了。
原因: 不同版本 automake 生成的 Makefile 结构不同,sed 匹配失败。
尝试 4:--enable-static + sed 去 extensions -static
做法: EXTRA_OECONF_append = " --enable-static" + do_compile_prepend sed 去 extension 的 -static
结果: 编译成功,但 file 还是 dynamically linked。
原因: 发现 --disable-static 来自 poky.conf → no-static-libs.inc:
# no-static-libs.inc
DISABLE_STATIC ?= " --disable-static"
EXTRA_OECONF_append = "${DISABLE_STATIC}"
EXTRA_OECONF_append 在 EXTRA_OECONF += "--enable-static" 之后才追加,所以 --disable-static 最后生效。
修复: 改用 EXTRA_OECONF_append = " --enable-static" 确保在最后。
尝试 5:--enable-static + 手动 do_install 重链接
核心发现: --enable-static 后 extensions GNUmakefile 把 .so 从 targets 中注释掉了:
# targets += ${pfx_solibs} ${pfb_solibs} ${pf4_solibs} ${pf6_solibs} ...
所以只有 .a 没有 .so,recipe 的 do_split_packages 找不到 .so 导致 QA 报错 installed-vs-shipped。
做法: 正常编译 + 在 do_install_append 中手动用交叉编译器重链接:
${CCLD} -static ${CFLAGS} -o ${D}${sbindir}/xtables-legacy-multi \
xtables_legacy_multi-*.o \
../extensions/libext.a ../extensions/libext4.a ../extensions/libext6.a \
../libiptc/.libs/libip4tc.a ../libiptc/.libs/libip6tc.a \
../libxtables/.libs/libxtables.a -ldl -lm
结果: ✅ file 显示 statically linked 编译成功!
尝试 6:板子上运行静态版本 → 崩溃
命令: iptables -A INPUT ... -j ACCEPT
报错:
iptables: dl-call-libc-early-init.c:37: _dl_call_libc_early_init: Assertion `sym != NULL' failed.
Aborted
原因: 静态链接 glibc + dlopen() 不兼容。libxtables 代码中调用了 dlopen/dlclose,但静态 glibc 没有动态链接器,调用 dlopen 触发断言崩溃。
结论: 全静态 glibc 二进制不可行(除非用 musl)。
尝试 7:动态编译 + 打包配套 .so
思路: 放弃静态,改为动态编译;将编译时配套的 libc.so.6、ld-linux-armhf.so.3 等一起打包。在板子上通过 LD_LIBRARY_PATH 使用配套库。
做法: 在 do_install_append 中创建 /opt/iptables/:
/opt/iptables/
├── run.sh # 包装脚本
├── bin/
│ ├── xtables-legacy-multi # 主程序
│ └── iptables → xtables-legacy-multi (及 ip6tables 等符号链接)
├── lib/
│ ├── ld-linux-armhf.so.3 # 动态链接器
│ ├── libc.so.6 # C 库
│ ├── libdl.so.2
│ ├── libm.so.6
│ └── libpthread.so.0
└── libexec/
└── xtables/ # 扩展模块 .so 文件
遭遇的 QA 报错:
| 报错 | 原因 | 修复 |
|---|---|---|
installed-vs-shipped |
/opt/iptables 不在 FILES 中 |
FILES_${PN} += "/opt/iptables/*" |
Multiple shlib providers |
打包了 libc.so.6 等,与 glibc 冲突 | PRIVATE_LIBS = "libc.so.6 ..." |
dev-so |
.so 符号链接在主包中 | INSANE_SKIP_${PN} = "dev-so" |
尝试 8:板子上运行 bundle → 又一报错
命令: LD_LIBRARY_PATH=/opt/iptables/lib /opt/iptables/bin/xtables-legacy-multi iptables ...
报错:
symbol _dl_fatal_printf, version GLIBC_PRIVATE not defined in file ld-linux-armhf.so.3
原因: 内核加载 ELF 时先读硬编码的 /lib/ld-linux-armhf.so.3(板子的),然后用板子的 ld 加载我们配套的 libc.so.6——两者版本不同步,内部符号对不上。
修复: 直接调用配套的 ld-linux:
# 正确做法
/opt/iptables/lib/ld-linux-armhf.so.3 \
--library-path /opt/iptables/lib \
/opt/iptables/bin/xtables-legacy-multi iptables ...
最终方案
bbappend 完整内容 (sources/meta-custom/recipes-extended/iptables/iptables_1.8.7.bbappend)
FILES_${PN} += "/opt/iptables/*"
PRIVATE_LIBS = "libc.so.6 libdl.so.2 libm.so.6 libpthread.so.0 ld-linux-armhf.so.3"
INSANE_SKIP_${PN} = "dev-so"
do_install_append() {
local BUNDLE="${D}/opt/iptables"
mkdir -p ${BUNDLE}/bin ${BUNDLE}/lib ${BUNDLE}/libexec
cp ${D}${sbindir}/xtables-legacy-multi ${BUNDLE}/bin/
chmod 755 ${BUNDLE}/bin/xtables-legacy-multi
cd ${BUNDLE}/bin
for cmd in iptables iptables-restore iptables-save ip6tables ip6tables-restore ip6tables-save; do
ln -sf xtables-legacy-multi ${cmd}
done
if [ -d "${D}${libdir}/xtables" ]; then
cp -r ${D}${libdir}/xtables ${BUNDLE}/libexec/
fi
local SYSROOT_LIB="${RECIPE_SYSROOT}/lib"
for lib in ld-linux-armhf.so.3 libc.so.6 libdl.so.2 libm.so.6 libpthread.so.0; do
cp -L ${SYSROOT_LIB}/${lib}* ${BUNDLE}/lib/ 2>/dev/null || true
done
cat > ${BUNDLE}/run.sh << 'RUNEOF'
#!/bin/sh
DIR=$(dirname "$0")
export XTABLES_LIBDIR="${DIR}/libexec/xtables"
exec "${DIR}/lib/ld-linux-armhf.so.3" --library-path "${DIR}/lib" \
"${DIR}/bin/xtables-legacy-multi" "$@"
RUNEOF
chmod 755 ${BUNDLE}/run.sh
for cmd in iptables iptables-restore iptables-save ip6tables ip6tables-restore ip6tables-save; do
cat > ${D}${sbindir}/${cmd} << 'WRAPEOF'
#!/bin/sh
DIR=/opt/iptables
export XTABLES_LIBDIR="${DIR}/libexec/xtables"
exec "${DIR}/lib/ld-linux-armhf.so.3" --library-path "${DIR}/lib" \
"${DIR}/bin/xtables-legacy-multi" "${0##*/}" "$@"
WRAPEOF
chmod 755 ${D}${sbindir}/${cmd}
done
}
板子上使用
# 传送
scp -r build/tmp/work/*/iptables/*/image/opt/iptables root@板子IP:/opt/
# 使用(两种方式均可)
iptables -L # 包装脚本(安装了deb包后)
/opt/iptables/run.sh iptables -L # 直接调用
关键教训
| 问题 | 教训 |
|---|---|
| glibc 全静态 + dlopen | 不可行!glibc 静态后无法使用 dlopen(),触发断言崩溃 |
| ld-linux 与 libc 必须配对 | 不能只用 LD_LIBRARY_PATH 覆盖 libc,动态链接器也必须配套 |
Yocto EXTRA_OECONF_append vs += |
+= 在 parsing 时立即求值,_append 在 finalize 阶段执行,顺序不同 |
--disable-static 来源 |
poky.conf → no-static-libs.inc |
| QA 机制 | installed-vs-shipped、dev-so、shlib-provider 三个常见检查 |
libtool -static vs -all-static |
-static 只让 libtool 选 .a 文件,-all-static 才传给 gcc -static |
本文来自博客园,作者:杨旭0324,转载请注明原文链接:https://www.cnblogs.com/allalonewithyou/p/21030635,个人邮箱:yaonie4444@foxmail.com

浙公网安备 33010602011771号