586 lines
18 KiB
C++
586 lines
18 KiB
C++
|
// Copyright 2017 The Crashpad Authors. All rights reserved.
|
|||
|
//
|
|||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|||
|
// you may not use this file except in compliance with the License.
|
|||
|
// You may obtain a copy of the License at
|
|||
|
//
|
|||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|||
|
//
|
|||
|
// Unless required by applicable law or agreed to in writing, software
|
|||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||
|
// See the License for the specific language governing permissions and
|
|||
|
// limitations under the License.
|
|||
|
|
|||
|
#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/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) {
|
|||
|
iovec iov;
|
|||
|
iov.iov_base = dest;
|
|||
|
iov.iov_len = sizeof(*dest);
|
|||
|
if (ptrace(PTRACE_GETREGSET, tid, reinterpret_cast<void*>(set), &iov) != 0) {
|
|||
|
PLOG_IF(ERROR, can_log) << "ptrace";
|
|||
|
return false;
|
|||
|
}
|
|||
|
if (iov.iov_len != sizeof(*dest)) {
|
|||
|
LOG_IF(ERROR, can_log) << "Unexpected registers size " << iov.iov_len
|
|||
|
<< " != " << sizeof(*dest);
|
|||
|
return false;
|
|||
|
}
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
bool GetFloatingPointRegisters32(pid_t tid,
|
|||
|
FloatContext* context,
|
|||
|
bool can_log) {
|
|||
|
return GetRegisterSet(tid, NT_PRXFPREG, &context->f32.fxsave, can_log);
|
|||
|
}
|
|||
|
|
|||
|
bool GetFloatingPointRegisters64(pid_t tid,
|
|||
|
FloatContext* context,
|
|||
|
bool can_log) {
|
|||
|
return GetRegisterSet(tid, NT_PRFPREG, &context->f64.fxsave, can_log);
|
|||
|
}
|
|||
|
|
|||
|
bool GetThreadArea32(pid_t tid,
|
|||
|
const ThreadContext& context,
|
|||
|
LinuxVMAddress* address,
|
|||
|
bool can_log) {
|
|||
|
size_t index = (context.t32.xgs & 0xffff) >> 3;
|
|||
|
user_desc desc;
|
|||
|
if (ptrace(
|
|||
|
PTRACE_GET_THREAD_AREA, tid, reinterpret_cast<void*>(index), &desc) !=
|
|||
|
0) {
|
|||
|
PLOG_IF(ERROR, can_log) << "ptrace";
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
*address = desc.base_addr;
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
bool GetThreadArea64(pid_t tid,
|
|||
|
const ThreadContext& context,
|
|||
|
LinuxVMAddress* address,
|
|||
|
bool can_log) {
|
|||
|
*address = context.t64.fs_base;
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
#elif defined(ARCH_CPU_ARM_FAMILY)
|
|||
|
|
|||
|
#if defined(ARCH_CPU_ARMEL)
|
|||
|
// PTRACE_GETREGSET, introduced in Linux 2.6.34 (2225a122ae26), requires kernel
|
|||
|
// support enabled by HAVE_ARCH_TRACEHOOK. This has been set for x86 (including
|
|||
|
// x86_64) since Linux 2.6.28 (99bbc4b1e677a), but for ARM only since
|
|||
|
// Linux 3.5.0 (0693bf68148c4). Older Linux kernels support PTRACE_GETREGS,
|
|||
|
// PTRACE_GETFPREGS, and PTRACE_GETVFPREGS instead, which don't allow checking
|
|||
|
// the size of data copied.
|
|||
|
//
|
|||
|
// Fortunately, 64-bit ARM support only appeared in Linux 3.7.0, so if
|
|||
|
// PTRACE_GETREGSET fails on ARM with EIO, indicating that the request is not
|
|||
|
// supported, the kernel must be old enough that 64-bit ARM isn’t supported
|
|||
|
// either.
|
|||
|
//
|
|||
|
// TODO(mark): Once helpers to interpret the kernel version are available, add
|
|||
|
// a DCHECK to ensure that the kernel is older than 3.5.
|
|||
|
|
|||
|
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:
|
|||
|
// These registers are optional on 32-bit ARM cpus
|
|||
|
break;
|
|||
|
default:
|
|||
|
PLOG_IF(ERROR, can_log) << "ptrace";
|
|||
|
return false;
|
|||
|
}
|
|||
|
} else {
|
|||
|
context->f32.have_vfp = true;
|
|||
|
}
|
|||
|
return true;
|
|||
|
}
|
|||
|
#endif // ARCH_CPU_ARMEL
|
|||
|
|
|||
|
// Normally, the Linux kernel will copy out register sets according to the size
|
|||
|
// of the struct that contains them. Tracing a 32-bit ARM process running in
|
|||
|
// compatibility mode on a 64-bit ARM cpu will only copy data for the number of
|
|||
|
// registers times the size of the register, which won't include any possible
|
|||
|
// trailing padding in the struct. These are the sizes of the register data, not
|
|||
|
// including any possible padding.
|
|||
|
constexpr size_t kArmVfpSize = 32 * 8 + 4;
|
|||
|
|
|||
|
// Target is 32-bit
|
|||
|
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 // ARCH_CPU_ARMEL
|
|||
|
case EINVAL:
|
|||
|
// A 32-bit process running on a 64-bit CPU doesn't have this register
|
|||
|
// set. It should have a VFP register set instead.
|
|||
|
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:
|
|||
|
// VFP may not be present on 32-bit ARM cpus.
|
|||
|
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
|
|||
|
// TODO(jperaza): it doesn't look like there is a way for a 64-bit ARM process
|
|||
|
// to get the thread area for a 32-bit ARM process with ptrace.
|
|||
|
LOG_IF(WARNING, can_log)
|
|||
|
<< "64-bit ARM cannot trace TLS area for a 32-bit process";
|
|||
|
return false;
|
|||
|
#endif // ARCH_CPU_ARMEL
|
|||
|
}
|
|||
|
|
|||
|
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)
|
|||
|
// PTRACE_GETREGSET, introduced in Linux 2.6.34 (2225a122ae26), requires kernel
|
|||
|
// support enabled by HAVE_ARCH_TRACEHOOK. This has been set for x86 (including
|
|||
|
// x86_64) since Linux 2.6.28 (99bbc4b1e677a), but for MIPS only since
|
|||
|
// Linux 3.13 (c0ff3c53d4f99). Older Linux kernels support PTRACE_GETREGS,
|
|||
|
// and PTRACE_GETFPREGS instead, which don't allow checking the size of data
|
|||
|
// copied. Also, PTRACE_GETREGS assumes register size of 64 bits even for 32 bit
|
|||
|
// MIPS CPU (contrary to PTRACE_GETREGSET behavior), so we need buffer
|
|||
|
// structure here.
|
|||
|
|
|||
|
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;
|
|||
|
}
|
|||
|
// Bitness of target process can't be determined through ptrace here, so we
|
|||
|
// assume target process has the same as current process, making cross-bit
|
|||
|
// ptrace unsupported on MIPS for kernels older than 3.13
|
|||
|
#if defined(ARCH_CPU_MIPSEL)
|
|||
|
#define THREAD_CONTEXT_FIELD t32
|
|||
|
#elif defined(ARCH_CPU_MIPS64EL)
|
|||
|
#define THREAD_CONTEXT_FIELD t64
|
|||
|
#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:
|
|||
|
// fp may not be present
|
|||
|
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:
|
|||
|
// fp may not be present
|
|||
|
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;
|
|||
|
}
|
|||
|
|
|||
|
#else
|
|||
|
#error Port.
|
|||
|
#endif // ARCH_CPU_X86_FAMILY
|
|||
|
|
|||
|
size_t GetGeneralPurposeRegistersAndLength(pid_t tid,
|
|||
|
ThreadContext* context,
|
|||
|
bool can_log) {
|
|||
|
iovec iov;
|
|||
|
iov.iov_base = context;
|
|||
|
iov.iov_len = sizeof(*context);
|
|||
|
if (ptrace(
|
|||
|
PTRACE_GETREGSET, tid, reinterpret_cast<void*>(NT_PRSTATUS), &iov) !=
|
|||
|
0) {
|
|||
|
switch (errno) {
|
|||
|
#if defined(ARCH_CPU_ARMEL) || defined(ARCH_CPU_MIPS_FAMILY)
|
|||
|
case EIO:
|
|||
|
return GetGeneralPurposeRegistersLegacy(tid, context, can_log)
|
|||
|
? sizeof(context->t32)
|
|||
|
: 0;
|
|||
|
#endif // ARCH_CPU_ARMEL
|
|||
|
default:
|
|||
|
PLOG_IF(ERROR, can_log) << "ptrace";
|
|||
|
return 0;
|
|||
|
}
|
|||
|
}
|
|||
|
return iov.iov_len;
|
|||
|
}
|
|||
|
|
|||
|
bool GetGeneralPurposeRegisters32(pid_t tid,
|
|||
|
ThreadContext* context,
|
|||
|
bool can_log) {
|
|||
|
size_t length = GetGeneralPurposeRegistersAndLength(tid, context, can_log);
|
|||
|
if (length != sizeof(context->t32)) {
|
|||
|
LOG_IF(ERROR, can_log) << "Unexpected registers size " << length
|
|||
|
<< " != " << sizeof(context->t32);
|
|||
|
return false;
|
|||
|
}
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
bool GetGeneralPurposeRegisters64(pid_t tid,
|
|||
|
ThreadContext* context,
|
|||
|
bool can_log) {
|
|||
|
size_t length = GetGeneralPurposeRegistersAndLength(tid, context, can_log);
|
|||
|
if (length != sizeof(context->t64)) {
|
|||
|
LOG_IF(ERROR, can_log) << "Unexpected registers size " << length
|
|||
|
<< " != " << sizeof(context->t64);
|
|||
|
return false;
|
|||
|
}
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
} // namespace
|
|||
|
|
|||
|
Ptracer::Ptracer(bool can_log)
|
|||
|
: is_64_bit_(false), can_log_(can_log), initialized_() {}
|
|||
|
|
|||
|
Ptracer::Ptracer(bool is_64_bit, bool can_log)
|
|||
|
: is_64_bit_(is_64_bit), can_log_(can_log), initialized_() {
|
|||
|
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
|
|||
|
INITIALIZATION_STATE_SET_VALID(initialized_);
|
|||
|
}
|
|||
|
|
|||
|
Ptracer::~Ptracer() {}
|
|||
|
|
|||
|
bool Ptracer::Initialize(pid_t pid) {
|
|||
|
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
|
|||
|
|
|||
|
ThreadContext context;
|
|||
|
size_t length = GetGeneralPurposeRegistersAndLength(pid, &context, can_log_);
|
|||
|
if (length == sizeof(context.t64)) {
|
|||
|
is_64_bit_ = true;
|
|||
|
} else if (length == sizeof(context.t32)) {
|
|||
|
is_64_bit_ = false;
|
|||
|
} else {
|
|||
|
LOG_IF(ERROR, can_log_)
|
|||
|
<< "Unexpected registers size " << length
|
|||
|
<< " != " << sizeof(context.t64) << ", " << sizeof(context.t32);
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
INITIALIZATION_STATE_SET_VALID(initialized_);
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
bool Ptracer::Is64Bit() {
|
|||
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|||
|
return is_64_bit_;
|
|||
|
}
|
|||
|
|
|||
|
bool Ptracer::GetThreadInfo(pid_t tid, ThreadInfo* info) {
|
|||
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|||
|
|
|||
|
if (is_64_bit_) {
|
|||
|
return GetGeneralPurposeRegisters64(tid, &info->thread_context, can_log_) &&
|
|||
|
GetFloatingPointRegisters64(tid, &info->float_context, can_log_) &&
|
|||
|
GetThreadArea64(tid,
|
|||
|
info->thread_context,
|
|||
|
&info->thread_specific_data_address,
|
|||
|
can_log_);
|
|||
|
}
|
|||
|
|
|||
|
return GetGeneralPurposeRegisters32(tid, &info->thread_context, can_log_) &&
|
|||
|
GetFloatingPointRegisters32(tid, &info->float_context, can_log_) &&
|
|||
|
GetThreadArea32(tid,
|
|||
|
info->thread_context,
|
|||
|
&info->thread_specific_data_address,
|
|||
|
can_log_);
|
|||
|
}
|
|||
|
|
|||
|
ssize_t Ptracer::ReadUpTo(pid_t pid,
|
|||
|
LinuxVMAddress address,
|
|||
|
size_t size,
|
|||
|
char* buffer) {
|
|||
|
size_t bytes_read = 0;
|
|||
|
while (size > 0) {
|
|||
|
errno = 0;
|
|||
|
|
|||
|
if (size >= sizeof(long)) {
|
|||
|
*reinterpret_cast<long*>(buffer) =
|
|||
|
ptrace(PTRACE_PEEKDATA, pid, address, nullptr);
|
|||
|
|
|||
|
if (errno == EIO) {
|
|||
|
ssize_t last_bytes = ReadLastBytes(pid, address, size, buffer);
|
|||
|
return last_bytes >= 0 ? bytes_read + last_bytes : -1;
|
|||
|
}
|
|||
|
|
|||
|
if (errno != 0) {
|
|||
|
PLOG_IF(ERROR, can_log_) << "ptrace";
|
|||
|
return -1;
|
|||
|
}
|
|||
|
|
|||
|
size -= sizeof(long);
|
|||
|
buffer += sizeof(long);
|
|||
|
address += sizeof(long);
|
|||
|
bytes_read += sizeof(long);
|
|||
|
} else {
|
|||
|
long word = ptrace(PTRACE_PEEKDATA, pid, address, nullptr);
|
|||
|
|
|||
|
if (errno == 0) {
|
|||
|
memcpy(buffer, reinterpret_cast<char*>(&word), size);
|
|||
|
return bytes_read + size;
|
|||
|
}
|
|||
|
|
|||
|
if (errno == EIO) {
|
|||
|
ssize_t last_bytes = ReadLastBytes(pid, address, size, buffer);
|
|||
|
return last_bytes >= 0 ? bytes_read + last_bytes : -1;
|
|||
|
}
|
|||
|
|
|||
|
PLOG_IF(ERROR, can_log_);
|
|||
|
return -1;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return bytes_read;
|
|||
|
}
|
|||
|
|
|||
|
// Handles an EIO by reading at most size bytes from address into buffer if
|
|||
|
// address was within a word of a possible page boundary, by aligning to read
|
|||
|
// the last word of the page and extracting the desired bytes.
|
|||
|
ssize_t Ptracer::ReadLastBytes(pid_t pid,
|
|||
|
LinuxVMAddress address,
|
|||
|
size_t size,
|
|||
|
char* buffer) {
|
|||
|
LinuxVMAddress aligned = ((address + 4095) & ~4095) - sizeof(long);
|
|||
|
if (aligned >= address || aligned == address - sizeof(long)) {
|
|||
|
PLOG_IF(ERROR, can_log_) << "ptrace";
|
|||
|
return -1;
|
|||
|
}
|
|||
|
DCHECK_GT(aligned, address - sizeof(long));
|
|||
|
|
|||
|
errno = 0;
|
|||
|
long word = ptrace(PTRACE_PEEKDATA, pid, aligned, nullptr);
|
|||
|
if (errno != 0) {
|
|||
|
PLOG_IF(ERROR, can_log_) << "ptrace";
|
|||
|
return -1;
|
|||
|
}
|
|||
|
|
|||
|
size_t bytes_read = address - aligned;
|
|||
|
size_t last_bytes = std::min(sizeof(long) - bytes_read, size);
|
|||
|
memcpy(buffer, reinterpret_cast<char*>(&word) + bytes_read, last_bytes);
|
|||
|
return last_bytes;
|
|||
|
}
|
|||
|
|
|||
|
} // namespace crashpad
|