链接不同符号版本库失败:libdeezer依赖libcurl符号缺失问题
问题描述
我正在构建一个名为exampleprog(实际编译目标为NanoPlayer)的程序,该程序仅能依赖共享库libdeezer.so。libdeezer.so依赖libcurl等库,但构建时链接器无法找到libcurl的相关函数,make输出报错如下:
gcc -o NanoPlayer main.o -L../libdeezer/SDK/Bins/Platforms/Linux/x86_64 -Wl,-rpath=../libdeezer/SDK/Bins/Platforms/Linux/x86_64 -ldeezer -lcurl /usr/bin/ld: ../libdeezer/SDK/Bins/Platforms/Linux/x86_64/libdeezer.so: undefined reference to `curl_multi_assign@CURL_OPENSSL_3' [...] /usr/bin/ld: ../libdeezer/SDK/Bins/Platforms/Linux/x86_64/libdeezer.so: undefined reference to `curl_global_cleanup@CURL_OPENSSL_3' /usr/bin/ld: ../libdeezer/SDK/Bins/Platforms/Linux/x86_64/libdeezer.so: undefined reference to `curl_easy_recv@CURL_OPENSSL_3'
执行ldd libdeezer.so的输出为:
./libdeezer.so: /lib64/libcurl.so.4: no version information available (required by ./libdeezer.so) linux-vdso.so.1 (0x00007ffde5920000) libssl.so.1.0.0 => /lib64/libssl.so.1.0.0 (0x00007fba07993000) libcrypto.so.1.0.0 => /lib64/libcrypto.so.1.0.0 (0x00007fba077b4000) libcurl.so.4 => /lib64/libcurl.so.4 (0x00007fba07706000) [...]
使用的Makefile内容如下:
LIBDEEZER_DIR=../libdeezer/SDK LIBDEEZER_INC_DIR=$(LIBDEEZER_DIR)/Include LIBDEEZER_LIB_DIR=$(LIBDEEZER_DIR)/Bins/Platforms/Linux/x86_64 #Set tools and flags CC=gcc CFLAGS=-W -Wall -I$(LIBDEEZER_INC_DIR) -Wno-unused-parameter LDFLAGS=-L$(LIBDEEZER_LIB_DIR) -Wl,-rpath=$(LIBDEEZER_LIB_DIR) -ldeezer -lcurl EXEC=NanoPlayer all: $(EXEC) NanoPlayer: main.o $(CC) -o $@ $^ $(LDFLAGS) # Build every source files %.o: %.c $(CC) -o $@ -c $< $(CFLAGS) .PHONY: clean mrproper # Clean every temporary files clean: rm -rf *.o # Clean everything than can be generated mrproper: clean rm -rf $(EXEC)
libcurl的符号定义如下:
~ readelf -sW /lib64/libcurl.so.4.8.0 | grep -E "curl_multi_assign|memcmp" 95: 0000000000000000 0 FUNC GLOBAL DEFAULT UND memcmp@GLIBC_2.2.5 (3) 508: 000000000004cfa0 108 FUNC GLOBAL DEFAULT 14 curl_multi_assign
可见curl_multi_assign函数无符号版本,而memcmp等函数带有符号版本。已尝试用patchelf修改libcurl的soname,以及编译带--with-ssl选项的旧版本libcurl,但问题未解决。想问:能否修改libcurl的符号版本,或告知链接器使用无版本符号?
解决方案
方法1:让链接器忽略符号版本要求
直接通过链接器选项-Wl,--undefined-version告诉ld忽略未找到的符号版本,绑定到无版本符号。修改Makefile中的LDFLAGS:
LDFLAGS=-L$(LIBDEEZER_LIB_DIR) -Wl,-rpath=$(LIBDEEZER_LIB_DIR),--undefined-version -ldeezer -lcurl
这个选项会让链接器对所有未匹配到版本的符号,直接使用系统中无版本的对应符号,适配你当前libcurl的状态。
方法2:创建符号包装库(更稳妥)
如果方法1兼容性不佳,可手动创建小型包装库,将带版本的符号重定向到无版本符号:
- 创建
curl_wrap.c文件:
#define _GNU_SOURCE #include <dlfcn.h> // 重定向带版本符号到系统libcurl的无版本符号 void curl_global_cleanup(void) { static void (*func)(void) = NULL; if (!func) func = dlsym(RTLD_NEXT, "curl_global_cleanup"); func(); } CURLMcode curl_multi_assign(CURLM *multi_handle, curl_socket_t sockfd, void *sockptr) { static CURLMcode (*func)(CURLM*, curl_socket_t, void*) = NULL; if (!func) func = dlsym(RTLD_NEXT, "curl_multi_assign"); return func(multi_handle, sockfd, sockptr); } CURLcode curl_easy_recv(CURL *curl, void *buffer, size_t buflen, size_t *n) { static CURLcode (*func)(CURL*, void*, size_t, size_t*) = NULL; if (!func) func = dlsym(RTLD_NEXT, "curl_easy_recv"); return func(curl, buffer, buflen, n); } // 按需添加其他报错的带版本符号
- 编译为共享库:
gcc -fPIC -shared -o libcurl_wrap.so curl_wrap.c -ldl
- 修改Makefile的
LDFLAGS,优先链接包装库:
LDFLAGS=-L$(LIBDEEZER_LIB_DIR) -Wl,-rpath=$(LIBDEEZER_LIB_DIR) -L. -lcurl_wrap -ldeezer -lcurl
这样libdeezer.so会先找到包装库中的带版本符号,再由包装库调用系统libcurl的无版本符号。
方法3:替换匹配版本的libcurl
若上述方法都不满足需求,需找到或编译带有CURL_OPENSSL_3版本符号的libcurl:
- 用
objdump -x ../libdeezer/SDK/Bins/Platforms/Linux/x86_64/libdeezer.so | grep CURL_OPENSSL查看libdeezer.so要求的具体版本。 - 下载对应版本的
libcurl源码,编译时确保使用与libdeezer.so依赖的libssl.so.1.0.0匹配的OpenSSL版本,安装到本地目录后,在Makefile中指定该目录的-L和-rpath路径。
内容的提问来源于stack exchange,提问作者Gwendal
相关产品推荐
相关产品推荐

