blob: c92a896f5194147f14af3f4215219b7f37571191 [file] [log] [blame]
// Copyright 2023 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/lib/unwinder/sigreturn_unwinder.h"
#include <array>
#include <cinttypes>
#include <cstdint>
#include "src/lib/unwinder/cfi_unwinder.h"
#include "src/lib/unwinder/error.h"
namespace unwinder {
Error SigReturnUnwinder::Step(Memory* stack, const Registers& current, Registers& next) {
switch (current.arch()) {
case Registers::Arch::kX64:
return StepX64(stack, current, next);
case Registers::Arch::kArm32:
return StepArm32(stack, current, next);
case Registers::Arch::kArm64:
return StepArm64(stack, current, next);
case Registers::Arch::kRiscv64:
return StepRiscv64(stack, current, next);
}
}
Error SigReturnUnwinder::ProbePCForSigReturn(CfiUnwinder* cfi_unwinder, const Registers& regs) {
uint64_t pc;
if (auto err = regs.GetPC(pc); err.has_err()) {
return err;
}
Module* elf_module;
if (auto err = cfi_unwinder->GetModuleForPc(pc, &elf_module); err.has_err()) {
return err;
}
if (elf_module->binary_memory == nullptr) {
return Error("No binary memory found for address: %#" PRIx64, pc);
}
Memory* binary_memory = elf_module->binary_memory;
switch (regs.arch()) {
case Registers::Arch::kArm32:
return ProbeArm32SigReturn(binary_memory, pc);
case Registers::Arch::kArm64:
return ProbeArm64SigReturn(binary_memory, pc);
default:
return Error("Not implemented.");
}
}
Error SigReturnUnwinder::StepX64(Memory* stack, const Registers& current, Registers& next) {
return Error("not implemented");
}
Error SigReturnUnwinder::StepArm32(Memory* stack, const Registers& current, Registers& next) {
// The sigreturn function looks like:
//
// 00000114 <__kernel_rt_sigreturn>:
// 114: e3a070ad mov r7, #173
// 118: ef000000 svc #0x0
uint64_t pc;
if (Error error = current.GetPC(pc); error.has_err()) {
return error;
}
if (auto err = ProbePCForSigReturn(cfi_unwinder_, current); err.has_err()) {
return err;
}
// The sp points to an rt_sigframe:
//
// 128 byte siginfo struct
// ucontext struct:
// 4 byte long: uc_flags
// 4 byte pointer: uc_link
// 12 byte stack_t
// sigcontext
const uint64_t sigcontext_offset = 128 + 4 + 4 + 12;
// Add another 12 to skip over the trap_no, error_code, and oldmask fields below.
const uint64_t regs_offset = sigcontext_offset + 12;
next = current;
uint64_t sp;
if (Error error = current.GetSP(sp); error.has_err()) {
return error;
}
// The layout of the sigcontext struct looks like this:
//
// struct sigcontext {
// unsigned long trap_no;
// unsigned long error_code;
// unsigned long oldmask;
// unsigned long arm_r0;
// unsigned long arm_r1;
// unsigned long arm_r2;
// unsigned long arm_r3;
// unsigned long arm_r4;
// unsigned long arm_r5;
// unsigned long arm_r6;
// unsigned long arm_r7;
// unsigned long arm_r8;
// unsigned long arm_r9;
// unsigned long arm_r10;
// unsigned long arm_fp;
// unsigned long arm_ip;
// unsigned long arm_sp;
// unsigned long arm_lr;
// unsigned long arm_pc;
// unsigned long arm_cpsr;
// unsigned long fault_address;
// };
//
// We don't care about anything other than the register values.
for (size_t i = 0; i < static_cast<size_t>(RegisterID::kArm32_last); i++) {
uint32_t gpr;
if (Error error = stack->Read(sp + regs_offset + (i * sizeof(gpr)), gpr); error.has_err()) {
return error;
}
next.Set(static_cast<RegisterID>(i), gpr);
}
return Success();
}
Error SigReturnUnwinder::StepArm64(Memory* stack, const Registers& current, Registers& next) {
// The sigreturn function looks like:
//
// 00000000000001d0 <__kernel_rt_sigreturn>:
// 1d0: d2801168 mov x8, #0x8b
// 1d4: d4000001 svc #0
if (auto err = ProbePCForSigReturn(cfi_unwinder_, current); err.has_err()) {
return err;
}
// The sp points to an rt_sigframe:
//
// 128 byte siginfo struct
// ucontext struct:
// 8 byte long: uc_flags
// 8 byte pointer: uc_link
// 24 byte stack_t
// 128 byte signal set
// 8 byte padding to 16 byte align sigcontext
// sigcontext
const uint64_t sigcontext_offset = 128 + 8 + 8 + 24 + 128 + 8;
const uint64_t regs_offset = sigcontext_offset + 8;
next = current;
uint64_t sp;
if (Error error = current.GetSP(sp); error.has_err()) {
return error;
}
// GPRs, sp, and pc are stored in sigcontext in the same order as aadwarf64
// names registers.
for (size_t i = 0; i < static_cast<size_t>(RegisterID::kArm64_last); i++) {
uint64_t gpr;
if (Error error = stack->Read(sp + regs_offset + (i * sizeof(gpr)), gpr); error.has_err()) {
return error;
}
next.Set(static_cast<RegisterID>(i), gpr);
}
return Success();
}
Error SigReturnUnwinder::StepRiscv64(Memory* stack, const Registers& current, Registers& next) {
return Error("not implemented");
}
Error SigReturnUnwinder::ProbeArm32SigReturn(Memory* stack, uint64_t pc) {
// Check for the sigreturn instruction sequence.
uint64_t instructions;
if (Error error = stack->Read(pc, instructions); error.has_err()) {
return error;
}
if (instructions != 0xef000000e3a070adULL) {
return Error("It doesn't look like a sigreturn function");
}
return Success();
}
Error SigReturnUnwinder::ProbeArm64SigReturn(Memory* stack, uint64_t pc) {
// Check for the sigreturn instruction sequence.
uint64_t instructions;
if (Error error = stack->Read(pc, instructions); error.has_err()) {
return error;
}
if (instructions != 0xd4000001d2801168ULL) {
return Error("It doesn't look like a sigreturn function");
}
return Success();
}
} // namespace unwinder