From 8988144ad86d17e0da493b32e899acfed2e471ca Mon Sep 17 00:00:00 2001 From: dg1sbg Date: Sat, 30 May 2026 18:46:33 +0200 Subject: [PATCH] Make snapshot load position-independent (match libraries by basename) snapshot_load resolves the C++ libraries a snapshot references via core::library_with_name, which matched a recorded library by requiring its (save-time) name to be a path SUFFIX of a currently-loaded library's path. Because the recorded name is the absolute path captured by dladdr at save time, a snapshot whose libraries are later loaded from a different directory -- a relocated or bundled install, or a different prefix than the build tree -- fails the suffix test and snapshot_load aborts ("Unable to find library"). Match by basename (SONAME) instead. The subsequent nm/dlsym step already uses the matched, currently-loaded path, so the matcher was the only place bound to the save-time path. Snapshots now load regardless of where their libraries sit, making save-lisp-and-die executables and snapshot files relocatable across prefixes/machines. Verified on aarch64 Linux: a boehmprecise :executable snapshot whose libLLVM / libstdc++ / libclasp were relocated into a bundle directory now boots from its embedded snapshot and runs. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/core/debugger.cc | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/core/debugger.cc b/src/core/debugger.cc index b27e21f3e6..39d0377f67 100644 --- a/src/core/debugger.cc +++ b/src/core/debugger.cc @@ -264,10 +264,20 @@ void executableTextSectionRange(gctools::clasp_ptr_t& start, gctools::clasp_ptr_ bool library_with_name(const std::string& name, bool isExecutable, std::string& libraryName, uintptr_t& start, uintptr_t& end, uintptr_t& vtableStart, uintptr_t& vtableEnd) { WITH_READ_LOCK(debugInfo()._OpenDynamicLibraryMutex); + // Match libraries by basename (SONAME), not by requiring the recorded name to be a path-suffix + // of the loaded library. The recorded name can be an absolute path captured at snapshot-save + // time; if the library is later loaded from a different directory (a relocated/bundled install, + // or the target's /usr/lib vs a build path) the old suffix test fails and snapshot load aborts. + // Comparing basenames makes snapshot load position-independent. + auto base_name = [](const std::string& p) -> std::string { + std::size_t slash = p.find_last_of('/'); + return slash == std::string::npos ? p : p.substr(slash + 1); + }; + const std::string nameBase = base_name(name); for (auto entry : debugInfo()._OpenDynamicLibraryHandles) { std::string libName = entry.second->_Filename; if (ExecutableLibraryInfo* eli = dynamic_cast(entry.second)) { - if (isExecutable || (name.size() <= libName.size() && name == libName.substr(libName.size() - name.size()))) { + if (isExecutable || base_name(libName) == nameBase) { libraryName = eli->_Filename; start = (uintptr_t)(eli->_TextStart); end = (uintptr_t)(eli->_TextEnd); @@ -278,7 +288,7 @@ bool library_with_name(const std::string& name, bool isExecutable, std::string& return true; } } else { - if (name.size() <= libName.size() && name == libName.substr(libName.size() - name.size())) { + if (base_name(libName) == nameBase) { if (isExecutable) { printf("%s:%d:%s THERE IS A PROBLEM - The library %s is being searched for as Executable but it is not\n", __FILE__, __LINE__, __FUNCTION__, name.c_str());