c++postgresqlvcpkglibpqlibpqxx

Undefined reference when linking libpq in C++ project


I am trying to use libpqxx (and implicitly libpq) in a C++ project.
I use vcpkg as a submodule to get my libs by setting CMAKE_TOOLCHAIN_FILE.

When I try to build, I get the following errors:

/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'
...

I have broken down the linking command here:

/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 

My CMakeLists.txt looks like this:

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)

I looked at the libpq.a from vcpkg installed dir with nm and I can see that for example, pg_md5_encrypt appears as undefined:

U pg_md5_encrypt

I don't get it, where are these missing functions like pg_md5_encrypt?
Is there another libpq... that I need to link or maybe a different version?


Solution

  • After some more digging around and testing I found that there are 2 more postgres static libraries that I need to link:

    Additionally, the link order is also important and altough I was expecting to have to pass libs that are needed before the libs that need them, this is not the case here and it feels backwards.
    Here is a working linker command:

    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
    

    In order to link the extra 2 static libs and also to achieve the order above, I updated the CMakeLists.txt like so:

    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)
    

    This translates into the following linker command:

    /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
    

    We can see that libpq.a appears twice in the link arguments:

    My conclusion is that this is a libpqxx bug, which should also link those 2 static libs with target_link_libraries(PUBLIC...) in the only target that it exposes (libpqxx::pqxx)