blob: 30006aad4b17049a19f71f31c112e185eb8ed2da [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/scs_unwinder.h"
#include "src/lib/unwinder/registers.h"
namespace unwinder {
Error ShadowCallStackUnwinder::Step(Memory* scs, const Registers& current, Registers& next) {
RegisterID scs_reg;
switch (current.arch()) {
case Registers::Arch::kX64:
return Error("Shadow call stack is not supported on x64");
case Registers::Arch::kArm64:
scs_reg = RegisterID::kArm64_x18;
break;
case Registers::Arch::kRiscv64:
scs_reg = RegisterID::kRiscv64_gp;
break;
}
uint64_t scsp; // shadow call stack pointer
if (auto err = current.Get(scs_reg, scsp); err.has_err()) {
return err;
}
if (!scsp) {
return Error("shadow call stack is not available");
}
// The shadow call stack is pushed/popped via (e.g., on arm64)
//
// str x30, [x18], #8 ; post-indexed
// ...
// ldr x30, [x18, #-8]! ; pre-indexed
//
// So x18 points to the next available slots. The same applies to riscv64.
uint64_t ra;
if (auto err = scs->Read(scsp - 8, ra); err.has_err()) {
return err;
}
// A zero ra indicates the beginning of the shadow call stack.
if (!ra) {
return Success();
}
if (!cfi_unwinder_->IsValidPC(ra)) {
return Error("Invalid shadow call stack");
}
next.SetPC(ra);
next.Set(scs_reg, scsp - 8);
return Success();
}
} // namespace unwinder