diff --git a/lldb/include/lldb/Symbol/ObjectFile.h b/lldb/include/lldb/Symbol/ObjectFile.h --- a/lldb/include/lldb/Symbol/ObjectFile.h +++ b/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; } diff --git a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h --- a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h +++ b/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; diff --git a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp --- a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp +++ b/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 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 + // // , 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())) { diff --git a/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp b/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp --- a/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp +++ b/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; } diff --git a/lldb/test/API/macosx/lc-note/firmware-corefile/TestFirmwareCorefiles.py b/lldb/test/API/macosx/lc-note/firmware-corefile/TestFirmwareCorefiles.py --- a/lldb/test/API/macosx/lc-note/firmware-corefile/TestFirmwareCorefiles.py +++ b/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 ""', - 'echo ""', - 'echo ""', - '', - 'if [ "$1" != "%s" ]' % (aout_uuid), - 'then', - ' echo "DBGErrornot found"', - ' echo ""', - ' 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 "$uuid"', - '', - 'echo "DBGDSYMPath$dsym"', - 'echo "DBGSymbolRichExecutable$bin"', - 'echo ""', - '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 ""', + 'echo ""', + 'echo ""', + '', + 'if [ "$1" != "%s" ]' % (aout_uuid), + 'then', + ' echo "DBGErrornot found"', + ' echo ""', + ' 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 "$uuid"', + '', + 'echo "DBGDSYMPath$dsym"', + 'echo "DBGSymbolRichExecutable$bin"', + 'echo ""', + '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) diff --git a/lldb/test/API/macosx/lc-note/firmware-corefile/create-empty-corefile.cpp b/lldb/test/API/macosx/lc-note/firmware-corefile/create-empty-corefile.cpp --- a/lldb/test/API/macosx/lc-note/firmware-corefile/create-empty-corefile.cpp +++ b/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 x86_lc_thread_load_command() { +std::vector lc_thread_load_command(cpu_type_t cputype) { std::vector 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> &loadcmds, std::vector &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 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> &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 " - "
\n"); + if (argc != 6) { + fprintf( + stderr, + "usage: create-empty-corefile version-string|main-bin-spec " + "
\n"); fprintf(stderr, "
is base 16, 0xffffffffffffffff means unknown\n"); + fprintf(stderr, + " 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> 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;