Opdis 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/ChangeLog +2 -0
- data/LICENSE +674 -0
- data/LICENSE.README +8 -0
- data/README +101 -0
- data/examples/array_linear.rb +23 -0
- data/examples/bfd_entry.rb +24 -0
- data/examples/bfd_section.rb +24 -0
- data/examples/bfd_symbol.rb +27 -0
- data/examples/buf_linear.rb +25 -0
- data/examples/decoder.rb +45 -0
- data/examples/file_linear.rb +31 -0
- data/examples/libopcodes_options.rb +11 -0
- data/examples/resolver.rb +191 -0
- data/examples/supported_architectures.rb +11 -0
- data/examples/visited_handler.rb +61 -0
- data/examples/x86_decoder.rb +46 -0
- data/lib/Opdis.rb +123 -0
- data/module/Arch.c +364 -0
- data/module/Arch.h +37 -0
- data/module/Callbacks.c +266 -0
- data/module/Callbacks.h +43 -0
- data/module/Model.c +1275 -0
- data/module/Model.h +230 -0
- data/module/Opdis.c +850 -0
- data/module/Opdis.h +89 -0
- data/module/extconf.rb +126 -0
- data/module/rdoc_input/Callbacks.rb +143 -0
- data/module/rdoc_input/Model.rb +636 -0
- data/module/rdoc_input/Opdis.rb +253 -0
- data/module/ruby_compat.c +72 -0
- data/module/ruby_compat.h +25 -0
- data/tests/ut_opdis.rb +30 -0
- data/tests/ut_opdis_bfd.rb +556 -0
- metadata +109 -0
data/module/Model.h
ADDED
@@ -0,0 +1,230 @@
|
|
1
|
+
/* Model.h
|
2
|
+
* Copyright 2010 Thoughtgang <http://www.thoughtgang.org>
|
3
|
+
* Written by TG Community Developers <community@thoughtgang.org>
|
4
|
+
* Released under the GNU Public License, version 3.
|
5
|
+
* See http://www.gnu.org/licenses/gpl.txt for details.
|
6
|
+
*/
|
7
|
+
|
8
|
+
#ifndef OPDIS_RB_MODEL_H
|
9
|
+
#define OPDIS_RB_MODEL_H
|
10
|
+
|
11
|
+
#include <opdis/opdis.h>
|
12
|
+
#include <ruby.h>
|
13
|
+
|
14
|
+
#define GEN_ATTR_ASCII "ascii"
|
15
|
+
|
16
|
+
/* Instruction Class */
|
17
|
+
#define INSN_DECODE_INVALID_NAME "DECODE_INVALID"
|
18
|
+
#define INSN_DECODE_INVALID "invalid"
|
19
|
+
#define INSN_DECODE_BASIC_NAME "DECODE_BASIC"
|
20
|
+
#define INSN_DECODE_BASIC "basic"
|
21
|
+
#define INSN_DECODE_MNEM_NAME "DECODE_MNEMONIC"
|
22
|
+
#define INSN_DECODE_MNEM "mnemonic"
|
23
|
+
#define INSN_DECODE_OPS_NAME "DECODE_OPERANDS"
|
24
|
+
#define INSN_DECODE_OPS "operands"
|
25
|
+
#define INSN_DECODE_MNEMFLG_NAME "DECODE_MNEMONIC_FLAGS"
|
26
|
+
#define INSN_DECODE_MNEMFLG "mnemonic flags"
|
27
|
+
#define INSN_DECODE_OPFLG_NAME "DECODE_OPERAND_FLAGS"
|
28
|
+
#define INSN_DECODE_OPFLG "operand flags"
|
29
|
+
|
30
|
+
#define INSN_ATTR_STATUS "status"
|
31
|
+
#define INSN_ATTR_OFFSET "offset"
|
32
|
+
#define INSN_ATTR_VMA "vma"
|
33
|
+
#define INSN_ATTR_SIZE "size"
|
34
|
+
#define INSN_ATTR_BYTES "bytes"
|
35
|
+
#define INSN_ATTR_PREFIXES "prefixes"
|
36
|
+
#define INSN_ATTR_MNEMONIC "mnemonic"
|
37
|
+
#define INSN_ATTR_CATEGORY "category"
|
38
|
+
#define INSN_ATTR_ISA "isa"
|
39
|
+
#define INSN_ATTR_FLAGS "flags"
|
40
|
+
#define INSN_ATTR_FLAGS "flags"
|
41
|
+
#define INSN_ATTR_COMMENT "comment"
|
42
|
+
#define INSN_ATTR_OPERANDS "operands"
|
43
|
+
#define INSN_ATTR_TGT "target" // method
|
44
|
+
#define INSN_ATTR_DEST "dest" // method
|
45
|
+
#define INSN_ATTR_SRC "src" // method
|
46
|
+
#define INSN_ATTR_TGT_IDX "tgt_idx" // private
|
47
|
+
#define INSN_ATTR_DEST_IDX "dest_idx" // private
|
48
|
+
#define INSN_ATTR_SRC_IDX "src_idx" // private
|
49
|
+
|
50
|
+
#define INSN_ISA_GEN_NAME "ISA_GEN"
|
51
|
+
#define INSN_ISA_GEN "general"
|
52
|
+
#define INSN_ISA_FPU_NAME "ISA_FPU"
|
53
|
+
#define INSN_ISA_FPU "fpu"
|
54
|
+
#define INSN_ISA_GPU_NAME "ISA_GPU"
|
55
|
+
#define INSN_ISA_GPU "gpu"
|
56
|
+
#define INSN_ISA_SIMD_NAME "ISA_SIMD"
|
57
|
+
#define INSN_ISA_SIMD "simd"
|
58
|
+
#define INSN_ISA_VM_NAME "ISA_VM"
|
59
|
+
#define INSN_ISA_VM "vm"
|
60
|
+
|
61
|
+
#define INSN_CAT_CFLOW_NAME "CAT_CFLOW"
|
62
|
+
#define INSN_CAT_CFLOW "control-flow"
|
63
|
+
#define INSN_CAT_STACK_NAME "CAT_STACK"
|
64
|
+
#define INSN_CAT_STACK "stack"
|
65
|
+
#define INSN_CAT_LOST_NAME "CAT_LOADSTORE"
|
66
|
+
#define INSN_CAT_LOST "load/store"
|
67
|
+
#define INSN_CAT_TEST_NAME "CAT_TEST"
|
68
|
+
#define INSN_CAT_TEST "test"
|
69
|
+
#define INSN_CAT_MATH_NAME "CAT_MATH"
|
70
|
+
#define INSN_CAT_MATH "mathematic"
|
71
|
+
#define INSN_CAT_BIT_NAME "CAT_BIT"
|
72
|
+
#define INSN_CAT_BIT "bitwise"
|
73
|
+
#define INSN_CAT_IO_NAME "CAT_IO"
|
74
|
+
#define INSN_CAT_IO "i/o"
|
75
|
+
#define INSN_CAT_TRAP_NAME "CAT_TRAP"
|
76
|
+
#define INSN_CAT_TRAP "trap"
|
77
|
+
#define INSN_CAT_PRIV_NAME "CAT_PRIV"
|
78
|
+
#define INSN_CAT_PRIV "privileged"
|
79
|
+
#define INSN_CAT_NOP_NAME "CAT_NOP"
|
80
|
+
#define INSN_CAT_NOP "no-op"
|
81
|
+
|
82
|
+
#define INSN_FLAG_CALL_NAME "FLG_CALL"
|
83
|
+
#define INSN_FLAG_CALL "call"
|
84
|
+
#define INSN_FLAG_CALLCC_NAME "FLG_CALLCC"
|
85
|
+
#define INSN_FLAG_CALLCC "conditional call"
|
86
|
+
#define INSN_FLAG_JMP_NAME "JMP"
|
87
|
+
#define INSN_FLAG_JMP "jump"
|
88
|
+
#define INSN_FLAG_JMPCC_NAME "FLG_JMPCC"
|
89
|
+
#define INSN_FLAG_JMPCC "conditional jump"
|
90
|
+
#define INSN_FLAG_RET_NAME "FLG_RET"
|
91
|
+
#define INSN_FLAG_RET "return"
|
92
|
+
#define INSN_FLAG_PUSH_NAME "FLG_PUSH"
|
93
|
+
#define INSN_FLAG_PUSH "push"
|
94
|
+
#define INSN_FLAG_POP_NAME "FLG_POP"
|
95
|
+
#define INSN_FLAG_POP "pop"
|
96
|
+
#define INSN_FLAG_FRAME_NAME "FLG_FRAME"
|
97
|
+
#define INSN_FLAG_FRAME "enter frame"
|
98
|
+
#define INSN_FLAG_UNFRAME_NAME "FLG_UNFRAME"
|
99
|
+
#define INSN_FLAG_UNFRAME "exit frame"
|
100
|
+
#define INSN_FLAG_AND_NAME "FLG_AND"
|
101
|
+
#define INSN_FLAG_AND "bitwise and"
|
102
|
+
#define INSN_FLAG_OR_NAME "FLG_OR"
|
103
|
+
#define INSN_FLAG_OR "bitwise or"
|
104
|
+
#define INSN_FLAG_XOR_NAME "FLG_XOR"
|
105
|
+
#define INSN_FLAG_XOR "bitwise xor"
|
106
|
+
#define INSN_FLAG_NOT_NAME "FLG_NOT"
|
107
|
+
#define INSN_FLAG_NOT "bitwise not"
|
108
|
+
#define INSN_FLAG_LSL_NAME "FLG_LSL"
|
109
|
+
#define INSN_FLAG_LSL "logical shift left"
|
110
|
+
#define INSN_FLAG_LSR_NAME "FLG_LSR"
|
111
|
+
#define INSN_FLAG_LSR "logical shift right"
|
112
|
+
#define INSN_FLAG_ASL_NAME "FLG_ASL"
|
113
|
+
#define INSN_FLAG_ASL "arithmetic shift left"
|
114
|
+
#define INSN_FLAG_ASR_NAME "FLG_ASR"
|
115
|
+
#define INSN_FLAG_ASR "arithmetic shift right"
|
116
|
+
#define INSN_FLAG_ROL_NAME "FLG_ROL"
|
117
|
+
#define INSN_FLAG_ROL "rotate left"
|
118
|
+
#define INSN_FLAG_ROR_NAME "FLG_ROR"
|
119
|
+
#define INSN_FLAG_ROR "rotate right"
|
120
|
+
#define INSN_FLAG_RCL_NAME "FLG_RCL"
|
121
|
+
#define INSN_FLAG_RCL "rotate carry left"
|
122
|
+
#define INSN_FLAG_RCR_NAME "FLG_RCR"
|
123
|
+
#define INSN_FLAG_RCR "rotate carry right"
|
124
|
+
#define INSN_FLAG_OUT_NAME "FLG_OUT"
|
125
|
+
#define INSN_FLAG_OUT "input from port"
|
126
|
+
#define INSN_FLAG_IN_NAME "FLG_IN"
|
127
|
+
#define INSN_FLAG_IN "output to port"
|
128
|
+
|
129
|
+
/* Operand Base Class */
|
130
|
+
|
131
|
+
#define OP_ATTR_FLAGS "flags"
|
132
|
+
#define OP_ATTR_DATA_SZ "data_size"
|
133
|
+
|
134
|
+
#define OP_FLAG_R_NAME "FLG_READ"
|
135
|
+
#define OP_FLAG_R "r"
|
136
|
+
#define OP_FLAG_W_NAME "FLG_WRITE"
|
137
|
+
#define OP_FLAG_W "w"
|
138
|
+
#define OP_FLAG_X_NAME "FLG_EXEC"
|
139
|
+
#define OP_FLAG_X "x"
|
140
|
+
#define OP_FLAG_SIGNED_NAME "FLG_SIGNED"
|
141
|
+
#define OP_FLAG_SIGNED "signed"
|
142
|
+
#define OP_FLAG_ADDR_NAME "FLG_ADDR"
|
143
|
+
#define OP_FLAG_ADDR "address"
|
144
|
+
#define OP_FLAG_IND_NAME "FLG_INDIRECT"
|
145
|
+
#define OP_FLAG_IND "indirect_address"
|
146
|
+
|
147
|
+
/* Immediate Class */
|
148
|
+
#define IMM_ATTR_VAL "value"
|
149
|
+
#define IMM_ATTR_SIGNED "signed"
|
150
|
+
#define IMM_ATTR_UNSIGNED "unsigned"
|
151
|
+
#define IMM_ATTR_VMA "vma"
|
152
|
+
|
153
|
+
/* Address Expression Class */
|
154
|
+
|
155
|
+
#define ADDR_EXP_ATTR_SHIFT "shift"
|
156
|
+
#define ADDR_EXP_ATTR_SCALE "scale"
|
157
|
+
#define ADDR_EXP_ATTR_INDEX "index"
|
158
|
+
#define ADDR_EXP_ATTR_BASE "base"
|
159
|
+
#define ADDR_EXP_ATTR_DISP "displacement"
|
160
|
+
|
161
|
+
#define ADDR_EXP_SHIFT_LSL_NAME "SHIFT_LSL"
|
162
|
+
#define ADDR_EXP_SHIFT_LSL "lsl"
|
163
|
+
#define ADDR_EXP_SHIFT_LSR_NAME "SHIFT_LSR"
|
164
|
+
#define ADDR_EXP_SHIFT_LSR "lsr"
|
165
|
+
#define ADDR_EXP_SHIFT_ASL_NAME "SHIFT_ASL"
|
166
|
+
#define ADDR_EXP_SHIFT_ASL "asl"
|
167
|
+
#define ADDR_EXP_SHIFT_ROR_NAME "SHIFT_ROR"
|
168
|
+
#define ADDR_EXP_SHIFT_ROR "ror"
|
169
|
+
#define ADDR_EXP_SHIFT_RRX_NAME "SHIFT_RRX"
|
170
|
+
#define ADDR_EXP_SHIFT_RRX "rrx"
|
171
|
+
|
172
|
+
/* Absolute Address Class */
|
173
|
+
|
174
|
+
#define ABS_ADDR_ATTR_SEG "segment"
|
175
|
+
#define ABS_ADDR_ATTR_OFF "offset"
|
176
|
+
|
177
|
+
/* Register Class */
|
178
|
+
|
179
|
+
#define REG_ATTR_FLAGS "purpose"
|
180
|
+
#define REG_ATTR_ID "id"
|
181
|
+
#define REG_ATTR_SIZE "size"
|
182
|
+
#define REG_ATTR_NAME "name"
|
183
|
+
|
184
|
+
#define REG_FLAG_GEN_NAME "FLG_GEN"
|
185
|
+
#define REG_FLAG_GEN "general purpose"
|
186
|
+
#define REG_FLAG_FPU_NAME "FLG_FPU"
|
187
|
+
#define REG_FLAG_FPU "fpu"
|
188
|
+
#define REG_FLAG_GPU_NAME "FLG_GPU"
|
189
|
+
#define REG_FLAG_GPU "gpu"
|
190
|
+
#define REG_FLAG_SIMD_NAME "FLG_SIMD"
|
191
|
+
#define REG_FLAG_SIMD "simd"
|
192
|
+
#define REG_FLAG_TASK_NAME "FLG_TASK"
|
193
|
+
#define REG_FLAG_TASK "task_mgt"
|
194
|
+
#define REG_FLAG_MEM_NAME "FLG_MEM"
|
195
|
+
#define REG_FLAG_MEM "memory_mgt"
|
196
|
+
#define REG_FLAG_DBG_NAME "FLG_DEBUG"
|
197
|
+
#define REG_FLAG_DBG "debug"
|
198
|
+
#define REG_FLAG_PC_NAME "FLG_PC"
|
199
|
+
#define REG_FLAG_PC "pc"
|
200
|
+
#define REG_FLAG_CC_NAME "FLG_FLAGS"
|
201
|
+
#define REG_FLAG_CC "flags"
|
202
|
+
#define REG_FLAG_STACK_NAME "FLG_STACK"
|
203
|
+
#define REG_FLAG_STACK "stack"
|
204
|
+
#define REG_FLAG_FRAME_NAME "FLG_FRAME"
|
205
|
+
#define REG_FLAG_FRAME "stack_frame"
|
206
|
+
#define REG_FLAG_SEG_NAME "FLG_SEGMENT"
|
207
|
+
#define REG_FLAG_SEG "segment"
|
208
|
+
#define REG_FLAG_Z_NAME "FLG_ZERO"
|
209
|
+
#define REG_FLAG_Z "zero"
|
210
|
+
#define REG_FLAG_IN_NAME "FLG_IN"
|
211
|
+
#define REG_FLAG_IN "args in"
|
212
|
+
#define REG_FLAG_OUT_NAME "FLG_OUT"
|
213
|
+
#define REG_FLAG_OUT "args out"
|
214
|
+
#define REG_FLAG_LOCALS_NAME "FLG_LOCALS"
|
215
|
+
#define REG_FLAG_LOCALS "locals"
|
216
|
+
#define REG_FLAG_RET_NAME "FLG_RET"
|
217
|
+
#define REG_FLAG_RET "return"
|
218
|
+
|
219
|
+
void Opdis_initModel( VALUE modOpdis );
|
220
|
+
|
221
|
+
/* Allocate and fill a Ruby Opdis::Instruction object from an opdis_insn_t */
|
222
|
+
VALUE Opdis_insnFromC( const opdis_insn_t * insn );
|
223
|
+
|
224
|
+
/* Fill a Ruby Opdis::Instruction object from an opdis_insn_t */
|
225
|
+
int Opdis_insnFillFromC( const opdis_insn_t * insn, VALUE dest );
|
226
|
+
|
227
|
+
/* Fill an opdis_insn_t from a Ruby Opdis::Instruction object */
|
228
|
+
int Opdis_insnToC( VALUE insn, opdis_insn_t * c_insn );
|
229
|
+
|
230
|
+
#endif
|
data/module/Opdis.c
ADDED
@@ -0,0 +1,850 @@
|
|
1
|
+
/* Opdis.c
|
2
|
+
* Copyright 2010 Thoughtgang <http://www.thoughtgang.org>
|
3
|
+
* Written by TG Community Developers <community@thoughtgang.org>
|
4
|
+
* Released under the GNU Public License, version 3.
|
5
|
+
* See http://www.gnu.org/licenses/gpl.txt for details.
|
6
|
+
*/
|
7
|
+
|
8
|
+
#include <errno.h>
|
9
|
+
#include <string.h>
|
10
|
+
|
11
|
+
#include <ruby.h>
|
12
|
+
#include "ruby_compat.h"
|
13
|
+
|
14
|
+
#include <opdis/opdis.h>
|
15
|
+
|
16
|
+
#include "Opdis.h"
|
17
|
+
#include "Arch.h"
|
18
|
+
#include "Callbacks.h"
|
19
|
+
#include "Model.h"
|
20
|
+
|
21
|
+
#define IVAR(attr) "@" attr
|
22
|
+
#define SETTER(attr) attr "="
|
23
|
+
|
24
|
+
static VALUE symToSym, symRead, symCall, symSize, symPath;
|
25
|
+
static VALUE symDecode, symVisited, symResolve;
|
26
|
+
|
27
|
+
static VALUE clsDisasm, clsOutput;
|
28
|
+
|
29
|
+
static VALUE modOpdis;
|
30
|
+
|
31
|
+
static VALUE str_to_sym( const char * str ) {
|
32
|
+
VALUE var = rb_str_new_cstr(str);
|
33
|
+
return (var == Qnil) ? Qnil : rb_funcall(var, symToSym, 0);
|
34
|
+
}
|
35
|
+
|
36
|
+
/* BFD Support (requires BFD gem) */
|
37
|
+
static VALUE clsBfdTgt = Qnil;
|
38
|
+
static VALUE clsBfdSec = Qnil;
|
39
|
+
static VALUE clsBfdSym = Qnil;
|
40
|
+
|
41
|
+
#define GET_BFD_CLASS(cls,name) (cls = cls == Qnil ? path2class(name) : cls)
|
42
|
+
|
43
|
+
#define ALLOC_FIXED_INSN opdis_insn_alloc_fixed(128, 32, 16, 32)
|
44
|
+
|
45
|
+
|
46
|
+
/* ---------------------------------------------------------------------- */
|
47
|
+
/* Disasm Output Class */
|
48
|
+
/* This is basically a Hash of VMA => Instruction entries, with an @errors
|
49
|
+
* attribute that gets filled with error messages from the disassembler */
|
50
|
+
|
51
|
+
/* insn containing vma */
|
52
|
+
static VALUE cls_output_contain( VALUE instance, VALUE vma ) {
|
53
|
+
unsigned long long addr = NUM2ULL(vma);
|
54
|
+
/* NOTE: '32 bytes is the largest insn' may not be valid */
|
55
|
+
unsigned long long orig = addr, min = addr - 32;
|
56
|
+
int cont = 1;
|
57
|
+
|
58
|
+
/* iterate backwards from vma looking for insn containing vma */
|
59
|
+
for ( cont = 1; cont > 0 && addr >= min; addr -= 1 ) {
|
60
|
+
VALUE rb_size;
|
61
|
+
VALUE val = rb_hash_lookup2( instance, ULL2NUM(addr), Qfalse );
|
62
|
+
|
63
|
+
if ( val == Qfalse ) {
|
64
|
+
if ( addr == 0 ) {
|
65
|
+
cont = 0;
|
66
|
+
}
|
67
|
+
continue;
|
68
|
+
}
|
69
|
+
|
70
|
+
/* requested vma exists; return it */
|
71
|
+
if ( addr == orig ) {
|
72
|
+
return val;
|
73
|
+
}
|
74
|
+
|
75
|
+
/* does insn contain (insn.size + addr) requested vma? */
|
76
|
+
rb_size = rb_funcall(val, rb_intern(IVAR(INSN_ATTR_SIZE)), 0);
|
77
|
+
if ( rb_size == Qnil || addr + NUM2UINT(rb_size) < orig ) {
|
78
|
+
/* nope - no insn contains requested vma */
|
79
|
+
cont = 0;
|
80
|
+
} else {
|
81
|
+
/* yup - found it */
|
82
|
+
return val;
|
83
|
+
}
|
84
|
+
}
|
85
|
+
|
86
|
+
return Qfalse;
|
87
|
+
}
|
88
|
+
|
89
|
+
static VALUE cls_output_init( VALUE instance ) {
|
90
|
+
rb_iv_set(instance, IVAR(OUT_ATTR_ERRORS), rb_ary_new() );
|
91
|
+
return instance;
|
92
|
+
}
|
93
|
+
|
94
|
+
static void init_output_class( VALUE modOpdis ) {
|
95
|
+
clsOutput = rb_define_class_under(modOpdis, OPDIS_OUTPUT_CLASS_NAME,
|
96
|
+
rb_cHash);
|
97
|
+
rb_define_method(clsOutput, "initialize", cls_output_init, 0);
|
98
|
+
|
99
|
+
/* read-only attribute for error list */
|
100
|
+
rb_define_attr(clsOutput, OUT_ATTR_ERRORS, 1, 0);
|
101
|
+
|
102
|
+
/* setters */
|
103
|
+
rb_define_method(clsOutput, OUT_METHOD_CONTAIN, cls_output_contain, 1);
|
104
|
+
}
|
105
|
+
|
106
|
+
|
107
|
+
/* ---------------------------------------------------------------------- */
|
108
|
+
/* Disassembler Class */
|
109
|
+
|
110
|
+
/* list all recognized syntaxes */
|
111
|
+
static VALUE cls_disasm_syntaxes( VALUE class ) {
|
112
|
+
VALUE ary = rb_ary_new();
|
113
|
+
rb_ary_push( ary, rb_str_new_cstr(DIS_SYNTAX_ATT) );
|
114
|
+
rb_ary_push( ary, rb_str_new_cstr(DIS_SYNTAX_INTEL) );
|
115
|
+
return ary;
|
116
|
+
}
|
117
|
+
|
118
|
+
static int fill_disasm_def_array( const Opdis_disasm_def * def, void * arg ) {
|
119
|
+
VALUE * ary = (VALUE *) arg;
|
120
|
+
const Opdis_disasm_def * invalid = Opdis_disasm_invalid();
|
121
|
+
|
122
|
+
/* do not report the INVALID disasm def */
|
123
|
+
if ( def != invalid ) {
|
124
|
+
rb_ary_push( * ary, rb_str_new_cstr(def->name) );
|
125
|
+
}
|
126
|
+
|
127
|
+
return 1;
|
128
|
+
}
|
129
|
+
|
130
|
+
/* list all recognized architectures */
|
131
|
+
static VALUE cls_disasm_architectures( VALUE class ) {
|
132
|
+
VALUE ary = rb_ary_new();
|
133
|
+
|
134
|
+
Opdis_disasm_iter( fill_disasm_def_array, &ary );
|
135
|
+
|
136
|
+
return ary;
|
137
|
+
}
|
138
|
+
|
139
|
+
/* list all recognized disassembly algorithms */
|
140
|
+
static VALUE cls_disasm_strategies( VALUE class ) {
|
141
|
+
VALUE ary = rb_ary_new();
|
142
|
+
rb_ary_push( ary, rb_str_new_cstr(DIS_STRAT_SINGLE) );
|
143
|
+
rb_ary_push( ary, rb_str_new_cstr(DIS_STRAT_LINEAR) );
|
144
|
+
rb_ary_push( ary, rb_str_new_cstr(DIS_STRAT_CFLOW) );
|
145
|
+
rb_ary_push( ary, rb_str_new_cstr(DIS_STRAT_SYMBOL) );
|
146
|
+
rb_ary_push( ary, rb_str_new_cstr(DIS_STRAT_SECTION) );
|
147
|
+
rb_ary_push( ary, rb_str_new_cstr(DIS_STRAT_ENTRY) );
|
148
|
+
return ary;
|
149
|
+
}
|
150
|
+
|
151
|
+
/* local decoder callback: this calls the decode method in the object provided
|
152
|
+
* by the user. */
|
153
|
+
static int local_decoder( const opdis_insn_buf_t in, opdis_insn_t * out,
|
154
|
+
const opdis_byte_t * buf, opdis_off_t offset,
|
155
|
+
opdis_vma_t vma, opdis_off_t length, void * arg ) {
|
156
|
+
/* Build a hash containing the arguments passed to the decoder */
|
157
|
+
VALUE hash = Opdis_decoderHash( in, buf, offset, vma, length );
|
158
|
+
/* Create a Ruby Opdis::Instruction object based on the C object */
|
159
|
+
VALUE insn = Opdis_insnFromC(out);
|
160
|
+
VALUE obj = (VALUE) arg;
|
161
|
+
|
162
|
+
/* invoke decode method in Decoder object */
|
163
|
+
VALUE var = rb_funcall(obj, symDecode, 2, insn, hash);
|
164
|
+
|
165
|
+
/* Move info back to C domain */
|
166
|
+
Opdis_insnToC( insn, out );
|
167
|
+
|
168
|
+
return (Qfalse == var || Qnil == var) ? 0 : 1;
|
169
|
+
}
|
170
|
+
|
171
|
+
static VALUE cls_disasm_set_decoder(VALUE instance, VALUE obj) {
|
172
|
+
opdis_t opdis;
|
173
|
+
Data_Get_Struct(instance, opdis_info_t, opdis);
|
174
|
+
if (! opdis ) {
|
175
|
+
rb_raise( rb_eRuntimeError, "Invalid opdis_t" );
|
176
|
+
}
|
177
|
+
|
178
|
+
/* 'nil' causes opdis to revert to default decoder */
|
179
|
+
if ( Qnil == obj ) {
|
180
|
+
opdis_set_decoder( opdis, opdis_default_decoder, NULL );
|
181
|
+
return Qtrue;
|
182
|
+
}
|
183
|
+
|
184
|
+
/* objects without a 'decode' method cannot be decoders */
|
185
|
+
if (! rb_respond_to(obj, rb_intern(DECODER_METHOD)) ) {
|
186
|
+
return Qfalse;
|
187
|
+
}
|
188
|
+
|
189
|
+
opdis_set_decoder( opdis, local_decoder, (void *) obj );
|
190
|
+
rb_iv_set(instance, IVAR(DIS_ATTR_DECODER), obj );
|
191
|
+
|
192
|
+
return Qtrue;
|
193
|
+
}
|
194
|
+
|
195
|
+
/* local insn handler object: this invokes the visited? method in the handler
|
196
|
+
* object provided by the user. */
|
197
|
+
static int local_handler( const opdis_insn_t * i, void * arg ) {
|
198
|
+
VALUE obj = (VALUE) arg;
|
199
|
+
VALUE insn = Opdis_insnFromC(i);
|
200
|
+
|
201
|
+
/* invoke visited? method in Handler object */
|
202
|
+
VALUE var = rb_funcall(obj, symVisited, 1, insn);
|
203
|
+
|
204
|
+
/* True means already visited, so continue = 0 */
|
205
|
+
return (Qtrue == var) ? 0 : 1;
|
206
|
+
}
|
207
|
+
|
208
|
+
static VALUE cls_disasm_set_handler(VALUE instance, VALUE obj) {
|
209
|
+
opdis_t opdis;
|
210
|
+
Data_Get_Struct(instance, opdis_info_t, opdis);
|
211
|
+
if (! opdis ) {
|
212
|
+
rb_raise( rb_eRuntimeError, "Invalid opdis_t" );
|
213
|
+
}
|
214
|
+
|
215
|
+
/* nil causes opdis to revert to default decoder */
|
216
|
+
if ( Qnil == obj ) {
|
217
|
+
opdis_set_handler( opdis, opdis_default_handler, opdis );
|
218
|
+
return Qtrue;
|
219
|
+
}
|
220
|
+
|
221
|
+
/* objects without a visited? method cannot be handlers */
|
222
|
+
if (! rb_respond_to(obj, rb_intern(HANDLER_METHOD)) ) {
|
223
|
+
return Qfalse;
|
224
|
+
}
|
225
|
+
|
226
|
+
opdis_set_handler( opdis, local_handler, (void *) obj );
|
227
|
+
rb_iv_set(instance, IVAR(DIS_ATTR_HANDLER), obj );
|
228
|
+
|
229
|
+
return Qtrue;
|
230
|
+
}
|
231
|
+
|
232
|
+
/* local resolver callback: this invokes the ruby resolve method in the object
|
233
|
+
* provided by the user */
|
234
|
+
static opdis_vma_t local_resolver ( const opdis_insn_t * i, void * arg ) {
|
235
|
+
VALUE obj = (VALUE) arg;
|
236
|
+
VALUE insn = Opdis_insnFromC(i);
|
237
|
+
|
238
|
+
/* invoke resolve method in Resolver object */
|
239
|
+
VALUE vma = rb_funcall(obj, symResolve, 1, insn);
|
240
|
+
|
241
|
+
return (Qnil == vma) ? OPDIS_INVALID_ADDR : (opdis_vma_t) NUM2UINT(vma);
|
242
|
+
}
|
243
|
+
|
244
|
+
static VALUE cls_disasm_set_resolver(VALUE instance, VALUE obj) {
|
245
|
+
opdis_t opdis;
|
246
|
+
Data_Get_Struct(instance, opdis_info_t, opdis);
|
247
|
+
if (! opdis ) {
|
248
|
+
rb_raise( rb_eRuntimeError, "Invalid opdis_t" );
|
249
|
+
}
|
250
|
+
|
251
|
+
/* nil causes opdis to revert to default resolver */
|
252
|
+
if ( Qnil == obj ) {
|
253
|
+
opdis_set_resolver( opdis, opdis_default_resolver, NULL );
|
254
|
+
return Qtrue;
|
255
|
+
}
|
256
|
+
|
257
|
+
/* objects without a resolve method cannot be Resolvers */
|
258
|
+
if (! rb_respond_to(obj, rb_intern(RESOLVER_METHOD)) ) {
|
259
|
+
return Qfalse;
|
260
|
+
}
|
261
|
+
|
262
|
+
opdis_set_resolver( opdis, local_resolver, (void *) obj );
|
263
|
+
rb_iv_set(instance, IVAR(DIS_ATTR_RESOLVER), obj );
|
264
|
+
|
265
|
+
return Qtrue;
|
266
|
+
}
|
267
|
+
|
268
|
+
static VALUE cls_disasm_get_debug(VALUE instance) {
|
269
|
+
opdis_t opdis;
|
270
|
+
Data_Get_Struct(instance, opdis_info_t, opdis);
|
271
|
+
return (opdis && opdis->debug) ? Qtrue : Qfalse;
|
272
|
+
}
|
273
|
+
|
274
|
+
static VALUE cls_disasm_set_debug(VALUE instance, VALUE enabled) {
|
275
|
+
opdis_t opdis;
|
276
|
+
Data_Get_Struct(instance, opdis_info_t, opdis);
|
277
|
+
if (! opdis ) {
|
278
|
+
rb_raise( rb_eRuntimeError, "Invalid opdis_t" );
|
279
|
+
}
|
280
|
+
opdis->debug = (enabled == Qtrue) ? 1 : 0;
|
281
|
+
return Qtrue;
|
282
|
+
}
|
283
|
+
|
284
|
+
static VALUE cls_disasm_get_syntax(VALUE instance) {
|
285
|
+
opdis_t opdis;
|
286
|
+
VALUE str;
|
287
|
+
|
288
|
+
Data_Get_Struct(instance, opdis_info_t, opdis);
|
289
|
+
if (! opdis ) {
|
290
|
+
rb_raise( rb_eRuntimeError, "Invalid opdis_t" );
|
291
|
+
}
|
292
|
+
|
293
|
+
if ( opdis->disassembler == print_insn_i386_intel ) {
|
294
|
+
str = rb_str_new_cstr(DIS_SYNTAX_INTEL);
|
295
|
+
} else {
|
296
|
+
str = rb_str_new_cstr(DIS_SYNTAX_ATT);
|
297
|
+
}
|
298
|
+
|
299
|
+
return str;
|
300
|
+
}
|
301
|
+
|
302
|
+
static VALUE cls_disasm_set_syntax(VALUE instance, VALUE syntax) {
|
303
|
+
opdis_t opdis;
|
304
|
+
enum opdis_x86_syntax_t syn;
|
305
|
+
VALUE syntax_s = rb_any_to_s(syntax);
|
306
|
+
const char * str = StringValueCStr(syntax_s);
|
307
|
+
|
308
|
+
if (! strcmp(str, DIS_SYNTAX_INTEL) ) {
|
309
|
+
syn = opdis_x86_syntax_intel;
|
310
|
+
} else if (! strcmp(str, DIS_SYNTAX_ATT) ) {
|
311
|
+
syn = opdis_x86_syntax_att;
|
312
|
+
} else {
|
313
|
+
rb_raise(rb_eArgError, "Syntax must be 'intel' or 'att'");
|
314
|
+
}
|
315
|
+
|
316
|
+
Data_Get_Struct(instance, opdis_info_t, opdis);
|
317
|
+
if (! opdis ) {
|
318
|
+
rb_raise( rb_eRuntimeError, "Invalid opdis_t" );
|
319
|
+
}
|
320
|
+
opdis_set_x86_syntax(opdis, syn);
|
321
|
+
|
322
|
+
return Qtrue;
|
323
|
+
}
|
324
|
+
|
325
|
+
struct get_arch_name_arg {
|
326
|
+
opdis_t opdis;
|
327
|
+
const char * name;
|
328
|
+
};
|
329
|
+
|
330
|
+
static int get_name_for_arch( const Opdis_disasm_def * def, void * arg ) {
|
331
|
+
struct get_arch_name_arg * out = (struct get_arch_name_arg *) arg;
|
332
|
+
if ( def->arch == out->opdis->config.arch &&
|
333
|
+
def->mach == out->opdis->config.mach ) {
|
334
|
+
out->name = def->name;
|
335
|
+
return 0;
|
336
|
+
}
|
337
|
+
|
338
|
+
return 1;
|
339
|
+
}
|
340
|
+
|
341
|
+
|
342
|
+
static VALUE cls_disasm_get_arch(VALUE instance) {
|
343
|
+
opdis_t opdis;
|
344
|
+
struct get_arch_name_arg arg = { NULL, "unknown" };
|
345
|
+
|
346
|
+
Data_Get_Struct(instance, opdis_info_t, opdis);
|
347
|
+
if (! opdis ) {
|
348
|
+
rb_raise( rb_eRuntimeError, "Invalid opdis_t" );
|
349
|
+
}
|
350
|
+
|
351
|
+
Opdis_disasm_iter( get_name_for_arch, &arg );
|
352
|
+
|
353
|
+
return rb_str_new_cstr(arg.name);
|
354
|
+
}
|
355
|
+
|
356
|
+
static VALUE cls_disasm_set_arch(VALUE instance, VALUE arch) {
|
357
|
+
opdis_t opdis;
|
358
|
+
const Opdis_disasm_def * def;
|
359
|
+
const char * name = rb_string_value_cstr(&arch);
|
360
|
+
|
361
|
+
Data_Get_Struct(instance, opdis_info_t, opdis);
|
362
|
+
if (! opdis ) {
|
363
|
+
rb_raise( rb_eRuntimeError, "Invalid opdis_t" );
|
364
|
+
}
|
365
|
+
|
366
|
+
def = Opdis_disasm_for_name( name );
|
367
|
+
|
368
|
+
if ( def != Opdis_disasm_invalid() ) {
|
369
|
+
struct disassemble_info * info = &opdis->config;
|
370
|
+
info->application_data = def->fn;
|
371
|
+
info->arch = def->arch;
|
372
|
+
info->mach = def->mach;
|
373
|
+
|
374
|
+
rb_iv_set(instance, IVAR(DIS_ATTR_ARCH), arch);
|
375
|
+
}
|
376
|
+
|
377
|
+
return Qfalse;
|
378
|
+
}
|
379
|
+
|
380
|
+
static VALUE cls_disasm_get_opts(VALUE instance) {
|
381
|
+
opdis_t opdis;
|
382
|
+
Data_Get_Struct(instance, opdis_info_t, opdis);
|
383
|
+
return (opdis == NULL) ? Qnil :
|
384
|
+
rb_str_new_cstr(opdis->config.disassembler_options);
|
385
|
+
}
|
386
|
+
|
387
|
+
static VALUE cls_disasm_set_opts(VALUE instance, VALUE opts) {
|
388
|
+
char * str;
|
389
|
+
opdis_t opdis;
|
390
|
+
VALUE opts_s = opts;
|
391
|
+
Data_Get_Struct(instance, opdis_info_t, opdis);
|
392
|
+
if (! opdis ) {
|
393
|
+
rb_raise( rb_eRuntimeError, "Invalid opdis_t" );
|
394
|
+
}
|
395
|
+
|
396
|
+
str = StringValueCStr(opts_s);
|
397
|
+
opdis_set_disassembler_options( opdis, str );
|
398
|
+
return Qtrue;
|
399
|
+
}
|
400
|
+
|
401
|
+
/* get opdis options from an argument hash */
|
402
|
+
static void cls_disasm_handle_args( VALUE instance, VALUE hash ) {
|
403
|
+
VALUE var;
|
404
|
+
|
405
|
+
/* opdis callbacks */
|
406
|
+
var = rb_hash_lookup2(hash, str_to_sym(DIS_ARG_RESOLVER), Qfalse);
|
407
|
+
if ( Qfalse != var ) cls_disasm_set_resolver(instance, var);
|
408
|
+
|
409
|
+
var = rb_hash_lookup2(hash, str_to_sym(DIS_ARG_HANDLER), Qfalse);
|
410
|
+
if ( Qfalse != var ) cls_disasm_set_handler(instance, var);
|
411
|
+
|
412
|
+
var = rb_hash_lookup2(hash, str_to_sym(DIS_ARG_DECODER), Qfalse);
|
413
|
+
if ( Qfalse != var ) cls_disasm_set_decoder(instance, var);
|
414
|
+
|
415
|
+
/* opdis settings */
|
416
|
+
var = rb_hash_lookup2(hash, str_to_sym(DIS_ARG_SYNTAX), Qfalse);
|
417
|
+
if ( Qfalse != var ) cls_disasm_set_syntax(instance, var);
|
418
|
+
|
419
|
+
var = rb_hash_lookup2(hash, str_to_sym(DIS_ARG_DEBUG), Qfalse);
|
420
|
+
if ( Qfalse != var ) cls_disasm_set_debug(instance, var);
|
421
|
+
|
422
|
+
/* libopcodes settings */
|
423
|
+
var = rb_hash_lookup2(hash, str_to_sym(DIS_ARG_OPTIONS), Qfalse);
|
424
|
+
if ( Qfalse != var ) cls_disasm_set_opts(instance, var);
|
425
|
+
|
426
|
+
var = rb_hash_lookup2(hash, str_to_sym(DIS_ARG_ARCH), Qfalse);
|
427
|
+
if ( Qfalse != var ) cls_disasm_set_arch(instance, var);
|
428
|
+
}
|
429
|
+
|
430
|
+
struct DISPLAY_ARGS { VALUE output; VALUE block; };
|
431
|
+
|
432
|
+
/* local display handler: this adds instructions to a Disassembly object
|
433
|
+
* and invokes block if provided. */
|
434
|
+
static void local_display( const opdis_insn_t * i, void * arg ) {
|
435
|
+
struct DISPLAY_ARGS * args = (struct DISPLAY_ARGS *) arg;
|
436
|
+
VALUE insn = Opdis_insnFromC(i);
|
437
|
+
|
438
|
+
if ( insn == Qnil ) {
|
439
|
+
char buf[128];
|
440
|
+
VALUE errors = rb_iv_get(args->output, IVAR(OUT_ATTR_ERRORS));
|
441
|
+
snprintf( buf, 127-1, "%s: Unable to convert C insn to Ruby",
|
442
|
+
DIS_ERR_DECODE );
|
443
|
+
rb_ary_push( errors, rb_str_new_cstr(buf) );
|
444
|
+
return;
|
445
|
+
}
|
446
|
+
|
447
|
+
if ( Qnil != args->block ) {
|
448
|
+
rb_funcall(args->block, symCall, 1, insn);
|
449
|
+
}
|
450
|
+
|
451
|
+
rb_hash_aset( args->output, INT2NUM(i->vma), insn );
|
452
|
+
|
453
|
+
rb_thread_schedule();
|
454
|
+
}
|
455
|
+
|
456
|
+
/* local error handler: this appends errors to a ruby array in arg */
|
457
|
+
static void local_error( enum opdis_error_t error, const char * msg,
|
458
|
+
void * arg ) {
|
459
|
+
const char * type;
|
460
|
+
char buf[128] = {0};
|
461
|
+
VALUE errors = (VALUE) arg;
|
462
|
+
|
463
|
+
switch (error) {
|
464
|
+
case opdis_error_bounds: type = DIS_ERR_BOUNDS; break;
|
465
|
+
case opdis_error_invalid_insn: type = DIS_ERR_INVALID; break;
|
466
|
+
case opdis_error_decode_insn: type = DIS_ERR_DECODE; break;
|
467
|
+
case opdis_error_bfd: type = DIS_ERR_BFD; break;
|
468
|
+
case opdis_error_max_items: type = DIS_ERR_MAX; break;
|
469
|
+
case opdis_error_unknown:
|
470
|
+
default: type = DIS_ERR_UNK; break;
|
471
|
+
}
|
472
|
+
|
473
|
+
snprintf( buf, 128-1, "%s: %s", type, msg );
|
474
|
+
|
475
|
+
/* append error message to error list */
|
476
|
+
rb_ary_push( errors, rb_str_new_cstr(buf) );
|
477
|
+
}
|
478
|
+
|
479
|
+
static void config_buf_from_args( opdis_buf_t buf, VALUE hash ) {
|
480
|
+
VALUE var;
|
481
|
+
|
482
|
+
/* buffer vma */
|
483
|
+
var = rb_hash_lookup2(hash, str_to_sym(DIS_ARG_BUFVMA), Qfalse);
|
484
|
+
if ( Qfalse != var && Qnil != var ) buf->vma = NUM2UINT(var);
|
485
|
+
|
486
|
+
// TODO: other options?
|
487
|
+
}
|
488
|
+
|
489
|
+
static opdis_buf_t opdis_buf_for_target( VALUE tgt, VALUE hash ) {
|
490
|
+
unsigned char * buf = NULL;
|
491
|
+
unsigned int buf_len = 0;
|
492
|
+
opdis_buf_t obuf;
|
493
|
+
|
494
|
+
/* String object containing bytes */
|
495
|
+
if ( Qtrue == rb_obj_is_kind_of( tgt, rb_cString ) ) {
|
496
|
+
buf = (unsigned char*) RSTRING_PTR(tgt);
|
497
|
+
buf_len = RSTRING_LEN(tgt);
|
498
|
+
|
499
|
+
/* Array object containing bytes */
|
500
|
+
} else if ( Qtrue == rb_obj_is_kind_of( tgt, rb_cArray ) ) {
|
501
|
+
int i;
|
502
|
+
unsigned char * sbuf;
|
503
|
+
buf_len = RARRAY_LEN(tgt);
|
504
|
+
sbuf = alloca(buf_len);
|
505
|
+
for( i=0; i < buf_len; i++ ) {
|
506
|
+
VALUE val = rb_ary_entry( tgt, i );
|
507
|
+
sbuf[i] = (unsigned char) NUM2UINT(val);
|
508
|
+
}
|
509
|
+
|
510
|
+
buf = sbuf;
|
511
|
+
|
512
|
+
/* IO object containing bytes */
|
513
|
+
} else if ( Qtrue == rb_obj_is_kind_of( tgt, rb_cIO ) ) {
|
514
|
+
VALUE str = rb_funcall( tgt, symRead, 0 );
|
515
|
+
buf = (unsigned char*) RSTRING_PTR(str);
|
516
|
+
buf_len = RSTRING_LEN(str);
|
517
|
+
|
518
|
+
} else {
|
519
|
+
rb_raise(rb_eArgError, "Buffer must be a String, IO or Array");
|
520
|
+
}
|
521
|
+
|
522
|
+
if (! buf || ! buf_len ) {
|
523
|
+
rb_raise(rb_eArgError, "Cannot disassemble empty buffer");
|
524
|
+
}
|
525
|
+
|
526
|
+
obuf = opdis_buf_alloc( buf_len, 0 );
|
527
|
+
opdis_buf_fill( obuf, 0, buf, buf_len );
|
528
|
+
|
529
|
+
/* apply target-specific args (vma, etc) */
|
530
|
+
config_buf_from_args( obuf, hash );
|
531
|
+
|
532
|
+
return obuf;
|
533
|
+
}
|
534
|
+
|
535
|
+
struct OPDIS_TGT {bfd * abfd; asection * sec; asymbol * sym; opdis_buf_t buf;};
|
536
|
+
|
537
|
+
static void load_target( opdis_t opdis, VALUE tgt, VALUE hash,
|
538
|
+
struct OPDIS_TGT * out ) {
|
539
|
+
|
540
|
+
/* Ruby Bfd::Target object */
|
541
|
+
if ( Qnil != GET_BFD_CLASS(clsBfdTgt, BFD_TGT_PATH) &&
|
542
|
+
Qtrue == rb_obj_is_kind_of( tgt, clsBfdTgt ) ) {
|
543
|
+
Data_Get_Struct(tgt, bfd, out->abfd );
|
544
|
+
if (! out->abfd ) {
|
545
|
+
rb_raise( rb_eRuntimeError, "Invalid bfd" );
|
546
|
+
}
|
547
|
+
|
548
|
+
/* Ruby Bfd::Symbol object */
|
549
|
+
} else if ( Qnil != GET_BFD_CLASS(clsBfdSym, BFD_SYM_PATH) &&
|
550
|
+
Qtrue == rb_obj_is_kind_of( tgt, clsBfdSym ) ) {
|
551
|
+
Data_Get_Struct(tgt, asymbol, out->sym );
|
552
|
+
if (! out->sym ) {
|
553
|
+
rb_raise( rb_eRuntimeError, "Invalid bfd symbol" );
|
554
|
+
}
|
555
|
+
out->abfd = out->sym->the_bfd;
|
556
|
+
|
557
|
+
/* Ruby Bfd::Section object */
|
558
|
+
} else if ( Qnil != GET_BFD_CLASS(clsBfdSec, BFD_SEC_PATH) &&
|
559
|
+
Qtrue == rb_obj_is_kind_of( tgt, clsBfdSec ) ) {
|
560
|
+
Data_Get_Struct(tgt, asection, out->sec );
|
561
|
+
if (! out->sec ) {
|
562
|
+
rb_raise( rb_eRuntimeError, "Invalid bfd section" );
|
563
|
+
}
|
564
|
+
out->abfd = out->sec->owner;
|
565
|
+
|
566
|
+
/* Other non-Bfd Ruby object */
|
567
|
+
} else {
|
568
|
+
out->buf = opdis_buf_for_target( tgt, hash );
|
569
|
+
}
|
570
|
+
|
571
|
+
/* Set arch, etc based on BFD info */
|
572
|
+
if ( out->abfd ) {
|
573
|
+
opdis_config_from_bfd( opdis, out->abfd );
|
574
|
+
}
|
575
|
+
}
|
576
|
+
|
577
|
+
static void perform_disassembly( VALUE instance, opdis_t opdis, VALUE target,
|
578
|
+
VALUE hash ) {
|
579
|
+
VALUE var;
|
580
|
+
VALUE rb_vma = rb_hash_lookup2(hash, str_to_sym(DIS_ARG_VMA),
|
581
|
+
INT2NUM(0));
|
582
|
+
VALUE rb_len = rb_hash_lookup2(hash, str_to_sym(DIS_ARG_LEN),
|
583
|
+
INT2NUM(0));
|
584
|
+
opdis_vma_t vma = NUM2ULL(rb_vma);
|
585
|
+
opdis_off_t len = NUM2UINT(rb_len);
|
586
|
+
const char * strategy = DIS_STRAT_LINEAR;
|
587
|
+
struct OPDIS_TGT tgt = {0};
|
588
|
+
|
589
|
+
/* load target based on its Ruby object type */
|
590
|
+
load_target( opdis, target, hash, &tgt );
|
591
|
+
|
592
|
+
/* apply general args (syntax, arch, etc), overriding Bfd config */
|
593
|
+
cls_disasm_handle_args(instance, hash);
|
594
|
+
|
595
|
+
/* get disassembly algorithm to use */
|
596
|
+
var = rb_hash_lookup2(hash, str_to_sym(DIS_ARG_STRATEGY), Qfalse);
|
597
|
+
if ( Qfalse != var ) strategy = StringValueCStr(var);
|
598
|
+
|
599
|
+
rb_thread_schedule();
|
600
|
+
|
601
|
+
// Disable garbage collection
|
602
|
+
// TODO: INVESTIGATE why without this, ruby crashes!
|
603
|
+
rb_gc_disable();
|
604
|
+
|
605
|
+
/* Single instruction disassembly */
|
606
|
+
if (! strcmp( strategy, DIS_STRAT_SINGLE ) ) {
|
607
|
+
opdis_insn_t * insn = ALLOC_FIXED_INSN;
|
608
|
+
|
609
|
+
if ( tgt.abfd ) {
|
610
|
+
opdis_disasm_bfd_insn( opdis, tgt.abfd, vma, insn );
|
611
|
+
} else {
|
612
|
+
opdis_disasm_insn( opdis, tgt.buf, vma, insn );
|
613
|
+
}
|
614
|
+
|
615
|
+
/* invoke display function */
|
616
|
+
opdis->display( insn, opdis->display_arg );
|
617
|
+
|
618
|
+
opdis_insn_free(insn);
|
619
|
+
|
620
|
+
/* Linear disassembly */
|
621
|
+
} else if (! strcmp( strategy, DIS_STRAT_LINEAR ) ) {
|
622
|
+
if ( tgt.abfd ) {
|
623
|
+
opdis_disasm_bfd_linear( opdis, tgt.abfd, vma, len );
|
624
|
+
} else {
|
625
|
+
opdis_disasm_linear( opdis, tgt.buf, vma, len );
|
626
|
+
}
|
627
|
+
|
628
|
+
/* Control Flow disassembly */
|
629
|
+
} else if (! strcmp( strategy, DIS_STRAT_CFLOW ) ) {
|
630
|
+
if ( tgt.abfd ) {
|
631
|
+
opdis_disasm_bfd_cflow( opdis, tgt.abfd, vma );
|
632
|
+
} else {
|
633
|
+
opdis_disasm_cflow( opdis, tgt.buf, vma );
|
634
|
+
}
|
635
|
+
|
636
|
+
/* Control Flow disassembly of BFD symbol */
|
637
|
+
} else if (! strcmp( strategy, DIS_STRAT_SYMBOL ) ) {
|
638
|
+
if (! tgt.sym ) {
|
639
|
+
rb_raise(rb_eArgError, "Bfd::Symbol required");
|
640
|
+
}
|
641
|
+
opdis_disasm_bfd_symbol( opdis, tgt.sym );
|
642
|
+
|
643
|
+
/* Linear disassembly of BFD section */
|
644
|
+
} else if (! strcmp( strategy, DIS_STRAT_SECTION ) ) {
|
645
|
+
if (! tgt.sec ) {
|
646
|
+
rb_raise(rb_eArgError, "Bfd::Section required");
|
647
|
+
}
|
648
|
+
opdis_disasm_bfd_section( opdis, tgt.sec );
|
649
|
+
|
650
|
+
/* Control Flow disassembly of BFD entry point */
|
651
|
+
} else if (! strcmp( strategy, DIS_STRAT_ENTRY ) ) {
|
652
|
+
if (! tgt.abfd ) {
|
653
|
+
rb_raise(rb_eArgError, "Bfd::Target required");
|
654
|
+
}
|
655
|
+
opdis_disasm_bfd_entry( opdis, tgt.abfd );
|
656
|
+
} else {
|
657
|
+
if ( tgt.buf ) {
|
658
|
+
opdis_buf_free(tgt.buf);
|
659
|
+
}
|
660
|
+
rb_raise(rb_eArgError, "Unknown strategy '%s'", strategy);
|
661
|
+
}
|
662
|
+
|
663
|
+
if ( tgt.buf ) {
|
664
|
+
opdis_buf_free(tgt.buf);
|
665
|
+
}
|
666
|
+
|
667
|
+
// Enable garbage collection
|
668
|
+
// TODO: INVESTIGATE why ruby needs gc disable/enable
|
669
|
+
rb_gc_enable();
|
670
|
+
}
|
671
|
+
|
672
|
+
|
673
|
+
/* Disassembler strategies produce blocks */
|
674
|
+
static VALUE cls_disasm_disassemble(VALUE instance, VALUE tgt, VALUE hash ) {
|
675
|
+
opdis_t opdis, opdis_orig;
|
676
|
+
VALUE args[1] = {Qnil};
|
677
|
+
struct DISPLAY_ARGS display_args = { Qnil, Qnil };
|
678
|
+
|
679
|
+
/* Create duplicate opdis_t in order to be threadsafe */
|
680
|
+
Data_Get_Struct(instance, opdis_info_t, opdis_orig);
|
681
|
+
if (! opdis_orig ) {
|
682
|
+
rb_raise( rb_eRuntimeError, "Invalid opdis_t" );
|
683
|
+
}
|
684
|
+
opdis = opdis_dupe(opdis_orig);
|
685
|
+
|
686
|
+
/* yield to a block, if provided */
|
687
|
+
if ( rb_block_given_p() ) {
|
688
|
+
display_args.block = rb_block_proc();
|
689
|
+
}
|
690
|
+
|
691
|
+
display_args.output = rb_class_new_instance( 0, args, clsOutput );
|
692
|
+
|
693
|
+
opdis_set_display( opdis, local_display, &display_args );
|
694
|
+
|
695
|
+
opdis_set_error_reporter( opdis, local_error,
|
696
|
+
(void *) rb_iv_get(display_args.output,
|
697
|
+
IVAR(OUT_ATTR_ERRORS)) );
|
698
|
+
|
699
|
+
perform_disassembly( instance, opdis, tgt, hash );
|
700
|
+
|
701
|
+
opdis_term(opdis);
|
702
|
+
|
703
|
+
return display_args.output;
|
704
|
+
}
|
705
|
+
|
706
|
+
/* new: takes hash of arguments */
|
707
|
+
static VALUE cls_disasm_new(VALUE class, VALUE hash) {
|
708
|
+
VALUE instance;
|
709
|
+
VALUE argv[1] = { Qnil };
|
710
|
+
opdis_t opdis = opdis_init();
|
711
|
+
|
712
|
+
instance = Data_Wrap_Struct(class, NULL, opdis_term, opdis);
|
713
|
+
rb_obj_call_init(instance, 0, argv);
|
714
|
+
|
715
|
+
cls_disasm_handle_args(instance, hash);
|
716
|
+
|
717
|
+
return instance;
|
718
|
+
}
|
719
|
+
|
720
|
+
/* usage: writes a list of disassembler options to IO object */
|
721
|
+
static VALUE cls_disasm_usage(VALUE class, VALUE io) {
|
722
|
+
const char * filename;
|
723
|
+
VALUE path;
|
724
|
+
FILE * f;
|
725
|
+
|
726
|
+
if (! rb_respond_to(io, symPath) ) {
|
727
|
+
rb_raise( rb_eArgError, "Argument must respond to :path" );
|
728
|
+
}
|
729
|
+
|
730
|
+
/* get path from ruby File object */
|
731
|
+
path = rb_funcall(io, symPath, 0);
|
732
|
+
if ( Qnil == path ) {
|
733
|
+
rb_raise( rb_eArgError, "Object#path returned nil" );
|
734
|
+
}
|
735
|
+
|
736
|
+
filename = StringValueCStr(path);
|
737
|
+
f = fopen( filename, "w");
|
738
|
+
if (! f ) {
|
739
|
+
rb_raise( rb_eRuntimeError,
|
740
|
+
"Could not open %s object for write: %s",
|
741
|
+
filename, strerror(errno) );
|
742
|
+
}
|
743
|
+
|
744
|
+
/* invoke libopcodes to write usage into to FILE * */
|
745
|
+
disassembler_usage(f);
|
746
|
+
|
747
|
+
fclose(f);
|
748
|
+
|
749
|
+
return Qtrue;
|
750
|
+
}
|
751
|
+
|
752
|
+
static void define_disasm_constants() {
|
753
|
+
/* Error types */
|
754
|
+
rb_define_const(clsDisasm, DIS_ERR_BOUNDS_NAME,
|
755
|
+
rb_str_new_cstr(DIS_ERR_BOUNDS));
|
756
|
+
rb_define_const(clsDisasm, DIS_ERR_INVALID_NAME,
|
757
|
+
rb_str_new_cstr(DIS_ERR_INVALID));
|
758
|
+
rb_define_const(clsDisasm, DIS_ERR_DECODE_NAME,
|
759
|
+
rb_str_new_cstr(DIS_ERR_DECODE));
|
760
|
+
rb_define_const(clsDisasm, DIS_ERR_BFD_NAME,
|
761
|
+
rb_str_new_cstr(DIS_ERR_BFD));
|
762
|
+
rb_define_const(clsDisasm, DIS_ERR_MAX_NAME,
|
763
|
+
rb_str_new_cstr(DIS_ERR_MAX));
|
764
|
+
|
765
|
+
/* Disassembly algorithms */
|
766
|
+
rb_define_const(clsDisasm, DIS_STRAT_SINGLE_NAME,
|
767
|
+
rb_str_new_cstr(DIS_STRAT_SINGLE));
|
768
|
+
rb_define_const(clsDisasm, DIS_STRAT_LINEAR_NAME,
|
769
|
+
rb_str_new_cstr(DIS_STRAT_LINEAR));
|
770
|
+
rb_define_const(clsDisasm, DIS_STRAT_CFLOW_NAME,
|
771
|
+
rb_str_new_cstr(DIS_STRAT_CFLOW));
|
772
|
+
rb_define_const(clsDisasm, DIS_STRAT_SYMBOL_NAME,
|
773
|
+
rb_str_new_cstr(DIS_STRAT_SYMBOL));
|
774
|
+
rb_define_const(clsDisasm, DIS_STRAT_SECTION_NAME,
|
775
|
+
rb_str_new_cstr(DIS_STRAT_SECTION));
|
776
|
+
rb_define_const(clsDisasm, DIS_STRAT_ENTRY_NAME,
|
777
|
+
rb_str_new_cstr(DIS_STRAT_ENTRY));
|
778
|
+
|
779
|
+
/* Lists of symbolic constants */
|
780
|
+
rb_define_singleton_method(clsDisasm, DIS_CONST_STRATEGIES,
|
781
|
+
cls_disasm_strategies, 0);
|
782
|
+
rb_define_singleton_method(clsDisasm, DIS_CONST_ARCHES,
|
783
|
+
cls_disasm_architectures, 0);
|
784
|
+
rb_define_singleton_method(clsDisasm, DIS_CONST_SYNTAXES,
|
785
|
+
cls_disasm_syntaxes, 0);
|
786
|
+
}
|
787
|
+
|
788
|
+
static void init_disasm_class( VALUE modOpdis ) {
|
789
|
+
clsDisasm = rb_define_class_under(modOpdis, OPDIS_DISASM_CLASS_NAME,
|
790
|
+
rb_cObject);
|
791
|
+
rb_define_singleton_method(clsDisasm, "ext_new", cls_disasm_new, 1);
|
792
|
+
rb_define_singleton_method(clsDisasm, "ext_usage", cls_disasm_usage, 1);
|
793
|
+
|
794
|
+
/* read-only attributes */
|
795
|
+
rb_define_attr(clsDisasm, DIS_ATTR_DECODER, 1, 0);
|
796
|
+
rb_define_attr(clsDisasm, DIS_ATTR_HANDLER, 1, 0);
|
797
|
+
rb_define_attr(clsDisasm, DIS_ATTR_RESOLVER, 1, 0);
|
798
|
+
|
799
|
+
/* setters */
|
800
|
+
rb_define_method(clsDisasm, SETTER(DIS_ATTR_DECODER),
|
801
|
+
cls_disasm_set_decoder, 1);
|
802
|
+
rb_define_method(clsDisasm, SETTER(DIS_ATTR_HANDLER),
|
803
|
+
cls_disasm_set_handler, 1);
|
804
|
+
rb_define_method(clsDisasm, SETTER(DIS_ATTR_RESOLVER),
|
805
|
+
cls_disasm_set_resolver, 1);
|
806
|
+
rb_define_method(clsDisasm, SETTER(DIS_ATTR_DEBUG),
|
807
|
+
cls_disasm_set_debug, 1);
|
808
|
+
rb_define_method(clsDisasm, SETTER(DIS_ATTR_SYNTAX),
|
809
|
+
cls_disasm_set_syntax, 1);
|
810
|
+
rb_define_method(clsDisasm, SETTER(DIS_ATTR_ARCH),
|
811
|
+
cls_disasm_set_arch, 1);
|
812
|
+
rb_define_method(clsDisasm, SETTER(DIS_ATTR_OPTS),
|
813
|
+
cls_disasm_set_opts, 1);
|
814
|
+
|
815
|
+
/* getters */
|
816
|
+
rb_define_method(clsDisasm, DIS_ATTR_DEBUG, cls_disasm_get_debug, 0);
|
817
|
+
rb_define_method(clsDisasm, DIS_ATTR_SYNTAX, cls_disasm_get_syntax, 0);
|
818
|
+
rb_define_method(clsDisasm, DIS_ATTR_ARCH, cls_disasm_get_arch, 0);
|
819
|
+
rb_define_method(clsDisasm, DIS_ATTR_OPTS, cls_disasm_get_opts, 0);
|
820
|
+
|
821
|
+
/* methods */
|
822
|
+
rb_define_method(clsDisasm, DIS_METHOD_DISASM, cls_disasm_disassemble,
|
823
|
+
2);
|
824
|
+
|
825
|
+
define_disasm_constants();
|
826
|
+
}
|
827
|
+
|
828
|
+
/* ---------------------------------------------------------------------- */
|
829
|
+
/* Opdis Module */
|
830
|
+
|
831
|
+
void Init_OpdisExt() {
|
832
|
+
symToSym = rb_intern("to_sym");
|
833
|
+
symCall = rb_intern("call");
|
834
|
+
symRead = rb_intern("read");
|
835
|
+
symSize = rb_intern("size");
|
836
|
+
symPath = rb_intern("path");
|
837
|
+
|
838
|
+
symDecode = rb_intern(DECODER_METHOD);
|
839
|
+
symVisited = rb_intern(HANDLER_METHOD);
|
840
|
+
symResolve = rb_intern(RESOLVER_METHOD);
|
841
|
+
|
842
|
+
modOpdis = rb_define_module(OPDIS_MODULE_NAME);
|
843
|
+
|
844
|
+
init_disasm_class(modOpdis);
|
845
|
+
init_output_class(modOpdis);
|
846
|
+
|
847
|
+
Opdis_initCallbacks(modOpdis);
|
848
|
+
|
849
|
+
Opdis_initModel(modOpdis);
|
850
|
+
}
|