blob: a60020846dd162a7fc2e52f4d16dad3ae19c2bee [file] [edit]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This file provides functions that provide access to key registers in the
// native register context.
#ifndef BASE_PROFILER_REGISTER_CONTEXT_REGISTERS_H_
#define BASE_PROFILER_REGISTER_CONTEXT_REGISTERS_H_
#include <ptrauth.h>
#include <stdint.h>
#include "base/compiler_specific.h"
#include "build/build_config.h"
#if BUILDFLAG(IS_WIN)
#include <windows.h>
#elif BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
#include <sys/ucontext.h>
#elif BUILDFLAG(IS_APPLE)
#include <mach/machine/thread_status.h>
#else
#include "base/profiler/register_context.h"
#endif
namespace base {
// Helper function to account for the fact that platform-specific register state
// types may be of the same size as uintptr_t, but not of the same type or
// signedness -- e.g. unsigned int vs. unsigned long on 32-bit Windows, unsigned
// long vs. unsigned long long on Mac, long long vs. unsigned long long on
// Linux.
template <typename T>
uintptr_t& AsUintPtr(T* value) {
static_assert(sizeof(T) == sizeof(uintptr_t),
"register state type must be of equivalent size to uintptr_t");
return *reinterpret_cast<uintptr_t*>(value);
}
#if BUILDFLAG(IS_WIN)
inline uintptr_t RegisterContextStackPointer(::CONTEXT* context) {
#if defined(ARCH_CPU_X86_64)
return context->Rsp;
#elif defined(ARCH_CPU_ARM64)
return context->Sp;
#elif defined(ARCH_CPU_X86)
return context->Esp;
#else
#error "Unknown architecture"
#endif
}
inline void SetRegisterContextStackPointer(::CONTEXT* context, uintptr_t val) {
#if defined(ARCH_CPU_X86_64)
context->Rsp = val;
#elif defined(ARCH_CPU_ARM64)
context->Sp = val;
#elif defined(ARCH_CPU_X86)
context->Esp = val;
#else
#error "Unknown architecture"
#endif
}
inline uintptr_t RegisterContextFramePointer(::CONTEXT* context) {
#if defined(ARCH_CPU_X86_64)
return context->Rbp;
#elif defined(ARCH_CPU_ARM64)
return context->Fp;
#elif defined(ARCH_CPU_X86)
return context->Ebp;
#else
#error "Unknown architecture"
#endif
}
inline void SetRegisterContextFramePointer(::CONTEXT* context, uintptr_t val) {
#if defined(ARCH_CPU_X86_64)
context->Rbp = val;
#elif defined(ARCH_CPU_ARM64)
context->Fp = val;
#elif defined(ARCH_CPU_X86)
context->Ebp = val;
#else
#error "Unknown architecture"
#endif
}
inline uintptr_t RegisterContextInstructionPointer(::CONTEXT* context) {
#if defined(ARCH_CPU_X86_64)
return context->Rip;
#elif defined(ARCH_CPU_ARM64)
return context->Pc;
#elif defined(ARCH_CPU_X86)
return context->Eip;
#else
#error "Unknown architecture"
#endif
}
inline void SetRegisterContextInstructionPointer(::CONTEXT* context,
uintptr_t val) {
#if defined(ARCH_CPU_X86_64)
context->Rip = val;
#elif defined(ARCH_CPU_ARM64)
context->Pc = val;
#elif defined(ARCH_CPU_X86)
context->Eip = val;
#else
#error "Unknown architecture"
#endif
}
#elif BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
inline uintptr_t RegisterContextStackPointer(mcontext_t* context) {
#if defined(ARCH_CPU_ARMEL)
return context->arm_sp;
#elif defined(ARCH_CPU_ARM64)
return context->sp;
#elif defined(ARCH_CPU_X86)
return AsUintPtr(&context->gregs[REG_ESP]);
#elif defined(ARCH_CPU_X86_64)
return AsUintPtr(&context->gregs[REG_RSP]);
#else
// The implementations here and below are placeholders for other POSIX
// platforms that just return the first three register slots in the context.
return *reinterpret_cast<uintptr_t*>(context);
#endif
}
inline void SetRegisterContextStackPointer(mcontext_t* context, uintptr_t val) {
#if defined(ARCH_CPU_ARMEL)
context->arm_sp = val;
#elif defined(ARCH_CPU_ARM64)
context->sp = val;
#elif defined(ARCH_CPU_X86)
AsUintPtr(&context->gregs[REG_ESP]) = val;
#elif defined(ARCH_CPU_X86_64)
AsUintPtr(&context->gregs[REG_RSP]) = val;
#else
// The implementations here and below are placeholders for other POSIX
// platforms that just the first three register slots in the context.
*reinterpret_cast<uintptr_t*>(context) = val;
#endif
}
inline uintptr_t RegisterContextFramePointer(mcontext_t* context) {
#if defined(ARCH_CPU_ARMEL)
return context->arm_fp;
#elif defined(ARCH_CPU_ARM64)
// r29 is the FP register on 64-bit ARM per the Procedure Call Standard,
// section 5.1.1.
return context->regs[29];
#elif defined(ARCH_CPU_X86)
return AsUintPtr(&context->gregs[REG_EBP]);
#elif defined(ARCH_CPU_X86_64)
return AsUintPtr(&context->gregs[REG_RBP]);
#else
return *(UNSAFE_TODO(reinterpret_cast<uintptr_t*>(context) + 1));
#endif
}
inline void SetRegisterContextFramePointer(mcontext_t* context, uintptr_t val) {
#if defined(ARCH_CPU_ARMEL)
context->arm_fp = val;
#elif defined(ARCH_CPU_ARM64)
// r29 is the FP register on 64-bit ARM per the Procedure Call Standard,
// section 5.1.1.
context->regs[29] = val;
#elif defined(ARCH_CPU_X86)
AsUintPtr(&context->gregs[REG_EBP]) = val;
#elif defined(ARCH_CPU_X86_64)
AsUintPtr(&context->gregs[REG_RBP]) = val;
#else
*(UNSAFE_TODO(reinterpret_cast<uintptr_t*>(context) + 1)) = val;
#endif
}
inline uintptr_t RegisterContextInstructionPointer(mcontext_t* context) {
#if defined(ARCH_CPU_ARMEL)
return context->arm_pc;
#elif defined(ARCH_CPU_ARM64)
return context->pc;
#elif defined(ARCH_CPU_X86)
return AsUintPtr(&context->gregs[REG_EIP]);
#elif defined(ARCH_CPU_X86_64)
return AsUintPtr(&context->gregs[REG_RIP]);
#else
return *(UNSAFE_TODO(reinterpret_cast<uintptr_t*>(context) + 2));
#endif
}
inline void SetRegisterContextInstructionPointer(mcontext_t* context,
uintptr_t val) {
#if defined(ARCH_CPU_ARMEL)
context->arm_pc = val;
#elif defined(ARCH_CPU_ARM64)
context->pc = val;
#elif defined(ARCH_CPU_X86)
AsUintPtr(&context->gregs[REG_EIP]) = val;
#elif defined(ARCH_CPU_X86_64)
AsUintPtr(&context->gregs[REG_RIP]) = val;
#else
*(UNSAFE_TODO(reinterpret_cast<uintptr_t*>(context) + 2)) = val;
#endif
}
#elif BUILDFLAG(IS_APPLE) && defined(ARCH_CPU_X86_64)
inline uintptr_t RegisterContextStackPointer(x86_thread_state64_t* context) {
return context->__rsp;
}
inline void SetRegisterContextStackPointer(x86_thread_state64_t* context,
uintptr_t val) {
context->__rsp = val;
}
inline uintptr_t RegisterContextFramePointer(x86_thread_state64_t* context) {
return context->__rbp;
}
inline void SetRegisterContextFramePointer(x86_thread_state64_t* context,
uintptr_t val) {
context->__rbp = val;
}
inline uintptr_t RegisterContextInstructionPointer(
x86_thread_state64_t* context) {
return context->__rip;
}
inline void SetRegisterContextInstructionPointer(x86_thread_state64_t* context,
uintptr_t val) {
context->__rip = val;
}
#elif BUILDFLAG(IS_APPLE) && defined(ARCH_CPU_ARM64)
#if BUILDFLAG(ARCH_CPU_PTRAUTH)
inline auto& Arm64Sp(arm_thread_state64_t* ctx) {
return ctx->__opaque_sp;
}
inline auto& Arm64Fp(arm_thread_state64_t* ctx) {
return ctx->__opaque_fp;
}
inline auto& Arm64Pc(arm_thread_state64_t* ctx) {
return ctx->__opaque_pc;
}
#else // !BUILDFLAG(ARCH_CPU_PTRAUTH)
inline auto& Arm64Sp(arm_thread_state64_t* ctx) {
return ctx->__sp;
}
inline auto& Arm64Fp(arm_thread_state64_t* ctx) {
return ctx->__fp;
}
inline auto& Arm64Pc(arm_thread_state64_t* ctx) {
return ctx->__pc;
}
#endif // !BUILDFLAG(ARCH_CPU_PTRAUTH)
inline uintptr_t RegisterContextStackPointer(arm_thread_state64_t* context) {
void* val = ptrauth_strip(reinterpret_cast<void*>(Arm64Sp(context)),
ptrauth_key_asda);
return AsUintPtr(&val);
}
inline void SetRegisterContextStackPointer(arm_thread_state64_t* context,
uintptr_t val) {
AsUintPtr(&Arm64Sp(context)) = val;
}
inline uintptr_t RegisterContextFramePointer(arm_thread_state64_t* context) {
void* val = ptrauth_strip(reinterpret_cast<void*>(Arm64Fp(context)),
ptrauth_key_asda);
return AsUintPtr(&val);
}
inline void SetRegisterContextFramePointer(arm_thread_state64_t* context,
uintptr_t val) {
AsUintPtr(&Arm64Fp(context)) = val;
}
inline uintptr_t RegisterContextInstructionPointer(
arm_thread_state64_t* context) {
void* val = ptrauth_strip(reinterpret_cast<void*>(Arm64Pc(context)),
ptrauth_key_asia);
return AsUintPtr(&val);
}
inline void SetRegisterContextInstructionPointer(arm_thread_state64_t* context,
uintptr_t val) {
AsUintPtr(&Arm64Pc(context)) = val;
}
#else // BUILDFLAG(IS_APPLE) && defined(ARCH_CPU_ARM64)
// Placeholders for other cases.
inline uintptr_t RegisterContextStackPointer(RegisterContext* context) {
return context->stack_pointer;
}
inline void SetRegisterContextStackPointer(RegisterContext* context,
uintptr_t val) {
context->stack_pointer = val;
}
inline uintptr_t RegisterContextFramePointer(RegisterContext* context) {
return context->frame_pointer;
}
inline void SetRegisterContextFramePointer(RegisterContext* context,
uintptr_t val) {
context->frame_pointer = val;
}
inline uintptr_t RegisterContextInstructionPointer(RegisterContext* context) {
return context->instruction_pointer;
}
inline void SetRegisterContextInstructionPointer(RegisterContext* context,
uintptr_t val) {
context->instruction_pointer = val;
}
#endif
} // namespace base
#endif // BASE_PROFILER_REGISTER_CONTEXT_REGISTERS_H_