summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLorenzo Colitti <lorenzo@google.com>2019-09-26 22:14:16 +0900
committerLorenzo Colitti <lorenzo@google.com>2019-10-14 21:54:55 +0900
commit983eb51423383190f80d31ed32fa38a112f5c04d (patch)
treec7bcfaf84645bffd2489e7ad482367d9a79bb595
parent90cde57df677a9d81cee55954d1393c783e94382 (diff)
downloadapf-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.bp6
-rw-r--r--apf_disassembler.c187
-rw-r--r--apf_interpreter.c9
-rw-r--r--apf_run.c30
-rw-r--r--disassembler.c217
-rw-r--r--disassembler.h21
6 files changed, 272 insertions, 198 deletions
diff --git a/Android.bp b/Android.bp
index d2185b5..c326428 100644
--- a/Android.bp
+++ b/Android.bp
@@ -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)) {
diff --git a/apf_run.c b/apf_run.c
index c915b80..5cf0da0 100644
--- a/apf_run.c
+++ b/apf_run.c
@@ -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);