简体   繁体   中英

Including header files in a PostgreSQL C extension

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" 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?

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM