blob: 3ae3a29c9537135581637a9f9ee099884ed051eb [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.
#ifndef SRC_LIB_UNWINDER_CFI_MODULE_H_
#define SRC_LIB_UNWINDER_CFI_MODULE_H_
#include <cstdint>
#include <map>
#include "src/lib/unwinder/error.h"
#include "src/lib/unwinder/memory.h"
#include "src/lib/unwinder/module.h"
#include "src/lib/unwinder/registers.h"
namespace unwinder {
// Represents the Call Frame Information (CFI) from the .eh_frame and/or the .debug_frame section
// of one ELF module.
//
// This class doesn't cache the memory so if repeated lookups are required, it's recommended to use
// a cached Memory implementation.
class CfiModule {
public:
// Caller must ensure elf to outlive us.
CfiModule(Memory* elf, uint64_t elf_ptr, Module::AddressMode address_mode)
: elf_(elf), elf_ptr_(elf_ptr), address_mode_(address_mode) {}
// Load the CFI from the ELF file.
[[nodiscard]] Error Load();
// Unwind one frame.
[[nodiscard]] Error Step(Memory* stack, const Registers& current, Registers& next);
// Check whether a given PC is in the valid range.
bool IsValidPC(uint64_t pc) const { return pc >= pc_begin_ && pc < pc_end_; }
// Memory accessor.
Memory* memory() const { return elf_; }
private:
// DWARF Common Information Entry.
struct DwarfCie {
uint64_t code_alignment_factor = 0; // usually 1.
int64_t data_alignment_factor = 0; // usually -4 on arm64, -8 on x64.
RegisterID return_address_register; // PC on x64, LR on arm64.
bool fde_have_augmentation_data = false; // should always be true for .eh_frame.
uint8_t fde_address_encoding = 0xFF; // default to an invalid encoding.
uint64_t instructions_begin = 0;
uint64_t instructions_end = 0; // exclusive.
};
// DWARF Frame Description Entry.
struct DwarfFde {
uint64_t pc_begin = 0;
uint64_t pc_end = 0;
uint64_t instructions_begin = 0;
uint64_t instructions_end = 0; // exclusive.
};
// Search for CIE and FDE in .eh_frame section.
[[nodiscard]] Error SearchEhFrame(uint64_t pc, DwarfCie& cie, DwarfFde& fde);
// Search for CIE and FDE in .debug_frame section.
[[nodiscard]] Error SearchDebugFrame(uint64_t pc, DwarfCie& cie, DwarfFde& fde);
[[nodiscard]] Error BuildDebugFrameMap();
// Helpers to decode CIE and FDE. Version could be 1 or 4.
[[nodiscard]] Error DecodeFde(uint8_t version, uint64_t fde_ptr, DwarfCie& cie, DwarfFde& fde);
[[nodiscard]] Error DecodeCie(uint8_t version, uint64_t cie_ptr, DwarfCie& cie);
// Inputs. Use const to prevent accidental modification.
Memory* const elf_;
const uint64_t elf_ptr_;
const Module::AddressMode address_mode_;
// Marks the executable section so that we don't need to find the FDE to know a PC is wrong.
uint64_t pc_begin_ = 0; // inclusive
uint64_t pc_end_ = 0; // exclusive
// .eh_frame_hdr binary search table info.
uint64_t eh_frame_hdr_ptr_ = 0;
uint64_t fde_count_ = 0; // Number of entries in the binary search table.
uint64_t table_ptr_ = 0; // Pointer to the binary search table.
uint8_t table_enc_ = 0; // Encoding for pointers in the table.
uint64_t table_entry_size_ = 0; // Size of each entry in the table.
// .debug_frame info.
uint64_t debug_frame_ptr_ = 0;
uint64_t debug_frame_end_ = 0;
// Binary search table for .debug_frame, similar to .eh_frame_hdr.
// To save space, we only store the mapping from pc to the start of FDE.
std::map<uint64_t, uint64_t> debug_frame_map_;
};
} // namespace unwinder
#endif // SRC_LIB_UNWINDER_CFI_MODULE_H_