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