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#ifndef RUNTIME_VM_DWARF_H_
6#define RUNTIME_VM_DWARF_H_
7
8#include "vm/allocation.h"
9#include "vm/hash_map.h"
10#include "vm/object.h"
11#include "vm/zone.h"
12
13namespace dart {
14
15#ifdef DART_PRECOMPILER
16
17class InliningNode;
18class SnapshotTextObjectNamer;
19
20struct ScriptIndexPair {
21 // Typedefs needed for the DirectChainedHashMap template.
22 typedef const Script* Key;
23 typedef intptr_t Value;
24 typedef ScriptIndexPair Pair;
25
26 static Key KeyOf(Pair kv) { return kv.script_; }
27
28 static Value ValueOf(Pair kv) { return kv.index_; }
29
30 static inline intptr_t Hashcode(Key key) {
31 return String::Handle(key->url()).Hash();
32 }
33
34 static inline bool IsKeyEqual(Pair pair, Key key) {
35 return pair.script_->raw() == key->raw();
36 }
37
38 ScriptIndexPair(const Script* s, intptr_t index) : script_(s), index_(index) {
39 ASSERT(!s->IsNull());
40 ASSERT(s->IsNotTemporaryScopedHandle());
41 }
42
43 ScriptIndexPair() : script_(NULL), index_(-1) {}
44
45 void Print() const;
46
47 const Script* script_;
48 intptr_t index_;
49};
50
51typedef DirectChainedHashMap<ScriptIndexPair> ScriptIndexMap;
52
53struct FunctionIndexPair {
54 // Typedefs needed for the DirectChainedHashMap template.
55 typedef const Function* Key;
56 typedef intptr_t Value;
57 typedef FunctionIndexPair Pair;
58
59 static Key KeyOf(Pair kv) { return kv.function_; }
60
61 static Value ValueOf(Pair kv) { return kv.index_; }
62
63 static inline intptr_t Hashcode(Key key) { return key->token_pos().value(); }
64
65 static inline bool IsKeyEqual(Pair pair, Key key) {
66 return pair.function_->raw() == key->raw();
67 }
68
69 FunctionIndexPair(const Function* f, intptr_t index)
70 : function_(f), index_(index) {
71 ASSERT(!f->IsNull());
72 ASSERT(f->IsNotTemporaryScopedHandle());
73 }
74
75 FunctionIndexPair() : function_(NULL), index_(-1) {}
76
77 void Print() const;
78
79 const Function* function_;
80 intptr_t index_;
81};
82
83typedef DirectChainedHashMap<FunctionIndexPair> FunctionIndexMap;
84
85struct SegmentRelativeOffset {
86 // Used for the empty constructor (for hash map usage).
87 static constexpr intptr_t kInvalidOffset = -2;
88 // Used for cases where we know which segment, but don't know the offset.
89 static constexpr intptr_t kUnknownOffset = -1;
90
91 SegmentRelativeOffset(bool vm, intptr_t offset) : vm(vm), offset(offset) {
92 ASSERT(offset >= 0);
93 }
94 explicit SegmentRelativeOffset(bool vm) : vm(vm), offset(kUnknownOffset) {}
95 SegmentRelativeOffset() : vm(false), offset(kInvalidOffset) {}
96
97 bool operator==(const SegmentRelativeOffset& b) const {
98 return vm == b.vm && offset == b.offset;
99 }
100 bool operator==(const SegmentRelativeOffset& b) {
101 return *const_cast<const SegmentRelativeOffset*>(this) == b;
102 }
103 bool operator!=(const SegmentRelativeOffset& b) { return !(*this == b); }
104
105 // Whether or not this is an offset into the VM text segment.
106 bool vm;
107 // The byte offset into the segment contents.
108 intptr_t offset;
109};
110
111struct CodeAddressPair {
112 // Typedefs needed for the DirectChainedHashMap template.
113 typedef const Code* Key;
114 typedef SegmentRelativeOffset Value;
115 typedef CodeAddressPair Pair;
116
117 static Key KeyOf(Pair kv) { return kv.code; }
118
119 static Value ValueOf(Pair kv) { return kv.segment_offset; }
120
121 static inline intptr_t Hashcode(Key key) {
122 // Code objects are always allocated in old space, so they don't move.
123 return key->PayloadStart();
124 }
125
126 static inline bool IsKeyEqual(Pair pair, Key key) {
127 return pair.code->raw() == key->raw();
128 }
129
130 CodeAddressPair(const Code* c, const SegmentRelativeOffset& o)
131 : code(c), segment_offset(o) {
132 ASSERT(!c->IsNull());
133 ASSERT(c->IsNotTemporaryScopedHandle());
134 ASSERT(o.offset == SegmentRelativeOffset::kUnknownOffset || o.offset >= 0);
135 }
136 CodeAddressPair() : code(nullptr), segment_offset() {}
137
138 const Code* code;
139 SegmentRelativeOffset segment_offset;
140};
141
142typedef DirectChainedHashMap<CodeAddressPair> CodeAddressMap;
143
144template <typename T>
145class Trie : public ZoneAllocated {
146 public:
147 // Returns whether [key] is a valid trie key (that is, a C string that
148 // contains only characters for which charIndex returns a non-negative value).
149 static bool IsValidKey(const char* key) {
150 for (intptr_t i = 0; key[i] != '\0'; i++) {
151 if (ChildIndex(key[i]) < 0) return false;
152 }
153 return true;
154 }
155
156 // Adds a binding of [key] to [value] in [trie]. Assumes that the string in
157 // [key] is a valid trie key and does not already have a value in [trie].
158 //
159 // If [trie] is nullptr, then a new trie is created and a pointer to the new
160 // trie is returned. Otherwise, [trie] will be returned.
161 static Trie<T>* AddString(Zone* zone,
162 Trie<T>* trie,
163 const char* key,
164 const T* value);
165
166 // Adds a binding of [key] to [value]. Assumes that the string in [key] is a
167 // valid trie key and does not already have a value in this trie.
168 void AddString(Zone* zone, const char* key, const T* value) {
169 AddString(zone, this, key, value);
170 }
171
172 // Looks up the value stored for [key] in [trie]. If one is not found, then
173 // nullptr is returned.
174 //
175 // If [end] is not nullptr, then the longest prefix of [key] that is a valid
176 // trie key prefix will be used for the lookup and the value pointed to by
177 // [end] is set to the index after that prefix. Otherwise, the whole [key]
178 // is used.
179 static const T* Lookup(const Trie<T>* trie,
180 const char* key,
181 intptr_t* end = nullptr);
182
183 // Looks up the value stored for [key]. If one is not found, then nullptr is
184 // returned.
185 //
186 // If [end] is not nullptr, then the longest prefix of [key] that is a valid
187 // trie key prefix will be used for the lookup and the value pointed to by
188 // [end] is set to the index after that prefix. Otherwise, the whole [key]
189 // is used.
190 const T* Lookup(const char* key, intptr_t* end = nullptr) const {
191 return Lookup(this, key, end);
192 }
193
194 private:
195 // Currently, only the following characters can appear in obfuscated names:
196 // '_', '@', '0-9', 'a-z', 'A-Z'
197 static const intptr_t kNumValidChars = 64;
198
199 Trie() {
200 for (intptr_t i = 0; i < kNumValidChars; i++) {
201 children_[i] = nullptr;
202 }
203 }
204
205 static intptr_t ChildIndex(char c) {
206 if (c == '_') return 0;
207 if (c == '@') return 1;
208 if (c >= '0' && c <= '9') return ('9' - c) + 2;
209 if (c >= 'a' && c <= 'z') return ('z' - c) + 12;
210 if (c >= 'A' && c <= 'Z') return ('Z' - c) + 38;
211 return -1;
212 }
213
214 const T* value_ = nullptr;
215 Trie<T>* children_[kNumValidChars];
216};
217
218class DwarfWriteStream : public ValueObject {
219 public:
220 DwarfWriteStream() {}
221 virtual ~DwarfWriteStream() {}
222
223 virtual void sleb128(intptr_t value) = 0;
224 virtual void uleb128(uintptr_t value) = 0;
225 virtual void u1(uint8_t value) = 0;
226 virtual void u2(uint16_t value) = 0;
227 virtual void u4(uint32_t value) = 0;
228 virtual void u8(uint64_t value) = 0;
229 virtual void string(const char* cstr) = 0; // NOLINT
230
231 // Returns the position (if any) to fix up in SetSize().
232 virtual intptr_t ReserveSize(const char* prefix, intptr_t* start) = 0;
233 virtual void SetSize(intptr_t position,
234 const char* prefix,
235 intptr_t start) = 0;
236
237 virtual void OffsetFromSymbol(const char* symbol, intptr_t offset) = 0;
238 // Returns the difference between the relocated address at offset1 from
239 // symbol1 and the relocated address at offset2 from symbol2.
240 virtual void DistanceBetweenSymbolOffsets(const char* symbol1,
241 intptr_t offset1,
242 const char* symbol2,
243 intptr_t offset2) = 0;
244
245 virtual void InitializeAbstractOrigins(intptr_t size) = 0;
246 virtual void RegisterAbstractOrigin(intptr_t index) = 0;
247 virtual void AbstractOrigin(intptr_t index) = 0;
248
249 DISALLOW_COPY_AND_ASSIGN(DwarfWriteStream);
250};
251
252class Dwarf : public ZoneAllocated {
253 public:
254 explicit Dwarf(Zone* zone);
255
256 const ZoneGrowableArray<const Code*>& codes() const { return codes_; }
257
258 // Stores the code object for later creating the line number program.
259 //
260 // Returns the stored index of the code object when the relocated address
261 // is not known at snapshot generation time (that is, when offset.offset is
262 // SegmentRelativeOffset::kUnknownOffset).
263 intptr_t AddCode(const Code& code, const SegmentRelativeOffset& offset);
264
265 // Returns the stored segment offset for the given Code object. If no
266 // address is stored, the second element will be kNoCodeAddressPairOffset.
267 SegmentRelativeOffset CodeAddress(const Code& code) const;
268
269 intptr_t AddFunction(const Function& function);
270 intptr_t AddScript(const Script& script);
271 intptr_t LookupFunction(const Function& function);
272 intptr_t LookupScript(const Script& script);
273
274 void WriteAbbreviations(DwarfWriteStream* stream);
275 void WriteDebugInfo(DwarfWriteStream* stream);
276 void WriteLineNumberProgram(DwarfWriteStream* stream);
277
278 private:
279 static const intptr_t DW_TAG_compile_unit = 0x11;
280 static const intptr_t DW_TAG_inlined_subroutine = 0x1d;
281 static const intptr_t DW_TAG_subprogram = 0x2e;
282
283 static const intptr_t DW_CHILDREN_no = 0x0;
284 static const intptr_t DW_CHILDREN_yes = 0x1;
285
286 static const intptr_t DW_AT_sibling = 0x1;
287 static const intptr_t DW_AT_name = 0x3;
288 static const intptr_t DW_AT_stmt_list = 0x10;
289 static const intptr_t DW_AT_low_pc = 0x11;
290 static const intptr_t DW_AT_high_pc = 0x12;
291 static const intptr_t DW_AT_comp_dir = 0x1b;
292 static const intptr_t DW_AT_inline = 0x20;
293 static const intptr_t DW_AT_producer = 0x25;
294 static const intptr_t DW_AT_abstract_origin = 0x31;
295 static const intptr_t DW_AT_decl_column = 0x39;
296 static const intptr_t DW_AT_decl_file = 0x3a;
297 static const intptr_t DW_AT_decl_line = 0x3b;
298 static const intptr_t DW_AT_call_column = 0x57;
299 static const intptr_t DW_AT_call_file = 0x58;
300 static const intptr_t DW_AT_call_line = 0x59;
301
302 static const intptr_t DW_FORM_addr = 0x01;
303 static const intptr_t DW_FORM_string = 0x08;
304 static const intptr_t DW_FORM_udata = 0x0f;
305 static const intptr_t DW_FORM_ref4 = 0x13;
306 static const intptr_t DW_FORM_ref_udata = 0x15;
307 static const intptr_t DW_FORM_sec_offset = 0x17;
308
309 static const intptr_t DW_INL_not_inlined = 0x0;
310 static const intptr_t DW_INL_inlined = 0x1;
311
312 static const intptr_t DW_LNS_copy = 0x1;
313 static const intptr_t DW_LNS_advance_pc = 0x2;
314 static const intptr_t DW_LNS_advance_line = 0x3;
315 static const intptr_t DW_LNS_set_file = 0x4;
316 static const intptr_t DW_LNS_set_column = 0x5;
317
318 static const intptr_t DW_LNE_end_sequence = 0x01;
319 static const intptr_t DW_LNE_set_address = 0x02;
320
321 enum {
322 kCompilationUnit = 1,
323 kAbstractFunction,
324 kConcreteFunction,
325 kInlinedFunction,
326 };
327
328 void WriteAbstractFunctions(DwarfWriteStream* stream);
329 void WriteConcreteFunctions(DwarfWriteStream* stream);
330 InliningNode* ExpandInliningTree(const Code& code);
331 void WriteInliningNode(DwarfWriteStream* stream,
332 InliningNode* node,
333 const char* root_code_name,
334 const Script& parent_script,
335 SnapshotTextObjectNamer* namer);
336
337 const char* Deobfuscate(const char* cstr);
338 static Trie<const char>* CreateReverseObfuscationTrie(Zone* zone);
339
340 Zone* const zone_;
341 Trie<const char>* const reverse_obfuscation_trie_;
342 ZoneGrowableArray<const Code*> codes_;
343 CodeAddressMap code_to_address_;
344 ZoneGrowableArray<const Function*> functions_;
345 FunctionIndexMap function_to_index_;
346 ZoneGrowableArray<const Script*> scripts_;
347 ScriptIndexMap script_to_index_;
348 intptr_t temp_;
349};
350
351#endif // DART_PRECOMPILER
352
353} // namespace dart
354
355#endif // RUNTIME_VM_DWARF_H_
356