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