Linking a dynamic library compiled from Rust with gcc - c++

I'd like to build a dynamic library from a Rust program and link it to an existing C++ project.
For the C++ project, we are stuck on using gcc for compilation (a relatively old gcc 4.8.2, but I'm also trying with gcc 7.3.0 with the same issue).
This is a minimal example of the issue:
src/lib.rs
#[no_mangle]
pub unsafe extern "C" fn hello() {
println!("Hello World, Rust here!");
}
Cargo.toml
[package]
name = "gcc-linking"
version = "0.1.0"
authors = ..
edition = "2018"
[lib]
crate-type = ["dylib"]
[dependencies]
hello.cpp:
extern "C" void hello();
int main() {
hello();
return 0;
}
Now, when I link with clang, everything is fine:
cargo build --lib
clang -L target/debug -l gcc_linking hello.cpp -o hello
LD_LIBRARY_PATH=target/debug:$LD_LIBRARY_PATH ./hello
As expected, this results in:
Hello World, Rust here!
But if I try to link this with gcc, I get the following linking error:
gcc -L target/debug -l gcc_linking hello.cpp -o hello
Output:
/tmp/ccRdGJOK.o: In function `main':
hello.cpp:(.text+0x5): undefined reference to `hello'
collect2: error: ld returned 1 exit status
Looking at the dynamical library:
# objdump -T output
0000000000043f60 g DF .text 0000000000000043 Base hello
# nm -gC output
0000000000043f60 T hello
I suspect the problem has something to do with mangling of function names, but I cannot figure out how to solve it.
Any ideas?

As #Jmb suggested, the solution was to change the order of arguments to gcc and list the shared library after the C++ file:
gcc -L target/debug hello.cpp -l gcc_linking -o hello

Related

Why does -Wl,--entry work with gcc, but not g++?

I am trying to compile a program so that it starts on a different entry point. I am running WSL1 with Ubuntu 20.04.5, and GCC and G++ 9.4.0
I found that adding the flag -Wl,--entry=foo to the compiler will link foo() as the entry function. Testing, this has worked with gcc, but not with g++.
Using the example file src/main.c:
#include <stdlib.h>
#include <stdio.h>
int main()
{
printf("Entering %s:%s\n", __FILE__, __func__);
return 0;
}
int not_main()
{
printf("Entering %s:%s\n", __FILE__, __func__);
exit(0); // Necessary, otherwise it throws a segfault
}
When compiled with gcc -Wl,--entry=not_main -o entry.o src/main.c the output is what I want: Entering src/main.c:not_main.
However, when compiled with g++ -Wl,--entry=not_main -o entry.o src/main.c, the following warning appears: /usr/bin/ld: warning: cannot find entry symbol not_main; defaulting to 0000000000001080.
This defaults to the main() function, outputting Entering src/main.c:main. The function not_main() is not found by the linker, but it is present in the source code.
The documentation for g++ says:
g++ is a program that calls GCC and automatically specifies linking against the C++ library.
I don't see how g++ can differ from gcc, if internally one calls the other. I understand that it is not the compiler but the linker which changes the entry point, and that g++ (unlike gcc) is linking against the C++ library, but I fail to understand how that is problematic.
What am I missing?
Because of name mangling, the function is not not_main but _Z8not_mainv.
how g++ can differ from gcc,
What is the difference between g++ and gcc? why use g++ instead of gcc to compile *.cc files?
C++, unlike C, uses name mangling to distinguish different overloads of the same function name.
When compiled with gcc:
$ objdump -t entry.o | grep not_main
000000000000117c g F .text 0000000000000036 not_main
When compiled with g++:
$ objdump -t entry.o | grep not_main
0000000000000000 *UND* 0000000000000000 not_main
000000000000117c g F .text 0000000000000036 _Z8not_mainv
The *UND*efined reference to not_main was probably placed there by the linker since you requested this as the entry point. The actual not_main function has its name mangled to _Z8not_mainv.
To export not_main under its original name, use extern "C":
extern "C" int not_main()
{
printf("Entering %s:%s\n", __FILE__, __func__);
exit(0); // Necessary, otherwise it throws a segfault
}

Using Go in C++

linux Debian Buster
go version go1.11.6 linux/amd64
gcc version 8.3.0 (Debian 8.3.0-6)
libmylib.go
package main
import "C"
import (
"fmt"
)
func say(text string) {
fmt.Println(text)
}
func main(){}
mylib.h
#ifndef MY_LIB_H
#define MY_LIB_H
#include <string>
void say(std::string text);
#endif
main.cpp
#include <string>
#include "mylib.h"
using namespace std;
int main() {
string text = "Hello, world!";
say(text);
return 0;
}
CGO_ENABLED=1 go build -o libmylib.so -buildmode=c-shared libmylib.go
g++ -L/path/to/lib/ -lmylib main.cpp -o my-test-program
/usr/bin/ld: /tmp/ccu4fXFB.o: in function 'main':
main.cpp:(.text+0x53): undefined reference to `say(std::__cxx11::basic_string<char, std::char_traits, std::allocator >)' collect2: error: ld returned 1 exit status
with change: package main -> package mylib
CGO_ENABLED=1 go build -o libmylib.so -buildmode=c-shared libmylib.go
-buildmode=c-shared requires exactly one main package
You have to use GoString rather than std::string in the C++ version. The error message you are getting is because of the type mismatch, manifesting as a link-time error.
See the cgo reference.
Here's a working example. There's a few differences from yours. The //export directive is needed to include the function in the generated header file, the argument is *C.char rather than string or GoString. The C++ code uses the header generated by cgo, and there has to be a const-removing cast from the static string (because go doesn't have C-like const).
libmylib.go
package main
import "C"
import (
"fmt"
)
//export say
func say(text *C.char) {
fmt.Println(C.GoString(text))
}
func main() {}
main.cpp
#include "libmylib.h"
int main(void) {
say(const_cast<char*>("hello world"));
return 0;
}
commands
This compiles to go file, generating libmylib.so and libmylib.h in the current directory.
go build -o libmylib.so -buildmode=c-shared libmylib.go
The compiles the C++ program, linking it to the shared library above:
g++ -L. main.cpp -lmylib -o hello_program
To run the program, LD_LIBRARY_PATH needs to be set to the current directory. That would be different if program was installed and the shared library put in a sensible place.
LD_LIBRARY_PATH=. ./hello_program
g++ -L/path/to/lib/ -lmylib main.cpp -o test
is probably wrong. Read the invoking GCC chapter of the GCC documentation. Order of arguments to g++ matters a lot.
Also, test(1) could be some existing executable. I recommend to use some other name.
So consider compiling with
g++ -Wall -g -O main.cpp -L/path/to/lib/ -lmylib -o my-test-program
You probably want debugging information (-g), warnings (-Wall) and some optimization (-O)
You did comment
I need to use some functions from a Go file in my C ++ project.
This is curious. I assume your operating system is some Linux. Then, can't you just use inter-process communication facilities between a process running a Go program and another process running your C++ program? Consider perhaps using JSONRPC or HTTP between them. There exist several open source libraries in Go and in C++ to help you.
PS. As I commented, calling C++ code from a Go program could be much simpler. Of course you do need to read the Go documentation and the blog about cgo and probably the C++ dlopen minihowto and some C++ reference.

GCC Cross compile to a i586 architecture (Vortex86DX)

I have Ubuntu 12.01 with gcc 4.8.2 and would like to cross compile for the Vortex86DX CPU running an old 2.6.23 kernel.
I´m trying the following testing code:
#include <iostream>
int main()
{
std::cout << "Hello world" << std::endl;
}
That is compiled using the following command line:
g++ -static-libgcc -static-libstdc++ -march=i586 test.cpp -otest586
When I run the test586 on the target architecture I´m getting this error:
$ ./test586
./teste586: symbol lookup error: ./test586: undefined symbol: _ZMSbIwSt11char_traitsIwESaIwEE4_Rep20_S_empty_rep_storageE
Any ideas of what is going on here ? This is just a small code - the real code has around 10 different libraries all written in C++ 11.
In fact the comment from Marco was right. The code still need some dynamics libraries:
$ ldd ./test586
linux-gate.so.1 => (0xb776b000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb75a4000)
/lib/ld-linux.so.2 (0xb776e000)
I have to avoid all dynamic library as the target system either does not have them or will have in a very old version.
Help appreciated to accomplish that.
I think the problem is the order of the command switches, i.e. the linker first discovers the dependencies (libgcc, libstdc++) and only then resolves them.
If you give it -static-libgcc before it found the dependency then it will simply ignore it.
the following works for me:
$ g++ -m32 -march=i586 test.cpp -o test586 -static -static-libgcc -static-libstdc++
$ ./test586
Hello world
$ ldd test586
not a dynamic executable

Compiling/linking to libpano13

I am trying to get a very simple program to compile while using functions from libpano13, but I am running into linking errors. I am on Ubuntu 10.04, and have done the following:
sudo apt-get install libpano13-dev
My sample program is as follows:
#include <cstring>
#include <pano13/PTcommon.h>
int main(int argc, char **argv) {
fullPath *outFile = new fullPath();
StringtoFullPath(outFile, (char *)"/tmp/randomImage.jpg");
return 0;
}
I am attempting to compile this with the following command (the above code is located in a file called panoTest.cpp):
$ g++ panoTest.cpp -o testApp -L/usr/lib -lpano13
When I run this command I get the following error:
$ g++ panoTest.cpp -o testApp -L/usr/lib -lpano13
/tmp/ccyIioEi.o: In function `main':
panoTest.cpp:(.text+0x87): undefined reference to `StringtoFullPath(fullPath*, char*)'
collect2: ld returned 1 exit status
make: *** [all] Error 1
Now, I've checked /usr/lib for libpano* and found the following files:
$ ls /usr/lib/libpano*
/usr/lib/libpano13.a /usr/lib/libpano13.so /usr/lib/libpano13.so.1
/usr/lib/libpano13.so.1.0.0
I've checked libpano13.so using nm and got the following output:
$ nm --demangle /usr/lib/libpano13.a | grep StringtoFullPath
U StringtoFullPath
U StringtoFullPath
00000000 T StringtoFullPath
$ nm --demangle /usr/lib/libpano13.so | grep StringtoFullPath
nm: /usr/lib/libpano13.so: no symbols
I also checked it using objdump:
$ objdump -T /usr/lib/libpano13.so | grep StringtoFullPath
00057fd0 g DF .text 0000004c Base StringtoFullPath
So, the following seems to be true:
libpano13 exists in /usr/lib,
libpano13 contains the symbols for StringtoFullPath,
I am using the correct library directory of /usr/lib,
I am using the correct library name of pano13, and
g++ is able to locate the library
But, for some reason, g++ is unable to find the symbol for StringtoFullPath.
Does anyone have any suggestions/ideas about why this is happening?
Any help would be greatly appreciated!
The
undefined reference to `StringtoFullPath(fullPath*, char*)'
worries me a bit: it contains the types of the parameters. Most Linux libraries are not C++, so there should be no types...
Try surrounding the #include for the header with an extern "C" { ... } block.

Linux c++ error: undefined reference to 'dlopen'

I work in Linux with C++ (Eclipse), and want to use a library.
Eclipse shows me an error:
undefined reference to 'dlopen'
Do you know a solution?
Here is my code:
#include <stdlib.h>
#include <stdio.h>
#include <dlfcn.h>
int main(int argc, char **argv) {
void *handle;
double (*desk)(char*);
char *error;
handle = dlopen ("/lib/CEDD_LIB.so.6", RTLD_LAZY);
if (!handle) {
fputs (dlerror(), stderr);
exit(1);
}
desk= dlsym(handle, "Apply");
if ((error = dlerror()) != NULL) {
fputs(error, stderr);
exit(1);
}
dlclose(handle);
}
You have to link against libdl, add
-ldl
to your linker options
#Masci is correct, but in case you're using C (and the gcc compiler) take in account that this doesn't work:
gcc -ldl dlopentest.c
But this does:
gcc dlopentest.c -ldl
Took me a bit to figure out...
this doesn't work:
gcc -ldl dlopentest.c
But this does:
gcc dlopentest.c -ldl
That's one annoying "feature" for sure
I was struggling with it when writing heredoc syntax and found some interesting facts. With CC=Clang, this works:
$CC -ldl -x c -o app.exe - << EOF
#include <dlfcn.h>
#include <stdio.h>
int main(void)
{
if(dlopen("libc.so.6", RTLD_LAZY | RTLD_GLOBAL))
printf("libc.so.6 loading succeeded\n");
else
printf("libc.so.6 loading failed\n");
return 0;
}
EOF
./app.exe
as well as all of these:
$CC -ldl -x c -o app.exe - << EOF
$CC -x c -ldl -o app.exe - << EOF
$CC -x c -o app.exe -ldl - << EOF
$CC -x c -o app.exe - -ldl << EOF
However, with CC=gcc, only the last variant works; -ldl after - (the stdin argument symbol).
I was using CMake to compile my project and I've found the same problem.
The solution described here works like a charm, simply add ${CMAKE_DL_LIBS} to the target_link_libraries() call
The topic is quite old, yet I struggled with the same issue today while compiling cegui 0.7.1 (openVibe prerequisite).
What worked for me was to set: LDFLAGS="-Wl,--no-as-needed"
in the Makefile.
I've also tried -ldl for LDFLAGS but to no avail.
you can try to add this
LIBS=-ldl CFLAGS=-fno-strict-aliasing
to the configure options
You needed to do something like this for the makefile:
LDFLAGS='-ldl'
make install
That'll pass the linker flags from make through to the linker. Doesn't matter that the makefile was autogenerated.
I met the same problem even using -ldl.
Besides this option, source files need to be placed before libraries, see undefined reference to `dlopen'.
In order to use dl functions you need to use the -ldl flag for the linker.
how you do it in eclipse ?
Press Project --> Properties --> C/C++ build --> Settings --> GCC C++ Linker --> Libraries -->
in the "Libraries(-l)" box press the "+" sign --> write "dl" (without the quotes)-> press ok --> clean & rebuild your project.
$gcc -o program program.c -l <library_to_resolve_program.c's_unresolved_symbols>
A good description of why the placement of -l dl matters
But there's also a pretty succinct explanation in the docs
From $man gcc
-llibrary
-l library
Search the library named library when linking. (The second
alternative with the library as a separate argument is only for POSIX
compliance and is not recommended.)
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.
Try to rebuild openssl (if you are linking with it) with flag no-threads.
Then try to link like this:
target_link_libraries(${project_name} dl pthread crypt m ${CMAKE_DL_LIBS})
In earlier versions(~2.7) of GNU tool chain, glibc did not have direct interface to link loader(dlopen and dlsym functions), so you had to provide -ldl(libdl) at compile time. You don't have to do that anymore with latest glibc version. Just include <dlcfn.h> and you are good to go.