forked from OSchip/llvm-project
Read and write a LC_NOTE "addrable bits" for addressing mask
This patch adds code to process save-core for Mach-O files which embeds an "addrable bits" LC_NOTE when the process is using a code address mask (e.g. AArch64 v8.3 with ptrauth aka arm64e). Add code to ObjectFileMachO to read that LC_NOTE from corefiles, and ProcessMachCore to set the process masks based on it when reading a corefile back in. Also have "process status --verbose" print the current address masks that lldb is using internally to strip ptrauth bits off of addresses. Differential Revision: https://reviews.llvm.org/D106348 rdar://68630113
This commit is contained in:
parent
924d62ca4a
commit
cdc6f8d728
|
@ -495,6 +495,16 @@ public:
|
||||||
return std::string();
|
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
|
/// 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
|
/// 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
|
/// binary, but some core files may have metadata describing where the main
|
||||||
|
|
|
@ -2899,7 +2899,8 @@ protected:
|
||||||
std::atomic<bool> m_finalizing;
|
std::atomic<bool> m_finalizing;
|
||||||
|
|
||||||
/// Mask for code an data addresses. The default value (0) means no mask is
|
/// 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_code_address_mask = 0;
|
||||||
lldb::addr_t m_data_address_mask = 0;
|
lldb::addr_t m_data_address_mask = 0;
|
||||||
|
|
|
@ -29,6 +29,8 @@
|
||||||
#include "lldb/Utility/Args.h"
|
#include "lldb/Utility/Args.h"
|
||||||
#include "lldb/Utility/State.h"
|
#include "lldb/Utility/State.h"
|
||||||
|
|
||||||
|
#include <bitset>
|
||||||
|
|
||||||
using namespace lldb;
|
using namespace lldb;
|
||||||
using namespace lldb_private;
|
using namespace lldb_private;
|
||||||
|
|
||||||
|
@ -1341,6 +1343,18 @@ protected:
|
||||||
num_frames, num_frames_with_source, stop_format);
|
num_frames, num_frames_with_source, stop_format);
|
||||||
|
|
||||||
if (m_options.m_verbose) {
|
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();
|
PlatformSP platform_sp = process->GetTarget().GetPlatform();
|
||||||
if (!platform_sp) {
|
if (!platform_sp) {
|
||||||
result.AppendError("Couldn'retrieve the target's platform");
|
result.AppendError("Couldn'retrieve the target's platform");
|
||||||
|
|
|
@ -62,6 +62,7 @@
|
||||||
#include <uuid/uuid.h>
|
#include <uuid/uuid.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <bitset>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#if LLVM_SUPPORT_XCODE_SIGNPOSTS
|
#if LLVM_SUPPORT_XCODE_SIGNPOSTS
|
||||||
|
@ -5571,6 +5572,46 @@ std::string ObjectFileMachO::GetIdentifierString() {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addr_t ObjectFileMachO::GetAddressMask() {
|
||||||
|
addr_t mask = 0;
|
||||||
|
ModuleSP module_sp(GetModule());
|
||||||
|
if (module_sp) {
|
||||||
|
std::lock_guard<std::recursive_mutex> 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,
|
bool ObjectFileMachO::GetCorefileMainBinaryInfo(addr_t &address, UUID &uuid,
|
||||||
ObjectFile::BinaryType &type) {
|
ObjectFile::BinaryType &type) {
|
||||||
address = LLDB_INVALID_ADDRESS;
|
address = LLDB_INVALID_ADDRESS;
|
||||||
|
@ -6652,6 +6693,15 @@ bool ObjectFileMachO::SaveCore(const lldb::ProcessSP &process_sp,
|
||||||
mach_header.sizeofcmds += 8 + LC_THREAD_data.GetSize();
|
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"
|
// LC_NOTE "all image infos"
|
||||||
mach_header.ncmds++;
|
mach_header.ncmds++;
|
||||||
mach_header.sizeofcmds += sizeof(llvm::MachO::note_command);
|
mach_header.sizeofcmds += sizeof(llvm::MachO::note_command);
|
||||||
|
@ -6673,28 +6723,48 @@ bool ObjectFileMachO::SaveCore(const lldb::ProcessSP &process_sp,
|
||||||
addr_t file_offset = buffer.GetSize() + mach_header.sizeofcmds;
|
addr_t file_offset = buffer.GetSize() + mach_header.sizeofcmds;
|
||||||
|
|
||||||
file_offset = llvm::alignTo(file_offset, 16);
|
file_offset = llvm::alignTo(file_offset, 16);
|
||||||
|
std::vector<std::unique_ptr<LCNoteEntry>> lc_notes;
|
||||||
|
|
||||||
// Create the "all image infos" LC_NOTE payload
|
// Add "addrable bits" LC_NOTE when an address mask is available
|
||||||
StreamString all_image_infos_payload(Stream::eBinary, addr_byte_size,
|
if (address_mask != 0) {
|
||||||
byte_order);
|
std::unique_ptr<LCNoteEntry> addrable_bits_lcnote_up(
|
||||||
offset_t all_image_infos_payload_start = file_offset;
|
new LCNoteEntry(addr_byte_size, byte_order));
|
||||||
file_offset = CreateAllImageInfosPayload(process_sp, file_offset,
|
addrable_bits_lcnote_up->name = "addrable bits";
|
||||||
all_image_infos_payload);
|
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
|
||||||
|
|
||||||
// Add the "all image infos" LC_NOTE load command
|
file_offset += addrable_bits_lcnote_up->payload.GetSize();
|
||||||
llvm::MachO::note_command all_image_info_note = {
|
|
||||||
LC_NOTE, /* uint32_t cmd */
|
lc_notes.push_back(std::move(addrable_bits_lcnote_up));
|
||||||
sizeof(llvm::MachO::note_command), /* uint32_t cmdsize */
|
}
|
||||||
"all image infos", /* char data_owner[16] */
|
|
||||||
all_image_infos_payload_start, /* uint64_t offset */
|
// Add "all image infos" LC_NOTE
|
||||||
file_offset - all_image_infos_payload_start /* uint64_t size */
|
std::unique_ptr<LCNoteEntry> all_image_infos_lcnote_up(
|
||||||
};
|
new LCNoteEntry(addr_byte_size, byte_order));
|
||||||
buffer.PutHex32(all_image_info_note.cmd);
|
all_image_infos_lcnote_up->name = "all image infos";
|
||||||
buffer.PutHex32(all_image_info_note.cmdsize);
|
all_image_infos_lcnote_up->payload_file_offset = file_offset;
|
||||||
buffer.PutRawBytes(all_image_info_note.data_owner,
|
file_offset = CreateAllImageInfosPayload(
|
||||||
sizeof(all_image_info_note.data_owner));
|
process_sp, file_offset, all_image_infos_lcnote_up->payload);
|
||||||
buffer.PutHex64(all_image_info_note.offset);
|
lc_notes.push_back(std::move(all_image_infos_lcnote_up));
|
||||||
buffer.PutHex64(all_image_info_note.size);
|
|
||||||
|
// 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.
|
// Align to 4096-byte page boundary for the LC_SEGMENTs.
|
||||||
file_offset = llvm::alignTo(file_offset, 4096);
|
file_offset = llvm::alignTo(file_offset, 4096);
|
||||||
|
@ -6749,19 +6819,21 @@ bool ObjectFileMachO::SaveCore(const lldb::ProcessSP &process_sp,
|
||||||
core_file.get()->Write(buffer.GetString().data(), bytes_written);
|
core_file.get()->Write(buffer.GetString().data(), bytes_written);
|
||||||
if (error.Success()) {
|
if (error.Success()) {
|
||||||
|
|
||||||
if (core_file.get()->SeekFromStart(all_image_info_note.offset) ==
|
for (auto &lcnote : lc_notes) {
|
||||||
-1) {
|
if (core_file.get()->SeekFromStart(lcnote->payload_file_offset) ==
|
||||||
error.SetErrorStringWithFormat(
|
-1) {
|
||||||
"Unable to seek to corefile pos to write all iamge infos");
|
error.SetErrorStringWithFormat("Unable to seek to corefile pos "
|
||||||
return false;
|
"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
|
// Now write the file data for all memory segments in the process
|
||||||
for (const auto &segment : segment_load_commands) {
|
for (const auto &segment : segment_load_commands) {
|
||||||
if (core_file.get()->SeekFromStart(segment.fileoff) == -1) {
|
if (core_file.get()->SeekFromStart(segment.fileoff) == -1) {
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include "lldb/Symbol/ObjectFile.h"
|
#include "lldb/Symbol/ObjectFile.h"
|
||||||
#include "lldb/Utility/FileSpec.h"
|
#include "lldb/Utility/FileSpec.h"
|
||||||
#include "lldb/Utility/RangeMap.h"
|
#include "lldb/Utility/RangeMap.h"
|
||||||
|
#include "lldb/Utility/StreamString.h"
|
||||||
#include "lldb/Utility/UUID.h"
|
#include "lldb/Utility/UUID.h"
|
||||||
|
|
||||||
// This class needs to be hidden as eventually belongs in a plugin that
|
// This class needs to be hidden as eventually belongs in a plugin that
|
||||||
|
@ -113,6 +114,8 @@ public:
|
||||||
|
|
||||||
std::string GetIdentifierString() override;
|
std::string GetIdentifierString() override;
|
||||||
|
|
||||||
|
lldb::addr_t GetAddressMask() override;
|
||||||
|
|
||||||
bool GetCorefileMainBinaryInfo(lldb::addr_t &address,
|
bool GetCorefileMainBinaryInfo(lldb::addr_t &address,
|
||||||
lldb_private::UUID &uuid,
|
lldb_private::UUID &uuid,
|
||||||
ObjectFile::BinaryType &type) override;
|
ObjectFile::BinaryType &type) override;
|
||||||
|
@ -225,6 +228,15 @@ protected:
|
||||||
segment_load_addresses;
|
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 {
|
struct MachOCorefileAllImageInfos {
|
||||||
std::vector<MachOCorefileImageEntry> all_image_infos;
|
std::vector<MachOCorefileImageEntry> all_image_infos;
|
||||||
bool IsValid() { return all_image_infos.size() > 0; }
|
bool IsValid() { return all_image_infos.size() > 0; }
|
||||||
|
|
|
@ -541,6 +541,11 @@ Status ProcessMachCore::DoLoadCore() {
|
||||||
if (arch.IsValid())
|
if (arch.IsValid())
|
||||||
GetTarget().SetArchitecture(arch);
|
GetTarget().SetArchitecture(arch);
|
||||||
|
|
||||||
|
addr_t address_mask = core_objfile->GetAddressMask();
|
||||||
|
if (address_mask != 0) {
|
||||||
|
SetCodeAddressMask(address_mask);
|
||||||
|
SetDataAddressMask(address_mask);
|
||||||
|
}
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
C_SOURCES := main.c
|
||||||
|
|
||||||
|
include Makefile.rules
|
|
@ -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)
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
Loading…
Reference in New Issue