Home > Software engineering >  Custom library compiles fine, but fails with undefined refences when linked against
Custom library compiles fine, but fails with undefined refences when linked against

Time:12-07

I'm currently building a library using cmake and crypto . While the library compiles just fine the code referencing it does not.

cmake for library:

add_library(MYCrypto Crypto.cpp)
target_link_libraries(MYCrypto libcrypto  .a)
set_target_properties(MYCrypto PROPERTIES PUBLIC_HEADER "includes/MYCrypto.hpp")
install(TARGETS File MYCrypto ARCHIVE DESTINATION ~/.local/dev-bin/bin PUBLIC_HEADER DESTINATION ~/.local/dev-bin/includes)

MYCrypto.hpp

#ifndef MYCrypto
#define MYCrypto
#include <string>
namespace MYCrypto
{
    std::string hashSha256(std::string);
    std::string hashMd5(std::string);
}
#endif

Crypto.cpp

#define CRYPTOPP_ENABLE_NAMESPACE_WEAK 1
#include <cryptopp/iterhash.h>
#include <cryptopp/cryptlib.h>
#include <cryptopp/sha.h>
#include <cryptopp/md5.h>
#include <sstream>
#include <iomanip>
#include "MYCrypto.hpp"

using namespace std;

template<typename T>
string encrypt(string data, T hasher)
{
    stringstream output;
    CryptoPP::byte* digest;

    hasher.Update((const CryptoPP::byte*)data.c_str(),data.size());
    digest = new CryptoPP::byte[hasher.DigestSize()];
    hasher.Final(digest);

    for (int i=0; i < hasher.DigestSize(); i  ) output << hex << setw(2) << setfill('0') << (int)digest[i];
    delete[] digest;

    return output.str();
}

string MYCrypto::hashSha256(string data)
{
    return encrypt(data, CryptoPP::SHA256());
}

string MYCrypto::hashMd5(string data)
{
    return encrypt(data, CryptoPP::Weak1::MD5());
}

CMake for consuming application

cmake_minimum_required(VERSION 3.0.0)
project(TEST VERSION 0.1.0)

include(CTest)

include_directories(~/.local/dev-bin/includes)
link_directories(~/.local/dev-bin/bin)

add_library(Archive Archive.cpp)
target_link_libraries(Archive MYCrypto)

Archive.hpp

#ifndef Archive
#define Archive
#include <string>
#include <unordered_set>
namespace Archive
{
   std::string getImportPath(std::string);
   bool fileExistsInDatabase(std::string, std::string);
}
#endif

archive.cpp

#include "MYCrypto.hpp"
#include "Archive.hpp"

using namespace std;

string Archive::getImportPath(string path)
{
    return MYCrypto::hashSha256(Path);
}

when i try to compile my second cmake progject i get errors like this

[build] /usr/bin/ld: /home/user/.local/dev-bin/bin/libMYCrypto.a(Crypto.cpp.o):(.data.rel.ro._ZTVN8CryptoPP5Weak13MD5E[_ZTVN8CryptoPP5Weak13MD5E] 0x38): undefined reference to `CryptoPP::IteratedHashBase<unsigned int, CryptoPP::HashTransformation>::Update(unsigned char const*, unsigned long)'

I don't understand how to fix this error. I linked statically so this second project should only know about the functions declared on the public header file and i should no longer require crypto .

How do i make this second project compile?

CodePudding user response:

Static linking is well defined only for applications not for the libraries.

There is "one definition rule" in the language and it applies to the executable as whole. If you link liba and libb libraries both providing foo (even it is the same and defined via transitional dependency) you most likely will get multiple definition warning from the linker - no symbol isolation is usually provided. There are platform-specific exceptions: DLLs on Windows require to fully resolve and link all dependencies and export only explicitly exported symbols but this is not the case e.g. for shared libraries on Linux, and static libraries are just an archive of object files and cannot link anything on all major platforms.

So, you link static libraries to the executable, not to the other static libraries.

Usually there are linker-specific ways to force merge the static libraries but again it will cause troubles is the library symbols get linked multiple times.

But if you have

target_link_libraries(MYCrypto libcrypto  .a)
...
target_link_libraries(Application MyCrypto)

in a single cmake file, it can see the dependencies and will link the dependecies automatically itself.

If you want to distribute the library separately and want to simplify its usage you can export it via cmake and if the executable is also built with cmake then the users can import your library with find_package that will link the requried dependencies.

  • Related