- 作業系統 Linux Ubuntu 18.04,gcc 7.4.0
程式應該加載一個依賴于其他 SO 的 SO。程式應呼叫所有 SO 的所有匯出函式。我發現了幾個相關的問題,但沒有明確處理這種情況。
這是我想要做的一個例子:有 3 個共享庫和一個應用程式:
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 輸出顯示該符號丟失,但我沒有找到如何防止這種情況發生。
基本思想是擁有一個“前端 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
標題
extern "C" __attribute__((visibility("default"))) const char* GetName();
總價
#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
標題
#include <stdio.h>
#include <stdlib.h>
class __attribute__((visibility("default"))) Base
{
public:
Base();
virtual ~Base();
const char* printBase();
int nTest;
};
總價
#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
Edit after selected answer
There are two main issues that code is not working. First, loading fails because the dynamic loader does not find the dependent shared objects, i.e. when loading libtest2.so it fails to locate libtest1.so and when loading libtest3.so it fails to locate libtest2.so and libtest1.so. That can be fixed by adding the path during linking. Second, accessing non extern "C" objects like classes require an object that must be created in the shared object. Therefore lib2.h/cpp, lib2.h/cpp and DllTest.cpp must be refactored.
For convenience here is the full compilation sequence with the corrections from the answer
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
With this the code allows the use of the const char* GetName() function no matter which shared object of the three is loaded.
uj5u.com熱心網友回復:
首先,更改鏈接命令中引數的順序
g -g -shared -o libtest2.so -L. lib2.o -ltest1
g -g -shared -o libtest3.so -L. lib3.o -ltest1 -ltest2
注意:不是每個聯結器都需要,但較新的gold聯結器只決議“從左到右”。還有一個 -Wl,--no-undefined選項,這對于捕獲這些錯誤非常有用。
然后添加rpath到這些命令中,以便共享物件在運行時找到它們的依賴項:
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
鏈接后,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");
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/361069.html
