1// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
2// for details. All rights reserved. Use of this source code is governed by a
3// BSD-style license that can be found in the LICENSE file.
4
5#include "vm/dwarf.h"
6
7#include "vm/code_descriptors.h"
8#include "vm/elf.h"
9#include "vm/image_snapshot.h"
10#include "vm/object_store.h"
11
12namespace dart {
13
14#if defined(DART_PRECOMPILER)
15
16class DwarfPosition {
17 public:
18 // The DWARF standard uses 0 to denote missing line or column information.
19 DwarfPosition(intptr_t line, intptr_t column)
20 : line_(line > 0 ? line : 0), column_(column > 0 ? column : 0) {
21 // Should only have no line information if also no column information.
22 ASSERT(line_ > 0 || column_ == 0);
23 }
24 explicit DwarfPosition(intptr_t line) : DwarfPosition(line, 0) {}
25 constexpr DwarfPosition() : line_(0), column_(0) {}
26
27 intptr_t line() const { return line_; }
28 intptr_t column() const { return column_; }
29
30 private:
31 intptr_t line_;
32 intptr_t column_;
33};
34
35static constexpr auto kNoDwarfPositionInfo = DwarfPosition();
36
37class InliningNode : public ZoneAllocated {
38 public:
39 InliningNode(const Function& function,
40 const DwarfPosition& position,
41 int32_t start_pc_offset)
42 : function(function),
43 position(position),
44 start_pc_offset(start_pc_offset),
45 end_pc_offset(-1),
46 children_head(NULL),
47 children_tail(NULL),
48 children_next(NULL) {
49 RELEASE_ASSERT(!function.IsNull());
50 RELEASE_ASSERT(function.IsNotTemporaryScopedHandle());
51 }
52
53 void AppendChild(InliningNode* child) {
54 if (children_tail == NULL) {
55 children_head = children_tail = child;
56 } else {
57 children_tail->children_next = child;
58 children_tail = child;
59 }
60 }
61
62 const Function& function;
63 DwarfPosition position;
64 int32_t start_pc_offset;
65 int32_t end_pc_offset;
66 InliningNode* children_head;
67 InliningNode* children_tail;
68 InliningNode* children_next;
69};
70
71template <typename T>
72Trie<T>* Trie<T>::AddString(Zone* zone,
73 Trie<T>* trie,
74 const char* key,
75 const T* value) {
76 ASSERT(key != nullptr);
77 if (trie == nullptr) {
78 trie = new (zone) Trie<T>();
79 }
80 if (*key == '\0') {
81 ASSERT(trie->value_ == nullptr);
82 trie->value_ = value;
83 } else {
84 auto const index = ChildIndex(*key);
85 ASSERT(index >= 0 && index < kNumValidChars);
86 trie->children_[index] =
87 AddString(zone, trie->children_[index], key + 1, value);
88 }
89
90 return trie;
91}
92
93template <typename T>
94const T* Trie<T>::Lookup(const Trie<T>* trie, const char* key, intptr_t* end) {
95 intptr_t i = 0;
96 for (; key[i] != '\0'; i++) {
97 auto const index = ChildIndex(key[i]);
98 ASSERT(index < kNumValidChars);
99 if (index < 0) {
100 if (end == nullptr) return nullptr;
101 break;
102 }
103 // Still find the longest valid trie prefix when no stored value.
104 if (trie == nullptr) continue;
105 trie = trie->children_[index];
106 }
107 if (end != nullptr) {
108 *end = i;
109 }
110 if (trie == nullptr) return nullptr;
111 return trie->value_;
112}
113
114Dwarf::Dwarf(Zone* zone)
115 : zone_(zone),
116 reverse_obfuscation_trie_(CreateReverseObfuscationTrie(zone)),
117 codes_(zone, 1024),
118 code_to_address_(zone),
119 functions_(zone, 1024),
120 function_to_index_(zone),
121 scripts_(zone, 1024),
122 script_to_index_(zone),
123 temp_(0) {}
124
125SegmentRelativeOffset Dwarf::CodeAddress(const Code& code) const {
126 const auto& pair = code_to_address_.LookupValue(&code);
127 // This is only used by Elf::Finalize(), and the image writers always give a
128 // text offset when calling AddCode() for an Elf object's Dwarf object. Thus,
129 // we should have known code offsets for each code object in the map.
130 ASSERT(pair.offset != SegmentRelativeOffset::kUnknownOffset);
131 return pair;
132}
133
134intptr_t Dwarf::AddCode(const Code& orig_code,
135 const SegmentRelativeOffset& offset) {
136 ASSERT(!orig_code.IsNull());
137 // We should never get the no-argument constructed version here.
138 ASSERT(offset.offset != SegmentRelativeOffset::kInvalidOffset);
139 // Generate an appropriately zoned ZoneHandle for storing.
140 const auto& code = Code::ZoneHandle(zone_, orig_code.raw());
141
142 // For now, we assume one of two flows for a given code object:
143 // ELF: Calls to AddCode(code, vm, offset), vm and offset are the same over
144 // all calls.
145 // Assembly: An initial call to AddCode(code, vm) (assembly), possibly
146 // followed by a later call to AddCode(code, vm, offset)
147 // (separate debugging info ELF)
148 if (offset.offset == SegmentRelativeOffset::kUnknownOffset) {
149 // A call without an address should always come before any calls with
150 // addresses.
151 ASSERT(code_to_address_.Lookup(&code) == nullptr);
152 // Insert a marker so on later calls, we know we've already added to codes_.
153 code_to_address_.Insert(CodeAddressPair(&code, offset));
154 } else {
155 const auto& old_value = code_to_address_.LookupValue(&code);
156 // ELF does not need to know the index. If we've already added this Code
157 // object to codes_ in a previous call, don't bother scanning codes_ to find
158 // the corresponding index, just return -1 instead.
159 switch (old_value.offset) {
160 case SegmentRelativeOffset::kInvalidOffset:
161 code_to_address_.Insert(CodeAddressPair(&code, offset));
162 break; // Still need to add to codes_.
163 case SegmentRelativeOffset::kUnknownOffset:
164 // Code objects should only be associated with either the VM or isolate.
165 ASSERT_EQUAL(old_value.vm, offset.vm);
166 code_to_address_.Update(CodeAddressPair(&code, offset));
167 return -1;
168 default:
169 // The information for the code object shouldn't have changed since the
170 // previous update.
171 ASSERT(old_value == offset);
172 return -1;
173 }
174 }
175
176 const intptr_t index = codes_.length();
177 codes_.Add(&code);
178 if (code.IsFunctionCode()) {
179 const Function& function = Function::Handle(zone_, code.function());
180 AddFunction(function);
181 }
182 const Array& inline_functions =
183 Array::Handle(zone_, code.inlined_id_to_function());
184 if (!inline_functions.IsNull()) {
185 Function& function = Function::Handle(zone_);
186 for (intptr_t i = 0; i < inline_functions.Length(); i++) {
187 function ^= inline_functions.At(i);
188 AddFunction(function);
189 }
190 }
191 return index;
192}
193
194intptr_t Dwarf::AddFunction(const Function& function) {
195 RELEASE_ASSERT(!function.IsNull());
196 FunctionIndexPair* pair = function_to_index_.Lookup(&function);
197 if (pair != NULL) {
198 return pair->index_;
199 }
200 intptr_t index = functions_.length();
201 const Function& zone_func = Function::ZoneHandle(zone_, function.raw());
202 function_to_index_.Insert(FunctionIndexPair(&zone_func, index));
203 functions_.Add(&zone_func);
204 const Script& script = Script::Handle(zone_, function.script());
205 AddScript(script);
206 return index;
207}
208
209intptr_t Dwarf::AddScript(const Script& script) {
210 RELEASE_ASSERT(!script.IsNull());
211 ScriptIndexPair* pair = script_to_index_.Lookup(&script);
212 if (pair != NULL) {
213 return pair->index_;
214 }
215 // DWARF file numbers start from 1.
216 intptr_t index = scripts_.length() + 1;
217 const Script& zone_script = Script::ZoneHandle(zone_, script.raw());
218 script_to_index_.Insert(ScriptIndexPair(&zone_script, index));
219 scripts_.Add(&zone_script);
220 return index;
221}
222
223intptr_t Dwarf::LookupFunction(const Function& function) {
224 RELEASE_ASSERT(!function.IsNull());
225 FunctionIndexPair* pair = function_to_index_.Lookup(&function);
226 if (pair == NULL) {
227 FATAL1("Function detected too late during DWARF generation: %s",
228 function.ToCString());
229 }
230 return pair->index_;
231}
232
233intptr_t Dwarf::LookupScript(const Script& script) {
234 RELEASE_ASSERT(!script.IsNull());
235 ScriptIndexPair* pair = script_to_index_.Lookup(&script);
236 if (pair == NULL) {
237 FATAL1("Script detected too late during DWARF generation: %s",
238 script.ToCString());
239 }
240 return pair->index_;
241}
242
243void Dwarf::WriteAbbreviations(DwarfWriteStream* stream) {
244 // Dwarf data mostly takes the form of a tree, whose nodes are called
245 // DIEs. Each DIE begins with an abbreviation code, and the abbreviation
246 // describes the attributes of that DIE and their representation.
247
248 stream->uleb128(kCompilationUnit); // Abbrev code.
249 stream->uleb128(DW_TAG_compile_unit); // Type.
250 stream->u1(DW_CHILDREN_yes);
251 stream->uleb128(DW_AT_name); // Start of attributes.
252 stream->uleb128(DW_FORM_string);
253 stream->uleb128(DW_AT_producer);
254 stream->uleb128(DW_FORM_string);
255 stream->uleb128(DW_AT_comp_dir);
256 stream->uleb128(DW_FORM_string);
257 stream->uleb128(DW_AT_low_pc);
258 stream->uleb128(DW_FORM_addr);
259 stream->uleb128(DW_AT_high_pc);
260 stream->uleb128(DW_FORM_addr);
261 stream->uleb128(DW_AT_stmt_list);
262 stream->uleb128(DW_FORM_sec_offset);
263 stream->uleb128(0);
264 stream->uleb128(0); // End of attributes.
265
266 stream->uleb128(kAbstractFunction); // Abbrev code.
267 stream->uleb128(DW_TAG_subprogram); // Type.
268 stream->u1(DW_CHILDREN_yes);
269 stream->uleb128(DW_AT_name); // Start of attributes.
270 stream->uleb128(DW_FORM_string);
271 stream->uleb128(DW_AT_decl_file);
272 stream->uleb128(DW_FORM_udata);
273 stream->uleb128(DW_AT_inline);
274 stream->uleb128(DW_FORM_udata);
275 stream->uleb128(0);
276 stream->uleb128(0); // End of attributes.
277
278 stream->uleb128(kConcreteFunction); // Abbrev code.
279 stream->uleb128(DW_TAG_subprogram); // Type.
280 stream->u1(DW_CHILDREN_yes);
281 stream->uleb128(DW_AT_abstract_origin); // Start of attributes.
282 stream->uleb128(DW_FORM_ref4);
283 stream->uleb128(DW_AT_low_pc);
284 stream->uleb128(DW_FORM_addr);
285 stream->uleb128(DW_AT_high_pc);
286 stream->uleb128(DW_FORM_addr);
287 stream->uleb128(0);
288 stream->uleb128(0); // End of attributes.
289
290 stream->uleb128(kInlinedFunction); // Abbrev code.
291 stream->uleb128(DW_TAG_inlined_subroutine); // Type.
292 stream->u1(DW_CHILDREN_yes);
293 stream->uleb128(DW_AT_abstract_origin); // Start of attributes.
294 stream->uleb128(DW_FORM_ref4);
295 stream->uleb128(DW_AT_low_pc);
296 stream->uleb128(DW_FORM_addr);
297 stream->uleb128(DW_AT_high_pc);
298 stream->uleb128(DW_FORM_addr);
299 stream->uleb128(DW_AT_call_file);
300 stream->uleb128(DW_FORM_udata);
301 stream->uleb128(DW_AT_call_line);
302 stream->uleb128(DW_FORM_udata);
303 stream->uleb128(DW_AT_call_column);
304 stream->uleb128(DW_FORM_udata);
305 stream->uleb128(0);
306 stream->uleb128(0); // End of attributes.
307
308 stream->uleb128(0); // End of abbreviations.
309}
310
311void Dwarf::WriteDebugInfo(DwarfWriteStream* stream) {
312 SnapshotTextObjectNamer namer(zone_);
313
314 // 7.5.1.1 Compilation Unit Header
315
316 // Unit length.
317 auto const cu_prefix = "cu";
318 intptr_t cu_start;
319 intptr_t cu_size_fixup = stream->ReserveSize(cu_prefix, &cu_start);
320
321 stream->u2(2); // DWARF version 2
322 stream->u4(0); // debug_abbrev_offset
323 stream->u1(compiler::target::kWordSize); // address_size
324
325 // Compilation Unit DIE. We describe the entire Dart program as a single
326 // compilation unit. Note we write attributes in the same order we declared
327 // them in our abbreviation above in WriteAbbreviations.
328 stream->uleb128(kCompilationUnit);
329 const Library& root_library = Library::Handle(
330 zone_, Isolate::Current()->object_store()->root_library());
331 const String& root_uri = String::Handle(zone_, root_library.url());
332 stream->string(root_uri.ToCString()); // DW_AT_name
333 stream->string("Dart VM"); // DW_AT_producer
334 stream->string(""); // DW_AT_comp_dir
335
336 // DW_AT_low_pc
337 // The lowest instruction address in this object file that is part of our
338 // compilation unit. Dwarf consumers use this to quickly decide which
339 // compilation unit DIE to consult for a given pc.
340 stream->OffsetFromSymbol(kIsolateSnapshotInstructionsAsmSymbol, 0);
341
342 // DW_AT_high_pc
343 // The highest instruction address in this object file that is part of our
344 // compilation unit. Dwarf consumers use this to quickly decide which
345 // compilation unit DIE to consult for a given pc.
346 intptr_t last_code_index = codes_.length() - 1;
347 const Code& last_code = *(codes_[last_code_index]);
348 auto const last_code_name = namer.SnapshotNameFor(last_code_index, last_code);
349 stream->OffsetFromSymbol(last_code_name, last_code.Size());
350
351 // DW_AT_stmt_list (offset into .debug_line)
352 // Indicates which line number program is associated with this compilation
353 // unit. We only emit a single line number program.
354 stream->u4(0);
355
356 WriteAbstractFunctions(stream);
357 WriteConcreteFunctions(stream);
358
359 stream->uleb128(0); // End of children.
360
361 stream->uleb128(0); // End of entries.
362 stream->SetSize(cu_size_fixup, cu_prefix, cu_start);
363}
364
365void Dwarf::WriteAbstractFunctions(DwarfWriteStream* stream) {
366 Script& script = Script::Handle(zone_);
367 String& name = String::Handle(zone_);
368 stream->InitializeAbstractOrigins(functions_.length());
369 // By the point we're creating DWARF information, scripts have already lost
370 // their token stream and we can't look up their line number or column
371 // information, hence the lack of DW_AT_decl_line and DW_AT_decl_column.
372 for (intptr_t i = 0; i < functions_.length(); i++) {
373 const Function& function = *(functions_[i]);
374 name = function.QualifiedUserVisibleName();
375 script = function.script();
376 const intptr_t file = LookupScript(script);
377 auto const name_cstr = Deobfuscate(name.ToCString());
378
379 stream->RegisterAbstractOrigin(i);
380 stream->uleb128(kAbstractFunction);
381 stream->string(name_cstr); // DW_AT_name
382 stream->uleb128(file); // DW_AT_decl_file
383 stream->uleb128(DW_INL_inlined); // DW_AT_inline
384 stream->uleb128(0); // End of children.
385 }
386}
387
388void Dwarf::WriteConcreteFunctions(DwarfWriteStream* stream) {
389 Function& function = Function::Handle(zone_);
390 Script& script = Script::Handle(zone_);
391 SnapshotTextObjectNamer namer(zone_);
392 for (intptr_t i = 0; i < codes_.length(); i++) {
393 const Code& code = *(codes_[i]);
394 RELEASE_ASSERT(!code.IsNull());
395 if (!code.IsFunctionCode()) {
396 continue;
397 }
398
399 function = code.function();
400 intptr_t function_index = LookupFunction(function);
401 script = function.script();
402 const char* asm_name = namer.SnapshotNameFor(i, code);
403
404 stream->uleb128(kConcreteFunction);
405 // DW_AT_abstract_origin
406 // References a node written above in WriteAbstractFunctions.
407 stream->AbstractOrigin(function_index);
408
409 // DW_AT_low_pc
410 stream->OffsetFromSymbol(asm_name, 0);
411 // DW_AT_high_pc
412 stream->OffsetFromSymbol(asm_name, code.Size());
413
414 InliningNode* node = ExpandInliningTree(code);
415 if (node != NULL) {
416 for (InliningNode* child = node->children_head; child != NULL;
417 child = child->children_next) {
418 WriteInliningNode(stream, child, asm_name, script, &namer);
419 }
420 }
421
422 stream->uleb128(0); // End of children.
423 }
424}
425
426static DwarfPosition ReadPosition(ReadStream* stream) {
427 const intptr_t line = stream->Read<int32_t>();
428 if (!FLAG_dwarf_stack_traces_mode) {
429 return DwarfPosition(line);
430 }
431 const intptr_t column = stream->Read<int32_t>();
432 return DwarfPosition(line, column);
433}
434
435// Our state machine encodes position metadata such that we don't know the
436// end pc for an inlined function until it is popped, but DWARF DIEs encode
437// it where the function is pushed. We expand the state transitions into
438// an in-memory tree to do the conversion.
439InliningNode* Dwarf::ExpandInliningTree(const Code& code) {
440 const CodeSourceMap& map =
441 CodeSourceMap::Handle(zone_, code.code_source_map());
442 if (map.IsNull()) {
443 return NULL;
444 }
445 const Array& functions = Array::Handle(zone_, code.inlined_id_to_function());
446 const Function& root_function = Function::ZoneHandle(zone_, code.function());
447 if (root_function.IsNull()) {
448 FATAL1("Wherefore art thou functionless code, %s?\n", code.ToCString());
449 }
450
451 GrowableArray<InliningNode*> node_stack(zone_, 4);
452 GrowableArray<DwarfPosition> token_positions(zone_, 4);
453
454 NoSafepointScope no_safepoint;
455 ReadStream stream(map.Data(), map.Length());
456
457 int32_t current_pc_offset = 0;
458 token_positions.Add(kNoDwarfPositionInfo);
459 InliningNode* root_node =
460 new (zone_) InliningNode(root_function, token_positions.Last(), 0);
461 root_node->end_pc_offset = code.Size();
462 node_stack.Add(root_node);
463
464 while (stream.PendingBytes() > 0) {
465 uint8_t opcode = stream.Read<uint8_t>();
466 switch (opcode) {
467 case CodeSourceMapBuilder::kChangePosition: {
468 token_positions[token_positions.length() - 1] = ReadPosition(&stream);
469 break;
470 }
471 case CodeSourceMapBuilder::kAdvancePC: {
472 int32_t delta = stream.Read<int32_t>();
473 current_pc_offset += delta;
474 break;
475 }
476 case CodeSourceMapBuilder::kPushFunction: {
477 int32_t func = stream.Read<int32_t>();
478 const Function& child_func =
479 Function::ZoneHandle(zone_, Function::RawCast(functions.At(func)));
480 InliningNode* child_node = new (zone_)
481 InliningNode(child_func, token_positions.Last(), current_pc_offset);
482 node_stack.Last()->AppendChild(child_node);
483 node_stack.Add(child_node);
484 token_positions.Add(kNoDwarfPositionInfo);
485 break;
486 }
487 case CodeSourceMapBuilder::kPopFunction: {
488 // We never pop the root function.
489 ASSERT(node_stack.length() > 1);
490 ASSERT(token_positions.length() > 1);
491 node_stack.Last()->end_pc_offset = current_pc_offset;
492 node_stack.RemoveLast();
493 token_positions.RemoveLast();
494 break;
495 }
496 case CodeSourceMapBuilder::kNullCheck: {
497 stream.Read<int32_t>();
498 break;
499 }
500 default:
501 UNREACHABLE();
502 }
503 }
504
505 while (node_stack.length() > 1) {
506 node_stack.Last()->end_pc_offset = current_pc_offset;
507 node_stack.RemoveLast();
508 token_positions.RemoveLast();
509 }
510 ASSERT(node_stack[0] == root_node);
511 return root_node;
512}
513
514void Dwarf::WriteInliningNode(DwarfWriteStream* stream,
515 InliningNode* node,
516 const char* root_asm_name,
517 const Script& parent_script,
518 SnapshotTextObjectNamer* namer) {
519 intptr_t file = LookupScript(parent_script);
520 intptr_t function_index = LookupFunction(node->function);
521 const Script& script = Script::Handle(zone_, node->function.script());
522
523 stream->uleb128(kInlinedFunction);
524 // DW_AT_abstract_origin
525 // References a node written above in WriteAbstractFunctions.
526 stream->AbstractOrigin(function_index);
527
528 // DW_AT_low_pc
529 stream->OffsetFromSymbol(root_asm_name, node->start_pc_offset);
530 // DW_AT_high_pc
531 stream->OffsetFromSymbol(root_asm_name, node->end_pc_offset);
532 // DW_AT_call_file
533 stream->uleb128(file);
534 // DW_AT_call_line
535 stream->uleb128(node->position.line());
536 // DW_at_call_column
537 stream->uleb128(node->position.column());
538
539 for (InliningNode* child = node->children_head; child != NULL;
540 child = child->children_next) {
541 WriteInliningNode(stream, child, root_asm_name, script, namer);
542 }
543
544 stream->uleb128(0); // End of children.
545}
546
547void Dwarf::WriteLineNumberProgram(DwarfWriteStream* stream) {
548 // 6.2.4 The Line Number Program Header
549
550 // 1. unit_length. This encoding implies 32-bit DWARF.
551 auto const line_prefix = "line";
552 intptr_t line_start;
553 intptr_t line_size_fixup = stream->ReserveSize(line_prefix, &line_start);
554
555 stream->u2(2); // 2. DWARF version 2
556
557 // 3. header_length
558 auto const lineheader_prefix = "lineheader";
559 intptr_t lineheader_start;
560 intptr_t lineheader_size_fixup =
561 stream->ReserveSize(lineheader_prefix, &lineheader_start);
562
563 stream->u1(1); // 4. minimum_instruction_length
564 stream->u1(1); // 5. default_is_stmt (true for compatibility with dsymutil).
565 stream->u1(0); // 6. line_base
566 stream->u1(1); // 7. line_range
567 stream->u1(13); // 8. opcode_base (12 standard opcodes in Dwarf 2)
568
569 // 9. standard_opcode_lengths
570 stream->u1(0); // DW_LNS_copy, 0 operands
571 stream->u1(1); // DW_LNS_advance_pc, 1 operands
572 stream->u1(1); // DW_LNS_advance_list, 1 operands
573 stream->u1(1); // DW_LNS_set_file, 1 operands
574 stream->u1(1); // DW_LNS_set_column, 1 operands
575 stream->u1(0); // DW_LNS_negate_stmt, 0 operands
576 stream->u1(0); // DW_LNS_set_basic_block, 0 operands
577 stream->u1(0); // DW_LNS_const_add_pc, 0 operands
578 stream->u1(1); // DW_LNS_fixed_advance_pc, 1 operands
579 stream->u1(0); // DW_LNS_set_prolog_end, 0 operands
580 stream->u1(0); // DW_LNS_set_epligoue_begin, 0 operands
581 stream->u1(1); // DW_LNS_set_isa, 1 operands
582
583 // 10. include_directories (sequence of path names)
584 // We don't emit any because we use full paths below.
585 stream->u1(0);
586
587 // 11. file_names (sequence of file entries)
588 String& uri = String::Handle(zone_);
589 for (intptr_t i = 0; i < scripts_.length(); i++) {
590 const Script& script = *(scripts_[i]);
591 uri = script.url();
592 auto const uri_cstr = Deobfuscate(uri.ToCString());
593 RELEASE_ASSERT(strlen(uri_cstr) != 0);
594
595 stream->string(uri_cstr); // NOLINT
596 stream->uleb128(0); // Include directory index.
597 stream->uleb128(0); // File modification time.
598 stream->uleb128(0); // File length.
599 }
600 stream->u1(0); // End of file names.
601 stream->SetSize(lineheader_size_fixup, lineheader_prefix, lineheader_start);
602
603 // 6.2.5 The Line Number Program
604
605 // The initial values for the line number program state machine registers
606 // according to the DWARF standard.
607 intptr_t previous_pc_offset = 0;
608 intptr_t previous_file = 1;
609 intptr_t previous_line = 1;
610 intptr_t previous_column = 0;
611 // Other info not stored in the state machine registers.
612 const char* previous_asm_name = nullptr;
613
614 Function& root_function = Function::Handle(zone_);
615 Script& script = Script::Handle(zone_);
616 CodeSourceMap& map = CodeSourceMap::Handle(zone_);
617 Array& functions = Array::Handle(zone_);
618 GrowableArray<const Function*> function_stack(zone_, 8);
619 GrowableArray<DwarfPosition> token_positions(zone_, 8);
620 SnapshotTextObjectNamer namer(zone_);
621
622 for (intptr_t i = 0; i < codes_.length(); i++) {
623 const Code& code = *(codes_[i]);
624 auto const asm_name = namer.SnapshotNameFor(i, code);
625
626 map = code.code_source_map();
627 if (map.IsNull()) {
628 continue;
629 }
630 root_function = code.function();
631 functions = code.inlined_id_to_function();
632
633 NoSafepointScope no_safepoint;
634 ReadStream code_map_stream(map.Data(), map.Length());
635
636 function_stack.Clear();
637 token_positions.Clear();
638
639 int32_t current_pc_offset = 0;
640 function_stack.Add(&root_function);
641 token_positions.Add(kNoDwarfPositionInfo);
642
643 while (code_map_stream.PendingBytes() > 0) {
644 uint8_t opcode = code_map_stream.Read<uint8_t>();
645 switch (opcode) {
646 case CodeSourceMapBuilder::kChangePosition: {
647 token_positions[token_positions.length() - 1] =
648 ReadPosition(&code_map_stream);
649 break;
650 }
651 case CodeSourceMapBuilder::kAdvancePC: {
652 int32_t delta = code_map_stream.Read<int32_t>();
653 current_pc_offset += delta;
654
655 const Function& function = *(function_stack.Last());
656 script = function.script();
657 intptr_t file = LookupScript(script);
658
659 // 1. Update LNP file.
660 if (file != previous_file) {
661 stream->u1(DW_LNS_set_file);
662 stream->uleb128(file);
663 previous_file = file;
664 }
665
666 // 2. Update LNP line.
667 const intptr_t line = token_positions.Last().line();
668 const intptr_t column = token_positions.Last().column();
669 if (line != previous_line) {
670 stream->u1(DW_LNS_advance_line);
671 stream->sleb128(line - previous_line);
672 previous_line = line;
673 }
674 if (column != previous_column) {
675 stream->u1(DW_LNS_set_column);
676 stream->uleb128(column);
677 previous_column = column;
678 }
679
680 // 3. Emit LNP row if the address register has been updated to a
681 // non-zero value (dartbug.com/41756).
682 if (previous_asm_name != nullptr) {
683 stream->u1(DW_LNS_copy);
684 }
685
686 // 4. Update LNP pc.
687 if (previous_asm_name == nullptr) {
688 auto const instr_size = 1 + compiler::target::kWordSize;
689 stream->u1(0); // This is an extended opcode
690 stream->u1(instr_size); // that is 5 or 9 bytes long
691 stream->u1(DW_LNE_set_address);
692 stream->OffsetFromSymbol(asm_name, current_pc_offset);
693 } else {
694 stream->u1(DW_LNS_advance_pc);
695 stream->DistanceBetweenSymbolOffsets(asm_name, current_pc_offset,
696 previous_asm_name,
697 previous_pc_offset);
698 }
699 previous_asm_name = asm_name;
700 previous_pc_offset = current_pc_offset;
701 break;
702 }
703 case CodeSourceMapBuilder::kPushFunction: {
704 int32_t func_index = code_map_stream.Read<int32_t>();
705 const Function& child_func = Function::Handle(
706 zone_, Function::RawCast(functions.At(func_index)));
707 function_stack.Add(&child_func);
708 token_positions.Add(kNoDwarfPositionInfo);
709 break;
710 }
711 case CodeSourceMapBuilder::kPopFunction: {
712 // We never pop the root function.
713 ASSERT(function_stack.length() > 1);
714 ASSERT(token_positions.length() > 1);
715 function_stack.RemoveLast();
716 token_positions.RemoveLast();
717 break;
718 }
719 case CodeSourceMapBuilder::kNullCheck: {
720 code_map_stream.Read<int32_t>();
721 break;
722 }
723 default:
724 UNREACHABLE();
725 }
726 }
727 }
728
729 // Advance pc to end of the compilation unit if not already there.
730 if (codes_.length() != 0) {
731 const intptr_t last_code_index = codes_.length() - 1;
732 const Code& last_code = *(codes_[last_code_index]);
733 const intptr_t last_pc_offset = last_code.Size();
734 const char* last_asm_name =
735 namer.SnapshotNameFor(last_code_index, last_code);
736
737 stream->u1(DW_LNS_advance_pc);
738 if (previous_asm_name != nullptr) {
739 stream->DistanceBetweenSymbolOffsets(
740 last_asm_name, last_pc_offset, previous_asm_name, previous_pc_offset);
741 } else {
742 // No LNP entries (e.g., only stub code).
743 ASSERT(previous_pc_offset == 0);
744 stream->uleb128(last_pc_offset);
745 }
746 }
747
748 // End of contiguous machine code.
749 stream->u1(0); // This is an extended opcode
750 stream->u1(1); // that is 1 byte long
751 stream->u1(DW_LNE_end_sequence);
752 stream->SetSize(line_size_fixup, line_prefix, line_start);
753}
754
755const char* Dwarf::Deobfuscate(const char* cstr) {
756 if (reverse_obfuscation_trie_ == nullptr) return cstr;
757 TextBuffer buffer(256);
758 // Used to avoid Zone-allocating strings if no deobfuscation was performed.
759 bool changed = false;
760 intptr_t i = 0;
761 while (cstr[i] != '\0') {
762 intptr_t offset;
763 auto const value = reverse_obfuscation_trie_->Lookup(cstr + i, &offset);
764 if (offset == 0) {
765 // The first character was an invalid key element (that isn't the null
766 // terminator due to the while condition), copy it and skip to the next.
767 buffer.AddChar(cstr[i++]);
768 } else if (value != nullptr) {
769 changed = true;
770 buffer.AddString(value);
771 } else {
772 buffer.AddRaw(reinterpret_cast<const uint8_t*>(cstr + i), offset);
773 }
774 i += offset;
775 }
776 if (!changed) return cstr;
777 return OS::SCreate(zone_, "%s", buffer.buffer());
778}
779
780Trie<const char>* Dwarf::CreateReverseObfuscationTrie(Zone* zone) {
781 auto const I = Thread::Current()->isolate();
782 auto const map_array = I->obfuscation_map();
783 if (map_array == nullptr) return nullptr;
784
785 Trie<const char>* trie = nullptr;
786 for (intptr_t i = 0; map_array[i] != nullptr; i += 2) {
787 auto const key = map_array[i];
788 auto const value = map_array[i + 1];
789 ASSERT(value != nullptr);
790 // Don't include identity mappings.
791 if (strcmp(key, value) == 0) continue;
792 // Otherwise, any value in the obfuscation map should be a valid key.
793 ASSERT(Trie<const char>::IsValidKey(value));
794 trie = Trie<const char>::AddString(zone, trie, value, key);
795 }
796 return trie;
797}
798
799#endif // DART_PRECOMPILER
800
801} // namespace dart
802