- OS Linux Ubuntu 18.04, gcc 7.4.0
A program should load one SO which is dependent on other SOs. All exported functions of all SOs should be called by the program. I found serveral related questions but none dealing explicitly with this situation.
Here is an exmple what I am trying to do: There are 3 shared libraries and one application:
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
This is what the application should to
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);
The error was that dlopen() failed to load due to an undefined symbol: ./libtest2.so: undefined symbol: GetName"
. The nm output shows that the symbol is missing, but I did not find out how I could prevent that.
The basic idea is to have a 'front SO' that is collecting all kind of seprate SO's and present a unified library to the program. In the example the program should only load libtest3.so. It should then be able to load any symbol as long as it was exposed by any of the single SOs.
My Question: Is it possible to do what I want and how? Or in other words: what is my error?
Here is the code and the commands I used to compile them is given below.
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";
}
compiled with
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(); }
compiled with
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";
}
Compiled
g -c -fvisibility=hidden -fPIC -o lib3.o lib3.cpp
g -shared -o libtest3.so -L. -ltest1 -ltest2 lib3.o
** Loading Application **
#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;
}
Running nm -DC
shows that for the last two libraries some symbold are not exported.
- symbols libtest1.so:
...
000000000000057a T GetName
- symbols 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
- symbols 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
- Finaly, the output 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.
CodePudding user response:
As a start, change the order of the arguments in the link-commands
g -g -shared -o libtest2.so -L. lib2.o -ltest1
g -g -shared -o libtest3.so -L. lib3.o -ltest1 -ltest2
Note: it's not necessary with every linker, but the newer gold
linker only resolves "from-left-to-right". Also there's a -Wl,--no-undefined
option, which is very useful to catch these error.
Then add rpath
into these commands so that the shared objects find their dependencies run-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
After linkage, readelf -d
should show the RPATH like this:
$ 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]
(Also there is libtool to deal with shared libraries.)
Mind you, because of name-mangling, the following change should be performed (though it is compiler-specific)
- OpenSymbol(pHandle, "printBase");
OpenSymbol(pHandle, "_ZN4Base9printBaseEv");