/* Copyright (c) 2013-2016 Jeffrey Pfau * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "cli-debugger.h" #ifdef USE_CLI_DEBUGGER #include "arm/debugger/memory-debugger.h" #include "arm/decoder.h" #include "core/core.h" #include "debugger/cli-debugger.h" static void _printStatus(struct CLIDebuggerSystem*); static void _disassembleArm(struct CLIDebugger*, struct CLIDebugVector*); static void _disassembleThumb(struct CLIDebugger*, struct CLIDebugVector*); static void _setBreakpointARM(struct CLIDebugger*, struct CLIDebugVector*); static void _setBreakpointThumb(struct CLIDebugger*, struct CLIDebugVector*); static void _writeRegister(struct CLIDebugger*, struct CLIDebugVector*); static void _disassembleMode(struct CLIDebugger*, struct CLIDebugVector*, enum ExecutionMode mode); static uint32_t _printLine(struct CLIDebugger* debugger, uint32_t address, enum ExecutionMode mode); static struct CLIDebuggerCommandSummary _armCommands[] = { { "b/a", _setBreakpointARM, CLIDVParse, "Set a software breakpoint as ARM" }, { "b/t", _setBreakpointThumb, CLIDVParse, "Set a software breakpoint as Thumb" }, { "break/a", _setBreakpointARM, CLIDVParse, "Set a software breakpoint as ARM" }, { "break/t", _setBreakpointThumb, CLIDVParse, "Set a software breakpoint as Thumb" }, { "dis/a", _disassembleArm, CLIDVParse, "Disassemble instructions as ARM" }, { "dis/t", _disassembleThumb, CLIDVParse, "Disassemble instructions as Thumb" }, { "disasm/a", _disassembleArm, CLIDVParse, "Disassemble instructions as ARM" }, { "disasm/t", _disassembleThumb, CLIDVParse, "Disassemble instructions as Thumb" }, { "disassemble/a", _disassembleArm, CLIDVParse, "Disassemble instructions as ARM" }, { "disassemble/t", _disassembleThumb, CLIDVParse, "Disassemble instructions as Thumb" }, { "w/r", _writeRegister, CLIDVParse, "Write a register" }, { 0, 0, 0, 0 } }; static inline void _printPSR(union PSR psr) { printf("%08X [%c%c%c%c%c%c%c]\n", psr.packed, psr.n ? 'N' : '-', psr.z ? 'Z' : '-', psr.c ? 'C' : '-', psr.v ? 'V' : '-', psr.i ? 'I' : '-', psr.f ? 'F' : '-', psr.t ? 'T' : '-'); } static void _disassemble(struct CLIDebuggerSystem* debugger, struct CLIDebugVector* dv) { struct ARMCore* cpu = debugger->p->d.core->cpu; _disassembleMode(debugger->p, dv, cpu->executionMode); } static void _disassembleArm(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { _disassembleMode(debugger, dv, MODE_ARM); } static void _disassembleThumb(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { _disassembleMode(debugger, dv, MODE_THUMB); } static void _disassembleMode(struct CLIDebugger* debugger, struct CLIDebugVector* dv, enum ExecutionMode mode) { struct ARMCore* cpu = debugger->d.core->cpu; uint32_t address; int size; int wordSize; if (mode == MODE_ARM) { wordSize = WORD_SIZE_ARM; } else { wordSize = WORD_SIZE_THUMB; } if (!dv || dv->type != CLIDV_INT_TYPE) { address = cpu->gprs[ARM_PC] - wordSize; } else { address = dv->intValue; dv = dv->next; } if (!dv || dv->type != CLIDV_INT_TYPE) { size = 1; } else { size = dv->intValue; dv = dv->next; // TODO: Check for excess args } int i; for (i = 0; i < size; ++i) { address += _printLine(debugger, address, mode); } } static inline uint32_t _printLine(struct CLIDebugger* debugger, uint32_t address, enum ExecutionMode mode) { char disassembly[48]; struct ARMInstructionInfo info; printf("%08X: ", address); if (mode == MODE_ARM) { uint32_t instruction = debugger->d.core->busRead32(debugger->d.core, address); ARMDecodeARM(instruction, &info); ARMDisassemble(&info, address + WORD_SIZE_ARM * 2, disassembly, sizeof(disassembly)); printf("%08X\t%s\n", instruction, disassembly); return WORD_SIZE_ARM; } else { struct ARMInstructionInfo info2; struct ARMInstructionInfo combined; uint16_t instruction = debugger->d.core->busRead16(debugger->d.core, address); uint16_t instruction2 = debugger->d.core->busRead16(debugger->d.core, address + WORD_SIZE_THUMB); ARMDecodeThumb(instruction, &info); ARMDecodeThumb(instruction2, &info2); if (ARMDecodeThumbCombine(&info, &info2, &combined)) { ARMDisassemble(&combined, address + WORD_SIZE_THUMB * 2, disassembly, sizeof(disassembly)); printf("%04X %04X\t%s\n", instruction, instruction2, disassembly); return WORD_SIZE_THUMB * 2; } else { ARMDisassemble(&info, address + WORD_SIZE_THUMB * 2, disassembly, sizeof(disassembly)); printf("%04X \t%s\n", instruction, disassembly); return WORD_SIZE_THUMB; } } } static void _printStatus(struct CLIDebuggerSystem* debugger) { struct ARMCore* cpu = debugger->p->d.core->cpu; int r; for (r = 0; r < 4; ++r) { printf("%08X %08X %08X %08X\n", cpu->gprs[r << 2], cpu->gprs[(r << 2) + 1], cpu->gprs[(r << 2) + 2], cpu->gprs[(r << 2) + 3]); } _printPSR(cpu->cpsr); int instructionLength; enum ExecutionMode mode = cpu->cpsr.t; if (mode == MODE_ARM) { instructionLength = WORD_SIZE_ARM; } else { instructionLength = WORD_SIZE_THUMB; } _printLine(debugger->p, cpu->gprs[ARM_PC] - instructionLength, mode); } static void _writeRegister(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { struct ARMCore* cpu = debugger->d.core->cpu; if (!dv || dv->type != CLIDV_INT_TYPE) { printf("%s\n", ERROR_MISSING_ARGS); return; } if (!dv->next || dv->next->type != CLIDV_INT_TYPE) { printf("%s\n", ERROR_MISSING_ARGS); return; } uint32_t regid = dv->intValue; uint32_t value = dv->next->intValue; if (regid >= ARM_PC) { return; } cpu->gprs[regid] = value; } static void _setBreakpointARM(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { if (!dv || dv->type != CLIDV_INT_TYPE) { printf("%s\n", ERROR_MISSING_ARGS); return; } uint32_t address = dv->intValue; ARMDebuggerSetSoftwareBreakpoint(&debugger->d, address, MODE_ARM); } static void _setBreakpointThumb(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { if (!dv || dv->type != CLIDV_INT_TYPE) { printf("%s\n", ERROR_MISSING_ARGS); return; } uint32_t address = dv->intValue; ARMDebuggerSetSoftwareBreakpoint(&debugger->d, address, MODE_THUMB); } static uint32_t _lookupPlatformIdentifier(struct CLIDebuggerSystem* debugger, const char* name, struct CLIDebugVector* dv) { struct ARMCore* cpu = debugger->p->d.core->cpu; if (strcmp(name, "sp") == 0) { return cpu->gprs[ARM_SP]; } if (strcmp(name, "lr") == 0) { return cpu->gprs[ARM_LR]; } if (strcmp(name, "pc") == 0) { return cpu->gprs[ARM_PC]; } if (strcmp(name, "cpsr") == 0) { return cpu->cpsr.packed; } // TODO: test if mode has SPSR if (strcmp(name, "spsr") == 0) { return cpu->spsr.packed; } if (name[0] == 'r' && name[1] >= '0' && name[1] <= '9') { int reg = atoi(&name[1]); if (reg < 16) { return cpu->gprs[reg]; } } dv->type = CLIDV_ERROR_TYPE; return 0; } void ARMCLIDebuggerCreate(struct CLIDebuggerSystem* debugger) { debugger->printStatus = _printStatus; debugger->disassemble = _disassemble; debugger->lookupPlatformIdentifier = _lookupPlatformIdentifier; debugger->platformName = "ARM"; debugger->platformCommands = _armCommands; } #endif