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).
Related
I want to link opencv library to my C project, I have to compile the C project by gcc cause it link lots of C library, if compile by g++ it reports compatible errors. and the opencv is a c++ library without C version.this may a gcc and g++ object file linking problem
the makefile example below simply describe the situation
#compile myproject by gcc
myproject.o: myproject.c
gcc -c myproject.c -o myproject.o
#compile opencv by g++
opencv.o: opencv.cpp
g++ -c opencv.cpp -o opencv.o
#link then by gcc or g++, it reports undefined reference error
main:opencv.o myproject.o
g++(or gcc) -o main opencv.o myproject.o
myproject.o reports undefined reference error at link step
I tried add "-x c++" flag on 'myproject' compile, I think this equivalent to compile by g++,cause it reports the same compatible errors
(Note: This question had been closed, citing that this had an answer. However, my question is not generic, I am asking why this works in ubuntu/redhat, but not in macos/cygwin. So I have edited this question, by modifying the title, mentioning the words macos and ubuntu.)
I have the following c++ code:
// main.cpp
#include<iostream>
#include<cstdio>
#include "defs.h" // has the function headers only
int func0(int a0) {
printf("func0-%d\n", a0);
return a0+1;
}
int func1(int a1) {
int x;
x=func0(a1);
printf("func1-%d\n", x);
return a1+1;
}
int func2(int a2) {
int x;
x=func1(a2);
printf("func2-%d\n", x);
return x+5;
}
int main() {
func1(5);
func2(8);
}
I can compile and run this code as:
g++ main.cpp; ./a.out
Now I would like to move the functions to different files (func1 to f1.cpp,
fun0 and func2 to f2.cpp, and main to main.cpp), and create shared libraries
like this:
g++ -c -pipe -std=c++11 -fPIC main.cpp
g++ -c -pipe -std=c++11 -fPIC f1.cpp
g++ -c -pipe -std=c++11 -fPIC f2.cpp
g++ -shared -o libx1.so f1.o
g++ -shared -o libx2.so f2.o
g++ main.o -L. -lx1 -lx2 -o exe
export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
./exe
The above instructions work in redhat linux and ubuntu. But when I run the same commands in other variants of linux (eg macos or cygwin) I get errors during creation of the shared library like this:
g++ -shared -o libx1.so f1.o
undefined reference to func0(int)
g++ -shared -o libx2.so f2.o
undefined reference to func1(int)
Why is this error happening only in some OS versions, and not happening in redhat/ubuntu? Is it due to the gcc versions, or something to do with the OS?
(The above instructions work with g++ in redhat(gcc version 8.3.1) and ubuntu (9.4.0). It does not work with g++ in cygwin(11.3.0) and in macos(11.2.0).)
The problem is caused by cyclic dependencies between the two libraries. Before doing anything else, you should ask yourself whether it is acceptable to have cyclic dependencies for your project. It is never a good idea, but if the alternative involves massive refactoring, it could be the lesser of two evils. Still, refactoring should probably be the default answer in most cases. If you cannot refactor, the rest of this answer is for you.
How are cyclic dependencies handled on different OSes?
On both Linux and Mac OS X (and on FreeBSD and on most commercial Unixes of old), references are resolved at load time. The loader uses the first suitable symbol definition it encounters, be it it in the main executable, in the shared object itself, or in a different shared object. It is not known until load time where that definition will be found.
So when the executable from the question is loaded, the dynamic loader finds the definition of func1 in libx1 and the definitions of func0 and func2 in libx2, and all is well.
The difference between Linux and Mac OS X lies in the linker (ld) behaviour. Both GNU ld and LLVM ld by default allow unresolved references when building a shared library. Mac OS X ld appears to be of a different breed and unresolved references are not allowed by default. One can either list the dependencies on the link line, or explicitly allow unresolved references using the Mac-specific ld option -undefined dynamic_lookup. But of course when the dependencies are cyclic, the first option is problematic. For code in question:
g++ -shared -o libx1.so f1.o -Wl,-undefined,dynamic_lookup
g++ -shared -o libx2.so f2.o -Wl,-undefined,dynamic_lookup
Windows DLLs work very differently. Each symbol must be resolved at link time. Unlike the Unix-y loaders, the loader must know exactly which DLL to search for each imported symbol. There is no option to allow unresolved references in DLLs because there is no mechanism to resolve them at load time from an unknown source.
Windows still allows cyclic dependencies between DLLs, but the mechanism is a bit different. The linker must use separate import libraries in this case (they are usually optional when using GNU or LLVM toolchains). The linking is done in two phases. First, the .lib files are generated for each future .dll, and then .dll themselves are produced using the .lib files from the first stage. For code in question:
# first stage
g++ -shared -Wl,--out-implib=x1.lib -o x1.dll f1.o
g++ -shared -Wl,--out-implib=x2.lib -o x2.dll f2.o
# second stage
g++ -shared -o x1.dll f1.o x2.lib
g++ -shared -o x2.dll f2.o x1.lib
The first stage will report undefined symbols but will still produce the .lib file needed for the second stage.
I am creating a gcc shared library having a static library dependency.
I compile the parts for static library as following:
gcc -c -m64 -O2 -fPIC -std=c99 -Wall ms*.c //there are 10 C files, no warnings
Next I create a static library with:
ar rc static_lib.a ms*.o
Next I compile the parts for my program as following:
g++ -c -m64 -O2 -fPIC -std=c++14 -Wall ab*.cpp //there are 5 C++ files, just -Wunused-variable warnings
Then I create a shared library as following:
g++ -shared -g -Wall ab*.o static_lib.a -o shared_lib.so
in the normal case, this shared_lib.so will be called by a Ruby program using a foreign function interface. There is no problem if I do it on ubuntu or mac(.dylib), but if I try this on debian stretch I get an error related to the static library as if the configurations are not set properly. If I run the application without foreign function interface, such as creating a tester and running with the cpp file main function as following:
> g++ -o library_test ab*.o static_lib.a
> ./library_test
There is no problem!
My question is what kind of configuration for creating a shared library may be missing here to not get that undesirable behaviour. Especially on debian stretch 9.5!
Or is there a way that I can understand if there is a problem in the shared library.
From the comments, you indicate the problem is with a #define. Those are preprocessor directives. Libraries are for the linker.
You might be confused because g++ does include the preprocessor phase, and might call the linker depending on the requested output. Still, g++ follows the C++ language rules.
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.
I'm using MinGw on Windows 7. The following simple program compiles fine, but the linker complains and I do not understand what's wrong:
#include <iostream>
#include <vector>
using namespace std;
int main()
{
std::vector<int> iv;
iv.push_back(7);
cout << iv.back() << endl;
return 0;
}
the compiler/linker messages look as follows:
mingw32-g++.exe -Wall -fexceptions -std=c++0x -Wall -g -std=c++0x -Wall -g -frepo -IC:\cppbuchincludes\include -IG:\Boost -IG:\Users\thomas\cpp\STLUsage\\include -c G:\Users\thomas\cpp\STLUsage\main.cpp -o obj\Debug\main.o
mingw32-g++.exe -o bin\Debug\STLUsage.exe obj\Debug\main.o G:\Boost\stage\lib\libboost_filesystem-mgw45-mt-d-1_45.dll.a G:\Boost\stage\lib\libboost_regex-mgw45-mt-d-1_45.dll.a G:\Boost\stage\lib\libboost_system-mgw45-mt-d-1_45.dll.a G:\Boost\stage\lib\libboost_thread-mgw45-mt-1_45.dll.a G:\Boost\stage\lib\libboost_unit_test_framework-mgw45-mt-d-1_45.dll.a
collect: recompiling G:\Users\thomas\cpp\STLUsage\main.cpp
collect: relinking
collect2: '_ZNSt12_Vector_baseIiSaIiEEC1Ev' was assigned to 'obj\Debug\main.rpo', but was not defined during recompilation, or vice versa
obj\Debug\main.o: In function `vector':
c:/mingw/bin/../lib/gcc/mingw32/4.5.2/include/c++/bits/stl_vector.h:208: undefined reference to `std::_Vector_base<int, std::allocator<int> >::_Vector_base()'
(...and so on...)
I can use templates I defined myself.
I have that MinGw binary from a book and followed the instructions in that book regarding compiler settings. In particular the references to the Boost libs are taken from there.
This must be a simple thing, I just want to make trivial use of the STL.
Edit following the advice given in an answer, I replaced the binary to be used to compile by g++.exe in the Settings -> Compiler and debugging -> toolchain executables dialog, but I'm getting the same error messages (with mingw32-g++.exe now replaced by g++.exe).
Edit (once more) this has to be problem eith the Code::Blocks settings, since compiling using g++ from the command line works just fine.
Use g++ to compile and link the program. mingw32-g++.exe doesn't do that.
FAQ says,
What's the difference between gcc and mingw32-gcc?
The mingw32-gcc, mingw32-g++, etc. binaries exist as an aid to cross development. They are created in a typical build of gcc. They are therefore distributed as the maintainers of GCC meant them to be. The gcc.exe indicates that the binary produces binaries for a target equal to the build, while the mingw32-gcc binary produces binaries to be executed on the mingw32 target.
So I guess the problem is because of mingw32-g++.exe which you're not supposed to use, for normal build.
Try these:
g++ program.cpp //simple build
g++ program.cpp -Wall //build with all warnings enabled
g++ program.cpp -Wall -O2 //enable warnings and optimization level 2
g++ program.cpp -std=c++0x //use C++11 features
Hope that helps.