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

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.

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).

Why does gcc produce a different result when bulding from source compared to linking a static library?

I have a single C++14 file, my.cpp, and from within it I'm trying to use a C99 library called open62541. For the latter, both full source open62541.c/.h and a library libopen62541.a exist. In my.cpp, where I include the open62541.h, I'm using C++ specific code (e.g. iostream), so technically I'm mixing C and C++.
I can get my.cpp to compile successfully by referencing the libopen62541.a:
gcc -x c++ -std=c++14 -Wall my.cpp -l:libopen62541.a -lstdc++ -o out
This outputs no warnings, and creates an executable out.
However, if I try to compile using source code only:
gcc -x c++ -std=c++14 -Wall my.cpp open62541.c -lstdc++ -o out
I get a lot of ISO C++ warnings (e.g. "ISO C++ forbids converting a string constant to ‘char'*") and some "jump to label" errors originating from within open62541.c, resulting in compilation failure.
I can get compilation to succeed by using the -fpermissive switch:
gcc -x c++ -std=c++14 -Wall my.cpp open62541.c -lstdc++ -fpermissive -o out
which still outputs a lot of warnings, but creates the executable successfully. However, I'm unsure if doing this is a good idea.
Perhaps worth mentioning is that open62541.h considers C++ at the beginning:
#ifdef __cplusplus
extern "C" {
#endif
Given that .a library, which comes bundled with the open62541 library code, is supposedly built from the same source, why are the first two approaches not consistent in terms of warnings and errors generated? Why does one work and the other doesn't?
Should one method - linking .a vs referring to .c - be preferred to another? I was under impression that they should be equivalent, but apparently they aren't.
Is using -fpermissive in this case more of a hack that could mask potential problems, and should thus be avoided?
The error (and warning) you see are compilation errors (and warning) output by a C++ compiler when compiling C code.
For instance, in C "literal" has type char[] while in C++ it has type const char[].
Would you get a C++ compiler build libopen62541.a from open62541.c, you would see the same errors (warnings). But a C compiler might be OK with it (depending on the state of that C source file).
On the other hand, when you compile my.cpp and links it against libopen62541.a, the compiler doesn't see that offending C code, so no errors (warnings).
From here, you basically have two options:
Use the procompiled library if it suits you as is
g++ -std=c++14 -Wall -Wextra -Werror my.cpp -lopen62541.a -o out
Compile the library's code as a first step if you need to modify it
gcc -Wall -Wextra -Werror -c open62541.c
g++ -std=c++14 -Wall -Wextra -Werror -c my.cpp
g++ open62541.o my.o -o out
gcc -x c++ -std=c++14 -Wall my.cpp open62541.c -lstdc++ -o out
This command forces the C code in open62541.c to be compiled as C++. That file apparently contains constructs that are valid in C but not C++.
What you should be doing is compiling each file as its own language and then linking them together:
gcc -std=gnu11 -Wall -c open62541.c
g++ -std=gnu++14 -Wall -c my.cpp
g++ -o out my.o open62541.o
Wrapping up those commands in an easily repeatable package is what Makefiles are for.
If you're wondering why I changed from the strict -std=c++14 to the loose -std=gnu++14 mode, it's because the strict mode is so strict that it may break the system headers! You don't need to deal with that on top of everything else. If you want a more practical additional amount of strictness, try adding -Wextra and -Wpedantic instead ... but be prepared for that to throw lots of warnings that don't actually indicate bugs, on the third-party code.

Why am I getting "undefined reference to main"

I am a very new to programming and have a very basic question that may be answered in other threads however I think they are far too advanced for me to understand how. I have actually found many answers so far on this site but this is the first problem that forced me to create an account and ask.
Anyway i am running a very basic example program on linux mint 18.3. Now I have seen this exact code work on a machine with windows 8 I believe so I was wondering if that could be the problem. I have created a class and when i plug in my object then build and run I get:
/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crt1.o||In function _start':|
(.text+0x20)||undefined reference tomain'|
This is the entire code:
#include <iostream>
#include "Gladius.h"
using namespace std;
int main()
{
Gladius io;
return 0;
}
Thats it very basic. here is the .h
#ifndef GLADIUS_H
#define GLADIUS_H
class Gladius
{
public:
Gladius();
};
#endif // GLADIUS_H
and the .cpp for the class.
#include "Gladius.h"
#include <iostream>
using namespace std;
Gladius::Gladius()
{
cout << "The Gladius is a short sword" << endl;
}
I know this seems extremely simple but I am just learning to code and i have been looking all over for an explanation why this isn't working yet I see it work on another pc exactly as is. Anyway any explanation would be greatly appreciated.
Here is what i found in command line If this answers your questions about what was in the cmd.
g++ -Wall -fexceptions -g -std=c++11 -Wall -I -c /home/gator/Documents/Spartan1/Gladius.cpp -o obj/Debug/Gladius.o
/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crt1.o: In function _start':
(.text+0x20): undefined reference tomain'
collect2: error: ld returned 1 exit status
Know the compiler options(gcc/g++ compiler):
-c : Compile and assemble, but do not link
-o file : Place the output into file
So when you run
g++ filename.cpp -o executable_name
, you generate an application which can be executed.
The problem is you are compiling, assembling as well as linking when you are trying to compile "Gladius.cpp" and compiler is trying to search for main() definition.
So in your case, the compilation steps would be:
First compile "Gladius.cpp" and generate object file "Gladius.o":
g++ -Wall -fexceptions -g -std=c++11 -c Gladius.cpp
Next compile "main.cpp" and generate object file "main.o":
g++ -Wall -fexceptions -g -std=c++11 -c main.cpp
Generate executable by linking "main.o" and "Gladius.o"
g++ -Wall -fexceptions -g -std=c++11 -o main main.o Gladius.o
Now you can run "main":
./main
Your compiler's command line contains -I -c sequence.
This -I option "swallows" your -c option. -I requires an additional argument, which is an include directory name. You failed to supply that argument, which is why -I assumes that -c that follows it is the directory name. So that -I consumes that -c.
The compiler never sees that -c. Without -c it assumes that you want to compile and link your program. Since Gladius.cpp does not have main in it, you get the error at linking stage.
Here 's a simple demo of the same problem: http://coliru.stacked-crooked.com/a/8a37cd3e90a443e2
You need to figure out why you have an orphaned -I in your command line.
If you are compiling this code using a command line like:
g++ -Wall -Wextra -Werror -O gladius.cpp -o output.exe
then make sure that you include all the .cpp files (not .h files) that contain code that your program needs.
g++ -Wall -Wextra -Werror -O gladius.cpp main.cpp -o output.exe
I explain this to beginners all the time as each .cpp being a bag of Lego's in a kit. You need all the bags that came with the box in order to build the kit. If you omitted main.cpp (or the file that contains main) then you will get the linker error that you are currently getting.
What command are you using to compile, link, and then execute? It should look something like
$ g++ main.cpp gladius.cpp -odemo
$ ./demo
check your command line for linking step.. You may forgot file with main as input, or you had forgot output file name after -o (and masked main.o in result)
I had this very kind of problem myself, and though it may not be the conventional, "proper" solution, I simply renamed the ".c" file to ".cpp", and it all worked.
After all, I was compiling both c and c++ together with a c++ compiler (recommended by the library), and the c code already had the proper c++ #extern flags (see here for more on that).
Also related:
C++ Error: undefined reference to `main'
Including C Code in C++
Why do you need an explicit `-lm` compiler option
Compilation on Linux - In function '_start': (.text+0x20): undefined reference to 'main'

C++ multi-stage linking

Ok, so this might not be a great question, but I'm a bit stuck.
I have 3 programs:
pegio.c/pegio.h - Does hardware initialization and functions for my device
PegIOHandler.cpp/PegIOHandler.h - Handles I/O for device
PegRTU.cpp - Main function
My project implements the opendnp3 (which allows one to transmit data using the DNP3 protocol) library.
Now, to compile it I first compile the pegio.c file
gcc -c pegio.c -o pegio.o
Easy, up to here I understand.
Secondly I compile the IOHandler and reference pegio.c as it uses it.
g++ -c PegIOHandler.cpp pegio.c -o PegIOHandler.o -std=c++0x
Now it gives me the following output.
g++: warning: pegio.o: linker input file unused because linking not done
But, it still creates the object file. HOWEVER, PegIOHandler implements opendnp3 header files, which is included in PegIOHandler.cpp and PegIOHandler.h. When I attempt to compile these files without using the '-c', it tells me that there are undefined references to the opendnp3 files. I know I have to link them to my project, but how do I do that?
Then compililing my third and final file:
g++ PegRTU.cpp pegio.o PegIOHandler.o -o pegrtu -std=c++0x
This now tells me that there are undefined references to the opendnp3 files.
So: How do I link the opendnp3 library to my project / code files???
Thanks!
Ouch. So many misunderstandings. You don't need the previously compiled object files to compile further source files to object code. However, you do need to link them together at the end of the compilation process, including any libraries required. All in all:
gcc -c pegio.c -o pegio.o
g++ -c PegIOHandler.cpp -o PegIOHandler.o -std=c++0x
g++ -c PegRTU.cpp -o PegRTU.o -std=c++0x
g++ -o executable_name *.o -lopendnp3 # or whatever linker flag is needed for OpenDNP3
And pretty please, read this.

`undefined reference to `main` in Cpp class without main()

I came across this while trying to get an answer. But it seems like the poster had multiple files and they were not getting linked, and hence the error.
But, why do I get this error when using a single file?
g++ myClass.cpp
/usr/lib/gcc/i686-redhat-linux/4.6.3/../../../crt1.o: In function `_start':
(.text+0x18): undefined reference to `main'
collect2: ld returned 1 exit status
And why is main necessary here at compile time (from where does it find a mention of main in my code) ? main is the starting point of code execution, but why does the compiler assume i need a main here. I can have it defined in some other file and use gcc -o to make an executable?
Or maybe I am missing something else in the code which causes the error?
#include<iostream>
class myClass
{
public:
myClass()
{
std::cout<<"Constructor";
}
~myClass()
{
std::cout<<"Destructor";
}
};
main is not necessary to compile a source file. It is necessary to link a program into an executable [1], because the program has to start somewhere.
You need to tell the compiler that "this is not the whole of my program, just compile, but don't link", using the '-c' option, so
g++ -c myClass.cpp
which will produce a myClass.o file that you can then use later, e.g.
g++ -o myprog myClass.o myOtherClass.o something_that_has_main.o -lsomelib
(Obviously, substitute names with whatever you have in your project)
[1] Assuming you use the regular linker scrips that come with the compiler. There are "ways around that too", but I think that's beyond this answer.
You are trying to compile an executable, so a main function is expected. You should compile an object file by using the -c flag:
g++ -c myClass.cpp
While you are at it, I suggest adding warning flags -Wall -Wextra at the very least.
You are building your source as an application. Add -c option to produce only object file:
g++ -c myClass.cpp
Compile only?! use -c option
g++ -c file.cpp
otherwise the project needs a main.
You need to use the -c flag to compile and only tell the compiler to generate an object file. You're telling the compiler to make an executable.