V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Distributions
Ubuntu
Fedora
CentOS
中文资源站
网易开源镜像站
mylovesaber
V2EX  ›  Linux

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

  •  
  •   mylovesaber · 2023-10-10 14:52:50 +08:00 · 1305 次点击
    这是一个创建于 411 天前的主题,其中的信息可能已经有所发展或是发生改变。

    症状

    我需要单独构建一个 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 有关构建文件添加这类信息的话,不会出现构建出来多一个依赖的情况,我对这东西不太了解,网上查了半天测试半天没什么头绪,有没有熟悉这方面的大佬解惑?提前感谢!


    手动复现

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

    • vmware 装的系统:fedora server 38 就是最新版,安装系统时指定最小化安装,安装时创建一个能 sudo 提权的 build 用户
    • 进系统后配置源和安装必要软件包( python 后面那版本号根据系统实际情况填写,我这是存在 3.12 版本的):
    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
    
    • 之后所有操作切到普通用户下: su - build
    • 创建 rpmbuild 打包环境:
    cd /home/build
    rpmdev-setuptree
    rpmdev-newspec -o ~/rpmbuild/SPECS/test.spec
    
    • 编写 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
    
    • 下载源码包和补丁并分别为手动和 rpmbuild 构建各准备一份(多行命令,可全选一次性粘贴进命令行执行,最好挂个代理,下载慢)
    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*
    

    运行输出信息

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

    10 条回复    2023-10-11 11:56:19 +08:00
    codehz
        1
    codehz  
       2023-10-10 15:09:40 +08:00
    libgcc_s 是 gcc 自己生成的依赖,没有的话,大概率只是静态链接上了,比如-static-libgcc 一类的选项,或者手动指定 spec --with-specs=%{!shared-libgcc:-static-libgcc}来避免链接到动态版本的
    mylovesaber
        2
    mylovesaber  
    OP
       2023-10-10 17:53:33 +08:00
    @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
        3
    mylovesaber  
    OP
       2023-10-10 17:55:19 +08:00
    @codehz 但 LDFLAGS 应该是链接库用的,这里面选项我查了下好像都不是和使用 libgcc 有什么关联的样子?我是否理解有什么错吗?
    codehz
        4
    codehz  
       2023-10-10 18:06:02 +08:00
    libgcc 是自动加上的,用到了相关语言特性(甚至不是函数)就会需要链接,问题只在于链接静态的还是动态的差异
    不过我怀疑,-specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 这个里面,可能指定了强制用动态的
    wk333
        5
    wk333  
       2023-10-10 19:58:18 +08:00
    @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
        6
    mylovesaber  
    OP
       2023-10-10 22:38:22 +08:00
    @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
        7
    mylovesaber  
    OP
       2023-10-10 22:39:45 +08:00
    @wk333 感谢回复,我更新了我的查找结果,请看 6 楼
    wk333
        8
    wk333  
       2023-10-11 09:44:12 +08:00
    确实很诡异,查找存在 /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
        9
    mylovesaber  
    OP
       2023-10-11 10:07:56 +08:00
    我找到的避开的方式,正如我预期的一样,如果我取消变量:
    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
        10
    codehz  
       2023-10-11 11:56:19 +08:00
    我猜还是因为启用了 hardened 的配置文件,导致了用上了一些先前没用上的语言特性,然后这些特性需要 gcc 的运行时库
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2814 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 07:57 · PVG 15:57 · LAX 23:57 · JFK 02:57
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.