Allow firmware binaries to be specified only by load address
Add support to Mach-O corefiles and to live gdb remote serial protocol connections for the corefile/remote stub to provide a list of load addresses of binaries that should be found & loaded by lldb, and nothing else. lldb will try to parse the binary out of memory, and if it can find a UUID, try to find a binary & its debug information based on the UUID, falling back to using the memory image if it must. A bit of code unification from three parts of lldb that were loading individual binaries already, so there is a shared method in DynamicLoader to handle all of the variations they were doing. Re-landing this with a uuid_is_null() implementation added to Utility/UuidCompatibility.h for non-Darwin systems. Differential Revision: https://reviews.llvm.org/D130813 rdar://94249937 rdar://94249384
This commit is contained in:
parent
560efad701
commit
96d12187b3
|
@ -1073,6 +1073,12 @@ main-binary-uuid: is the UUID of a firmware type binary that the gdb stub knows
|
||||||
main-binary-address: is the load address of the firmware type binary
|
main-binary-address: is the load address of the firmware type binary
|
||||||
main-binary-slide: is the slide of the firmware type binary, if address isn't known
|
main-binary-slide: is the slide of the firmware type binary, if address isn't known
|
||||||
|
|
||||||
|
binary-addresses: A comma-separated list of binary load addresses base16.
|
||||||
|
lldb will parse the binaries in memory to get UUIDs, then
|
||||||
|
try to find the binaries & debug info by UUID. Intended for
|
||||||
|
use with a small number of firmware type binaries where the
|
||||||
|
search for binary/debug info may be expensive.
|
||||||
|
|
||||||
//----------------------------------------------------------------------
|
//----------------------------------------------------------------------
|
||||||
// "qShlibInfoAddr"
|
// "qShlibInfoAddr"
|
||||||
//
|
//
|
||||||
|
|
|
@ -210,6 +210,52 @@ public:
|
||||||
lldb::addr_t base_addr,
|
lldb::addr_t base_addr,
|
||||||
bool base_addr_is_offset);
|
bool base_addr_is_offset);
|
||||||
|
|
||||||
|
/// Find/load a binary into lldb given a UUID and the address where it is
|
||||||
|
/// loaded in memory, or a slide to be applied to the file address.
|
||||||
|
/// May force an expensive search on the computer to find the binary by
|
||||||
|
/// UUID, should not be used for a large number of binaries - intended for
|
||||||
|
/// an environment where there may be one, or a few, binaries resident in
|
||||||
|
/// memory.
|
||||||
|
///
|
||||||
|
/// Given a UUID, search for a binary and load it at the address provided,
|
||||||
|
/// or with the slide applied, or at the file address unslid.
|
||||||
|
///
|
||||||
|
/// Given an address, try to read the binary out of memory, get the UUID,
|
||||||
|
/// find the file if possible and load it unslid, or add the memory module.
|
||||||
|
///
|
||||||
|
/// \param[in] process
|
||||||
|
/// The process to add this binary to.
|
||||||
|
///
|
||||||
|
/// \param[in] uuid
|
||||||
|
/// UUID of the binary to be loaded. UUID may be empty, and if a
|
||||||
|
/// load address is supplied, will read the binary from memory, get
|
||||||
|
/// a UUID and try to find a local binary. There is a performance
|
||||||
|
/// cost to doing this, it is not preferable.
|
||||||
|
///
|
||||||
|
/// \param[in] value
|
||||||
|
/// Address where the binary should be loaded, or read out of memory.
|
||||||
|
/// Or a slide value, to be applied to the file addresses of the binary.
|
||||||
|
///
|
||||||
|
/// \param[in] value_is_offset
|
||||||
|
/// A flag indicating that \p value is an address, or an offset to
|
||||||
|
/// be applied to the file addresses.
|
||||||
|
///
|
||||||
|
/// \param[in] force_symbol_search
|
||||||
|
/// Allow the search to do a possibly expensive external search for
|
||||||
|
/// the ObjectFile and/or SymbolFile.
|
||||||
|
///
|
||||||
|
/// \param[in] notify
|
||||||
|
/// Whether ModulesDidLoad should be called when a binary has been added
|
||||||
|
/// to the Target. The caller may prefer to batch up these when loading
|
||||||
|
/// multiple binaries.
|
||||||
|
///
|
||||||
|
/// \return
|
||||||
|
/// Returns a shared pointer for the Module that has been added.
|
||||||
|
static lldb::ModuleSP
|
||||||
|
LoadBinaryWithUUIDAndAddress(Process *process, UUID uuid, lldb::addr_t value,
|
||||||
|
bool value_is_offset, bool force_symbol_search,
|
||||||
|
bool notify);
|
||||||
|
|
||||||
/// Get information about the shared cache for a process, if possible.
|
/// Get information about the shared cache for a process, if possible.
|
||||||
///
|
///
|
||||||
/// On some systems (e.g. Darwin based systems), a set of libraries that are
|
/// On some systems (e.g. Darwin based systems), a set of libraries that are
|
||||||
|
|
|
@ -13,11 +13,15 @@
|
||||||
#include "lldb/Core/ModuleSpec.h"
|
#include "lldb/Core/ModuleSpec.h"
|
||||||
#include "lldb/Core/PluginManager.h"
|
#include "lldb/Core/PluginManager.h"
|
||||||
#include "lldb/Core/Section.h"
|
#include "lldb/Core/Section.h"
|
||||||
|
#include "lldb/Symbol/LocateSymbolFile.h"
|
||||||
#include "lldb/Symbol/ObjectFile.h"
|
#include "lldb/Symbol/ObjectFile.h"
|
||||||
#include "lldb/Target/MemoryRegionInfo.h"
|
#include "lldb/Target/MemoryRegionInfo.h"
|
||||||
|
#include "lldb/Target/Platform.h"
|
||||||
#include "lldb/Target/Process.h"
|
#include "lldb/Target/Process.h"
|
||||||
#include "lldb/Target/Target.h"
|
#include "lldb/Target/Target.h"
|
||||||
#include "lldb/Utility/ConstString.h"
|
#include "lldb/Utility/ConstString.h"
|
||||||
|
#include "lldb/Utility/LLDBLog.h"
|
||||||
|
#include "lldb/Utility/Log.h"
|
||||||
#include "lldb/lldb-private-interfaces.h"
|
#include "lldb/lldb-private-interfaces.h"
|
||||||
|
|
||||||
#include "llvm/ADT/StringRef.h"
|
#include "llvm/ADT/StringRef.h"
|
||||||
|
@ -171,6 +175,100 @@ ModuleSP DynamicLoader::LoadModuleAtAddress(const FileSpec &file,
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ModuleSP ReadUnnamedMemoryModule(Process *process, addr_t addr) {
|
||||||
|
char namebuf[80];
|
||||||
|
snprintf(namebuf, sizeof(namebuf), "memory-image-0x%" PRIx64, addr);
|
||||||
|
return process->ReadModuleFromMemory(FileSpec(namebuf), addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
ModuleSP DynamicLoader::LoadBinaryWithUUIDAndAddress(Process *process,
|
||||||
|
UUID uuid, addr_t value,
|
||||||
|
bool value_is_offset,
|
||||||
|
bool force_symbol_search,
|
||||||
|
bool notify) {
|
||||||
|
ModuleSP memory_module_sp;
|
||||||
|
ModuleSP module_sp;
|
||||||
|
PlatformSP platform_sp = process->GetTarget().GetPlatform();
|
||||||
|
Target &target = process->GetTarget();
|
||||||
|
Status error;
|
||||||
|
ModuleSpec module_spec;
|
||||||
|
module_spec.GetUUID() = uuid;
|
||||||
|
|
||||||
|
if (!uuid.IsValid() && !value_is_offset) {
|
||||||
|
memory_module_sp = ReadUnnamedMemoryModule(process, value);
|
||||||
|
|
||||||
|
if (memory_module_sp)
|
||||||
|
uuid = memory_module_sp->GetUUID();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uuid.IsValid()) {
|
||||||
|
ModuleSpec module_spec;
|
||||||
|
module_spec.GetUUID() = uuid;
|
||||||
|
|
||||||
|
if (!module_sp)
|
||||||
|
module_sp = target.GetOrCreateModule(module_spec, false, &error);
|
||||||
|
|
||||||
|
// If we haven't found a binary, or we don't have a SymbolFile, see
|
||||||
|
// if there is an external search tool that can find it.
|
||||||
|
if (force_symbol_search &&
|
||||||
|
(!module_sp || !module_sp->GetSymbolFileFileSpec())) {
|
||||||
|
Symbols::DownloadObjectAndSymbolFile(module_spec, error, true);
|
||||||
|
if (FileSystem::Instance().Exists(module_spec.GetFileSpec())) {
|
||||||
|
module_sp = std::make_shared<Module>(module_spec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we couldn't find the binary anywhere else, as a last resort,
|
||||||
|
// read it out of memory.
|
||||||
|
if (!module_sp.get() && value != LLDB_INVALID_ADDRESS && !value_is_offset) {
|
||||||
|
if (!memory_module_sp)
|
||||||
|
memory_module_sp = ReadUnnamedMemoryModule(process, value);
|
||||||
|
if (memory_module_sp)
|
||||||
|
module_sp = memory_module_sp;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log *log = GetLog(LLDBLog::DynamicLoader);
|
||||||
|
if (module_sp.get()) {
|
||||||
|
target.GetImages().AppendIfNeeded(module_sp, false);
|
||||||
|
|
||||||
|
bool changed = false;
|
||||||
|
if (module_sp->GetObjectFile()) {
|
||||||
|
if (value != LLDB_INVALID_ADDRESS) {
|
||||||
|
LLDB_LOGF(log, "Loading binary UUID %s at %s 0x%" PRIx64,
|
||||||
|
uuid.GetAsString().c_str(),
|
||||||
|
value_is_offset ? "offset" : "address", value);
|
||||||
|
module_sp->SetLoadAddress(target, value, value_is_offset, changed);
|
||||||
|
} else {
|
||||||
|
// No address/offset/slide, load the binary at file address,
|
||||||
|
// offset 0.
|
||||||
|
LLDB_LOGF(log, "Loading binary UUID %s at file address",
|
||||||
|
uuid.GetAsString().c_str());
|
||||||
|
module_sp->SetLoadAddress(target, 0, true /* value_is_slide */,
|
||||||
|
changed);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// In-memory image, load at its true address, offset 0.
|
||||||
|
LLDB_LOGF(log, "Loading binary UUID %s from memory at address 0x%" PRIx64,
|
||||||
|
uuid.GetAsString().c_str(), value);
|
||||||
|
module_sp->SetLoadAddress(target, 0, true /* value_is_slide */, changed);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (notify) {
|
||||||
|
ModuleList added_module;
|
||||||
|
added_module.Append(module_sp, false);
|
||||||
|
target.ModulesDidLoad(added_module);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LLDB_LOGF(log, "Unable to find binary with UUID %s and load it at "
|
||||||
|
"%s 0x%" PRIx64,
|
||||||
|
uuid.GetAsString().c_str(),
|
||||||
|
value_is_offset ? "offset" : "address", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return module_sp;
|
||||||
|
}
|
||||||
|
|
||||||
int64_t DynamicLoader::ReadUnsignedIntWithSizeInBytes(addr_t addr,
|
int64_t DynamicLoader::ReadUnsignedIntWithSizeInBytes(addr_t addr,
|
||||||
int size_in_bytes) {
|
int size_in_bytes) {
|
||||||
Status error;
|
Status error;
|
||||||
|
|
|
@ -5600,7 +5600,8 @@ bool ObjectFileMachO::GetCorefileMainBinaryInfo(addr_t &value,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_data.CopyData(offset, sizeof(uuid_t), raw_uuid) != 0) {
|
if (m_data.CopyData(offset, sizeof(uuid_t), raw_uuid) != 0) {
|
||||||
uuid = UUID::fromOptionalData(raw_uuid, sizeof(uuid_t));
|
if (!uuid_is_null(raw_uuid))
|
||||||
|
uuid = UUID::fromOptionalData(raw_uuid, sizeof(uuid_t));
|
||||||
// convert the "main bin spec" type into our
|
// convert the "main bin spec" type into our
|
||||||
// ObjectFile::BinaryType enum
|
// ObjectFile::BinaryType enum
|
||||||
switch (binspec_type) {
|
switch (binspec_type) {
|
||||||
|
@ -6901,7 +6902,8 @@ ObjectFileMachO::GetCorefileAllImageInfos() {
|
||||||
|
|
||||||
MachOCorefileImageEntry image_entry;
|
MachOCorefileImageEntry image_entry;
|
||||||
image_entry.filename = (const char *)m_data.GetCStr(&filepath_offset);
|
image_entry.filename = (const char *)m_data.GetCStr(&filepath_offset);
|
||||||
image_entry.uuid = UUID::fromData(uuid, sizeof(uuid_t));
|
if (!uuid_is_null(uuid))
|
||||||
|
image_entry.uuid = UUID::fromData(uuid, sizeof(uuid_t));
|
||||||
image_entry.load_address = load_address;
|
image_entry.load_address = load_address;
|
||||||
image_entry.currently_executing = currently_executing;
|
image_entry.currently_executing = currently_executing;
|
||||||
|
|
||||||
|
@ -6932,9 +6934,11 @@ ObjectFileMachO::GetCorefileAllImageInfos() {
|
||||||
|
|
||||||
MachOCorefileImageEntry image_entry;
|
MachOCorefileImageEntry image_entry;
|
||||||
image_entry.filename = filename;
|
image_entry.filename = filename;
|
||||||
image_entry.uuid = UUID::fromData(uuid, sizeof(uuid_t));
|
if (!uuid_is_null(uuid))
|
||||||
|
image_entry.uuid = UUID::fromData(uuid, sizeof(uuid_t));
|
||||||
image_entry.load_address = load_address;
|
image_entry.load_address = load_address;
|
||||||
image_entry.slide = slide;
|
image_entry.slide = slide;
|
||||||
|
image_entry.currently_executing = true;
|
||||||
image_infos.all_image_infos.push_back(image_entry);
|
image_infos.all_image_infos.push_back(image_entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6951,42 +6955,41 @@ bool ObjectFileMachO::LoadCoreFileImages(lldb_private::Process &process) {
|
||||||
|
|
||||||
ModuleList added_modules;
|
ModuleList added_modules;
|
||||||
for (const MachOCorefileImageEntry &image : image_infos.all_image_infos) {
|
for (const MachOCorefileImageEntry &image : image_infos.all_image_infos) {
|
||||||
ModuleSpec module_spec;
|
ModuleSP module_sp;
|
||||||
module_spec.GetUUID() = image.uuid;
|
|
||||||
if (image.filename.empty()) {
|
if (!image.filename.empty()) {
|
||||||
char namebuf[80];
|
|
||||||
if (image.load_address != LLDB_INVALID_ADDRESS)
|
|
||||||
snprintf(namebuf, sizeof(namebuf), "mem-image-0x%" PRIx64,
|
|
||||||
image.load_address);
|
|
||||||
else
|
|
||||||
snprintf(namebuf, sizeof(namebuf), "mem-image+0x%" PRIx64, image.slide);
|
|
||||||
module_spec.GetFileSpec() = FileSpec(namebuf);
|
|
||||||
} else {
|
|
||||||
module_spec.GetFileSpec() = FileSpec(image.filename.c_str());
|
|
||||||
}
|
|
||||||
if (image.currently_executing) {
|
|
||||||
Status error;
|
Status error;
|
||||||
Symbols::DownloadObjectAndSymbolFile(module_spec, error, true);
|
ModuleSpec module_spec;
|
||||||
if (FileSystem::Instance().Exists(module_spec.GetFileSpec())) {
|
module_spec.GetUUID() = image.uuid;
|
||||||
process.GetTarget().GetOrCreateModule(module_spec, false);
|
module_spec.GetFileSpec() = FileSpec(image.filename.c_str());
|
||||||
|
if (image.currently_executing) {
|
||||||
|
Symbols::DownloadObjectAndSymbolFile(module_spec, error, true);
|
||||||
|
if (FileSystem::Instance().Exists(module_spec.GetFileSpec())) {
|
||||||
|
process.GetTarget().GetOrCreateModule(module_spec, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
module_sp =
|
||||||
Status error;
|
process.GetTarget().GetOrCreateModule(module_spec, false, &error);
|
||||||
ModuleSP module_sp =
|
process.GetTarget().GetImages().AppendIfNeeded(module_sp,
|
||||||
process.GetTarget().GetOrCreateModule(module_spec, false, &error);
|
false /* notify */);
|
||||||
if (!module_sp.get() || !module_sp->GetObjectFile()) {
|
} else {
|
||||||
if (image.load_address != LLDB_INVALID_ADDRESS) {
|
if (image.load_address != LLDB_INVALID_ADDRESS) {
|
||||||
module_sp = process.ReadModuleFromMemory(module_spec.GetFileSpec(),
|
module_sp = DynamicLoader::LoadBinaryWithUUIDAndAddress(
|
||||||
image.load_address);
|
&process, image.uuid, image.load_address,
|
||||||
|
false /* value_is_offset */, image.currently_executing,
|
||||||
|
false /* notify */);
|
||||||
|
} else if (image.slide != LLDB_INVALID_ADDRESS) {
|
||||||
|
module_sp = DynamicLoader::LoadBinaryWithUUIDAndAddress(
|
||||||
|
&process, image.uuid, image.slide, true /* value_is_offset */,
|
||||||
|
image.currently_executing, false /* notify */);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (module_sp.get()) {
|
if (module_sp.get()) {
|
||||||
// Will call ModulesDidLoad with all modules once they've all
|
// Will call ModulesDidLoad with all modules once they've all
|
||||||
// been added to the Target with load addresses. Don't notify
|
// been added to the Target with load addresses. Don't notify
|
||||||
// here, before the load address is set.
|
// here, before the load address is set.
|
||||||
const bool notify = false;
|
added_modules.Append(module_sp, false /* notify */);
|
||||||
process.GetTarget().GetImages().AppendIfNeeded(module_sp, notify);
|
|
||||||
added_modules.Append(module_sp, notify);
|
|
||||||
if (image.segment_load_addresses.size() > 0) {
|
if (image.segment_load_addresses.size() > 0) {
|
||||||
if (log) {
|
if (log) {
|
||||||
std::string uuidstr = image.uuid.GetAsString();
|
std::string uuidstr = image.uuid.GetAsString();
|
||||||
|
|
|
@ -1033,6 +1033,13 @@ bool GDBRemoteCommunicationClient::GetProcessStandaloneBinary(
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<addr_t>
|
||||||
|
GDBRemoteCommunicationClient::GetProcessStandaloneBinaries() {
|
||||||
|
if (m_qProcessInfo_is_valid == eLazyBoolCalculate)
|
||||||
|
GetCurrentProcessInfo();
|
||||||
|
return m_binary_addresses;
|
||||||
|
}
|
||||||
|
|
||||||
bool GDBRemoteCommunicationClient::GetGDBServerVersion() {
|
bool GDBRemoteCommunicationClient::GetGDBServerVersion() {
|
||||||
if (m_qGDBServerVersion_is_valid == eLazyBoolCalculate) {
|
if (m_qGDBServerVersion_is_valid == eLazyBoolCalculate) {
|
||||||
m_gdb_server_name.clear();
|
m_gdb_server_name.clear();
|
||||||
|
@ -2192,6 +2199,14 @@ bool GDBRemoteCommunicationClient::GetCurrentProcessInfo(bool allow_lazy) {
|
||||||
m_process_standalone_value_is_offset = false;
|
m_process_standalone_value_is_offset = false;
|
||||||
++num_keys_decoded;
|
++num_keys_decoded;
|
||||||
}
|
}
|
||||||
|
} else if (name.equals("binary-addresses")) {
|
||||||
|
addr_t addr;
|
||||||
|
while (!value.empty()) {
|
||||||
|
llvm::StringRef addr_str;
|
||||||
|
std::tie(addr_str, value) = value.split(',');
|
||||||
|
if (!addr_str.getAsInteger(16, addr))
|
||||||
|
m_binary_addresses.push_back(addr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (num_keys_decoded > 0)
|
if (num_keys_decoded > 0)
|
||||||
|
|
|
@ -220,6 +220,8 @@ public:
|
||||||
bool GetProcessStandaloneBinary(UUID &uuid, lldb::addr_t &value,
|
bool GetProcessStandaloneBinary(UUID &uuid, lldb::addr_t &value,
|
||||||
bool &value_is_offset);
|
bool &value_is_offset);
|
||||||
|
|
||||||
|
std::vector<lldb::addr_t> GetProcessStandaloneBinaries();
|
||||||
|
|
||||||
void GetRemoteQSupported();
|
void GetRemoteQSupported();
|
||||||
|
|
||||||
bool GetVContSupported(char flavor);
|
bool GetVContSupported(char flavor);
|
||||||
|
@ -593,6 +595,7 @@ protected:
|
||||||
UUID m_process_standalone_uuid;
|
UUID m_process_standalone_uuid;
|
||||||
lldb::addr_t m_process_standalone_value = LLDB_INVALID_ADDRESS;
|
lldb::addr_t m_process_standalone_value = LLDB_INVALID_ADDRESS;
|
||||||
bool m_process_standalone_value_is_offset = false;
|
bool m_process_standalone_value_is_offset = false;
|
||||||
|
std::vector<lldb::addr_t> m_binary_addresses;
|
||||||
llvm::VersionTuple m_os_version;
|
llvm::VersionTuple m_os_version;
|
||||||
llvm::VersionTuple m_maccatalyst_version;
|
llvm::VersionTuple m_maccatalyst_version;
|
||||||
std::string m_os_build;
|
std::string m_os_build;
|
||||||
|
|
|
@ -581,80 +581,31 @@ Status ProcessGDBRemote::DoConnectRemote(llvm::StringRef remote_url) {
|
||||||
ModuleSP module_sp;
|
ModuleSP module_sp;
|
||||||
|
|
||||||
if (standalone_uuid.IsValid()) {
|
if (standalone_uuid.IsValid()) {
|
||||||
ModuleSpec module_spec;
|
const bool force_symbol_search = true;
|
||||||
module_spec.GetUUID() = standalone_uuid;
|
const bool notify = true;
|
||||||
|
DynamicLoader::LoadBinaryWithUUIDAndAddress(
|
||||||
|
this, standalone_uuid, standalone_value,
|
||||||
|
standalone_value_is_offset, force_symbol_search, notify);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Look up UUID in global module cache before attempting
|
// The remote stub may know about a list of binaries to
|
||||||
// a more expensive search.
|
// force load into the process -- a firmware type situation
|
||||||
Status error = ModuleList::GetSharedModule(module_spec, module_sp,
|
// where multiple binaries are present in virtual memory,
|
||||||
nullptr, nullptr, nullptr);
|
// and we are only given the addresses of the binaries.
|
||||||
|
// Not intended for use with userland debugging when we
|
||||||
|
// a DynamicLoader plugin that knows how to find the loaded
|
||||||
|
// binaries and will track updates as binaries are added.
|
||||||
|
|
||||||
if (!module_sp) {
|
std::vector<addr_t> bin_addrs = m_gdb_comm.GetProcessStandaloneBinaries();
|
||||||
// Force a an external lookup, if that tool is available.
|
if (bin_addrs.size()) {
|
||||||
if (!module_spec.GetSymbolFileSpec()) {
|
UUID uuid;
|
||||||
Status error;
|
const bool value_is_slide = false;
|
||||||
Symbols::DownloadObjectAndSymbolFile(module_spec, error, true);
|
for (addr_t addr : bin_addrs) {
|
||||||
}
|
const bool force_symbol_search = true;
|
||||||
|
const bool notify = true;
|
||||||
if (FileSystem::Instance().Exists(module_spec.GetFileSpec())) {
|
DynamicLoader::LoadBinaryWithUUIDAndAddress(
|
||||||
module_sp = std::make_shared<Module>(module_spec);
|
this, uuid, addr, value_is_slide, force_symbol_search, notify);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we couldn't find the binary anywhere else, as a last resort,
|
|
||||||
// read it out of memory.
|
|
||||||
if (!module_sp.get() && standalone_value != LLDB_INVALID_ADDRESS &&
|
|
||||||
!standalone_value_is_offset) {
|
|
||||||
char namebuf[80];
|
|
||||||
snprintf(namebuf, sizeof(namebuf), "mem-image-0x%" PRIx64,
|
|
||||||
standalone_value);
|
|
||||||
module_sp =
|
|
||||||
ReadModuleFromMemory(FileSpec(namebuf), standalone_value);
|
|
||||||
}
|
|
||||||
|
|
||||||
Log *log = GetLog(LLDBLog::DynamicLoader);
|
|
||||||
if (module_sp.get()) {
|
|
||||||
target.GetImages().AppendIfNeeded(module_sp, false);
|
|
||||||
|
|
||||||
bool changed = false;
|
|
||||||
if (module_sp->GetObjectFile()) {
|
|
||||||
if (standalone_value != LLDB_INVALID_ADDRESS) {
|
|
||||||
if (log)
|
|
||||||
log->Printf("Loading binary UUID %s at %s 0x%" PRIx64,
|
|
||||||
standalone_uuid.GetAsString().c_str(),
|
|
||||||
standalone_value_is_offset ? "offset" : "address",
|
|
||||||
standalone_value);
|
|
||||||
module_sp->SetLoadAddress(target, standalone_value,
|
|
||||||
standalone_value_is_offset, changed);
|
|
||||||
} else {
|
|
||||||
// No address/offset/slide, load the binary at file address,
|
|
||||||
// offset 0.
|
|
||||||
if (log)
|
|
||||||
log->Printf("Loading binary UUID %s at file address",
|
|
||||||
standalone_uuid.GetAsString().c_str());
|
|
||||||
const bool value_is_slide = true;
|
|
||||||
module_sp->SetLoadAddress(target, 0, value_is_slide, changed);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// In-memory image, load at its true address, offset 0.
|
|
||||||
if (log)
|
|
||||||
log->Printf("Loading binary UUID %s from memory",
|
|
||||||
standalone_uuid.GetAsString().c_str());
|
|
||||||
const bool value_is_slide = true;
|
|
||||||
module_sp->SetLoadAddress(target, 0, value_is_slide, changed);
|
|
||||||
}
|
|
||||||
|
|
||||||
ModuleList added_module;
|
|
||||||
added_module.Append(module_sp, false);
|
|
||||||
target.ModulesDidLoad(added_module);
|
|
||||||
} else {
|
|
||||||
if (log)
|
|
||||||
log->Printf("Unable to find binary with UUID %s and load it at "
|
|
||||||
"%s 0x%" PRIx64,
|
|
||||||
standalone_uuid.GetAsString().c_str(),
|
|
||||||
standalone_value_is_offset ? "offset" : "address",
|
|
||||||
standalone_value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -180,88 +180,6 @@ bool ProcessMachCore::GetDynamicLoaderAddress(lldb::addr_t addr) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We have a hint about a binary -- a UUID, possibly a load address.
|
|
||||||
// Try to load a file with that UUID into lldb, and if we have a load
|
|
||||||
// address, set it correctly. Else assume that the binary was loaded
|
|
||||||
// with no slide.
|
|
||||||
static bool load_standalone_binary(UUID uuid, addr_t value,
|
|
||||||
bool value_is_offset, Target &target) {
|
|
||||||
if (uuid.IsValid()) {
|
|
||||||
ModuleSpec module_spec;
|
|
||||||
module_spec.GetUUID() = uuid;
|
|
||||||
|
|
||||||
// Look up UUID in global module cache before attempting
|
|
||||||
// dsymForUUID-like action.
|
|
||||||
ModuleSP module_sp;
|
|
||||||
Status error = ModuleList::GetSharedModule(module_spec, module_sp, nullptr,
|
|
||||||
nullptr, nullptr);
|
|
||||||
|
|
||||||
if (!module_sp.get()) {
|
|
||||||
// Force a a dsymForUUID lookup, if that tool is available.
|
|
||||||
if (!module_spec.GetSymbolFileSpec()) {
|
|
||||||
Status error;
|
|
||||||
Symbols::DownloadObjectAndSymbolFile(module_spec, error, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (FileSystem::Instance().Exists(module_spec.GetFileSpec())) {
|
|
||||||
module_sp = std::make_shared<Module>(module_spec);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we couldn't find the binary anywhere else, as a last resort,
|
|
||||||
// read it out of memory in the corefile.
|
|
||||||
if (!module_sp.get() && value != LLDB_INVALID_ADDRESS && !value_is_offset) {
|
|
||||||
char namebuf[80];
|
|
||||||
snprintf(namebuf, sizeof(namebuf), "mem-image-0x%" PRIx64, value);
|
|
||||||
module_sp =
|
|
||||||
target.GetProcessSP()->ReadModuleFromMemory(FileSpec(namebuf), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (module_sp.get()) {
|
|
||||||
target.SetArchitecture(module_sp->GetObjectFile()->GetArchitecture());
|
|
||||||
target.GetImages().AppendIfNeeded(module_sp, false);
|
|
||||||
|
|
||||||
// TODO: Instead of using the load address as a value, if we create a
|
|
||||||
// memory module from that address, we could get the correct segment
|
|
||||||
// offset values from the in-memory load commands and set them correctly.
|
|
||||||
// In case the load address we were given is not correct for all segments,
|
|
||||||
// e.g. something in the shared cache. DynamicLoaderDarwinKernel does
|
|
||||||
// something similar for kexts. In the context of a corefile, this would
|
|
||||||
// be an inexpensive operation. Not all binaries in a corefile will have
|
|
||||||
// a Mach-O header/load commands in memory, so this will not work in all
|
|
||||||
// cases.
|
|
||||||
|
|
||||||
bool changed = false;
|
|
||||||
if (module_sp->GetObjectFile()) {
|
|
||||||
if (value != LLDB_INVALID_ADDRESS) {
|
|
||||||
module_sp->SetLoadAddress(target, value, value_is_offset, changed);
|
|
||||||
} else {
|
|
||||||
// No address/offset/slide, load the binary at file address,
|
|
||||||
// offset 0.
|
|
||||||
const bool value_is_slide = true;
|
|
||||||
module_sp->SetLoadAddress(target, 0, value_is_slide, changed);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// In-memory image, load at its true address, offset 0.
|
|
||||||
const bool value_is_slide = true;
|
|
||||||
module_sp->SetLoadAddress(target, 0, value_is_slide, changed);
|
|
||||||
}
|
|
||||||
|
|
||||||
ModuleList added_module;
|
|
||||||
added_module.Append(module_sp, false);
|
|
||||||
target.ModulesDidLoad(added_module);
|
|
||||||
|
|
||||||
// Flush info in the process (stack frames, etc).
|
|
||||||
ProcessSP process_sp(target.GetProcessSP());
|
|
||||||
if (process_sp)
|
|
||||||
process_sp->Flush();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process Control
|
// Process Control
|
||||||
Status ProcessMachCore::DoLoadCore() {
|
Status ProcessMachCore::DoLoadCore() {
|
||||||
Log *log(GetLog(LLDBLog::DynamicLoader | LLDBLog::Process));
|
Log *log(GetLog(LLDBLog::DynamicLoader | LLDBLog::Process));
|
||||||
|
@ -359,28 +277,21 @@ Status ProcessMachCore::DoLoadCore() {
|
||||||
objfile_binary_uuid.GetAsString().c_str(), objfile_binary_value,
|
objfile_binary_uuid.GetAsString().c_str(), objfile_binary_value,
|
||||||
objfile_binary_value_is_offset, type);
|
objfile_binary_value_is_offset, type);
|
||||||
}
|
}
|
||||||
if (objfile_binary_value != LLDB_INVALID_ADDRESS &&
|
const bool force_symbol_search = true;
|
||||||
!objfile_binary_value_is_offset) {
|
const bool notify = true;
|
||||||
if (type == ObjectFile::eBinaryTypeUser) {
|
if (DynamicLoader::LoadBinaryWithUUIDAndAddress(
|
||||||
load_standalone_binary(objfile_binary_uuid, objfile_binary_value,
|
this, objfile_binary_uuid, objfile_binary_value,
|
||||||
objfile_binary_value_is_offset, GetTarget());
|
objfile_binary_value_is_offset, force_symbol_search, notify)) {
|
||||||
m_dyld_addr = objfile_binary_value;
|
found_main_binary_definitively = true;
|
||||||
m_dyld_plugin_name = DynamicLoaderMacOSXDYLD::GetPluginNameStatic();
|
m_dyld_plugin_name = DynamicLoaderStatic::GetPluginNameStatic();
|
||||||
found_main_binary_definitively = true;
|
|
||||||
}
|
|
||||||
if (type == ObjectFile::eBinaryTypeKernel) {
|
|
||||||
m_mach_kernel_addr = objfile_binary_value;
|
|
||||||
m_dyld_plugin_name = DynamicLoaderDarwinKernel::GetPluginNameStatic();
|
|
||||||
found_main_binary_definitively = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (!found_main_binary_definitively) {
|
if (type == ObjectFile::eBinaryTypeUser) {
|
||||||
// ObjectFile::eBinaryTypeStandalone, undeclared types
|
m_dyld_addr = objfile_binary_value;
|
||||||
if (load_standalone_binary(objfile_binary_uuid, objfile_binary_value,
|
m_dyld_plugin_name = DynamicLoaderMacOSXDYLD::GetPluginNameStatic();
|
||||||
objfile_binary_value_is_offset, GetTarget())) {
|
}
|
||||||
found_main_binary_definitively = true;
|
if (type == ObjectFile::eBinaryTypeKernel) {
|
||||||
m_dyld_plugin_name = DynamicLoaderStatic::GetPluginNameStatic();
|
m_mach_kernel_addr = objfile_binary_value;
|
||||||
}
|
m_dyld_plugin_name = DynamicLoaderDarwinKernel::GetPluginNameStatic();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -426,8 +337,11 @@ Status ProcessMachCore::DoLoadCore() {
|
||||||
// We have no address specified, only a UUID. Load it at the file
|
// We have no address specified, only a UUID. Load it at the file
|
||||||
// address.
|
// address.
|
||||||
const bool value_is_offset = false;
|
const bool value_is_offset = false;
|
||||||
if (load_standalone_binary(ident_uuid, ident_binary_addr, value_is_offset,
|
const bool force_symbol_search = true;
|
||||||
GetTarget())) {
|
const bool notify = true;
|
||||||
|
if (DynamicLoader::LoadBinaryWithUUIDAndAddress(
|
||||||
|
this, ident_uuid, ident_binary_addr, value_is_offset,
|
||||||
|
force_symbol_search, notify)) {
|
||||||
found_main_binary_definitively = true;
|
found_main_binary_definitively = true;
|
||||||
m_dyld_plugin_name = DynamicLoaderStatic::GetPluginNameStatic();
|
m_dyld_plugin_name = DynamicLoaderStatic::GetPluginNameStatic();
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,4 +14,12 @@
|
||||||
// uuid_t is guaranteed to always be a 16-byte array
|
// uuid_t is guaranteed to always be a 16-byte array
|
||||||
typedef unsigned char uuid_t[16];
|
typedef unsigned char uuid_t[16];
|
||||||
|
|
||||||
|
// Return 1 if uuid is null, that is, all zeroes.
|
||||||
|
int uuid_is_null(uuid_t uuid) {
|
||||||
|
for (int i = 0; i < 16; i++)
|
||||||
|
if (uuid[i])
|
||||||
|
return 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
#endif // utility_UUID_COMPATIBILITY_H
|
#endif // utility_UUID_COMPATIBILITY_H
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
MAKE_DSYM := NO
|
||||||
|
C_SOURCES := main.c
|
||||||
|
LD_EXTRAS := -L. -lone -ltwo
|
||||||
|
|
||||||
|
.PHONY: libone.dylib libtwo.dylib
|
||||||
|
all: libone.dylib libtwo.dylib a.out create-empty-corefile
|
||||||
|
|
||||||
|
create-empty-corefile:
|
||||||
|
"$(MAKE)" -f "$(MAKEFILE_RULES)" EXE=create-multibin-corefile \
|
||||||
|
CXX_SOURCES=create-multibin-corefile.cpp
|
||||||
|
|
||||||
|
libone.dylib: one.c
|
||||||
|
$(MAKE) -f $(MAKEFILE_RULES) \
|
||||||
|
DYLIB_ONLY=YES DYLIB_NAME=one DYLIB_C_SOURCES=one.c
|
||||||
|
|
||||||
|
libtwo.dylib: two.c
|
||||||
|
$(MAKE) -f $(MAKEFILE_RULES) \
|
||||||
|
DYLIB_ONLY=YES DYLIB_NAME=two DYLIB_C_SOURCES=two.c
|
||||||
|
|
||||||
|
include Makefile.rules
|
|
@ -0,0 +1,187 @@
|
||||||
|
"""Test corefiles with "main bin spec"/"load binary" with only addrs work."""
|
||||||
|
|
||||||
|
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
import lldb
|
||||||
|
from lldbsuite.test.decorators import *
|
||||||
|
from lldbsuite.test.lldbtest import *
|
||||||
|
from lldbsuite.test import lldbutil
|
||||||
|
|
||||||
|
|
||||||
|
class TestMultipleBinaryCorefile(TestBase):
|
||||||
|
|
||||||
|
def initial_setup(self):
|
||||||
|
self.build()
|
||||||
|
self.aout_exe_basename = "a.out"
|
||||||
|
self.libone_exe_basename = "libone.dylib"
|
||||||
|
self.libtwo_exe_basename = "libtwo.dylib"
|
||||||
|
self.aout_exe = self.getBuildArtifact(self.aout_exe_basename)
|
||||||
|
self.aout_slide = 0x5000
|
||||||
|
self.libone_exe = self.getBuildArtifact(self.libone_exe_basename)
|
||||||
|
self.libone_slide = 0x100840000
|
||||||
|
self.libtwo_exe = self.getBuildArtifact(self.libtwo_exe_basename)
|
||||||
|
self.libtwo_slide = 0
|
||||||
|
self.corefile = self.getBuildArtifact("multiple-binaries.core")
|
||||||
|
self.create_corefile = self.getBuildArtifact("create-multibin-corefile")
|
||||||
|
cmd="%s %s %s@%x %s@%x %s@%x" % (self.create_corefile, self.corefile,
|
||||||
|
self.aout_exe, self.aout_slide,
|
||||||
|
self.libone_exe, self.libone_slide,
|
||||||
|
self.libtwo_exe, self.libtwo_slide)
|
||||||
|
call(cmd, shell=True)
|
||||||
|
|
||||||
|
|
||||||
|
def load_corefile_and_test(self):
|
||||||
|
target = self.dbg.CreateTarget('')
|
||||||
|
err = lldb.SBError()
|
||||||
|
if self.TraceOn():
|
||||||
|
self.runCmd("script print('loading corefile %s')" % self.corefile)
|
||||||
|
process = target.LoadCore(self.corefile)
|
||||||
|
self.assertEqual(process.IsValid(), True)
|
||||||
|
if self.TraceOn():
|
||||||
|
self.runCmd("script print('image list after loading corefile:')")
|
||||||
|
self.runCmd("image list")
|
||||||
|
|
||||||
|
self.assertEqual(target.GetNumModules(), 3)
|
||||||
|
fspec = target.GetModuleAtIndex(0).GetFileSpec()
|
||||||
|
self.assertEqual(fspec.GetFilename(), self.aout_exe_basename)
|
||||||
|
|
||||||
|
# libone.dylib was never loaded into lldb, see that we added a memory module.
|
||||||
|
fspec = target.GetModuleAtIndex(1).GetFileSpec()
|
||||||
|
self.assertIn('memory-image', fspec.GetFilename())
|
||||||
|
|
||||||
|
dwarfdump_uuid_regex = re.compile(
|
||||||
|
'UUID: ([-0-9a-fA-F]+) \(([^\(]+)\) .*')
|
||||||
|
dwarfdump_cmd_output = subprocess.check_output(
|
||||||
|
('/usr/bin/dwarfdump --uuid "%s"' % self.libone_exe), shell=True).decode("utf-8")
|
||||||
|
libone_uuid = None
|
||||||
|
for line in dwarfdump_cmd_output.splitlines():
|
||||||
|
match = dwarfdump_uuid_regex.search(line)
|
||||||
|
if match:
|
||||||
|
libone_uuid = match.group(1)
|
||||||
|
|
||||||
|
memory_image_uuid = target.GetModuleAtIndex(1).GetUUIDString()
|
||||||
|
self.assertEqual(libone_uuid, memory_image_uuid)
|
||||||
|
|
||||||
|
fspec = target.GetModuleAtIndex(2).GetFileSpec()
|
||||||
|
self.assertEqual(fspec.GetFilename(), self.libtwo_exe_basename)
|
||||||
|
|
||||||
|
# Executables "always" have this base address
|
||||||
|
aout_load = target.GetModuleAtIndex(0).GetObjectFileHeaderAddress().GetLoadAddress(target)
|
||||||
|
self.assertEqual(aout_load, 0x100000000 + self.aout_slide)
|
||||||
|
|
||||||
|
# Value from Makefile
|
||||||
|
libone_load = target.GetModuleAtIndex(1).GetObjectFileHeaderAddress().GetLoadAddress(target)
|
||||||
|
self.assertEqual(libone_load, self.libone_slide)
|
||||||
|
|
||||||
|
# Value from Makefile
|
||||||
|
libtwo_load = target.GetModuleAtIndex(2).GetObjectFileHeaderAddress().GetLoadAddress(target)
|
||||||
|
self.assertEqual(libtwo_load, self.libtwo_slide)
|
||||||
|
|
||||||
|
self.dbg.DeleteTarget(target)
|
||||||
|
self.dbg.Clear()
|
||||||
|
|
||||||
|
NO_DEBUG_INFO_TESTCASE = True
|
||||||
|
|
||||||
|
@skipIf(archs=no_match(['x86_64', 'arm64', 'arm64e', 'aarch64']))
|
||||||
|
@skipIfRemote
|
||||||
|
@skipUnlessDarwin
|
||||||
|
def test_corefile_binaries_dsymforuuid(self):
|
||||||
|
self.initial_setup()
|
||||||
|
|
||||||
|
if self.TraceOn():
|
||||||
|
self.runCmd("log enable lldb dyld host")
|
||||||
|
self.addTearDownHook(lambda: self.runCmd("log disable lldb dyld host"))
|
||||||
|
|
||||||
|
## We can hook in our dsym-for-uuid shell script to lldb with this env
|
||||||
|
## var instead of requiring a defaults write.
|
||||||
|
dsym_for_uuid = self.getBuildArtifact("dsym-for-uuid.sh")
|
||||||
|
os.environ['LLDB_APPLE_DSYMFORUUID_EXECUTABLE'] = dsym_for_uuid
|
||||||
|
if self.TraceOn():
|
||||||
|
print("Setting env var LLDB_APPLE_DSYMFORUUID_EXECUTABLE=" + dsym_for_uuid)
|
||||||
|
self.addTearDownHook(lambda: os.environ.pop('LLDB_APPLE_DSYMFORUUID_EXECUTABLE', None))
|
||||||
|
|
||||||
|
self.runCmd("settings set target.load-script-from-symbol-file true")
|
||||||
|
self.addTearDownHook(lambda: self.runCmd("settings set target.load-script-from-symbol-file false"))
|
||||||
|
|
||||||
|
dwarfdump_uuid_regex = re.compile(
|
||||||
|
'UUID: ([-0-9a-fA-F]+) \(([^\(]+)\) .*')
|
||||||
|
dwarfdump_cmd_output = subprocess.check_output(
|
||||||
|
('/usr/bin/dwarfdump --uuid "%s"' % self.libtwo_exe), shell=True).decode("utf-8")
|
||||||
|
libtwo_uuid = None
|
||||||
|
for line in dwarfdump_cmd_output.splitlines():
|
||||||
|
match = dwarfdump_uuid_regex.search(line)
|
||||||
|
if match:
|
||||||
|
libtwo_uuid = match.group(1)
|
||||||
|
self.assertNotEqual(libtwo_uuid, None, "Could not get uuid of built libtwo.dylib")
|
||||||
|
|
||||||
|
### Create our dsym-for-uuid shell script which returns aout_exe
|
||||||
|
shell_cmds = [
|
||||||
|
'#! /bin/sh',
|
||||||
|
'# the last argument is the uuid',
|
||||||
|
'while [ $# -gt 1 ]',
|
||||||
|
'do',
|
||||||
|
' shift',
|
||||||
|
'done',
|
||||||
|
'ret=0',
|
||||||
|
'echo "<?xml version=\\"1.0\\" encoding=\\"UTF-8\\"?>"',
|
||||||
|
'echo "<!DOCTYPE plist PUBLIC \\"-//Apple//DTD PLIST 1.0//EN\\" \\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\\">"',
|
||||||
|
'echo "<plist version=\\"1.0\\">"',
|
||||||
|
'',
|
||||||
|
'if [ "$1" != "%s" ]' % (libtwo_uuid),
|
||||||
|
'then',
|
||||||
|
' echo "<key>DBGError</key><string>not found</string>"',
|
||||||
|
' echo "</plist>"',
|
||||||
|
' exit 1',
|
||||||
|
'fi',
|
||||||
|
' uuid=%s' % libtwo_uuid,
|
||||||
|
' bin=%s' % self.libtwo_exe,
|
||||||
|
' dsym=%s.dSYM/Contents/Resources/DWARF/%s' % (self.libtwo_exe, os.path.basename(self.libtwo_exe)),
|
||||||
|
'echo "<dict><key>$uuid</key><dict>"',
|
||||||
|
'',
|
||||||
|
'echo "<key>DBGDSYMPath</key><string>$dsym</string>"',
|
||||||
|
'echo "<key>DBGSymbolRichExecutable</key><string>$bin</string>"',
|
||||||
|
'echo "</dict></dict></plist>"',
|
||||||
|
'exit $ret'
|
||||||
|
]
|
||||||
|
|
||||||
|
with open(dsym_for_uuid, "w") as writer:
|
||||||
|
for l in shell_cmds:
|
||||||
|
writer.write(l + '\n')
|
||||||
|
|
||||||
|
os.chmod(dsym_for_uuid, 0o755)
|
||||||
|
|
||||||
|
# Register TWO of our binaries, but require dsymForUUID to find the third.
|
||||||
|
target = self.dbg.CreateTarget(self.aout_exe, '', '', False, lldb.SBError())
|
||||||
|
self.dbg.DeleteTarget(target)
|
||||||
|
|
||||||
|
if self.TraceOn():
|
||||||
|
self.runCmd("script print('Global image list, before loading corefile:')")
|
||||||
|
self.runCmd("image list -g")
|
||||||
|
|
||||||
|
self.load_corefile_and_test()
|
||||||
|
|
||||||
|
@skipIf(archs=no_match(['x86_64', 'arm64', 'arm64e', 'aarch64']))
|
||||||
|
@skipIfRemote
|
||||||
|
@skipUnlessDarwin
|
||||||
|
def test_corefile_binaries_preloaded(self):
|
||||||
|
self.initial_setup()
|
||||||
|
|
||||||
|
if self.TraceOn():
|
||||||
|
self.runCmd("log enable lldb dyld host")
|
||||||
|
self.addTearDownHook(lambda: self.runCmd("log disable lldb dyld host"))
|
||||||
|
|
||||||
|
# Register all three binaries in lldb's global module
|
||||||
|
# cache, then throw the Targets away.
|
||||||
|
target = self.dbg.CreateTarget(self.aout_exe, '', '', False, lldb.SBError())
|
||||||
|
self.dbg.DeleteTarget(target)
|
||||||
|
target = self.dbg.CreateTarget(self.libtwo_exe, '', '', False, lldb.SBError())
|
||||||
|
self.dbg.DeleteTarget(target)
|
||||||
|
|
||||||
|
if self.TraceOn():
|
||||||
|
self.runCmd("script print('Global image list, before loading corefile:')")
|
||||||
|
self.runCmd("image list -g")
|
||||||
|
|
||||||
|
self.load_corefile_and_test()
|
|
@ -0,0 +1,484 @@
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <mach-o/loader.h>
|
||||||
|
#include <mach/thread_status.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <string>
|
||||||
|
#include <sys/errno.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/uio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <uuid/uuid.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
// Given a list of binaries, and optional slides to be applied,
|
||||||
|
// create a corefile whose memory is those binaries laid at at
|
||||||
|
// their slid addresses.
|
||||||
|
//
|
||||||
|
// Add a 'main bin spec' LC_NOTE for the first binary, and
|
||||||
|
// 'load binary' LC_NOTEs for any additional binaries, and
|
||||||
|
// these LC_NOTEs will ONLY have the vmaddr of the binary - no
|
||||||
|
// UUID, no slide, no filename.
|
||||||
|
//
|
||||||
|
// Test that lldb can use the load addresses, find the UUIDs,
|
||||||
|
// and load the binaries/dSYMs and put them at the correct load
|
||||||
|
// address.
|
||||||
|
|
||||||
|
struct main_bin_spec_payload {
|
||||||
|
uint32_t version;
|
||||||
|
uint32_t type;
|
||||||
|
uint64_t address;
|
||||||
|
uint64_t slide;
|
||||||
|
uuid_t uuid;
|
||||||
|
uint32_t log2_pagesize;
|
||||||
|
uint32_t platform;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct load_binary_payload {
|
||||||
|
uint32_t version;
|
||||||
|
uuid_t uuid;
|
||||||
|
uint64_t address;
|
||||||
|
uint64_t slide;
|
||||||
|
const char name[4];
|
||||||
|
};
|
||||||
|
|
||||||
|
union uint32_buf {
|
||||||
|
uint8_t bytebuf[4];
|
||||||
|
uint32_t val;
|
||||||
|
};
|
||||||
|
|
||||||
|
union uint64_buf {
|
||||||
|
uint8_t bytebuf[8];
|
||||||
|
uint64_t val;
|
||||||
|
};
|
||||||
|
|
||||||
|
void add_uint64(std::vector<uint8_t> &buf, uint64_t val) {
|
||||||
|
uint64_buf conv;
|
||||||
|
conv.val = val;
|
||||||
|
for (int i = 0; i < 8; i++)
|
||||||
|
buf.push_back(conv.bytebuf[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_uint32(std::vector<uint8_t> &buf, uint32_t val) {
|
||||||
|
uint32_buf conv;
|
||||||
|
conv.val = val;
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
buf.push_back(conv.bytebuf[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> lc_thread_load_command(cpu_type_t cputype) {
|
||||||
|
std::vector<uint8_t> data;
|
||||||
|
// Emit an LC_THREAD register context appropriate for the cputype
|
||||||
|
// of the binary we're embedded. The tests in this case do not
|
||||||
|
// use the register values, so 0's are fine, lldb needs to see at
|
||||||
|
// least one LC_THREAD in the corefile.
|
||||||
|
#if defined(__x86_64__)
|
||||||
|
if (cputype == CPU_TYPE_X86_64) {
|
||||||
|
add_uint32(data, LC_THREAD); // thread_command.cmd
|
||||||
|
add_uint32(data,
|
||||||
|
16 + (x86_THREAD_STATE64_COUNT * 4)); // thread_command.cmdsize
|
||||||
|
add_uint32(data, x86_THREAD_STATE64); // thread_command.flavor
|
||||||
|
add_uint32(data, x86_THREAD_STATE64_COUNT); // thread_command.count
|
||||||
|
for (int i = 0; i < x86_THREAD_STATE64_COUNT; i++) {
|
||||||
|
add_uint32(data, 0); // whatever, just some empty register values
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#if defined(__arm64__) || defined(__aarch64__)
|
||||||
|
if (cputype == CPU_TYPE_ARM64) {
|
||||||
|
add_uint32(data, LC_THREAD); // thread_command.cmd
|
||||||
|
add_uint32(data,
|
||||||
|
16 + (ARM_THREAD_STATE64_COUNT * 4)); // thread_command.cmdsize
|
||||||
|
add_uint32(data, ARM_THREAD_STATE64); // thread_command.flavor
|
||||||
|
add_uint32(data, ARM_THREAD_STATE64_COUNT); // thread_command.count
|
||||||
|
for (int i = 0; i < ARM_THREAD_STATE64_COUNT; i++) {
|
||||||
|
add_uint32(data, 0); // whatever, just some empty register values
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_lc_note_main_bin_spec_load_command(
|
||||||
|
std::vector<std::vector<uint8_t>> &loadcmds, std::vector<uint8_t> &payload,
|
||||||
|
int payload_file_offset, std::string uuidstr, uint64_t address,
|
||||||
|
uint64_t slide) {
|
||||||
|
std::vector<uint8_t> loadcmd_data;
|
||||||
|
|
||||||
|
add_uint32(loadcmd_data, LC_NOTE); // note_command.cmd
|
||||||
|
add_uint32(loadcmd_data, 40); // note_command.cmdsize
|
||||||
|
char lc_note_name[16];
|
||||||
|
memset(lc_note_name, 0, 16);
|
||||||
|
strcpy(lc_note_name, "main bin spec");
|
||||||
|
|
||||||
|
// lc_note.data_owner
|
||||||
|
for (int i = 0; i < 16; i++)
|
||||||
|
loadcmd_data.push_back(lc_note_name[i]);
|
||||||
|
|
||||||
|
// we start writing the payload at payload_file_offset to leave
|
||||||
|
// room at the start for the header & the load commands.
|
||||||
|
uint64_t current_payload_offset = payload.size() + payload_file_offset;
|
||||||
|
|
||||||
|
add_uint64(loadcmd_data, current_payload_offset); // note_command.offset
|
||||||
|
add_uint64(loadcmd_data,
|
||||||
|
sizeof(struct main_bin_spec_payload)); // note_command.size
|
||||||
|
|
||||||
|
loadcmds.push_back(loadcmd_data);
|
||||||
|
|
||||||
|
// Now write the "main bin spec" payload.
|
||||||
|
add_uint32(payload, 2); // version
|
||||||
|
add_uint32(payload, 3); // type == 3 [ firmware, standalone, etc ]
|
||||||
|
add_uint64(payload, address); // load address
|
||||||
|
add_uint64(payload, slide); // slide
|
||||||
|
uuid_t uuid;
|
||||||
|
uuid_parse(uuidstr.c_str(), uuid);
|
||||||
|
for (int i = 0; i < sizeof(uuid_t); i++)
|
||||||
|
payload.push_back(uuid[i]);
|
||||||
|
add_uint32(payload, 0); // log2_pagesize unspecified
|
||||||
|
add_uint32(payload, 0); // platform unspecified
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_lc_note_load_binary_load_command(
|
||||||
|
std::vector<std::vector<uint8_t>> &loadcmds, std::vector<uint8_t> &payload,
|
||||||
|
int payload_file_offset, std::string uuidstr, uint64_t address,
|
||||||
|
uint64_t slide) {
|
||||||
|
std::vector<uint8_t> loadcmd_data;
|
||||||
|
|
||||||
|
add_uint32(loadcmd_data, LC_NOTE); // note_command.cmd
|
||||||
|
add_uint32(loadcmd_data, 40); // note_command.cmdsize
|
||||||
|
char lc_note_name[16];
|
||||||
|
memset(lc_note_name, 0, 16);
|
||||||
|
strcpy(lc_note_name, "load binary");
|
||||||
|
|
||||||
|
// lc_note.data_owner
|
||||||
|
for (int i = 0; i < 16; i++)
|
||||||
|
loadcmd_data.push_back(lc_note_name[i]);
|
||||||
|
|
||||||
|
// we start writing the payload at payload_file_offset to leave
|
||||||
|
// room at the start for the header & the load commands.
|
||||||
|
uint64_t current_payload_offset = payload.size() + payload_file_offset;
|
||||||
|
|
||||||
|
add_uint64(loadcmd_data, current_payload_offset); // note_command.offset
|
||||||
|
add_uint64(loadcmd_data,
|
||||||
|
sizeof(struct load_binary_payload)); // note_command.size
|
||||||
|
|
||||||
|
loadcmds.push_back(loadcmd_data);
|
||||||
|
|
||||||
|
// Now write the "load binary" payload.
|
||||||
|
add_uint32(payload, 1); // version
|
||||||
|
uuid_t uuid;
|
||||||
|
uuid_parse(uuidstr.c_str(), uuid);
|
||||||
|
for (int i = 0; i < sizeof(uuid_t); i++)
|
||||||
|
payload.push_back(uuid[i]);
|
||||||
|
add_uint64(payload, address); // load address
|
||||||
|
add_uint64(payload, slide); // slide
|
||||||
|
add_uint32(payload, 0); // name
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_lc_segment(std::vector<std::vector<uint8_t>> &loadcmds,
|
||||||
|
std::vector<uint8_t> &payload, int payload_file_offset,
|
||||||
|
uint64_t vmaddr, uint64_t size) {
|
||||||
|
std::vector<uint8_t> loadcmd_data;
|
||||||
|
struct segment_command_64 seg;
|
||||||
|
seg.cmd = LC_SEGMENT_64;
|
||||||
|
seg.cmdsize = sizeof(struct segment_command_64); // no sections
|
||||||
|
memset(seg.segname, 0, 16);
|
||||||
|
seg.vmaddr = vmaddr;
|
||||||
|
seg.vmsize = size;
|
||||||
|
seg.fileoff = payload.size() + payload_file_offset;
|
||||||
|
seg.filesize = size;
|
||||||
|
seg.maxprot = 1;
|
||||||
|
seg.initprot = 1;
|
||||||
|
seg.nsects = 0;
|
||||||
|
seg.flags = 0;
|
||||||
|
|
||||||
|
uint8_t *p = (uint8_t *)&seg;
|
||||||
|
for (int i = 0; i < sizeof(struct segment_command_64); i++) {
|
||||||
|
loadcmd_data.push_back(*(p + i));
|
||||||
|
}
|
||||||
|
loadcmds.push_back(loadcmd_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string scan_binary(const char *fn, uint64_t &vmaddr, cpu_type_t &cputype,
|
||||||
|
cpu_subtype_t &cpusubtype) {
|
||||||
|
FILE *f = fopen(fn, "r");
|
||||||
|
if (f == nullptr) {
|
||||||
|
fprintf(stderr, "Unable to open binary '%s' to get uuid\n", fn);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
uint32_t num_of_load_cmds = 0;
|
||||||
|
uint32_t size_of_load_cmds = 0;
|
||||||
|
std::string uuid;
|
||||||
|
off_t file_offset = 0;
|
||||||
|
vmaddr = UINT64_MAX;
|
||||||
|
|
||||||
|
uint8_t magic[4];
|
||||||
|
if (::fread(magic, 1, 4, f) != 4) {
|
||||||
|
fprintf(stderr, "Failed to read magic number from input file %s\n", fn);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
uint8_t magic_32_be[] = {0xfe, 0xed, 0xfa, 0xce};
|
||||||
|
uint8_t magic_32_le[] = {0xce, 0xfa, 0xed, 0xfe};
|
||||||
|
uint8_t magic_64_be[] = {0xfe, 0xed, 0xfa, 0xcf};
|
||||||
|
uint8_t magic_64_le[] = {0xcf, 0xfa, 0xed, 0xfe};
|
||||||
|
|
||||||
|
if (memcmp(magic, magic_32_be, 4) == 0 ||
|
||||||
|
memcmp(magic, magic_64_be, 4) == 0) {
|
||||||
|
fprintf(stderr, "big endian corefiles not supported\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
::fseeko(f, 0, SEEK_SET);
|
||||||
|
if (memcmp(magic, magic_32_le, 4) == 0) {
|
||||||
|
struct mach_header mh;
|
||||||
|
if (::fread(&mh, 1, sizeof(mh), f) != sizeof(mh)) {
|
||||||
|
fprintf(stderr, "error reading mach header from input file\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if (mh.cputype != CPU_TYPE_X86_64 && mh.cputype != CPU_TYPE_ARM64) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"This tool creates an x86_64/arm64 corefile but "
|
||||||
|
"the supplied binary '%s' is cputype 0x%x\n",
|
||||||
|
fn, (uint32_t)mh.cputype);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
num_of_load_cmds = mh.ncmds;
|
||||||
|
size_of_load_cmds = mh.sizeofcmds;
|
||||||
|
file_offset += sizeof(struct mach_header);
|
||||||
|
cputype = mh.cputype;
|
||||||
|
cpusubtype = mh.cpusubtype;
|
||||||
|
} else {
|
||||||
|
struct mach_header_64 mh;
|
||||||
|
if (::fread(&mh, 1, sizeof(mh), f) != sizeof(mh)) {
|
||||||
|
fprintf(stderr, "error reading mach header from input file\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if (mh.cputype != CPU_TYPE_X86_64 && mh.cputype != CPU_TYPE_ARM64) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"This tool creates an x86_64/arm64 corefile but "
|
||||||
|
"the supplied binary '%s' is cputype 0x%x\n",
|
||||||
|
fn, (uint32_t)mh.cputype);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
num_of_load_cmds = mh.ncmds;
|
||||||
|
size_of_load_cmds = mh.sizeofcmds;
|
||||||
|
file_offset += sizeof(struct mach_header_64);
|
||||||
|
cputype = mh.cputype;
|
||||||
|
cpusubtype = mh.cpusubtype;
|
||||||
|
}
|
||||||
|
|
||||||
|
off_t load_cmds_offset = file_offset;
|
||||||
|
|
||||||
|
for (int i = 0; i < num_of_load_cmds &&
|
||||||
|
(file_offset - load_cmds_offset) < size_of_load_cmds;
|
||||||
|
i++) {
|
||||||
|
::fseeko(f, file_offset, SEEK_SET);
|
||||||
|
uint32_t cmd;
|
||||||
|
uint32_t cmdsize;
|
||||||
|
::fread(&cmd, sizeof(uint32_t), 1, f);
|
||||||
|
::fread(&cmdsize, sizeof(uint32_t), 1, f);
|
||||||
|
if (vmaddr == UINT64_MAX && cmd == LC_SEGMENT_64) {
|
||||||
|
struct segment_command_64 segcmd;
|
||||||
|
::fseeko(f, file_offset, SEEK_SET);
|
||||||
|
if (::fread(&segcmd, 1, sizeof(segcmd), f) != sizeof(segcmd)) {
|
||||||
|
fprintf(stderr, "Unable to read LC_SEGMENT_64 load command.\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if (strcmp("__TEXT", segcmd.segname) == 0)
|
||||||
|
vmaddr = segcmd.vmaddr;
|
||||||
|
}
|
||||||
|
if (cmd == LC_UUID) {
|
||||||
|
struct uuid_command uuidcmd;
|
||||||
|
::fseeko(f, file_offset, SEEK_SET);
|
||||||
|
if (::fread(&uuidcmd, 1, sizeof(uuidcmd), f) != sizeof(uuidcmd)) {
|
||||||
|
fprintf(stderr, "Unable to read LC_UUID load command.\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
uuid_string_t uuidstr;
|
||||||
|
uuid_unparse(uuidcmd.uuid, uuidstr);
|
||||||
|
uuid = uuidstr;
|
||||||
|
}
|
||||||
|
file_offset += cmdsize;
|
||||||
|
}
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
void slide_macho_binary(std::vector<uint8_t> &image, uint64_t slide) {
|
||||||
|
uint8_t *p = image.data();
|
||||||
|
struct mach_header_64 *mh = (struct mach_header_64 *)p;
|
||||||
|
p += sizeof(struct mach_header_64);
|
||||||
|
for (int lc_idx = 0; lc_idx < mh->ncmds; lc_idx++) {
|
||||||
|
struct load_command *lc = (struct load_command *)p;
|
||||||
|
if (lc->cmd == LC_SEGMENT_64) {
|
||||||
|
struct segment_command_64 *seg = (struct segment_command_64 *)p;
|
||||||
|
if (seg->maxprot != 0 && seg->nsects > 0) {
|
||||||
|
seg->vmaddr += slide;
|
||||||
|
uint8_t *j = p + sizeof(segment_command_64);
|
||||||
|
for (int sect_idx = 0; sect_idx < seg->nsects; sect_idx++) {
|
||||||
|
struct section_64 *sect = (struct section_64 *)j;
|
||||||
|
sect->addr += slide;
|
||||||
|
j += sizeof(struct section_64);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p += lc->cmdsize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
if (argc < 3) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"usage: output-corefile binary1[@optional-slide] "
|
||||||
|
"[binary2[@optional-slide] [binary3[@optional-slide] ...]]\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// An array of load commands (in the form of byte arrays)
|
||||||
|
std::vector<std::vector<uint8_t>> load_commands;
|
||||||
|
|
||||||
|
// An array of corefile contents (page data, lc_note data, etc)
|
||||||
|
std::vector<uint8_t> payload;
|
||||||
|
|
||||||
|
std::vector<std::string> input_filenames;
|
||||||
|
std::vector<uint64_t> input_slides;
|
||||||
|
std::vector<uint64_t> input_filesizes;
|
||||||
|
std::vector<uint64_t> input_filevmaddrs;
|
||||||
|
uint64_t main_binary_cputype = CPU_TYPE_ARM64;
|
||||||
|
uint64_t vmaddr = UINT64_MAX;
|
||||||
|
cpu_type_t cputype;
|
||||||
|
cpu_subtype_t cpusubtype;
|
||||||
|
for (int i = 2; i < argc; i++) {
|
||||||
|
std::string filename;
|
||||||
|
std::string filename_and_opt_hex(argv[i]);
|
||||||
|
uint64_t slide = 0;
|
||||||
|
auto at_pos = filename_and_opt_hex.find_last_of('@');
|
||||||
|
if (at_pos == std::string::npos) {
|
||||||
|
filename = filename_and_opt_hex;
|
||||||
|
} else {
|
||||||
|
filename = filename_and_opt_hex.substr(0, at_pos);
|
||||||
|
std::string hexstr = filename_and_opt_hex.substr(at_pos + 1);
|
||||||
|
errno = 0;
|
||||||
|
slide = (uint64_t)strtoull(hexstr.c_str(), nullptr, 16);
|
||||||
|
if (errno != 0) {
|
||||||
|
fprintf(stderr, "Unable to parse hex slide value in %s\n", argv[i]);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
struct stat stbuf;
|
||||||
|
if (stat(filename.c_str(), &stbuf) == -1) {
|
||||||
|
fprintf(stderr, "Unable to stat '%s', exiting.\n", filename.c_str());
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
input_filenames.push_back(filename);
|
||||||
|
input_slides.push_back(slide);
|
||||||
|
input_filesizes.push_back(stbuf.st_size);
|
||||||
|
scan_binary(filename.c_str(), vmaddr, cputype, cpusubtype);
|
||||||
|
input_filevmaddrs.push_back(vmaddr + slide);
|
||||||
|
if (i == 2) {
|
||||||
|
main_binary_cputype = cputype;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *output_corefile_name = argv[1];
|
||||||
|
std::string empty_uuidstr = "00000000-0000-0000-0000-000000000000";
|
||||||
|
|
||||||
|
// First add all the load commands / payload so we can figure out how large
|
||||||
|
// the load commands will actually be.
|
||||||
|
load_commands.push_back(lc_thread_load_command(cputype));
|
||||||
|
|
||||||
|
add_lc_note_main_bin_spec_load_command(load_commands, payload, 0,
|
||||||
|
empty_uuidstr, 0, UINT64_MAX);
|
||||||
|
for (int i = 1; i < input_filenames.size(); i++) {
|
||||||
|
add_lc_note_load_binary_load_command(load_commands, payload, 0,
|
||||||
|
empty_uuidstr, 0, UINT64_MAX);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < input_filenames.size(); i++) {
|
||||||
|
add_lc_segment(load_commands, payload, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int size_of_load_commands = 0;
|
||||||
|
for (const auto &lc : load_commands)
|
||||||
|
size_of_load_commands += lc.size();
|
||||||
|
|
||||||
|
int size_of_header_and_load_cmds =
|
||||||
|
sizeof(struct mach_header_64) + size_of_load_commands;
|
||||||
|
|
||||||
|
// Erase the load commands / payload now that we know how much space is
|
||||||
|
// needed, redo it.
|
||||||
|
load_commands.clear();
|
||||||
|
payload.clear();
|
||||||
|
|
||||||
|
// Push the LC_THREAD load command.
|
||||||
|
load_commands.push_back(lc_thread_load_command(main_binary_cputype));
|
||||||
|
|
||||||
|
const off_t payload_offset = size_of_header_and_load_cmds;
|
||||||
|
|
||||||
|
add_lc_note_main_bin_spec_load_command(load_commands, payload, payload_offset,
|
||||||
|
empty_uuidstr, input_filevmaddrs[0],
|
||||||
|
UINT64_MAX);
|
||||||
|
|
||||||
|
for (int i = 1; i < input_filenames.size(); i++) {
|
||||||
|
add_lc_note_load_binary_load_command(load_commands, payload, payload_offset,
|
||||||
|
empty_uuidstr, input_filevmaddrs[i],
|
||||||
|
UINT64_MAX);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < input_filenames.size(); i++) {
|
||||||
|
add_lc_segment(load_commands, payload, payload_offset, input_filevmaddrs[i],
|
||||||
|
input_filesizes[i]);
|
||||||
|
|
||||||
|
// Copy the contents of the binary into payload.
|
||||||
|
int fd = open(input_filenames[i].c_str(), O_RDONLY);
|
||||||
|
if (fd == -1) {
|
||||||
|
fprintf(stderr, "Unable to open %s for reading\n",
|
||||||
|
input_filenames[i].c_str());
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
std::vector<uint8_t> binary_contents;
|
||||||
|
for (int j = 0; j < input_filesizes[i]; j++) {
|
||||||
|
uint8_t byte;
|
||||||
|
read(fd, &byte, 1);
|
||||||
|
binary_contents.push_back(byte);
|
||||||
|
}
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
size_t cur_payload_size = payload.size();
|
||||||
|
payload.resize(cur_payload_size + binary_contents.size());
|
||||||
|
slide_macho_binary(binary_contents, input_slides[i]);
|
||||||
|
memcpy(payload.data() + cur_payload_size, binary_contents.data(),
|
||||||
|
binary_contents.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
struct mach_header_64 mh;
|
||||||
|
mh.magic = MH_MAGIC_64;
|
||||||
|
mh.cputype = cputype;
|
||||||
|
|
||||||
|
mh.cpusubtype = cpusubtype;
|
||||||
|
mh.filetype = MH_CORE;
|
||||||
|
mh.ncmds = load_commands.size();
|
||||||
|
mh.sizeofcmds = size_of_load_commands;
|
||||||
|
mh.flags = 0;
|
||||||
|
mh.reserved = 0;
|
||||||
|
|
||||||
|
FILE *f = fopen(output_corefile_name, "w");
|
||||||
|
|
||||||
|
if (f == nullptr) {
|
||||||
|
fprintf(stderr, "Unable to open file %s for writing\n",
|
||||||
|
output_corefile_name);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fwrite(&mh, sizeof(mh), 1, f);
|
||||||
|
|
||||||
|
for (const auto &lc : load_commands)
|
||||||
|
fwrite(lc.data(), lc.size(), 1, f);
|
||||||
|
|
||||||
|
fwrite(payload.data(), payload.size(), 1, f);
|
||||||
|
|
||||||
|
fclose(f);
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
int one();
|
||||||
|
int two();
|
||||||
|
int main() {
|
||||||
|
puts("this is the standalone binary test program");
|
||||||
|
return one() + two();
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
int one() { return 5; }
|
|
@ -0,0 +1 @@
|
||||||
|
int two() { return 10; }
|
Loading…
Reference in New Issue