main.cpp
#include "string"
#include "unordered_map"
#include "iostream"
#include "dlfcn.h"
typedef void x(std::unordered_map<std::string, std::string> &);
int main(int argc, char **argv)
{
auto handle = dlopen("./test3.so", RTLD_NOW);
if(!handle)
{
std::cout << dlerror() << std::endl;
return -1;
}
x *x;
*(void **)&x = dlsym(handle, "x");
// std::unordered_map<std::string, std::string> result{{"y", "Hello, cpp"}}; //ok
std::unordered_map<std::string, std::string> result; //failed if not dynamic link test3.so
x(result);
std::cout << result["x"] << std::endl;
dlclose(handle);
return 0;
}
test3.cpp
#include "unordered_map"
#include "string"
#ifdef __cplusplus
extern "C"
{
#endif
void x(std::unordered_map<std::string, std::string> &result)
{
result["x"] = "Hello, world";
}
#ifdef __cplusplus
}
#endif
if main.cpp build with gcc-11 while test3.cpp with gcc-4.8, the program would crash.
g++ -g -fpic -shared test3.cpp -o test3.so //g++ version is 4.8
g++ -g main.cpp -ldl //g++ version is 11
[zrar@CentOS7 cpp]$ ./a.out
*** Error in `./a.out': munmap_chunk(): invalid pointer: 0x00007ffd3747f020 ***
======= Backtrace: =========
/lib64/libc.so.6(+0x7340f)[0x7f2d328b340f]
/lib64/libc.so.6(+0x78c7e)[0x7f2d328b8c7e]
./test3.so(_ZN9__gnu_cxx13new_allocatorIPNSt8__detail15_Hash_node_baseEE10deallocateEPS3_m+0x20)[0x7f2d3263d506]
./test3.so(_ZNSt10_HashtableISsSt4pairIKSsSsESaIS2_ENSt8__detail10_Select1stESt8equal_toISsESt4hashISsENS4_18_Mod_range_hashingENS4_20_Default_ranged_hashENS4_20_Prime_rehash_policyENS4_17_Hashtable_traitsILb1ELb0ELb1EEEE21_M_deallocate_bucketsEPPNS4_15_Hash_node_baseEm+0x4a)[0x7f2d3263d342]
./test3.so(_ZNSt10_HashtableISsSt4pairIKSsSsESaIS2_ENSt8__detail10_Select1stESt8equal_toISsESt4hashISsENS4_18_Mod_range_hashingENS4_20_Default_ranged_hashENS4_20_Prime_rehash_policyENS4_17_Hashtable_traitsILb1ELb0ELb1EEEE13_M_rehash_auxEmSt17integral_constantIbLb1EE+0x188)[0x7f2d3263d08a]
./test3.so(_ZNSt10_HashtableISsSt4pairIKSsSsESaIS2_ENSt8__detail10_Select1stESt8equal_toISsESt4hashISsENS4_18_Mod_range_hashingENS4_20_Default_ranged_hashENS4_20_Prime_rehash_policyENS4_17_Hashtable_traitsILb1ELb0ELb1EEEE9_M_rehashEmRKm+0x2b)[0x7f2d3263cb4f]
./test3.so(_ZNSt10_HashtableISsSt4pairIKSsSsESaIS2_ENSt8__detail10_Select1stESt8equal_toISsESt4hashISsENS4_18_Mod_range_hashingENS4_20_Default_ranged_hashENS4_20_Prime_rehash_policyENS4_17_Hashtable_traitsILb1ELb0ELb1EEEE21_M_insert_unique_nodeEmmPNS4_10_Hash_nodeIS2_Lb1EEE+0x85)[0x7f2d3263c7eb]
./test3.so(_ZNSt8__detail9_Map_baseISsSt4pairIKSsSsESaIS3_ENS_10_Select1stESt8equal_toISsESt4hashISsENS_18_Mod_range_hashingENS_20_Default_ranged_hashENS_20_Prime_rehash_policyENS_17_Hashtable_traitsILb1ELb0ELb1EEELb1EEixEOSs+0xc2)[0x7f2d3263c3b6]
./test3.so(_ZNSt13unordered_mapISsSsSt4hashISsESt8equal_toISsESaISt4pairIKSsSsEEEixEOSs+0x2e)[0x7f2d3263c2f2]
./test3.so(x+0x43)[0x7f2d3263c138]
./a.out[0x40279a]
/lib64/libc.so.6(__libc_start_main+0xf5)[0x7f2d32861ac5]
./a.out[0x402661]
======= Memory map: ========
00400000-00402000 r--p 00000000 fd:00 100685803 /home/zrar/Documents/cpp/a.out
00402000-0040a000 r-xp 00002000 fd:00 100685803 /home/zrar/Documents/cpp/a.out
0040a000-0040f000 r--p 0000a000 fd:00 100685803 /home/zrar/Documents/cpp/a.out
0040f000-00410000 r--p 0000e000 fd:00 100685803 /home/zrar/Documents/cpp/a.out
00410000-00411000 rw-p 0000f000 fd:00 100685803 /home/zrar/Documents/cpp/a.out
00aa2000-00ac3000 rw-p 00000000 00:00 0 [heap]
7f2d32636000-7f2d3263f000 r-xp 00000000 fd:00 100685761 /home/zrar/Documents/cpp/test3.so
7f2d3263f000-7f2d3283e000 ---p 00009000 fd:00 100685761 /home/zrar/Documents/cpp/test3.so
7f2d3283e000-7f2d3283f000 r--p 00008000 fd:00 100685761 /home/zrar/Documents/cpp/test3.so
7f2d3283f000-7f2d32840000 rw-p 00009000 fd:00 100685761 /home/zrar/Documents/cpp/test3.so
7f2d32840000-7f2d329e2000 r-xp 00000000 fd:00 452646 /usr/lib64/libc-2.18.so
7f2d329e2000-7f2d32be2000 ---p 001a2000 fd:00 452646 /usr/lib64/libc-2.18.so
7f2d32be2000-7f2d32be6000 r--p 001a2000 fd:00 452646 /usr/lib64/libc-2.18.so
7f2d32be6000-7f2d32be8000 rw-p 001a6000 fd:00 452646 /usr/lib64/libc-2.18.so
7f2d32be8000-7f2d32bec000 rw-p 00000000 00:00 0
7f2d32bec000-7f2d32c01000 r-xp 00000000 fd:00 1527643 /usr/lib64/libgcc_s-4.8.5-20150702.so.1
7f2d32c01000-7f2d32e00000 ---p 00015000 fd:00 1527643 /usr/lib64/libgcc_s-4.8.5-20150702.so.1
7f2d32e00000-7f2d32e01000 r--p 00014000 fd:00 1527643 /usr/lib64/libgcc_s-4.8.5-20150702.so.1
7f2d32e01000-7f2d32e02000 rw-p 00015000 fd:00 1527643 /usr/lib64/libgcc_s-4.8.5-20150702.so.1
7f2d32e02000-7f2d32f03000 r-xp 00000000 fd:00 1459 /usr/lib64/libm-2.18.so
7f2d32f03000-7f2d33102000 ---p 00101000 fd:00 1459 /usr/lib64/libm-2.18.so
7f2d33102000-7f2d33103000 r--p 00100000 fd:00 1459 /usr/lib64/libm-2.18.so
7f2d33103000-7f2d33104000 rw-p 00101000 fd:00 1459 /usr/lib64/libm-2.18.so
7f2d33104000-7f2d331ed000 r-xp 00000000 fd:00 18326 /usr/lib64/libstdc++.so.6.0.19
7f2d331ed000-7f2d333ed000 ---p 000e9000 fd:00 18326 /usr/lib64/libstdc++.so.6.0.19
7f2d333ed000-7f2d333f5000 r--p 000e9000 fd:00 18326 /usr/lib64/libstdc++.so.6.0.19
7f2d333f5000-7f2d333f7000 rw-p 000f1000 fd:00 18326 /usr/lib64/libstdc++.so.6.0.19
7f2d333f7000-7f2d3340c000 rw-p 00000000 00:00 0
7f2d3340c000-7f2d3340e000 r-xp 00000000 fd:00 452582 /usr/lib64/libdl-2.18.so
7f2d3340e000-7f2d3360e000 ---p 00002000 fd:00 452582 /usr/lib64/libdl-2.18.so
7f2d3360e000-7f2d3360f000 r--p 00002000 fd:00 452582 /usr/lib64/libdl-2.18.so
7f2d3360f000-7f2d33610000 rw-p 00003000 fd:00 452582 /usr/lib64/libdl-2.18.so
7f2d33610000-7f2d33630000 r-xp 00000000 fd:00 230826 /usr/lib64/ld-2.18.so
7f2d33812000-7f2d33818000 rw-p 00000000 00:00 0
7f2d3382e000-7f2d33830000 rw-p 00000000 00:00 0
7f2d33830000-7f2d33831000 r--p 00020000 fd:00 230826 /usr/lib64/ld-2.18.so
7f2d33831000-7f2d33832000 rw-p 00021000 fd:00 230826 /usr/lib64/ld-2.18.so
7f2d33832000-7f2d33833000 rw-p 00000000 00:00 0
7ffd37461000-7ffd37482000 rw-p 00000000 00:00 0 [stack]
7ffd375d0000-7ffd375d2000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
Aborted
[zrar@CentOS7 cpp]$ ldd a.out
linux-vdso.so.1 (0x00007ffc793ca000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007ff2ebd37000)
libstdc++.so.6 => /lib64/libstdc++.so.6 (0x00007ff2eba2f000)
libm.so.6 => /lib64/libm.so.6 (0x00007ff2eb72d000)
libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007ff2eb517000)
libc.so.6 => /lib64/libc.so.6 (0x00007ff2eb16b000)
/lib64/ld-linux-x86-64.so.2 (0x00007ff2ebf3b000)
[zrar@CentOS7 cpp]$ ldd test3.so
linux-vdso.so.1 (0x00007ffeff7c6000)
libstdc++.so.6 => /lib64/libstdc++.so.6 (0x00007fb3c0764000)
libm.so.6 => /lib64/libm.so.6 (0x00007fb3c0462000)
libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007fb3c024c000)
libc.so.6 => /lib64/libc.so.6 (0x00007fb3bfea0000)
/lib64/ld-linux-x86-64.so.2 (0x00007fb3c0c76000)
if main.cpp build with test3.so in linking list, the program would run normaly.
g++ -g -fpic -shared test3.cpp -o test3.so //g++ version is 4.8
g++ -g main.cpp -Wl,-rpath,'$ORIGIN' -ldl test3.so //g++ version is 11
[zrar@CentOS7 cpp]$ ./a.out
Hello, world
[zrar@CentOS7 cpp]$ ldd ./a.out
linux-vdso.so.1 (0x00007ffea6bb9000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007fd670b7f000)
test3.so => /home/zrar/Documents/cpp/test3.so (0x00007fd670975000)
libstdc++.so.6 => /lib64/libstdc++.so.6 (0x00007fd67066d000)
libm.so.6 => /lib64/libm.so.6 (0x00007fd67036b000)
libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007fd670155000)
libc.so.6 => /lib64/libc.so.6 (0x00007fd66fda9000)
/lib64/ld-linux-x86-64.so.2 (0x00007fd670d83000)
[zrar@CentOS7 cpp]$ ldd test3.so
linux-vdso.so.1 (0x00007ffc91596000)
libstdc++.so.6 => /lib64/libstdc++.so.6 (0x00007fc484378000)
libm.so.6 => /lib64/libm.so.6 (0x00007fc484076000)
libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007fc483e60000)
libc.so.6 => /lib64/libc.so.6 (0x00007fc483ab4000)
/lib64/ld-linux-x86-64.so.2 (0x00007fc48488a000)
In my requirment, I want to dynamicly load a plugin(may be built with other-release gcc) and trans STL object directly(host and plugin are both written in cpp, and wrapping of class in c-style is a complexity work) in plugin interface.
I do not have
g++-4.8handy, but chances are that if you compile the following code withg++-4.8andg++-11, then runnm foo.o | grep Function, you will get different mangled name.Using
g++-11I get this:If the function name is different between the two compilers, then this function is not ABI-compatible, and you can't do what you want.
By marking the function as
extern "C"you've hidden this ABI incompatibility from the compiler, but if you lie to the compiler (sooner or later) it will get you.(If the function name is the same, GCC developers made a mistake.)
Too bad. If the requirement is for different compilers (
gcc-4.xandgcc-11.xare different compilers for all practical purposes) then there is no shortcut -- you must ensure ABI compatibility, and the only sure way to achieve that is to only transferCstructs across the interface.