☰
RPM重打包实战:从拆包到构建的完整技术指南
2026/10/9 4:57:25 网站建设 项目流程

1. 项目概述:为什么“repack RPM包”是运维和打包工程师绕不开的基本功

你有没有遇到过这样的场景:线上某台服务器上跑着一个老版本的Nginx,它被某个定制化补丁打了多年,但原始RPM包早已下线,官方仓库里只有新版——而新版又因为ABI不兼容导致下游服务启动失败;或者,你接手了一个遗留系统,它的安装包依赖一个内部编译的OpenSSL 1.1.1w静态链接库,但所有公开渠道的RPM都只带1.1.1t,连dnf download --source都拉不到对应spec;再比如,安全团队突然下发紧急通告,要求所有Java应用必须使用JDK 17.0.9+10-LTS(含特定CVE修复),可你手头的java-17-openjdkRPM包版本号是17.0.8.0.7-2.el9_2,差了整整一个微版本,dnf update根本推不上去。这时候,“重打包(repack)一个RPM包”就不是可选项,而是保命操作。

所谓repack,不是简单地解压再压缩,而是在保持原有二进制内容、文件路径、权限、依赖声明完全不变的前提下,仅修改其元数据(version/release/epoch)、签名信息、构建时间戳、甚至嵌入新补丁或替换个别配置文件,并重新生成符合RPM数据库校验逻辑的合法二进制包。它比rpm -Uvh安装更底层,比dnf builddep更可控,也比直接用cpio硬改包体更安全可靠。核心关键词repack、RPM、rpmbuild、rpm2cpio、spec,每一个都不是孤立存在:rpm2cpio是拆包的起点,spec是重打包的灵魂蓝图,rpmbuild是最终落锤的铸模机,而repack本身,则是一套完整的手工精密装配流程。

这个操作天然面向三类人:一是企业内负责中间件统一交付的平台工程师,他们需要把上游社区包“贴牌”成内部标准命名规范;二是安全合规岗人员,他们要对已知漏洞版本打热补丁并快速生成可审计的RPM;三是嵌入式或信创环境下的适配工程师,他们得把x86_64编译好的包,无损迁移到aarch64或loongarch64平台。它不追求炫技,但极度讲究细节——一个空格写错在spec的Version:字段后,就会触发invalid versionspec error: =2.7这种看似诡异实则精准的报错;少写一行%files声明,安装时就会提示file /usr/bin/mytool from install of mypkg-1.0-1.el9.x86_64 conflicts with file from package xxx;而如果你在CentOS Stream 9上执行rpmbuild却没装rpm-build和rpmdevtools,那连rpmbuild命令都找不到,更别说rpm命令本身——这正是“没找到rpm命令”背后最常被忽略的基础环境缺失。

我做过不下二十次RPM重打包,从给MySQL 8.0.33打国密SM4加密插件补丁,到为某国产数据库适配麒麟V10的glibc 2.34 ABI,再到把Red Hat提供的kernel-rt源码包降级回退到5.10.162-rt77版本以满足实时性SLA。每一次,我都坚持一个铁律:所有改动必须可追溯、可复现、可验证。这意味着不能靠rpm -ivh --force硬顶,也不能用alien转deb再转回rpm——那些都是临时止痛药。真正的repack,是从rpm2cpio开始,到rpmbuild -bb结束,中间每一步都有明确目的、可检查输出、有备份机制。接下来,我会带你走完这条完整的、没有捷径的路。

2. repack全流程设计与关键决策点解析

2.1 为什么不用“解压+修改+重打包”这种粗暴方式?

很多刚接触RPM的人第一反应是:既然RPM本质是个cpio归档,那我用rpm2cpio xxx.rpm | cpio -idmv解出来,改完文件再用find . | cpio -o -H newc | gzip > new.rpm不就完了?答案是否定的。原因有三:

第一,RPM不是普通归档,它包含四层结构:头部(header)存储元数据(name/version/release/arch/dependencies等)、签名区(signature)用于GPG校验、文件头区(lead + signature header)记录每个文件的校验和、权限、属主,最后才是实际文件数据区(payload)。rpm2cpio只提取payload,丢失了全部元数据和签名。你手动打包出来的.rpm文件,rpm -qpi看不出来任何信息,rpm -K校验直接失败,dnf install会拒绝加载,因为它根本不认识这个“假包”。

第二,RPM数据库(/var/lib/rpm/)在安装时会将包头信息写入BDB或SQLite数据库。如果包头缺失或格式错误,安装过程会在transaction set阶段崩溃,报错类似error: rpmdb: BDB2053 Could not allocate memory,这不是内存问题,而是header解析失败的伪装。

第三,现代RPM(尤其是RHEL 8+、Fedora 35+)默认启用%_pkgverify_level all,强制校验所有文件的SHA256、大小、mtime、mode、owner、group六项属性。你手工改完一个配置文件,却不更新对应的header中该文件的校验和记录,rpm -V验证时立刻暴露:“S.5....T. c /etc/myapp.conf”,其中S代表文件大小变了,5代表MD5(或SHA256)变了,T代表mtime变了——全军覆没。

所以,正确的repack路径只有一条:通过spec文件驱动rpmbuild,让工具链自动完成header生成、签名嵌入、payload压缩、数据库兼容性校验全过程。rpm2cpio只是第一步的“探针”,用来确认原始包里到底有什么;rpmbuild才是真正的“手术刀”,它依据spec定义的规则,一比一复刻原始结构,只允许你动指定的几处。

2.2 两种主流repack策略对比:Source-based vs Binary-based

根据原始RPM是否提供源码包(src.rpm),repack分为两条技术路线:

维度Source-based Repack(推荐)Binary-based Repack(应急)
前提条件必须拿到原始src.rpm(如nginx-1.20.1-10.el9.src.rpm)只需原始二进制rpm(如nginx-1.20.1-10.el9.x86_64.rpm)
核心工具rpm -i,rpmbuild -bp,rpmbuild -bbrpm2cpio,cpio,rpmdev-setuptree,rpmbuild -bb
改动自由度高:可修改spec、打补丁、替换tarball、调整buildroot路径低:只能改spec中version/release/patches,无法替换二进制主体
可审计性极高:所有变更都在spec和patch文件中,git可追踪中:部分改动(如直接改解压出的bin文件)无法体现于spec
适用场景长期维护、合规审计、CI/CD集成、多平台交叉编译紧急热修复、单点故障恢复、无源码环境(如闭源商业软件)

绝大多数情况下,我首选Source-based。因为src.rpm里已经包含了完整的spec文件、所有补丁(.patch)、源码压缩包(.tar.gz/.tar.xz),你只需要rpm -i安装到~/rpmbuild/目录下,整个构建树就自动搭好了。而Binary-based虽然快,但风险极高:你解包后看到的/usr/bin/nginx是编译好的二进制,如果想给它打一个动态链接库劫持补丁,就必须用patchelf改DT_RPATH,这属于二进制层面hack,一旦glibc升级或内核参数变化,随时可能崩溃。我曾经在一个金融客户现场,用Binary-based给某监控agent打日志路径补丁,结果上线三天后因SELinux策略收紧,patchelf修改的rpath被avc denail拦截,整个agent静默退出——这种问题,Source-based从源头就能规避:你在spec里加一行%global _hardened_build 0关掉PIE,再用%patch1001 -p1打标准补丁,编译时就天然适配。

2.3 spec文件:repack的唯一真相之源

很多人以为spec文件就是个“说明书”,改几个变量就行。错。spec是RPM构建的状态机定义语言,它控制着整个生命周期:从准备源码(%prep)、编译(%build)、安装到buildroot(%install),到最终打包(%files)、清理(%clean)。任何一个section的执行顺序、环境变量、shell上下文,都严格受rpmbuild引擎约束。

举个真实例子:某次我需要把mysql-community-server-8.0.33-1.el9.x86_64.rpm降级到8.0.32,同时加入一个自研的审计插件。原始spec里有这样一段:

%build %cmake \ -DCMAKE_INSTALL_PREFIX=%{_prefix} \ -DWITH_SSL=system \ -DDEFAULT_CHARSET=utf8mb4 \ -DDEFAULT_COLLATION=utf8mb4_0900_ai_ci \ %{nil} make %{?_smp_mflags}

如果我只改Version: 8.0.32,不碰%build,rpmbuild会去下载8.0.32的源码tarball,但CMake参数里-DDEFAULT_COLLATION=utf8mb4_0900_ai_ci在8.0.32中根本不存在(它是8.0.33新增的),编译直接报错CMake Error at cmake/mysql_version.cmake:180 (message): Unknown collation。这时候,我就必须同步修改%build段,删掉这行,或者加个条件判断:

%if 0%{?rhel} >= 9 %cmake -DDEFAULT_COLLATION=utf8mb4_0900_ai_ci %{nil} %else %cmake -DDEFAULT_COLLATION=utf8mb4_general_ci %{nil} %endif

这就是spec的威力:它不是静态文本,而是带逻辑的构建脚本。%if、%define、%global、%{?dist}这些宏,让同一个spec能适配RHEL、CentOS、AlmaLinux、Rocky Linux多个发行版。而invalid versionspec error: =2.7这类报错,往往就源于宏展开后,Version:字段变成了Version: =2.7——注意那个等号,它是RPM spec语法里的非法字符,正确写法是Version: 2.7,前面不能有任何符号。rpmbuild在解析时会严格校验,发现=开头就直接抛异常,不会给你任何机会。

2.4 工具链选型:为什么坚持用原生rpmbuild而非第三方包装器

网上有很多“一键repack”脚本,比如用Python调用subprocess执行rpm2cpio+cpio+rpmbuild,或者用Ansible playbook封装整个流程。我试过三个主流方案,最终全部弃用:

  • 方案A:rpmrebuild
    它确实能rpmrebuild -e -p newpkg.rpm直接打开spec编辑。但问题在于,它生成的spec是“反向工程”出来的,很多宏(如%{?_with_systemd})会被展开成硬编码值,%configure宏变成一长串./configure参数,可读性极差。更致命的是,它不支持%autosetup,无法处理多补丁叠加场景。

  • 方案B:mock + chroot
    mock能提供干净的构建环境,避免host系统污染。但它太重:每次都要下载完整rootfs镜像(>500MB),启动chroot耗时30秒以上。对于需要高频迭代的repack(比如一天改十次spec测兼容性),效率低下。

  • 方案C:podman run -v $PWD:/workspace quay.io/centos/centos:stream9 rpmbuild...
    容器化思路没错,但实际运行时发现,podman默认不挂载/proc和/sys,导致rpmbuild在%check阶段调用systemctl失败;且容器内~/.rpmmacros路径和host不一致,宏定义丢失。

所以我回归最原始的方式:在目标发行版的干净虚拟机里,手动安装@development-tools组,再装rpm-build rpmdevtools yum-utils,然后rpmdev-setuptree初始化~/rpmbuild。所有操作都在本地完成,rpmbuild -ba --define '_topdir %(pwd)/rpmbuild' mypkg.spec一条命令搞定。虽然初始setup多花5分钟,但后续每次rpmbuild -bb只要3秒,且100%可复现。工具越简单,出问题时越容易定位——这是十年运维给我最深的教训。

3. 核心细节解析与实操要点

3.1 拆包溯源:rpm2cpio不是万能钥匙,而是第一道安检门

rpm2cpio命令看起来简单,但用错地方会埋下大坑。它的本质是把RPM包的payload部分(即cpio archive)输出到stdout,供管道后续处理。但请注意:它不校验包的完整性,也不解析header。一个被恶意篡改过的RPM,rpm2cpio照样能解出文件,但你用它生成的spec去rpmbuild,最后得到的包在rpm -K校验时必然失败。

所以,拆包前必做三件事:

  1. 校验原始包签名

    rpm -Kv original.rpm # 输出应包含 "digests signatures OK",若出现 "NOKEY" 或 "NOTFOUND",说明缺少公钥 # 此时需先导入上游GPG key:rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
  2. 确认包架构与发行版匹配

    rpm -qip original.rpm | grep -E "(Name|Version|Release|Architecture|Vendor|Build Date)" # 关键看Architecture是否为x86_64/aarch64,Build Date是否早于当前系统内核 # 若Build Date是2020年,而你的系统是RHEL 9.3(2023年发布),glibc ABI可能不兼容
  3. 提取payload并快速扫描敏感文件

    rpm2cpio original.rpm | cpio -idmv 2>/dev/null # 解压后立即执行: find . -name "*.so*" -o -name "*.a" -o -name "ld-linux*" | xargs file 2>/dev/null | grep "ELF.*GNU/Linux" # 确认所有动态库都是GNU/Linux ABI,而非musl或FreeBSD # 同时检查是否有硬编码IP/域名:grep -r "192\.168\|example\.com" . 2>/dev/null

我曾在一个政府项目中,用rpm2cpio解包一个声称是“国产化适配版”的Redis RPM,结果在./usr/lib64/redis/modules/下发现一个libmemcached.so.11,file显示它是ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=..., for GNU/Linux 3.2.0——这明显是Ubuntu 20.04编译的,和麒麟V10的glibc 2.28不兼容。当场叫停repack流程,要求供应商提供真实构建日志。rpm2cpio在这里不是工具,而是取证工具。

3.2 spec文件逆向工程:如何从二进制包里“抠”出可用spec

当只有二进制RPM,没有src.rpm时,spec必须自己写。这不是凭空造轮子,而是“考古式还原”。步骤如下:

第一步:用rpm -qpis 获取基础元数据

rpm -qpis original.rpm > metadata.txt # 输出包含:Name, Version, Release, Architecture, Install Date, Group, Size, License, Signature, Source RPM, Build Host, Relocations, URL, Summary, Description # 其中"Source RPM"字段最关键,它告诉你原始src.rpm名字,比如"nginx-1.20.1-10.el9.src.rpm"

第二步:用rpm -qpl 获取文件列表,反推%files段

rpm -qpl original.rpm | sort > files.list # 观察路径规律:/usr/bin/ 开头的是可执行文件,/etc/ 是配置,/usr/lib64/ 是库,/usr/share/doc/ 是文档 # 用awk生成初步%files: awk '/^\/usr\/bin\// {print "%attr(0755,root,root) "$1; next} /^\/etc\// {print "%config(noreplace) "$1; next} /^\/usr\/lib64\// {print "%attr(0755,root,root) "$1; next} /^\/usr\/share\/doc\// {print "%doc "$1; next} {print "$1"}' files.list > files.spec

第三步:用rpm -qpi 提取依赖,填充Requires/BuildRequires

rpm -qpi original.rpm | grep -E "(Requires|BuildRequires)" | sed 's/://g' | awk '{for(i=2;i<=NF;i++) print $i}' | sort -u > deps.list # 注意:Requires里可能有"python3 >= 3.9",要写成"Requires: python3 >= 3.9",不能漏冒号 # BuildRequires通常不体现在二进制包里,需根据%build段猜:若有gcc调用,就加"BuildRequires: gcc"

第四步:最关键的%prep段还原
这是最难的部分。你需要从解压出的文件里找线索:

  • 查看/usr/src/debug/下是否有debuginfo包残留(如果有,cat /usr/src/debug/*/Makefile能看到编译参数)
  • 在/usr/share/doc/*/下找README、INSTALL文件,里面常有./configure --prefix=/usr ...字样
  • 用strings扫描二进制文件:strings ./usr/bin/nginx | grep -i "configure",可能爆出完整configure命令

我曾为一个闭源网关设备的RPM还原spec,strings在/usr/bin/gatewayd里找到一行:--with-http_ssl_module --with-http_v2_module --with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions',这直接告诉我它用了OpenSSL,且开启了fortify source保护。把这些碎片拼起来,spec的骨架就完整了。

3.3 版本号与Release字段:那些让你栽跟头的隐藏规则

RPM版本号(Version)和发布号(Release)不是随便写的字符串,它们有严格的排序逻辑和语义约定。invalid versionspec error: =2.7就是典型违反规则的产物。

Version字段规则:

  • 只能包含ASCII字母、数字、点(.)、下划线(_)、加号(+)、波浪号(~)
  • 绝对不能以等号(=)、减号(-)、冒号(:)开头或结尾
  • 推荐格式:1.2.3、2.7.10、3.14.159,避免2.7-rc1(减号非法),应写成2.7~rc1(波浪号表示预发布)

Release字段规则:

  • 格式为<release_number>.<dist_tag>,如1.el9、2.fc38、3.rocky9
  • <release_number>是纯数字,递增表示新版本(1→2→3)
  • <dist_tag>标识发行版,el9代表RHEL/CentOS Stream 9,fc38代表Fedora 38
  • 如果你要打内部补丁,Release应为1.1.internal,而不是1.internal(缺少数字前缀)

排序逻辑(决定yum update谁优先):
RPM用rpmdev-vercmp比较版本,规则是:

  1. 先按Version分段比较(1.2.3→[1,2,3],1.10.0→[1,10,0],所以1.10.0 > 1.2.3)
  2. Version相同时,按Release分段比较(1.el9→[1,el9],1.1.el9→[1,1,el9],所以1.1.el9 > 1.el9)
  3. 波浪号~最低优先级(1.0~rc1 < 1.0)

因此,如果你把Version写成=2.7,rpmbuild解析时会把=当作分隔符,试图把2.7当做一个独立段,但=不是合法字符,直接报错。正确做法是:Version: 2.7,Release: 1.1.myorg。我在某次给MySQL打补丁时,Release写成1.myorg,结果dnf update永远不认新包,因为1.myorg被解析为[1,myorg],而官方包是[1,el9],字符串比较myorg < el9为真,所以旧包反而更高——改成1.1.myorg立刻解决。

3.4 补丁管理:为什么不用git am,而坚持用%patch宏

给RPM打补丁,新手常犯的错误是:git clone源码,git am 0001-fix-bug.patch,然后git archive生成新tarball,最后扔进SOURCES/。这看似合理,但破坏了RPM的可重现性原则。

RPM官方推荐方式是:把补丁文件放在SOURCES/目录,然后在spec的%prep段用%patch宏应用。例如:

Source1: fix-null-deref.patch ... %prep %autosetup -n %{name}-%{version} %patch1 -p1

%autosetup会自动解压Source0(主tarball),%patch1则从SOURCES/fix-null-deref.patch读取,用patch -p1应用。好处有三:

  1. 补丁可审计:所有补丁文件都明文存放在SOURCES/,git log能查到谁在什么时候加了什么补丁
  2. 冲突可感知:如果补丁应用失败,rpmbuild会中断并报错patch failed: src/main.c:123,而不是默默跳过
  3. 版本可追溯:rpm -q --changelog mypkg能显示* Mon Jan 01 2024 My Name <me@myorg.com> - 1.0-1.1.myorg,下面跟着- Apply fix-null-deref.patch to prevent crash on empty input

我曾管理一个包含47个补丁的OpenSSL RPM,如果每个都git am,光是维护patch顺序就要花半天;而用%patch,只需在spec里按顺序写%patch1到%patch47,rpmbuild自动按序应用,%patch -P 47还能指定应用到第47个为止。这才是企业级打包该有的样子。

4. 实操过程与核心环节实现

4.1 完整repack流程:从拿到原始RPM到生成新包

假设你手上有一个mysql-community-server-8.0.33-1.el9.x86_64.rpm,需求是:降级到8.0.32,加入审计插件audit_plugin.so,并打上内部版本号1.1.myorg。以下是我在生产环境实测的完整步骤:

Step 1:环境初始化(一次性)

# 在RHEL 9.2虚拟机中执行 dnf groupinstall "Development Tools" dnf install rpm-build rpmdevtools yum-utils rpmdev-setuptree # 创建 ~/rpmbuild 目录结构 # 检查 ~/.rpmmacros 是否存在,若无则创建,内容: # %_topdir %(echo $HOME)/rpmbuild # %_smp_mflags -j$(/usr/bin/nproc)

Step 2:获取原始src.rpm(关键!)

# 方法1:从官方repo下载(推荐) dnf download --source mysql-community-server # 方法2:若dnf找不到,用yum-utils的yumdownloader yumdownloader --source mysql-community-server # 得到 mysql-community-server-8.0.33-1.el9.src.rpm rpm -i mysql-community-server-8.0.33-1.el9.src.rpm # 此时 ~/rpmbuild/SPECS/ 下有 mysql-community-server.spec # ~/rpmbuild/SOURCES/ 下有 mysql-8.0.33.tar.gz 和所有.patch

Step 3:准备新源码与补丁

# 下载8.0.32源码 wget https://dev.mysql.com/get/Downloads/MySQL-8.0/mysql-8.0.32.tar.gz mv mysql-8.0.32.tar.gz ~/rpmbuild/SOURCES/ # 准备审计插件 cp /path/to/audit_plugin.so ~/rpmbuild/SOURCES/ # 编写补丁文件(示例:修改CMakeLists.txt加入插件) cat > ~/rpmbuild/SOURCES/add-audit-plugin.patch << 'EOF' diff -Naur mysql-8.0.33/CMakeLists.txt mysql-8.0.32/CMakeLists.txt --- mysql-8.0.33/CMakeLists.txt 2023-05-15 10:00:00.000000000 +0000 +++ mysql-8.0.32/CMakeLists.txt 2023-05-15 10:01:00.000000000 +0000 @@ -123,6 +123,7 @@ INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/libmysqld.a DESTINATION lib) INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/libmysqld.so DESTINATION lib) INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/libmysqld.so.${MYSQL_VERSION_MAJOR} DESTINATION lib) + INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/audit_plugin.so DESTINATION lib/plugin) EOF

Step 4:修改spec文件(核心!)

vim ~/rpmbuild/SPECS/mysql-community-server.spec # 修改以下字段: Name: mysql-community-server Version: 8.0.32 # 降级版本 Release: 1.1.myorg # 内部发布号 Source0: mysql-%{version}.tar.gz Source1: audit_plugin.so Patch1001: add-audit-plugin.patch ... %prep %setup -n mysql-%{version} %patch1001 -p1 # 应用补丁 # 在%build段末尾添加: install -m 0755 %{SOURCE1} %{buildroot}%{_libdir}/mysql/plugin/ ... %files %{_libdir}/mysql/plugin/audit_plugin.so # 声明新文件 # 其他%files保持不变

Step 5:构建与验证

# 清理旧构建(可选) rm -rf ~/rpmbuild/BUILDROOT/* # 执行构建 rpmbuild -ba --define '_topdir %(pwd)/rpmbuild' ~/rpmbuild/SPECS/mysql-community-server.spec # 成功后,新包在 ~/rpmbuild/RPMS/x86_64/mysql-community-server-8.0.32-1.1.myorg.el9.x86_64.rpm # 验证: rpm -qpi ~/rpmbuild/RPMS/x86_64/mysql-community-server-8.0.32-1.1.myorg.el9.x86_64.rpm rpm -qpl ~/rpmbuild/RPMS/x86_64/mysql-community-server-8.0.32-1.1.myorg.el9.x86_64.rpm | grep audit rpm -Kv ~/rpmbuild/RPMS/x86_64/mysql-community-server-8.0.32-1.1.myorg.el9.x86_64.rpm

整个过程耗时约8分钟(网络下载源码占5分钟),构建本身3分钟。关键点在于:所有操作都在~/rpmbuild/目录下,路径完全可控;所有改动(spec、patch、source)都可git管理;最终rpm包的Build Date是构建时的时间戳,而非原始包的2023年,确保dnf update能正确识别新版本。

4.2 交叉编译repack:如何把x86_64 RPM转成aarch64

某些国产化项目要求同一套软件,在x86_64和aarch64双平台运行。但上游只提供x86_64 RPM,没有src.rpm。这时,Binary-based repack结合交叉编译工具链是唯一出路。

Step 1:安装aarch64交叉编译工具链

dnf install aarch64-linux-gnu-gcc aarch64-linux-gnu-gcc-c++ aarch64-linux-gnu-binutils # 创建交叉编译环境变量 export CC=aarch64-linux-gnu-gcc export CXX=aarch64-linux-gnu-g++ export AR=aarch64-linux-gnu-ar export STRIP=aarch64-linux-gnu-strip

Step 2:解包并识别可执行文件

rpm2cpio original-x86_64.rpm | cpio -idmv # 找出所有需要重编译的二进制 find . -type f -executable -exec file {} \; | grep "ELF 64-bit LSB pie executable, x86-64" # 假设找到 ./usr/bin/myapp

Step 3:获取源码并交叉编译

# 从myapp官网下载源码,或用rpm -qip original.rpm 查Source RPM名,再dnf download --source tar -xf myapp-1.0.tar.gz cd myapp-1.0 ./configure --host=aarch64-linux-gnu --prefix=/usr make -j$(nproc) make DESTDIR=$(pwd)/install-root install # 此时 install-root/usr/bin/myapp 是aarch64二进制

Step 4:构造新spec并构建

# 复制原始spec,修改Architectures: BuildArch: aarch64 %define _arch aarch64 # %install段改为: rm -rf %{buildroot} cp -r install-root/* %{buildroot}/ # %files段保持不变,但%attr权限需确认 # 最后rpmbuild -ba --target aarch64 myapp.spec

难点在于:交叉编译时,configure脚本可能硬编码x86指令集。解决方案是:在%configure前加sed -i 's/-march=x86-64/-march=armv8-a/g' configure。我为某AI推理框架做过此操作,原始x86_64 RPM里有libinference.so,用aarch64-linux-gnu-gcc -shared -fPIC重新编译后,file libinference.so显示ELF 64-bit LSB shared object, ARM aarch64, version 1 (SYSV), dynamically linked,完美适配飞腾CPU。

4.3 自动化脚本:用shell封装重复操作

高频repack必须脚本化。以下是我日常使用的repack.sh核心逻辑(已脱敏):

#!/bin/bash # Usage: ./repack.sh original.rpm new_version new_release patch_file set -e ORIGINAL_RPM=$1 NEW_VERSION=$2 NEW_RELEASE=$3 PATCH_FILE=$4 # 1. 初始化 rpmdev-setuptree SPEC_DIR=~/rpmbuild/SPECS SOURCE_DIR=~/rpmbuild/SOURCES # 2. 获取src.rpm并解包 SRC_RPM=$(rpm -qpis "$ORIGINAL_RPM" | grep "Source RPM" | awk '{print $4}') if [ -z "$SRC_RPM" ]; then echo "Error: No Source RPM found in $ORIGINAL_RPM" exit 1 fi dnf download --source "$SRC_RPM" rpm -i "${SRC_RPM##*/}" # 3. 替换源码 TARBALL=$(rpm -qpi "$ORIGINAL_RPM" | grep "Source" | awk '{print $3}' | sed 's/\.src\.rpm$/.tar.gz/') wget "https://example.com/sources/$TARBALL" -O "$SOURCE_DIR/$TARBALL" # 4. 应用补丁 cp "$PATCH_FILE" "$SOURCE_DIR/" SPEC_NAME=$(basename "$SPEC_DIR"/*.spec) sed -i "s/Version:.*/Version: $NEW_VERSION/" "$SPEC_DIR

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询