jasonmolenda created this revision.
jasonmolenda added a reviewer: jingham.
jasonmolenda added a project: LLDB.
Herald added a subscriber: JDevlieghere.
Herald added a reviewer: JDevlieghere.
jasonmolenda requested review of this revision.

This patch is primarily to ProcessMachCore to correctly load a 
firmware/standalone main binary in a corefile which is using the "main bin 
spec" LC_NOTE.  There's already support for this for a "kern ver str" LC_NOTE 
that includes a UUID to load, this patch extends that to the other LC_NOTE and 
also sets the load address on the binary it locates to 0 (no-slide) as a 
default loading.  I also stopped an unnecessary scan over the corefile looking 
for user-process dyld or kernel binaries once I'd already gotten one of these 
two LC_NOTEs.

I also modified ObjectFile::GetCorefileMainBinaryInfo to pass up the type of 
binary that was specified, which was in the "main bin spec" but not returned 
previously.

The test case is based on the one I wrote earlier for 
test/API/macosx/lc-note/kern-ver-str.  It adds the ability to write both types 
of LC_NOTEs (kern ver str, main bin spec) and compiles two binaries (with 
random UUIDs).  It creates a dsym-for-uuid.sh that recognizes the two UUIDs and 
points to the correct binaries/dSYMs, creates corefiles using the tool it built 
earlier, and runs lldb against the corefiles to confirm that the binaries are 
loaded correctly.  It only runs on macOS x86_64 systems for now.

I'm not sure there's anyone who's really interested in reviewing this one; 
ProcessMachCore is my back yard in the codebase :) but I'm open to any comments 
or suggestions, naturally.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D88282

Files:
  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
  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

Index: lldb/test/API/macosx/lc-note/firmware-corefile/main.c
===================================================================
--- /dev/null
+++ 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."); }
Index: lldb/test/API/macosx/lc-note/firmware-corefile/create-empty-corefile.cpp
===================================================================
--- /dev/null
+++ lldb/test/API/macosx/lc-note/firmware-corefile/create-empty-corefile.cpp
@@ -0,0 +1,371 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <mach-o/loader.h>
+#include <vector>
+#include <string>
+#include <mach/thread_status.h>
+#include <string.h>
+#include <uuid/uuid.h>
+
+// 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);
+}
Index: lldb/test/API/macosx/lc-note/firmware-corefile/bout.mk
===================================================================
--- /dev/null
+++ 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
Index: lldb/test/API/macosx/lc-note/firmware-corefile/TestFirmwareCorefiles.py
===================================================================
--- /dev/null
+++ 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)
Index: lldb/test/API/macosx/lc-note/firmware-corefile/Makefile
===================================================================
--- /dev/null
+++ 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
Index: lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp
===================================================================
--- lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp
+++ 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();
+      }
     }
   }
 
Index: lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h
===================================================================
--- lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h
+++ 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;
Index: lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp
===================================================================
--- lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp
+++ 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,41 @@
         // "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));
+              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;
             }
           }
Index: lldb/include/lldb/Symbol/ObjectFile.h
===================================================================
--- lldb/include/lldb/Symbol/ObjectFile.h
+++ 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<uint8_t> 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
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to