diff options
author | Lorenzo Colitti <lorenzo@google.com> | 2019-09-26 22:14:16 +0900 |
---|---|---|
committer | Lorenzo Colitti <lorenzo@google.com> | 2019-10-14 21:54:55 +0900 |
commit | 983eb51423383190f80d31ed32fa38a112f5c04d (patch) | |
tree | c7bcfaf84645bffd2489e7ad482367d9a79bb595 | |
parent | 90cde57df677a9d81cee55954d1393c783e94382 (diff) | |
download | apf-983eb51423383190f80d31ed32fa38a112f5c04d.tar.gz |
Print disassembled code in apf_run.
apf_run is a great tool to debug APF programs, but it does not
currently print disassemled code. Fix this.
Before:
PC: 0 R0: 0 R1: 0
PC: 2 R0: 86dd R1: 0
PC: 7 R0: 86dd R1: 0
PC: 64 R0: 86dd R1: 0
PC: 141 R0: 86dd R1: 0
After:
R0 R1 PC Instruction
-------------------------------------------------
0 0 0: ldh r0, [12]
2 86dd 2: jlt r0, 0x600, drop
7 86dd 7: jne r0, 0x806, 64
40 86dd 64: jne r0, 0x800, 141
8d 86dd 141: jeq r0, 0x86dd, 161
...
Bug: 66928272
Test: manually compared output on a sample program
Change-Id: I1b93f70e0381d6e43d7f7d9785ab8c935baa1e4b
-rw-r--r-- | Android.bp | 6 | ||||
-rw-r--r-- | apf_disassembler.c | 187 | ||||
-rw-r--r-- | apf_interpreter.c | 9 | ||||
-rw-r--r-- | apf_run.c | 30 | ||||
-rw-r--r-- | disassembler.c | 217 | ||||
-rw-r--r-- | disassembler.h | 21 |
6 files changed, 272 insertions, 198 deletions
@@ -26,7 +26,10 @@ cc_library_static { cc_binary_host { name: "apf_disassembler", defaults: ["apf_defaults"], - srcs: ["apf_disassembler.c"], + srcs: [ + "apf_disassembler.c", + "disassembler.c", + ], } cc_binary_host { @@ -38,6 +41,7 @@ cc_binary_host { srcs: [ "apf_run.c", "apf_interpreter.c", + "disassembler.c", ], cflags: [ "-DAPF_TRACE_HOOK=apf_trace_hook", diff --git a/apf_disassembler.c b/apf_disassembler.c index 818de7b..a7401f3 100644 --- a/apf_disassembler.c +++ b/apf_disassembler.c @@ -17,52 +17,7 @@ #include <stdint.h> #include <stdio.h> -#include "apf.h" - -// If "c" is of an unsigned type, generate a compile warning that gets promoted to an error. -// This makes bounds checking simpler because ">= 0" can be avoided. Otherwise adding -// superfluous ">= 0" with unsigned expressions generates compile warnings. -#define ENFORCE_UNSIGNED(c) ((c)==(uint32_t)(c)) - -static void print_opcode(const char* opcode) { - printf("%-6s", opcode); -} - -// Mapping from opcode number to opcode name. -static const char* opcode_names [] = { - [LDB_OPCODE] = "ldb", - [LDH_OPCODE] = "ldh", - [LDW_OPCODE] = "ldw", - [LDBX_OPCODE] = "ldb", - [LDHX_OPCODE] = "ldh", - [LDWX_OPCODE] = "ldw", - [ADD_OPCODE] = "add", - [MUL_OPCODE] = "mul", - [DIV_OPCODE] = "div", - [AND_OPCODE] = "and", - [OR_OPCODE] = "or", - [SH_OPCODE] = "sh", - [LI_OPCODE] = "li", - [JMP_OPCODE] = "jmp", - [JEQ_OPCODE] = "jeq", - [JNE_OPCODE] = "jne", - [JGT_OPCODE] = "jgt", - [JLT_OPCODE] = "jlt", - [JSET_OPCODE] = "jset", - [JNEBS_OPCODE] = "jnebs", - [LDDW_OPCODE] = "lddw", - [STDW_OPCODE] = "stdw", -}; - -static void print_jump_target(uint32_t target, uint32_t program_len) { - if (target == program_len) { - printf("pass"); - } else if (target == program_len + 1) { - printf("drop"); - } else { - printf("%u", target); - } -} +#include "disassembler.h" // Disassembles an APF program. A hex dump of the program is supplied on stdin. // @@ -82,144 +37,6 @@ int main(void) { } for (uint32_t pc = 0; pc < program_len;) { - printf("%8u: ", pc); - const uint8_t bytecode = program[pc++]; - const uint32_t opcode = EXTRACT_OPCODE(bytecode); -#define PRINT_OPCODE() print_opcode(opcode_names[opcode]) - const uint32_t reg_num = EXTRACT_REGISTER(bytecode); - // All instructions have immediate fields, so load them now. - const uint32_t len_field = EXTRACT_IMM_LENGTH(bytecode); - uint32_t imm = 0; - int32_t signed_imm = 0; - if (len_field != 0) { - const uint32_t imm_len = 1 << (len_field - 1); - uint32_t i; - for (i = 0; i < imm_len && pc < program_len; i++) - imm = (imm << 8) | program[pc++]; - // Sign extend imm into signed_imm. - signed_imm = imm << ((4 - imm_len) * 8); - signed_imm >>= (4 - imm_len) * 8; - } - switch (opcode) { - case LDB_OPCODE: - case LDH_OPCODE: - case LDW_OPCODE: - PRINT_OPCODE(); - printf("r%d, [%u]", reg_num, imm); - break; - case LDBX_OPCODE: - case LDHX_OPCODE: - case LDWX_OPCODE: - PRINT_OPCODE(); - printf("r%d, [%u+r1]", reg_num, imm); - break; - case JMP_OPCODE: - PRINT_OPCODE(); - print_jump_target(pc + imm, program_len); - break; - case JEQ_OPCODE: - case JNE_OPCODE: - case JGT_OPCODE: - case JLT_OPCODE: - case JSET_OPCODE: - case JNEBS_OPCODE: { - PRINT_OPCODE(); - printf("r0, "); - // Load second immediate field. - uint32_t cmp_imm = 0; - if (reg_num == 1) { - printf("r1, "); - } else if (len_field == 0) { - printf("0, "); - } else { - uint32_t cmp_imm_len = 1 << (len_field - 1); - uint32_t i; - for (i = 0; i < cmp_imm_len && pc < program_len; i++) - cmp_imm = (cmp_imm << 8) | program[pc++]; - printf("0x%x, ", cmp_imm); - } - if (opcode == JNEBS_OPCODE) { - print_jump_target(pc + imm + cmp_imm, program_len); - printf(", "); - while (cmp_imm--) - printf("%02x", program[pc++]); - } else { - print_jump_target(pc + imm, program_len); - } - break; - } - case ADD_OPCODE: - case SH_OPCODE: - PRINT_OPCODE(); - if (reg_num) { - printf("r0, r1"); - } else { - printf("r0, %d", signed_imm); - } - break; - case MUL_OPCODE: - case DIV_OPCODE: - case AND_OPCODE: - case OR_OPCODE: - PRINT_OPCODE(); - if (reg_num) { - printf("r0, r1"); - } else { - printf("r0, %u", imm); - } - break; - case LI_OPCODE: - PRINT_OPCODE(); - printf("r%d, %d", reg_num, signed_imm); - break; - case EXT_OPCODE: - if ( -// If LDM_EXT_OPCODE is 0 and imm is compared with it, a compiler error will result, -// instead just enforce that imm is unsigned (so it's always greater or equal to 0). -#if LDM_EXT_OPCODE == 0 - ENFORCE_UNSIGNED(imm) && -#else - imm >= LDM_EXT_OPCODE && -#endif - imm < (LDM_EXT_OPCODE + MEMORY_ITEMS)) { - print_opcode("ldm"); - printf("r%d, m[%u]", reg_num, imm - LDM_EXT_OPCODE); - } else if (imm >= STM_EXT_OPCODE && imm < (STM_EXT_OPCODE + MEMORY_ITEMS)) { - print_opcode("stm"); - printf("r%d, m[%u]", reg_num, imm - STM_EXT_OPCODE); - } else switch (imm) { - case NOT_EXT_OPCODE: - print_opcode("not"); - printf("r%d", reg_num); - break; - case NEG_EXT_OPCODE: - print_opcode("neg"); - printf("r%d", reg_num); - break; - case SWAP_EXT_OPCODE: - print_opcode("swap"); - break; - case MOV_EXT_OPCODE: - print_opcode("mov"); - printf("r%d, r%d", reg_num, reg_num ^ 1); - break; - default: - printf("unknown_ext %u", imm); - break; - } - break; - case LDDW_OPCODE: - case STDW_OPCODE: - PRINT_OPCODE(); - printf("r%u, [%d+r%u]", reg_num, signed_imm, reg_num ^ 1); - break; - - // Unknown opcode - default: - printf("unknown %u", opcode); - break; - } - printf("\n"); + pc = apf_disassemble(program, program_len, pc); } - return 0; } diff --git a/apf_interpreter.c b/apf_interpreter.c index 9dae13f..c04a6e5 100644 --- a/apf_interpreter.c +++ b/apf_interpreter.c @@ -23,10 +23,11 @@ // User hook for interpreter debug tracing. #ifdef APF_TRACE_HOOK extern void APF_TRACE_HOOK(uint32_t pc, const uint32_t* regs, const uint8_t* program, - const uint8_t* packet, const uint32_t* memory); + uint32_t program_len, const uint8_t *packet, uint32_t packet_len, + const uint32_t* memory, uint32_t ram_len); #else -#define APF_TRACE_HOOK(pc, regs, program, packet, memory) \ - do { /* nop*/ \ +#define APF_TRACE_HOOK(pc, regs, program, program_len, packet, packet_len, memory, memory_len) \ + do { /* nop*/ \ } while (0) #endif @@ -86,7 +87,7 @@ int accept_packet(uint8_t* program, uint32_t program_len, uint32_t ram_len, uint32_t instructions_remaining = program_len; do { - APF_TRACE_HOOK(pc, registers, program, packet, memory); + APF_TRACE_HOOK(pc, registers, program, program_len, packet, packet_len, memory, ram_len); if (pc == program_len) { return PASS_PACKET; } else if (pc == (program_len + 1)) { @@ -27,8 +27,11 @@ #include <stdlib.h> #include <string.h> +#include "disassembler.h" #include "apf_interpreter.h" +#define __unused __attribute__((unused)) + enum { OPT_PROGRAM, OPT_PACKET, @@ -79,12 +82,24 @@ void print_hex(const uint8_t* input, int len) { } } +int tracing_enabled = 0; + +void maybe_print_tracing_header() { + if (!tracing_enabled) return; + + printf(" R0 R1 PC Instruction\n"); + printf("-------------------------------------------------\n"); + +} + // Process packet through APF filter void packet_handler(uint8_t* program, uint32_t program_len, uint32_t ram_len, const char* pkt, uint32_t filter_age) { uint8_t* packet; uint32_t packet_len = parse_hex(pkt, &packet); + maybe_print_tracing_header(); + int ret = accept_packet(program, program_len, ram_len, packet, packet_len, filter_age); printf("Packet %sed\n", ret ? "pass" : "dropp"); @@ -92,16 +107,13 @@ void packet_handler(uint8_t* program, uint32_t program_len, uint32_t ram_len, free(packet); } -int tracing_enabled = 0; -void apf_trace_hook(uint32_t pc, const uint32_t* regs, const uint8_t* program, - const uint8_t* packet, const uint32_t* memory) { +void apf_trace_hook(uint32_t pc, const uint32_t* regs, const uint8_t* program, uint32_t program_len, + const uint8_t* packet __unused, uint32_t packet_len __unused, + const uint32_t* memory __unused, uint32_t memory_len __unused) { if (!tracing_enabled) return; - // TODO: disassemble opcodes and dump memory locations - (void)program; - (void)packet; - (void)memory; - printf("PC:%8u R0:%8" PRIx32 " R1:%8" PRIx32 "\n", pc, regs[0], regs[1]); + printf("%8" PRIx32 " %8" PRIx32 " ", pc, regs[0], regs[1]); + apf_disassemble(program, program_len, pc); } // Process pcap file through APF filter and generate output files @@ -133,6 +145,8 @@ void file_handler(uint8_t* program, uint32_t program_len, uint32_t ram_len, cons } while ((apf_packet = pcap_next(pcap, &apf_header)) != NULL) { + maybe_print_tracing_header(); + int result = accept_packet(program, program_len, ram_len, apf_packet, apf_header.len, filter_age); diff --git a/disassembler.c b/disassembler.c new file mode 100644 index 0000000..3b66265 --- /dev/null +++ b/disassembler.c @@ -0,0 +1,217 @@ +/* + * Copyright 2016, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdint.h> +#include <stdio.h> + +#include "apf.h" + +// If "c" is of a signed type, generate a compile warning that gets promoted to an error. +// This makes bounds checking simpler because ">= 0" can be avoided. Otherwise adding +// superfluous ">= 0" with unsigned expressions generates compile warnings. +#define ENFORCE_UNSIGNED(c) ((c)==(uint32_t)(c)) + +static void print_opcode(const char* opcode) { + printf("%-6s", opcode); +} + +// Mapping from opcode number to opcode name. +static const char* opcode_names [] = { + [LDB_OPCODE] = "ldb", + [LDH_OPCODE] = "ldh", + [LDW_OPCODE] = "ldw", + [LDBX_OPCODE] = "ldbx", + [LDHX_OPCODE] = "ldhx", + [LDWX_OPCODE] = "ldwx", + [ADD_OPCODE] = "add", + [MUL_OPCODE] = "mul", + [DIV_OPCODE] = "div", + [AND_OPCODE] = "and", + [OR_OPCODE] = "or", + [SH_OPCODE] = "sh", + [LI_OPCODE] = "li", + [JMP_OPCODE] = "jmp", + [JEQ_OPCODE] = "jeq", + [JNE_OPCODE] = "jne", + [JGT_OPCODE] = "jgt", + [JLT_OPCODE] = "jlt", + [JSET_OPCODE] = "jset", + [JNEBS_OPCODE] = "jnebs", + [LDDW_OPCODE] = "lddw", + [STDW_OPCODE] = "stdw", +}; + +static void print_jump_target(uint32_t target, uint32_t program_len) { + if (target == program_len) { + printf("PASS"); + } else if (target == program_len + 1) { + printf("DROP"); + } else { + printf("%u", target); + } +} + +uint32_t apf_disassemble(const uint8_t* program, uint32_t program_len, uint32_t pc) { + printf("%8u: ", pc); + + if (pc == program_len) { + printf("PASS\n"); + return ++pc; + } + + if (pc == program_len + 1) { + printf("DROP\n"); + return ++pc; + } + + const uint8_t bytecode = program[pc++]; + const uint32_t opcode = EXTRACT_OPCODE(bytecode); +#define PRINT_OPCODE() print_opcode(opcode_names[opcode]) + const uint32_t reg_num = EXTRACT_REGISTER(bytecode); + // All instructions have immediate fields, so load them now. + const uint32_t len_field = EXTRACT_IMM_LENGTH(bytecode); + uint32_t imm = 0; + int32_t signed_imm = 0; + if (len_field != 0) { + const uint32_t imm_len = 1 << (len_field - 1); + for (uint32_t i = 0; i < imm_len && pc < program_len; i++) + imm = (imm << 8) | program[pc++]; + // Sign extend imm into signed_imm. + signed_imm = imm << ((4 - imm_len) * 8); + signed_imm >>= (4 - imm_len) * 8; + } + switch (opcode) { + case LDB_OPCODE: + case LDH_OPCODE: + case LDW_OPCODE: + PRINT_OPCODE(); + printf("r%d, [%u]", reg_num, imm); + break; + case LDBX_OPCODE: + case LDHX_OPCODE: + case LDWX_OPCODE: + PRINT_OPCODE(); + printf("r%d, [r1+%u]", reg_num, imm); + break; + case JMP_OPCODE: + PRINT_OPCODE(); + print_jump_target(pc + imm, program_len); + break; + case JEQ_OPCODE: + case JNE_OPCODE: + case JGT_OPCODE: + case JLT_OPCODE: + case JSET_OPCODE: + case JNEBS_OPCODE: { + PRINT_OPCODE(); + printf("r0, "); + // Load second immediate field. + uint32_t cmp_imm = 0; + if (reg_num == 1) { + printf("r1, "); + } else if (len_field == 0) { + printf("0, "); + } else { + uint32_t cmp_imm_len = 1 << (len_field - 1); + uint32_t i; + for (i = 0; i < cmp_imm_len && pc < program_len; i++) + cmp_imm = (cmp_imm << 8) | program[pc++]; + printf("0x%x, ", cmp_imm); + } + if (opcode == JNEBS_OPCODE) { + print_jump_target(pc + imm + cmp_imm, program_len); + printf(", "); + while (cmp_imm--) + printf("%02x", program[pc++]); + } else { + print_jump_target(pc + imm, program_len); + } + break; + } + case ADD_OPCODE: + case SH_OPCODE: + PRINT_OPCODE(); + if (reg_num) { + printf("r0, r1"); + } else { + printf("r0, %d", signed_imm); + } + break; + case MUL_OPCODE: + case DIV_OPCODE: + case AND_OPCODE: + case OR_OPCODE: + PRINT_OPCODE(); + if (reg_num) { + printf("r0, r1"); + } else { + printf("r0, %u", imm); + } + break; + case LI_OPCODE: + PRINT_OPCODE(); + printf("r%d, %d", reg_num, signed_imm); + break; + case EXT_OPCODE: + if ( +// If LDM_EXT_OPCODE is 0 and imm is compared with it, a compiler error will result, +// instead just enforce that imm is unsigned (so it's always greater or equal to 0). +#if LDM_EXT_OPCODE == 0 + ENFORCE_UNSIGNED(imm) && +#else + imm >= LDM_EXT_OPCODE && +#endif + imm < (LDM_EXT_OPCODE + MEMORY_ITEMS)) { + print_opcode("ldm"); + printf("r%d, m[%u]", reg_num, imm - LDM_EXT_OPCODE); + } else if (imm >= STM_EXT_OPCODE && imm < (STM_EXT_OPCODE + MEMORY_ITEMS)) { + print_opcode("stm"); + printf("r%d, m[%u]", reg_num, imm - STM_EXT_OPCODE); + } else switch (imm) { + case NOT_EXT_OPCODE: + print_opcode("not"); + printf("r%d", reg_num); + break; + case NEG_EXT_OPCODE: + print_opcode("neg"); + printf("r%d", reg_num); + break; + case SWAP_EXT_OPCODE: + print_opcode("swap"); + break; + case MOV_EXT_OPCODE: + print_opcode("mov"); + printf("r%d, r%d", reg_num, reg_num ^ 1); + break; + default: + printf("unknown_ext %u", imm); + break; + } + break; + case LDDW_OPCODE: + case STDW_OPCODE: + PRINT_OPCODE(); + printf("r%u, [r%u+%d]", reg_num, reg_num ^ 1, signed_imm); + break; + + // Unknown opcode + default: + printf("unknown %u", opcode); + break; + } + printf("\n"); + return pc; +} diff --git a/disassembler.h b/disassembler.h new file mode 100644 index 0000000..c13320e --- /dev/null +++ b/disassembler.h @@ -0,0 +1,21 @@ +/* + * Copyright 2019, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <stdint.h> + +uint32_t apf_disassemble(const uint8_t* program, uint32_t program_len, uint32_t pc); |