Search code examples
c++crypto++

Undefined reference for Crypto++ though it is linked and works in other projects


So I am trying to make a wrapper function for generating a hash using Cryptop++ I created this test function:

#include <cryptopp/sha.h>
#include <cryptopp/hex.h>
#include <cryptopp/cryptlib.h>

#include <vector>
#include <cstdint>

#include <string>

#include <iostream>

void test2()
{
    CryptoPP::SHA1 hash;
    CryptoPP::byte digest[CryptoPP::SHA1::DIGESTSIZE];

    std::vector<uint8_t> v;
    for (uint32_t i = 0; i < 1000; ++i)
    {
        v.push_back(i % 9);
    }

    hash.CalculateDigest(digest, v.data(), v.size());

    CryptoPP::HexEncoder encoder;

    std::string output;

    encoder.Attach(new CryptoPP::StringSink(output));
    encoder.Put(digest, sizeof(digest));
    encoder.MessageEnd();

    std::cout << output << std::endl;
}

And compiled it with the following clang++ string: clang++ main2.cpp -lcryptopp. However, when I use it in my project where the function is defined like this:

template<typename Hash>
std::string get_hash(std::vector<uint8_t> data)
{
Hash hash;

// Intialise a byte "array" with space enough for the result
CryptoPP::byte digest[Hash::DIGESTSIZE];

// Create hash for the data
hash.CalculateDigest(digest, data.data(), data.size());

CryptoPP::HexEncoder encoder;
// result will hold the hex representation of the hash
std::string result;

// Tell the Hex encoder that result is the destination
// for its operations
encoder.Attach(new CryptoPP::StringSink(result));
encoder.Put(digest, sizeof(digest));
// As we will not put more in the message we end it
encoder.MessageEnd();

return result;
}

And call it like this: hash::get_hash<CryptoPP::SHA1>(pair.pivot); with following compiler command: clang++ -std=c++17 -Wall -Werror -Wextra -pthread -pthread -lpqxx -lpq -lcryptopp examples/sql_index_example/sql_index_example.cpp.1.o -o/home/tools/git/alexandria/build/examples/sql_index_example/sql_index_example -Wl-Bstatic -L. -lalexandria -Wl-Bdynamic

I get a ton of undefined references to Crypto++, such as:

examples/sql_index_example/sql_index_example.cpp.1.o: In function `alexandria::data::hash::GetHash[abi:cxx11](std::vector<unsigned char, std::allocator<unsigned char> >)':
sql_index_example.cpp:(.text+0x197): undefined reference to `CryptoPP::StringSinkTemplate<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::StringSinkTemplate(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&)'

and I am quite lost as to what actually happens when the simple test works. So hope someone can help.

EDIT: Already tried: Undefined reference to symbol, even though the library is linked


Solution

  • clang++ -std=c++17 -Wall -Werror -Wextra -pthread -pthread -lpqxx -lpq -lcryptopp \
    examples/sql_index_example/sql_index_example.cpp.1.o -o/home/tools/git/alexandria/
    build/examples/sql_index_example/sql_index_example -Wl-Bstatic -L. -lalexandria -Wl-Bdynamic
    

    And:

    examples/sql_index_example/sql_index_example.cpp.1.o: In function `alexandria::data::hash::
    GetHash[abi:cxx11](std::vector<unsigned char, std::allocator<unsigned char> >)':
    sql_index_example.cpp:(.text+0x197): undefined reference to ...
    

    To restate what @HolyBlackCat said, -lcryptopp needs to follow sql_index_example.cpp.o because the sql object file needs stuff from the Crypto++ archive. So the compile and link command should probably look something like this:

    clang++ -std=c++17 -Wall -Werror -Wextra -pthread -pthread \
      examples/sql_index_example/sql_index_example.cpp.1.o \
      -o /home/tools/git/.../sql_index_example \
      -lpqxx -lpq -lcryptopp -Wl-Bstatic -L. -lalexandria -Wl-Bdynamic
    

    I would even recommend static linking in this case to avoid (1) the stupid Linux path problems; and (2) the path planting/injection games. So maybe smething like:

    clang++ -std=c++17 -Wall -Werror -Wextra -pthread -pthread \
      examples/sql_index_example/sql_index_example.cpp.1.o \
      /usr/local/lib/libcryptopp.a \
      -o /home/tools/git/.../sql_index_example \
      -lpqxx -lpq -Wl-Bstatic -L. -lalexandria -Wl-Bdynamic
    

    Also see Why does the order in which libraries are linked sometimes cause errors in GCC?


    You may want to change this signature to take a const reference:

    std::string get_hash(const std::vector<uint8_t>& data);
    

    There's no need for deep copies here. A reference or pointer will do. References cannot be NULL so they are a little easier to work with.

    Also see When to use const and const reference in function args? How to pass objects to functions in C++? has more modern information, including C++11.


    Regarding this:

    HexEncoder encoder;
    // result will hold the hex representation of the hash
    std::string result;
    // Tell the Hex encoder that result is the destination
    // for its operations
    encoder.Attach(new StringSink(result));
    

    You can condense it a bit to:

    std::string result;
    HexEncoder encoder(new StringSink(result));
    

    Since you are printing to std::cout you could even:

    std::string result;
    HexEncoder encoder(new FileSink(std::cout));
    

    And if you want to get really slick, you can both print it and return it:

    ChannelSwitch channels;
    channels.AddRoute(new FileSink(std::cout));
    channels.AddRoute(new StringSink(result));
    
    HexEncoder encoder(new Redirector(channels));
    

    Now, when you insert data into encoder, it will be hex encoded and then sent to both std::cout and the std::string.


    And regarding:

    std::vector<uint8_t> data;
    ...
    
    HexEncoder encoder;
    encoder.Attach(new CryptoPP::StringSink(result));
    encoder.Put(digest, sizeof(digest));
    // As we will not put more in the message we end it
    encoder.MessageEnd();
    

    Crypto++ 8.0 will have a VectorSource and VectorSink. You will be able to:

    VectorSource(data, true, new HashFilter(hash, 
        new HexEncoder(new StringSink(result))));
    return result;
    

    Also see Pull Request 730.