[Lldb-commits] [lldb] f212032 - Add support for a "load binary" LC_NOTE in mach-o corefiles

Jason Molenda via lldb-commits lldb-commits at lists.llvm.org
Mon Dec 13 13:22:03 PST 2021


Author: Jason Molenda
Date: 2021-12-13T13:21:56-08:00
New Revision: f2120328e81879bf14d2a5c381749a11577fa304

URL: https://github.com/llvm/llvm-project/commit/f2120328e81879bf14d2a5c381749a11577fa304
DIFF: https://github.com/llvm/llvm-project/commit/f2120328e81879bf14d2a5c381749a11577fa304.diff

LOG: Add support for a "load binary" LC_NOTE in mach-o corefiles

Add lldb support for a Mach-O "load binary" LC_NOTE which provides
a UUID, load address/slide, and possibly a name of a binary that
should be loaded when examining the core.

struct load_binary
{
    uint32_t version;        // currently 1
    uuid_t   uuid;           // all zeroes if uuid not specified
    uint64_t load_address;   // virtual address where the macho is loaded, UINT64_MAX if unavail
    uint64_t slide;          // slide to be applied to file address to get load address, 0 if unavail
    char     name_cstring[]; // must be nul-byte terminated c-string, '\0' alone if name unavail
} __attribute__((packed));

Differential Revision: https://reviews.llvm.org/D115494
rdar://85069250

Added: 
    

Modified: 
    lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp
    lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h
    lldb/test/API/macosx/corefile-default-ptrauth/TestCorefileDefaultPtrauth.py
    lldb/test/API/macosx/corefile-default-ptrauth/create-corefile.c

Removed: 
    


################################################################################
diff  --git a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp
index 29cc399f7c2f1..e0087dbd4941e 100644
--- a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp
+++ b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp
@@ -6973,6 +6973,23 @@ ObjectFileMachO::GetCorefileAllImageInfos() {
           }
           image_infos.all_image_infos.push_back(image_entry);
         }
+      } else if (strcmp("load binary", data_owner) == 0) {
+        uint32_t version = m_data.GetU32(&fileoff);
+        if (version == 1) {
+          uuid_t uuid;
+          memcpy(&uuid, m_data.GetData(&fileoff, sizeof(uuid_t)),
+                 sizeof(uuid_t));
+          uint64_t load_address = m_data.GetU64(&fileoff);
+          uint64_t slide = m_data.GetU64(&fileoff);
+          std::string filename = m_data.GetCStr(&fileoff);
+
+          MachOCorefileImageEntry image_entry;
+          image_entry.filename = filename;
+          image_entry.uuid = UUID::fromData(uuid, sizeof(uuid_t));
+          image_entry.load_address = load_address;
+          image_entry.slide = slide;
+          image_infos.all_image_infos.push_back(image_entry);
+        }
       }
     }
     offset = cmd_offset + lc.cmdsize;
@@ -6983,29 +7000,42 @@ ObjectFileMachO::GetCorefileAllImageInfos() {
 
 bool ObjectFileMachO::LoadCoreFileImages(lldb_private::Process &process) {
   MachOCorefileAllImageInfos image_infos = GetCorefileAllImageInfos();
-  bool added_images = false;
-  if (image_infos.IsValid()) {
-    for (const MachOCorefileImageEntry &image : image_infos.all_image_infos) {
-      ModuleSpec module_spec;
-      module_spec.GetUUID() = image.uuid;
-      module_spec.GetFileSpec() = FileSpec(image.filename.c_str());
-      if (image.currently_executing) {
-        Symbols::DownloadObjectAndSymbolFile(module_spec, true);
-        if (FileSystem::Instance().Exists(module_spec.GetFileSpec())) {
-          process.GetTarget().GetOrCreateModule(module_spec, false);
-        }
+  Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
+
+  ModuleList added_modules;
+  for (const MachOCorefileImageEntry &image : image_infos.all_image_infos) {
+    ModuleSpec module_spec;
+    module_spec.GetUUID() = image.uuid;
+    module_spec.GetFileSpec() = FileSpec(image.filename.c_str());
+    if (image.currently_executing) {
+      Symbols::DownloadObjectAndSymbolFile(module_spec, 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()) {
-        if (image.load_address != LLDB_INVALID_ADDRESS) {
-          module_sp = process.ReadModuleFromMemory(module_spec.GetFileSpec(),
-                                                   image.load_address);
-        }
+    }
+    Status error;
+    ModuleSP module_sp =
+        process.GetTarget().GetOrCreateModule(module_spec, false, &error);
+    if (!module_sp.get() || !module_sp->GetObjectFile()) {
+      if (image.load_address != LLDB_INVALID_ADDRESS) {
+        module_sp = process.ReadModuleFromMemory(module_spec.GetFileSpec(),
+                                                 image.load_address);
       }
-      if (module_sp.get()) {
-        added_images = true;
+    }
+    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);
+      if (image.segment_load_addresses.size() > 0) {
+        if (log) {
+          std::string uuidstr = image.uuid.GetAsString();
+          log->Printf("ObjectFileMachO::LoadCoreFileImages adding binary '%s' "
+                      "UUID %s with section load addresses",
+                      image.filename.c_str(), uuidstr.c_str());
+        }
         for (auto name_vmaddr_tuple : image.segment_load_addresses) {
           SectionList *sectlist = module_sp->GetObjectFile()->GetSectionList();
           if (sectlist) {
@@ -7017,8 +7047,47 @@ bool ObjectFileMachO::LoadCoreFileImages(lldb_private::Process &process) {
             }
           }
         }
+      } else if (image.load_address != LLDB_INVALID_ADDRESS) {
+        if (log) {
+          std::string uuidstr = image.uuid.GetAsString();
+          log->Printf("ObjectFileMachO::LoadCoreFileImages adding binary '%s' "
+                      "UUID %s with load address 0x%" PRIx64,
+                      image.filename.c_str(), uuidstr.c_str(),
+                      image.load_address);
+        }
+        const bool address_is_slide = false;
+        bool changed = false;
+        module_sp->SetLoadAddress(process.GetTarget(), image.load_address,
+                                  address_is_slide, changed);
+      } else if (image.slide != 0) {
+        if (log) {
+          std::string uuidstr = image.uuid.GetAsString();
+          log->Printf("ObjectFileMachO::LoadCoreFileImages adding binary '%s' "
+                      "UUID %s with slide amount 0x%" PRIx64,
+                      image.filename.c_str(), uuidstr.c_str(), image.slide);
+        }
+        const bool address_is_slide = true;
+        bool changed = false;
+        module_sp->SetLoadAddress(process.GetTarget(), image.slide,
+                                  address_is_slide, changed);
+      } else {
+        if (log) {
+          std::string uuidstr = image.uuid.GetAsString();
+          log->Printf("ObjectFileMachO::LoadCoreFileImages adding binary '%s' "
+                      "UUID %s at its file address, no slide applied",
+                      image.filename.c_str(), uuidstr.c_str());
+        }
+        const bool address_is_slide = true;
+        bool changed = false;
+        module_sp->SetLoadAddress(process.GetTarget(), 0, address_is_slide,
+                                  changed);
       }
     }
   }
-  return added_images;
+  if (added_modules.GetSize() > 0) {
+    process.GetTarget().ModulesDidLoad(added_modules);
+    process.Flush();
+    return true;
+  }
+  return false;
 }

diff  --git a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h
index b3a941a957222..084b0ac110ba1 100644
--- a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h
+++ b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h
@@ -225,7 +225,8 @@ class ObjectFileMachO : public lldb_private::ObjectFile {
     std::string filename;
     lldb_private::UUID uuid;
     lldb::addr_t load_address = LLDB_INVALID_ADDRESS;
-    bool currently_executing;
+    lldb::addr_t slide = 0;
+    bool currently_executing = false;
     std::vector<std::tuple<lldb_private::ConstString, lldb::addr_t>>
         segment_load_addresses;
   };

diff  --git a/lldb/test/API/macosx/corefile-default-ptrauth/TestCorefileDefaultPtrauth.py b/lldb/test/API/macosx/corefile-default-ptrauth/TestCorefileDefaultPtrauth.py
index 7540763faa0f7..d3b086191ca52 100644
--- a/lldb/test/API/macosx/corefile-default-ptrauth/TestCorefileDefaultPtrauth.py
+++ b/lldb/test/API/macosx/corefile-default-ptrauth/TestCorefileDefaultPtrauth.py
@@ -18,6 +18,7 @@ class TestCorefileDefaultPtrauth(TestBase):
     @skipIf(debug_info=no_match(["dsym"]), bugnumber="This test is looking explicitly for a dSYM")
     @skipIf(archs=no_match(['arm64','arm64e']))
     @skipUnlessDarwin
+    @skipIfRemote
     def test_lc_note(self):
         self.build()
         self.test_exe = self.getBuildArtifact("a.out")
@@ -32,16 +33,18 @@ def test_lc_note(self):
         ## to fall back on its old default value for Darwin arm64 ABIs
         ## to correctly strip the bits.
 
+        # Create a Target with our main executable binary to get it
+        # seeded in lldb's global module cache.  Then delete the Target.
+        # This way when the corefile searches for a binary with its UUID,
+        # it'll be found by that search.
+        initial_target = self.dbg.CreateTarget(self.test_exe)
+        self.dbg.DeleteTarget(initial_target)
+
         self.target = self.dbg.CreateTarget('')
         err = lldb.SBError()
         self.process = self.target.LoadCore(self.corefile)
         self.assertEqual(self.process.IsValid(), True)
-        modspec = lldb.SBModuleSpec()
-        modspec.SetFileSpec(lldb.SBFileSpec(self.test_exe, True))
-        m = self.target.AddModule(modspec)
-        self.assertTrue(m.IsValid())
-        self.target.SetModuleLoadAddress (m, 0)
-        
+
         # target variable should show us both the actual function
         # pointer with ptrauth bits and the symbol it resolves to,
         # with the ptrauth bits stripped, e.g.

diff  --git a/lldb/test/API/macosx/corefile-default-ptrauth/create-corefile.c b/lldb/test/API/macosx/corefile-default-ptrauth/create-corefile.c
index c1052e1b18446..e5168089b87d2 100644
--- a/lldb/test/API/macosx/corefile-default-ptrauth/create-corefile.c
+++ b/lldb/test/API/macosx/corefile-default-ptrauth/create-corefile.c
@@ -6,6 +6,7 @@
 #include <mach/machine/thread_state.h>
 #include <inttypes.h>
 #include <sys/syslimits.h>
+#include <uuid/uuid.h>
 
 // Given an executable binary with 
 //   "fmain" (a function pointer to main)
@@ -55,6 +56,28 @@ int main(int argc, char **argv)
   }
   pclose (nm);
 
+  sprintf (buf, "dwarfdump -u '%s'", argv[1]);
+  FILE *dwarfdump = popen(buf, "r");
+  if (!dwarfdump) {
+    fprintf (stderr, "Unable to run dwarfdump -u on '%s'\n", argv[1]);
+    exit (1);
+  }
+  uuid_t uuid;
+  uuid_clear (uuid);
+  while (fgets (buf, sizeof(buf), dwarfdump)) {
+    if (strncmp (buf, "UUID: ", 6) == 0) {
+      buf[6 + 36] = '\0';
+      if (uuid_parse (buf + 6, uuid) != 0) {
+        fprintf (stderr, "Unable to parse UUID in '%s'\n", buf);
+        exit (1);
+      }
+    }
+  }
+  if (uuid_is_null(uuid)) {
+    fprintf (stderr, "Got a null uuid for the binary\n");
+    exit (1);
+  }
+
   if (main_addr == 0 || fmain_addr == 0) {
     fprintf(stderr, "Unable to find address of main or fmain in %s.\n",
         argv[1]);
@@ -65,7 +88,9 @@ int main(int argc, char **argv)
   //    1. mach header
   //    2. LC_THREAD load command
   //    3. LC_SEGMENT_64 load command
-  //    4. memory segment contents
+  //    4. LC_NOTE load command
+  //    5. memory segment contents
+  //    6. "load binary" note contents
 
   // struct thread_command {
   //       uint32_t        cmd;    
@@ -80,13 +105,14 @@ int main(int argc, char **argv)
   mh.cputype = CPU_TYPE_ARM64;
   mh.cpusubtype = CPU_SUBTYPE_ARM64E;
   mh.filetype = MH_CORE;
-  mh.ncmds = 2; // LC_THREAD, LC_SEGMENT_64
-  mh.sizeofcmds = size_of_thread_cmd + sizeof(struct segment_command_64);
+  mh.ncmds = 3; // LC_THREAD, LC_SEGMENT_64, LC_NOTE
+  mh.sizeofcmds = size_of_thread_cmd + sizeof(struct segment_command_64) + sizeof(struct note_command);
   mh.flags = 0;
   mh.reserved = 0;
 
   fwrite(&mh, sizeof (mh), 1, out);
 
+  struct note_command lcnote;
   struct segment_command_64 seg;
   seg.cmd = LC_SEGMENT_64;
   seg.cmdsize = sizeof(seg);
@@ -94,7 +120,7 @@ int main(int argc, char **argv)
   seg.vmaddr = fmain_addr;
   seg.vmsize = 8;
   // Offset to segment contents
-  seg.fileoff = sizeof (mh) + size_of_thread_cmd + sizeof(seg);
+  seg.fileoff = sizeof (mh) + size_of_thread_cmd + sizeof(seg) + sizeof(lcnote);
   seg.filesize = 8;
   seg.maxprot = 3;
   seg.initprot = 3;
@@ -116,15 +142,47 @@ int main(int argc, char **argv)
   memset (&regstate, 0, sizeof (regstate));
   fwrite (&regstate, sizeof (regstate), 1, out);
 
+  lcnote.cmd = LC_NOTE;
+  lcnote.cmdsize = sizeof (lcnote);
+  strcpy (lcnote.data_owner, "load binary");
+
+  // 8 is the size of the LC_SEGMENT contents
+  lcnote.offset = sizeof (mh) + size_of_thread_cmd + sizeof(seg) + sizeof(lcnote) + 8;
+
+  // struct load_binary
+  // {
+  // uint32_t version;        // currently 1
+  // uuid_t   uuid;           // all zeroes if uuid not specified
+  // uint64_t load_address;   // virtual address where the macho is loaded, UINT64_MAX if unavail
+  // uint64_t slide;          // slide to be applied to file address to get load address, 0 if unavail
+  // char     name_cstring[]; // must be nul-byte terminated c-string, '\0' alone if name unavail
+  // } __attribute__((packed));
+  lcnote.size = 4 + 16 + 8 + 8 + sizeof("a.out");
+
+  fwrite (&lcnote, sizeof(lcnote), 1, out);
+
+  // Write the contents of the memory segment
 
   // Or together a random PAC value from a system using 39 bits 
   // of addressing with the address of main().  lldb will need
   // to correctly strip off the high bits to find the address of
   // main.
   uint64_t segment_contents = 0xe46bff0000000000 | main_addr;
-
   fwrite (&segment_contents, sizeof (segment_contents), 1, out);
 
+  // Now write the contents of the "load binary" LC_NOTE.
+  {
+    uint32_t version = 1;
+    fwrite (&version, sizeof (version), 1, out);
+    fwrite (&uuid, sizeof (uuid), 1, out);
+    uint64_t load_address = UINT64_MAX;
+    fwrite (&load_address, sizeof (load_address), 1, out);
+    uint64_t slide = 0;
+    fwrite (&slide, sizeof (slide), 1, out);
+    strcpy (buf, "a.out");
+    fwrite (buf, 6, 1, out);
+  }
+
   fclose (out);
 
   exit (0);


        


More information about the lldb-commits mailing list