1 /*
2 * Copyright 2016, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <stdint.h>
18 #include <stdio.h>
19
20 #include "apf.h"
21
22 // If "c" is of a signed type, generate a compile warning that gets promoted to an error.
23 // This makes bounds checking simpler because ">= 0" can be avoided. Otherwise adding
24 // superfluous ">= 0" with unsigned expressions generates compile warnings.
25 #define ENFORCE_UNSIGNED(c) ((c)==(uint32_t)(c))
26
print_opcode(const char * opcode)27 static void print_opcode(const char* opcode) {
28 printf("%-6s", opcode);
29 }
30
31 // Mapping from opcode number to opcode name.
32 static const char* opcode_names [] = {
33 [LDB_OPCODE] = "ldb",
34 [LDH_OPCODE] = "ldh",
35 [LDW_OPCODE] = "ldw",
36 [LDBX_OPCODE] = "ldbx",
37 [LDHX_OPCODE] = "ldhx",
38 [LDWX_OPCODE] = "ldwx",
39 [ADD_OPCODE] = "add",
40 [MUL_OPCODE] = "mul",
41 [DIV_OPCODE] = "div",
42 [AND_OPCODE] = "and",
43 [OR_OPCODE] = "or",
44 [SH_OPCODE] = "sh",
45 [LI_OPCODE] = "li",
46 [JMP_OPCODE] = "jmp",
47 [JEQ_OPCODE] = "jeq",
48 [JNE_OPCODE] = "jne",
49 [JGT_OPCODE] = "jgt",
50 [JLT_OPCODE] = "jlt",
51 [JSET_OPCODE] = "jset",
52 [JNEBS_OPCODE] = "jnebs",
53 [LDDW_OPCODE] = "lddw",
54 [STDW_OPCODE] = "stdw",
55 };
56
print_jump_target(uint32_t target,uint32_t program_len)57 static void print_jump_target(uint32_t target, uint32_t program_len) {
58 if (target == program_len) {
59 printf("PASS");
60 } else if (target == program_len + 1) {
61 printf("DROP");
62 } else {
63 printf("%u", target);
64 }
65 }
66
apf_disassemble(const uint8_t * program,uint32_t program_len,uint32_t pc)67 uint32_t apf_disassemble(const uint8_t* program, uint32_t program_len, uint32_t pc) {
68 printf("%8u: ", pc);
69
70 if (pc == program_len) {
71 printf("PASS\n");
72 return ++pc;
73 }
74
75 if (pc == program_len + 1) {
76 printf("DROP\n");
77 return ++pc;
78 }
79
80 const uint8_t bytecode = program[pc++];
81 const uint32_t opcode = EXTRACT_OPCODE(bytecode);
82 #define PRINT_OPCODE() print_opcode(opcode_names[opcode])
83 const uint32_t reg_num = EXTRACT_REGISTER(bytecode);
84 // All instructions have immediate fields, so load them now.
85 const uint32_t len_field = EXTRACT_IMM_LENGTH(bytecode);
86 uint32_t imm = 0;
87 int32_t signed_imm = 0;
88 if (len_field != 0) {
89 const uint32_t imm_len = 1 << (len_field - 1);
90 for (uint32_t i = 0; i < imm_len && pc < program_len; i++)
91 imm = (imm << 8) | program[pc++];
92 // Sign extend imm into signed_imm.
93 signed_imm = imm << ((4 - imm_len) * 8);
94 signed_imm >>= (4 - imm_len) * 8;
95 }
96 switch (opcode) {
97 case LDB_OPCODE:
98 case LDH_OPCODE:
99 case LDW_OPCODE:
100 PRINT_OPCODE();
101 printf("r%d, [%u]", reg_num, imm);
102 break;
103 case LDBX_OPCODE:
104 case LDHX_OPCODE:
105 case LDWX_OPCODE:
106 PRINT_OPCODE();
107 printf("r%d, [r1+%u]", reg_num, imm);
108 break;
109 case JMP_OPCODE:
110 PRINT_OPCODE();
111 print_jump_target(pc + imm, program_len);
112 break;
113 case JEQ_OPCODE:
114 case JNE_OPCODE:
115 case JGT_OPCODE:
116 case JLT_OPCODE:
117 case JSET_OPCODE:
118 case JNEBS_OPCODE: {
119 PRINT_OPCODE();
120 printf("r0, ");
121 // Load second immediate field.
122 uint32_t cmp_imm = 0;
123 if (reg_num == 1) {
124 printf("r1, ");
125 } else if (len_field == 0) {
126 printf("0, ");
127 } else {
128 uint32_t cmp_imm_len = 1 << (len_field - 1);
129 uint32_t i;
130 for (i = 0; i < cmp_imm_len && pc < program_len; i++)
131 cmp_imm = (cmp_imm << 8) | program[pc++];
132 printf("0x%x, ", cmp_imm);
133 }
134 if (opcode == JNEBS_OPCODE) {
135 print_jump_target(pc + imm + cmp_imm, program_len);
136 printf(", ");
137 while (cmp_imm--)
138 printf("%02x", program[pc++]);
139 } else {
140 print_jump_target(pc + imm, program_len);
141 }
142 break;
143 }
144 case ADD_OPCODE:
145 case SH_OPCODE:
146 PRINT_OPCODE();
147 if (reg_num) {
148 printf("r0, r1");
149 } else {
150 printf("r0, %d", signed_imm);
151 }
152 break;
153 case MUL_OPCODE:
154 case DIV_OPCODE:
155 case AND_OPCODE:
156 case OR_OPCODE:
157 PRINT_OPCODE();
158 if (reg_num) {
159 printf("r0, r1");
160 } else {
161 printf("r0, %u", imm);
162 }
163 break;
164 case LI_OPCODE:
165 PRINT_OPCODE();
166 printf("r%d, %d", reg_num, signed_imm);
167 break;
168 case EXT_OPCODE:
169 if (
170 // If LDM_EXT_OPCODE is 0 and imm is compared with it, a compiler error will result,
171 // instead just enforce that imm is unsigned (so it's always greater or equal to 0).
172 #if LDM_EXT_OPCODE == 0
173 ENFORCE_UNSIGNED(imm) &&
174 #else
175 imm >= LDM_EXT_OPCODE &&
176 #endif
177 imm < (LDM_EXT_OPCODE + MEMORY_ITEMS)) {
178 print_opcode("ldm");
179 printf("r%d, m[%u]", reg_num, imm - LDM_EXT_OPCODE);
180 } else if (imm >= STM_EXT_OPCODE && imm < (STM_EXT_OPCODE + MEMORY_ITEMS)) {
181 print_opcode("stm");
182 printf("r%d, m[%u]", reg_num, imm - STM_EXT_OPCODE);
183 } else switch (imm) {
184 case NOT_EXT_OPCODE:
185 print_opcode("not");
186 printf("r%d", reg_num);
187 break;
188 case NEG_EXT_OPCODE:
189 print_opcode("neg");
190 printf("r%d", reg_num);
191 break;
192 case SWAP_EXT_OPCODE:
193 print_opcode("swap");
194 break;
195 case MOV_EXT_OPCODE:
196 print_opcode("mov");
197 printf("r%d, r%d", reg_num, reg_num ^ 1);
198 break;
199 default:
200 printf("unknown_ext %u", imm);
201 break;
202 }
203 break;
204 case LDDW_OPCODE:
205 case STDW_OPCODE:
206 PRINT_OPCODE();
207 printf("r%u, [r%u+%d]", reg_num, reg_num ^ 1, signed_imm);
208 break;
209
210 // Unknown opcode
211 default:
212 printf("unknown %u", opcode);
213 break;
214 }
215 printf("\n");
216 return pc;
217 }
218