ld reports undefined reference to symbol found by nm - c++

I am building a shared library with the following instructions :
cc -shared -Wl,-soname,libmy.so.0 [lots of .o files] -o libmy.so.0
ln -f -s libmy.so.0 libmy.so
I don't know if this is important, but this library is written in C.
I then try to link with this library, from a C++ program :
/usr/bin/c++ -g CMakeFiles/codegen.dir/client/codegen.cpp.o CMakeFiles/codegen.dir
/namenode/shared_objects.cpp.o -o codegen -rdynamic -L/path/to/libmy -lpthread
-lboost_system-mt -lboost_filesystem-mt -lboost_unit_test_framework-mt
-lboost_serialization-mt -lmy -Wl,-rpath,/path/to/libmy
But ld reports an error :
CMakeFiles/codegen.dir/client/codegen.cpp.o: In function `main':
[...]/src/client/codegen.cpp:46:
undefined reference to `alloc_code(int, int, int, int, int)'
Even if alloc_code appears to be in the shared library:
$ nm libmy.so | grep alloc_code
0000000000002c80 T alloc_code
Note that libmy.so and my codegen program are compiled using different compiler flags (one is compiled in debug mode while the other is compiled in optimized mode), but I don't think the issue comes from here.
What could make ld unable to link codegen with libmy.so ?

Your C function needs to be declared to C++ as this:
#ifdef __cplusplus
extern "C" {
#endif
void alloc_code(int, int, int, int, int);
#ifdef __cplusplus
}
#endif
Otherwise, the compiler and thus linker assume C++ linkage (where multiple functions can exist with the same name so long as their arguments differ) - and will search for the "C++-mangled" variant of the function.
When using extern "C" you tell the compiler and linker to use the unmangled "C" name.
To demonstrate this, let's write the same "library" and compile it with both a C compiler and a C++ compiler:
/* Library Source Code */
int library_function(void) {
return 0;
}
Then compile with C:
gcc -o libdummy.so -shared -fPIC dummy-library.c
nm libdummy.so | grep library_function
Which outputs:
0000000000000640 T library_function
Running the same thing with a C++ compiler yields:
g++ -o libdummy.so -shared -fPIC dummy-library.c
nm libdummy.so
=>
0000000000000670 T _Z16library_functionv

Related

Running C++ (with STL) functions in a C application

I am creating a C++ library with exported C functions that use some STL functionality. I want to include the this library in a C application.
I have reduced the problem as much as I could to the following 4 files.
main.c
#include "aaa.h"
#include <stdio.h>
int main()
{
printf("Version: %u.%u\n", GetAPIMajorVersion(), GetAPIMinorVersion());
return 0;
}
aaa.h
#ifndef AAA_H
#define AAA_H
#ifdef __cplusplus
#define DllExport extern "C"
#else // __cplusplus
#define DllExport
#endif // __cplusplus
#include <stdint.h>
DllExport uint32_t GetAPIMajorVersion();
DllExport uint32_t GetAPIMinorVersion();
#endif // AAA_H
aaa.cpp
#include "aaa.h"
#include <string>
#include <vector>
// Builds and works fine.
uint32_t GetAPIMajorVersion()
{
std::string val = "hello world";
return val.size();
}
// Produces the error messages
uint32_t GetAPIMinorVersion()
{
std::vector<bool> test;
test.push_back(true);
return test.size();
}
I am using the following script to build the library and the application.
build.sh
# Build the C++ library
g++ -m64 -Wall -O3 -c -fmessage-length=0 -fPIC -MMD -MP aaa.cpp -o aaa.o
ar rcs libaaa.a aaa.o
# Build the executable
gcc -m64 -Wall -static main.c -o main -L./ -laaa
I get the following errors when I try to build the C application
.//libaaa.a(aaa.o): In function `GetAPIMinorVersion':
aaa.cpp:(.text+0xeb): undefined reference to `operator delete(void*)'
aaa.cpp:(.text+0x1c7): undefined reference to `operator delete(void*)'
.//libaaa.a(aaa.o): In function `std::vector<bool, std::allocator<bool> >::_M_insert_aux(std::_Bit_iterator, bool)':
aaa.cpp:(.text._ZNSt6vectorIbSaIbEE13_M_insert_auxESt13_Bit_iteratorb[_ZNSt6vectorIbSaIbEE13_M_insert_auxESt13_Bit_iteratorb]+0x1d8): undefined reference to `operator new(unsigned long)'aaa.cpp:(.text._ZNSt6vectorIbSaIbEE13_M_insert_auxESt13_Bit_iteratorb[_ZNSt6vectorIbSaIbEE13_M_insert_auxESt13_Bit_iteratorb]+0x339): undefined reference to `operator delete(void*)'
aaa.cpp:(.text._ZNSt6vectorIbSaIbEE13_M_insert_auxESt13_Bit_iteratorb[_ZNSt6vectorIbSaIbEE13_M_insert_auxESt13_Bit_iteratorb]+0x3cb): undefined reference to `std::__throw_length_error(char const*)'.//libaaa.a(aaa.o):(.data.DW.ref.__gxx_personality_v0[DW.ref.__gxx_personality_v0]+0x0): undefined reference to `__gxx_personality_v0'
collect2: error: ld returned 1 exit status
I looked into this error and it seems to be because the C application does not have access to the STL libraries but If we alter main.c to only make a call to the GetAPIMajorVersion() and remove the GetAPIMinorVersion() function from the library. The application compiles and runs as expected.
That leads me to believe that the issue is not with the STL library in general but with some of the functions in the STL library.
My next guess is that it is possible that the std::vector<bool>::push_back() function could throw a exception and this is including elements into the aaa.a library that the C application can not find.
If this is the issue then, how do I include the require parts of the STL library in the aaa.a library so it can be used by the C Application?
I have found that if I change the C application to be build with g++ instead of gcc it builds and runs fine. Unfortunately the compiler that I am using in the end only supports C99 and this is not an option for me.
g++ -m64 -Wall -static main.c -o main -L./ -laaa
How should I build this library, that includes STL functions, in a way that the library functions can be called from a C application?
Edit
The compiler that I am using at the end is Arm Keil
There does not seem to be an option to include the stdc++ as a library in the Arm Keil IDE/Compiler. I can't change the command to build the C application to gcc -m64 -Wall -static main.c -o main -L./ -laaa -lstdc++ as far as I am aware.
You could try to build a C++ shared library, linking -lstdc++.
So let -laaa be a shared library libaaa.so (from source files aaa1.cc and aaa2.cc, and having position-independent code) that you would build with:
g++ -fPIC -O3 -g aaa1.cc -o aaa1.pic.o
g++ -fPIC -O3 -g aaa2.cc -o aaa2.pic.o
g++ -fPIC -shared -O3 -g aaa1.pic.o aaa2.pic.o -lstdc++ -o libaaa.so
You might also set some rpath.
Read Program Library HowTo and Drepper's How to write shared libraries
The compiler that I am using at the end is Arm Keil
You'll better use instead some recent version of a GCC cross-compiler (or of Clang one). Either you build that cross-compiler yourself from the source code of GCC 8 (in autumn 2018), or you install some cross-compiler on your Linux distribution. For example, Debian/Sid has gcc-8-arm-linux-gnueabi and gcc-8-arm-linux-gnueabihf
By experience, hardware vendors provide ancient cross-compilers (and are not good in software engineering). That is why I recommend using a recent GCC cross-compiler, on the command line.
And you'll better link your application with g++.
My next guess is that it is possible that the std::vector::push_back() function could throw a exception
Exceptions need some support at the crt0 level (for std::terminate). If your library throws some exception, the main program has to be linked with g++ (if you want a C++ library usable from C, it should not throw exception outside).
However, it is possible, with some care, to build a C++ library usable from gcc-compiled C code. The libgccjit is such a library (but it does not throw exceptions outside).
I can't change the command to build the C application to gcc -m64 -Wall -static main.c -o main -L./ -laaa -lstdc++ as far as I am aware
You surely could. You need to avoid using Arm Kell and use directly the appropriate cross-compiler on the command line (either the one supplied inside it, or preferably a more recent one that you build from GCC source code or Clang one).

Undefined references even though library is linking and it contains correct symbols

I am trying to compile my executable with this line from my Makefile:
g++-8.1.0 -Wall -Wextra -pthread -std=c++17 -ggdb3 -I/usr/local/include ./src/barometer.o ./src/serial.o ./src/ptpcontroller.o ./src/stream.o ./src/helper.o ./src/thetav.o ./src/gps.o ./src/flightcontroller.o ./src/gui.o ./src/maneuvers.o ./src/main.o ./src/fcinterface.o ./libs/NemaTode/src/NumberConversion.o ./libs/NemaTode/src/NMEAParser.o ./libs/NemaTode/src/GPSFix.o ./libs/NemaTode/src/NMEACommand.o ./libs/NemaTode/src/GPSService.o ./libs/ptpcam/ptpcam.o -o halo -L/usr/local/lib -lwiringPi -lrt -lpigpio -lncurses -lptp2 -lusb
And I get lots of undefined reference errors like this:
./libs/ptpcam/ptpcam.o: In function `init_ptp_usb(_PTPParams*, _PTP_USB*, usb_device*)':
/home/pi/ProjectHaloDrone/RPiCM3/libs/ptpcam/ptpcam.cpp:322: undefined reference to `ptp_usb_sendreq(_PTPParams*, _PTPContainer*)'
Even though all these symbols are in the libptp2 library that I am linking against with -lptp2:
pi#raspberrypi:~/ProjectHaloDrone/RPiCM3 $ nm -g /usr/local/lib/libptp2.so | grep ptp_usb_sendreq
00002d3c T ptp_usb_sendreq
I am very confused why the linker thinks the symbols aren't defined...
The fact that linker reports unresolved external symbols as ptp_usb_sendreq(_PTPParams*, _PTPContainer*) means that those symbols are mangled in C++ way (otherwise, function arguments would not be visible).
At the same time, nm reports the symbol as ptp_usb_sendreq, which means, it is not mangled.
Most likely solution: check and make sure your function signature (likely in a header file) is wrapped in extern "C" specifier.

Call a function in shared .so from C

My goal is creating a shared library created from c++. And I want to call a function in that library from a C program
I have a compareImage.h:
#ifdef __cplusplus
#define EXTERNC extern "C"
#else
#define EXTERNC
#endif
EXTERNC int compareTwoImages();
#undef EXTERNC
and a compareImage.cpp file:
#include "SURF_Homography.h"
extern "C" int compareTwoImages(){
..
}
I have already created a shared library by using this command:
g++ -ggdb `pkg-config --cflags opencv` -fpic -shared compareImage.cpp -o libcompareImage.so `pkg-config --libs opencv`
Then, I write a c program to call compareTwoImages() function from that shared lib like this:
#include <stdio.h>
int main() {
/* my first program in C */
int test = compareTwoImages();
printf("Comparison Results: %d\n", test);
return 0;
}
and compile it with this command:
gcc -lcompareImage c_call_cpp.c -o callCpp.o
But it shows an error:
/tmp/cc0wuZTU.o: In function `main':
c_call_cpp.c:(.text+0xe): undefined reference to `compareTwoImages'
collect2: error: ld returned 1 exit status
So I don't know what the problem is.
The problem is not with C++ or with your shared library or anything like that.
Narrow down your problem to a simple example next time.
Here you simply put the link flag in the wrong place:
gcc -lcompareImage c_call_cpp.c -o callCpp.o
# ^^^^^^^^^^^^^^
It needs to go after the object that'll use its symbols.
gcc c_call_cpp.c -o callCpp.o -lcompareImage
This is clearly stated in the documentation for -l:
It makes a difference where in the command you write this option; the linker searches and processes libraries and object files in the order they are specified. Thus, ‘foo.o -lz bar.o’ searches library ‘z’ after file foo.o but before bar.o. If bar.o refers to functions in ‘z’, those functions may not be loaded.

Linker error in switching from cc compiler to g++ compiler

I have been using C to code for sometime and I had no issues in compiling and linking my code to archive libraries (.a files). However, now I need to switch to C++ and doing so, I have to use g++ instead of cc for linking and compiling.
Using C, first thing to do was compiling the source and creating the object file and then linking it to the library using the same command but without the -c option:
cc -c -ggdb -Wall -Werror -I.. test.c -o test.o
cc -o test -ggdb -Wall -Werror test.o ../libpmem/libpmem.a
As I think the same procedure should be done for doing the same thing with g++, I tried to change the compiling and linking phase as follow:
g++ -c -ggdb -Wall -Werror -I.. test.c -o test.o
g++ test.o -I.. -L/path/libpmem -lpmem -o test
Although both sets should do the same thing, I always get an error while trying to link using g++. Here is the error message:
test.o: In function `main':
/path/test/test.c:5: undefined reference to `pmem_msync_mode()'
collect2: ld returned 1 exit status
make: *** [all] Error 1
There must be something wrong with the linking phase as the method definition must be found in the library file (just as the cc linker can find the definition and do the linking without any problem).
I also tried to do both linking and compiling using a single g++ command, but no matter what I do, I always get the same error. Any idea how can I fix this?
you probably have some sort of name mangling problem...
since it looks like that is declared in a c library there should already be some sort of
#ifdef __cplusplus
extern "C" {
#endif
int pmem_msync_mode();
#ifdef __cplusplus
}
#endif
but if there isn't in the library header you may have to do something like;
extern "C" {
#include <pmem.h>
}
See http://www.cplusplus.com/forum/general/1143/
You need to tell the compiler that the code is compiled as C
I am assuming that the pmem library is compiled and linked as C code.
C and C++ have different conventions for how they store the names of functions in binary format. See http://en.wikipedia.org/wiki/Name_mangling for a reference.
To solve your problem, probably the solution is to:
extern "C" {
#include <pmem.h>
}
When the C++ compiler imports the function declarations from the header, without external C linkage being specified, it expects the binary it links against to have the associated functions' representations in a different format, namely whatever format your C++ compiler mangles function names to.

g++ linking error: Undefined reference to a function when the function name is written using #define

function.cpp -> it is compiled to function.o, then libFunction.so
#define PASTEHELPER(a,b) a ## b
#define PASTE(a,b) PASTEHELPER(a,b)
void PASTE(My,Function)() { .... }
main.cpp -> compiled to main.o. But error when linking
void MyFunction();
linking error in g++. (however, successful in Visual Studio)
g++ -mtune=i686 -MP -MMD -MT .o -Wextra -Woverloaded-virtual -Wno-sign-compare -Wno-conversion -Wno-missing-field-initializers -O3 -DNDEBUG -fno-strict-aliasing -fPIC -fdiagnostics-show-option -DPROJECT=MyProjec -o /mypath main.o -L.. -L/sbcimp/run/pd/gcc/32-bit/4.4.2/lib/gcc/i686-pc-linux-gnu/4.4.2 -L/sbcimp/run/pd/gcc/32-bit/4.4.2/lib -lFunction -pthread -L/sbcimp/run/pd/python/2.7.1/lib -lpython2.7 -l util -L/sbcimp/run/pd/boost/32-bit/1.47.0_gcc4.4.2/lib -lboost_filesystem -lboost_thread -lboost_regex -lboost_system -Wl,-rpath,/sbcimp/run/pd/boost/32-bit/1.47.0_gcc4.4.2/lib -L/sbcimp/run/pd/apache_xerces-c/32-bit/3.1.1_mt_gcc4.4.2/lib -lxerces-c -Wl,-rpath,/sbcimp/run/pd/apache_xerces-c/32-bit/3.1.1_mt_gcc4.4.2/lib -ldl -Wl,-z,origin -Wl,-rpath,'$ORIGIN' -Wl,-rpath,/sbcimp/run/pd/gcc/32-bit/4.4.2/lib/gcc/i686-pc-linux-gnu/4.4.2,-rpath,/sbcimp/run/pd/gcc/32-bit/4.4.2/lib,-soname,MyProject
main.cpp: undefined reference to 'MyFunction'.
The function is not defined in any .h file. Is this the problem? If need to declare the function in .h, how to write it?
I'm not sure if the pasted linker error is complete.
But anyway, if the linker really complains about MyFunction then it expects the symbol MyFunction to be C, not C++ (in C++ it should complain about MyFunction()).
So I would guess you're either declaring the void MyFunction(); as extern "C" or you're in fact got main.o by compiling main.c using gcc instead of main.cpp.
You can check what you have in the library and what your linker expects by running:
objdump -t function.o |grep MyFunction
objdump -t main.o |grep MyFunction
If you compiled both .o file from .cpp files, you should get symbol names like _Z10MyFunctionv. The symbol name should be the same for both.
But if one of them is not mangled (ie. is just MyFunction), then it's from C, not C++.
If both of them are mangled, but different, then you declared the function differently in both cases.
The use of #define shouldn't affect the linker at all.