/src/capstonenext/arch/BPF/BPFInstPrinter.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Capstone Disassembly Engine */ |
2 | | /* BPF Backend by david942j <david942j@gmail.com>, 2019 */ |
3 | | |
4 | | #include <capstone/platform.h> |
5 | | |
6 | | #include "BPFConstants.h" |
7 | | #include "BPFInstPrinter.h" |
8 | | #include "BPFMapping.h" |
9 | | |
10 | | static cs_bpf_op *expand_bpf_operands(cs_bpf *bpf) |
11 | 46.5k | { |
12 | | /* assert(bpf->op_count < 3); */ |
13 | 46.5k | return &bpf->operands[bpf->op_count++]; |
14 | 46.5k | } |
15 | | |
16 | | static void push_op_reg(cs_bpf *bpf, bpf_op_type val, uint8_t ac_mode) |
17 | 15.8k | { |
18 | 15.8k | cs_bpf_op *op = expand_bpf_operands(bpf); |
19 | | |
20 | 15.8k | op->type = BPF_OP_REG; |
21 | 15.8k | op->reg = val; |
22 | 15.8k | op->access = ac_mode; |
23 | 15.8k | } |
24 | | |
25 | | static void push_op_imm(cs_bpf *bpf, uint64_t val) |
26 | 14.5k | { |
27 | 14.5k | cs_bpf_op *op = expand_bpf_operands(bpf); |
28 | | |
29 | 14.5k | op->type = BPF_OP_IMM; |
30 | 14.5k | op->imm = val; |
31 | 14.5k | } |
32 | | |
33 | | static void push_op_off(cs_bpf *bpf, uint32_t val) |
34 | 6.83k | { |
35 | 6.83k | cs_bpf_op *op = expand_bpf_operands(bpf); |
36 | | |
37 | 6.83k | op->type = BPF_OP_OFF; |
38 | 6.83k | op->off = val; |
39 | 6.83k | } |
40 | | |
41 | | static void push_op_mem(cs_bpf *bpf, bpf_reg reg, uint32_t val) |
42 | 7.81k | { |
43 | 7.81k | cs_bpf_op *op = expand_bpf_operands(bpf); |
44 | | |
45 | 7.81k | op->type = BPF_OP_MEM; |
46 | 7.81k | op->mem.base = reg; |
47 | 7.81k | op->mem.disp = val; |
48 | 7.81k | } |
49 | | |
50 | | static void push_op_mmem(cs_bpf *bpf, uint32_t val) |
51 | 726 | { |
52 | 726 | cs_bpf_op *op = expand_bpf_operands(bpf); |
53 | | |
54 | 726 | op->type = BPF_OP_MMEM; |
55 | 726 | op->mmem = val; |
56 | 726 | } |
57 | | |
58 | | static void push_op_msh(cs_bpf *bpf, uint32_t val) |
59 | 518 | { |
60 | 518 | cs_bpf_op *op = expand_bpf_operands(bpf); |
61 | | |
62 | 518 | op->type = BPF_OP_MSH; |
63 | 518 | op->msh = val; |
64 | 518 | } |
65 | | |
66 | | static void push_op_ext(cs_bpf *bpf, bpf_ext_type val) |
67 | 326 | { |
68 | 326 | cs_bpf_op *op = expand_bpf_operands(bpf); |
69 | | |
70 | 326 | op->type = BPF_OP_EXT; |
71 | 326 | op->ext = val; |
72 | 326 | } |
73 | | |
74 | | static void convert_operands(MCInst *MI, cs_bpf *bpf) |
75 | 29.9k | { |
76 | 29.9k | unsigned opcode = MCInst_getOpcode(MI); |
77 | 29.9k | unsigned mc_op_count = MCInst_getNumOperands(MI); |
78 | 29.9k | MCOperand *op; |
79 | 29.9k | MCOperand *op2; |
80 | 29.9k | unsigned i; |
81 | | |
82 | 29.9k | bpf->op_count = 0; |
83 | 29.9k | if (BPF_CLASS(opcode) == BPF_CLASS_LD || BPF_CLASS(opcode) == BPF_CLASS_LDX) { |
84 | 9.32k | switch (BPF_MODE(opcode)) { |
85 | 2.63k | case BPF_MODE_IMM: |
86 | 2.63k | if (EBPF_MODE(MI->csh)) { |
87 | 923 | push_op_reg(bpf, MCOperand_getReg(MCInst_getOperand(MI, 0)), CS_AC_WRITE); |
88 | 923 | push_op_imm(bpf, MCOperand_getImm(MCInst_getOperand(MI, 1))); |
89 | 1.70k | } else { |
90 | 1.70k | push_op_imm(bpf, MCOperand_getImm(MCInst_getOperand(MI, 0))); |
91 | 1.70k | } |
92 | 2.63k | break; |
93 | 1.68k | case BPF_MODE_ABS: |
94 | 1.68k | op = MCInst_getOperand(MI, 0); |
95 | 1.68k | push_op_mem(bpf, BPF_REG_INVALID, (uint32_t)MCOperand_getImm(op)); |
96 | 1.68k | break; |
97 | 2.35k | case BPF_MODE_IND: |
98 | 2.35k | op = MCInst_getOperand(MI, 0); |
99 | 2.35k | op2 = MCInst_getOperand(MI, 1); |
100 | 2.35k | push_op_mem(bpf, MCOperand_getReg(op), (uint32_t)MCOperand_getImm(op2)); |
101 | 2.35k | break; |
102 | 1.80k | case BPF_MODE_MEM: |
103 | 1.80k | if (EBPF_MODE(MI->csh)) { |
104 | | /* ldx{w,h,b,dw} dst, [src+off] */ |
105 | 1.21k | push_op_reg(bpf, MCOperand_getReg(MCInst_getOperand(MI, 0)), CS_AC_WRITE); |
106 | 1.21k | op = MCInst_getOperand(MI, 1); |
107 | 1.21k | op2 = MCInst_getOperand(MI, 2); |
108 | 1.21k | push_op_mem(bpf, MCOperand_getReg(op), (uint32_t)MCOperand_getImm(op2)); |
109 | 1.21k | } |
110 | 588 | else { |
111 | 588 | push_op_mmem(bpf, (uint32_t)MCOperand_getImm(MCInst_getOperand(MI, 0))); |
112 | 588 | } |
113 | 1.80k | break; |
114 | 326 | case BPF_MODE_LEN: |
115 | 326 | push_op_ext(bpf, BPF_EXT_LEN); |
116 | 326 | break; |
117 | 518 | case BPF_MODE_MSH: |
118 | 518 | op = MCInst_getOperand(MI, 0); |
119 | 518 | push_op_msh(bpf, (uint32_t)MCOperand_getImm(op)); |
120 | 518 | break; |
121 | | /* case BPF_MODE_XADD: // not exists */ |
122 | 9.32k | } |
123 | 9.32k | return; |
124 | 9.32k | } |
125 | 20.6k | if (BPF_CLASS(opcode) == BPF_CLASS_ST || BPF_CLASS(opcode) == BPF_CLASS_STX) { |
126 | 2.69k | if (!EBPF_MODE(MI->csh)) { |
127 | | // cBPF has only one case - st* M[k] |
128 | 138 | push_op_mmem(bpf, (uint32_t)MCOperand_getImm(MCInst_getOperand(MI, 0))); |
129 | 138 | return; |
130 | 138 | } |
131 | | /* eBPF has two cases: |
132 | | * - st [dst + off], src |
133 | | * - xadd [dst + off], src |
134 | | * they have same form of operands. |
135 | | */ |
136 | 2.55k | op = MCInst_getOperand(MI, 0); |
137 | 2.55k | op2 = MCInst_getOperand(MI, 1); |
138 | 2.55k | push_op_mem(bpf, MCOperand_getReg(op), (uint32_t)MCOperand_getImm(op2)); |
139 | 2.55k | op = MCInst_getOperand(MI, 2); |
140 | 2.55k | if (MCOperand_isImm(op)) |
141 | 1.35k | push_op_imm(bpf, MCOperand_getImm(op)); |
142 | 1.20k | else if (MCOperand_isReg(op)) |
143 | 1.20k | push_op_reg(bpf, MCOperand_getReg(op), CS_AC_READ); |
144 | 2.55k | return; |
145 | 2.69k | } |
146 | | |
147 | 17.9k | if (BPF_CLASS(opcode) == BPF_CLASS_JMP) { |
148 | 20.7k | for (i = 0; i < mc_op_count; i++) { |
149 | 14.2k | op = MCInst_getOperand(MI, i); |
150 | 14.2k | if (MCOperand_isImm(op)) { |
151 | | /* decide the imm is BPF_OP_IMM or BPF_OP_OFF type here */ |
152 | | /* |
153 | | * 1. ja +off |
154 | | * 2. j {x,k}, +jt, +jf // cBPF |
155 | | * 3. j dst_reg, {src_reg, k}, +off // eBPF |
156 | | */ |
157 | 10.7k | if (BPF_OP(opcode) == BPF_JUMP_JA || |
158 | 10.7k | (!EBPF_MODE(MI->csh) && i >= 1) || |
159 | 10.7k | (EBPF_MODE(MI->csh) && i == 2)) |
160 | 6.83k | push_op_off(bpf, (uint32_t)MCOperand_getImm(op)); |
161 | 3.87k | else |
162 | 3.87k | push_op_imm(bpf, MCOperand_getImm(op)); |
163 | 10.7k | } |
164 | 3.54k | else if (MCOperand_isReg(op)) { |
165 | 3.54k | push_op_reg(bpf, MCOperand_getReg(op), CS_AC_READ); |
166 | 3.54k | } |
167 | 14.2k | } |
168 | 6.44k | return; |
169 | 6.44k | } |
170 | | |
171 | 11.4k | if (!EBPF_MODE(MI->csh)) { |
172 | | /* In cBPF mode, all registers in operands are accessed as read */ |
173 | 11.8k | for (i = 0; i < mc_op_count; i++) { |
174 | 5.60k | op = MCInst_getOperand(MI, i); |
175 | 5.60k | if (MCOperand_isImm(op)) |
176 | 2.42k | push_op_imm(bpf, MCOperand_getImm(op)); |
177 | 3.17k | else if (MCOperand_isReg(op)) |
178 | 3.17k | push_op_reg(bpf, MCOperand_getReg(op), CS_AC_READ); |
179 | 5.60k | } |
180 | 6.21k | return; |
181 | 6.21k | } |
182 | | |
183 | | /* remain cases are: eBPF mode && ALU */ |
184 | | /* if (BPF_CLASS(opcode) == BPF_CLASS_ALU || BPF_CLASS(opcode) == BPF_CLASS_ALU64) */ |
185 | | |
186 | | /* We have three types: |
187 | | * 1. {l,b}e dst // dst = byteswap(dst) |
188 | | * 2. neg dst // dst = -dst |
189 | | * 3. <op> dst, {src_reg, imm} // dst = dst <op> src |
190 | | * so we can simply check the number of operands, |
191 | | * exactly one operand means we are in case 1. and 2., |
192 | | * otherwise in case 3. |
193 | | */ |
194 | 5.27k | if (mc_op_count == 1) { |
195 | 540 | op = MCInst_getOperand(MI, 0); |
196 | 540 | push_op_reg(bpf, MCOperand_getReg(op), CS_AC_READ | CS_AC_WRITE); |
197 | 540 | } |
198 | 4.73k | else { // if (mc_op_count == 2) |
199 | 4.73k | op = MCInst_getOperand(MI, 0); |
200 | 4.73k | push_op_reg(bpf, MCOperand_getReg(op), CS_AC_READ | CS_AC_WRITE); |
201 | | |
202 | 4.73k | op = MCInst_getOperand(MI, 1); |
203 | 4.73k | if (MCOperand_isImm(op)) |
204 | 4.25k | push_op_imm(bpf, MCOperand_getImm(op)); |
205 | 476 | else if (MCOperand_isReg(op)) |
206 | 476 | push_op_reg(bpf, MCOperand_getReg(op), CS_AC_READ); |
207 | 4.73k | } |
208 | 5.27k | } |
209 | | |
210 | | static void print_operand(MCInst *MI, struct SStream *O, const cs_bpf_op *op) |
211 | 46.5k | { |
212 | 46.5k | switch (op->type) { |
213 | 0 | case BPF_OP_INVALID: |
214 | 0 | SStream_concat(O, "invalid"); |
215 | 0 | break; |
216 | 15.8k | case BPF_OP_REG: |
217 | 15.8k | SStream_concat(O, BPF_reg_name((csh)MI->csh, op->reg)); |
218 | 15.8k | break; |
219 | 14.5k | case BPF_OP_IMM: |
220 | 14.5k | SStream_concat(O, "0x%" PRIx64, op->imm); |
221 | 14.5k | break; |
222 | 6.83k | case BPF_OP_OFF: |
223 | 6.83k | SStream_concat(O, "+0x%x", op->off); |
224 | 6.83k | break; |
225 | 7.81k | case BPF_OP_MEM: |
226 | 7.81k | SStream_concat(O, "["); |
227 | 7.81k | if (op->mem.base != BPF_REG_INVALID) |
228 | 6.12k | SStream_concat(O, BPF_reg_name((csh)MI->csh, op->mem.base)); |
229 | 7.81k | if (op->mem.disp != 0) { |
230 | 7.73k | if (op->mem.base != BPF_REG_INVALID) |
231 | 6.08k | SStream_concat(O, "+"); |
232 | 7.73k | SStream_concat(O, "0x%x", op->mem.disp); |
233 | 7.73k | } |
234 | 7.81k | if (op->mem.base == BPF_REG_INVALID && op->mem.disp == 0) // special case |
235 | 42 | SStream_concat(O, "0x0"); |
236 | 7.81k | SStream_concat(O, "]"); |
237 | 7.81k | break; |
238 | 726 | case BPF_OP_MMEM: |
239 | 726 | SStream_concat(O, "m[0x%x]", op->mmem); |
240 | 726 | break; |
241 | 518 | case BPF_OP_MSH: |
242 | 518 | SStream_concat(O, "4*([0x%x]&0xf)", op->msh); |
243 | 518 | break; |
244 | 326 | case BPF_OP_EXT: |
245 | 326 | switch (op->ext) { |
246 | 326 | case BPF_EXT_LEN: |
247 | 326 | SStream_concat(O, "#len"); |
248 | 326 | break; |
249 | 326 | } |
250 | 326 | break; |
251 | 46.5k | } |
252 | 46.5k | } |
253 | | |
254 | | /* |
255 | | * 1. human readable mnemonic |
256 | | * 2. set pubOpcode (BPF_INSN_*) |
257 | | * 3. set detail->bpf.operands |
258 | | * */ |
259 | | void BPF_printInst(MCInst *MI, struct SStream *O, void *PrinterInfo) |
260 | 29.9k | { |
261 | 29.9k | int i; |
262 | 29.9k | cs_insn insn; |
263 | 29.9k | cs_bpf bpf; |
264 | | |
265 | 29.9k | insn.detail = NULL; |
266 | | /* set pubOpcode as instruction id */ |
267 | 29.9k | BPF_get_insn_id((cs_struct*)MI->csh, &insn, MCInst_getOpcode(MI)); |
268 | 29.9k | MCInst_setOpcodePub(MI, insn.id); |
269 | | |
270 | 29.9k | SStream_concat(O, BPF_insn_name((csh)MI->csh, insn.id)); |
271 | 29.9k | convert_operands(MI, &bpf); |
272 | 76.5k | for (i = 0; i < bpf.op_count; i++) { |
273 | 46.5k | if (i == 0) |
274 | 28.9k | SStream_concat(O, "\t"); |
275 | 17.6k | else |
276 | 17.6k | SStream_concat(O, ", "); |
277 | 46.5k | print_operand(MI, O, &bpf.operands[i]); |
278 | 46.5k | } |
279 | | |
280 | 29.9k | #ifndef CAPSTONE_DIET |
281 | 29.9k | if (MI->flat_insn->detail) { |
282 | 29.9k | MI->flat_insn->detail->bpf = bpf; |
283 | 29.9k | } |
284 | 29.9k | #endif |
285 | 29.9k | } |