求助:如何阻止 rpmbuild 往动态库文件“投毒” (在 meson/ninja 构建时强行将 libgcc_s.so.1 加入动态库)

219 天前
 mylovesaber

症状

我需要单独构建一个 libsystemd.so ,但我发现手动构建和编写 spec 文件后用 rpmbuild 构建出来的 libsystemd.so.0.35.0 不同,rpmbuild 构建出来始终多一个 libgcc_s.so.1 的依赖:

build/libsystemd.so.0.35.0:
        linux-vdso.so.1 (0x0000ffffb5d77000)
        libcap.so.2 => /lib64/libcap.so.2 (0x0000ffffb5ab0000)
        liblzma.so.5 => /lib64/liblzma.so.5 (0x0000ffffb5a50000)
        libzstd.so.1 => /lib64/libzstd.so.1 (0x0000ffffb5980000)
        libc.so.6 => /lib64/libc.so.6 (0x0000ffffb57b0000)
        /lib/ld-linux-aarch64.so.1 (0x0000ffffb5d2a000)

# 以上是手动构建效果,没有 libgcc_s.so.1
# 以下是 rpmbuild 构建效果,强行塞了个 libgcc_s.so.1 进去

../BUILD/systemd-252/build/libsystemd.so.0.35.0:
        linux-vdso.so.1 (0x0000ffff92396000)
        libcap.so.2 => /lib64/libcap.so.2 (0x0000ffff921f0000)
        liblzma.so.5 => /lib64/liblzma.so.5 (0x0000ffff92190000)
        libzstd.so.1 => /lib64/libzstd.so.1 (0x0000ffff920c0000)
        libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x0000ffff92060000)
        libc.so.6 => /lib64/libc.so.6 (0x0000ffff91e90000)
        /lib/ld-linux-aarch64.so.1 (0x0000ffff92349000)

这对于想构建一个只依赖于 glibc 和其他文件体积不大的必备依赖的 libsystemd.so.0.35.0 来说是不可接受的。

既然手动构建没有添加 libgcc_s.so.1 ,那么问题应该出在 rpmbuild 上,他会用某种方式向 meson 或 ninja 构建时传递了什么参数或选项导致(应该不是往构建规则文件中写入内容),我上网查找了下,可能导致 libgcc 被添加的是一些 gcc 构建选项比如:

-Wl,--no-whole-archive
-Wl,--no-as-needed
--with-pie=no
...

但网上查了下,如果不往 meson 有关构建文件添加这类信息的话,不会出现构建出来多一个依赖的情况,我对这东西不太了解,网上查了半天测试半天没什么头绪,有没有熟悉这方面的大佬解惑?提前感谢!


手动复现

我试了下还挺好复现问题的(后文涉及的很多段命令挂好代理后可以全选直接复制运行):

systemctl stop dnf-makecache.timer
systemctl disable dnf-makecache.timer
sed -e 's|^metalink=|#metalink=|g' \
    -e 's|^#baseurl=http://download.example/pub/fedora/linux|baseurl=https://mirrors.ustc.edu.cn/fedora|g' \
    -i.bak \
    /etc/yum.repos.d/fedora.repo \
    /etc/yum.repos.d/fedora-modular.repo \
    /etc/yum.repos.d/fedora-updates.repo \
    /etc/yum.repos.d/fedora-updates-modular.repo
dnf makecache

dnf update
dnf install wget nano tree -y
dnf groupinstall "Development Tools" -y
dnf install texinfo gcc-c++ perl -y
[ -f /usr/bin/yacc ] && mv /usr/bin/yacc /usr/bin/yacc.bak
ln -s /usr/bin/bison /usr/bin/yacc
dnf install meson gperf python3.12 python3-pip libcap-devel libmount-devel cmake -y
dnf install fedora-packager rpmdevtools -y
cd /home/build
rpmdev-setuptree
rpmdev-newspec -o ~/rpmbuild/SPECS/test.spec
cat > ~/rpmbuild/SPECS/test.spec << EOF
Name:              aaa
Version:           1.0.0
Release:           1%{?dist}
Summary:           aaa
License:           GPLv3

%global s0_dir systemd-252
Source0:           %{s0_dir}.tar.gz
#Source1:         systemd-252-security_fix-1.patch

%description
%prep
rm -rf %{_builddir}/%{s0_dir}
%setup -q -T -D -n %{s0_dir} -b 0

%build
cd %{_builddir}/%{s0_dir}
#patch -Np1 -i %{S:1}
meson build
soName=\$(find build -maxdepth 1 -type d -name libsystemd.so* | sed  's#build/##; s#.p\$##')
ninja -C build \${soName}
# 这里加上退出是到构建操作结束直接停止,减少后续不必要的构建 rpm 软件包时的大量内容输出
exit 123

%install
%pre
%post
%preun
%postun
%files
%changelog
%autochangelog
EOF
wget -P ~ https://github.com/systemd/systemd/archive/v252/systemd-252.tar.gz
cp -rp ~/systemd-252.tar.gz ~/rpmbuild/SOURCES

cd ~
rm -rf ~/systemd-252
tar xfv ~/systemd-252.tar.gz
cd ~/systemd-252
meson build
soName=$(find build -maxdepth 1 -type d -name libsystemd.so* | sed  's#build/##; s#.p$##')
ninja -C build ${soName}

# 以上手动构建
# 以下自动构建

rpmbuild -D '_topdir /home/build/rpmbuild'  -bb ~/rpmbuild/SPECS/test.spec
ldd ~/systemd-252/build/libsystemd.so*

# 以上手动构建检查
# 以下自动构建检查

ldd ~/rpmbuild/BUILD/systemd-252/build/libsystemd.so*

运行输出信息

输出信息多还没高亮,而且超两万字限制了发不出来,建议本地复现检查

1098 次点击
所在节点    Linux
10 条回复
codehz
219 天前
libgcc_s 是 gcc 自己生成的依赖,没有的话,大概率只是静态链接上了,比如-static-libgcc 一类的选项,或者手动指定 spec --with-specs=%{!shared-libgcc:-static-libgcc}来避免链接到动态版本的
mylovesaber
219 天前
@codehz 你意思是手动构建的时候,meson 和 ninja 构建是直接连到了系统中的 libgcc.a ,而 rpm 构建的时候链接到了 libgcc_s.so

我能看到和 libgcc_s 有关的库:
find /usr/lib -name libgcc*结果有:
/usr/lib/gcc/aarch64-redhat-linux/13/libgcc.a

find /usr/lib64 -name libgcc*结果有:
/usr/lib64/libgcc_s-13-20230728.so.1
/usr/lib64/libgcc_s.so.1
/usr/lib64/libgccpp.so.1
/usr/lib64/libgccpp.so.1.5.0

ll /usr/lib64/libgcc_s.so.1 结果有:
lrwxrwxrwx. 1 root root 25 Jul 28 08:00 /usr/lib64/libgcc_s.so.1 -> libgcc_s-13-20230728.so.1

现在的问题是,systemd 是 meson 构建,我不知道如何传递 gcc 的选项参数给它。

rpmbuild 启动后会指定这些环境变量,这里面有没有可能导致走的链接 libgcc_s.so ?我知道 libgcc 有关的库是构建 gcc 源码的时候生成的库(之前测试过),但
+ CFLAGS='-O2 -flto=auto -ffat-lto-objects -fexceptions -g -grecord-gcc-switches -pipe -Wall -Werror=format-security -Wp,-U_FORTIFY_SOURCE,-D_FORTIFY_SOURCE=3 -Wp,-D_GLIBCXX_ASSERTIONS -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -fstack-protector-strong -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -mbranch-protection=standard -fasynchronous-unwind-tables -fstack-clash-protection -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer '
+ export CFLAGS
+ CXXFLAGS='-O2 -flto=auto -ffat-lto-objects -fexceptions -g -grecord-gcc-switches -pipe -Wall -Werror=format-security -Wp,-U_FORTIFY_SOURCE,-D_FORTIFY_SOURCE=3 -Wp,-D_GLIBCXX_ASSERTIONS -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -fstack-protector-strong -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -mbranch-protection=standard -fasynchronous-unwind-tables -fstack-clash-protection -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer '
+ export CXXFLAGS
+ FFLAGS='-O2 -flto=auto -ffat-lto-objects -fexceptions -g -grecord-gcc-switches -pipe -Wall -Werror=format-security -Wp,-U_FORTIFY_SOURCE,-D_FORTIFY_SOURCE=3 -Wp,-D_GLIBCXX_ASSERTIONS -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -fstack-protector-strong -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -mbranch-protection=standard -fasynchronous-unwind-tables -fstack-clash-protection -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -I/usr/lib64/gfortran/modules '
+ export FFLAGS
+ FCFLAGS='-O2 -flto=auto -ffat-lto-objects -fexceptions -g -grecord-gcc-switches -pipe -Wall -Werror=format-security -Wp,-U_FORTIFY_SOURCE,-D_FORTIFY_SOURCE=3 -Wp,-D_GLIBCXX_ASSERTIONS -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -fstack-protector-strong -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -mbranch-protection=standard -fasynchronous-unwind-tables -fstack-clash-protection -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -I/usr/lib64/gfortran/modules '
+ export FCFLAGS
+ VALAFLAGS=-g
+ export VALAFLAGS
+ LDFLAGS='-Wl,-z,relro -Wl,--as-needed -Wl,-z,now -specs=/usr/lib/rpm/redhat/redhat-hardened-ld -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -Wl,--build-id=sha1 -specs=/usr/lib/rpm/redhat/redhat-package-notes '
+ export LDFLAGS
+ LT_SYS_LIBRARY_PATH=/usr/lib64:
+ export LT_SYS_LIBRARY_PATH
+ CC=gcc
+ export CC
+ CXX=g++
+ export CXX
mylovesaber
219 天前
@codehz 但 LDFLAGS 应该是链接库用的,这里面选项我查了下好像都不是和使用 libgcc 有什么关联的样子?我是否理解有什么错吗?
codehz
219 天前
libgcc 是自动加上的,用到了相关语言特性(甚至不是函数)就会需要链接,问题只在于链接静态的还是动态的差异
不过我怀疑,-specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 这个里面,可能指定了强制用动态的
wk333
219 天前
@codehz 确实有可能,国内 openEuler 环境测试并没有链接 libgcc
```
[root@openEuler ~]# ldd /usr/lib64/libsystemd.so.0
linux-vdso.so.1 (0x0000ffffb371b000)
libcap.so.2 => /usr/lib64/libcap.so.2 (0x0000ffffb35c0000)
libgcrypt.so.20 => /usr/lib64/libgcrypt.so.20 (0x0000ffffb34c0000)
liblzma.so.5 => /usr/lib64/liblzma.so.5 (0x0000ffffb3470000)
liblz4.so.1 => /usr/lib64/liblz4.so.1 (0x0000ffffb3430000)
libc.so.6 => /usr/lib64/libc.so.6 (0x0000ffffb3230000)
/lib/ld-linux-aarch64.so.1 (0x0000ffffb36de000)
libgpg-error.so.0 => /usr/lib64/libgpg-error.so.0 (0x0000ffffb31e0000)
```
mylovesaber
219 天前
@codehz
命令 file /usr/lib/rpm/redhat/redhat-annobin-cc1 结果是:
/usr/lib/rpm/redhat/redhat-annobin-cc1: symbolic link to redhat-annobin-select-annobin-built-plugin

命令 ll /usr/lib/rpm/redhat/redhat-annobin-cc1 结果是:
lrwxrwxrwx. 1 root root 42 Oct 4 23:54 /usr/lib/rpm/redhat/redhat-annobin-cc1 -> redhat-annobin-select-annobin-built-plugin

命令 file /usr/lib/rpm/redhat/redhat-annobin-select-annobin-built-plugin 结果是:
/usr/lib/rpm/redhat/redhat-annobin-select-annobin-built-plugin: ASCII text

命令 cat /usr/lib/rpm/redhat/redhat-annobin-select-annobin-built-plugin 结果是:
*cc1_options:
+ %{!-fno-use-annobin:%{!iplugindir*:%:find-plugindir()} -fplugin=annobin}

关于这个文件我找到了这个软件包的构建 git:
https://src.fedoraproject.org/rpms/redhat-rpm-config/tree/rawhide

这里面有和上面 cat 出来的信息完全相同的内容,请过目

ldd 查看的依赖都应该是运行依赖,直接手动执行构建的话,我查看了 lfs 手册,systemd 的运行依赖不可能包含 gcc 的动态库,也就是一楼手动构建完成后 ldd 看到链接的动态库对应是 glibc/libcap/zstd/xz ,而不应该对 gcc 有运行依赖。

我尝试将 spec 文件中的解压缩还有 meson 和 ninja 构建命令都拿到了一个子脚本中,通过在 spec 文件中执行 shell 脚本来构建,结果出来的 libsystemd 动态库依旧有 libgcc_s 的运行依赖


libgcc_s 库绝对路径中的路径/lib64 其实是/usr/lib64 的软链接,我怀疑 rpmbuild 在运行时临时产生了一些环境变量导致,我通过往子脚本中添加 set 和 env 命令抓出来了变量,然后和手动执行命令重定向的变量进行 diff ,发现有很多不同的地方,搜索关键字 lib64 还是能出来不少,明天再一个个对比下看看。。。好诡异。。。
mylovesaber
219 天前
@wk333 感谢回复,我更新了我的查找结果,请看 6 楼
wk333
218 天前
确实很诡异,查找存在 /usr/lib64/libsystemd.so.0 的 rpm 包,也只有 redhat 系存在 gcc 依赖
http://rpmfind.net/linux/rpm2html/search.php?query=%2Fusr%2Flib64%2Flibsystemd.so.0&submit=Search+...&system=&arch=
mylovesaber
218 天前
我找到的避开的方式,正如我预期的一样,如果我取消变量:
unset \
FFLAGS \
FCLAGS \
CFLAGS \
CXXFLAGS \
LDFLAGS
那么构建完成的 libsystemd.so 就一定不带 libgcc_s 的依赖,但减小其中任意一个或多个变量,结果没法稳定复现,总在三种可能之间乱跳:
- 编译不过
- 不带 libgcc_s 依赖
- 带 libgcc_s 依赖

反而是另外我看着最像问题来源的几个变量居然不处理也不会出问题:
LT_SYS_LIBRARY_PATH \
PKG_CONFIG_PATH \
RPM_LD_FLAGS \
RPM_OPT_FLAGS
@wk333 @codehz 虽然问题可以解决,但我好奇为啥会出现这问题
codehz
218 天前
我猜还是因为启用了 hardened 的配置文件,导致了用上了一些先前没用上的语言特性,然后这些特性需要 gcc 的运行时库

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/980632

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX