#include "lldb/Utility/ArchSpec.h"
#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/UUID.h"
#include "lldb/Utility/UserIDResolver.h"
#include "lldb/Utility/XcodeSDK.h"
#include "lldb/lldb-enumerations.h"
class FileSpec;
+struct SharedCacheImageInfo {
+ UUID uuid;
+ lldb::DataBufferSP data_sp;
+};
+
class HostInfoBase {
private:
// Static class, unconstructable.
/// Return the directory containing a specific Xcode SDK.
static llvm::StringRef GetXcodeSDKPath(XcodeSDK sdk) { return {}; }
+ /// Return information about module \p image_name if it is loaded in
+ /// the current process's address space.
+ static SharedCacheImageInfo
+ GetSharedCacheImageInfo(llvm::StringRef image_name) {
+ return {};
+ }
+
protected:
static bool ComputeSharedLibraryDirectory(FileSpec &file_spec);
static bool ComputeSupportExeDirectory(FileSpec &file_spec);
/// Query xcrun to find an Xcode SDK directory.
static llvm::StringRef GetXcodeSDKPath(XcodeSDK sdk);
+
+ /// Shared cache utilities
+ static SharedCacheImageInfo
+ GetSharedCacheImageInfo(llvm::StringRef image_name);
+
protected:
static bool ComputeSupportExeDirectory(FileSpec &file_spec);
static void ComputeHostArchitectureSupport(ArchSpec &arch_32,
#include "lldb/Host/HostInfo.h"
#include "lldb/Utility/Args.h"
#include "lldb/Utility/Log.h"
+#include "Utility/UuidCompatibility.h"
#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringMap.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
auto it_new = g_sdk_path.insert({sdk.GetString(), GetXcodeSDK(sdk)});
return it_new.first->second;
}
+
+namespace {
+struct dyld_shared_cache_dylib_text_info {
+ uint64_t version; // current version 1
+ // following fields all exist in version 1
+ uint64_t loadAddressUnslid;
+ uint64_t textSegmentSize;
+ uuid_t dylibUuid;
+ const char *path; // pointer invalid at end of iterations
+ // following fields all exist in version 2
+ uint64_t textSegmentOffset; // offset from start of cache
+};
+typedef struct dyld_shared_cache_dylib_text_info
+ dyld_shared_cache_dylib_text_info;
+}
+
+extern "C" int dyld_shared_cache_iterate_text(
+ const uuid_t cacheUuid,
+ void (^callback)(const dyld_shared_cache_dylib_text_info *info));
+extern "C" uint8_t *_dyld_get_shared_cache_range(size_t *length);
+extern "C" bool _dyld_get_shared_cache_uuid(uuid_t uuid);
+
+namespace {
+class SharedCacheInfo {
+public:
+ const UUID &GetUUID() const { return m_uuid; };
+ const llvm::StringMap<SharedCacheImageInfo> &GetImages() const {
+ return m_images;
+ };
+
+ SharedCacheInfo();
+
+private:
+ llvm::StringMap<SharedCacheImageInfo> m_images;
+ UUID m_uuid;
+};
+}
+
+SharedCacheInfo::SharedCacheInfo() {
+ size_t shared_cache_size;
+ uint8_t *shared_cache_start =
+ _dyld_get_shared_cache_range(&shared_cache_size);
+ uuid_t dsc_uuid;
+ _dyld_get_shared_cache_uuid(dsc_uuid);
+ m_uuid = UUID::fromData(dsc_uuid);
+
+ dyld_shared_cache_iterate_text(
+ dsc_uuid, ^(const dyld_shared_cache_dylib_text_info *info) {
+ m_images[info->path] = SharedCacheImageInfo{
+ UUID::fromData(info->dylibUuid, 16),
+ std::make_shared<DataBufferUnowned>(
+ shared_cache_start + info->textSegmentOffset,
+ shared_cache_size - info->textSegmentOffset)};
+ });
+}
+
+SharedCacheImageInfo
+HostInfoMacOSX::GetSharedCacheImageInfo(llvm::StringRef image_name) {
+ static SharedCacheInfo g_shared_cache_info;
+ return g_shared_cache_info.GetImages().lookup(image_name);
+}
#include "lldb/Core/Section.h"
#include "lldb/Expression/DiagnosticManager.h"
#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/HostInfo.h"
#include "lldb/Symbol/Function.h"
#include "lldb/Symbol/ObjectFile.h"
#include "lldb/Target/ABI.h"
module_sp.reset();
}
- if (!module_sp) {
- if (can_create) {
- // We'll call Target::ModulesDidLoad after all the modules have been
- // added to the target, don't let it be called for every one.
- module_sp = target.GetOrCreateModule(module_spec, false /* notify */);
- if (!module_sp || module_sp->GetObjectFile() == nullptr)
- module_sp = m_process->ReadModuleFromMemory(image_info.file_spec,
- image_info.address);
-
- if (did_create_ptr)
- *did_create_ptr = (bool)module_sp;
+ if (module_sp || !can_create)
+ return module_sp;
+
+ if (HostInfo::GetArchitecture().IsCompatibleMatch(target.GetArchitecture())) {
+ // When debugging on the host, we are most likely using the same shared
+ // cache as our inferior. The dylibs from the shared cache might not
+ // exist on the filesystem, so let's use the images in our own memory
+ // to create the modules.
+ // Check if the requested image is in our shared cache.
+ SharedCacheImageInfo image_info =
+ HostInfo::GetSharedCacheImageInfo(module_spec.GetFileSpec().GetPath());
+
+ // If we found it and it has the correct UUID, let's proceed with
+ // creating a module from the memory contents.
+ if (image_info.uuid &&
+ (!module_spec.GetUUID() || module_spec.GetUUID() == image_info.uuid)) {
+ ModuleSpec shared_cache_spec(module_spec.GetFileSpec(), image_info.uuid,
+ image_info.data_sp);
+ module_sp =
+ target.GetOrCreateModule(shared_cache_spec, false /* notify */);
}
}
+ // We'll call Target::ModulesDidLoad after all the modules have been
+ // added to the target, don't let it be called for every one.
+ if (!module_sp)
+ module_sp = target.GetOrCreateModule(module_spec, false /* notify */);
+ if (!module_sp || module_sp->GetObjectFile() == nullptr)
+ module_sp = m_process->ReadModuleFromMemory(image_info.file_spec,
+ image_info.address);
+
+ if (did_create_ptr)
+ *did_create_ptr = (bool)module_sp;
+
return module_sp;
}
#include "ObjectFileMachO.h"
-#if defined(__APPLE__) && \
- (defined(__arm__) || defined(__arm64__) || defined(__aarch64__))
+#if defined(__APPLE__)
+#include <TargetConditionals.h>
// GetLLDBSharedCacheUUID() needs to call dlsym()
#include <dlfcn.h>
#endif
if (m_length == 0 || seg_cmd.filesize == 0)
return;
+ if ((m_header.flags & MH_DYLIB_IN_CACHE) && !IsInMemory()) {
+ // In shared cache images, the load commands are relative to the
+ // shared cache file, and not the the specific image we are
+ // examining. Let's fix this up so that it looks like a normal
+ // image.
+ if (strncmp(seg_cmd.segname, "__TEXT", sizeof(seg_cmd.segname)) == 0)
+ m_text_address = seg_cmd.vmaddr;
+ if (strncmp(seg_cmd.segname, "__LINKEDIT", sizeof(seg_cmd.segname)) == 0)
+ m_linkedit_original_offset = seg_cmd.fileoff;
+
+ seg_cmd.fileoff = seg_cmd.vmaddr - m_text_address;
+ }
+
if (seg_cmd.fileoff > m_length) {
// We have a load command that says it extends past the end of the file.
// This is likely a corrupt file. We don't have any way to return an error
if (m_data.GetU32(&offset, §64.offset, num_u32s) == nullptr)
break;
+ if ((m_header.flags & MH_DYLIB_IN_CACHE) && !IsInMemory()) {
+ sect64.offset = sect64.addr - m_text_address;
+ }
+
// Keep a list of mach sections around in case we need to get at data that
// isn't stored in the abstracted Sections.
m_mach_sections.push_back(sect64);
Process *process = process_sp.get();
uint32_t memory_module_load_level = eMemoryModuleLoadLevelComplete;
+ bool is_shared_cache_image = m_header.flags & MH_DYLIB_IN_CACHE;
+ bool is_local_shared_cache_image = is_shared_cache_image && !IsInMemory();
+ SectionSP linkedit_section_sp(
+ section_list->FindSectionByName(GetSegmentNameLINKEDIT()));
- if (process && m_header.filetype != llvm::MachO::MH_OBJECT) {
+ if (process && m_header.filetype != llvm::MachO::MH_OBJECT &&
+ !is_local_shared_cache_image) {
Target &target = process->GetTarget();
memory_module_load_level = target.GetMemoryModuleLoadLevel();
- SectionSP linkedit_section_sp(
- section_list->FindSectionByName(GetSegmentNameLINKEDIT()));
// Reading mach file from memory in a process or core file...
if (linkedit_section_sp) {
strtab_addr = linkedit_load_addr + symtab_load_command.stroff -
linkedit_file_offset;
- bool data_was_read = false;
-
-#if defined(__APPLE__) && \
- (defined(__arm__) || defined(__arm64__) || defined(__aarch64__))
- if (m_header.flags & MH_DYLIB_IN_CACHE &&
- process->GetAddressByteSize() == sizeof(void *)) {
- // This mach-o memory file is in the dyld shared cache. If this
- // program is not remote and this is iOS, then this process will
- // share the same shared cache as the process we are debugging and we
- // can read the entire __LINKEDIT from the address space in this
- // process. This is a needed optimization that is used for local iOS
- // debugging only since all shared libraries in the shared cache do
- // not have corresponding files that exist in the file system of the
- // device. They have been combined into a single file. This means we
- // always have to load these files from memory. All of the symbol and
- // string tables from all of the __LINKEDIT sections from the shared
- // libraries in the shared cache have been merged into a single large
- // symbol and string table. Reading all of this symbol and string
- // table data across can slow down debug launch times, so we optimize
- // this by reading the memory for the __LINKEDIT section from this
- // process.
-
- UUID lldb_shared_cache;
- addr_t lldb_shared_cache_addr;
- GetLLDBSharedCacheUUID(lldb_shared_cache_addr, lldb_shared_cache);
- UUID process_shared_cache;
- addr_t process_shared_cache_addr;
- GetProcessSharedCacheUUID(process, process_shared_cache_addr,
- process_shared_cache);
- bool use_lldb_cache = true;
- if (lldb_shared_cache.IsValid() && process_shared_cache.IsValid() &&
- (lldb_shared_cache != process_shared_cache ||
- process_shared_cache_addr != lldb_shared_cache_addr)) {
- use_lldb_cache = false;
- }
-
- PlatformSP platform_sp(target.GetPlatform());
- if (platform_sp && platform_sp->IsHost() && use_lldb_cache) {
- data_was_read = true;
- nlist_data.SetData((void *)symoff_addr, nlist_data_byte_size,
- eByteOrderLittle);
- strtab_data.SetData((void *)strtab_addr, strtab_data_byte_size,
- eByteOrderLittle);
- if (function_starts_load_command.cmd) {
- const addr_t func_start_addr =
- linkedit_load_addr + function_starts_load_command.dataoff -
- linkedit_file_offset;
- function_starts_data.SetData((void *)func_start_addr,
- function_starts_load_command.datasize,
- eByteOrderLittle);
- }
- }
- }
-#endif
-
- if (!data_was_read) {
// Always load dyld - the dynamic linker - from memory if we didn't
// find a binary anywhere else. lldb will not register
// dylib/framework/bundle loads/unloads if we don't have the dyld
// problem. For binaries outside the shared cache, it's faster to
// read the entire strtab at once instead of piece-by-piece as we
// process the nlist records.
- if ((m_header.flags & MH_DYLIB_IN_CACHE) == 0) {
+ if (!is_shared_cache_image) {
DataBufferSP strtab_data_sp(
ReadMemory(process_sp, strtab_addr, strtab_data_byte_size));
if (strtab_data_sp) {
}
}
}
- }
if (memory_module_load_level >= eMemoryModuleLoadLevelPartial) {
if (function_starts_load_command.cmd) {
const addr_t func_start_addr =
}
}
} else {
+ if (is_local_shared_cache_image) {
+ // The load commands in shared cache images are relative to the
+ // beginning of the shared cache, not the library image. The
+ // data we get handed when creating the ObjectFileMachO starts
+ // at the beginning of a specific library and spans to the end
+ // of the cache to be able to reach the shared LINKEDIT
+ // segments. We need to convert the load command offsets to be
+ // relative to the beginning of our specific image.
+ lldb::addr_t linkedit_offset = linkedit_section_sp->GetFileOffset();
+ lldb::offset_t linkedit_slide =
+ linkedit_offset - m_linkedit_original_offset;
+ symtab_load_command.symoff += linkedit_slide;
+ symtab_load_command.stroff += linkedit_slide;
+ dyld_info.export_off += linkedit_slide;
+ m_dysymtab.indirectsymoff += linkedit_slide;
+ function_starts_load_command.dataoff += linkedit_slide;
+ }
+
nlist_data.SetData(m_data, symtab_load_command.symoff,
nlist_data_byte_size);
strtab_data.SetData(m_data, symtab_load_command.stroff,
uuid.Clear();
base_addr = LLDB_INVALID_ADDRESS;
-#if defined(__APPLE__) && \
- (defined(__arm__) || defined(__arm64__) || defined(__aarch64__))
+#if defined(__APPLE__)
uint8_t *(*dyld_get_all_image_infos)(void);
dyld_get_all_image_infos =
(uint8_t * (*)()) dlsym(RTLD_DEFAULT, "_dyld_get_all_image_infos");
typedef lldb_private::RangeVector<uint32_t, uint32_t> FileRangeArray;
lldb_private::Address m_entry_point_address;
FileRangeArray m_thread_context_offsets;
+ lldb::offset_t m_linkedit_original_offset;
+ lldb::addr_t m_text_address;
bool m_thread_context_offsets_valid;
lldb_private::FileSpecList m_reexported_dylibs;
bool m_allow_assembly_emulation_unwind_plans;
Status err;
+ if (IsHost()) {
+ // When debugging on the host, we are most likely using the same shared
+ // cache as our inferior. The dylibs from the shared cache might not
+ // exist on the filesystem, so let's use the images in our own memory
+ // to create the modules.
+
+ // Check if the requested image is in our shared cache.
+ SharedCacheImageInfo image_info =
+ HostInfo::GetSharedCacheImageInfo(module_spec.GetFileSpec().GetPath());
+
+ // If we found it and it has the correct UUID, let's proceed with
+ // creating a module from the memory contents.
+ if (image_info.uuid &&
+ (!module_spec.GetUUID() || module_spec.GetUUID() == image_info.uuid)) {
+ ModuleSpec shared_cache_spec(module_spec.GetFileSpec(), image_info.uuid,
+ image_info.data_sp);
+ err = ModuleList::GetSharedModule(shared_cache_spec, module_sp,
+ module_search_paths_ptr,
+ old_module_sp_ptr, did_create_ptr);
+ if (module_sp)
+ return err;
+ }
+ }
+
err = ModuleList::GetSharedModule(module_spec, module_sp,
module_search_paths_ptr, old_module_sp_ptr,
did_create_ptr);
add_subdirectory(Breakpad)
add_subdirectory(ELF)
+add_subdirectory(MachO)
add_subdirectory(PECOFF)
--- /dev/null
+add_lldb_unittest(ObjectFileMachOTests
+ TestObjectFileMachO.cpp
+
+ LINK_LIBS
+ lldbPluginObjectFileMachO
+ lldbPluginSymbolFileSymtab
+ lldbCore
+ lldbUtilityHelpers
+ LLVMTestingSupport
+ )
--- /dev/null
+//===-- ObjectFileMachOTest.cpp -------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Host/HostInfo.h"
+#include "Plugins/ObjectFile/Mach-O/ObjectFileMachO.h"
+#include "TestingSupport/SubsystemRAII.h"
+#include "TestingSupport/TestUtilities.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/lldb-defines.h"
+#include "gtest/gtest.h"
+
+#ifdef __APPLE__
+#include <dlfcn.h>
+#endif
+
+using namespace lldb_private;
+using namespace llvm;
+
+namespace {
+class ObjectFileMachOTest : public ::testing::Test {
+ SubsystemRAII<FileSystem, HostInfo, ObjectFileMachO> subsystems;
+};
+} // namespace
+
+#if defined(__APPLE__)
+TEST_F(ObjectFileMachOTest, ModuleFromSharedCacheInfo) {
+ SharedCacheImageInfo image_info =
+ HostInfo::GetSharedCacheImageInfo("/usr/lib/libobjc.A.dylib");
+ EXPECT_TRUE(image_info.uuid);
+ EXPECT_TRUE(image_info.data_sp);
+
+ ModuleSpec spec(FileSpec(), UUID(), image_info.data_sp);
+ lldb::ModuleSP module = std::make_shared<Module>(spec);
+ ObjectFile *OF = module->GetObjectFile();
+ ASSERT_TRUE(llvm::isa<ObjectFileMachO>(OF));
+ EXPECT_TRUE(
+ OF->GetArchitecture().IsCompatibleMatch(HostInfo::GetArchitecture()));
+ Symtab *symtab = OF->GetSymtab();
+ ASSERT_NE(symtab, nullptr);
+ void *libobjc = dlopen("/usr/lib/libobjc.A.dylib", RTLD_LAZY);
+ ASSERT_NE(libobjc, nullptr);
+
+ // This function checks that if we read something from the
+ // ObjectFile we get through the shared cache in-mmeory
+ // buffer, it matches what we get by reading directly the
+ // memory of the symbol.
+ auto check_symbol = [&](const char *sym_name) {
+ std::vector<uint32_t> symbol_indices;
+ symtab->FindAllSymbolsWithNameAndType(ConstString(sym_name),
+ lldb::eSymbolTypeAny, symbol_indices);
+ EXPECT_EQ(symbol_indices.size(), 1u);
+
+ Symbol *sym = symtab->SymbolAtIndex(symbol_indices[0]);
+ ASSERT_NE(sym, nullptr);
+ Address base = sym->GetAddress();
+ size_t size = sym->GetByteSize();
+ ASSERT_NE(size, 0u);
+ uint8_t buffer[size];
+ EXPECT_EQ(OF->ReadSectionData(base.GetSection().get(), base.GetOffset(),
+ buffer, size),
+ size);
+
+ void *sym_addr = dlsym(libobjc, sym_name);
+ ASSERT_NE(sym_addr, nullptr);
+ EXPECT_EQ(memcmp(buffer, sym_addr, size), 0);
+ };
+
+ // Read a symbol from the __TEXT segment...
+ check_symbol("objc_msgSend");
+ // ... and one from the __DATA segment
+ check_symbol("OBJC_CLASS_$_NSObject");
+}
+#endif