jasonmolenda created this revision. jasonmolenda added a project: LLDB. Herald added a subscriber: JDevlieghere. jasonmolenda requested review of this revision.
For some of our non-userland / non-kernel environments, there can be multiple binary images involved in the environment, and each environment has a different way of discovering these binaries and would require a specialized DynamicLoader plugin in lldb to handle. This patch adds a new "load binary" LC_NOTE for corefiles which allows the corefile creator to list the binaries that lldb should attempt to load, and where to load them, for viewing the corefile. The actual patch to read this new LC_NOTE is a dozen lines thanks to the "all image infos" support I added earlier this year; the mechanisms are similar. "all image infos" always gave us the segment load addresses for each binary, but "load binaries" allows for load address or a slide to be specified. So I added support to handle those cases in ObjectFileMachO, and added logging to help debug any problems. I updated my TestCorefileDefaultPtrauth.py test from https://reviews.llvm.org/D115431 to add this new LC_NOTE to the corefile that it creates. Previously, TestCorefileDefaultPtrauth.py was manually loading the a.out binary and setting its load address; now the API test load the executable into a Target and deletes the Target, to get it registered in lldb's global module cache. Then we load the corefile and the binary is discovered by its UUID. No additional tests were needed to confirm the binary was loaded; the test looking at a global will fail if it is not. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D115494 Files: 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
Index: lldb/test/API/macosx/corefile-default-ptrauth/create-corefile.c =================================================================== --- lldb/test/API/macosx/corefile-default-ptrauth/create-corefile.c +++ 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 @@ } 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 @@ // 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 @@ 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 @@ 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 @@ memset (®state, 0, sizeof (regstate)); fwrite (®state, 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); Index: lldb/test/API/macosx/corefile-default-ptrauth/TestCorefileDefaultPtrauth.py =================================================================== --- lldb/test/API/macosx/corefile-default-ptrauth/TestCorefileDefaultPtrauth.py +++ lldb/test/API/macosx/corefile-default-ptrauth/TestCorefileDefaultPtrauth.py @@ -18,6 +18,7 @@ @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 @@ ## 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. 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 @@ -225,7 +225,8 @@ 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; }; 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 @@ -6973,6 +6973,23 @@ } 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 = (const char *)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; @@ -6984,6 +7001,7 @@ bool ObjectFileMachO::LoadCoreFileImages(lldb_private::Process &process) { MachOCorefileAllImageInfos image_infos = GetCorefileAllImageInfos(); bool added_images = false; + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER)); if (image_infos.IsValid()) { for (const MachOCorefileImageEntry &image : image_infos.all_image_infos) { ModuleSpec module_spec; @@ -7005,17 +7023,64 @@ } } if (module_sp.get()) { + process.GetTarget().GetImages().AppendIfNeeded(module_sp); added_images = true; - for (auto name_vmaddr_tuple : image.segment_load_addresses) { - SectionList *sectlist = module_sp->GetObjectFile()->GetSectionList(); - if (sectlist) { - SectionSP sect_sp = - sectlist->FindSectionByName(std::get<0>(name_vmaddr_tuple)); - if (sect_sp) { - process.GetTarget().SetSectionLoadAddress( - sect_sp, std::get<1>(name_vmaddr_tuple)); + 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) { + SectionSP sect_sp = + sectlist->FindSectionByName(std::get<0>(name_vmaddr_tuple)); + if (sect_sp) { + process.GetTarget().SetSectionLoadAddress( + sect_sp, std::get<1>(name_vmaddr_tuple)); + } } } + } 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 address 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 unslid", + 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); } } }
_______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits