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