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.confno-static-libs.inc

# no-static-libs.inc
DISABLE_STATIC ?= " --disable-static"
EXTRA_OECONF_append = "${DISABLE_STATIC}"

EXTRA_OECONF_appendEXTRA_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.6ld-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.confno-static-libs.inc
QA 机制 installed-vs-shipped、dev-so、shlib-provider 三个常见检查
libtool -static vs -all-static -static 只让 libtool 选 .a 文件,-all-static 才传给 gcc -static
posted @ 2026-07-02 10:53  杨旭0324  阅读(8)  评论(0)    收藏  举报