xref: /freebsd/contrib/llvm-project/lldb/source/Symbol/UnwindPlan.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
1 //===-- UnwindPlan.cpp ----------------------------------------------------===//
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 #include "lldb/Symbol/UnwindPlan.h"
10 
11 #include "lldb/Target/Process.h"
12 #include "lldb/Target/RegisterContext.h"
13 #include "lldb/Target/Target.h"
14 #include "lldb/Target/Thread.h"
15 #include "lldb/Utility/ConstString.h"
16 #include "lldb/Utility/LLDBLog.h"
17 #include "lldb/Utility/Log.h"
18 #include "llvm/ADT/STLExtras.h"
19 #include "llvm/DebugInfo/DIContext.h"
20 #include "llvm/DebugInfo/DWARF/DWARFExpressionPrinter.h"
21 #include "llvm/DebugInfo/DWARF/LowLevel/DWARFExpression.h"
22 #include <optional>
23 
24 using namespace lldb;
25 using namespace lldb_private;
26 
operator ==(const UnwindPlan::Row::AbstractRegisterLocation & rhs) const27 bool UnwindPlan::Row::AbstractRegisterLocation::operator==(
28     const UnwindPlan::Row::AbstractRegisterLocation &rhs) const {
29   if (m_type == rhs.m_type) {
30     switch (m_type) {
31     case unspecified:
32     case undefined:
33     case same:
34       return true;
35 
36     case atCFAPlusOffset:
37     case isCFAPlusOffset:
38     case atAFAPlusOffset:
39     case isAFAPlusOffset:
40       return m_location.offset == rhs.m_location.offset;
41 
42     case inOtherRegister:
43       return m_location.reg_num == rhs.m_location.reg_num;
44 
45     case atDWARFExpression:
46     case isDWARFExpression:
47       if (m_location.expr.length == rhs.m_location.expr.length)
48         return !memcmp(m_location.expr.opcodes, rhs.m_location.expr.opcodes,
49                        m_location.expr.length);
50       break;
51     case isConstant:
52       return m_location.constant_value == rhs.m_location.constant_value;
53     }
54   }
55   return false;
56 }
57 
58 // This function doesn't copy the dwarf expression bytes; they must remain in
59 // allocated memory for the lifespan of this UnwindPlan object.
SetAtDWARFExpression(const uint8_t * opcodes,uint32_t len)60 void UnwindPlan::Row::AbstractRegisterLocation::SetAtDWARFExpression(
61     const uint8_t *opcodes, uint32_t len) {
62   m_type = atDWARFExpression;
63   m_location.expr.opcodes = opcodes;
64   m_location.expr.length = len;
65 }
66 
67 // This function doesn't copy the dwarf expression bytes; they must remain in
68 // allocated memory for the lifespan of this UnwindPlan object.
SetIsDWARFExpression(const uint8_t * opcodes,uint32_t len)69 void UnwindPlan::Row::AbstractRegisterLocation::SetIsDWARFExpression(
70     const uint8_t *opcodes, uint32_t len) {
71   m_type = isDWARFExpression;
72   m_location.expr.opcodes = opcodes;
73   m_location.expr.length = len;
74 }
75 
76 static std::optional<std::pair<lldb::ByteOrder, uint32_t>>
GetByteOrderAndAddrSize(Thread * thread)77 GetByteOrderAndAddrSize(Thread *thread) {
78   if (!thread)
79     return std::nullopt;
80   ProcessSP process_sp = thread->GetProcess();
81   if (!process_sp)
82     return std::nullopt;
83   ArchSpec arch = process_sp->GetTarget().GetArchitecture();
84   return std::make_pair(arch.GetByteOrder(), arch.GetAddressByteSize());
85 }
86 
DumpDWARFExpr(Stream & s,llvm::ArrayRef<uint8_t> expr,Thread * thread)87 static void DumpDWARFExpr(Stream &s, llvm::ArrayRef<uint8_t> expr, Thread *thread) {
88   if (auto order_and_width = GetByteOrderAndAddrSize(thread)) {
89     llvm::DataExtractor data(expr, order_and_width->first == eByteOrderLittle,
90                              order_and_width->second);
91     llvm::DWARFExpression E(data, order_and_width->second,
92                             llvm::dwarf::DWARF32);
93     printDwarfExpression(&E, s.AsRawOstream(), llvm::DIDumpOptions(), nullptr);
94   } else
95     s.PutCString("dwarf-expr");
96 }
97 
Dump(Stream & s,const UnwindPlan * unwind_plan,const UnwindPlan::Row * row,Thread * thread,bool verbose) const98 void UnwindPlan::Row::AbstractRegisterLocation::Dump(
99     Stream &s, const UnwindPlan *unwind_plan, const UnwindPlan::Row *row,
100     Thread *thread, bool verbose) const {
101   switch (m_type) {
102   case unspecified:
103     if (verbose)
104       s.PutCString("=<unspec>");
105     else
106       s.PutCString("=!");
107     break;
108   case undefined:
109     if (verbose)
110       s.PutCString("=<undef>");
111     else
112       s.PutCString("=?");
113     break;
114   case same:
115     s.PutCString("= <same>");
116     break;
117 
118   case atCFAPlusOffset:
119   case isCFAPlusOffset: {
120     s.PutChar('=');
121     if (m_type == atCFAPlusOffset)
122       s.PutChar('[');
123     s.Printf("CFA%+d", m_location.offset);
124     if (m_type == atCFAPlusOffset)
125       s.PutChar(']');
126   } break;
127 
128   case atAFAPlusOffset:
129   case isAFAPlusOffset: {
130     s.PutChar('=');
131     if (m_type == atAFAPlusOffset)
132       s.PutChar('[');
133     s.Printf("AFA%+d", m_location.offset);
134     if (m_type == atAFAPlusOffset)
135       s.PutChar(']');
136   } break;
137 
138   case inOtherRegister: {
139     const RegisterInfo *other_reg_info = nullptr;
140     if (unwind_plan)
141       other_reg_info = unwind_plan->GetRegisterInfo(thread, m_location.reg_num);
142     if (other_reg_info)
143       s.Printf("=%s", other_reg_info->name);
144     else
145       s.Printf("=reg(%u)", m_location.reg_num);
146   } break;
147 
148   case atDWARFExpression:
149   case isDWARFExpression: {
150     s.PutChar('=');
151     if (m_type == atDWARFExpression)
152       s.PutChar('[');
153     DumpDWARFExpr(
154         s, llvm::ArrayRef(m_location.expr.opcodes, m_location.expr.length),
155         thread);
156     if (m_type == atDWARFExpression)
157       s.PutChar(']');
158   } break;
159   case isConstant:
160     s.Printf("=0x%" PRIx64, m_location.constant_value);
161     break;
162   }
163 }
164 
DumpRegisterName(Stream & s,const UnwindPlan * unwind_plan,Thread * thread,uint32_t reg_num)165 static void DumpRegisterName(Stream &s, const UnwindPlan *unwind_plan,
166                              Thread *thread, uint32_t reg_num) {
167   const RegisterInfo *reg_info = unwind_plan->GetRegisterInfo(thread, reg_num);
168   if (reg_info)
169     s.PutCString(reg_info->name);
170   else
171     s.Printf("reg(%u)", reg_num);
172 }
173 
174 bool UnwindPlan::Row::FAValue::
operator ==(const UnwindPlan::Row::FAValue & rhs) const175 operator==(const UnwindPlan::Row::FAValue &rhs) const {
176   if (m_type == rhs.m_type) {
177     switch (m_type) {
178     case unspecified:
179     case isRaSearch:
180       return m_value.ra_search_offset == rhs.m_value.ra_search_offset;
181 
182     case isRegisterPlusOffset:
183       return m_value.reg.offset == rhs.m_value.reg.offset;
184 
185     case isRegisterDereferenced:
186       return m_value.reg.reg_num == rhs.m_value.reg.reg_num;
187 
188     case isDWARFExpression:
189       if (m_value.expr.length == rhs.m_value.expr.length)
190         return !memcmp(m_value.expr.opcodes, rhs.m_value.expr.opcodes,
191                        m_value.expr.length);
192       break;
193     case isConstant:
194       return m_value.constant == rhs.m_value.constant;
195     }
196   }
197   return false;
198 }
199 
Dump(Stream & s,const UnwindPlan * unwind_plan,Thread * thread) const200 void UnwindPlan::Row::FAValue::Dump(Stream &s, const UnwindPlan *unwind_plan,
201                                      Thread *thread) const {
202   switch (m_type) {
203   case isRegisterPlusOffset:
204     DumpRegisterName(s, unwind_plan, thread, m_value.reg.reg_num);
205     s.Printf("%+3d", m_value.reg.offset);
206     break;
207   case isRegisterDereferenced:
208     s.PutChar('[');
209     DumpRegisterName(s, unwind_plan, thread, m_value.reg.reg_num);
210     s.PutChar(']');
211     break;
212   case isDWARFExpression:
213     DumpDWARFExpr(s, llvm::ArrayRef(m_value.expr.opcodes, m_value.expr.length),
214                   thread);
215     break;
216   case unspecified:
217     s.PutCString("unspecified");
218     break;
219   case isRaSearch:
220     s.Printf("RaSearch@SP%+d", m_value.ra_search_offset);
221     break;
222   case isConstant:
223     s.Printf("0x%" PRIx64, m_value.constant);
224   }
225 }
226 
Clear()227 void UnwindPlan::Row::Clear() {
228   m_cfa_value.SetUnspecified();
229   m_afa_value.SetUnspecified();
230   m_offset = 0;
231   m_unspecified_registers_are_undefined = false;
232   m_register_locations.clear();
233 }
234 
Dump(Stream & s,const UnwindPlan * unwind_plan,Thread * thread,addr_t base_addr) const235 void UnwindPlan::Row::Dump(Stream &s, const UnwindPlan *unwind_plan,
236                            Thread *thread, addr_t base_addr) const {
237   if (base_addr != LLDB_INVALID_ADDRESS)
238     s.Printf("0x%16.16" PRIx64 ": CFA=", base_addr + GetOffset());
239   else
240     s.Printf("%4" PRId64 ": CFA=", GetOffset());
241 
242   m_cfa_value.Dump(s, unwind_plan, thread);
243 
244   if (!m_afa_value.IsUnspecified()) {
245     s.Printf(" AFA=");
246     m_afa_value.Dump(s, unwind_plan, thread);
247   }
248 
249   s.Printf(" => ");
250   for (collection::const_iterator idx = m_register_locations.begin();
251        idx != m_register_locations.end(); ++idx) {
252     DumpRegisterName(s, unwind_plan, thread, idx->first);
253     const bool verbose = false;
254     idx->second.Dump(s, unwind_plan, this, thread, verbose);
255     s.PutChar(' ');
256   }
257 }
258 
Row()259 UnwindPlan::Row::Row() : m_cfa_value(), m_afa_value(), m_register_locations() {}
260 
GetRegisterInfo(uint32_t reg_num,UnwindPlan::Row::AbstractRegisterLocation & register_location) const261 bool UnwindPlan::Row::GetRegisterInfo(
262     uint32_t reg_num,
263     UnwindPlan::Row::AbstractRegisterLocation &register_location) const {
264   collection::const_iterator pos = m_register_locations.find(reg_num);
265   if (pos != m_register_locations.end()) {
266     register_location = pos->second;
267     return true;
268   }
269   if (m_unspecified_registers_are_undefined) {
270     register_location.SetUndefined();
271     return true;
272   }
273   return false;
274 }
275 
RemoveRegisterInfo(uint32_t reg_num)276 void UnwindPlan::Row::RemoveRegisterInfo(uint32_t reg_num) {
277   collection::const_iterator pos = m_register_locations.find(reg_num);
278   if (pos != m_register_locations.end()) {
279     m_register_locations.erase(pos);
280   }
281 }
282 
SetRegisterInfo(uint32_t reg_num,const UnwindPlan::Row::AbstractRegisterLocation register_location)283 void UnwindPlan::Row::SetRegisterInfo(
284     uint32_t reg_num,
285     const UnwindPlan::Row::AbstractRegisterLocation register_location) {
286   m_register_locations[reg_num] = register_location;
287 }
288 
SetRegisterLocationToAtCFAPlusOffset(uint32_t reg_num,int32_t offset,bool can_replace)289 bool UnwindPlan::Row::SetRegisterLocationToAtCFAPlusOffset(uint32_t reg_num,
290                                                            int32_t offset,
291                                                            bool can_replace) {
292   if (!can_replace &&
293       m_register_locations.find(reg_num) != m_register_locations.end())
294     return false;
295   AbstractRegisterLocation reg_loc;
296   reg_loc.SetAtCFAPlusOffset(offset);
297   m_register_locations[reg_num] = reg_loc;
298   return true;
299 }
300 
SetRegisterLocationToIsCFAPlusOffset(uint32_t reg_num,int32_t offset,bool can_replace)301 bool UnwindPlan::Row::SetRegisterLocationToIsCFAPlusOffset(uint32_t reg_num,
302                                                            int32_t offset,
303                                                            bool can_replace) {
304   if (!can_replace &&
305       m_register_locations.find(reg_num) != m_register_locations.end())
306     return false;
307   AbstractRegisterLocation reg_loc;
308   reg_loc.SetIsCFAPlusOffset(offset);
309   m_register_locations[reg_num] = reg_loc;
310   return true;
311 }
312 
SetRegisterLocationToUndefined(uint32_t reg_num,bool can_replace,bool can_replace_only_if_unspecified)313 bool UnwindPlan::Row::SetRegisterLocationToUndefined(
314     uint32_t reg_num, bool can_replace, bool can_replace_only_if_unspecified) {
315   collection::iterator pos = m_register_locations.find(reg_num);
316   collection::iterator end = m_register_locations.end();
317 
318   if (pos != end) {
319     if (!can_replace)
320       return false;
321     if (can_replace_only_if_unspecified && !pos->second.IsUnspecified())
322       return false;
323   }
324   AbstractRegisterLocation reg_loc;
325   reg_loc.SetUndefined();
326   m_register_locations[reg_num] = reg_loc;
327   return true;
328 }
329 
SetRegisterLocationToUnspecified(uint32_t reg_num,bool can_replace)330 bool UnwindPlan::Row::SetRegisterLocationToUnspecified(uint32_t reg_num,
331                                                        bool can_replace) {
332   if (!can_replace &&
333       m_register_locations.find(reg_num) != m_register_locations.end())
334     return false;
335   AbstractRegisterLocation reg_loc;
336   reg_loc.SetUnspecified();
337   m_register_locations[reg_num] = reg_loc;
338   return true;
339 }
340 
SetRegisterLocationToRegister(uint32_t reg_num,uint32_t other_reg_num,bool can_replace)341 bool UnwindPlan::Row::SetRegisterLocationToRegister(uint32_t reg_num,
342                                                     uint32_t other_reg_num,
343                                                     bool can_replace) {
344   if (!can_replace &&
345       m_register_locations.find(reg_num) != m_register_locations.end())
346     return false;
347   AbstractRegisterLocation reg_loc;
348   reg_loc.SetInRegister(other_reg_num);
349   m_register_locations[reg_num] = reg_loc;
350   return true;
351 }
352 
SetRegisterLocationToSame(uint32_t reg_num,bool must_replace)353 bool UnwindPlan::Row::SetRegisterLocationToSame(uint32_t reg_num,
354                                                 bool must_replace) {
355   if (must_replace &&
356       m_register_locations.find(reg_num) == m_register_locations.end())
357     return false;
358   AbstractRegisterLocation reg_loc;
359   reg_loc.SetSame();
360   m_register_locations[reg_num] = reg_loc;
361   return true;
362 }
363 
SetRegisterLocationToIsDWARFExpression(uint32_t reg_num,const uint8_t * opcodes,uint32_t len,bool can_replace)364 bool UnwindPlan::Row::SetRegisterLocationToIsDWARFExpression(
365     uint32_t reg_num, const uint8_t *opcodes, uint32_t len, bool can_replace) {
366   if (!can_replace &&
367       m_register_locations.find(reg_num) != m_register_locations.end())
368     return false;
369   AbstractRegisterLocation reg_loc;
370   reg_loc.SetIsDWARFExpression(opcodes, len);
371   m_register_locations[reg_num] = reg_loc;
372   return true;
373 }
374 
SetRegisterLocationToIsConstant(uint32_t reg_num,uint64_t constant,bool can_replace)375 bool UnwindPlan::Row::SetRegisterLocationToIsConstant(uint32_t reg_num,
376                                                       uint64_t constant,
377                                                       bool can_replace) {
378   if (!can_replace &&
379       m_register_locations.find(reg_num) != m_register_locations.end())
380     return false;
381   AbstractRegisterLocation reg_loc;
382   reg_loc.SetIsConstant(constant);
383   m_register_locations[reg_num] = reg_loc;
384   return true;
385 }
386 
operator ==(const UnwindPlan::Row & rhs) const387 bool UnwindPlan::Row::operator==(const UnwindPlan::Row &rhs) const {
388   return m_offset == rhs.m_offset && m_cfa_value == rhs.m_cfa_value &&
389          m_afa_value == rhs.m_afa_value &&
390          m_unspecified_registers_are_undefined ==
391              rhs.m_unspecified_registers_are_undefined &&
392          m_register_locations == rhs.m_register_locations;
393 }
394 
AppendRow(Row row)395 void UnwindPlan::AppendRow(Row row) {
396   if (m_row_list.empty() || m_row_list.back().GetOffset() != row.GetOffset())
397     m_row_list.push_back(std::move(row));
398   else
399     m_row_list.back() = std::move(row);
400 }
401 
402 struct RowLess {
operator ()RowLess403   bool operator()(int64_t a, const UnwindPlan::Row &b) const {
404     return a < b.GetOffset();
405   }
operator ()RowLess406   bool operator()(const UnwindPlan::Row &a, int64_t b) const {
407     return a.GetOffset() < b;
408   }
409 };
410 
InsertRow(Row row,bool replace_existing)411 void UnwindPlan::InsertRow(Row row, bool replace_existing) {
412   auto it = llvm::lower_bound(m_row_list, row.GetOffset(), RowLess());
413   if (it == m_row_list.end() || it->GetOffset() > row.GetOffset())
414     m_row_list.insert(it, std::move(row));
415   else {
416     assert(it->GetOffset() == row.GetOffset());
417     if (replace_existing)
418       *it = std::move(row);
419   }
420 }
421 
422 const UnwindPlan::Row *
GetRowForFunctionOffset(std::optional<int64_t> offset) const423 UnwindPlan::GetRowForFunctionOffset(std::optional<int64_t> offset) const {
424   auto it = offset ? llvm::upper_bound(m_row_list, *offset, RowLess())
425                    : m_row_list.end();
426   if (it == m_row_list.begin())
427     return nullptr;
428   // upper_bound returns the row strictly greater than our desired offset, which
429   // means that the row before it is a match.
430   return &*std::prev(it);
431 }
432 
IsValidRowIndex(uint32_t idx) const433 bool UnwindPlan::IsValidRowIndex(uint32_t idx) const {
434   return idx < m_row_list.size();
435 }
436 
GetRowAtIndex(uint32_t idx) const437 const UnwindPlan::Row *UnwindPlan::GetRowAtIndex(uint32_t idx) const {
438   if (idx < m_row_list.size())
439     return &m_row_list[idx];
440   LLDB_LOG(GetLog(LLDBLog::Unwind),
441            "error: UnwindPlan::GetRowAtIndex(idx = {0}) invalid index "
442            "(number rows is {1})",
443            idx, m_row_list.size());
444   return nullptr;
445 }
446 
GetLastRow() const447 const UnwindPlan::Row *UnwindPlan::GetLastRow() const {
448   if (m_row_list.empty()) {
449     LLDB_LOG(GetLog(LLDBLog::Unwind),
450              "UnwindPlan::GetLastRow() when rows are empty");
451     return nullptr;
452   }
453   return &m_row_list.back();
454 }
455 
PlanValidAtAddress(Address addr) const456 bool UnwindPlan::PlanValidAtAddress(Address addr) const {
457   // If this UnwindPlan has no rows, it is an invalid UnwindPlan.
458   if (GetRowCount() == 0) {
459     Log *log = GetLog(LLDBLog::Unwind);
460     if (log) {
461       StreamString s;
462       if (addr.Dump(&s, nullptr, Address::DumpStyleSectionNameOffset)) {
463         LLDB_LOGF(log,
464                   "UnwindPlan is invalid -- no unwind rows for UnwindPlan "
465                   "'%s' at address %s",
466                   m_source_name.GetCString(), s.GetData());
467       } else {
468         LLDB_LOGF(log,
469                   "UnwindPlan is invalid -- no unwind rows for UnwindPlan '%s'",
470                   m_source_name.GetCString());
471       }
472     }
473     return false;
474   }
475 
476   // If the 0th Row of unwind instructions is missing, or if it doesn't provide
477   // a register to use to find the Canonical Frame Address, this is not a valid
478   // UnwindPlan.
479   const Row *row0 = GetRowAtIndex(0);
480   if (!row0 ||
481       row0->GetCFAValue().GetValueType() == Row::FAValue::unspecified) {
482     Log *log = GetLog(LLDBLog::Unwind);
483     if (log) {
484       StreamString s;
485       if (addr.Dump(&s, nullptr, Address::DumpStyleSectionNameOffset)) {
486         LLDB_LOGF(log,
487                   "UnwindPlan is invalid -- no CFA register defined in row 0 "
488                   "for UnwindPlan '%s' at address %s",
489                   m_source_name.GetCString(), s.GetData());
490       } else {
491         LLDB_LOGF(log,
492                   "UnwindPlan is invalid -- no CFA register defined in row 0 "
493                   "for UnwindPlan '%s'",
494                   m_source_name.GetCString());
495       }
496     }
497     return false;
498   }
499 
500   if (m_plan_valid_ranges.empty())
501     return true;
502 
503   if (!addr.IsValid())
504     return true;
505 
506   return llvm::any_of(m_plan_valid_ranges, [&](const AddressRange &range) {
507     return range.ContainsFileAddress(addr);
508   });
509 }
510 
Dump(Stream & s,Thread * thread,lldb::addr_t base_addr) const511 void UnwindPlan::Dump(Stream &s, Thread *thread, lldb::addr_t base_addr) const {
512   if (!m_source_name.IsEmpty()) {
513     s.Printf("This UnwindPlan originally sourced from %s\n",
514              m_source_name.GetCString());
515   }
516   s.Printf("This UnwindPlan is sourced from the compiler: ");
517   switch (m_plan_is_sourced_from_compiler) {
518   case eLazyBoolYes:
519     s.Printf("yes.\n");
520     break;
521   case eLazyBoolNo:
522     s.Printf("no.\n");
523     break;
524   case eLazyBoolCalculate:
525     s.Printf("not specified.\n");
526     break;
527   }
528   s.Printf("This UnwindPlan is valid at all instruction locations: ");
529   switch (m_plan_is_valid_at_all_instruction_locations) {
530   case eLazyBoolYes:
531     s.Printf("yes.\n");
532     break;
533   case eLazyBoolNo:
534     s.Printf("no.\n");
535     break;
536   case eLazyBoolCalculate:
537     s.Printf("not specified.\n");
538     break;
539   }
540   s.Printf("This UnwindPlan is for a trap handler function: ");
541   switch (m_plan_is_for_signal_trap) {
542   case eLazyBoolYes:
543     s.Printf("yes.\n");
544     break;
545   case eLazyBoolNo:
546     s.Printf("no.\n");
547     break;
548   case eLazyBoolCalculate:
549     s.Printf("not specified.\n");
550     break;
551   }
552   if (!m_plan_valid_ranges.empty()) {
553     s.PutCString("Address range of this UnwindPlan: ");
554     TargetSP target_sp(thread->CalculateTarget());
555     for (const AddressRange &range : m_plan_valid_ranges)
556       range.Dump(&s, target_sp.get(), Address::DumpStyleSectionNameOffset);
557     s.EOL();
558   }
559   for (const auto &[index, row] : llvm::enumerate(m_row_list)) {
560     s.Format("row[{0}]: ", index);
561     row.Dump(s, this, thread, base_addr);
562     s << "\n";
563   }
564 }
565 
SetSourceName(const char * source)566 void UnwindPlan::SetSourceName(const char *source) {
567   m_source_name = ConstString(source);
568 }
569 
GetSourceName() const570 ConstString UnwindPlan::GetSourceName() const { return m_source_name; }
571 
GetRegisterInfo(Thread * thread,uint32_t unwind_reg) const572 const RegisterInfo *UnwindPlan::GetRegisterInfo(Thread *thread,
573                                                 uint32_t unwind_reg) const {
574   if (thread) {
575     RegisterContext *reg_ctx = thread->GetRegisterContext().get();
576     if (reg_ctx) {
577       uint32_t reg;
578       if (m_register_kind == eRegisterKindLLDB)
579         reg = unwind_reg;
580       else
581         reg = reg_ctx->ConvertRegisterKindToRegisterNumber(m_register_kind,
582                                                            unwind_reg);
583       if (reg != LLDB_INVALID_REGNUM)
584         return reg_ctx->GetRegisterInfoAtIndex(reg);
585     }
586   }
587   return nullptr;
588 }
589