jasonmolenda created this revision.
jasonmolenda added reviewers: labath, JDevlieghere.
jasonmolenda added a project: LLDB.
Herald added a project: All.
jasonmolenda requested review of this revision.
Herald added a subscriber: lldb-commits.

I'm adding support for a firmware type environment, without any dynamic linker, 
where we may have more than one binary in memory and we'll only be given the 
load addresses of the binaries.

lldb already has support for loading a single binary given a UUID & address or 
slide.  It slowly grew to the point (my fault) where three different classes 
(ProcessGDBRemote, ProcessMachCore, ObjectFileMachO) had versions of code to 
find and load the correct binary at the correct address into the Target.  This 
would have been yet another variation added to all three places, so obviously 
the first thing to do was make a unified utility function to do this.  I chose 
a static method in the DynamicLoader base class, 
`LoadBinaryWithUUIDAndAddress()` which handles this new case, as well as most 
of the others.  I removed the other copies of code that were doing similar 
things from those three classes.

I thought of making this a static method in DynamicLoaderStatic.  I couldn't 
come up with a strong preference either way -- it's only a method you'll be 
calling when you don't have a DynamicLoader plugin, so maybe that is the best 
place for it.

I added a new key-value to qProcessInfo, binary-addresses, which is a base16 
array of addresses where binaries are.  When given no UUID, lldb will try to 
read the binary from memory and find the UUID in the in-memory load commands, 
then do the normal "find me a binary for this UUID" schemes, loading it into 
the Target.

lldb already has "main bin spec" and "load binary" LC_NOTEs that always needed 
a UUID until today; this code now handles the case where no UUID is supplied, 
but we have a load address.  I was a little unhappy to find that 
UUID::IsValid() returns true for an all-zeroes UUID which seems like it should 
probably return invalid?  The LC_NOTEs use all-zeroes to indicate "no uuid", 
whereas our UUID class uses "optional data exists".  I have a few checks in 
ObjectFileMachO where I check the uuid_t to see if it is all-zeros before I 
copy them in to the UUID object.

I tested the qProcessInfo load method manually with a hacked debugserver & lldb 
which had no DynamicLoaderMacOS plugin; anything less than that looks like a 
valid macOS debug session and it was difficult to fool lldb into using these 
correctly.

I wrote a fun test case for the corefile support where it builds three test 
binaries - an executable and two shared libraries - and a program that creates 
a corefile of those three binaries at their load addresses.  Then we can run 
lldb on the corefile and confirm it loads binaries already known to lldb, via 
dsymForUUID, or loads a memory image as a last resort.  It took a bit to get 
the corefile creator working correctly, but it's pretty cool.

One tiny oddity I noticed is that we've ended up with

  include/lldb/Target/DynamicLoader.h
  source/Core/DynamicLoader.cpp

I didn't look into which one was intended to be correct & fix it for now.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D130813

Files:
  lldb/docs/lldb-gdb-remote.txt
  lldb/include/lldb/Target/DynamicLoader.h
  lldb/source/Core/DynamicLoader.cpp
  lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp
  lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
  lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
  lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
  lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp
  lldb/test/API/macosx/lc-note/multiple-binary-corefile/Makefile
  
lldb/test/API/macosx/lc-note/multiple-binary-corefile/TestMultipleBinaryCorefile.py
  
lldb/test/API/macosx/lc-note/multiple-binary-corefile/create-multibin-corefile.cpp
  lldb/test/API/macosx/lc-note/multiple-binary-corefile/main.c
  lldb/test/API/macosx/lc-note/multiple-binary-corefile/one.c
  lldb/test/API/macosx/lc-note/multiple-binary-corefile/two.c

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

Reply via email to