Разрешение вторичной зависимости С++ с помощью пути выполнения

У меня есть следующая проблема:

Я пытаюсь создать программное обеспечение на Ubuntu18, используя компилятор gnu по умолчанию (gcc-7) и доступные версии компоновщика.

Теперь у нас есть случай, когда исполняемый файл может связать общую библиотеку, которая снова связывает другие общие библиотеки. Таким образом, исполняемый файл имеет вторичную зависимость. Но вторичные зависимости берутся только из rpath, а не из runpath. Таким образом, даже если вторичная зависимость будет помещена в папку пути выполнения исполняемого файла, она не будет найдена.

Проблема была выявлена ​​при использовании googletest в сочетании с cmake. Там тесты не могут быть скомпилированы из-за ненайденных вторичных зависимостей. Так что установка LD_LIBRARY_PATH может быть не вариантом или, по крайней мере, сильно все усложняет.

Вот пример того, что не так с gcc-7 в системе Ubuntu18 (но отлично работает с gcc-5 в Ubuntu16):

вторичный.hpp

#include <string>

class World{
    public:
        std::string world();
};

вторичный.cpp

#include "secondary.hpp"

std::string World::world(){
    return "world";
}

primary.hpp

#include <string>

class Hello{
  public:
    std::string helloWorld();
};

primary.cpp

#include "primary.hpp"
#include "secondary.hpp"
#include <sstream>

std::string Hello::helloWorld(){
    std::stringstream strm;
    World world;

    strm << "hello ";
    strm << world.world();
    strm << "!";

    return strm.str();
}

и скомпилируйте обе библиотеки, как это

mkdir build
cd build
gcc -shared -o libsecondary.so -fPIC ../secondary.cpp
gcc -shared -o libprimary.so -fPIC ../primary.cpp  -lsecondary -L$(pwd)

Если я сейчас соберу исполняемый файл из следующих файлов

main.cpp

#include "primary.hpp"
#include <iostream>

int main(int argc, char** argv){
    Hello hello;
    std::cout << hello.helloWorld() << std::endl;
}

выполнив следующие действия в новом каталоге сборки (параллельно предыдущему)

cd ..
mkdir build_app
cd build_app
gcc -o app ../main.cpp -L../build -lstdc++ -lsecondary -lprimary  -Wl,-rpath=$(pwd)/../build

Если я сейчас сделаю ldd app, то увижу, что libsecondary.so не найдено

    linux-vdso.so.1 (0x00007fff0cf8c000)
    libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f9e86fe7000)
    libprimary.so => <some_path>/libprimary.so (0x00007f9e86de5000)
    libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f9e86bcd000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f9e867dc000)
    libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f9e8643e000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f9e87573000)
    libsecondary.so => not found

и путь выполнения приложения (запустив readelf -d app)

Dynamic section at offset 0x1d40 contains 31 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libstdc++.so.6]
 0x0000000000000001 (NEEDED)             Shared library: [libprimary.so]
 0x0000000000000001 (NEEDED)             Shared library: [libgcc_s.so.1]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 0x000000000000001d (RUNPATH)            Library runpath:[<some_path>/build]

Вопрос в том, почему ld не берет libsecondary.so с пути следования app? И libprimary.so, и libsecondary.so находятся в одной папке, но найден только libprimary.so.

Одним из решений проблемы является компиляция с --disable-new-dtags, которая будет использовать rpath вместо runpath и является значением по умолчанию для gcc-5 в Ubuntu16. Однако использование rpath кажется устаревшим из ld.

Так как же правильно использовать runpath вместе со вторичными зависимостями?


person Stefan K.    schedule 09.12.2019    source источник


Ответы (1)


libprimary отвечает за загрузку libsecondary, а не app. Это означает, что запись DT_RUNPATH для app не применяется.

Поскольку у вас есть контроль над всеми библиотеками, вы также можете добавить -Wl,-rpath=... в операторы компиляции для libprimary и libsecondary.

person Botje    schedule 09.12.2019
comment
Это правда, что это может решить проблему. Однако обычно я могу перемещать библиотеки, поэтому установка rpath (или пути выполнения) для libprimary будет работать, только если я не решу переместить libsecondary. По сути, это правда, если библиотека была скомпилирована в другой системе (например, в конвейере CI), я все еще не понимаю, что rpath выбирает любую библиотеку (также транзитивную), а runpath - нет. Не уверен, почему поведение такое разное - person Stefan K.; 09.12.2019
comment
Изменить На самом деле это правильный ответ, его также можно найти в руководстве по ld: man7.org/linux/man-pages/man8/ld.so.8.html - person Stefan K.; 09.01.2020
comment
Что если у нас нет контроля над libsecondary и это сторонняя библиотека? - person Ali Kanat; 02.08.2021
comment
Пожалуйста, задайте новый вопрос (со ссылкой на этот) вместо того, чтобы комментировать вопрос годовой давности. - person Botje; 02.08.2021