Skip to content

Commit dd4dd79

Browse files
committed
Add support for arm64 registers in minidump core file saving.
This patch adds support for saving minidumps with the arm64 architecture. It also will cause unsupported architectures to emit an error where before this patch it would emit a minidump with partial information. This new code is tested by the arm64 windows buildbot that was failing: https://lab.llvm.org/buildbot/#/builders/219/builds/6868 This is needed following this PR: llvm#71772
1 parent d3876c5 commit dd4dd79

File tree

4 files changed

+173
-89
lines changed

4 files changed

+173
-89
lines changed

lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp

Lines changed: 154 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "MinidumpFileBuilder.h"
1010

1111
#include "Plugins/Process/minidump/RegisterContextMinidump_x86_64.h"
12+
#include "Plugins/Process/minidump/RegisterContextMinidump_ARM64.h"
1213

1314
#include "lldb/Core/Module.h"
1415
#include "lldb/Core/ModuleList.h"
@@ -293,7 +294,7 @@ Status MinidumpFileBuilder::AddModuleList(Target &target) {
293294
}
294295

295296
uint16_t read_register_u16_raw(RegisterContext *reg_ctx,
296-
const std::string &reg_name) {
297+
llvm::StringRef reg_name) {
297298
const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(reg_name);
298299
if (!reg_info)
299300
return 0;
@@ -305,7 +306,7 @@ uint16_t read_register_u16_raw(RegisterContext *reg_ctx,
305306
}
306307

307308
uint32_t read_register_u32_raw(RegisterContext *reg_ctx,
308-
const std::string &reg_name) {
309+
llvm::StringRef reg_name) {
309310
const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(reg_name);
310311
if (!reg_info)
311312
return 0;
@@ -317,7 +318,7 @@ uint32_t read_register_u32_raw(RegisterContext *reg_ctx,
317318
}
318319

319320
uint64_t read_register_u64_raw(RegisterContext *reg_ctx,
320-
const std::string &reg_name) {
321+
llvm::StringRef reg_name) {
321322
const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(reg_name);
322323
if (!reg_info)
323324
return 0;
@@ -329,25 +330,44 @@ uint64_t read_register_u64_raw(RegisterContext *reg_ctx,
329330
}
330331

331332
llvm::support::ulittle16_t read_register_u16(RegisterContext *reg_ctx,
332-
const std::string &reg_name) {
333+
llvm::StringRef reg_name) {
333334
return static_cast<llvm::support::ulittle16_t>(
334335
read_register_u16_raw(reg_ctx, reg_name));
335336
}
336337

337338
llvm::support::ulittle32_t read_register_u32(RegisterContext *reg_ctx,
338-
const std::string &reg_name) {
339+
llvm::StringRef reg_name) {
339340
return static_cast<llvm::support::ulittle32_t>(
340341
read_register_u32_raw(reg_ctx, reg_name));
341342
}
342343

343344
llvm::support::ulittle64_t read_register_u64(RegisterContext *reg_ctx,
344-
const std::string &reg_name) {
345+
llvm::StringRef reg_name) {
345346
return static_cast<llvm::support::ulittle64_t>(
346347
read_register_u64_raw(reg_ctx, reg_name));
347348
}
348349

350+
void read_register_u128(RegisterContext *reg_ctx, llvm::StringRef reg_name,
351+
uint8_t *dst) {
352+
const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(reg_name);
353+
if (reg_info) {
354+
lldb_private::RegisterValue reg_value;
355+
if (reg_ctx->ReadRegister(reg_info, reg_value)) {
356+
Status error;
357+
uint32_t bytes_copied =
358+
reg_value.GetAsMemoryData(*reg_info, dst, 16,
359+
lldb::ByteOrder::eByteOrderLittle,
360+
error);
361+
if (bytes_copied == 16)
362+
return;
363+
}
364+
}
365+
// If anything goes wrong, then zero out the register value.
366+
memset(dst, 0, 16);
367+
}
368+
349369
lldb_private::minidump::MinidumpContext_x86_64
350-
GetThreadContext_64(RegisterContext *reg_ctx) {
370+
GetThreadContext_x86_64(RegisterContext *reg_ctx) {
351371
lldb_private::minidump::MinidumpContext_x86_64 thread_context = {};
352372
thread_context.p1_home = {};
353373
thread_context.context_flags = static_cast<uint32_t>(
@@ -381,6 +401,73 @@ GetThreadContext_64(RegisterContext *reg_ctx) {
381401
return thread_context;
382402
}
383403

404+
minidump::RegisterContextMinidump_ARM64::Context
405+
GetThreadContext_ARM64(RegisterContext *reg_ctx) {
406+
minidump::RegisterContextMinidump_ARM64::Context thread_context = {};
407+
thread_context.context_flags = static_cast<uint32_t>(
408+
minidump::RegisterContextMinidump_ARM64::Flags::ARM64_Flag |
409+
minidump::RegisterContextMinidump_ARM64::Flags::Integer |
410+
minidump::RegisterContextMinidump_ARM64::Flags::FloatingPoint);
411+
char reg_name[16];
412+
for (uint32_t i=0; i<31; ++i) {
413+
snprintf(reg_name, sizeof(reg_name), "x%u", i);
414+
thread_context.x[i] = read_register_u64(reg_ctx, reg_name);
415+
}
416+
// Work around a bug in debugserver where "sp" on arm64 doesn't have the alt
417+
// name set to "x31"
418+
thread_context.x[31] = read_register_u64(reg_ctx, "sp");
419+
thread_context.pc = read_register_u64(reg_ctx, "pc");
420+
thread_context.cpsr = read_register_u32(reg_ctx, "cpsr");
421+
thread_context.fpsr = read_register_u32(reg_ctx, "fpsr");
422+
thread_context.fpcr = read_register_u32(reg_ctx, "fpcr");
423+
for (uint32_t i=0; i<32; ++i) {
424+
snprintf(reg_name, sizeof(reg_name), "v%u", i);
425+
read_register_u128(reg_ctx, reg_name, &thread_context.v[i*16]);
426+
}
427+
return thread_context;
428+
}
429+
430+
class ArchThreadContexts {
431+
llvm::Triple::ArchType m_arch;
432+
union {
433+
lldb_private::minidump::MinidumpContext_x86_64 x86_64;
434+
lldb_private::minidump::RegisterContextMinidump_ARM64::Context arm64;
435+
};
436+
public:
437+
ArchThreadContexts(llvm::Triple::ArchType arch) : m_arch(arch) {}
438+
439+
bool prepareRegisterContext(RegisterContext *reg_ctx) {
440+
switch (m_arch) {
441+
case llvm::Triple::ArchType::x86_64:
442+
x86_64 = GetThreadContext_x86_64(reg_ctx);
443+
return true;
444+
case llvm::Triple::ArchType::aarch64:
445+
arm64 = GetThreadContext_ARM64(reg_ctx);
446+
return true;
447+
default:
448+
break;
449+
}
450+
return false;
451+
}
452+
453+
const void *data() const {
454+
return &x86_64;
455+
}
456+
457+
size_t size() const {
458+
switch (m_arch) {
459+
case llvm::Triple::ArchType::x86_64:
460+
return sizeof(x86_64);
461+
case llvm::Triple::ArchType::aarch64:
462+
return sizeof(arm64);
463+
default:
464+
break;
465+
}
466+
return 0;
467+
}
468+
469+
};
470+
384471
// Function returns start and size of the memory region that contains
385472
// memory location pointed to by the current stack pointer.
386473
llvm::Expected<std::pair<addr_t, addr_t>>
@@ -434,11 +521,19 @@ Status MinidumpFileBuilder::AddThreadList(const lldb::ProcessSP &process_sp) {
434521
return error;
435522
}
436523
RegisterContext *reg_ctx = reg_ctx_sp.get();
437-
auto thread_context = GetThreadContext_64(reg_ctx);
438-
uint64_t rsp = read_register_u64_raw(reg_ctx, "rsp");
439-
auto expected_address_range = findStackHelper(process_sp, rsp);
524+
Target &target = process_sp->GetTarget();
525+
const ArchSpec &arch = target.GetArchitecture();
526+
ArchThreadContexts thread_context(arch.GetMachine());
527+
if (!thread_context.prepareRegisterContext(reg_ctx)) {
528+
error.SetErrorStringWithFormat("architecture %s not supported.",
529+
arch.GetTriple().getArchName().str().c_str());
530+
return error;
531+
}
532+
uint64_t sp = reg_ctx->GetSP();
533+
auto expected_address_range = findStackHelper(process_sp, sp);
440534

441535
if (!expected_address_range) {
536+
consumeError(expected_address_range.takeError());
442537
error.SetErrorString("Unable to get the stack address.");
443538
return error;
444539
}
@@ -468,13 +563,14 @@ Status MinidumpFileBuilder::AddThreadList(const lldb::ProcessSP &process_sp) {
468563

469564
LocationDescriptor thread_context_memory_locator;
470565
thread_context_memory_locator.DataSize =
471-
static_cast<llvm::support::ulittle32_t>(sizeof(thread_context));
566+
static_cast<llvm::support::ulittle32_t>(thread_context.size());
472567
thread_context_memory_locator.RVA = static_cast<llvm::support::ulittle32_t>(
473568
size_before + thread_stream_size + helper_data.GetByteSize());
569+
// Cache thie thread context memory so we can reuse for exceptions.
570+
m_tid_to_reg_ctx[thread_sp->GetID()] = thread_context_memory_locator;
571+
572+
helper_data.AppendData(thread_context.data(), thread_context.size());
474573

475-
helper_data.AppendData(
476-
&thread_context,
477-
sizeof(lldb_private::minidump::MinidumpContext_x86_64));
478574

479575
llvm::minidump::Thread t;
480576
t.ThreadId = static_cast<llvm::support::ulittle32_t>(thread_sp->GetID());
@@ -492,68 +588,55 @@ Status MinidumpFileBuilder::AddThreadList(const lldb::ProcessSP &process_sp) {
492588
return Status();
493589
}
494590

495-
Status MinidumpFileBuilder::AddException(const lldb::ProcessSP &process_sp) {
496-
Status error;
591+
void MinidumpFileBuilder::AddExceptions(const lldb::ProcessSP &process_sp) {
497592
lldb_private::ThreadList thread_list = process_sp->GetThreadList();
498593

499594
const uint32_t num_threads = thread_list.GetSize();
500-
uint32_t stop_reason_thread_idx = 0;
501-
for (stop_reason_thread_idx = 0; stop_reason_thread_idx < num_threads;
502-
++stop_reason_thread_idx) {
503-
ThreadSP thread_sp(thread_list.GetThreadAtIndex(stop_reason_thread_idx));
595+
for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) {
596+
ThreadSP thread_sp(thread_list.GetThreadAtIndex(thread_idx));
504597
StopInfoSP stop_info_sp = thread_sp->GetStopInfo();
505-
506-
if (stop_info_sp && stop_info_sp->IsValid())
507-
break;
508-
}
509-
510-
if (stop_reason_thread_idx == num_threads) {
511-
error.SetErrorString("No stop reason thread found.");
512-
return error;
598+
bool add_exception = false;
599+
if (stop_info_sp) {
600+
switch (stop_info_sp->GetStopReason()) {
601+
case eStopReasonSignal:
602+
case eStopReasonException:
603+
add_exception = true;
604+
break;
605+
default:
606+
break;
607+
}
608+
}
609+
if (add_exception) {
610+
constexpr size_t minidump_exception_size =
611+
sizeof(llvm::minidump::ExceptionStream);
612+
AddDirectory(StreamType::Exception, minidump_exception_size);
613+
StopInfoSP stop_info_sp = thread_sp->GetStopInfo();
614+
RegisterContextSP reg_ctx_sp(thread_sp->GetRegisterContext());
615+
Exception exp_record = {};
616+
exp_record.ExceptionCode =
617+
static_cast<llvm::support::ulittle32_t>(stop_info_sp->GetValue());
618+
exp_record.ExceptionFlags = static_cast<llvm::support::ulittle32_t>(0);
619+
exp_record.ExceptionRecord = static_cast<llvm::support::ulittle64_t>(0);
620+
exp_record.ExceptionAddress = reg_ctx_sp->GetPC();
621+
exp_record.NumberParameters = static_cast<llvm::support::ulittle32_t>(0);
622+
exp_record.UnusedAlignment = static_cast<llvm::support::ulittle32_t>(0);
623+
// exp_record.ExceptionInformation;
624+
625+
ExceptionStream exp_stream;
626+
exp_stream.ThreadId =
627+
static_cast<llvm::support::ulittle32_t>(thread_sp->GetID());
628+
exp_stream.UnusedAlignment = static_cast<llvm::support::ulittle32_t>(0);
629+
exp_stream.ExceptionRecord = exp_record;
630+
auto Iter = m_tid_to_reg_ctx.find(thread_sp->GetID());
631+
if (Iter != m_tid_to_reg_ctx.end()) {
632+
exp_stream.ThreadContext = Iter->second;
633+
} else {
634+
exp_stream.ThreadContext.DataSize = 0;
635+
exp_stream.ThreadContext.RVA = 0;
636+
}
637+
m_data.AppendData(&exp_stream, minidump_exception_size);
638+
}
513639
}
514-
515-
constexpr size_t minidump_exception_size =
516-
sizeof(llvm::minidump::ExceptionStream);
517-
AddDirectory(StreamType::Exception, minidump_exception_size);
518-
size_t size_before = GetCurrentDataEndOffset();
519-
520-
ThreadSP thread_sp(thread_list.GetThreadAtIndex(stop_reason_thread_idx));
521-
RegisterContextSP reg_ctx_sp(thread_sp->GetRegisterContext());
522-
RegisterContext *reg_ctx = reg_ctx_sp.get();
523-
auto thread_context = GetThreadContext_64(reg_ctx);
524-
StopInfoSP stop_info_sp = thread_sp->GetStopInfo();
525-
526-
DataBufferHeap helper_data;
527-
528-
LocationDescriptor thread_context_memory_locator;
529-
thread_context_memory_locator.DataSize =
530-
static_cast<llvm::support::ulittle32_t>(sizeof(thread_context));
531-
thread_context_memory_locator.RVA = static_cast<llvm::support::ulittle32_t>(
532-
size_before + minidump_exception_size + helper_data.GetByteSize());
533-
534-
helper_data.AppendData(
535-
&thread_context, sizeof(lldb_private::minidump::MinidumpContext_x86_64));
536-
537-
Exception exp_record = {};
538-
exp_record.ExceptionCode =
539-
static_cast<llvm::support::ulittle32_t>(stop_info_sp->GetValue());
540-
exp_record.ExceptionFlags = static_cast<llvm::support::ulittle32_t>(0);
541-
exp_record.ExceptionRecord = static_cast<llvm::support::ulittle64_t>(0);
542-
exp_record.ExceptionAddress = read_register_u64(reg_ctx, "rip");
543-
exp_record.NumberParameters = static_cast<llvm::support::ulittle32_t>(0);
544-
exp_record.UnusedAlignment = static_cast<llvm::support::ulittle32_t>(0);
545-
// exp_record.ExceptionInformation;
546-
547-
ExceptionStream exp_stream;
548-
exp_stream.ThreadId =
549-
static_cast<llvm::support::ulittle32_t>(thread_sp->GetID());
550-
exp_stream.UnusedAlignment = static_cast<llvm::support::ulittle32_t>(0);
551-
exp_stream.ExceptionRecord = exp_record;
552-
exp_stream.ThreadContext = thread_context_memory_locator;
553-
554-
m_data.AppendData(&exp_stream, minidump_exception_size);
555-
m_data.AppendData(helper_data.GetBytes(), helper_data.GetByteSize());
556-
return error;
557640
}
558641

559642
lldb_private::Status

lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.h

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#define LLDB_SOURCE_PLUGINS_OBJECTFILE_MINIDUMP_MINIDUMPFILEBUILDER_H
1818

1919
#include <cstddef>
20+
#include <map>
2021

2122
#include "lldb/Target/Target.h"
2223
#include "lldb/Utility/DataBufferHeap.h"
@@ -59,10 +60,8 @@ class MinidumpFileBuilder {
5960
// at the moment of core saving. Contains information about thread
6061
// contexts.
6162
lldb_private::Status AddThreadList(const lldb::ProcessSP &process_sp);
62-
// Add Exception stream, this contains information about the exception
63-
// that stopped the process. In case no thread made exception it return
64-
// failed status.
65-
lldb_private::Status AddException(const lldb::ProcessSP &process_sp);
63+
// Add Exception streams for any threads that stopped with exceptions.
64+
void AddExceptions(const lldb::ProcessSP &process_sp);
6665
// Add MemoryList stream, containing dumps of important memory segments
6766
lldb_private::Status AddMemoryList(const lldb::ProcessSP &process_sp,
6867
lldb::SaveCoreStyle core_style);
@@ -88,6 +87,11 @@ class MinidumpFileBuilder {
8887
// Main data buffer consisting of data without the minidump header and
8988
// directories
9089
lldb_private::DataBufferHeap m_data;
90+
91+
// More that one place can mention the register thread context locations,
92+
// so when we emit the thread contents, remember where it is so we don't have
93+
// to duplicate it in the exception data.
94+
std::map<lldb::tid_t, llvm::minidump::LocationDescriptor> m_tid_to_reg_ctx;
9195
};
9296

9397
#endif // LLDB_SOURCE_PLUGINS_OBJECTFILE_MINIDUMP_MINIDUMPFILEBUILDER_H

lldb/source/Plugins/ObjectFile/Minidump/ObjectFileMinidump.cpp

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -78,19 +78,16 @@ bool ObjectFileMinidump::SaveCore(const lldb::ProcessSP &process_sp,
7878

7979
builder.AddMiscInfo(process_sp);
8080

81-
if (target.GetArchitecture().GetMachine() == llvm::Triple::ArchType::x86_64) {
82-
error = builder.AddThreadList(process_sp);
83-
if (error.Fail())
84-
return false;
85-
86-
error = builder.AddException(process_sp);
87-
if (error.Fail())
88-
return false;
89-
90-
error = builder.AddMemoryList(process_sp, core_style);
91-
if (error.Fail())
92-
return false;
93-
}
81+
error = builder.AddThreadList(process_sp);
82+
if (error.Fail())
83+
return false;
84+
85+
// Add any exceptions but only if there are any in any threads.
86+
builder.AddExceptions(process_sp);
87+
88+
error = builder.AddMemoryList(process_sp, core_style);
89+
if (error.Fail())
90+
return false;
9491

9592
if (target.GetArchitecture().GetTriple().getOS() ==
9693
llvm::Triple::OSType::Linux) {

lldb/source/Plugins/Process/minidump/RegisterContextMinidump_ARM64.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,13 @@ class RegisterContextMinidump_ARM64 : public lldb_private::RegisterContext {
6767
uint8_t v[32 * 16]; // 32 128-bit floating point registers
6868
};
6969

70-
protected:
7170
enum class Flags : uint32_t {
7271
ARM64_Flag = 0x80000000,
7372
Integer = ARM64_Flag | 0x00000002,
7473
FloatingPoint = ARM64_Flag | 0x00000004,
7574
LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ FloatingPoint)
7675
};
76+
protected:
7777
Context m_regs;
7878
};
7979

0 commit comments

Comments
 (0)