• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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