(已解决)C++ 加载一个具有依赖项的共享 Object 并访问它们的函数
(solved) C++ Load one Shared Object with dependencies and access their functions
- OS Linux Ubuntu 18.04, gcc 7.4.0
一个程序应该加载一个依赖于其他 SO 的 SO。所有 SO 的所有导出函数都应由程序调用。
我发现了几个相关问题,但 none 明确处理了这种情况。
这是我正在尝试做的一个例子:
共有 3 个共享库和 1 个应用程序:
libtest1.so has a extern "C" print function
libtest2.so has a base class "Base" with a function usig the C-function
libtest3.so has a derived class "Test" with a function using the C-function-
DllTest Application: loads the *.so's
这是应用程序应该做的
pHandle = OpenSharedLib("./libtest1.so"); # works
OpenSymbol(pHandle, "GetName"); # works
CloseLib(pHandle);
pHandle = OpenSharedLib("./libtest2.so"); # error
OpenSymbol(pHandle, "GetName");
OpenSymbol(pHandle, "printBase");
CloseLib(pHandle);
pHandle = OpenSharedLib("./libtest3.so");
OpenSymbol(pHandle, "GetName");
OpenSymbol(pHandle, "printBase");
OpenSymbol(pHandle, "print");
CloseLib(pHandle);
错误是 dlopen() 由于未定义的符号而未能加载:./libtest2.so: undefined symbol: GetName"
。 nm 输出显示符号丢失,但我没有找到如何防止它。
基本思想是有一个 'front SO' 收集所有类型的独立 SO 并为程序提供一个统一的库。在这个例子中,程序应该只加载 libtest3.so。然后它应该能够加载任何符号,只要它被任何单个 SO 公开。
我的问题:是否可以做我想做的事以及如何做?或者换句话说:我的错误是什么?
这是代码,下面给出了我用来编译它们的命令。
lib1.h, lib1.cpp for libtest1.so
lib2.h, lib2.cpp for libtest2.so
lib3.cpp for libtest3.so
DllTest.cpp the application
libtest1.so
header
extern "C" __attribute__((visibility("default"))) const char* GetName();
Cpp
#include <stdio.h>
#include <stdlib.h>
#include "lib1.h"
__attribute__((visibility("default"))) const char* GetName()
{
return "Hello from lib1";
}
编译为
g++ -c -fvisibility=hidden -fPIC -o lib1.o lib1.cpp
g++ -shared -o libtest1.so lib1.o
libtest2.so
Header
#include <stdio.h>
#include <stdlib.h>
class __attribute__((visibility("default"))) Base
{
public:
Base();
virtual ~Base();
const char* printBase();
int nTest;
};
Cpp
#include <stdio.h>
#include <stdlib.h>
#include "lib2.h"
#include "lib1.h" // for GetName()
Base::Base()
{ nTest=1; }
Base::~Base()
{ }
const char* Base::printBase()
{ return GetName(); }
编译为
g++ -c -fvisibility=hidden -fPIC -o lib2.o lib2.cpp
g++ -shared -o libtest2.so -L. -ltest1 lib2.o
libtest3.so
#include <stdio.h>
#include <stdlib.h>
#include "lib1.h"
#include "lib2.h"
class __attribute__((visibility("default"))) Test : public Base
{
public:
Test();
virtual ~Test();
const char* print();
};
Test::Test()
{ }
Test::~Test()
{ }
const char* Test::print() {
char* pChar = (char*)GetName();
printf( "hello from lib3: %d", nTest);
return "test3";
}
已编译
g++ -c -fvisibility=hidden -fPIC -o lib3.o lib3.cpp
g++ -shared -o libtest3.so -L. -ltest1 -ltest2 lib3.o
** 正在加载应用程序 **
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <dlfcn.h>
void OpenSymbol(void* pHandle, char* strName)
{
typedef char* (*pfnChar)(void);
pfnChar pFunction = NULL;
char* cError;
printf(" Find symbol %s\n", strName);
dlerror();
pFunction = (pfnChar)dlsym( pHandle, strName );
cError = dlerror();
if (cError != 0) {
std::cout << cError << std::endl;
exit(1); }
printf(" Exec symbol: %p\n", pFunction );
std::cout << pFunction() << std::endl;
}
void* OpenSharedLib(char* strName)
{
void* pHandle;
char* cError;
printf(" open lib %s\n", strName);
dlerror();
pHandle = dlopen( strName, RTLD_NOW );
cError = dlerror();
if (cError != 0) {
std::cout << cError << std::endl;
exit(1); }
printf(" found DLL %p\n", pHandle );
return pHandle;
}
void* CloseLib(void* pHandle)
{ dlclose(pHandle); }
main()
{
void* pHandle;
pHandle = OpenSharedLib("./libtest1.so");
OpenSymbol(pHandle, "GetName");
CloseLib(pHandle);
pHandle = OpenSharedLib("./libtest2.so");
OpenSymbol(pHandle, "GetName");
OpenSymbol(pHandle, "printBase");
CloseLib(pHandle);
pHandle = OpenSharedLib("./libtest3.so");
OpenSymbol(pHandle, "GetName");
OpenSymbol(pHandle, "printBase");
OpenSymbol(pHandle, "print");
CloseLib(pHandle);
std::cout << "done" << std::endl;
}
运行 nm -DC
表明对于最后两个库,一些符号没有导出。
- 符号libtest1.so:
...
000000000000057a T GetName
- 符号libtest2.so:
...
U GetName
U operator delete(void*, unsigned long)
000000000000094c T Base::printBase()
00000000000008da T Base::Base()
00000000000008da T Base::Base()
0000000000000920 T Base::~Base()
0000000000000902 T Base::~Base()
0000000000000902 T Base::~Base()
0000000000200e08 V typeinfo for Base
0000000000000969 V typeinfo name for Base
0000000000200de8 V vtable for Base
U vtable for __cxxabiv1::__class_type_info
- 符号libtest3.so:
...
U GetName
U printf
U operator delete(void*, unsigned long)
U Base::Base()
U Base::~Base()
0000000000000ab2 T Test::print()
0000000000000a2a T Test::Test()
0000000000000a2a T Test::Test()
0000000000000a86 T Test::~Test()
0000000000000a58 T Test::~Test()
0000000000000a58 T Test::~Test()
U typeinfo for Base
0000000000200df0 V typeinfo for Test
0000000000000b0f V typeinfo name for Test
0000000000200dd0 V vtable for Test
U vtable for __cxxabiv1::__si_class_type_info
- 最后,输出DllTest
open lib ./libtest1.so
found DLL 0x55965d711ea0
Find symbol GetName
Exec symbol: 0x7f902c38157a
Hello from lib1
open lib ./libtest2.so
./libtest2.so: undefined symbol: GetName
选择答案后编辑
代码无法正常工作有两个主要问题。首先加载失败是因为动态加载器没有找到依赖的sharedobject,即加载libtest2.so
时定位不到libtest1.so
,加载libtest3.so
时定位不到libtest2.so
和 libtest1.so
。这可以通过在链接期间添加路径来解决。其次,访问像 类 这样的非 extern "C"
object 需要一个必须在共享 object 中创建的 object。因此必须重构lib2.h/cpp
、lib2.h/cpp
和DllTest.cpp
。
为方便起见,这里是完整的编译序列以及答案中的更正
g++ -c -fvisibility=hidden -fPIC -o lib1.o lib1.cpp
g++ -shared -o libtest1.so lib1.o
g++ -c -fvisibility=hidden -fPIC -o lib2.o lib2.cpp
g++ -shared -o libtest2.so -Wl,-rpath,$PWD -L.lib2.o -ltest1
g++ -c -fvisibility=hidden -fPIC -o lib3.o lib3.cpp
g++ -shared -o libtest3.so -Wl,-rpath,$PWD -L. lib3.o -ltest1 -ltest2
g++ -o DllTest DllTest.cpp -ldl
有了这个,代码允许使用 const char* GetName()
函数,无论三个共享 object 被加载。
首先,更改 link-commands
中参数的顺序
g++ -g -shared -o libtest2.so -L. lib2.o -ltest1
g++ -g -shared -o libtest3.so -L. lib3.o -ltest1 -ltest2
注意:every linker 没有必要,但较新的 gold
linker 只解析 “从左到右”。 还有一个 -Wl,--no-undefined
选项,这对捕获这些错误非常有用。
然后将rpath
添加到这些命令中,以便共享对象找到它们的依赖项运行-time:
g++ -g -shared -o libtest2.so "-Wl,-rpath,${PWD}" -L. lib2.o -ltest1
g++ -g -shared -o libtest3.so "-Wl,-rpath,${PWD}" -L. lib3.o -ltest1 -ltest2
在 linkage 之后,readelf -d
应该像这样显示 RPATH:
$ readelf -d libtest3.so |head -n10
Dynamic section at offset 0xdd8 contains 28 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libtest1.so]
0x0000000000000001 (NEEDED) Shared library: [libtest2.so]
0x0000000000000001 (NEEDED) Shared library: [libstdc++.so.6]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
0x000000000000001d (RUNPATH) Library runpath: [/home/projects/proba/CatMan]
(还有libtool处理共享库。)
请注意,由于 name-mangling,应执行以下更改(尽管它是特定于编译器的)
- OpenSymbol(pHandle, "printBase");
+ OpenSymbol(pHandle, "_ZN4Base9printBaseEv");
- OS Linux Ubuntu 18.04, gcc 7.4.0
一个程序应该加载一个依赖于其他 SO 的 SO。所有 SO 的所有导出函数都应由程序调用。 我发现了几个相关问题,但 none 明确处理了这种情况。
这是我正在尝试做的一个例子: 共有 3 个共享库和 1 个应用程序:
libtest1.so has a extern "C" print function
libtest2.so has a base class "Base" with a function usig the C-function
libtest3.so has a derived class "Test" with a function using the C-function-
DllTest Application: loads the *.so's
这是应用程序应该做的
pHandle = OpenSharedLib("./libtest1.so"); # works
OpenSymbol(pHandle, "GetName"); # works
CloseLib(pHandle);
pHandle = OpenSharedLib("./libtest2.so"); # error
OpenSymbol(pHandle, "GetName");
OpenSymbol(pHandle, "printBase");
CloseLib(pHandle);
pHandle = OpenSharedLib("./libtest3.so");
OpenSymbol(pHandle, "GetName");
OpenSymbol(pHandle, "printBase");
OpenSymbol(pHandle, "print");
CloseLib(pHandle);
错误是 dlopen() 由于未定义的符号而未能加载:./libtest2.so: undefined symbol: GetName"
。 nm 输出显示符号丢失,但我没有找到如何防止它。
基本思想是有一个 'front SO' 收集所有类型的独立 SO 并为程序提供一个统一的库。在这个例子中,程序应该只加载 libtest3.so。然后它应该能够加载任何符号,只要它被任何单个 SO 公开。
我的问题:是否可以做我想做的事以及如何做?或者换句话说:我的错误是什么?
这是代码,下面给出了我用来编译它们的命令。
lib1.h, lib1.cpp for libtest1.so
lib2.h, lib2.cpp for libtest2.so
lib3.cpp for libtest3.so
DllTest.cpp the application
libtest1.so
header
extern "C" __attribute__((visibility("default"))) const char* GetName();
Cpp
#include <stdio.h>
#include <stdlib.h>
#include "lib1.h"
__attribute__((visibility("default"))) const char* GetName()
{
return "Hello from lib1";
}
编译为
g++ -c -fvisibility=hidden -fPIC -o lib1.o lib1.cpp
g++ -shared -o libtest1.so lib1.o
libtest2.so
Header
#include <stdio.h>
#include <stdlib.h>
class __attribute__((visibility("default"))) Base
{
public:
Base();
virtual ~Base();
const char* printBase();
int nTest;
};
Cpp
#include <stdio.h>
#include <stdlib.h>
#include "lib2.h"
#include "lib1.h" // for GetName()
Base::Base()
{ nTest=1; }
Base::~Base()
{ }
const char* Base::printBase()
{ return GetName(); }
编译为
g++ -c -fvisibility=hidden -fPIC -o lib2.o lib2.cpp
g++ -shared -o libtest2.so -L. -ltest1 lib2.o
libtest3.so
#include <stdio.h>
#include <stdlib.h>
#include "lib1.h"
#include "lib2.h"
class __attribute__((visibility("default"))) Test : public Base
{
public:
Test();
virtual ~Test();
const char* print();
};
Test::Test()
{ }
Test::~Test()
{ }
const char* Test::print() {
char* pChar = (char*)GetName();
printf( "hello from lib3: %d", nTest);
return "test3";
}
已编译
g++ -c -fvisibility=hidden -fPIC -o lib3.o lib3.cpp
g++ -shared -o libtest3.so -L. -ltest1 -ltest2 lib3.o
** 正在加载应用程序 **
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <dlfcn.h>
void OpenSymbol(void* pHandle, char* strName)
{
typedef char* (*pfnChar)(void);
pfnChar pFunction = NULL;
char* cError;
printf(" Find symbol %s\n", strName);
dlerror();
pFunction = (pfnChar)dlsym( pHandle, strName );
cError = dlerror();
if (cError != 0) {
std::cout << cError << std::endl;
exit(1); }
printf(" Exec symbol: %p\n", pFunction );
std::cout << pFunction() << std::endl;
}
void* OpenSharedLib(char* strName)
{
void* pHandle;
char* cError;
printf(" open lib %s\n", strName);
dlerror();
pHandle = dlopen( strName, RTLD_NOW );
cError = dlerror();
if (cError != 0) {
std::cout << cError << std::endl;
exit(1); }
printf(" found DLL %p\n", pHandle );
return pHandle;
}
void* CloseLib(void* pHandle)
{ dlclose(pHandle); }
main()
{
void* pHandle;
pHandle = OpenSharedLib("./libtest1.so");
OpenSymbol(pHandle, "GetName");
CloseLib(pHandle);
pHandle = OpenSharedLib("./libtest2.so");
OpenSymbol(pHandle, "GetName");
OpenSymbol(pHandle, "printBase");
CloseLib(pHandle);
pHandle = OpenSharedLib("./libtest3.so");
OpenSymbol(pHandle, "GetName");
OpenSymbol(pHandle, "printBase");
OpenSymbol(pHandle, "print");
CloseLib(pHandle);
std::cout << "done" << std::endl;
}
运行 nm -DC
表明对于最后两个库,一些符号没有导出。
- 符号libtest1.so:
...
000000000000057a T GetName
- 符号libtest2.so:
...
U GetName
U operator delete(void*, unsigned long)
000000000000094c T Base::printBase()
00000000000008da T Base::Base()
00000000000008da T Base::Base()
0000000000000920 T Base::~Base()
0000000000000902 T Base::~Base()
0000000000000902 T Base::~Base()
0000000000200e08 V typeinfo for Base
0000000000000969 V typeinfo name for Base
0000000000200de8 V vtable for Base
U vtable for __cxxabiv1::__class_type_info
- 符号libtest3.so:
...
U GetName
U printf
U operator delete(void*, unsigned long)
U Base::Base()
U Base::~Base()
0000000000000ab2 T Test::print()
0000000000000a2a T Test::Test()
0000000000000a2a T Test::Test()
0000000000000a86 T Test::~Test()
0000000000000a58 T Test::~Test()
0000000000000a58 T Test::~Test()
U typeinfo for Base
0000000000200df0 V typeinfo for Test
0000000000000b0f V typeinfo name for Test
0000000000200dd0 V vtable for Test
U vtable for __cxxabiv1::__si_class_type_info
- 最后,输出DllTest
open lib ./libtest1.so
found DLL 0x55965d711ea0
Find symbol GetName
Exec symbol: 0x7f902c38157a
Hello from lib1
open lib ./libtest2.so
./libtest2.so: undefined symbol: GetName
选择答案后编辑
代码无法正常工作有两个主要问题。首先加载失败是因为动态加载器没有找到依赖的sharedobject,即加载libtest2.so
时定位不到libtest1.so
,加载libtest3.so
时定位不到libtest2.so
和 libtest1.so
。这可以通过在链接期间添加路径来解决。其次,访问像 类 这样的非 extern "C"
object 需要一个必须在共享 object 中创建的 object。因此必须重构lib2.h/cpp
、lib2.h/cpp
和DllTest.cpp
。
为方便起见,这里是完整的编译序列以及答案中的更正
g++ -c -fvisibility=hidden -fPIC -o lib1.o lib1.cpp
g++ -shared -o libtest1.so lib1.o
g++ -c -fvisibility=hidden -fPIC -o lib2.o lib2.cpp
g++ -shared -o libtest2.so -Wl,-rpath,$PWD -L.lib2.o -ltest1
g++ -c -fvisibility=hidden -fPIC -o lib3.o lib3.cpp
g++ -shared -o libtest3.so -Wl,-rpath,$PWD -L. lib3.o -ltest1 -ltest2
g++ -o DllTest DllTest.cpp -ldl
有了这个,代码允许使用 const char* GetName()
函数,无论三个共享 object 被加载。
首先,更改 link-commands
中参数的顺序g++ -g -shared -o libtest2.so -L. lib2.o -ltest1
g++ -g -shared -o libtest3.so -L. lib3.o -ltest1 -ltest2
注意:every linker 没有必要,但较新的 gold
linker 只解析 “从左到右”。 还有一个 -Wl,--no-undefined
选项,这对捕获这些错误非常有用。
然后将rpath
添加到这些命令中,以便共享对象找到它们的依赖项运行-time:
g++ -g -shared -o libtest2.so "-Wl,-rpath,${PWD}" -L. lib2.o -ltest1
g++ -g -shared -o libtest3.so "-Wl,-rpath,${PWD}" -L. lib3.o -ltest1 -ltest2
在 linkage 之后,readelf -d
应该像这样显示 RPATH:
$ readelf -d libtest3.so |head -n10
Dynamic section at offset 0xdd8 contains 28 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libtest1.so]
0x0000000000000001 (NEEDED) Shared library: [libtest2.so]
0x0000000000000001 (NEEDED) Shared library: [libstdc++.so.6]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
0x000000000000001d (RUNPATH) Library runpath: [/home/projects/proba/CatMan]
(还有libtool处理共享库。)
请注意,由于 name-mangling,应执行以下更改(尽管它是特定于编译器的)
- OpenSymbol(pHandle, "printBase");
+ OpenSymbol(pHandle, "_ZN4Base9printBaseEv");