Странное взаимодействие RTLD_DEEPBIND, кода, не зависящего от позиции (PIC), и C STL (std::cout)

#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 компилируются двумя способами:

  1. либстатический-нопический, основной процесс-нопический. В самом CMakeLists.txt для этих целей устанавливается значение set(CMAKE_POSITION_INDEPENDENT_CODE ВЫКЛ.)
  2. 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)

 

Строить

  1. Скопируйте приведенный выше пример кода в каталог positionIndependentCode
  2. cd positionIndependentCode
  3. mkdir build amp;amp; cd build
  4. cmake .. amp;amp; make

Комментарии:

1. Наличие нескольких экземпляров libstdc .поэтому загрузка-верный способ потерпеть неудачу, постарайтесь этого избежать; также убедитесь, что все компоненты скомпилированы с одинаковыми параметрами компилятора, например -D_THREAD_SAFE (или -pthreads ), иначе они будут несовместимы.