我目前正在编写一个小型测试应用程序,以探索静态链接库并在运行时使用 Boost.DLL 访问它们的符号。我正在编译一个非常简单的静态库,并将其与一个非常简单的应用程序链接,使用 MinGW-w64,并使用 Makefile 和
mingw32-make
来完成此操作。
A.cpp
定义了一个类 A
,用于存储和修改对 int
的引用。该库唯一的模块 B.cpp
定义了 A
的子类 B
以及工厂方法。主模块定义了一个类C
,它存储对A
实例的引用,并尝试访问B.cpp
中定义的符号以在B
中存储C
的实例。当我尝试运行该应用程序时,输出文件已成功加载,但程序无法找到静态链接符号。
A.hpp:
#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 仅适用于 exported 符号。 您的符号不会导出。 “导出”符号超出了 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下它工作得很好。 当我在 Windows 上运行使用 mingGW 构建的 .exe 时,我收到相同的消息“boost::dll::shared_library::get() failed: 找不到指定的过程 [system:127]”
当使用 boost 中的“Import_symbol”而不是 Get() 时,程序在访问对象时崩溃。