• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include <algorithm>
17 #include <sys/types.h>
18 #include <sys/stat.h>
19 #include <fcntl.h>
20 #include <unistd.h>
21 #include <libdwarf/dwarf.h>
22 #include "debug_info.h"
23 #include "utils/logger.h"
24 
25 namespace panda {
26 
27 class DwarfGuard {
28 public:
DwarfGuard(Dwarf_Debug dbg,void * mem,Dwarf_Unsigned tag)29     DwarfGuard(Dwarf_Debug dbg, void *mem, Dwarf_Unsigned tag) : dbg_(dbg), mem_(mem), tag_(tag) {}
30 
Release()31     void Release()
32     {
33         mem_ = nullptr;
34     }
35 
Reset(void * new_mem)36     void Reset(void *new_mem)
37     {
38         if (mem_ != new_mem && mem_ != nullptr) {
39             dwarf_dealloc(dbg_, mem_, tag_);
40             mem_ = new_mem;
41         }
42     }
43 
~DwarfGuard()44     ~DwarfGuard()
45     {
46         Reset(nullptr);
47     }
48 
49     NO_MOVE_SEMANTIC(DwarfGuard);
50     NO_COPY_SEMANTIC(DwarfGuard);
51 
52 private:
53     Dwarf_Debug dbg_;
54     void *mem_;
55     Dwarf_Unsigned tag_;
56 };
57 
58 template <class F>
59 class AtReturn {
60 public:
AtReturn(F func)61     explicit AtReturn(F func) : func_(func) {}
62 
~AtReturn()63     ~AtReturn()
64     {
65         func_();
66     }
67 
68     NO_MOVE_SEMANTIC(AtReturn);
69     NO_COPY_SEMANTIC(AtReturn);
70 
71 private:
72     F func_;
73 };
74 
FreeAranges(Dwarf_Debug dbg,Dwarf_Arange * aranges,Dwarf_Signed count)75 static void FreeAranges(Dwarf_Debug dbg, Dwarf_Arange *aranges, Dwarf_Signed count)
76 {
77     for (Dwarf_Signed i = 0; i < count; ++i) {
78         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
79         dwarf_dealloc(dbg, aranges[i], DW_DLA_ARANGE);
80     }
81     dwarf_dealloc(dbg, aranges, DW_DLA_LIST);
82 }
83 
SkipCuHeaders(Dwarf_Debug dbg)84 static void SkipCuHeaders(Dwarf_Debug dbg)
85 {
86     Dwarf_Unsigned cu_header_idx;
87     Dwarf_Half cu_type;
88     while (dwarf_next_cu_header_d(dbg, static_cast<Dwarf_Bool>(true), nullptr, nullptr, nullptr, nullptr, nullptr,
89                                   nullptr, nullptr, nullptr, &cu_header_idx, &cu_type, nullptr) != DW_DLV_NO_ENTRY) {
90     }
91 }
92 
DwarfErrorHandler(Dwarf_Error err,Dwarf_Ptr errarg)93 static void DwarfErrorHandler(Dwarf_Error err, [[maybe_unused]] Dwarf_Ptr errarg)
94 {
95     LOG(ERROR, RUNTIME) << "libdwarf error: " << dwarf_errmsg(err);
96 }
97 
GetDieRange(Dwarf_Die die,Dwarf_Addr * out_low_pc,Dwarf_Addr * out_high_pc)98 static bool GetDieRange(Dwarf_Die die, Dwarf_Addr *out_low_pc, Dwarf_Addr *out_high_pc)
99 {
100     Dwarf_Addr low_pc = DW_DLV_BADADDR;
101     Dwarf_Addr high_pc = 0;
102     Dwarf_Half form = 0;
103     Dwarf_Form_Class formclass;
104 
105     if (dwarf_lowpc(die, &low_pc, nullptr) != DW_DLV_OK ||
106         dwarf_highpc_b(die, &high_pc, &form, &formclass, nullptr) != DW_DLV_OK) {
107         return false;
108     }
109     if (formclass == DW_FORM_CLASS_CONSTANT) {
110         high_pc += low_pc;
111     }
112     *out_low_pc = low_pc;
113     *out_high_pc = high_pc;
114     return true;
115 }
116 
117 template <class F>
IterateDieRanges(Dwarf_Debug dbg,Dwarf_Die die,F func)118 bool IterateDieRanges(Dwarf_Debug dbg, Dwarf_Die die, F func)
119 {
120     Dwarf_Addr low_pc = DW_DLV_BADADDR;
121     Dwarf_Addr high_pc = DW_DLV_BADADDR;
122 
123     if (GetDieRange(die, &low_pc, &high_pc)) {
124         return func(low_pc, high_pc);
125     }
126 
127     Dwarf_Attribute attr;
128     if (dwarf_attr(die, DW_AT_ranges, &attr, nullptr) != DW_DLV_OK) {
129         return false;
130     }
131     DwarfGuard g(dbg, attr, DW_DLA_ATTR);
132     Dwarf_Unsigned offset = 0;
133     Dwarf_Addr base_addr = 0;
134     if (low_pc != DW_DLV_BADADDR) {
135         base_addr = low_pc;
136     }
137     Dwarf_Signed count = 0;
138     Dwarf_Ranges *buf = nullptr;
139     if (dwarf_global_formref(attr, &offset, nullptr) == DW_DLV_OK &&
140         dwarf_get_ranges_a(dbg, offset, die, &buf, &count, nullptr, nullptr) == DW_DLV_OK) {
141         AtReturn r([dbg, buf, count]() { dwarf_ranges_dealloc(dbg, buf, count); });
142         Span<Dwarf_Ranges> ranges(buf, count);
143         for (const Dwarf_Ranges &range : ranges) {
144             if (range.dwr_type == DW_RANGES_ENTRY) {
145                 Dwarf_Addr rng_low_pc = base_addr + range.dwr_addr1;
146                 Dwarf_Addr rng_high_pc = base_addr + range.dwr_addr2;
147                 if (func(rng_low_pc, rng_high_pc)) {
148                     return true;
149                 }
150             } else if (range.dwr_type == DW_RANGES_ADDRESS_SELECTION) {
151                 base_addr = range.dwr_addr2;
152             } else {
153                 break;
154             }
155         }
156     }
157     return false;
158 }
159 
~CompUnit()160 DebugInfo::CompUnit::~CompUnit()
161 {
162     if (line_ctx_ != nullptr) {
163         dwarf_srclines_dealloc_b(line_ctx_);
164     }
165     if (cu_die_ != nullptr) {
166         dwarf_dealloc(dbg_, cu_die_, DW_DLA_DIE);
167     }
168 }
169 
GetLineContext()170 Dwarf_Line_Context DebugInfo::CompUnit::GetLineContext()
171 {
172     if (line_ctx_ != nullptr) {
173         return line_ctx_;
174     }
175     // Decode line number information for the whole compilation unit
176     Dwarf_Unsigned version = 0;
177     Dwarf_Small table_count = 0;
178     if (dwarf_srclines_b(cu_die_, &version, &table_count, &line_ctx_, nullptr) != DW_DLV_OK) {
179         line_ctx_ = nullptr;
180     }
181     return line_ctx_;
182 }
183 
Destroy()184 void DebugInfo::Destroy()
185 {
186     if (dbg_ == nullptr) {
187         return;
188     }
189     if (aranges_ != nullptr) {
190         FreeAranges(dbg_, aranges_, arange_count_);
191     }
192     aranges_ = nullptr;
193     arange_count_ = 0;
194     cu_list_.clear();
195     ranges_.clear();
196     dwarf_finish(dbg_, nullptr);
197     {
198         os::memory::LockHolder lock(fd_mutex_);
199         close(fd_);
200         fd_ = INVALID_FD;
201     }
202     dbg_ = nullptr;
203 }
204 
ReadFromFile(const char * filename)205 DebugInfo::ErrorCode DebugInfo::ReadFromFile(const char *filename)
206 {
207     Dwarf_Error err = nullptr;
208     // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg, hicpp-signed-bitwise)
209     fd_ = open(filename, O_RDONLY | O_CLOEXEC);
210     int res = dwarf_init(fd_, DW_DLC_READ, DwarfErrorHandler, nullptr, &dbg_, &err);
211     if (res != DW_DLV_OK) {
212         // TODO(audovichenko): Libdwarf has a bug (memory leak).
213         // In case dwarf_init fails it allocates memory for the error and  returns it in 'err' variable.
214         // But since dbg is NULL, dwarf_dealloc just returns in case of dbg == nullptr and doesn't free this memory
215         // A possible solution is to use 20201201 version and call dwarf_dealloc.
216         free(err);  // NOLINT(cppcoreguidelines-no-malloc)
217         {
218             os::memory::LockHolder lock(fd_mutex_);
219             close(fd_);
220             fd_ = INVALID_FD;
221         }
222         dbg_ = nullptr;
223     }
224     if (res == DW_DLV_ERROR) {
225         return ERROR;
226     }
227     if (res == DW_DLV_NO_ENTRY) {
228         return NO_DEBUG_INFO;
229     }
230     // Aranges (address ranges) is an entity which help us to find the compilation unit quickly (something like index)
231     if (dwarf_get_aranges(dbg_, &aranges_, &arange_count_, nullptr) != DW_DLV_OK) {
232         aranges_ = nullptr;
233         arange_count_ = 0;
234     }
235     return SUCCESS;
236 }
237 
GetSrcLocation(uintptr_t pc,std::string * function,std::string * src_file,uint32_t * line)238 bool DebugInfo::GetSrcLocation(uintptr_t pc, std::string *function, std::string *src_file, uint32_t *line)
239 {
240     if (dbg_ == nullptr) {
241         return false;
242     }
243     // Debug information have hierarchical structure.
244     // Each node is represented by DIE (debug information entity).
245     // .debug_info has a list of DIE which corresponds to compilation units (object files).
246     // Mapping pc to function is to find the compilation unit DIE and then find the subprogram DIE.
247     // From the subprogram DIE we get the function name.
248     // Line information is available for compilation unit DIEs. So we decode lines for the whole
249     // compilation unit and find the corresponding line and file which matches the pc.
250     //
251     // You could use objdump --dwarf=info <object file> to view available debug information.
252 
253     Range range(pc, pc);
254     auto it = ranges_.upper_bound(range);
255     if (it == ranges_.end() || !it->Contain(pc)) {
256         Dwarf_Die cu_die = nullptr;
257         if (!FindCompUnitByPc(pc, &cu_die)) {
258             return false;
259         }
260         cu_list_.emplace_back(CompUnit(cu_die, dbg_));
261         auto ranges = &ranges_;
262         auto cu = &cu_list_.back();
263         IterateDieRanges(dbg_, cu_die, [ranges, cu](Dwarf_Addr low_pc, Dwarf_Addr high_pc) {
264             ranges->insert(Range(low_pc, high_pc, cu));
265             return false;
266         });
267         TraverseChildren(cu, cu_die);
268     }
269     it = ranges_.upper_bound(range);
270     if (it == ranges_.end() || !it->Contain(pc)) {
271         return false;
272     }
273 
274     ASSERT(it->GetCu() != nullptr);
275     *function = it->GetFunction();
276     // Find the corresponding line number and source file.
277     GetSrcFileAndLine(pc, it->GetCu()->GetLineContext(), src_file, line);
278     return true;
279 }
280 
FindCompUnitByPc(uintptr_t pc,Dwarf_Die * cu_die)281 bool DebugInfo::FindCompUnitByPc(uintptr_t pc, Dwarf_Die *cu_die)
282 {
283     if (aranges_ != nullptr) {
284         Dwarf_Arange arange = nullptr;
285         Dwarf_Off offset = 0;
286         if (dwarf_get_arange(aranges_, arange_count_, pc, &arange, nullptr) == DW_DLV_OK &&
287             dwarf_get_cu_die_offset(arange, &offset, nullptr) == DW_DLV_OK &&
288             dwarf_offdie(dbg_, offset, cu_die, nullptr) == DW_DLV_OK) {
289             return true;
290         }
291     }
292 
293     // No aranges are available or we can't find the corresponding arange. Iterate over all compilation units.
294     // Its slow but works.
295     Dwarf_Unsigned cu_header_idx;
296     Dwarf_Half cu_type;
297     int res = dwarf_next_cu_header_d(dbg_, static_cast<Dwarf_Bool>(true), nullptr, nullptr, nullptr, nullptr, nullptr,
298                                      nullptr, nullptr, nullptr, &cu_header_idx, &cu_type, nullptr);
299     while (res == DW_DLV_OK) {
300         Dwarf_Die die = nullptr;
301         if (dwarf_siblingof_b(dbg_, nullptr, static_cast<Dwarf_Bool>(true), &die, nullptr) == DW_DLV_OK) {
302             if (PcMatches(pc, die)) {
303                 *cu_die = die;
304                 // Skip the rest cu headers because next time we need to stat search from the beginning.
305                 SkipCuHeaders(dbg_);
306                 return true;
307             }
308             dwarf_dealloc(dbg_, die, DW_DLA_DIE);
309         }
310         res = dwarf_next_cu_header_d(dbg_, static_cast<Dwarf_Bool>(true), nullptr, nullptr, nullptr, nullptr, nullptr,
311                                      nullptr, nullptr, nullptr, &cu_header_idx, &cu_type, nullptr);
312     }
313     return false;
314 }
315 
TraverseChildren(CompUnit * cu,Dwarf_Die die)316 void DebugInfo::TraverseChildren(CompUnit *cu, Dwarf_Die die)
317 {
318     Dwarf_Die child_die = nullptr;
319     if (dwarf_child(die, &child_die, nullptr) != DW_DLV_OK) {
320         return;
321     }
322     TraverseSiblings(cu, child_die);
323 }
324 
TraverseSiblings(CompUnit * cu,Dwarf_Die die)325 void DebugInfo::TraverseSiblings(CompUnit *cu, Dwarf_Die die)
326 {
327     DwarfGuard g(dbg_, die, DW_DLA_DIE);
328     Dwarf_Half tag = 0;
329     int res;
330     do {
331         if (dwarf_tag(die, &tag, nullptr) != DW_DLV_OK) {
332             return;
333         }
334         if ((tag == DW_TAG_subprogram || tag == DW_TAG_inlined_subroutine)) {
335             Dwarf_Addr low_pc = DW_DLV_BADADDR;
336             Dwarf_Addr high_pc = 0;
337 
338             if (GetDieRange(die, &low_pc, &high_pc)) {
339                 std::string fname;
340                 GetFunctionName(die, &fname);
341                 AddFunction(cu, low_pc, high_pc, fname);
342             }
343         }
344         TraverseChildren(cu, die);
345         Dwarf_Die sibling = nullptr;
346         res = dwarf_siblingof_b(dbg_, die, static_cast<Dwarf_Bool>(true), &sibling, nullptr);
347         if (res == DW_DLV_OK) {
348             g.Reset(sibling);
349             die = sibling;
350         }
351     } while (res == DW_DLV_OK);
352 }
353 
AddFunction(CompUnit * cu,Dwarf_Addr low_pc,Dwarf_Addr high_pc,const std::string & function)354 void DebugInfo::AddFunction(CompUnit *cu, Dwarf_Addr low_pc, Dwarf_Addr high_pc, const std::string &function)
355 {
356     auto it = ranges_.upper_bound(Range(low_pc, low_pc));
357     ASSERT(it != ranges_.end());
358     Range range(low_pc, high_pc, cu, function);
359     if (it->Contain(range)) {
360         Range enclosing = *it;
361         ranges_.erase(it);
362         if (enclosing.GetLowPc() < low_pc) {
363             ranges_.insert(Range(enclosing.GetLowPc(), low_pc, enclosing.GetCu(), enclosing.GetFunction()));
364         }
365         ranges_.insert(range);
366         if (high_pc < enclosing.GetHighPc()) {
367             ranges_.insert(Range(high_pc, enclosing.GetHighPc(), enclosing.GetCu(), enclosing.GetFunction()));
368         }
369     } else if (range.Contain(*it)) {
370         ranges_.insert(Range(range.GetLowPc(), it->GetLowPc(), cu, function));
371         ranges_.insert(Range(it->GetHighPc(), range.GetHighPc(), cu, function));
372     } else if (high_pc <= it->GetLowPc()) {
373         ranges_.insert(range);
374     }
375 }
376 
GetFunctionName(Dwarf_Die die,std::string * function)377 void DebugInfo::GetFunctionName(Dwarf_Die die, std::string *function)
378 {
379     char *name = nullptr;
380 
381     // Prefer linkage name instead of name
382     // Linkage name is a mangled name which contains information about enclosing class,
383     // return type, parameters and so on.
384     // The name which is stored in DW_AT_name attribute is only a function name.
385     if (dwarf_die_text(die, DW_AT_linkage_name, &name, nullptr) == DW_DLV_OK ||
386         dwarf_diename(die, &name, nullptr) == DW_DLV_OK) {
387         DwarfGuard g(dbg_, name, DW_DLA_STRING);
388         *function = name;
389         return;
390     }
391 
392     Dwarf_Off off = 0;
393     Dwarf_Attribute attr = nullptr;
394     Dwarf_Die abs_orig_die = nullptr;
395     // If there is no name | linkage_name the function may be inlined.
396     // Try to get it from the abstract origin
397     if (dwarf_attr(die, DW_AT_abstract_origin, &attr, nullptr) == DW_DLV_OK) {
398         DwarfGuard ag(dbg_, attr, DW_DLA_ATTR);
399         if (dwarf_global_formref(attr, &off, nullptr) == DW_DLV_OK &&
400             dwarf_offdie(dbg_, off, &abs_orig_die, nullptr) == DW_DLV_OK) {
401             DwarfGuard dg(dbg_, abs_orig_die, DW_DLA_DIE);
402             GetFunctionName(abs_orig_die, function);
403             return;
404         }
405     }
406 
407     // If there is no name | linkage_name try to get it from the specification.
408     Dwarf_Die spec_die = nullptr;
409     if (dwarf_attr(die, DW_AT_specification, &attr, nullptr) == DW_DLV_OK) {
410         DwarfGuard ag(dbg_, attr, DW_DLA_ATTR);
411         if (dwarf_global_formref(attr, &off, nullptr) == DW_DLV_OK &&
412             dwarf_offdie(dbg_, off, &spec_die, nullptr) == DW_DLV_OK) {
413             DwarfGuard dg(dbg_, spec_die, DW_DLA_DIE);
414             GetFunctionName(spec_die, function);
415         }
416     }
417 }
418 
GetSrcFileAndLine(uintptr_t pc,Dwarf_Line_Context line_ctx,std::string * out_src_file,uint32_t * out_line)419 bool DebugInfo::GetSrcFileAndLine(uintptr_t pc, Dwarf_Line_Context line_ctx, std::string *out_src_file,
420                                   uint32_t *out_line)
421 {
422     if (line_ctx == nullptr) {
423         return false;
424     }
425     Dwarf_Line *line_buf = nullptr;
426     Dwarf_Signed line_buf_size = 0;
427     if (dwarf_srclines_from_linecontext(line_ctx, &line_buf, &line_buf_size, nullptr) != DW_DLV_OK) {
428         return false;
429     }
430     Span<Dwarf_Line> lines(line_buf, line_buf_size);
431     Dwarf_Addr prev_line_pc = 0;
432     Dwarf_Line prev_line = nullptr;
433     bool found = false;
434     for (auto it = lines.begin(); it != lines.end() && !found; ++it) {
435         Dwarf_Line line = *it;
436         Dwarf_Addr line_pc = 0;
437         dwarf_lineaddr(line, &line_pc, nullptr);
438         if (pc == line_pc) {
439             GetSrcFileAndLine(GetLastLineWithPc(pc, it, lines.end()), out_src_file, out_line);
440             found = true;
441         } else if (prev_line != nullptr && prev_line_pc < pc && pc < line_pc) {
442             GetSrcFileAndLine(prev_line, out_src_file, out_line);
443             found = true;
444         } else {
445             Dwarf_Bool is_line_end;
446             dwarf_lineendsequence(line, &is_line_end, nullptr);
447             if (is_line_end != 0) {
448                 prev_line = nullptr;
449             } else {
450                 prev_line_pc = line_pc;
451                 prev_line = line;
452             }
453         }
454     }
455     return found;
456 }
457 
GetLastLineWithPc(Dwarf_Addr pc,Span<Dwarf_Line>::ConstIterator it,Span<Dwarf_Line>::ConstIterator end)458 Dwarf_Line DebugInfo::GetLastLineWithPc(Dwarf_Addr pc, Span<Dwarf_Line>::ConstIterator it,
459                                         Span<Dwarf_Line>::ConstIterator end)
460 {
461     Dwarf_Addr line_pc = 0;
462     auto next = std::next(it);
463     while (next != end) {
464         dwarf_lineaddr(*next, &line_pc, nullptr);
465         if (pc != line_pc) {
466             return *it;
467         }
468         it = next;
469         ++next;
470     }
471     return *it;
472 }
473 
GetSrcFileAndLine(Dwarf_Line line,std::string * out_src_file,uint32_t * out_line)474 void DebugInfo::GetSrcFileAndLine(Dwarf_Line line, std::string *out_src_file, uint32_t *out_line)
475 {
476     Dwarf_Unsigned ln;
477     dwarf_lineno(line, &ln, nullptr);
478     *out_line = ln;
479     char *src_file = nullptr;
480     if (dwarf_linesrc(line, &src_file, nullptr) == DW_DLV_OK) {
481         *out_src_file = src_file;
482         DwarfGuard g(dbg_, src_file, DW_DLA_STRING);
483     } else {
484         dwarf_linesrc(line, &src_file, nullptr);
485         *out_src_file = src_file;
486         DwarfGuard g(dbg_, src_file, DW_DLA_STRING);
487     }
488 }
489 
PcMatches(uintptr_t pc,Dwarf_Die die)490 bool DebugInfo::PcMatches(uintptr_t pc, Dwarf_Die die)
491 {
492     Dwarf_Addr low_pc = DW_DLV_BADADDR;
493     Dwarf_Addr high_pc = 0;
494     return GetDieRangeForPc(pc, die, &low_pc, &high_pc);
495 }
496 
GetDieRange(Dwarf_Die die,Dwarf_Addr * out_low_pc,Dwarf_Addr * out_high_pc)497 bool DebugInfo::GetDieRange(Dwarf_Die die, Dwarf_Addr *out_low_pc, Dwarf_Addr *out_high_pc)
498 {
499     Dwarf_Addr low_pc = DW_DLV_BADADDR;
500     Dwarf_Addr high_pc = 0;
501     Dwarf_Half form = 0;
502     Dwarf_Form_Class formclass;
503 
504     if (dwarf_lowpc(die, &low_pc, nullptr) != DW_DLV_OK ||
505         dwarf_highpc_b(die, &high_pc, &form, &formclass, nullptr) != DW_DLV_OK) {
506         return false;
507     }
508     if (formclass == DW_FORM_CLASS_CONSTANT) {
509         high_pc += low_pc;
510     }
511     *out_low_pc = low_pc;
512     *out_high_pc = high_pc;
513     return true;
514 }
515 
GetDieRangeForPc(uintptr_t pc,Dwarf_Die die,Dwarf_Addr * out_low_pc,Dwarf_Addr * out_high_pc)516 bool DebugInfo::GetDieRangeForPc(uintptr_t pc, Dwarf_Die die, Dwarf_Addr *out_low_pc, Dwarf_Addr *out_high_pc)
517 {
518     Dwarf_Addr low_pc = DW_DLV_BADADDR;
519     Dwarf_Addr high_pc = 0;
520 
521     if (GetDieRange(die, &low_pc, &high_pc) && (*out_low_pc <= pc && pc < *out_high_pc)) {
522         *out_low_pc = low_pc;
523         *out_high_pc = high_pc;
524         return true;
525     }
526 
527     Dwarf_Attribute attr;
528     if (dwarf_attr(die, DW_AT_ranges, &attr, nullptr) == DW_DLV_OK) {
529         DwarfGuard g(dbg_, attr, DW_DLA_ATTR);
530         Dwarf_Unsigned offset;
531         Dwarf_Addr base_addr = 0;
532         if (low_pc != DW_DLV_BADADDR) {
533             base_addr = low_pc;
534         }
535         Dwarf_Signed count = 0;
536         Dwarf_Ranges *ranges = nullptr;
537         if (dwarf_global_formref(attr, &offset, nullptr) == DW_DLV_OK &&
538             dwarf_get_ranges_a(dbg_, offset, die, &ranges, &count, nullptr, nullptr) == DW_DLV_OK) {
539             Dwarf_Debug dbg = dbg_;
540             AtReturn r([dbg, ranges, count]() { dwarf_ranges_dealloc(dbg, ranges, count); });
541             return FindRangeForPc(pc, Span<Dwarf_Ranges>(ranges, count), base_addr, out_low_pc, out_high_pc);
542         }
543     }
544     return false;
545 }
546 
FindRangeForPc(uintptr_t pc,const Span<Dwarf_Ranges> & ranges,Dwarf_Addr base_addr,Dwarf_Addr * out_low_pc,Dwarf_Addr * out_high_pc)547 bool DebugInfo::FindRangeForPc(uintptr_t pc, const Span<Dwarf_Ranges> &ranges, Dwarf_Addr base_addr,
548                                Dwarf_Addr *out_low_pc, Dwarf_Addr *out_high_pc)
549 {
550     for (const Dwarf_Ranges &range : ranges) {
551         if (range.dwr_type == DW_RANGES_ENTRY) {
552             Dwarf_Addr rng_low_pc = base_addr + range.dwr_addr1;
553             Dwarf_Addr rng_high_pc = base_addr + range.dwr_addr2;
554             if (rng_low_pc <= pc && pc < rng_high_pc) {
555                 *out_low_pc = rng_low_pc;
556                 *out_high_pc = rng_high_pc;
557                 return true;
558             }
559         } else if (range.dwr_type == DW_RANGES_ADDRESS_SELECTION) {
560             base_addr = range.dwr_addr2;
561         } else {
562             break;
563         }
564     }
565     return false;
566 }
567 
568 }  // namespace panda
569