0

I have this code for my C extension in a seal_diff_cpp.cpp file:

extern "C" { // C Headers must be inside exter "C" { } block.
#include <postgres.h>
#include <utils/rel.h>
#include <fmgr.h>
#include <utils/array.h>
#include <utils/builtins.h>
#include <catalog/pg_type.h>
#include <stdlib.h>
#include <stdint.h>

PG_MODULE_MAGIC;
}

// CPP Header must be outside extern "C" { } block.
#include <string>
#include <vector>
#include <iostream>
#include <fstream>
#include <sstream>
#include <iterator> // For the ostream_iterator

// External projects c++ libraries compiled and linked on running 'make'.
#include <seal/seal.h>
#include <cppcodec/base64_rfc4648.hpp>

// CPP function can be declared and used inside or outside extern "C" { } block.
std::stringstream dec(std::string st){
        // Decode the base64 string into a stringstream 
        auto decodeBase64 = cppcodec::base64_rfc4648::decode(st);
        std::stringstream decodeBase64SS;
        std::move(decodeBase64.begin(), decodeBase64.end(), std::ostream_iterator<unsigned char>(decodeBase64SS));

        return decodeBase64SS;
}

std::string enc(std::string st){

        // Create a vector to hold the raw data
        std::vector<uint8_t> encodeStream;

        // Push all the characters from the raw data string into the vector
        for (auto &ch : st){
                encodeStream.push_back((unsigned char&&)(ch));
        }

        // Encode the vector as base64 string
        std::string encodeBase64 = cppcodec::base64_rfc4648::encode(encodeStream);
        encodeStream.clear();
        return encodeBase64;

}


std::string seal_diff_operation(std::string decodedLocalEncParamTmp, std::string decodedLocalTmp, std::string decodedLocalTmp){
        std::stringstream decodedLocalEncParam;
        decodedLocalEncParam.str(decodedLocalEncParamTmp);
        std::stringstream decodedLocalT1;
        decodedLocalT1.str(decodedLocalTmp);
        std::stringstream decodedLocalT2;
        decodedLocalT2.str(decodedLocalTmp);

        // Execute seal library operations
        // Load the ecryption parameters
        seal::EncryptionParameters IntegerEncryptorParms;
        IntegerEncryptorParms.load(decodedLocalEncParam);
        // Set Context and evaluator objects
        seal::SEALContext context(IntegerEncryptorParms);
        seal::Evaluator evaluator(context);
        // Set the Encoder parameters
        seal::IntegerEncoder encoder(context.plain_modulus());

        // Create Ciphertexts and load Chipertext information into them
        seal::Ciphertext number1Encoded;
        seal::Ciphertext number2Encoded;
        seal::Ciphertext diffEncodedResult;
        number1Encoded.load(decodedLocalT1);
        number2Encoded.load(decodedLocalT2);

        // Do the diff operation on the Ciphertexts and prepare the result for output
        evaluator.sub(number1Encoded, number2Encoded, diffEncodedResult);
        std::stringstream encResult;
        diffEncodedResult.save(encResult);

        std::string output = enc(encResult.str());

        return output;

}

extern "C" { // PostgreSQL functions be inside extern "C" { } block. 
Datum seal_diff_cpp(PG_FUNCTION_ARGS){

        // Get the Parameters
        text *t1 = PG_GETARG_TEXT_PP(0);
        text *t2 = PG_GETARG_TEXT_PP(1);
        text *encParam = PG_GETARG_TEXT_PP(2);
        std::string localT1;
        std::string localT2;
        std::string localEncParam;
        localT1 = text_to_cstring(t1);
        localT2 = text_to_cstring(t2);
        localEncParam = text_to_cstring(encParam);

        // Decode the parameters 
        std::stringstream decodedLocalT1 = dec(localT1);
        std::stringstream decodedLocalT2 = dec(localT2);
        std::stringstream decodedLocalEncParam = dec(localEncParam);

        // Encode the parameters
        std::string encodedLocalT1 = enc(decodedLocalT1.str());
        //std::string encodedLocalT1 = enc(encResult.str());
        std::string encodedLocalT2 = enc(decodedLocalT2.str());
        //std::string outputParam = seal_diff_operation(decodedLocalEncParam.str(), decodedLocalT1.str(), decodedLocalT2.str());

        // Returns Text 
        PG_RETURN_TEXT_P(cstring_to_text_with_len(localT1.c_str(), localT1.size()));

};
PG_FUNCTION_INFO_V1(seal_diff_cpp);
}

And I use this quick and dirty Makefile to create my .so file:

MODULES = seal_diff_cpp

PG_CONFIG = /usr/pgsql-10/bin/pg_config
PGXS = $(shell $(PG_CONFIG) --pgxs)
INCLUDEDIR = $(shell $(PG_CONFIG) --includedir-server)
INCLUDE_SEAL = /usr/local/include/seal
INCLUDE_SEAL_LIB = /usr/local/lib
INCLUDE_CPPCODEC = /usr/local/include/cppcodec
CXX = g++
CXXFLAGS = -std=c++17 -fPIC -Wall -Werror -g -O0 -pthread \
        -I$(INCLUDEDIR) -L$(INCLUDE_SEAL_LIB) -l libseal.a -I$(INCLUDE_SEAL) -I$(INCLUDE_CPPCODEC)
include $(PGXS)
seal_diff_cpp.so: seal_diff_cpp.o
        $(CXX) -shared -o seal_diff_cpp.so seal_diff_cpp.o

seal_diff_cpp.o: seal_diff_cpp.cpp
         $(CXX) $(CXXFLAGS) -o seal_diff_cpp.o -c seal_diff_cpp.cpp

When I try to add my C extension after copying it to the /usr/pgsql-10/lib/ folder with

-- Drop the function first, needed when changing return type
DROP FUNCTION IF EXISTS public.seal_diff_cpp(CHARACTER VARYING, CHARACTER VARYING, CHARACTER VARYING);

-- Create the new function
CREATE OR REPLACE FUNCTION 
   seal_diff_cpp(CHARACTER VARYING, CHARACTER VARYING, CHARACTER VARYING) RETURNS CHARACTER VARYING AS 'seal_diff_cpp' 
LANGUAGE C STRICT;

I get the error:

ERROR: could not load library "/usr/pgsql-10/lib/seal_diff_cpp.so": /usr/pgsql-10/lib/seal_diff_cpp.so: undefined symbol: _ZN4seal9Evaluator3subERNS_10CiphertextERKS1_ SQL state: XX000

This _ZN4seal9Evaluator3subERNS_10CiphertextERKS1_ is created because of using the seal::<some parameter>, the #include <seal/seal.h> is there so such errors won't happen. The SEAL library is an external library installed to /usr/local/include/seal and its libseal.a into /usr/local/lib/.

The error occurs with the first declaration of a seal:: object, I have no problems/errors compiling it into the needed seal_diff_cpp.so file with my Makefile. When I comment all the SEAL code the extension runs perfectly with the second external library (#include <cppcodec/base64_rfc4648.hpp>) which is under /usr/local/include/cppcodec.

Am I doing something wrong with the seal library in the .cpp file or in Makefile? Do I need to install the external SEAL and cppcodec libraries into /usr/pgsql-10/include/ or some other pgsql-10 path? I have the feeling that #include <seal/seal.h> doesn't work.

UPDATE

After changing my Makefile as suggested by Alan Britles and and running make gave me this output, showing no errors with linking:

g++ -Wl,--no-undefined -std=c++17 -fPIC -Wall -Werror -g -O0 -pthread -I/usr/pgsql-10/include/server -I/usr/local/include/seal -I/usr/local/include/cppcodec -o seal_diff_cpp.o -c seal_diff_cpp.cpp g++ -L/usr/pgsql-10/lib -L/usr/lib64 -Wl,--as-needed -Wl,-rpath,'/usr/pgsql-10/lib',--enable-new-dtags -shared -o seal_diff_cpp.so seal_diff_cpp.o

With nm -gC seal_diff_cpp.so | grep "seal": I get this output:

000000000000e641 T pg_finfo_seal_diff_cpp
000000000000e26c T seal_diff_cpp
000000000000de73 T seal_diff_operation(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)
                 U seal::Ciphertext::load(std::istream&)
                 U seal::Ciphertext::operator=(seal::Ciphertext const&)
000000000000f3b0 W seal::Ciphertext::Ciphertext()
000000000000f3b0 W seal::Ciphertext::Ciphertext()
000000000000f42c W seal::Ciphertext::~Ciphertext()
000000000000f42c W seal::Ciphertext::~Ciphertext()
                 U seal::SEALContext::SEALContext(seal::EncryptionParameters const&)
000000000000f1e6 W seal::SEALContext::~SEALContext()
000000000000f1e6 W seal::SEALContext::~SEALContext()
                 U seal::IntegerEncoder::IntegerEncoder(seal::SmallModulus const&, unsigned long)
                 U seal::IntegerEncoder::~IntegerEncoder()
000000000000eb80 W seal::MemoryPoolHandle::MemoryPoolHandle()
000000000000eb80 W seal::MemoryPoolHandle::MemoryPoolHandle()
000000000000eabe W seal::MemoryPoolHandle::~MemoryPoolHandle()
000000000000eabe W seal::MemoryPoolHandle::~MemoryPoolHandle()
                 U seal::EncryptionParameters::load(std::istream&)
                 U seal::EncryptionParameters::EncryptionParameters()
000000000000ebd2 W seal::EncryptionParameters::~EncryptionParameters()
000000000000ebd2 W seal::EncryptionParameters::~EncryptionParameters()
000000000000effc W seal::util::BaseConverter::~BaseConverter()
000000000000effc W seal::util::BaseConverter::~BaseConverter()
000000000000f166 W seal::util::SmallNTTTables::~SmallNTTTables()
000000000000f166 W seal::util::SmallNTTTables::~SmallNTTTables()
000000000000eba0 W seal::util::Modulus::~Modulus()
000000000000eba0 W seal::util::Modulus::~Modulus()
000000000000e9bc W seal::util::Pointer::release()
000000000000e986 W seal::util::Pointer::Pointer()
000000000000e986 W seal::util::Pointer::Pointer()
000000000000ea66 W seal::util::Pointer::~Pointer()
000000000000ea66 W seal::util::Pointer::~Pointer()
                 U seal::BigPoly::~BigPoly()
                 U seal::BigUInt::~BigUInt()
000000000000ec1c W seal::Evaluator::sub(seal::Ciphertext const&, seal::Ciphertext const&, seal::Ciphertext&)
                 U seal::Evaluator::sub(seal::Ciphertext&, seal::Ciphertext const&)
                 U seal::Evaluator::Evaluator(seal::SEALContext const&)
000000000000f276 W seal::Evaluator::~Evaluator()
000000000000f276 W seal::Evaluator::~Evaluator()
0000000000011c16 W __gnu_cxx::new_allocator<seal::SmallModulus>::deallocate(seal::SmallModulus*, unsigned long)
0000000000010afe W __gnu_cxx::new_allocator<seal::SmallModulus>::~new_allocator()
0000000000010afe W __gnu_cxx::new_allocator<seal::SmallModulus>::~new_allocator()
0000000000013e54 W __gnu_cxx::new_allocator<seal::util::SmallNTTTables>::deallocate(seal::util::SmallNTTTables*, unsigned long)
000000000001383a W __gnu_cxx::new_allocator<seal::util::SmallNTTTables>::~new_allocator()
000000000001383a W __gnu_cxx::new_allocator<seal::util::SmallNTTTables>::~new_allocator()
                 U seal::Ciphertext::save(std::ostream&) const
000000000000ebfe W seal::SEALContext::plain_modulus() const
000000000000ebc0 W seal::EncryptionParameters::plain_modulus() const
000000000000ff92 W std::allocator<seal::SmallModulus>::~allocator()
000000000000ff92 W std::allocator<seal::SmallModulus>::~allocator()
00000000000114fe W std::allocator<seal::util::SmallNTTTables>::~allocator()
00000000000114fe W std::allocator<seal::util::SmallNTTTables>::~allocator()
000000000000eb20 W std::shared_ptr<seal::util::MemoryPool>::shared_ptr(decltype(nullptr))
000000000000eb04 W std::shared_ptr<seal::util::MemoryPool>::shared_ptr()
000000000000eaa2 W std::shared_ptr<seal::util::MemoryPool>::~shared_ptr()
000000000000eaa2 W std::shared_ptr<seal::util::MemoryPool>::~shared_ptr()
0000000000013881 W void std::_Destroy_aux<false>::__destroy<seal::util::SmallNTTTables*>(seal::util::SmallNTTTables*, seal::util::SmallNTTTables*)
0000000000011c06 W void std::_Destroy_aux<true>::__destroy<seal::SmallModulus*>(seal::SmallModulus*, seal::SmallModulus*)
000000000000eb40 W std::__shared_ptr<seal::util::MemoryPool, (__gnu_cxx::_Lock_policy)2>::__shared_ptr()
000000000000ea82 W std::__shared_ptr<seal::util::MemoryPool, (__gnu_cxx::_Lock_policy)2>::~__shared_ptr()
000000000000ea82 W std::__shared_ptr<seal::util::MemoryPool, (__gnu_cxx::_Lock_policy)2>::~__shared_ptr()
000000000000ffae W std::_Vector_base<seal::SmallModulus, std::allocator<seal::SmallModulus> >::_Vector_impl::~_Vector_impl()
000000000000ffae W std::_Vector_base<seal::SmallModulus, std::allocator<seal::SmallModulus> >::_Vector_impl::~_Vector_impl()
0000000000010060 W std::_Vector_base<seal::SmallModulus, std::allocator<seal::SmallModulus> >::_M_deallocate(seal::SmallModulus*, unsigned long)
0000000000010028 W std::_Vector_base<seal::SmallModulus, std::allocator<seal::SmallModulus> >::_M_get_Tp_allocator()
000000000000ffca W std::_Vector_base<seal::SmallModulus, std::allocator<seal::SmallModulus> >::~_Vector_base()
000000000000ffca W std::_Vector_base<seal::SmallModulus, std::allocator<seal::SmallModulus> >::~_Vector_base()
000000000001073a W std::_Vector_base<seal::util::SmallNTTTables, std::allocator<seal::util::SmallNTTTables> >::_Vector_impl::~_Vector_impl()
000000000001073a W std::_Vector_base<seal::util::SmallNTTTables, std::allocator<seal::util::SmallNTTTables> >::_Vector_impl::~_Vector_impl()
000000000001151a W std::_Vector_base<seal::util::SmallNTTTables, std::allocator<seal::util::SmallNTTTables> >::_M_deallocate(seal::util::SmallNTTTables*, unsigned long)
00000000000107b4 W std::_Vector_base<seal::util::SmallNTTTables, std::allocator<seal::util::SmallNTTTables> >::_M_get_Tp_allocator()
0000000000010756 W std::_Vector_base<seal::util::SmallNTTTables, std::allocator<seal::util::SmallNTTTables> >::~_Vector_base()
0000000000010756 W std::_Vector_base<seal::util::SmallNTTTables, std::allocator<seal::util::SmallNTTTables> >::~_Vector_base()
0000000000010b2f W std::allocator_traits<std::allocator<seal::SmallModulus> >::deallocate(std::allocator<seal::SmallModulus>&, seal::SmallModulus*, unsigned long)
0000000000013845 W std::allocator_traits<std::allocator<seal::util::SmallNTTTables> >::deallocate(std::allocator<seal::util::SmallNTTTables>&, seal::util::SmallNTTTables*, unsigned long)
000000000000f574 W std::vector<seal::SmallModulus, std::allocator<seal::SmallModulus> >::~vector()
000000000000f574 W std::vector<seal::SmallModulus, std::allocator<seal::SmallModulus> >::~vector()
000000000000fbbe W std::vector<seal::util::SmallNTTTables, std::allocator<seal::util::SmallNTTTables> >::~vector()
000000000000fbbe W std::vector<seal::util::SmallNTTTables, std::allocator<seal::util::SmallNTTTables> >::~vector()
0000000000013873 W seal::util::SmallNTTTables* std::__addressof<seal::util::SmallNTTTables>(seal::util::SmallNTTTables&)
0000000000013e76 W void std::_Destroy<seal::util::SmallNTTTables>(seal::util::SmallNTTTables*)
0000000000010b09 W void std::_Destroy<seal::SmallModulus*>(seal::SmallModulus*, seal::SmallModulus*)
0000000000010036 W void std::_Destroy<seal::SmallModulus*, seal::SmallModulus>(seal::SmallModulus*, seal::SmallModulus*, std::allocator<seal::SmallModulus>&)
000000000001154f W void std::_Destroy<seal::util::SmallNTTTables*>(seal::util::SmallNTTTables*, seal::util::SmallNTTTables*)
00000000000107c2 W void std::_Destroy<seal::util::SmallNTTTables*, seal::util::SmallNTTTables>(seal::util::SmallNTTTables*, seal::util::SmallNTTTables*, std::allocator<seal::util::SmallNTTTables>&)

So I guess that the SEAL library is getting linked correctly, but I do have some important symbols mared with undefined (U) and therefore I'm still getting the same error from PostgreSQL. But why are those symbols get marked as undefined?

TalG
  • 677
  • 1
  • 9
  • 26
  • Try to use this `CXXFLAGS = -std=c++17 -fPIC -Wall -Werror -g -O0 -pthread \ -I$(INCLUDEDIR) -L$(INCLUDE_SEAL_LIB) -lseal -I$(INCLUDE_SEAL) -I$(INCLUDE_CPPCODEC)` instead in your makefile and use [objdump](https://stackoverflow.com/questions/34732/how-do-i-list-the-symbols-in-a-so-file) to check if the symbol is in the libseal.a and `so` file – leiyc Aug 18 '18 at 07:32
  • Try adding `--no-undefined` to your link command, this will make GCC report undefined symbols at link time rather than letting them sneak up on you at runtime – Alan Birtles Aug 18 '18 at 08:08
  • And after you've done that move the library linker flags from your compile command to your link command – Alan Birtles Aug 18 '18 at 08:10
  • @leiyc I've updated my post, it looks like the SEAL library links correctly. – TalG Aug 18 '18 at 10:28
  • @leiyc I do have some symbols with the `U` flag. If I'm not wrong, this means that they are undefined. – TalG Aug 18 '18 at 11:14
  • @TaiG, maybe still have some other `seal` libraries that need to be linked. – leiyc Aug 18 '18 at 11:48
  • @leiyc, unfortunately this is the only one. – TalG Aug 18 '18 at 11:50
  • @TaiG, if you have the source code of `seal`, it is recommended to check if it has external dependency or built properly. – leiyc Aug 18 '18 at 12:05
  • @leiyc, I'll do that. Last time I spoke with the library creators (Cryptography Research Group at Microsoft), I was told that all I need is the installd header files in `/usr/local/include/seal` and the libseal.a in /usr/local/lib/. Everything gets created automatically with `make install`. I use this library on another pc without any problems (without PostgreSQL) and the only problem I have is using it in a PostgreSQL C Extension function. – TalG Aug 18 '18 at 12:21

1 Answers1

0

GCC has an option to report undefined symbols when linking, this is off by default for shared libraries. Turn it on by passing the --no-undefined switch. This will make the linker fail when there are undefined symbols rather than them being reported at runtime.

Your issue is that you are passing the library link commands to your compile commands (where they are ignored) and not to your link commands. Try modifying your make file as follows:

CXXFLAGS = -std=c++17 -fPIC -Wall -Werror -g -O0 -pthread \
        -I$(INCLUDEDIR) -I$(INCLUDE_SEAL) -I$(INCLUDE_CPPCODEC)
LDFLAGS = -L$(INCLUDE_SEAL_LIB) -l libseal.a -pthread

seal_diff_cpp.so: seal_diff_cpp.o
        $(CXX) $(LDFLAGS) -shared -o seal_diff_cpp.so seal_diff_cpp.o

seal_diff_cpp.o: seal_diff_cpp.cpp
         $(CXX) $(CXXFLAGS) -o seal_diff_cpp.o -c seal_diff_cpp.cpp

Note: not passing -pthread to the linker may also cause subtle issues.

Alan Birtles
  • 32,622
  • 4
  • 31
  • 60
  • I've added changed the last line to be `$(CXX) -Wl,--no-undefined $(CXXFLAGS) -o seal_diff_cpp.o -c seal_diff_cpp.cpp`, using `make` creates this output `g++ -Wl,--no-undefined -std=c++17 -fPIC -Wall -Werror -g -O0 -pthread -I/usr/pgsql-10/include/server -I/usr/local/include/seal -I/usr/local/include/cppcodec -o seal_diff_cpp.o -c seal_diff_cpp.cpp g++ -L/usr/pgsql-10/lib -L/usr/lib64 -Wl,--as-needed -Wl,-rpath,'/usr/pgsql-10/lib',--enable-new-dtags -shared -o seal_diff_cpp.so seal_diff_cpp.o` – TalG Aug 18 '18 at 10:10
  • @TalG you need to add this flag to the linker command not the compiler command – Alan Birtles Aug 18 '18 at 13:39
  • @AlanBritles, thanks, now I get the errors :). But I still don't know why am I getting them. I've linked and included everything. – TalG Aug 18 '18 at 13:48
  • did you move `-l libseal.a` to your linker command as I suggested? – Alan Birtles Aug 18 '18 at 14:15
  • @AlanBritles, I'm using your version in my `Makefile` – TalG Aug 18 '18 at 14:16
  • @AlanBritles, maybe the `#include ` is not working, funny that the `#include ` does. – TalG Aug 18 '18 at 14:29
  • from your nm output `seal::Evaluator::sub(seal::Ciphertext&, seal::Ciphertext const&)` is undefined in `libseal.a` which will be why it isn't working, you'll have to ask the authors for help – Alan Birtles Aug 18 '18 at 14:32
  • That is very weird! I use the this option in some other code on some other pc with the same `libseal.a` – TalG Aug 18 '18 at 14:37
  • I dont think it is other pc issue. It should work. There must be something you missing. Can you renew your question with @Alan Birtles advice about -l libseal.a in linker stage added. – I S Aug 10 '20 at 18:56