#include "util/linux/ptracer.h"
#include <errno.h>
#include <linux/elf.h>
#include <string.h>
#include <sys/ptrace.h>
#include <sys/uio.h>
#include "base/check_op.h"
#include "base/logging.h"
#include "build/build_config.h"
#include "util/misc/from_pointer_cast.h"
#if defined(ARCH_CPU_X86_FAMILY)
#include <asm/ldt.h>
#endif
namespace crashpad {
namespace {
#if defined(ARCH_CPU_X86_FAMILY)
template <typename Destination>
bool GetRegisterSet(pid_t tid, int set, Destination* dest, bool can_log) { … }
bool GetFloatingPointRegisters32(pid_t tid,
FloatContext* context,
bool can_log) { … }
bool GetFloatingPointRegisters64(pid_t tid,
FloatContext* context,
bool can_log) { … }
bool GetThreadArea32(pid_t tid,
const ThreadContext& context,
LinuxVMAddress* address,
bool can_log) { … }
bool GetThreadArea64(pid_t tid,
const ThreadContext& context,
LinuxVMAddress* address,
bool can_log) { … }
#elif defined(ARCH_CPU_ARM_FAMILY)
#if defined(ARCH_CPU_ARMEL)
bool GetGeneralPurposeRegistersLegacy(pid_t tid,
ThreadContext* context,
bool can_log) {
if (ptrace(PTRACE_GETREGS, tid, nullptr, &context->t32) != 0) {
PLOG_IF(ERROR, can_log) << "ptrace";
return false;
}
return true;
}
bool GetFloatingPointRegistersLegacy(pid_t tid,
FloatContext* context,
bool can_log) {
if (ptrace(PTRACE_GETFPREGS, tid, nullptr, &context->f32.fpregs) != 0) {
PLOG_IF(ERROR, can_log) << "ptrace";
return false;
}
context->f32.have_fpregs = true;
if (ptrace(PTRACE_GETVFPREGS, tid, nullptr, &context->f32.vfp) != 0) {
switch (errno) {
case EINVAL:
break;
default:
PLOG_IF(ERROR, can_log) << "ptrace";
return false;
}
} else {
context->f32.have_vfp = true;
}
return true;
}
#endif
constexpr size_t kArmVfpSize = 32 * 8 + 4;
bool GetFloatingPointRegisters32(pid_t tid,
FloatContext* context,
bool can_log) {
context->f32.have_fpregs = false;
context->f32.have_vfp = false;
iovec iov;
iov.iov_base = &context->f32.fpregs;
iov.iov_len = sizeof(context->f32.fpregs);
if (ptrace(
PTRACE_GETREGSET, tid, reinterpret_cast<void*>(NT_PRFPREG), &iov) !=
0) {
switch (errno) {
#if defined(ARCH_CPU_ARMEL)
case EIO:
return GetFloatingPointRegistersLegacy(tid, context, can_log);
#endif
case EINVAL:
break;
default:
PLOG_IF(ERROR, can_log) << "ptrace";
return false;
}
} else {
if (iov.iov_len != sizeof(context->f32.fpregs)) {
LOG_IF(ERROR, can_log) << "Unexpected registers size " << iov.iov_len
<< " != " << sizeof(context->f32.fpregs);
return false;
}
context->f32.have_fpregs = true;
}
iov.iov_base = &context->f32.vfp;
iov.iov_len = sizeof(context->f32.vfp);
if (ptrace(
PTRACE_GETREGSET, tid, reinterpret_cast<void*>(NT_ARM_VFP), &iov) !=
0) {
switch (errno) {
case EINVAL:
break;
default:
PLOG_IF(ERROR, can_log) << "ptrace";
return false;
}
} else {
if (iov.iov_len != kArmVfpSize && iov.iov_len != sizeof(context->f32.vfp)) {
LOG_IF(ERROR, can_log) << "Unexpected registers size " << iov.iov_len
<< " != " << sizeof(context->f32.vfp);
return false;
}
context->f32.have_vfp = true;
}
if (!(context->f32.have_fpregs || context->f32.have_vfp)) {
LOG_IF(ERROR, can_log) << "Unable to collect registers";
return false;
}
return true;
}
bool GetFloatingPointRegisters64(pid_t tid,
FloatContext* context,
bool can_log) {
iovec iov;
iov.iov_base = context;
iov.iov_len = sizeof(*context);
if (ptrace(
PTRACE_GETREGSET, tid, reinterpret_cast<void*>(NT_PRFPREG), &iov) !=
0) {
PLOG_IF(ERROR, can_log) << "ptrace";
return false;
}
if (iov.iov_len != sizeof(context->f64)) {
LOG_IF(ERROR, can_log) << "Unexpected registers size " << iov.iov_len
<< " != " << sizeof(context->f64);
return false;
}
return true;
}
bool GetThreadArea32(pid_t tid,
const ThreadContext& context,
LinuxVMAddress* address,
bool can_log) {
#if defined(ARCH_CPU_ARMEL)
void* result;
if (ptrace(PTRACE_GET_THREAD_AREA, tid, nullptr, &result) != 0) {
PLOG_IF(ERROR, can_log) << "ptrace";
return false;
}
*address = FromPointerCast<LinuxVMAddress>(result);
return true;
#else
LOG_IF(WARNING, can_log)
<< "64-bit ARM cannot trace TLS area for a 32-bit process";
return false;
#endif
}
bool GetThreadArea64(pid_t tid,
const ThreadContext& context,
LinuxVMAddress* address,
bool can_log) {
iovec iov;
iov.iov_base = address;
iov.iov_len = sizeof(*address);
if (ptrace(
PTRACE_GETREGSET, tid, reinterpret_cast<void*>(NT_ARM_TLS), &iov) !=
0) {
PLOG_IF(ERROR, can_log) << "ptrace";
return false;
}
if (iov.iov_len != 8) {
LOG_IF(ERROR, can_log) << "address size mismatch";
return false;
}
return true;
}
#elif defined(ARCH_CPU_MIPS_FAMILY)
bool GetGeneralPurposeRegistersLegacy(pid_t tid,
ThreadContext* context,
bool can_log) {
ThreadContext context_buffer;
if (ptrace(PTRACE_GETREGS, tid, nullptr, &context_buffer.t64) != 0) {
PLOG_IF(ERROR, can_log) << "ptrace";
return false;
}
#if defined(ARCH_CPU_MIPSEL)
#define THREAD_CONTEXT_FIELD …
#elif defined(ARCH_CPU_MIPS64EL)
#define THREAD_CONTEXT_FIELD …
#endif
for (size_t reg = 0; reg < 32; ++reg) {
context->THREAD_CONTEXT_FIELD.regs[reg] = context_buffer.t64.regs[reg];
}
context->THREAD_CONTEXT_FIELD.lo = context_buffer.t64.lo;
context->THREAD_CONTEXT_FIELD.hi = context_buffer.t64.hi;
context->THREAD_CONTEXT_FIELD.cp0_epc = context_buffer.t64.cp0_epc;
context->THREAD_CONTEXT_FIELD.cp0_badvaddr = context_buffer.t64.cp0_badvaddr;
context->THREAD_CONTEXT_FIELD.cp0_status = context_buffer.t64.cp0_status;
context->THREAD_CONTEXT_FIELD.cp0_cause = context_buffer.t64.cp0_cause;
#undef THREAD_CONTEXT_FIELD
return true;
}
bool GetFloatingPointRegistersLegacy(pid_t tid,
FloatContext* context,
bool can_log) {
if (ptrace(PTRACE_GETFPREGS, tid, nullptr, &context->f32.fpregs) != 0) {
PLOG_IF(ERROR, can_log) << "ptrace";
return false;
}
return true;
}
bool GetFloatingPointRegisters32(pid_t tid,
FloatContext* context,
bool can_log) {
iovec iov;
iov.iov_base = &context->f32.fpregs;
iov.iov_len = sizeof(context->f32.fpregs);
if (ptrace(PTRACE_GETFPREGS, tid, nullptr, &context->f32.fpregs) != 0) {
switch (errno) {
case EINVAL:
break;
case EIO:
return GetFloatingPointRegistersLegacy(tid, context, can_log);
default:
PLOG_IF(ERROR, can_log) << "ptrace";
return false;
}
}
return true;
}
bool GetFloatingPointRegisters64(pid_t tid,
FloatContext* context,
bool can_log) {
iovec iov;
iov.iov_base = &context->f64.fpregs;
iov.iov_len = sizeof(context->f64.fpregs);
if (ptrace(PTRACE_GETFPREGS, tid, nullptr, &context->f64.fpregs) != 0) {
switch (errno) {
case EINVAL:
break;
case EIO:
return GetFloatingPointRegistersLegacy(tid, context, can_log);
default:
PLOG_IF(ERROR, can_log) << "ptrace";
return false;
}
}
return true;
}
bool GetThreadArea32(pid_t tid,
const ThreadContext& context,
LinuxVMAddress* address,
bool can_log) {
#if defined(ARCH_CPU_MIPSEL)
void* result;
if (ptrace(PTRACE_GET_THREAD_AREA, tid, nullptr, &result) != 0) {
PLOG_IF(ERROR, can_log) << "ptrace";
return false;
}
*address = FromPointerCast<LinuxVMAddress>(result);
return true;
#else
return false;
#endif
}
bool GetThreadArea64(pid_t tid,
const ThreadContext& context,
LinuxVMAddress* address,
bool can_log) {
void* result;
#if defined(ARCH_CPU_MIPSEL)
if (ptrace(PTRACE_GET_THREAD_AREA_3264, tid, nullptr, &result) != 0) {
#else
if (ptrace(PTRACE_GET_THREAD_AREA, tid, nullptr, &result) != 0) {
#endif
PLOG_IF(ERROR, can_log) << "ptrace";
return false;
}
*address = FromPointerCast<LinuxVMAddress>(result);
return true;
}
#elif defined(ARCH_CPU_RISCV64)
bool GetFloatingPointRegisters64(pid_t tid,
FloatContext* context,
bool can_log) {
iovec iov;
iov.iov_base = context;
iov.iov_len = sizeof(*context);
if (ptrace(
PTRACE_GETREGSET, tid, reinterpret_cast<void*>(NT_PRFPREG), &iov) !=
0) {
PLOG_IF(ERROR, can_log) << "ptrace";
return false;
}
if (iov.iov_len != sizeof(context->f64)) {
LOG_IF(ERROR, can_log) << "Unexpected registers size " << iov.iov_len
<< " != " << sizeof(context->f64);
return false;
}
return true;
}
bool GetThreadArea64(pid_t tid,
const ThreadContext& context,
LinuxVMAddress* address,
bool can_log) {
*address = context.t64.regs[3];
return true;
}
#else
#error Port.
#endif
size_t GetGeneralPurposeRegistersAndLength(pid_t tid,
ThreadContext* context,
bool can_log) { … }
#if !defined(ARCH_CPU_RISCV64)
bool GetGeneralPurposeRegisters32(pid_t tid,
ThreadContext* context,
bool can_log) { … }
#endif
bool GetGeneralPurposeRegisters64(pid_t tid,
ThreadContext* context,
bool can_log) { … }
}
Ptracer::Ptracer(bool can_log)
: … { … }
Ptracer::Ptracer(bool is_64_bit, bool can_log)
: … { … }
Ptracer::~Ptracer() { … }
bool Ptracer::Initialize(pid_t pid) { … }
bool Ptracer::Is64Bit() { … }
bool Ptracer::GetThreadInfo(pid_t tid, ThreadInfo* info) { … }
ssize_t Ptracer::ReadUpTo(pid_t pid,
LinuxVMAddress address,
size_t size,
char* buffer) { … }
ssize_t Ptracer::ReadLastBytes(pid_t pid,
LinuxVMAddress address,
size_t size,
char* buffer) { … }
}