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 @@ -495,6 +495,16 @@ return std::string(); } + /// Some object files may have the number of bits used for addressing + /// embedded in them, e.g. a Mach-O core file using an LC_NOTE. These + /// object files can return the address mask that should be used in + /// the Process. + /// \return + /// The mask will have bits set which aren't used for addressing -- + /// typically, the high bits. + /// Zero is returned when no address bits mask is available. + virtual lldb::addr_t GetAddressMask() { return 0; } + /// When the ObjectFile is a core file, lldb needs to locate the "binary" in /// the core file. lldb can iterate over the pages looking for a valid /// binary, but some core files may have metadata describing where the main diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h --- a/lldb/include/lldb/Target/Process.h +++ b/lldb/include/lldb/Target/Process.h @@ -2899,7 +2899,8 @@ std::atomic m_finalizing; /// Mask for code an data addresses. The default value (0) means no mask is - /// set. + /// set. The bits set to 1 indicate bits that are NOT significant for + /// addressing. /// @{ lldb::addr_t m_code_address_mask = 0; lldb::addr_t m_data_address_mask = 0; diff --git a/lldb/source/Commands/CommandObjectProcess.cpp b/lldb/source/Commands/CommandObjectProcess.cpp --- a/lldb/source/Commands/CommandObjectProcess.cpp +++ b/lldb/source/Commands/CommandObjectProcess.cpp @@ -29,6 +29,8 @@ #include "lldb/Utility/Args.h" #include "lldb/Utility/State.h" +#include + using namespace lldb; using namespace lldb_private; @@ -1341,6 +1343,18 @@ num_frames, num_frames_with_source, stop_format); if (m_options.m_verbose) { + addr_t code_mask = process->GetCodeAddressMask(); + addr_t data_mask = process->GetDataAddressMask(); + if (code_mask != 0) { + int bits = std::bitset<64>(~code_mask).count(); + result.AppendMessageWithFormat( + "Addressable code address mask: 0x%" PRIx64 "\n", code_mask); + result.AppendMessageWithFormat( + "Addressable data address mask: 0x%" PRIx64 "\n", data_mask); + result.AppendMessageWithFormat( + "Number of bits used in addressing (code): %d\n", bits); + } + PlatformSP platform_sp = process->GetTarget().GetPlatform(); if (!platform_sp) { result.AppendError("Couldn'retrieve the target's platform"); 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 @@ -15,6 +15,7 @@ #include "lldb/Symbol/ObjectFile.h" #include "lldb/Utility/FileSpec.h" #include "lldb/Utility/RangeMap.h" +#include "lldb/Utility/StreamString.h" #include "lldb/Utility/UUID.h" // This class needs to be hidden as eventually belongs in a plugin that @@ -113,6 +114,8 @@ std::string GetIdentifierString() override; + lldb::addr_t GetAddressMask() override; + bool GetCorefileMainBinaryInfo(lldb::addr_t &address, lldb_private::UUID &uuid, ObjectFile::BinaryType &type) override; @@ -225,6 +228,15 @@ segment_load_addresses; }; + struct LCNoteEntry { + LCNoteEntry(uint32_t addr_byte_size, lldb::ByteOrder byte_order) + : payload(lldb_private::Stream::eBinary, addr_byte_size, byte_order) {} + + std::string name; + lldb::addr_t payload_file_offset = 0; + lldb_private::StreamString payload; + }; + struct MachOCorefileAllImageInfos { std::vector all_image_infos; bool IsValid() { return all_image_infos.size() > 0; } 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 @@ -62,6 +62,7 @@ #include #endif +#include #include #if LLVM_SUPPORT_XCODE_SIGNPOSTS @@ -5571,6 +5572,46 @@ return result; } +addr_t ObjectFileMachO::GetAddressMask() { + addr_t mask = 0; + ModuleSP module_sp(GetModule()); + if (module_sp) { + std::lock_guard guard(module_sp->GetMutex()); + lldb::offset_t offset = MachHeaderSizeFromMagic(m_header.magic); + for (uint32_t i = 0; i < m_header.ncmds; ++i) { + const uint32_t cmd_offset = offset; + llvm::MachO::load_command lc; + if (m_data.GetU32(&offset, &lc.cmd, 2) == nullptr) + break; + if (lc.cmd == LC_NOTE) { + char data_owner[17]; + m_data.CopyData(offset, 16, data_owner); + data_owner[16] = '\0'; + offset += 16; + uint64_t fileoff = m_data.GetU64_unchecked(&offset); + + // "addrable bits" has a uint32_t version and a uint32_t + // number of bits used in addressing. + if (strcmp("addrable bits", data_owner) == 0) { + offset = fileoff; + uint32_t version; + if (m_data.GetU32(&offset, &version, 1) != nullptr) { + if (version == 3) { + uint32_t num_addr_bits = m_data.GetU32_unchecked(&offset); + if (num_addr_bits != 0) { + mask = ~((1ULL << num_addr_bits) - 1); + } + break; + } + } + } + } + offset = cmd_offset + lc.cmdsize; + } + } + return mask; +} + bool ObjectFileMachO::GetCorefileMainBinaryInfo(addr_t &address, UUID &uuid, ObjectFile::BinaryType &type) { address = LLDB_INVALID_ADDRESS; @@ -6652,6 +6693,15 @@ mach_header.sizeofcmds += 8 + LC_THREAD_data.GetSize(); } + // Bits will be set to indicate which bits are NOT used in + // addressing in this process or 0 for unknown. + uint64_t address_mask = process_sp->GetCodeAddressMask(); + if (address_mask != 0) { + // LC_NOTE "addrable bits" + mach_header.ncmds++; + mach_header.sizeofcmds += sizeof(llvm::MachO::note_command); + } + // LC_NOTE "all image infos" mach_header.ncmds++; mach_header.sizeofcmds += sizeof(llvm::MachO::note_command); @@ -6673,28 +6723,48 @@ addr_t file_offset = buffer.GetSize() + mach_header.sizeofcmds; file_offset = llvm::alignTo(file_offset, 16); + std::vector> lc_notes; + + // Add "addrable bits" LC_NOTE when an address mask is available + if (address_mask != 0) { + std::unique_ptr addrable_bits_lcnote_up( + new LCNoteEntry(addr_byte_size, byte_order)); + addrable_bits_lcnote_up->name = "addrable bits"; + addrable_bits_lcnote_up->payload_file_offset = file_offset; + int bits = std::bitset<64>(~address_mask).count(); + addrable_bits_lcnote_up->payload.PutHex32(3); // version + addrable_bits_lcnote_up->payload.PutHex32( + bits); // # of bits used for addressing + addrable_bits_lcnote_up->payload.PutHex64(0); // unused + + file_offset += addrable_bits_lcnote_up->payload.GetSize(); + + lc_notes.push_back(std::move(addrable_bits_lcnote_up)); + } - // Create the "all image infos" LC_NOTE payload - StreamString all_image_infos_payload(Stream::eBinary, addr_byte_size, - byte_order); - offset_t all_image_infos_payload_start = file_offset; - file_offset = CreateAllImageInfosPayload(process_sp, file_offset, - all_image_infos_payload); - - // Add the "all image infos" LC_NOTE load command - llvm::MachO::note_command all_image_info_note = { - LC_NOTE, /* uint32_t cmd */ - sizeof(llvm::MachO::note_command), /* uint32_t cmdsize */ - "all image infos", /* char data_owner[16] */ - all_image_infos_payload_start, /* uint64_t offset */ - file_offset - all_image_infos_payload_start /* uint64_t size */ - }; - buffer.PutHex32(all_image_info_note.cmd); - buffer.PutHex32(all_image_info_note.cmdsize); - buffer.PutRawBytes(all_image_info_note.data_owner, - sizeof(all_image_info_note.data_owner)); - buffer.PutHex64(all_image_info_note.offset); - buffer.PutHex64(all_image_info_note.size); + // Add "all image infos" LC_NOTE + std::unique_ptr all_image_infos_lcnote_up( + new LCNoteEntry(addr_byte_size, byte_order)); + all_image_infos_lcnote_up->name = "all image infos"; + all_image_infos_lcnote_up->payload_file_offset = file_offset; + file_offset = CreateAllImageInfosPayload( + process_sp, file_offset, all_image_infos_lcnote_up->payload); + lc_notes.push_back(std::move(all_image_infos_lcnote_up)); + + // Add LC_NOTE load commands + for (auto &lcnote : lc_notes) { + // Add the LC_NOTE load command to the file. + buffer.PutHex32(LC_NOTE); + buffer.PutHex32(sizeof(llvm::MachO::note_command)); + char namebuf[16]; + memset(namebuf, 0, sizeof(namebuf)); + // this is the uncommon case where strncpy is exactly + // the right one, doesn't need to be nul terminated. + strncpy(namebuf, lcnote->name.c_str(), sizeof(namebuf)); + buffer.PutRawBytes(namebuf, sizeof(namebuf)); + buffer.PutHex64(lcnote->payload_file_offset); + buffer.PutHex64(lcnote->payload.GetSize()); + } // Align to 4096-byte page boundary for the LC_SEGMENTs. file_offset = llvm::alignTo(file_offset, 4096); @@ -6749,19 +6819,21 @@ core_file.get()->Write(buffer.GetString().data(), bytes_written); if (error.Success()) { - if (core_file.get()->SeekFromStart(all_image_info_note.offset) == - -1) { - error.SetErrorStringWithFormat( - "Unable to seek to corefile pos to write all iamge infos"); - return false; + for (auto &lcnote : lc_notes) { + if (core_file.get()->SeekFromStart(lcnote->payload_file_offset) == + -1) { + error.SetErrorStringWithFormat("Unable to seek to corefile pos " + "to write '%s' LC_NOTE payload", + lcnote->name.c_str()); + return false; + } + bytes_written = lcnote->payload.GetSize(); + error = core_file.get()->Write(lcnote->payload.GetData(), + bytes_written); + if (!error.Success()) + return false; } - bytes_written = all_image_infos_payload.GetString().size(); - error = core_file.get()->Write( - all_image_infos_payload.GetString().data(), bytes_written); - if (!error.Success()) - return false; - // Now write the file data for all memory segments in the process for (const auto &segment : segment_load_commands) { if (core_file.get()->SeekFromStart(segment.fileoff) == -1) { 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 @@ -541,6 +541,11 @@ if (arch.IsValid()) GetTarget().SetArchitecture(arch); + addr_t address_mask = core_objfile->GetAddressMask(); + if (address_mask != 0) { + SetCodeAddressMask(address_mask); + SetDataAddressMask(address_mask); + } return error; } diff --git a/lldb/test/API/macosx/lc-note/addrable-bits/Makefile b/lldb/test/API/macosx/lc-note/addrable-bits/Makefile new file mode 100644 --- /dev/null +++ b/lldb/test/API/macosx/lc-note/addrable-bits/Makefile @@ -0,0 +1,3 @@ +C_SOURCES := main.c + +include Makefile.rules diff --git a/lldb/test/API/macosx/lc-note/addrable-bits/TestAddrableBitsCorefile.py b/lldb/test/API/macosx/lc-note/addrable-bits/TestAddrableBitsCorefile.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/macosx/lc-note/addrable-bits/TestAddrableBitsCorefile.py @@ -0,0 +1,59 @@ +"""Test that corefiles with LC_NOTE "addrable bits" load command, creating and reading.""" + + + +import os +import re +import subprocess + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class TestAddrableBitsCorefile(TestBase): + + mydir = TestBase.compute_mydir(__file__) + NO_DEBUG_INFO_TESTCASE = True + + def initial_setup(self): + self.build() + self.exe = self.getBuildArtifact("a.out") + self.corefile = self.getBuildArtifact("corefile") + + @skipIf(archs=no_match(['arm64e'])) + @skipUnlessDarwin + def test_lc_note_addrable_bits(self): + self.initial_setup() + + self.target = self.dbg.CreateTarget(self.exe) + err = lldb.SBError() + (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(self, "break here", + lldb.SBFileSpec('main.c')) + self.assertEqual(process.IsValid(), True) + + found_main = False + for f in thread.frames: + if f.GetFunctionName() == "main": + found_main = True + self.assertTrue(found_main) + + cmdinterp = self.dbg.GetCommandInterpreter() + res = lldb.SBCommandReturnObject() + cmdinterp.HandleCommand("process save-core %s" % self.corefile, res) + self.assertTrue(res.Succeeded(), True) + process.Kill() + self.dbg.DeleteTarget(target) + + target = self.dbg.CreateTarget('') + process = target.LoadCore(self.corefile) + self.assertTrue(process.IsValid(), True) + thread = process.GetSelectedThread() + + found_main = False + for f in thread.frames: + if f.GetFunctionName() == "main": + found_main = True + self.assertTrue(found_main) + diff --git a/lldb/test/API/macosx/lc-note/addrable-bits/main.c b/lldb/test/API/macosx/lc-note/addrable-bits/main.c new file mode 100644 --- /dev/null +++ b/lldb/test/API/macosx/lc-note/addrable-bits/main.c @@ -0,0 +1,12 @@ +int pat (int in) { + return in + 5; // break here +} + +int tat (int in) { return pat(in + 10); } + +int mat (int in) { return tat(in + 15); } + +int main() { + int (*matp)(int) = mat; + return matp(10); +}