3

I encountered the following linking issues when trying to use librbd.

The following is my code snippet.

  • main.cc
#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;
}

An error occurred when I compiled using the following command

$ 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

but I use nm to see that get_id exists:

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

and it is globally visible:

$ 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

why do I get an error when compiling: undefined reference to librbd::Image::get_id. It clearly exists, which makes me wonder.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
toliu
  • 33
  • 4
  • Do a `g++ -c main.cc` and check `nm main.o` for the undefined `get_id` symbol. If it has a different mangled name, you've run into an ABI incompatibility. – aschepler Mar 31 '20 at 03:06
  • @aschepler i think you are right, how to solve this kind of problem? ```bash $ nm main.o | grep get_id U _ZN6librbd5Image6get_idEPNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE ``` – toliu Mar 31 '20 at 03:11
  • This link has the whole explanation for what’s going on and some ideas for how to resolve it: https://gcc.gnu.org/onlinedocs/libstdc++/manual/using_dual_abi.html – John Drouhard Mar 31 '20 at 03:24

2 Answers2

5

Some background: C++11 changed the std::string interface slightly by adding noexcept specifiers to a few member functions, but it turns out that slight change meant that libstdc++ had to re-write their std::string implementation in a non-ABI-compatible way. For backwards compatibility, they retained the old version and put the new one in an inline namespace so it's named std::__cxx11::basic_string as far as the linker is concerned. See this page for more info.

That's where you're running into trouble. _ZN6librbd5Image6get_idEPSs demangles to

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

That function accepts the old version of std::string, but you're passing it a pointer to the new version of std::string. Presumably the version of librbd you have was either built with an old version of GCC, or was purposely built against the old ABI.

You have a few options to work around this:

  1. Find a version of librbd that was built for libstdc++'s new ABI.
    • If the version you're using is from your distro's package manager, you may need to look elsewhere (like Conan or vcpkg or something).
  2. Build librbd yourself against the new ABI.
    • I'm not familiar with that library, so I don't know how hard this would be. It seems that on some distros their toolchain prevents it.
  3. Build your application against the old ABI.
    • As the page I linked above says, you can define the _GLIBCXX_USE_CXX11_ABI preprocessor macro to 0 to tell libstdc++ to use the old implementation of std::string. It doesn't technically fully comply with C++11 and later standard revisions, but it's mostly the same.
Miles Budnek
  • 28,216
  • 2
  • 35
  • 52
0

Seems like you use a C++11-compatible compiler, that generates _ZN6librbd5Image6get_idEPNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE names, but your library was compiled using C++03, C++98 or something like that, before C++11.

If you have access to the library sources, recompile it with C++11, if you don't, compile your program in the C++03 compatibility mode.

lenik
  • 23,228
  • 4
  • 34
  • 43