[Lldb-commits] [lldb] 1bec6eb - Add support for firmware/standalone LC_NOTE "main bin spec" corefiles
Jason Molenda via lldb-commits
lldb-commits at lists.llvm.org
Fri Sep 25 15:19:31 PDT 2020
Author: Jason Molenda
Date: 2020-09-25T15:19:22-07:00
New Revision: 1bec6eb3f5cba594698bae5b2789744e0c8ee5f2
URL: https://github.com/llvm/llvm-project/commit/1bec6eb3f5cba594698bae5b2789744e0c8ee5f2
DIFF: https://github.com/llvm/llvm-project/commit/1bec6eb3f5cba594698bae5b2789744e0c8ee5f2.diff
LOG: Add support for firmware/standalone LC_NOTE "main bin spec" corefiles
When a Mach-O corefile has an LC_NOTE "main bin spec" for a
standalone binary / firmware, with only a UUID and no load
address, try to locate the binary and dSYM by UUID and if
found, load it at offset 0 for the user.
Add a test case that tests a firmware/standalone corefile
with both the "kern ver str" and "main bin spec" LC_NOTEs.
<rdar://problem/68193804>
Differential Revision: https://reviews.llvm.org/D88282
Added:
lldb/test/API/macosx/lc-note/firmware-corefile/Makefile
lldb/test/API/macosx/lc-note/firmware-corefile/TestFirmwareCorefiles.py
lldb/test/API/macosx/lc-note/firmware-corefile/bout.mk
lldb/test/API/macosx/lc-note/firmware-corefile/create-empty-corefile.cpp
lldb/test/API/macosx/lc-note/firmware-corefile/main.c
Modified:
lldb/include/lldb/Symbol/ObjectFile.h
lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp
lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h
lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp
Removed:
################################################################################
diff --git a/lldb/include/lldb/Symbol/ObjectFile.h b/lldb/include/lldb/Symbol/ObjectFile.h
index e814015c0bf7..080724cb86bd 100644
--- a/lldb/include/lldb/Symbol/ObjectFile.h
+++ b/lldb/include/lldb/Symbol/ObjectFile.h
@@ -91,6 +91,17 @@ class ObjectFile : public std::enable_shared_from_this<ObjectFile>,
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<uint8_t> Contents;
@@ -500,12 +511,17 @@ class ObjectFile : public std::enable_shared_from_this<ObjectFile>,
/// 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.cpp b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp
index babe5a384727..f9f27d0ee6c2 100644
--- a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp
+++ b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp
@@ -5519,7 +5519,8 @@ std::string ObjectFileMachO::GetIdentifierString() {
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 @@ bool ObjectFileMachO::GetCorefileMainBinaryInfo(addr_t &address, UUID &uuid) {
// "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/ObjectFile/Mach-O/ObjectFileMachO.h b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h
index 0c1d178b1921..2308c496c421 100644
--- a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h
+++ b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h
@@ -112,7 +112,9 @@ class ObjectFileMachO : public lldb_private::ObjectFile {
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/Process/mach-core/ProcessMachCore.cpp b/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp
index b78276d345e1..bee161bde96b 100644
--- a/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp
+++ b/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp
@@ -281,8 +281,9 @@ Status ProcessMachCore::DoLoadCore() {
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 @@ Status ProcessMachCore::DoLoadCore() {
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 @@ Status ProcessMachCore::DoLoadCore() {
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 @@ Status ProcessMachCore::DoLoadCore() {
}
}
- // 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
index 000000000000..faa5ef2f29f4
--- /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
index 000000000000..79a79056476b
--- /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 "<?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" -a "$1" != "%s" ]' % (aout_uuid, bout_uuid),
+ 'then',
+ ' echo "<key>DBGError</key><string>not found</string>"',
+ ' echo "</plist>"',
+ ' 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 "<dict><key>$uuid</key><dict>"',
+ '',
+ 'echo "<key>DBGArchitecture</key><string>x86_64</string>"',
+ 'echo "<key>DBGDSYMPath</key><string>$dsym</string>"',
+ 'echo "<key>DBGSymbolRichExecutable</key><string>$bin</string>"',
+ 'echo "</dict></dict></plist>"',
+ '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
index 000000000000..049b7b49aa46
--- /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
index 000000000000..5e02bad51d76
--- /dev/null
+++ b/lldb/test/API/macosx/lc-note/firmware-corefile/create-empty-corefile.cpp
@@ -0,0 +1,347 @@
+#include <mach-o/loader.h>
+#include <mach/thread_status.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <string>
+#include <uuid/uuid.h>
+#include <vector>
+
+// 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<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> x86_lc_thread_load_command() {
+ std::vector<uint8_t> 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<std::vector<uint8_t>> &loadcmds, std::vector<uint8_t> &payload,
+ int payload_file_offset, std::string uuid) {
+ std::string ident = "EFI UUID=";
+ ident += uuid;
+ 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, "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<std::vector<uint8_t>> &loadcmds, std::vector<uint8_t> &payload,
+ int payload_file_offset, std::string uuidstr) {
+ 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, 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<std::vector<uint8_t>> &loadcmds,
+ std::vector<uint8_t> &payload, int payload_file_offset) {
+ 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 = 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 "
+ "<output-core-name> <binary-to-copy-uuid-from>\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<std::vector<uint8_t>> load_commands;
+
+ // An array of corefile contents (page data, lc_note data, etc)
+ std::vector<uint8_t> 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
index 000000000000..70a72e0b80b1
--- /dev/null
+++ b/lldb/test/API/macosx/lc-note/firmware-corefile/main.c
@@ -0,0 +1,2 @@
+#include <stdio.h>
+int main () { puts ("this is the lc-note test program."); }
More information about the lldb-commits
mailing list