I have been provided with .so file.
ldd libTodoAPI.so
linux-vdso.so.1 => (0x00007ffc766b8000)
libcrypto.so.1.0.0 => /lib/x86_64-linux-gnu/libcrypto.so.1.0.0 (0x00007fead8e3f000)
libsqlite3.so => /usr/lib/libsqlite3.so (0x00007fead8b6a000)
libQt5Network.so.5 => /usr/lib/libQt5Network.so.5 (0x00007fead8804000)
libQt5Script.so.5 => /usr/lib/libQt5Script.so.5 (0x00007fead838b000)
libQt5Core.so.5 => /usr/lib/libQt5Core.so.5 (0x00007fead7c4f000)
libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007fead78cd000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007fead76b7000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fead72ed000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fead70e9000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fead6ecc000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fead6bc3000)
libicui18n.so.53 => /usr/lib/libicui18n.so.53 (0x00007fead6777000)
libicuuc.so.53 => /usr/lib/libicuuc.so.53 (0x00007fead63ec000)
libgthread-2.0.so.0 => /usr/lib/x86_64-linux-gnu/libgthread-2.0.so.0 (0x00007fead61ea000)
librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007fead5fe2000)
libglib-2.0.so.0 => /lib/x86_64-linux-gnu/libglib-2.0.so.0 (0x00007fead5cd1000)
/lib64/ld-linux-x86-64.so.2 (0x00007fead949a000)
libicudata.so.53 => /usr/lib/libicudata.so.53 (0x00007fead4649000)
libpcre.so.3 => /lib/x86_64-linux-gnu/libpcre.so.3 (0x00007fead43d9000)
and with header
#ifndef TODO_API_H
#define TODO_API_H
#include <string>
using namespace std;
namespace todoarea {
class TodoAPI{
public:
static string checkDoer();
static string getInformation();
static string callTask(string funcName, string param);
static string Done(string param);
static string returnStatus(string param);
static string Close();
};
}
#endif //TODO_API_H
I am trying to create a program to call from this shared object and having problem.
//load.cpp
#include <iostream>
#include "TodoAPI.h"
using namespace todoarea;
int main(int argc, char **argv)
{
// TodoAPI* myTodo = new TodoAPI();
// myTodo>checkDoer();
// TodoAPI m;
string data = TodoAPI::checkDoer();
string a = TodoAPI::getInformation();
}
When I try to compile it
g++ load.cpp -lTodoAPI
/tmp/ccs1hwWP.o: In function `main':
load.cpp:(.text+0x27): undefined reference to `todoarea::TodoAPI::checkDoer[abi:cxx11]()'
load.cpp:(.text+0x33): undefined reference to `todoarea::TodoAPI::getInformation[abi:cxx11]()'
collect2: error: ld returned 1 exit status
If I try to see the so file
nm -s libTodoAPI.so
nm: libTodoAPI.so: no symbols
Also
nm -DC libTodoAPI.so | grep todoarea
000000000000a63a T todoarea::TodoAPI::Done(std::string)
000000000000a098 T todoarea::TodoAPI::callTask(std::string, std::string)
0000000000009e9c T todoarea::TodoAPI::getInformation()
000000000000a398 T todoarea::TodoAPI::returnStatus(std::string)
0000000000009ca0 T todoarea::TodoAPI::checkDoer()
000000000000a8dc T todoarea::TodoAPI::Close()
It has lots of functions (mostly seems like Qt) listed when I try nm with -D option.
Question: How can I use this library to create app in linux? What am I doing wrong? I tried to call functions dynamically but failed. Any help would be appreciated.
You are falling foul of an ABI break between the GCC release with libTodoAPI.so
was built and the one with which you are compiling your code. See Dual ABI Troubleshooting
Troubleshooting
If you get linker errors about undefined references to symbols that involve types in the
std::__cxx11 namespace or the tag [abi:cxx11] then it probably indicates that you are
trying to link together object files that were compiled with different values for the
_GLIBCXX_USE_CXX11_ABI macro. This commonly happens when linking to a third-party library that
was compiled with an older version of GCC. If the third-party library cannot be rebuilt
with the new ABI then you will need to recompile your code with the old ABI.
Related
I am trying to make our program runnable on some old Linux versions. One common import that prevents it is __longjmp_chk, added in glibc 2.11 but missing in older ones. One "solution" is to use -D_FORTIFY_SOURCE=0 but this turns off other fortify functions (__printf_chk etc) which are present in the target libc. Is there a way to make __longjmp_chk a "weak import" which would use the function from libc.so.6 if present, and fall back to local stub if not?
Is there a way to make __longjmp_chk a "weak import" which would use
the function from libc.so.6 if present, and fall back to local stub
if not?
I'd say yes, using dlsym() to check for __longjmp_chk and acting accordingly:
/* cc -ldl */
#define _GNU_SOURCE
#include <setjmp.h>
#include <stdio.h>
#include <dlfcn.h>
void __longjmp_chk(sigjmp_buf env, int val)
{
void (*p)(sigjmp_buf, int) = dlsym(RTLD_NEXT, "__longjmp_chk");
if (p)
printf("use the function from libc\n"),
p(env, val);
else
{
printf("falling back to local stub\n");
/* local stub - whatever that may be */
}
}
main()
{ // try it
sigjmp_buf env;
while (!setjmp(env)) __longjmp_chk(env, 1);
return 0;
}
I am trying to make our program runnable on some old Linux versions.
There are only a few ways to make this work, and most of them are enumerated here.
Is there a way to make __longjmp_chk a "weak import".
No.
I'm writing a shared-object which is supposed to be LD_PRELOADed into processes.
In that shared object I have some initialization like
__attribute__((constructor)) void initFunc();
That I'd like to be called before any other code in the process.
With processes that are just an executable this works fine but if the process depends on some other share-objects on its own, these get initialized before my LD_PRELOAD shared object.
I tried giving the linker the option -Wl,-z,initfirst but that doesn't seem to have any effect at all. When I'm running the process with LD_DEBUG=files I still see the application so inited before mine.
I'm running CentOS 5.5
The problem is that the loader only supports one shared library with -z initfirst, and libpthread.so (which is used by almost everything) already has this set. Even if you use LD_PRELOAD to load a library, libpthread's constructors will be called first.
You can get around this by patching the loader to support multiple shared libraries with -z initfirst. Here is a patch for ld.so version 2.21 which preserves the binary ABI but makes a linked list out of initfirst libraries and calls them with LD_PRELOAD constructors first.
diff --git a/elf/dl-load.c b/elf/dl-load.c
index 6dd8550..ac3b079 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
## -1387,7 +1387,27 ## cannot enable executable stack as shared object requires");
/* Remember whether this object must be initialized first. */
if (l->l_flags_1 & DF_1_INITFIRST)
- GL(dl_initfirst) = l;
+ {
+#if 0
+ struct initfirst_list *first = malloc(sizeof(*first));
+ first->which = l;
+ first->next = GL(dl_initfirst);
+ GL(dl_initfirst) = first;
+#else
+ struct initfirst_list *node = malloc(sizeof(*node));
+ node->which = l;
+ node->next = NULL;
+ struct initfirst_list *it = GL(dl_initfirst);
+ if (!it)
+ GL(dl_initfirst) = node;
+ else
+ {
+ while (it->next)
+ it = it->next;
+ it->next = node;
+ }
+#endif
+ }
/* Finally the file information. */
l->l_dev = st.st_dev;
diff --git a/elf/dl-map-segments.h b/elf/dl-map-segments.h
index baaa813..bca961c 100644
--- a/elf/dl-map-segments.h
+++ b/elf/dl-map-segments.h
## -55,7 +55,11 ## _dl_map_segments (struct link_map *l, int fd,
/* Remember which part of the address space this object uses. */
l->l_map_start = (ElfW(Addr)) __mmap ((void *) mappref, maplength,
c->prot,
+#if 0
MAP_COPY|MAP_FILE,
+#else
+ MAP_COPY|MAP_FILE|MAP_32BIT,
+#endif
fd, c->mapoff);
if (__glibc_unlikely ((void *) l->l_map_start == MAP_FAILED))
return DL_MAP_SEGMENTS_ERROR_MAP_SEGMENT;
diff --git a/elf/dl-support.c b/elf/dl-support.c
index 835dcb3..9ea0c05 100644
--- a/elf/dl-support.c
+++ b/elf/dl-support.c
## -148,7 +148,7 ## struct r_search_path_elem *_dl_all_dirs;
struct r_search_path_elem *_dl_init_all_dirs;
/* The object to be initialized first. */
-struct link_map *_dl_initfirst;
+struct initfirst_list *_dl_initfirst;
/* Descriptor to write debug messages to. */
int _dl_debug_fd = STDERR_FILENO;
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index b421931..7bb7a69 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
## -318,7 +318,11 ## struct rtld_global
EXTERN unsigned long long _dl_load_adds;
/* The object to be initialized first. */
- EXTERN struct link_map *_dl_initfirst;
+ /*EXTERN struct link_map *_dl_initfirst;*/
+ EXTERN struct initfirst_list {
+ struct link_map *which;
+ struct initfirst_list *next;
+ } *_dl_initfirst;
#if HP_SMALL_TIMING_AVAIL || defined HP_TIMING_PAD
/* Start time on CPU clock. */
I suppose you can try hacking libpthread to not use -z initfirst, but this seems like the simplest option. I have used it successfully to get a constructor called before anything else. You just have to make sure your LD_PRELOADed library does not use libc, because then libc's constructors will be called first, and in a multithreaded program libc depends on libpthread, so pthread's constructors will be called before that.
Here's an example. I compile a hello, world program with -pthread (otherwise there are no problems). I write a small library which is meant to be LD_PRELOADed and which does not depend on libc. With the default loader, you can't get your init function called first:
$ cat hello.c
#include <stdio.h>
int main() {
puts("Hello, world!");
return 0;
}
$ gcc hello.c -o hello -pthread
$ cat superearly.c
#include <unistd.h>
#include <sys/syscall.h>
long write(int fd, const void *buffer, size_t len) {
unsigned long ret;
__asm__ __volatile__("syscall" : "=a"(ret) : "a"(__NR_write), "D"(fd),
"S"(buffer), "d"(len));
return ret;
}
void hello(void) __attribute__((constructor));
void hello(void) {
write(STDOUT_FILENO, "Got in first!\n", 14);
}
$ gcc superearly.c -fPIC -shared -nostdlib -o libsuperearly.so -Wl,-z,initfirst
$ LD_DEBUG=libs LD_PRELOAD=./libsuperearly.so ./hello
19997: find library=libpthread.so.0 [0]; searching
19997: search cache=/etc/ld.so.cache
19997: trying file=/lib/x86_64-linux-gnu/libpthread.so.0
19997:
19997: find library=libc.so.6 [0]; searching
19997: search cache=/etc/ld.so.cache
19997: trying file=/lib/x86_64-linux-gnu/libc.so.6
19997:
19997:
19997: calling init: /lib/x86_64-linux-gnu/libpthread.so.0
19997:
19997:
19997: calling init: /lib/x86_64-linux-gnu/libc.so.6
19997:
19997:
19997: calling init: ./libsuperearly.so
19997:
Got in first!
19997:
19997: initialize program: ./hello
19997:
19997:
19997: transferring control: ./hello
19997:
Hello, world!
19997:
19997: calling fini: ./hello [0]
19997:
19997:
19997: calling fini: /lib/x86_64-linux-gnu/libpthread.so.0 [0]
19997:
$
However, with an ld.so which has been patched with my patch above:
$ LD_DEBUG=libs LD_PRELOAD=./libsuperearly.so ~/libc/lib/ld-2.21.so ./hello
19986: find library=libpthread.so.0 [0]; searching
19986: search cache=/home/user/libc/etc/ld.so.cache
19986: trying file=/home/user/libc/lib/libpthread.so.0
19986:
19986: find library=libc.so.6 [0]; searching
19986: search cache=/home/user/libc/etc/ld.so.cache
19986: trying file=/home/user/libc/lib/libc.so.6
19986:
19986:
19986: calling init: ./libsuperearly.so
19986:
Got in first!
19986:
19986: calling init: /home/user/libc/lib/libpthread.so.0
19986:
19986:
19986: calling init: /home/user/libc/lib/libc.so.6
19986:
19986:
19986: initialize program: ./hello
19986:
Hello, world!
19986:
19986: calling fini: ./hello [0]
19986:
19986:
19986: calling fini: /home/user/libc/lib/libpthread.so.0 [0]
19986:
$
Can someone explain what I am doing wrong here:
#include <future>
#include <iostream>
int main() {
std::future<int> result( std::async([](int m) { return m+1;}, 2));
std::cout << "In main" << std::endl;
std::cout << result.get();
}
With GCC 4.8 or 4.9, I get a segmentation fault.
:
./a.out:
In main
Segmentation fault: 11
: /opt/local/bin/g++-mp-4.8 -v
Using built-in specs.
COLLECT_GCC=/opt/local/bin/g++-mp-4.8
COLLECT_LTO_WRAPPER=/opt/local/libexec/gcc/x86_64-apple-darwin12/4.8.1/lto-wrapper
Target: x86_64-apple-darwin12
Configured with: ../gcc-4.8.1/configure --prefix=/opt/local --build=x86_64-apple-darwin12 --enable-languages=c,c++,objc,obj-c++,lto,fortran,java --libdir=/opt/local/lib/gcc48 --includedir=/opt/local/include/gcc48 --infodir=/opt/local/share/info --mandir=/opt/local/share/man --datarootdir=/opt/local/share/gcc-4.8 --with-local-prefix=/opt/local --with-system-zlib --disable-nls --program-suffix=-mp-4.8 --with-gxx-include-dir=/opt/local/include/gcc48/c++/ --with-gmp=/opt/local --with-mpfr=/opt/local --with-mpc=/opt/local --with-ppl=/opt/local --with-cloog=/opt/local --enable-cloog-backend=isl --disable-cloog-version-check --enable-stage1-checking --disable-multilib --enable-lto --enable-libstdcxx-time --with-as=/opt/local/bin/as --with-ld=/opt/local/bin/ld --with-ar=/opt/local/bin/ar --with-bugurl=https://trac.macports.org/newticket --with-pkgversion='MacPorts gcc48 4.8.1_0'
Thread model: posix
gcc version 4.8.1 (MacPorts gcc48 4.8.1_0)
Edit: Some people have pointed to another answer that says link with pthread. That does not solve the problem:
: /opt/local/bin/g++-mp-4.8 -std=c++11 -lpthread thread5.cc
: ./a.out
In main
Segmentation fault: 11
I've tested the code with clang 3.2(apple llvm 4.2) and it runs just fine. it returns
In main
3
edit:
Also other people tested with gcc 4.8.1 and it works. The most probable issue is that you are linking to the default pthread library instead of the one that comes with gcc 4.8.1 from macports
Two different so dlopen & dlclose couple of times, blocked on dlopen.
Hangs on dlopen, which outputs nothing, cpuidle down to 0%, and couldn't quit via ctrl+c.
LOG_TRACE("attaching...");
handle = dlopen(plugin_path.c_str(), RTLD_LAZY);
LOG_DEBUG("dlopen called"); // this line did not output, after success couple of times;
then I use gdb attach to the procedure:
(gdb) bt
#0 0x0000002a960dbe60 in tcmalloc::ThreadCache::InitTSD () at src/thread_cache.cc:321
#1 0x0000002a960d51bf in TCMallocGuard (this=Variable "this" is not available.) at src/tcmalloc.cc:908
#2 0x0000002a960d5e00 in global constructors keyed to _ZN61FLAG__namespace_do_not_use_directly_use_DECLARE_int64_instead43FLAGS_tcmalloc_large_alloc_report_thresholdE () at src/tcmalloc.cc:935
#3 0x0000002a960fafc6 in __do_global_ctors_aux () at ./src/base/spinlock.h:54
#4 0x0000002a96010f13 in _init () from ../plugins/libmonitor.so
#5 0x0000002a00000000 in ?? ()
#6 0x000000302ad0acaf in _dl_init_internal () from /lib64/ld-linux-x86-64.so.2
#7 0x000000302aff725c in dl_open_worker () from /lib64/tls/libc.so.6
#8 0x000000302ad0aa60 in _dl_catch_error () from /lib64/ld-linux-x86-64.so.2
#9 0x000000302aff79fa in _dl_open () from /lib64/tls/libc.so.6
#10 0x000000302b201054 in dlopen_doit () from /lib64/libdl.so.2
#11 0x000000302ad0aa60 in _dl_catch_error () from /lib64/ld-linux-x86-64.so.2
#12 0x000000302b201552 in _dlerror_run () from /lib64/libdl.so.2
#13 0x000000302b201092 in dlopen##GLIBC_2.2.5 () from /lib64/libdl.so.2
#14 0x000000000041b559 in uap::meta::MetaManageServiceHandler::plugin_action this=0xb26000, _return=#0x7fbffff500, plugin_name=#0x7fbffff4e0, plugin_path=#0x7fbffff570, t=Variable "t" is not available.)
at /usr/lib/gcc/x86_64-redhat-linux/3.4.5/../../../../include/c++/3.4.5/bits/basic_string.h:1456
#15 0x000000000041b0bc in uap::meta::MetaManageServiceHandler::plugin_action (this=0xb26000, _return=#0x7fbffff500, plugin_name=#0x7fbffff4e0, plugin_path=#0x7fbffff570, t=uap::meta::PluginActionType::RELOAD)
at server/service_handler.cpp:173
#16 0x0000000000417641 in uap::meta::test_Service_Handler_suite_test_case_manage_service_plugin_action_Test::TestBody (this=0xb16080) at test_load.cpp:73
#17 0x00000000004446c6 in testing::internal::HandleExceptionsInMethodIfSupported<testing::Test, void> (object=0xb16080, method={__pfn = 0x21, __delta = 0}, location=0x537f30 "the test body")
at ../../../../com/btest/gtest/src/gtest.cc:2744
#18 0x000000000042dd1c in testing::Test::Run (this=0xb16080) at ../../../../com/btest/gtest/src/gtest.cc:2766
#19 0x000000000042e8b4 in testing::TestInfo::Run (this=0xb17160) at ../../../../com/btest/gtest/src/gtest.cc:2958
#20 0x000000000042f415 in testing::TestCase::Run (this=0xb23000, runtype=0) at ../../../../com/btest/gtest/src/gtest.cc:3160
#21 0x0000000000436352 in testing::internal::UnitTestImpl::RunAllTests (this=0xb22000) at ../../../../com/btest/gtest/src/gtest.cc:5938
#22 0x0000000000434299 in testing::UnitTest::Run (this=0x6f4220, run_type=0) at ../../../../com/btest/gtest/src/gtest.cc:5449
#23 0x0000000000434268 in testing::UnitTest::Run (this=0x6f4220) at ../../../../com/btest/gtest/src/gtest.cc:5387
#24 0x0000000000455404 in main (argc=1, argv=0x7fbffff8c8) at ../../../../com/btest/gtest/src/gtest_main.cc:38
actually i have redefined the four functions:
void __attribute__((constructor)) dlinit()
{
}
void __attribute__((destructor)) dldeinit()
{
}
void _init()
{
}
void _fini()
{
}
I think I have found the root cause: in gdb info , the hang comes form tcmalloc, i have read the tcmalloc corelated code , and couple of locks, then i complie and link so without tcmalloc, nothing happens, this would be tcmalloc bugs when work with so
You should compile both your application and your plugin with gcc -Wall -g and use the debugger gdb (don't forget to compile the plugin sources also with -fPIC and to link its object files with -shared).
As you probably know, dlopen-ing a shared object will run the function having a constructor function attribute (and also, as dlopen(3) says, the obsolete _init function). Also, constructors of C++ static data have the constructor attribute.
I guess that some of these constructors is blocked somehow (perhaps on input). You could also strace your program.
There might be some other reasons for such blocking, e.g. dlopen-ing an NFS mounted file from an unresponsive NFS server, etc...
See also rtld-audit(7), ld.so(8) and LD_DEBUG environment variable (try to set it to all). Also, run ldd on both the plugin and the program.
BTW, in your code the lack of terminating newline \n in your printf format strings is suspicious (and bad taste), and you should print dlerror() when dlopen fails. At least add a call to fflush(NULL); after your code. Try to code instead:
handle = dlopen(plugin_path.c_str(), RTLD_LAZY);
if(!handle) {
printf("dlopening %s failed %s\n", plugin_path.c_str(), dlerror());
} else {
printf("dlopen %s success\n", plugin_path.c_str());
}
fflush(NULL);
You may also have corrupted your heap (elsewhere in your program) to the point that dlopen (or your plugin) cannot work anymore. Use valgrind to hunt memory corruption bugs!
I have a shared library "libwiston.so". I am using this to create another shared library called "libAnimation.so", which will be used by another project. Now, the second library "libAnimation.so" can't be used in test code correctly. So I doubt that the creation of the second lib "libAnimation.so" is right. The gcc command to create this lib is
g++ -g -shared -Wl,-soname,libwiston.so -o libAnimation.so $(objs) -lc".
Has someone come across this problem?
That looks like a weird link line - you are creating libAnimation.so, but its internal DT_SONAME name is libwiston.so.
I don't think that what you wanted to do. Don't you want to link libAnimation.so against libwiston.so (-lwiston)?
g++ -g -shared -o libAnimation.so $(objs) -lc -lwiston
I think it would be easier to wrap your build in automake/autoconf and rely on libtool to get the shared library creation correct.
I'll do a humble review on the process of creating shared libraries.
Let's begin by creating libwiston.so. First we implement the function we would like to export and then define it on a header so other programs knows how to call it.
/* file libwiston.cpp
* Implementation of hello_wiston(), called by libAnimation.so
*/
#include "libwiston.h"
#include <iostream>
int hello_wiston(std::string& msg)
{
std::cout << msg << std::endl;
return 0;
}
and:
/* file libwiston.h
* Exports hello_wiston() as a C symbol.
*/
#include <string>
extern "C" {
int hello_wiston(std::string& msg);
};
This code can be compiled with: g++ libwiston.cpp -o libwiston.so -shared
Now we implement the second shared library, named libAnimation.so that calls the function exported by the first library.
/* file libAnimation.cpp
* Implementation of call_wiston().
* This function is a simple wrapper around hello_wiston().
*/
#include "libAnimation.h"
#include "libwiston.h"
#include <iostream>
int call_wiston(std::string& param)
{
hello_wiston(param);
return 0;
}
and header:
/* file libAnimation.h
* Exports call_wiston() as a C symbol.
*/
#include <string>
extern "C" {
int call_wiston(std::string& param);
};
Compile it with: g++ libAnimation.cpp -o libAnimation.so -shared -L. -lwiston
Finally, we create a small application to test libAnimation.
/* file demo.cpp
* Implementation of the test application.
*/
#include "libAnimation.h"
int main()
{
std::string msg = "hello stackoverflow!";
call_wiston(msg);
}
And compile it with: g++ demo.cpp -o demo -L. -lAnimation
There's an interesting tool named nm that you can use to list the symbols exported by your shared library. Using these examples, you could execute the following commands to check for the symbols:
nm libAnimation.so | grep call_wiston
outputs:
00000634 t _GLOBAL__I_call_wiston
000005dc T call_wiston
and also:
nm libwiston.so | grep hello_wiston
outputs:
0000076c t _GLOBAL__I_hello_wiston
000006fc T hello_wiston