Skip to content

Commit 2940c22

Browse files
[lldb][Mach-O] Allow "process metadata" LC_NOTE to supply registers (llvm#144627)
The "process metadata" LC_NOTE allows for thread IDs to be specified in a Mach-O corefile. This extends the JSON recognzied in that LC_NOTE to allow for additional registers to be supplied on a per-thread basis. The registers included in a Mach-O corefile LC_THREAD load command can only be one of the register flavors that the kernel (xnu) defines in <mach/arm/thread_status.h> for arm64 -- the general purpose registers, floating point registers, exception registers. JTAG style corefile producers may have access to many additional registers beyond these that EL0 programs typically use, for instance TCR_EL1 on AArch64, and people developing low level code need access to these registers. This patch defines a format for including these registers for any thread. The JSON in "process metadata" is a dictionary that must have a `threads` key. The value is an array of entries, one per LC_THREAD in the Mach-O corefile. The number of entries must match the LC_THREADs so they can be correctly associated. Each thread's dictionary must have two keys, `sets`, and `registers`. `sets` is an array of register set names. If a register set name matches one from the LC_THREAD core registers, any registers that are defined will be added to that register set. e.g. metadata can add a register to the "General Purpose Registers" set that lldb shows users. `registers` is an array of dictionaries, one per register. Each register must have the keys `name`, `value`, `bitsize`, and `set`. It may provide additional keys like `alt-name`, that `DynamicRegisterInfo::SetRegisterInfo` recognizes. This `sets` + `registers` formatting is the same that is used by the `target.process.python-os-plugin-path` script interface uses, both are parsed by `DynamicRegisterInfo`. The one addition is that in this LC_NOTE metadata, each register must also have a `value` field, with the value provided in big-endian base 10, as usual with JSON. In RegisterContextUnifiedCore, I combine the register sets & registers from the LC_THREAD for a specific thread, and the metadata sets & registers for that thread from the LC_NOTE. Even if no LC_NOTE is present, this class ingests the LC_THREAD register contexts and reformats it to its internal stores before returning itself as the RegisterContex, instead of shortcutting and returning the core's native RegisterContext. I could have gone either way with that, but in the end I decided if the code is correct, we should live on it always. I added a test where we process save-core to create a userland corefile, then use a utility "add-lcnote" to strip the existing "process metadata" LC_NOTE that lldb put in it, and adds a new one from a JSON string. rdar://74358787 --------- Co-authored-by: Jonas Devlieghere <[email protected]> (cherry picked from commit a64db49)
1 parent 91adf08 commit 2940c22

File tree

11 files changed

+1026
-35
lines changed

11 files changed

+1026
-35
lines changed

lldb/include/lldb/Symbol/ObjectFile.h

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "lldb/Utility/Endian.h"
1919
#include "lldb/Utility/FileSpec.h"
2020
#include "lldb/Utility/FileSpecList.h"
21+
#include "lldb/Utility/StructuredData.h"
2122
#include "lldb/Utility/UUID.h"
2223
#include "lldb/lldb-private.h"
2324
#include "llvm/Support/Threading.h"
@@ -544,9 +545,9 @@ class ObjectFile : public std::enable_shared_from_this<ObjectFile>,
544545
return false;
545546
}
546547

547-
/// Get metadata about threads from the corefile.
548+
/// Get metadata about thread ids from the corefile.
548549
///
549-
/// The corefile may have metadata (e.g. a Mach-O "thread extrainfo"
550+
/// The corefile may have metadata (e.g. a Mach-O "process metadata"
550551
/// LC_NOTE) which for the threads in the process; this method tries
551552
/// to retrieve them.
552553
///
@@ -568,6 +569,18 @@ class ObjectFile : public std::enable_shared_from_this<ObjectFile>,
568569
return false;
569570
}
570571

572+
/// Get process metadata from the corefile in a StructuredData dictionary.
573+
///
574+
/// The corefile may have notes (e.g. a Mach-O "process metadata" LC_NOTE)
575+
/// which provide metadata about the process and threads in a JSON or
576+
/// similar format.
577+
///
578+
/// \return
579+
/// A StructuredData object with the metadata in the note, if there is
580+
/// one. An empty shared pointer is returned if not metadata is found,
581+
/// or a problem parsing it.
582+
virtual StructuredData::ObjectSP GetCorefileProcessMetadata() { return {}; }
583+
571584
virtual lldb::RegisterContextSP
572585
GetThreadContextAtIndex(uint32_t idx, lldb_private::Thread &thread) {
573586
return lldb::RegisterContextSP();

lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp

Lines changed: 44 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5893,27 +5893,8 @@ bool ObjectFileMachO::GetCorefileThreadExtraInfos(
58935893
std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
58945894

58955895
Log *log(GetLog(LLDBLog::Object | LLDBLog::Process | LLDBLog::Thread));
5896-
auto lc_notes = FindLC_NOTEByName("process metadata");
5897-
for (auto lc_note : lc_notes) {
5898-
offset_t payload_offset = std::get<0>(lc_note);
5899-
offset_t strsize = std::get<1>(lc_note);
5900-
std::string buf(strsize, '\0');
5901-
if (m_data.CopyData(payload_offset, strsize, buf.data()) != strsize) {
5902-
LLDB_LOGF(log,
5903-
"Unable to read %" PRIu64
5904-
" bytes of 'process metadata' LC_NOTE JSON contents",
5905-
strsize);
5906-
return false;
5907-
}
5908-
while (buf.back() == '\0')
5909-
buf.resize(buf.size() - 1);
5910-
StructuredData::ObjectSP object_sp = StructuredData::ParseJSON(buf);
5896+
if (StructuredData::ObjectSP object_sp = GetCorefileProcessMetadata()) {
59115897
StructuredData::Dictionary *dict = object_sp->GetAsDictionary();
5912-
if (!dict) {
5913-
LLDB_LOGF(log, "Unable to read 'process metadata' LC_NOTE, did not "
5914-
"get a dictionary.");
5915-
return false;
5916-
}
59175898
StructuredData::Array *threads;
59185899
if (!dict->GetValueForKeyAsArray("threads", threads) || !threads) {
59195900
LLDB_LOGF(log,
@@ -5956,6 +5937,49 @@ bool ObjectFileMachO::GetCorefileThreadExtraInfos(
59565937
return false;
59575938
}
59585939

5940+
StructuredData::ObjectSP ObjectFileMachO::GetCorefileProcessMetadata() {
5941+
ModuleSP module_sp(GetModule());
5942+
if (!module_sp)
5943+
return {};
5944+
5945+
Log *log(GetLog(LLDBLog::Object | LLDBLog::Process | LLDBLog::Thread));
5946+
std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
5947+
auto lc_notes = FindLC_NOTEByName("process metadata");
5948+
if (lc_notes.size() == 0)
5949+
return {};
5950+
5951+
if (lc_notes.size() > 1)
5952+
LLDB_LOGF(
5953+
log,
5954+
"Multiple 'process metadata' LC_NOTEs found, only using the first.");
5955+
5956+
auto [payload_offset, strsize] = lc_notes[0];
5957+
std::string buf(strsize, '\0');
5958+
if (m_data.CopyData(payload_offset, strsize, buf.data()) != strsize) {
5959+
LLDB_LOGF(log,
5960+
"Unable to read %" PRIu64
5961+
" bytes of 'process metadata' LC_NOTE JSON contents",
5962+
strsize);
5963+
return {};
5964+
}
5965+
while (buf.back() == '\0')
5966+
buf.resize(buf.size() - 1);
5967+
StructuredData::ObjectSP object_sp = StructuredData::ParseJSON(buf);
5968+
if (!object_sp) {
5969+
LLDB_LOGF(log, "Unable to read 'process metadata' LC_NOTE, did not "
5970+
"parse as valid JSON.");
5971+
return {};
5972+
}
5973+
StructuredData::Dictionary *dict = object_sp->GetAsDictionary();
5974+
if (!dict) {
5975+
LLDB_LOGF(log, "Unable to read 'process metadata' LC_NOTE, did not "
5976+
"get a dictionary.");
5977+
return {};
5978+
}
5979+
5980+
return object_sp;
5981+
}
5982+
59595983
lldb::RegisterContextSP
59605984
ObjectFileMachO::GetThreadContextAtIndex(uint32_t idx,
59615985
lldb_private::Thread &thread) {

lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,8 @@ class ObjectFileMachO : public lldb_private::ObjectFile {
132132

133133
bool GetCorefileThreadExtraInfos(std::vector<lldb::tid_t> &tids) override;
134134

135+
lldb_private::StructuredData::ObjectSP GetCorefileProcessMetadata() override;
136+
135137
bool LoadCoreFileImages(lldb_private::Process &process) override;
136138

137139
lldb::RegisterContextSP

lldb/source/Plugins/Process/mach-core/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
add_lldb_library(lldbPluginProcessMachCore PLUGIN
22
ProcessMachCore.cpp
33
ThreadMachCore.cpp
4+
RegisterContextUnifiedCore.cpp
45

56
LINK_LIBS
67
lldbBreakpoint

0 commit comments

Comments
 (0)