GP-2467: Change SleighProgramCompiler to use String (text block) instead of List<String>

This commit is contained in:
Dan 2022-09-12 09:03:54 -04:00
parent 6a2cd80550
commit 9d6f278f39
48 changed files with 730 additions and 613 deletions

View file

@ -24,7 +24,6 @@
//@toolbar //@toolbar
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.List;
import ghidra.app.plugin.assembler.Assembler; import ghidra.app.plugin.assembler.Assembler;
import ghidra.app.plugin.assembler.Assemblers; import ghidra.app.plugin.assembler.Assemblers;
@ -149,9 +148,10 @@ public class DebuggerEmuExampleScript extends GhidraScript {
* Inject a call to our custom print userop. Otherwise, the language itself will never * Inject a call to our custom print userop. Otherwise, the language itself will never
* invoke it. * invoke it.
*/ */
emulator.inject(injectHere, List.of( emulator.inject(injectHere, """
"print_utf8(RCX);", print_utf8(RCX);
"emu_exec_decoded();")); emu_exec_decoded();
""");
/* /*
* Run the experiment: This should interrupt on the second SYSCALL, because any value other * Run the experiment: This should interrupt on the second SYSCALL, because any value other

View file

@ -22,7 +22,6 @@
//@toolbar //@toolbar
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.List;
import ghidra.app.plugin.assembler.*; import ghidra.app.plugin.assembler.*;
import ghidra.app.plugin.processors.sleigh.SleighLanguage; import ghidra.app.plugin.processors.sleigh.SleighLanguage;
@ -102,10 +101,10 @@ public class StandAloneEmuExampleScript extends GhidraScript {
*/ */
byte[] hw = "Hello, World!\n".getBytes(UTF8); byte[] hw = "Hello, World!\n".getBytes(UTF8);
emulator.getSharedState().setVar(dyn, 0xdeadbeefL, hw.length, true, hw); emulator.getSharedState().setVar(dyn, 0xdeadbeefL, hw.length, true, hw);
PcodeProgram init = SleighProgramCompiler.compileProgram(language, "init", List.of( PcodeProgram init = SleighProgramCompiler.compileProgram(language, "init", String.format("""
"RIP = 0x" + entry + ";", RIP = 0x%s;
"RSP = 0x00001000;"), RSP = 0x00001000;
library); """, entry), library);
thread.getExecutor().execute(init, library); thread.getExecutor().execute(init, library);
thread.overrideContextWithDefault(); thread.overrideContextWithDefault();
thread.reInitialize(); thread.reInitialize();
@ -114,9 +113,10 @@ public class StandAloneEmuExampleScript extends GhidraScript {
* Inject a call to our custom print userop. Otherwise, the language itself will never * Inject a call to our custom print userop. Otherwise, the language itself will never
* invoke it. * invoke it.
*/ */
emulator.inject(injectHere, List.of( emulator.inject(injectHere, """
"print_utf8(RCX);", print_utf8(RCX);
"emu_exec_decoded();")); emu_exec_decoded();
""");
/* /*
* Run the experiment: This should interrupt on the second SYSCALL, because any value other * Run the experiment: This should interrupt on the second SYSCALL, because any value other

View file

@ -103,9 +103,7 @@ public class StandAloneStructuredSleighScript extends GhidraScript {
print(userop.getName() + "("); print(userop.getName() + "(");
print(userop.getInputs().stream().collect(Collectors.joining(","))); print(userop.getInputs().stream().collect(Collectors.joining(",")));
print(") {\n"); print(") {\n");
for (String line : userop.getLines()) { print(userop.getBody());
print(line);
}
print("}\n\n"); print("}\n\n");
} }
} }

View file

@ -22,7 +22,6 @@
//@toolbar //@toolbar
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.List;
import ghidra.app.plugin.assembler.Assembler; import ghidra.app.plugin.assembler.Assembler;
import ghidra.app.plugin.assembler.Assemblers; import ghidra.app.plugin.assembler.Assemblers;
@ -184,10 +183,11 @@ public class StandAloneSyscallEmuExampleScript extends GhidraScript {
/* /*
* Initialize the thread * Initialize the thread
*/ */
PcodeProgram init = SleighProgramCompiler.compileProgram(language, "init", List.of( PcodeProgram init =
"RIP = 0x" + entry + ";", SleighProgramCompiler.compileProgram(language, "init", String.format("""
"RSP = 0x00001000;"), RIP = 0x%s;
library); RSP = 0x00001000;
""", entry), library);
thread.getExecutor().execute(init, library); thread.getExecutor().execute(init, library);
thread.overrideContextWithDefault(); thread.overrideContextWithDefault();
thread.reInitialize(); thread.reInitialize();

View file

@ -69,8 +69,10 @@ public class DebuggerPcodeStepperPluginScreenShots extends GhidraScreenShotGener
PcodeExecutor<byte[]> exe = PcodeExecutor<byte[]> exe =
TraceSleighUtils.buildByteExecutor(tb.trace, snap0, thread, 0); TraceSleighUtils.buildByteExecutor(tb.trace, snap0, thread, 0);
exe.executeSleighLine("RIP = 0x00400000"); exe.executeSleigh("""
exe.executeSleighLine("RSP = 0x0010fff8"); RIP = 0x00400000;
RSP = 0x0010fff8;
""");
Assembler asm = Assemblers.getAssembler(tb.trace.getFixedProgramView(snap0)); Assembler asm = Assemblers.getAssembler(tb.trace.getFixedProgramView(snap0));
asm.assemble(tb.addr(0x00400000), "SUB RSP,0x40"); asm.assemble(tb.addr(0x00400000), "SUB RSP,0x40");

View file

@ -69,14 +69,18 @@ public class DebuggerWatchesPluginScreenShots extends GhidraScreenShotGenerator
PcodeExecutor<byte[]> executor0 = PcodeExecutor<byte[]> executor0 =
TraceSleighUtils.buildByteExecutor(tb.trace, snap0, thread, 0); TraceSleighUtils.buildByteExecutor(tb.trace, snap0, thread, 0);
executor0.executeSleighLine("RSP = 0x7ffefff8"); executor0.executeSleigh("""
executor0.executeSleighLine("*:4 (RSP+8) = 0x4030201"); RSP = 0x7ffefff8;
*:4 (RSP+8) = 0x4030201;
""");
PcodeExecutor<byte[]> executor1 = PcodeExecutor<byte[]> executor1 =
TraceSleighUtils.buildByteExecutor(tb.trace, snap1, thread, 0); TraceSleighUtils.buildByteExecutor(tb.trace, snap1, thread, 0);
executor1.executeSleighLine("RSP = 0x7ffefff8"); executor1.executeSleigh("""
executor1.executeSleighLine("*:4 (RSP+8) = 0x1020304"); RSP = 0x7ffefff8;
executor1.executeSleighLine("*:4 0x7fff0004:8 = 0x4A9A70C8"); *:4 (RSP+8) = 0x1020304;
*:4 0x7fff0004:8 = 0x4A9A70C8;
""");
} }
watchesProvider.addWatch("RSP"); watchesProvider.addWatch("RSP");

View file

@ -134,11 +134,12 @@ public class DebuggerBreakpointMarkerPluginTest extends AbstractGhidraHeadedDebu
}); });
} }
protected TraceRecorder addMappedBreakpointOpenAndWait() throws Exception { protected TraceRecorder addMappedBreakpointOpenAndWait() throws Throwable {
createTestModel(); createTestModel();
mb.createTestProcessesAndThreads(); mb.createTestProcessesAndThreads();
TraceRecorder recorder = modelService.recordTarget(mb.testProcess1, TraceRecorder recorder = modelService.recordTarget(mb.testProcess1,
createTargetTraceMapper(mb.testProcess1), ActionSource.AUTOMATIC); createTargetTraceMapper(mb.testProcess1), ActionSource.AUTOMATIC);
waitRecorder(recorder);
Trace trace = recorder.getTrace(); Trace trace = recorder.getTrace();
createProgramFromTrace(trace); createProgramFromTrace(trace);
intoProject(trace); intoProject(trace);
@ -260,7 +261,7 @@ public class DebuggerBreakpointMarkerPluginTest extends AbstractGhidraHeadedDebu
Set.of("SW_EXECUTE", "HW_EXECUTE", "READ,WRITE", "READ", "WRITE"); Set.of("SW_EXECUTE", "HW_EXECUTE", "READ,WRITE", "READ", "WRITE");
@Test @Test
public void testProgramNoBreakPopupMenus() throws Exception { public void testProgramNoBreakPopupMenus() throws Throwable {
// NOTE: Need a target to have any breakpoint actions, even on programs // NOTE: Need a target to have any breakpoint actions, even on programs
addMappedBreakpointOpenAndWait(); addMappedBreakpointOpenAndWait();
@ -276,7 +277,7 @@ public class DebuggerBreakpointMarkerPluginTest extends AbstractGhidraHeadedDebu
} }
@Test @Test
public void testTraceNoBreakPopupMenus() throws Exception { public void testTraceNoBreakPopupMenus() throws Throwable {
TraceRecorder recorder = addMappedBreakpointOpenAndWait(); TraceRecorder recorder = addMappedBreakpointOpenAndWait();
Trace trace = recorder.getTrace(); Trace trace = recorder.getTrace();
traceManager.activateTrace(trace); traceManager.activateTrace(trace);
@ -414,7 +415,7 @@ public class DebuggerBreakpointMarkerPluginTest extends AbstractGhidraHeadedDebu
@Test @Test
public void testActionToggleBreakpointProgramWithNoCurrentBreakpointOnInstruction() public void testActionToggleBreakpointProgramWithNoCurrentBreakpointOnInstruction()
throws Exception { throws Throwable {
addMappedBreakpointOpenAndWait(); // wasteful, but whatever addMappedBreakpointOpenAndWait(); // wasteful, but whatever
for (LogicalBreakpoint lb : List.copyOf(breakpointService.getAllBreakpoints())) { for (LogicalBreakpoint lb : List.copyOf(breakpointService.getAllBreakpoints())) {
lb.delete(); lb.delete();
@ -442,7 +443,7 @@ public class DebuggerBreakpointMarkerPluginTest extends AbstractGhidraHeadedDebu
} }
@Test @Test
public void testActionToggleBreakpointProgramWithNoCurrentBreakpointOnData() throws Exception { public void testActionToggleBreakpointProgramWithNoCurrentBreakpointOnData() throws Throwable {
addMappedBreakpointOpenAndWait(); // wasteful, but whatever addMappedBreakpointOpenAndWait(); // wasteful, but whatever
for (LogicalBreakpoint lb : List.copyOf(breakpointService.getAllBreakpoints())) { for (LogicalBreakpoint lb : List.copyOf(breakpointService.getAllBreakpoints())) {
lb.delete(); lb.delete();
@ -470,7 +471,7 @@ public class DebuggerBreakpointMarkerPluginTest extends AbstractGhidraHeadedDebu
} }
@Test @Test
public void testActionToggleBreakpointProgram() throws Exception { public void testActionToggleBreakpointProgram() throws Throwable {
addMappedBreakpointOpenAndWait(); addMappedBreakpointOpenAndWait();
LogicalBreakpoint lb = Unique.assertOne(breakpointService.getAllBreakpoints()); LogicalBreakpoint lb = Unique.assertOne(breakpointService.getAllBreakpoints());
@ -486,7 +487,7 @@ public class DebuggerBreakpointMarkerPluginTest extends AbstractGhidraHeadedDebu
} }
@Test @Test
public void testActionToggleBreakpointTrace() throws Exception { public void testActionToggleBreakpointTrace() throws Throwable {
TraceRecorder recorder = addMappedBreakpointOpenAndWait(); TraceRecorder recorder = addMappedBreakpointOpenAndWait();
Trace trace = recorder.getTrace(); Trace trace = recorder.getTrace();
LogicalBreakpoint lb = Unique.assertOne(breakpointService.getAllBreakpoints()); LogicalBreakpoint lb = Unique.assertOne(breakpointService.getAllBreakpoints());
@ -527,14 +528,16 @@ public class DebuggerBreakpointMarkerPluginTest extends AbstractGhidraHeadedDebu
} }
protected void testActionSetBreakpointProgram(DockingAction action, protected void testActionSetBreakpointProgram(DockingAction action,
Set<TraceBreakpointKind> expectedKinds) throws Exception { Set<TraceBreakpointKind> expectedKinds) throws Throwable {
addMappedBreakpointOpenAndWait(); // Adds an unneeded breakpoint. Aw well. addMappedBreakpointOpenAndWait(); // Adds an unneeded breakpoint. Aw well.
performAction(action, staticCtx(addr(program, 0x0400321)), false); performAction(action, staticCtx(addr(program, 0x0400321)), false);
DebuggerPlaceBreakpointDialog dialog = DebuggerPlaceBreakpointDialog dialog =
waitForDialogComponent(DebuggerPlaceBreakpointDialog.class); waitForDialogComponent(DebuggerPlaceBreakpointDialog.class);
runSwing(() -> {
dialog.setName("Test name"); dialog.setName("Test name");
runSwing(() -> dialog.okCallback()); dialog.okCallback();
});
waitForPass(() -> { waitForPass(() -> {
LogicalBreakpoint lb = Unique.assertOne( LogicalBreakpoint lb = Unique.assertOne(
@ -546,7 +549,7 @@ public class DebuggerBreakpointMarkerPluginTest extends AbstractGhidraHeadedDebu
} }
protected void testActionSetBreakpointTrace(DockingAction action, protected void testActionSetBreakpointTrace(DockingAction action,
Set<TraceBreakpointKind> expectedKinds) throws Exception { Set<TraceBreakpointKind> expectedKinds) throws Throwable {
TraceRecorder recorder = addMappedBreakpointOpenAndWait(); // Adds an unneeded breakpoint. Aw well. TraceRecorder recorder = addMappedBreakpointOpenAndWait(); // Adds an unneeded breakpoint. Aw well.
Trace trace = recorder.getTrace(); Trace trace = recorder.getTrace();
@ -568,67 +571,67 @@ public class DebuggerBreakpointMarkerPluginTest extends AbstractGhidraHeadedDebu
} }
@Test @Test
public void testActionSetSoftwareBreakpointProgram() throws Exception { public void testActionSetSoftwareBreakpointProgram() throws Throwable {
testActionSetBreakpointProgram(breakpointMarkerPlugin.actionSetSoftwareBreakpoint, testActionSetBreakpointProgram(breakpointMarkerPlugin.actionSetSoftwareBreakpoint,
Set.of(TraceBreakpointKind.SW_EXECUTE)); Set.of(TraceBreakpointKind.SW_EXECUTE));
} }
@Test @Test
public void testActionSetSoftwareBreakpointTrace() throws Exception { public void testActionSetSoftwareBreakpointTrace() throws Throwable {
testActionSetBreakpointTrace(breakpointMarkerPlugin.actionSetSoftwareBreakpoint, testActionSetBreakpointTrace(breakpointMarkerPlugin.actionSetSoftwareBreakpoint,
Set.of(TraceBreakpointKind.SW_EXECUTE)); Set.of(TraceBreakpointKind.SW_EXECUTE));
} }
@Test @Test
public void testActionSetExecuteBreakpointProgram() throws Exception { public void testActionSetExecuteBreakpointProgram() throws Throwable {
testActionSetBreakpointProgram(breakpointMarkerPlugin.actionSetExecuteBreakpoint, testActionSetBreakpointProgram(breakpointMarkerPlugin.actionSetExecuteBreakpoint,
Set.of(TraceBreakpointKind.HW_EXECUTE)); Set.of(TraceBreakpointKind.HW_EXECUTE));
} }
@Test @Test
public void testActionSetExecuteBreakpointTrace() throws Exception { public void testActionSetExecuteBreakpointTrace() throws Throwable {
testActionSetBreakpointTrace(breakpointMarkerPlugin.actionSetExecuteBreakpoint, testActionSetBreakpointTrace(breakpointMarkerPlugin.actionSetExecuteBreakpoint,
Set.of(TraceBreakpointKind.HW_EXECUTE)); Set.of(TraceBreakpointKind.HW_EXECUTE));
} }
@Test @Test
public void testActionSetReadWriteBreakpointProgram() throws Exception { public void testActionSetReadWriteBreakpointProgram() throws Throwable {
testActionSetBreakpointProgram(breakpointMarkerPlugin.actionSetReadWriteBreakpoint, testActionSetBreakpointProgram(breakpointMarkerPlugin.actionSetReadWriteBreakpoint,
Set.of(TraceBreakpointKind.READ, TraceBreakpointKind.WRITE)); Set.of(TraceBreakpointKind.READ, TraceBreakpointKind.WRITE));
} }
@Test @Test
public void testActionSetReadWriteBreakpointTrace() throws Exception { public void testActionSetReadWriteBreakpointTrace() throws Throwable {
testActionSetBreakpointTrace(breakpointMarkerPlugin.actionSetReadWriteBreakpoint, testActionSetBreakpointTrace(breakpointMarkerPlugin.actionSetReadWriteBreakpoint,
Set.of(TraceBreakpointKind.READ, TraceBreakpointKind.WRITE)); Set.of(TraceBreakpointKind.READ, TraceBreakpointKind.WRITE));
} }
@Test @Test
public void testActionSetReadBreakpointProgram() throws Exception { public void testActionSetReadBreakpointProgram() throws Throwable {
testActionSetBreakpointProgram(breakpointMarkerPlugin.actionSetReadBreakpoint, testActionSetBreakpointProgram(breakpointMarkerPlugin.actionSetReadBreakpoint,
Set.of(TraceBreakpointKind.READ)); Set.of(TraceBreakpointKind.READ));
} }
@Test @Test
public void testActionSetReadBreakpointTrace() throws Exception { public void testActionSetReadBreakpointTrace() throws Throwable {
testActionSetBreakpointTrace(breakpointMarkerPlugin.actionSetReadBreakpoint, testActionSetBreakpointTrace(breakpointMarkerPlugin.actionSetReadBreakpoint,
Set.of(TraceBreakpointKind.READ)); Set.of(TraceBreakpointKind.READ));
} }
@Test @Test
public void testActionSetWriteBreakpointProgram() throws Exception { public void testActionSetWriteBreakpointProgram() throws Throwable {
testActionSetBreakpointProgram(breakpointMarkerPlugin.actionSetWriteBreakpoint, testActionSetBreakpointProgram(breakpointMarkerPlugin.actionSetWriteBreakpoint,
Set.of(TraceBreakpointKind.WRITE)); Set.of(TraceBreakpointKind.WRITE));
} }
@Test @Test
public void testActionSetWriteBreakpointTrace() throws Exception { public void testActionSetWriteBreakpointTrace() throws Throwable {
testActionSetBreakpointTrace(breakpointMarkerPlugin.actionSetWriteBreakpoint, testActionSetBreakpointTrace(breakpointMarkerPlugin.actionSetWriteBreakpoint,
Set.of(TraceBreakpointKind.WRITE)); Set.of(TraceBreakpointKind.WRITE));
} }
@Test @Test
public void testActionEnableBreakpointProgram() throws Exception { public void testActionEnableBreakpointProgram() throws Throwable {
addMappedBreakpointOpenAndWait(); addMappedBreakpointOpenAndWait();
LogicalBreakpoint lb = Unique.assertOne(breakpointService.getAllBreakpoints()); LogicalBreakpoint lb = Unique.assertOne(breakpointService.getAllBreakpoints());
lb.disable(); lb.disable();
@ -641,7 +644,7 @@ public class DebuggerBreakpointMarkerPluginTest extends AbstractGhidraHeadedDebu
} }
@Test @Test
public void testActionEnableBreakpointTrace() throws Exception { public void testActionEnableBreakpointTrace() throws Throwable {
TraceRecorder recorder = addMappedBreakpointOpenAndWait(); TraceRecorder recorder = addMappedBreakpointOpenAndWait();
Trace trace = recorder.getTrace(); Trace trace = recorder.getTrace();
LogicalBreakpoint lb = Unique.assertOne(breakpointService.getAllBreakpoints()); LogicalBreakpoint lb = Unique.assertOne(breakpointService.getAllBreakpoints());
@ -656,7 +659,7 @@ public class DebuggerBreakpointMarkerPluginTest extends AbstractGhidraHeadedDebu
} }
@Test @Test
public void testActionDisableBreakpointProgram() throws Exception { public void testActionDisableBreakpointProgram() throws Throwable {
addMappedBreakpointOpenAndWait(); addMappedBreakpointOpenAndWait();
LogicalBreakpoint lb = Unique.assertOne(breakpointService.getAllBreakpoints()); LogicalBreakpoint lb = Unique.assertOne(breakpointService.getAllBreakpoints());
@ -667,7 +670,7 @@ public class DebuggerBreakpointMarkerPluginTest extends AbstractGhidraHeadedDebu
} }
@Test @Test
public void testActionDisableBreakpointTrace() throws Exception { public void testActionDisableBreakpointTrace() throws Throwable {
TraceRecorder recorder = addMappedBreakpointOpenAndWait(); TraceRecorder recorder = addMappedBreakpointOpenAndWait();
Trace trace = recorder.getTrace(); Trace trace = recorder.getTrace();
LogicalBreakpoint lb = Unique.assertOne(breakpointService.getAllBreakpoints()); LogicalBreakpoint lb = Unique.assertOne(breakpointService.getAllBreakpoints());
@ -680,7 +683,7 @@ public class DebuggerBreakpointMarkerPluginTest extends AbstractGhidraHeadedDebu
} }
@Test @Test
public void testActionClearBreakpointProgram() throws Exception { public void testActionClearBreakpointProgram() throws Throwable {
addMappedBreakpointOpenAndWait(); addMappedBreakpointOpenAndWait();
performAction(breakpointMarkerPlugin.actionClearBreakpoint, performAction(breakpointMarkerPlugin.actionClearBreakpoint,
@ -690,7 +693,7 @@ public class DebuggerBreakpointMarkerPluginTest extends AbstractGhidraHeadedDebu
} }
@Test @Test
public void testActionClearBreakpointTrace() throws Exception { public void testActionClearBreakpointTrace() throws Throwable {
TraceRecorder recorder = addMappedBreakpointOpenAndWait(); TraceRecorder recorder = addMappedBreakpointOpenAndWait();
Trace trace = recorder.getTrace(); Trace trace = recorder.getTrace();
LogicalBreakpoint lb = Unique.assertOne(breakpointService.getAllBreakpoints()); LogicalBreakpoint lb = Unique.assertOne(breakpointService.getAllBreakpoints());

View file

@ -1269,7 +1269,7 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerGUI
.createRegion(".text", 0, tb.range(0x00400000, 0x0040ffff), .createRegion(".text", 0, tb.range(0x00400000, 0x0040ffff),
TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE); TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE);
thread1 = tb.getOrAddThread("Thread1", 0); thread1 = tb.getOrAddThread("Thread1", 0);
tb.exec(0, 0, thread1, java.util.List.of("RIP = 0x00400000;")); tb.exec(0, 0, thread1, "RIP = 0x00400000;");
} }
TraceThread thread2; TraceThread thread2;
@ -1279,7 +1279,7 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerGUI
.createRegion(".text", 0, tb2.range(0x200, 0x3ff), TraceMemoryFlag.READ, .createRegion(".text", 0, tb2.range(0x200, 0x3ff), TraceMemoryFlag.READ,
TraceMemoryFlag.EXECUTE); TraceMemoryFlag.EXECUTE);
thread2 = tb2.getOrAddThread("Thread2", 0); thread2 = tb2.getOrAddThread("Thread2", 0);
tb2.exec(0, 0, thread2, java.util.List.of("PC = 0x100;")); tb2.exec(0, 0, thread2, "PC = 0x100;");
} }
traceManager.openTrace(tb.trace); traceManager.openTrace(tb.trace);

View file

@ -82,7 +82,7 @@ public class DebuggerPcodeStepperProviderTest extends AbstractGhidraHeadedDebugg
thread = tb.getOrAddThread("1", 0); thread = tb.getOrAddThread("1", 0);
PcodeExecutor<byte[]> init = TraceSleighUtils.buildByteExecutor(tb.trace, 0, thread, 0); PcodeExecutor<byte[]> init = TraceSleighUtils.buildByteExecutor(tb.trace, 0, thread, 0);
init.executeSleighLine("pc = 0x00400000"); init.executeSleigh("pc = 0x00400000;");
Assembler asm = Assemblers.getAssembler(tb.trace.getFixedProgramView(0)); Assembler asm = Assemblers.getAssembler(tb.trace.getFixedProgramView(0));
iit = asm.assemble(start, iit = asm.assemble(start,
@ -166,7 +166,7 @@ public class DebuggerPcodeStepperProviderTest extends AbstractGhidraHeadedDebugg
.anyMatch(r -> r.getCode().contains("emu_swi")))); .anyMatch(r -> r.getCode().contains("emu_swi"))));
} }
protected List<PcodeRow> format(List<String> sleigh) { protected List<PcodeRow> format(String sleigh) {
SleighLanguage language = (SleighLanguage) getToyBE64Language(); SleighLanguage language = (SleighLanguage) getToyBE64Language();
PcodeProgram prog = SleighProgramCompiler.compileProgram(language, "test", sleigh, PcodeProgram prog = SleighProgramCompiler.compileProgram(language, "test", sleigh,
PcodeUseropLibrary.nil()); PcodeUseropLibrary.nil());
@ -179,7 +179,7 @@ public class DebuggerPcodeStepperProviderTest extends AbstractGhidraHeadedDebugg
@Test @Test
public void testPcodeFormatterSimple() { public void testPcodeFormatterSimple() {
List<PcodeRow> rows = format(List.of("r0 = 1;")); List<PcodeRow> rows = format("r0 = 1;");
assertEquals(2, rows.size()); assertEquals(2, rows.size());
assertEquals("<html></html>", rows.get(0).getLabel()); assertEquals("<html></html>", rows.get(0).getLabel());
assertEquals(FallthroughPcodeRow.class, rows.get(1).getClass()); assertEquals(FallthroughPcodeRow.class, rows.get(1).getClass());
@ -187,9 +187,10 @@ public class DebuggerPcodeStepperProviderTest extends AbstractGhidraHeadedDebugg
@Test @Test
public void testPcodeFormatterStartsLabel() { public void testPcodeFormatterStartsLabel() {
List<PcodeRow> rows = format(List.of( List<PcodeRow> rows = format("""
"<L0> r0 = 1;", <L0> r0 = 1;
"goto <L0>;")); goto <L0>;
""");
assertEquals(3, rows.size()); assertEquals(3, rows.size());
assertEquals("<html><span class=\"lab\">&lt;0&gt;</span></html>", rows.get(0).getLabel()); assertEquals("<html><span class=\"lab\">&lt;0&gt;</span></html>", rows.get(0).getLabel());
assertEquals("<html></html>", rows.get(1).getLabel()); assertEquals("<html></html>", rows.get(1).getLabel());
@ -198,10 +199,11 @@ public class DebuggerPcodeStepperProviderTest extends AbstractGhidraHeadedDebugg
@Test @Test
public void testPcodeFormatterMiddleLabel() { public void testPcodeFormatterMiddleLabel() {
List<PcodeRow> rows = format(List.of( List<PcodeRow> rows = format("""
"if 1:1 goto <SKIP>;", if 1:1 goto <SKIP>;
"r0 = 1;", r0 = 1;
"<SKIP> r1 = 2;")); <SKIP> r1 = 2;
""");
assertEquals(4, rows.size()); assertEquals(4, rows.size());
assertEquals("<html></html>", rows.get(0).getLabel()); assertEquals("<html></html>", rows.get(0).getLabel());
assertEquals("<html></html>", rows.get(1).getLabel()); assertEquals("<html></html>", rows.get(1).getLabel());
@ -211,10 +213,11 @@ public class DebuggerPcodeStepperProviderTest extends AbstractGhidraHeadedDebugg
@Test @Test
public void testPcodeFormatterFallthroughLabel() { public void testPcodeFormatterFallthroughLabel() {
List<PcodeRow> rows = format(List.of( List<PcodeRow> rows = format("""
"if 1:1 goto <SKIP>;", if 1:1 goto <SKIP>;
"r0 = 1;", r0 = 1;
"<SKIP>")); <SKIP>
""");
assertEquals(3, rows.size()); assertEquals(3, rows.size());
assertEquals("<html></html>", rows.get(0).getLabel()); assertEquals("<html></html>", rows.get(0).getLabel());
assertEquals("<html></html>", rows.get(1).getLabel()); assertEquals("<html></html>", rows.get(1).getLabel());
@ -224,12 +227,13 @@ public class DebuggerPcodeStepperProviderTest extends AbstractGhidraHeadedDebugg
@Test @Test
public void testPcodeFormatterManyLabel() { public void testPcodeFormatterManyLabel() {
List<PcodeRow> rows = format(List.of( List<PcodeRow> rows = format("""
"<L0> goto <L1>;", <L0> goto <L1>;
"<L1> goto <L2>;", <L1> goto <L2>;
"<L2> goto <L3>;", <L2> goto <L3>;
"goto <L0>;", goto <L0>;
"<L3>")); <L3>
""");
assertEquals(5, rows.size()); assertEquals(5, rows.size());
// NB. templates number labels in order of appearance in BRANCHes // NB. templates number labels in order of appearance in BRANCHes
assertEquals("<html><span class=\"lab\">&lt;3&gt;</span></html>", rows.get(0).getLabel()); assertEquals("<html><span class=\"lab\">&lt;3&gt;</span></html>", rows.get(0).getLabel());

View file

@ -469,7 +469,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
TraceThread thread = addThread(); TraceThread thread = addThread();
try (UndoableTransaction tid = tb.startTransaction()) { try (UndoableTransaction tid = tb.startTransaction()) {
tb.exec(0, 0, thread, List.of("pc = 100;")); tb.exec(0, 0, thread, "pc = 100;");
} }
traceManager.activateThread(thread); traceManager.activateThread(thread);
waitForSwing(); waitForSwing();
@ -800,10 +800,11 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
modelData.stream().filter(r -> r.getRegister() == pc).findFirst().orElse(null); modelData.stream().filter(r -> r.getRegister() == pc).findFirst().orElse(null);
assertNotNull(pcAvail); assertNotNull(pcAvail);
runSwing(() -> {
pcAvail.setSelected(false); pcAvail.setSelected(false);
dialog.availableTableModel.fireTableDataChanged(); dialog.availableTableModel.fireTableDataChanged();
dialog.okCallback(); dialog.okCallback();
waitForSwing(); });
assertNull(getRegisterRow(pc)); assertNull(getRegisterRow(pc));
assertTrue(registersProvider.actionSelectRegisters.isEnabled()); assertTrue(registersProvider.actionSelectRegisters.isEnabled());

View file

@ -148,7 +148,7 @@ public class DebuggerStateEditingServiceTest extends AbstractGhidraHeadedDebugge
Assembler asm = Assemblers.getAssembler(tb.trace.getFixedProgramView(0)); Assembler asm = Assemblers.getAssembler(tb.trace.getFixedProgramView(0));
asm.assemble(tb.addr(0x00400000), "imm r0,#123"); asm.assemble(tb.addr(0x00400000), "imm r0,#123");
executor.executeSleighLine("pc = 0x00400000"); executor.executeSleigh("pc = 0x00400000;");
} }
traceManager.activateTrace(tb.trace); traceManager.activateTrace(tb.trace);
editingService.setCurrentMode(tb.trace, StateEditingMode.WRITE_EMULATOR); editingService.setCurrentMode(tb.trace, StateEditingMode.WRITE_EMULATOR);
@ -186,7 +186,7 @@ public class DebuggerStateEditingServiceTest extends AbstractGhidraHeadedDebugge
Assembler asm = Assemblers.getAssembler(tb.trace.getFixedProgramView(0)); Assembler asm = Assemblers.getAssembler(tb.trace.getFixedProgramView(0));
asm.assemble(tb.addr(0x00400000), "imm r0,#123"); asm.assemble(tb.addr(0x00400000), "imm r0,#123");
executor.executeSleighLine("pc = 0x00400000"); executor.executeSleigh("pc = 0x00400000;");
} }
traceManager.activateTrace(tb.trace); traceManager.activateTrace(tb.trace);
editingService.setCurrentMode(tb.trace, StateEditingMode.WRITE_EMULATOR); editingService.setCurrentMode(tb.trace, StateEditingMode.WRITE_EMULATOR);

View file

@ -337,7 +337,7 @@ public class DebuggerTraceManagerServiceTest extends AbstractGhidraHeadedDebugge
} }
@Test @Test
public void testAutoActivatePresent() throws Exception { public void testAutoActivatePresent() throws Throwable {
assertTrue(traceManager.isAutoActivatePresent()); assertTrue(traceManager.isAutoActivatePresent());
createTestModel(); createTestModel();
@ -345,6 +345,7 @@ public class DebuggerTraceManagerServiceTest extends AbstractGhidraHeadedDebugge
TraceRecorder recorder = modelService.recordTarget(mb.testProcess1, TraceRecorder recorder = modelService.recordTarget(mb.testProcess1,
createTargetTraceMapper(mb.testProcess1), ActionSource.AUTOMATIC); createTargetTraceMapper(mb.testProcess1), ActionSource.AUTOMATIC);
waitRecorder(recorder);
Trace trace = recorder.getTrace(); Trace trace = recorder.getTrace();
traceManager.openTrace(trace); traceManager.openTrace(trace);

View file

@ -18,7 +18,6 @@ package ghidra.pcode.exec;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.List;
import java.util.Map; import java.util.Map;
import org.junit.Test; import org.junit.Test;
@ -29,7 +28,6 @@ import ghidra.app.plugin.processors.sleigh.SleighLanguage;
import ghidra.app.services.ActionSource; import ghidra.app.services.ActionSource;
import ghidra.app.services.TraceRecorder; import ghidra.app.services.TraceRecorder;
import ghidra.dbg.model.TestTargetRegisterBankInThread; import ghidra.dbg.model.TestTargetRegisterBankInThread;
import ghidra.pcode.exec.*;
import ghidra.pcode.utils.Utils; import ghidra.pcode.utils.Utils;
import ghidra.program.model.lang.Register; import ghidra.program.model.lang.Register;
import ghidra.trace.model.Trace; import ghidra.trace.model.Trace;
@ -100,7 +98,7 @@ public class TraceRecorderAsyncPcodeExecTest extends AbstractGhidraHeadedDebugge
SleighLanguage language = (SleighLanguage) trace.getBaseLanguage(); SleighLanguage language = (SleighLanguage) trace.getBaseLanguage();
PcodeProgram prog = SleighProgramCompiler.compileProgram(language, "test", PcodeProgram prog = SleighProgramCompiler.compileProgram(language, "test",
List.of("r2 = r0 + r1;"), PcodeUseropLibrary.NIL); "r2 = r0 + r1;", PcodeUseropLibrary.NIL);
Register r0 = language.getRegister("r0"); Register r0 = language.getRegister("r0");
Register r1 = language.getRegister("r1"); Register r1 = language.getRegister("r1");

View file

@ -314,7 +314,7 @@ public class PatchStep implements Step {
@Override @Override
public <T> void execute(PcodeThread<T> emuThread, Stepper<T> stepper, TaskMonitor monitor) public <T> void execute(PcodeThread<T> emuThread, Stepper<T> stepper, TaskMonitor monitor)
throws CancelledException { throws CancelledException {
PcodeProgram prog = emuThread.getMachine().compileSleigh("schedule", List.of(sleigh + ";")); PcodeProgram prog = emuThread.getMachine().compileSleigh("schedule", sleigh + ";");
emuThread.getExecutor().execute(prog, emuThread.getUseropLibrary()); emuThread.getExecutor().execute(prog, emuThread.getUseropLibrary());
} }
@ -368,7 +368,7 @@ public class PatchStep implements Step {
protected Map<AddressSpace, SemisparseByteArray> getPatches(Language language) { protected Map<AddressSpace, SemisparseByteArray> getPatches(Language language) {
PcodeProgram prog = SleighProgramCompiler.compileProgram((SleighLanguage) language, PcodeProgram prog = SleighProgramCompiler.compileProgram((SleighLanguage) language,
"schedule", List.of(sleigh + ";"), PcodeUseropLibrary.nil()); "schedule", sleigh + ";", PcodeUseropLibrary.nil());
// SemisparseArray is a bit overkill, no? // SemisparseArray is a bit overkill, no?
Map<AddressSpace, SemisparseByteArray> result = new TreeMap<>(); Map<AddressSpace, SemisparseByteArray> result = new TreeMap<>();
for (PcodeOp op : prog.getCode()) { for (PcodeOp op : prog.getCode()) {

View file

@ -35,7 +35,7 @@ import ghidra.util.database.UndoableTransaction;
public class AbstractTracePcodeEmulatorTest extends AbstractGhidraHeadlessIntegrationTest { public class AbstractTracePcodeEmulatorTest extends AbstractGhidraHeadlessIntegrationTest {
public TraceThread initTrace(ToyDBTraceBuilder tb, List<String> stateInit, public TraceThread initTrace(ToyDBTraceBuilder tb, String stateInit,
List<String> assembly) throws Throwable { List<String> assembly) throws Throwable {
return initTrace(tb, tb.range(0x00400000, 0x0040ffff), tb.range(0x00100000, 0x0010ffff), return initTrace(tb, tb.range(0x00400000, 0x0040ffff), tb.range(0x00100000, 0x0010ffff),
stateInit, assembly); stateInit, assembly);
@ -53,14 +53,13 @@ public class AbstractTracePcodeEmulatorTest extends AbstractGhidraHeadlessIntegr
* memory where it was assembled. * memory where it was assembled.
* *
* @param tb the trace builder * @param tb the trace builder
* @param stateInit SLEIGH source lines to execute to initialize the trace state before * @param stateInit Sleigh source to execute to initialize the trace state before emulation
* emulation. Each line must end with ";"
* @param assembly lines of assembly to place starting at {@code 0x00400000} * @param assembly lines of assembly to place starting at {@code 0x00400000}
* @return a new trace thread, whose register state is initialized as specified * @return a new trace thread, whose register state is initialized as specified
* @throws Throwable if anything goes wrong * @throws Throwable if anything goes wrong
*/ */
public TraceThread initTrace(ToyDBTraceBuilder tb, AddressRange text, AddressRange stack, public TraceThread initTrace(ToyDBTraceBuilder tb, AddressRange text, AddressRange stack,
List<String> stateInit, List<String> assembly) throws Throwable { String stateInit, List<String> assembly) throws Throwable {
TraceMemoryManager mm = tb.trace.getMemoryManager(); TraceMemoryManager mm = tb.trace.getMemoryManager();
TraceThread thread; TraceThread thread;
try (UndoableTransaction tid = tb.startTransaction()) { try (UndoableTransaction tid = tb.startTransaction()) {

View file

@ -54,10 +54,10 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
@Test @Test
public void testSinglePUSH() throws Throwable { public void testSinglePUSH() throws Throwable {
try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "x86:LE:64:default")) { try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "x86:LE:64:default")) {
TraceThread thread = initTrace(tb, TraceThread thread = initTrace(tb, """
List.of( RIP = 0x00400000;
"RIP = 0x00400000;", RSP = 0x00110000;
"RSP = 0x00110000;"), """,
List.of( List.of(
"PUSH 0xdeadbeef")); "PUSH 0xdeadbeef"));
@ -94,10 +94,10 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
@Test @Test
public void testDoublePUSH() throws Throwable { public void testDoublePUSH() throws Throwable {
try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "x86:LE:64:default")) { try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "x86:LE:64:default")) {
TraceThread thread = initTrace(tb, TraceThread thread = initTrace(tb, """
List.of( RIP = 0x00400000;
"RIP = 0x00400000;", RSP = 0x00110000;
"RSP = 0x00110000;"), """,
List.of( List.of(
"PUSH 0xdeadbeef", "PUSH 0xdeadbeef",
"PUSH 0xbaadf00d")); "PUSH 0xbaadf00d"));
@ -133,11 +133,11 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "x86:LE:64:default")) { try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "x86:LE:64:default")) {
Register pc = tb.language.getProgramCounter(); Register pc = tb.language.getProgramCounter();
TraceThread thread = initTrace(tb, TraceThread thread = initTrace(tb, """
List.of( RIP = 0x00400000;
"RIP = 0x00400000;", RSP = 0x00110000;
"RSP = 0x00110000;", RAX = 0x12345678;
"RAX = 0x12345678;"), """,
List.of( List.of(
"JMP 0x00400007", // 2 bytes "JMP 0x00400007", // 2 bytes
"MOV EAX,0xdeadbeef", // 5 bytes "MOV EAX,0xdeadbeef", // 5 bytes
@ -189,11 +189,12 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
AssemblyPatternBlock thumbPat = AssemblyPatternBlock.fromRegisterValue(thumbCtx); AssemblyPatternBlock thumbPat = AssemblyPatternBlock.fromRegisterValue(thumbCtx);
// NOTE: Assemble the thumb section separately // NOTE: Assemble the thumb section separately
TraceThread thread = initTrace(tb, // write 0x00401001 immediately after bx (0x00400008)
List.of( TraceThread thread = initTrace(tb, """
"pc = 0x00400000;", pc = 0x00400000;
"sp = 0x00110000;", sp = 0x00110000;
"*:4 0x00400008:4 = 0x00401001;"), // immediately after bx *:4 0x00400008:4 = 0x00401001;
""",
List.of( List.of(
"ldr r6, [pc,#0]!", // 4 bytes, pc+4 should be 00400008 "ldr r6, [pc,#0]!", // 4 bytes, pc+4 should be 00400008
"bx r6")); // 4 bytes "bx r6")); // 4 bytes
@ -241,10 +242,10 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "Toy:BE:64:default")) { try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "Toy:BE:64:default")) {
assertEquals(Register.NO_CONTEXT, tb.language.getContextBaseRegister()); assertEquals(Register.NO_CONTEXT, tb.language.getContextBaseRegister());
TraceThread thread = initTrace(tb, TraceThread thread = initTrace(tb, """
List.of( pc = 0x00400000;
"pc = 0x00400000;", sp = 0x00110000;
"sp = 0x00110000;"), """,
List.of( List.of(
"imm r0, #911")); // decimal "imm r0, #911")); // decimal
@ -272,10 +273,10 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
@Test @Test
public void testBRDS() throws Throwable { public void testBRDS() throws Throwable {
try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "Toy:BE:64:default")) { try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "Toy:BE:64:default")) {
TraceThread thread = initTrace(tb, TraceThread thread = initTrace(tb, """
List.of( pc = 0x00400000;
"pc = 0x00400000;", sp = 0x00110000;
"sp = 0x00110000;"), """,
List.of( List.of(
"brds 0x00400006", "brds 0x00400006",
"imm r0, #911", // decimal "imm r0, #911", // decimal
@ -314,13 +315,13 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
@Test @Test
public void testSelfModifyingX86() throws Throwable { public void testSelfModifyingX86() throws Throwable {
try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "x86:LE:64:default")) { try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "x86:LE:64:default")) {
TraceThread thread = initTrace(tb, // NB. Assembly actually happens first, so Sleigh will modify it
List.of( TraceThread thread = initTrace(tb, """
"RIP = 0x00400000;", RIP = 0x00400000;
"RSP = 0x00110000;", RSP = 0x00110000;
"RAX = 0x12345678;", RAX = 0x12345678;
// NB. Assembly actually happens first, so this is modifying *:1 0x00400007:8 = *0x00400007:8 ^ 0xcc;
"*:1 0x00400007:8 = *0x00400007:8 ^ 0xcc;"), """,
List.of( List.of(
// First instruction undoes the modification above // First instruction undoes the modification above
"XOR byte ptr [0x00400007], 0xcc", // 7 bytes "XOR byte ptr [0x00400007], 0xcc", // 7 bytes
@ -356,10 +357,10 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
@Test @Test
public void testDoublePUSH_pCode() throws Throwable { public void testDoublePUSH_pCode() throws Throwable {
try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "x86:LE:64:default")) { try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "x86:LE:64:default")) {
TraceThread thread = initTrace(tb, TraceThread thread = initTrace(tb, """
List.of( RIP = 0x00400000;
"RIP = 0x00400000;", RSP = 0x00110000;
"RSP = 0x00110000;"), """,
List.of( List.of(
"PUSH 0xdeadbeef", "PUSH 0xdeadbeef",
"PUSH 0xbaadf00d")); "PUSH 0xbaadf00d"));
@ -424,10 +425,10 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
dumped.append(NumericUtilities.convertBytesToString(in)); dumped.append(NumericUtilities.convertBytesToString(in));
} }
}; };
TraceThread thread = initTrace(tb, TraceThread thread = initTrace(tb, """
List.of( RIP = 0x00400000;
"RIP = 0x00400000;", RSP = 0x00110000;
"RSP = 0x00110000;"), """,
List.of( List.of(
"PUSH 0xdeadbeef", "PUSH 0xdeadbeef",
"PUSH 0xbaadf00d")); "PUSH 0xbaadf00d"));
@ -438,7 +439,7 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
return hexLib; return hexLib;
} }
}; };
emu.inject(tb.addr(0x00400006), List.of("hexdump(RSP);")); emu.inject(tb.addr(0x00400006), "hexdump(RSP);");
PcodeThread<byte[]> emuThread = emu.newThread(thread.getPath()); PcodeThread<byte[]> emuThread = emu.newThread(thread.getPath());
emuThread.overrideContextWithDefault(); emuThread.overrideContextWithDefault();
@ -472,10 +473,10 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
dumped.append(NumericUtilities.convertBytesToString(in)); dumped.append(NumericUtilities.convertBytesToString(in));
} }
}; };
TraceThread thread = initTrace(tb, TraceThread thread = initTrace(tb, """
List.of( RIP = 0x00400000;
"RIP = 0x00400000;", RSP = 0x00110000;
"RSP = 0x00110000;"), """,
List.of( List.of(
"PUSH 0xdeadbeef", "PUSH 0xdeadbeef",
"PUSH 0xbaadf00d")); "PUSH 0xbaadf00d"));
@ -486,12 +487,13 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
return hexLib; return hexLib;
} }
}; };
emu.inject(tb.addr(0x00400006), List.of( emu.inject(tb.addr(0x00400006), """
"hexdump(RSP);", hexdump(RSP);
"emu_swi();", emu_swi();
"hexdump(RIP);", hexdump(RIP);
"emu_exec_decoded();", emu_exec_decoded();
"hexdump(RIP);")); hexdump(RIP);
""");
PcodeThread<byte[]> emuThread = emu.newThread(thread.getPath()); PcodeThread<byte[]> emuThread = emu.newThread(thread.getPath());
emuThread.overrideContextWithDefault(); emuThread.overrideContextWithDefault();
@ -527,11 +529,11 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
@Test @Test
public void testBreakpoints() throws Throwable { public void testBreakpoints() throws Throwable {
try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "x86:LE:64:default")) { try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "x86:LE:64:default")) {
TraceThread thread = initTrace(tb, TraceThread thread = initTrace(tb, """
List.of( RIP = 0x00400000;
"RIP = 0x00400000;", RSP = 0x00110000;
"RSP = 0x00110000;", RAX = 0;
"RAX = 0;"), """,
List.of( List.of(
"PUSH 0xdeadbeef", "PUSH 0xdeadbeef",
"PUSH 0xbaadf00d")); "PUSH 0xbaadf00d"));
@ -561,11 +563,11 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
@Test @Test
public void testCLZ() throws Throwable { public void testCLZ() throws Throwable {
try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "ARM:LE:32:v8")) { try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "ARM:LE:32:v8")) {
TraceThread thread = initTrace(tb, TraceThread thread = initTrace(tb, """
List.of( pc = 0x00400000;
"pc = 0x00400000;", sp = 0x00110000;
"sp = 0x00110000;", r0 = 0x00008000;
"r0 = 0x00008000;"), """,
List.of( List.of(
"clz r1, r0")); "clz r1, r0"));
@ -594,12 +596,12 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "x86:LE:64:default")) { try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "x86:LE:64:default")) {
Register pc = tb.language.getProgramCounter(); Register pc = tb.language.getProgramCounter();
TraceThread thread = initTrace(tb, TraceThread thread = initTrace(tb, """
List.of( RIP = 0x00400000;
"RIP = 0x00400000;", RSP = 0x00110000;
"RSP = 0x00110000;", *:8 0x00600008:8 = 0x0123456789abcdef;
"*:8 0x00600008:8 = 0x0123456789abcdef;", // LE *:8 0x00600000:8 = 0xfedcba9876543210;
"*:8 0x00600000:8 = 0xfedcba9876543210;"), """,
List.of( List.of(
"MOVAPS XMM0, xmmword ptr [0x00600000]")); "MOVAPS XMM0, xmmword ptr [0x00600000]"));
@ -632,12 +634,12 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "x86:LE:64:default")) { try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "x86:LE:64:default")) {
Register pc = tb.language.getProgramCounter(); Register pc = tb.language.getProgramCounter();
TraceThread thread = initTrace(tb, TraceThread thread = initTrace(tb, """
List.of( RIP = 0x00400000;
"RIP = 0x00400000;", RSP = 0x00110000;
"RSP = 0x00110000;", RAX = 0x7fffffff;
"RAX = 0x7fffffff;", RCX = 4;
"RCX = 4;"), """,
List.of( List.of(
"SAR EAX, CL")); "SAR EAX, CL"));
@ -662,11 +664,11 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
@Test @Test
public void testCachedReadAfterSmallWrite() throws Throwable { public void testCachedReadAfterSmallWrite() throws Throwable {
try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "x86:LE:64:default")) { try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "x86:LE:64:default")) {
TraceThread thread = initTrace(tb, TraceThread thread = initTrace(tb, """
List.of( RIP = 0x00400000;
"RIP = 0x00400000;", RSP = 0x00110000;
"RSP = 0x00110000;", RAX = 0x12345678;
"RAX = 0x12345678;"), """,
List.of( List.of(
"XOR AH, AH", "XOR AH, AH",
"MOV RCX, RAX")); "MOV RCX, RAX"));
@ -689,9 +691,9 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
@Test(expected = AccessPcodeExecutionException.class) @Test(expected = AccessPcodeExecutionException.class)
public void testCheckedMOV_err() throws Throwable { public void testCheckedMOV_err() throws Throwable {
try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "x86:LE:64:default")) { try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "x86:LE:64:default")) {
TraceThread thread = initTrace(tb, TraceThread thread = initTrace(tb, """
List.of( RIP = 0x00400000;
"RIP = 0x00400000;"), """,
List.of( List.of(
"MOV RCX,RAX")); "MOV RCX,RAX"));
@ -711,10 +713,11 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
@Test @Test
public void testCheckedMOV_known() throws Throwable { public void testCheckedMOV_known() throws Throwable {
try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "x86:LE:64:default")) { try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "x86:LE:64:default")) {
TraceThread thread = initTrace(tb, // Make RAX known in the trace
List.of( TraceThread thread = initTrace(tb, """
"RIP = 0x00400000;", RIP = 0x00400000;
"RAX = 0x1234;"), // Make it known in the trace RAX = 0x1234;
""",
List.of( List.of(
"MOV RCX,RAX")); "MOV RCX,RAX"));
@ -735,10 +738,11 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
@Test(expected = AccessPcodeExecutionException.class) @Test(expected = AccessPcodeExecutionException.class)
public void testCheckedMOV_knownPast_err() throws Throwable { public void testCheckedMOV_knownPast_err() throws Throwable {
try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "x86:LE:64:default")) { try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "x86:LE:64:default")) {
TraceThread thread = initTrace(tb, // Make RAX known in the trace
List.of( TraceThread thread = initTrace(tb, """
"RIP = 0x00400000;", RIP = 0x00400000;
"RAX = 0x1234;"), // Make it known in the trace RAX = 0x1234;
""",
List.of( List.of(
"MOV RCX,RAX")); "MOV RCX,RAX"));
@ -760,10 +764,11 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
@Test @Test
public void testCheckedMOV_knownPast_has() throws Throwable { public void testCheckedMOV_knownPast_has() throws Throwable {
try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "x86:LE:64:default")) { try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "x86:LE:64:default")) {
TraceThread thread = initTrace(tb, // Make RAX known in the trace
List.of( TraceThread thread = initTrace(tb, """
"RIP = 0x00400000;", RIP = 0x00400000;
"RAX = 0x1234;"), // Make it known in the trace RAX = 0x1234;
""",
List.of( List.of(
"MOV RCX,RAX")); "MOV RCX,RAX"));
@ -785,9 +790,9 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
@Test @Test
public void testCheckedMOV_initialized() throws Throwable { public void testCheckedMOV_initialized() throws Throwable {
try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "x86:LE:64:default")) { try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "x86:LE:64:default")) {
TraceThread thread = initTrace(tb, TraceThread thread = initTrace(tb, """
List.of( RIP = 0x00400000;
"RIP = 0x00400000;"), """,
List.of( List.of(
"MOV RAX,0", // Have the program initialize it "MOV RAX,0", // Have the program initialize it
"MOV RCX,RAX")); "MOV RCX,RAX"));
@ -820,11 +825,11 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
ctxManager.setValue(lang, ctxVal, Range.atLeast(0L), ctxManager.setValue(lang, ctxVal, Range.atLeast(0L),
tb.range(0x00400000, 0x00400002)); tb.range(0x00400000, 0x00400002));
} }
TraceThread thread = initTrace(tb, TraceThread thread = initTrace(tb, """
List.of( RIP = 0x00400000;
"RIP = 0x00400000;", RSP = 0x00110000;
"RSP = 0x00110000;", RAX = 0xff12345678;
"RAX = 0xff12345678;"), """,
List.of( List.of(
"DEC EAX", "DEC EAX",
"MOV ECX,EAX")); "MOV ECX,EAX"));
@ -863,11 +868,11 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
@Test @Test
public void testMOV_EAX_dword_RBPm4() throws Throwable { public void testMOV_EAX_dword_RBPm4() throws Throwable {
try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "x86:LE:64:default")) { try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "x86:LE:64:default")) {
TraceThread thread = initTrace(tb, TraceThread thread = initTrace(tb, """
List.of( RIP = 0x00400000;
"RIP = 0x00400000;", RSP = 0x00110000;
"RSP = 0x00110000;", *:4 (0:8-4) = 0x12345678;
"*:4 (0:8-4) = 0x12345678;"), """,
List.of( List.of(
"MOV EAX, dword ptr [RBP + -0x4]")); "MOV EAX, dword ptr [RBP + -0x4]"));
@ -898,10 +903,10 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
@Test(expected = PcodeExecutionException.class) @Test(expected = PcodeExecutionException.class)
public void testMOV_EAX_dword_RBPm2_x64() throws Throwable { public void testMOV_EAX_dword_RBPm2_x64() throws Throwable {
try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "x86:LE:64:default")) { try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "x86:LE:64:default")) {
TraceThread thread = initTrace(tb, TraceThread thread = initTrace(tb, """
List.of( RIP = 0x00400000;
"RIP = 0x00400000;", RSP = 0x00110000;
"RSP = 0x00110000;"), """,
List.of( List.of(
"MOV EAX, dword ptr [RBP + -0x2]")); "MOV EAX, dword ptr [RBP + -0x2]"));
@ -921,10 +926,10 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
@Test(expected = PcodeExecutionException.class) @Test(expected = PcodeExecutionException.class)
public void testMOV_EAX_dword_EBPm2_x86() throws Throwable { public void testMOV_EAX_dword_EBPm2_x86() throws Throwable {
try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "x86:LE:32:default")) { try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "x86:LE:32:default")) {
TraceThread thread = initTrace(tb, TraceThread thread = initTrace(tb, """
List.of( EIP = 0x00400000;
"EIP = 0x00400000;", ESP = 0x00110000;
"ESP = 0x00110000;"), """,
List.of( List.of(
"MOV EAX, dword ptr [EBP + -0x2]")); "MOV EAX, dword ptr [EBP + -0x2]"));
@ -944,10 +949,10 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "Toy:BE:64:default")) { try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "Toy:BE:64:default")) {
assertEquals(Register.NO_CONTEXT, tb.language.getContextBaseRegister()); assertEquals(Register.NO_CONTEXT, tb.language.getContextBaseRegister());
TraceThread thread = initTrace(tb, TraceThread thread = initTrace(tb, """
List.of( pc = 0x00400000;
"pc = 0x00400000;", sp = 0x00110000;
"sp = 0x00110000;"), """,
List.of( List.of(
"unimpl")); "unimpl"));
@ -966,11 +971,11 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
Address stackStart = tb.language.getDefaultDataSpace().getAddress(0, true); Address stackStart = tb.language.getDefaultDataSpace().getAddress(0, true);
TraceThread thread = initTrace(tb, TraceThread thread = initTrace(tb,
new AddressRangeImpl(textStart, 0x200), new AddressRangeImpl(textStart, 0x200),
new AddressRangeImpl(stackStart, 1), new AddressRangeImpl(stackStart, 1), """
List.of( PC = 0x000100;
"PC = 0x000100;", W1 = 0x0800;
"W1 = 0x0800;", *[ram]:2 0x000800:3 = 0x1234;
"*[ram]:2 0x000800:3 = 0x1234;"), """,
List.of( List.of(
"mov.w [W1], W0")); "mov.w [W1], W0"));

View file

@ -19,7 +19,6 @@ import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.List;
import java.util.Map; import java.util.Map;
import org.junit.Before; import org.junit.Before;
@ -207,14 +206,14 @@ public class TraceSleighUtilsTest extends AbstractGhidraHeadlessIntegrationTest
public void testCompileSleighProgram() throws Exception { public void testCompileSleighProgram() throws Exception {
try (ToyDBTraceBuilder b = new ToyDBTraceBuilder("test", TOY_BE_64_HARVARD)) { try (ToyDBTraceBuilder b = new ToyDBTraceBuilder("test", TOY_BE_64_HARVARD)) {
PcodeProgram sp = SleighProgramCompiler.compileProgram((SleighLanguage) b.language, PcodeProgram sp = SleighProgramCompiler.compileProgram((SleighLanguage) b.language,
"test", List.of( "test", """
"if (r0) goto <else>;", if (r0) goto <else>;
" r1 = 6;", r1 = 6;
" goto <done>;", goto <done>;
"<else>", <else>
" r1 = 7;", r1 = 7;
"<done>"), <done>
PcodeUseropLibrary.NIL); """, PcodeUseropLibrary.NIL);
TraceThread thread; TraceThread thread;
try (UndoableTransaction tid = b.startTransaction()) { try (UndoableTransaction tid = b.startTransaction()) {
thread = b.getOrAddThread("Thread1", 0); thread = b.getOrAddThread("Thread1", 0);

View file

@ -27,7 +27,6 @@ import java.nio.charset.CharsetEncoder;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Collection; import java.util.Collection;
import java.util.List;
import com.google.common.collect.Range; import com.google.common.collect.Range;
@ -131,9 +130,9 @@ public class ToyDBTraceBuilder implements AutoCloseable {
* @param snap the snap to modify * @param snap the snap to modify
* @param frame the frame to modify * @param frame the frame to modify
* @param thread the thread to modify, can be {@code null} if only memory is used * @param thread the thread to modify, can be {@code null} if only memory is used
* @param sleigh the lines of Sleigh, including semicolons. * @param sleigh the Sleigh source
*/ */
public void exec(long snap, int frame, TraceThread thread, List<String> sleigh) { public void exec(long snap, int frame, TraceThread thread, String sleigh) {
PcodeProgram program = SleighProgramCompiler.compileProgram((SleighLanguage) language, PcodeProgram program = SleighProgramCompiler.compileProgram((SleighLanguage) language,
"builder", sleigh, PcodeUseropLibrary.nil()); "builder", sleigh, PcodeUseropLibrary.nil());
TraceSleighUtils.buildByteExecutor(trace, snap, thread, frame) TraceSleighUtils.buildByteExecutor(trace, snap, thread, frame)

View file

@ -15,8 +15,6 @@
*/ */
package ghidra.trace.model.time.schedule; package ghidra.trace.model.time.schedule;
import java.util.List;
import ghidra.app.plugin.processors.sleigh.SleighLanguage; import ghidra.app.plugin.processors.sleigh.SleighLanguage;
import ghidra.pcode.emu.PcodeThread; import ghidra.pcode.emu.PcodeThread;
import ghidra.pcode.emu.ThreadPcodeExecutorState; import ghidra.pcode.emu.ThreadPcodeExecutorState;
@ -167,7 +165,7 @@ class TestThread implements PcodeThread<Void> {
} }
@Override @Override
public void inject(Address address, List<String> sleigh) { public void inject(Address address, String source) {
} }
@Override @Override

View file

@ -257,19 +257,19 @@ public abstract class AbstractPcodeMachine<T> implements PcodeMachine<T> {
} }
@Override @Override
public PcodeProgram compileSleigh(String sourceName, List<String> lines) { public PcodeProgram compileSleigh(String sourceName, String source) {
return SleighProgramCompiler.compileProgram(language, sourceName, lines, stubLibrary); return SleighProgramCompiler.compileProgram(language, sourceName, source, stubLibrary);
} }
@Override @Override
public void inject(Address address, List<String> sleigh) { public void inject(Address address, String source) {
/** /**
* TODO: Can I compile the template and build as if the inject were a * TODO: Can I compile the template and build as if the inject were a
* instruction:^instruction constructor? This would require me to delay that build until * instruction:^instruction constructor? This would require me to delay that build until
* execution, or at least check for instruction modification, if I do want to cache the * execution, or at least check for instruction modification, if I do want to cache the
* built p-code. * built p-code.
*/ */
PcodeProgram pcode = compileSleigh("machine_inject:" + address, sleigh); PcodeProgram pcode = compileSleigh("machine_inject:" + address, source);
injects.put(address, pcode); injects.put(address, pcode);
} }
@ -292,11 +292,12 @@ public abstract class AbstractPcodeMachine<T> implements PcodeMachine<T> {
* addressed by formalizing and better exposing the notion of p-code stacks (of p-code * addressed by formalizing and better exposing the notion of p-code stacks (of p-code
* frames) * frames)
*/ */
PcodeProgram pcode = compileSleigh("breakpoint:" + address, List.of( PcodeProgram pcode = compileSleigh("breakpoint:" + address, String.format("""
"if (!(" + sleighCondition + ")) goto <nobreak>;", if (!(%s)) goto <nobreak>;
" emu_swi();", emu_swi();
"<nobreak>", <nobreak>
" emu_exec_decoded();")); emu_exec_decoded();
""", sleighCondition));
injects.put(address, pcode); injects.put(address, pcode);
} }
} }

View file

@ -33,10 +33,10 @@ import ghidra.util.Msg;
* The default implementation of {@link PcodeThread} suitable for most applications * The default implementation of {@link PcodeThread} suitable for most applications
* *
* <p> * <p>
* When emulating on concrete state, consider using {@link ModifiedPcodeThread}, so that * When emulating on concrete state, consider using {@link ModifiedPcodeThread}, so that state
* state modifiers from the older {@link Emulator} are incorporated. In either case, it may be * modifiers from the older {@link Emulator} are incorporated. In either case, it may be worthwhile
* worthwhile to examine existing state modifiers to ensure they are appropriately represented in * to examine existing state modifiers to ensure they are appropriately represented in any abstract
* any abstract state. It may be necessary to port them. * state. It may be necessary to port them.
* *
* <p> * <p>
* This class implements the control-flow logic of the target machine, cooperating with the p-code * This class implements the control-flow logic of the target machine, cooperating with the p-code
@ -148,9 +148,9 @@ public class DefaultPcodeThread<T> implements PcodeThread<T> {
} }
@Override @Override
public void executeSleighLine(String line) { public void executeSleigh(String source) {
PcodeProgram program = SleighProgramCompiler.compileProgram(language, "line", PcodeProgram program =
List.of(line + ";"), thread.library); SleighProgramCompiler.compileProgram(language, "exec", source, thread.library);
execute(program, thread.library); execute(program, thread.library);
} }
@ -588,9 +588,9 @@ public class DefaultPcodeThread<T> implements PcodeThread<T> {
} }
@Override @Override
public void inject(Address address, List<String> sleigh) { public void inject(Address address, String source) {
PcodeProgram pcode = SleighProgramCompiler.compileProgram( PcodeProgram pcode = SleighProgramCompiler.compileProgram(
language, "thread_inject:" + address, sleigh, library); language, "thread_inject:" + address, source, library);
injects.put(address, pcode); injects.put(address, pcode);
} }

View file

@ -16,7 +16,6 @@
package ghidra.pcode.emu; package ghidra.pcode.emu;
import java.util.Collection; import java.util.Collection;
import java.util.List;
import ghidra.app.plugin.processors.sleigh.SleighLanguage; import ghidra.app.plugin.processors.sleigh.SleighLanguage;
import ghidra.pcode.emu.DefaultPcodeThread.PcodeEmulationLibrary; import ghidra.pcode.emu.DefaultPcodeThread.PcodeEmulationLibrary;
@ -31,7 +30,7 @@ import ghidra.program.model.address.Address;
public interface PcodeMachine<T> { public interface PcodeMachine<T> {
/** /**
* Get the machine's SLEIGH language (processor model) * Get the machine's Sleigh language (processor model)
* *
* @return the language * @return the language
*/ */
@ -61,7 +60,7 @@ public interface PcodeMachine<T> {
* *
* <p> * <p>
* Thread userop libraries may have more userops than are defined in the machine's userop * Thread userop libraries may have more userops than are defined in the machine's userop
* library. However, to compile SLEIGH programs linked to thread libraries, the thread's userops * library. However, to compile Sleigh programs linked to thread libraries, the thread's userops
* must be known to the compiler. The stub library will name all userops common among the * must be known to the compiler. The stub library will name all userops common among the
* threads, even if their definitions vary. <b>WARNING:</b> The stub library is not required to * threads, even if their definitions vary. <b>WARNING:</b> The stub library is not required to
* provide implementations of the userops. Often they will throw exceptions, so do not attempt * provide implementations of the userops. Often they will throw exceptions, so do not attempt
@ -114,26 +113,26 @@ public interface PcodeMachine<T> {
PcodeExecutorState<T> getSharedState(); PcodeExecutorState<T> getSharedState();
/** /**
* Compile the given SLEIGH code for execution by a thread of this machine * Compile the given Sleigh code for execution by a thread of this machine
* *
* <p> * <p>
* This links in the userop library given at construction time and those defining the emulation * This links in the userop library given at construction time and those defining the emulation
* userops, e.g., {@code emu_swi}. * userops, e.g., {@code emu_swi}.
* *
* @param sourceName a user-defined source name for the resulting "program" * @param sourceName a user-defined source name for the resulting "program"
* @param lines the lines of SLEIGH source code * @param lines the Sleigh source
* @return the compiled program * @return the compiled program
*/ */
PcodeProgram compileSleigh(String sourceName, List<String> lines); PcodeProgram compileSleigh(String sourceName, String source);
/** /**
* Override the p-code at the given address with the given SLEIGH source * Override the p-code at the given address with the given Sleigh source
* *
* <p> * <p>
* This will attempt to compile the given source against this machine's userop library and then * This will attempt to compile the given source against this machine's userop library and then
* will inject it at the given address. The resulting p-code <em>replaces</em> that which would * will inject it at the given address. The resulting p-code <em>replaces</em> that which would
* be executed by decoding the instruction at the given address. The means the machine will not * be executed by decoding the instruction at the given address. The means the machine will not
* decode, nor advance its counter, unless the SLEIGH causes it. In most cases, the SLEIGH will * decode, nor advance its counter, unless the Sleigh causes it. In most cases, the Sleigh will
* call {@link PcodeEmulationLibrary#emu_exec_decoded()} to cause the machine to decode and * call {@link PcodeEmulationLibrary#emu_exec_decoded()} to cause the machine to decode and
* execute the overridden instruction. * execute the overridden instruction.
* *
@ -143,9 +142,9 @@ public interface PcodeMachine<T> {
* double-wrapping, etc. * double-wrapping, etc.
* *
* @param address the address to inject at * @param address the address to inject at
* @param sleigh the SLEIGH source to compile and inject * @param source the Sleigh source to compile and inject
*/ */
void inject(Address address, List<String> sleigh); void inject(Address address, String source);
/** /**
* Remove the inject, if present, at the given address * Remove the inject, if present, at the given address
@ -165,10 +164,10 @@ public interface PcodeMachine<T> {
* <p> * <p>
* Breakpoints are implemented at the p-code level using an inject, without modification to the * Breakpoints are implemented at the p-code level using an inject, without modification to the
* emulated image. As such, it cannot coexist with another inject. A client needing to break * emulated image. As such, it cannot coexist with another inject. A client needing to break
* during an inject must use {@link PcodeEmulationLibrary#emu_swi()} in the injected SLEIGH. * during an inject must use {@link PcodeEmulationLibrary#emu_swi()} in the injected Sleigh.
* *
* @param address the address at which to break * @param address the address at which to break
* @param sleighCondition a SLEIGH expression which controls the breakpoint * @param sleighCondition a Sleigh expression which controls the breakpoint
*/ */
void addBreakpoint(Address address, String sleighCondition); void addBreakpoint(Address address, String sleighCondition);
} }

View file

@ -336,9 +336,9 @@ public interface PcodeThread<T> {
* inject. * inject.
* *
* @param address the address to inject at * @param address the address to inject at
* @param sleigh the SLEIGH source to compile and inject * @param source the Sleigh source to compile and inject
*/ */
void inject(Address address, List<String> sleigh); void inject(Address address, String source);
/** /**
* Remove the per-thread inject, if present, at the given address * Remove the per-thread inject, if present, at the given address

View file

@ -28,9 +28,10 @@ public class X86PcodeStateInitializer implements PcodeStateInitializer {
private static final List<LanguageID> LANG_IDS = List.of( private static final List<LanguageID> LANG_IDS = List.of(
new LanguageID("x86:LE:32:default"), new LanguageID("x86:LE:32:default"),
new LanguageID("x86:LE:64:default")); new LanguageID("x86:LE:64:default"));
private static final List<String> SOURCE = List.of( private static final String SOURCE = """
"FS_OFFSET = 0;", FS_OFFSET = 0;
"GS_OFFSET = 0;"); GS_OFFSET = 0;
""";
@Override @Override
public boolean isApplicable(Language language) { public boolean isApplicable(Language language) {

View file

@ -91,13 +91,13 @@ public class PcodeExecutor<T> {
} }
/** /**
* Compile and execute a line of Sleigh * Compile and execute a block of Sleigh
* *
* @param line the line, excluding the semicolon * @param source the Sleigh source
*/ */
public void executeSleighLine(String line) { public void executeSleigh(String source) {
PcodeProgram program = SleighProgramCompiler.compileProgram(language, PcodeProgram program =
"line", List.of(line + ";"), PcodeUseropLibrary.NIL); SleighProgramCompiler.compileProgram(language, "exec", source, PcodeUseropLibrary.NIL);
execute(program, PcodeUseropLibrary.nil()); execute(program, PcodeUseropLibrary.nil());
} }

View file

@ -15,7 +15,6 @@
*/ */
package ghidra.pcode.exec; package ghidra.pcode.exec;
import java.text.MessageFormat;
import java.util.*; import java.util.*;
import ghidra.app.plugin.processors.sleigh.SleighLanguage; import ghidra.app.plugin.processors.sleigh.SleighLanguage;
@ -65,7 +64,7 @@ public class SleighPcodeUseropDefinition<T> implements PcodeUseropDefinition<T>
private final Factory factory; private final Factory factory;
private final String name; private final String name;
private final List<String> params = new ArrayList<>(); private final List<String> params = new ArrayList<>();
private final List<String> lines = new ArrayList<>(); private final StringBuffer body = new StringBuffer();
protected Builder(Factory factory, String name) { protected Builder(Factory factory, String name) {
this.factory = factory; this.factory = factory;
@ -93,41 +92,12 @@ public class SleighPcodeUseropDefinition<T> implements PcodeUseropDefinition<T>
} }
/** /**
* Add lines of SLEIGH source * Add Sleigh source to the body
* *
* <p> * @param additionalBody the additional source
* NOTE: The lines are joined only with line separators. No semicolons (;) are added at the
* end of each line.
*
* <p>
* TODO: See if this can be made any prettier with text blocks in newer Java versions.
*
* @param additionalLines the additional lines
* @return this builder
*/ */
public Builder sleigh(Collection<String> additionalLines) { public Builder body(CharSequence additionalBody) {
this.lines.addAll(additionalLines); body.append(additionalBody);
return this;
}
/**
* @see #sleigh(Collection)
*/
public Builder sleigh(String... additionalLines) {
return this.sleigh(Arrays.asList(additionalLines));
}
/**
* Treat each line as a pattern as in {@link MessageFormat#format(String, Object...)},
* replacing each with the result.
*
* @param arguments the arguments to pass to the formatter
* @return this builder
*/
public Builder applyAsPattern(Object[] arguments) {
for (int i = 0; i < lines.size(); i++) {
lines.set(i, MessageFormat.format(lines.get(i), arguments));
}
return this; return this;
} }
@ -144,23 +114,23 @@ public class SleighPcodeUseropDefinition<T> implements PcodeUseropDefinition<T>
*/ */
public <T> SleighPcodeUseropDefinition<T> build() { public <T> SleighPcodeUseropDefinition<T> build() {
return new SleighPcodeUseropDefinition<>(factory.language, name, List.copyOf(params), return new SleighPcodeUseropDefinition<>(factory.language, name, List.copyOf(params),
List.copyOf(lines)); body.toString());
} }
} }
private final SleighLanguage language; private final SleighLanguage language;
private final String name; private final String name;
private final List<String> params; private final List<String> params;
private final List<String> lines; private final String body;
private final Map<List<Varnode>, PcodeProgram> cacheByArgs = new HashMap<>(); private final Map<List<Varnode>, PcodeProgram> cacheByArgs = new HashMap<>();
protected SleighPcodeUseropDefinition(SleighLanguage language, String name, List<String> params, protected SleighPcodeUseropDefinition(SleighLanguage language, String name, List<String> params,
List<String> lines) { String body) {
this.language = language; this.language = language;
this.name = name; this.name = name;
this.params = params; this.params = params;
this.lines = lines; this.body = body;
} }
/** /**
@ -181,7 +151,7 @@ public class SleighPcodeUseropDefinition<T> implements PcodeUseropDefinition<T>
args.add(outArg); args.add(outArg);
args.addAll(inArgs); args.addAll(inArgs);
return cacheByArgs.computeIfAbsent(args, return cacheByArgs.computeIfAbsent(args,
a -> SleighProgramCompiler.compileUserop(language, name, params, lines, library, a)); a -> SleighProgramCompiler.compileUserop(language, name, params, body, library, a));
} }
@Override @Override
@ -211,11 +181,11 @@ public class SleighPcodeUseropDefinition<T> implements PcodeUseropDefinition<T>
} }
/** /**
* Get the lines of source that define this userop * Get the Sleigh source that defines this userop
* *
* @return the lines * @return the lines
*/ */
public List<String> getLines() { public String getBody() {
return lines; return body;
} }
} }

View file

@ -19,8 +19,6 @@ import java.io.IOException;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import ghidra.app.plugin.processors.sleigh.*; import ghidra.app.plugin.processors.sleigh.*;
import ghidra.app.plugin.processors.sleigh.template.ConstructTpl; import ghidra.app.plugin.processors.sleigh.template.ConstructTpl;
import ghidra.pcodeCPort.pcoderaw.VarnodeData; import ghidra.pcodeCPort.pcoderaw.VarnodeData;
@ -66,12 +64,12 @@ public class SleighProgramCompiler {
* @param language the language * @param language the language
* @param parser the parser * @param parser the parser
* @param sourceName the name of the program, for error diagnostics * @param sourceName the name of the program, for error diagnostics
* @param text the SLEIGH source * @param source the Sleigh source
* @return the constructor template * @return the constructor template
*/ */
public static ConstructTpl compileTemplate(Language language, PcodeParser parser, public static ConstructTpl compileTemplate(Language language, PcodeParser parser,
String sourceName, String text) { String sourceName, String source) {
return parser.compilePcode(text, EXPRESSION_SOURCE_NAME, 1); return parser.compilePcode(source, EXPRESSION_SOURCE_NAME, 1);
} }
/** /**
@ -164,33 +162,31 @@ public class SleighProgramCompiler {
} }
/** /**
* Compile the given SLEIGH source into a simple p-code program * Compile the given Sleigh source into a simple p-code program
* *
* <p> * <p>
* This is suitable for modifying program state using SLEIGH statements. Most likely, in * This is suitable for modifying program state using Sleigh statements. Most likely, in
* scripting, or perhaps in a SLEIGH repl. The library given during compilation must match the * scripting, or perhaps in a Sleigh repl. The library given during compilation must match the
* library given for execution, at least in its binding of userop IDs to symbols. * library given for execution, at least in its binding of userop IDs to symbols.
* *
* @param language the language of the target p-code machine * @param language the language of the target p-code machine
* @param sourceName a diagnostic name for the SLEIGH source * @param sourceName a diagnostic name for the Sleigh source
* @param lines the lines of SLEIGH source. These are joined with line separators but no * @param source the Sleigh source
* semicolon!
* @param library the userop library or stub library for binding userop symbols * @param library the userop library or stub library for binding userop symbols
* @return the compiled p-code program * @return the compiled p-code program
*/ */
public static PcodeProgram compileProgram(SleighLanguage language, String sourceName, public static PcodeProgram compileProgram(SleighLanguage language, String sourceName,
List<String> lines, PcodeUseropLibrary<?> library) { String source, PcodeUseropLibrary<?> library) {
PcodeParser parser = createParser(language); PcodeParser parser = createParser(language);
Map<Integer, UserOpSymbol> symbols = library.getSymbols(language); Map<Integer, UserOpSymbol> symbols = library.getSymbols(language);
addParserSymbols(parser, symbols); addParserSymbols(parser, symbols);
ConstructTpl template = ConstructTpl template = compileTemplate(language, parser, sourceName, source);
compileTemplate(language, parser, sourceName, StringUtils.join(lines, "\n"));
return constructProgram(PcodeProgram::new, language, template, symbols); return constructProgram(PcodeProgram::new, language, template, symbols);
} }
/** /**
* Compile the given SLEIGH expression into a p-code program that can evaluate it * Compile the given Sleigh expression into a p-code program that can evaluate it
* *
* <p> * <p>
* TODO: Currently, expressions cannot be compiled for a user-supplied userop library. The * TODO: Currently, expressions cannot be compiled for a user-supplied userop library. The
@ -198,7 +194,7 @@ public class SleighProgramCompiler {
* userop libraries are easily composed. It should be easy to add that feature if needed. * userop libraries are easily composed. It should be easy to add that feature if needed.
* *
* @param language the languge of the target p-code machine * @param language the languge of the target p-code machine
* @param expression the SLEIGH expression to be evaluated * @param expression the Sleigh expression to be evaluated
* @return a p-code program whose {@link PcodeExpression#evaluate(PcodeExecutor)} method will * @return a p-code program whose {@link PcodeExpression#evaluate(PcodeExecutor)} method will
* evaluate the expression on the given executor and its state. * evaluate the expression on the given executor and its state.
*/ */
@ -213,7 +209,7 @@ public class SleighProgramCompiler {
} }
/** /**
* Generate a SLEIGH symbol for context when compiling a userop definition * Generate a Sleigh symbol for context when compiling a userop definition
* *
* @param language the language of the target p-code machine * @param language the language of the target p-code machine
* @param sleigh a means of translating address spaces between execution and compilation * @param sleigh a means of translating address spaces between execution and compilation
@ -221,7 +217,7 @@ public class SleighProgramCompiler {
* @param opName a diagnostic name for the userop in which this parameter applies * @param opName a diagnostic name for the userop in which this parameter applies
* @param paramName the symbol name for the parameter * @param paramName the symbol name for the parameter
* @param arg the varnode to bind to the parameter symbol * @param arg the varnode to bind to the parameter symbol
* @return the named SLEIGH symbol bound to the given varnode * @return the named Sleigh symbol bound to the given varnode
*/ */
public static VarnodeSymbol paramSym(Language language, SleighBase sleigh, String opName, public static VarnodeSymbol paramSym(Language language, SleighBase sleigh, String opName,
String paramName, Varnode arg) { String paramName, Varnode arg) {
@ -232,11 +228,11 @@ public class SleighProgramCompiler {
} }
/** /**
* Compile the definition of a p-code userop from SLEIGH source into a p-code program * Compile the definition of a p-code userop from Sleigh source into a p-code program
* *
* <p> * <p>
* TODO: Defining a userop from SLEIGH source is currently a bit of a hack. It would be nice if * TODO: Defining a userop from Sleigh source is currently a bit of a hack. It would be nice if
* there were a formalization of SLEIGH/p-code subroutines. At the moment, the control flow for * there were a formalization of Sleigh/p-code subroutines. At the moment, the control flow for
* subroutines is handled out of band, which actually works fairly well. However, parameter * subroutines is handled out of band, which actually works fairly well. However, parameter
* passing and returning results is not well defined. The current solution is to alias the * passing and returning results is not well defined. The current solution is to alias the
* parameters to their arguments, implementing a pass-by-reference scheme. Similarly, the output * parameters to their arguments, implementing a pass-by-reference scheme. Similarly, the output
@ -256,14 +252,13 @@ public class SleighProgramCompiler {
* @param opName the name of the userop (used only for diagnostics here) * @param opName the name of the userop (used only for diagnostics here)
* @param params the names of parameters in order. Index 0 names the output symbol, probably * @param params the names of parameters in order. Index 0 names the output symbol, probably
* {@link SleighPcodeUseropDefinition#OUT_SYMBOL_NAME} * {@link SleighPcodeUseropDefinition#OUT_SYMBOL_NAME}
* @param lines the lines of SLEIGH source. These are joined with line separators but no * @param source the Sleigh source
* semicolon!
* @param library the userop library or stub library for binding userop symbols * @param library the userop library or stub library for binding userop symbols
* @param args the varnode arguments in order. Index 0 is the output varnode. * @param args the varnode arguments in order. Index 0 is the output varnode.
* @return a p-code program that implements the userop for the given arguments * @return a p-code program that implements the userop for the given arguments
*/ */
public static PcodeProgram compileUserop(SleighLanguage language, String opName, public static PcodeProgram compileUserop(SleighLanguage language, String opName,
List<String> params, List<String> lines, PcodeUseropLibrary<?> library, List<String> params, String source, PcodeUseropLibrary<?> library,
List<Varnode> args) { List<Varnode> args) {
PcodeParser parser = createParser(language); PcodeParser parser = createParser(language);
Map<Integer, UserOpSymbol> symbols = library.getSymbols(language); Map<Integer, UserOpSymbol> symbols = library.getSymbols(language);
@ -288,7 +283,6 @@ public class SleighProgramCompiler {
} }
} }
String source = StringUtils.join(lines, "\n");
try { try {
ConstructTpl template = compileTemplate(language, parser, opName, source); ConstructTpl template = compileTemplate(language, parser, opName, source);
return constructProgram(PcodeProgram::new, language, template, symbols); return constructProgram(PcodeProgram::new, language, template, symbols);

View file

@ -79,7 +79,7 @@ abstract class AbstractStmt implements Stmt {
* @param fall the label positioned immediately after this statement in the generated code * @param fall the label positioned immediately after this statement in the generated code
* @return the generated Sleigh code * @return the generated Sleigh code
*/ */
protected abstract String generate(Label next, Label fall); protected abstract StringTree generate(Label next, Label fall);
/** /**
* Check if the statement is or contains a single branch statement * Check if the statement is or contains a single branch statement

View file

@ -49,8 +49,14 @@ class AssignStmt extends AbstractStmt implements RValInternal, StmtWithVal {
} }
@Override @Override
protected String generate(Label next, Label fall) { protected StringTree generate(Label next, Label fall) {
return lhs.generate() + " = " + rhs.generate() + ";\n" + next.genGoto(fall); StringTree st = new StringTree();
st.append(lhs.generate());
st.append(" = ");
st.append(rhs.generate());
st.append(";\n");
st.append(next.genGoto(fall));
return st;
} }
@Override @Override

View file

@ -49,16 +49,16 @@ class BlockStmt extends AbstractStmt {
} }
@Override @Override
protected String generate(Label next, Label fall) { protected StringTree generate(Label next, Label fall) {
if (children.isEmpty()) { if (children.isEmpty()) {
return next.genGoto(fall); return next.genGoto(fall);
} }
StringBuilder sb = new StringBuilder(); StringTree st = new StringTree();
for (AbstractStmt c : children.subList(0, children.size() - 1)) { for (AbstractStmt c : children.subList(0, children.size() - 1)) {
sb.append(c.generate(ctx.FALL, ctx.FALL)); st.append(c.generate(ctx.FALL, ctx.FALL));
} }
sb.append(children.get(children.size() - 1).generate(next, fall)); st.append(children.get(children.size() - 1).generate(next, fall));
return sb.toString(); return st;
} }
@Override @Override

View file

@ -29,8 +29,14 @@ class DeclStmt extends AbstractStmt {
} }
@Override @Override
protected String generate(Label next, Label fall) { protected StringTree generate(Label next, Label fall) {
return "local " + name + ":" + type.getLength() + ";\n" + StringTree st = new StringTree();
next.genGoto(fall); st.append("local ");
st.append(name);
st.append(":");
st.append(Integer.toString(type.getLength()));
st.append(";\n");
st.append(next.genGoto(fall));
return st;
} }
} }

View file

@ -29,23 +29,26 @@ class ForStmt extends LoopStmt {
} }
@Override @Override
protected String generate(Label next, Label fall) { protected StringTree generate(Label next, Label fall) {
Label lTest = ctx.new FreshLabel(); Label lTest = ctx.new FreshLabel();
Label lBegin = ctx.new FreshLabel(); Label lBegin = ctx.new FreshLabel();
Label lExit = lBreak = next.freshOrBorrow(); Label lExit = lBreak = next.freshOrBorrow();
Label lStep = lContinue = ctx.new FreshLabel(); Label lStep = lContinue = ctx.new FreshLabel();
String initGen = init.generate(lTest, lTest); StringTree initGen = init.generate(lTest, lTest);
String testGen = lExit.genGoto(cond.notb(), lBegin); StringTree testGen = lExit.genGoto(cond.notb(), lBegin);
String stmtGen = stmt.generate(lStep, lStep); StringTree stmtGen = stmt.generate(lStep, lStep);
String stepGen = step.generate(lTest, fall); StringTree stepGen = step.generate(lTest, fall);
return initGen +
lTest.genAnchor() + StringTree st = new StringTree();
testGen + st.append(initGen);
lBegin.genAnchor() + st.append(lTest.genAnchor());
stmtGen + st.append(testGen);
lStep.genAnchor() + st.append(lBegin.genAnchor());
stepGen + st.append(stmtGen);
lExit.genAnchor(); st.append(lStep.genAnchor());
st.append(stepGen);
st.append(lExit.genAnchor());
return st;
} }
} }

View file

@ -25,7 +25,8 @@ class IfStmt extends ConditionalStmt {
} }
@Override @Override
protected String generate(Label next, Label fall) { protected StringTree generate(Label next, Label fall) {
StringTree st = new StringTree();
if (elseStmt == null) { if (elseStmt == null) {
if (stmt.isSingleGoto()) { if (stmt.isSingleGoto()) {
return stmt.getNext().genGoto(cond, fall); return stmt.getNext().genGoto(cond, fall);
@ -34,28 +35,31 @@ class IfStmt extends ConditionalStmt {
Label lTrue = ctx.new FreshLabel(); Label lTrue = ctx.new FreshLabel();
Label lFalse = next.freshOrBorrow(); Label lFalse = next.freshOrBorrow();
String condGen = lFalse.genGoto(cond.notb(), lTrue); StringTree condGen = lFalse.genGoto(cond.notb(), lTrue);
String stmtGen = stmt.generate(next, fall); StringTree stmtGen = stmt.generate(next, fall);
return condGen +
lTrue.genAnchor() +
stmtGen +
lFalse.genAnchor();
}
st.append(condGen);
st.append(lTrue.genAnchor());
st.append(stmtGen);
st.append(lFalse.genAnchor());
}
else {
Label lFalse = ctx.new FreshLabel(); Label lFalse = ctx.new FreshLabel();
Label lTrue = ctx.new FreshLabel(); Label lTrue = ctx.new FreshLabel();
Label lExit = next.freshOrBorrow(); Label lExit = next.freshOrBorrow();
String condGen = lTrue.genGoto(cond, lFalse); StringTree condGen = lTrue.genGoto(cond, lFalse);
String elseGen = elseStmt.generate(lExit, lTrue); StringTree elseGen = elseStmt.generate(lExit, lTrue);
String stmtGen = stmt.generate(next, fall); StringTree stmtGen = stmt.generate(next, fall);
return condGen + st.append(condGen);
lFalse.genAnchor() + st.append(lFalse.genAnchor());
elseGen + st.append(elseGen);
lTrue.genAnchor() + st.append(lTrue.genAnchor());
stmtGen + st.append(stmtGen);
lExit.genAnchor(); st.append(lExit.genAnchor());
}
return st;
} }
protected void addElse(Stmt elseStmt) { protected void addElse(Stmt elseStmt) {

View file

@ -32,7 +32,7 @@ abstract class LoopTruncateStmt extends AbstractStmt {
} }
@Override @Override
protected String generate(Label next, Label fall) { protected StringTree generate(Label next, Label fall) {
return getNext().genGoto(fall); return getNext().genGoto(fall);
} }

View file

@ -26,7 +26,11 @@ class RawStmt extends AbstractStmt {
} }
@Override @Override
protected String generate(Label next, Label fall) { protected StringTree generate(Label next, Label fall) {
return stmt + ";\n" + next.genGoto(fall); StringTree st = new StringTree();
st.append(stmt);
st.append(";\n");
st.append(next.genGoto(fall));
return st;
} }
} }

View file

@ -30,14 +30,19 @@ class ResultStmt extends AbstractStmt {
} }
@Override @Override
protected String generate(Label next, Label fall) { protected StringTree generate(Label next, Label fall) {
RoutineStmt routine = Objects.requireNonNull(nearest(RoutineStmt.class)); RoutineStmt routine = Objects.requireNonNull(nearest(RoutineStmt.class));
if (!ctx.isAssignable(routine.retType, result.getType())) { if (!ctx.isAssignable(routine.retType, result.getType())) {
ctx.emitResultTypeMismatch(routine, result); ctx.emitResultTypeMismatch(routine, result);
} }
return SleighPcodeUseropDefinition.OUT_SYMBOL_NAME + " = " + result.generate() + ";\n" + StringTree st = new StringTree();
routine.lReturn.genGoto(fall); st.append(SleighPcodeUseropDefinition.OUT_SYMBOL_NAME);
st.append(" = ");
st.append(result.generate());
st.append(";\n");
st.append(routine.lReturn.genGoto(fall));
return st;
} }
} }

View file

@ -27,7 +27,11 @@ class ReturnStmt extends AbstractStmt {
} }
@Override @Override
protected String generate(Label next, Label fall) { protected StringTree generate(Label next, Label fall) {
return "return " + target.generate() + ";\n"; StringTree st = new StringTree();
st.append("return ");
st.append(target.generate());
st.append(";\n");
return st;
} }
} }

View file

@ -32,14 +32,17 @@ class RoutineStmt extends BlockStmt {
} }
@Override @Override
protected String generate(Label next, Label fall) { protected StringTree generate(Label next, Label fall) {
if (children.isEmpty()) { if (children.isEmpty()) {
return ""; return StringTree.single("");
} }
Label lExit = lReturn = next.freshOrBorrow(); Label lExit = lReturn = next.freshOrBorrow();
// This is an odd case, because it's the root: use lExit instead of fall // This is an odd case, because it's the root: use lExit instead of fall
String blockGen = super.generate(lReturn, lExit); StringTree blockGen = super.generate(lReturn, lExit);
return blockGen +
lExit.genAnchor(); StringTree st = new StringTree();
st.append(blockGen);
st.append(lExit.genAnchor());
return st;
} }
} }

View file

@ -0,0 +1,76 @@
/* ###
* IP: GHIDRA
*
* 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.
*/
package ghidra.pcode.struct;
import java.util.LinkedList;
import java.util.List;
public class StringTree {
public static StringTree single(CharSequence seq) {
StringTree st = new StringTree();
st.append(seq);
return st;
}
interface Node {
void walk(StringBuffer buf);
}
class Branch implements Node {
List<Node> children = new LinkedList<>();
void addChild(Node child) {
children.add(child);
}
@Override
public void walk(StringBuffer buf) {
for (Node child : children) {
child.walk(buf);
}
}
}
class Leaf implements Node {
final CharSequence seq;
public Leaf(CharSequence seq) {
this.seq = seq;
}
@Override
public void walk(StringBuffer buf) {
buf.append(seq);
}
}
Branch root = new Branch();
public void append(CharSequence seq) {
root.addChild(new Leaf(seq));
}
public void append(StringTree tree) {
root.addChild(tree.root);
}
@Override
public String toString() {
StringBuffer buf = new StringBuffer();
root.walk(buf);
return buf.toString();
}
}

View file

@ -987,13 +987,14 @@ public class StructuredSleigh {
/** /**
* Generate code for this label * Generate code for this label
* *
* <p>
* This must be the last method called on the label, because it relies on knowing whether or * This must be the last method called on the label, because it relies on knowing whether or
* not the label is actually used. (The Sleigh compiler rejects code if it contains unused * not the label is actually used. (The Sleigh compiler rejects code if it contains unused
* labels.) * labels.)
* *
* @return the Sleigh code * @return the Sleigh code
*/ */
abstract String genAnchor(); abstract StringTree genAnchor();
/** /**
* Generate a reference to this label as it should appear in a Sleigh "{@code goto}" * Generate a reference to this label as it should appear in a Sleigh "{@code goto}"
@ -1001,7 +1002,7 @@ public class StructuredSleigh {
* *
* @return the label's expression * @return the label's expression
*/ */
abstract String ref(); abstract StringTree ref();
/** /**
* Generate a goto statement that targets this label * Generate a goto statement that targets this label
@ -1009,7 +1010,7 @@ public class StructuredSleigh {
* @param fall the label following the goto * @param fall the label following the goto
* @return the Sleigh code * @return the Sleigh code
*/ */
abstract String genGoto(Label fall); abstract StringTree genGoto(Label fall);
/** /**
* Generate a conditional goto statement that targets this label * Generate a conditional goto statement that targets this label
@ -1018,7 +1019,7 @@ public class StructuredSleigh {
* @param fall the label following the goto * @param fall the label following the goto
* @return the Sleigh code * @return the Sleigh code
*/ */
abstract String genGoto(RVal cond, Label fall); abstract StringTree genGoto(RVal cond, Label fall);
} }
/** /**
@ -1041,32 +1042,49 @@ public class StructuredSleigh {
} }
@Override @Override
public String genAnchor() { public StringTree genAnchor() {
if (name == null) { if (name == null) {
return ""; return StringTree.single("");
} }
return "<" + name + ">\n"; StringTree st = new StringTree();
st.append("<");
st.append(name);
st.append(">\n");
return st;
} }
@Override @Override
public String ref() { public StringTree ref() {
return "<" + getName() + ">"; StringTree st = new StringTree();
st.append("<");
st.append(getName());
st.append(">");
return st;
} }
@Override @Override
public String genGoto(Label fall) { public StringTree genGoto(Label fall) {
if (this == fall) { if (this == fall) {
return ""; return StringTree.single("");
} }
return "goto " + ref() + ";\n"; StringTree st = new StringTree();
st.append("goto ");
st.append(ref());
st.append(";\n");
return st;
} }
@Override @Override
public String genGoto(RVal cond, Label fall) { public StringTree genGoto(RVal cond, Label fall) {
if (this == fall) { if (this == fall) {
return ""; return StringTree.single("");
} }
return "if " + ((RValInternal) cond).generate() + " " + genGoto(fall); StringTree st = new StringTree();
st.append("if ");
st.append(((RValInternal) cond).generate());
st.append(" ");
st.append(genGoto(fall));
return st;
} }
} }
@ -1091,27 +1109,27 @@ public class StructuredSleigh {
} }
@Override @Override
public String genAnchor() { public StringTree genAnchor() {
return ""; return StringTree.single("");
} }
@Override @Override
public String ref() { public StringTree ref() {
return borrowed.ref(); return borrowed.ref();
} }
@Override @Override
public String genGoto(Label fall) { public StringTree genGoto(Label fall) {
if (this == fall) { // placed will also check if (this == fall) { // placed will also check
return ""; return StringTree.single("");
} }
return borrowed.genGoto(fall); return borrowed.genGoto(fall);
} }
@Override @Override
public String genGoto(RVal cond, Label fall) { public StringTree genGoto(RVal cond, Label fall) {
if (this == fall) { // placed with also check if (this == fall) { // placed with also check
return ""; return StringTree.single("");
} }
return borrowed.genGoto(cond, fall); return borrowed.genGoto(cond, fall);
} }
@ -1132,22 +1150,22 @@ public class StructuredSleigh {
} }
@Override @Override
public String genAnchor() { public StringTree genAnchor() {
return ""; return StringTree.single("");
} }
@Override @Override
public String ref() { public StringTree ref() {
throw new AssertionError(); throw new AssertionError();
} }
@Override @Override
public String genGoto(Label fall) { public StringTree genGoto(Label fall) {
return ""; return StringTree.single("");
} }
@Override @Override
public String genGoto(RVal cond, Label fall) { public StringTree genGoto(RVal cond, Label fall) {
throw new AssertionError(); throw new AssertionError();
} }
} }
@ -1703,8 +1721,8 @@ public class StructuredSleigh {
e); e);
} }
}); });
String source = root.generate(FALL, FALL); StringTree source = root.generate(FALL, FALL);
builder.sleigh(source); builder.body(source.toString());
return builder.build(); return builder.build();
} }

View file

@ -43,8 +43,12 @@ class VoidExprStmt extends AbstractStmt implements RValInternal, StmtWithVal {
} }
@Override @Override
protected String generate(Label next, Label fall) { protected StringTree generate(Label next, Label fall) {
return expr.generate() + ";\n" + next.genGoto(fall); StringTree st = new StringTree();
st.append(expr.generate());
st.append(";\n");
st.append(next.genGoto(fall));
return st;
} }
@Override @Override

View file

@ -23,17 +23,21 @@ class WhileStmt extends LoopStmt {
} }
@Override @Override
protected String generate(Label next, Label fall) { protected StringTree generate(Label next, Label fall) {
Label lTest = lContinue = ctx.new FreshLabel(); Label lTest = lContinue = ctx.new FreshLabel();
Label lBegin = ctx.new FreshLabel(); Label lBegin = ctx.new FreshLabel();
Label lExit = lBreak = next.freshOrBorrow(); Label lExit = lBreak = next.freshOrBorrow();
String testGen = lExit.genGoto(cond.notb(), lBegin); StringTree testGen = lExit.genGoto(cond.notb(), lBegin);
String stmtGen = stmt.generate(lTest, fall); StringTree stmtGen = stmt.generate(lTest, fall);
return lTest.genAnchor() +
testGen + StringTree st = new StringTree();
lBegin.genAnchor() +
stmtGen + st.append(lTest.genAnchor());
lExit.genAnchor(); st.append(testGen);
st.append(lBegin.genAnchor());
st.append(stmtGen);
st.append(lExit.genAnchor());
return st;
} }
} }

View file

@ -20,7 +20,6 @@ import static org.junit.Assert.assertTrue;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup; import java.lang.invoke.MethodHandles.Lookup;
import java.util.List;
import org.junit.Test; import org.junit.Test;
@ -53,15 +52,15 @@ public class AnnotatedPcodeUseropLibraryTest extends AbstractGhidraHeadlessInteg
} }
protected <T> void executeSleigh(PcodeExecutor<T> executor, PcodeUseropLibrary<T> library, protected <T> void executeSleigh(PcodeExecutor<T> executor, PcodeUseropLibrary<T> library,
String... lines) { String source) {
PcodeProgram program = SleighProgramCompiler.compileProgram(executor.getLanguage(), "test", PcodeProgram program = SleighProgramCompiler.compileProgram(executor.getLanguage(), "test",
List.of(lines), library); source, library);
executor.execute(program, library); executor.execute(program, library);
} }
protected void executeSleigh(PcodeUseropLibrary<byte[]> library, String... lines) protected void executeSleigh(PcodeUseropLibrary<byte[]> library, String source)
throws Exception { throws Exception {
executeSleigh(createBytesExecutor(), library, lines); executeSleigh(createBytesExecutor(), library, source);
} }
protected static void assertBytes(long expectedVal, int expectedSize, byte[] actual) { protected static void assertBytes(long expectedVal, int expectedSize, byte[] actual) {

View file

@ -17,8 +17,6 @@ package ghidra.pcode.exec;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import java.util.List;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@ -27,26 +25,34 @@ import ghidra.program.model.lang.LanguageID;
import ghidra.test.AbstractGhidraHeadlessIntegrationTest; import ghidra.test.AbstractGhidraHeadlessIntegrationTest;
public class PcodeFrameTest extends AbstractGhidraHeadlessIntegrationTest { public class PcodeFrameTest extends AbstractGhidraHeadlessIntegrationTest {
static final List<String> SAMPLE_ADD = List.of( static final String SAMPLE_ADD = """
"r0 = r0 + r1;"); r0 = r0 + r1;
static final List<String> SAMPLE_ADD2 = List.of( """;
"r0 = r0 + r1 + r2;"); static final String SAMPLE_ADD2 = """
static final List<String> SAMPLE_IF = List.of( r0 = r0 + r1 + r2;
"if (r0 == r1) goto <skip>;", """;
"r2 = r2 + 1;", static final String SAMPLE_IF = """
"<skip>"); if (r0 == r1) goto <skip>;
static final List<String> SAMPLE_LOOP = List.of( r2 = r2 + 1;
"<loop>", <skip>
"r0 = r0 + 1;", """;
"if (r0 == r1) goto <loop>;"); static final String SAMPLE_LOOP = """
static final List<String> SAMPLE_BRANCH = List.of( <loop>
"goto 0x1234;"); r0 = r0 + 1;
static final List<String> SAMPLE_LOAD = List.of( if (r0 == r1) goto <loop>;
"r0 = *:8 r1;"); """;
static final List<String> SAMPLE_LANG_USEROP = List.of( static final String SAMPLE_BRANCH = """
"pcodeop_one(r0);"); goto 0x1234;
static final List<String> SAMPLE_LIB_USEROP = List.of( """;
"__lib_userop(r0);"); static final String SAMPLE_LOAD = """
r0 = *:8 r1;
""";
static final String SAMPLE_LANG_USEROP = """
pcodeop_one(r0);
""";
static final String SAMPLE_LIB_USEROP = """
__lib_userop(r0);
""";
static class MyLib extends AnnotatedPcodeUseropLibrary<Void> { static class MyLib extends AnnotatedPcodeUseropLibrary<Void> {
@PcodeUserop @PcodeUserop
@ -63,11 +69,11 @@ public class PcodeFrameTest extends AbstractGhidraHeadlessIntegrationTest {
(SleighLanguage) getLanguageService().getLanguage(new LanguageID("Toy:BE:64:default")); (SleighLanguage) getLanguageService().getLanguage(new LanguageID("Toy:BE:64:default"));
} }
private PcodeProgram compile(List<String> sample) { private PcodeProgram compile(String sample) {
return SleighProgramCompiler.compileProgram(language, getName(), sample, library); return SleighProgramCompiler.compileProgram(language, getName(), sample, library);
} }
private PcodeFrame frame(List<String> sample) { private PcodeFrame frame(String sample) {
PcodeProgram program = compile(sample); PcodeProgram program = compile(sample);
return new PcodeFrame(language, program.code, program.useropNames); return new PcodeFrame(language, program.code, program.useropNames);
} }
@ -75,172 +81,172 @@ public class PcodeFrameTest extends AbstractGhidraHeadlessIntegrationTest {
@Test @Test
public void testProgramToStringAdd() throws Exception { public void testProgramToStringAdd() throws Exception {
PcodeProgram program = compile(SAMPLE_ADD); PcodeProgram program = compile(SAMPLE_ADD);
assertEquals("" + assertEquals("""
"<PcodeProgram:\n" + <PcodeProgram:
" r0 = INT_ADD r0, r1\n" + r0 = INT_ADD r0, r1
">", >""",
program.toString()); program.toString());
} }
@Test @Test
public void testProgramToStringAdd2() throws Exception { public void testProgramToStringAdd2() throws Exception {
PcodeProgram program = compile(SAMPLE_ADD2); PcodeProgram program = compile(SAMPLE_ADD2);
assertEquals("" + assertEquals("""
"<PcodeProgram:\n" + <PcodeProgram:
" $U2000:8 = INT_ADD r0, r1\n" + $U2000:8 = INT_ADD r0, r1
" r0 = INT_ADD $U2000:8, r2\n" + r0 = INT_ADD $U2000:8, r2
">", >""",
program.toString()); program.toString());
} }
@Test @Test
public void testProgramToStringIf() throws Exception { public void testProgramToStringIf() throws Exception {
PcodeProgram program = compile(SAMPLE_IF); PcodeProgram program = compile(SAMPLE_IF);
assertEquals("" + assertEquals("""
"<PcodeProgram:\n" + <PcodeProgram:
" $U2000:1 = INT_EQUAL r0, r1\n" + $U2000:1 = INT_EQUAL r0, r1
" CBRANCH <0>, $U2000:1\n" + CBRANCH <0>, $U2000:1
" r2 = INT_ADD r2, 1:8\n" + r2 = INT_ADD r2, 1:8
"<0>\n" + <0>
">", >""",
program.toString()); program.toString());
} }
@Test @Test
public void testProgramToStringLoop() throws Exception { public void testProgramToStringLoop() throws Exception {
PcodeProgram program = compile(SAMPLE_LOOP); PcodeProgram program = compile(SAMPLE_LOOP);
assertEquals("" + assertEquals("""
"<PcodeProgram:\n" + <PcodeProgram:
"<0>\n" + <0>
" r0 = INT_ADD r0, 1:8\n" + r0 = INT_ADD r0, 1:8
" $U2080:1 = INT_EQUAL r0, r1\n" + $U2080:1 = INT_EQUAL r0, r1
" CBRANCH <0>, $U2080:1\n" + CBRANCH <0>, $U2080:1
">", >""",
program.toString()); program.toString());
} }
@Test @Test
public void testProgramToStringLoad() throws Exception { public void testProgramToStringLoad() throws Exception {
PcodeProgram program = compile(SAMPLE_LOAD); PcodeProgram program = compile(SAMPLE_LOAD);
assertEquals("" + assertEquals("""
"<PcodeProgram:\n" + <PcodeProgram:
" r0 = LOAD ram(r1)\n" + r0 = LOAD ram(r1)
">", >""",
program.toString()); program.toString());
} }
@Test @Test
public void testProgramToStringLangUserop() throws Exception { public void testProgramToStringLangUserop() throws Exception {
PcodeProgram program = compile(SAMPLE_LANG_USEROP); PcodeProgram program = compile(SAMPLE_LANG_USEROP);
assertEquals("" + assertEquals("""
"<PcodeProgram:\n" + <PcodeProgram:
" CALLOTHER \"pcodeop_one\", r0\n" + CALLOTHER "pcodeop_one", r0
">", >""",
program.toString()); program.toString());
} }
@Test @Test
public void testProgramToStringLibUserop() throws Exception { public void testProgramToStringLibUserop() throws Exception {
PcodeProgram program = compile(SAMPLE_LIB_USEROP); PcodeProgram program = compile(SAMPLE_LIB_USEROP);
assertEquals("" + assertEquals("""
"<PcodeProgram:\n" + <PcodeProgram:
" CALLOTHER \"__lib_userop\", r0\n" + CALLOTHER "__lib_userop", r0
">", >""",
program.toString()); program.toString());
} }
@Test @Test
public void testFrameToStringAdd() throws Exception { public void testFrameToStringAdd() throws Exception {
PcodeFrame frame = frame(SAMPLE_ADD); PcodeFrame frame = frame(SAMPLE_ADD);
assertEquals("" + assertEquals("""
"<p-code frame: index=0 {\n" + <p-code frame: index=0 {
" -> r0 = INT_ADD r0, r1\n" + -> r0 = INT_ADD r0, r1
"}>", }>""",
frame.toString()); frame.toString());
frame.advance(); frame.advance();
assertEquals("" + assertEquals("""
"<p-code frame: index=1 {\n" + <p-code frame: index=1 {
" r0 = INT_ADD r0, r1\n" + r0 = INT_ADD r0, r1
" *> fall-through\n" + *> fall-through
"}>", }>""",
frame.toString()); frame.toString());
} }
@Test @Test
public void testFrameToStringIf() throws Exception { public void testFrameToStringIf() throws Exception {
PcodeFrame frame = frame(SAMPLE_IF); PcodeFrame frame = frame(SAMPLE_IF);
assertEquals("" + assertEquals("""
"<p-code frame: index=0 {\n" + <p-code frame: index=0 {
" -> $U2000:1 = INT_EQUAL r0, r1\n" + -> $U2000:1 = INT_EQUAL r0, r1
" CBRANCH <0>, $U2000:1\n" + CBRANCH <0>, $U2000:1
" r2 = INT_ADD r2, 1:8\n" + r2 = INT_ADD r2, 1:8
" <0>\n" + <0>
"}>", }>""",
frame.toString()); frame.toString());
frame.advance(); frame.advance();
frame.advance(); frame.advance();
frame.advance(); frame.advance();
assertEquals("" + assertEquals("""
"<p-code frame: index=3 {\n" + <p-code frame: index=3 {
" $U2000:1 = INT_EQUAL r0, r1\n" + $U2000:1 = INT_EQUAL r0, r1
" CBRANCH <0>, $U2000:1\n" + CBRANCH <0>, $U2000:1
" r2 = INT_ADD r2, 1:8\n" + r2 = INT_ADD r2, 1:8
" <0>\n" + <0>
" *> fall-through\n" + *> fall-through
"}>", }>""",
frame.toString()); frame.toString());
} }
@Test @Test
public void testFrameToStringLoop() throws Exception { public void testFrameToStringLoop() throws Exception {
PcodeFrame frame = frame(SAMPLE_LOOP); PcodeFrame frame = frame(SAMPLE_LOOP);
assertEquals("" + assertEquals("""
"<p-code frame: index=0 {\n" + <p-code frame: index=0 {
" <0>\n" + <0>
" -> r0 = INT_ADD r0, 1:8\n" + -> r0 = INT_ADD r0, 1:8
" $U2080:1 = INT_EQUAL r0, r1\n" + $U2080:1 = INT_EQUAL r0, r1
" CBRANCH <0>, $U2080:1\n" + CBRANCH <0>, $U2080:1
"}>", }>""",
frame.toString()); frame.toString());
} }
@Test @Test
public void testFrameToStringBranch() throws Exception { public void testFrameToStringBranch() throws Exception {
PcodeFrame frame = frame(SAMPLE_BRANCH); PcodeFrame frame = frame(SAMPLE_BRANCH);
assertEquals("" + assertEquals("""
"<p-code frame: index=0 {\n" + <p-code frame: index=0 {
" -> BRANCH *[ram]0x1234:8\n" + -> BRANCH *[ram]0x1234:8
"}>", }>""",
frame.toString()); frame.toString());
frame.advance(); frame.advance();
frame.finishAsBranch(); frame.finishAsBranch();
assertEquals("" + assertEquals("""
"<p-code frame: index=-1 branched=0 {\n" + <p-code frame: index=-1 branched=0 {
" *> BRANCH *[ram]0x1234:8\n" + *> BRANCH *[ram]0x1234:8
"}>", }>""",
frame.toString()); frame.toString());
} }
@Test @Test
public void testFrameToStringLangUserop() throws Exception { public void testFrameToStringLangUserop() throws Exception {
PcodeFrame frame = frame(SAMPLE_LANG_USEROP); PcodeFrame frame = frame(SAMPLE_LANG_USEROP);
assertEquals("" + assertEquals("""
"<p-code frame: index=0 {\n" + <p-code frame: index=0 {
" -> CALLOTHER \"pcodeop_one\", r0\n" + -> CALLOTHER \"pcodeop_one\", r0
"}>", }>""",
frame.toString()); frame.toString());
} }
@Test @Test
public void testFrameToStringLibUserop() throws Exception { public void testFrameToStringLibUserop() throws Exception {
PcodeFrame frame = frame(SAMPLE_LIB_USEROP); PcodeFrame frame = frame(SAMPLE_LIB_USEROP);
assertEquals("" + assertEquals("""
"<p-code frame: index=0 {\n" + <p-code frame: index=0 {
" -> CALLOTHER \"__lib_userop\", r0\n" + -> CALLOTHER \"__lib_userop\", r0
"}>", }>""",
frame.toString()); frame.toString());
} }
} }

View file

@ -65,7 +65,7 @@ public class StructuredSleighTest extends AbstractGhidraHeadlessIntegrationTest
} }
}; };
SleighPcodeUseropDefinition<Object> myUserop = ss.generate().get("my_userop"); SleighPcodeUseropDefinition<Object> myUserop = ss.generate().get("my_userop");
assertEquals(List.of("__op_output = (param_1 * 0x2:4);\n"), myUserop.getLines()); assertEquals("__op_output = (param_1 * 0x2:4);\n", myUserop.getBody());
} }
@Test(expected = SleighException.class) @Test(expected = SleighException.class)
@ -90,7 +90,7 @@ public class StructuredSleighTest extends AbstractGhidraHeadlessIntegrationTest
} }
}; };
SleighPcodeUseropDefinition<Object> myUserop = ss.generate().get("my_userop"); SleighPcodeUseropDefinition<Object> myUserop = ss.generate().get("my_userop");
assertEquals(List.of("__op_output = (r0 * 0x2:4);\n"), myUserop.getLines()); assertEquals("__op_output = (r0 * 0x2:4);\n", myUserop.getBody());
} }
@Test @Test
@ -103,10 +103,10 @@ public class StructuredSleighTest extends AbstractGhidraHeadlessIntegrationTest
} }
}; };
SleighPcodeUseropDefinition<Object> myUserop = ss.generate().get("my_userop"); SleighPcodeUseropDefinition<Object> myUserop = ss.generate().get("my_userop");
assertEquals(List.of("" + assertEquals("""
"local my_var:4;\n" + local my_var:4;
"__op_output = (my_var * 0x2:4);\n"), __op_output = (my_var * 0x2:4);
myUserop.getLines()); """, myUserop.getBody());
// Verify the source compiles // Verify the source compiles
myUserop.programFor(new Varnode(r0.getAddress(), r0.getNumBytes()), List.of(), myUserop.programFor(new Varnode(r0.getAddress(), r0.getNumBytes()), List.of(),
PcodeUseropLibrary.NIL); PcodeUseropLibrary.NIL);
@ -121,7 +121,7 @@ public class StructuredSleighTest extends AbstractGhidraHeadlessIntegrationTest
} }
}; };
SleighPcodeUseropDefinition<Object> myUserop = ss.generate().get("my_userop"); SleighPcodeUseropDefinition<Object> myUserop = ss.generate().get("my_userop");
assertEquals(List.of(""), myUserop.getLines()); assertEquals("", myUserop.getBody());
} }
@Test @Test
@ -137,14 +137,14 @@ public class StructuredSleighTest extends AbstractGhidraHeadlessIntegrationTest
} }
}; };
SleighPcodeUseropDefinition<Object> myUserop = ss.generate().get("my_userop"); SleighPcodeUseropDefinition<Object> myUserop = ss.generate().get("my_userop");
assertEquals(List.of("" + assertEquals("""
"if 0x1:1 goto <L1>;\n" + if 0x1:1 goto <L1>;
"tmp = 0x2:4;\n" + tmp = 0x2:4;
"goto <L2>;\n" + goto <L2>;
"<L1>\n" + <L1>
"tmp = 0x1:4;\n" + tmp = 0x1:4;
"<L2>\n"), <L2>
myUserop.getLines()); """, myUserop.getBody());
} }
@Test @Test
@ -158,11 +158,11 @@ public class StructuredSleighTest extends AbstractGhidraHeadlessIntegrationTest
} }
}; };
SleighPcodeUseropDefinition<Object> myUserop = ss.generate().get("my_userop"); SleighPcodeUseropDefinition<Object> myUserop = ss.generate().get("my_userop");
assertEquals(List.of("" + assertEquals("""
"if (!0x1:1) goto <L1>;\n" + if (!0x1:1) goto <L1>;
"tmp = 0x1:4;\n" + tmp = 0x1:4;
"<L1>\n"), <L1>
myUserop.getLines()); """, myUserop.getBody());
} }
@Test @Test
@ -179,17 +179,18 @@ public class StructuredSleighTest extends AbstractGhidraHeadlessIntegrationTest
} }
}; };
SleighPcodeUseropDefinition<Object> myUserop = ss.generate().get("my_userop"); SleighPcodeUseropDefinition<Object> myUserop = ss.generate().get("my_userop");
assertEquals(List.of("" + "local i:4;\n" + assertEquals("""
"local sum:4;\n" + local i:4;
"i = 0x0:4;\n" + local sum:4;
"<L2>\n" + i = 0x0:4;
"if (i >= n) goto <L1>;\n" + <L2>
"sum = (sum + i);\n" + if (i >= n) goto <L1>;
"i = (i + 0x1:4);\n" + sum = (sum + i);
"goto <L2>;\n" + i = (i + 0x1:4);
"<L1>\n" + goto <L2>;
"__op_output = sum;\n"), <L1>
myUserop.getLines()); __op_output = sum;
""", myUserop.getBody());
} }
@Test @Test
@ -209,19 +210,19 @@ public class StructuredSleighTest extends AbstractGhidraHeadlessIntegrationTest
} }
}; };
SleighPcodeUseropDefinition<Object> myUserop = ss.generate().get("my_userop"); SleighPcodeUseropDefinition<Object> myUserop = ss.generate().get("my_userop");
assertEquals(List.of("" + assertEquals("""
"local i:4;\n" + local i:4;
"local sum:4;\n" + local sum:4;
"i = 0x0:4;\n" + i = 0x0:4;
"<L2>\n" + <L2>
"if (i >= n) goto <L1>;\n" + if (i >= n) goto <L1>;
"sum = (sum + i);\n" + sum = (sum + i);
"if (sum >= 0x3e8:4) goto <L1>;\n" + if (sum >= 0x3e8:4) goto <L1>;
"i = (i + 0x1:4);\n" + i = (i + 0x1:4);
"goto <L2>;\n" + goto <L2>;
"<L1>\n" + <L1>
"__op_output = sum;\n"), __op_output = sum;
myUserop.getLines()); """, myUserop.getBody());
} }
@Test @Test
@ -233,7 +234,7 @@ public class StructuredSleighTest extends AbstractGhidraHeadlessIntegrationTest
} }
}; };
SleighPcodeUseropDefinition<Object> myUserop = ss.generate().get("my_userop"); SleighPcodeUseropDefinition<Object> myUserop = ss.generate().get("my_userop");
assertEquals(List.of("return (* 0xdeadbeef:8);\n"), myUserop.getLines()); assertEquals("return (* 0xdeadbeef:8);\n", myUserop.getBody());
// TODO: Test that the generated code compiles in a slaspec file. // TODO: Test that the generated code compiles in a slaspec file.
// It's rejected for injects because "return" is not valid there. // It's rejected for injects because "return" is not valid there.
} }

View file

@ -18,7 +18,6 @@ package ghidra.pcode.emu.taint.full;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import java.util.List;
import java.util.Set; import java.util.Set;
import org.junit.Before; import org.junit.Before;
@ -105,8 +104,7 @@ public class TaintDebuggerPcodeEmulatorTest extends AbstractGhidraHeadedDebugger
new DefaultTraceLocation(tb.trace, null, Range.atLeast(0L), tb.addr(0x55550000)), new DefaultTraceLocation(tb.trace, null, Range.atLeast(0L), tb.addr(0x55550000)),
new ProgramLocation(program, tb.addr(0x00400000)), 0x1000, false); new ProgramLocation(program, tb.addr(0x00400000)), 0x1000, false);
thread = tb.getOrAddThread("Threads[0]", 0); thread = tb.getOrAddThread("Threads[0]", 0);
tb.exec(0, 0, thread, List.of( tb.exec(0, 0, thread, "RIP = 0x55550000;");
"RIP = 0x55550000;"));
} }
waitForDomainObject(tb.trace); waitForDomainObject(tb.trace);
waitForPass(() -> assertEquals(new ProgramLocation(program, tb.addr(0x00400000)), waitForPass(() -> assertEquals(new ProgramLocation(program, tb.addr(0x00400000)),

View file

@ -194,7 +194,7 @@ public class TaintPcodeEmulatorTest extends AbstractGhidraHeadlessIntegrationTes
public void testTaintViaSleigh() throws Exception { public void testTaintViaSleigh() throws Exception {
prepareEmulator(); prepareEmulator();
PcodeThread<?> thread = launchThread(start); PcodeThread<?> thread = launchThread(start);
thread.getExecutor().executeSleighLine("*:8 0x00400000:8 = taint_arr(*:8 0x004000000:8)"); thread.getExecutor().executeSleigh("*:8 0x00400000:8 = taint_arr(*:8 0x004000000:8);");
Pair<byte[], TaintVec> taintVal = Pair<byte[], TaintVec> taintVal =
emulator.getSharedState().getVar(space, 0x00400000, 8, true); emulator.getSharedState().getVar(space, 0x00400000, 8, true);

View file

@ -63,7 +63,7 @@ public class TaintTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
@Test @Test
public void testReadStateMemory() throws Throwable { public void testReadStateMemory() throws Throwable {
try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "x86:LE:64:default")) { try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "x86:LE:64:default")) {
TraceThread thread = initTrace(tb, List.of(), List.of()); TraceThread thread = initTrace(tb, "", List.of());
try (UndoableTransaction tid = tb.startTransaction()) { try (UndoableTransaction tid = tb.startTransaction()) {
TracePropertyMap<String> taintMap = tb.trace.getAddressPropertyManager() TracePropertyMap<String> taintMap = tb.trace.getAddressPropertyManager()
@ -73,7 +73,7 @@ public class TaintTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
TaintTracePcodeEmulator emu = new TaintTracePcodeEmulator(tb.trace, 0); TaintTracePcodeEmulator emu = new TaintTracePcodeEmulator(tb.trace, 0);
PcodeThread<Pair<byte[], TaintVec>> emuThread = emu.newThread(thread.getPath()); PcodeThread<Pair<byte[], TaintVec>> emuThread = emu.newThread(thread.getPath());
emuThread.getExecutor().executeSleighLine("RAX = *0x00400000:8"); emuThread.getExecutor().executeSleigh("RAX = *0x00400000:8;");
Pair<byte[], TaintVec> valRAX = Pair<byte[], TaintVec> valRAX =
emuThread.getState().getVar(tb.language.getRegister("RAX")); emuThread.getState().getVar(tb.language.getRegister("RAX"));
@ -89,7 +89,7 @@ public class TaintTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
@Test @Test
public void testReadStateRegister() throws Throwable { public void testReadStateRegister() throws Throwable {
try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "x86:LE:64:default")) { try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "x86:LE:64:default")) {
TraceThread thread = initTrace(tb, List.of(), List.of()); TraceThread thread = initTrace(tb, "", List.of());
Register regRAX = tb.language.getRegister("RAX"); Register regRAX = tb.language.getRegister("RAX");
Register regEBX = tb.language.getRegister("EBX"); Register regEBX = tb.language.getRegister("EBX");
@ -103,7 +103,7 @@ public class TaintTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
TaintTracePcodeEmulator emu = new TaintTracePcodeEmulator(tb.trace, 0); TaintTracePcodeEmulator emu = new TaintTracePcodeEmulator(tb.trace, 0);
PcodeThread<Pair<byte[], TaintVec>> emuThread = emu.newThread(thread.getPath()); PcodeThread<Pair<byte[], TaintVec>> emuThread = emu.newThread(thread.getPath());
emuThread.getExecutor().executeSleighLine("RAX = RBX"); emuThread.getExecutor().executeSleigh("RAX = RBX;");
Pair<byte[], TaintVec> valRAX = Pair<byte[], TaintVec> valRAX =
emuThread.getState().getVar(regRAX); emuThread.getState().getVar(regRAX);
@ -119,7 +119,7 @@ public class TaintTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
@Test @Test
public void testWriteStateMemory() throws Throwable { public void testWriteStateMemory() throws Throwable {
try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "x86:LE:64:default")) { try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "x86:LE:64:default")) {
initTrace(tb, List.of(), List.of()); initTrace(tb, "", List.of());
TaintTracePcodeEmulator emu = new TaintTracePcodeEmulator(tb.trace, 0); TaintTracePcodeEmulator emu = new TaintTracePcodeEmulator(tb.trace, 0);
TaintVec taintVal = TaintVec.empties(8); TaintVec taintVal = TaintVec.empties(8);
@ -146,7 +146,7 @@ public class TaintTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
public void testWriteStateRegister() throws Throwable { public void testWriteStateRegister() throws Throwable {
try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "x86:LE:64:default")) { try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "x86:LE:64:default")) {
AddressSpace rs = tb.language.getAddressFactory().getRegisterSpace(); AddressSpace rs = tb.language.getAddressFactory().getRegisterSpace();
TraceThread thread = initTrace(tb, List.of(), List.of()); TraceThread thread = initTrace(tb, "", List.of());
TaintTracePcodeEmulator emu = new TaintTracePcodeEmulator(tb.trace, 0); TaintTracePcodeEmulator emu = new TaintTracePcodeEmulator(tb.trace, 0);
PcodeThread<Pair<byte[], TaintVec>> emuThread = emu.newThread(thread.getPath()); PcodeThread<Pair<byte[], TaintVec>> emuThread = emu.newThread(thread.getPath());
@ -176,9 +176,9 @@ public class TaintTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
public void testEmptyTaintClears() throws Throwable { public void testEmptyTaintClears() throws Throwable {
try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "x86:LE:64:default")) { try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "x86:LE:64:default")) {
AddressSpace ram = tb.language.getAddressFactory().getDefaultAddressSpace(); AddressSpace ram = tb.language.getAddressFactory().getDefaultAddressSpace();
TraceThread thread = initTrace(tb, TraceThread thread = initTrace(tb, """
List.of( RIP = 0x00400000;
"RIP = 0x00400000;"), """,
List.of( List.of(
"MOV qword ptr [0x00600000], RAX", "MOV qword ptr [0x00600000], RAX",
"MOV qword ptr [0x00600000], RBX")); "MOV qword ptr [0x00600000], RBX"));
@ -214,9 +214,9 @@ public class TaintTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
@Test @Test
public void testZeroByXor() throws Throwable { public void testZeroByXor() throws Throwable {
try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "x86:LE:64:default")) { try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "x86:LE:64:default")) {
TraceThread thread = initTrace(tb, TraceThread thread = initTrace(tb, """
List.of( RIP = 0x00400000;
"RIP = 0x00400000;"), """,
List.of( List.of(
"XOR RAX, RAX")); "XOR RAX, RAX"));
@ -245,9 +245,9 @@ public class TaintTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
@Test @Test
public void testZeroByXorVia32() throws Throwable { public void testZeroByXorVia32() throws Throwable {
try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "x86:LE:64:default")) { try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "x86:LE:64:default")) {
TraceThread thread = initTrace(tb, TraceThread thread = initTrace(tb, """
List.of( RIP = 0x00400000;
"RIP = 0x00400000;"), """,
List.of( List.of(
"XOR EAX, EAX")); "XOR EAX, EAX"));