1 // Copyright 2011 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "src/disassembler.h"
6 
7 #include <memory>
8 #include <vector>
9 
10 #include "src/assembler-inl.h"
11 #include "src/code-reference.h"
12 #include "src/code-stubs.h"
13 #include "src/debug/debug.h"
14 #include "src/deoptimizer.h"
15 #include "src/disasm.h"
16 #include "src/ic/ic.h"
17 #include "src/instruction-stream.h"
18 #include "src/macro-assembler.h"
19 #include "src/objects-inl.h"
20 #include "src/snapshot/serializer-common.h"
21 #include "src/string-stream.h"
22 #include "src/wasm/wasm-code-manager.h"
23 #include "src/wasm/wasm-engine.h"
24 
25 namespace v8 {
26 namespace internal {
27 
28 #ifdef ENABLE_DISASSEMBLER
29 
30 class V8NameConverter: public disasm::NameConverter {
31  public:
V8NameConverter(Isolate * isolate,CodeReference code={})32   explicit V8NameConverter(Isolate* isolate, CodeReference code = {})
33       : isolate_(isolate), code_(code) {}
34   const char* NameOfAddress(byte* pc) const override;
35   const char* NameInCode(byte* addr) const override;
36   const char* RootRelativeName(int offset) const override;
37 
code() const38   const CodeReference& code() const { return code_; }
39 
40  private:
41   Isolate* isolate_;
42   CodeReference code_;
43 
44   EmbeddedVector<char, 128> v8_buffer_;
45 };
46 
47 
NameOfAddress(byte * pc) const48 const char* V8NameConverter::NameOfAddress(byte* pc) const {
49   if (!code_.is_null()) {
50     const char* name =
51         isolate_ ? isolate_->builtins()->Lookup(reinterpret_cast<Address>(pc))
52                  : nullptr;
53 
54     if (name != nullptr) {
55       SNPrintF(v8_buffer_, "%p  (%s)", static_cast<void*>(pc), name);
56       return v8_buffer_.start();
57     }
58 
59     int offs = static_cast<int>(reinterpret_cast<Address>(pc) -
60                                 code_.instruction_start());
61     // print as code offset, if it seems reasonable
62     if (0 <= offs && offs < code_.instruction_size()) {
63       SNPrintF(v8_buffer_, "%p  <+0x%x>", static_cast<void*>(pc), offs);
64       return v8_buffer_.start();
65     }
66 
67     wasm::WasmCode* wasm_code =
68         isolate_ ? isolate_->wasm_engine()->code_manager()->LookupCode(
69                        reinterpret_cast<Address>(pc))
70                  : nullptr;
71     if (wasm_code != nullptr) {
72       SNPrintF(v8_buffer_, "%p  (%s)", static_cast<void*>(pc),
73                wasm::GetWasmCodeKindAsString(wasm_code->kind()));
74       return v8_buffer_.start();
75     }
76   }
77 
78   return disasm::NameConverter::NameOfAddress(pc);
79 }
80 
81 
NameInCode(byte * addr) const82 const char* V8NameConverter::NameInCode(byte* addr) const {
83   // The V8NameConverter is used for well known code, so we can "safely"
84   // dereference pointers in generated code.
85   return code_.is_null() ? "" : reinterpret_cast<const char*>(addr);
86 }
87 
RootRelativeName(int offset) const88 const char* V8NameConverter::RootRelativeName(int offset) const {
89   if (isolate_ == nullptr) return nullptr;
90 
91   const int kRootsStart = 0;
92   const int kRootsEnd = Heap::roots_to_external_reference_table_offset();
93   const int kExtRefsStart = Heap::roots_to_external_reference_table_offset();
94   const int kExtRefsEnd = Heap::roots_to_builtins_offset();
95 
96   if (kRootsStart <= offset && offset < kRootsEnd) {
97     uint32_t offset_in_roots_table = offset - kRootsStart;
98 
99     // Fail safe in the unlikely case of an arbitrary root-relative offset.
100     if (offset_in_roots_table % kPointerSize != 0) return nullptr;
101 
102     Heap::RootListIndex root_index =
103         static_cast<Heap::RootListIndex>(offset_in_roots_table / kPointerSize);
104 
105     HeapStringAllocator allocator;
106     StringStream accumulator(&allocator);
107     isolate_->heap()->root(root_index)->ShortPrint(&accumulator);
108     std::unique_ptr<char[]> obj_name = accumulator.ToCString();
109 
110     SNPrintF(v8_buffer_, "root (%s)", obj_name.get());
111     return v8_buffer_.start();
112   } else if (kExtRefsStart <= offset && offset < kExtRefsEnd) {
113     uint32_t offset_in_extref_table = offset - kExtRefsStart;
114 
115     // Fail safe in the unlikely case of an arbitrary root-relative offset.
116     if (offset_in_extref_table % ExternalReferenceTable::EntrySize() != 0) {
117       return nullptr;
118     }
119 
120     // Likewise if the external reference table is uninitialized.
121     if (!isolate_->heap()->external_reference_table()->is_initialized()) {
122       return nullptr;
123     }
124 
125     SNPrintF(v8_buffer_, "external reference (%s)",
126              isolate_->heap()->external_reference_table()->NameFromOffset(
127                  offset_in_extref_table));
128     return v8_buffer_.start();
129   } else {
130     return nullptr;
131   }
132 }
133 
DumpBuffer(std::ostream * os,StringBuilder * out)134 static void DumpBuffer(std::ostream* os, StringBuilder* out) {
135   (*os) << out->Finalize() << std::endl;
136   out->Reset();
137 }
138 
139 
140 static const int kOutBufferSize = 2048 + String::kMaxShortPrintLength;
141 static const int kRelocInfoPosition = 57;
142 
PrintRelocInfo(StringBuilder * out,Isolate * isolate,const ExternalReferenceEncoder * ref_encoder,std::ostream * os,RelocInfo * relocinfo,bool first_reloc_info=true)143 static void PrintRelocInfo(StringBuilder* out, Isolate* isolate,
144                            const ExternalReferenceEncoder* ref_encoder,
145                            std::ostream* os, RelocInfo* relocinfo,
146                            bool first_reloc_info = true) {
147   // Indent the printing of the reloc info.
148   if (first_reloc_info) {
149     // The first reloc info is printed after the disassembled instruction.
150     out->AddPadding(' ', kRelocInfoPosition - out->position());
151   } else {
152     // Additional reloc infos are printed on separate lines.
153     DumpBuffer(os, out);
154     out->AddPadding(' ', kRelocInfoPosition);
155   }
156 
157   RelocInfo::Mode rmode = relocinfo->rmode();
158   if (rmode == RelocInfo::DEOPT_SCRIPT_OFFSET) {
159     out->AddFormatted("    ;; debug: deopt position, script offset '%d'",
160                       static_cast<int>(relocinfo->data()));
161   } else if (rmode == RelocInfo::DEOPT_INLINING_ID) {
162     out->AddFormatted("    ;; debug: deopt position, inlining id '%d'",
163                       static_cast<int>(relocinfo->data()));
164   } else if (rmode == RelocInfo::DEOPT_REASON) {
165     DeoptimizeReason reason = static_cast<DeoptimizeReason>(relocinfo->data());
166     out->AddFormatted("    ;; debug: deopt reason '%s'",
167                       DeoptimizeReasonToString(reason));
168   } else if (rmode == RelocInfo::DEOPT_ID) {
169     out->AddFormatted("    ;; debug: deopt index %d",
170                       static_cast<int>(relocinfo->data()));
171   } else if (rmode == RelocInfo::EMBEDDED_OBJECT) {
172     HeapStringAllocator allocator;
173     StringStream accumulator(&allocator);
174     relocinfo->target_object()->ShortPrint(&accumulator);
175     std::unique_ptr<char[]> obj_name = accumulator.ToCString();
176     out->AddFormatted("    ;; object: %s", obj_name.get());
177   } else if (rmode == RelocInfo::EXTERNAL_REFERENCE) {
178     const char* reference_name =
179         ref_encoder ? ref_encoder->NameOfAddress(
180                           isolate, relocinfo->target_external_reference())
181                     : "unknown";
182     out->AddFormatted("    ;; external reference (%s)", reference_name);
183   } else if (RelocInfo::IsCodeTargetMode(rmode)) {
184     out->AddFormatted("    ;; code:");
185     Code* code = isolate->heap()->GcSafeFindCodeForInnerPointer(
186         relocinfo->target_address());
187     Code::Kind kind = code->kind();
188     if (kind == Code::STUB) {
189       // Get the STUB key and extract major and minor key.
190       uint32_t key = code->stub_key();
191       uint32_t minor_key = CodeStub::MinorKeyFromKey(key);
192       CodeStub::Major major_key = CodeStub::GetMajorKey(code);
193       DCHECK(major_key == CodeStub::MajorKeyFromKey(key));
194       out->AddFormatted(" %s, %s, ", Code::Kind2String(kind),
195                         CodeStub::MajorName(major_key));
196       out->AddFormatted("minor: %d", minor_key);
197     } else if (code->is_builtin()) {
198       out->AddFormatted(" Builtin::%s", Builtins::name(code->builtin_index()));
199     } else {
200       out->AddFormatted(" %s", Code::Kind2String(kind));
201     }
202   } else if (RelocInfo::IsRuntimeEntry(rmode) && isolate &&
203              isolate->deoptimizer_data() != nullptr) {
204     // A runtime entry relocinfo might be a deoptimization bailout.
205     Address addr = relocinfo->target_address();
206     DeoptimizeKind type;
207     if (Deoptimizer::IsDeoptimizationEntry(isolate, addr, &type)) {
208       int id = relocinfo->GetDeoptimizationId(isolate, type);
209       out->AddFormatted("    ;; %s deoptimization bailout %d",
210                         Deoptimizer::MessageFor(type), id);
211     } else {
212       out->AddFormatted("    ;; %s", RelocInfo::RelocModeName(rmode));
213     }
214   } else {
215     out->AddFormatted("    ;; %s", RelocInfo::RelocModeName(rmode));
216   }
217 }
218 
DecodeIt(Isolate * isolate,ExternalReferenceEncoder * ref_encoder,std::ostream * os,CodeReference code,const V8NameConverter & converter,byte * begin,byte * end,Address current_pc)219 static int DecodeIt(Isolate* isolate, ExternalReferenceEncoder* ref_encoder,
220                     std::ostream* os, CodeReference code,
221                     const V8NameConverter& converter, byte* begin, byte* end,
222                     Address current_pc) {
223   v8::internal::EmbeddedVector<char, 128> decode_buffer;
224   v8::internal::EmbeddedVector<char, kOutBufferSize> out_buffer;
225   StringBuilder out(out_buffer.start(), out_buffer.length());
226   byte* pc = begin;
227   disasm::Disassembler d(converter,
228                          disasm::Disassembler::kContinueOnUnimplementedOpcode);
229   RelocIterator* it = nullptr;
230   if (!code.is_null()) {
231     it = new RelocIterator(code);
232   } else {
233     // No relocation information when printing code stubs.
234   }
235   int constants = -1;  // no constants being decoded at the start
236 
237   while (pc < end) {
238     // First decode instruction so that we know its length.
239     byte* prev_pc = pc;
240     if (constants > 0) {
241       SNPrintF(decode_buffer,
242                "%08x       constant",
243                *reinterpret_cast<int32_t*>(pc));
244       constants--;
245       pc += 4;
246     } else {
247       int num_const = d.ConstantPoolSizeAt(pc);
248       if (num_const >= 0) {
249         SNPrintF(decode_buffer,
250                  "%08x       constant pool begin (num_const = %d)",
251                  *reinterpret_cast<int32_t*>(pc), num_const);
252         constants = num_const;
253         pc += 4;
254       } else if (it != nullptr && !it->done() &&
255                  it->rinfo()->pc() == reinterpret_cast<Address>(pc) &&
256                  it->rinfo()->rmode() == RelocInfo::INTERNAL_REFERENCE) {
257         // raw pointer embedded in code stream, e.g., jump table
258         byte* ptr = *reinterpret_cast<byte**>(pc);
259         SNPrintF(
260             decode_buffer, "%08" V8PRIxPTR "      jump table entry %4" PRIuS,
261             reinterpret_cast<intptr_t>(ptr), static_cast<size_t>(ptr - begin));
262         pc += sizeof(ptr);
263       } else {
264         decode_buffer[0] = '\0';
265         pc += d.InstructionDecode(decode_buffer, pc);
266       }
267     }
268 
269     // Collect RelocInfo for this instruction (prev_pc .. pc-1)
270     std::vector<const char*> comments;
271     std::vector<Address> pcs;
272     std::vector<RelocInfo::Mode> rmodes;
273     std::vector<intptr_t> datas;
274     if (it != nullptr) {
275       while (!it->done() && it->rinfo()->pc() < reinterpret_cast<Address>(pc)) {
276         if (RelocInfo::IsComment(it->rinfo()->rmode())) {
277           // For comments just collect the text.
278           comments.push_back(
279               reinterpret_cast<const char*>(it->rinfo()->data()));
280         } else {
281           // For other reloc info collect all data.
282           pcs.push_back(it->rinfo()->pc());
283           rmodes.push_back(it->rinfo()->rmode());
284           datas.push_back(it->rinfo()->data());
285         }
286         it->next();
287       }
288     }
289 
290     // Comments.
291     for (size_t i = 0; i < comments.size(); i++) {
292       out.AddFormatted("                  %s", comments[i]);
293       DumpBuffer(os, &out);
294     }
295 
296     // Instruction address and instruction offset.
297     if (FLAG_log_colour && reinterpret_cast<Address>(prev_pc) == current_pc) {
298       // If this is the given "current" pc, make it yellow and bold.
299       out.AddFormatted("\033[33;1m");
300     }
301     out.AddFormatted("%p  %4" V8PRIxPTRDIFF "  ", static_cast<void*>(prev_pc),
302                      prev_pc - begin);
303 
304     // Instruction.
305     out.AddFormatted("%s", decode_buffer.start());
306 
307     // Print all the reloc info for this instruction which are not comments.
308     for (size_t i = 0; i < pcs.size(); i++) {
309       // Put together the reloc info
310       const CodeReference& host = code;
311       Address constant_pool =
312           host.is_null() ? kNullAddress : host.constant_pool();
313       RelocInfo relocinfo(pcs[i], rmodes[i], datas[i], nullptr, constant_pool);
314 
315       bool first_reloc_info = (i == 0);
316       PrintRelocInfo(&out, isolate, ref_encoder, os, &relocinfo,
317                      first_reloc_info);
318     }
319 
320     // If this is a constant pool load and we haven't found any RelocInfo
321     // already, check if we can find some RelocInfo for the target address in
322     // the constant pool.
323     if (pcs.empty() && !code.is_null()) {
324       RelocInfo dummy_rinfo(reinterpret_cast<Address>(prev_pc), RelocInfo::NONE,
325                             0, nullptr);
326       if (dummy_rinfo.IsInConstantPool()) {
327         Address constant_pool_entry_address =
328             dummy_rinfo.constant_pool_entry_address();
329         RelocIterator reloc_it(code);
330         while (!reloc_it.done()) {
331           if (reloc_it.rinfo()->IsInConstantPool() &&
332               (reloc_it.rinfo()->constant_pool_entry_address() ==
333                constant_pool_entry_address)) {
334             PrintRelocInfo(&out, isolate, ref_encoder, os, reloc_it.rinfo());
335             break;
336           }
337           reloc_it.next();
338         }
339       }
340     }
341 
342     if (FLAG_log_colour && reinterpret_cast<Address>(prev_pc) == current_pc) {
343       out.AddFormatted("\033[m");
344     }
345 
346     DumpBuffer(os, &out);
347   }
348 
349   // Emit comments following the last instruction (if any).
350   if (it != nullptr) {
351     for ( ; !it->done(); it->next()) {
352       if (RelocInfo::IsComment(it->rinfo()->rmode())) {
353         out.AddFormatted("                  %s",
354                          reinterpret_cast<const char*>(it->rinfo()->data()));
355         DumpBuffer(os, &out);
356       }
357     }
358   }
359 
360   delete it;
361   return static_cast<int>(pc - begin);
362 }
363 
Decode(Isolate * isolate,std::ostream * os,byte * begin,byte * end,CodeReference code,Address current_pc)364 int Disassembler::Decode(Isolate* isolate, std::ostream* os, byte* begin,
365                          byte* end, CodeReference code, Address current_pc) {
366   V8NameConverter v8NameConverter(isolate, code);
367   bool decode_off_heap = isolate && InstructionStream::PcIsOffHeap(
368                                         isolate, bit_cast<Address>(begin));
369   CodeReference code_ref = decode_off_heap ? CodeReference() : code;
370   if (isolate) {
371     // We have an isolate, so support external reference names.
372     SealHandleScope shs(isolate);
373     DisallowHeapAllocation no_alloc;
374     ExternalReferenceEncoder ref_encoder(isolate);
375     return DecodeIt(isolate, &ref_encoder, os, code_ref, v8NameConverter, begin,
376                     end, current_pc);
377   } else {
378     // No isolate => isolate-independent code. No external reference names.
379     return DecodeIt(nullptr, nullptr, os, code_ref, v8NameConverter, begin, end,
380                     current_pc);
381   }
382 }
383 
384 #else  // ENABLE_DISASSEMBLER
385 
386 int Disassembler::Decode(Isolate* isolate, std::ostream* os, byte* begin,
387                          byte* end, CodeReference code, Address current_pc) {
388   return 0;
389 }
390 
391 #endif  // ENABLE_DISASSEMBLER
392 
393 }  // namespace internal
394 }  // namespace v8
395