Author: Jason Molenda Date: 2022-08-02T13:49:30-07:00 New Revision: d8879fba8825b9799166ba0ea552d4027bfb8ad1
URL: https://github.com/llvm/llvm-project/commit/d8879fba8825b9799166ba0ea552d4027bfb8ad1 DIFF: https://github.com/llvm/llvm-project/commit/d8879fba8825b9799166ba0ea552d4027bfb8ad1.diff LOG: 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. Differential Revision: https://reviews.llvm.org/D130813 rdar://94249937 rdar://94249384 Added: lldb/test/API/macosx/lc-note/multiple-binary-corefile/Makefile lldb/test/API/macosx/lc-note/multiple-binary-corefile/TestMultipleBinaryCorefile.py lldb/test/API/macosx/lc-note/multiple-binary-corefile/create-multibin-corefile.cpp lldb/test/API/macosx/lc-note/multiple-binary-corefile/main.c lldb/test/API/macosx/lc-note/multiple-binary-corefile/one.c lldb/test/API/macosx/lc-note/multiple-binary-corefile/two.c Modified: lldb/docs/lldb-gdb-remote.txt lldb/include/lldb/Target/DynamicLoader.h lldb/source/Core/DynamicLoader.cpp lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp Removed: ################################################################################ diff --git a/lldb/docs/lldb-gdb-remote.txt b/lldb/docs/lldb-gdb-remote.txt index 820f3bd7bbc20..497980ed1229b 100644 --- a/lldb/docs/lldb-gdb-remote.txt +++ b/lldb/docs/lldb-gdb-remote.txt @@ -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-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" // diff --git a/lldb/include/lldb/Target/DynamicLoader.h b/lldb/include/lldb/Target/DynamicLoader.h index 397b4aa303448..7e7a4218810b5 100644 --- a/lldb/include/lldb/Target/DynamicLoader.h +++ b/lldb/include/lldb/Target/DynamicLoader.h @@ -210,6 +210,52 @@ class DynamicLoader : public PluginInterface { lldb::addr_t base_addr, 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. /// /// On some systems (e.g. Darwin based systems), a set of libraries that are diff --git a/lldb/source/Core/DynamicLoader.cpp b/lldb/source/Core/DynamicLoader.cpp index 96e0d4ec6555d..1fe60e000c6d5 100644 --- a/lldb/source/Core/DynamicLoader.cpp +++ b/lldb/source/Core/DynamicLoader.cpp @@ -13,11 +13,15 @@ #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/Section.h" +#include "lldb/Symbol/LocateSymbolFile.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Target/MemoryRegionInfo.h" +#include "lldb/Target/Platform.h" #include "lldb/Target/Process.h" #include "lldb/Target/Target.h" #include "lldb/Utility/ConstString.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" #include "lldb/lldb-private-interfaces.h" #include "llvm/ADT/StringRef.h" @@ -171,6 +175,100 @@ ModuleSP DynamicLoader::LoadModuleAtAddress(const FileSpec &file, 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, int size_in_bytes) { Status error; diff --git a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp index 5dc71fca36c25..15b06dbbfaec0 100644 --- a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp +++ b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp @@ -5600,7 +5600,8 @@ bool ObjectFileMachO::GetCorefileMainBinaryInfo(addr_t &value, } 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 // ObjectFile::BinaryType enum switch (binspec_type) { @@ -6901,7 +6902,8 @@ ObjectFileMachO::GetCorefileAllImageInfos() { MachOCorefileImageEntry image_entry; 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.currently_executing = currently_executing; @@ -6932,9 +6934,11 @@ ObjectFileMachO::GetCorefileAllImageInfos() { MachOCorefileImageEntry image_entry; 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.slide = slide; + image_entry.currently_executing = true; image_infos.all_image_infos.push_back(image_entry); } } @@ -6951,42 +6955,41 @@ bool ObjectFileMachO::LoadCoreFileImages(lldb_private::Process &process) { ModuleList added_modules; for (const MachOCorefileImageEntry &image : image_infos.all_image_infos) { - ModuleSpec module_spec; - module_spec.GetUUID() = image.uuid; - 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) { + ModuleSP module_sp; + + if (!image.filename.empty()) { Status error; - Symbols::DownloadObjectAndSymbolFile(module_spec, error, true); - if (FileSystem::Instance().Exists(module_spec.GetFileSpec())) { - process.GetTarget().GetOrCreateModule(module_spec, false); + ModuleSpec module_spec; + module_spec.GetUUID() = image.uuid; + 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); + } } - } - Status error; - ModuleSP module_sp = - process.GetTarget().GetOrCreateModule(module_spec, false, &error); - if (!module_sp.get() || !module_sp->GetObjectFile()) { + module_sp = + process.GetTarget().GetOrCreateModule(module_spec, false, &error); + process.GetTarget().GetImages().AppendIfNeeded(module_sp, + false /* notify */); + } else { if (image.load_address != LLDB_INVALID_ADDRESS) { - module_sp = process.ReadModuleFromMemory(module_spec.GetFileSpec(), - image.load_address); + module_sp = DynamicLoader::LoadBinaryWithUUIDAndAddress( + &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()) { // Will call ModulesDidLoad with all modules once they've all // been added to the Target with load addresses. Don't notify // here, before the load address is set. - const bool notify = false; - process.GetTarget().GetImages().AppendIfNeeded(module_sp, notify); - added_modules.Append(module_sp, notify); + added_modules.Append(module_sp, false /* notify */); if (image.segment_load_addresses.size() > 0) { if (log) { std::string uuidstr = image.uuid.GetAsString(); diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp index 580cdde57d80f..8687175ee36c4 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp @@ -1033,6 +1033,13 @@ bool GDBRemoteCommunicationClient::GetProcessStandaloneBinary( return true; } +std::vector<addr_t> +GDBRemoteCommunicationClient::GetProcessStandaloneBinaries() { + if (m_qProcessInfo_is_valid == eLazyBoolCalculate) + GetCurrentProcessInfo(); + return m_binary_addresses; +} + bool GDBRemoteCommunicationClient::GetGDBServerVersion() { if (m_qGDBServerVersion_is_valid == eLazyBoolCalculate) { m_gdb_server_name.clear(); @@ -2192,6 +2199,14 @@ bool GDBRemoteCommunicationClient::GetCurrentProcessInfo(bool allow_lazy) { m_process_standalone_value_is_offset = false; ++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) diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h index 3d838d6d80747..3a62747603f6d 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h @@ -220,6 +220,8 @@ class GDBRemoteCommunicationClient : public GDBRemoteClientBase { bool GetProcessStandaloneBinary(UUID &uuid, lldb::addr_t &value, bool &value_is_offset); + std::vector<lldb::addr_t> GetProcessStandaloneBinaries(); + void GetRemoteQSupported(); bool GetVContSupported(char flavor); @@ -593,6 +595,7 @@ class GDBRemoteCommunicationClient : public GDBRemoteClientBase { UUID m_process_standalone_uuid; lldb::addr_t m_process_standalone_value = LLDB_INVALID_ADDRESS; bool m_process_standalone_value_is_offset = false; + std::vector<lldb::addr_t> m_binary_addresses; llvm::VersionTuple m_os_version; llvm::VersionTuple m_maccatalyst_version; std::string m_os_build; diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp index b7de05d498aa9..e3dada1864da5 100644 --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -581,80 +581,31 @@ Status ProcessGDBRemote::DoConnectRemote(llvm::StringRef remote_url) { ModuleSP module_sp; if (standalone_uuid.IsValid()) { - ModuleSpec module_spec; - module_spec.GetUUID() = standalone_uuid; - - // Look up UUID in global module cache before attempting - // a more expensive search. - Status error = ModuleList::GetSharedModule(module_spec, module_sp, - nullptr, nullptr, nullptr); - - if (!module_sp) { - // Force a an external 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. - 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); - } + const bool force_symbol_search = true; + const bool notify = true; + DynamicLoader::LoadBinaryWithUUIDAndAddress( + this, standalone_uuid, standalone_value, + standalone_value_is_offset, force_symbol_search, notify); + } + } - 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); - } + // The remote stub may know about a list of binaries to + // force load into the process -- a firmware type situation + // where multiple binaries are present in virtual memory, + // 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. + + std::vector<addr_t> bin_addrs = m_gdb_comm.GetProcessStandaloneBinaries(); + if (bin_addrs.size()) { + UUID uuid; + const bool value_is_slide = false; + for (addr_t addr : bin_addrs) { + const bool force_symbol_search = true; + const bool notify = true; + DynamicLoader::LoadBinaryWithUUIDAndAddress( + this, uuid, addr, value_is_slide, force_symbol_search, notify); } } diff --git a/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp b/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp index 7f943a5258c40..68786e8d80b00 100644 --- a/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp +++ b/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp @@ -180,88 +180,6 @@ bool ProcessMachCore::GetDynamicLoaderAddress(lldb::addr_t addr) { 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 Status ProcessMachCore::DoLoadCore() { 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_value_is_offset, type); } - if (objfile_binary_value != LLDB_INVALID_ADDRESS && - !objfile_binary_value_is_offset) { - if (type == ObjectFile::eBinaryTypeUser) { - load_standalone_binary(objfile_binary_uuid, objfile_binary_value, - objfile_binary_value_is_offset, GetTarget()); - m_dyld_addr = objfile_binary_value; - m_dyld_plugin_name = DynamicLoaderMacOSXDYLD::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; - } + const bool force_symbol_search = true; + const bool notify = true; + if (DynamicLoader::LoadBinaryWithUUIDAndAddress( + this, objfile_binary_uuid, objfile_binary_value, + objfile_binary_value_is_offset, force_symbol_search, notify)) { + found_main_binary_definitively = true; + m_dyld_plugin_name = DynamicLoaderStatic::GetPluginNameStatic(); } - if (!found_main_binary_definitively) { - // ObjectFile::eBinaryTypeStandalone, undeclared types - if (load_standalone_binary(objfile_binary_uuid, objfile_binary_value, - objfile_binary_value_is_offset, GetTarget())) { - found_main_binary_definitively = true; - m_dyld_plugin_name = DynamicLoaderStatic::GetPluginNameStatic(); - } + if (type == ObjectFile::eBinaryTypeUser) { + m_dyld_addr = objfile_binary_value; + m_dyld_plugin_name = DynamicLoaderMacOSXDYLD::GetPluginNameStatic(); + } + if (type == ObjectFile::eBinaryTypeKernel) { + 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 // address. const bool value_is_offset = false; - if (load_standalone_binary(ident_uuid, ident_binary_addr, value_is_offset, - GetTarget())) { + const bool force_symbol_search = true; + 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; m_dyld_plugin_name = DynamicLoaderStatic::GetPluginNameStatic(); } diff --git a/lldb/test/API/macosx/lc-note/multiple-binary-corefile/Makefile b/lldb/test/API/macosx/lc-note/multiple-binary-corefile/Makefile new file mode 100644 index 0000000000000..8e561f17383fe --- /dev/null +++ b/lldb/test/API/macosx/lc-note/multiple-binary-corefile/Makefile @@ -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 diff --git a/lldb/test/API/macosx/lc-note/multiple-binary-corefile/TestMultipleBinaryCorefile.py b/lldb/test/API/macosx/lc-note/multiple-binary-corefile/TestMultipleBinaryCorefile.py new file mode 100644 index 0000000000000..5e74f0c3f8bfa --- /dev/null +++ b/lldb/test/API/macosx/lc-note/multiple-binary-corefile/TestMultipleBinaryCorefile.py @@ -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() diff --git a/lldb/test/API/macosx/lc-note/multiple-binary-corefile/create-multibin-corefile.cpp b/lldb/test/API/macosx/lc-note/multiple-binary-corefile/create-multibin-corefile.cpp new file mode 100644 index 0000000000000..ebe71606806dd --- /dev/null +++ b/lldb/test/API/macosx/lc-note/multiple-binary-corefile/create-multibin-corefile.cpp @@ -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); +} diff --git a/lldb/test/API/macosx/lc-note/multiple-binary-corefile/main.c b/lldb/test/API/macosx/lc-note/multiple-binary-corefile/main.c new file mode 100644 index 0000000000000..eaab873b53e44 --- /dev/null +++ b/lldb/test/API/macosx/lc-note/multiple-binary-corefile/main.c @@ -0,0 +1,7 @@ +#include <stdio.h> +int one(); +int two(); +int main() { + puts("this is the standalone binary test program"); + return one() + two(); +} diff --git a/lldb/test/API/macosx/lc-note/multiple-binary-corefile/one.c b/lldb/test/API/macosx/lc-note/multiple-binary-corefile/one.c new file mode 100644 index 0000000000000..6e8fe4ad5ff75 --- /dev/null +++ b/lldb/test/API/macosx/lc-note/multiple-binary-corefile/one.c @@ -0,0 +1 @@ +int one() { return 5; } diff --git a/lldb/test/API/macosx/lc-note/multiple-binary-corefile/two.c b/lldb/test/API/macosx/lc-note/multiple-binary-corefile/two.c new file mode 100644 index 0000000000000..f44baa69c293a --- /dev/null +++ b/lldb/test/API/macosx/lc-note/multiple-binary-corefile/two.c @@ -0,0 +1 @@ +int two() { return 10; } _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits