简介:面向 Windows 平台 C/C++ 开发者的 OpenSSL 1.1.1c 预编译产物包,基于 Visual Studio 2015 构建,适合需要在桌面程序、服务端组件或网络通信模块中集成 SSL/TLS 与加密能力的开发者。包内同时覆盖静态库与动态库的 32 位、64 位版本,包含头文件、导入库、动态运行库及调试符号,可省去自行编译 OpenSSL 的环境配置与依赖处理成本;静态库便于减少额外运行库分发,动态库则方便多模块共享与按需替换。压缩包共 928 个文件,以 h 头文件为主,另有 lib 导入库、dll 动态库、pdb 调试符号以及少量 exe、pl、c 辅助文件,整体约 43.73MB,目录层级较清晰,便于按架构和链接方式检索取用。已有 1636 人学习,说明其在同类编译产物中具有一定参考价值。对于需要稳定加密依赖、排查链接错误或运行时加载问题的项目,这套产物能提供较完整的集成素材,调试符号也有助于定位异常。
1. 同事把这包发给你的时候,别急着塞进工程
老项目一编译就卡在LNK2019: unresolved external symbol EVP_EncryptInit_ex,或者 Linux 上undefined reference to SSL_CTX_new,这种场面我见过太多次。多数时候问题不在代码,在于机器上没有一份能直接被链接器认出来的 openssl 二进制产物。手上这份 OpenSSL_1_1_1c 编译文件,本质上就是把 1.1.1c 这个补丁位的头文件、导入库、静态库和运行时库打成一个成套的包,省掉装 Perl、NASM、配 VS 命令行那一步。它适合两类人:手里压着 1.1.1 时代老工程、不敢随便跳大版本的人;以及需要在 CI 里固定依赖版本、要求每次构建都拿到同一份 so/dll 的人。但先说结论:直接把它拷进 include 路径就开编,大概率翻车,因为位数、运行库模型、头文件与库的匹配关系,任何一项对不上都会在链接期或运行期炸出来。
2. 拆包先看三样东西:头文件、导入库、运行时库
一份完整可用的 openssl 二进制包,缺一样都不算完整。头文件决定编译期能看到哪些声明,导入库(Windows 的 .lib、Linux 的 .so 或 .a)决定链接器能不能把符号接上,运行时库决定程序真正跑起来时去哪里找实现。这三者的版本必须严格同源,混搭是最常见的坑。
2.1 目录布局与产物命名对照
1.1.1c 的产物命名规则跟 1.0.2 时代不一样,光看文件名就能判断这包是动态构建还是静态构建。下面这张表是我拿到任何一份 openssl 包都会先扫一遍的东西。
| 平台 | 动态构建产物 | 静态构建产物 | 说明 |
|---|---|---|---|
| Windows x64 | libcrypto-1_1-x64.dll、libssl-1_1-x64.dll+ 同名.lib导入库 | libcrypto_static.lib、libssl_static.lib | 32 位 dll 名不带-x64,是libcrypto-1_1.dll |
| Linux x86_64 | libcrypto.so.1.1、libssl.so.1.1,外加libcrypto.so、libssl.so两个开发用符号链接 | libcrypto.a、libssl.a | SONAME 是libcrypto.so.1.1,不是.so.1.1.1 |
| 公共部分 | include/openssl/*.h | 同左 | 其中opensslconf.h由 Configure 生成,带平台宏 |
include/openssl/opensslv.h里有OPENSSL_VERSION_TEXT和OPENSSL_VERSION_NUMBER两个宏,前者给人看,后者给预处理器判断。opensslconf.h则是 Configure 当场吐出来的,里面写着OPENSSL_SYS_LINUX、RC4_INT之类跟平台绑定的定义,所以这份文件绝对不能跨平台复制,32 位包里拿来的opensslconf.h放到 64 位工程里,编译期就能给你整出一堆类型长度不一致的警告。
Windows 包一般还带一个bin/目录放 dll,lib/放导入库,include/放头文件。Linux 包如果是make install_sw装的,会多出lib/pkgconfig/下的libcrypto.pc、libssl.pc、openssl.pc三个文件,后面接入构建系统时能省不少事。
2.2 为什么锚定 1.1.1c 而不是随手换个版本
1.1.1 这条线上最关键的改动是结构体不透明化,这个变化从 1.1.0 就开始了,到 1.1.1 已经彻底落地。以前RSA->n、X509->cert_info这种直接摸成员的写法全部失效,必须改成RSA_get0_key、X509_get0_notBefore这类访问器。老代码迁过来编译不过,第一反应往往是「随便找个旧版本库对付一下」,结果就是安全和兼容两头不讨好。
锚定 1.1.1c 的真实理由通常是这几个:工程里某几个第三方依赖只声明兼容 1.1.1 系列;CI 镜像里跑着的其他组件已经跟这个版本做过联调;或者干脆是历史遗留,换版本要走一轮完整回归,代价太大。1.1.1 系列内部是 ABI 兼容的,libcrypto.so.1.1这个 SONAME 从 1.1.1a 一直用到 1.1.1w 都没变过,所以理论上你可以用 1.1.1w 的 so 替换 1.1.1c 的 so 而不重新编译。反过来,1.1.1 的库和 3.x 的库 SONAME 不同,绝不能互换。
版本号本身是编码进宏里的,格式是MNNFFPPS,也就是大版本、小版本、修正号、补丁位各占几位十六进制,最后一位是发布状态。查这个宏比查字符串可靠,因为它参与预处理。
| 版本 | OPENSSL_VERSION_NUMBER | 状态位 |
|---|---|---|
| 1.0.2u | 0x1000215fL | 已停止维护 |
| 1.1.1a | 0x1010101fL | 1.1.1 早期补丁位 |
| 1.1.1c | 0x1010103fL | 本资源对应版本 |
| 1.1.1w | 0x1010117fL | 1.1.1 分支末位 |
写代码里做版本门槛时,别写#if OPENSSL_VERSION_NUMBER == 0x1010103fL,那样换个补丁位就编译不过。写成大于等于更稳,比如#if OPENSSL_VERSION_NUMBER < 0x1010100fL就报错,把「至少 1.1.1」这个约束表达清楚。
2.3 三步确认这份包到底能不能直接用
拿到任何一份预编译包,我一般先跑三件事再谈接入。第一件看文件类型和位数,第二件看版本串,第三件写个十行小程序把链接和运行都验一遍。这三步加起来不到五分钟,能挡掉后面百分之八十的返工。
# 1. 看架构:确认是 x86-64 还是 i386,别拿 32 位包往 64 位工程里塞 file libcrypto.so.1.1 # 期望输出类似:ELF 64-bit LSB shared object, x86-64 # 2. 看 SONAME 和依赖,确认它没依赖一堆你机器上没有的库 readelf -d libcrypto.so.1.1 | grep -E 'SONAME|NEEDED' # 3. 看版本串,确认包里的库和头文件说的是同一个版本 strings libcrypto.so.1.1 | grep -m1 'OpenSSL 1.1.1'file那条命令是防呆的,我确实见过把 32 位包解压到 64 位工程目录、然后花半天查LNK2019的情况。readelf -d重点看SONAME是不是libcrypto.so.1.1,如果是libcrypto.so.3那说明包本身就不是 1.1.1 的。strings抓版本串只是粗筛,真正严谨的做法是编译下面这段。
/* vercheck.c —— 编译期和运行期各验一次版本 */ #include <stdio.h> #include <openssl/opensslv.h> #include <openssl/crypto.h> #if OPENSSL_VERSION_NUMBER < 0x1010100fL #error "这份头文件低于 1.1.1,不接受" #endif int main(void) { /* 运行期打印实际加载到的库版本,和头文件版本对照 */ printf("header : %s\n", OPENSSL_VERSION_TEXT); printf("runtime: %s\n", OpenSSL_version(OPENSSL_VERSION)); return 0; }编译命令是gcc vercheck.c -o vercheck -I<include路径> -L<lib路径> -lcrypto。如果两行输出不一致,说明头文件和库不是同一份来源,这种包不要用。OpenSSL_version是 1.1.0 引入的接口,替代了老的SSLeay_version,在 1.1.1c 上可以直接调。Windows 下把gcc换成cl,把-lcrypto换成libcrypto.lib,运行前记得把 dll 放到 exe 同目录,否则会直接报找不到模块。
3. 接进工程:MSVC 与 GCC 两条链路的落地配置
接入这件事没有想象中复杂,难的是把配置固化下来,让团队里每个人、每台 CI 机器拿到的路径和链接顺序都一致。手动在 IDE 里点属性页配一遍,换台机器就得重来,所以下面给的都是能进版本库的写法。
3.1 Windows/MSVC:include、lib、dll 三件套
MSVC 的坑集中在「编译期找得到头文件、链接期找得到 lib、运行期找得到 dll」这三件事分别由不同机制管。头文件靠附加包含目录,lib 靠附加库目录加附加依赖项,dll 靠 PATH 或者 exe 同目录。前两个配错在编译链接阶段就报错,第三个配错要等到运行才炸,所以最容易被漏掉。
用 CMake 管这件事最省心,因为它把三件事写在一处,还能顺手把 dll 拷到输出目录。
# CMakeLists.txt —— 指向解压出来的 1.1.1c 目录 cmake_minimum_required(VERSION 3.12) project(demo C) # 让 find_package 去这个前缀下找 include 和 lib set(OPENSSL_ROOT_DIR "${CMAKE_SOURCE_DIR}/third_party/openssl-1.1.1c") find_package(OpenSSL 1.1.1 REQUIRED) add_executable(demo main.c) target_link_libraries(demo PRIVATE OpenSSL::SSL OpenSSL::Crypto) # Windows 下把 bin 里的 dll 拷到 exe 旁边,省掉运行期找不到模块 if(WIN32) file(GLOB _ssl_dlls "${OPENSSL_ROOT_DIR}/bin/*.dll") add_custom_command(TARGET demo POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${_ssl_dlls} $<TARGET_FILE_DIR:demo>) endif()OPENSSL_ROOT_DIR是 FindOpenSSL 模块认的变量,指向包根目录即可,它会自己去<root>/include和<root>/lib下面找。find_package里写1.1.1是版本下限,写EXACT才是精确匹配,一般情况下不需要 EXACT,因为 1.1.1 系列内部可以互换。OpenSSL::SSL和OpenSSL::Crypto是导入目标,链接顺序由 CMake 自动处理,比手写-lssl -lcrypto可靠。
不用 CMake 的话,cl命令行是这样:
:: 在 x64 Native Tools 命令行里执行 cl main.c /I third_party\openssl-1.1.1c\include ^ /link /LIBPATH:third_party\openssl-1.1.1c\lib libssl.lib libcrypto.lib/I是头文件目录,/link之后才是链接器参数,顺序不能反。如果用的是静态库,把libssl.lib libcrypto.lib换成libssl_static.lib libcrypto_static.lib,还要额外链ws2_32.lib、crypt32.lib、user32.lib、gdi32.lib这几个系统库,因为静态库不会自己带上这些依赖。
3.2 Linux/GCC:-I、-L、-l 的顺序不是玄学
Linux 下链接顺序是硬规则,不是玄学。链接器从左往右扫描,遇到-lssl时把 libssl 里被引用但还没解析的符号记下来,同时它需要 libcrypto 里的符号来补齐,所以-lcrypto必须出现在-lssl右边。反过来写,链接器扫完-lssl就以为事情结束了,后面再看到-lcrypto也不会回头补,于是报一堆undefined reference to SSL_CTX_new。
gcc main.c -o demo \ -I/opt/openssl11/include \ -L/opt/openssl11/lib \ -lssl -lcrypto-I和-L分别指定头文件和库搜索路径,-lssl -lcrypto的顺序按上面的规则来。这套命令在静态链接时更敏感,因为静态库是按需抽取目标文件的,顺序错了就是真的抽不出来。如果系统里本来就有/usr/lib/x86_64-linux-gnu/libcrypto.so.3,-L指定的路径要放在默认路径之前,否则可能链到系统的 3.x 上,编译能过但运行时会因为符号版本不匹配直接段错误。
验证链到的是哪一份,用ldd看运行期依赖,用gcc -Wl,-t看链接期实际抽取了哪些库文件,这两个输出对不上就说明路径有问题。
3.3 用 CMake 和 pkg-config 把它固化进构建系统
Linux 侧最省事的方案是 pkg-config,前提是这份包是用make install_sw装的,lib/pkgconfig下有.pc文件。
# 让 pkg-config 去自定义前缀下找 export PKG_CONFIG_PATH=/opt/openssl11/lib/pkgconfig pkg-config --modversion libssl # 期望输出 1.1.1c pkg-config --cflags --libs libssl # 期望输出 -I... -L... -lssl -lcryptoPKG_CONFIG_PATH只对当前 shell 生效,写进 CI 脚本时要么在脚本开头 export,要么直接写进~/.bashrc。--modversion打印的版本来自.pc文件里的Version:字段,如果它和strings抓出来的版本串不一致,说明这份.pc是手工改过的,不要信。
CMake 侧可以两种方式二选一:要么用OPENSSL_ROOT_DIR走 FindOpenSSL,要么find_package(PkgConfig)后pkg_check_modules(OPENSSL REQUIRED libssl libcrypto)。前者更通用,后者在交叉编译时更好控制。我一般在交叉编译场景用后者,因为PKG_CONFIG_SYSROOT_DIR可以统一改前缀,不用逐个变量去调。
4. 自己复现一遍:用同一份源码编出匹配的库
预编译包最大的风险是 ABI 对不上,尤其是 Windows 上不同 VS 版本、不同运行库模型(/MT 与 /MD)编出来的库混在一起用。凡是工程要长期维护,我都会建议自己编一遍,哪怕最后还是用预编译包,至少知道它怎么来的、哪个参数一变会出什么问题。
4.1 Configure 目标怎么选
1.1.1 的构建系统跟 1.0.2 完全不同,不需要先跑ms\do_ms.bat再nmake -f ms\ntdll.mak,直接Configure生成 Makefile 就行。目标名写错是最常见的失败原因,下面这几个是实际会用到的。
| 目标名 | 平台 | 位数 | 备注 |
|---|---|---|---|
linux-x86_64 | Linux | 64 | 默认开汇编优化,需要 Perl |
linux-x86_64-clang | Linux | 64 | 用 clang 而非 gcc |
VC-WIN32 | Windows | 32 | 产物名不带-x64 |
VC-WIN64A | Windows | 64 | 用的是 LLP64 模型 |
mingw64 | Windows | 64 | MinGW-w64 工具链 |
./config是个快捷脚本,会自动探测平台再调Configure,代价是没法精确指定目标。要控制产物形态,一律用./Configure加显式目标名。
4.2 Windows 下的 Perl + nmake 流程
Windows 编译需要 Perl 和 NASM,前者跑 Configure,后者编汇编优化的那部分。缺 NASM 时 Configure 不会报错,只是默默关掉汇编优化,性能会掉一截,所以编完建议看一眼输出里有没有asm相关的提示。
:: 必须在 "x64 Native Tools Command Prompt for VS" 里跑 :: 普通 cmd 里 cl 和 nmake 都不在 PATH 上 perl Configure VC-WIN64A ^ --prefix=C:\openssl-1.1.1c ^ --openssldir=C:\openssl-1.1.1c\ssl ^ no-tests nmake nmake test nmake install_sw--prefix是安装根目录,--openssldir是配置文件(openssl.cnf)和证书目录的落点,两者不要指到同一个地方,否则install_sw会把配置目录也覆盖一遍。no-tests跳过测试用例的构建,能省一大半时间,但第一次编的时候建议去掉,让nmake test真跑一遍。install_sw只装库和头文件,不装 man 手册,install会连文档一起装,在 Windows 上装文档意义不大。
4.3 Linux 下的 make 与安装布局
Linux 侧流程类似,差别在于make install_sw之后目录结构更规整,lib/pkgconfig下的.pc文件也会一并生成。
tar xzf openssl-1.1.1c.tar.gz && cd openssl-1.1.1c ./Configure linux-x86_64 \ --prefix=/opt/openssl11 \ --openssldir=/opt/openssl11/ssl \ shared \ no-tests make -j"$(nproc)" make test # 首次编译建议跑 make install_swshared决定生成.so还是只用.a,不加shared就只出静态库。make -j$(nproc)并行编译要小心内存,1.1.1 的汇编模块比较吃内存,机器内存小于 8G 时并行度别开满。装完之后/opt/openssl11/lib下应该有libcrypto.so.1.1、libssl.so.1.1、两个指向它们的.so符号链接,以及libcrypto.a、libssl.a。符号链接是给-lcrypto用的,libcrypto.so断了的话编译期会直接报cannot find -lcrypto,这个坑在打包时经常踩到,后面还会提。
4.4 裁剪与优化开关:no-shared、no-asm 到底该不该加
1.1.1 的 Configure 支持大量裁剪开关,用好了能把库体积压下去一半,用不好就是给自己埋雷。
| 开关 | 作用 | 什么时候加 |
|---|---|---|
no-shared | 只出静态库 | 要做单文件分发、不想带 so/dll |
no-asm | 关汇编优化 | 交叉编译或目标架构不支持时 |
no-tests | 不构建测试程序 | 只做交付、不跑回归 |
no-comp | 去掉压缩算法 | 服务端不需要压缩时 |
no-ssl3 | 去掉 SSLv3 | 基本都是默认关的,显式加更保险 |
--debug | 带调试符号 | 要在 gdb 里追进库内部时 |
no-shared这个选项最需要想清楚:静态库编出来能省掉运行时依赖,但一旦和别的三方库同时静态链接了不同版本的 openssl,符号冲突会非常难查。我在服务端场景一般用动态库,客户端分发才考虑静态链接,而且要配-fvisibility=hidden之类的手段控制符号导出。
5. 避坑排查:五类高频翻车
下面这五类问题几乎覆盖了这份包在实际使用中的绝大部分故障。每条我都按「现象 → 原因 → 解决」写,你可以直接拿去对照现场。
5.1 链接期报 LNK2019 或 undefined reference
现象是编译阶段没报错,链接时冒出几十条unresolved external symbol,符号名都是EVP_、SSL_、X509_开头。
原因通常有三个:库路径没加进去,编译器根本不知道去哪找.lib或.so;只链了libssl没链libcrypto,1.1.1 里这两个库是分开的,SSL_CTX_new在 libssl 里但它内部调用的实现大量在 libcrypto 里;或者是最阴的一种,链到了系统自带的 3.x 版本,3.x 里部分函数签名变了,符号名对不上。
排查第一件事是看链接器实际用了哪个路径的库,Linux 上gcc -Wl,-t能看到抽取过程,Windows 上dumpbin /dependents看最终 exe 依赖哪些 dll。确认路径对之后,把-lcrypto补上,注意顺序要排在-lssl右边。如果工程是 CMake 管的,用导入目标OpenSSL::SSL能自动处理顺序,别手写-l列表。
5.2 编译过了跑不起来:dll 与 so.1.1 找不到
现象是程序启动就退出,Windows 弹窗提示找不到libcrypto-1_1-x64.dll,Linux 上报error while loading shared libraries: libcrypto.so.1.1: cannot open shared object file。
原因是运行时库的解析机制跟链接期完全无关。Windows 按 exe 所在目录、系统目录、PATH 顺序找 dll;Linux 按rpath、LD_LIBRARY_PATH、/etc/ld.so.cache顺序找 so。链接期能找到,只能说明.lib或.so在编译机的搜索路径上,不能保证运行时也能找到。
Windows 上最稳的做法是把 dll 拷到 exe 同目录,CMake 里用POST_BUILD拷一下就行。Linux 上有两条路:编译时加-Wl,-rpath,/opt/openssl11/lib把搜索路径写进 ELF,或者部署时把库放到/usr/local/lib再ldconfig。我倾向前者,因为它是跟着二进制走的,换台机器不用重新配环境。改完用ldd ./demo | grep crypto确认解析到了正确路径,别看到有输出就以为对了,ldd会同时列出找不到的项,标着not found才是要处理的。
5.3 头文件与库版本错配:编译期正常、运行期崩
现象比前两种隐蔽得多:编译链接全过,跑起来在SSL_CTX_new或EVP_CIPHER_CTX_new里段错误,换台机器可能又正常,属于典型的随机崩。
原因是头文件来自一份包、库来自另一份包。头文件决定了结构体大小和函数调用约定,库里的实现按另一套内存布局去读写,指针一越界就崩。1.1.1 里结构体是不透明的,正常情况下不该有这个问题,但如果是从 1.1.1 头文件配 1.0.2 的库,或者配的是某个自己改过的分支,就会踩上。
解决办法是强制版本校验,把 2.3 节那段vercheck放进构建流程,让它在编译期就检查OPENSSL_VERSION_NUMBER,运行期再调OpenSSL_version打印实际加载的版本。两行输出不一致就说明包是混搭的,直接换掉,不要试图靠调参数绕过。这类问题没有后悔药,只能靠前置检查挡。
5.4 静态库链接进来一堆 LNK2005 或运行库冲突
现象是接手一个老工程,把 openssl 换成静态库之后,链接器报大量LNK2005: xxx already defined,或者LNK4098: defaultlib 'MSVCRT' conflicts with use of other libs。
原因是 MSVC 的运行库模型有/MT(静态链 CRT)和/MD(动态链 CRT)两套,openssl 静态库编的时候用了一套,你的工程用另一套,两套 CRT 里的全局符号重名,链接器就懵了。Linux 上对应的现象是multiple definition of或者运行时malloc/free跨库不匹配导致堆损坏。
确认方式用dumpbin /directives libcrypto_static.lib | findstr DEFAULTLIB,能看到它声明的默认运行库。解决方式是让 openssl 的编译参数跟主工程保持一致:工程用/MD,Configure 时就加no-static-crt之类的对等选项(1.1.1 的 Windows 构建默认跟随编译器设置,需要显式对齐)。实在对不齐,就换成动态库,把那部分内存管理交给同一份 CRT。
5.5 位数与架构混用
现象是链接器报module machine type 'x86' conflicts with target machine type 'x64',或者更隐蔽的,链接过了但运行时在读某个结构体字段时拿到垃圾值。
原因是 32 位包被塞进了 64 位工程,或者反过来。file命令和dumpbin /headers都能看出来,前者看 ELF 的class字段,后者看machine字段。这类问题在有历史包袱的团队里特别常见,因为压缩包名字往往只写openssl-1.1.1c.zip,不写位数,解压出来看着都是.lib就用了。
我的习惯是在包的根目录放一个ARCH.txt,写清楚构建目标名、位数、编译器版本、Configure 参数,团队里谁拿包都先看一眼。听着土,但确实把这类问题挡在了沟通环节,比事后查链接错误便宜得多。
6. 进阶:用符号表和 SONAME 判断这份库值不值得留
判断一份预编译包能不能长期用,光看版本串不够,得看它导出了什么、依赖了什么、SONAME 是什么。这三件事决定了它能不能被你的工程安全替换、能不能在目标机器上跑起来。
先看导出符号。1.1.1c 的 libcrypto 导出符号数量在几千这个量级,太少说明被裁剪过(比如加了no-deprecated),太多说明可能带了调试符号或者没做符号隐藏。
# 统计动态符号表里定义为函数的符号数量 nm -D --defined-only libcrypto.so.1.1 | awk '$2=="T"' | wc -l # 抽查几个关键符号在不在,缺了说明这份库被裁过 nm -D --defined-only libcrypto.so.1.1 | grep -E ' (EVP_EncryptInit_ex|OPENSSL_init_crypto|RAND_bytes)$' # 看 SONAME 和依赖 readelf -d libcrypto.so.1.1 | grep -E 'SONAME|NEEDED'nm -D看的是动态符号表,--defined-only过滤掉未定义引用,awk '$2=="T"'只留代码段里的函数符号。抽查符号这一步很关键,如果这份包是用no-deprecated编的,EVP_EncryptInit_ex这类 1.1.1 里已标记废弃的接口可能还在,但像DES_encrypt1这种 1.0.2 时代的就彻底没了,老代码一编就报未定义。
Windows 上对应的是dumpbin /exports libcrypto-1_1-x64.dll | findstr EVP_EncryptInit_ex,看得到序号和 RVA 就说明在。再补一句dumpbin /dependents,确认它除了系统 dll 之外没依赖别的东西,依赖里出现奇怪的三方库说明这份包不是干净编译出来的。
SONAME 那段最容易被忽视。libcrypto.so.1.1这个 SONAME 意味着它可以在 1.1.1 系列内部被替换,readelf -d输出的NEEDED则说明它自己依赖谁——正常的 1.1.1c 应该只依赖libc.so.6、libdl.so.2、libpthread.so.0这几个基础库,如果冒出libcrypto.so.3那就成环了,必然加载失败。
还有一件我吃过亏的事:Linux 包里那两个libcrypto.so符号链接。打包工具在 Windows 上压缩时经常把符号链接展开成普通文件副本,解压到 Linux 之后就变成两个独立文件,-lcrypto能找到但内容可能是旧版。判断方法是ls -l,正常应该是libcrypto.so -> libcrypto.so.1.1,如果是两个同样大小的普通文件,这包就得重新打。
从那以后我每次拿预编译包,都强制走一遍file、readelf -d、ls -l三条命令再接入,五分钟的事,能省掉后面半天查链接错误的时间。希望帮到你。
本文还有配套的精品资源,点击获取