forked from OSchip/llvm-project
[lldb/ObjectFileMachO] Fetch shared cache images from our own shared cache
Summary: On macOS 11, the libraries that have been integrated in the system shared cache are not present on the filesystem anymore. LLDB was using those files to get access to the symbols of those libraries. LLDB can get the images from the target process memory though. This has 2 consequences: - LLDB cannot load the images before the process starts, reporting an error if someone tries to break on a system symbol. - Loading the symbols by downloading the data from the inferior is super slow. It takes tens of seconds at the start of the debug session to populate the Module list. To fix this, we can use the library images LLDB has in its own mapping of the shared cache. Shared cache images are somewhat special as their LINKEDIT segment is moved to the end of the cache and thus the images are not contiguous in memory. All of this can hidden in ObjectFileMachO. This patch fixes a number of test failures on macOS 11 due to the first problem described above and adds some specific unittesting for the new SharedCache Host utilities. Reviewers: jasonmolenda, labath Subscribers: llvm-commits, lldb-commits Tags: #lldb, #llvm Differential Revision: https://reviews.llvm.org/D83023
This commit is contained in:
parent
219a9fea14
commit
8113a8bb79
|
@ -11,6 +11,7 @@
|
|||
|
||||
#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"
|
||||
|
@ -24,6 +25,11 @@ namespace lldb_private {
|
|||
|
||||
class FileSpec;
|
||||
|
||||
struct SharedCacheImageInfo {
|
||||
UUID uuid;
|
||||
lldb::DataBufferSP data_sp;
|
||||
};
|
||||
|
||||
class HostInfoBase {
|
||||
private:
|
||||
// Static class, unconstructable.
|
||||
|
@ -98,6 +104,13 @@ public:
|
|||
/// 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);
|
||||
|
|
|
@ -37,6 +37,11 @@ public:
|
|||
|
||||
/// 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,
|
||||
|
|
|
@ -12,8 +12,10 @@
|
|||
#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"
|
||||
|
@ -457,3 +459,64 @@ llvm::StringRef HostInfoMacOSX::GetXcodeSDKPath(XcodeSDK sdk) {
|
|||
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);
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#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"
|
||||
|
@ -123,19 +124,39 @@ ModuleSP DynamicLoaderDarwin::FindTargetModuleForImageInfo(
|
|||
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 (module_sp || !can_create)
|
||||
return module_sp;
|
||||
|
||||
if (did_create_ptr)
|
||||
*did_create_ptr = (bool)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;
|
||||
}
|
||||
|
||||
|
|
|
@ -47,8 +47,8 @@
|
|||
|
||||
#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
|
||||
|
@ -1328,6 +1328,19 @@ void ObjectFileMachO::SanitizeSegmentCommand(segment_command_64 &seg_cmd,
|
|||
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
|
||||
|
@ -1664,6 +1677,10 @@ void ObjectFileMachO::ProcessSegmentCommand(const load_command &load_cmd_,
|
|||
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);
|
||||
|
@ -2264,14 +2281,17 @@ size_t ObjectFileMachO::ParseSymtab() {
|
|||
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) {
|
||||
|
@ -2293,62 +2313,6 @@ size_t ObjectFileMachO::ParseSymtab() {
|
|||
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
|
||||
|
@ -2379,7 +2343,7 @@ size_t ObjectFileMachO::ParseSymtab() {
|
|||
// 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) {
|
||||
|
@ -2388,7 +2352,6 @@ size_t ObjectFileMachO::ParseSymtab() {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (memory_module_load_level >= eMemoryModuleLoadLevelPartial) {
|
||||
if (function_starts_load_command.cmd) {
|
||||
const addr_t func_start_addr =
|
||||
|
@ -2405,6 +2368,24 @@ size_t ObjectFileMachO::ParseSymtab() {
|
|||
}
|
||||
}
|
||||
} 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,
|
||||
|
@ -5807,8 +5788,7 @@ void ObjectFileMachO::GetLLDBSharedCacheUUID(addr_t &base_addr, UUID &uuid) {
|
|||
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");
|
||||
|
|
|
@ -225,6 +225,8 @@ protected:
|
|||
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;
|
||||
|
|
|
@ -237,6 +237,30 @@ lldb_private::Status PlatformDarwin::GetSharedModuleWithLocalCache(
|
|||
|
||||
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);
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
add_subdirectory(Breakpad)
|
||||
add_subdirectory(ELF)
|
||||
add_subdirectory(MachO)
|
||||
add_subdirectory(PECOFF)
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
add_lldb_unittest(ObjectFileMachOTests
|
||||
TestObjectFileMachO.cpp
|
||||
|
||||
LINK_LIBS
|
||||
lldbPluginObjectFileMachO
|
||||
lldbPluginSymbolFileSymtab
|
||||
lldbCore
|
||||
lldbUtilityHelpers
|
||||
LLVMTestingSupport
|
||||
)
|
|
@ -0,0 +1,79 @@
|
|||
//===-- 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
|
Loading…
Reference in New Issue