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 an unsigned 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] = "ldb",
37 [LDHX_OPCODE] = "ldh",
38 [LDWX_OPCODE] = "ldw",
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
67 // Disassembles an APF program. A hex dump of the program is supplied on stdin.
68 //
69 // NOTE: This is a simple debugging tool not meant for shipping or production use. It is by no
70 // means hardened against malicious input and contains known vulnerabilities.
71 //
72 // Example usage:
73 // adb shell dumpsys wifi ipmanager | sed '/Last program:/,+1!d;/Last program:/d;s/[ ]*//' | out/host/linux-x86/bin/apf_disassembler
main(void)74 int main(void) {
75 uint32_t program_len = 0;
76 uint8_t program[10000];
77
78 // Read in hex program bytes
79 int byte;
80 while (scanf("%2x", &byte) == 1 && program_len < sizeof(program)) {
81 program[program_len++] = byte;
82 }
83
84 for (uint32_t pc = 0; pc < program_len;) {
85 printf("%8u: ", pc);
86 const uint8_t bytecode = program[pc++];
87 const uint32_t opcode = EXTRACT_OPCODE(bytecode);
88 #define PRINT_OPCODE() print_opcode(opcode_names[opcode])
89 const uint32_t reg_num = EXTRACT_REGISTER(bytecode);
90 // All instructions have immediate fields, so load them now.
91 const uint32_t len_field = EXTRACT_IMM_LENGTH(bytecode);
92 uint32_t imm = 0;
93 int32_t signed_imm = 0;
94 if (len_field != 0) {
95 const uint32_t imm_len = 1 << (len_field - 1);
96 uint32_t i;
97 for (i = 0; i < imm_len && pc < program_len; i++)
98 imm = (imm << 8) | program[pc++];
99 // Sign extend imm into signed_imm.
100 signed_imm = imm << ((4 - imm_len) * 8);
101 signed_imm >>= (4 - imm_len) * 8;
102 }
103 switch (opcode) {
104 case LDB_OPCODE:
105 case LDH_OPCODE:
106 case LDW_OPCODE:
107 PRINT_OPCODE();
108 printf("r%d, [%u]", reg_num, imm);
109 break;
110 case LDBX_OPCODE:
111 case LDHX_OPCODE:
112 case LDWX_OPCODE:
113 PRINT_OPCODE();
114 printf("r%d, [%u+r1]", reg_num, imm);
115 break;
116 case JMP_OPCODE:
117 PRINT_OPCODE();
118 print_jump_target(pc + imm, program_len);
119 break;
120 case JEQ_OPCODE:
121 case JNE_OPCODE:
122 case JGT_OPCODE:
123 case JLT_OPCODE:
124 case JSET_OPCODE:
125 case JNEBS_OPCODE: {
126 PRINT_OPCODE();
127 printf("r0, ");
128 // Load second immediate field.
129 uint32_t cmp_imm = 0;
130 if (reg_num == 1) {
131 printf("r1, ");
132 } else if (len_field == 0) {
133 printf("0, ");
134 } else {
135 uint32_t cmp_imm_len = 1 << (len_field - 1);
136 uint32_t i;
137 for (i = 0; i < cmp_imm_len && pc < program_len; i++)
138 cmp_imm = (cmp_imm << 8) | program[pc++];
139 printf("0x%x, ", cmp_imm);
140 }
141 if (opcode == JNEBS_OPCODE) {
142 print_jump_target(pc + imm + cmp_imm, program_len);
143 printf(", ");
144 while (cmp_imm--)
145 printf("%02x", program[pc++]);
146 } else {
147 print_jump_target(pc + imm, program_len);
148 }
149 break;
150 }
151 case ADD_OPCODE:
152 case SH_OPCODE:
153 PRINT_OPCODE();
154 if (reg_num) {
155 printf("r0, r1");
156 } else {
157 printf("r0, %d", signed_imm);
158 }
159 break;
160 case MUL_OPCODE:
161 case DIV_OPCODE:
162 case AND_OPCODE:
163 case OR_OPCODE:
164 PRINT_OPCODE();
165 if (reg_num) {
166 printf("r0, r1");
167 } else {
168 printf("r0, %u", imm);
169 }
170 break;
171 case LI_OPCODE:
172 PRINT_OPCODE();
173 printf("r%d, %d", reg_num, signed_imm);
174 break;
175 case EXT_OPCODE:
176 if (
177 // If LDM_EXT_OPCODE is 0 and imm is compared with it, a compiler error will result,
178 // instead just enforce that imm is unsigned (so it's always greater or equal to 0).
179 #if LDM_EXT_OPCODE == 0
180 ENFORCE_UNSIGNED(imm) &&
181 #else
182 imm >= LDM_EXT_OPCODE &&
183 #endif
184 imm < (LDM_EXT_OPCODE + MEMORY_ITEMS)) {
185 print_opcode("ldm");
186 printf("r%d, m[%u]", reg_num, imm - LDM_EXT_OPCODE);
187 } else if (imm >= STM_EXT_OPCODE && imm < (STM_EXT_OPCODE + MEMORY_ITEMS)) {
188 print_opcode("stm");
189 printf("r%d, m[%u]", reg_num, imm - STM_EXT_OPCODE);
190 } else switch (imm) {
191 case NOT_EXT_OPCODE:
192 print_opcode("not");
193 printf("r%d", reg_num);
194 break;
195 case NEG_EXT_OPCODE:
196 print_opcode("neg");
197 printf("r%d", reg_num);
198 break;
199 case SWAP_EXT_OPCODE:
200 print_opcode("swap");
201 break;
202 case MOV_EXT_OPCODE:
203 print_opcode("mov");
204 printf("r%d, r%d", reg_num, reg_num ^ 1);
205 break;
206 default:
207 printf("unknown_ext %u", imm);
208 break;
209 }
210 break;
211 case LDDW_OPCODE:
212 case STDW_OPCODE:
213 PRINT_OPCODE();
214 printf("r%u, [%d+r%u]", reg_num, signed_imm, reg_num ^ 1);
215 break;
216
217 // Unknown opcode
218 default:
219 printf("unknown %u", opcode);
220 break;
221 }
222 printf("\n");
223 }
224 return 0;
225 }
226