3

我在尝试使用 librbd 时遇到了以下链接问题。

以下是我的代码片段。

  • 主文件
#include <iostream>
#include <rados/librados.hpp>
#include <rbd/librbd.hpp>

int main(){
    // Initialize and open an rbd image
    std::string pool = "xxx";
    std::string image_name = "xxxx";
    int r;
    librados::Rados cluster;
    librados::IoCtx io_ctx;
    librbd::Image image;
    librbd::RBD rbd;
    r = cluster.init("cinder-ctest");
    r = cluster.connect();
    r = cluster.ioctx_create(pool.c_str(), io_ctx);
    r = rbd.open_read_only(io_ctx, image, image_name.c_str(), NULL);

    std::string id;
    image.get_id(&id);   // <- Where the problem occurs
    std::cerr << id << std::endl;
    return 0;
}

使用以下命令编译时出现错误

$ g++ main.cc -o info -lrbd -lrados 
/tmp/ccOpSFrv.o: In function `main':
main.cc:(.text+0x12b): undefined reference to `librbd::Image::get_id(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*)'
collect2: error: ld returned 1 exit status

但我使用 nm 来查看 get_id 存在:

$ nm -D /usr/lib64/librbd.so | grep get_id
0000000000083d00 T rbd_get_id
000000000008de10 T _ZN6librbd5Image6get_idEPSs
                 U _ZN8librados7v14_2_05IoCtx6get_idEv

它是全球可见的:

$ readelf -s /usr/lib64/librbd.so | grep get_id
   498: 0000000000083d00    70 FUNC    GLOBAL DEFAULT   11 rbd_get_id
   559: 000000000008de10    54 FUNC    GLOBAL DEFAULT   11 _ZN6librbd5Image6get_idEP

为什么编译时会报错:undefined reference to librbd::Image::get_id. 它显然存在,这让我感到奇怪。

4

2 回答 2

5

一些背景知识:C++11通过向一些成员函数std::string添加noexcept说明符来稍微改变了接口,但事实证明,微小的改变意味着 libstdc++ 必须以std::string非 ABI 兼容的方式重新编写它们的实现。为了向后兼容,他们保留了旧版本并将新版本放在内联命名空间中,因此就std::__cxx11::basic_string链接器而言它被命名。有关更多信息,请参阅此页面

那就是你遇到麻烦的地方。 _ZN6librbd5Image6get_idEPSs破坏到

librbd::Image::get_id(
    std::basic_string<
        char,
        std::char_traits<char>,
        std::allocator<char>
    >*
)

该函数接受旧版本std::stringstd::string. 大概您拥有的 librbd 版本是使用旧版本的 GCC 构建的,或者是专门针对旧 ABI 构建的。

您有几个选项可以解决此问题:

  1. 查找为 libstdc++ 的新 ABI 构建的 librbd 版本。
    • 如果您使用的版本来自发行版的包管理器,您可能需要查看其他地方(如 Conan 或 vcpkg 或其他东西)。
  2. 针对新的 ABI 自己构建 librbd。
    • 我不熟悉那个库,所以我不知道这会有多难。似乎在某些发行版上,他们的工具链阻止了它
  3. 针对旧 ABI 构建您的应用程序。
    • 正如我上面链接的页面所说,您可以定义_GLIBCXX_USE_CXX11_ABI预处理器宏0来告诉 libstdc++ 使用std::string. 它在技术上并不完全符合 C++11 和更高版本的标准,但基本相同。
于 2020-03-31T03:31:11.647 回答
0

似乎您使用的是与 C++11 兼容的编译器,它会生成_ZN6librbd5Image6get_idEPNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE名称,但您的库是在 C++11 之前使用 C++03、C++98 或类似的东西编译的。

如果您可以访问库源,请使用 C++11 重新编译它,如果没有,请在 C++03 兼容模式下编译您的程序。

于 2020-03-31T03:14:48.120 回答