This revision was automatically updated to reflect the committed changes.
Closed by commit rG28fb39f16af1: [lldb] Adjust for changes in objc runtime
(authored by bulbazord).
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D153597/new/
https://reviews.llvm.org/D153597
Files:
lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.cpp
lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.h
lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp
lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h
lldb/test/API/lang/objc/exceptions/TestObjCExceptions.py
Index: lldb/test/API/lang/objc/exceptions/TestObjCExceptions.py
===================================================================
--- lldb/test/API/lang/objc/exceptions/TestObjCExceptions.py
+++ lldb/test/API/lang/objc/exceptions/TestObjCExceptions.py
@@ -87,7 +87,6 @@
"userInfo = ",
"1 key/value pair",
"reserved = ",
- "nil",
],
)
@@ -105,7 +104,6 @@
self.assertEqual(
userInfo.GetChildAtIndex(0).GetChildAtIndex(1).description, "some_value"
)
- self.assertEqual(e1.GetChildMemberWithName("reserved").description, "<nil>")
self.expect(
"frame variable e2", substrs=["(NSException *) e2 = ", '"SomeReason"']
Index: lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h
===================================================================
--- lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h
+++ lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h
@@ -19,6 +19,8 @@
#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
+#include "llvm/ADT/BitVector.h"
+
class RemoteNXMapTable;
namespace lldb_private {
@@ -96,6 +98,12 @@
void GetValuesForGlobalCFBooleans(lldb::addr_t &cf_true,
lldb::addr_t &cf_false) override;
+ void ModulesDidLoad(const ModuleList &module_list) override;
+
+ bool IsSharedCacheImageLoaded(uint16_t image_index);
+
+ std::optional<uint64_t> GetSharedCacheImageHeaderVersion();
+
protected:
lldb::BreakpointResolverSP
CreateExceptionResolver(const lldb::BreakpointSP &bkpt, bool catch_bp,
@@ -374,6 +382,35 @@
lldb::addr_t m_args = LLDB_INVALID_ADDRESS;
};
+ class SharedCacheImageHeaders {
+ public:
+ static std::unique_ptr<SharedCacheImageHeaders>
+ CreateSharedCacheImageHeaders(AppleObjCRuntimeV2 &runtime);
+
+ void SetNeedsUpdate() { m_needs_update = true; }
+
+ bool IsImageLoaded(uint16_t image_index);
+
+ uint64_t GetVersion();
+
+ private:
+ SharedCacheImageHeaders(AppleObjCRuntimeV2 &runtime,
+ lldb::addr_t headerInfoRWs_ptr, uint32_t count,
+ uint32_t entsize)
+ : m_runtime(runtime), m_headerInfoRWs_ptr(headerInfoRWs_ptr),
+ m_loaded_images(count, false), m_version(0), m_count(count),
+ m_entsize(entsize), m_needs_update(true) {}
+ llvm::Error UpdateIfNeeded();
+
+ AppleObjCRuntimeV2 &m_runtime;
+ lldb::addr_t m_headerInfoRWs_ptr;
+ llvm::BitVector m_loaded_images;
+ uint64_t m_version;
+ uint32_t m_count;
+ uint32_t m_entsize;
+ bool m_needs_update;
+ };
+
AppleObjCRuntimeV2(Process *process, const lldb::ModuleSP &objc_module_sp);
ObjCISA GetPointerISA(ObjCISA isa);
@@ -435,6 +472,7 @@
std::once_flag m_no_expanded_cache_warning;
std::optional<std::pair<lldb::addr_t, lldb::addr_t>> m_CFBoolean_values;
uint64_t m_realized_class_generation_count;
+ std::unique_ptr<SharedCacheImageHeaders> m_shared_cache_image_headers_up;
};
} // namespace lldb_private
Index: lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp
===================================================================
--- lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp
+++ lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp
@@ -1619,6 +1619,146 @@
return m_isa_hash_table_ptr;
}
+std::unique_ptr<AppleObjCRuntimeV2::SharedCacheImageHeaders>
+AppleObjCRuntimeV2::SharedCacheImageHeaders::CreateSharedCacheImageHeaders(
+ AppleObjCRuntimeV2 &runtime) {
+ Log *log = GetLog(LLDBLog::Process | LLDBLog::Types);
+ Process *process = runtime.GetProcess();
+ ModuleSP objc_module_sp(runtime.GetObjCModule());
+ if (!objc_module_sp || !process)
+ return nullptr;
+
+ const Symbol *symbol = objc_module_sp->FindFirstSymbolWithNameAndType(
+ ConstString("objc_debug_headerInfoRWs"), lldb::eSymbolTypeAny);
+ if (!symbol) {
+ LLDB_LOG(log, "Symbol 'objc_debug_headerInfoRWs' unavailable. Some "
+ "information concerning the shared cache may be unavailable");
+ return nullptr;
+ }
+
+ lldb::addr_t objc_debug_headerInfoRWs_addr =
+ symbol->GetLoadAddress(&process->GetTarget());
+ if (objc_debug_headerInfoRWs_addr == LLDB_INVALID_ADDRESS) {
+ LLDB_LOG(log, "Symbol 'objc_debug_headerInfoRWs' was found but we were "
+ "unable to get its load address");
+ return nullptr;
+ }
+
+ Status error;
+ lldb::addr_t objc_debug_headerInfoRWs_ptr =
+ process->ReadPointerFromMemory(objc_debug_headerInfoRWs_addr, error);
+ if (error.Fail()) {
+ LLDB_LOG(log,
+ "Failed to read address of 'objc_debug_headerInfoRWs' at {0:x}",
+ objc_debug_headerInfoRWs_addr);
+ return nullptr;
+ }
+
+ const size_t metadata_size =
+ sizeof(uint32_t) + sizeof(uint32_t); // count + entsize
+ DataBufferHeap metadata_buffer(metadata_size, '\0');
+ process->ReadMemory(objc_debug_headerInfoRWs_ptr, metadata_buffer.GetBytes(),
+ metadata_size, error);
+ if (error.Fail()) {
+ LLDB_LOG(log,
+ "Unable to read metadata for 'objc_debug_headerInfoRWs' at {0:x}",
+ objc_debug_headerInfoRWs_ptr);
+ return nullptr;
+ }
+
+ DataExtractor metadata_extractor(metadata_buffer.GetBytes(), metadata_size,
+ process->GetByteOrder(),
+ process->GetAddressByteSize());
+ lldb::offset_t cursor = 0;
+ uint32_t count = metadata_extractor.GetU32_unchecked(&cursor);
+ uint32_t entsize = metadata_extractor.GetU32_unchecked(&cursor);
+ if (count == 0 || entsize == 0) {
+ LLDB_LOG(log,
+ "'objc_debug_headerInfoRWs' had count {0} with entsize {1}. These "
+ "should both be non-zero.",
+ count, entsize);
+ return nullptr;
+ }
+
+ std::unique_ptr<SharedCacheImageHeaders> shared_cache_image_headers(
+ new SharedCacheImageHeaders(runtime, objc_debug_headerInfoRWs_ptr, count,
+ entsize));
+ if (auto Err = shared_cache_image_headers->UpdateIfNeeded()) {
+ LLDB_LOG_ERROR(log, std::move(Err),
+ "Failed to update SharedCacheImageHeaders");
+ return nullptr;
+ }
+
+ return shared_cache_image_headers;
+}
+
+llvm::Error AppleObjCRuntimeV2::SharedCacheImageHeaders::UpdateIfNeeded() {
+ if (!m_needs_update)
+ return llvm::Error::success();
+
+ Process *process = m_runtime.GetProcess();
+ constexpr lldb::addr_t metadata_size =
+ sizeof(uint32_t) + sizeof(uint32_t); // count + entsize
+
+ Status error;
+ const lldb::addr_t first_header_addr = m_headerInfoRWs_ptr + metadata_size;
+ DataBufferHeap header_buffer(m_entsize, '\0');
+ lldb::offset_t cursor = 0;
+ for (uint32_t i = 0; i < m_count; i++) {
+ const lldb::addr_t header_addr = first_header_addr + (i * m_entsize);
+ process->ReadMemory(header_addr, header_buffer.GetBytes(), m_entsize,
+ error);
+ if (error.Fail())
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "Failed to read memory from inferior when "
+ "populating SharedCacheImageHeaders");
+
+ DataExtractor header_extractor(header_buffer.GetBytes(), m_entsize,
+ process->GetByteOrder(),
+ process->GetAddressByteSize());
+ cursor = 0;
+ bool is_loaded = false;
+ if (m_entsize == 4) {
+ uint32_t header = header_extractor.GetU32_unchecked(&cursor);
+ if (header & 1)
+ is_loaded = true;
+ } else {
+ uint64_t header = header_extractor.GetU64_unchecked(&cursor);
+ if (header & 1)
+ is_loaded = true;
+ }
+
+ if (is_loaded)
+ m_loaded_images.set(i);
+ else
+ m_loaded_images.reset(i);
+ }
+ m_needs_update = false;
+ m_version++;
+ return llvm::Error::success();
+}
+
+bool AppleObjCRuntimeV2::SharedCacheImageHeaders::IsImageLoaded(
+ uint16_t image_index) {
+ if (image_index >= m_count)
+ return false;
+ if (auto Err = UpdateIfNeeded()) {
+ Log *log = GetLog(LLDBLog::Process | LLDBLog::Types);
+ LLDB_LOG_ERROR(log, std::move(Err),
+ "Failed to update SharedCacheImageHeaders");
+ }
+ return m_loaded_images.test(image_index);
+}
+
+uint64_t AppleObjCRuntimeV2::SharedCacheImageHeaders::GetVersion() {
+ if (auto Err = UpdateIfNeeded()) {
+ Log *log = GetLog(LLDBLog::Process | LLDBLog::Types);
+ LLDB_LOG_ERROR(log, std::move(Err),
+ "Failed to update SharedCacheImageHeaders");
+ }
+ return m_version;
+}
+
std::unique_ptr<UtilityFunction>
AppleObjCRuntimeV2::DynamicClassInfoExtractor::GetClassInfoUtilityFunctionImpl(
ExecutionContext &exe_ctx, Helper helper, std::string code,
@@ -3239,6 +3379,34 @@
this->AppleObjCRuntime::GetValuesForGlobalCFBooleans(cf_true, cf_false);
}
+void AppleObjCRuntimeV2::ModulesDidLoad(const ModuleList &module_list) {
+ AppleObjCRuntime::ModulesDidLoad(module_list);
+ if (HasReadObjCLibrary() && m_shared_cache_image_headers_up)
+ m_shared_cache_image_headers_up->SetNeedsUpdate();
+}
+
+bool AppleObjCRuntimeV2::IsSharedCacheImageLoaded(uint16_t image_index) {
+ if (!m_shared_cache_image_headers_up) {
+ m_shared_cache_image_headers_up =
+ SharedCacheImageHeaders::CreateSharedCacheImageHeaders(*this);
+ }
+ if (m_shared_cache_image_headers_up)
+ return m_shared_cache_image_headers_up->IsImageLoaded(image_index);
+
+ return false;
+}
+
+std::optional<uint64_t> AppleObjCRuntimeV2::GetSharedCacheImageHeaderVersion() {
+ if (!m_shared_cache_image_headers_up) {
+ m_shared_cache_image_headers_up =
+ SharedCacheImageHeaders::CreateSharedCacheImageHeaders(*this);
+ }
+ if (m_shared_cache_image_headers_up)
+ return m_shared_cache_image_headers_up->GetVersion();
+
+ return std::nullopt;
+}
+
#pragma mark Frame recognizers
class ObjCExceptionRecognizedStackFrame : public RecognizedStackFrame {
Index: lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.h
===================================================================
--- lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.h
+++ lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.h
@@ -146,6 +146,9 @@
bool Read(Process *process, lldb::addr_t addr);
};
+ std::optional<method_list_t>
+ GetMethodList(Process *process, lldb::addr_t method_list_ptr) const;
+
struct method_t {
lldb::addr_t m_name_ptr;
lldb::addr_t m_types_ptr;
@@ -201,6 +204,21 @@
bool Read(Process *process, lldb::addr_t addr);
};
+ struct relative_list_entry_t {
+ uint16_t m_image_index;
+ int64_t m_list_offset;
+
+ bool Read(Process *process, lldb::addr_t addr);
+ };
+
+ struct relative_list_list_t {
+ uint32_t m_entsize;
+ uint32_t m_count;
+ lldb::addr_t m_first_ptr;
+
+ bool Read(Process *process, lldb::addr_t addr);
+ };
+
class iVarsStorage {
public:
iVarsStorage();
@@ -223,7 +241,8 @@
ClassDescriptorV2(AppleObjCRuntimeV2 &runtime,
ObjCLanguageRuntime::ObjCISA isa, const char *name)
: m_runtime(runtime), m_objc_class_ptr(isa), m_name(name),
- m_ivars_storage() {}
+ m_ivars_storage(), m_image_to_method_lists(), m_last_version_updated() {
+ }
bool Read_objc_class(Process *process,
std::unique_ptr<objc_class_t> &objc_class) const;
@@ -232,6 +251,15 @@
std::unique_ptr<class_ro_t> &class_ro,
std::unique_ptr<class_rw_t> &class_rw) const;
+ bool ProcessMethodList(std::function<bool(const char *, const char *)> const
+ &instance_method_func,
+ method_list_t &method_list) const;
+
+ bool ProcessRelativeMethodLists(
+ std::function<bool(const char *, const char *)> const
+ &instance_method_func,
+ lldb::addr_t relative_method_list_ptr) const;
+
AppleObjCRuntimeV2
&m_runtime; // The runtime, so we can read information lazily.
lldb::addr_t m_objc_class_ptr; // The address of the objc_class_t. (I.e.,
@@ -239,6 +267,10 @@
// their ISA)
ConstString m_name; // May be NULL
iVarsStorage m_ivars_storage;
+
+ mutable std::map<uint16_t, std::vector<method_list_t>>
+ m_image_to_method_lists;
+ mutable std::optional<uint64_t> m_last_version_updated;
};
// tagged pointer descriptor
Index: lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.cpp
===================================================================
--- lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.cpp
+++ lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.cpp
@@ -370,6 +370,155 @@
return !error.Fail();
}
+bool ClassDescriptorV2::relative_list_entry_t::Read(Process *process,
+ lldb::addr_t addr) {
+ Log *log = GetLog(LLDBLog::Types);
+ size_t size = sizeof(uint64_t); // m_image_index : 16
+ // m_list_offset : 48
+
+ DataBufferHeap buffer(size, '\0');
+ Status error;
+
+ process->ReadMemory(addr, buffer.GetBytes(), size, error);
+ // FIXME: Propagate this error up
+ if (error.Fail()) {
+ LLDB_LOG(log, "Failed to read relative_list_entry_t at address {0:x}",
+ addr);
+ return false;
+ }
+
+ DataExtractor extractor(buffer.GetBytes(), size, process->GetByteOrder(),
+ process->GetAddressByteSize());
+ lldb::offset_t cursor = 0;
+ uint64_t raw_entry = extractor.GetU64_unchecked(&cursor);
+ m_image_index = raw_entry & 0xFFFF;
+ m_list_offset = (int64_t)(raw_entry >> 16);
+ return true;
+}
+
+bool ClassDescriptorV2::relative_list_list_t::Read(Process *process,
+ lldb::addr_t addr) {
+ Log *log = GetLog(LLDBLog::Types);
+ size_t size = sizeof(uint32_t) // m_entsize
+ + sizeof(uint32_t); // m_count
+
+ DataBufferHeap buffer(size, '\0');
+ Status error;
+
+ // FIXME: Propagate this error up
+ process->ReadMemory(addr, buffer.GetBytes(), size, error);
+ if (error.Fail()) {
+ LLDB_LOG(log, "Failed to read relative_list_list_t at address 0x" PRIx64,
+ addr);
+ return false;
+ }
+
+ DataExtractor extractor(buffer.GetBytes(), size, process->GetByteOrder(),
+ process->GetAddressByteSize());
+ lldb::offset_t cursor = 0;
+ m_entsize = extractor.GetU32_unchecked(&cursor);
+ m_count = extractor.GetU32_unchecked(&cursor);
+ m_first_ptr = addr + cursor;
+ return true;
+}
+
+std::optional<ClassDescriptorV2::method_list_t>
+ClassDescriptorV2::GetMethodList(Process *process,
+ lldb::addr_t method_list_ptr) const {
+ Log *log = GetLog(LLDBLog::Types);
+ ClassDescriptorV2::method_list_t method_list;
+ if (!method_list.Read(process, method_list_ptr))
+ return std::nullopt;
+
+ const size_t method_size = method_t::GetSize(process, method_list.m_is_small);
+ if (method_list.m_entsize != method_size) {
+ LLDB_LOG(log,
+ "method_list_t at address 0x" PRIx64 " has an entsize of " PRIu16
+ " but method size should be " PRIu64,
+ method_list_ptr, method_list.m_entsize, method_size);
+ return std::nullopt;
+ }
+
+ return method_list;
+}
+
+bool ClassDescriptorV2::ProcessMethodList(
+ std::function<bool(const char *, const char *)> const &instance_method_func,
+ ClassDescriptorV2::method_list_t &method_list) const {
+ lldb_private::Process *process = m_runtime.GetProcess();
+ auto method = std::make_unique<method_t>();
+ lldb::addr_t relative_selector_base_addr =
+ m_runtime.GetRelativeSelectorBaseAddr();
+ for (uint32_t i = 0, e = method_list.m_count; i < e; ++i) {
+ method->Read(process, method_list.m_first_ptr + (i * method_list.m_entsize),
+ relative_selector_base_addr, method_list.m_is_small,
+ method_list.m_has_direct_selector);
+ if (instance_method_func(method->m_name.c_str(), method->m_types.c_str()))
+ break;
+ }
+ return true;
+}
+
+// The relevant data structures:
+// - relative_list_list_t
+// - uint32_t count
+// - uint32_t entsize
+// - Followed by <count> number of relative_list_entry_t of size <entsize>
+//
+// - relative_list_entry_t
+// - uint64_t image_index : 16
+// - int64_t list_offset : 48
+// - Note: The above 2 fit into 8 bytes always
+//
+// image_index corresponds to an image in the shared cache
+// list_offset is used to calculate the address of the method_list_t we want
+bool ClassDescriptorV2::ProcessRelativeMethodLists(
+ std::function<bool(const char *, const char *)> const &instance_method_func,
+ lldb::addr_t relative_method_list_ptr) const {
+ lldb_private::Process *process = m_runtime.GetProcess();
+ auto relative_method_lists = std::make_unique<relative_list_list_t>();
+
+ // 1. Process the count and entsize of the relative_list_list_t
+ if (!relative_method_lists->Read(process, relative_method_list_ptr))
+ return false;
+
+ auto entry = std::make_unique<relative_list_entry_t>();
+ for (uint32_t i = 0; i < relative_method_lists->m_count; i++) {
+ // 2. Extract the image index and the list offset from the
+ // relative_list_entry_t
+ const lldb::addr_t entry_addr = relative_method_lists->m_first_ptr +
+ (i * relative_method_lists->m_entsize);
+ if (!entry->Read(process, entry_addr))
+ return false;
+
+ // 3. Calculate the pointer to the method_list_t from the
+ // relative_list_entry_t
+ const lldb::addr_t method_list_addr = entry_addr + entry->m_list_offset;
+
+ // 4. Get the method_list_t from the pointer
+ std::optional<method_list_t> method_list =
+ GetMethodList(process, method_list_addr);
+ if (!method_list)
+ return false;
+
+ // 5. Cache the result so we don't need to reconstruct it later.
+ m_image_to_method_lists[entry->m_image_index].emplace_back(*method_list);
+
+ // 6. If the relevant image is loaded, add the methods to the Decl
+ if (!m_runtime.IsSharedCacheImageLoaded(entry->m_image_index))
+ continue;
+
+ if (!ProcessMethodList(instance_method_func, *method_list))
+ return false;
+ }
+
+ // We need to keep track of the last time we updated so we can re-update the
+ // type information in the future
+ m_last_version_updated = m_runtime.GetSharedCacheImageHeaderVersion();
+
+ return true;
+}
+
bool ClassDescriptorV2::Describe(
std::function<void(ObjCLanguageRuntime::ObjCISA)> const &superclass_func,
std::function<bool(const char *, const char *)> const &instance_method_func,
@@ -393,29 +542,18 @@
superclass_func(objc_class->m_superclass);
if (instance_method_func) {
- std::unique_ptr<method_list_t> base_method_list;
-
- base_method_list = std::make_unique<method_list_t>();
- if (!base_method_list->Read(process, class_ro->m_baseMethods_ptr))
- return false;
-
- bool is_small = base_method_list->m_is_small;
- bool has_direct_selector = base_method_list->m_has_direct_selector;
-
- if (base_method_list->m_entsize != method_t::GetSize(process, is_small))
- return false;
-
- std::unique_ptr<method_t> method = std::make_unique<method_t>();
- lldb::addr_t relative_selector_base_addr =
- m_runtime.GetRelativeSelectorBaseAddr();
- for (uint32_t i = 0, e = base_method_list->m_count; i < e; ++i) {
- method->Read(process,
- base_method_list->m_first_ptr +
- (i * base_method_list->m_entsize),
- relative_selector_base_addr, is_small, has_direct_selector);
-
- if (instance_method_func(method->m_name.c_str(), method->m_types.c_str()))
- break;
+ // This is a relative list of lists
+ if (class_ro->m_baseMethods_ptr & 1) {
+ if (!ProcessRelativeMethodLists(instance_method_func,
+ class_ro->m_baseMethods_ptr ^ 1))
+ return false;
+ } else {
+ std::optional<method_list_t> base_method_list =
+ GetMethodList(process, class_ro->m_baseMethods_ptr);
+ if (!base_method_list)
+ return false;
+ if (!ProcessMethodList(instance_method_func, *base_method_list))
+ return false;
}
}
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits