7

Trying to compile this cfgparser example.

$ g++ example.cc -lcfgparser
: In function `main':
example.cc:(.text+0x6b): undefined reference to `ConfigParser_t::readFile(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)'
example.cc:(.text+0x160): undefined reference to `ConfigParser_t::getValue(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*) const'
example.cc:(.text+0x2d9): undefined reference to `ConfigParser_t::getValue(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, int*) const'
example.cc:(.text+0x43c): undefined reference to `ConfigParser_t::getValue(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, double*) const'
example.cc:(.text+0x5b1): undefined reference to `ConfigParser_t::getValue(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool*) const'
example.cc:(.text+0x78c): undefined reference to `ConfigParser_t::getValue(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >*) const'
example.cc:(.text+0xa15): undefined reference to `ConfigParser_t::getValue(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*) const'
example.cc:(.text+0xba2): undefined reference to `ConfigParser_t::getValue(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*) const'
example.cc:(.text+0xd15): undefined reference to `ConfigParser_t::getValue(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*) const'
example.cc:(.text+0xe7f): undefined reference to `ConfigParser_t::getValue(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*) const'
collect2: error: ld returned 1 exit statu

But clearly those symbols are there:

$ nm -gC /usr/lib/libcfgparser.so
000000000003710 T ConfigParser_t::readFile(std::string const&)
0000000000004880 T ConfigParser_t::ConfigParser_t(std::string const&)
00000000000024c0 T ConfigParser_t::ConfigParser_t()
0000000000004ad0 T ConfigParser_t::ConfigParser_t(std::string const&)
00000000000024a0 T ConfigParser_t::ConfigParser_t()
0000000000004d20 T ConfigParser_t::getOptions(std::string const&) const
00000000000028d0 T ConfigParser_t::getSections() const
0000000000002ff0 T ConfigParser_t::getValue(std::string, std::string, bool*) const
0000000000002de0 T ConfigParser_t::getValue(std::string, std::string, double*) const
0000000000002bd0 T ConfigParser_t::getValue(std::string, std::string, int*) const
00000000000027d0 T ConfigParser_t::getValue(std::string, std::string, std::string*) const
0000000000003500 T ConfigParser_t::getValue(std::string, std::string, std::vector<std::string, std::allocator<std::string> >*) const

Unlike other similar problems on SO, this is NOT about linking order, as there is only one object file being linked. Anything I'm missing??

qweruiop
  • 3,156
  • 6
  • 31
  • 55
  • 1
    My guess is, the two modules are built in such a way that `std::string` means different things to them. Linked with different flavors of C++ runtime library, perhaps? – Igor Tandetnik Dec 14 '16 at 04:50
  • The problem can be that you're including `stdio.h` and not `cstdio` if you copied the example code. That would match broken linkage on char-related stuff. Other things to check: was your `libcfgparser.so` built with `std::string` supporting `wchar_t` ? – starturtle Dec 14 '16 at 06:18
  • I had the same problem ensure for ABI compatibility ensure the -m32/-m64 matches for you library/executable builds. – ceorron Aug 30 '17 at 14:14
  • 1
    I just wanted to say thank you for sharing the command line you used for nm. I had never heard of this tool, and finding this method of diagnosing my (what turned out for me to be a namespace) problem saved me hours. Thank you! – Daniel Peirano Dec 09 '17 at 22:12

1 Answers1

14

But clearly those symbols are there:

Clearly, they're not. Take a closer look and you will see that there are no matches between the signatures reported by your linker as undefined and those reported from the library by nm.

The Standard header <string> defines std::string as typedef for:

std::basic_string<char, std::char_traits<char>, std::allocator<char>>

The GCC implementation of <string>, as of the cxx11 ABI (GCC 4.7.0) defines std::string as a typedef for:

std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>

The std::__cxx11 name space encloses types whose binary implementations are compliant with the cxx11 ABI.

That type definition means that C++ translation unit that includes the Standard header <string> won't compile, with GCC >= 4.7, into object code containing a symbol that demangles as std::string.

If a binary - like your libcfgparser.so - contains a symbol that demangles as std::string, then whatever that symbol might have referred to in the sources from which it was built, it does not refer to std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>, and cannot be linked with other binaries that were compiled with that definition of std::string.

Your libcfgparser.so is the one installed by libcfgparser0_1.1.2_amd64.deb from the cfgparser Sourceforce project (which was last updated three years ago). I guess this from that the fact that I get your linkage error with same example.cc program linked against the library that is installed by that package.

The explanation for the error is revealed by:

/usr/lib$ strings -a libcfgparser.so | grep "GCC: ("
GCC: (Debian 4.3.2-1.1) 4.3.2
...

which tells us that this library was built with GCC 4.3.2 - pre the cxx11 ABI.

The std::string that appears in your nm output is a pre-cxx11 demangling abbreviation of std::basic_string<char, std::char_traits<char>, std::allocator<char>>.

This libcfgparser.so is ABI-incompatible with your current GCC, so you can't link it with programs that you build with that compiler. What you can do is build libcfgparser from the source package, libcfgparser-1.1.2.tar.bz2, with your current compiler and then link your programs with the library you have built yourself.

That works, but not effortlessly. First you'll need to fix the broken and antiquated autotooling of the source package to create a working ./configure script.

Which doesn't give a reassuring impression of the proficiency of the package maintainer. Plus the source code of the package, copyright 2008, is of amateur quality. If what you are after is a C++ parser for INI-style files, then boost::property_tree::ini_parser would be the goto solution. See this answer

Community
  • 1
  • 1
Mike Kinghan
  • 55,740
  • 12
  • 153
  • 182
  • 1
    Thanks! Your answer is very helpful and educational. I was guessing the similar reason but I can't conclude probably because "The `std::string` that appears in your `nm` output is a pre-cxx11 demangling **abbreviation** of `std::basic_string, std::allocator>` -- I don't how to de-abbreviate `std::string` correctly. – qweruiop Dec 15 '16 at 21:03
  • 1
    Also using `boost::property_tree::ini_parser` is a good call. – qweruiop Dec 15 '16 at 21:05
  • @qweruiop You're welcome. The `std::string` (demangled) = `Ss` (mangled) was a *hardcoded* abbreviation in the pre-cxx11 GCC name-mangling. No logic to it but convenience. But what you can count on is that symbols within binaries, whether mangled or demangled, that are *different* are *different to the linker*, 'cos they're what the linker sees. – Mike Kinghan Dec 15 '16 at 22:10