the c++ code:
#include <iostream>
int main() {
std::cout << "hello world\n";
}
use clang & lli:
clang++ -S -emit-llvm hello.cpp -o hello.ll
lli.exe hello.ll
It reports a fatal error, something like "Program used external function ??_7type_info##6B# which could not be resolved".
How can I resolve that linking issue? the symbol ??_7type_info##6B# is defined in?
Related
I'm attempting to write a simple program that calls a function written in a pair of Header and CPP files.
I'm doing this on a Raspberry Pi 3 Model B, and the Geany IDE v1.37.1.
Compile Command:
g++ -Wall -c "%f" -c test.cpp
Build Command:
g++ -Wall -o "%e" "%f" -o test test.cpp
main.cpp:
#include "test.h"
int main()
{
test_function();
return 0;
}
test.h:
#ifndef _test_h_
#define _test_h_
#include <iostream>
void test_function();
#endif
test.cpp:
#include "test.h"
void test_function()
{
std::cout << "hello world";
}
The code above compiles & builds fine, however attempting to run it yields the following error:
./main: not found
(program exited with code: 127)
Perhaps I am messing something up with the Compile & Build Commands?
Thank you for reading my post, any guidance is apprecaited!
Notice the compile command:
-o test
This means that the output binary will be test, so you can execute the application in your terminal or shell via ./test.
In an attempt to undersand how lazily loaded dynamic libraries work, I've made up the following (unfortunately non-working) example.
dynamic.hpp - Header of the library
#pragma once
void foo();
dynamic.cpp - Implementation of the library
#include "dynamic.hpp"
#include <iostream>
void foo() {
std::cout << "Hello world, dynamic library speaking" << std::endl;
}
main.cpp - main function that wants to use the library (edited from the snippet in this question)
#include <iostream>
#include <dlfcn.h>
#include "dynamic.hpp"
int main() {
void * lib = dlopen("./libdynamic.so", RTLD_LAZY);
if (!lib) {
std::cerr << "Error (when loading the lib): " << dlerror() << std::endl;
}
dlerror();
auto foo = dlsym(lib, "foo");
auto error = dlerror();
if (error) {
std::cerr << "Error (when loading the symbol `foo`): " << error << std::endl;
}
dlerror();
using Foo = void (*)();
(Foo(foo)());
}
Compilation and linking¹
# compile main.cpp
g++ -g -O0 -c main.cpp
# compile dynamic.cpp into shared library
g++ -fPIC -Wall -g -O0 -pedantic -shared -std=c++20 dynamic.cpp -o libdynamic.so
# link
g++ -Wall -g -pedantic -L. -ldynamic main.o -o main
Run
LD_LIBRARY_PATH='.' ./main
Error
Error (when loading the symbol `foo`): ./libdynamic.so: undefined symbol: foo
Segmentation fault (core dumped)
As far as I can tell, the error above clearly shows that the library is correctly loaded, but it's the retrieval of the symbol which fails for some reason.
(¹) A few options are redundant or, at least, not necessary. I don't think this really affects what's happening, but if you think so, I can try again with the options you suggest.
auto foo = dlsym(lib, "foo");
Perform the following simple thought experiment: in C++ you can have overloaded functions:
void foo();
void foo(int bar);
So, if your shared library has these two functions, which one would you expect to get from a simple "dlsym(lib, "foo")" and why that one, exactly?
If you ponder and wrap your brain around this simple question you will reach the inescapable conclusion that you must be missing something fundamental. And you are: name mangling.
The actual symbol names used for functions in C++ code are "mangled". That is, if you use objdump and/or nm tools to dump the actual symbols in the shared libraries you will see a bunch of convoluted symbols, with "foo" hiding somewhere in the middle of them.
The mangling is used to encode the "signature" of a function: its name and the type of its parameters, so that different overloads of "foo" produce distinct and unique symbol names.
You need to feed the mangled name into dlsym in order to resolve the symbol.
I have two .cpp files, main.cpp and secondFile.cpp:
#include <iostream>
int main()
{
std::cout << "Hello, World!\n" << std::endl;
std::cout << "I was also able to add this line!" << std::endl;
return 0;
}
And
#include <iostream>
int main()
{
std::cout << "This was from the second file!" << std::endl;
return 0;
}
I have successfully run g++ -o main.cpp main and g++ -o secondFile.cpp secondFile, as well as run each of their corresponding executables. However when I attempt to compile them simultaneously into a single executable g++ -o main.cpp secondFile.cpp bothScripts or clang++ main.cpp secondFile.cpp -o bothScripts I receive the following error:
"duplicate symbol _main in:
/var/folders/49/38grlkzs44zcth3v_dw9m9dm0000gn/T/main-d43536.o
/var/folders/49/38grlkzs44zcth3v_dw9m9dm0000gn/T/secondfile-2bee63.o
ld: 1 duplicate symbol for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)"
Clearly something is being loaded twice, but I am unsure whether this is a library (iostream), that I've named both sections 'main', or something else entirely. There are certainly questions similar to this already, but many are convoluted and not as fundamental for new C++ members (hence my question here).
Context: My rationale is to practice building executables from multiple .cpp files. Is there a better way to go about this? (New to C++ but not to programming/code as a whole.)
The reason for your error is simple. You have 2 main() functions. As you should know, in a C++ program, the function main() generally defines the entry point of a program. When each of the files are compiled together, and have their own main() function, the compiler gets confused and throws an error. To solve this, simply change the name of the main() function in one file, and call it from the other file, if you are planning to run them together.
I moved from Windows to Ubuntu and I wanted to try some C++ programming on Ubuntu. So here is very simple code and very stupid error which I can't resolve:
horse.h
#ifndef _horse_
#define _horse_
class Horse{
int speed;
public:
void saySomething();
};
#endif
horse.cpp
#include "horse.h"
#include <iostream>
using namespace std;
void Horse::saySomething(){
cout << "iiiihaaaaaaa brrrrr."<<endl;
}
and Main.cpp
#include "horse.h"
int main(){
Horse h;
h.saySomething();
}
After I compile (compilation is successful) and run this I get this error message:
/tmp/ccxuDyrd.o: In function `main':
Main.cpp:(.text+0x11): undefined reference to `Horse::saySomething()'
collect2: ld returned 1 exit status
Please help me somehow.
Try
g++ -c main.cpp horse.cpp (to compile)
g++ -o a.out main.o horse.o (to link)
It seems you only compiled your code but did not link the resulting object files. You probably invoked the compiler like this:
g++ main.cpp
You should instead compile every *.cpp file separately and then link each resulting *.o file. And you should do this with a Makefile.
Actually, the basic idea is the same on Windows with MSVC. The compiler produces object files, the linker links them together.
I'm trying to write a program to use a static library of a C++ code into another C++ code. The first C++ code is hello.cpp:
#include <iostream>
#include <string.h>
using namespace std;
extern "C" void say_hello(const char* name) {
cout << "Hello " << name << "!\n";
}
int main(){
return 0;
}
The I made a static library from this code, hello.a, using this command:
g++ -o hello.a -static -fPIC hello.cpp -ldl
Here's the second C++ code to use the library, say_hello.cpp:
#include <iostream>
#include <string>
#include <dlfcn.h>
using namespace std;
int main(){
void* handle = dlopen("./hello.a", RTLD_LAZY);
cout<<handle<<"\n";
if (!handle) {
cerr<<"Cannot open library: "<<dlerror()<<'\n';
return 1;
}
typedef void (*hello_t)();
dlerror(); // reset errors
hello_t say_hello = (hello_t) dlsym(handle, "say_hello");
const char *dlsym_error = dlerror();
if (dlsym_error) {
cerr<<"Cannot load symbol 'say_hello': "<<dlsym_error<<'\n';
dlclose(handle);
return 1;
}
say_hello("World");
dlclose(handle);
return 0;
}
Then I compiled say_hello.cpp using:
g++ -W -ldl say_hello.cpp -o say_hello
and ran ./say_hello in the command line. I expected to get Hello World! as output, but I got this instead:
0x8ea4020
Hello ▒▒▒▒!
What is the problem? Is there any trick to make compatibility for method's argument like what we use in ctypes or what?
If it helps I use a lenny.
EDIT 1:
I have changed the code and used a dynamic library, 'hello.so', which I've created using this command:
g++ -o hello.so -shared -fPIC hello.cpp -ldl
The 6th line of the code changed to:
void* handle = dlopen("./hello.so", RTLD_LAZY);
When I tried to compile say_hello.cpp, I got this error:
say_hello.cpp: In function ‘int main()’:
say_hello.cpp:21: error: too many arguments to function
I also tried to compile it using this line:
g++ -Wall -rdynamic say_hello.cpp -ldl -o say_hello
But same error raised. So I removed the argument "World" and the it has been compiled with no error; but when I run the executable, I get the same output like I have mentioned before.
EDIT 2:
Based on #Basile Starynkevitch 's suggestions, I changed my say_hello.cpp code to this:
#include <iostream>
#include <string>
#include <dlfcn.h>
using namespace std;
int main(){
void* handle = dlopen("./hello.so", RTLD_LAZY);
cout<<handle<<"\n";
if (!handle) {
cerr<<"Cannot open library: "<<dlerror()<<'\n';
return 1;
}
typedef void hello_sig(const char *);
void* hello_ad = dlsym(handle, "say_hello");
if (!hello_ad){
cerr<<"dlsym failed:"<<dlerror()<<endl;
return 1;
}
hello_sig* fun = reinterpret_cast<hello_sig*>(hello_ad);
fun("from main");
fun = NULL;
hello_ad = NULL;
dlclose(handle);
return 0;
}
Before that, I used below line to make a .so file:
g++ -Wall -fPIC -g -shared hello.cpp -o hello.so
Then I compiled say_hello.cpp wth this command:
g++ -Wall -rdynamic -g say_hello.cc -ldl -o say_hello
And then ran it using ./say_hello. Now everything is going right. Thanks to #Basile Starynkevitch for being patient about my problem.
Functions never have null addresses, so dlsym on a function name (or actually on any name defined in C++ or C) cannot be NULL without failing:
hello_t say_hello = (hello_t) dlsym(handle, "say_hello");
if (!say_hello) {
cerr<<"Cannot load symbol 'say_hello': "<<dlerror()<<endl;
exit(EXIT_FAILURE);
};
And dlopen(3) is documented to dynamically load only dynamic libraries (not static ones!). This implies shared objects (*.so) in ELF format. Read Drepper's paper How To Use Shared Libraries
I believe you might have found a bug in dlopen (see also its POSIX dlopen specification); it should fail for a static library hello.a; it is always used on position independent shared libraries (like hello.so).
You should dlopen only position independent code shared objects compiled with
g++ -Wall -O -shared -fPIC hello.cpp -o hello.so
or if you have several C++ source files:
g++ -Wall -O -fPIC src1.cc -c -o src1.pic.o
g++ -Wall -O -fPIC src2.cc -c -o src2.pic.o
g++ -shared src1.pic.o src2.pic.o -o yourdynlib.so
you could remove the -O optimization flag or add -g for debugging or replace it with -O2 if you want.
and this works extremely well: my MELT project (a domain specific language to extend GCC) is using this a lot (generating C++ code, forking a compilation like above on the fly, then dlopen-ing the resulting shared object). And my manydl.c example demonstrates that you can dlopen a big lot of (different) shared objects on Linux (typically millions, and hundred of thousands at least). Actually the limitation is the address space.
BTW, you should not dlopen something having a main function, since main is by definition defined in the main program calling (perhaps indirectly) dlopen.
Also, order of arguments to g++ matters a lot; you should compile the main program with
g++ -Wall -rdynamic say_hello.cpp -ldl -o say_hello
The -rdynamic flag is required to let the loaded plugin (hello.so) call functions from inside your say_hello program.
For debugging purposes always pass -Wall -g to g++ above.
BTW, you could in principle dlopen a shared object which don't have PIC (i.e. was not compiled with -fPIC); but it is much better to dlopen some PIC shared object.
Read also the Program Library HowTo and the C++ dlopen mini-howto (because of name mangling).
example
File helloshared.cc (my tiny plugin source code in C++) is
#include <iostream>
#include <string.h>
using namespace std;
extern "C" void say_hello(const char* name) {
cout << __FILE__ << ":" << __LINE__ << " hello "
<< name << "!" << endl;
}
and I am compiling it with:
g++ -Wall -fPIC -g -shared helloshared.cc -o hello.so
The main program is in file mainhello.cc :
#include <iostream>
#include <string>
#include <dlfcn.h>
#include <stdlib.h>
using namespace std;
int main() {
cout << __FILE__ << ":" << __LINE__ << " starting." << endl;
void* handle = dlopen("./hello.so", RTLD_LAZY);
if (!handle) {
cerr << "dlopen failed:" << dlerror() << endl;
exit(EXIT_FAILURE);
};
// signature of loaded function
typedef void hello_sig_t(const char*);
void* hello_ad = dlsym(handle,"say_hello");
if (!hello_ad) {
cerr << "dlsym failed:" << dlerror() << endl;
exit(EXIT_FAILURE);
}
hello_sig_t* fun = reinterpret_cast<hello_sig_t*>(hello_ad);
fun("from main");
fun = NULL; hello_ad = NULL;
dlclose(handle);
cout << __FILE__ << ":" << __LINE__ << " ended." << endl;
return 0;
}
which I compile with
g++ -Wall -rdynamic -g mainhello.cc -ldl -o mainhello
Then I am running ./mainhello with the expected output:
mainhello.cc:7 starting.
helloshared.cc:5 hello from main!
mainhello.cc:24 ended.
Please notice that the signature hello_sig_t in mainhello.cc should be compatible (homomorphic, i.e. the same as) with the function say_hello of the helloshared.cc plugin, otherwise it is undefined behavior (and you probably would have a SIGSEGV crash).