diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h index 32035c471f744..65189916b41f1 100644 --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h @@ -9,15 +9,14 @@ #ifndef LLVM_DEBUGINFO_DWARF_DWARFDEBUGFRAME_H #define LLVM_DEBUGINFO_DWARF_DWARFDEBUGFRAME_H -#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/iterator.h" #include "llvm/DebugInfo/DWARF/LowLevel/DWARFCFIProgram.h" #include "llvm/DebugInfo/DWARF/LowLevel/DWARFExpression.h" +#include "llvm/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/Error.h" #include "llvm/TargetParser/Triple.h" -#include #include #include @@ -30,356 +29,6 @@ struct DIDumpOptions; namespace dwarf { -constexpr uint32_t InvalidRegisterNumber = UINT32_MAX; - -/// A class that represents a location for the Call Frame Address (CFA) or a -/// register. This is decoded from the DWARF Call Frame Information -/// instructions and put into an UnwindRow. -class UnwindLocation { -public: - enum Location { - /// Not specified. - Unspecified, - /// Register is not available and can't be recovered. - Undefined, - /// Register value is in the register, nothing needs to be done to unwind - /// it: - /// reg = reg - Same, - /// Register is in or at the CFA plus an offset: - /// reg = CFA + offset - /// reg = defef(CFA + offset) - CFAPlusOffset, - /// Register or CFA is in or at a register plus offset, optionally in - /// an address space: - /// reg = reg + offset [in addrspace] - /// reg = deref(reg + offset [in addrspace]) - RegPlusOffset, - /// Register or CFA value is in or at a value found by evaluating a DWARF - /// expression: - /// reg = eval(dwarf_expr) - /// reg = deref(eval(dwarf_expr)) - DWARFExpr, - /// Value is a constant value contained in "Offset": - /// reg = Offset - Constant, - }; - -private: - Location Kind; /// The type of the location that describes how to unwind it. - uint32_t RegNum; /// The register number for Kind == RegPlusOffset. - int32_t Offset; /// The offset for Kind == CFAPlusOffset or RegPlusOffset. - std::optional AddrSpace; /// The address space for Kind == - /// RegPlusOffset for CFA. - std::optional Expr; /// The DWARF expression for Kind == - /// DWARFExpression. - bool Dereference; /// If true, the resulting location must be dereferenced - /// after the location value is computed. - - // Constructors are private to force people to use the create static - // functions. - UnwindLocation(Location K) - : Kind(K), RegNum(InvalidRegisterNumber), Offset(0), - AddrSpace(std::nullopt), Dereference(false) {} - - UnwindLocation(Location K, uint32_t Reg, int32_t Off, - std::optional AS, bool Deref) - : Kind(K), RegNum(Reg), Offset(Off), AddrSpace(AS), Dereference(Deref) {} - - UnwindLocation(DWARFExpression E, bool Deref) - : Kind(DWARFExpr), RegNum(InvalidRegisterNumber), Offset(0), Expr(E), - Dereference(Deref) {} - -public: - /// Create a location whose rule is set to Unspecified. This means the - /// register value might be in the same register but it wasn't specified in - /// the unwind opcodes. - LLVM_ABI static UnwindLocation createUnspecified(); - /// Create a location where the value is undefined and not available. This can - /// happen when a register is volatile and can't be recovered. - LLVM_ABI static UnwindLocation createUndefined(); - /// Create a location where the value is known to be in the register itself. - LLVM_ABI static UnwindLocation createSame(); - /// Create a location that is in (Deref == false) or at (Deref == true) the - /// CFA plus an offset. Most registers that are spilled onto the stack use - /// this rule. The rule for the register will use this rule and specify a - /// unique offset from the CFA with \a Deref set to true. This value will be - /// relative to a CFA value which is typically defined using the register - /// plus offset location. \see createRegisterPlusOffset(...) for more - /// information. - LLVM_ABI static UnwindLocation createIsCFAPlusOffset(int32_t Off); - LLVM_ABI static UnwindLocation createAtCFAPlusOffset(int32_t Off); - /// Create a location where the saved value is in (Deref == false) or at - /// (Deref == true) a regiser plus an offset and, optionally, in the specified - /// address space (used mostly for the CFA). - /// - /// The CFA is usually defined using this rule by using the stack pointer or - /// frame pointer as the register, with an offset that accounts for all - /// spilled registers and all local variables in a function, and Deref == - /// false. - LLVM_ABI static UnwindLocation - createIsRegisterPlusOffset(uint32_t Reg, int32_t Off, - std::optional AddrSpace = std::nullopt); - LLVM_ABI static UnwindLocation - createAtRegisterPlusOffset(uint32_t Reg, int32_t Off, - std::optional AddrSpace = std::nullopt); - /// Create a location whose value is the result of evaluating a DWARF - /// expression. This allows complex expressions to be evaluated in order to - /// unwind a register or CFA value. - LLVM_ABI static UnwindLocation createIsDWARFExpression(DWARFExpression Expr); - LLVM_ABI static UnwindLocation createAtDWARFExpression(DWARFExpression Expr); - LLVM_ABI static UnwindLocation createIsConstant(int32_t Value); - - Location getLocation() const { return Kind; } - uint32_t getRegister() const { return RegNum; } - int32_t getOffset() const { return Offset; } - uint32_t getAddressSpace() const { - assert(Kind == RegPlusOffset && AddrSpace); - return *AddrSpace; - } - int32_t getConstant() const { return Offset; } - bool getDereference() const { return Dereference; } - - /// Some opcodes will modify the CFA location's register only, so we need - /// to be able to modify the CFA register when evaluating DWARF Call Frame - /// Information opcodes. - void setRegister(uint32_t NewRegNum) { RegNum = NewRegNum; } - /// Some opcodes will modify the CFA location's offset only, so we need - /// to be able to modify the CFA offset when evaluating DWARF Call Frame - /// Information opcodes. - void setOffset(int32_t NewOffset) { Offset = NewOffset; } - /// Some opcodes modify a constant value and we need to be able to update - /// the constant value (DW_CFA_GNU_window_save which is also known as - // DW_CFA_AARCH64_negate_ra_state). - void setConstant(int32_t Value) { Offset = Value; } - - std::optional getDWARFExpressionBytes() const { - return Expr; - } - /// Dump a location expression as text and use the register information if - /// some is provided. - /// - /// \param OS the stream to use for output. - /// - /// \param MRI register information that helps emit register names insteead - /// of raw register numbers. - /// - /// \param IsEH true if the DWARF Call Frame Information is from .eh_frame - /// instead of from .debug_frame. This is needed for register number - /// conversion because some register numbers differ between the two sections - /// for certain architectures like x86. - LLVM_ABI void dump(raw_ostream &OS, DIDumpOptions DumpOpts) const; - - LLVM_ABI bool operator==(const UnwindLocation &RHS) const; -}; - -LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const UnwindLocation &R); - -/// A class that can track all registers with locations in a UnwindRow object. -/// -/// Register locations use a map where the key is the register number and the -/// the value is a UnwindLocation. -/// -/// The register maps are put into a class so that all register locations can -/// be copied when parsing the unwind opcodes DW_CFA_remember_state and -/// DW_CFA_restore_state. -class RegisterLocations { - std::map Locations; - -public: - /// Return the location for the register in \a RegNum if there is a location. - /// - /// \param RegNum the register number to find a location for. - /// - /// \returns A location if one is available for \a RegNum, or std::nullopt - /// otherwise. - std::optional getRegisterLocation(uint32_t RegNum) const { - auto Pos = Locations.find(RegNum); - if (Pos == Locations.end()) - return std::nullopt; - return Pos->second; - } - - /// Set the location for the register in \a RegNum to \a Location. - /// - /// \param RegNum the register number to set the location for. - /// - /// \param Location the UnwindLocation that describes how to unwind the value. - void setRegisterLocation(uint32_t RegNum, const UnwindLocation &Location) { - Locations.erase(RegNum); - Locations.insert(std::make_pair(RegNum, Location)); - } - - /// Removes any rule for the register in \a RegNum. - /// - /// \param RegNum the register number to remove the location for. - void removeRegisterLocation(uint32_t RegNum) { Locations.erase(RegNum); } - - /// Dump all registers + locations that are currently defined in this object. - /// - /// \param OS the stream to use for output. - /// - /// \param MRI register information that helps emit register names insteead - /// of raw register numbers. - /// - /// \param IsEH true if the DWARF Call Frame Information is from .eh_frame - /// instead of from .debug_frame. This is needed for register number - /// conversion because some register numbers differ between the two sections - /// for certain architectures like x86. - LLVM_ABI void dump(raw_ostream &OS, DIDumpOptions DumpOpts) const; - - /// Returns true if we have any register locations in this object. - bool hasLocations() const { return !Locations.empty(); } - - size_t size() const { return Locations.size(); } - - bool operator==(const RegisterLocations &RHS) const { - return Locations == RHS.Locations; - } -}; - -LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const RegisterLocations &RL); - -/// A class that represents a single row in the unwind table that is decoded by -/// parsing the DWARF Call Frame Information opcodes. -/// -/// The row consists of an optional address, the rule to unwind the CFA and all -/// rules to unwind any registers. If the address doesn't have a value, this -/// row represents the initial instructions for a CIE. If the address has a -/// value the UnwindRow represents a row in the UnwindTable for a FDE. The -/// address is the first address for which the CFA location and register rules -/// are valid within a function. -/// -/// UnwindRow objects are created by parsing opcodes in the DWARF Call Frame -/// Information and UnwindRow objects are lazily populated and pushed onto a -/// stack in the UnwindTable when evaluating this state machine. Accessors are -/// needed for the address, CFA value, and register locations as the opcodes -/// encode a state machine that produces a sorted array of UnwindRow objects -/// \see UnwindTable. -class UnwindRow { - /// The address will be valid when parsing the instructions in a FDE. If - /// invalid, this object represents the initial instructions of a CIE. - std::optional Address; ///< Address for row in FDE, invalid for CIE. - UnwindLocation CFAValue; ///< How to unwind the Call Frame Address (CFA). - RegisterLocations RegLocs; ///< How to unwind all registers in this list. - -public: - UnwindRow() : CFAValue(UnwindLocation::createUnspecified()) {} - - /// Returns true if the address is valid in this object. - bool hasAddress() const { return Address.has_value(); } - - /// Get the address for this row. - /// - /// Clients should only call this function after verifying it has a valid - /// address with a call to \see hasAddress(). - uint64_t getAddress() const { return *Address; } - - /// Set the address for this UnwindRow. - /// - /// The address represents the first address for which the CFAValue and - /// RegLocs are valid within a function. - void setAddress(uint64_t Addr) { Address = Addr; } - - /// Offset the address for this UnwindRow. - /// - /// The address represents the first address for which the CFAValue and - /// RegLocs are valid within a function. Clients must ensure that this object - /// already has an address (\see hasAddress()) prior to calling this - /// function. - void slideAddress(uint64_t Offset) { *Address += Offset; } - UnwindLocation &getCFAValue() { return CFAValue; } - const UnwindLocation &getCFAValue() const { return CFAValue; } - RegisterLocations &getRegisterLocations() { return RegLocs; } - const RegisterLocations &getRegisterLocations() const { return RegLocs; } - - /// Dump the UnwindRow to the stream. - /// - /// \param OS the stream to use for output. - /// - /// \param MRI register information that helps emit register names insteead - /// of raw register numbers. - /// - /// \param IsEH true if the DWARF Call Frame Information is from .eh_frame - /// instead of from .debug_frame. This is needed for register number - /// conversion because some register numbers differ between the two sections - /// for certain architectures like x86. - /// - /// \param IndentLevel specify the indent level as an integer. The UnwindRow - /// will be output to the stream preceded by 2 * IndentLevel number of spaces. - LLVM_ABI void dump(raw_ostream &OS, DIDumpOptions DumpOpts, - unsigned IndentLevel = 0) const; -}; - -LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const UnwindRow &Row); - -/// A class that contains all UnwindRow objects for an FDE or a single unwind -/// row for a CIE. To unwind an address the rows, which are sorted by start -/// address, can be searched to find the UnwindRow with the lowest starting -/// address that is greater than or equal to the address that is being looked -/// up. -class UnwindTable { -public: - using RowContainer = std::vector; - using iterator = RowContainer::iterator; - using const_iterator = RowContainer::const_iterator; - - UnwindTable(RowContainer &&Rows) : Rows(std::move(Rows)) {} - - size_t size() const { return Rows.size(); } - iterator begin() { return Rows.begin(); } - const_iterator begin() const { return Rows.begin(); } - iterator end() { return Rows.end(); } - const_iterator end() const { return Rows.end(); } - const UnwindRow &operator[](size_t Index) const { - assert(Index < size()); - return Rows[Index]; - } - - /// Dump the UnwindTable to the stream. - /// - /// \param OS the stream to use for output. - /// - /// \param MRI register information that helps emit register names instead - /// of raw register numbers. - /// - /// \param IsEH true if the DWARF Call Frame Information is from .eh_frame - /// instead of from .debug_frame. This is needed for register number - /// conversion because some register numbers differ between the two sections - /// for certain architectures like x86. - /// - /// \param IndentLevel specify the indent level as an integer. The UnwindRow - /// will be output to the stream preceded by 2 * IndentLevel number of spaces. - LLVM_ABI void dump(raw_ostream &OS, DIDumpOptions DumpOpts, - unsigned IndentLevel = 0) const; - -private: - RowContainer Rows; -}; - -/// Parse the information in the CFIProgram and update the CurrRow object -/// that the state machine describes. -/// -/// This function emulates the state machine described in the DWARF Call Frame -/// Information opcodes and will push CurrRow onto a RowContainer when needed. -/// -/// \param CFIP the CFI program that contains the opcodes from a CIE or FDE. -/// -/// \param CurrRow the current row to modify while parsing the state machine. -/// -/// \param InitialLocs If non-NULL, we are parsing a FDE and this contains -/// the initial register locations from the CIE. If NULL, then a CIE's -/// opcodes are being parsed and this is not needed. This is used for the -/// DW_CFA_restore and DW_CFA_restore_extended opcodes. -/// -/// \returns An error if the DWARF Call Frame Information opcodes have state -/// machine errors, or the accumulated rows otherwise. -LLVM_ABI Expected -parseRows(const CFIProgram &CFIP, UnwindRow &CurrRow, - const RegisterLocations *InitialLocs); - -LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const UnwindTable &Rows); - class CIE; /// Create an UnwindTable from a Common Information Entry (CIE). diff --git a/llvm/include/llvm/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.h b/llvm/include/llvm/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.h new file mode 100644 index 0000000000000..d324e4dc8329d --- /dev/null +++ b/llvm/include/llvm/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.h @@ -0,0 +1,376 @@ +//===- DWARFUnwindTable.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 LLVM_DEBUGINFO_DWARF_DWARFUNWINDTABLE_H +#define LLVM_DEBUGINFO_DWARF_DWARFUNWINDTABLE_H + +#include "llvm/DebugInfo/DWARF/LowLevel/DWARFCFIProgram.h" +#include "llvm/DebugInfo/DWARF/LowLevel/DWARFExpression.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/Error.h" +#include +#include + +namespace llvm { + +namespace dwarf { +constexpr uint32_t InvalidRegisterNumber = UINT32_MAX; + +/// A class that represents a location for the Call Frame Address (CFA) or a +/// register. This is decoded from the DWARF Call Frame Information +/// instructions and put into an UnwindRow. +class UnwindLocation { +public: + enum Location { + /// Not specified. + Unspecified, + /// Register is not available and can't be recovered. + Undefined, + /// Register value is in the register, nothing needs to be done to unwind + /// it: + /// reg = reg + Same, + /// Register is in or at the CFA plus an offset: + /// reg = CFA + offset + /// reg = defef(CFA + offset) + CFAPlusOffset, + /// Register or CFA is in or at a register plus offset, optionally in + /// an address space: + /// reg = reg + offset [in addrspace] + /// reg = deref(reg + offset [in addrspace]) + RegPlusOffset, + /// Register or CFA value is in or at a value found by evaluating a DWARF + /// expression: + /// reg = eval(dwarf_expr) + /// reg = deref(eval(dwarf_expr)) + DWARFExpr, + /// Value is a constant value contained in "Offset": + /// reg = Offset + Constant, + }; + +private: + Location Kind; /// The type of the location that describes how to unwind it. + uint32_t RegNum; /// The register number for Kind == RegPlusOffset. + int32_t Offset; /// The offset for Kind == CFAPlusOffset or RegPlusOffset. + std::optional AddrSpace; /// The address space for Kind == + /// RegPlusOffset for CFA. + std::optional Expr; /// The DWARF expression for Kind == + /// DWARFExpression. + bool Dereference; /// If true, the resulting location must be dereferenced + /// after the location value is computed. + + // Constructors are private to force people to use the create static + // functions. + UnwindLocation(Location K) + : Kind(K), RegNum(InvalidRegisterNumber), Offset(0), + AddrSpace(std::nullopt), Dereference(false) {} + + UnwindLocation(Location K, uint32_t Reg, int32_t Off, + std::optional AS, bool Deref) + : Kind(K), RegNum(Reg), Offset(Off), AddrSpace(AS), Dereference(Deref) {} + + UnwindLocation(DWARFExpression E, bool Deref) + : Kind(DWARFExpr), RegNum(InvalidRegisterNumber), Offset(0), Expr(E), + Dereference(Deref) {} + +public: + /// Create a location whose rule is set to Unspecified. This means the + /// register value might be in the same register but it wasn't specified in + /// the unwind opcodes. + LLVM_ABI static UnwindLocation createUnspecified(); + /// Create a location where the value is undefined and not available. This can + /// happen when a register is volatile and can't be recovered. + LLVM_ABI static UnwindLocation createUndefined(); + /// Create a location where the value is known to be in the register itself. + LLVM_ABI static UnwindLocation createSame(); + /// Create a location that is in (Deref == false) or at (Deref == true) the + /// CFA plus an offset. Most registers that are spilled onto the stack use + /// this rule. The rule for the register will use this rule and specify a + /// unique offset from the CFA with \a Deref set to true. This value will be + /// relative to a CFA value which is typically defined using the register + /// plus offset location. \see createRegisterPlusOffset(...) for more + /// information. + LLVM_ABI static UnwindLocation createIsCFAPlusOffset(int32_t Off); + LLVM_ABI static UnwindLocation createAtCFAPlusOffset(int32_t Off); + /// Create a location where the saved value is in (Deref == false) or at + /// (Deref == true) a regiser plus an offset and, optionally, in the specified + /// address space (used mostly for the CFA). + /// + /// The CFA is usually defined using this rule by using the stack pointer or + /// frame pointer as the register, with an offset that accounts for all + /// spilled registers and all local variables in a function, and Deref == + /// false. + LLVM_ABI static UnwindLocation + createIsRegisterPlusOffset(uint32_t Reg, int32_t Off, + std::optional AddrSpace = std::nullopt); + LLVM_ABI static UnwindLocation + createAtRegisterPlusOffset(uint32_t Reg, int32_t Off, + std::optional AddrSpace = std::nullopt); + /// Create a location whose value is the result of evaluating a DWARF + /// expression. This allows complex expressions to be evaluated in order to + /// unwind a register or CFA value. + LLVM_ABI static UnwindLocation createIsDWARFExpression(DWARFExpression Expr); + LLVM_ABI static UnwindLocation createAtDWARFExpression(DWARFExpression Expr); + LLVM_ABI static UnwindLocation createIsConstant(int32_t Value); + + Location getLocation() const { return Kind; } + uint32_t getRegister() const { return RegNum; } + int32_t getOffset() const { return Offset; } + uint32_t getAddressSpace() const { + assert(Kind == RegPlusOffset && AddrSpace); + return *AddrSpace; + } + int32_t getConstant() const { return Offset; } + bool getDereference() const { return Dereference; } + + /// Some opcodes will modify the CFA location's register only, so we need + /// to be able to modify the CFA register when evaluating DWARF Call Frame + /// Information opcodes. + void setRegister(uint32_t NewRegNum) { RegNum = NewRegNum; } + /// Some opcodes will modify the CFA location's offset only, so we need + /// to be able to modify the CFA offset when evaluating DWARF Call Frame + /// Information opcodes. + void setOffset(int32_t NewOffset) { Offset = NewOffset; } + /// Some opcodes modify a constant value and we need to be able to update + /// the constant value (DW_CFA_GNU_window_save which is also known as + // DW_CFA_AARCH64_negate_ra_state). + void setConstant(int32_t Value) { Offset = Value; } + + std::optional getDWARFExpressionBytes() const { + return Expr; + } + /// Dump a location expression as text and use the register information if + /// some is provided. + /// + /// \param OS the stream to use for output. + /// + /// \param MRI register information that helps emit register names insteead + /// of raw register numbers. + /// + /// \param IsEH true if the DWARF Call Frame Information is from .eh_frame + /// instead of from .debug_frame. This is needed for register number + /// conversion because some register numbers differ between the two sections + /// for certain architectures like x86. + LLVM_ABI void dump(raw_ostream &OS, DIDumpOptions DumpOpts) const; + + LLVM_ABI bool operator==(const UnwindLocation &RHS) const; +}; + +LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const UnwindLocation &R); + +/// A class that can track all registers with locations in a UnwindRow object. +/// +/// Register locations use a map where the key is the register number and the +/// the value is a UnwindLocation. +/// +/// The register maps are put into a class so that all register locations can +/// be copied when parsing the unwind opcodes DW_CFA_remember_state and +/// DW_CFA_restore_state. +class RegisterLocations { + std::map Locations; + +public: + /// Return the location for the register in \a RegNum if there is a location. + /// + /// \param RegNum the register number to find a location for. + /// + /// \returns A location if one is available for \a RegNum, or std::nullopt + /// otherwise. + std::optional getRegisterLocation(uint32_t RegNum) const { + auto Pos = Locations.find(RegNum); + if (Pos == Locations.end()) + return std::nullopt; + return Pos->second; + } + + /// Set the location for the register in \a RegNum to \a Location. + /// + /// \param RegNum the register number to set the location for. + /// + /// \param Location the UnwindLocation that describes how to unwind the value. + void setRegisterLocation(uint32_t RegNum, const UnwindLocation &Location) { + Locations.erase(RegNum); + Locations.insert(std::make_pair(RegNum, Location)); + } + + /// Removes any rule for the register in \a RegNum. + /// + /// \param RegNum the register number to remove the location for. + void removeRegisterLocation(uint32_t RegNum) { Locations.erase(RegNum); } + + /// Dump all registers + locations that are currently defined in this object. + /// + /// \param OS the stream to use for output. + /// + /// \param MRI register information that helps emit register names insteead + /// of raw register numbers. + /// + /// \param IsEH true if the DWARF Call Frame Information is from .eh_frame + /// instead of from .debug_frame. This is needed for register number + /// conversion because some register numbers differ between the two sections + /// for certain architectures like x86. + LLVM_ABI void dump(raw_ostream &OS, DIDumpOptions DumpOpts) const; + + /// Returns true if we have any register locations in this object. + bool hasLocations() const { return !Locations.empty(); } + + size_t size() const { return Locations.size(); } + + bool operator==(const RegisterLocations &RHS) const { + return Locations == RHS.Locations; + } +}; + +LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const RegisterLocations &RL); + +/// A class that represents a single row in the unwind table that is decoded by +/// parsing the DWARF Call Frame Information opcodes. +/// +/// The row consists of an optional address, the rule to unwind the CFA and all +/// rules to unwind any registers. If the address doesn't have a value, this +/// row represents the initial instructions for a CIE. If the address has a +/// value the UnwindRow represents a row in the UnwindTable for a FDE. The +/// address is the first address for which the CFA location and register rules +/// are valid within a function. +/// +/// UnwindRow objects are created by parsing opcodes in the DWARF Call Frame +/// Information and UnwindRow objects are lazily populated and pushed onto a +/// stack in the UnwindTable when evaluating this state machine. Accessors are +/// needed for the address, CFA value, and register locations as the opcodes +/// encode a state machine that produces a sorted array of UnwindRow objects +/// \see UnwindTable. +class UnwindRow { + /// The address will be valid when parsing the instructions in a FDE. If + /// invalid, this object represents the initial instructions of a CIE. + std::optional Address; ///< Address for row in FDE, invalid for CIE. + UnwindLocation CFAValue; ///< How to unwind the Call Frame Address (CFA). + RegisterLocations RegLocs; ///< How to unwind all registers in this list. + +public: + UnwindRow() : CFAValue(UnwindLocation::createUnspecified()) {} + + /// Returns true if the address is valid in this object. + bool hasAddress() const { return Address.has_value(); } + + /// Get the address for this row. + /// + /// Clients should only call this function after verifying it has a valid + /// address with a call to \see hasAddress(). + uint64_t getAddress() const { return *Address; } + + /// Set the address for this UnwindRow. + /// + /// The address represents the first address for which the CFAValue and + /// RegLocs are valid within a function. + void setAddress(uint64_t Addr) { Address = Addr; } + + /// Offset the address for this UnwindRow. + /// + /// The address represents the first address for which the CFAValue and + /// RegLocs are valid within a function. Clients must ensure that this object + /// already has an address (\see hasAddress()) prior to calling this + /// function. + void slideAddress(uint64_t Offset) { *Address += Offset; } + UnwindLocation &getCFAValue() { return CFAValue; } + const UnwindLocation &getCFAValue() const { return CFAValue; } + RegisterLocations &getRegisterLocations() { return RegLocs; } + const RegisterLocations &getRegisterLocations() const { return RegLocs; } + + /// Dump the UnwindRow to the stream. + /// + /// \param OS the stream to use for output. + /// + /// \param MRI register information that helps emit register names insteead + /// of raw register numbers. + /// + /// \param IsEH true if the DWARF Call Frame Information is from .eh_frame + /// instead of from .debug_frame. This is needed for register number + /// conversion because some register numbers differ between the two sections + /// for certain architectures like x86. + /// + /// \param IndentLevel specify the indent level as an integer. The UnwindRow + /// will be output to the stream preceded by 2 * IndentLevel number of spaces. + LLVM_ABI void dump(raw_ostream &OS, DIDumpOptions DumpOpts, + unsigned IndentLevel = 0) const; +}; + +LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const UnwindRow &Row); + +/// A class that contains all UnwindRow objects for an FDE or a single unwind +/// row for a CIE. To unwind an address the rows, which are sorted by start +/// address, can be searched to find the UnwindRow with the lowest starting +/// address that is greater than or equal to the address that is being looked +/// up. +class UnwindTable { +public: + using RowContainer = std::vector; + using iterator = RowContainer::iterator; + using const_iterator = RowContainer::const_iterator; + + UnwindTable(RowContainer &&Rows) : Rows(std::move(Rows)) {} + + size_t size() const { return Rows.size(); } + iterator begin() { return Rows.begin(); } + const_iterator begin() const { return Rows.begin(); } + iterator end() { return Rows.end(); } + const_iterator end() const { return Rows.end(); } + const UnwindRow &operator[](size_t Index) const { + assert(Index < size()); + return Rows[Index]; + } + + /// Dump the UnwindTable to the stream. + /// + /// \param OS the stream to use for output. + /// + /// \param MRI register information that helps emit register names instead + /// of raw register numbers. + /// + /// \param IsEH true if the DWARF Call Frame Information is from .eh_frame + /// instead of from .debug_frame. This is needed for register number + /// conversion because some register numbers differ between the two sections + /// for certain architectures like x86. + /// + /// \param IndentLevel specify the indent level as an integer. The UnwindRow + /// will be output to the stream preceded by 2 * IndentLevel number of spaces. + LLVM_ABI void dump(raw_ostream &OS, DIDumpOptions DumpOpts, + unsigned IndentLevel = 0) const; + +private: + RowContainer Rows; +}; + +/// Parse the information in the CFIProgram and update the CurrRow object +/// that the state machine describes. +/// +/// This function emulates the state machine described in the DWARF Call Frame +/// Information opcodes and will push CurrRow onto a RowContainer when needed. +/// +/// \param CFIP the CFI program that contains the opcodes from a CIE or FDE. +/// +/// \param CurrRow the current row to modify while parsing the state machine. +/// +/// \param InitialLocs If non-NULL, we are parsing a FDE and this contains +/// the initial register locations from the CIE. If NULL, then a CIE's +/// opcodes are being parsed and this is not needed. This is used for the +/// DW_CFA_restore and DW_CFA_restore_extended opcodes. +/// +/// \returns An error if the DWARF Call Frame Information opcodes have state +/// machine errors, or the accumulated rows otherwise. +LLVM_ABI Expected +parseRows(const CFIProgram &CFIP, UnwindRow &CurrRow, + const RegisterLocations *InitialLocs); + +LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const UnwindTable &Rows); + +} // end namespace dwarf + +} // end namespace llvm + +#endif diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp index 2bf88c9574c2d..f8505743aace0 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp @@ -32,180 +32,6 @@ using namespace llvm; using namespace dwarf; -static void printRegister(raw_ostream &OS, DIDumpOptions DumpOpts, - unsigned RegNum) { - if (DumpOpts.GetNameForDWARFReg) { - auto RegName = DumpOpts.GetNameForDWARFReg(RegNum, DumpOpts.IsEH); - if (!RegName.empty()) { - OS << RegName; - return; - } - } - OS << "reg" << RegNum; -} - -UnwindLocation UnwindLocation::createUnspecified() { return {Unspecified}; } - -UnwindLocation UnwindLocation::createUndefined() { return {Undefined}; } - -UnwindLocation UnwindLocation::createSame() { return {Same}; } - -UnwindLocation UnwindLocation::createIsConstant(int32_t Value) { - return {Constant, InvalidRegisterNumber, Value, std::nullopt, false}; -} - -UnwindLocation UnwindLocation::createIsCFAPlusOffset(int32_t Offset) { - return {CFAPlusOffset, InvalidRegisterNumber, Offset, std::nullopt, false}; -} - -UnwindLocation UnwindLocation::createAtCFAPlusOffset(int32_t Offset) { - return {CFAPlusOffset, InvalidRegisterNumber, Offset, std::nullopt, true}; -} - -UnwindLocation -UnwindLocation::createIsRegisterPlusOffset(uint32_t RegNum, int32_t Offset, - std::optional AddrSpace) { - return {RegPlusOffset, RegNum, Offset, AddrSpace, false}; -} - -UnwindLocation -UnwindLocation::createAtRegisterPlusOffset(uint32_t RegNum, int32_t Offset, - std::optional AddrSpace) { - return {RegPlusOffset, RegNum, Offset, AddrSpace, true}; -} - -UnwindLocation UnwindLocation::createIsDWARFExpression(DWARFExpression Expr) { - return {Expr, false}; -} - -UnwindLocation UnwindLocation::createAtDWARFExpression(DWARFExpression Expr) { - return {Expr, true}; -} - -void UnwindLocation::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const { - if (Dereference) - OS << '['; - switch (Kind) { - case Unspecified: - OS << "unspecified"; - break; - case Undefined: - OS << "undefined"; - break; - case Same: - OS << "same"; - break; - case CFAPlusOffset: - OS << "CFA"; - if (Offset == 0) - break; - if (Offset > 0) - OS << "+"; - OS << Offset; - break; - case RegPlusOffset: - printRegister(OS, DumpOpts, RegNum); - if (Offset == 0 && !AddrSpace) - break; - if (Offset >= 0) - OS << "+"; - OS << Offset; - if (AddrSpace) - OS << " in addrspace" << *AddrSpace; - break; - case DWARFExpr: { - if (Expr) - printDwarfExpression(&Expr.value(), OS, DumpOpts, nullptr); - break; - } - case Constant: - OS << Offset; - break; - } - if (Dereference) - OS << ']'; -} - -raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS, - const UnwindLocation &UL) { - auto DumpOpts = DIDumpOptions(); - UL.dump(OS, DumpOpts); - return OS; -} - -bool UnwindLocation::operator==(const UnwindLocation &RHS) const { - if (Kind != RHS.Kind) - return false; - switch (Kind) { - case Unspecified: - case Undefined: - case Same: - return true; - case CFAPlusOffset: - return Offset == RHS.Offset && Dereference == RHS.Dereference; - case RegPlusOffset: - return RegNum == RHS.RegNum && Offset == RHS.Offset && - Dereference == RHS.Dereference; - case DWARFExpr: - return *Expr == *RHS.Expr && Dereference == RHS.Dereference; - case Constant: - return Offset == RHS.Offset; - } - return false; -} - -void RegisterLocations::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const { - bool First = true; - for (const auto &RegLocPair : Locations) { - if (First) - First = false; - else - OS << ", "; - printRegister(OS, DumpOpts, RegLocPair.first); - OS << '='; - RegLocPair.second.dump(OS, DumpOpts); - } -} - -raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS, - const RegisterLocations &RL) { - auto DumpOpts = DIDumpOptions(); - RL.dump(OS, DumpOpts); - return OS; -} - -void UnwindRow::dump(raw_ostream &OS, DIDumpOptions DumpOpts, - unsigned IndentLevel) const { - OS.indent(2 * IndentLevel); - if (hasAddress()) - OS << format("0x%" PRIx64 ": ", *Address); - OS << "CFA="; - CFAValue.dump(OS, DumpOpts); - if (RegLocs.hasLocations()) { - OS << ": "; - RegLocs.dump(OS, DumpOpts); - } - OS << "\n"; -} - -raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS, const UnwindRow &Row) { - auto DumpOpts = DIDumpOptions(); - Row.dump(OS, DumpOpts, 0); - return OS; -} - -void UnwindTable::dump(raw_ostream &OS, DIDumpOptions DumpOpts, - unsigned IndentLevel) const { - for (const UnwindRow &Row : Rows) - Row.dump(OS, DumpOpts, IndentLevel); -} - -raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS, const UnwindTable &Rows) { - auto DumpOpts = DIDumpOptions(); - Rows.dump(OS, DumpOpts, 0); - return OS; -} - Expected llvm::dwarf::createUnwindTable(const FDE *Fde) { const CIE *Cie = Fde->getLinkedCIE(); if (Cie == nullptr) @@ -259,316 +85,6 @@ Expected llvm::dwarf::createUnwindTable(const CIE *Cie) { return UnwindTable(std::move(Rows)); } -Expected -llvm::dwarf::parseRows(const CFIProgram &CFIP, UnwindRow &Row, - const RegisterLocations *InitialLocs) { - // All the unwinding rows parsed during processing of the CFI program. - UnwindTable::RowContainer Rows; - - // State consists of CFA value and register locations. - std::vector> States; - for (const CFIProgram::Instruction &Inst : CFIP) { - switch (Inst.Opcode) { - case dwarf::DW_CFA_set_loc: { - // The DW_CFA_set_loc instruction takes a single operand that - // represents a target address. The required action is to create a new - // table row using the specified address as the location. All other - // values in the new row are initially identical to the current row. - // The new location value is always greater than the current one. If - // the segment_size field of this FDE's CIE is non- zero, the initial - // location is preceded by a segment selector of the given length - llvm::Expected NewAddress = Inst.getOperandAsUnsigned(CFIP, 0); - if (!NewAddress) - return NewAddress.takeError(); - if (*NewAddress <= Row.getAddress()) - return createStringError( - errc::invalid_argument, - "%s with adrress 0x%" PRIx64 " which must be greater than the " - "current row address 0x%" PRIx64, - CFIP.callFrameString(Inst.Opcode).str().c_str(), *NewAddress, - Row.getAddress()); - Rows.push_back(Row); - Row.setAddress(*NewAddress); - break; - } - - case dwarf::DW_CFA_advance_loc: - case dwarf::DW_CFA_advance_loc1: - case dwarf::DW_CFA_advance_loc2: - case dwarf::DW_CFA_advance_loc4: { - // The DW_CFA_advance instruction takes a single operand that - // represents a constant delta. The required action is to create a new - // table row with a location value that is computed by taking the - // current entry’s location value and adding the value of delta * - // code_alignment_factor. All other values in the new row are initially - // identical to the current row. - Rows.push_back(Row); - llvm::Expected Offset = Inst.getOperandAsUnsigned(CFIP, 0); - if (!Offset) - return Offset.takeError(); - Row.slideAddress(*Offset); - break; - } - - case dwarf::DW_CFA_restore: - case dwarf::DW_CFA_restore_extended: { - // The DW_CFA_restore instruction takes a single operand (encoded with - // the opcode) that represents a register number. The required action - // is to change the rule for the indicated register to the rule - // assigned it by the initial_instructions in the CIE. - if (InitialLocs == nullptr) - return createStringError( - errc::invalid_argument, "%s encountered while parsing a CIE", - CFIP.callFrameString(Inst.Opcode).str().c_str()); - llvm::Expected RegNum = Inst.getOperandAsUnsigned(CFIP, 0); - if (!RegNum) - return RegNum.takeError(); - if (std::optional O = - InitialLocs->getRegisterLocation(*RegNum)) - Row.getRegisterLocations().setRegisterLocation(*RegNum, *O); - else - Row.getRegisterLocations().removeRegisterLocation(*RegNum); - break; - } - - case dwarf::DW_CFA_offset: - case dwarf::DW_CFA_offset_extended: - case dwarf::DW_CFA_offset_extended_sf: { - llvm::Expected RegNum = Inst.getOperandAsUnsigned(CFIP, 0); - if (!RegNum) - return RegNum.takeError(); - llvm::Expected Offset = Inst.getOperandAsSigned(CFIP, 1); - if (!Offset) - return Offset.takeError(); - Row.getRegisterLocations().setRegisterLocation( - *RegNum, UnwindLocation::createAtCFAPlusOffset(*Offset)); - break; - } - - case dwarf::DW_CFA_nop: - break; - - case dwarf::DW_CFA_remember_state: - States.push_back( - std::make_pair(Row.getCFAValue(), Row.getRegisterLocations())); - break; - - case dwarf::DW_CFA_restore_state: - if (States.empty()) - return createStringError(errc::invalid_argument, - "DW_CFA_restore_state without a matching " - "previous DW_CFA_remember_state"); - Row.getCFAValue() = States.back().first; - Row.getRegisterLocations() = States.back().second; - States.pop_back(); - break; - - case dwarf::DW_CFA_GNU_window_save: - switch (CFIP.triple()) { - case Triple::aarch64: - case Triple::aarch64_be: - case Triple::aarch64_32: { - // DW_CFA_GNU_window_save is used for different things on different - // architectures. For aarch64 it is known as - // DW_CFA_AARCH64_negate_ra_state. The action is to toggle the - // value of the return address state between 1 and 0. If there is - // no rule for the AARCH64_DWARF_PAUTH_RA_STATE register, then it - // should be initially set to 1. - constexpr uint32_t AArch64DWARFPAuthRaState = 34; - auto LRLoc = Row.getRegisterLocations().getRegisterLocation( - AArch64DWARFPAuthRaState); - if (LRLoc) { - if (LRLoc->getLocation() == UnwindLocation::Constant) { - // Toggle the constant value from 0 to 1 or 1 to 0. - LRLoc->setConstant(LRLoc->getConstant() ^ 1); - Row.getRegisterLocations().setRegisterLocation( - AArch64DWARFPAuthRaState, *LRLoc); - } else { - return createStringError( - errc::invalid_argument, - "%s encountered when existing rule for this register is not " - "a constant", - CFIP.callFrameString(Inst.Opcode).str().c_str()); - } - } else { - Row.getRegisterLocations().setRegisterLocation( - AArch64DWARFPAuthRaState, UnwindLocation::createIsConstant(1)); - } - break; - } - - case Triple::sparc: - case Triple::sparcv9: - case Triple::sparcel: - for (uint32_t RegNum = 16; RegNum < 32; ++RegNum) { - Row.getRegisterLocations().setRegisterLocation( - RegNum, UnwindLocation::createAtCFAPlusOffset((RegNum - 16) * 8)); - } - break; - - default: { - return createStringError( - errc::not_supported, - "DW_CFA opcode %#x is not supported for architecture %s", - Inst.Opcode, Triple::getArchTypeName(CFIP.triple()).str().c_str()); - - break; - } - } - break; - - case dwarf::DW_CFA_AARCH64_negate_ra_state_with_pc: { - constexpr uint32_t AArch64DWARFPAuthRaState = 34; - auto LRLoc = Row.getRegisterLocations().getRegisterLocation( - AArch64DWARFPAuthRaState); - if (LRLoc) { - if (LRLoc->getLocation() == UnwindLocation::Constant) { - // Toggle the constant value of bits[1:0] from 0 to 1 or 1 to 0. - LRLoc->setConstant(LRLoc->getConstant() ^ 0x3); - } else { - return createStringError( - errc::invalid_argument, - "%s encountered when existing rule for this register is not " - "a constant", - CFIP.callFrameString(Inst.Opcode).str().c_str()); - } - } else { - Row.getRegisterLocations().setRegisterLocation( - AArch64DWARFPAuthRaState, UnwindLocation::createIsConstant(0x3)); - } - break; - } - - case dwarf::DW_CFA_undefined: { - llvm::Expected RegNum = Inst.getOperandAsUnsigned(CFIP, 0); - if (!RegNum) - return RegNum.takeError(); - Row.getRegisterLocations().setRegisterLocation( - *RegNum, UnwindLocation::createUndefined()); - break; - } - - case dwarf::DW_CFA_same_value: { - llvm::Expected RegNum = Inst.getOperandAsUnsigned(CFIP, 0); - if (!RegNum) - return RegNum.takeError(); - Row.getRegisterLocations().setRegisterLocation( - *RegNum, UnwindLocation::createSame()); - break; - } - - case dwarf::DW_CFA_GNU_args_size: - break; - - case dwarf::DW_CFA_register: { - llvm::Expected RegNum = Inst.getOperandAsUnsigned(CFIP, 0); - if (!RegNum) - return RegNum.takeError(); - llvm::Expected NewRegNum = Inst.getOperandAsUnsigned(CFIP, 1); - if (!NewRegNum) - return NewRegNum.takeError(); - Row.getRegisterLocations().setRegisterLocation( - *RegNum, UnwindLocation::createIsRegisterPlusOffset(*NewRegNum, 0)); - break; - } - - case dwarf::DW_CFA_val_offset: - case dwarf::DW_CFA_val_offset_sf: { - llvm::Expected RegNum = Inst.getOperandAsUnsigned(CFIP, 0); - if (!RegNum) - return RegNum.takeError(); - llvm::Expected Offset = Inst.getOperandAsSigned(CFIP, 1); - if (!Offset) - return Offset.takeError(); - Row.getRegisterLocations().setRegisterLocation( - *RegNum, UnwindLocation::createIsCFAPlusOffset(*Offset)); - break; - } - - case dwarf::DW_CFA_expression: { - llvm::Expected RegNum = Inst.getOperandAsUnsigned(CFIP, 0); - if (!RegNum) - return RegNum.takeError(); - Row.getRegisterLocations().setRegisterLocation( - *RegNum, UnwindLocation::createAtDWARFExpression(*Inst.Expression)); - break; - } - - case dwarf::DW_CFA_val_expression: { - llvm::Expected RegNum = Inst.getOperandAsUnsigned(CFIP, 0); - if (!RegNum) - return RegNum.takeError(); - Row.getRegisterLocations().setRegisterLocation( - *RegNum, UnwindLocation::createIsDWARFExpression(*Inst.Expression)); - break; - } - - case dwarf::DW_CFA_def_cfa_register: { - llvm::Expected RegNum = Inst.getOperandAsUnsigned(CFIP, 0); - if (!RegNum) - return RegNum.takeError(); - if (Row.getCFAValue().getLocation() != UnwindLocation::RegPlusOffset) - Row.getCFAValue() = - UnwindLocation::createIsRegisterPlusOffset(*RegNum, 0); - else - Row.getCFAValue().setRegister(*RegNum); - break; - } - - case dwarf::DW_CFA_def_cfa_offset: - case dwarf::DW_CFA_def_cfa_offset_sf: { - llvm::Expected Offset = Inst.getOperandAsSigned(CFIP, 0); - if (!Offset) - return Offset.takeError(); - if (Row.getCFAValue().getLocation() != UnwindLocation::RegPlusOffset) { - return createStringError( - errc::invalid_argument, - "%s found when CFA rule was not RegPlusOffset", - CFIP.callFrameString(Inst.Opcode).str().c_str()); - } - Row.getCFAValue().setOffset(*Offset); - break; - } - - case dwarf::DW_CFA_def_cfa: - case dwarf::DW_CFA_def_cfa_sf: { - llvm::Expected RegNum = Inst.getOperandAsUnsigned(CFIP, 0); - if (!RegNum) - return RegNum.takeError(); - llvm::Expected Offset = Inst.getOperandAsSigned(CFIP, 1); - if (!Offset) - return Offset.takeError(); - Row.getCFAValue() = - UnwindLocation::createIsRegisterPlusOffset(*RegNum, *Offset); - break; - } - - case dwarf::DW_CFA_LLVM_def_aspace_cfa: - case dwarf::DW_CFA_LLVM_def_aspace_cfa_sf: { - llvm::Expected RegNum = Inst.getOperandAsUnsigned(CFIP, 0); - if (!RegNum) - return RegNum.takeError(); - llvm::Expected Offset = Inst.getOperandAsSigned(CFIP, 1); - if (!Offset) - return Offset.takeError(); - llvm::Expected CFAAddrSpace = - Inst.getOperandAsUnsigned(CFIP, 2); - if (!CFAAddrSpace) - return CFAAddrSpace.takeError(); - Row.getCFAValue() = UnwindLocation::createIsRegisterPlusOffset( - *RegNum, *Offset, *CFAAddrSpace); - break; - } - - case dwarf::DW_CFA_def_cfa_expression: - Row.getCFAValue() = - UnwindLocation::createIsDWARFExpression(*Inst.Expression); - break; - } - } - return Rows; -} - // Returns the CIE identifier to be used by the requested format. // CIE ids for .debug_frame sections are defined in Section 7.24 of DWARFv5. // For CIE ID in .eh_frame sections see diff --git a/llvm/lib/DebugInfo/DWARF/LowLevel/CMakeLists.txt b/llvm/lib/DebugInfo/DWARF/LowLevel/CMakeLists.txt index c11a2589a552d..4e920aa7ae1a6 100644 --- a/llvm/lib/DebugInfo/DWARF/LowLevel/CMakeLists.txt +++ b/llvm/lib/DebugInfo/DWARF/LowLevel/CMakeLists.txt @@ -1,6 +1,7 @@ add_llvm_component_library(LLVMDebugInfoDWARFLowLevel DWARFCFIProgram.cpp DWARFExpression.cpp + DWARFUnwindTable.cpp ADDITIONAL_HEADER_DIRS ${LLVM_MAIN_INCLUDE_DIR}/llvm/DebugInfo/DWARF/LowLevel diff --git a/llvm/lib/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.cpp b/llvm/lib/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.cpp new file mode 100644 index 0000000000000..6e57d3344b95e --- /dev/null +++ b/llvm/lib/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.cpp @@ -0,0 +1,506 @@ +//=== DWARFUnwindTable.cpp - Parsing CFI instructions into unwinding table ===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.h" +#include "llvm/DebugInfo/DIContext.h" +#include "llvm/DebugInfo/DWARF/DWARFExpressionPrinter.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" +#include +#include +#include +#include + +using namespace llvm; +using namespace dwarf; + +static void printRegister(raw_ostream &OS, DIDumpOptions DumpOpts, + unsigned RegNum) { + if (DumpOpts.GetNameForDWARFReg) { + auto RegName = DumpOpts.GetNameForDWARFReg(RegNum, DumpOpts.IsEH); + if (!RegName.empty()) { + OS << RegName; + return; + } + } + OS << "reg" << RegNum; +} + +UnwindLocation UnwindLocation::createUnspecified() { return {Unspecified}; } + +UnwindLocation UnwindLocation::createUndefined() { return {Undefined}; } + +UnwindLocation UnwindLocation::createSame() { return {Same}; } + +UnwindLocation UnwindLocation::createIsConstant(int32_t Value) { + return {Constant, InvalidRegisterNumber, Value, std::nullopt, false}; +} + +UnwindLocation UnwindLocation::createIsCFAPlusOffset(int32_t Offset) { + return {CFAPlusOffset, InvalidRegisterNumber, Offset, std::nullopt, false}; +} + +UnwindLocation UnwindLocation::createAtCFAPlusOffset(int32_t Offset) { + return {CFAPlusOffset, InvalidRegisterNumber, Offset, std::nullopt, true}; +} + +UnwindLocation +UnwindLocation::createIsRegisterPlusOffset(uint32_t RegNum, int32_t Offset, + std::optional AddrSpace) { + return {RegPlusOffset, RegNum, Offset, AddrSpace, false}; +} + +UnwindLocation +UnwindLocation::createAtRegisterPlusOffset(uint32_t RegNum, int32_t Offset, + std::optional AddrSpace) { + return {RegPlusOffset, RegNum, Offset, AddrSpace, true}; +} + +UnwindLocation UnwindLocation::createIsDWARFExpression(DWARFExpression Expr) { + return {Expr, false}; +} + +UnwindLocation UnwindLocation::createAtDWARFExpression(DWARFExpression Expr) { + return {Expr, true}; +} + +void UnwindLocation::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const { + if (Dereference) + OS << '['; + switch (Kind) { + case Unspecified: + OS << "unspecified"; + break; + case Undefined: + OS << "undefined"; + break; + case Same: + OS << "same"; + break; + case CFAPlusOffset: + OS << "CFA"; + if (Offset == 0) + break; + if (Offset > 0) + OS << "+"; + OS << Offset; + break; + case RegPlusOffset: + printRegister(OS, DumpOpts, RegNum); + if (Offset == 0 && !AddrSpace) + break; + if (Offset >= 0) + OS << "+"; + OS << Offset; + if (AddrSpace) + OS << " in addrspace" << *AddrSpace; + break; + case DWARFExpr: { + if (Expr) + printDwarfExpression(&(*Expr), OS, DumpOpts, nullptr); + break; + } + case Constant: + OS << Offset; + break; + } + if (Dereference) + OS << ']'; +} + +raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS, + const UnwindLocation &UL) { + auto DumpOpts = DIDumpOptions(); + UL.dump(OS, DumpOpts); + return OS; +} + +bool UnwindLocation::operator==(const UnwindLocation &RHS) const { + if (Kind != RHS.Kind) + return false; + switch (Kind) { + case Unspecified: + case Undefined: + case Same: + return true; + case CFAPlusOffset: + return Offset == RHS.Offset && Dereference == RHS.Dereference; + case RegPlusOffset: + return RegNum == RHS.RegNum && Offset == RHS.Offset && + Dereference == RHS.Dereference; + case DWARFExpr: + return *Expr == *RHS.Expr && Dereference == RHS.Dereference; + case Constant: + return Offset == RHS.Offset; + } + return false; +} + +void RegisterLocations::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const { + bool First = true; + for (const auto &RegLocPair : Locations) { + if (First) + First = false; + else + OS << ", "; + printRegister(OS, DumpOpts, RegLocPair.first); + OS << '='; + RegLocPair.second.dump(OS, DumpOpts); + } +} + +raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS, + const RegisterLocations &RL) { + auto DumpOpts = DIDumpOptions(); + RL.dump(OS, DumpOpts); + return OS; +} + +void UnwindRow::dump(raw_ostream &OS, DIDumpOptions DumpOpts, + unsigned IndentLevel) const { + OS.indent(2 * IndentLevel); + if (hasAddress()) + OS << format("0x%" PRIx64 ": ", *Address); + OS << "CFA="; + CFAValue.dump(OS, DumpOpts); + if (RegLocs.hasLocations()) { + OS << ": "; + RegLocs.dump(OS, DumpOpts); + } + OS << "\n"; +} + +raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS, const UnwindRow &Row) { + auto DumpOpts = DIDumpOptions(); + Row.dump(OS, DumpOpts, 0); + return OS; +} + +void UnwindTable::dump(raw_ostream &OS, DIDumpOptions DumpOpts, + unsigned IndentLevel) const { + for (const UnwindRow &Row : Rows) + Row.dump(OS, DumpOpts, IndentLevel); +} + +raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS, const UnwindTable &Rows) { + auto DumpOpts = DIDumpOptions(); + Rows.dump(OS, DumpOpts, 0); + return OS; +} + +Expected +llvm::dwarf::parseRows(const CFIProgram &CFIP, UnwindRow &Row, + const RegisterLocations *InitialLocs) { + // All the unwinding rows parsed during processing of the CFI program. + UnwindTable::RowContainer Rows; + + // State consists of CFA value and register locations. + std::vector> States; + for (const CFIProgram::Instruction &Inst : CFIP) { + switch (Inst.Opcode) { + case dwarf::DW_CFA_set_loc: { + // The DW_CFA_set_loc instruction takes a single operand that + // represents a target address. The required action is to create a new + // table row using the specified address as the location. All other + // values in the new row are initially identical to the current row. + // The new location value is always greater than the current one. If + // the segment_size field of this FDE's CIE is non- zero, the initial + // location is preceded by a segment selector of the given length + llvm::Expected NewAddress = Inst.getOperandAsUnsigned(CFIP, 0); + if (!NewAddress) + return NewAddress.takeError(); + if (*NewAddress <= Row.getAddress()) + return createStringError( + errc::invalid_argument, + "%s with adrress 0x%" PRIx64 " which must be greater than the " + "current row address 0x%" PRIx64, + CFIP.callFrameString(Inst.Opcode).str().c_str(), *NewAddress, + Row.getAddress()); + Rows.push_back(Row); + Row.setAddress(*NewAddress); + break; + } + + case dwarf::DW_CFA_advance_loc: + case dwarf::DW_CFA_advance_loc1: + case dwarf::DW_CFA_advance_loc2: + case dwarf::DW_CFA_advance_loc4: { + // The DW_CFA_advance instruction takes a single operand that + // represents a constant delta. The required action is to create a new + // table row with a location value that is computed by taking the + // current entry’s location value and adding the value of delta * + // code_alignment_factor. All other values in the new row are initially + // identical to the current row. + Rows.push_back(Row); + llvm::Expected Offset = Inst.getOperandAsUnsigned(CFIP, 0); + if (!Offset) + return Offset.takeError(); + Row.slideAddress(*Offset); + break; + } + + case dwarf::DW_CFA_restore: + case dwarf::DW_CFA_restore_extended: { + // The DW_CFA_restore instruction takes a single operand (encoded with + // the opcode) that represents a register number. The required action + // is to change the rule for the indicated register to the rule + // assigned it by the initial_instructions in the CIE. + if (InitialLocs == nullptr) + return createStringError( + errc::invalid_argument, "%s encountered while parsing a CIE", + CFIP.callFrameString(Inst.Opcode).str().c_str()); + llvm::Expected RegNum = Inst.getOperandAsUnsigned(CFIP, 0); + if (!RegNum) + return RegNum.takeError(); + if (std::optional O = + InitialLocs->getRegisterLocation(*RegNum)) + Row.getRegisterLocations().setRegisterLocation(*RegNum, *O); + else + Row.getRegisterLocations().removeRegisterLocation(*RegNum); + break; + } + + case dwarf::DW_CFA_offset: + case dwarf::DW_CFA_offset_extended: + case dwarf::DW_CFA_offset_extended_sf: { + llvm::Expected RegNum = Inst.getOperandAsUnsigned(CFIP, 0); + if (!RegNum) + return RegNum.takeError(); + llvm::Expected Offset = Inst.getOperandAsSigned(CFIP, 1); + if (!Offset) + return Offset.takeError(); + Row.getRegisterLocations().setRegisterLocation( + *RegNum, UnwindLocation::createAtCFAPlusOffset(*Offset)); + break; + } + + case dwarf::DW_CFA_nop: + break; + + case dwarf::DW_CFA_remember_state: + States.push_back( + std::make_pair(Row.getCFAValue(), Row.getRegisterLocations())); + break; + + case dwarf::DW_CFA_restore_state: + if (States.empty()) + return createStringError(errc::invalid_argument, + "DW_CFA_restore_state without a matching " + "previous DW_CFA_remember_state"); + Row.getCFAValue() = States.back().first; + Row.getRegisterLocations() = States.back().second; + States.pop_back(); + break; + + case dwarf::DW_CFA_GNU_window_save: + switch (CFIP.triple()) { + case Triple::aarch64: + case Triple::aarch64_be: + case Triple::aarch64_32: { + // DW_CFA_GNU_window_save is used for different things on different + // architectures. For aarch64 it is known as + // DW_CFA_AARCH64_negate_ra_state. The action is to toggle the + // value of the return address state between 1 and 0. If there is + // no rule for the AARCH64_DWARF_PAUTH_RA_STATE register, then it + // should be initially set to 1. + constexpr uint32_t AArch64DWARFPAuthRaState = 34; + auto LRLoc = Row.getRegisterLocations().getRegisterLocation( + AArch64DWARFPAuthRaState); + if (LRLoc) { + if (LRLoc->getLocation() == UnwindLocation::Constant) { + // Toggle the constant value from 0 to 1 or 1 to 0. + LRLoc->setConstant(LRLoc->getConstant() ^ 1); + Row.getRegisterLocations().setRegisterLocation( + AArch64DWARFPAuthRaState, *LRLoc); + } else { + return createStringError( + errc::invalid_argument, + "%s encountered when existing rule for this register is not " + "a constant", + CFIP.callFrameString(Inst.Opcode).str().c_str()); + } + } else { + Row.getRegisterLocations().setRegisterLocation( + AArch64DWARFPAuthRaState, UnwindLocation::createIsConstant(1)); + } + break; + } + + case Triple::sparc: + case Triple::sparcv9: + case Triple::sparcel: + for (uint32_t RegNum = 16; RegNum < 32; ++RegNum) { + Row.getRegisterLocations().setRegisterLocation( + RegNum, UnwindLocation::createAtCFAPlusOffset((RegNum - 16) * 8)); + } + break; + + default: { + return createStringError( + errc::not_supported, + "DW_CFA opcode %#x is not supported for architecture %s", + Inst.Opcode, Triple::getArchTypeName(CFIP.triple()).str().c_str()); + + break; + } + } + break; + + case dwarf::DW_CFA_AARCH64_negate_ra_state_with_pc: { + constexpr uint32_t AArch64DWARFPAuthRaState = 34; + auto LRLoc = Row.getRegisterLocations().getRegisterLocation( + AArch64DWARFPAuthRaState); + if (LRLoc) { + if (LRLoc->getLocation() == UnwindLocation::Constant) { + // Toggle the constant value of bits[1:0] from 0 to 1 or 1 to 0. + LRLoc->setConstant(LRLoc->getConstant() ^ 0x3); + } else { + return createStringError( + errc::invalid_argument, + "%s encountered when existing rule for this register is not " + "a constant", + CFIP.callFrameString(Inst.Opcode).str().c_str()); + } + } else { + Row.getRegisterLocations().setRegisterLocation( + AArch64DWARFPAuthRaState, UnwindLocation::createIsConstant(0x3)); + } + break; + } + + case dwarf::DW_CFA_undefined: { + llvm::Expected RegNum = Inst.getOperandAsUnsigned(CFIP, 0); + if (!RegNum) + return RegNum.takeError(); + Row.getRegisterLocations().setRegisterLocation( + *RegNum, UnwindLocation::createUndefined()); + break; + } + + case dwarf::DW_CFA_same_value: { + llvm::Expected RegNum = Inst.getOperandAsUnsigned(CFIP, 0); + if (!RegNum) + return RegNum.takeError(); + Row.getRegisterLocations().setRegisterLocation( + *RegNum, UnwindLocation::createSame()); + break; + } + + case dwarf::DW_CFA_GNU_args_size: + break; + + case dwarf::DW_CFA_register: { + llvm::Expected RegNum = Inst.getOperandAsUnsigned(CFIP, 0); + if (!RegNum) + return RegNum.takeError(); + llvm::Expected NewRegNum = Inst.getOperandAsUnsigned(CFIP, 1); + if (!NewRegNum) + return NewRegNum.takeError(); + Row.getRegisterLocations().setRegisterLocation( + *RegNum, UnwindLocation::createIsRegisterPlusOffset(*NewRegNum, 0)); + break; + } + + case dwarf::DW_CFA_val_offset: + case dwarf::DW_CFA_val_offset_sf: { + llvm::Expected RegNum = Inst.getOperandAsUnsigned(CFIP, 0); + if (!RegNum) + return RegNum.takeError(); + llvm::Expected Offset = Inst.getOperandAsSigned(CFIP, 1); + if (!Offset) + return Offset.takeError(); + Row.getRegisterLocations().setRegisterLocation( + *RegNum, UnwindLocation::createIsCFAPlusOffset(*Offset)); + break; + } + + case dwarf::DW_CFA_expression: { + llvm::Expected RegNum = Inst.getOperandAsUnsigned(CFIP, 0); + if (!RegNum) + return RegNum.takeError(); + Row.getRegisterLocations().setRegisterLocation( + *RegNum, UnwindLocation::createAtDWARFExpression(*Inst.Expression)); + break; + } + + case dwarf::DW_CFA_val_expression: { + llvm::Expected RegNum = Inst.getOperandAsUnsigned(CFIP, 0); + if (!RegNum) + return RegNum.takeError(); + Row.getRegisterLocations().setRegisterLocation( + *RegNum, UnwindLocation::createIsDWARFExpression(*Inst.Expression)); + break; + } + + case dwarf::DW_CFA_def_cfa_register: { + llvm::Expected RegNum = Inst.getOperandAsUnsigned(CFIP, 0); + if (!RegNum) + return RegNum.takeError(); + if (Row.getCFAValue().getLocation() != UnwindLocation::RegPlusOffset) + Row.getCFAValue() = + UnwindLocation::createIsRegisterPlusOffset(*RegNum, 0); + else + Row.getCFAValue().setRegister(*RegNum); + break; + } + + case dwarf::DW_CFA_def_cfa_offset: + case dwarf::DW_CFA_def_cfa_offset_sf: { + llvm::Expected Offset = Inst.getOperandAsSigned(CFIP, 0); + if (!Offset) + return Offset.takeError(); + if (Row.getCFAValue().getLocation() != UnwindLocation::RegPlusOffset) { + return createStringError( + errc::invalid_argument, + "%s found when CFA rule was not RegPlusOffset", + CFIP.callFrameString(Inst.Opcode).str().c_str()); + } + Row.getCFAValue().setOffset(*Offset); + break; + } + + case dwarf::DW_CFA_def_cfa: + case dwarf::DW_CFA_def_cfa_sf: { + llvm::Expected RegNum = Inst.getOperandAsUnsigned(CFIP, 0); + if (!RegNum) + return RegNum.takeError(); + llvm::Expected Offset = Inst.getOperandAsSigned(CFIP, 1); + if (!Offset) + return Offset.takeError(); + Row.getCFAValue() = + UnwindLocation::createIsRegisterPlusOffset(*RegNum, *Offset); + break; + } + + case dwarf::DW_CFA_LLVM_def_aspace_cfa: + case dwarf::DW_CFA_LLVM_def_aspace_cfa_sf: { + llvm::Expected RegNum = Inst.getOperandAsUnsigned(CFIP, 0); + if (!RegNum) + return RegNum.takeError(); + llvm::Expected Offset = Inst.getOperandAsSigned(CFIP, 1); + if (!Offset) + return Offset.takeError(); + llvm::Expected CFAAddrSpace = + Inst.getOperandAsUnsigned(CFIP, 2); + if (!CFAAddrSpace) + return CFAAddrSpace.takeError(); + Row.getCFAValue() = UnwindLocation::createIsRegisterPlusOffset( + *RegNum, *Offset, *CFAAddrSpace); + break; + } + + case dwarf::DW_CFA_def_cfa_expression: + Row.getCFAValue() = + UnwindLocation::createIsDWARFExpression(*Inst.Expression); + break; + } + } + return Rows; +}