c++cmakeopensslshared-librariesvcpkg

How to force a CMake target to use a specific OpenSSL version


I have a CMake project in Linux where I build several shared libraries and some of them need OpenSSL latest version. I use Microsoft vcpkg for my toolchain and I had to install OpenSSL 3.2.0 23 Nov 2023 using ./vcpkg install openssl:x64-linux.

One of the libraries, myrrlib, uses a proprietary library for which libssl.a and libcrypto.a are provided by the vendor in the library source folder. Their version appears to be OpenSSL 1.1.1m 14 Dec 2021 when I run strings libcrypto.a | grep "OpenSSL".

In the root folder, my CMakeLists.txt has find_package(OpenSSL REQUIRED), which will result in vcpkg finding the following file for libcrypto.a:

/root/programs/vcpkg/installed/x64-linux/debug/lib/libcrypto.a

My question is how can I force using a OpenSSL 1.1.1 for myrrlib?

cmake_minimum_required(VERSION 3.26)

set(RR_LIB_DIR "external/pakages/rr/lib")   
set(RR_LIB_FILES
        vendorlib1
        vendorlib2
        vendorlib3
        ssl    # libssl.a is provided by the RR library (it is not installed by vcpkg)
        crypto # libcrypto.a is provided by the RR library (it is not installed by vcpkg)
)

add_library(myrrlib SHARED)
set_property(TARGET myrrlib PROPERTY CXX_STANDARD 17)
target_sources(myrrlib PRIVATE "src/myrrlib.cpp" "src/myrrlib.h")
target_include_directories(myrrlib PUBLIC ${RR_INCLUDE_DIR})
target_link_directories(myrrlib PUBLIC ${RR_LIB_DIR})
target_link_libraries(myrrlib PUBLIC ${RR_LIB_FILES})

When I build the shard library myrrlib I get the following link errors:

/opt/rh/devtoolset-11/root/usr/libexec/gcc/x86_64-redhat-linux/11/ld: warning: alignment 8 of symbol `bio_lookup_lock' in /root/programs/vcpkg/installed/x64-linux/debug/lib/libcrypto.a(libcrypto-lib-bio_addr.o) is smaller than 32 in /mnt/c/Dev/myproject/external/packages/rr/lib/libcrypto.a(b_addr.o)
/opt/rh/devtoolset-11/root/usr/libexec/gcc/x86_64-redhat-linux/11/ld: /root/programs/vcpkg/installed/x64-linux/debug/lib/libcrypto.a(libcrypto-lib-bio_addr.o): in function `BIO_ADDR_new':
/root/programs/vcpkg/buildtrees/openssl/x64-linux-dbg/../src/nssl-3.2.0-5a1db6e780.clean/crypto/bio/bio_addr.c:53: multiple definition of `BIO_ADDR_new'; /mnt/c/Dev/myproject/external/packages/rr/lib/libcrypto.a(b_addr.o):b_addr.c:(.text+0x0): first defined here
/opt/rh/devtoolset-11/root/usr/libexec/gcc/x86_64-redhat-linux/11/ld: /root/programs/vcpkg/installed/x64-linux/debug/lib/libcrypto.a(libcrypto-lib-bio_addr.o): in function `BIO_ADDR_free':
/root/programs/vcpkg/buildtrees/openssl/x64-linux-dbg/../src/nssl-3.2.0-5a1db6e780.clean/crypto/bio/bio_addr.c:64: multiple definition of `BIO_ADDR_free'; /mnt/c/Dev/myproject/external/packages/rr/lib/libcrypto.a(b_addr.o):b_addr.c:(.text+0x50): first defined here
/opt/rh/devtoolset-11/root/usr/libexec/gcc/x86_64-redhat-linux/11/ld: /root/programs/vcpkg/installed/x64-linux/debug/lib/libcrypto.a(libcrypto-lib-bio_addr.o): in function `BIO_ADDR_clear':
/root/programs/vcpkg/buildtrees/openssl/x64-linux-dbg/../src/nssl-3.2.0-5a1db6e780.clean/crypto/bio/bio_addr.c:96: multiple definition of `BIO_ADDR_clear'; /mnt/c/Dev/myproject/external/packages/rr/lib/libcrypto.a(b_addr.o):b_addr.c:(.text+0x70): first defined here
/opt/rh/devtoolset-11/root/usr/libexec/gcc/x86_64-redhat-linux/11/ld: /root/programs/vcpkg/installed/x64-linux/debug/lib/libcrypto.a(libcrypto-lib-bio_addr.o): in function `BIO_ADDR_make':
/root/programs/vcpkg/buildtrees/openssl/x64-linux-dbg/../src/nssl-3.2.0-5a1db6e780.clean/crypto/bio/bio_addr.c:106: multiple definition of `BIO_ADDR_make'; /mnt/c/Dev/myproject/external/packages/rr/lib/libcrypto.a(b_addr.o):b_addr.c:(.text+0x110): first defined here

...
many more
...

/root/programs/vcpkg/buildtrees/openssl/x64-linux-dbg/../src/nssl-3.2.0-5a1db6e780.clean/crypto/engine/eng_lib.c:280: multiple definition of `ENGINE_get_finish_function'; /mnt/c/Dev/myproject/external/packages/rr/lib/libcrypto.a(eng_lib.o):eng_lib.c:(.text+0x530): first defined here
/opt/rh/devtoolset-11/root/usr/libexec/gcc/x86_64-redhat-linux/11/ld: /root/programs/vcpkg/installed/x64-linux/debug/lib/libcrypto.a(libcrypto-lib-eng_lib.o): in function `ENGINE_get_ctrl_function':
/root/programs/vcpkg/buildtrees/openssl/x64-linux-dbg/../src/nssl-3.2.0-5a1db6e780.clean/crypto/engine/eng_lib.c:285: multiple definition of `ENGINE_get_ctrl_function'; /mnt/c/Dev/myproject/external/packages/rr/lib/libcrypto.a(eng_lib.o):eng_lib.c:(.text+0x540): first defined here
/opt/rh/devtoolset-11/root/usr/libexec/gcc/x86_64-redhat-linux/11/ld: /root/programs/vcpkg/installed/x64-linux/debug/lib/libcrypto.a(libcrypto-lib-eng_lib.o): in function `ENGINE_get_flags':
/root/programs/vcpkg/buildtrees/openssl/x64-linux-dbg/../src/nssl-3.2.0-5a1db6e780.clean/crypto/engine/eng_lib.c:290: multiple definition of `ENGINE_get_flags'; /mnt/c/Dev/myproject/external/packages/rr/lib/libcrypto.a(eng_lib.o):eng_lib.c:(.text+0x550): first defined here
/opt/rh/devtoolset-11/root/usr/libexec/gcc/x86_64-redhat-linux/11/ld: /root/programs/vcpkg/installed/x64-linux/debug/lib/libcrypto.a(libcrypto-lib-eng_lib.o): in function `ENGINE_get_cmd_defns':
/root/programs/vcpkg/buildtrees/openssl/x64-linux-dbg/../src/nssl-3.2.0-5a1db6e780.clean/crypto/engine/eng_lib.c:295: multiple definition of `ENGINE_get_cmd_defns'; /mnt/c/Dev/myproject/external/packages/rr/lib/libcrypto.a(eng_lib.o):eng_lib.c:(.text+0x560): first defined here
/opt/rh/devtoolset-11/root/usr/libexec/gcc/x86_64-redhat-linux/11/ld: /root/programs/vcpkg/installed/x64-linux/debug/lib/libcrypto.a(libcrypto-lib-eng_lib.o): in function `ENGINE_get_static_state':
/root/programs/vcpkg/buildtrees/openssl/x64-linux-dbg/../src/nssl-3.2.0-5a1db6e780.clean/crypto/engine/eng_lib.c:307: multiple definition of `ENGINE_get_static_state'; /mnt/c/Dev/myproject/external/packages/rr/lib/libcrypto.a(eng_lib.o):eng_lib.c:(.text+0x570): first defined here
collect2: error: ld returned 1 exit status
make[3]: *** [CMakeFiles/myrrlib.dir/build.make:119: lib/libmyrrlib.so] Error 1
make[2]: *** [CMakeFiles/Makefile2:520: CMakeFiles/myrrlib.dir/all] Error 2
make[1]: *** [CMakeFiles/Makefile2:527: CMakeFiles/myrrlib.dir/rule] Error 2
make: *** [Makefile:283: myrrlib] Error 2

If I build it as static, add_library(myrrlib STATIC), I can successfully build the library with no issues. I don't know where the problem is when I build it as a shared library.

Could someone kindly let me know how this can be solved?


Solution

  • TL/DR:
    First of all changing to STATIC didn't solve the problem, but it just delayed it in time to the final linkage.
    Solution is to use only one version of OpenSSL and in your case it looks like 1.1.1m provided by the vendor would be easier(I think), so use cmake -DOPENSSL_ROOT_DIR=/path/to/vendor/openssl or try using find_package (<package> PATHS paths... NO_DEFAULT_PATH)

    To understand it lets get back to the basic how compile process looks like in this case I will explain in GCC.

    When we have some C++ file a.cpp first is to generate object file a.o it's just an output of each Translation Unit(simply saying one *.o file per .cpp file).

    Next step depends on what we want to create:

    Shared Library and Executable are almost the same (Executable needs to have main()) but Static Library is much different because especially in this case it's not linking everything and not verifying all the connections between function. So this in your case using Static Library not showing that some functions are linked two because there is not linking at all. Why I said it just delayed it in time to the final linkage? At the end you will need to create Executable or Shared Library and errors of duplicates will reappear.

    The demonstrate the issue we need 2 static libs(let say a and b) that will simulate two difference version of OpenSSL, next library that will be our myrrlib from the question and to show that it's only delayed in time some executable (or dynamic lib as in this case they behave the same).

    Let create a.hpp

    #pragma once
    
    int add(int, int);
    int mul(int, int);
    

    a.cpp

    #include "a.hpp"
    
    int add(int a, int b)
    {
        return a + b;
    }
    
    int mul(int a, int b)
    {
        return a * b;
    }
    

    b.hpp

    #pragma once
    
    int add(int, int);
    int sub(int, int);
    

    b.cpp

    #include "a.hpp"
    
    int add(int a, int b)
    {
        return a + b;
    }
    
    int sub(int a, int b)
    {
        return a - b;
    }
    

    also myrrlib.cpp

    #include <cstdio>
    
    #include "a.hpp"
    #include "b.hpp"
    
    void func()
    {
      // some usage of all functions
      printf("add %i!\n", add(2, 5));
      printf("sub %i!\n", sub(2, 5));
      printf("mul %i!\n", mul(2, 5));
    }
    

    and main.cpp

    extern void func();
    
    int main()
    {
      func();
    }
    

    and CMakeLists.txt

    cmake_minimum_required(VERSION 3.27)
    project(issue)
    
    add_library(a STATIC a.cpp)
    add_library(b STATIC b.cpp)
    
    add_library(myrrlib STATIC myrrlib.cpp)
    target_link_libraries(myrrlib b a)
    

    To me able to reproduce this error we need library a and b to have at least one function in common and also one unique per library, so add is duplicated but mul and sub unique per lib(same happen in the example some function are the same in both versions of OpenSSL and some have to differ).

    Let build it with

    cmake -S . -B build
    cmake --build build --clean-first
    

    You should see no issues and [100%] Built target myrrlib but when we change myrrlib to SHARED we will get exactly the same error for add function.

    /usr/lib/gcc/x86_64-alpine-linux-musl/13.2.1/../../../../x86_64-alpine-linux-musl/bin/ld: liba.a(a.cpp.o): in function `add(int, int)':
    a.cpp:(.text+0x0): multiple definition of `add(int, int)'; libb.a(b.cpp.o):b.cpp:(.text+0x0): first defined here
    collect2: error: ld returned 1 exit status
    gmake[2]: *** [CMakeFiles/myrrlib.dir/build.make:99: libmyrrlib.so] Error 1
    gmake[1]: *** [CMakeFiles/Makefile2:140: CMakeFiles/myrrlib.dir/all] Error 2
    gmake: *** [Makefile:91: all] Error 2
    

    To show that error it is only delayed in time, let change it back to STATIC and add executable

    cmake_minimum_required(VERSION 3.27)
    project(issue)
    
    add_library(a STATIC a.cpp)
    add_library(b STATIC b.cpp)
    
    add_library(myrrlib STATIC myrrlib.cpp)
    target_link_libraries(myrrlib b a)
    
    add_executable(final main.cpp)
    target_link_libraries(final myrrlib)
    

    When we compile it we will get

    [ 75%] Built target myrrlib
    [ 87%] Building CXX object CMakeFiles/final.dir/main.cpp.o
    [100%] Linking CXX executable final
    /usr/lib/gcc/x86_64-alpine-linux-musl/13.2.1/../../../../x86_64-alpine-linux-musl/bin/ld: liba.a(a.cpp.o): in function `add(int, int)':
    a.cpp:(.text+0x0): multiple definition of `add(int, int)'; libb.a(b.cpp.o):b.cpp:(.text+0x0): first defined here
    collect2: error: ld returned 1 exit status
    gmake[2]: *** [CMakeFiles/final.dir/build.make:100: final] Error 1
    gmake[1]: *** [CMakeFiles/Makefile2:170: CMakeFiles/final.dir/all] Error 2
    gmake: *** [Makefile:91: all] Error 2
    

    myrrlib compiled without errors but everything failed when creating final executable.

    Finally to answer you questions
    My question is how can I force using a OpenSSL 1.1.1 for myrrlib?
    You have to use only one version of OpenSSL and probably provided by the vendor would be the best for you, so instead of using vcpkg to install it use cmake -DOPENSSL_ROOT_DIR=/path/to/vendor/openssl or try using find_package (<package> PATHS paths... NO_DEFAULT_PATH)

    If I build it as static, add_library(myrrlib STATIC),
    I can successfully build the library with no issues.
    I don't know where the problem is when I build it as a shared library.
    

    As describe issue is not resolved but just delayed in time