Skip to content

Commit 50a449a

Browse files
jasonmolendaJDevlieghere
authored andcommitted
[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]>
1 parent 6e58ff1 commit 50a449a

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
@@ -5794,27 +5794,8 @@ bool ObjectFileMachO::GetCorefileThreadExtraInfos(
57945794
std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
57955795

57965796
Log *log(GetLog(LLDBLog::Object | LLDBLog::Process | LLDBLog::Thread));
5797-
auto lc_notes = FindLC_NOTEByName("process metadata");
5798-
for (auto lc_note : lc_notes) {
5799-
offset_t payload_offset = std::get<0>(lc_note);
5800-
offset_t strsize = std::get<1>(lc_note);
5801-
std::string buf(strsize, '\0');
5802-
if (m_data.CopyData(payload_offset, strsize, buf.data()) != strsize) {
5803-
LLDB_LOGF(log,
5804-
"Unable to read %" PRIu64
5805-
" bytes of 'process metadata' LC_NOTE JSON contents",
5806-
strsize);
5807-
return false;
5808-
}
5809-
while (buf.back() == '\0')
5810-
buf.resize(buf.size() - 1);
5811-
StructuredData::ObjectSP object_sp = StructuredData::ParseJSON(buf);
5797+
if (StructuredData::ObjectSP object_sp = GetCorefileProcessMetadata()) {
58125798
StructuredData::Dictionary *dict = object_sp->GetAsDictionary();
5813-
if (!dict) {
5814-
LLDB_LOGF(log, "Unable to read 'process metadata' LC_NOTE, did not "
5815-
"get a dictionary.");
5816-
return false;
5817-
}
58185799
StructuredData::Array *threads;
58195800
if (!dict->GetValueForKeyAsArray("threads", threads) || !threads) {
58205801
LLDB_LOGF(log,
@@ -5857,6 +5838,49 @@ bool ObjectFileMachO::GetCorefileThreadExtraInfos(
58575838
return false;
58585839
}
58595840

5841+
StructuredData::ObjectSP ObjectFileMachO::GetCorefileProcessMetadata() {
5842+
ModuleSP module_sp(GetModule());
5843+
if (!module_sp)
5844+
return {};
5845+
5846+
Log *log(GetLog(LLDBLog::Object | LLDBLog::Process | LLDBLog::Thread));
5847+
std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
5848+
auto lc_notes = FindLC_NOTEByName("process metadata");
5849+
if (lc_notes.size() == 0)
5850+
return {};
5851+
5852+
if (lc_notes.size() > 1)
5853+
LLDB_LOGF(
5854+
log,
5855+
"Multiple 'process metadata' LC_NOTEs found, only using the first.");
5856+
5857+
auto [payload_offset, strsize] = lc_notes[0];
5858+
std::string buf(strsize, '\0');
5859+
if (m_data.CopyData(payload_offset, strsize, buf.data()) != strsize) {
5860+
LLDB_LOGF(log,
5861+
"Unable to read %" PRIu64
5862+
" bytes of 'process metadata' LC_NOTE JSON contents",
5863+
strsize);
5864+
return {};
5865+
}
5866+
while (buf.back() == '\0')
5867+
buf.resize(buf.size() - 1);
5868+
StructuredData::ObjectSP object_sp = StructuredData::ParseJSON(buf);
5869+
if (!object_sp) {
5870+
LLDB_LOGF(log, "Unable to read 'process metadata' LC_NOTE, did not "
5871+
"parse as valid JSON.");
5872+
return {};
5873+
}
5874+
StructuredData::Dictionary *dict = object_sp->GetAsDictionary();
5875+
if (!dict) {
5876+
LLDB_LOGF(log, "Unable to read 'process metadata' LC_NOTE, did not "
5877+
"get a dictionary.");
5878+
return {};
5879+
}
5880+
5881+
return object_sp;
5882+
}
5883+
58605884
lldb::RegisterContextSP
58615885
ObjectFileMachO::GetThreadContextAtIndex(uint32_t idx,
58625886
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
@@ -133,6 +133,8 @@ class ObjectFileMachO : public lldb_private::ObjectFile {
133133

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

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

138140
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_COMPONENTS
67
Support

0 commit comments

Comments
 (0)