4 # Usage: run `command script import -r misc/lldb_disasm.py` on LLDB
8 # (rb_iseq_t *) $147 = 0x0000000101068400
10 # 0000 putspecialobject( 3 )
12 # 0003 defineclass( ID: 0x560b, (rb_iseq_t *)0x1010681d0, 2 )
14 # 0008 putspecialobject( 3 )
16 # 0011 defineclass( ID: 0x56eb, (rb_iseq_t *)0x101063b58, 2 )
24 class IseqDisassembler
:
42 TS_BUILTIN
: "(rb_builtin_function *)%0#x",
44 TS_FUNCPTR
: "(rb_insn_func_t) %0#x",
46 TS_VALUE
: "(VALUE)%0#x",
48 TS_ISEQ
: "(rb_iseq_t *)%0#x",
49 TS_ISE
: "(iseq_inline_storage_entry *)%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
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")
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
)
83 print("Error decoding", file=self
.result
)
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
)
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
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
)
124 print("%0#14x %04d %s" % (pc
, n
, name
), file=result
, end
="")
127 print("", file=result
)
130 print("(", end
="", file=result
)
131 for idx
, op_type
in enumerate(op_types
):
133 print(" ", end
="", file=result
)
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
)
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
))
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
)
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
)
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
)
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}")
233 def disasm(debugger
, command
, result
, internal_dict
):
234 disassembler
= IseqDisassembler(debugger
, command
, result
, internal_dict
)
235 frame
= disassembler
.frame
238 val
= frame
.EvaluateExpression(command
)
240 val
= target
.EvaluateExpression(command
)
241 error
= val
.GetError()
243 print >> result
, error
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.")