#c #linux #gcc #g
Вопрос:
Проверка Кода, не зависящего от позиции
Краткие сведения
Операционная система-linux. В нашем программном обеспечении у нас есть следующая проблема: основной исполняемый файл, назовем его MainProcess, связан со статической библиотекой (например, libstatic), которая, в свою очередь, зависит от libstdc (для вызовов cout). Он загружает плагины, которые также связаны с libstdc , через dlopen. Однако MainProcess загружает плагины с помощью RTLD_DEEPBIND, так как и плагин, и MainProcess зависят от одной и той же внешней библиотеки (opencv), но разных ее версий!
Я обнаружил, что если я не скомпилирую MainProcess и libstatic с помощью PIC, то он вылетает, когда загружается общая библиотека, называемая libshared, и std::cout вызывается из libshared
Вопрос
Может ли кто-нибудь пролить свет на то, почему мне нужен PIC в главном процессе, чтобы заставить std::cout работать с RTLD_DEEPBIND? Ниже я написал то, что узнал, я хотел бы подробнее рассказать о том, как PIC и RTLD_DEEPBIND влияют друг на друга.
Я читал, что для плагинов следует избегать привязки RTLD_DEEP, но нам действительно нужна привязка RTLD_DEEP, как я объяснил выше.
Дизайн теста
тест кода, не зависящего от позиции, предназначен для устранения проблемы следующим образом:
- Основной процесс, похожий на наш основной исполняемый файл. Он статически связывается с libstatic, а также использует dlopen для загрузки libshared
- libstatic имеет функцию printStatic (), которая вызывает std::cout
- в libshared есть функция printShared (), которая также вызывает std::cout
Полный код указан в конце поста
Сборник
MainProcess и libstatic компилируются двумя способами:
- либстатический-нопический, основной процесс-нопический. В самом CMakeLists.txt для этих целей устанавливается значение set(CMAKE_POSITION_INDEPENDENT_CODE ВЫКЛ.)
- libstatic-pic, mainProcess-pic. In the CMakeLists.txt set(CMAKE_POSITION_INDEPENDENT_CODE ON) is set for these targets
With PIC
mainProcess-pic runs without problems
The static library libstatic-pic is linked to mainProcess-pic and printStatic() is called:
void printStatic()
{
printf("printStatic Cn");
std::cout << "printStatic C " << std::endl;
}
The shared object liblibshared.so is loaded by dlopen and the function printShared() is called.
It’s definition is
void printShared()
{
printf("printShared Cn");
std::cout << "printShared C " << std::endl;
}
The output of mainProcess-pic is:
./mainProcess-pic
printStatic C
printStatic C
printShared C
printShared C
Thus neither printf() nor std::cout have problems being loaded twice (through libstatic and dynamically through libshared) into memory
Without PIC
In CMakeLists.txt the targets mainProcess-nopic and libstatic-nopic are compiled without position independent code. When mainProcess-nopic it crashes when the library libshared is loaded and printShared() is called:
./mainProcess-nopic
printStatic C
printStatic C
printShared C
Segmentation fault (core dumped)
The crash happens at std::cout in printShared(). This is because printStatic and thus std::cout has been compiled/linked in a non position independent way so that the linker cannot relocate std::cout (from printStatic) when libshared is loaded with RTLD_DEEPBIND (remeber: without RTLD_DEEPBIND the cout in printShared would just be linked to the same code as cout in printStatic)
The effect of RTLD_DEEPBIND in dlopen
By prefixing mainProcess-(no)pic with LD_DEBUG=<ld options, e.g help> we get detailed infos on how the dynamic linker loads mainProcess.
With LD_DEBUG=scopes we get the order of scopes the dynamic linker searches for symbols when dlopen opens libshared.so
LD_DEBUG=scopes ./mainProcess-pic
42991: object=./liblibshared.so [0]
42991: scope 0: ./liblibshared.so /lib/x86_64-linux-gnu/libstdc .so.6 /lib/x86_64-linux-gnu/libc.so.6 /lib/x86_64-linux-gnu/libm.so.6 /lib64/ld-linux-x86-64.so.2 /lib/x86_64-linux-gnu/libgcc_s.so.1
42991: scope 1: ./mainProcess-pic /lib/x86_64-linux-gnu/libdl.so.2 /lib/x86_64-linux-gnu/libstdc .so.6 /lib/x86_64-linux-gnu/libc.so.6 /lib64/ld-linux-x86-64.so.2 /lib/x86_64-linux-gnu/libm.so.6 /lib/x86_64-linux-gnu/libgcc_s.so.1
...
printShared C
printShared C
Above we see that the libshared.so library (scope 0) is placed infront of mainProcess-pic (scope 1). Hence printShared and its dependency symbols (printf and cout) are looked up in libshared and its dependencies.
Without the RTLD_DEEPBIND parameter to dlopen in mainProcess.cpp the lookup order is reversed
46799: object=./liblibshared.so [0]
46799: scope 0: ./mainProcess-pic /lib/x86_64-linux-gnu/libdl.so.2 /lib/x86_64-linux-gnu/libstdc .so.6 /lib/x86_64-linux-gnu/libc.so.6 /lib64/ld-linux-x86-64.so.2 /lib/x86_64-linux-gnu/libm.so.6 /lib/x86_64-linux-gnu/libgcc_s.so.1
46799: scope 1: ./liblibshared.so /lib/x86_64-linux-gnu/libstdc .so.6 /lib/x86_64-linux-gnu/libc.so.6 /lib/x86_64-linux-gnu/libm.so.6 /lib64/ld-linux-x86-64.so.2 /lib/x86_64-linux-gnu/libgcc_s.so.1
46799:
...
printShared C
printShared C
Code
mainProcess.cpp
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
//printStatic is defined in libstatic.cpp
extern "C" void printStatic();
int main(int argv, char* argc[])
{
//Call the statically linked print function
printStatic();
//Load libshared
//Define function pointer for functions with signature void func()
typedef void (*printshared_t)();
//Load the shared object libshared
void *libhandle;
libhandle=dlopen("./liblibshared.so",RTLD_NOW|RTLD_DEEPBIND);
if(!libhandle)
{
printf("couldnt open lib: %sn",dlerror());
exit(-1);
}
//Reset the error cache
dlerror();
//Load the symbol printShared
printshared_t printSharedLib = (printshared_t)dlsym(libhandle, "printShared");
char* err= dlerror();
if(err!=0)
{
printf("failed to get symbol setGlobalMiddle: %sn",err);
exit(-1);
}
//call printShared in libshared library
printSharedLib();
//Unload the library
dlclose(libhandle);
return 0;
}
libstatic.cpp
#include <iostream>
#include <stdio.h>
extern "C"
{
void printStatic()
{
printf("printStatic Cn");
std::cout << "printStatic C " << std::endl;
}
}
libshared.cpp
#include <iostream>
#include <stdio.h>
extern "C"
{
void printShared()
{
printf("printShared Cn");
std::cout << "printShared C " << std::endl;
}
}
CMakeLists.txt
cmake_minimum_required(VERSION 3.3.1)
project(positionindependentcodetest)
set(CMAKE_CXX_STANDARD 11)
add_library(libshared MODULE libshared.cpp )
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
add_library(libstatic-pic STATIC libstatic.cpp )
add_executable(mainProcess-pic mainProcess.cpp)
target_link_libraries(mainProcess-pic libstatic-pic dl)
set(CMAKE_POSITION_INDEPENDENT_CODE OFF)
add_library(libstatic-nopic STATIC libstatic.cpp )
target_link_libraries(libstatic-nopic)
add_executable(mainProcess-nopic mainProcess.cpp)
target_link_libraries(mainProcess-nopic libstatic-nopic dl)
Строить
- Скопируйте приведенный выше пример кода в каталог positionIndependentCode
cd positionIndependentCode
mkdir build amp;amp; cd build
cmake .. amp;amp; make
Комментарии:
1. Наличие нескольких экземпляров libstdc .поэтому загрузка-верный способ потерпеть неудачу, постарайтесь этого избежать; также убедитесь, что все компоненты скомпилированы с одинаковыми параметрами компилятора, например
-D_THREAD_SAFE
(или-pthreads
), иначе они будут несовместимы.