mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 18:29:37 +02:00
GP-1881: Implement editable Repr column for Registers and Watches providers.
This commit is contained in:
parent
8e8b193ac1
commit
dbe670bf85
12 changed files with 314 additions and 28 deletions
|
@ -325,9 +325,9 @@
|
|||
|
||||
<H3><A name="toggle_update_while_running">Update While Running</A></H3>
|
||||
|
||||
<P>By default, events are passed to the Objects Viewer even while the target is running.
|
||||
The resulting changes in the GUI may be distracting for some. To disable updates to the
|
||||
Objects Viewer, toggle "Updates While Running" off.</P>
|
||||
<P>By default, events are passed to the Objects Viewer even while the target is running. The
|
||||
resulting changes in the GUI may be distracting for some. To disable updates to the Objects
|
||||
Viewer, toggle "Updates While Running" off.</P>
|
||||
|
||||
<H2><A name="color"></A>Color Options</H2>
|
||||
|
||||
|
|
|
@ -58,7 +58,8 @@
|
|||
time on."</LI>
|
||||
|
||||
<LI>Representation - the value of the register as interpreted by its data type. If the value
|
||||
is an address, double-clicking this field will navigate to it.</LI>
|
||||
is an address, double-clicking this field will navigate to it. This field is user modifiable
|
||||
whenever the Value column is modifiable and the selected data type provides an encoder.</LI>
|
||||
</UL>
|
||||
|
||||
<H2>Actions</H2>
|
||||
|
|
|
@ -78,10 +78,10 @@
|
|||
trace. Clicking the Apply Data Type action will apply it to the current trace, if
|
||||
possible.</LI>
|
||||
|
||||
<LI>Representation - the value of the watch as interpreted by the selected data type. This
|
||||
field is not yet user modifiable, even if the <B>Enable Edits</B> toggle is on. If the value
|
||||
is an address, i.e., Type is a pointer, then double-clicking this cell will navigate the
|
||||
primary dynamic listing, if possible.</LI>
|
||||
<LI>Representation - the value of the watch as interpreted by the selected data type. If the
|
||||
value is an address, i.e., Type is a pointer, then double-clicking this cell will navigate
|
||||
the primary dynamic listing, if possible. This field is user-modifiable whenever the Value
|
||||
column is modifiable and the selected data type provides an encoder.</LI>
|
||||
|
||||
<LI>Error - if an error occurs during compilation or evaluation of the expression, that error
|
||||
is rendered here. Double-clicking the row will display the stack trace. Note that errors
|
||||
|
|
|
@ -62,8 +62,7 @@ import ghidra.framework.plugintool.AutoService;
|
|||
import ghidra.framework.plugintool.ComponentProviderAdapter;
|
||||
import ghidra.framework.plugintool.annotation.AutoServiceConsumed;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.data.DataTypeConflictException;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.util.CodeUnitInsertionException;
|
||||
import ghidra.trace.model.*;
|
||||
|
@ -89,12 +88,16 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
|
|||
|
||||
protected enum RegisterTableColumns
|
||||
implements EnumeratedTableColumn<RegisterTableColumns, RegisterRow> {
|
||||
FAV("Fav", Boolean.class, RegisterRow::isFavorite, RegisterRow::setFavorite, r -> true, SortDirection.DESCENDING),
|
||||
FAV("Fav", Boolean.class, RegisterRow::isFavorite, RegisterRow::setFavorite, //
|
||||
r -> true, SortDirection.DESCENDING),
|
||||
NUMBER("#", Integer.class, RegisterRow::getNumber),
|
||||
NAME("Name", String.class, RegisterRow::getName),
|
||||
VALUE("Value", BigInteger.class, RegisterRow::getValue, RegisterRow::setValue, RegisterRow::isValueEditable, SortDirection.ASCENDING),
|
||||
TYPE("Type", DataType.class, RegisterRow::getDataType, RegisterRow::setDataType, r -> true, SortDirection.ASCENDING),
|
||||
REPR("Repr", String.class, RegisterRow::getRepresentation);
|
||||
VALUE("Value", BigInteger.class, RegisterRow::getValue, RegisterRow::setValue, //
|
||||
RegisterRow::isValueEditable, SortDirection.ASCENDING),
|
||||
TYPE("Type", DataType.class, RegisterRow::getDataType, RegisterRow::setDataType, //
|
||||
r -> true, SortDirection.ASCENDING),
|
||||
REPR("Repr", String.class, RegisterRow::getRepresentation, RegisterRow::setRepresentation, //
|
||||
RegisterRow::isRepresentationEditable, SortDirection.ASCENDING);
|
||||
|
||||
private final String header;
|
||||
private final Function<RegisterRow, ?> getter;
|
||||
|
@ -859,6 +862,35 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
|
|||
return data.getDataType();
|
||||
}
|
||||
|
||||
void writeRegisterValueRepresentation(Register register, String representation) {
|
||||
TraceData data = getRegisterData(register);
|
||||
if (data == null) {
|
||||
// isEditable should have been false
|
||||
tool.setStatusInfo("Register has no data type", true);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
RegisterValue rv = TraceRegisterUtils.encodeValueRepresentationHackPointer(
|
||||
register, data, representation);
|
||||
writeRegisterValue(rv);
|
||||
}
|
||||
catch (DataTypeEncodeException e) {
|
||||
tool.setStatusInfo(e.getMessage(), true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
boolean canWriteRegisterRepresentation(Register register) {
|
||||
if (!canWriteRegister(register)) {
|
||||
return false;
|
||||
}
|
||||
TraceData data = getRegisterData(register);
|
||||
if (data == null) {
|
||||
return false;
|
||||
}
|
||||
return data.getBaseDataType().isEncodable();
|
||||
}
|
||||
|
||||
String getRegisterValueRepresentation(Register register) {
|
||||
TraceData data = getRegisterData(register);
|
||||
if (data == null) {
|
||||
|
|
|
@ -91,7 +91,13 @@ public class RegisterRow {
|
|||
return provider.getRegisterDataType(register);
|
||||
}
|
||||
|
||||
// TODO: setValueRepresentation. Requires support from data types.
|
||||
public void setRepresentation(String representation) {
|
||||
provider.writeRegisterValueRepresentation(register, representation);
|
||||
}
|
||||
|
||||
public boolean isRepresentationEditable() {
|
||||
return provider.canWriteRegisterRepresentation(register);
|
||||
}
|
||||
|
||||
public String getRepresentation() {
|
||||
return provider.getRegisterValueRepresentation(register);
|
||||
|
|
|
@ -86,9 +86,11 @@ public class DebuggerWatchesProvider extends ComponentProviderAdapter {
|
|||
protected enum WatchTableColumns implements EnumeratedTableColumn<WatchTableColumns, WatchRow> {
|
||||
EXPRESSION("Expression", String.class, WatchRow::getExpression, WatchRow::setExpression),
|
||||
ADDRESS("Address", Address.class, WatchRow::getAddress),
|
||||
VALUE("Value", String.class, WatchRow::getRawValueString, WatchRow::setRawValueString, WatchRow::isValueEditable),
|
||||
VALUE("Value", String.class, WatchRow::getRawValueString, WatchRow::setRawValueString, //
|
||||
WatchRow::isRawValueEditable),
|
||||
TYPE("Type", DataType.class, WatchRow::getDataType, WatchRow::setDataType),
|
||||
REPR("Repr", String.class, WatchRow::getValueString),
|
||||
REPR("Repr", String.class, WatchRow::getValueString, WatchRow::setValueString, //
|
||||
WatchRow::isValueEditable),
|
||||
ERROR("Error", String.class, WatchRow::getErrorMessage);
|
||||
|
||||
private final String header;
|
||||
|
|
|
@ -33,6 +33,7 @@ import ghidra.pcode.exec.trace.TraceSleighUtils;
|
|||
import ghidra.pcode.utils.Utils;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.data.DataTypeEncodeException;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.program.model.mem.ByteMemBufferImpl;
|
||||
import ghidra.program.model.mem.MemBuffer;
|
||||
|
@ -143,6 +144,8 @@ public class WatchRow {
|
|||
return dataType.getRepresentation(buffer, SettingsImpl.NO_SETTINGS, value.length);
|
||||
}
|
||||
|
||||
// TODO: DataType settings
|
||||
|
||||
protected Object parseAsDataTypeObj() {
|
||||
if (dataType == null || value == null) {
|
||||
return null;
|
||||
|
@ -373,7 +376,7 @@ public class WatchRow {
|
|||
return valueObj;
|
||||
}
|
||||
|
||||
public boolean isValueEditable() {
|
||||
public boolean isRawValueEditable() {
|
||||
if (!provider.isEditsEnabled()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -423,8 +426,13 @@ public class WatchRow {
|
|||
if (address == null) {
|
||||
throw new IllegalStateException("Cannot write to watch variable without an address");
|
||||
}
|
||||
if (bytes.length != value.length) {
|
||||
throw new IllegalArgumentException("Byte array values must match length of variable");
|
||||
if (bytes.length > value.length) {
|
||||
throw new IllegalArgumentException("Byte arrays cannot exceed length of variable");
|
||||
}
|
||||
if (bytes.length < value.length) {
|
||||
byte[] fillOld = Arrays.copyOf(value, value.length);
|
||||
System.arraycopy(bytes, 0, fillOld, 0, bytes.length);
|
||||
bytes = fillOld;
|
||||
}
|
||||
DebuggerStateEditingService editingService = provider.editingService;
|
||||
if (editingService == null) {
|
||||
|
@ -438,6 +446,33 @@ public class WatchRow {
|
|||
});
|
||||
}
|
||||
|
||||
public void setValueString(String valueString) {
|
||||
if (dataType == null || value == null) {
|
||||
// isValueEditable should have been false
|
||||
provider.getTool().setStatusInfo("Watch no value or no data type", true);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
byte[] encoded = dataType.encodeRepresentation(valueString,
|
||||
new ByteMemBufferImpl(address, value, language.isBigEndian()),
|
||||
SettingsImpl.NO_SETTINGS, value.length);
|
||||
setRawValueBytes(encoded);
|
||||
}
|
||||
catch (DataTypeEncodeException e) {
|
||||
provider.getTool().setStatusInfo(e.getMessage(), true);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isValueEditable() {
|
||||
if (!isRawValueEditable()) {
|
||||
return false;
|
||||
}
|
||||
if (dataType == null) {
|
||||
return false;
|
||||
}
|
||||
return dataType.isEncodable();
|
||||
}
|
||||
|
||||
public int getValueLength() {
|
||||
return value == null ? 0 : value.length;
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ import static ghidra.lifecycle.Unfinished.TODO;
|
|||
import static org.junit.Assert.*;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
@ -159,6 +160,11 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
|
|||
row.setValue(new BigInteger(text, 16));
|
||||
}
|
||||
|
||||
protected void setRowRepr(RegisterRow row, String repr) {
|
||||
assertTrue(row.isRepresentationEditable());
|
||||
row.setRepresentation(repr);
|
||||
}
|
||||
|
||||
protected void assertRowValueEmpty(RegisterRow row) {
|
||||
assertEquals(BigInteger.ZERO, row.getValue());
|
||||
}
|
||||
|
@ -393,6 +399,48 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
|
|||
});
|
||||
}
|
||||
|
||||
long encodeDouble(double value) {
|
||||
ByteBuffer buf = ByteBuffer.allocate(Double.BYTES);
|
||||
buf.putDouble(0, value);
|
||||
return buf.getLong(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testModifyRepresentationEmu() throws Exception {
|
||||
traceManager.openTrace(tb.trace);
|
||||
|
||||
TraceThread thread = addThread();
|
||||
traceManager.activateThread(thread);
|
||||
waitForSwing();
|
||||
|
||||
editingService.setCurrentMode(tb.trace, StateEditingMode.WRITE_EMULATOR);
|
||||
|
||||
assertTrue(registersProvider.actionEnableEdits.isEnabled());
|
||||
performAction(registersProvider.actionEnableEdits);
|
||||
|
||||
addRegisterValues(thread);
|
||||
waitForDomainObject(tb.trace);
|
||||
|
||||
TraceMemoryRegisterSpace regVals =
|
||||
tb.trace.getMemoryManager().getMemoryRegisterSpace(thread, false);
|
||||
|
||||
RegisterRow row = findRegisterRow(r0);
|
||||
assertFalse(row.isRepresentationEditable());
|
||||
|
||||
row.setDataType(DoubleDataType.dataType);
|
||||
waitForDomainObject(tb.trace);
|
||||
|
||||
setRowRepr(row, "1234");
|
||||
waitForSwing();
|
||||
waitForPass(() -> {
|
||||
long viewSnap = traceManager.getCurrent().getViewSnap();
|
||||
assertTrue(DBTraceUtils.isScratch(viewSnap));
|
||||
assertEquals(BigInteger.valueOf(encodeDouble(1234)),
|
||||
regVals.getValue(viewSnap, r0).getUnsignedValue());
|
||||
assertEquals(BigInteger.valueOf(encodeDouble(1234)), row.getValue());
|
||||
});
|
||||
}
|
||||
|
||||
// NOTE: Value modification only allowed on live target
|
||||
|
||||
@Test
|
||||
|
|
|
@ -270,17 +270,17 @@ public class DebuggerWatchesProviderTest extends AbstractGhidraHeadedDebuggerGUI
|
|||
WatchRow row = Unique.assertOne(watchesProvider.watchTableModel.getModelData());
|
||||
row.setExpression(expression);
|
||||
|
||||
assertFalse(row.isValueEditable());
|
||||
assertFalse(row.isRawValueEditable());
|
||||
traceManager.openTrace(tb.trace);
|
||||
traceManager.activateThread(thread);
|
||||
editingService.setCurrentMode(tb.trace, StateEditingMode.WRITE_EMULATOR);
|
||||
waitForSwing();
|
||||
|
||||
assertNoErr(row);
|
||||
assertFalse(row.isValueEditable());
|
||||
assertFalse(row.isRawValueEditable());
|
||||
|
||||
performAction(watchesProvider.actionEnableEdits);
|
||||
assertEquals(expectWritable, row.isValueEditable());
|
||||
assertEquals(expectWritable, row.isRawValueEditable());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -314,6 +314,12 @@ public class DebuggerWatchesProviderTest extends AbstractGhidraHeadedDebuggerGUI
|
|||
return row;
|
||||
}
|
||||
|
||||
long encodeDouble(double value) {
|
||||
ByteBuffer buf = ByteBuffer.allocate(Double.BYTES);
|
||||
buf.putDouble(0, value);
|
||||
return buf.getLong(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEditRegisterEmu() {
|
||||
WatchRow row = prepareTestEditEmu("r0");
|
||||
|
@ -339,9 +345,33 @@ public class DebuggerWatchesProviderTest extends AbstractGhidraHeadedDebuggerGUI
|
|||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEditRegisterRepresentationEmu() {
|
||||
WatchRow row = prepareTestEditEmu("r0");
|
||||
assertFalse(row.isValueEditable());
|
||||
|
||||
row.setDataType(DoubleDataType.dataType);
|
||||
waitForSwing();
|
||||
assertTrue(row.isValueEditable());
|
||||
|
||||
TraceMemoryRegisterSpace regVals =
|
||||
tb.trace.getMemoryManager().getMemoryRegisterSpace(thread, false);
|
||||
|
||||
row.setValueString("1234");
|
||||
waitForPass(() -> {
|
||||
long viewSnap = traceManager.getCurrent().getViewSnap();
|
||||
assertTrue(DBTraceUtils.isScratch(viewSnap));
|
||||
assertEquals(BigInteger.valueOf(encodeDouble(1234)),
|
||||
regVals.getValue(viewSnap, r0).getUnsignedValue());
|
||||
assertEquals("0x4093480000000000", row.getRawValueString());
|
||||
assertEquals("1234.0", row.getValueString());
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEditMemoryEmu() {
|
||||
WatchRow row = prepareTestEditEmu("*:8 r0");
|
||||
|
||||
TraceMemoryOperations mem = tb.trace.getMemoryManager();
|
||||
ByteBuffer buf = ByteBuffer.allocate(8);
|
||||
|
||||
|
@ -366,6 +396,55 @@ public class DebuggerWatchesProviderTest extends AbstractGhidraHeadedDebuggerGUI
|
|||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEditMemoryRepresentationEmu() {
|
||||
WatchRow row = prepareTestEditEmu("*:8 r0");
|
||||
assertFalse(row.isValueEditable());
|
||||
|
||||
row.setDataType(DoubleDataType.dataType);
|
||||
waitForSwing();
|
||||
assertTrue(row.isValueEditable());
|
||||
|
||||
TraceMemoryOperations mem = tb.trace.getMemoryManager();
|
||||
ByteBuffer buf = ByteBuffer.allocate(8);
|
||||
|
||||
row.setValueString("1234");
|
||||
waitForPass(() -> {
|
||||
long viewSnap = traceManager.getCurrent().getViewSnap();
|
||||
assertTrue(DBTraceUtils.isScratch(viewSnap));
|
||||
buf.clear();
|
||||
mem.getBytes(viewSnap, tb.addr(0x00400000), buf);
|
||||
buf.flip();
|
||||
assertEquals(encodeDouble(1234), buf.getLong());
|
||||
assertEquals("1234.0", row.getValueString());
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEditMemoryStringEmu() {
|
||||
// Variable size must exceed that of desired string's bytes
|
||||
WatchRow row = prepareTestEditEmu("*:16 r0");
|
||||
assertFalse(row.isValueEditable());
|
||||
|
||||
row.setDataType(TerminatedStringDataType.dataType);
|
||||
waitForSwing();
|
||||
assertTrue(row.isValueEditable());
|
||||
|
||||
TraceMemoryOperations mem = tb.trace.getMemoryManager();
|
||||
ByteBuffer buf = ByteBuffer.allocate(14);
|
||||
|
||||
row.setValueString("\"Hello, World!\"");
|
||||
waitForPass(() -> {
|
||||
long viewSnap = traceManager.getCurrent().getViewSnap();
|
||||
assertTrue(DBTraceUtils.isScratch(viewSnap));
|
||||
buf.clear();
|
||||
mem.getBytes(viewSnap, tb.addr(0x00400000), buf);
|
||||
buf.flip();
|
||||
assertArrayEquals("Hello, World!\0".getBytes(), buf.array());
|
||||
assertEquals("\"Hello, World!\"", row.getValueString());
|
||||
});
|
||||
}
|
||||
|
||||
protected WatchRow prepareTestEditTarget(String expression) throws Exception {
|
||||
createTestModel();
|
||||
mb.createTestProcessesAndThreads();
|
||||
|
@ -422,7 +501,7 @@ public class DebuggerWatchesProviderTest extends AbstractGhidraHeadedDebuggerGUI
|
|||
// Sanity check
|
||||
assertFalse(recorder.isRegisterOnTarget(thread, r1));
|
||||
|
||||
assertFalse(row.isValueEditable());
|
||||
assertFalse(row.isRawValueEditable());
|
||||
row.setRawValueString("0x1234");
|
||||
}
|
||||
|
||||
|
|
|
@ -44,7 +44,21 @@ public class PatchStep implements Step {
|
|||
protected String sleigh;
|
||||
protected int hashCode;
|
||||
|
||||
public static String generateSleigh(Language language, Address address, byte[] data,
|
||||
/**
|
||||
* Generate a single line of Sleigh
|
||||
*
|
||||
* <p>
|
||||
* Note that when length is greater than 8, this will generate constants which are too large for
|
||||
* the Java implementation of Sleigh. Use {@link #generateSleigh(Language, Address, byte[])}
|
||||
* instead to write the variable in chunks.
|
||||
*
|
||||
* @param language the target language
|
||||
* @param address the (start) address of the variable
|
||||
* @param data the bytes to write to the variable
|
||||
* @param length the length of the variable
|
||||
* @return the Sleigh code
|
||||
*/
|
||||
public static String generateSleighLine(Language language, Address address, byte[] data,
|
||||
int length) {
|
||||
BigInteger value = Utils.bytesToBigInteger(data, length, language.isBigEndian(), false);
|
||||
if (address.isMemoryAddress()) {
|
||||
|
@ -67,8 +81,34 @@ public class PatchStep implements Step {
|
|||
return String.format("%s=0x%s", register, value.toString(16));
|
||||
}
|
||||
|
||||
public static String generateSleigh(Language language, Address address, byte[] data) {
|
||||
return generateSleigh(language, address, data, data.length);
|
||||
/**
|
||||
* Generate a single line of Sleigh
|
||||
*
|
||||
* @see #generateSleighLine(Language, Address, byte[], int)
|
||||
*/
|
||||
public static String generateSleighLine(Language language, Address address, byte[] data) {
|
||||
return generateSleighLine(language, address, data, data.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate multiple lines of Sleigh, all to set a single variable
|
||||
*
|
||||
* @param language the target language
|
||||
* @param address the (start) address of the variable
|
||||
* @param data the bytes to write to the variable
|
||||
* @return the lines of Sleigh code
|
||||
*/
|
||||
public static List<String> generateSleigh(Language language, Address address, byte[] data) {
|
||||
List<String> result = new ArrayList<>();
|
||||
generateSleigh(result, language, address, data);
|
||||
return result;
|
||||
}
|
||||
|
||||
protected static void generateSleigh(List<String> result, Language language, Address address,
|
||||
byte[] data) {
|
||||
SemisparseByteArray array = new SemisparseByteArray(); // TODO: Seems heavy-handed
|
||||
array.putData(address.getOffset(), data);
|
||||
generateSleigh(result, language, address.getAddressSpace(), array);
|
||||
}
|
||||
|
||||
protected static List<String> generateSleigh(Language language,
|
||||
|
@ -102,7 +142,7 @@ public class PatchStep implements Step {
|
|||
Address min = chunk.getMinAddress();
|
||||
int length = (int) chunk.getLength();
|
||||
array.getData(min.getOffset(), data, 0, length);
|
||||
result.add(generateSleigh(language, min, data, length));
|
||||
result.add(generateSleighLine(language, min, data, length));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -494,6 +494,7 @@ public class TraceSchedule implements Comparable<TraceSchedule> {
|
|||
/**
|
||||
* Returns the equivalent of executing this schedule then performing a given patch
|
||||
*
|
||||
* @param thread the thread context for the patch; cannot be null
|
||||
* @param sleigh a single line of sleigh, excluding the terminating semicolon.
|
||||
* @return the resulting schedule
|
||||
*/
|
||||
|
@ -509,4 +510,27 @@ public class TraceSchedule implements Comparable<TraceSchedule> {
|
|||
ticks.coalescePatches(thread.getTrace().getBaseLanguage());
|
||||
return new TraceSchedule(snap, ticks, new Sequence());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the equivalent of executing this schedule then performing the given patches
|
||||
*
|
||||
* @param thread the thread context for the patch; cannot be null
|
||||
* @param sleigh the lines of sleigh, excluding the terminating semicolons.
|
||||
* @return the resulting schedule
|
||||
*/
|
||||
public TraceSchedule patched(TraceThread thread, List<String> sleigh) {
|
||||
if (!this.pSteps.isNop()) {
|
||||
Sequence pTicks = this.pSteps.clone();
|
||||
for (String line : sleigh) {
|
||||
pTicks.advance(new PatchStep(thread.getKey(), line));
|
||||
}
|
||||
pTicks.coalescePatches(thread.getTrace().getBaseLanguage());
|
||||
return new TraceSchedule(snap, steps.clone(), pTicks);
|
||||
}
|
||||
Sequence ticks = this.steps.clone();
|
||||
for (String line : sleigh) {
|
||||
ticks.advance(new PatchStep(thread.getKey(), line));
|
||||
}
|
||||
return new TraceSchedule(snap, ticks, new Sequence());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,12 +15,14 @@
|
|||
*/
|
||||
package ghidra.trace.util;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
|
||||
import ghidra.pcode.utils.Utils;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.lang.Register;
|
||||
|
@ -122,6 +124,23 @@ public enum TraceRegisterUtils {
|
|||
return addr.toString();
|
||||
}
|
||||
|
||||
public static RegisterValue encodeValueRepresentationHackPointer(Register register,
|
||||
TraceData data, String representation) throws DataTypeEncodeException {
|
||||
DataType dataType = data.getBaseDataType();
|
||||
if (data.getValueClass() != Address.class) {
|
||||
byte[] bytes =
|
||||
dataType.encodeRepresentation(representation, data, data, data.getLength());
|
||||
BigInteger value = Utils.bytesToBigInteger(bytes, register.getMinimumByteSize(),
|
||||
register.isBigEndian(), false);
|
||||
return new RegisterValue(register, value);
|
||||
}
|
||||
Address addr = data.getTrace().getBaseAddressFactory().getAddress(representation);
|
||||
if (addr == null) {
|
||||
throw new DataTypeEncodeException("Invalid address", representation, dataType);
|
||||
}
|
||||
return new RegisterValue(register, addr.getOffsetAsBigInteger());
|
||||
}
|
||||
|
||||
public static RegisterValue combineWithTraceBaseRegisterValue(RegisterValue rv, long snap,
|
||||
TraceMemoryRegisterSpace regs, boolean requireKnown) {
|
||||
Register reg = rv.getRegister();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue