news 2026/10/22 2:33:38

OpenSSL 在 UNIX 类平台上的构建与链接指南:编译器链接、RPATH 与非默认安装路径的运行时库搜索

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
OpenSSL 在 UNIX 类平台上的构建与链接指南:编译器链接、RPATH 与非默认安装路径的运行时库搜索
  • 固件
  • 操作系统

【免费下载链接】CloverBootloader

Bootloader for macOS, Windows and Linux in UEFI and in legacy mode

项目地址:https://gitcode.com/gh_mirrors/cl/CloverBootloader
点击查看免费下载

导读

OpenSSL 的 UNIX 构建系统(Configure+ Makefile)有一套独特且容易踩坑的链接机制:链接工作始终经由 C 编译器命令行完成,因此所有链接选项都必须写成编译器能接受的形态;同时,当把 OpenSSL 安装到非系统默认目录时,动态链接的程序会因找不到共享库而无法运行,必须通过 RPATH 等运行时搜索路径机制来解决。本文以 OpenSSL 官方文档 NOTES-UNIX.md 为骨架,结合本仓库(CloverBootloader,一个在 UEFI 与传统模式下引导 macOS、Windows 和 Linux 的开源 Bootloader)中携带的 OpenSSL 3.0.9 源码树及其配置脚本,系统讲解编译器链接、各平台 RPATH 选项、ELF 的DT_RPATH/DT_RUNPATH查找顺序,以及第三方应用链接与静态库混用等实战问题,读完即可独立完成 OpenSSL 的 UNIX 定制构建与排错。


一、仓库中的 OpenSSL:UEFI 静态构建 vs UNIX 动态构建

本仓库将 OpenSSL 作为密码学实现引入,用于 UEFI 固件环境。与本文档讨论的 UNIX 动态链接场景不同,UEFI 没有共享库的概念,因此 Library/OpensslLib/configure.py 在调用 OpenSSL 的Configure时显式传入了大量裁剪参数,其中最关键的是:

perl Configure \ --config=../UefiAsm.conf \ --api=1.1.1 \ --with-rand-seed=none \ ... no-shared \ # 强制静态构建,回避动态链接问题 no-dso \ no-module \ ...

而 Library/OpensslLib/openssl/VERSION.dat 表明本仓库携带的是 OpenSSL3.0.9(2023-05-30 发布,SHLIB_VERSION=3)。生成的头文件与汇编文件被放置到 Library/OpensslLib/OpensslGen 下,并通过 Library/OpensslLib/OpensslLib.inf 以 EDK2 库的形式参与固件构建。

理解这一点很重要:NOTES-UNIX.md 面向的是在 Linux、BSD、Solaris、AIX、HP-UX 等传统 UNIX 类系统上构建与部署 OpenSSL 的场景,此时动态共享库才是默认形态,运行时搜索路径(RPATH)问题也随之而来。本文档即专门解答这类问题。


二、OpenSSL 使用编译器来链接:所有链接选项必须经编译器转交

OpenSSL 生成的 Makefile不是用ld直接链接,而是用 C 编译器命令行来完成程序、共享库以及可动态加载共享对象的链接。这一点在 NOTES-UNIX.md 中开宗明义。

其直接后果是:任何传给配置脚本的链接选项,都必须写成编译器能够接受的形态。各系统的差异在于:

  • 有些系统的编译器直接接受链接器标志(如-rpath);
  • 更多编译器要求用-Wl,前缀把选项转交给背后的链接器。

你需要阅读自己编译器的文档了解可接受的写法,并查阅ld(1)手册了解可用的链接器选项。

⚠️ 注意:-Wl中的l是小写字母 L(不是数字 1)。形如-Wl,xxx的选项会被 OpenSSL 配置脚本统一识别为链接器选项并透传给生成的 Makefile。

这一行为在源码中有直接证据。查看 Library/OpensslLib/openssl/Configure 的参数解析逻辑:

elsif (/^-L(.*)$/ or /^-Wl,/) { push @{$useradd{LDFLAGS}}, $_; } elsif (/^-rpath$/ or /^-R$/) # -rpath is the OSF1 rpath flag # -R is the old Solaris rpath flag { my $rpath = shift(@argvcopy) || ""; $rpath .= " " if $rpath ne ""; push @{$useradd{LDFLAGS}}, $_, $rpath; }

可见Configure会:把所有以-Wl,开头的参数与-L参数收入LDFLAGS;并把-rpath(OSF1/Tru64 风格)和-R(旧 Solaris 风格)连同其取值一并加入LDFLAGS。这也印证了文档所述“配置脚本能识别所有这些选项”。


三、非默认安装位置:共享库找不到,程序就无法运行

每个 UNIX 系统都有自己默认的共享库目录集合,例如/lib、/usr/lib,或可能的/usr/local/lib。如果把共享库装到非默认位置,动态链接的二进制文件在运行时将找不到它们而直接启动失败,除非通过运行时共享库搜索路径(runtime shared library search path)给予帮助。

3.1 OpenSSL 应用默认不会替你设置运行路径

对于openssl命令行程序,OpenSSL 的配置脚本通常不会自动设置运行时共享库搜索路径。因此,除非你确认库会安装到默认搜索目录列表之内,否则强烈建议在配置阶段显式指定运行路径。

3.2 各平台设置运行时搜索路径的选项对照

文档给出的各平台可用选项如下(不同系统、不同版本写法各异,RPATH 是最常见的机制;注意 ELF 系统其实有两个运行时搜索路径标签,下文第四节详述):

平台/系统运行时搜索路径选项
Linux、*BSD 等-Wl,-rpath,/whatever/path
Solaris-R /whatever/path
AIX-Wl,-R,/whatever/path(内部会额外传入-bsvr4)
HP-UX-Wl,+b,/whatever/path
Tru64、IRIX-rpath /whatever/path

OpenSSL 的配置脚本能够识别以上全部选项并传递到它所生成的 Makefile(事实上,所有以-Wl,开头的参数都会被识别为链接器选项)。

3.3 不要硬编码路径:使用 LIBRPATH 便利变量

文档特别提醒:不要在运行时共享库搜索路径中使用硬编码目录。因为部分 OpenSSL 配置目标(config target)会为 multilib 安装额外增加一级目录(例如lib64、libx32)。为帮助用户应对这种差异,生成的 Makefile 提供了一个便利变量LIBRPATH,其定义可在 Library/OpensslLib/openssl/Configurations/unix-Makefile.tmpl 中找到:

# Convenience variable for those who want to set the rpath in shared # libraries and applications LIBRPATH=$(libdir)

也就是说LIBRPATH就是解析后的libdir(在$(INSTALLTOP)/$(LIBDIR)与 multilib 后缀解析完毕之后的值),因此应把它与运行时搜索路径选项组合使用,例如:

$ ./Configure --prefix=/usr/local/ssl --openssldir=/usr/local/ssl \ '-Wl,-rpath,$(LIBRPATH)'

这样即使构建目标带有 multilib 后缀(默认libdir变为lib<multilib-postfix>,可参考 INSTALL.md 中--libdir的说明),RPATH 也会跟随实际的libdir而变化,不会写死出错。


四、ELF 系统的两个运行时搜索路径标签:DT_RPATH 与 DT_RUNPATH

现代基于 ELF 的系统中有两个运行时搜索路径标签需要区分:DT_RPATH和DT_RUNPATH。共享对象按以下顺序被搜索:

  1. DT_RPATH中指定的目录——前提是DT_RUNPATH未被设置;
  2. 环境变量LD_LIBRARY_PATH中的目录;
  3. DT_RUNPATH中指定的目录;
  4. 系统的共享对象缓存(如/etc/ld.so.cache)与默认目录。

这个顺序带来的一个重要推论是:当库能在DT_RPATH(且未设置DT_RUNPATH)给出的路径中找到时,LD_LIBRARY_PATH的值将不起任何作用——因为DT_RPATH的优先级高于环境变量。这往往是调试“明明设了LD_LIBRARY_PATH却仍加载了错误版本库”的根源。

4.1 默认设置哪个标签取决于系统

文档指出,默认设置DT_RPATH还是DT_RUNPATH因系统而异:

  • Solaris 上DT_RPATH据文档已被弃用,转而推荐DT_RUNPATH;
  • Debian GNU/Linux 上两者皆可设置,且写作文档时默认是DT_RPATH。

具体应选择哪个标签,请查阅你系统上的ld(1)手册。

4.2 强制使用 DT_RUNPATH:--enable-new-dtags

如果希望 Debian GNU/Linux 等系统设置DT_RUNPATH而非DT_RPATH,可以指示链接器开启新的 dtags,例如:

$ ./Configure --prefix=/usr/local/ssl --openssldir=/usr/local/ssl \ '-Wl,--enable-new-dtags,-rpath,$(LIBRPATH)'

4.3 可执行文件相对搜索:$ORIGIN

文档还提醒:多数 ELF 系统支持相对于“当前可执行文件所在目录”的运行时搜索路径,其实现方式是解释$ORIGIN(以及其他一些内部变量)。这允许你把共享库放在可执行文件旁边实现“可移植的”动态部署。具体支持情况请查阅系统文档。


五、链接第三方应用程序:与 OpenSSL 动态库对接

与 OpenSSL 动态链接(或任何其他共享库)的第三方应用,在“库安装于非默认位置”时面临完全相同的问题——应用的可执行文件里也必须有对应的运行时搜索路径。

文档指出,上面介绍的 OpenSSL 配置选项对目标应用的链接“可能有影响也可能没有”:

  • “可能有影响”的情形:某些环境下,用自然的链接方式就足够,例如:

    cc -o myapp myapp.c -L/whatever/path -lssl -lcrypto
  • 另一些情形下,你必须在链接自己的应用时显式指定运行时搜索路径,例如将-Wl,-rpath,/whatever/path加入链接命令行。

判断哪种写法适用,请查阅系统文档,并以上文第三节的各平台选项表为参考。


六、共享构建中的静态库:链接器优先找共享库,AIX 需加 _a 后缀

共享版 OpenSSL 构建(shared build)同样会安装静态库。链接这些静态库需要特别小心,因为:

  • 链接器通常优先寻找共享库,对同时存在的静态 OpenSSL 库“视而不见”(倾向于选择.so而忽略.a);
  • 需要借助系统文档中的方法来强制使用静态库(例如显式指定.a文件路径,或使用-static等标志)。

此外文档点名了一个角落情况:AIX 在共享构建下,静态库的命名不同——需要附加_a后缀才能链接它们,例如用-lcrypto_a而非-lcrypto。

顺带一提,在 UEFI 场景下本仓库完全没有这一层困扰:Library/OpensslLib/configure.py 的配置参数中包含了no-shared,所有算法均以静态库形态直接编入固件,这也从反面印证了共享/静态两种形态的差异所在。


七、完整实战示例:一次规范的 UNIX 定制构建

综合本文全部要点,一个把 OpenSSL 安装到/usr/local/ssl并确保程序自带运行路径的典型配置流程如下:

# 1. 列出本机可用的构建目标(linux-elf / linux-x86_64 等) $ ./Configure LIST # 2. 指定平台与安装目录,并用 LIBRPATH 设置 DT_RUNPATH $ ./Configure linux-x86_64 \ --prefix=/usr/local/ssl \ --openssldir=/usr/local/ssl \ '-Wl,--enable-new-dtags,-rpath,$(LIBRPATH)' # 3. 编译并安装 $ make -j$(nproc) $ make install # 4. 验证运行时确实能找到共享库 $ /usr/local/ssl/bin/openssl version -a

其中:

  • --prefix=/usr/local/ssl指定安装目录树的顶层(UNIX 默认是/usr/local,参见 INSTALL.md);
  • --openssldir=/usr/local/ssl指定 OpenSSL 配置文件与默认证书/密钥库目录(Unix 默认/usr/local/ssl,参见 INSTALL.md);
  • 若某个构建目标带有 multilib 后缀导致默认libdir变成lib64等,可用--libdir=lib覆盖,或直接用$(LIBRPATH)让 RPATH 跟随实际目录。

如果你是为本仓库的 UEFI 固件重新生成 OpenSSL 源文件,则应参照 Library/OpensslLib/configure.py 的openssl_configure()方式:在openssl/目录内运行perl Configure并传入--config=../UefiAsm.conf、--api=1.1.1、no-shared以及大量no-*裁剪项,随后用make生成文件并由generate_files()复制到 Library/OpensslLib/OpensslGen 目录(详见generate_include_files()、generate_library_files()与copy_generated_file()的实现)。


八、常见排错速查

症状可能原因对策
openssl程序报error while loading shared libraries: libssl.so.3库装到非默认目录,且未设置 RPATH配置时加入'-Wl,-rpath,$(LIBRPATH)'
设置了LD_LIBRARY_PATH仍加载到旧版本库目标库由DT_RPATH路径先命中(DT_RPATH优先于环境变量)用--enable-new-dtags改用DT_RUNPATH,或清理 RPATH
-Wl,-rpath被当作普通编译选项处理参数形态错误确认小写l(非数字 1),且整段用引号包裹
multilib 系统上 RPATH 指向错误目录硬编码了/lib而非实际libdir改用$(LIBRPATH)
AIX 上链接-lcrypto得到共享库而非静态库静态库命名带_a后缀使用-lcrypto_a

以上表格内容均由 NOTES-UNIX.md 的说明与 Configure 的源码逻辑推导整理,可放心作为 UNIX 平台上 OpenSSL 构建与链接的排错参考。

  • 固件
  • 操作系统

【免费下载链接】CloverBootloader

Bootloader for macOS, Windows and Linux in UEFI and in legacy mode

项目地址:https://gitcode.com/gh_mirrors/cl/CloverBootloader
点击查看免费下载

相关推荐

上一篇:如何用cross-en-de-fr-roberta-sentence-transformer快速生成跨语言句子嵌入?5分钟入门教程
下一篇:デジタル庁デザインシステム:@internationalized/dateを用いた多国語日付処理の完全実装

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/18 12:15:49

双电池单元均衡方案:基于MP2642与STM32L4R5的BMS设计

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/21 16:41:43

多Agent调度容错实战:节点失败后如何保证任务继续推进

1. 多 Agent 调度到底在解决什么问题多 Agent 调度这个词这两年出现的频率越来越高&#xff0c;但很多人第一次接触的时候会把它和"多线程""分布式任务队列"混为一谈。我先把这个概念掰开说清楚&#xff1a;多 Agent 调度指的是在一个系统里同时运行多个具…

作者头像 李华