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