forked from ruby/ruby
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdisasm.rs
214 lines (175 loc) · 6.63 KB
/
disasm.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
use crate::core::*;
use crate::cruby::*;
use crate::yjit::yjit_enabled_p;
/// Primitive called in yjit.rb
/// Produce a string representing the disassembly for an ISEQ
#[no_mangle]
pub extern "C" fn rb_yjit_disasm_iseq(_ec: EcPtr, _ruby_self: VALUE, iseqw: VALUE) -> VALUE {
#[cfg(not(feature = "disasm"))]
{
let _ = iseqw;
return Qnil;
}
#[cfg(feature = "disasm")]
{
// TODO:
//if unsafe { CLASS_OF(iseqw) != rb_cISeq } {
// return Qnil;
//}
if !yjit_enabled_p() {
return Qnil;
}
// Get the iseq pointer from the wrapper
let iseq = unsafe { rb_iseqw_to_iseq(iseqw) };
let out_string = disasm_iseq(iseq);
return rust_str_to_ruby(&out_string);
}
}
#[cfg(feature = "disasm")]
fn disasm_iseq(iseq: IseqPtr) -> String {
let mut out = String::from("");
// Get a list of block versions generated for this iseq
let mut block_list = get_iseq_block_list(iseq);
// Get a list of codeblocks relevant to this iseq
let global_cb = crate::codegen::CodegenGlobals::get_inline_cb();
// Sort the blocks by increasing start addresses
block_list.sort_by(|a, b| {
use std::cmp::Ordering;
// Get the start addresses for each block
let addr_a = a.borrow().get_start_addr().unwrap().raw_ptr();
let addr_b = b.borrow().get_start_addr().unwrap().raw_ptr();
if addr_a < addr_b {
Ordering::Less
} else if addr_a == addr_b {
Ordering::Equal
} else {
Ordering::Greater
}
});
// Compute total code size in bytes for all blocks in the function
let mut total_code_size = 0;
for blockref in &block_list {
total_code_size += blockref.borrow().code_size();
}
// Initialize capstone
use capstone::prelude::*;
let cs = Capstone::new()
.x86()
.mode(arch::x86::ArchMode::Mode64)
.syntax(arch::x86::ArchSyntax::Intel)
.build()
.unwrap();
out.push_str(&format!("NUM BLOCK VERSIONS: {}\n", block_list.len()));
out.push_str(&format!(
"TOTAL INLINE CODE SIZE: {} bytes\n",
total_code_size
));
// For each block, sorted by increasing start address
for block_idx in 0..block_list.len() {
let block = block_list[block_idx].borrow();
let blockid = block.get_blockid();
let end_idx = block.get_end_idx();
let start_addr = block.get_start_addr().unwrap().raw_ptr();
let end_addr = block.get_end_addr().unwrap().raw_ptr();
let code_size = block.code_size();
// Write some info about the current block
let block_ident = format!(
"BLOCK {}/{}, ISEQ RANGE [{},{}), {} bytes ",
block_idx + 1,
block_list.len(),
blockid.idx,
end_idx,
code_size
);
out.push_str(&format!("== {:=<60}\n", block_ident));
// Disassemble the instructions
let code_slice = unsafe { std::slice::from_raw_parts(start_addr, code_size) };
let insns = cs.disasm_all(code_slice, start_addr as u64).unwrap();
// For each instruction in this block
for insn in insns.as_ref() {
// Comments for this block
if let Some(comment_list) = global_cb.comments_at(insn.address() as usize) {
for comment in comment_list {
out.push_str(&format!(" \x1b[1m# {}\x1b[0m\n", comment));
}
}
out.push_str(&format!(" {}\n", insn));
}
// If this is not the last block
if block_idx < block_list.len() - 1 {
// Compute the size of the gap between this block and the next
let next_block = block_list[block_idx + 1].borrow();
let next_start_addr = next_block.get_start_addr().unwrap().raw_ptr();
let gap_size = (next_start_addr as usize) - (end_addr as usize);
// Log the size of the gap between the blocks if nonzero
if gap_size > 0 {
out.push_str(&format!("... {} byte gap ...\n", gap_size));
}
}
}
return out;
}
/// Primitive called in yjit.rb
/// Produce a list of instructions compiled for an isew
#[no_mangle]
pub extern "C" fn rb_yjit_insns_compiled(_ec: EcPtr, _ruby_self: VALUE, iseqw: VALUE) -> VALUE {
{
// TODO:
//if unsafe { CLASS_OF(iseqw) != rb_cISeq } {
// return Qnil;
//}
if !yjit_enabled_p() {
return Qnil;
}
// Get the iseq pointer from the wrapper
let iseq = unsafe { rb_iseqw_to_iseq(iseqw) };
// Get the list of instructions compiled
let insn_vec = insns_compiled(iseq);
unsafe {
let insn_ary = rb_ary_new_capa((insn_vec.len() * 2) as i64);
// For each instruction compiled
for idx in 0..insn_vec.len() {
let op_name = &insn_vec[idx].0;
let insn_idx = insn_vec[idx].1;
let op_sym = rust_str_to_sym(&op_name);
// Store the instruction index and opcode symbol
rb_ary_store(
insn_ary,
(2 * idx + 0) as i64,
VALUE::fixnum_from_usize(insn_idx as usize),
);
rb_ary_store(insn_ary, (2 * idx + 1) as i64, op_sym);
}
insn_ary
}
}
}
fn insns_compiled(iseq: IseqPtr) -> Vec<(String, u32)> {
let mut insn_vec = Vec::new();
// Get a list of block versions generated for this iseq
let block_list = get_iseq_block_list(iseq);
// For each block associated with this iseq
for blockref in &block_list {
let block = blockref.borrow();
let start_idx = block.get_blockid().idx;
let end_idx = block.get_end_idx();
assert!(end_idx <= unsafe { get_iseq_encoded_size(iseq) });
// For each YARV instruction in the block
let mut insn_idx = start_idx;
while insn_idx < end_idx {
// Get the current pc and opcode
let pc = unsafe { rb_iseq_pc_at_idx(iseq, insn_idx) };
// try_into() call below is unfortunate. Maybe pick i32 instead of usize for opcodes.
let opcode: usize = unsafe { rb_iseq_opcode_at_pc(iseq, pc) }
.try_into()
.unwrap();
// Get the mnemonic for this opcode
let op_name = insn_name(opcode);
// Add the instruction to the list
insn_vec.push((op_name, insn_idx));
// Move to the next instruction
insn_idx += insn_len(opcode);
}
}
return insn_vec;
}