Home | History | Annotate | Download | only in apf
      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 
     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 
     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
     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