UnsatisfiedLinkError on Linux

I am building the latest release of cesium-native on Linux and then linking the built static libraries to my own library. When I try to load my library I get a UnsatisfiedLinkError:

java.lang.UnsatisfiedLinkError: libmyengine.so: undefined symbol: _ZN4absl12lts_2024072216numbers_internal9kHexTableE

This points to the abseil libraries, and specifically this symbol is found in libabsl_str_format_internal.a:

nm libabsl_str_format_internal.a | grep _ZN4absl12lts_2024072216numbers_internal9kHexTableE
                 U _ZN4absl12lts_2024072216numbers_internal9kHexTableE
nm -C libabsl_str_format_internal.a | grep HexTable
                 U absl::lts_20240722::numbers_internal::kHexTable

Strangely, when I check my library I can also see the symbol there:

nm libmyengine.so | grep _ZN4absl12lts_2024072216numbers_internal9kHexTableE
                 U _ZN4absl12lts_2024072216numbers_internal9kHexTableE
nm -C libmyengine.so | grep HexTable
                 U absl::lts_20240722::numbers_internal::kHexTable

Some additional verification:

readelf -aW libmyengine.so | grep _ZN4absl12lts_2024072216numbers_internal9kHexTableE
0000000001476140  0000000400000006 R_X86_64_GLOB_DAT      0000000000000000 _ZN4absl12lts_2024072216numbers_internal9kHexTableE + 0
     4: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _ZN4absl12lts_2024072216numbers_internal9kHexTableE
 14438: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _ZN4absl12lts_2024072216numbers_internal9kHexTableE
scanelf libmyengine.so -s _ZN4absl12lts_2024072216numbers_internal9kHexTableE
 TYPE   SYM FILE 
ET_DYN _ZN4absl12lts_2024072216numbers_internal9kHexTableE libmyengine.so

Note that I am successfully building and running on both Windows and Android without this issue. I feel like there is something subtle about the building process that I am missing. I don’t believe it is this one abseil symbol in particular, but probably the whole way it’s being built. I have checked the Linux build CI scripts on github and tried to mirror them. The cesium-native build command line is approximately (I’ve tried a few variations):

cmake -B build -S cesium-native -DCMAKE_INSTALL_PREFIX=build/linux-x86_64 -DVCPKG_TRIPLET=x64-linux -DCMAKE_BUILD_TYPE=Release -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCESIUM_TESTS_ENABLED=OFF

My library is also using cmake for its build and adding all the output libraries (including all abseil libraries) from cesium-native.

Can anyone provide some guidance or point me in the right direction? Should I escalate this to a github issue? Thank you!

There are different flavors of the UnsatisfiedLinkError, and unfortunately, the error itself is thrown when anything goes wrong, so it can be really hard to debug. That undefined symbol part is a bit unusual. I cannot pinpoint “the” reason for that, but it looks like something went wrong during the linking. Quick searches suggest that adding -Wl,-no-undefined to the linker call could generate an error message during the linking process itself (and not only when trying to load the library). Maybe that can help to narrow down what’s wrong here…?

Good advice and the results are interesting. I am seeing a large list of unresolved symbols as seen in the sampling below. (note that the original symbols above were from the latest cesium-native, however these results are from 0.40.1 which is the version we are currently targeting, but the problem is the same)

/usr/bin/ld: /home/tak/.conan/data/cesium-native/0.40.1/_/_/package/2d5685a99e2ff5a0d2b054bb1fbfa46781f8a46a/linux-x86_64/lib/libs2.a(s2cell_id.cc.o): in function `S2CellId::ToString[abi:cxx11]() const':
s2cell_id.cc:(.text+0x26f4): undefined reference to `absl::lts_20240116::numbers_internal::FastIntToBuffer(int, char*)'
/usr/bin/ld: s2cell_id.cc:(.text+0x271d): undefined reference to `absl::lts_20240116::StrCat[abi:cxx11](absl::lts_20240116::AlphaNum const&, absl::lts_20240116::AlphaNum const&)'
/usr/bin/ld: s2cell_id.cc:(.text+0x27e3): undefined reference to `absl::lts_20240116::numbers_internal::kHexTable'
/usr/bin/ld: s2cell_id.cc:(.text+0x28be): undefined reference to `absl::lts_20240116::strings_internal::StringifySink::Append(std::basic_string_view<char, std::char_traits<char> >)'
/usr/bin/ld: s2cell_id.cc:(.text+0x28f9): undefined reference to `absl::lts_20240116::StrCat[abi:cxx11](absl::lts_20240116::AlphaNum const&, absl::lts_20240116::AlphaNum const&)'
/usr/bin/ld: /home/tak/.conan/data/cesium-native/0.40.1/_/_/package/2d5685a99e2ff5a0d2b054bb1fbfa46781f8a46a/linux-x86_64/lib/libs2.a(s2latlng.cc.o): in function `S2LatLng::ToStringInDegrees[abi:cxx11]() const':
s2latlng.cc:(.text+0x465): undefined reference to `bool absl::lts_20240116::str_format_internal::FormatArgImpl::Dispatch<double>(absl::lts_20240116::str_format_internal::FormatArgImpl::Data, absl::lts_20240116::str_format_internal::FormatConversionSpecImpl, void*)'
/usr/bin/ld: /home/tak/.conan/data/cesium-native/0.40.1/_/_/package/2d5685a99e2ff5a0d2b054bb1fbfa46781f8a46a/linux-x86_64/lib/libCesium3DTilesSelection.a(TilesetJsonLoader.cpp.o): in function `Cesium3DTilesSelection::(anonymous namespace)::parseTilesetMetadata(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, rapidjson::GenericDocument<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator>, rapidjson::CrtAllocator> const&, Cesium3DTilesSelection::TileExternalContent&)':
TilesetJsonLoader.cpp:(.text+0x76a7): undefined reference to `Cesium3DTilesReader::SchemaReader::SchemaReader()'
/usr/bin/ld: /home/tak/.conan/data/cesium-native/0.40.1/_/_/package/2d5685a99e2ff5a0d2b054bb1fbfa46781f8a46a/linux-x86_64/lib/libCesium3DTilesSelection.a(ImplicitOctreeLoader.cpp.o): in function `Cesium3DTilesSelection::(anonymous namespace)::populateSubtree(Cesium3DTilesContent::SubtreeAvailability const&, unsigned int, CesiumGeometry::OctreeTileID const&, Cesium3DTilesSelection::Tile const&, Cesium3DTilesSelection::ImplicitOctreeLoader&, CesiumGeospatial::Ellipsoid const&)':
ImplicitOctreeLoader.cpp:(.text+0x1f29): undefined reference to `Cesium3DTilesContent::OctreeChildren::begin() const'
/usr/bin/ld: ImplicitOctreeLoader.cpp:(.text+0x1f40): undefined reference to `Cesium3DTilesContent::OctreeChildren::end() const'
/usr/bin/ld: ImplicitOctreeLoader.cpp:(.text+0x1f7b): undefined reference to `Cesium3DTilesContent::SubtreeAvailability::isTileAvailable(unsigned int, unsigned long) const'
/usr/bin/ld: ImplicitOctreeLoader.cpp:(.text+0x1f93): undefined reference to `Cesium3DTilesContent::OctreeChildren::iterator::operator++()'
/usr/bin/ld: ImplicitOctreeLoader.cpp:(.text+0x1fa0): undefined reference to `Cesium3DTilesContent::OctreeChildren::iterator::operator!=(Cesium3DTilesContent::OctreeChildren::iterator const&) const'
/usr/bin/ld: ImplicitOctreeLoader.cpp:(.text+0x1fbb): undefined reference to `Cesium3DTilesContent::ImplicitTilingUtilities::computeRelativeMortonIndex(CesiumGeometry::OctreeTileID const&, CesiumGeometry::OctreeTileID const&)'
/usr/bin/ld: ImplicitOctreeLoader.cpp:(.text+0x1fd4): undefined reference to `Cesium3DTilesContent::SubtreeAvailability::isSubtreeAvailable(unsigned long) const'
/usr/bin/ld: ImplicitOctreeLoader.cpp:(.text+0x21ce): undefined reference to `Cesium3DTilesContent::SubtreeAvailability::isContentAvailable(unsigned int, unsigned long, unsigned long) const'

I don’t know if that helps narrow down a solution.

I think that it does … at least for someone who is more familiar with cesium-native, C++, linking, packaging, changes between versions and their dependencies, the universe, and everything…

So I hope it’s OK to ping @Kevin_Ring :

There seem to be some undefined references from the S2 implementation to abseil. That highly specific namespace name lts_20240116 should make a version incompatibility unlikely, but at the same time, makes me suspect that it could be ~“something like that”.

What’s a bit more surprising for me is that there seem to be undefined references from Cesium classes like ImplicitOctreeLoader to Cesium classes like Cesium3DTilesContent::OctreeChildren. Could there be something wrong (like… you know, some #include "x.h" that should be #include <x.h>" or vice versa, or some CMake-quirk) that could explain this? Do you know from the tip of your head whether there have been changes in that area, and something like “conan caching” or so could pull in some wrong version…?

Hi @kurtzmarc,
My first guess is that it has to do with the order the library (.a) files are specified on the linker command-line. The GCC linker is quite persnickety about this. See here:

Rather that mucking about too much trying to get the order right, you can probably use --start-group and --end-group. See here for details and how to pass those options to the linker if you’re invoking it via gcc:

Ahh, thank you! Order matters. Rearranging the libraries in our cmake script until all the undefined symbols are resolved with -no-undefined got us to a working build. Thank you both!

I somehow like that the response
“Get your dependencies in order!”
would have sounded very rude, but is the actual solution here :laughing:

2 Likes