blob: c47e8eeae53c6b6fd3bfa4f0120caf1d92cff2d5 [file] [log] [blame]
// Copyright 2021 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.
// This file exists merely to offload logics from cfi_module.h to avoid the file being too large.
#ifndef SRC_LIB_UNWINDER_CFI_PARSER_H_
#define SRC_LIB_UNWINDER_CFI_PARSER_H_
#include <cstdint>
#include <vector>
#include "src/lib/unwinder/dwarf_expr.h"
#include "src/lib/unwinder/memory.h"
#include "src/lib/unwinder/registers.h"
namespace unwinder {
// Parse the call frame instructions to get the locations of CFA and registers.
class CfiParser {
public:
// arch is needed to default initialize register_locations_.
CfiParser(Registers::Arch arch, uint64_t code_alignment_factor, int64_t data_alignment_factor);
// Parse the CFA instructions until the (relative) pc reaches pc_limit.
[[nodiscard]] Error ParseInstructions(Memory* elf, uint64_t instructions_begin,
uint64_t instructions_end, uint64_t pc_limit);
// Helper for DW_CFA_restore. This function should be called after CIE instructions are parsed but
// before the FDE instructions are parsed.
void Snapshot() { initial_register_locations_ = register_locations_; }
// Apply the frame info to unwind one frame.
[[nodiscard]] Error Step(Memory* stack, RegisterID return_address_register,
const Registers& current, Registers& next);
private:
struct RegisterLocation {
enum class Type {
kUndefined, // the previous value is scratched, i.e. DW_CFA_undefined.
kSameValue, // the previous value is preserved, i.e. DW_CFA_same_value.
kRegister, // the previous value is stored in another register, i.e. DW_CFA_register.
kOffset, // the previous value is saved at CFA+offset, i.e. DW_CFA_offset.
kValOffset, // the previous value is just CFA+offset, i.e., DW_CFA_val_offset.
kExpression, // the previous value is saved at an address calculated by a DWARF expression.
kValExpression, // the previous value can be calculated by a DWARF expression.
} type = Type::kUndefined;
// The ID of the other register. Only valid when type is kRegister.
RegisterID reg_id;
// Only valid when type is kOffset or kValOffset.
int64_t offset;
// Only valid when type is kExpression or kValExpression.
DwarfExpr expression;
};
const uint64_t code_alignment_factor_;
const int64_t data_alignment_factor_;
struct CfaLocation {
enum class Type {
kUndefined, // CFA is not set yet.
kOffset, // CFA is reg+offset.
kExpression, // CFA can be calculated by a DWARF expression.
} type = Type::kUndefined;
// Only valid when type is kOffset.
RegisterID reg = RegisterID::kInvalid;
int64_t offset;
// Only valid when type is kValExpression.
DwarfExpr expression;
} cfa_location_;
using RegisterLocations = std::map<RegisterID, RegisterLocation>;
RegisterLocations register_locations_;
// Copy of register_locations_ for DW_CFA_restore.
RegisterLocations initial_register_locations_;
// Stack of states for DW_CFA_remember_state and DW_CFA_restore_state.
// CFA is also included, see https://dwarfstd.org/issues/230103.1.html.
std::vector<std::pair<CfaLocation, RegisterLocations>> state_stack_;
};
} // namespace unwinder
#endif // SRC_LIB_UNWINDER_CFI_PARSER_H_