Boost.DLL 无法使用静态链接库在可执行文件中找到符号

问题描述 投票:0回答:2

我目前正在编写一个小型测试应用程序,以探索静态链接库并在运行时使用 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)
这样的任何符号导出限定符(尽管我已经尝试了所有那些东西也是如此)。

c++ boost static-libraries debug-symbols
2个回答
1
投票

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*);

0
投票

此主题有任何更新吗? 我也有类似的实现,在linux下它工作得很好。 当我在 Windows 上运行使用 mingGW 构建的 .exe 时,我收到相同的消息“boost::dll::shared_library::get() failed: 找不到指定的过程 [system:127]”

当使用 boost 中的“Import_symbol”而不是 Get() 时,程序在访问对象时崩溃。

© www.soinside.com 2019 - 2024. All rights reserved.