diff --git a/lldb/include/lldb/Symbol/ObjectFile.h b/lldb/include/lldb/Symbol/ObjectFile.h --- a/lldb/include/lldb/Symbol/ObjectFile.h +++ b/lldb/include/lldb/Symbol/ObjectFile.h @@ -91,6 +91,17 @@ eStrataJIT }; + /// If we have a corefile binary hint, this enum + /// specifies the binary type which we can use to + /// select the correct DynamicLoader plugin. + enum BinaryType { + eBinaryTypeInvalid = 0, + eBinaryTypeUnknown, + eBinaryTypeKernel, /// kernel binary + eBinaryTypeUser, /// user process binary + eBinaryTypeStandalone /// standalone binary / firmware + }; + struct LoadableData { lldb::addr_t Dest; llvm::ArrayRef Contents; @@ -500,12 +511,17 @@ /// If the uuid of the binary is specified, this will be set. /// If no UUID is available, will be cleared. /// + /// \param[out] type + /// Return the type of the binary, which will dictate which + /// DynamicLoader plugin should be used. + /// /// \return /// Returns true if either address or uuid has been set. - virtual bool GetCorefileMainBinaryInfo (lldb::addr_t &address, UUID &uuid) { - address = LLDB_INVALID_ADDRESS; - uuid.Clear(); - return false; + virtual bool GetCorefileMainBinaryInfo(lldb::addr_t &address, UUID &uuid, + ObjectFile::BinaryType &type) { + address = LLDB_INVALID_ADDRESS; + uuid.Clear(); + return false; } virtual lldb::RegisterContextSP diff --git a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h --- a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h +++ b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h @@ -112,7 +112,9 @@ std::string GetIdentifierString() override; - bool GetCorefileMainBinaryInfo (lldb::addr_t &address, lldb_private::UUID &uuid) override; + bool GetCorefileMainBinaryInfo(lldb::addr_t &address, + lldb_private::UUID &uuid, + ObjectFile::BinaryType &type) override; lldb::RegisterContextSP GetThreadContextAtIndex(uint32_t idx, lldb_private::Thread &thread) override; diff --git a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp --- a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp +++ b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp @@ -5519,7 +5519,8 @@ return result; } -bool ObjectFileMachO::GetCorefileMainBinaryInfo(addr_t &address, UUID &uuid) { +bool ObjectFileMachO::GetCorefileMainBinaryInfo(addr_t &address, UUID &uuid, + ObjectFile::BinaryType &type) { address = LLDB_INVALID_ADDRESS; uuid.Clear(); ModuleSP module_sp(GetModule()); @@ -5542,24 +5543,43 @@ // "main bin spec" (main binary specification) data payload is // formatted: // uint32_t version [currently 1] - // uint32_t type [0 == unspecified, 1 == kernel, 2 == user - // process] uint64_t address [ UINT64_MAX if address not - // specified ] uuid_t uuid [ all zero's if uuid not - // specified ] uint32_t log2_pagesize [ process page size in log base - // 2, e.g. 4k pages are 12. 0 for unspecified ] + // uint32_t type [0 == unspecified, 1 == kernel, + // 2 == user process, 3 == firmware ] + // uint64_t address [ UINT64_MAX if address not specified ] + // uuid_t uuid [ all zero's if uuid not specified ] + // uint32_t log2_pagesize [ process page size in log base + // 2, e.g. 4k pages are 12. + // 0 for unspecified ] + // uint32_t unused [ for alignment ] if (strcmp("main bin spec", data_owner) == 0 && size >= 32) { offset = fileoff; uint32_t version; if (m_data.GetU32(&offset, &version, 1) != nullptr && version == 1) { - uint32_t type = 0; + uint32_t binspec_type = 0; uuid_t raw_uuid; memset(raw_uuid, 0, sizeof(uuid_t)); - if (m_data.GetU32(&offset, &type, 1) && + if (m_data.GetU32(&offset, &binspec_type, 1) && m_data.GetU64(&offset, &address, 1) && m_data.CopyData(offset, sizeof(uuid_t), raw_uuid) != 0) { uuid = UUID::fromOptionalData(raw_uuid, sizeof(uuid_t)); + // convert the "main bin spec" type into our + // ObjectFile::BinaryType enum + switch (binspec_type) { + case 0: + type = eBinaryTypeUnknown; + break; + case 1: + type = eBinaryTypeKernel; + break; + case 2: + type = eBinaryTypeUser; + break; + case 3: + type = eBinaryTypeStandalone; + break; + } return true; } } diff --git a/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp b/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp --- a/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp +++ b/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp @@ -281,8 +281,9 @@ addr_t objfile_binary_addr; UUID objfile_binary_uuid; - if (core_objfile->GetCorefileMainBinaryInfo (objfile_binary_addr, objfile_binary_uuid)) - { + ObjectFile::BinaryType type; + if (core_objfile->GetCorefileMainBinaryInfo(objfile_binary_addr, + objfile_binary_uuid, type)) { if (objfile_binary_addr != LLDB_INVALID_ADDRESS) { m_mach_kernel_addr = objfile_binary_addr; @@ -293,7 +294,7 @@ m_mach_kernel_addr); } } - + // This checks for the presence of an LC_IDENT string in a core file; // LC_IDENT is very obsolete and should not be used in new code, but if the // load command is present, let's use the contents. @@ -326,58 +327,80 @@ addr, corefile_identifier.c_str()); } } - if (found_main_binary_definitively == false - && corefile_identifier.find("EFI ") != std::string::npos) { - UUID uuid; + + // In the case where we have an LC_NOTE specifying a standalone + // binary with only a UUID (and no load address) (iBoot, EFI, etc), + // then let's try to force a load of the binary and set its + // load address to 0-offset. + // + // The two forms this can come in is either a + // 'kern ver str' LC_NOTE with "EFI UUID=...." + // 'main bin spec' LC_NOTE with UUID and no load address. + + if (found_main_binary_definitively == false && + (corefile_identifier.find("EFI ") != std::string::npos || + (objfile_binary_uuid.IsValid() && + objfile_binary_addr == LLDB_INVALID_ADDRESS))) { + UUID uuid; + if (objfile_binary_uuid.IsValid()) { + uuid = objfile_binary_uuid; + LLDB_LOGF(log, + "ProcessMachCore::DoLoadCore: Using the main bin spec " + "LC_NOTE with UUID %s and no load address", + uuid.GetAsString().c_str()); + } else { if (corefile_identifier.find("UUID=") != std::string::npos) { - size_t p = corefile_identifier.find("UUID=") + strlen("UUID="); - std::string uuid_str = corefile_identifier.substr(p, 36); - uuid.SetFromStringRef(uuid_str); + size_t p = corefile_identifier.find("UUID=") + strlen("UUID="); + std::string uuid_str = corefile_identifier.substr(p, 36); + uuid.SetFromStringRef(uuid_str); + if (uuid.IsValid()) { + LLDB_LOGF(log, + "ProcessMachCore::DoLoadCore: Using the EFI " + "from LC_IDENT/LC_NOTE 'kern ver str' string: '%s'", + corefile_identifier.c_str()); + } } - if (uuid.IsValid()) { - LLDB_LOGF(log, - "ProcessMachCore::DoLoadCore: Using the EFI " - "from LC_IDENT/LC_NOTE 'kern ver str' string: '%s'", - corefile_identifier.c_str()); - - // We're only given a UUID here, not a load address. - // But there are python scripts in the EFI binary's dSYM which - // know how to relocate the binary to the correct load address. - // lldb only needs to locate & load the binary + dSYM. - ModuleSpec module_spec; - module_spec.GetUUID() = uuid; - module_spec.GetArchitecture() = GetTarget().GetArchitecture(); - - // Lookup UUID locally, before attempting dsymForUUID like action - FileSpecList search_paths = Target::GetDefaultDebugFileSearchPaths(); - module_spec.GetSymbolFileSpec() = - Symbols::LocateExecutableSymbolFile(module_spec, search_paths); - if (module_spec.GetSymbolFileSpec()) { - ModuleSpec executable_module_spec = - Symbols::LocateExecutableObjectFile(module_spec); - if (FileSystem::Instance().Exists( - executable_module_spec.GetFileSpec())) { - module_spec.GetFileSpec() = executable_module_spec.GetFileSpec(); - } + } + + if (uuid.IsValid()) { + ModuleSpec module_spec; + module_spec.GetUUID() = uuid; + module_spec.GetArchitecture() = GetTarget().GetArchitecture(); + + // Lookup UUID locally, before attempting dsymForUUID-like action + FileSpecList search_paths = Target::GetDefaultDebugFileSearchPaths(); + module_spec.GetSymbolFileSpec() = + Symbols::LocateExecutableSymbolFile(module_spec, search_paths); + if (module_spec.GetSymbolFileSpec()) { + ModuleSpec executable_module_spec = + Symbols::LocateExecutableObjectFile(module_spec); + if (FileSystem::Instance().Exists( + executable_module_spec.GetFileSpec())) { + module_spec.GetFileSpec() = executable_module_spec.GetFileSpec(); } + } - // Force a a dsymForUUID lookup, if that tool is available. - if (!module_spec.GetSymbolFileSpec()) - Symbols::DownloadObjectAndSymbolFile(module_spec, true); - - if (FileSystem::Instance().Exists(module_spec.GetFileSpec())) { - ModuleSP module_sp(new Module(module_spec)); - if (module_sp.get() && module_sp->GetObjectFile()) { - // Get the current target executable - ModuleSP exe_module_sp(GetTarget().GetExecutableModule()); - - // Make sure you don't already have the right module loaded - // and they will be uniqued - if (exe_module_sp.get() != module_sp.get()) - GetTarget().SetExecutableModule(module_sp, eLoadDependentsNo); - } + // Force a a dsymForUUID lookup, if that tool is available. + if (!module_spec.GetSymbolFileSpec()) + Symbols::DownloadObjectAndSymbolFile(module_spec, true); + + // If we found a binary, load it at offset 0 and set our + // dyld_plugin to be the static plugin. + if (FileSystem::Instance().Exists(module_spec.GetFileSpec())) { + ModuleSP module_sp(new Module(module_spec)); + if (module_sp.get() && module_sp->GetObjectFile()) { + GetTarget().GetImages().AppendIfNeeded(module_sp, true); + GetTarget().SetExecutableModule(module_sp, eLoadDependentsNo); + found_main_binary_definitively = true; + bool changed = true; + module_sp->SetLoadAddress(GetTarget(), 0, true, changed); + ModuleList added_module; + added_module.Append(module_sp, false); + GetTarget().ModulesDidLoad(added_module); + m_dyld_plugin_name = DynamicLoaderDarwinKernel::GetPluginNameStatic(); } } + } } if (!found_main_binary_definitively && @@ -440,35 +463,37 @@ } } - // If we found both a user-process dyld and a kernel binary, we need to - // decide which to prefer. - if (GetCorefilePreference() == eKernelCorefile) { - if (m_mach_kernel_addr != LLDB_INVALID_ADDRESS) { - LLDB_LOGF(log, - "ProcessMachCore::DoLoadCore: Using kernel corefile image " - "at 0x%" PRIx64, - m_mach_kernel_addr); - m_dyld_plugin_name = DynamicLoaderDarwinKernel::GetPluginNameStatic(); - } else if (m_dyld_addr != LLDB_INVALID_ADDRESS) { - LLDB_LOGF(log, - "ProcessMachCore::DoLoadCore: Using user process dyld " - "image at 0x%" PRIx64, - m_dyld_addr); - m_dyld_plugin_name = DynamicLoaderMacOSXDYLD::GetPluginNameStatic(); - } - } else { - if (m_dyld_addr != LLDB_INVALID_ADDRESS) { - LLDB_LOGF(log, - "ProcessMachCore::DoLoadCore: Using user process dyld " - "image at 0x%" PRIx64, - m_dyld_addr); - m_dyld_plugin_name = DynamicLoaderMacOSXDYLD::GetPluginNameStatic(); - } else if (m_mach_kernel_addr != LLDB_INVALID_ADDRESS) { - LLDB_LOGF(log, - "ProcessMachCore::DoLoadCore: Using kernel corefile image " - "at 0x%" PRIx64, - m_mach_kernel_addr); - m_dyld_plugin_name = DynamicLoaderDarwinKernel::GetPluginNameStatic(); + if (m_dyld_plugin_name.IsEmpty()) { + // If we found both a user-process dyld and a kernel binary, we need to + // decide which to prefer. + if (GetCorefilePreference() == eKernelCorefile) { + if (m_mach_kernel_addr != LLDB_INVALID_ADDRESS) { + LLDB_LOGF(log, + "ProcessMachCore::DoLoadCore: Using kernel corefile image " + "at 0x%" PRIx64, + m_mach_kernel_addr); + m_dyld_plugin_name = DynamicLoaderDarwinKernel::GetPluginNameStatic(); + } else if (m_dyld_addr != LLDB_INVALID_ADDRESS) { + LLDB_LOGF(log, + "ProcessMachCore::DoLoadCore: Using user process dyld " + "image at 0x%" PRIx64, + m_dyld_addr); + m_dyld_plugin_name = DynamicLoaderMacOSXDYLD::GetPluginNameStatic(); + } + } else { + if (m_dyld_addr != LLDB_INVALID_ADDRESS) { + LLDB_LOGF(log, + "ProcessMachCore::DoLoadCore: Using user process dyld " + "image at 0x%" PRIx64, + m_dyld_addr); + m_dyld_plugin_name = DynamicLoaderMacOSXDYLD::GetPluginNameStatic(); + } else if (m_mach_kernel_addr != LLDB_INVALID_ADDRESS) { + LLDB_LOGF(log, + "ProcessMachCore::DoLoadCore: Using kernel corefile image " + "at 0x%" PRIx64, + m_mach_kernel_addr); + m_dyld_plugin_name = DynamicLoaderDarwinKernel::GetPluginNameStatic(); + } } } diff --git a/lldb/test/API/macosx/lc-note/firmware-corefile/Makefile b/lldb/test/API/macosx/lc-note/firmware-corefile/Makefile new file mode 100644 --- /dev/null +++ b/lldb/test/API/macosx/lc-note/firmware-corefile/Makefile @@ -0,0 +1,14 @@ +MAKE_DSYM := NO +C_SOURCES := main.c +CFLAGS_EXTRAS := -Wl,-random_uuid + +all: a.out b.out create-empty-corefile + +create-empty-corefile: + $(MAKE) -f $(MAKEFILE_RULES) EXE=create-empty-corefile \ + C_SOURCES=create-empty-corefile.c + +b.out: + $(MAKE) VPATH=$(SRCDIR)/$* -I $(SRCDIR) -f $(SRCDIR)/bout.mk + +include Makefile.rules diff --git a/lldb/test/API/macosx/lc-note/firmware-corefile/TestFirmwareCorefiles.py b/lldb/test/API/macosx/lc-note/firmware-corefile/TestFirmwareCorefiles.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/macosx/lc-note/firmware-corefile/TestFirmwareCorefiles.py @@ -0,0 +1,133 @@ +"""Test that corefiles with LC_NOTE "kern ver str" and "main bin spec" load commands works.""" + + + +import os +import re +import subprocess + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class TestFirmwareCorefiles(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipIf(debug_info=no_match(["dsym"]), bugnumber="This test is looking explicitly for a dSYM") + @skipIf(archs=no_match(['x86_64'])) + @skipUnlessDarwin + def test_lc_note(self): + self.build() + self.aout_exe = self.getBuildArtifact("a.out") + self.bout_exe = self.getBuildArtifact("b.out") + self.create_corefile = self.getBuildArtifact("create-empty-corefile") + self.dsym_for_uuid = self.getBuildArtifact("dsym-for-uuid.sh") + self.aout_corefile = self.getBuildArtifact("aout.core") + self.bout_corefile = self.getBuildArtifact("bout.core") + + ## We can hook in our dsym-for-uuid shell script to lldb with this env + ## var instead of requiring a defaults write. + os.environ['LLDB_APPLE_DSYMFORUUID_EXECUTABLE'] = self.dsym_for_uuid + self.addTearDownHook(lambda: os.environ.pop('LLDB_APPLE_DSYMFORUUID_EXECUTABLE', None)) + + dwarfdump_uuid_regex = re.compile( + 'UUID: ([-0-9a-fA-F]+) \(([^\(]+)\) .*') + dwarfdump_cmd_output = subprocess.check_output( + ('/usr/bin/dwarfdump --uuid "%s"' % self.aout_exe), shell=True).decode("utf-8") + aout_uuid = None + for line in dwarfdump_cmd_output.splitlines(): + match = dwarfdump_uuid_regex.search(line) + if match: + aout_uuid = match.group(1) + self.assertNotEqual(aout_uuid, None, "Could not get uuid of built a.out") + + dwarfdump_cmd_output = subprocess.check_output( + ('/usr/bin/dwarfdump --uuid "%s"' % self.bout_exe), shell=True).decode("utf-8") + bout_uuid = None + for line in dwarfdump_cmd_output.splitlines(): + match = dwarfdump_uuid_regex.search(line) + if match: + bout_uuid = match.group(1) + self.assertNotEqual(bout_uuid, None, "Could not get uuid of built b.out") + + ### Create our dsym-for-uuid shell script which returns self.aout_exe + ### or self.bout_exe, depending on the UUID on the command line. + shell_cmds = [ + '#! /bin/sh', + '# the last argument is the uuid', + 'while [ $# -gt 1 ]', + 'do', + ' shift', + 'done', + 'ret=0', + 'echo ""', + 'echo ""', + 'echo ""', + '', + 'if [ "$1" != "%s" -a "$1" != "%s" ]' % (aout_uuid, bout_uuid), + 'then', + ' echo "DBGErrornot found"', + ' echo ""', + ' exit 1', + 'fi', + 'if [ "$1" = "%s" ]' % aout_uuid, + 'then', + ' uuid=%s' % aout_uuid, + ' bin=%s' % self.aout_exe, + ' dsym=%s.dSYM/Contents/Resources/DWARF/%s' % (self.aout_exe, os.path.basename(self.aout_exe)), + 'else', + ' uuid=%s' % bout_uuid, + ' bin=%s' % self.bout_exe, + ' dsym=%s.dSYM/Contents/Resources/DWARF/%s' % (self.bout_exe, os.path.basename(self.bout_exe)), + 'fi', + 'echo "$uuid"', + '', + 'echo "DBGArchitecturex86_64"', + 'echo "DBGDSYMPath$dsym"', + 'echo "DBGSymbolRichExecutable$bin"', + 'echo ""', + 'exit $ret' + ] + + with open(self.dsym_for_uuid, "w") as writer: + for l in shell_cmds: + writer.write(l + '\n') + + os.chmod(self.dsym_for_uuid, 0o755) + + ### Create our corefile + retcode = call(self.create_corefile + " version-string " + self.aout_corefile + " " + self.aout_exe, shell=True) + retcode = call(self.create_corefile + " main-bin-spec " + self.bout_corefile + " " + self.bout_exe, shell=True) + + ### Now run lldb on the corefile + ### which will give us a UUID + ### which we call dsym-for-uuid.sh with + ### which gives us a binary and dSYM + ### which lldb should load! + + # First, try the "kern ver str" corefile + self.target = self.dbg.CreateTarget('') + err = lldb.SBError() + self.process = self.target.LoadCore(self.aout_corefile) + self.assertEqual(self.process.IsValid(), True) + if self.TraceOn(): + self.runCmd("image list") + self.assertEqual(self.target.GetNumModules(), 1) + fspec = self.target.GetModuleAtIndex(0).GetFileSpec() + filepath = fspec.GetDirectory() + "/" + fspec.GetFilename() + self.assertEqual(filepath, self.aout_exe) + + + # Second, try the "main bin spec" corefile + self.target = self.dbg.CreateTarget('') + self.process = self.target.LoadCore(self.bout_corefile) + self.assertEqual(self.process.IsValid(), True) + if self.TraceOn(): + self.runCmd("image list") + self.assertEqual(self.target.GetNumModules(), 1) + fspec = self.target.GetModuleAtIndex(0).GetFileSpec() + filepath = fspec.GetDirectory() + "/" + fspec.GetFilename() + self.assertEqual(filepath, self.bout_exe) diff --git a/lldb/test/API/macosx/lc-note/firmware-corefile/bout.mk b/lldb/test/API/macosx/lc-note/firmware-corefile/bout.mk new file mode 100644 --- /dev/null +++ b/lldb/test/API/macosx/lc-note/firmware-corefile/bout.mk @@ -0,0 +1,10 @@ +MAKE_DSYM := NO + +C_SOURCES := main.c +CFLAGS_EXTRAS := -Wl,-random_uuid + +EXE := b.out + +all: b.out + +include Makefile.rules diff --git a/lldb/test/API/macosx/lc-note/firmware-corefile/create-empty-corefile.cpp b/lldb/test/API/macosx/lc-note/firmware-corefile/create-empty-corefile.cpp new file mode 100644 --- /dev/null +++ b/lldb/test/API/macosx/lc-note/firmware-corefile/create-empty-corefile.cpp @@ -0,0 +1,347 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +// Create an empty corefile with a "kern ver str" LC_NOTE +// or a "main bin spec" LC_NOTE.. +// If an existing binary is given as a 3rd argument on the cmd line, +// the UUID from that binary will be encoded in the corefile. +// Otherwise a pre-set UUID will be put in the corefile that +// is created. + +struct main_bin_spec_payload { + uint32_t version; + uint32_t type; + uint64_t address; + uuid_t uuid; + uint32_t log2_pagesize; + uint32_t unused; +}; + +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 &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 &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 x86_lc_thread_load_command() { + std::vector data; + add_uint32(data, LC_THREAD); // thread_command.cmd + add_uint32(data, 184); // thread_command.cmdsize + add_uint32(data, x86_THREAD_STATE64); // thread_command.flavor + add_uint32(data, x86_THREAD_STATE64_COUNT); // thread_command.count + add_uint64(data, 0x0000000000000000); // rax + add_uint64(data, 0x0000000000000400); // rbx + add_uint64(data, 0x0000000000000000); // rcx + add_uint64(data, 0x0000000000000000); // rdx + add_uint64(data, 0x0000000000000000); // rdi + add_uint64(data, 0x0000000000000000); // rsi + add_uint64(data, 0xffffff9246e2ba20); // rbp + add_uint64(data, 0xffffff9246e2ba10); // rsp + add_uint64(data, 0x0000000000000000); // r8 + add_uint64(data, 0x0000000000000000); // r9 + add_uint64(data, 0x0000000000000000); // r10 + add_uint64(data, 0x0000000000000000); // r11 + add_uint64(data, 0xffffff7f96ce5fe1); // r12 + add_uint64(data, 0x0000000000000000); // r13 + add_uint64(data, 0x0000000000000000); // r14 + add_uint64(data, 0xffffff9246e2bac0); // r15 + add_uint64(data, 0xffffff8015a8f6d0); // rip + add_uint64(data, 0x0000000000011111); // rflags + add_uint64(data, 0x0000000000022222); // cs + add_uint64(data, 0x0000000000033333); // fs + add_uint64(data, 0x0000000000044444); // gs + return data; +} + +void add_lc_note_kern_ver_str_load_command( + std::vector> &loadcmds, std::vector &payload, + int payload_file_offset, std::string uuid) { + std::string ident = "EFI UUID="; + ident += uuid; + std::vector 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, "kern ver str"); + + // 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, 4 + ident.size() + 1); // note_command.size + + loadcmds.push_back(loadcmd_data); + + add_uint32(payload, 1); // kerneL_version_string.version + for (int i = 0; i < ident.size() + 1; i++) { + payload.push_back(ident[i]); + } +} + +void add_lc_note_main_bin_spec_load_command( + std::vector> &loadcmds, std::vector &payload, + int payload_file_offset, std::string uuidstr) { + std::vector 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, 1); // version + add_uint32(payload, 3); // type == 3 [ firmware, standalone,e tc ] + add_uint64(payload, UINT64_MAX); // load address unknown/unspecified + 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); // unused +} + +void add_lc_segment(std::vector> &loadcmds, + std::vector &payload, int payload_file_offset) { + std::vector 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 = 0xffffff7f96400000; + seg.vmsize = 4096; + seg.fileoff = payload.size() + payload_file_offset; + seg.filesize = 0; + 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 get_uuid_from_binary(const char *fn) { + 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; + + 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) { + fprintf(stderr, + "This tool creates an x86_64 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); + } 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) { + fprintf(stderr, + "This tool creates an x86_64 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); + } + + 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 (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; + break; + } + file_offset += cmdsize; + } + return uuid; +} + +int main(int argc, char **argv) { + if (argc != 4) { + fprintf(stderr, "usage: create-empty-corefile version-string|main-bin-spec " + " \n"); + fprintf( + stderr, + "Create a Mach-O corefile with an either LC_NOTE 'kern ver str' or \n"); + fprintf(stderr, "an LC_NOTE 'main bin spec' load command without an " + "address specified, depending on\n"); + fprintf(stderr, "whether the 1st arg is version-string or main-bin-spec\n"); + exit(1); + } + if (strcmp(argv[1], "version-string") != 0 && + strcmp(argv[1], "main-bin-spec") != 0) { + fprintf(stderr, "arg1 was not version-string or main-bin-spec\n"); + exit(1); + } + + std::string uuid = get_uuid_from_binary(argv[3]); + + // An array of load commands (in the form of byte arrays) + std::vector> load_commands; + + // An array of corefile contents (page data, lc_note data, etc) + std::vector payload; + + // First add all the load commands / payload so we can figure out how large + // the load commands will actually be. + load_commands.push_back(x86_lc_thread_load_command()); + if (strcmp(argv[1], "version-string") == 0) + add_lc_note_kern_ver_str_load_command(load_commands, payload, 0, uuid); + else + add_lc_note_main_bin_spec_load_command(load_commands, payload, 0, uuid); + add_lc_segment(load_commands, payload, 0); + + int size_of_load_commands = 0; + for (const auto &lc : load_commands) + size_of_load_commands += lc.size(); + + int header_and_load_cmd_room = + 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(); + + load_commands.push_back(x86_lc_thread_load_command()); + + if (strcmp(argv[1], "version-string") == 0) + add_lc_note_kern_ver_str_load_command(load_commands, payload, + header_and_load_cmd_room, uuid); + else + add_lc_note_main_bin_spec_load_command(load_commands, payload, + header_and_load_cmd_room, uuid); + + add_lc_segment(load_commands, payload, header_and_load_cmd_room); + + struct mach_header_64 mh; + mh.magic = MH_MAGIC_64; + mh.cputype = CPU_TYPE_X86_64; + + mh.cpusubtype = CPU_SUBTYPE_X86_64_ALL; + mh.filetype = MH_CORE; + mh.ncmds = load_commands.size(); + mh.sizeofcmds = size_of_load_commands; + mh.flags = 0; + mh.reserved = 0; + + FILE *f = fopen(argv[2], "w"); + + if (f == nullptr) { + fprintf(stderr, "Unable to open file %s for writing\n", argv[2]); + exit(1); + } + + fwrite(&mh, sizeof(struct mach_header_64), 1, f); + + for (const auto &lc : load_commands) + fwrite(lc.data(), lc.size(), 1, f); + + fseek(f, header_and_load_cmd_room, SEEK_SET); + + fwrite(payload.data(), payload.size(), 1, f); + + fclose(f); +} diff --git a/lldb/test/API/macosx/lc-note/firmware-corefile/main.c b/lldb/test/API/macosx/lc-note/firmware-corefile/main.c new file mode 100644 --- /dev/null +++ b/lldb/test/API/macosx/lc-note/firmware-corefile/main.c @@ -0,0 +1,2 @@ +#include +int main () { puts ("this is the lc-note test program."); }