xref: /freebsd/contrib/llvm-project/lldb/include/lldb/Symbol/UnwindPlan.h (revision 700637cbb5e582861067a11aaca4d053546871d2)
1 //===-- UnwindPlan.h --------------------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #ifndef LLDB_SYMBOL_UNWINDPLAN_H
10 #define LLDB_SYMBOL_UNWINDPLAN_H
11 
12 #include <map>
13 #include <memory>
14 #include <vector>
15 
16 #include "lldb/Core/AddressRange.h"
17 #include "lldb/Utility/ConstString.h"
18 #include "lldb/Utility/Stream.h"
19 #include "lldb/lldb-private.h"
20 
21 namespace lldb_private {
22 
23 // The UnwindPlan object specifies how to unwind out of a function - where this
24 // function saves the caller's register values before modifying them (for non-
25 // volatile aka saved registers) and how to find this frame's Canonical Frame
26 // Address (CFA) or Aligned Frame Address (AFA).
27 
28 // CFA is a DWARF's Canonical Frame Address.
29 // Most commonly, registers are saved on the stack, offset some bytes from the
30 // Canonical Frame Address, or CFA, which is the starting address of this
31 // function's stack frame (the CFA is same as the eh_frame's CFA, whatever that
32 // may be on a given architecture). The CFA address for the stack frame does
33 // not change during the lifetime of the function.
34 
35 // AFA is an artificially introduced Aligned Frame Address.
36 // It is used only for stack frames with realignment (e.g. when some of the
37 // locals has an alignment requirement higher than the stack alignment right
38 // after the function call). It is used to access register values saved on the
39 // stack after the realignment (and so they are inaccessible through the CFA).
40 // AFA usually equals the stack pointer value right after the realignment.
41 
42 // Internally, the UnwindPlan is structured as a vector of register locations
43 // organized by code address in the function, showing which registers have been
44 // saved at that point and where they are saved. It can be thought of as the
45 // expanded table form of the DWARF CFI encoded information.
46 
47 // Other unwind information sources will be converted into UnwindPlans before
48 // being added to a FuncUnwinders object.  The unwind source may be an eh_frame
49 // FDE, a DWARF debug_frame FDE, or assembly language based prologue analysis.
50 // The UnwindPlan is the canonical form of this information that the unwinder
51 // code will use when walking the stack.
52 
53 class UnwindPlan {
54 public:
55   class Row {
56   public:
57     class AbstractRegisterLocation {
58     public:
59       enum RestoreType {
60         unspecified,       // not specified, we may be able to assume this
61                            // is the same register. gcc doesn't specify all
62                            // initial values so we really don't know...
63         undefined,         // reg is not available, e.g. volatile reg
64         same,              // reg is unchanged
65         atCFAPlusOffset,   // reg = deref(CFA + offset)
66         isCFAPlusOffset,   // reg = CFA + offset
67         atAFAPlusOffset,   // reg = deref(AFA + offset)
68         isAFAPlusOffset,   // reg = AFA + offset
69         inOtherRegister,   // reg = other reg
70         atDWARFExpression, // reg = deref(eval(dwarf_expr))
71         isDWARFExpression, // reg = eval(dwarf_expr)
72         isConstant         // reg = constant
73       };
74 
AbstractRegisterLocation()75       AbstractRegisterLocation() : m_location() {}
76 
77       bool operator==(const AbstractRegisterLocation &rhs) const;
78 
79       bool operator!=(const AbstractRegisterLocation &rhs) const {
80         return !(*this == rhs);
81       }
82 
SetUnspecified()83       void SetUnspecified() { m_type = unspecified; }
84 
SetUndefined()85       void SetUndefined() { m_type = undefined; }
86 
SetSame()87       void SetSame() { m_type = same; }
88 
IsSame()89       bool IsSame() const { return m_type == same; }
90 
IsUnspecified()91       bool IsUnspecified() const { return m_type == unspecified; }
92 
IsUndefined()93       bool IsUndefined() const { return m_type == undefined; }
94 
IsCFAPlusOffset()95       bool IsCFAPlusOffset() const { return m_type == isCFAPlusOffset; }
96 
IsAtCFAPlusOffset()97       bool IsAtCFAPlusOffset() const { return m_type == atCFAPlusOffset; }
98 
IsAFAPlusOffset()99       bool IsAFAPlusOffset() const { return m_type == isAFAPlusOffset; }
100 
IsAtAFAPlusOffset()101       bool IsAtAFAPlusOffset() const { return m_type == atAFAPlusOffset; }
102 
IsInOtherRegister()103       bool IsInOtherRegister() const { return m_type == inOtherRegister; }
104 
IsAtDWARFExpression()105       bool IsAtDWARFExpression() const { return m_type == atDWARFExpression; }
106 
IsDWARFExpression()107       bool IsDWARFExpression() const { return m_type == isDWARFExpression; }
108 
IsConstant()109       bool IsConstant() const { return m_type == isConstant; }
110 
SetIsConstant(uint64_t value)111       void SetIsConstant(uint64_t value) {
112         m_type = isConstant;
113         m_location.constant_value = value;
114       }
115 
GetConstant()116       uint64_t GetConstant() const { return m_location.constant_value; }
117 
SetAtCFAPlusOffset(int32_t offset)118       void SetAtCFAPlusOffset(int32_t offset) {
119         m_type = atCFAPlusOffset;
120         m_location.offset = offset;
121       }
122 
SetIsCFAPlusOffset(int32_t offset)123       void SetIsCFAPlusOffset(int32_t offset) {
124         m_type = isCFAPlusOffset;
125         m_location.offset = offset;
126       }
127 
SetAtAFAPlusOffset(int32_t offset)128       void SetAtAFAPlusOffset(int32_t offset) {
129         m_type = atAFAPlusOffset;
130         m_location.offset = offset;
131       }
132 
SetIsAFAPlusOffset(int32_t offset)133       void SetIsAFAPlusOffset(int32_t offset) {
134         m_type = isAFAPlusOffset;
135         m_location.offset = offset;
136       }
137 
SetInRegister(uint32_t reg_num)138       void SetInRegister(uint32_t reg_num) {
139         m_type = inOtherRegister;
140         m_location.reg_num = reg_num;
141       }
142 
GetRegisterNumber()143       uint32_t GetRegisterNumber() const {
144         if (m_type == inOtherRegister)
145           return m_location.reg_num;
146         return LLDB_INVALID_REGNUM;
147       }
148 
GetLocationType()149       RestoreType GetLocationType() const { return m_type; }
150 
GetOffset()151       int32_t GetOffset() const {
152         switch(m_type)
153         {
154         case atCFAPlusOffset:
155         case isCFAPlusOffset:
156         case atAFAPlusOffset:
157         case isAFAPlusOffset:
158           return m_location.offset;
159         default:
160           return 0;
161         }
162       }
163 
GetDWARFExpr(const uint8_t ** opcodes,uint16_t & len)164       void GetDWARFExpr(const uint8_t **opcodes, uint16_t &len) const {
165         if (m_type == atDWARFExpression || m_type == isDWARFExpression) {
166           *opcodes = m_location.expr.opcodes;
167           len = m_location.expr.length;
168         } else {
169           *opcodes = nullptr;
170           len = 0;
171         }
172       }
173 
174       void SetAtDWARFExpression(const uint8_t *opcodes, uint32_t len);
175 
176       void SetIsDWARFExpression(const uint8_t *opcodes, uint32_t len);
177 
GetDWARFExpressionBytes()178       const uint8_t *GetDWARFExpressionBytes() const {
179         if (m_type == atDWARFExpression || m_type == isDWARFExpression)
180           return m_location.expr.opcodes;
181         return nullptr;
182       }
183 
GetDWARFExpressionLength()184       int GetDWARFExpressionLength() const {
185         if (m_type == atDWARFExpression || m_type == isDWARFExpression)
186           return m_location.expr.length;
187         return 0;
188       }
189 
190       void Dump(Stream &s, const UnwindPlan *unwind_plan,
191                 const UnwindPlan::Row *row, Thread *thread, bool verbose) const;
192 
193     private:
194       RestoreType m_type = unspecified; // How do we locate this register?
195       union {
196         // For m_type == atCFAPlusOffset or m_type == isCFAPlusOffset
197         int32_t offset;
198         // For m_type == inOtherRegister
199         uint32_t reg_num; // The register number
200         // For m_type == atDWARFExpression or m_type == isDWARFExpression
201         struct {
202           const uint8_t *opcodes;
203           uint16_t length;
204         } expr;
205         // For m_type == isConstant
206         uint64_t constant_value;
207       } m_location;
208     };
209 
210     class FAValue {
211     public:
212       enum ValueType {
213         unspecified,            // not specified
214         isRegisterPlusOffset,   // FA = register + offset
215         isRegisterDereferenced, // FA = [reg]
216         isDWARFExpression,      // FA = eval(dwarf_expr)
217         isRaSearch,             // FA = SP + offset + ???
218         isConstant,             // FA = constant
219       };
220 
FAValue()221       FAValue() : m_value() {}
222 
223       bool operator==(const FAValue &rhs) const;
224 
225       bool operator!=(const FAValue &rhs) const { return !(*this == rhs); }
226 
SetUnspecified()227       void SetUnspecified() { m_type = unspecified; }
228 
IsUnspecified()229       bool IsUnspecified() const { return m_type == unspecified; }
230 
SetRaSearch(int32_t offset)231       void SetRaSearch(int32_t offset) {
232         m_type = isRaSearch;
233         m_value.ra_search_offset = offset;
234       }
235 
IsRegisterPlusOffset()236       bool IsRegisterPlusOffset() const {
237         return m_type == isRegisterPlusOffset;
238       }
239 
SetIsRegisterPlusOffset(uint32_t reg_num,int32_t offset)240       void SetIsRegisterPlusOffset(uint32_t reg_num, int32_t offset) {
241         m_type = isRegisterPlusOffset;
242         m_value.reg.reg_num = reg_num;
243         m_value.reg.offset = offset;
244       }
245 
IsRegisterDereferenced()246       bool IsRegisterDereferenced() const {
247         return m_type == isRegisterDereferenced;
248       }
249 
SetIsRegisterDereferenced(uint32_t reg_num)250       void SetIsRegisterDereferenced(uint32_t reg_num) {
251         m_type = isRegisterDereferenced;
252         m_value.reg.reg_num = reg_num;
253       }
254 
IsDWARFExpression()255       bool IsDWARFExpression() const { return m_type == isDWARFExpression; }
256 
SetIsDWARFExpression(const uint8_t * opcodes,uint32_t len)257       void SetIsDWARFExpression(const uint8_t *opcodes, uint32_t len) {
258         m_type = isDWARFExpression;
259         m_value.expr.opcodes = opcodes;
260         m_value.expr.length = len;
261       }
262 
IsConstant()263       bool IsConstant() const { return m_type == isConstant; }
264 
SetIsConstant(uint64_t constant)265       void SetIsConstant(uint64_t constant) {
266         m_type = isConstant;
267         m_value.constant = constant;
268       }
269 
GetConstant()270       uint64_t GetConstant() const { return m_value.constant; }
271 
GetRegisterNumber()272       uint32_t GetRegisterNumber() const {
273         if (m_type == isRegisterDereferenced || m_type == isRegisterPlusOffset)
274           return m_value.reg.reg_num;
275         return LLDB_INVALID_REGNUM;
276       }
277 
GetValueType()278       ValueType GetValueType() const { return m_type; }
279 
GetOffset()280       int32_t GetOffset() const {
281         switch (m_type) {
282           case isRegisterPlusOffset:
283             return m_value.reg.offset;
284           case isRaSearch:
285             return m_value.ra_search_offset;
286           default:
287             return 0;
288         }
289       }
290 
IncOffset(int32_t delta)291       void IncOffset(int32_t delta) {
292         if (m_type == isRegisterPlusOffset)
293           m_value.reg.offset += delta;
294       }
295 
SetOffset(int32_t offset)296       void SetOffset(int32_t offset) {
297         if (m_type == isRegisterPlusOffset)
298           m_value.reg.offset = offset;
299       }
300 
GetDWARFExpr(const uint8_t ** opcodes,uint16_t & len)301       void GetDWARFExpr(const uint8_t **opcodes, uint16_t &len) const {
302         if (m_type == isDWARFExpression) {
303           *opcodes = m_value.expr.opcodes;
304           len = m_value.expr.length;
305         } else {
306           *opcodes = nullptr;
307           len = 0;
308         }
309       }
310 
GetDWARFExpressionBytes()311       const uint8_t *GetDWARFExpressionBytes() const {
312         if (m_type == isDWARFExpression)
313           return m_value.expr.opcodes;
314         return nullptr;
315       }
316 
GetDWARFExpressionLength()317       int GetDWARFExpressionLength() const {
318         if (m_type == isDWARFExpression)
319           return m_value.expr.length;
320         return 0;
321       }
322 
323       void Dump(Stream &s, const UnwindPlan *unwind_plan, Thread *thread) const;
324 
325     private:
326       ValueType m_type = unspecified; // How do we compute CFA value?
327       union {
328         struct {
329           // For m_type == isRegisterPlusOffset or m_type ==
330           // isRegisterDereferenced
331           uint32_t reg_num; // The register number
332           // For m_type == isRegisterPlusOffset
333           int32_t offset;
334         } reg;
335         // For m_type == isDWARFExpression
336         struct {
337           const uint8_t *opcodes;
338           uint16_t length;
339         } expr;
340         // For m_type == isRaSearch
341         int32_t ra_search_offset;
342         // For m_type = isConstant
343         uint64_t constant;
344       } m_value;
345     }; // class FAValue
346 
347     Row();
348 
349     bool operator==(const Row &rhs) const;
350 
351     bool GetRegisterInfo(uint32_t reg_num,
352                          AbstractRegisterLocation &register_location) const;
353 
354     void SetRegisterInfo(uint32_t reg_num,
355                          const AbstractRegisterLocation register_location);
356 
357     void RemoveRegisterInfo(uint32_t reg_num);
358 
GetOffset()359     int64_t GetOffset() const { return m_offset; }
360 
SetOffset(int64_t offset)361     void SetOffset(int64_t offset) { m_offset = offset; }
362 
SlideOffset(int64_t offset)363     void SlideOffset(int64_t offset) { m_offset += offset; }
364 
GetCFAValue()365     const FAValue &GetCFAValue() const { return m_cfa_value; }
GetCFAValue()366     FAValue &GetCFAValue() { return m_cfa_value; }
367 
GetAFAValue()368     const FAValue &GetAFAValue() const { return m_afa_value; }
GetAFAValue()369     FAValue &GetAFAValue() { return m_afa_value; }
370 
371     bool SetRegisterLocationToAtCFAPlusOffset(uint32_t reg_num, int32_t offset,
372                                               bool can_replace);
373 
374     bool SetRegisterLocationToIsCFAPlusOffset(uint32_t reg_num, int32_t offset,
375                                               bool can_replace);
376 
377     bool SetRegisterLocationToUndefined(uint32_t reg_num, bool can_replace,
378                                         bool can_replace_only_if_unspecified);
379 
380     bool SetRegisterLocationToUnspecified(uint32_t reg_num, bool can_replace);
381 
382     bool SetRegisterLocationToRegister(uint32_t reg_num, uint32_t other_reg_num,
383                                        bool can_replace);
384 
385     bool SetRegisterLocationToSame(uint32_t reg_num, bool must_replace);
386 
387     /// This method does not make a copy of the \a opcodes memory, it is
388     /// assumed to have the same lifetime as the Module this UnwindPlan will
389     /// be registered in.
390     bool SetRegisterLocationToIsDWARFExpression(uint32_t reg_num,
391                                                 const uint8_t *opcodes,
392                                                 uint32_t len, bool can_replace);
393 
394     bool SetRegisterLocationToIsConstant(uint32_t reg_num, uint64_t constant,
395                                          bool can_replace);
396 
397     // When this UnspecifiedRegistersAreUndefined mode is
398     // set, any register that is not specified by this Row will
399     // be described as Undefined.
400     // This will prevent the unwinder from iterating down the
401     // stack looking for a spill location, or a live register value
402     // at frame 0.
403     // It would be used for an UnwindPlan row where we can't track
404     // spilled registers -- for instance a jitted stack frame where
405     // we have no unwind information or start address -- and registers
406     // MAY have been spilled and overwritten, so providing the
407     // spilled/live value from a newer frame may show an incorrect value.
SetUnspecifiedRegistersAreUndefined(bool unspec_is_undef)408     void SetUnspecifiedRegistersAreUndefined(bool unspec_is_undef) {
409       m_unspecified_registers_are_undefined = unspec_is_undef;
410     }
411 
GetUnspecifiedRegistersAreUndefined()412     bool GetUnspecifiedRegistersAreUndefined() {
413       return m_unspecified_registers_are_undefined;
414     }
415 
416     void Clear();
417 
418     void Dump(Stream &s, const UnwindPlan *unwind_plan, Thread *thread,
419               lldb::addr_t base_addr) const;
420 
421   protected:
422     typedef std::map<uint32_t, AbstractRegisterLocation> collection;
423     int64_t m_offset = 0; // Offset into the function for this row
424 
425     FAValue m_cfa_value;
426     FAValue m_afa_value;
427     collection m_register_locations;
428     bool m_unspecified_registers_are_undefined = false;
429   }; // class Row
430 
UnwindPlan(lldb::RegisterKind reg_kind)431   UnwindPlan(lldb::RegisterKind reg_kind)
432       : m_register_kind(reg_kind), m_return_addr_register(LLDB_INVALID_REGNUM),
433         m_plan_is_sourced_from_compiler(eLazyBoolCalculate),
434         m_plan_is_valid_at_all_instruction_locations(eLazyBoolCalculate),
435         m_plan_is_for_signal_trap(eLazyBoolCalculate) {}
436 
437   // Performs a deep copy of the plan, including all the rows (expensive).
438   UnwindPlan(const UnwindPlan &rhs) = default;
439   UnwindPlan &operator=(const UnwindPlan &rhs) = default;
440 
441   UnwindPlan(UnwindPlan &&rhs) = default;
442   UnwindPlan &operator=(UnwindPlan &&) = default;
443 
444   ~UnwindPlan() = default;
445 
446   void Dump(Stream &s, Thread *thread, lldb::addr_t base_addr) const;
447 
448   void AppendRow(Row row);
449 
450   void InsertRow(Row row, bool replace_existing = false);
451 
452   // Returns a pointer to the best row for the given offset into the function's
453   // instructions. If offset is std::nullopt it indicates that the function
454   // start is unknown - the final row in the UnwindPlan is returned. In
455   // practice, the UnwindPlan for a function with no known start address will be
456   // the architectural default UnwindPlan which will only have one row.
457   const UnwindPlan::Row *
458   GetRowForFunctionOffset(std::optional<int64_t> offset) const;
459 
GetRegisterKind()460   lldb::RegisterKind GetRegisterKind() const { return m_register_kind; }
461 
SetRegisterKind(lldb::RegisterKind kind)462   void SetRegisterKind(lldb::RegisterKind kind) { m_register_kind = kind; }
463 
SetReturnAddressRegister(uint32_t regnum)464   void SetReturnAddressRegister(uint32_t regnum) {
465     m_return_addr_register = regnum;
466   }
467 
GetReturnAddressRegister()468   uint32_t GetReturnAddressRegister() const { return m_return_addr_register; }
469 
GetInitialCFARegister()470   uint32_t GetInitialCFARegister() const {
471     if (m_row_list.empty())
472       return LLDB_INVALID_REGNUM;
473     return m_row_list.front().GetCFAValue().GetRegisterNumber();
474   }
475 
476   // This UnwindPlan may not be valid at every address of the function span.
477   // For instance, a FastUnwindPlan will not be valid at the prologue setup
478   // instructions - only in the body of the function.
SetPlanValidAddressRanges(std::vector<AddressRange> ranges)479   void SetPlanValidAddressRanges(std::vector<AddressRange> ranges) {
480     m_plan_valid_ranges = std::move(ranges);
481   }
482 
483   bool PlanValidAtAddress(Address addr) const;
484 
485   bool IsValidRowIndex(uint32_t idx) const;
486 
487   const UnwindPlan::Row *GetRowAtIndex(uint32_t idx) const;
488 
489   const UnwindPlan::Row *GetLastRow() const;
490 
491   lldb_private::ConstString GetSourceName() const;
492 
493   void SetSourceName(const char *);
494 
495   // Was this UnwindPlan emitted by a compiler?
GetSourcedFromCompiler()496   lldb_private::LazyBool GetSourcedFromCompiler() const {
497     return m_plan_is_sourced_from_compiler;
498   }
499 
500   // Was this UnwindPlan emitted by a compiler?
SetSourcedFromCompiler(lldb_private::LazyBool from_compiler)501   void SetSourcedFromCompiler(lldb_private::LazyBool from_compiler) {
502     m_plan_is_sourced_from_compiler = from_compiler;
503   }
504 
505   // Is this UnwindPlan valid at all instructions?  If not, then it is assumed
506   // valid at call sites, e.g. for exception handling.
GetUnwindPlanValidAtAllInstructions()507   lldb_private::LazyBool GetUnwindPlanValidAtAllInstructions() const {
508     return m_plan_is_valid_at_all_instruction_locations;
509   }
510 
511   // Is this UnwindPlan valid at all instructions?  If not, then it is assumed
512   // valid at call sites, e.g. for exception handling.
SetUnwindPlanValidAtAllInstructions(lldb_private::LazyBool valid_at_all_insn)513   void SetUnwindPlanValidAtAllInstructions(
514       lldb_private::LazyBool valid_at_all_insn) {
515     m_plan_is_valid_at_all_instruction_locations = valid_at_all_insn;
516   }
517 
518   // Is this UnwindPlan for a signal trap frame?  If so, then its saved pc
519   // may have been set manually by the signal dispatch code and therefore
520   // not follow a call to the child frame.
GetUnwindPlanForSignalTrap()521   lldb_private::LazyBool GetUnwindPlanForSignalTrap() const {
522     return m_plan_is_for_signal_trap;
523   }
524 
SetUnwindPlanForSignalTrap(lldb_private::LazyBool is_for_signal_trap)525   void SetUnwindPlanForSignalTrap(lldb_private::LazyBool is_for_signal_trap) {
526     m_plan_is_for_signal_trap = is_for_signal_trap;
527   }
528 
GetRowCount()529   int GetRowCount() const { return m_row_list.size(); }
530 
Clear()531   void Clear() {
532     m_row_list.clear();
533     m_plan_valid_ranges.clear();
534     m_register_kind = lldb::eRegisterKindDWARF;
535     m_source_name.Clear();
536     m_plan_is_sourced_from_compiler = eLazyBoolCalculate;
537     m_plan_is_valid_at_all_instruction_locations = eLazyBoolCalculate;
538     m_plan_is_for_signal_trap = eLazyBoolCalculate;
539   }
540 
541   const RegisterInfo *GetRegisterInfo(Thread *thread, uint32_t reg_num) const;
542 
543 private:
544   std::vector<Row> m_row_list;
545   std::vector<AddressRange> m_plan_valid_ranges;
546   lldb::RegisterKind m_register_kind; // The RegisterKind these register numbers
547                                       // are in terms of - will need to be
548   // translated to lldb native reg nums at unwind time
549   uint32_t m_return_addr_register; // The register that has the return address
550                                    // for the caller frame
551                                    // e.g. the lr on arm
552   lldb_private::ConstString
553       m_source_name; // for logging, where this UnwindPlan originated from
554   lldb_private::LazyBool m_plan_is_sourced_from_compiler;
555   lldb_private::LazyBool m_plan_is_valid_at_all_instruction_locations;
556   lldb_private::LazyBool m_plan_is_for_signal_trap;
557 };                                 // class UnwindPlan
558 
559 } // namespace lldb_private
560 
561 #endif // LLDB_SYMBOL_UNWINDPLAN_H
562