From 0bb0fded52cc930e3ea785dd3005ca0378c58ef2 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 (cherry picked from commit 903c72f62560a16a1a846c9c0bb65c85bdc68f26) --- lldb/include/lldb/Core/Section.h | 3 +++ lldb/include/lldb/Symbol/ObjectFile.h | 6 ++++++ lldb/source/Core/Section.cpp | 3 +++ .../Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp | 14 ++++++++++++++ .../Plugins/ObjectFile/Mach-O/ObjectFileMachO.h | 2 ++ 5 files changed, 28 insertions(+) diff --git a/lldb/include/lldb/Core/Section.h b/lldb/include/lldb/Core/Section.h index c9d6a4ef16cfb..76b59559a2987 100644 --- a/lldb/include/lldb/Core/Section.h +++ b/lldb/include/lldb/Core/Section.h @@ -262,6 +262,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 25be8485bdeb3..6330a15c50a78 100644 --- a/lldb/include/lldb/Symbol/ObjectFile.h +++ b/lldb/include/lldb/Symbol/ObjectFile.h @@ -740,6 +740,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 d696d492d81e1..3e18bdd5bf62f 100644 --- a/lldb/source/Core/Section.cpp +++ b/lldb/source/Core/Section.cpp @@ -471,6 +471,9 @@ bool Section::ContainsOnlyDebugInfo() const { return false; } +bool Section::IsGOTSection() const { + return GetObjectFile()->IsGOTSection(*this); +} #pragma mark SectionList diff --git a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp index 77a704f4c64c5..9fe3e27ca0b46 100644 --- a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp +++ b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp @@ -6305,6 +6305,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 81644697b3630..cd7e7bef0bd56 100644 --- a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h +++ b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h @@ -159,6 +159,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 774ece6146aa889667f0cbce41a3e263d4e7d82b 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 (cherry picked from commit eddff611b6a3ef32791fef0bb0fadc2da12cdeb0) --- .../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 ee7237c6c1ddc..a87ca82dc054c 100644 --- a/lldb/source/Plugins/LanguageRuntime/Swift/LLDBMemoryReader.cpp +++ b/lldb/source/Plugins/LanguageRuntime/Swift/LLDBMemoryReader.cpp @@ -313,6 +313,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 [success, _] = readBytesImpl(address, dest, size); diff --git a/lldb/source/Plugins/LanguageRuntime/Swift/LLDBMemoryReader.h b/lldb/source/Plugins/LanguageRuntime/Swift/LLDBMemoryReader.h index 901e4636561c6..7edc7145d3f00 100644 --- a/lldb/source/Plugins/LanguageRuntime/Swift/LLDBMemoryReader.h +++ b/lldb/source/Plugins/LanguageRuntime/Swift/LLDBMemoryReader.h @@ -80,6 +80,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;