在C++项目中链接libpq时未定义的引用

Undefined reference when linking libpq in C++ project

本文关键字:未定义 引用 libpq 链接 C++ 项目      更新时间:2024-04-28

我正试图在C++项目中使用libpqxx(以及隐含的libpq(
我使用vcpkg作为submodule,通过设置CMAKE_TOOLCHAIN_FILE来获取我的库。

当我尝试构建时,我会得到以下错误:

/usr/bin/ld: /home/user/work/cxx-playground/third-party/vcpkg/installed/x64-linux/lib/libpq.a(fe-auth.o): in function `pg_fe_sendauth':
fe-auth.c:(.text+0x4fe): undefined reference to `pg_md5_encrypt'
/usr/bin/ld: fe-auth.c:(.text+0x51b): undefined reference to `pg_md5_encrypt'
/usr/bin/ld: /home/user/work/cxx-playground/third-party/vcpkg/installed/x64-linux/lib/libpq.a(fe-auth.o): in function `pg_fe_getauthname':
fe-auth.c:(.text+0x838): undefined reference to `pqGetpwuid'
/usr/bin/ld: fe-auth.c:(.text+0x8a4): undefined reference to `pg_strerror_r'
/usr/bin/ld: /home/user/work/cxx-playground/third-party/vcpkg/installed/x64-linux/lib/libpq.a(fe-auth.o): in function `PQencryptPassword':
fe-auth.c:(.text+0x936): undefined reference to `pg_md5_encrypt'
/usr/bin/ld: /home/user/work/cxx-playground/third-party/vcpkg/installed/x64-linux/lib/libpq.a(fe-auth.o): in function `PQencryptPasswordConn':
fe-auth.c:(.text+0x9ed): undefined reference to `pg_md5_encrypt'
/usr/bin/ld: /home/user/work/cxx-playground/third-party/vcpkg/installed/x64-linux/lib/libpq.a(fe-auth-scram.o): in function `build_client_final_message':
fe-auth-scram.c:(.text+0xdb): undefined reference to `scram_SaltedPassword'
/usr/bin/ld: fe-auth-scram.c:(.text+0xee): undefined reference to `scram_ClientKey'
/usr/bin/ld: fe-auth-scram.c:(.text+0xfe): undefined reference to `scram_H'
/usr/bin/ld: fe-auth-scram.c:(.text+0x113): undefined reference to `scram_HMAC_init'
/usr/bin/ld: fe-auth-scram.c:(.text+0x12d): undefined reference to `scram_HMAC_update'
/usr/bin/ld: fe-auth-scram.c:(.text+0x141): undefined reference to `scram_HMAC_update'
/usr/bin/ld: fe-auth-scram.c:(.text+0x15b): undefined reference to `scram_HMAC_update'
/usr/bin/ld: fe-auth-scram.c:(.text+0x16f): undefined reference to `scram_HMAC_update'
/usr/bin/ld: fe-auth-scram.c:(.text+0x185): undefined reference to `scram_HMAC_update'
/usr/bin/ld: fe-auth-scram.c:(.text+0x195): undefined reference to `scram_HMAC_final'
/usr/bin/ld: fe-auth-scram.c:(.text+0x1e4): undefined reference to `pg_b64_enc_len'
/usr/bin/ld: fe-auth-scram.c:(.text+0x213): undefined reference to `pg_b64_encode'
/usr/bin/ld: fe-auth-scram.c:(.text+0x344): undefined reference to `pg_b64_enc_len'
/usr/bin/ld: fe-auth-scram.c:(.text+0x368): undefined reference to `pg_b64_encode'
...

我在这里分解了链接命令:

/usr/bin/cmake -E cmake_link_script CMakeFiles/hello-pq.dir/link.txt --verbose=1
/usr/bin/c++     CMakeFiles/hello-pq.dir/CMakeFiles/3.16.3/CompilerIdCXX/CMakeCXXCompilerId.cpp.o CMakeFiles/hello-pq.dir/main.cpp.o  
-o ../bin/hello-pq  
/home/user/work/cxx-playground/third-party/vcpkg/installed/x64-linux/debug/lib/libssl.a 
/home/user/work/cxx-playground/third-party/vcpkg/installed/x64-linux/debug/lib/libcrypto.a 
/home/user/work/cxx-playground/third-party/vcpkg/installed/x64-linux/debug/lib/libpqxx-7.3.a 
-lpthread 
/home/user/work/cxx-playground/third-party/vcpkg/installed/x64-linux/lib/libpq.a -ldl 

我的CMakeLists.txt看起来像这样:

cmake_minimum_required(VERSION 3.16)
set(CMAKE_TOOLCHAIN_FILE ${CMAKE_CURRENT_SOURCE_DIR}/../../third-party/vcpkg/scripts/buildsystems/vcpkg.cmake
CACHE STRING "Vcpkg toolchain file")
project(hello-pq)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/bin)
find_package(OpenSSL REQUIRED) 
find_package(libpqxx CONFIG REQUIRED)
file(GLOB_RECURSE PROJECT_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
add_executable(${PROJECT_NAME} ${PROJECT_SOURCES})
target_link_libraries(${PROJECT_NAME}
PRIVATE OpenSSL::SSL 
PRIVATE OpenSSL::Crypto
PRIVATE libpqxx::pqxx)

我查看了vcpkg中安装的带有nm目录中的libpq.a,可以看到,例如,pg_md5_encrypt显示为未定义

U pg_md5_encrypt

我不明白,像pg_md5_encrypt这样缺失的函数在哪里
是否有其他libpq...需要链接,或者可能是不同的版本?

经过更多的挖掘和测试,我发现还有两个postgres静态库需要链接:

  • libpqcommon.a
  • libpgport.a

此外,链接顺序也很重要,尽管我原以为必须在libs that need them之前通过libs that are needed,但这里的情况并非如此,感觉有些倒退
这是一个有效的链接器命令:

usr/bin/c++ CMakeFiles/hello-pq.dir/src/main.cpp.o 
-o ../bin/hello-pq 
/home/user/work/cxx-playground/third-party/vcpkg/installed/x64-linux/debug/lib/libpqxx-7.3.a 
/home/user/work/cxx-playground/third-party/vcpkg/installed/x64-linux/lib/libpq.a 
/home/user/work/cxx-playground/third-party/vcpkg/installed/x64-linux/lib/libpgcommon.a 
/home/user/work/cxx-playground/third-party/vcpkg/installed/x64-linux/lib/libpgport.a 
/home/user/work/cxx-playground/third-party/vcpkg/installed/x64-linux/debug/lib/libssl.a 
/home/user/work/cxx-playground/third-party/vcpkg/installed/x64-linux/debug/lib/libcrypto.a 
-lpthread 
-ldl

为了链接额外的2个静态库,也为了实现上面的顺序,我更新了CMakeLists.txt,如下所示:

cmake_minimum_required(VERSION 3.16)
set(CMAKE_TOOLCHAIN_FILE ${CMAKE_CURRENT_SOURCE_DIR}/../../third-party/vcpkg/scripts/buildsystems/vcpkg.cmake
CACHE STRING "Vcpkg toolchain file")
project(hello-pq)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/bin)
find_package(OpenSSL REQUIRED) 
#find_package(PostgreSQL REQUIRED) # Not needed (it will duplicate the libs)
find_package(libpqxx CONFIG REQUIRED)
file(GLOB_RECURSE PROJECT_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
add_executable(${PROJECT_NAME} ${PROJECT_SOURCES})
target_link_libraries(${PROJECT_NAME}
PRIVATE libpqxx::pqxx
PRIVATE PostgreSQL::PostgreSQL
PRIVATE OpenSSL::SSL 
PRIVATE OpenSSL::Crypto)

这转换为以下链接器命令:

/usr/bin/c++ 
CMakeFiles/hello-pq.dir/src/main.cpp.o 
-o ../bin/hello-pq 
/home/user/work/cxx-playground/third-party/vcpkg/installed/x64-linux/debug/lib/libpqxx-7.3.a 
/home/user/work/cxx-playground/third-party/vcpkg/installed/x64-linux/debug/lib/libpq.a 
/home/user/work/cxx-playground/third-party/vcpkg/installed/x64-linux/debug/lib/libssl.a 
/home/user/work/cxx-playground/third-party/vcpkg/installed/x64-linux/debug/lib/libcrypto.a 
/home/user/work/cxx-playground/third-party/vcpkg/installed/x64-linux/lib/libpq.a 
/home/user/work/cxx-playground/third-party/vcpkg/installed/x64-linux/lib/libpgport.a 
/home/user/work/cxx-playground/third-party/vcpkg/installed/x64-linux/lib/libpgcommon.a 
-ldl 
-lpthread

我们可以看到libpq.a在链接参数中出现了两次:

由于libpqxx::pqxx目标,
  • 一次(我认为(
  • 由于PostgreSQL::PostgreSQL目标其中还包含需要链接的2个丢失的静态库(libpgport.alibpgcommon.a(

我的结论是,这是一个libpqxx错误,它还应该将这两个静态库与它公开的唯一目标(libpqxx::pqxx(中的target_link_libraries(PUBLIC...)链接起来