ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

OpenSSL 1.1.1c 编译产物接入:动态库、静态库与版本对齐

OpenSSL 1.1.1c 编译产物接入:动态库、静态库与版本对齐

简介:面向 Windows 平台 C/C++ 开发者的 OpenSSL 1.1.1c 预编译成果包,适合需要在 Visual Studio 2015 环境中快速集成加密通信、证书处理与 TLS 能力的项目使用,可免去从源码自行配置、编译和排查依赖的繁琐过程。压缩包共 928 个文件,约 43.73MB,以 848 个头文件为主体,并包含 16 个 lib、16 个 dll、28 个 pdb 以及少量 Perl 脚本、测试 exe 和 C 源文件;lib 与 dll 覆盖静态库、动态库的 32 位和 64 位版本,可分别对应单文件分发与运行时加载场景,pdb 便于调试时定位符号,完整头文件则满足接口查阅和工程引用。已有 1636 人学习下载,说明该版本组合在旧项目维护和教学实验中具有一定参考价值。读者可直接按静态/动态、32/64 位组合选取对应产物,用于工程链接、运行验证与调试分析,减少重复编译成本,并为后续排查库版本与平台匹配问题提供现成基础。

1. 拿到一份 OpenSSL_1_1_1c 编译产物:先确认它是什么形态

很多团队把 openssl 的编译产物当成"解压就能用"的压缩包,结果工程一接就报一堆 undefined reference,或者编译期干干净净、一运行就提示找不到 libssl.so.1.1。这份 OpenSSL_1_1_1c 编译文件的价值从来不是"省一次 make",而是把一份确定的库、头文件和命令行工具钉死在某一个版本上,让 1.1.1c 这个具体点位在多台机器、多个工程之间保持一致——尤其是那些还挂在 1.1.1 系列、又不敢一步跨到 3.x 的老项目,版本被钉住比版本新更重要。

它适合三类人:需要给已有 C/C++ 工程补一份可控 OpenSSL 的维护者;要在内网离线环境交付、装不了系统包的实施人员;以及被系统自带 openssl 坑过一次、想用独立 prefix 把风险隔离出去的人。但它不是万能的,前提是你先搞清楚它是哪个平台、哪种架构、动态库还是静态库、头文件带没带全——这几件事没核对清楚,后面每一步都只能靠猜。

2. 库文件形态与链接方式:动态库、静态库和头文件怎么对上

2.1 一份编译产物里通常有哪几类文件

先别急着写编译命令,把目录摊开看一遍。一份完整的 1.1.1c 产物在 Linux 上大体是下面这几类,缺任何一类都会在某个环节卡住,而且报错位置往往离真正缺的东西很远。

路径类型作用缺失后的典型症状
lib/libssl.so.1.1动态库实体TLS 协议实现运行期cannot open shared object file
lib/libssl.so软链接链接期-lssl的落点链接报cannot find -lssl
lib/libcrypto.so.1.1动态库实体密码学原语、X.509加载 libssl 时连带失败
lib/libssl.a/libcrypto.a静态库免依赖分发静态链接时缺-ldl -pthread
include/openssl/*.h头文件编译期 API 声明openssl/ssl.h: No such file
bin/openssl可执行命令行工具、证书操作无法本地验证库是否可用
lib/engines-1.1/动态引擎硬件加速等可选模块指定 engine 时加载失败
lib/pkgconfig/*.pc元数据给 pkg-config 提供参数pkg-config --cflags openssl为空

这里有个容易被忽略的点:libssl.so和libssl.so.1.1是两个东西。前者是给链接器看的软链接,后者是带 SONAME 的真实文件,运行时加载的是后者。有些打包方式为了缩小体积把不带版本号的软链接删了,结果编译期直接挂掉,你还以为是库文件损坏。同理,include/openssl/下面是几十个头文件,opensslconf.h是编译时按平台生成的,跟库是绑定的,拿别的版本的opensslconf.h顶替,轻则宏判断走错分支,重则结构体大小对不上,跑起来直接段错误。

.pc文件经常被无视,但它是最省事的接入方式。1.1.1c 的openssl.pc里写着Libs: -L${libdir} -lssl -lcrypto和Cflags: -I${includedir},只要PKG_CONFIG_PATH指到lib/pkgconfig,编译参数就不用背了。

2.2 动态链接与静态链接:选哪个,命令怎么写

选型上没有标准答案,只有约束。同一台机器上只跑一个服务、系统库版本可控,用动态链接,升级方便、体积小;要把程序拷到客户机器上、对方的 glibc 和 OpenSSL 都不受你控制,用静态链接,把libssl.a和libcrypto.a打进去,代价是二进制变大、且 OpenSSL 的安全更新必须重新编译发布。

动态链接的编译命令长这样:

# 假设产物解压在 /opt/openssl-1.1.1c gcc -o demo demo.c \ -I/opt/openssl-1.1.1c/include \ -L/opt/openssl-1.1.1c/lib \ -lssl -lcrypto \ -Wl,-rpath,/opt/openssl-1.1.1c/lib # 把搜索路径写进 ELF,省掉 LD_LIBRARY_PATH

参数逐个说清楚:-I必须在-L前面理解成"头文件从哪找",-L是"链接时去哪找 .so/.a",-lssl -lcrypto的顺序不能反——libssl依赖libcrypto,链接器从左到右解析,把-lcrypto写在前面会导致libssl里引用的符号解析不到。-Wl,-rpath,...是把运行期搜索路径直接写进 ELF 的DT_RUNPATH,比让用户配环境变量可靠得多。

静态链接要额外补系统库:

gcc -o demo_static demo.c \ -I/opt/openssl-1.1.1c/include \ /opt/openssl-1.1.1c/lib/libssl.a \ /opt/openssl-1.1.1c/lib/libcrypto.a \ -ldl -lpthread -lz # 1.1.1 起内置线程支持,dl 用于 engine 动态加载,z 用于压缩

直接写.a的完整路径而不是-lssl,是为了避免链接器优先挑到系统目录里的同名库。-ldl和-lpthread是 1.1.1 静态链接绕不过去的,漏了会报undefined reference to 'dlopen'或者线程相关符号缺失,这个报错信息指向的位置跟 OpenSSL 一点关系都没有,第一次遇到很容易往别处查。

2.3 三条必须先验证的属性:架构、glibc、SONAME

在把库接进工程之前,花两分钟做三项检查,能省掉后面几个小时。第一项是架构,file一看就知道是 x86-64 还是 aarch64,32 位和 64 位混用是最常见的"库没问题但就是链不上"。第二项是 glibc 版本下限,编译产物的符号版本决定了它能在多老的系统上跑,客户机器glibc 比编译机老,就会报GLIBC_2.xx not found,这个错在运行期才出现,编译期完全无感。第三项是 SONAME,它决定运行期到底按什么名字去找库。

# 1. 架构与位数 file /opt/openssl-1.1.1c/lib/libssl.so.1.1 # 2. glibc 符号版本下限(看有没有高版本 GLIBC_x.y) objdump -T /opt/openssl-1.1.1c/lib/libcrypto.so.1.1 | grep -o 'GLIBC_[0-9.]*' | sort -uV | tail -5 # 3. SONAME 与依赖 readelf -d /opt/openssl-1.1.1c/lib/libssl.so.1.1 | grep -E 'SONAME|NEEDED'

三条命令的输出含义值得记一下:file里出现ARM aarch64就说明这是给 ARM 服务器或国产化平台用的,拿去 x86 上必然失败;objdump里最高那个GLIBC_2.28之类的版本号,就是目标机器 glibc 的最低门槛;SONAME那一行如果是libssl.so.1.1,那就意味着运行期只要凑齐这个名字的文件就能加载,跟你目录里叫libssl.so.1.1c还是别的什么名字无关——这也是后面版本冲突那一章要展开的根子。

3. 把编译产物接进工程:三条接入路径的实操

3.1 CMake 工程:从 find_package 到手动 imported target

CMake 自带FindOpenSSL模块,正常情况下一行find_package(OpenSSL REQUIRED)就够,但它默认会先去翻系统路径,你辛辛苦苦编的 1.1.1c 很可能被/usr/lib/x86_64-linux-gnu/libssl.so.1.1抢先命中。控制这个行为的开关是OPENSSL_ROOT_DIR,而且必须在find_package之前设。

cmake_minimum_required(VERSION 3.10) project(demo C) # 关键:必须在 find_package 之前指定,否则模块会优先命中系统库 set(OPENSSL_ROOT_DIR "/opt/openssl-1.1.1c") set(OPENSSL_USE_STATIC_LIBS FALSE) # 想静态链接改成 TRUE find_package(OpenSSL 1.1.1 REQUIRED) if(NOT OPENSSL_FOUND) message(FATAL_ERROR "OpenSSL not found, check OPENSSL_ROOT_DIR") endif() # 打印出来确认命中的到底是不是你那份 message(STATUS "OpenSSL version: ${OPENSSL_VERSION}") message(STATUS "SSL lib: ${OPENSSL_SSL_LIBRARY}") message(STATUS "Crypto lib: ${OPENSSL_CRYPTO_LIBRARY}") add_executable(demo demo.c) target_link_libraries(demo PRIVATE OpenSSL::SSL OpenSSL::Crypto)

target_link_libraries用OpenSSL::SSL这种 target 形式,而不是老的${OPENSSL_LIBRARIES}变量,好处是 include 路径、编译定义、依赖顺序都由 target 携带,不用自己拼。中间那几行message(STATUS ...)不是装饰——OPENSSL_VERSION输出的如果是1.1.1c,你就能确信找对了;输出的还是1.1.1f之类,说明OPENSSL_ROOT_DIR没生效,去查 CMake 缓存里有没有旧的OPENSSL_INCLUDE_DIR残留,清掉CMakeCache.txt重来。

如果目标 CMake 版本太老(低于 3.4 没有 imported target),或者产物目录结构被裁剪得不符合标准布局,可以手动造一个:

add_library(openssl_ssl SHARED IMPORTED) set_target_properties(openssl_ssl PROPERTIES IMPORTED_LOCATION "/opt/openssl-1.1.1c/lib/libssl.so.1.1" INTERFACE_INCLUDE_DIRECTORIES "/opt/openssl-1.1.1c/include") add_library(openssl_crypto SHARED IMPORTED) set_target_properties(openssl_crypto PROPERTIES IMPORTED_LOCATION "/opt/openssl-1.1.1c/lib/libcrypto.so.1.1" INTERFACE_INCLUDE_DIRECTORIES "/opt/openssl-1.1.1c/include")

IMPORTED_LOCATION要指到带 SONAME 的真实文件,不要指软链接,某些生成器在处理软链接时会解析成绝对路径写死,换台机器就找不到。INTERFACE_INCLUDE_DIRECTORIES是"谁链接我,谁就自动获得这个 include 路径",比全局include_directories干净。

3.2 Makefile 手写:链接参数与 rpath 的正确顺序

不用 CMake 的老工程更常见,手写 Makefile 时把 pkg-config 用上,参数就不用手抄了:

PREFIX := /opt/openssl-1.1.1c PKGCFG := $(PREFIX)/lib/pkgconfig CFLAGS += $(shell PKG_CONFIG_PATH=$(PKGCFG) pkg-config --cflags openssl) LDLIBS += $(shell PKG_CONFIG_PATH=$(PKGCFG) pkg-config --libs openssl) LDFLAGS += -Wl,-rpath,$(PREFIX)/lib demo: demo.o $(CC) -o $@ $^ $(LDFLAGS) $(LDLIBS)

$(shell ...)在 Makefile 解析阶段就展开,所以PKG_CONFIG_PATH必须内联写在命令里,不能指望外部 shell 已经 export 过。LDFLAGS和LDLIBS分开写是有讲究的:LDFLAGS放链接器选项(rpath、-L),LDLIBS放库(-lssl -lcrypto),顺序上$^放在两者之前,保证目标文件先出现,库在后,符合链接器的解析方向。

如果你不想依赖 pkg-config,全手写也行,但要注意-L只影响-l的搜索,不影响运行期,所以 rpath 一个都不能省:

CFLAGS += -I/opt/openssl-1.1.1c/include LDFLAGS += -L/opt/openssl-1.1.1c/lib -Wl,-rpath,/opt/openssl-1.1.1c/lib LDLIBS += -lssl -lcrypto

一个实用技巧:用$ORIGIN让 rpath 相对可执行文件所在目录,这样整个目录(含 lib 子目录)可以整体挪走:

LDFLAGS += -Wl,-rpath,'$$ORIGIN/../lib'

注意 Makefile 里$要写两次,$$ORIGIN才会被传给链接器;写成$ORIGIN会被 make 当成变量展开成空。这个转义坑几乎每个手写 Makefile 的人都踩过,症状是 rpath 变成/../lib,运行期照样找不到库。

3.3 交叉编译:sysroot 与 prefix 的组织方式

给 ARM 板子或国产化平台准备库的时候,产物本身没问题,但目录结构必须按 sysroot 来摆,否则-I和-L会互相打架。常见做法是把产物塞进一个根目录,编译时只给--sysroot,让编译器自己去下面找:

# 目录结构约定 # /path/to/sysroot/usr/include/openssl/*.h # /path/to/sysroot/usr/lib/libssl.so.1.1 arm-linux-gnueabihf-gcc -o demo demo.c \ --sysroot=/path/to/sysroot \ -lssl -lcrypto

--sysroot会让编译器把/usr/include和/usr/lib自动重定向到 sysroot 下,好处是编译参数干净、不会误链宿主机的库;坏处是目录必须摆得规规矩矩,头文件必须在usr/include,库必须在usr/lib,放在别处就得额外补-I/-L,一补又容易把宿主机路径带进来。我一般会在交叉编译前先跑一次arm-linux-gnueabihf-gcc -print-sysroot,确认工具链默认的 sysroot 位置,避免你以为的路径和工具链实际用的路径不是同一个地方。

另一个细节是目标机的运行期库路径。嵌入式环境经常没有 ldconfig 缓存,/etc/ld.so.conf也不一定可写,最稳的做法还是 rpath 写死,或者把库放在可执行文件同目录下用$ORIGIN引用。交付前用readelf -d demo | grep -E 'RPATH|RUNPATH'看一眼,确认路径进去了,比到现场再调试便宜得多。

4. 版本与 ABI 对齐:为什么 1.1.1c 的库不能随便换

4.1 SONAME、符号版本与 1.1.1 系列的小版本兼容

1.1.1 系列从 1.1.1 到 1.1.1w,SONAME 一直是libssl.so.1.1,这意味着运行期把所有 1.1.1x 当成同一个东西看待。设计意图是补丁版本之间 ABI 兼容、可以互换,实际使用中这条规则确实成立度很高——但成立的前提是两边都是"原版"编译出来的。

问题出在编译选项上。no-ssl3、no-comp、enable-weak-ssl-ciphers、no-tls1、no-async这些 Configure 开关会改变库内部的结构体布局和符号集合。A 工程用的库编译时带了no-comp,B 工程用的库没带,两者都叫libssl.so.1.1,把一个换到另一个的机器上运行,可能直接崩。判断的办法是看符号:

# 看某个开关相关符号是否存在 nm -D --defined-only /opt/openssl-1.1.1c/lib/libcrypto.so.1.1 | grep -i comp readelf -d /opt/openssl-1.1.1c/lib/libcrypto.so.1.1 | grep -i 'SONAME'

nm -D输出的是动态符号表,--defined-only过滤掉未定义引用,只看这个库自己提供的符号。如果明明开了压缩支持却搜不到COMP_*系列符号,说明产物被裁剪过,别拿它当完整版用。

还有一个更隐蔽的坑:OpenSSL 1.1.0 开始把大量结构体改成了不透明指针,SSL_CTX、SSL、X509的成员不能再直接访问。1.1.1c 当然是这个规则,但如果工程里混进了 1.0.2 时代的代码,编译期靠头文件能过(因为用的是老头文件的字段访问),链接的就是 1.1.1c 的库,运行时结构体大小对不上,症状是随机崩溃或读到垃圾值,而且堆栈完全指不到 OpenSSL。

4.2 运行期探测:用 OPENSSL_VERSION_NUMBER 卡住版本

代码里做版本判断,别用字符串比较,用编译期宏加运行期函数配合。OPENSSL_VERSION_NUMBER是编译期常量,OpenSSL_version()是运行期真实值,两者不一致就是"头文件和库不配套"的铁证,这种错误必须在启动阶段就拦下来,不能等到握手时才炸。

#include <openssl/opensslv.h> #include <openssl/crypto.h> #include <stdio.h> int main(void) { /* 1.1.1c 的编译期常量应为 0x1010103fL */ unsigned long build_ver = OPENSSL_VERSION_NUMBER; /* 运行期从 libcrypto 实际读到的版本 */ unsigned long run_ver = OpenSSL_version_num(); printf("build: 0x%lx runtime: 0x%lx\n", build_ver, run_ver); printf("runtime string: %s\n", OpenSSL_version(OPENSSL_VERSION)); if ((build_ver ^ run_ver) & 0xfffff000L) { /* 只比较主版本+次版本+补丁位,忽略最后的 status 位 */ fprintf(stderr, "header/library mismatch!\n"); return 1; } if (run_ver < 0x1010103fL) { fprintf(stderr, "runtime openssl older than 1.1.1c\n"); return 2; } return 0; }

关键是那个掩码0xfffff000L。版本号低 4 位是状态位(release、dev、beta 之类),高位才是版本本体,直接==比较会在某些发行版打了补丁后误报。另外,拿OpenSSL_version_num()而不是SSLeay()——SSLeay()是 1.0.x 时代的接口,1.1.0 之后虽然还能用,但语义已经不推荐。

如果工程用的语言不是 C,Python 侧可以用ssl.OPENSSL_VERSION快速核对,用它来验证一个虚拟环境到底链的是哪份库:

import ssl print(ssl.OPENSSL_VERSION) # 形如 OpenSSL 1.1.1c 28 May 2019 print(ssl.OPENSSL_VERSION_INFO) # (1, 1, 1, 3, 15) 最后一位是状态

OPENSSL_VERSION_INFO里第四位是补丁字母对应的数字,1.1.1c 是 3。这个值在排查"为什么测试环境能跑、生产环境握手失败"时特别有用——两个环境的ssl.OPENSSL_VERSION打印出来不一致,问题立刻定位,不用再猜。

5. 排查:链接失败、加载失败、握手失败的五条记录

5.1 先建立排查顺序:从 ELF 到加载器到运行时

遇到 OpenSSL 相关问题,最忌讳直接去改代码。先按下面这条链路走一遍,八成问题在前三步就能定位。

第一步看 ELF 依赖,确认可执行文件到底需要哪些库、有没有把路径写进去:

readelf -d ./demo | grep -E 'NEEDED|RPATH|RUNPATH' ldd ./demo | grep -i ssl

NEEDED里出现libssl.so.1.1说明是动态链接;RPATH(老式,DT_RPATH)和RUNPATH(新式,DT_RUNPATH)的区别很重要——RPATH的优先级高于LD_LIBRARY_PATH,RUNPATH低于它。现代链接器默认生成RUNPATH,所以用户设了LD_LIBRARY_PATH就能覆盖你的 rpath,这既是逃生通道也是事故来源。

第二步看加载器实际找到了哪个文件:

LD_DEBUG=libs ./demo 2>&1 | grep -i 'libssl\|libcrypto'

LD_DEBUG=libs会打印每一次库查找的尝试路径和最终命中结果,比ldd更贴近真实加载过程。ldd本质上是在跑加载器但会受环境影响,某些加固过的系统上ldd的结果和实际运行不一致,这时候LD_DEBUG才作数。

第三步确认库本身能不能单独加载,排除文件损坏:

/opt/openssl-1.1.1c/bin/openssl version -a

这个命令输出里带OPENSSLDIR、built on、compiler等信息,能一次性确认版本、构建平台、默认证书目录。如果这条命令本身就报错,说明产物有问题,往下查业务代码毫无意义。

5.2 五条踩坑记录

现象一:编译期报undefined reference to 'SSL_CTX_new',头文件明明包含了。

原因通常不是库缺符号,而是链接顺序或者根本没链上。-lssl写在demo.o前面,链接器扫过-lssl时还没有任何未定义符号,于是直接跳过,后面demo.o产生引用时已经没人管了。另一种可能是-L指向的目录里只有libssl.so.1.1而没有libssl.so软链接。

解决:把库参数挪到目标文件之后,并确认软链接存在。没有软链接就手动补一个ln -s libssl.so.1.1 libssl.so,或者在命令行直接写.so.1.1的完整路径绕开-l机制。

现象二:编译通过,运行报error while loading shared libraries: libssl.so.1.1: cannot open shared object file。

原因是 rpath 没写、写错,或者被RUNPATH的优先级规则坑了。还有一种情况是程序本身没链 libssl,是某个第三方.so间接依赖它,而间接依赖不会继承你的 rpath——DT_RUNPATH只对直接依赖生效,孙子辈的库只认全局路径。

解决:先LD_DEBUG=libs看它到底在哪些目录里找过。临时方案是LD_LIBRARY_PATH=/opt/openssl-1.1.1c/lib ./demo;长期方案是把第三方库也一起重新链接,或者干脆静态链接 OpenSSL 进那个第三方库,把依赖压平。

现象三:本机跑得好好的,拷到另一台机器报version 'GLIBC_2.28' not found。

原因是编译机的 glibc 比目标机新。这个错跟 OpenSSL 没关系,但报错发生在加载 libssl 的时候,很容易误判成库的问题。

解决:用 2.3 节那条objdump命令查一下产物依赖的最高 GLIBC 版本,然后在 glibc 不高于目标机的环境里重新编译,或者改用静态链接 glibc(风险较大,一般不建议)。血泪经验是:交付前一定在最低配置的目标环境里跑一次openssl version -a,别在自己开发机上验证完就打包。

现象四:握手失败,报certificate verify failed,但证书文件路径看着没错。

原因是SSL_CTX_set_default_verify_paths()找的是编译时--openssldir定下的目录,产物被挪到别处后,那个目录下的cert.pem或certs/就不存在了,于是 CA 列表为空,任何证书都验不过。

解决:不要依赖默认路径,显式加载。用SSL_CTX_load_verify_locations(ctx, ca_path, NULL)指定 CA 文件,或者用SSL_CTX_set_default_verify_paths之前先setenv("SSL_CERT_FILE", ...)。另外可以用openssl version -d看当前库认定的OPENSSLDIR,一眼就知道它在哪找证书。

现象五:同一台机器上系统 openssl 和自编译 1.1.1c 打架,程序行为不稳定。

原因是两个版本都提供libssl.so.1.1,SONAME 相同,加载器只按名字找,谁先出现在搜索路径里就用谁。系统的openssl命令行工具升级过、库还是老的,或者反过来,都会导致"命令行能验过的证书,程序里验不过"这种玄学现象。

解决:隔离。自编译产物装到独立 prefix(比如/opt/openssl-1.1.1c),编译时用 rpath 钉死到这个绝对路径,运行时用ldd确认命中的是它。绝对不要make install覆盖/usr/lib下的系统库——系统包管理器不知道你动过,下次升级一覆盖,你的程序立刻挂,而且找不出原因。

6. 复刻一份可对齐的产物:Configure 参数与验收清单

拿到的产物不一定完全贴合你的场景,比如对方编的是动态库、你需要静态库,或者对方开了no-comp、你的业务要用压缩。自己复刻一份的成本其实很低,关键是参数要记牢、验收要走全。

Configure 阶段决定了大半命运,下面这套是我常用的起手式:

tar xzf openssl-1.1.1c.tar.gz && cd openssl-1.1.1c # --prefix 决定安装位置,--openssldir 决定证书和配置的默认目录,两者分开便于整体搬迁 ./config --prefix=/opt/openssl-1.1.1c \ --openssldir=/opt/openssl-1.1.1c/ssl \ shared zlib no-ssl3 no-comp \ -fPIC make -j"$(nproc)" make test # 别跳过,1.1.1 的自测覆盖了大部分算法路径 make install_sw # 只装库和头文件,跳过文档,避免 perl 依赖问题

shared生成动态库,要纯静态就去掉它;zlib启用压缩,需要系统有 zlib 开发包;no-ssl3关掉早已废弃的 SSLv3,安全基线要求;no-comp关掉 TLS 压缩,防 CRIME 类攻击,代价是老客户端可能握不上手;-fPIC是为了让静态库也能被链接进共享库,不加的话别人拿你的.a去做.so会报 relocation 错误。install_sw和install的区别就在文档,前者省事且不会因为缺 perl 模块中断。

编完之后按这张表逐项验收,任何一项不过都别交付:

验收项命令期望结果
版本字符串bin/openssl version -a显示OpenSSL 1.1.1c,OPENSSLDIR指向 prefix
动态库 SONAMEreadelf -d lib/libssl.so.1.1 | grep SONAMElibssl.so.1.1
静态库存在ls lib/libssl.a lib/libcrypto.a两个文件都在
符号完整性nm -D --defined-only lib/libcrypto.so.1.1 | grep -c ' T '数量与官方产物同量级
头文件配套grep OPENSSL_VERSION_TEXT include/openssl/opensslv.hOpenSSL 1.1.1c
自测通过make test无 FAIL

最后一项make test我要多说一句。它耗时几分钟,很多人图快直接跳过,然后在业务里遇到一个只在特定曲线或特定 padding 模式下才出现的错误,回头查几天。我吃过这个亏之后,凡是自己编的库,make test和上面这张表都强制走一遍,宁可多等五分钟。

还有一个习惯值得养成:产物目录里放一个BUILD_INFO.txt,写清楚编译日期、平台、Configure 全参数、glibc 版本。半年后有人问"这份库到底开没开压缩",翻一下就有答案,不用对着一堆.so猜。希望帮到你。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进