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