[rubygems/rubygems] Use a constant empty tar header to avoid extra allocations
[ruby.git] / misc / lldb_disasm.py
blobab759f009a84d7f15379b3f6e2847eb873ee8c11
1 #!/usr/bin/env python
2 #coding: utf-8
4 # Usage: run `command script import -r misc/lldb_disasm.py` on LLDB
7 # (lldb) p iseq
8 # (rb_iseq_t *) $147 = 0x0000000101068400
9 # (lldb) rbdisasm iseq
10 # 0000 putspecialobject( 3 )
11 # 0002 putnil
12 # 0003 defineclass( ID: 0x560b, (rb_iseq_t *)0x1010681d0, 2 )
13 # 0007 pop
14 # 0008 putspecialobject( 3 )
15 # 0010 putnil
16 # 0011 defineclass( ID: 0x56eb, (rb_iseq_t *)0x101063b58, 2 )
17 # 0015 leave
20 import lldb
21 import os
22 import shlex
24 class IseqDisassembler:
25 TS_VARIABLE = b'.'[0]
26 TS_CALLDATA = b'C'[0]
27 TS_CDHASH = b'H'[0]
28 TS_IC = b'K'[0]
29 TS_IVC = b'A'[0]
30 TS_ICVARC = b'J'[0]
31 TS_ID = b'I'[0]
32 TS_ISE = b'T'[0]
33 TS_ISEQ = b'S'[0]
34 TS_OFFSET = b'O'[0]
35 TS_VALUE = b'V'[0]
36 TS_LINDEX = b'L'[0]
37 TS_FUNCPTR = b'F'[0]
38 TS_NUM = b'N'[0]
39 TS_BUILTIN = b'R'[0]
41 ISEQ_OPT_DISPATCH = {
42 TS_BUILTIN: "(rb_builtin_function *)%0#x",
43 TS_NUM: "%d",
44 TS_FUNCPTR: "(rb_insn_func_t) %0#x",
45 TS_LINDEX: "%d",
46 TS_VALUE: "(VALUE)%0#x",
47 TS_OFFSET: "%d",
48 TS_ISEQ: "(rb_iseq_t *)%0#x",
49 TS_ISE: "(iseq_inline_storage_entry *)%0#x",
50 TS_ID: "ID: %0#x",
51 TS_IVC: "(struct iseq_inline_iv_cache_entry *)%0#x",
52 TS_ICVARC: "(struct iseq_inline_cvar_cache_entry *)%0#x",
53 TS_IC: "(struct iseq_inline_cache_entry *)%0#x",
54 TS_CDHASH: "CDHASH (VALUE)%0#x",
55 TS_CALLDATA: "(struct rb_call_data *)%0#x",
56 TS_VARIABLE: "VARIABLE %0#x",
59 def __init__(self, debugger, command, result, internal_dict):
60 self.debugger = debugger
61 self.command = command
62 self.result = result
63 self.internal_dict = internal_dict
65 self.target = debugger.GetSelectedTarget()
66 self.insns_address_table = self.__get_insns_address_table()
67 self.process = self.target.GetProcess()
68 self.thread = self.process.GetSelectedThread()
69 self.frame = self.thread.GetSelectedFrame()
70 self.addr2insn = self.build_addr2insn(self.target)
71 self.tChar = self.target.FindFirstType("char")
73 def disasm(self, val):
74 tRbISeq = self.target.FindFirstType("struct rb_iseq_struct").GetPointerType()
75 val = val.Cast(tRbISeq)
76 iseq_size = val.GetValueForExpressionPath("->body->iseq_size").GetValueAsUnsigned()
77 iseqs = val.GetValueForExpressionPath("->body->iseq_encoded")
78 idx = 0
79 print("PC IDX insn_name(operands) ", file=self.result)
80 while idx < iseq_size:
81 m = self.iseq_extract_values(self.debugger, self.target, self.process, self.result, iseqs, idx)
82 if m < 1:
83 print("Error decoding", file=self.result)
84 return
85 else:
86 idx += m
88 def build_addr2insn(self, target):
89 tIntPtr = target.FindFirstType("intptr_t")
90 size = target.EvaluateExpression('ruby_vminsn_type::VM_INSTRUCTION_SIZE').unsigned
91 sizeOfIntPtr = tIntPtr.GetByteSize()
92 addr_of_table = self.insns_address_table.GetStartAddress().GetLoadAddress(target)
94 my_dict = {}
96 for insn in range(size):
97 addr_in_table = addr_of_table + (insn * sizeOfIntPtr)
98 addr = lldb.SBAddress(addr_in_table, target)
99 machine_insn = target.CreateValueFromAddress("insn", addr, tIntPtr).GetValueAsUnsigned()
100 my_dict[machine_insn] = insn
102 return my_dict
104 def rb_vm_insn_addr2insn2(self, target, result, wanted_addr):
105 return self.addr2insn.get(wanted_addr)
107 def iseq_extract_values(self, debugger, target, process, result, iseqs, n):
108 tValueP = target.FindFirstType("VALUE")
109 sizeofValueP = tValueP.GetByteSize()
110 pc = iseqs.unsigned + (n * sizeofValueP)
111 insn = target.CreateValueFromAddress("i", lldb.SBAddress(pc, target), tValueP)
112 addr = insn.GetValueAsUnsigned()
113 orig_insn = self.rb_vm_insn_addr2insn2(target, result, addr)
115 name = self.insn_name(target, process, result, orig_insn)
116 length = self.insn_len(target, orig_insn)
117 op_str = self.insn_op_types(target, process, result, orig_insn)
118 op_types = bytes(op_str, 'utf-8')
120 if length != (len(op_types) + 1):
121 print("error decoding iseqs", file=result)
122 return -1
124 print("%0#14x %04d %s" % (pc, n, name), file=result, end="")
126 if length == 1:
127 print("", file=result)
128 return length
130 print("(", end="", file=result)
131 for idx, op_type in enumerate(op_types):
132 if idx == 0:
133 print(" ", end="", file=result)
134 else:
135 print(", ", end="", file=result)
137 opAddr = lldb.SBAddress(iseqs.unsigned + ((n + idx + 1) * sizeofValueP), target)
138 opValue = target.CreateValueFromAddress("op", opAddr, tValueP)
139 op = opValue.GetValueAsUnsigned()
140 print(self.ISEQ_OPT_DISPATCH.get(op_type) % op, end="", file=result)
142 print(" )", file=result)
143 return length
145 def insn_len(self, target, offset):
146 size_of_char = self.tChar.GetByteSize()
148 symbol = target.FindSymbols("rb_vm_insn_len_info")[0].GetSymbol()
149 section = symbol.GetStartAddress().GetSection()
150 addr_of_table = symbol.GetStartAddress().GetOffset()
152 error = lldb.SBError()
153 length = section.GetSectionData().GetUnsignedInt8(error, addr_of_table + (offset * size_of_char))
155 if error.Success():
156 return length
157 else:
158 print("error getting length: ", error)
160 def insn_op_types(self, target, process, result, insn):
161 tUShort = target.FindFirstType("unsigned short")
163 size_of_short = tUShort.GetByteSize()
164 size_of_char = self.tChar.GetByteSize()
166 symbol = target.FindSymbols("rb_vm_insn_op_offset")[0].GetSymbol()
167 section = symbol.GetStartAddress().GetSection()
168 addr_of_table = symbol.GetStartAddress().GetOffset()
170 addr_in_table = addr_of_table + (insn * size_of_short)
172 error = lldb.SBError()
173 offset = section.GetSectionData().GetUnsignedInt16(error, addr_in_table)
175 if not error.Success():
176 print("error getting op type offset: ", error)
178 symbol = target.FindSymbols("rb_vm_insn_op_base")[0].GetSymbol()
179 section = symbol.GetStartAddress().GetSection()
180 addr_of_table = symbol.GetStartAddress().GetOffset()
181 addr_in_name_table = addr_of_table + (offset * size_of_char)
183 error = lldb.SBError()
184 types = section.GetSectionData().GetString(error, addr_in_name_table)
185 if error.Success():
186 return types
187 else:
188 print("error getting op types: ", error)
190 def insn_name_table_offset(self, target, offset):
191 tUShort = target.FindFirstType("unsigned short")
192 size_of_short = tUShort.GetByteSize()
194 symbol = target.FindSymbols("rb_vm_insn_name_offset")[0].GetSymbol()
195 section = symbol.GetStartAddress().GetSection()
196 table_offset = symbol.GetStartAddress().GetOffset()
198 table_offset = table_offset + (offset * size_of_short)
200 error = lldb.SBError()
201 offset = section.GetSectionData().GetUnsignedInt16(error, table_offset)
203 if error.Success():
204 return offset
205 else:
206 print("error getting insn name table offset: ", error)
208 def insn_name(self, target, process, result, offset):
209 symbol = target.FindSymbols("rb_vm_insn_name_base")[0].GetSymbol()
210 section = symbol.GetStartAddress().GetSection()
211 addr_of_table = symbol.GetStartAddress().GetOffset()
213 name_table_offset = self.insn_name_table_offset(target, offset)
214 addr_in_name_table = addr_of_table + name_table_offset
216 error = lldb.SBError()
217 name = section.GetSectionData().GetString(error, addr_in_name_table)
219 if error.Success():
220 return name
221 else:
222 print('error getting insn name', error)
224 def __get_insns_address_table(self):
225 module = self.target.FindSymbols("vm_exec_core")[0].GetModule()
227 for symbol in module:
228 if "insns_address_table" in symbol.name and symbol.GetType() == lldb.eSymbolTypeData:
229 print(f"found symbol {symbol.name}")
230 return symbol
233 def disasm(debugger, command, result, internal_dict):
234 disassembler = IseqDisassembler(debugger, command, result, internal_dict)
235 frame = disassembler.frame
237 if frame.IsValid():
238 val = frame.EvaluateExpression(command)
239 else:
240 val = target.EvaluateExpression(command)
241 error = val.GetError()
242 if error.Fail():
243 print >> result, error
244 return
246 disassembler.disasm(val);
248 def __lldb_init_module(debugger, internal_dict):
249 debugger.HandleCommand("command script add -f lldb_disasm.disasm rbdisasm")
250 print("lldb Ruby disasm installed.")