• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "dwarf_info.h"
18 
19 #include <cinttypes>
20 #include <queue>
21 
22 #include "berberis/base/stringprintf.h"
23 
24 #include "dwarf_constants.h"
25 
26 namespace nogrod {
27 
28 namespace {
29 
30 using berberis::StringPrintf;
31 
32 class DwarfParser {
33  public:
DwarfParser(const uint8_t * abbrev,size_t abbrev_size,const uint8_t * info,size_t info_size,StringTable debug_str_table,std::optional<StringOffsetTable> string_offset_table)34   DwarfParser(const uint8_t* abbrev,
35               size_t abbrev_size,
36               const uint8_t* info,
37               size_t info_size,
38               StringTable debug_str_table,
39               std::optional<StringOffsetTable> string_offset_table)
40       : abbrev_{abbrev},
41         abbrev_size_{abbrev_size},
42         info_{info},
43         info_size_{info_size},
44         debug_str_table_{debug_str_table},
45         string_offset_table_{std::move(string_offset_table)} {}
46 
ReadDwarfInfo(std::vector<std::unique_ptr<DwarfCompilationUnit>> * compilation_units,std::unordered_map<uint64_t,std::unique_ptr<DwarfDie>> * die_map,std::string * error_msg)47   [[nodiscard]] bool ReadDwarfInfo(
48       std::vector<std::unique_ptr<DwarfCompilationUnit>>* compilation_units,
49       std::unordered_map<uint64_t, std::unique_ptr<DwarfDie>>* die_map,
50       std::string* error_msg) {
51     ByteInputStream bs(info_, info_size_);
52     DwarfContext context(&bs, &debug_str_table_, string_offset_table_);
53 
54     while (bs.available()) {
55       std::unique_ptr<DwarfCompilationUnit> cu = ReadCompilationUnit(&context, die_map, error_msg);
56       if (!cu) {
57         return false;
58       }
59       compilation_units->push_back(std::move(cu));
60     }
61 
62     return true;
63   }
64 
65  private:
ReadAttribute(const DwarfCompilationUnitHeader * cu,const DwarfAbbrevAttribute * abbrev_attr,DwarfContext * context,std::string * error_msg)66   [[nodiscard]] static std::unique_ptr<DwarfAttribute> ReadAttribute(
67       const DwarfCompilationUnitHeader* cu,
68       const DwarfAbbrevAttribute* abbrev_attr,
69       DwarfContext* context,
70       std::string* error_msg) {
71     const DwarfClass* attribute_class = abbrev_attr->dwarf_class();
72     return attribute_class->ReadAttribute(cu, abbrev_attr, context, error_msg);
73   }
74 
ReadOneDie(DwarfContext * context,const DwarfDie * parent_die,const DwarfCompilationUnitHeader * cu,const std::unordered_map<uint64_t,DwarfAbbrev> * abbrev_map,std::unordered_map<uint64_t,std::unique_ptr<DwarfDie>> * die_map,std::string * error_msg)75   [[nodiscard]] static const DwarfDie* ReadOneDie(
76       DwarfContext* context,
77       const DwarfDie* parent_die,
78       const DwarfCompilationUnitHeader* cu,
79       const std::unordered_map<uint64_t, DwarfAbbrev>* abbrev_map,
80       std::unordered_map<uint64_t, std::unique_ptr<DwarfDie>>* die_map,
81       std::string* error_msg) {
82     ByteInputStream* bs = context->info_stream();
83 
84     uint64_t offset = bs->offset();
85     uint64_t abbrev_code = bs->ReadLeb128();
86 
87     if (abbrev_code == 0) {
88       // null-die
89       std::unique_ptr<DwarfDie> null_die(new DwarfDie(cu, parent_die, offset, 0));
90       const DwarfDie* result = null_die.get();
91       (*die_map)[offset] = std::move(null_die);
92       return result;
93     }
94 
95     auto it = abbrev_map->find(abbrev_code);
96     if (it == abbrev_map->end()) {
97       *error_msg = StringPrintf("<%" PRIx64 "> Abbrev code %" PRId64
98                                 " was not found in .debug_abbrev "
99                                 "with offset %" PRIx64,
100                                 bs->offset(),
101                                 abbrev_code,
102                                 cu->abbrev_offset());
103       return nullptr;
104     }
105 
106     auto& abbrev = it->second;
107 
108     std::unique_ptr<DwarfDie> die(new DwarfDie(cu, parent_die, offset, abbrev.tag()));
109 
110     for (auto& abbrev_attr : abbrev.attributes()) {
111       std::unique_ptr<DwarfAttribute> attribute =
112           ReadAttribute(cu, abbrev_attr.get(), context, error_msg);
113       if (!attribute) {
114         return nullptr;
115       }
116 
117       if (attribute->name() == DW_AT_str_offsets_base) {
118         if (abbrev.tag() != DW_TAG_compile_unit) {
119           *error_msg = StringPrintf(
120               "<%" PRIx64
121               "> DW_AT_str_offsets_base is only supported for DW_TAG_compile_unit abbrev.",
122               bs->offset());
123           return nullptr;
124         }
125 
126         context->SetStrOffsetsBase(attribute->Uint64Value().value());
127       }
128 
129       die->AddAttribute(attribute.release());
130     }
131 
132     die->ResolveAttributes(context);
133 
134     if (abbrev.has_children()) {
135       while (true) {
136         const DwarfDie* child_die =
137             ReadOneDie(context, die.get(), cu, abbrev_map, die_map, error_msg);
138         if (!child_die) {
139           return nullptr;
140         }
141 
142         if (child_die->tag() == 0) {
143           break;
144         }
145 
146         die->AddChild(child_die);
147       }
148     }
149 
150     const DwarfDie* result = die.get();
151 
152     (*die_map)[offset] = std::move(die);
153 
154     return result;
155   }
156 
ReadCompilationUnit(DwarfContext * context,std::unordered_map<uint64_t,std::unique_ptr<DwarfDie>> * die_map,std::string * error_msg)157   [[nodiscard]] std::unique_ptr<DwarfCompilationUnit> ReadCompilationUnit(
158       DwarfContext* context,
159       std::unordered_map<uint64_t, std::unique_ptr<DwarfDie>>* die_map,
160       std::string* error_msg) {
161     ByteInputStream* bs = context->info_stream();
162 
163     uint64_t offset = bs->offset();
164 
165     uint64_t unit_length = bs->ReadUint32();
166     bool is_dwarf64 = false;
167     if (unit_length == 0xFFFFFFFF) {
168       unit_length = bs->ReadUint64();
169       is_dwarf64 = true;
170     }
171 
172     uint16_t version = bs->ReadUint16();
173     uint64_t abbrev_offset;
174     uint8_t address_size;
175 
176     if (version >= 2 && version <= 4) {
177       abbrev_offset = is_dwarf64 ? bs->ReadUint64() : bs->ReadUint32();
178       address_size = bs->ReadUint8();
179     } else if (version == 5) {
180       uint8_t unit_type = bs->ReadUint8();
181       // TODO(dimitry): can a .so file have DW_UT_partial CUs?
182       if (unit_type != DW_UT_compile) {
183         *error_msg =
184             StringPrintf("Unsupported DWARF5 compilation unit type encoding: %x", unit_type);
185         return nullptr;
186       }
187 
188       address_size = bs->ReadUint8();
189       abbrev_offset = is_dwarf64 ? bs->ReadUint64() : bs->ReadUint32();
190     } else {
191       *error_msg =
192           StringPrintf("Unsupported dwarf version: %d, CU offset: 0x%" PRIx64, version, offset);
193       return nullptr;
194     }
195 
196     std::unique_ptr<DwarfCompilationUnit> cu(new DwarfCompilationUnit(
197         offset, unit_length, version, abbrev_offset, address_size, is_dwarf64));
198 
199     // Even though in .so files abbrev codes is a sequence [1..n]
200     // the spec does not specify this as a requirement. Therefore
201     // it is safer to use unordered_map.
202     std::unordered_map<uint64_t, DwarfAbbrev>* abbrev_map =
203         ReadAbbrev(version, abbrev_offset, error_msg);
204 
205     if (abbrev_map == nullptr) {
206       *error_msg =
207           StringPrintf("error reading abbrev for compilation unit at offset 0x%" PRIx64 ": %s",
208                        offset,
209                        error_msg->c_str());
210       return nullptr;
211     }
212 
213     // We expect this attribute to be set if needed in the DW_TAG_compile_unit die.
214     context->ResetStrOffsetsBase();
215 
216     // CU consists of one DIE (DW_TAG_compile_unit) - read it
217     const DwarfDie* cu_die =
218         ReadOneDie(context, nullptr, &cu->header(), abbrev_map, die_map, error_msg);
219 
220     if (!cu_die) {
221       return nullptr;
222     }
223 
224     if (cu_die->tag() != DW_TAG_compile_unit) {
225       *error_msg = StringPrintf(
226           "Unexpected DIE tag for Compilation Unit: %d, expected DW_TAG_compile_unit(%d)",
227           cu_die->tag(),
228           DW_TAG_compile_unit);
229       return nullptr;
230     }
231 
232     cu->SetDie(cu_die);
233 
234     return cu;
235   }
236 
ReadAbbrev(uint16_t version,uint64_t offset,std::string * error_msg)237   std::unordered_map<uint64_t, DwarfAbbrev>* ReadAbbrev(uint16_t version,
238                                                         uint64_t offset,
239                                                         std::string* error_msg) {
240     auto it = abbrevs_.find(offset);
241     if (it != abbrevs_.end()) {
242       return &it->second;
243     }
244 
245     if (offset >= abbrev_size_) {
246       *error_msg = StringPrintf(
247           "abbrev offset (%" PRId64 ") is out of bounds: %" PRId64, offset, abbrev_size_);
248       return nullptr;
249     }
250 
251     std::unordered_map<uint64_t, DwarfAbbrev> abbrev_map;
252     ByteInputStream bs(abbrev_ + offset, abbrev_size_ - offset);
253     while (true) {
254       uint64_t code = bs.ReadLeb128();
255 
256       // The abbreviations for a given compilation unit end with an entry consisting of a 0 byte
257       // for the abbreviation code.
258       if (code == 0) {
259         break;
260       }
261 
262       uint64_t entry_tag = bs.ReadLeb128();
263       uint8_t has_children = bs.ReadUint8();
264 
265       DwarfAbbrev abbrev(code, entry_tag, has_children == DW_CHILDREN_yes);
266 
267       while (true) {
268         uint64_t attr_offset = offset + bs.offset();
269         uint64_t attr_name = bs.ReadLeb128();
270         uint64_t attr_form = bs.ReadLeb128();
271         int64_t value = 0;
272         // The series of attribute specifications ends with an entry containing 0 for the name
273         // and 0 for the form.
274         if (attr_name == 0 && attr_form == 0) {
275           break;
276         }
277 
278         // "The attribute form DW_FORM_implicit_const is another special case. For
279         // attributes with this form, the attribute specification contains a third part, which is
280         // a signed LEB128 number."
281 
282         if (attr_form == DW_FORM_implicit_const) {
283           value = bs.ReadSleb128();
284         }
285 
286         std::unique_ptr<const DwarfAbbrevAttribute> abbrev_attribute =
287             DwarfAbbrevAttribute::CreateAbbrevAttribute(
288                 version, attr_name, attr_form, value, error_msg);
289 
290         if (!abbrev_attribute) {
291           *error_msg =
292               StringPrintf("error getting attribute at debug_abbrev offset 0x%" PRIx64 ": %s",
293                            attr_offset,
294                            error_msg->c_str());
295           return nullptr;
296         }
297         abbrev.AddAttribute(std::move(abbrev_attribute));
298       }
299 
300       abbrev_map[code] = std::move(abbrev);
301     }
302 
303     abbrevs_[offset] = std::move(abbrev_map);
304     return &abbrevs_[offset];
305   }
306 
307  private:
308   const uint8_t* abbrev_;
309   uint64_t abbrev_size_;
310   const uint8_t* info_;
311   uint64_t info_size_;
312   StringTable debug_str_table_;
313   std::optional<StringOffsetTable> string_offset_table_;
314 
315   std::unordered_map<uint64_t, std::unordered_map<uint64_t, DwarfAbbrev>> abbrevs_;
316 };
317 
318 }  // namespace
319 
DwarfCompilationUnit(uint64_t unit_offset,uint64_t unit_length,uint16_t version,uint64_t abbrev_offset,uint8_t address_size,bool is_dwarf64)320 DwarfCompilationUnit::DwarfCompilationUnit(uint64_t unit_offset,
321                                            uint64_t unit_length,
322                                            uint16_t version,
323                                            uint64_t abbrev_offset,
324                                            uint8_t address_size,
325                                            bool is_dwarf64)
326     : header_(unit_offset, unit_length, version, abbrev_offset, address_size, is_dwarf64),
327       cu_die_(nullptr) {}
328 
SetDie(const DwarfDie * die)329 void DwarfCompilationUnit::SetDie(const DwarfDie* die) {
330   cu_die_ = die;
331 }
332 
DwarfInfo(const uint8_t * abbrev,size_t abbrev_size,const uint8_t * info,size_t info_size,StringTable string_table,std::optional<StringOffsetTable> string_offset_table)333 DwarfInfo::DwarfInfo(const uint8_t* abbrev,
334                      size_t abbrev_size,
335                      const uint8_t* info,
336                      size_t info_size,
337                      StringTable string_table,
338                      std::optional<StringOffsetTable> string_offset_table)
339     : abbrev_{abbrev},
340       abbrev_size_{abbrev_size},
341       info_{info},
342       info_size_{info_size},
343       string_table_{string_table},
344       string_offset_table_{std::move(string_offset_table)} {}
345 
Parse(std::string * error_msg)346 bool DwarfInfo::Parse(std::string* error_msg) {
347   DwarfParser parser(abbrev_, abbrev_size_, info_, info_size_, string_table_, string_offset_table_);
348   if (!parser.ReadDwarfInfo(&compilation_units_, &die_offset_map_, error_msg)) {
349     return false;
350   }
351 
352   return true;
353 }
354 
FindDiesByName(const std::string & name) const355 std::vector<const DwarfDie*> DwarfInfo::FindDiesByName(const std::string& name) const {
356   std::vector<const DwarfDie*> result;
357 
358   for (auto& cu : compilation_units_) {
359     const DwarfDie* cu_die = cu->GetDie();
360 
361     // DIE and name prefix
362     std::queue<std::pair<const DwarfDie*, std::string>> visit_queue;
363     visit_queue.push(make_pair(cu_die, std::string("")));
364     while (!visit_queue.empty()) {
365       auto current = visit_queue.front();
366       visit_queue.pop();  // why doesn't pop() return the value on the front again?
367       auto current_die = current.first;
368       auto current_prefix = current.second;
369 
370       for (const DwarfDie* child : current_die->children()) {
371         // TODO(random-googler): Can we rely on DW_AT_linkage_name being present for all members?
372         // It looks like if member is not a function (DW_TAG_member) it lacks
373         // DW_AT_linkage_name. There is non-zero chance that this is going to
374         // need a C++ mangler in order to resolve all the names.
375         if (child->tag() == DW_TAG_class_type || child->tag() == DW_TAG_structure_type ||
376             child->tag() == DW_TAG_namespace) {
377           auto die_name = child->GetStringAttribute(DW_AT_name);
378           if (!die_name) {
379             // do not search anonymous dies
380             continue;
381           }
382           visit_queue.push(make_pair(child, current_prefix + die_name.value() + "::"));
383         }
384 
385         auto die_name = child->GetStringAttribute(DW_AT_linkage_name);
386 
387         if (!die_name) {
388           die_name = child->GetStringAttribute(DW_AT_name);
389           if (die_name) {
390             die_name = make_optional(current_prefix + die_name.value());
391           }
392         }
393 
394         if (die_name && die_name.value() == name) {
395           result.push_back(child);
396         }
397       }
398     }
399   }
400 
401   return result;
402 }
403 
GetDieByOffset(uint64_t offset) const404 const DwarfDie* DwarfInfo::GetDieByOffset(uint64_t offset) const {
405   auto it = die_offset_map_.find(offset);
406   if (it == die_offset_map_.end()) {
407     return nullptr;
408   }
409 
410   return it->second.get();
411 }
412 
DwarfDie(const DwarfCompilationUnitHeader * cu,const DwarfDie * parent,uint64_t offset,uint16_t tag)413 DwarfDie::DwarfDie(const DwarfCompilationUnitHeader* cu,
414                    const DwarfDie* parent,
415                    uint64_t offset,
416                    uint16_t tag)
417     : compilation_unit_header_(cu), parent_(parent), offset_(offset), tag_(tag) {}
418 
AddAttribute(DwarfAttribute * attr)419 void DwarfDie::AddAttribute(DwarfAttribute* attr) {
420   attributes_.push_back(std::unique_ptr<DwarfAttribute>(attr));
421 }
422 
AddChild(const DwarfDie * child)423 void DwarfDie::AddChild(const DwarfDie* child) {
424   children_.push_back(child);
425 }
426 
GetStringAttribute(uint16_t attr_name) const427 std::optional<std::string> DwarfDie::GetStringAttribute(uint16_t attr_name) const {
428   for (auto& attr : attributes_) {
429     if (attr->name() == attr_name) {
430       std::optional<std::string> result = attr->StringValue();
431       CHECK(result.has_value());
432       return result;
433     }
434   }
435   return {};
436 }
437 
GetUint64Attribute(uint16_t attr_name) const438 std::optional<uint64_t> DwarfDie::GetUint64Attribute(uint16_t attr_name) const {
439   for (auto& attr : attributes_) {
440     if (attr->name() == attr_name) {
441       std::optional<uint64_t> result = attr->Uint64Value();
442       CHECK(result.has_value());
443       return result;
444     }
445   }
446   return {};
447 }
448 
GetBoolAttributeOr(uint16_t attr_name,bool default_value) const449 bool DwarfDie::GetBoolAttributeOr(uint16_t attr_name, bool default_value) const {
450   for (auto& attr : attributes_) {
451     if (attr->name() == attr_name) {
452       std::optional<bool> result = attr->BoolValue();
453       CHECK(result.has_value());
454       return result.value();
455     }
456   }
457 
458   return default_value;
459 }
460 
ResolveAttributes(DwarfContext * context)461 void DwarfDie::ResolveAttributes(DwarfContext* context) {
462   for (auto& attr : attributes_) {
463     attr->Resolve(context);
464   }
465 }
466 
467 }  // namespace nogrod
468