From 903c72f62560a16a1a846c9c0bb65c85bdc68f26 Mon Sep 17 00:00:00 2001 From: Augusto Noronha Date: Fri, 24 Oct 2025 14:12:48 -0700 Subject: [PATCH 1/2] [lldb] Add function to tell if a section is a GOT section A global offset table is a section that holds the address of functions that are dynamically linked. rdar://160837587 --- lldb/include/lldb/Core/Section.h | 3 +++ lldb/include/lldb/Symbol/ObjectFile.h | 6 ++++++ lldb/source/Core/Section.cpp | 4 ++++ .../Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp | 14 ++++++++++++++ .../Plugins/ObjectFile/Mach-O/ObjectFileMachO.h | 2 ++ 5 files changed, 29 insertions(+) diff --git a/lldb/include/lldb/Core/Section.h b/lldb/include/lldb/Core/Section.h index b9e6dfc47daad..12c1aab535cf5 100644 --- a/lldb/include/lldb/Core/Section.h +++ b/lldb/include/lldb/Core/Section.h @@ -278,6 +278,9 @@ class Section : public std::enable_shared_from_this
, /// return true. bool ContainsOnlyDebugInfo() const; + /// Returns true if this is a global offset table section. + bool IsGOTSection() const; + protected: ObjectFile *m_obj_file; // The object file that data for this section should // be read from diff --git a/lldb/include/lldb/Symbol/ObjectFile.h b/lldb/include/lldb/Symbol/ObjectFile.h index a6d03c69df810..337fe2e3dadd9 100644 --- a/lldb/include/lldb/Symbol/ObjectFile.h +++ b/lldb/include/lldb/Symbol/ObjectFile.h @@ -761,6 +761,12 @@ class ObjectFile : public std::enable_shared_from_this, return false; } + /// Returns true if the section is a global offset table section. + virtual bool IsGOTSection(const lldb_private::Section §ion) const { + assert(section.GetObjectFile() == this && "Wrong object file!"); + return false; + } + /// Get a hash that can be used for caching object file releated information. /// /// Data for object files can be cached between runs of debug sessions and diff --git a/lldb/source/Core/Section.cpp b/lldb/source/Core/Section.cpp index 41ef98eeef6de..6480375741ed4 100644 --- a/lldb/source/Core/Section.cpp +++ b/lldb/source/Core/Section.cpp @@ -473,6 +473,10 @@ bool Section::ContainsOnlyDebugInfo() const { return false; } +bool Section::IsGOTSection() const { + return GetObjectFile()->IsGOTSection(*this); +} + #pragma mark SectionList SectionList &SectionList::operator=(const SectionList &rhs) { diff --git a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp index 0d0cbde68b4f7..59ba6fa7a1f0e 100644 --- a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp +++ b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp @@ -6069,6 +6069,20 @@ Section *ObjectFileMachO::GetMachHeaderSection() { return nullptr; } +bool ObjectFileMachO::IsGOTSection(const lldb_private::Section §ion) const { + assert(section.GetObjectFile() == this && "Wrong object file!"); + SectionSP segment = section.GetParent(); + if (!segment) + return false; + + bool is_data_const_got = + segment->GetName() == "__DATA_CONST" && section.GetName() == "__got"; + bool is_auth_const_ptr = + segment->GetName() == "__AUTH_CONST" && + (section.GetName() == "__auth_got" || section.GetName() == "__auth_ptr"); + return is_data_const_got || is_auth_const_ptr; +} + bool ObjectFileMachO::SectionIsLoadable(const Section *section) { if (!section) return false; diff --git a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h index 25643aacb3d2d..5456f0315c942 100644 --- a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h +++ b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h @@ -162,6 +162,8 @@ class ObjectFileMachO : public lldb_private::ObjectFile { lldb_private::Section *GetMachHeaderSection(); + bool IsGOTSection(const lldb_private::Section §ion) const override; + // PluginInterface protocol llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } From eddff611b6a3ef32791fef0bb0fadc2da12cdeb0 Mon Sep 17 00:00:00 2001 From: Augusto Noronha Date: Thu, 23 Oct 2025 11:24:58 -0700 Subject: [PATCH 2/2] [lldb] Fix LLDBMemoryReader resolving GOT pointers incorrectly The DYLD shared cache, as an optimization, can merge GOT pointers from multiple images into one location, and fix all relative offsets to point to the new updated location. When reading metadata, LLDB tries, as an optimization, to read memory from local files instead of live memory. This means that the relative offset will point to the old location in the GOT section. In this case, LLDB needs to re-read the offset from live memory, to get the correct offset in live memory. rdar://160837587 --- .../Swift/LLDBMemoryReader.cpp | 126 ++++++++++++++++++ .../LanguageRuntime/Swift/LLDBMemoryReader.h | 5 + 2 files changed, 131 insertions(+) diff --git a/lldb/source/Plugins/LanguageRuntime/Swift/LLDBMemoryReader.cpp b/lldb/source/Plugins/LanguageRuntime/Swift/LLDBMemoryReader.cpp index 2ee3f25efc1ab..13146fffa73fb 100644 --- a/lldb/source/Plugins/LanguageRuntime/Swift/LLDBMemoryReader.cpp +++ b/lldb/source/Plugins/LanguageRuntime/Swift/LLDBMemoryReader.cpp @@ -317,6 +317,132 @@ LLDBMemoryReader::resolvePointer(swift::remote::RemoteAddress address, return tagged_pointer; } +swift::reflection::RemoteAddress +LLDBMemoryReader::resolveIndirectAddressAtOffset( + swift::reflection::RemoteAddress address, uint64_t offset, + bool directnessEncodedInOffset) { + // Usually, simply adding the address with the offset will produce the correct + // remote address. + // + // However, on Apple platforms, the shared cache can coalesce pointers in the + // global offset table from multiple images into one location, patch all the + // uses to point to the new location, and zero out the original pointer. In + // this case, LLDBMemoryReader needs to be conservative and re-fetch the + // offset from live memory to ensure it points to the new, coalesced location + // instead. + Log *log = GetLog(LLDBLog::Types); + + LLDB_LOGV(log, + "[MemoryReader::resolveAddressAtOffset] Asked to resolve address " + "{0:x} at offset {1:x}", + address.getRawAddress(), offset); + + swift::reflection::RemoteAddress offset_address = address + offset; + + if (!readMetadataFromFileCacheEnabled()) + return offset_address; + + // Addresses in the process can be read directly. + if (offset_address.getAddressSpace() == + swift::reflection::RemoteAddress::DefaultAddressSpace) + return offset_address; + + // Check if offset_address points to a GOT entry. + std::optional
maybeAddr = + resolveRemoteAddressFromSymbolObjectFile(offset_address); + + if (!maybeAddr) + maybeAddr = remoteAddressToLLDBAddress(offset_address); + + if (!maybeAddr) { + LLDB_LOGV(log, + "[MemoryReader::resolveAddressAtOffset] could not resolve " + "address {0:x}", + offset_address.getRawAddress()); + return offset_address; + } + + Address lldb_offset_address = *maybeAddr; + if (!lldb_offset_address.IsSectionOffset()) { + LLDB_LOGV( + log, + "[MemoryReader::resolveAddressAtOffset] lldb offset address has no " + "section {0:x}", + offset_address.getRawAddress()); + return offset_address; + } + + // This is only necessary on Apple platforms. + ObjectFile *obj_file = lldb_offset_address.GetModule()->GetObjectFile(); + if (!obj_file || !obj_file->GetArchitecture().GetTriple().isOSDarwin()) + return offset_address; + + SectionSP section = lldb_offset_address.GetSection(); + if (!section->IsGOTSection()) + return offset_address; + + // offset_address is in a GOT section. Re-read the offset from the base + // address in live memory, since the offset in live memory can have been + // patched in the shared cache to point somewhere else. + std::optional
maybe_lldb_addr = + resolveRemoteAddressFromSymbolObjectFile(address); + + if (!maybe_lldb_addr) + maybe_lldb_addr = remoteAddressToLLDBAddress(address); + + if (!maybe_lldb_addr) { + LLDB_LOGV(log, + "[MemoryReader::resolveAddressAtOffset] could not resolve offset " + "address {0:x}", + address.getRawAddress()); + return offset_address; + } + + auto lldb_addr = *maybe_lldb_addr; + Target &target(m_process.GetTarget()); + Status error; + const bool force_live_memory = true; + bool did_read_live_memory = false; + + // Relative offsets are always 4 bytes long, regardless of target. + uint32_t live_offset = 0; + size_t size = sizeof(live_offset); + if (size != + target.ReadMemory(lldb_addr, &live_offset, size, error, force_live_memory, + /*load_addr_ptr=*/nullptr, &did_read_live_memory)) { + LLDB_LOG(log, + "[MemoryReader::resolveAddressAtOffset] Resolve address returned " + "different bytes than asked " + "for {0:x}", + lldb_addr.GetLoadAddress(&target)); + return offset_address; + } + if (error.Fail()) { + LLDB_LOG(log, + "[MemoryReader::resolveAddressAtOffset] memory read returned " + "error: {0}", + error.AsCString()); + return offset_address; + } + // Some Swift metadata encodes the directness directly into the offset, + // in that case clear the directness bit. + if (directnessEncodedInOffset) + live_offset &= ~1u; + + // Now, get the live address counterpart of the lldb address this function + // started with, and add the live offset we just read to it. + addr_t live_address = lldb_addr.GetLoadAddress(&target); + LLDB_LOGV( + log, + "[MemoryReader::resolveAddressAtOffset] Succesfully resolved address " + "into live address {0:x} and offset {1:x} resulting in address {2:x}", + live_address, live_offset, live_address + live_offset); + + return swift::remote::RemoteAddress( + live_address + live_offset, + swift::remote::RemoteAddress::DefaultAddressSpace); +} + bool LLDBMemoryReader::readBytes(swift::remote::RemoteAddress address, uint8_t *dest, uint64_t size) { auto read_bytes_result = readBytesImpl(address, dest, size); diff --git a/lldb/source/Plugins/LanguageRuntime/Swift/LLDBMemoryReader.h b/lldb/source/Plugins/LanguageRuntime/Swift/LLDBMemoryReader.h index 6d37b7f18c820..a9f238fd05dbf 100644 --- a/lldb/source/Plugins/LanguageRuntime/Swift/LLDBMemoryReader.h +++ b/lldb/source/Plugins/LanguageRuntime/Swift/LLDBMemoryReader.h @@ -87,6 +87,11 @@ class LLDBMemoryReader : public swift::remote::MemoryReader { std::optional resolveRemoteAddress(swift::remote::RemoteAddress address) const override; + swift::reflection::RemoteAddress + resolveIndirectAddressAtOffset(swift::reflection::RemoteAddress address, + uint64_t offset, + bool directnessEncodedInOffset) override; + bool readBytes(swift::remote::RemoteAddress address, uint8_t *dest, uint64_t size) override;