From af762e9e27d9e130edf7ed5c6a76a266f1ec0afb Mon Sep 17 00:00:00 2001 From: Gennaro Prota Date: Tue, 21 Jul 2026 11:54:34 +0200 Subject: [PATCH] Add a regression test for cross-library base pointer deserialization A derived class defined in a shared library is round-tripped through a `unique_ptr` to its abstract base, across the library boundary, using a binary-backed polymorphic archive, from an executable built with hidden symbol visibility. The test reuses the existing `dll_polymorphic_base` and `dll_polymorphic_derived2` libraries. On GCC/ELF the per-type serializer singletons were duplicated across the boundary, so the reader could not map the class id back to the derived type and recovered the wrong object. Refs issue #117. --- test/Jamfile.v2 | 1 + test/test_dll_base_pointer.cpp | 75 ++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 test/test_dll_base_pointer.cpp diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 6800c81d4..72716b945 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -146,6 +146,7 @@ if ! $(BOOST_ARCHIVE_LIST) { # we suppress tests of our dlls when using static libraries [ test-bsl-run test_dll_simple : : dll_a : static:no ] + [ test-bsl-run test_dll_base_pointer : : dll_polymorphic_base dll_polymorphic_derived2 : static:no ] # [ test-bsl-run test_dll_plugin : : dll_derived2 : static:no linux:-ldl ] [ test-bsl-run test_private_ctor ] diff --git a/test/test_dll_base_pointer.cpp b/test/test_dll_base_pointer.cpp new file mode 100644 index 000000000..8a938c65d --- /dev/null +++ b/test/test_dll_base_pointer.cpp @@ -0,0 +1,75 @@ +/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8 +// test_dll_base_pointer.cpp + +// Copyright 2026 Gennaro Prota +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// Regression test for issue #117. A derived class defined in a shared +// library is serialized and deserialized through a pointer to its abstract +// base, from an executable built with hidden symbol visibility. +// Historically this failed on GCC/ELF: the per type serializer singletons +// were duplicated across the shared library boundary, so the reader could not +// map the class id back to the derived type. The round trip must recover an +// object of the derived type. + +#include +#include +#include + +#include + +#include "test_tools.hpp" + +#ifndef BOOST_NO_CXX11_SMART_PTR + +#include +#include +#include + +#include +#include +#include + +// polymorphic_derived2 (and its base, polymorphic_base) live in the shared +// library this test links against +#define POLYMORPHIC_DERIVED2_IMPORT +#include "polymorphic_derived2.hpp" + +int test_main(int /* argc */, char * /* argv */ []){ + const char * testfile = boost::archive::tmpnam(NULL); + BOOST_REQUIRE(NULL != testfile); + + std::unique_ptr saved = + boost::make_unique(); + { + std::ofstream os(testfile, std::ios::binary); + boost::archive::polymorphic_binary_oarchive oa(os); + boost::archive::polymorphic_oarchive & poa = oa; + poa << boost::serialization::make_nvp("ptr", saved); + } + + std::unique_ptr loaded; + { + std::ifstream is(testfile, std::ios::binary); + boost::archive::polymorphic_binary_iarchive ia(is); + boost::archive::polymorphic_iarchive & pia = ia; + pia >> boost::serialization::make_nvp("ptr", loaded); + } + + BOOST_CHECK(0 != loaded.get()); + // the object recovered through the base pointer must be the derived type + BOOST_CHECK(0 != dynamic_cast(loaded.get())); + + std::remove(testfile); + return EXIT_SUCCESS; +} + +#else + +int test_main(int /* argc */, char * /* argv */ []){ + return EXIT_SUCCESS; +} + +#endif // BOOST_NO_CXX11_SMART_PTR