From 17c571a393d387d4a1a922c677547d975f7c1f23 Mon Sep 17 00:00:00 2001 From: ghidorahrex Date: Mon, 3 Jul 2023 18:14:45 +0000 Subject: [PATCH 1/4] GP-3606: Fixed 6x09 sub/cmp flags and 2-byte stack push/pop ordering --- .../MC6800/data/languages/6x09.sinc | 67 +++++++++++-------- 1 file changed, 39 insertions(+), 28 deletions(-) diff --git a/Ghidra/Processors/MC6800/data/languages/6x09.sinc b/Ghidra/Processors/MC6800/data/languages/6x09.sinc index 035e5ab4eb..585827f731 100644 --- a/Ghidra/Processors/MC6800/data/languages/6x09.sinc +++ b/Ghidra/Processors/MC6800/data/languages/6x09.sinc @@ -505,56 +505,69 @@ macro clear(op) macro addition(reg, op) { - $(C) = carry(reg, op); - $(V) = scarry(reg, op); + local tmp = reg; + local val = op; + $(C) = carry(tmp, val); + $(V) = scarry(tmp, val); - reg = reg + op; + tmp = tmp + val; - setNZFlags(reg); + setNZFlags(tmp); + reg = tmp; } macro additionWithCarry(reg, op) { local carryIn = zext($(C)); + local tmp = reg; + local val = op; local mask = 0x0F; # Low nibble mask - local tmpResult = reg + op; + local result = tmp + val; - $(H) = (((reg & mask) + (op & mask) + carryIn) >> 4) & 1; - $(C) = carry(reg, op) || carry(tmpResult, carryIn); - $(V) = scarry(reg, op) ^^ scarry(tmpResult, carryIn); + $(H) = (((tmp & mask) + (val & mask) + carryIn) >> 4) & 1; + $(C) = carry(tmp, val) || carry(result, carryIn); + $(V) = scarry(tmp, val) ^^ scarry(result, carryIn); - reg = tmpResult + carryIn; + tmp = result + carryIn; - setNZFlags(reg); + setNZFlags(tmp); + reg = tmp; } macro subtraction(reg, op) { - $(V) = sborrow(reg, op); - reg = reg - op; - setNZFlags(reg); - $(C) = (reg < op); + local tmp = reg; + local val = op; + $(V) = sborrow(tmp, val); + $(C) = (tmp < val); + tmp = tmp - val; + setNZFlags(tmp); + reg = tmp; } macro subtractionWithCarry(reg, op) { local carryIn = zext($(C)); - local tmpResult = reg - op; + local tmp = reg; + local val = op; + local tmpResult = tmp - val; - $(C) = (reg < op) || (tmpResult < carryIn); - $(V) = sborrow(reg, op) ^^ sborrow(tmpResult, carryIn); - - reg = tmpResult - carryIn; - - setNZFlags(reg); + $(C) = (tmp < val) || (tmpResult < carryIn); + $(V) = sborrow(tmp, val) ^^ sborrow(tmpResult, carryIn); + tmp = tmpResult - carryIn; + setNZFlags(tmp); + reg = tmp; } macro compare(reg, op) { - $(V) = sborrow(reg, op); - local tmp = reg - op; + local tmp = reg; + local val = op; + $(V) = sborrow(tmp, val); + $(C) = (tmp < val); + tmp = tmp - val; setNZFlags(tmp); - $(C) = (tmp < op); + reg = tmp; } macro logicalAnd(reg, op) @@ -609,9 +622,8 @@ macro Push1(reg, op) # Push 2 byte operand op2 macro Push2(reg, op) { - reg = reg - 1; + reg = reg - 2; *:2 reg = op; - reg = reg - 1; } # Pull 1 byte operand op1 @@ -624,9 +636,8 @@ macro Pull1(reg, op) # Pull 2 byte operand op2 macro Pull2(reg, op) { - reg = reg + 1; op = *:2 reg; - reg = reg + 1; + reg = reg + 2; } macro PushUYXDpD() From 8cd3a31afaffd43542a33b2419683da8f1309d3d Mon Sep 17 00:00:00 2001 From: ghidorahrex Date: Mon, 10 Jul 2023 14:29:37 +0000 Subject: [PATCH 2/4] GP-3621: Fixed 6x09 left-shift instruction flags --- .../MC6800/data/languages/6x09.sinc | 41 ++++++++++--------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/Ghidra/Processors/MC6800/data/languages/6x09.sinc b/Ghidra/Processors/MC6800/data/languages/6x09.sinc index 035e5ab4eb..b155b12337 100644 --- a/Ghidra/Processors/MC6800/data/languages/6x09.sinc +++ b/Ghidra/Processors/MC6800/data/languages/6x09.sinc @@ -434,6 +434,15 @@ macro complement(op) setNZFlags(op); } +# Signed shift right. +# P-code INT_SRIGHT. +macro arithmeticShiftRight(op) +{ + $(C) = op & 1; + op = (op s>> 1); + setNZFlags(op); +} + macro logicalShiftRight(op) { $(C) = op & 1; @@ -450,31 +459,25 @@ macro rotateRightWithCarry(op) setNZFlags(op); } +macro logicalShiftLeft(op) +{ + local tmp = (op >> 7); + $(C) = tmp; + op = op << 1; + $(V) = tmp ^ (op >> 7); + setNZFlags(op); +} + macro rotateLeftWithCarry(op) { local carryIn = $(C); - $(C) = op >> 7; + local tmp = (op >> 7); + $(C) = tmp; op = (op << 1) | carryIn; + $(V) = tmp ^ (op >> 7); setNZFlags(op); } -# Signed shift right. -# P-code INT_SRIGHT. -macro arithmeticShiftRight(op) -{ - $(C) = op & 1; - op = (op s>> 1); - setNZFlags(op); -} - -macro logicalShiftLeft(op) -{ - $(C) = (op >> 7); - op = op << 1; - $(Z) = (op == 0); - $(N) = (op >> 7); -} - macro increment(op) { $(V) = (op == 0x7F); @@ -1204,7 +1207,7 @@ macro PushEntireState() :ADDB OP1 is (op=0xCB | op=0xDB | op=0xEB | op=0xFB) ... & OP1 { - setHFlag(A, OP1); + setHFlag(B, OP1); addition(B, OP1); } From b02101298f0e118bda112e850e8812c29ffb40aa Mon Sep 17 00:00:00 2001 From: emteere <47253321+emteere@users.noreply.github.com> Date: Mon, 10 Jul 2023 20:40:57 +0000 Subject: [PATCH 3/4] GP-5 Fixed WindowsResource Analyzer wasting time decompiling functions that have previously failed to decompile due to timeout --- .../ghidra_scripts/WindowsResourceReference.java | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/WindowsResourceReference.java b/Ghidra/Features/Decompiler/ghidra_scripts/WindowsResourceReference.java index fce4b52b2c..2e94b848a5 100644 --- a/Ghidra/Features/Decompiler/ghidra_scripts/WindowsResourceReference.java +++ b/Ghidra/Features/Decompiler/ghidra_scripts/WindowsResourceReference.java @@ -36,8 +36,7 @@ import ghidra.app.script.GhidraScript; import ghidra.framework.options.ToolOptions; import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.util.OptionsService; -import ghidra.program.model.address.Address; -import ghidra.program.model.address.AddressSetView; +import ghidra.program.model.address.*; import ghidra.program.model.listing.*; import ghidra.program.model.pcode.*; import ghidra.program.model.symbol.*; @@ -56,6 +55,9 @@ public class WindowsResourceReference extends GhidraScript { ArrayList> defUseLists = new ArrayList<>(); protected AddressSetPropertyMap alreadyDoneAddressSetPropertyMap; + + // set of functions that decompilation failed on + protected AddressSet badDecompFunctions = new AddressSet(); public AddressSetPropertyMap getOrCreatePropertyMap(Program program, String mapName) { if (alreadyDoneAddressSetPropertyMap != null) { @@ -367,6 +369,11 @@ public class WindowsResourceReference extends GhidraScript { if (f == null) { return; } + + // check if decompilation of this function failed previously + if (badDecompFunctions.contains(f.getEntryPoint())) { + return; + } Instruction instr = prog.getListing().getInstructionAt(refAddr); if (instr == null) { @@ -376,7 +383,9 @@ public class WindowsResourceReference extends GhidraScript { decompileFunction(f, decompiler); if (hfunction == null) { - return; // failed to decompile + // failed to decompile, add to bad list + badDecompFunctions.add(f.getEntryPoint()); + return; } Iterator ops = hfunction.getPcodeOps(refAddr); From 93469f828a6a83f3f3468c575dd853c6fb754616 Mon Sep 17 00:00:00 2001 From: Dan <46821332+nsadeveloper789@users.noreply.github.com> Date: Mon, 10 Jul 2023 19:30:46 -0400 Subject: [PATCH 4/4] GP-3437: Fix emulator stack initialization. --- .../DebuggerEmulationServicePlugin.html | 6 ++ .../DebuggerEmulationServicePlugin.java | 10 +- .../emulation/ProgramEmulationUtils.java | 96 ++++++++++++++++--- .../DebuggerEmulationServiceTest.java | 33 ++++++- 4 files changed, 127 insertions(+), 18 deletions(-) diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerEmulationServicePlugin/DebuggerEmulationServicePlugin.html b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerEmulationServicePlugin/DebuggerEmulationServicePlugin.html index 7a5b750095..e8ea74094e 100644 --- a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerEmulationServicePlugin/DebuggerEmulationServicePlugin.html +++ b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerEmulationServicePlugin/DebuggerEmulationServicePlugin.html @@ -33,6 +33,12 @@ href="help/topics/DebuggerControlPlugin/DebuggerControlPlugin.html#emu_actions">emulator controls can then be used.

+

To control the initial stack allocation, create a STACK block in the target + program database before emulating. If the stack is already in the target image's memory map, + create an overlay block named STACK. This will initialize the stack pointer + without modifying the emulator's memory map. Note that customizing the stack initialization may + prevent you from adding a second thread.

+

Add Emulated Thread

This action is available whenever a "pure emulation" trace is active. It spawns a new thread diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/DebuggerEmulationServicePlugin.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/DebuggerEmulationServicePlugin.java index 9f0fcc0fc4..9da7b249a8 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/DebuggerEmulationServicePlugin.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/DebuggerEmulationServicePlugin.java @@ -512,14 +512,14 @@ public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEm if (!block.isExecute()) { return; }*/ - ProgramLocation progLoc = - staticMappings.getOpenMappedLocation(new DefaultTraceLocation(view.getTrace(), null, - Lifespan.at(view.getSnap()), tracePc)); - Program program = progLoc == null ? null : progLoc.getProgram(); - Address programPc = progLoc == null ? null : progLoc.getAddress(); long snap = view.getViewport().getOrderedSnaps().stream().filter(s -> s >= 0).findFirst().get(); + ProgramLocation progLoc = staticMappings.getOpenMappedLocation( + new DefaultTraceLocation(trace, null, Lifespan.at(snap), tracePc)); + Program program = progLoc == null ? null : progLoc.getProgram(); + Address programPc = progLoc == null ? null : progLoc.getAddress(); + TraceThread thread = ProgramEmulationUtils.launchEmulationThread(trace, snap, program, tracePc, programPc); traceManager.activateThread(thread); diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/ProgramEmulationUtils.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/ProgramEmulationUtils.java index 691396aba5..146097f5f0 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/ProgramEmulationUtils.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/ProgramEmulationUtils.java @@ -43,8 +43,7 @@ import ghidra.trace.model.target.TraceObject; import ghidra.trace.model.target.TraceObjectKeyPath; import ghidra.trace.model.thread.*; import ghidra.trace.model.time.TraceSnapshot; -import ghidra.util.DifferenceAddressSetView; -import ghidra.util.NumericUtilities; +import ghidra.util.*; import ghidra.util.exception.DuplicateNameException; /** @@ -56,6 +55,7 @@ import ghidra.util.exception.DuplicateNameException; */ public enum ProgramEmulationUtils { ; + public static final String BLOCK_NAME_STACK = "STACK"; /** * Conventional prefix for first snapshot to identify "pure emulation" traces. @@ -227,10 +227,10 @@ public enum ProgramEmulationUtils { * @param program the program whose context to use * @param tracePc the program counter in the trace's memory map * @param programPc the program counter in the program's memory map - * @param stack optionally, the region representing the thread's stack + * @param stack optionally, the range for the thread's stack allocation */ public static void initializeRegisters(Trace trace, long snap, TraceThread thread, - Program program, Address tracePc, Address programPc, TraceMemoryRegion stack) { + Program program, Address tracePc, Address programPc, AddressRange stack) { TraceMemoryManager memory = trace.getMemoryManager(); if (thread instanceof TraceObjectThread ot) { TraceObject object = ot.getObject(); @@ -283,28 +283,92 @@ public enum ProgramEmulationUtils { } } + public static AddressRange allocateStackCustom(Trace trace, long snap, TraceThread thread, + Program program) { + if (program == null) { + return null; + } + AddressSpace space = trace.getBaseCompilerSpec().getStackBaseSpace(); + MemoryBlock stackBlock = program.getMemory().getBlock(BLOCK_NAME_STACK); + if (stackBlock == null) { + return null; + } + if (space != stackBlock.getStart().getAddressSpace().getPhysicalSpace()) { + Msg.showError(ProgramEmulationUtils.class, null, "Invalid STACK block", + "The STACK block must be in the stack's base space. Ignoring."); + return null; + } + AddressRange alloc = new AddressRangeImpl( + stackBlock.getStart().getPhysicalAddress(), + stackBlock.getEnd().getPhysicalAddress()); + if (stackBlock.isOverlay() || DebuggerStaticMappingUtils.isReal(stackBlock)) { + return alloc; + } + PathPattern patRegion = computePatternRegion(trace); + String path = PathUtils.toString( + patRegion.applyKeys(stackBlock.getStart() + "-STACK") + .getSingletonPath()); + TraceMemoryManager mm = trace.getMemoryManager(); + try { + return mm.createRegion(path, snap, alloc, + TraceMemoryFlag.READ, TraceMemoryFlag.WRITE).getRange(); + } + catch (TraceOverlappedRegionException e) { + Msg.showError(ProgramEmulationUtils.class, null, "Stack conflict", + ("The STACK region %s conflicts with another: %s. " + + "You may need to initialize the stack pointer manually.").formatted( + alloc, e.getConflicts().iterator().next())); + return alloc; + } + catch (DuplicateNameException e) { + Msg.showError(ProgramEmulationUtils.class, null, "Stack conflict", + ("A region already exists with the same name: %s. " + + "You may need to initialize the stack pointer manually.") + .formatted(path)); + return alloc; + } + } + /** * Attempt to allocate a new stack region for the given thread * + *

+ * If successful, this will create a dynamic memory region representing the stack. If the stack + * is specified by an override (STACK block) in the program, and that block overlays the image, + * then no region is created. + * * @param trace the trace containing the stack and thread * @param snap the creation snap for the new region * @param thread the thread for which the stack is being allocated + * @param program the program being emulated (to check for stack allocation override) * @param size the desired size of the region - * @return the new region representing the allocated stack + * @return the range allocated for the stack * * @throws EmulatorOutOfMemoryException if the stack cannot be allocated */ - public static TraceMemoryRegion allocateStack(Trace trace, long snap, TraceThread thread, - long size) { + public static AddressRange allocateStack(Trace trace, long snap, TraceThread thread, + Program program, long size) { + AddressRange custom = allocateStackCustom(trace, snap, thread, program); + if (custom != null) { + return custom; + } + // Otherwise, just search for an un-allocated block of the given size. AddressSpace space = trace.getBaseCompilerSpec().getStackBaseSpace(); - AddressSet except0 = new AddressSet(space.getAddress(0x1000), space.getMaxAddress()); + Address max = space.getMaxAddress(); + AddressSet eligible; + if (max.getOffsetAsBigInteger().compareTo(BigInteger.valueOf(0x1000)) < 0) { + eligible = new AddressSet(space.getMinAddress(), max); + } + else { + eligible = new AddressSet(space.getAddress(0x1000), max); + } TraceMemoryManager mm = trace.getMemoryManager(); AddressSetView left = - new DifferenceAddressSetView(except0, mm.getRegionsAddressSet(snap)); + new DifferenceAddressSetView(eligible, mm.getRegionsAddressSet(snap)); PathPattern patRegion = computePatternRegion(trace); try { for (AddressRange candidate : left) { - if (Long.compareUnsigned(candidate.getLength(), size) > 0) { + if (Long.compareUnsigned(candidate.getLength(), size) >= 0) { AddressRange alloc = new AddressRangeImpl(candidate.getMinAddress(), size); String threadName = PathUtils.isIndex(thread.getName()) ? PathUtils.parseIndex(thread.getName()) @@ -313,7 +377,7 @@ public enum ProgramEmulationUtils { patRegion.applyKeys(alloc.getMinAddress() + "-stack " + threadName) .getSingletonPath()); return mm.createRegion(path, snap, alloc, - TraceMemoryFlag.READ, TraceMemoryFlag.WRITE); + TraceMemoryFlag.READ, TraceMemoryFlag.WRITE).getRange(); } } } @@ -373,7 +437,15 @@ public enum ProgramEmulationUtils { public static TraceThread doLaunchEmulationThread(Trace trace, long snap, Program program, Address tracePc, Address programPc) { TraceThread thread = spawnThread(trace, snap); - TraceMemoryRegion stack = allocateStack(trace, snap, thread, 0x4000); + AddressRange stack; + try { + stack = allocateStack(trace, snap, thread, program, 0x4000); + } + catch (EmulatorOutOfMemoryException e) { + Msg.warn(ProgramEmulationUtils.class, + "Cannot allocate a stack. Please initialize manually."); + stack = null; + } initializeRegisters(trace, snap, thread, program, tracePc, programPc, stack); return thread; } diff --git a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/emulation/DebuggerEmulationServiceTest.java b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/emulation/DebuggerEmulationServiceTest.java index a981816957..de00be020c 100644 --- a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/emulation/DebuggerEmulationServiceTest.java +++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/emulation/DebuggerEmulationServiceTest.java @@ -381,7 +381,8 @@ public class DebuggerEmulationServiceTest extends AbstractGhidraHeadedDebuggerGU assertTrue(result.error() instanceof DecodePcodeExecutionException); long scratch = result.snapshot(); - assertEquals(new BigInteger("003ffffe", 16), regs.getViewValue(scratch, regPC).getUnsignedValue()); + assertEquals(new BigInteger("003ffffe", 16), + regs.getViewValue(scratch, regPC).getUnsignedValue()); } @Test @@ -715,4 +716,34 @@ public class DebuggerEmulationServiceTest extends AbstractGhidraHeadedDebuggerGU assertEquals(new BigInteger("5678", 16), regs.getViewValue(scratch, regR2).getUnsignedValue()); } + + @Test + public void testCustomStack() throws Exception { + createProgram(); + intoProject(program); + Memory memory = program.getMemory(); + Address addrText = addr(program, 0x00400000); + Register regSP = program.getRegister("sp"); + try (Transaction tx = program.openTransaction("Initialize")) { + MemoryBlock blockText = memory.createInitializedBlock(".text", addrText, 0x1000, + (byte) 0, TaskMonitor.DUMMY, false); + blockText.setExecute(true); + memory.createUninitializedBlock("STACK", addr(program, 0x00001234), 0x1000, false); + } + + programManager.openProgram(program); + waitForSwing(); + codeBrowser.goTo(new ProgramLocation(program, addrText)); + waitForSwing(); + + assertTrue(emulationPlugin.actionEmulateProgram.isEnabled()); + performAction(emulationPlugin.actionEmulateProgram); + + Trace trace = traceManager.getCurrentTrace(); + assertNotNull(trace); + + TraceThread thread = Unique.assertOne(trace.getThreadManager().getAllThreads()); + TraceMemorySpace regs = trace.getMemoryManager().getMemoryRegisterSpace(thread, false); + assertEquals(new BigInteger("2234", 16), regs.getViewValue(0, regSP).getUnsignedValue()); + } }