| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560 |
- //===-- UnwindPlan.h --------------------------------------------*- C++ -*-===//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- #ifndef LLDB_SYMBOL_UNWINDPLAN_H
- #define LLDB_SYMBOL_UNWINDPLAN_H
- #include <map>
- #include <memory>
- #include <vector>
- #include "lldb/Core/AddressRange.h"
- #include "lldb/Utility/ConstString.h"
- #include "lldb/Utility/Stream.h"
- #include "lldb/lldb-private.h"
- namespace lldb_private {
- // The UnwindPlan object specifies how to unwind out of a function - where this
- // function saves the caller's register values before modifying them (for non-
- // volatile aka saved registers) and how to find this frame's Canonical Frame
- // Address (CFA) or Aligned Frame Address (AFA).
- // CFA is a DWARF's Canonical Frame Address.
- // Most commonly, registers are saved on the stack, offset some bytes from the
- // Canonical Frame Address, or CFA, which is the starting address of this
- // function's stack frame (the CFA is same as the eh_frame's CFA, whatever that
- // may be on a given architecture). The CFA address for the stack frame does
- // not change during the lifetime of the function.
- // AFA is an artificially introduced Aligned Frame Address.
- // It is used only for stack frames with realignment (e.g. when some of the
- // locals has an alignment requirement higher than the stack alignment right
- // after the function call). It is used to access register values saved on the
- // stack after the realignment (and so they are inaccessible through the CFA).
- // AFA usually equals the stack pointer value right after the realignment.
- // Internally, the UnwindPlan is structured as a vector of register locations
- // organized by code address in the function, showing which registers have been
- // saved at that point and where they are saved. It can be thought of as the
- // expanded table form of the DWARF CFI encoded information.
- // Other unwind information sources will be converted into UnwindPlans before
- // being added to a FuncUnwinders object. The unwind source may be an eh_frame
- // FDE, a DWARF debug_frame FDE, or assembly language based prologue analysis.
- // The UnwindPlan is the canonical form of this information that the unwinder
- // code will use when walking the stack.
- class UnwindPlan {
- public:
- class Row {
- public:
- class RegisterLocation {
- public:
- enum RestoreType {
- unspecified, // not specified, we may be able to assume this
- // is the same register. gcc doesn't specify all
- // initial values so we really don't know...
- undefined, // reg is not available, e.g. volatile reg
- same, // reg is unchanged
- atCFAPlusOffset, // reg = deref(CFA + offset)
- isCFAPlusOffset, // reg = CFA + offset
- atAFAPlusOffset, // reg = deref(AFA + offset)
- isAFAPlusOffset, // reg = AFA + offset
- inOtherRegister, // reg = other reg
- atDWARFExpression, // reg = deref(eval(dwarf_expr))
- isDWARFExpression // reg = eval(dwarf_expr)
- };
- RegisterLocation() : m_type(unspecified), m_location() {}
- bool operator==(const RegisterLocation &rhs) const;
- bool operator!=(const RegisterLocation &rhs) const {
- return !(*this == rhs);
- }
- void SetUnspecified() { m_type = unspecified; }
- void SetUndefined() { m_type = undefined; }
- void SetSame() { m_type = same; }
- bool IsSame() const { return m_type == same; }
- bool IsUnspecified() const { return m_type == unspecified; }
- bool IsUndefined() const { return m_type == undefined; }
- bool IsCFAPlusOffset() const { return m_type == isCFAPlusOffset; }
- bool IsAtCFAPlusOffset() const { return m_type == atCFAPlusOffset; }
- bool IsAFAPlusOffset() const { return m_type == isAFAPlusOffset; }
- bool IsAtAFAPlusOffset() const { return m_type == atAFAPlusOffset; }
- bool IsInOtherRegister() const { return m_type == inOtherRegister; }
- bool IsAtDWARFExpression() const { return m_type == atDWARFExpression; }
- bool IsDWARFExpression() const { return m_type == isDWARFExpression; }
- void SetAtCFAPlusOffset(int32_t offset) {
- m_type = atCFAPlusOffset;
- m_location.offset = offset;
- }
- void SetIsCFAPlusOffset(int32_t offset) {
- m_type = isCFAPlusOffset;
- m_location.offset = offset;
- }
- void SetAtAFAPlusOffset(int32_t offset) {
- m_type = atAFAPlusOffset;
- m_location.offset = offset;
- }
- void SetIsAFAPlusOffset(int32_t offset) {
- m_type = isAFAPlusOffset;
- m_location.offset = offset;
- }
- void SetInRegister(uint32_t reg_num) {
- m_type = inOtherRegister;
- m_location.reg_num = reg_num;
- }
- uint32_t GetRegisterNumber() const {
- if (m_type == inOtherRegister)
- return m_location.reg_num;
- return LLDB_INVALID_REGNUM;
- }
- RestoreType GetLocationType() const { return m_type; }
- int32_t GetOffset() const {
- switch(m_type)
- {
- case atCFAPlusOffset:
- case isCFAPlusOffset:
- case atAFAPlusOffset:
- case isAFAPlusOffset:
- return m_location.offset;
- default:
- return 0;
- }
- }
- void GetDWARFExpr(const uint8_t **opcodes, uint16_t &len) const {
- if (m_type == atDWARFExpression || m_type == isDWARFExpression) {
- *opcodes = m_location.expr.opcodes;
- len = m_location.expr.length;
- } else {
- *opcodes = nullptr;
- len = 0;
- }
- }
- void SetAtDWARFExpression(const uint8_t *opcodes, uint32_t len);
- void SetIsDWARFExpression(const uint8_t *opcodes, uint32_t len);
- const uint8_t *GetDWARFExpressionBytes() {
- if (m_type == atDWARFExpression || m_type == isDWARFExpression)
- return m_location.expr.opcodes;
- return nullptr;
- }
- int GetDWARFExpressionLength() {
- if (m_type == atDWARFExpression || m_type == isDWARFExpression)
- return m_location.expr.length;
- return 0;
- }
- void Dump(Stream &s, const UnwindPlan *unwind_plan,
- const UnwindPlan::Row *row, Thread *thread, bool verbose) const;
- private:
- RestoreType m_type; // How do we locate this register?
- union {
- // For m_type == atCFAPlusOffset or m_type == isCFAPlusOffset
- int32_t offset;
- // For m_type == inOtherRegister
- uint32_t reg_num; // The register number
- // For m_type == atDWARFExpression or m_type == isDWARFExpression
- struct {
- const uint8_t *opcodes;
- uint16_t length;
- } expr;
- } m_location;
- };
- class FAValue {
- public:
- enum ValueType {
- unspecified, // not specified
- isRegisterPlusOffset, // FA = register + offset
- isRegisterDereferenced, // FA = [reg]
- isDWARFExpression, // FA = eval(dwarf_expr)
- isRaSearch, // FA = SP + offset + ???
- };
- FAValue() : m_type(unspecified), m_value() {}
- bool operator==(const FAValue &rhs) const;
- bool operator!=(const FAValue &rhs) const { return !(*this == rhs); }
- void SetUnspecified() { m_type = unspecified; }
- bool IsUnspecified() const { return m_type == unspecified; }
- void SetRaSearch(int32_t offset) {
- m_type = isRaSearch;
- m_value.ra_search_offset = offset;
- }
- bool IsRegisterPlusOffset() const {
- return m_type == isRegisterPlusOffset;
- }
- void SetIsRegisterPlusOffset(uint32_t reg_num, int32_t offset) {
- m_type = isRegisterPlusOffset;
- m_value.reg.reg_num = reg_num;
- m_value.reg.offset = offset;
- }
- bool IsRegisterDereferenced() const {
- return m_type == isRegisterDereferenced;
- }
- void SetIsRegisterDereferenced(uint32_t reg_num) {
- m_type = isRegisterDereferenced;
- m_value.reg.reg_num = reg_num;
- }
- bool IsDWARFExpression() const { return m_type == isDWARFExpression; }
- void SetIsDWARFExpression(const uint8_t *opcodes, uint32_t len) {
- m_type = isDWARFExpression;
- m_value.expr.opcodes = opcodes;
- m_value.expr.length = len;
- }
- uint32_t GetRegisterNumber() const {
- if (m_type == isRegisterDereferenced || m_type == isRegisterPlusOffset)
- return m_value.reg.reg_num;
- return LLDB_INVALID_REGNUM;
- }
- ValueType GetValueType() const { return m_type; }
- int32_t GetOffset() const {
- switch (m_type) {
- case isRegisterPlusOffset:
- return m_value.reg.offset;
- case isRaSearch:
- return m_value.ra_search_offset;
- default:
- return 0;
- }
- }
- void IncOffset(int32_t delta) {
- if (m_type == isRegisterPlusOffset)
- m_value.reg.offset += delta;
- }
- void SetOffset(int32_t offset) {
- if (m_type == isRegisterPlusOffset)
- m_value.reg.offset = offset;
- }
- void GetDWARFExpr(const uint8_t **opcodes, uint16_t &len) const {
- if (m_type == isDWARFExpression) {
- *opcodes = m_value.expr.opcodes;
- len = m_value.expr.length;
- } else {
- *opcodes = nullptr;
- len = 0;
- }
- }
- const uint8_t *GetDWARFExpressionBytes() {
- if (m_type == isDWARFExpression)
- return m_value.expr.opcodes;
- return nullptr;
- }
- int GetDWARFExpressionLength() {
- if (m_type == isDWARFExpression)
- return m_value.expr.length;
- return 0;
- }
- void Dump(Stream &s, const UnwindPlan *unwind_plan, Thread *thread) const;
- private:
- ValueType m_type; // How do we compute CFA value?
- union {
- struct {
- // For m_type == isRegisterPlusOffset or m_type ==
- // isRegisterDereferenced
- uint32_t reg_num; // The register number
- // For m_type == isRegisterPlusOffset
- int32_t offset;
- } reg;
- // For m_type == isDWARFExpression
- struct {
- const uint8_t *opcodes;
- uint16_t length;
- } expr;
- // For m_type == isRaSearch
- int32_t ra_search_offset;
- } m_value;
- }; // class FAValue
- Row();
- bool operator==(const Row &rhs) const;
- bool GetRegisterInfo(uint32_t reg_num,
- RegisterLocation ®ister_location) const;
- void SetRegisterInfo(uint32_t reg_num,
- const RegisterLocation register_location);
- void RemoveRegisterInfo(uint32_t reg_num);
- lldb::addr_t GetOffset() const { return m_offset; }
- void SetOffset(lldb::addr_t offset) { m_offset = offset; }
- void SlideOffset(lldb::addr_t offset) { m_offset += offset; }
- FAValue &GetCFAValue() { return m_cfa_value; }
- FAValue &GetAFAValue() { return m_afa_value; }
- bool SetRegisterLocationToAtCFAPlusOffset(uint32_t reg_num, int32_t offset,
- bool can_replace);
- bool SetRegisterLocationToIsCFAPlusOffset(uint32_t reg_num, int32_t offset,
- bool can_replace);
- bool SetRegisterLocationToUndefined(uint32_t reg_num, bool can_replace,
- bool can_replace_only_if_unspecified);
- bool SetRegisterLocationToUnspecified(uint32_t reg_num, bool can_replace);
- bool SetRegisterLocationToRegister(uint32_t reg_num, uint32_t other_reg_num,
- bool can_replace);
- bool SetRegisterLocationToSame(uint32_t reg_num, bool must_replace);
- // When this UnspecifiedRegistersAreUndefined mode is
- // set, any register that is not specified by this Row will
- // be described as Undefined.
- // This will prevent the unwinder from iterating down the
- // stack looking for a spill location, or a live register value
- // at frame 0.
- // It would be used for an UnwindPlan row where we can't track
- // spilled registers -- for instance a jitted stack frame where
- // we have no unwind information or start address -- and registers
- // MAY have been spilled and overwritten, so providing the
- // spilled/live value from a newer frame may show an incorrect value.
- void SetUnspecifiedRegistersAreUndefined(bool unspec_is_undef) {
- m_unspecified_registers_are_undefined = unspec_is_undef;
- }
- bool GetUnspecifiedRegistersAreUndefined() {
- return m_unspecified_registers_are_undefined;
- }
- void Clear();
- void Dump(Stream &s, const UnwindPlan *unwind_plan, Thread *thread,
- lldb::addr_t base_addr) const;
- protected:
- typedef std::map<uint32_t, RegisterLocation> collection;
- lldb::addr_t m_offset; // Offset into the function for this row
- FAValue m_cfa_value;
- FAValue m_afa_value;
- collection m_register_locations;
- bool m_unspecified_registers_are_undefined;
- }; // class Row
- typedef std::shared_ptr<Row> RowSP;
- UnwindPlan(lldb::RegisterKind reg_kind)
- : m_row_list(), m_plan_valid_address_range(), m_register_kind(reg_kind),
- m_return_addr_register(LLDB_INVALID_REGNUM), m_source_name(),
- m_plan_is_sourced_from_compiler(eLazyBoolCalculate),
- m_plan_is_valid_at_all_instruction_locations(eLazyBoolCalculate),
- m_plan_is_for_signal_trap(eLazyBoolCalculate),
- m_lsda_address(), m_personality_func_addr() {}
- // Performs a deep copy of the plan, including all the rows (expensive).
- UnwindPlan(const UnwindPlan &rhs)
- : m_plan_valid_address_range(rhs.m_plan_valid_address_range),
- m_register_kind(rhs.m_register_kind),
- m_return_addr_register(rhs.m_return_addr_register),
- m_source_name(rhs.m_source_name),
- m_plan_is_sourced_from_compiler(rhs.m_plan_is_sourced_from_compiler),
- m_plan_is_valid_at_all_instruction_locations(
- rhs.m_plan_is_valid_at_all_instruction_locations),
- m_plan_is_for_signal_trap(rhs.m_plan_is_for_signal_trap),
- m_lsda_address(rhs.m_lsda_address),
- m_personality_func_addr(rhs.m_personality_func_addr) {
- m_row_list.reserve(rhs.m_row_list.size());
- for (const RowSP &row_sp : rhs.m_row_list)
- m_row_list.emplace_back(new Row(*row_sp));
- }
- ~UnwindPlan() = default;
- void Dump(Stream &s, Thread *thread, lldb::addr_t base_addr) const;
- void AppendRow(const RowSP &row_sp);
- void InsertRow(const RowSP &row_sp, bool replace_existing = false);
- // Returns a pointer to the best row for the given offset into the function's
- // instructions. If offset is -1 it indicates that the function start is
- // unknown - the final row in the UnwindPlan is returned. In practice, the
- // UnwindPlan for a function with no known start address will be the
- // architectural default UnwindPlan which will only have one row.
- UnwindPlan::RowSP GetRowForFunctionOffset(int offset) const;
- lldb::RegisterKind GetRegisterKind() const { return m_register_kind; }
- void SetRegisterKind(lldb::RegisterKind kind) { m_register_kind = kind; }
- void SetReturnAddressRegister(uint32_t regnum) {
- m_return_addr_register = regnum;
- }
- uint32_t GetReturnAddressRegister(void) { return m_return_addr_register; }
- uint32_t GetInitialCFARegister() const {
- if (m_row_list.empty())
- return LLDB_INVALID_REGNUM;
- return m_row_list.front()->GetCFAValue().GetRegisterNumber();
- }
- // This UnwindPlan may not be valid at every address of the function span.
- // For instance, a FastUnwindPlan will not be valid at the prologue setup
- // instructions - only in the body of the function.
- void SetPlanValidAddressRange(const AddressRange &range);
- const AddressRange &GetAddressRange() const {
- return m_plan_valid_address_range;
- }
- bool PlanValidAtAddress(Address addr);
- bool IsValidRowIndex(uint32_t idx) const;
- const UnwindPlan::RowSP GetRowAtIndex(uint32_t idx) const;
- const UnwindPlan::RowSP GetLastRow() const;
- lldb_private::ConstString GetSourceName() const;
- void SetSourceName(const char *);
- // Was this UnwindPlan emitted by a compiler?
- lldb_private::LazyBool GetSourcedFromCompiler() const {
- return m_plan_is_sourced_from_compiler;
- }
- // Was this UnwindPlan emitted by a compiler?
- void SetSourcedFromCompiler(lldb_private::LazyBool from_compiler) {
- m_plan_is_sourced_from_compiler = from_compiler;
- }
- // Is this UnwindPlan valid at all instructions? If not, then it is assumed
- // valid at call sites, e.g. for exception handling.
- lldb_private::LazyBool GetUnwindPlanValidAtAllInstructions() const {
- return m_plan_is_valid_at_all_instruction_locations;
- }
- // Is this UnwindPlan valid at all instructions? If not, then it is assumed
- // valid at call sites, e.g. for exception handling.
- void SetUnwindPlanValidAtAllInstructions(
- lldb_private::LazyBool valid_at_all_insn) {
- m_plan_is_valid_at_all_instruction_locations = valid_at_all_insn;
- }
- // Is this UnwindPlan for a signal trap frame? If so, then its saved pc
- // may have been set manually by the signal dispatch code and therefore
- // not follow a call to the child frame.
- lldb_private::LazyBool GetUnwindPlanForSignalTrap() const {
- return m_plan_is_for_signal_trap;
- }
- void SetUnwindPlanForSignalTrap(lldb_private::LazyBool is_for_signal_trap) {
- m_plan_is_for_signal_trap = is_for_signal_trap;
- }
- int GetRowCount() const;
- void Clear() {
- m_row_list.clear();
- m_plan_valid_address_range.Clear();
- m_register_kind = lldb::eRegisterKindDWARF;
- m_source_name.Clear();
- m_plan_is_sourced_from_compiler = eLazyBoolCalculate;
- m_plan_is_valid_at_all_instruction_locations = eLazyBoolCalculate;
- m_plan_is_for_signal_trap = eLazyBoolCalculate;
- m_lsda_address.Clear();
- m_personality_func_addr.Clear();
- }
- const RegisterInfo *GetRegisterInfo(Thread *thread, uint32_t reg_num) const;
- Address GetLSDAAddress() const { return m_lsda_address; }
- void SetLSDAAddress(Address lsda_addr) { m_lsda_address = lsda_addr; }
- Address GetPersonalityFunctionPtr() const { return m_personality_func_addr; }
- void SetPersonalityFunctionPtr(Address presonality_func_ptr) {
- m_personality_func_addr = presonality_func_ptr;
- }
- private:
- typedef std::vector<RowSP> collection;
- collection m_row_list;
- AddressRange m_plan_valid_address_range;
- lldb::RegisterKind m_register_kind; // The RegisterKind these register numbers
- // are in terms of - will need to be
- // translated to lldb native reg nums at unwind time
- uint32_t m_return_addr_register; // The register that has the return address
- // for the caller frame
- // e.g. the lr on arm
- lldb_private::ConstString
- m_source_name; // for logging, where this UnwindPlan originated from
- lldb_private::LazyBool m_plan_is_sourced_from_compiler;
- lldb_private::LazyBool m_plan_is_valid_at_all_instruction_locations;
- lldb_private::LazyBool m_plan_is_for_signal_trap;
- Address m_lsda_address; // Where the language specific data area exists in the
- // module - used
- // in exception handling.
- Address m_personality_func_addr; // The address of a pointer to the
- // personality function - used in
- // exception handling.
- }; // class UnwindPlan
- } // namespace lldb_private
- #endif // LLDB_SYMBOL_UNWINDPLAN_H
|