jasonmolenda created this revision. jasonmolenda added a project: LLDB. Herald added a subscriber: JDevlieghere. jasonmolenda requested review of this revision.
The Mach-O corefiles have a "main bin spec" LC_NOTE which allows the corefile creator to specify a binary's UUID, load address, and type (userland, kernel, frimware). This patch extends that structure with two new fields - an optional slide, and an optional platform (currently unused). We have a corefile creator that will only know the offset that a binary was loaded at, as well as its UUID, so lldb will need to find the original binary and add the offset to its file address to get the correct load address. I changed the ObjectFile::GetCorefileMainBinaryInfo method from taking a load address, to taking a value and a bool of whether the value is an address or an offset. This is based on Module::SetLoadAddress, and it seemed good to stick with that argument style. The majority of the patch is changing TestFirmwareCorefiles.py. I changed create-empty-corefile.cpp to work on Intel or Apple Silicon macs, have it generate version 2 of the 'main bin spec' LC_NOTE, both with an address and with a slide. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D116094 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/TestFirmwareCorefiles.py lldb/test/API/macosx/lc-note/firmware-corefile/create-empty-corefile.cpp
Index: lldb/test/API/macosx/lc-note/firmware-corefile/create-empty-corefile.cpp =================================================================== --- lldb/test/API/macosx/lc-note/firmware-corefile/create-empty-corefile.cpp +++ lldb/test/API/macosx/lc-note/firmware-corefile/create-empty-corefile.cpp @@ -20,9 +20,10 @@ uint32_t version; uint32_t type; uint64_t address; + uint64_t slide; uuid_t uuid; uint32_t log2_pagesize; - uint32_t unused; + uint32_t platform; }; union uint32_buf { @@ -49,33 +50,36 @@ buf.push_back(conv.bytebuf[i]); } -std::vector<uint8_t> x86_lc_thread_load_command() { +std::vector<uint8_t> lc_thread_load_command(cpu_type_t cputype) { 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 + // 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; } @@ -121,7 +125,8 @@ 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) { + 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 @@ -145,15 +150,16 @@ loadcmds.push_back(loadcmd_data); // Now write the "main bin spec" payload. - add_uint32(payload, 1); // version + 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); // unused + add_uint32(payload, 0); // platform unspecified } void add_lc_segment(std::vector<std::vector<uint8_t>> &loadcmds, @@ -179,7 +185,8 @@ loadcmds.push_back(loadcmd_data); } -std::string get_uuid_from_binary(const char *fn) { +std::string get_uuid_from_binary(const char *fn, 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); @@ -213,9 +220,9 @@ fprintf(stderr, "error reading mach header from input file\n"); exit(1); } - if (mh.cputype != CPU_TYPE_X86_64) { + if (mh.cputype != CPU_TYPE_X86_64 && mh.cputype != CPU_TYPE_ARM64) { fprintf(stderr, - "This tool creates an x86_64 corefile but " + "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); @@ -223,15 +230,17 @@ 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) { + if (mh.cputype != CPU_TYPE_X86_64 && mh.cputype != CPU_TYPE_ARM64) { fprintf(stderr, - "This tool creates an x86_64 corefile but " + "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); @@ -239,6 +248,8 @@ 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; @@ -269,12 +280,15 @@ } int main(int argc, char **argv) { - if (argc != 5) { - fprintf(stderr, - "usage: create-empty-corefile version-string|main-bin-spec " - "<output-core-name> <binary-to-copy-uuid-from> <address>\n"); + if (argc != 6) { + fprintf( + stderr, + "usage: create-empty-corefile version-string|main-bin-spec " + "<output-core-name> <binary-to-copy-uuid-from> <address> <slide>\n"); fprintf(stderr, " <address> is base 16, 0xffffffffffffffff means unknown\n"); + fprintf(stderr, + " <slide> is base 16, 0xffffffffffffffff means unknown\n"); fprintf( stderr, "Create a Mach-O corefile with an either LC_NOTE 'kern ver str' or \n"); @@ -289,7 +303,9 @@ exit(1); } - std::string uuid = get_uuid_from_binary(argv[3]); + cpu_type_t cputype; + cpu_subtype_t cpusubtype; + std::string uuid = get_uuid_from_binary(argv[3], cputype, cpusubtype); // An array of load commands (in the form of byte arrays) std::vector<std::vector<uint8_t>> load_commands; @@ -304,15 +320,22 @@ exit(1); } + errno = 0; + uint64_t slide = strtoull(argv[5], NULL, 16); + if (errno != 0) { + fprintf(stderr, "Unable to parse slide %s as base 16", argv[4]); + exit(1); + } + // 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()); + load_commands.push_back(lc_thread_load_command(cputype)); if (strcmp(argv[1], "version-string") == 0) add_lc_note_kern_ver_str_load_command(load_commands, payload, 0, uuid, address); else add_lc_note_main_bin_spec_load_command(load_commands, payload, 0, uuid, - address); + address, slide); add_lc_segment(load_commands, payload, 0); int size_of_load_commands = 0; @@ -327,22 +350,22 @@ load_commands.clear(); payload.clear(); - load_commands.push_back(x86_lc_thread_load_command()); + load_commands.push_back(lc_thread_load_command(cputype)); if (strcmp(argv[1], "version-string") == 0) add_lc_note_kern_ver_str_load_command( load_commands, payload, header_and_load_cmd_room, uuid, address); else add_lc_note_main_bin_spec_load_command( - load_commands, payload, header_and_load_cmd_room, uuid, address); + load_commands, payload, header_and_load_cmd_room, uuid, address, slide); 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.cputype = cputype; - mh.cpusubtype = CPU_SUBTYPE_X86_64_ALL; + mh.cpusubtype = cpusubtype; mh.filetype = MH_CORE; mh.ncmds = load_commands.size(); mh.sizeofcmds = size_of_load_commands; Index: lldb/test/API/macosx/lc-note/firmware-corefile/TestFirmwareCorefiles.py =================================================================== --- lldb/test/API/macosx/lc-note/firmware-corefile/TestFirmwareCorefiles.py +++ lldb/test/API/macosx/lc-note/firmware-corefile/TestFirmwareCorefiles.py @@ -18,93 +18,28 @@ def initial_setup(self): self.build() - self.aout_exe = self.getBuildArtifact("a.out") + aout_exe = self.getBuildArtifact("a.out") self.aout_exe_basename = "a.out" - self.create_corefile = self.getBuildArtifact("create-empty-corefile") - self.dsym_for_uuid = self.getBuildArtifact("dsym-for-uuid.sh") + create_corefile = self.getBuildArtifact("create-empty-corefile") self.verstr_corefile = self.getBuildArtifact("verstr.core") self.verstr_corefile_addr = self.getBuildArtifact("verstr-addr.core") self.binspec_corefile = self.getBuildArtifact("binspec.core") self.binspec_corefile_addr = self.getBuildArtifact("binspec-addr.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)) - - 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")) - - dsym_python_dir = '%s.dSYM/Contents/Resources/Python' % (self.aout_exe) - os.makedirs(dsym_python_dir) - python_os_plugin_path = os.path.join(self.getSourceDir(), - 'operating_system.py') - python_init = [ - 'def __lldb_init_module(debugger, internal_dict):', - ' debugger.HandleCommand(\'settings set target.process.python-os-plugin-path %s\')' % python_os_plugin_path, - ] - with open(dsym_python_dir + "/a_out.py", "w") as writer: - for l in python_init: - writer.write(l + '\n') - - 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") - - ### Create our dsym-for-uuid shell script which returns self.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" ]' % (aout_uuid), - 'then', - ' echo "<key>DBGError</key><string>not found</string>"', - ' echo "</plist>"', - ' exit 1', - 'fi', - ' 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)), - '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(self.dsym_for_uuid, "w") as writer: - for l in shell_cmds: - writer.write(l + '\n') - - os.chmod(self.dsym_for_uuid, 0o755) + self.binspec_corefile_slideonly = self.getBuildArtifact("binspec-addr-slideonly.core") self.slide = 0x70000000000 ### Create our corefile # 0xffffffffffffffff means load address unknown - retcode = call(self.create_corefile + " version-string " + self.verstr_corefile + " " + self.aout_exe + " 0xffffffffffffffff", shell=True) - retcode = call(self.create_corefile + " version-string " + self.verstr_corefile_addr + " " + self.aout_exe + (" 0x%x" % self.slide), shell=True) - retcode = call(self.create_corefile + " main-bin-spec " + self.binspec_corefile + " " + self.aout_exe + " 0xffffffffffffffff", shell=True) - retcode = call(self.create_corefile + " main-bin-spec " + self.binspec_corefile_addr + " " + self.aout_exe + (" 0x%x" % self.slide), shell=True) + retcode = call(create_corefile + " version-string " + self.verstr_corefile + " " + aout_exe + " 0xffffffffffffffff 0xffffffffffffffff", shell=True) + retcode = call(create_corefile + " version-string " + self.verstr_corefile_addr + " " + aout_exe + (" 0x%x" % self.slide) + " 0xffffffffffffffff", shell=True) + retcode = call(create_corefile + " main-bin-spec " + self.binspec_corefile + " " + aout_exe + " 0xffffffffffffffff 0xffffffffffffffff", shell=True) + retcode = call(create_corefile + " main-bin-spec " + self.binspec_corefile_addr + " " + aout_exe + (" 0x%x" % self.slide) + " 0xffffffffffffffff", shell=True) + retcode = call(create_corefile + " main-bin-spec " + self.binspec_corefile_slideonly + " " + aout_exe + " 0xffffffffffffffff" + (" 0x%x" % self.slide), shell=True) @skipIf(debug_info=no_match(["dsym"]), bugnumber="This test is looking explicitly for a dSYM") - @skipIf(archs=no_match(['x86_64'])) + @skipIf(archs=no_match(['x86_64', 'arm64', 'arm64e', 'aarch64'])) + @skipIfRemote @skipUnlessDarwin def test_lc_note_version_string(self): self.initial_setup() @@ -113,56 +48,48 @@ self.runCmd("log enable lldb dyld host") self.addTearDownHook(lambda: self.runCmd("log disable lldb dyld host")) - ### 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! + # Register the a.out binary with this UUID in lldb's global module + # cache, then throw the Target away. + target = self.dbg.CreateTarget(self.getBuildArtifact("a.out")) + self.dbg.DeleteTarget(target) # First, try the "kern ver str" corefile - self.target = self.dbg.CreateTarget('') + target = self.dbg.CreateTarget('') err = lldb.SBError() if self.TraceOn(): self.runCmd("script print('loading corefile %s')" % self.verstr_corefile) - self.process = self.target.LoadCore(self.verstr_corefile) + self.process = target.LoadCore(self.verstr_corefile) self.assertEqual(self.process.IsValid(), True) if self.TraceOn(): self.runCmd("image list") self.runCmd("target mod dump sections") - self.assertEqual(self.target.GetNumModules(), 1) - fspec = self.target.GetModuleAtIndex(0).GetFileSpec() + self.assertEqual(target.GetNumModules(), 1) + fspec = target.GetModuleAtIndex(0).GetFileSpec() self.assertEqual(fspec.GetFilename(), self.aout_exe_basename) - self.process.Kill() - self.process = None - self.target.Clear() - self.target = None - self.dbg.MemoryPressureDetected() + self.dbg.DeleteTarget(target) # Second, try the "kern ver str" corefile where it loads at an address - self.target = self.dbg.CreateTarget('') + target = self.dbg.CreateTarget('') err = lldb.SBError() if self.TraceOn(): self.runCmd("script print('loading corefile %s')" % self.verstr_corefile_addr) - self.process = self.target.LoadCore(self.verstr_corefile_addr) + self.process = target.LoadCore(self.verstr_corefile_addr) self.assertEqual(self.process.IsValid(), True) if self.TraceOn(): self.runCmd("image list") self.runCmd("target mod dump sections") - self.assertEqual(self.target.GetNumModules(), 1) - fspec = self.target.GetModuleAtIndex(0).GetFileSpec() + self.assertEqual(target.GetNumModules(), 1) + fspec = target.GetModuleAtIndex(0).GetFileSpec() self.assertEqual(fspec.GetFilename(), self.aout_exe_basename) - main_sym = self.target.GetModuleAtIndex(0).FindSymbol("main", lldb.eSymbolTypeAny) + main_sym = target.GetModuleAtIndex(0).FindSymbol("main", lldb.eSymbolTypeAny) main_addr = main_sym.GetStartAddress() - self.assertGreater(main_addr.GetLoadAddress(self.target), self.slide) - self.assertNotEqual(main_addr.GetLoadAddress(self.target), lldb.LLDB_INVALID_ADDRESS) - self.process.Kill() - self.process = None - self.target.Clear() - self.target = None - self.dbg.MemoryPressureDetected() + self.assertGreater(main_addr.GetLoadAddress(target), self.slide) + self.assertNotEqual(main_addr.GetLoadAddress(target), lldb.LLDB_INVALID_ADDRESS) + self.dbg.DeleteTarget(target) @skipIf(debug_info=no_match(["dsym"]), bugnumber="This test is looking explicitly for a dSYM") - @skipIf(archs=no_match(['x86_64'])) + @skipIf(archs=no_match(['x86_64', 'arm64', 'arm64e', 'aarch64'])) + @skipIfRemote @skipUnlessDarwin def test_lc_note_main_bin_spec(self): self.initial_setup() @@ -171,52 +98,146 @@ self.runCmd("log enable lldb dyld host") self.addTearDownHook(lambda: self.runCmd("log disable lldb dyld host")) + + # Register the a.out binary with this UUID in lldb's global module + # cache, then throw the Target away. + target = self.dbg.CreateTarget(self.getBuildArtifact("a.out")) + self.dbg.DeleteTarget(target) + # Third, try the "main bin spec" corefile - self.target = self.dbg.CreateTarget('') + target = self.dbg.CreateTarget('') if self.TraceOn(): self.runCmd("script print('loading corefile %s')" % self.binspec_corefile) - self.process = self.target.LoadCore(self.binspec_corefile) + self.process = target.LoadCore(self.binspec_corefile) self.assertEqual(self.process.IsValid(), True) if self.TraceOn(): self.runCmd("image list") self.runCmd("target mod dump sections") - self.assertEqual(self.target.GetNumModules(), 1) - fspec = self.target.GetModuleAtIndex(0).GetFileSpec() + self.assertEqual(target.GetNumModules(), 1) + fspec = target.GetModuleAtIndex(0).GetFileSpec() self.assertEqual(fspec.GetFilename(), self.aout_exe_basename) - self.process.Kill() - self.process = None - self.target.Clear() - self.target = None - self.dbg.MemoryPressureDetected() + self.dbg.DeleteTarget(target) # Fourth, try the "main bin spec" corefile where it loads at an address - self.target = self.dbg.CreateTarget('') + target = self.dbg.CreateTarget('') if self.TraceOn(): self.runCmd("script print('loading corefile %s')" % self.binspec_corefile_addr) - self.process = self.target.LoadCore(self.binspec_corefile_addr) + self.process = target.LoadCore(self.binspec_corefile_addr) self.assertEqual(self.process.IsValid(), True) if self.TraceOn(): self.runCmd("image list") self.runCmd("target mod dump sections") - self.assertEqual(self.target.GetNumModules(), 1) - fspec = self.target.GetModuleAtIndex(0).GetFileSpec() + self.assertEqual(target.GetNumModules(), 1) + fspec = target.GetModuleAtIndex(0).GetFileSpec() + self.assertEqual(fspec.GetFilename(), self.aout_exe_basename) + main_sym = target.GetModuleAtIndex(0).FindSymbol("main", lldb.eSymbolTypeAny) + main_addr = main_sym.GetStartAddress() + self.assertGreater(main_addr.GetLoadAddress(target), self.slide) + self.assertNotEqual(main_addr.GetLoadAddress(target), lldb.LLDB_INVALID_ADDRESS) + self.dbg.DeleteTarget(target) + + # Fifth, try the "main bin spec" corefile where it loads at a slide + target = self.dbg.CreateTarget('') + if self.TraceOn(): + self.runCmd("script print('loading corefile %s')" % self.binspec_corefile_slideonly) + self.process = target.LoadCore(self.binspec_corefile_slideonly) + self.assertEqual(self.process.IsValid(), True) + if self.TraceOn(): + self.runCmd("image list") + self.runCmd("target mod dump sections") + self.assertEqual(target.GetNumModules(), 1) + fspec = target.GetModuleAtIndex(0).GetFileSpec() self.assertEqual(fspec.GetFilename(), self.aout_exe_basename) - main_sym = self.target.GetModuleAtIndex(0).FindSymbol("main", lldb.eSymbolTypeAny) + main_sym = target.GetModuleAtIndex(0).FindSymbol("main", lldb.eSymbolTypeAny) main_addr = main_sym.GetStartAddress() - self.assertGreater(main_addr.GetLoadAddress(self.target), self.slide) - self.assertNotEqual(main_addr.GetLoadAddress(self.target), lldb.LLDB_INVALID_ADDRESS) - self.process.Kill() - self.process = None - self.target.Clear() - self.target = None - self.dbg.MemoryPressureDetected() + self.assertGreater(main_addr.GetLoadAddress(target), self.slide) + self.assertNotEqual(main_addr.GetLoadAddress(target), lldb.LLDB_INVALID_ADDRESS) + self.dbg.DeleteTarget(target) @skipIf(debug_info=no_match(["dsym"]), bugnumber="This test is looking explicitly for a dSYM") - @skipIf(archs=no_match(['x86_64'])) + @skipIf(archs=no_match(['x86_64', 'arm64', 'arm64e', 'aarch64'])) + @skipIfRemote @skipUnlessDarwin def test_lc_note_main_bin_spec_os_plugin(self): self.initial_setup() + ## We can hook in our dsym-for-uuid shell script to lldb with this env + ## var instead of requiring a defaults write. + aout_exe = self.getBuildArtifact("a.out") + 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")) + + dsym_python_dir = '%s.dSYM/Contents/Resources/Python' % (aout_exe) + os.makedirs(dsym_python_dir) + python_os_plugin_path = os.path.join(self.getSourceDir(), + 'operating_system.py') + python_init = [ + 'def __lldb_init_module(debugger, internal_dict):', + ' debugger.HandleCommand(\'settings set target.process.python-os-plugin-path %s\')' % python_os_plugin_path, + ] + with open(dsym_python_dir + "/a_out.py", "w") as writer: + for l in python_init: + writer.write(l + '\n') + + dwarfdump_uuid_regex = re.compile( + 'UUID: ([-0-9a-fA-F]+) \(([^\(]+)\) .*') + dwarfdump_cmd_output = subprocess.check_output( + ('/usr/bin/dwarfdump --uuid "%s"' % 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") + + ### 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" ]' % (aout_uuid), + 'then', + ' echo "<key>DBGError</key><string>not found</string>"', + ' echo "</plist>"', + ' exit 1', + 'fi', + ' uuid=%s' % aout_uuid, + ' bin=%s' % aout_exe, + ' dsym=%s.dSYM/Contents/Resources/DWARF/%s' % (aout_exe, os.path.basename(aout_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) + + ### 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! + if self.TraceOn(): self.runCmd("log enable lldb dyld host") self.addTearDownHook(lambda: self.runCmd("log disable lldb dyld host")) @@ -225,6 +246,7 @@ if self.TraceOn(): self.runCmd("script print('loading corefile %s with OS plugin')" % self.binspec_corefile_addr) + self.process = self.target.LoadCore(self.binspec_corefile_addr) self.assertEqual(self.process.IsValid(), True) if self.TraceOn(): @@ -246,8 +268,5 @@ self.assertTrue(thread.IsValid(), "Make sure there is a thread 0x333333333 after we load the python OS plug-in") - self.process.Kill() - self.process = None - self.target.Clear() - self.target = None - self.dbg.MemoryPressureDetected() + self.runCmd("settings clear target.process.python-os-plugin-path") + self.dbg.DeleteTarget(self.target) 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 @@ -184,7 +184,8 @@ // 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 addr, Target &target) { +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; @@ -205,18 +206,35 @@ } } - if (module_sp.get() && module_sp->GetObjectFile()) { + // 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]; + sprintf(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); - Address base_addr = module_sp->GetObjectFile()->GetBaseAddress(); - addr_t slide = 0; - if (addr != LLDB_INVALID_ADDRESS && base_addr.IsValid()) { - addr_t file_load_addr = base_addr.GetFileAddress(); - slide = addr - file_load_addr; + if (module_sp->GetObjectFile()) { + if (value != LLDB_INVALID_ADDRESS) { + bool changed = false; + module_sp->SetLoadAddress(target, value, value_is_offset, changed); + } else { + // No address/offset/slide, load the binary at file address + const bool value_is_slide = true; + bool changed = false; + module_sp->SetLoadAddress(target, 0, value_is_slide, changed); + } + } else { + // In-memory image + const bool value_is_slide = true; + bool changed = false; + module_sp->SetLoadAddress(target, 0, value_is_slide, changed); } - bool changed = false; - module_sp->SetLoadAddress(target, slide, true, changed); ModuleList added_module; added_module.Append(module_sp, false); @@ -316,33 +334,38 @@ bool found_main_binary_definitively = false; - addr_t objfile_binary_addr; + addr_t objfile_binary_value; + bool objfile_binary_value_is_offset; UUID objfile_binary_uuid; ObjectFile::BinaryType type; - if (core_objfile->GetCorefileMainBinaryInfo(objfile_binary_addr, + if (core_objfile->GetCorefileMainBinaryInfo(objfile_binary_value, + objfile_binary_value_is_offset, objfile_binary_uuid, type)) { if (log) { log->Printf( "ProcessMachCore::DoLoadCore: using binary hint from 'main bin spec' " - "LC_NOTE with UUID %s address 0x%" PRIx64 " and type %d", - objfile_binary_uuid.GetAsString().c_str(), objfile_binary_addr, type); + "LC_NOTE with UUID %s value 0x%" PRIx64 + " value is offset %d and type %d", + objfile_binary_uuid.GetAsString().c_str(), objfile_binary_value, + objfile_binary_value_is_offset, type); } - if (objfile_binary_addr != LLDB_INVALID_ADDRESS) { + if (objfile_binary_value != LLDB_INVALID_ADDRESS && + !objfile_binary_value_is_offset) { if (type == ObjectFile::eBinaryTypeUser) { - m_dyld_addr = objfile_binary_addr; + 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_addr; + m_mach_kernel_addr = objfile_binary_value; m_dyld_plugin_name = DynamicLoaderDarwinKernel::GetPluginNameStatic(); found_main_binary_definitively = true; } } if (!found_main_binary_definitively) { // ObjectFile::eBinaryTypeStandalone, undeclared types - if (load_standalone_binary(objfile_binary_uuid, objfile_binary_addr, - GetTarget())) { + 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(); } @@ -388,17 +411,21 @@ m_mach_kernel_addr = ident_binary_addr; found_main_binary_definitively = true; } else if (ident_uuid.IsValid()) { - if (load_standalone_binary(ident_uuid, ident_binary_addr, GetTarget())) { + // 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())) { found_main_binary_definitively = true; m_dyld_plugin_name = DynamicLoaderStatic::GetPluginNameStatic(); } } } + bool did_load_extra_binaries = core_objfile->LoadCoreFileImages(*this); // If we have a "all image infos" LC_NOTE, try to load all of the // binaries listed, and set their Section load addresses in the Target. - if (found_main_binary_definitively == false && - core_objfile->LoadCoreFileImages(*this)) { + if (found_main_binary_definitively == false && did_load_extra_binaries) { m_dyld_plugin_name = DynamicLoaderDarwinKernel::GetPluginNameStatic(); found_main_binary_definitively = true; } 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 @@ -120,7 +120,7 @@ lldb::addr_t GetAddressMask() override; - bool GetCorefileMainBinaryInfo(lldb::addr_t &address, + bool GetCorefileMainBinaryInfo(lldb::addr_t &value, bool &value_is_offset, lldb_private::UUID &uuid, ObjectFile::BinaryType &type) 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 @@ -5620,10 +5620,15 @@ return mask; } -bool ObjectFileMachO::GetCorefileMainBinaryInfo(addr_t &address, UUID &uuid, +bool ObjectFileMachO::GetCorefileMainBinaryInfo(addr_t &value, + bool &value_is_offset, + UUID &uuid, ObjectFile::BinaryType &type) { - address = LLDB_INVALID_ADDRESS; + value = LLDB_INVALID_ADDRESS; + value_is_offset = false; uuid.Clear(); + uint32_t log2_pagesize = 0; // not currently passed up to caller + uint32_t platform = 0; // not currently passed up to caller ModuleSP module_sp(GetModule()); if (module_sp) { std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex()); @@ -5641,6 +5646,26 @@ uint64_t fileoff = m_data.GetU64_unchecked(&offset); uint64_t size = m_data.GetU64_unchecked(&offset); + // struct main_bin_spec + // { + // uint32_t version; // currently 2 + // uint32_t type; // 0 == unspecified, 1 == kernel, + // // 2 == user process, + // // 3 == standalone binary + // uint64_t address; // UINT64_MAX if address not specified + // uint64_t slide; // slide, UINT64_MAX if unspecified + // // 0 if no slide needs to be applied to + // // file address + // 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 platform; // The Mach-O platform for this corefile. + // // 0 for unspecified. + // // The values are defined in + // // <mach-o/loader.h>, PLATFORM_*. + // } __attribute((packed)); + // "main bin spec" (main binary specification) data payload is // formatted: // uint32_t version [currently 1] @@ -5656,14 +5681,25 @@ 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) { + if (m_data.GetU32(&offset, &version, 1) != nullptr && version <= 2) { uint32_t binspec_type = 0; uuid_t raw_uuid; memset(raw_uuid, 0, sizeof(uuid_t)); - if (m_data.GetU32(&offset, &binspec_type, 1) && - m_data.GetU64(&offset, &address, 1) && - m_data.CopyData(offset, sizeof(uuid_t), raw_uuid) != 0) { + if (!m_data.GetU32(&offset, &binspec_type, 1)) + return false; + if (!m_data.GetU64(&offset, &value, 1)) + return false; + uint64_t slide = LLDB_INVALID_ADDRESS; + if (version > 1 && !m_data.GetU64(&offset, &slide, 1)) + return false; + if (value == LLDB_INVALID_ADDRESS && + slide != LLDB_INVALID_ADDRESS) { + value = slide; + value_is_offset = true; + } + + if (m_data.CopyData(offset, sizeof(uuid_t), raw_uuid) != 0) { uuid = UUID::fromOptionalData(raw_uuid, sizeof(uuid_t)); // convert the "main bin spec" type into our // ObjectFile::BinaryType enum @@ -5681,6 +5717,10 @@ type = eBinaryTypeStandalone; break; } + if (!m_data.GetU32(&offset, &log2_pagesize, 1)) + return false; + if (version > 1 && !m_data.GetU32(&offset, &platform, 1)) + return false; return true; } } @@ -7006,7 +7046,16 @@ 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.filename.empty()) { + char namebuf[80]; + if (image.load_address != LLDB_INVALID_ADDRESS) + sprintf(namebuf, "mem-image-0x%" PRIx64, image.load_address); + else + sprintf(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) { Symbols::DownloadObjectAndSymbolFile(module_spec, true); if (FileSystem::Instance().Exists(module_spec.GetFileSpec())) { Index: lldb/include/lldb/Symbol/ObjectFile.h =================================================================== --- lldb/include/lldb/Symbol/ObjectFile.h +++ lldb/include/lldb/Symbol/ObjectFile.h @@ -527,11 +527,15 @@ /// binary is exactly which removes ambiguity when there are multiple /// binaries present in the captured memory pages. /// - /// \param[out] address - /// If the address of the binary is specified, this will be set. - /// This is an address is the virtual address space of the core file - /// memory segments; it is not an offset into the object file. - /// If no address is available, will be set to LLDB_INVALID_ADDRESS. + /// \param[out] value + /// The address or offset (slide) where the binary is loaded in memory. + /// LLDB_INVALID_ADDRESS for unspecified. If an offset is given, + /// this offset should be added to the binary's file address to get + /// the load address. + /// + /// \param[out] value_is_offset + /// Specifies if \b value is a load address, or an offset to calculate + /// the load address. /// /// \param[out] uuid /// If the uuid of the binary is specified, this will be set. @@ -543,9 +547,11 @@ /// /// \return /// Returns true if either address or uuid has been set. - virtual bool GetCorefileMainBinaryInfo(lldb::addr_t &address, UUID &uuid, + virtual bool GetCorefileMainBinaryInfo(lldb::addr_t &value, + bool &value_is_offset, UUID &uuid, ObjectFile::BinaryType &type) { - address = LLDB_INVALID_ADDRESS; + value = LLDB_INVALID_ADDRESS; + value_is_offset = false; uuid.Clear(); return false; }
_______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits