有谁知道为什么在dlopen()中初始化的库会初始化主程序拥有的静态变量.主程序和共享库都有静态变量的副本,但由于某种原因,共享库重新初始化主程序的静态变量副本并对其进行破坏,在主程序试图破坏它时导致段错误.
这是在符号表中出现错误名称的情况吗?
symbols shared-libraries static-variables segmentation-fault dlopen
我正在尝试加载一个共享库(插件)我在Linux ARM平台下使用dlopen提供了(封闭源代码).我正在尝试加载这种方式:
void* handle = dlopen(<library_path>/<library_name>, RTLD_NOW);
Run Code Online (Sandbox Code Playgroud)
结果是此消息失败:
Failed to load <library_path>/<library_name>: undefined symbol: <symbol_name>.
Run Code Online (Sandbox Code Playgroud)
我试图用nm查看库内部,但似乎lib被剥离,找不到符号.我也试过使用readelf -s,事实上,我得到了这个结果:
12663: 00000000 0 NOTYPE GLOBAL DEFAULT UND <symbol_name>
Run Code Online (Sandbox Code Playgroud)
通过阅读,我得到readelf -s返回所有符号,包括在它引用的库中定义的那些符号.
这个问题的答案对我来说并不完全清楚:这是一个应该在库中的符号,它不存在,因为它是以错误的方式编译的,或者这是一个我应该在其他地方找到的符号?readelf -d的输出似乎表明我提供了所有需要的共享库.可能这个错误与我正在编译可执行文件的方式中的错误有关,或者这与加载程序无关?
另外,我读到了每列的含义,但这些值非常奇怪.你如何解释这个符号描述?为什么地址为0?为什么键入NOTYPE?
我浏览了下面的链接,通过它我了解了如何创建和使用共享库。 https://www.cprogramming.com/tutorial/shared-libraries-linux-gcc.html
Step 1: Compiling with Position Independent Code
$ gcc -c -Wall -Werror -fpic foo.c
Step 2: Creating a shared library from an object file
$ gcc -shared -o libfoo.so foo.o
Step 3: Linking with a shared library
$ gcc -L/home/username/foo -Wall -o test main.c -lfoo
Step 4: Making the library available at runtime
$ export LD_LIBRARY_PATH=/home/username/foo:$LD_LIBRARY_PATH
$ ./test
This is a shared library test...
Hello, I am a shared library
Run Code Online (Sandbox Code Playgroud)
但是,我有几个问题:
背景:
我发现自己有一个将C++ GNU/Linux应用程序移植到Windows上的令人尴尬的任务.此应用程序执行的操作之一是在特定路径上搜索共享库,然后使用posix dlopen()和dlsym()调用动态地加载它们中的类.我们有充分的理由以这种方式加载,我不会进入这里.
问题:
要使用dlsym()或GetProcAddress()动态发现由C++编译器生成的符号,必须使用extern"C"链接块对其进行解组.例如:
#include <list>
#include <string>
using std::list;
using std::string;
extern "C" {
list<string> get_list()
{
list<string> myList;
myList.push_back("list object");
return myList;
}
}
Run Code Online (Sandbox Code Playgroud)
此代码是完全有效的C++,可在Linux和Windows上的众多编译器上编译和运行.但是,它不能与MSVC一起编译,因为"返回类型无效C".我们提出的解决方法是更改函数以返回指向列表而不是列表对象的指针:
#include <list>
#include <string>
using std::list;
using std::string;
extern "C" {
list<string>* get_list()
{
list<string>* myList = new list<string>();
myList->push_back("ptr to list");
return myList;
}
}
Run Code Online (Sandbox Code Playgroud)
我一直在努力为GNU/Linux加载器找到一个最佳解决方案,它既可以使用新函数也可以使用旧的遗留函数原型,或至少检测何时遇到不推荐使用的函数并发出警告.如果代码在他们尝试使用旧库时只是分段,那对我们的用户来说是不合适的.我最初的想法是在调用get_list期间设置一个SIGSEGV信号处理程序(我知道这很icky - 我对更好的想法持开放态度).所以只是为了确认加载一个旧库会发生段错误,我认为我会使用旧的函数原型(返回列表对象)通过新的加载代码(期望指向列表的指针)运行库,令我惊讶的是刚刚工作.我的问题是为什么?
下面的加载代码适用于上面列出的两个函数原型.我已经确认它适用于使用gcc版本4.1.2和4.4.4的Fedora 12,RedHat 5.5和RedHawk 5.1.使用带有-shared和-fPIC的g ++编译库,并且可执行文件需要与dl(-ldl)链接.
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <list>
#include <string>
using std::list;
using std::string;
int …Run Code Online (Sandbox Code Playgroud) 操作系统是MacOS X,特别是PowerPC G4上的10.5(Leopard),但我在运行10.6的x86上遇到了同样的问题.
我正在编写一个动态加载DLL的应用程序.DLL(让我们称之为foo.dylib)是另一个应用程序的一部分,位于硬盘的其他位置; 我的应用程序以foo.dylib编程方式找到(确切的安置可能会改变,可能用户通过运行的应用程序本身的GUI指定DLL路径).例如,假设我的应用程序位于目录中/Application/MyApp.app/Contents/MacOS,并且foo.dylib恰好位于/Application/OtherApp.app/Contents/MacOS.DLL加载使用dlopen().
现在,事实证明,foo.dylib它本身需要一堆其他DLL,它们位于同一目录中,但我事先并不知道.每个这样的额外DLL都foo.dylib以诸如的路径注册@executable_path/bar.dylib.语义@executable_path是它应该被找到当前进程可执行文件的目录替换.这适用于OtherApp,不适合我:当我打开时foo.dylib,它会尝试加载bar.dylib,并且它会查找/Application/MyApp.app/Contents/MacOS/bar.dylib,这不是正确的目录.
解决方法是将DYLD_FALLBACK_LIBRARY_PATH环境变量设置为/Application/OtherApp.app/Contents/MacOS,但这必须在启动我的应用程序之前完成(该环境变量仅由动态链接器读取一次;以编程方式更改其值setenv()或putenv()不起作用).这与foo.dylib文件位置的动态发现不兼容.
是否有一种编程方式来覆盖效果@executable_path?
我有一个Solaris进程,它是一个C++应用程序,由ld几个.so库加载.该应用程序有一个函数,它在调用函数中获取一个返回地址,然后尝试确定所述调用函数的名称.
如果我使用dladdr(3)它,它并不总是把我期望在Dl_info :: dli_sname中看到.看起来它返回的函数名称不是最接近下方或指针值.如果我取指针值并查看输出nm,我可以将该值与我期望的确切函数匹配.
我想知道是否有办法检索进程的符号映射,让它搜索函数名称而不使用dladdr(3).我特别感兴趣的是获得一个符号映射,不仅可以用于可执行文件本身,还可以用于.so它已加载的所有库.
我在Solaris10/SPARC上运行,我正在使用gcc 4.2.x.
谢谢!
我想我可以在共享库中实现类的一部分,只要在使用时加载符号.
myclass.h
---
class C {
void method();
}
main.cpp
---
#include "myclass.h"
int main() {
//dynamically load mylib.so using dlopen/dlsym/dlclose
...
C *c = new C();
c->method();
delete c;
}
mylib.so compiled separately:
====
mylib.cpp
---
#include "mylib.h"
void C::method() {
...
}
Run Code Online (Sandbox Code Playgroud)
这很好用.
但是一旦我完成了使用C :: method(),我想卸载它,所以我可以更改,重新编译并重新加载它而无需重新启动主程序
int main() {
//dynamically load mylib.so using dlopen/dlsym/dlclose
...
C *c = new C();
c->method();
delete c;
// close the lib, remove the handle...
....
char pause;
cin << pause; // before …Run Code Online (Sandbox Code Playgroud) 我正在使用dlopen在运行时加载共享库
dlopen("SharedLibarary1.so", RTLD_NOW | RTLD_GLOBAL);
Run Code Online (Sandbox Code Playgroud)
在该共享对象中,我引用另一个共享库"SharedLibarary2.so"中定义的const char*.
Executable和两个库都是使用-rdynamic构建的.
但是在使用dlopen时我仍然遇到运行时错误:"/ usr/lib/SharedLibarary1.so:undefined symbol"并指向错位的const char*有未定义的符号.
Whith GDB"info share"我可以看到第二个库没有在错误点加载.
如果我在第一个库之前在第二个库上执行dlopen,那问题怎么会消失.
有没有更好的方法来强制加载器为未解析的符号加载第二个库?
假设我有一个带有GCC构造函数的libA.so.
我的程序"程序"依赖于libA.so,所以当我运行它时,libA.so被打开并且它的构造函数被执行.现在,我还有一个模块libC.so,它也依赖于libA.我跑dlopen("libC.so"),加载libC,并根据我的实验,也执行libA的构造函数.
依赖关系看起来像这样:
现在,当我运行程序时:
显然,当它们被加载到内存中时,dlopen会执行库的构造函数.这是在某处指定的,链接器如何检查哪些库已经加载?
(为什么我要问:在一个场合,我在一些未完全理解的情况下得到了一个两次执行的构造函数.我是否正确地假设这完全被破坏并且在正常情况下不应该发生?)
lib.so如果在运行时满足特定条件,则需要动态打开共享库。该库包含约700个函数,我需要加载所有符号。
一个简单的解决方案是定义指向其中包含的所有符号的函数指针lib.so,使用加载库dlopen,最后使用来获取所有符号的地址dlsym。但是,鉴于功能的数量,实现此解决方案的代码非常繁琐。
我想知道是否存在一个更优雅,更简洁的解决方案,也许是通过适当地使用宏来定义函数指针。谢谢!