如何调用通过使用 dlopen 加载共享对象 运行 时间创建的对象的方法
How to call methods of an object that is created by loading shared object run time using dlopen
我创建了一个名为 libmathClass.so 的测试库,我将从下面的代码中加载它。这个共享对象有一个 class 并且库调用被创建到这个 class 的 return 对象。
如何从下面显示的主要代码中调用此对象的方法。我从 ld(linker) 收到未定义的引用错误,因为它不知道方法的定义。
void* handle;
handle=dlopen("math1/libmathClass.so", RTLD_LAZY);
if(!handle)
{
cout<<"error loading library: "<<dlerror()<<endl;
exit(2);
}
else
{
cout<<"***libmathClass.so library load successful!"<<endl;
}
void* (*mathInit) ();
mathInit = (void* (*)())dlsym(handle, "CreateMathOperationInstance");
if(!mathInit)
{
cout<<"error loading instance method: "<<dlerror()<<endl;
exit(3);
}
else
{
cout<<"***method load successful!"<<endl;
}
mathOperationClass *mathInstance;
auto obj = (*mathInit)();
if(!obj)
{
cout<<"object is not created"<<endl;
exit(4);
}
else
{
cout<<"object created!!!"<<endl;
mathInstance = reinterpret_cast<mathOperationClass *>(obj);
}
int num1 = atoi(argv[1]);
int num2 = atoi(argv[2]);
cout<< mathInstance->AddInt(num1, num2)<<endl;
我用来编译的命令 - g++ --std=c++11 -g -o dynamicTest dynamicMain.cpp -ldl
错误信息:
dynamicMain.cpp:54: 未定义对“mathOperationClass::AddInt(int, int)”的引用
collect2:错误:ld returned 1 退出状态
mathInit = (void* (*)())dlsym(handle, "CreateMathOperationInstance");
您在这里使用 dlsym()
在共享库中查找此符号。这必须是 C
linkage 的函数,因为符号名称没有被破坏。这很重要,当您盯着这一行时请记住这一点:
cout<< mathInstance->AddInt(num1, num2)<<endl;
这里,AddInt
是mathInstance
指向的class的一个方法。 class 方法只是另一个函数,只是它总是将隐藏的 this
指针作为额外参数。简而言之,这就是 class 方法,而典型的 C++ 实现实际上就是这种情况。 C++ 在技术上实际上并不需要这种情况。 C++ 实现可以自由地以任何方式实现方法,从而产生符合 C++ 规范的结果。但是,实际上,在典型的 C++ 实现中,这就是 class 方法的实际含义。具有引用为 this
.
的额外参数的函数
因此,从某种意义上说,上面这行基本上等同于:
cout<< mathOperationClass::AddInt(mathInstance, num1, num2)<<endl;
这基本上就是这里发生的事情,说得非常松散。
这个 mathOperationClass::AddInt
method/function 大概在您 dlopen
编辑的同一个共享库中;并且因为你 dlopen
-ed 它而你实际上并没有 link 它,你有一个对该符号的引用,并且这个引用无法在运行时解析,因此你的运行时未定义符号错误。
如果您可以调用此 class 方法,唯一的方法就是同时使用 dlsym()
。但是,为了能够真正实现这一目标,即使是最轻微的祈祷,也需要恰到好处地发生一大堆事情。
首先,你得搞清楚actual mangled C++ symbol name。使用我的 Linux x86_64 g++ 编译器作为参考,此方法的损坏名称将是“_ZN18mathOperationClass6AddIntEii”。有了它,您可以使用 dlsym
在您的共享库中找到这个符号(或者您的 C++ 实现的这个方法的实际损坏的符号名称是什么)。
一旦你有了这个符号,现在怎么办?好吧,让我们希望你的 C++ 实现确实有一个可破解的 C++ ABI,你可以在其中通过显式传递一个额外的 this
参数来调用 class 方法,如下所示:
int (*addInt)(mathOperationClass *, int, int)=
reinterpret_cast<int (*)(mathOperationClass *, int, int)>
(dlsym(handle, "_ZN18mathOperationClass6AddIntEii"));
cout << (*addInt)(mathInstance, num1, num2) << endl;
除非可以确认可以在您的 C++ 实现的 ABI 中以这种骇人听闻的方式调用 C++ 方法,否则整个纸牌屋都会崩溃。因为你已经在使用 dlopen
() 你已经在非 portable 领域,使用你的 C++ 实现特定资源,所以你不妨弄清楚你的 C++ 方法是否可以这么叫的。如果没有,您将不得不弄清楚如何使用普通指针调用它们。
现在来点完全不同的东西...
综上所述:
有一种方法可以避免处理这种混乱情况:将此 class 方法设为虚拟 class 方法。虚拟 class 方法通过内部虚拟函数 table 分派。因此,只需尝试将此 AddInt
方法声明为虚拟 class 方法,然后按原样调用它。它很可能在您的 C++ 实现中起作用,因为在这种情况下,编译器不会发出 mathOperationClass::AddInt
的显式符号引用。它将通过虚拟函数 table 找到方法,该函数悄悄地附加到对象的每个实例。
当然,您还需要记住什么是虚函数,以及它们的含义。但是,在几乎所有情况下,这是调用从共享库动态加载的 classes 方法的一种非常便宜的方法。
我创建了一个名为 libmathClass.so 的测试库,我将从下面的代码中加载它。这个共享对象有一个 class 并且库调用被创建到这个 class 的 return 对象。 如何从下面显示的主要代码中调用此对象的方法。我从 ld(linker) 收到未定义的引用错误,因为它不知道方法的定义。
void* handle;
handle=dlopen("math1/libmathClass.so", RTLD_LAZY);
if(!handle)
{
cout<<"error loading library: "<<dlerror()<<endl;
exit(2);
}
else
{
cout<<"***libmathClass.so library load successful!"<<endl;
}
void* (*mathInit) ();
mathInit = (void* (*)())dlsym(handle, "CreateMathOperationInstance");
if(!mathInit)
{
cout<<"error loading instance method: "<<dlerror()<<endl;
exit(3);
}
else
{
cout<<"***method load successful!"<<endl;
}
mathOperationClass *mathInstance;
auto obj = (*mathInit)();
if(!obj)
{
cout<<"object is not created"<<endl;
exit(4);
}
else
{
cout<<"object created!!!"<<endl;
mathInstance = reinterpret_cast<mathOperationClass *>(obj);
}
int num1 = atoi(argv[1]);
int num2 = atoi(argv[2]);
cout<< mathInstance->AddInt(num1, num2)<<endl;
我用来编译的命令 - g++ --std=c++11 -g -o dynamicTest dynamicMain.cpp -ldl
错误信息: dynamicMain.cpp:54: 未定义对“mathOperationClass::AddInt(int, int)”的引用 collect2:错误:ld returned 1 退出状态
mathInit = (void* (*)())dlsym(handle, "CreateMathOperationInstance");
您在这里使用 dlsym()
在共享库中查找此符号。这必须是 C
linkage 的函数,因为符号名称没有被破坏。这很重要,当您盯着这一行时请记住这一点:
cout<< mathInstance->AddInt(num1, num2)<<endl;
这里,AddInt
是mathInstance
指向的class的一个方法。 class 方法只是另一个函数,只是它总是将隐藏的 this
指针作为额外参数。简而言之,这就是 class 方法,而典型的 C++ 实现实际上就是这种情况。 C++ 在技术上实际上并不需要这种情况。 C++ 实现可以自由地以任何方式实现方法,从而产生符合 C++ 规范的结果。但是,实际上,在典型的 C++ 实现中,这就是 class 方法的实际含义。具有引用为 this
.
因此,从某种意义上说,上面这行基本上等同于:
cout<< mathOperationClass::AddInt(mathInstance, num1, num2)<<endl;
这基本上就是这里发生的事情,说得非常松散。
这个 mathOperationClass::AddInt
method/function 大概在您 dlopen
编辑的同一个共享库中;并且因为你 dlopen
-ed 它而你实际上并没有 link 它,你有一个对该符号的引用,并且这个引用无法在运行时解析,因此你的运行时未定义符号错误。
如果您可以调用此 class 方法,唯一的方法就是同时使用 dlsym()
。但是,为了能够真正实现这一目标,即使是最轻微的祈祷,也需要恰到好处地发生一大堆事情。
首先,你得搞清楚actual mangled C++ symbol name。使用我的 Linux x86_64 g++ 编译器作为参考,此方法的损坏名称将是“_ZN18mathOperationClass6AddIntEii”。有了它,您可以使用 dlsym
在您的共享库中找到这个符号(或者您的 C++ 实现的这个方法的实际损坏的符号名称是什么)。
一旦你有了这个符号,现在怎么办?好吧,让我们希望你的 C++ 实现确实有一个可破解的 C++ ABI,你可以在其中通过显式传递一个额外的 this
参数来调用 class 方法,如下所示:
int (*addInt)(mathOperationClass *, int, int)=
reinterpret_cast<int (*)(mathOperationClass *, int, int)>
(dlsym(handle, "_ZN18mathOperationClass6AddIntEii"));
cout << (*addInt)(mathInstance, num1, num2) << endl;
除非可以确认可以在您的 C++ 实现的 ABI 中以这种骇人听闻的方式调用 C++ 方法,否则整个纸牌屋都会崩溃。因为你已经在使用 dlopen
() 你已经在非 portable 领域,使用你的 C++ 实现特定资源,所以你不妨弄清楚你的 C++ 方法是否可以这么叫的。如果没有,您将不得不弄清楚如何使用普通指针调用它们。
现在来点完全不同的东西...
综上所述:
有一种方法可以避免处理这种混乱情况:将此 class 方法设为虚拟 class 方法。虚拟 class 方法通过内部虚拟函数 table 分派。因此,只需尝试将此 AddInt
方法声明为虚拟 class 方法,然后按原样调用它。它很可能在您的 C++ 实现中起作用,因为在这种情况下,编译器不会发出 mathOperationClass::AddInt
的显式符号引用。它将通过虚拟函数 table 找到方法,该函数悄悄地附加到对象的每个实例。
当然,您还需要记住什么是虚函数,以及它们的含义。但是,在几乎所有情况下,这是调用从共享库动态加载的 classes 方法的一种非常便宜的方法。