Control: clone -1 -2 -3
Control: reassign -2 apt: -Wp,-D_GLIBCXX_ASSERTION defines new symbols
Control: severity -2 important
Control: reassign -3 g++-14
Control: retitle -3 g++-14: Linking picks custom library symbols over stdc++
Control: severity -3 normal
std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_replace(unsigned long, unsigned long, char const*, unsigned long)@@APTPKG_6.0
aka std::string::replace().
In APT 2.9.4 that is being tested, here, the symbol is undefined and referencing the version shipped
in libstdc++:
root@84857ed287c5:/# grep _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE10_M_replaceEmmPKcm <(nm -D /usr/lib/x86_64-linux-gnu/libapt-pkg.so.6.0)
U _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE10_M_replaceEmmPKcm@GLIBCXX_3.4.21
However in 2.9.14, the symbol is suddenly defined and hence emitted using the APTPKG_6.0 versioning per our
APTPKG_6.0 { global: *; };
version script:
# grep _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE10_M_replaceEmmPKcm <(nm -D /usr/lib/x86_64-linux-gnu/libapt-pkg.so.6.0)
000000000009b1f0 W _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE10_M_replaceEmmPKcm@@APTPKG_6.0
This now caused python-apt to favour that version of the symbol over the C++
one when it called std::string::replace() itself. Normally if we did not have
that version script, all callers of an inline function should receive a weak
symbol and one is resolved at runtime - however, here the linker decided to
instead drop the weak symbol in favor of using the export from APTPKG_6.0;
the inverse of what happened in libapt-pkg6.0t64 itself.
This is all not correct: The expectation for the symbols provided by GLIBCXX_
versions is to have them be satisfied by libstdc++, so you don't need to
keep around multiple copies of common things like std::string::replace().
Rebuilding apt 2.9.14 in testing does not restore the symbol to undefined,
so it must be down to a change in apt. And there is one: In the second object
that is linked in, a call to std::string::replace has been dropped. Is
it that?
So now we build 2.9.4 and 2.9.14 and get:
root@2540807186ac:/build/apt# nm -D obj-x86_64-linux-gnu/apt-pkg/libapt-pkg.so | grep _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE10_M_replaceEmmPKcm
U _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE10_M_replaceEmmPKcm@GLIBCXX_3.4.21
root@2540807186ac:/build/apt# cp ^C
root@2540807186ac:/build/apt# cp -a obj-x86_64-linux-gnu/ 2.9.4
root@2540807186ac:/build/apt# git checkout 2.9.14
Previous HEAD position was de7c78680 Release 2.9.4
HEAD is now at 1a03ebc11 Release 2.9.14
root@2540807186ac:/build/apt# ninja -C obj-x86_64-linux-gnu/ apt-pkg
ninja: Entering directory `obj-x86_64-linux-gnu/'
[0/1] Re-running CMake...
-- Performing Test have-compiler-flag:-Wp,-D_GLIBCXX_ASSERTIONS
-- Performing Test have-compiler-flag:-Wp,-D_GLIBCXX_ASSERTIONS - Success
-- Detected vendor: debian (cached)
-- Building libapt-pkg 6.0 (release 0)
-- Could NOT find GTest (missing: GTEST_LIBRARY GTEST_INCLUDE_DIR GTEST_MAIN_LIBRARY)
-- Found GTest at /usr/src/googletest/googletest, headers at /usr/src/googletest/googletest/include
-- Configuring done (0.4s)
-- Generating done (0.1s)
-- Build files have been written to: /build/apt/obj-x86_64-linux-gnu
[67/68] Creating library symlink apt-pkg/libapt-pkg.so.6.0 apt-pkg/libapt-pkg.so
root@2540807186ac:/build/apt# nm -D obj-x86_64-linux-gnu/apt-pkg/libapt-pkg.so | grep _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE10_M_replaceEmmPKcm
0000000000089f60 W _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE10_M_replaceEmmPKcm@@APTPKG_6.0
Hmm but oh Let's revert -Wp,-D_GLIBCXX_ASSERTIONS and we get:
root@2540807186ac:/build/apt# ninja -C obj-x86_64-linux-gnu/ apt-pkg
ninja: Entering directory `obj-x86_64-linux-gnu/'
[0/1] Re-running CMake...
-- Detected vendor: debian (cached)
-- Building libapt-pkg 6.0 (release 0)
-- Could NOT find GTest (missing: GTEST_LIBRARY GTEST_INCLUDE_DIR GTEST_MAIN_LIBRARY)
-- Found GTest at /usr/src/googletest/googletest, headers at /usr/src/googletest/googletest/include
-- Configuring done (0.3s)
-- Generating done (0.1s)
-- Build files have been written to: /build/apt/obj-x86_64-linux-gnu
[67/68] Creating library symlink apt-pkg/libapt-pkg.so.6.0 apt-pkg/libapt-pkg.so
root@2540807186ac:/build/apt# nm -D obj-x86_64-linux-gnu/apt-pkg/libapt-pkg.so | grep _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE10_M_replaceEmmPKcm
U _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE10_M_replaceEmmPKcm@GLIBCXX_3.4.21
So what happens is that the linker is deduplicating the symbol,
preferring the @GLIBCXX_3.4.21 version when it's the same, but
now we built with assertions, so it's not the same anymore,
so it's not dedupped.
It's unclear why when python-apt is built the linker prefers libapt-pkg's
version over libstdc++
And of course we used to have these symbols pop up before we
used -fvisibility=hidden, so it turns out that they had an
entry in the symbols file using a regex to match, that was
set to 0.8.0, and hence python-apt got the wrong version.
But what is going on in python-apt itself now? It is confronted
with the symbol from libstdc++ and libapt-pkg.
Building the normal way we get the APTPKG_6.0 symbol:
x86_64-linux-gnu-g++ -fno-strict-overflow -Wsign-compare -DNDEBUG -g -O2 -Wall -shared -Wl,-O1 -Wl,-Bsymbolic-functions -Wl,-z,relro -g -fwrapv -O2 build/temp.linux-x86_64-cpython-312/python/acquire-item.o build/temp.linux-x86_64-cpython-312/python/acquire.o build/temp.linux-x86_64-cpython-312/python/apt_pkgmodule.o build/temp.linux-x86_64-cpython-312/python/cache.o build/temp.linux-x86_64-cpython-312/python/cachegroup.o build/temp.linux-x86_64-cpython-312/python/cdrom.o build/temp.linux-x86_64-cpython-312/python/configuration.o build/temp.linux-x86_64-cpython-312/python/depcache.o build/temp.linux-x86_64-cpython-312/python/generic.o build/temp.linux-x86_64-cpython-312/python/hashes.o build/temp.linux-x86_64-cpython-312/python/hashstring.o build/temp.linux-x86_64-cpython-312/python/hashstringlist.o build/temp.linux-x86_64-cpython-312/python/indexfile.o build/temp.linux-x86_64-cpython-312/python/lock.o build/temp.linux-x86_64-cpython-312/python/metaindex.o build/temp.linux-x86_64-cpython-312/python/orderlist.o build/temp.linux-x86_64-cpython-312/python/pkgmanager.o build/temp.linux-x86_64-cpython-312/python/pkgmanagerprogress.o build/temp.linux-x86_64-cpython-312/python/pkgrecords.o build/temp.linux-x86_64-cpython-312/python/pkgsrcrecords.o build/temp.linux-x86_64-cpython-312/python/policy.o build/temp.linux-x86_64-cpython-312/python/progress.o build/temp.linux-x86_64-cpython-312/python/python-apt-helpers.o build/temp.linux-x86_64-cpython-312/python/sourcelist.o build/temp.linux-x86_64-cpython-312/python/string.o build/temp.linux-x86_64-cpython-312/python/tag.o -L/usr/lib/x86_64-linux-gnu -lapt-pkg -o build/lib.linux-x86_64-cpython-312/apt_pkg.cpython-312-x86_64-linux-gnu.so
root@79f30361847b:/build/python-apt# nm -D build/lib.linux-x86_64-cpython-312/apt_pkg.cpython-312-x86_64-linux-gnu.so | grep '_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE10_M_replaceEmmPKcm'
U _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE10_M_replaceEmmPKcm@APTPKG_6.0
If we pass -lstdc++ -lapt-pkg we get the GLIBCXX one:
x86_64-linux-gnu-g++ -fno-strict-overflow -Wsign-compare -DNDEBUG -g -O2 -Wall -shared -Wl,-O1 -Wl,-Bsymbolic-functions -Wl,-z,relro -g -fwrapv -O2 build/temp.linux-x86_64-cpython-312/python/acquire-item.o build/temp.linux-x86_64-cpython-312/python/acquire.o build/temp.linux-x86_64-cpython-312/python/apt_pkgmodule.o build/temp.linux-x86_64-cpython-312/python/cache.o build/temp.linux-x86_64-cpython-312/python/cachegroup.o build/temp.linux-x86_64-cpython-312/python/cdrom.o build/temp.linux-x86_64-cpython-312/python/configuration.o build/temp.linux-x86_64-cpython-312/python/depcache.o build/temp.linux-x86_64-cpython-312/python/generic.o build/temp.linux-x86_64-cpython-312/python/hashes.o build/temp.linux-x86_64-cpython-312/python/hashstring.o build/temp.linux-x86_64-cpython-312/python/hashstringlist.o build/temp.linux-x86_64-cpython-312/python/indexfile.o build/temp.linux-x86_64-cpython-312/python/lock.o build/temp.linux-x86_64-cpython-312/python/metaindex.o build/temp.linux-x86_64-cpython-312/python/orderlist.o build/temp.linux-x86_64-cpython-312/python/pkgmanager.o build/temp.linux-x86_64-cpython-312/python/pkgmanagerprogress.o build/temp.linux-x86_64-cpython-312/python/pkgrecords.o build/temp.linux-x86_64-cpython-312/python/pkgsrcrecords.o build/temp.linux-x86_64-cpython-312/python/policy.o build/temp.linux-x86_64-cpython-312/python/progress.o build/temp.linux-x86_64-cpython-312/python/python-apt-helpers.o build/temp.linux-x86_64-cpython-312/python/sourcelist.o build/temp.linux-x86_64-cpython-312/python/string.o build/temp.linux-x86_64-cpython-312/python/tag.o -L/usr/lib/x86_64-linux-gnu -lstdc++ -lapt-pkg -o build/lib.linux-x86_64-cpython-312/apt_pkg.cpython-312-x86_64-linux-gnu.so
root@79f30361847b:/build/python-apt# nm -D build/lib.linux-x86_64-cpython-312/apt_pkg.cpython-312-x86_64-linux-gnu.so | grep '_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE10_M_replaceEmmPKcm'
U _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE10_M_replaceEmmPKcm@GLIBCXX_3.4.21
My expectation would have been for ld as invoked by g++ to always pass
-lstdc++ first such that the symbol resolves to the libstdc++ one.