mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-06 03:50:02 +02:00
Merge remote-tracking branch 'origin/GP-1225_Dan_pcodeFrameUseropDisplay--SQUASHED'
This commit is contained in:
commit
185bc0b2c2
9 changed files with 163 additions and 16 deletions
|
@ -215,7 +215,10 @@ public class DebuggerPcodeStepperProvider extends ComponentProviderAdapter {
|
|||
boolean isCurrent = counter == data.getRowModelIndex();
|
||||
if (data.isSelected()) {
|
||||
if (isCurrent) {
|
||||
setBackground(ColorUtils.blend(counterColor, cursorColor, 0.5f));
|
||||
Color blend = ColorUtils.blend(counterColor, cursorColor, 0.5f);
|
||||
if (blend != null) {
|
||||
setBackground(blend);
|
||||
}
|
||||
}
|
||||
// else background is already set. Leave it alone
|
||||
}
|
||||
|
@ -574,7 +577,8 @@ public class DebuggerPcodeStepperProvider extends ComponentProviderAdapter {
|
|||
int index = frame.index();
|
||||
List<PcodeRow> toAdd = frame.getCode()
|
||||
.stream()
|
||||
.map(op -> new OpPcodeRow(language, op, index == op.getSeqnum().getTime()))
|
||||
.map(op -> new OpPcodeRow(language, op, index == op.getSeqnum().getTime(),
|
||||
frame.getUseropNames()))
|
||||
.collect(Collectors.toCollection(ArrayList::new));
|
||||
if (frame.isBranch()) {
|
||||
counter = toAdd.size();
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.debug.gui.pcode;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import ghidra.pcode.exec.PcodeProgram;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.program.model.pcode.PcodeOp;
|
||||
|
@ -23,11 +25,14 @@ public class OpPcodeRow implements PcodeRow {
|
|||
protected final Language language;
|
||||
protected final PcodeOp op;
|
||||
protected final boolean isNext;
|
||||
protected final Map<Integer, String> useropNames;
|
||||
|
||||
public OpPcodeRow(Language language, PcodeOp op, boolean isNext) {
|
||||
public OpPcodeRow(Language language, PcodeOp op, boolean isNext,
|
||||
Map<Integer, String> useropNames) {
|
||||
this.language = language;
|
||||
this.op = op;
|
||||
this.isNext = isNext;
|
||||
this.useropNames = useropNames;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -37,7 +42,7 @@ public class OpPcodeRow implements PcodeRow {
|
|||
|
||||
@Override
|
||||
public String getCode() {
|
||||
return "<html>" + PcodeProgram.opToString(language, op, true) + "</html>";
|
||||
return "<html>" + PcodeProgram.opToString(language, op, true, useropNames) + "</html>";
|
||||
}
|
||||
|
||||
public boolean isNext() {
|
||||
|
|
|
@ -56,7 +56,7 @@ public interface DebuggerEmulationService {
|
|||
*
|
||||
* <p>
|
||||
* This is the preferred means of performing emulation. Because the underlying emulator may
|
||||
* request <em>blocking</em> read of a target, it is important that
|
||||
* request a <em>blocking</em> read from a target, it is important that
|
||||
* {@link #emulate(Trace, TraceSchedule, TaskMonitor)} is <em>never</em> called by the Swing
|
||||
* thread.
|
||||
*
|
||||
|
@ -72,7 +72,9 @@ public interface DebuggerEmulationService {
|
|||
*
|
||||
* <p>
|
||||
* To guarantee the emulator is present, call {@link #backgroundEmulate(Trace, TraceSchedule)}
|
||||
* first. WARNING: This emulator belongs to this service. Stepping it, or otherwise manipulating
|
||||
* first.
|
||||
* <p>
|
||||
* <b>WARNING:</b> This emulator belongs to this service. Stepping it, or otherwise manipulating
|
||||
* it without the service's knowledge can lead to unintended consequences.
|
||||
*
|
||||
* @param trace the trace containing the initial state
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
/* ###
|
||||
* 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.app.plugin.core.debug.gui.pcode;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.google.common.collect.Range;
|
||||
|
||||
import ghidra.app.plugin.assembler.Assembler;
|
||||
import ghidra.app.plugin.assembler.Assemblers;
|
||||
import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerGUITest;
|
||||
import ghidra.app.plugin.core.debug.gui.listing.DebuggerListingPlugin;
|
||||
import ghidra.app.plugin.core.debug.service.emulation.DebuggerTracePcodeEmulator;
|
||||
import ghidra.app.plugin.core.debug.service.tracemgr.DebuggerTraceManagerServicePlugin;
|
||||
import ghidra.app.services.DebuggerEmulationService;
|
||||
import ghidra.app.services.DebuggerTraceManagerService;
|
||||
import ghidra.pcode.emu.PcodeThread;
|
||||
import ghidra.pcode.exec.PcodeExecutor;
|
||||
import ghidra.pcode.exec.trace.TraceSleighUtils;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Instruction;
|
||||
import ghidra.program.model.listing.InstructionIterator;
|
||||
import ghidra.trace.model.memory.TraceMemoryFlag;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.trace.model.time.TraceSchedule;
|
||||
import ghidra.util.database.UndoableTransaction;
|
||||
|
||||
public class DebuggerPcodeStepperProviderTest extends AbstractGhidraHeadedDebuggerGUITest {
|
||||
|
||||
protected DebuggerTraceManagerService traceManager;
|
||||
protected DebuggerPcodeStepperPlugin pcodePlugin;
|
||||
protected DebuggerListingPlugin listingPlugin;
|
||||
|
||||
protected DebuggerPcodeStepperProvider pcodeProvider;
|
||||
protected DebuggerEmulationService emuService;
|
||||
|
||||
@Before
|
||||
public void setUpPcodeStepperProviderTest() throws Exception {
|
||||
traceManager = addPlugin(tool, DebuggerTraceManagerServicePlugin.class);
|
||||
pcodePlugin = addPlugin(tool, DebuggerPcodeStepperPlugin.class);
|
||||
listingPlugin = addPlugin(tool, DebuggerListingPlugin.class); // For colors
|
||||
emuService = tool.getService(DebuggerEmulationService.class);
|
||||
|
||||
pcodeProvider = waitForComponentProvider(DebuggerPcodeStepperProvider.class);
|
||||
|
||||
createTrace();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomUseropDisplay() throws Exception {
|
||||
Address start = tb.addr(0x00400000);
|
||||
TraceThread thread;
|
||||
InstructionIterator iit;
|
||||
try (UndoableTransaction tid = tb.startTransaction()) {
|
||||
tb.trace.getMemoryManager()
|
||||
.addRegion("echo:.text", Range.atLeast(0L), tb.range(0x00400000, 0x0040ffff),
|
||||
TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE);
|
||||
|
||||
thread = tb.getOrAddThread("1", 0);
|
||||
|
||||
PcodeExecutor<byte[]> init = TraceSleighUtils.buildByteExecutor(tb.trace, 0, thread, 0);
|
||||
init.executeLine("pc = 0x00400000");
|
||||
|
||||
Assembler asm = Assemblers.getAssembler(tb.trace.getFixedProgramView(0));
|
||||
iit = asm.assemble(start,
|
||||
"imm r0, #1234",
|
||||
"imm r1, #2045"); // 11 bits unsigned
|
||||
|
||||
}
|
||||
Instruction imm1234 = iit.next();
|
||||
Instruction imm2045 = iit.next();
|
||||
|
||||
TraceSchedule schedule1 = TraceSchedule.parse("0:.t0-1");
|
||||
traceManager.openTrace(tb.trace);
|
||||
traceManager.activateThread(thread);
|
||||
traceManager.activateTime(schedule1);
|
||||
|
||||
DebuggerTracePcodeEmulator emu =
|
||||
waitForValue(() -> emuService.getCachedEmulator(tb.trace, schedule1));
|
||||
assertNotNull(emu);
|
||||
|
||||
// P-code step to decode already done. One for each op. One to retire.
|
||||
TraceSchedule schedule2 =
|
||||
schedule1.steppedPcodeForward(thread, imm1234.getPcode().length + 1);
|
||||
PcodeThread<byte[]> et = emu.getThread(thread.getPath(), false);
|
||||
traceManager.activateTime(schedule2);
|
||||
waitForPass(() -> assertNull(et.getFrame()));
|
||||
|
||||
/**
|
||||
* NB. at the moment, there is no API to customize the service's emulator. In the meantime,
|
||||
* the vanilla PcodeThread does inject a custom library for breakpoints, so we'll use that
|
||||
* as our "custom userop" test case. It might also be nice if the emulator service placed
|
||||
* breakpoints, no?
|
||||
*/
|
||||
emu.addBreakpoint(imm2045.getAddress(), "1:1");
|
||||
|
||||
// Just one to decode is necessary
|
||||
TraceSchedule schedule3 = schedule2.steppedPcodeForward(thread, 1);
|
||||
traceManager.activateTime(schedule3);
|
||||
waitForPass(() -> assertEquals(schedule3, pcodeProvider.current.getTime()));
|
||||
|
||||
assertTrue(pcodeProvider.pcodeTableModel.getModelData()
|
||||
.stream()
|
||||
.anyMatch(r -> r.getCode().contains("emu_swi")));
|
||||
}
|
||||
}
|
|
@ -258,7 +258,7 @@ public class DefaultPcodeThread<T> implements PcodeThread<T> {
|
|||
}
|
||||
|
||||
protected void beginInstructionOrInject() {
|
||||
PcodeProgram inj = injects.get(counter);
|
||||
PcodeProgram inj = getInject(counter);
|
||||
if (inj != null) {
|
||||
instruction = null;
|
||||
frame = executor.begin(inj);
|
||||
|
|
|
@ -69,7 +69,8 @@ public class PcodeFrame {
|
|||
sb.append(" ");
|
||||
}
|
||||
PcodeOp op = code.get(i);
|
||||
sb.append(op.getSeqnum() + ": " + PcodeProgram.opToString(language, op, false));
|
||||
sb.append(
|
||||
op.getSeqnum() + ": " + PcodeProgram.opToString(language, op, false, useropNames));
|
||||
}
|
||||
if (index == code.size()) {
|
||||
sb.append("\n *> fall-through");
|
||||
|
@ -98,6 +99,10 @@ public class PcodeFrame {
|
|||
return useropNames.get(userop);
|
||||
}
|
||||
|
||||
public Map<Integer, String> getUseropNames() {
|
||||
return useropNames;
|
||||
}
|
||||
|
||||
public boolean isFallThrough() {
|
||||
return index == code.size();
|
||||
}
|
||||
|
|
|
@ -102,11 +102,12 @@ public class PcodeProgram {
|
|||
}
|
||||
}
|
||||
|
||||
public static String useropToString(Language language, Varnode vn, boolean markup) {
|
||||
public static String useropToString(Language language, Varnode vn, boolean markup,
|
||||
Map<Integer, String> useropNames) {
|
||||
if (!vn.isConstant()) {
|
||||
throw new IllegalArgumentException("userop index must be a constant varnode");
|
||||
}
|
||||
String display = "\"" + language.getUserDefinedOpName((int) vn.getOffset()) + "\"";
|
||||
String display = "\"" + useropNames.get((int) vn.getOffset()) + "\"";
|
||||
if (markup) {
|
||||
return htmlSpan("userop", display);
|
||||
}
|
||||
|
@ -124,7 +125,8 @@ public class PcodeProgram {
|
|||
}
|
||||
}
|
||||
|
||||
public static String opToString(Language language, PcodeOp op, boolean markup) {
|
||||
public static String opToString(Language language, PcodeOp op, boolean markup,
|
||||
Map<Integer, String> useropNames) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
Varnode output = op.getOutput();
|
||||
if (output != null) {
|
||||
|
@ -146,7 +148,7 @@ public class PcodeProgram {
|
|||
}
|
||||
else if (isUserop) {
|
||||
sb.append(' ');
|
||||
sb.append(useropToString(language, op.getInput(0), markup));
|
||||
sb.append(useropToString(language, op.getInput(0), markup, useropNames));
|
||||
i = 1;
|
||||
}
|
||||
else {
|
||||
|
@ -208,7 +210,8 @@ public class PcodeProgram {
|
|||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder("<" + getHead() + ":");
|
||||
for (PcodeOp op : code) {
|
||||
sb.append("\n " + op.getSeqnum() + ": " + opToString(language, op, false));
|
||||
sb.append(
|
||||
"\n " + op.getSeqnum() + ": " + opToString(language, op, false, useropNames));
|
||||
}
|
||||
sb.append("\n>");
|
||||
return sb.toString();
|
||||
|
|
|
@ -168,7 +168,12 @@ public class ColorUtils {
|
|||
* @return the new color
|
||||
*/
|
||||
public static Color blend(Color c1, Color c2, float ratio) {
|
||||
|
||||
if (c1 == null) {
|
||||
return c2;
|
||||
}
|
||||
if (c2 == null) {
|
||||
return c1;
|
||||
}
|
||||
float rgb1[] = new float[3];
|
||||
float rgb2[] = new float[3];
|
||||
c1.getColorComponents(rgb1);
|
||||
|
|
|
@ -113,8 +113,9 @@ public class SleighAssembler implements Assembler {
|
|||
Address end = at.add(insbytes.length - 1);
|
||||
listing.clearCodeUnits(at, end, false);
|
||||
memory.setBytes(at, insbytes);
|
||||
dis.disassemble(at, new AddressSet(at));
|
||||
return listing.getInstructions(new AddressSet(at, end), true);
|
||||
AddressSet set = new AddressSet(at, end);
|
||||
dis.disassemble(at, set);
|
||||
return listing.getInstructions(set, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue