Boost.DLL не может найти символы в исполняемом файле, используя статически связанную библиотеку

В настоящее время я пишу небольшое тестовое приложение для изучения статического связывания библиотек и доступа к их символам во время выполнения с помощью Boost.DLL. Я компилирую очень простую статическую библиотеку и связываю ее с очень простым приложением, используя MinGW-w64, и делаю это с помощью Makefile и mingw32-make.

A.cpp определяет класс A, который хранит и изменяет ссылку на int. Единственный модуль библиотеки B.cpp определяет подкласс A, называемый B, а также фабричный метод. Основной модуль определяет класс C, в котором хранится ссылка на экземпляр A, и пытается получить доступ к символам, определенным в B.cpp, чтобы сохранить экземпляр B в C. Когда я пытаюсь запустить приложение, выходной файл успешно загружается, но программе не удается найти статически связанные символы.

А.хпп:

#pragma once

#ifndef A_HPP_
#define A_HPP_

#include <memory>
#include <boost/dll/config.hpp>
#include <boost/dll.hpp>
#include <boost/function.hpp>
#include <boost/bind/bind.hpp>
#include <iostream>

class A {
protected:
    int *_p;
public:
    A(int *p = NULL);
    virtual ~A() = default;
    
    void setp(int *p);
    int deref();

    virtual void add();
};

#endif

B.cpp:

#include "A.hpp"

class B : public A {
public:
    B();
    ~B();

    void add();
};

const size_t B_SIZE = sizeof(B);
void B_get(void *addr) {
    B *tmp = new B();
    memcpy(addr, tmp, B_SIZE);
    delete tmp;
}

B::B() {}
B::~B() {}

void B::add() {
    if (_p != NULL)
        *_p = *_p + 100;
    std::cout << "B: _p is now " << *_p << std::endl;
}

main.cpp:

#include "A.hpp"

class C {
public:
    std::shared_ptr<A> a;

    C(std::shared_ptr<A> aparam, int *x) : a(aparam) { a->setp(x); }
    int deref() { return a->deref(); }
};

int main() {
    std::string loc = boost::dll::program_location().string();

    std::cout << "loading file '" << loc << "'" << std::endl;
    boost::dll::shared_library lib(loc);
    
    std::cout << "getting symbol 'B_SIZE'" << std::endl;
    size_t B_SIZE = lib.get<size_t>("B_SIZE");

    std::cout << "getting symbol 'B_get(void*)'" << std::endl;
    boost::function<void(void*)> B_get = lib.get<void(void*)>("B_get");
    
    std::cout << "getting instance of B" << std::endl;
    void *b = new char[B_SIZE];
    B_get(b);
    
    //assign x and C
    int x = 5;
    C c(std::shared_ptr<A>((A*)b), &x);

    std::cout << "x: " << x << std::endl;
    std::cout << "c.a->deref(): " << c.a->deref() << std::endl;

    std::cout << "executing c.a->add()" << std::endl;
    c.a->add();

    std::cout << "x: " << x << std::endl;
    std::cout << "c.a->deref(): " << c.a->deref() << std::endl;
    
    return 0;
}

Makefile:

#build executable
out:
    g++ -Wall -c -IC:/Unix/boost_1_78_0/ B.cpp -o B.o
    ar -rcs -o libB.a B.o
    g++ -Wall -IC:/Unix/boost_1_78_0/ -LC:/Unix/boost_1_78_0/stage/lib -L. main.cpp A.cpp -llibboost_filesystem-mgw8-mt-x64-1_78 -llibboost_system-mgw8-mt-x64-1_78 -Wl,-Bstatic -lB -o out

#build and run executable
run: out
    ./out

Выход:

C:\Unix\VSC\Static_Test>mingw32-make run
g++ -Wall -c -IC:/Unix/boost_1_78_0/ B.cpp -o B.o
ar -rcs -o libB.a B.o
g++ -Wall -IC:/Unix/boost_1_78_0/ -LC:/Unix/boost_1_78_0/stage/lib -L. main.cpp A.cpp -llibboost_filesystem-mgw8-mt-x64-1_78 -llibboost_system-mgw8-mt-x64-1_78 -Wl,-Bstatic -lB -o out
./out
loading file 'C:\Unix\VSC\Static_Test\out.exe'
getting symbol 'B_SIZE'
terminate called after throwing an instance of 'boost::wrapexcept<boost::system::system_error>'
  what():  boost::dll::shared_library::get() failed: The specified procedure could not be found [system:127]
mingw32-make: *** [Makefile:9: run] Error 3

Я использовал nm для проверки out.exe и libB.a, и все соответствующие символы для A, B и C определены, но символы для B отсутствуют в out.exe.

Я также пытался скомпилировать исполняемый файл с комбинациями флагов -g и -O0, но безуспешно.

Насколько я понимаю, заголовок для B.cpp, включенный в main.cppне должен, необходим для поиска символов, равно как и квалификаторы экспорта символов, такие как extern "C" или __declspec(dllexport) (хотя я тоже пробовал все эти вещи).

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
41
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

boost::DLL работает только с символами экспортируется. Ваши символы не экспортируются. «Экспорт» символов выходит за рамки языка C++. Это нужно сделать с помощью __declspec(dllexport) или аналогичного.

Статически связанные символы недоступны во время выполнения. Компоновщик отбрасывает их.


Ошибка говорит, что "B_SIZE" не найден. B_SIZE определяется в B.cpp как

const size_t B_SIZE = sizeof(B);

Этот символ никогда не экспортируется. Он статически связан с вашим .exe, и его определение будет удалено.

Вероятно, вы могли бы изменить это на

const size_t __declspec(dllexport) B_SIZE = sizeof(B);

чтобы заставить его экспортироваться (хотя экспорт переменной, а не функции, имеет свои проблемы). Затем, теоретически, компилятор должен пометить его как «экспортировать это», а команда lib ar должна учитывать эту информацию при создании статической библиотеки «B.o».

Наконец, когда неявный вызов компоновщика в вашей команде g++ -Wall ... exe -lB get выполняется, компоновщик должен связать статическую библиотеку «B.o» с вашим exe, найти флаг «пожалуйста, экспортируйте меня» и скопировать информацию о символе для B_SIZE в .exe.

Только если вы экспорт символ, информация будет записана в .dll. Тогда другие программы смогут найти его.


Еще одна вещь, скрытая в туториале по Boost DLL. В Windows __declspec(dllexport) экспортирует символы даже для файла .EXE. Поэтому, если вы статически свяжете свой B.o с .EXE, инструменты Windows будем учитывают тег dllexport и экспортируют символ. Это верно для Microsoft и подобных компоновщиков.

Небольшое примечание в руководстве по Linux говорит, что вы должны передать -rdynamic компоновщику Linux. Я предполагаю, что экспорт символов из .EXE является необычным (стандартным является .DLL), инструменты Linux могут игнорировать все теги dllexport и явно скрывать экспортированные символы из среды выполнения, если только вы явно не укажете им включить их, что для чего может быть -rdynamic.


Кстати, обычно это делается так:

#if defined(MYLIB_EXPORTS)
#define MYLIB_API __declspec(dllexport)
#else if defined(MYLIB_IMPORTS)
#define MYLIB_API __declspec(dllimport)
#else
#define MYLIB_API /* */
#endif

void MYLIB_API B_get(void*);

Добавление __declspec(dllexport) не решает проблему. Как показано в учебник для Boost.DLL, действительно можно использовать статически связанные библиотеки; однако этот __declspec используется, как вы сказали (макрос BOOST_DLL_ALIAS расширяется таким образом, но я считаю, что они делают это только для предоставления псевдонима), но даже его использование все равно не решает проблему. Я даже сделал то же, что и в учебнике, создав B.hpp и включив этот заголовок в модуль main.cpp (хотя я бы хотел этого избежать), но безуспешно.

user3658679 18.03.2022 01:05

Я немного изменил свой ответ. Также см. «примечание» для Linux в учебнике относительно -rdynamic. Поскольку вы используете mingw/c++, это замечание, вероятно, применимо и к вашему примеру, поскольку mingw по сути является инструментом Linux для Windows.

Hajo Kirchhoff 18.03.2022 10:10

Другие вопросы по теме