В настоящее время я пишу небольшое тестовое приложение для изучения статического связывания библиотек и доступа к их символам во время выполнения с помощью 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)
(хотя я тоже пробовал все эти вещи).
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*);
Я немного изменил свой ответ. Также см. «примечание» для Linux в учебнике относительно -rdynamic. Поскольку вы используете mingw/c++, это замечание, вероятно, применимо и к вашему примеру, поскольку mingw по сути является инструментом Linux для Windows.
Добавление
__declspec(dllexport)
не решает проблему. Как показано в учебник для Boost.DLL, действительно можно использовать статически связанные библиотеки; однако этот__declspec
используется, как вы сказали (макросBOOST_DLL_ALIAS
расширяется таким образом, но я считаю, что они делают это только для предоставления псевдонима), но даже его использование все равно не решает проблему. Я даже сделал то же, что и в учебнике, создавB.hpp
и включив этот заголовок в модульmain.cpp
(хотя я бы хотел этого избежать), но безуспешно.