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