mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-30 16:47:43 +08:00
GP-1230: Add Taint Analysis prototype and emulator framework support
This commit is contained in:
+5
-3
@@ -16,20 +16,22 @@
|
||||
package docking.widgets.table;
|
||||
|
||||
import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.program.util.ProgramSelection;
|
||||
|
||||
public class DefaultEnumeratedColumnProgramTableModel<C extends Enum<C> & EnumeratedTableColumn<C, ? super R>, R>
|
||||
public class DefaultEnumeratedColumnProgramTableModel<C extends Enum<C> & EnumeratedTableColumn<C, R>, R>
|
||||
extends DefaultEnumeratedColumnTableModel<C, R>
|
||||
implements EnumeratedColumnProgramTableModel<R> {
|
||||
protected final C selColumn;
|
||||
|
||||
private Program program;
|
||||
|
||||
public DefaultEnumeratedColumnProgramTableModel(String name, Class<C> colType, C selColumn) {
|
||||
super(name, colType);
|
||||
public DefaultEnumeratedColumnProgramTableModel(PluginTool tool, String name, Class<C> colType,
|
||||
C selColumn) {
|
||||
super(tool, name, colType);
|
||||
if (selColumn != null) {
|
||||
Class<?> valueClass = selColumn.getValueClass();
|
||||
if (!Address.class.isAssignableFrom(valueClass) &&
|
||||
|
||||
+152
-116
@@ -20,104 +20,104 @@ import java.util.function.Predicate;
|
||||
|
||||
import docking.widgets.table.ColumnSortState.SortDirection;
|
||||
import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn;
|
||||
import ghidra.docking.settings.Settings;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.framework.plugintool.ServiceProvider;
|
||||
|
||||
public class DefaultEnumeratedColumnTableModel<C extends Enum<C> & EnumeratedTableColumn<C, ? super R>, R>
|
||||
extends AbstractSortedTableModel<R> implements EnumeratedColumnTableModel<R> {
|
||||
/**
|
||||
* A table model whose columns are described using an {@link Enum}.
|
||||
*
|
||||
* <p>
|
||||
* See the callers to this class' constructor to find example uses.
|
||||
*
|
||||
* @param <C> the type of the enum
|
||||
* @param <R> the type of rows
|
||||
*/
|
||||
public class DefaultEnumeratedColumnTableModel<C extends Enum<C> & EnumeratedTableColumn<C, R>, R>
|
||||
extends GDynamicColumnTableModel<R, Void> implements EnumeratedColumnTableModel<R> {
|
||||
// NOTE: If I need to track indices, addSortListener
|
||||
/**
|
||||
* An interface on enums used to describe table columns
|
||||
*
|
||||
* @param <C> the type of the enum
|
||||
* @param <R> the type of rows
|
||||
*/
|
||||
public static interface EnumeratedTableColumn<C extends Enum<C>, R> {
|
||||
/**
|
||||
* Get the value class of cells in this column
|
||||
*
|
||||
* @return the class
|
||||
*/
|
||||
public Class<?> getValueClass();
|
||||
|
||||
/**
|
||||
* Get the value of this column for the given row
|
||||
*
|
||||
* @param row the row
|
||||
* @return the value
|
||||
*/
|
||||
public Object getValueOf(R row);
|
||||
|
||||
/**
|
||||
* Get the name of this column
|
||||
*
|
||||
* @return the name
|
||||
*/
|
||||
public String getHeader();
|
||||
|
||||
/**
|
||||
* Get the value of this column for the given row
|
||||
*
|
||||
* @param row the row
|
||||
* @param value the new value
|
||||
*/
|
||||
default public void setValueOf(R row, Object value) {
|
||||
throw new UnsupportedOperationException("Cell is not editable");
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this column can be modified for the given row
|
||||
*
|
||||
* @param row the row
|
||||
* @return true if editable
|
||||
*/
|
||||
default public boolean isEditable(R row) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this column can be sorted
|
||||
*
|
||||
* <p>
|
||||
* TODO: Either this should be implemented as ported to {@link GDynamicColumnTableModel}, or
|
||||
* removed.
|
||||
*
|
||||
* @return true if sortable
|
||||
*/
|
||||
default public boolean isSortable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default sort direction for this column
|
||||
*
|
||||
* @return the sort direction
|
||||
*/
|
||||
default public SortDirection defaultSortDirection() {
|
||||
return SortDirection.ASCENDING;
|
||||
}
|
||||
}
|
||||
|
||||
public class TableRowIterator implements RowIterator<R> {
|
||||
protected final ListIterator<R> it = modelData.listIterator();
|
||||
protected int index;
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return it.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public R next() {
|
||||
index = it.nextIndex();
|
||||
return it.next();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPrevious() {
|
||||
return it.hasPrevious();
|
||||
}
|
||||
|
||||
@Override
|
||||
public R previous() {
|
||||
index = it.previousIndex();
|
||||
return it.previous();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int nextIndex() {
|
||||
return it.nextIndex();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int previousIndex() {
|
||||
return it.previousIndex();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
it.remove();
|
||||
fireTableRowsDeleted(index, index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(R e) {
|
||||
it.set(e);
|
||||
fireTableRowsUpdated(index, index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyUpdated() {
|
||||
fireTableRowsUpdated(index, index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(R e) {
|
||||
it.add(e);
|
||||
int nextIndex = it.nextIndex();
|
||||
fireTableRowsInserted(nextIndex, nextIndex);
|
||||
}
|
||||
}
|
||||
|
||||
private final List<R> modelData = new ArrayList<>();
|
||||
private final String name;
|
||||
private final C[] cols;
|
||||
|
||||
public DefaultEnumeratedColumnTableModel(String name, Class<C> colType) {
|
||||
public DefaultEnumeratedColumnTableModel(PluginTool tool, String name, Class<C> colType) {
|
||||
super(tool);
|
||||
this.name = name;
|
||||
this.cols = colType.getEnumConstants();
|
||||
|
||||
setupDefaultSortOrder();
|
||||
reloadColumns(); // Smell
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -130,76 +130,112 @@ public class DefaultEnumeratedColumnTableModel<C extends Enum<C> & EnumeratedTab
|
||||
return modelData;
|
||||
}
|
||||
|
||||
protected void setupDefaultSortOrder() {
|
||||
List<C> defaultOrder = defaultSortOrder();
|
||||
if (defaultOrder.isEmpty()) {
|
||||
return;
|
||||
static class EnumeratedDynamicTableColumn<R>
|
||||
extends AbstractDynamicTableColumn<R, Object, Void>
|
||||
implements EditableDynamicTableColumn<R, Object, Void> {
|
||||
private final EnumeratedTableColumn<?, R> col;
|
||||
|
||||
public EnumeratedDynamicTableColumn(EnumeratedTableColumn<?, R> col) {
|
||||
this.col = col;
|
||||
}
|
||||
TableSortStateEditor editor = new TableSortStateEditor();
|
||||
for (C col : defaultOrder) {
|
||||
editor.addSortedColumn(col.ordinal(), col.defaultSortDirection());
|
||||
|
||||
@Override
|
||||
public String getColumnName() {
|
||||
return col.getHeader();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue(R rowObject, Settings settings, Void data,
|
||||
ServiceProvider serviceProvider) throws IllegalArgumentException {
|
||||
return col.getValueOf(rowObject);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Class<Object> getColumnClass() {
|
||||
return (Class<Object>) col.getValueClass();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEditable(R row, Settings settings, Void dataSource,
|
||||
ServiceProvider serviceProvider) {
|
||||
return col.isEditable(row);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValueOf(R row, Object value, Settings settings, Void dataSource,
|
||||
ServiceProvider serviceProvider) {
|
||||
col.setValueOf(row, value);
|
||||
}
|
||||
setDefaultTableSortState(editor.createTableSortState());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TableColumnDescriptor<R> createTableColumnDescriptor() {
|
||||
TableColumnDescriptor<R> descriptor = new TableColumnDescriptor<>();
|
||||
if (cols != null) { // Smells
|
||||
List<C> defaultOrder = defaultSortOrder();
|
||||
for (C col : cols) {
|
||||
descriptor.addVisibleColumn(
|
||||
new EnumeratedDynamicTableColumn<R>(col),
|
||||
defaultOrder.indexOf(col), // -1 means not found, not sorted
|
||||
col.defaultSortDirection().isAscending());
|
||||
}
|
||||
}
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void getDataSource() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default sort order of the table
|
||||
*
|
||||
* @return the list of columns in order of descending priority
|
||||
*/
|
||||
public List<C> defaultSortOrder() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/*@Override
|
||||
public int getRowCount() {
|
||||
return modelData.size();
|
||||
}*/
|
||||
|
||||
@Override
|
||||
public int getColumnCount() {
|
||||
return cols.length;
|
||||
}
|
||||
|
||||
/*@Override
|
||||
public Object getValueAt(int rowIndex, int columnIndex) {
|
||||
return getColumnValueForRow(modelData.get(rowIndex), columnIndex);
|
||||
}*/
|
||||
|
||||
@Override
|
||||
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
|
||||
DynamicTableColumn<R, ?, ?> column = tableColumns.get(columnIndex);
|
||||
if (!(column instanceof EditableDynamicTableColumn)) {
|
||||
return; // TODO: throw?
|
||||
}
|
||||
@SuppressWarnings("unchecked") // tableColumns doesn't include DATA_SOURCE
|
||||
// May cause ClassCastException if given value can't be cast. Good.
|
||||
EditableDynamicTableColumn<R, Object, Void> editable =
|
||||
(EditableDynamicTableColumn<R, Object, Void>) column;
|
||||
synchronized (modelData) {
|
||||
if (rowIndex < 0 || rowIndex >= modelData.size()) {
|
||||
return; // TODO: throw?
|
||||
}
|
||||
R row = modelData.get(rowIndex);
|
||||
C col = cols[columnIndex];
|
||||
Class<?> cls = col.getValueClass();
|
||||
col.setValueOf(row, cls.cast(aValue));
|
||||
editable.setValueOf(row, aValue, columnSettings.get(column), getDataSource(),
|
||||
serviceProvider);
|
||||
}
|
||||
fireTableCellUpdated(rowIndex, columnIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCellEditable(int rowIndex, int columnIndex) {
|
||||
synchronized (modelData) {
|
||||
R row = modelData.get(rowIndex);
|
||||
C col = cols[columnIndex];
|
||||
return col.isEditable(row);
|
||||
DynamicTableColumn<R, ?, ?> column = tableColumns.get(columnIndex);
|
||||
if (!(column instanceof EditableDynamicTableColumn)) {
|
||||
return false;
|
||||
}
|
||||
@SuppressWarnings("unchecked") // tableColumns doesn't include DATA_SOURCE
|
||||
EditableDynamicTableColumn<R, ?, Void> editable =
|
||||
(EditableDynamicTableColumn<R, ?, Void>) column;
|
||||
synchronized (modelData) {
|
||||
if (rowIndex < 0 || rowIndex >= modelData.size()) {
|
||||
return false;
|
||||
}
|
||||
R row = modelData.get(rowIndex);
|
||||
return editable.isEditable(row, columnSettings.get(column), getDataSource(),
|
||||
serviceProvider);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSortable(int columnIndex) {
|
||||
C col = cols[columnIndex];
|
||||
return col.isSortable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getColumnClass(int columnIndex) {
|
||||
return cols[columnIndex].getValueClass();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getColumnName(int column) {
|
||||
return cols[column].getHeader();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getColumnValueForRow(R t, int columnIndex) {
|
||||
return cols[columnIndex].getValueOf(t);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+19
-3
@@ -15,12 +15,28 @@
|
||||
*/
|
||||
package docking.widgets.table;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import javax.help.UnsupportedOperationException;
|
||||
|
||||
import ghidra.docking.settings.Settings;
|
||||
import ghidra.framework.plugintool.ServiceProvider;
|
||||
|
||||
public interface EnumeratedColumnTableModel<R> extends RowObjectTableModel<R> {
|
||||
interface RowIterator<R> extends ListIterator<R> {
|
||||
void notifyUpdated();
|
||||
|
||||
public interface EditableDynamicTableColumn<ROW_TYPE, COLUMN_TYPE, DATA_SOURCE>
|
||||
extends DynamicTableColumn<ROW_TYPE, COLUMN_TYPE, DATA_SOURCE> {
|
||||
default public boolean isEditable(ROW_TYPE row, Settings settings, DATA_SOURCE dataSource,
|
||||
ServiceProvider serviceProvider) {
|
||||
return false;
|
||||
}
|
||||
|
||||
default public void setValueOf(ROW_TYPE row, COLUMN_TYPE value, Settings settings,
|
||||
DATA_SOURCE dataSource, ServiceProvider serviceProvider) {
|
||||
throw new UnsupportedOperationException("Cell is not editable");
|
||||
}
|
||||
}
|
||||
|
||||
void add(R row);
|
||||
|
||||
+3
-2
@@ -21,6 +21,7 @@ import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
/**
|
||||
@@ -38,9 +39,9 @@ public class RowWrappedEnumeratedColumnTableModel<C extends Enum<C> & Enumerated
|
||||
private final Function<T, R> wrapper;
|
||||
private final Map<K, R> map = new HashMap<>();
|
||||
|
||||
public RowWrappedEnumeratedColumnTableModel(String name, Class<C> colType,
|
||||
public RowWrappedEnumeratedColumnTableModel(PluginTool tool, String name, Class<C> colType,
|
||||
Function<T, K> keyFunc, Function<T, R> wrapper) {
|
||||
super(name, colType);
|
||||
super(tool, name, colType);
|
||||
this.keyFunc = keyFunc;
|
||||
this.wrapper = wrapper;
|
||||
}
|
||||
|
||||
-26
@@ -1,26 +0,0 @@
|
||||
/* ###
|
||||
* 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 docking.widgets.timeline;
|
||||
|
||||
import com.google.common.collect.Range;
|
||||
|
||||
public interface TimelineListener {
|
||||
default void viewRangeChanged(Range<Double> viewRange) {
|
||||
}
|
||||
|
||||
default void itemActivated(int index) {
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
+39
-5
@@ -19,6 +19,7 @@ import java.util.*;
|
||||
|
||||
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
|
||||
import ghidra.pcode.exec.*;
|
||||
import ghidra.pcode.exec.PcodeArithmetic.Purpose;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.util.classfinder.ClassSearcher;
|
||||
@@ -27,9 +28,36 @@ import ghidra.util.classfinder.ClassSearcher;
|
||||
* An abstract implementation of {@link PcodeMachine} suitable as a base for most implementations
|
||||
*
|
||||
* <p>
|
||||
* For a complete example of a p-code emulator, see {@link PcodeEmulator}.
|
||||
* A note regarding terminology: A p-code "machine" refers to any p-code-based machine simulator,
|
||||
* whether or not it operates on abstract or concrete values. The term "emulator" is reserved for
|
||||
* machines whose values always include a concrete piece. That piece doesn't necessarily have to be
|
||||
* a (derivative of) {@link BytesPcodeExecutorStatePiece}, but it usually is. To be called an
|
||||
* "emulator" implies that {@link PcodeArithmetic#toConcrete(Object, Purpose)} never throws
|
||||
* {@link ConcretionError} for any value in its state.
|
||||
*
|
||||
* <p>
|
||||
* For a complete example of a p-code emulator, see {@link PcodeEmulator}. For an alternative
|
||||
* implementation incorporating an abstract piece, see the Taint Analyzer.
|
||||
*/
|
||||
public abstract class AbstractPcodeMachine<T> implements PcodeMachine<T> {
|
||||
|
||||
/**
|
||||
* Check and cast the language to Sleigh
|
||||
*
|
||||
* <p>
|
||||
* Sleigh is currently the only realization, but this should give a decent error should that
|
||||
* ever change.
|
||||
*
|
||||
* @param language the language
|
||||
* @return the same language, cast to Sleigh
|
||||
*/
|
||||
protected static SleighLanguage assertSleigh(Language language) {
|
||||
if (!(language instanceof SleighLanguage)) {
|
||||
throw new IllegalArgumentException("Emulation requires a sleigh language");
|
||||
}
|
||||
return (SleighLanguage) language;
|
||||
}
|
||||
|
||||
protected final SleighLanguage language;
|
||||
protected final PcodeArithmetic<T> arithmetic;
|
||||
protected final PcodeUseropLibrary<T> library;
|
||||
@@ -48,12 +76,11 @@ public abstract class AbstractPcodeMachine<T> implements PcodeMachine<T> {
|
||||
* Construct a p-code machine with the given language and arithmetic
|
||||
*
|
||||
* @param language the processor language to be emulated
|
||||
* @param arithmetic the definition of arithmetic p-code ops to be used in emulation
|
||||
*/
|
||||
public AbstractPcodeMachine(SleighLanguage language, PcodeArithmetic<T> arithmetic) {
|
||||
this.language = language;
|
||||
this.arithmetic = arithmetic;
|
||||
public AbstractPcodeMachine(Language language) {
|
||||
this.language = assertSleigh(language);
|
||||
|
||||
this.arithmetic = createArithmetic();
|
||||
this.library = createUseropLibrary();
|
||||
this.stubLibrary = createThreadStubLibrary().compose(library);
|
||||
|
||||
@@ -65,6 +92,13 @@ public abstract class AbstractPcodeMachine<T> implements PcodeMachine<T> {
|
||||
this.initializer = getPluggableInitializer(language);
|
||||
}
|
||||
|
||||
/**
|
||||
* A factory method to create the arithmetic used by this machine
|
||||
*
|
||||
* @return the arithmetic
|
||||
*/
|
||||
protected abstract PcodeArithmetic<T> createArithmetic();
|
||||
|
||||
/**
|
||||
* A factory method to create the userop library shared by all threads in this machine
|
||||
*
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
*/
|
||||
package ghidra.pcode.emu;
|
||||
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
|
||||
/**
|
||||
* A simple p-code thread that operates on concrete bytes
|
||||
*
|
||||
@@ -24,7 +22,7 @@ import ghidra.program.model.address.AddressSpace;
|
||||
* For a complete example of a p-code emulator, see {@link PcodeEmulator}. This is the default
|
||||
* thread for that emulator.
|
||||
*/
|
||||
public class BytesPcodeThread extends AbstractModifiedPcodeThread<byte[]> {
|
||||
public class BytesPcodeThread extends ModifiedPcodeThread<byte[]> {
|
||||
/**
|
||||
* Construct a new thread
|
||||
*
|
||||
@@ -35,17 +33,4 @@ public class BytesPcodeThread extends AbstractModifiedPcodeThread<byte[]> {
|
||||
public BytesPcodeThread(String name, AbstractPcodeMachine<byte[]> machine) {
|
||||
super(name, machine);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getBytesChunk(byte[] res, AddressSpace spc, long off, int size,
|
||||
boolean stopOnUnintialized) {
|
||||
byte[] var = state.getVar(spc, off, size, true);
|
||||
System.arraycopy(var, 0, res, 0, var.length);
|
||||
return var.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setBytesChunk(byte[] val, AddressSpace spc, long off, int size) {
|
||||
state.setVar(spc, off, size, true, val);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import java.util.*;
|
||||
import ghidra.app.emulator.Emulator;
|
||||
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
|
||||
import ghidra.pcode.exec.*;
|
||||
import ghidra.pcode.exec.PcodeArithmetic.Purpose;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.listing.Instruction;
|
||||
@@ -32,7 +33,7 @@ import ghidra.util.Msg;
|
||||
* The default implementation of {@link PcodeThread} suitable for most applications
|
||||
*
|
||||
* <p>
|
||||
* When emulating on concrete state, consider using {@link AbstractModifiedPcodeThread}, so that
|
||||
* When emulating on concrete state, consider using {@link ModifiedPcodeThread}, so that
|
||||
* state modifiers from the older {@link Emulator} are incorporated. In either case, it may be
|
||||
* worthwhile to examine existing state modifiers to ensure they are appropriately represented in
|
||||
* any abstract state. It may be necessary to port them.
|
||||
@@ -129,8 +130,9 @@ public class DefaultPcodeThread<T> implements PcodeThread<T> {
|
||||
* This executor checks for thread suspension and updates the program counter register upon
|
||||
* execution of (external) branches.
|
||||
*/
|
||||
public class PcodeThreadExecutor extends PcodeExecutor<T> {
|
||||
public static class PcodeThreadExecutor<T> extends PcodeExecutor<T> {
|
||||
volatile boolean suspended = false;
|
||||
protected final DefaultPcodeThread<T> thread;
|
||||
|
||||
/**
|
||||
* Construct the executor
|
||||
@@ -140,9 +142,16 @@ public class DefaultPcodeThread<T> implements PcodeThread<T> {
|
||||
* @param arithmetic the arithmetic of the containing machine
|
||||
* @param state the composite state assigned to the thread
|
||||
*/
|
||||
public PcodeThreadExecutor(SleighLanguage language, PcodeArithmetic<T> arithmetic,
|
||||
PcodeExecutorStatePiece<T, T> state) {
|
||||
super(language, arithmetic, state);
|
||||
public PcodeThreadExecutor(DefaultPcodeThread<T> thread) {
|
||||
super(thread.language, thread.arithmetic, thread.state);
|
||||
this.thread = thread;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void executeSleighLine(String line) {
|
||||
PcodeProgram program = SleighProgramCompiler.compileProgram(language, "line",
|
||||
List.of(line + ";"), thread.library);
|
||||
execute(program, thread.library);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -155,11 +164,24 @@ public class DefaultPcodeThread<T> implements PcodeThread<T> {
|
||||
|
||||
@Override
|
||||
protected void branchToAddress(Address target) {
|
||||
overrideCounter(target);
|
||||
thread.overrideCounter(target);
|
||||
}
|
||||
|
||||
public Instruction getInstruction() {
|
||||
return instruction;
|
||||
@Override
|
||||
protected void onMissingUseropDef(PcodeOp op, PcodeFrame frame, String opName,
|
||||
PcodeUseropLibrary<T> library) {
|
||||
if (!thread.onMissingUseropDef(op, opName)) {
|
||||
super.onMissingUseropDef(op, frame, opName, library);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the thread owning this executor
|
||||
*
|
||||
* @return the thread
|
||||
*/
|
||||
public DefaultPcodeThread<T> getThread() {
|
||||
return thread;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,7 +193,7 @@ public class DefaultPcodeThread<T> implements PcodeThread<T> {
|
||||
protected final InstructionDecoder decoder;
|
||||
protected final PcodeUseropLibrary<T> library;
|
||||
|
||||
protected final PcodeThreadExecutor executor;
|
||||
protected final PcodeThreadExecutor<T> executor;
|
||||
protected final Register pc;
|
||||
protected final Register contextreg;
|
||||
|
||||
@@ -245,8 +267,8 @@ public class DefaultPcodeThread<T> implements PcodeThread<T> {
|
||||
*
|
||||
* @return the executor
|
||||
*/
|
||||
protected PcodeThreadExecutor createExecutor() {
|
||||
return new PcodeThreadExecutor(language, arithmetic, state);
|
||||
protected PcodeThreadExecutor<T> createExecutor() {
|
||||
return new PcodeThreadExecutor<>(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -317,12 +339,12 @@ public class DefaultPcodeThread<T> implements PcodeThread<T> {
|
||||
|
||||
@Override
|
||||
public void reInitialize() {
|
||||
long offset = arithmetic.toConcrete(state.getVar(pc)).longValue();
|
||||
long offset = arithmetic.toLong(state.getVar(pc), Purpose.BRANCH);
|
||||
setCounter(language.getDefaultSpace().getAddress(offset, true));
|
||||
|
||||
if (contextreg != Register.NO_CONTEXT) {
|
||||
try {
|
||||
BigInteger ctx = arithmetic.toConcrete(state.getVar(contextreg), true);
|
||||
BigInteger ctx = arithmetic.toBigInteger(state.getVar(contextreg), Purpose.CONTEXT);
|
||||
assignContext(new RegisterValue(contextreg, ctx));
|
||||
}
|
||||
catch (AccessPcodeExecutionException e) {
|
||||
@@ -442,25 +464,34 @@ public class DefaultPcodeThread<T> implements PcodeThread<T> {
|
||||
}
|
||||
|
||||
/**
|
||||
* An extension point for hooking instruction execution before the fact
|
||||
* Extension point: Extra behavior before executing an instruction
|
||||
*
|
||||
* <p>
|
||||
* This is currently used for incorporating state modifiers from the older {@link Emulator}
|
||||
* framework. There is likely utility here when porting those to this framework.
|
||||
*/
|
||||
protected void preExecuteInstruction() {
|
||||
// Extension point
|
||||
}
|
||||
|
||||
/**
|
||||
* An extension point for hooking instruction execution after the fact
|
||||
* Extension point: Extra behavior after executing an instruction
|
||||
*
|
||||
* <p>
|
||||
* This is currently used for incorporating state modifiers from the older {@link Emulator}
|
||||
* framework. There is likely utility here when porting those to this framework.
|
||||
*/
|
||||
protected void postExecuteInstruction() {
|
||||
// Extension point
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension point: Behavior when a p-code userop definition is not found
|
||||
*
|
||||
* @param op the op
|
||||
* @param opName the name of the p-code userop
|
||||
* @return true if handle, false if still undefined
|
||||
*/
|
||||
protected boolean onMissingUseropDef(PcodeOp op, String opName) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -514,6 +545,16 @@ public class DefaultPcodeThread<T> implements PcodeThread<T> {
|
||||
executor.suspended = suspended;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SleighLanguage getLanguage() {
|
||||
return language;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PcodeArithmetic<T> getArithmetic() {
|
||||
return arithmetic;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PcodeExecutor<T> getExecutor() {
|
||||
return executor;
|
||||
|
||||
+30
-39
@@ -20,7 +20,8 @@ import java.lang.reflect.Constructor;
|
||||
import ghidra.app.emulator.Emulator;
|
||||
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
|
||||
import ghidra.pcode.emulate.*;
|
||||
import ghidra.pcode.exec.*;
|
||||
import ghidra.pcode.exec.ConcretionError;
|
||||
import ghidra.pcode.exec.PcodeArithmetic.Purpose;
|
||||
import ghidra.pcode.memstate.MemoryState;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
@@ -29,7 +30,13 @@ import ghidra.program.model.pcode.PcodeOp;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
/**
|
||||
* A p-code thread which incorporates per-architecture state modifiers on concrete bytes
|
||||
* A p-code thread which incorporates per-architecture state modifiers
|
||||
*
|
||||
* <p>
|
||||
* All machines that include a concrete state piece, i.e., all emulators, should use threads derived
|
||||
* from this class. This implementation assumes that the modified state can be concretized. This
|
||||
* doesn't necessarily require the machine to be a concrete emulator, but an abstract machine must
|
||||
* avoid or handle {@link ConcretionError}s arising from state modifiers.
|
||||
*
|
||||
* <p>
|
||||
* For a complete example of a p-code emulator, see {@link PcodeEmulator}.
|
||||
@@ -39,7 +46,7 @@ import ghidra.util.Msg;
|
||||
* incorporated into threads extended from this abstract class, so that they do not yet need to be
|
||||
* ported to this emulator.
|
||||
*/
|
||||
public abstract class AbstractModifiedPcodeThread<T> extends DefaultPcodeThread<T> {
|
||||
public class ModifiedPcodeThread<T> extends DefaultPcodeThread<T> {
|
||||
|
||||
/**
|
||||
* Glue for incorporating state modifiers
|
||||
@@ -106,33 +113,6 @@ public abstract class AbstractModifiedPcodeThread<T> extends DefaultPcodeThread<
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Glue for incorporating state modifiers
|
||||
*
|
||||
* <p>
|
||||
* This allows the modifiers to provider userop definitions.
|
||||
*/
|
||||
protected class GluePcodeThreadExecutor extends PcodeThreadExecutor {
|
||||
public GluePcodeThreadExecutor(SleighLanguage language, PcodeArithmetic<T> arithmetic,
|
||||
PcodeExecutorStatePiece<T, T> state) {
|
||||
super(language, arithmetic, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void executeCallother(PcodeOp op, PcodeFrame frame,
|
||||
PcodeUseropLibrary<T> library) {
|
||||
// Prefer one in the library. Fall-back to state modifier's impl
|
||||
try {
|
||||
super.executeCallother(op, frame, library);
|
||||
}
|
||||
catch (SleighLinkException e) {
|
||||
if (modifier == null || !modifier.executeCallOther(op)) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Part of the glue that makes existing state modifiers work in new emulation framework
|
||||
protected final EmulateInstructionStateModifier modifier;
|
||||
protected final Emulate emulate;
|
||||
@@ -146,7 +126,7 @@ public abstract class AbstractModifiedPcodeThread<T> extends DefaultPcodeThread<
|
||||
* @param name the name of the new thread
|
||||
* @param machine the machine to which the new thread belongs
|
||||
*/
|
||||
public AbstractModifiedPcodeThread(String name, AbstractPcodeMachine<T> machine) {
|
||||
public ModifiedPcodeThread(String name, AbstractPcodeMachine<T> machine) {
|
||||
super(name, machine);
|
||||
|
||||
/**
|
||||
@@ -190,25 +170,28 @@ public abstract class AbstractModifiedPcodeThread<T> extends DefaultPcodeThread<
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PcodeThreadExecutor createExecutor() {
|
||||
return new GluePcodeThreadExecutor(language, arithmetic, state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by a state modifier to read concrete bytes from the thread's state
|
||||
*
|
||||
* @see {@link MemoryState#getChunk(byte[], AddressSpace, long, int, boolean)}
|
||||
*/
|
||||
protected abstract int getBytesChunk(byte[] res, AddressSpace spc, long off, int size,
|
||||
boolean stopOnUnintialized);
|
||||
protected int getBytesChunk(byte[] res, AddressSpace spc, long off, int size,
|
||||
boolean stopOnUnintialized) {
|
||||
T t = state.getVar(spc, off, size, true);
|
||||
byte[] val = arithmetic.toConcrete(t, Purpose.OTHER);
|
||||
System.arraycopy(val, 0, res, 0, val.length);
|
||||
return val.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by a state modifier to write concrete bytes to the thread's state
|
||||
*
|
||||
* @see {@link MemoryState#setChunk(byte[], AddressSpace, long, int)}
|
||||
*/
|
||||
protected abstract void setBytesChunk(byte[] val, AddressSpace spc, long off, int size);
|
||||
protected void setBytesChunk(byte[] val, AddressSpace spc, long off, int size) {
|
||||
T t = arithmetic.fromConst(val);
|
||||
state.setVar(spc, off, size, true, t);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void preExecuteInstruction() {
|
||||
@@ -225,4 +208,12 @@ public abstract class AbstractModifiedPcodeThread<T> extends DefaultPcodeThread<
|
||||
frame.getBranched(), getCounter());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onMissingUseropDef(PcodeOp op, String opName) {
|
||||
if (modifier != null) {
|
||||
return modifier.executeCallOther(op);
|
||||
}
|
||||
return super.onMissingUseropDef(op, opName);
|
||||
}
|
||||
}
|
||||
@@ -17,10 +17,11 @@ package ghidra.pcode.emu;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
|
||||
import ghidra.pcode.emu.auxiliary.AuxPcodeEmulator;
|
||||
import ghidra.pcode.emu.sys.EmuSyscallLibrary;
|
||||
import ghidra.pcode.exec.*;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.lang.Language;
|
||||
|
||||
/**
|
||||
* A p-code machine which executes on concrete bytes and incorporates per-architecture state
|
||||
@@ -36,8 +37,8 @@ import ghidra.program.model.address.Address;
|
||||
* Every class should be extensible and have overridable factory methods so that those extensions
|
||||
* can be incorporated into even more capable emulators. Furthermore, many components, e.g.,
|
||||
* {@link PcodeExecutorState} were designed with composition in mind. Referring to examples, it is
|
||||
* generally pretty easy to extend the emulator via composition. Search for references to
|
||||
* {@link PairedPcodeExecutorState} to find such examples.
|
||||
* straightforward to extend the emulator via composition. Consider using {@link AuxPcodeEmulator}
|
||||
* or one of its derivatives to create a concrete-plus-auxiliary style emulator.
|
||||
*
|
||||
* <pre>
|
||||
* emulator : PcodeMachine<T>
|
||||
@@ -69,9 +70,8 @@ import ghidra.program.model.address.Address;
|
||||
* components. For state, the composition directs memory accesses to the machine's state and
|
||||
* register accesses to the thread's state. (Accesses to the "unique" space are also directed to the
|
||||
* thread's state.) This properly emulates the thread semantics of most platforms. For the userop
|
||||
* library, composition is achieved simply via
|
||||
* {@link PcodeUseropLibrary#compose(PcodeUseropLibrary)}. Thus, each invocation is directed to the
|
||||
* library that exports the invoked userop.
|
||||
* library, composition is achieved via {@link PcodeUseropLibrary#compose(PcodeUseropLibrary)}.
|
||||
* Thus, each invocation is directed to the library that exports the invoked userop.
|
||||
*
|
||||
* <p>
|
||||
* Each thread creates an {@link InstructionDecoder} and a {@link PcodeExecutor}, providing the
|
||||
@@ -83,21 +83,21 @@ import ghidra.program.model.address.Address;
|
||||
* follows: 1) decode the current instruction, 2) generate that instruction's p-code, 3) feed the
|
||||
* code to the executor, 4) resolve the outcome and advance the program counter, then 5) repeat. So
|
||||
* long as the arithmetic and state objects agree in type, a p-code machine can be readily
|
||||
* implemented to manipulate values of that type. Both arithmetic and state are readily composed
|
||||
* using {@link PairedPcodeArithmetic} and {@link PairedPcodeExecutorState} or
|
||||
* {@link PairedPcodeExecutorStatePiece}.
|
||||
* implemented to manipulate values of that type.
|
||||
*
|
||||
* <p>
|
||||
* This concrete emulator chooses a {@link BytesPcodeArithmetic} based on the endianness of the
|
||||
* target language. Its threads are {@link BytesPcodeThread}. The shared and thread-local states are
|
||||
* all {@link BytesPcodeExecutorState}. That state class can be extended to read through to some
|
||||
* other backing object. For example, the memory state could read through to an imported program
|
||||
* image, which allows the emulator's memory to be loaded lazily. The default userop library is
|
||||
* empty. For many use cases, it will be necessary to override {@link #createUseropLibrary()} if
|
||||
* only to implement the language-defined userops. If needed, simulation of the host operating
|
||||
* system is typically achieved by implementing the {@code syscall} userop. The fidelity of that
|
||||
* simulation depends on the use case. See {@link EmuSyscallLibrary} and its implementations to see
|
||||
* what simulations are available "out of the box."
|
||||
* all {@link BytesPcodeExecutorState}. That pieces of that state can be extended to read through to
|
||||
* some other backing object. For example, the memory state could read through to an imported
|
||||
* program image, which allows the emulator's memory to be loaded lazily.
|
||||
*
|
||||
* <p>
|
||||
* The default userop library is empty. For many use cases, it will be necessary to override
|
||||
* {@link #createUseropLibrary()} if only to implement the language-defined userops. If needed,
|
||||
* simulation of the host operating system is typically achieved by implementing the {@code syscall}
|
||||
* userop. The fidelity of that simulation depends on the use case. See {@link EmuSyscallLibrary}
|
||||
* and its implementations to see what simulators are available "out of the box."
|
||||
*
|
||||
* <p>
|
||||
* Alternatively, if the target program never invokes system calls directly, but rather via
|
||||
@@ -117,8 +117,13 @@ public class PcodeEmulator extends AbstractPcodeMachine<byte[]> {
|
||||
*
|
||||
* @param language the language of the target processor
|
||||
*/
|
||||
public PcodeEmulator(SleighLanguage language) {
|
||||
super(language, BytesPcodeArithmetic.forLanguage(language));
|
||||
public PcodeEmulator(Language language) {
|
||||
super(language);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PcodeArithmetic<byte[]> createArithmetic() {
|
||||
return BytesPcodeArithmetic.forLanguage(language);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -47,7 +47,7 @@ public interface PcodeThread<T> {
|
||||
PcodeMachine<T> getMachine();
|
||||
|
||||
/**
|
||||
* Set the emulator's counter without writing to its machine state
|
||||
* Set the thread's program counter without writing to its executor state
|
||||
*
|
||||
* @see #overrideCounter(Address)
|
||||
* @param counter the new target address
|
||||
@@ -62,7 +62,7 @@ public interface PcodeThread<T> {
|
||||
Address getCounter();
|
||||
|
||||
/**
|
||||
* Set the emulator's counter and write the PC of its machine state
|
||||
* Set the thread's program counter and write the pc register of its executor state
|
||||
*
|
||||
* @see #setCounter(Address)
|
||||
* @param counter the new target address
|
||||
@@ -70,7 +70,7 @@ public interface PcodeThread<T> {
|
||||
void overrideCounter(Address counter);
|
||||
|
||||
/**
|
||||
* Adjust the emulator's decoding context without writing to its machine state
|
||||
* Adjust the thread's decoding context without writing to its executor state
|
||||
*
|
||||
* <p>
|
||||
* As in {@link RegisterValue#assign(Register, RegisterValue)}, only those bits having a value
|
||||
@@ -82,14 +82,14 @@ public interface PcodeThread<T> {
|
||||
void assignContext(RegisterValue context);
|
||||
|
||||
/**
|
||||
* Get the emulator's decoding context
|
||||
* Get the thread's decoding context
|
||||
*
|
||||
* @return the context
|
||||
*/
|
||||
RegisterValue getContext();
|
||||
|
||||
/**
|
||||
* Adjust the emulator's parsing context and write the contextreg of its machine state
|
||||
* Adjust the thread's decoding context and write the contextreg of its executor state
|
||||
*
|
||||
* @see #assignContext(RegisterValue)
|
||||
* @param context the new context
|
||||
@@ -100,12 +100,8 @@ public interface PcodeThread<T> {
|
||||
* Set the context at the current counter to the default given by the language
|
||||
*
|
||||
* <p>
|
||||
* This also writes the context to the machine's state. For languages without context, this call
|
||||
* This also writes the context to the thread's state. For languages without context, this call
|
||||
* does nothing.
|
||||
*
|
||||
* <p>
|
||||
* TODO: Seems to me, since this method must be called upon creating any emulator thread, that's
|
||||
* evidence the trace's context manager is not providing correct defaults.
|
||||
*/
|
||||
void overrideContextWithDefault();
|
||||
|
||||
@@ -119,9 +115,9 @@ public interface PcodeThread<T> {
|
||||
*
|
||||
* <p>
|
||||
* Note because of the way Ghidra and Sleigh handle delay slots, the execution of an instruction
|
||||
* with delay slots cannot be separated from the following instructions filling those slots. It
|
||||
* and its slotted instructions are executed in a single "step." However, stepping the
|
||||
* individual p-code ops is still possible using {@link #stepPcodeOp(PcodeFrame)}.
|
||||
* with delay slots cannot be separated from the instructions filling those slots. It and its
|
||||
* slotted instructions are executed in a single "step." However, stepping the individual p-code
|
||||
* ops is still possible using {@link #stepPcodeOp()}.
|
||||
*/
|
||||
void stepInstruction();
|
||||
|
||||
@@ -279,37 +275,33 @@ public interface PcodeThread<T> {
|
||||
*
|
||||
* <p>
|
||||
* When {@link #run()} is invoked by a dedicated thread, suspending the pcode thread is the most
|
||||
* reliable way to halt execution. Note the emulator will halt mid instruction. If this is not
|
||||
* reliable way to halt execution. Note the emulator may halt mid instruction. If this is not
|
||||
* desired, then upon catching the exception, un-suspend the p-code thread and call
|
||||
* {@link #finishInstruction()} or {@link #dropInstruction()}.
|
||||
*/
|
||||
void setSuspended(boolean suspended);
|
||||
|
||||
/**
|
||||
* Get the thread's SLEIGH language (processor model)
|
||||
* Get the thread's Sleigh language (processor model)
|
||||
*
|
||||
* @return the language
|
||||
*/
|
||||
default SleighLanguage getLanguage() {
|
||||
return getExecutor().getLanguage();
|
||||
}
|
||||
SleighLanguage getLanguage();
|
||||
|
||||
/**
|
||||
* Get the thread's p-code arithmetic
|
||||
*
|
||||
* @return the arithmetic
|
||||
*/
|
||||
default PcodeArithmetic<T> getArithmetic() {
|
||||
return getExecutor().getArithmetic();
|
||||
}
|
||||
PcodeArithmetic<T> getArithmetic();
|
||||
|
||||
/**
|
||||
* Get the thread's p-code executor
|
||||
*
|
||||
* <p>
|
||||
* This can be used to execute inject p-code execution, e.g., as part of implementing a userop,
|
||||
* or as part of testing, outside the emulator's usual control flow. Any new frame generated by
|
||||
* the executor is ignored by the emulator. It retains the instruction frame, if any. Note that
|
||||
* This can be used to execute injected p-code, e.g., as part of implementing a userop, or as
|
||||
* part of testing, outside the thread's usual control flow. Any new frame generated by the
|
||||
* executor is ignored by the thread. It retains the instruction frame, if any. Note that
|
||||
* suspension is implemented by the executor, so if this p-code thread is suspended, the
|
||||
* executor cannot execute any code.
|
||||
*
|
||||
@@ -318,8 +310,7 @@ public interface PcodeThread<T> {
|
||||
PcodeExecutor<T> getExecutor();
|
||||
|
||||
/**
|
||||
* Get the complete userop library for this thread, including userops for controlling this
|
||||
* thread
|
||||
* Get the complete userop library for this thread
|
||||
*
|
||||
* @return the library
|
||||
*/
|
||||
@@ -336,7 +327,7 @@ public interface PcodeThread<T> {
|
||||
ThreadPcodeExecutorState<T> getState();
|
||||
|
||||
/**
|
||||
* Override the p-code at the given address with the given SLEIGH source for only this thread
|
||||
* Override the p-code at the given address with the given Sleigh source for only this thread
|
||||
*
|
||||
* <p>
|
||||
* This works the same {@link PcodeMachine#inject(Address, List)} but on a per-thread basis.
|
||||
@@ -353,7 +344,7 @@ public interface PcodeThread<T> {
|
||||
* Remove the per-thread inject, if present, at the given address
|
||||
*
|
||||
* <p>
|
||||
* This has no affect on machine-level injects. If there is one present, it will still override
|
||||
* This has no effect on machine-level injects. If there is one present, it will still override
|
||||
* this thread's p-code if execution reaches the address.
|
||||
*
|
||||
* @param address the address to clear
|
||||
|
||||
+3
-1
@@ -16,6 +16,7 @@
|
||||
package ghidra.pcode.emu;
|
||||
|
||||
import ghidra.pcode.emulate.InstructionDecodeException;
|
||||
import ghidra.pcode.exec.PcodeArithmetic.Purpose;
|
||||
import ghidra.pcode.exec.PcodeExecutorState;
|
||||
import ghidra.program.disassemble.Disassembler;
|
||||
import ghidra.program.disassemble.DisassemblerMessageListener;
|
||||
@@ -72,7 +73,8 @@ public class SleighInstructionDecoder implements InstructionDecoder {
|
||||
public Instruction decodeInstruction(Address address, RegisterValue context) {
|
||||
lastMsg = DEFAULT_ERROR;
|
||||
// Always re-parse block in case bytes change
|
||||
block = disassembler.pseudoDisassembleBlock(state.getConcreteBuffer(address), context, 1);
|
||||
block = disassembler.pseudoDisassembleBlock(
|
||||
state.getConcreteBuffer(address, Purpose.DECODE), context, 1);
|
||||
instruction = block == null ? null : block.getInstructionAt(address);
|
||||
if (instruction == null) {
|
||||
throw new InstructionDecodeException(lastMsg, address);
|
||||
|
||||
+23
-25
@@ -15,20 +15,25 @@
|
||||
*/
|
||||
package ghidra.pcode.emu;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import ghidra.pcode.exec.PcodeArithmetic;
|
||||
import ghidra.pcode.exec.PcodeArithmetic.Purpose;
|
||||
import ghidra.pcode.exec.PcodeExecutorState;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.mem.MemBuffer;
|
||||
|
||||
/**
|
||||
* A p-code executor state that multiplexes shared and thread-local states for use in a
|
||||
* multi-threaded emulator
|
||||
* A p-code executor state that multiplexes shared and thread-local states for use in a machine that
|
||||
* models multi-threading
|
||||
*
|
||||
* @param <T> the type of values stored in the states
|
||||
*/
|
||||
public class ThreadPcodeExecutorState<T> implements PcodeExecutorState<T> {
|
||||
protected final PcodeExecutorState<T> sharedState;
|
||||
protected final PcodeExecutorState<T> localState;
|
||||
protected final PcodeArithmetic<T> arithmetic;
|
||||
|
||||
/**
|
||||
* Create a multiplexed state
|
||||
@@ -39,8 +44,15 @@ public class ThreadPcodeExecutorState<T> implements PcodeExecutorState<T> {
|
||||
*/
|
||||
public ThreadPcodeExecutorState(PcodeExecutorState<T> sharedState,
|
||||
PcodeExecutorState<T> localState) {
|
||||
assert Objects.equals(sharedState.getArithmetic(), localState.getArithmetic());
|
||||
this.sharedState = sharedState;
|
||||
this.localState = localState;
|
||||
this.arithmetic = sharedState.getArithmetic();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PcodeArithmetic<T> getArithmetic() {
|
||||
return arithmetic;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -54,40 +66,26 @@ public class ThreadPcodeExecutorState<T> implements PcodeExecutorState<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public T longToOffset(AddressSpace space, long l) {
|
||||
public void setVar(AddressSpace space, T offset, int size, boolean quantize, T val) {
|
||||
if (isThreadLocalSpace(space)) {
|
||||
return localState.longToOffset(space, l);
|
||||
}
|
||||
else {
|
||||
return sharedState.longToOffset(space, l);
|
||||
localState.setVar(space, offset, size, quantize, val);
|
||||
return;
|
||||
}
|
||||
sharedState.setVar(space, offset, size, quantize, val);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVar(AddressSpace space, T offset, int size, boolean truncateAddressableUnit,
|
||||
T val) {
|
||||
public T getVar(AddressSpace space, T offset, int size, boolean quantize) {
|
||||
if (isThreadLocalSpace(space)) {
|
||||
localState.setVar(space, offset, size, truncateAddressableUnit, val);
|
||||
}
|
||||
else {
|
||||
sharedState.setVar(space, offset, size, truncateAddressableUnit, val);
|
||||
return localState.getVar(space, offset, size, quantize);
|
||||
}
|
||||
return sharedState.getVar(space, offset, size, quantize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getVar(AddressSpace space, T offset, int size, boolean truncateAddressableUnit) {
|
||||
if (isThreadLocalSpace(space)) {
|
||||
return localState.getVar(space, offset, size, truncateAddressableUnit);
|
||||
}
|
||||
else {
|
||||
return sharedState.getVar(space, offset, size, truncateAddressableUnit);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemBuffer getConcreteBuffer(Address address) {
|
||||
public MemBuffer getConcreteBuffer(Address address, Purpose purpose) {
|
||||
assert !isThreadLocalSpace(address.getAddressSpace());
|
||||
return sharedState.getConcreteBuffer(address);
|
||||
return sharedState.getConcreteBuffer(address, purpose);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
/* ###
|
||||
* 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.emu.auxiliary;
|
||||
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import ghidra.pcode.emu.*;
|
||||
import ghidra.pcode.emu.DefaultPcodeThread.PcodeThreadExecutor;
|
||||
import ghidra.pcode.exec.*;
|
||||
import ghidra.program.model.lang.Language;
|
||||
|
||||
/**
|
||||
* An auxiliary emulator parts factory for stand-alone emulation
|
||||
*
|
||||
* <p>
|
||||
* This can manufacture all the parts needed for a stand-alone emulator with concrete and some
|
||||
* implementation-defined auxiliary state. More capable emulators may also use many of these parts.
|
||||
* Usually, the additional capabilities deal with how state is loaded and stored or otherwise made
|
||||
* available to the user. The pattern of use for a stand-alone emulator is usually in a script:
|
||||
* Create an emulator, initialize its state, write instructions to its memory, create and initialize
|
||||
* a thread, point its counter at the instructions, instrument, step/run, inspect, and finally
|
||||
* terminate.
|
||||
*
|
||||
* <p>
|
||||
* This "parts factory" pattern aims to flatten the extension points of the
|
||||
* {@link AbstractPcodeMachine} and its components into a single class. Its use is not required, but
|
||||
* may make things easier. It also encapsulates some "special knowledge," that might not otherwise
|
||||
* be obvious to a developer, e.g., it creates the concrete state pieces, so the developer need not
|
||||
* guess (or keep up to date) the concrete state piece classes to instantiate.
|
||||
*
|
||||
* <p>
|
||||
* The factory itself should be a singleton object. See the Taint Analyzer for a complete solution
|
||||
* using this interface.
|
||||
*
|
||||
* @param <U> the type of auxiliary values
|
||||
*/
|
||||
public interface AuxEmulatorPartsFactory<U> {
|
||||
/**
|
||||
* Get the arithmetic for the emulator given a target langauge
|
||||
*
|
||||
* @param language the language
|
||||
* @return the arithmetic
|
||||
*/
|
||||
PcodeArithmetic<U> getArithmetic(Language language);
|
||||
|
||||
/**
|
||||
* Create the userop library for the emulator (used by all threads)
|
||||
*
|
||||
* @param emulator the emulator
|
||||
* @return the userop library
|
||||
*/
|
||||
PcodeUseropLibrary<Pair<byte[], U>> createSharedUseropLibrary(AuxPcodeEmulator<U> emulator);
|
||||
|
||||
/**
|
||||
* Create a stub userop library for the emulator's threads
|
||||
*
|
||||
* @param emulator the emulator
|
||||
* @return the library of stubs
|
||||
*/
|
||||
PcodeUseropLibrary<Pair<byte[], U>> createLocalUseropStub(AuxPcodeEmulator<U> emulator);
|
||||
|
||||
/**
|
||||
* Create a userop library for a given thread
|
||||
*
|
||||
* @param emulator the emulator
|
||||
* @param thread the thread
|
||||
* @return the userop library
|
||||
*/
|
||||
PcodeUseropLibrary<Pair<byte[], U>> createLocalUseropLibrary(AuxPcodeEmulator<U> emulator,
|
||||
PcodeThread<Pair<byte[], U>> thread);
|
||||
|
||||
/**
|
||||
* Create an executor for the given thread
|
||||
*
|
||||
* <p>
|
||||
* This allows the implementor to override or intercept the logic for individual p-code
|
||||
* operations that would not otherwise be possible in the arithmetic, e.g., to print diagnostics
|
||||
* on a conditional branch.
|
||||
*
|
||||
* @param emulator the emulator
|
||||
* @param thread the thread
|
||||
* @return the executor
|
||||
*/
|
||||
default PcodeThreadExecutor<Pair<byte[], U>> createExecutor(
|
||||
AuxPcodeEmulator<U> emulator, DefaultPcodeThread<Pair<byte[], U>> thread) {
|
||||
return new PcodeThreadExecutor<>(thread);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a thread with the given name
|
||||
*
|
||||
* @param emulator the emulator
|
||||
* @param name the thread's name
|
||||
* @return the thread
|
||||
*/
|
||||
default PcodeThread<Pair<byte[], U>> createThread(AuxPcodeEmulator<U> emulator, String name) {
|
||||
return new AuxPcodeThread<>(name, emulator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the shared (memory) state of a new stand-alone emulator
|
||||
*
|
||||
* <p>
|
||||
* This is usually composed of pieces using {@link PairedPcodeExecutorStatePiece}, but it does
|
||||
* not have to be. It must incorporate the concrete piece provided. It should be self contained
|
||||
* and relatively fast.
|
||||
*
|
||||
* @param emulator the emulator
|
||||
* @param concrete the concrete piece
|
||||
* @return the composed state
|
||||
*/
|
||||
PcodeExecutorState<Pair<byte[], U>> createSharedState(AuxPcodeEmulator<U> emulator,
|
||||
BytesPcodeExecutorStatePiece concrete);
|
||||
|
||||
/**
|
||||
* Create the local (register) state of a new stand-alone emulator
|
||||
*
|
||||
* <p>
|
||||
* This is usually composed of pieces using {@link PairedPcodeExecutorStatePiece}, but it does
|
||||
* not have to be. It must incorporate the concrete piece provided. It should be self contained
|
||||
* and relatively fast.
|
||||
*
|
||||
* @param emulator the emulator
|
||||
* @param thread the thread
|
||||
* @param concrete the concrete piece
|
||||
* @return the composed state
|
||||
*/
|
||||
PcodeExecutorState<Pair<byte[], U>> createLocalState(AuxPcodeEmulator<U> emulator,
|
||||
PcodeThread<Pair<byte[], U>> thread, BytesPcodeExecutorStatePiece concrete);
|
||||
}
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
/* ###
|
||||
* 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.emu.auxiliary;
|
||||
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import ghidra.pcode.emu.AbstractPcodeMachine;
|
||||
import ghidra.pcode.emu.PcodeThread;
|
||||
import ghidra.pcode.exec.*;
|
||||
import ghidra.program.model.lang.Language;
|
||||
|
||||
/**
|
||||
* A stand-alone emulator whose parts are manufactured by a {@link AuxEmulatorPartsFactory}
|
||||
*
|
||||
* <p>
|
||||
* See the parts factory interface: {@link AuxEmulatorPartsFactory}. Also see the Taint Analyzer for
|
||||
* a complete solution based on this class.
|
||||
*
|
||||
* @param <U> the type of auxiliary values
|
||||
*/
|
||||
public abstract class AuxPcodeEmulator<U> extends AbstractPcodeMachine<Pair<byte[], U>> {
|
||||
/**
|
||||
* Create a new emulator
|
||||
*
|
||||
* @param language the language (processor model)
|
||||
*/
|
||||
public AuxPcodeEmulator(Language language) {
|
||||
super(language);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the factory that manufactures parts for this emulator
|
||||
*
|
||||
* @implNote This should just return a singleton, since it is called repeatedly (without
|
||||
* caching) during emulator and thread construction. If, for some reason, a singleton
|
||||
* is not suitable, then this should instantiate it just once and cache the factory
|
||||
* itself. If cached, it should be done in a thread-safe manner.
|
||||
*
|
||||
* @return the factory
|
||||
*/
|
||||
protected abstract AuxEmulatorPartsFactory<U> getPartsFactory();
|
||||
|
||||
@Override
|
||||
protected PcodeArithmetic<Pair<byte[], U>> createArithmetic() {
|
||||
return new PairedPcodeArithmetic<>(
|
||||
BytesPcodeArithmetic.forLanguage(language),
|
||||
getPartsFactory().getArithmetic(language));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PcodeUseropLibrary<Pair<byte[], U>> createUseropLibrary() {
|
||||
return getPartsFactory().createSharedUseropLibrary(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PcodeUseropLibrary<Pair<byte[], U>> createThreadStubLibrary() {
|
||||
return getPartsFactory().createLocalUseropStub(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PcodeExecutorState<Pair<byte[], U>> createSharedState() {
|
||||
return getPartsFactory().createSharedState(this,
|
||||
new BytesPcodeExecutorStatePiece(language));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PcodeExecutorState<Pair<byte[], U>> createLocalState(
|
||||
PcodeThread<Pair<byte[], U>> thread) {
|
||||
return getPartsFactory().createLocalState(this, thread,
|
||||
new BytesPcodeExecutorStatePiece(language));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PcodeThread<Pair<byte[], U>> createThread(String name) {
|
||||
return getPartsFactory().createThread(this, name);
|
||||
}
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
/* ###
|
||||
* 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.emu.auxiliary;
|
||||
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import ghidra.pcode.emu.ModifiedPcodeThread;
|
||||
import ghidra.pcode.exec.PcodeUseropLibrary;
|
||||
|
||||
/**
|
||||
* The default thread for {@link AuxPcodeEmulator}
|
||||
*
|
||||
* <p>
|
||||
* Generally, extending this class should not be necessary, as it already defers to the emulator's
|
||||
* parts factory
|
||||
*
|
||||
* @param <U> the type of auxiliary values
|
||||
*/
|
||||
public class AuxPcodeThread<U> extends ModifiedPcodeThread<Pair<byte[], U>> {
|
||||
|
||||
public AuxPcodeThread(String name, AuxPcodeEmulator<U> emulator) {
|
||||
super(name, emulator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuxPcodeEmulator<U> getMachine() {
|
||||
return (AuxPcodeEmulator<U>) super.getMachine();
|
||||
}
|
||||
|
||||
protected AuxEmulatorPartsFactory<U> getPartsFactory() {
|
||||
return getMachine().getPartsFactory();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PcodeUseropLibrary<Pair<byte[], U>> createUseropLibrary() {
|
||||
return super.createUseropLibrary().compose(
|
||||
getPartsFactory().createLocalUseropLibrary(getMachine(), this));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PcodeThreadExecutor<Pair<byte[], U>> createExecutor() {
|
||||
return getPartsFactory().createExecutor(getMachine(), this);
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -24,6 +24,7 @@ import ghidra.framework.Application;
|
||||
import ghidra.pcode.emu.PcodeMachine;
|
||||
import ghidra.pcode.emu.unix.EmuUnixFileSystem;
|
||||
import ghidra.pcode.emu.unix.EmuUnixUser;
|
||||
import ghidra.pcode.exec.PcodeArithmetic.Purpose;
|
||||
import ghidra.pcode.exec.PcodeExecutor;
|
||||
import ghidra.pcode.exec.PcodeExecutorStatePiece;
|
||||
import ghidra.program.model.data.DataTypeManager;
|
||||
@@ -91,7 +92,7 @@ public class EmuLinuxAmd64SyscallUseropLibrary<T> extends AbstractEmuLinuxSyscal
|
||||
|
||||
@Override
|
||||
public long readSyscallNumber(PcodeExecutorStatePiece<T, T> state) {
|
||||
return machine.getArithmetic().toConcrete(state.getVar(regRAX)).longValue();
|
||||
return machine.getArithmetic().toLong(state.getVar(regRAX), Purpose.OTHER);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+18
-10
@@ -21,15 +21,17 @@ import java.util.List;
|
||||
|
||||
import generic.jar.ResourceFile;
|
||||
import ghidra.framework.Application;
|
||||
import ghidra.pcode.emu.DefaultPcodeThread;
|
||||
import ghidra.pcode.emu.DefaultPcodeThread.PcodeThreadExecutor;
|
||||
import ghidra.pcode.emu.PcodeMachine;
|
||||
import ghidra.pcode.emu.unix.EmuUnixFileSystem;
|
||||
import ghidra.pcode.emu.unix.EmuUnixUser;
|
||||
import ghidra.pcode.exec.*;
|
||||
import ghidra.pcode.exec.PcodeArithmetic.Purpose;
|
||||
import ghidra.program.model.data.DataTypeManager;
|
||||
import ghidra.program.model.data.FileDataTypeManager;
|
||||
import ghidra.program.model.lang.Register;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.pcode.PcodeOp;
|
||||
|
||||
/**
|
||||
* A system call library simulating Linux for x86 (32-bit)
|
||||
@@ -91,7 +93,7 @@ public class EmuLinuxX86SyscallUseropLibrary<T> extends AbstractEmuLinuxSyscallU
|
||||
|
||||
@Override
|
||||
public long readSyscallNumber(PcodeExecutorStatePiece<T, T> state) {
|
||||
return machine.getArithmetic().toConcrete(state.getVar(regEAX)).longValue();
|
||||
return machine.getArithmetic().toLong(state.getVar(regEAX), Purpose.OTHER);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -102,26 +104,32 @@ public class EmuLinuxX86SyscallUseropLibrary<T> extends AbstractEmuLinuxSyscallU
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement this to detect and interpret the {@code INT 0x80} instruction as the syscall
|
||||
* convention
|
||||
*
|
||||
* @param executor to receive the executor
|
||||
* @param library to receive the userop library, presumably replete with syscalls
|
||||
* @param number the interrupt number
|
||||
* @return the address of the fall-through, to hack the {@link PcodeOp#CALLIND}
|
||||
*/
|
||||
@PcodeUserop
|
||||
public T swi(@OpExecutor PcodeExecutor<T> executor, @OpLibrary PcodeUseropLibrary<T> library,
|
||||
T number) {
|
||||
PcodeArithmetic<T> arithmetic = executor.getArithmetic();
|
||||
long intNo = arithmetic.toConcrete(number).longValue();
|
||||
long intNo = arithmetic.toLong(number, Purpose.OTHER);
|
||||
if (intNo == 0x80) {
|
||||
// A CALLIND follows to the return of swi().... OK.
|
||||
// We'll just make that "fall through" instead
|
||||
T next = executor.getState().getVar(regEIP);
|
||||
DefaultPcodeThread<T>.PcodeThreadExecutor te =
|
||||
(DefaultPcodeThread<T>.PcodeThreadExecutor) executor;
|
||||
PcodeThreadExecutor<T> te = (PcodeThreadExecutor<T>) executor;
|
||||
int pcSize = regEIP.getNumBytes();
|
||||
int iLen = te.getInstruction().getLength();
|
||||
next = arithmetic.binaryOp(PcodeArithmetic.INT_ADD, pcSize, pcSize, next, pcSize,
|
||||
int iLen = te.getThread().getInstruction().getLength();
|
||||
next = arithmetic.binaryOp(PcodeOp.INT_ADD, pcSize, pcSize, next, pcSize,
|
||||
arithmetic.fromConst(iLen, pcSize));
|
||||
syscall(executor, library);
|
||||
return next;
|
||||
}
|
||||
else {
|
||||
throw new PcodeExecutionException("Unknown interrupt: 0x" + Long.toString(intNo, 16));
|
||||
}
|
||||
throw new PcodeExecutionException("Unknown interrupt: 0x" + Long.toString(intNo, 16));
|
||||
}
|
||||
}
|
||||
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
/* ###
|
||||
* 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.emu.sys;
|
||||
|
||||
/**
|
||||
* A concrete in-memory bytes store for simulated file contents
|
||||
*
|
||||
* <p>
|
||||
* Note that currently, the total contents cannot exceed a Java array, so the file must remain less
|
||||
* than 2GB in size.
|
||||
*/
|
||||
public class BytesEmuFileContents implements EmuFileContents<byte[]> {
|
||||
protected static final int INIT_CONTENT_SIZE = 1024;
|
||||
|
||||
protected byte[] content = new byte[INIT_CONTENT_SIZE];
|
||||
|
||||
@Override
|
||||
public synchronized long read(long offset, byte[] buf, long fileSize) {
|
||||
// We're using an in-memory array, so limited to int offsets
|
||||
if (offset > Integer.MAX_VALUE) {
|
||||
throw new EmuIOException("Offset is past end of file");
|
||||
}
|
||||
long len = Math.min(buf.length, fileSize - offset);
|
||||
if (len < 0) {
|
||||
throw new EmuIOException("Offset is past end of file");
|
||||
}
|
||||
System.arraycopy(content, (int) offset, buf, 0, (int) len);
|
||||
return len;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized long write(long offset, byte[] buf, long curSize) {
|
||||
long newSize = offset + buf.length;
|
||||
if (newSize > Integer.MAX_VALUE || newSize < 0) {
|
||||
throw new EmuIOException("File size cannot exceed " + Integer.MAX_VALUE + " bytes");
|
||||
}
|
||||
if (newSize > content.length) {
|
||||
byte[] grown = new byte[content.length * 2];
|
||||
System.arraycopy(content, 0, grown, 0, (int) curSize);
|
||||
content = grown;
|
||||
}
|
||||
System.arraycopy(buf, 0, content, (int) offset, buf.length);
|
||||
return buf.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void truncate() {
|
||||
if (content.length > INIT_CONTENT_SIZE) {
|
||||
content = new byte[INIT_CONTENT_SIZE];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/* ###
|
||||
* 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.emu.sys;
|
||||
|
||||
/**
|
||||
* The content store to back a simulated file
|
||||
*
|
||||
* <p>
|
||||
* TODO: Could/should this just be the same interface as an execute state? If so, we'd need to
|
||||
* formalize the store interface and require one for each address space in the state. Sharing that
|
||||
* interface may not be a good idea.... I think implementors can use a common realization if that
|
||||
* suits them.
|
||||
*
|
||||
* <p>
|
||||
* TODO: Actually, a better idea might be to introduce an address factory with custom spaces into
|
||||
* the emulator. Then a library/file could just create an address space and use the state to store
|
||||
* and retrieve the file contents. Better yet, when written down, those contents and markings could
|
||||
* appear in the user's trace.
|
||||
*
|
||||
* @param <T> the type of values in the file
|
||||
*/
|
||||
public interface EmuFileContents<T> {
|
||||
/**
|
||||
* Copy values from the file into the given buffer
|
||||
*
|
||||
* @param offset the offset in the file to read
|
||||
* @param buf the destination buffer, whose size must be known
|
||||
* @param fileSize the size of the file
|
||||
* @return the number of bytes (not necessarily concrete) read
|
||||
*/
|
||||
long read(long offset, T buf, long fileSize);
|
||||
|
||||
/**
|
||||
* Write values from the given buffer into the file
|
||||
*
|
||||
* @param offset the offset in the file to write
|
||||
* @param buf the source buffer, whose size must be known
|
||||
* @param curSize the current size of the file
|
||||
* @return the number of bytes (not necessarily concrete) written
|
||||
*/
|
||||
long write(long offset, T buf, long curSize);
|
||||
|
||||
/**
|
||||
* Erase the contents
|
||||
*
|
||||
* <p>
|
||||
* Note that the file's size will be set to 0, so actual erasure of the contents may not be
|
||||
* necessary, but if the contents are expensive to store, they ought to be disposed.
|
||||
*/
|
||||
void truncate();
|
||||
}
|
||||
+14
-1
@@ -18,15 +18,28 @@ package ghidra.pcode.emu.sys;
|
||||
import java.math.BigInteger;
|
||||
|
||||
import ghidra.pcode.exec.PcodeArithmetic;
|
||||
import ghidra.pcode.exec.PcodeArithmetic.Purpose;
|
||||
|
||||
/**
|
||||
* A simulated process (or thread group) has exited
|
||||
*
|
||||
* <p>
|
||||
* The simulator should catch this exception and terminate accordingly. Continuing execution of the
|
||||
* emulator beyond this exception will cause undefined behavior.
|
||||
*/
|
||||
public class EmuProcessExitedException extends EmuSystemException {
|
||||
|
||||
/**
|
||||
* Attempt to concretize a value and convert it to hex
|
||||
*
|
||||
* @param <T> the type of the status
|
||||
* @param arithmetic the arithmetic to operate on the value
|
||||
* @param status the status value
|
||||
* @return the hex string, or the error message
|
||||
*/
|
||||
public static <T> String tryConcereteToString(PcodeArithmetic<T> arithmetic, T status) {
|
||||
try {
|
||||
BigInteger value = arithmetic.toConcrete(status);
|
||||
BigInteger value = arithmetic.toBigInteger(status, Purpose.INSPECT);
|
||||
return value.toString();
|
||||
}
|
||||
catch (Exception e) {
|
||||
|
||||
+2
-3
@@ -24,7 +24,6 @@ import generic.jar.ResourceFile;
|
||||
import ghidra.framework.Application;
|
||||
import ghidra.pcode.exec.*;
|
||||
import ghidra.pcode.exec.AnnotatedPcodeUseropLibrary.*;
|
||||
import ghidra.pcode.exec.PcodeUseropLibrary.PcodeUseropDefinition;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.lang.PrototypeModel;
|
||||
import ghidra.program.model.listing.Function;
|
||||
@@ -42,11 +41,11 @@ import ghidra.program.model.symbol.*;
|
||||
* userop is automatically included in the userop library. The simplest means of implementing a
|
||||
* syscall library is probably via {@link AnnotatedEmuSyscallUseropLibrary}. It implements this
|
||||
* interface and extends {@link AnnotatedPcodeUseropLibrary}. In addition, it provides its own
|
||||
* annotation system for exporting Java methods as system calls.
|
||||
* annotation system for exporting userops as system calls.
|
||||
*
|
||||
* @param <T> the type of data processed by the system calls, typically {@code byte[]}
|
||||
*/
|
||||
public interface EmuSyscallLibrary<T> {
|
||||
public interface EmuSyscallLibrary<T> extends PcodeUseropLibrary<T> {
|
||||
String SYSCALL_SPACE_NAME = "syscall";
|
||||
String SYSCALL_CONVENTION_NAME = "syscall";
|
||||
|
||||
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
/* ###
|
||||
* 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.emu.sys;
|
||||
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import ghidra.pcode.exec.PairedPcodeExecutorStatePiece;
|
||||
|
||||
/**
|
||||
* The analog of {@link PairedPcodeExecutorStatePiece} for simulated file contents
|
||||
*
|
||||
* @param <L> the type of values for the left
|
||||
* @param <R> the type of values for the right
|
||||
*/
|
||||
public class PairedEmuFileContents<L, R> implements EmuFileContents<Pair<L, R>> {
|
||||
protected final EmuFileContents<L> left;
|
||||
protected final EmuFileContents<R> right;
|
||||
|
||||
/**
|
||||
* Create a paired file contents
|
||||
*
|
||||
* @param left the left contents
|
||||
* @param right the right contents
|
||||
*/
|
||||
public PairedEmuFileContents(EmuFileContents<L> left, EmuFileContents<R> right) {
|
||||
this.left = left;
|
||||
this.right = right;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long read(long offset, Pair<L, R> buf, long fileSize) {
|
||||
long result = left.read(offset, buf.getLeft(), fileSize);
|
||||
right.read(offset, buf.getRight(), fileSize);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long write(long offset, Pair<L, R> buf, long curSize) {
|
||||
long result = left.write(offset, buf.getLeft(), curSize);
|
||||
right.write(offset, buf.getRight(), curSize);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void truncate() {
|
||||
left.truncate();
|
||||
right.truncate();
|
||||
}
|
||||
}
|
||||
+39
-2
@@ -15,6 +15,11 @@
|
||||
*/
|
||||
package ghidra.pcode.emu.unix;
|
||||
|
||||
import ghidra.pcode.emu.sys.EmuFileContents;
|
||||
import ghidra.pcode.exec.PcodeArithmetic;
|
||||
import ghidra.pcode.exec.PcodeArithmetic.Purpose;
|
||||
import ghidra.util.MathUtilities;
|
||||
|
||||
/**
|
||||
* An abstract file contained in an emulated file system
|
||||
*
|
||||
@@ -25,8 +30,10 @@ package ghidra.pcode.emu.unix;
|
||||
* @param <T> the type of values stored in the file
|
||||
*/
|
||||
public abstract class AbstractEmuUnixFile<T> implements EmuUnixFile<T> {
|
||||
|
||||
protected final String pathname;
|
||||
protected final EmuUnixFileStat stat = createStat();
|
||||
protected final EmuUnixFileStat stat;
|
||||
protected EmuFileContents<T> contents;
|
||||
|
||||
/**
|
||||
* Construct a new file
|
||||
@@ -41,7 +48,9 @@ public abstract class AbstractEmuUnixFile<T> implements EmuUnixFile<T> {
|
||||
*/
|
||||
public AbstractEmuUnixFile(String pathname, int mode) {
|
||||
this.pathname = pathname;
|
||||
stat.st_mode = mode;
|
||||
this.stat = createStat();
|
||||
this.stat.st_mode = mode;
|
||||
this.contents = createDefaultContents();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -53,6 +62,13 @@ public abstract class AbstractEmuUnixFile<T> implements EmuUnixFile<T> {
|
||||
return new EmuUnixFileStat();
|
||||
}
|
||||
|
||||
/**
|
||||
* A factory method for the file's default contents
|
||||
*
|
||||
* @return the contents
|
||||
*/
|
||||
protected abstract EmuFileContents<T> createDefaultContents();
|
||||
|
||||
@Override
|
||||
public String getPathname() {
|
||||
return pathname;
|
||||
@@ -62,4 +78,25 @@ public abstract class AbstractEmuUnixFile<T> implements EmuUnixFile<T> {
|
||||
public EmuUnixFileStat getStat() {
|
||||
return stat;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T read(PcodeArithmetic<T> arithmetic, T offset, T buf) {
|
||||
long off = arithmetic.toLong(offset, Purpose.OTHER);
|
||||
long len = contents.read(off, buf, stat.st_size);
|
||||
return arithmetic.fromConst(len, (int) arithmetic.sizeOf(offset));
|
||||
}
|
||||
|
||||
@Override
|
||||
public T write(PcodeArithmetic<T> arithmetic, T offset, T buf) {
|
||||
long off = arithmetic.toLong(offset, Purpose.OTHER);
|
||||
long len = contents.write(off, buf, stat.st_size);
|
||||
stat.st_size = MathUtilities.unsignedMax(stat.st_size, off + len);
|
||||
return arithmetic.fromConst(len, (int) arithmetic.sizeOf(offset));
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void truncate() {
|
||||
stat.st_size = 0;
|
||||
contents.truncate();
|
||||
}
|
||||
}
|
||||
|
||||
+132
-20
@@ -23,6 +23,7 @@ import ghidra.pcode.emu.sys.AnnotatedEmuSyscallUseropLibrary;
|
||||
import ghidra.pcode.emu.sys.EmuProcessExitedException;
|
||||
import ghidra.pcode.emu.unix.EmuUnixFileSystem.OpenFlag;
|
||||
import ghidra.pcode.exec.*;
|
||||
import ghidra.pcode.exec.PcodeArithmetic.Purpose;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.data.StringDataInstance;
|
||||
import ghidra.program.model.data.StringDataType;
|
||||
@@ -33,6 +34,10 @@ import ghidra.program.model.mem.MemBuffer;
|
||||
* An abstract library of UNIX system calls, suitable for use with any processor
|
||||
*
|
||||
* <p>
|
||||
* See the UNIX manual pages for more information about each specific system call, error numbers,
|
||||
* etc.
|
||||
*
|
||||
* <p>
|
||||
* TODO: The rest of the system calls common to UNIX.
|
||||
*
|
||||
* @param <T> the type of values processed by the library
|
||||
@@ -41,10 +46,7 @@ public abstract class AbstractEmuUnixSyscallUseropLibrary<T>
|
||||
extends AnnotatedEmuSyscallUseropLibrary<T> {
|
||||
|
||||
/**
|
||||
* The errno values as defined by the simulator
|
||||
*
|
||||
* <p>
|
||||
* See a UNIX manual for their exact meaning
|
||||
* The errno values as defined by the OS simulator
|
||||
*/
|
||||
public enum Errno {
|
||||
EBADF;
|
||||
@@ -87,6 +89,11 @@ public abstract class AbstractEmuUnixSyscallUseropLibrary<T>
|
||||
this.intSize = program.getCompilerSpec().getDataOrganization().getIntegerSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the first available file descriptor
|
||||
*
|
||||
* @return the lowest available descriptor
|
||||
*/
|
||||
protected int lowestFd() {
|
||||
Integer lowest = closedFds.pollFirst();
|
||||
if (lowest != null) {
|
||||
@@ -95,6 +102,15 @@ public abstract class AbstractEmuUnixSyscallUseropLibrary<T>
|
||||
return descriptors.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Claim the lowest available file descriptor number for the given descriptor object
|
||||
*
|
||||
* <p>
|
||||
* The descriptor will be added to the descriptor table for the claimed number
|
||||
*
|
||||
* @param desc the descriptor object
|
||||
* @return the descriptor number
|
||||
*/
|
||||
protected int claimFd(EmuUnixFileDescriptor<T> desc) {
|
||||
synchronized (descriptors) {
|
||||
int fd = lowestFd();
|
||||
@@ -103,6 +119,13 @@ public abstract class AbstractEmuUnixSyscallUseropLibrary<T>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file descriptor object for the given file descriptor number
|
||||
*
|
||||
* @param fd the descriptor number
|
||||
* @return the descriptor object
|
||||
* @throws EmuUnixException with {@link Errno#EBADF} if the file descriptor is invalid
|
||||
*/
|
||||
protected EmuUnixFileDescriptor<T> findFd(int fd) {
|
||||
synchronized (descriptors) {
|
||||
EmuUnixFileDescriptor<T> desc = descriptors.get(fd);
|
||||
@@ -113,6 +136,13 @@ public abstract class AbstractEmuUnixSyscallUseropLibrary<T>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Release/invalidate the given file descriptor number
|
||||
*
|
||||
* @param fd the file descriptor number
|
||||
* @return the removed descriptor object
|
||||
* @throws EmuUnixException with {@link Errno#EBADF} if the file descriptor is invalid
|
||||
*/
|
||||
protected EmuUnixFileDescriptor<T> releaseFd(int fd) {
|
||||
synchronized (descriptors) {
|
||||
if (descriptors.size() + closedFds.size() - 1 == fd) {
|
||||
@@ -127,6 +157,9 @@ public abstract class AbstractEmuUnixSyscallUseropLibrary<T>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Plug our Sleigh-defined syscalls in
|
||||
*/
|
||||
@Override
|
||||
protected StructuredPart newStructuredPart() {
|
||||
return new UnixStructuredPart();
|
||||
@@ -172,6 +205,13 @@ public abstract class AbstractEmuUnixSyscallUseropLibrary<T>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Place the errno into the machine as expected by the simulated platform's ABI
|
||||
*
|
||||
* @param executor the executor for the thread running this system call
|
||||
* @param errno the error number
|
||||
* @return true if the errno was successfully placed
|
||||
*/
|
||||
protected abstract boolean returnErrno(PcodeExecutor<T> executor, int errno);
|
||||
|
||||
@Override
|
||||
@@ -186,54 +226,91 @@ public abstract class AbstractEmuUnixSyscallUseropLibrary<T>
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* The UNIX {@code exit} system call
|
||||
*
|
||||
* <p>
|
||||
* This just throws an exception, which the overall simulator or script should catch.
|
||||
*
|
||||
* @param status the status code
|
||||
* @return never
|
||||
* @throws EmuProcessExitedException always
|
||||
*/
|
||||
@PcodeUserop
|
||||
@EmuSyscall("exit")
|
||||
public T unix_exit(T status) {
|
||||
throw new EmuProcessExitedException(machine.getArithmetic(), status);
|
||||
}
|
||||
|
||||
/**
|
||||
* The UNIX {@code read} system call
|
||||
*
|
||||
* @param state to receive the thread's state
|
||||
* @param fd the file descriptor
|
||||
* @param bufPtr the pointer to the buffer to receive the data
|
||||
* @param count the number of bytes to read
|
||||
* @return the number of bytes successfully read
|
||||
*/
|
||||
@PcodeUserop
|
||||
@EmuSyscall("read")
|
||||
public T unix_read(@OpState PcodeExecutorStatePiece<T, T> state, T fd, T bufPtr, T count) {
|
||||
public T unix_read(@OpState PcodeExecutorState<T> state, T fd, T bufPtr, T count) {
|
||||
PcodeArithmetic<T> arithmetic = machine.getArithmetic();
|
||||
int ifd = arithmetic.toConcrete(fd).intValue();
|
||||
int ifd = (int) arithmetic.toLong(fd, Purpose.OTHER);
|
||||
EmuUnixFileDescriptor<T> desc = findFd(ifd);
|
||||
AddressSpace space = machine.getLanguage().getAddressFactory().getDefaultAddressSpace();
|
||||
int size = arithmetic.toConcrete(count).intValue(); // TODO: Not idea to require concrete size
|
||||
// TODO: Not ideal to require concrete size, but gets unwieldy to leave it abstract
|
||||
int size = (int) arithmetic.toLong(count, Purpose.OTHER);
|
||||
T buf = arithmetic.fromConst(0, size);
|
||||
T result = desc.read(buf);
|
||||
int iresult = arithmetic.toConcrete(result).intValue();
|
||||
int iresult = (int) arithmetic.toLong(result, Purpose.OTHER);
|
||||
state.setVar(space, bufPtr, iresult, true, buf);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* The UNIX {@code write} system call
|
||||
*
|
||||
* @param state to receive the thread's state
|
||||
* @param fd the file descriptor
|
||||
* @param bufPtr the pointer to the buffer of data to write
|
||||
* @param count the number of bytes to write
|
||||
* @return the number of bytes successfully written
|
||||
*/
|
||||
@PcodeUserop
|
||||
@EmuSyscall("write")
|
||||
public T unix_write(@OpState PcodeExecutorStatePiece<T, T> state, T fd, T bufPtr, T count) {
|
||||
public T unix_write(@OpState PcodeExecutorState<T> state, T fd, T bufPtr, T count) {
|
||||
PcodeArithmetic<T> arithmetic = machine.getArithmetic();
|
||||
int ifd = arithmetic.toConcrete(fd).intValue();
|
||||
int ifd = (int) arithmetic.toLong(fd, Purpose.OTHER);
|
||||
EmuUnixFileDescriptor<T> desc = findFd(ifd);
|
||||
AddressSpace space = machine.getLanguage().getAddressFactory().getDefaultAddressSpace();
|
||||
// TODO: Not ideal to require concrete size. What are the alternatives, though?
|
||||
// TODO: size should actually be long (size_t)
|
||||
int size = arithmetic.toConcrete(count).intValue();
|
||||
int size = (int) arithmetic.toLong(count, Purpose.OTHER);
|
||||
T buf = state.getVar(space, bufPtr, size, true);
|
||||
// TODO: Write back into state? "write" shouldn't touch the buffer....
|
||||
return desc.write(buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* The UNIX {@code open} system call
|
||||
*
|
||||
* @param state to receive the thread's state
|
||||
* @param pathnamePtr the file's path (pointer to character string)
|
||||
* @param flags the flags
|
||||
* @param mode the mode
|
||||
* @return the file descriptor
|
||||
*/
|
||||
@PcodeUserop
|
||||
@EmuSyscall("open")
|
||||
public T unix_open(@OpState PcodeExecutorStatePiece<T, T> state, T pathnamePtr, T flags,
|
||||
T mode) {
|
||||
public T unix_open(@OpState PcodeExecutorState<T> state, T pathnamePtr, T flags, T mode) {
|
||||
PcodeArithmetic<T> arithmetic = machine.getArithmetic();
|
||||
int iflags = arithmetic.toConcrete(flags).intValue();
|
||||
int imode = arithmetic.toConcrete(mode).intValue();
|
||||
long pathnameOff = arithmetic.toConcrete(pathnamePtr).longValue();
|
||||
int iflags = (int) arithmetic.toLong(flags, Purpose.OTHER);
|
||||
int imode = (int) arithmetic.toLong(mode, Purpose.OTHER);
|
||||
long pathnameOff = arithmetic.toLong(pathnamePtr, Purpose.OTHER);
|
||||
AddressSpace space = machine.getLanguage().getAddressFactory().getDefaultAddressSpace();
|
||||
|
||||
SettingsImpl settings = new SettingsImpl();
|
||||
MemBuffer buffer = state.getConcreteBuffer(space.getAddress(pathnameOff));
|
||||
MemBuffer buffer = state.getConcreteBuffer(space.getAddress(pathnameOff), Purpose.OTHER);
|
||||
StringDataInstance sdi =
|
||||
new StringDataInstance(StringDataType.dataType, settings, buffer, -1);
|
||||
sdi = new StringDataInstance(StringDataType.dataType, settings, buffer,
|
||||
@@ -245,26 +322,47 @@ public abstract class AbstractEmuUnixSyscallUseropLibrary<T>
|
||||
return arithmetic.fromConst(ifd, intSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* The UNIX {@code close} system call
|
||||
*
|
||||
* @param fd the file descriptor
|
||||
* @return 0 for success
|
||||
*/
|
||||
@PcodeUserop
|
||||
@EmuSyscall("close")
|
||||
public T unix_close(T fd) {
|
||||
PcodeArithmetic<T> arithmetic = machine.getArithmetic();
|
||||
int ifd = arithmetic.toConcrete(fd).intValue();
|
||||
int ifd = (int) arithmetic.toLong(fd, Purpose.OTHER);
|
||||
// TODO: Some fs.close or file.close, when all handles have released it?
|
||||
EmuUnixFileDescriptor<T> desc = releaseFd(ifd);
|
||||
desc.close();
|
||||
return arithmetic.fromConst(0, intSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* The UNIX {@code group_exit} system call
|
||||
*
|
||||
* <p>
|
||||
* This just throws an exception, which the overall simulator or script should catch.
|
||||
*
|
||||
* @param status the status code
|
||||
* @return never
|
||||
* @throws EmuProcessExitedException always
|
||||
*/
|
||||
@PcodeUserop
|
||||
@EmuSyscall("group_exit")
|
||||
public void unix_group_exit(T status) {
|
||||
throw new EmuProcessExitedException(machine.getArithmetic(), status);
|
||||
}
|
||||
|
||||
/**
|
||||
* System calls defined using Structured Sleigh
|
||||
*/
|
||||
protected class UnixStructuredPart extends StructuredPart {
|
||||
/** "Extern" declaration of {@code unix_read} */
|
||||
final UseropDecl unix_read = userop(type("size_t"), "unix_read",
|
||||
types("int", "void *", "size_t"));
|
||||
/** "Extern" declaration of {@code unix_write} */
|
||||
final UseropDecl unix_write = userop(type("size_t"), "unix_write",
|
||||
types("int", "void *", "size_t"));;
|
||||
|
||||
@@ -273,8 +371,8 @@ public abstract class AbstractEmuUnixSyscallUseropLibrary<T>
|
||||
*
|
||||
* <p>
|
||||
* This is essentially a macro by virtue of the host (Java) language. Note that
|
||||
* {@link #_result(RVal)} from here will cause the whole userop to return, not just this
|
||||
* inlined portion.
|
||||
* {@link #_result(RVal)} from here will cause the whole userop to return, not just from
|
||||
* {@link #gatherScatterIovec(Var, Var, Var, UseropDecl)}.
|
||||
*/
|
||||
protected void gatherScatterIovec(Var in_fd, Var in_iovec, Var in_iovcnt,
|
||||
UseropDecl subOp) {
|
||||
@@ -293,6 +391,13 @@ public abstract class AbstractEmuUnixSyscallUseropLibrary<T>
|
||||
_result(tmp_total);
|
||||
}
|
||||
|
||||
/**
|
||||
* The UNIX {@code readv} system call
|
||||
*
|
||||
* @param in_fd the file descriptor
|
||||
* @param in_iovec pointer to the vector of buffers
|
||||
* @param in_iovcnt the number of buffers
|
||||
*/
|
||||
@StructuredUserop(type = "size_t")
|
||||
@EmuSyscall("readv")
|
||||
public void unix_readv(@Param(type = "int", name = "in_fd") Var in_fd,
|
||||
@@ -301,6 +406,13 @@ public abstract class AbstractEmuUnixSyscallUseropLibrary<T>
|
||||
gatherScatterIovec(in_fd, in_iovec, in_iovcnt, unix_read);
|
||||
}
|
||||
|
||||
/**
|
||||
* The UNIX {@code writev} system call
|
||||
*
|
||||
* @param in_fd the file descriptor
|
||||
* @param in_iovec pointer to the vector of buffers
|
||||
* @param in_iovcnt the number of buffers
|
||||
*/
|
||||
@StructuredUserop(type = "size_t")
|
||||
@EmuSyscall("writev")
|
||||
public void unix_writev(@Param(type = "int", name = "in_fd") Var in_fd,
|
||||
|
||||
+4
-46
@@ -15,11 +15,8 @@
|
||||
*/
|
||||
package ghidra.pcode.emu.unix;
|
||||
|
||||
import ghidra.pcode.emu.sys.EmuIOException;
|
||||
import ghidra.pcode.exec.BytesPcodeArithmetic;
|
||||
import ghidra.pcode.exec.PcodeArithmetic;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.util.MathUtilities;
|
||||
import ghidra.pcode.emu.sys.BytesEmuFileContents;
|
||||
import ghidra.pcode.emu.sys.EmuFileContents;
|
||||
|
||||
/**
|
||||
* A concrete in-memory file system simulator suitable for UNIX programs
|
||||
@@ -30,52 +27,13 @@ public class BytesEmuUnixFileSystem extends AbstractEmuUnixFileSystem<byte[]> {
|
||||
* A concrete in-memory file suitable for UNIX programs
|
||||
*/
|
||||
protected static class BytesEmuUnixFile extends AbstractEmuUnixFile<byte[]> {
|
||||
protected static final int INIT_CONTENT_SIZE = 1024;
|
||||
|
||||
protected byte[] content = new byte[INIT_CONTENT_SIZE];
|
||||
|
||||
/**
|
||||
* Construct a new file
|
||||
*
|
||||
* @see BytesEmuUnixFileSystem#newFile(String)
|
||||
* @param pathname the original pathname of the file
|
||||
*/
|
||||
public BytesEmuUnixFile(String pathname, int mode) {
|
||||
super(pathname, mode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized byte[] read(PcodeArithmetic<byte[]> arithmetic, byte[] offset,
|
||||
byte[] buf) {
|
||||
// NOTE: UNIX takes long offsets, but since we're backing with arrays, we use int
|
||||
int off = arithmetic.toConcrete(offset).intValue();
|
||||
int len = Math.min(buf.length, (int) stat.st_size - off);
|
||||
if (len < 0) {
|
||||
throw new EmuIOException("Offset is past end of file");
|
||||
}
|
||||
System.arraycopy(content, off, buf, 0, len);
|
||||
return arithmetic.fromConst(len, offset.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized byte[] write(PcodeArithmetic<byte[]> arithmetic, byte[] offset,
|
||||
byte[] buf) {
|
||||
int off = arithmetic.toConcrete(offset).intValue();
|
||||
if (off + buf.length > content.length) {
|
||||
byte[] grown = new byte[content.length * 2];
|
||||
System.arraycopy(content, 0, grown, 0, (int) stat.st_size);
|
||||
content = grown;
|
||||
}
|
||||
System.arraycopy(buf, 0, content, off, buf.length);
|
||||
// TODO: Uhh, arrays can't get larger than INT_MAX anyway
|
||||
stat.st_size = MathUtilities.unsignedMax(stat.st_size, off + buf.length);
|
||||
return arithmetic.fromConst(buf.length, offset.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void truncate() {
|
||||
stat.st_size = 0;
|
||||
// TODO: Zero content?
|
||||
protected EmuFileContents<byte[]> createDefaultContents() {
|
||||
return new BytesEmuFileContents();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+12
-4
@@ -21,7 +21,6 @@ import ghidra.pcode.emu.PcodeMachine;
|
||||
import ghidra.pcode.emu.sys.EmuIOException;
|
||||
import ghidra.pcode.emu.unix.EmuUnixFileSystem.OpenFlag;
|
||||
import ghidra.pcode.exec.PcodeArithmetic;
|
||||
import ghidra.pcode.opbehavior.*;
|
||||
import ghidra.program.model.lang.CompilerSpec;
|
||||
import ghidra.program.model.pcode.PcodeOp;
|
||||
|
||||
@@ -62,6 +61,15 @@ public class DefaultEmuUnixFileHandle<T> implements EmuUnixFileDescriptor<T> {
|
||||
this.offset = arithmetic.fromConst(0, offsetBytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file opened to this handle
|
||||
*
|
||||
* @return the file
|
||||
*/
|
||||
public EmuUnixFile<T> getFile() {
|
||||
return file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the file is readable, throwing {@link EmuIOException} if not
|
||||
*/
|
||||
@@ -86,9 +94,9 @@ public class DefaultEmuUnixFileHandle<T> implements EmuUnixFileDescriptor<T> {
|
||||
* @param len the number of bytes to advance
|
||||
*/
|
||||
protected void advanceOffset(T len) {
|
||||
int sizeofLen = arithmetic.toConcrete(arithmetic.sizeOf(len)).intValue();
|
||||
offset = arithmetic.binaryOp(PcodeArithmetic.INT_ADD, offsetBytes, offsetBytes, offset,
|
||||
sizeofLen, len);
|
||||
int sizeofLen = (int) arithmetic.sizeOf(len);
|
||||
offset =
|
||||
arithmetic.binaryOp(PcodeOp.INT_ADD, offsetBytes, offsetBytes, offset, sizeofLen, len);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+28
-45
@@ -16,31 +16,29 @@
|
||||
package ghidra.pcode.exec;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import ghidra.pcode.utils.Utils;
|
||||
import ghidra.pcode.exec.PcodeArithmetic.Purpose;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.program.model.mem.*;
|
||||
import ghidra.program.model.pcode.PcodeOp;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
/**
|
||||
* An abstract p-code executor state for storing bytes, retrieved and set as arrays.
|
||||
* An abstract p-code executor state piece for storing and retrieving bytes as arrays
|
||||
*
|
||||
* @param <B> if this state is a cache, the type of object backing each address space
|
||||
* @param <S> the type of an execute state space, internally associated with an address space
|
||||
* @param <S> the type of an executor state space, internally associated with an address space
|
||||
*/
|
||||
public abstract class AbstractBytesPcodeExecutorState<B, S extends BytesPcodeExecutorStateSpace<B>>
|
||||
extends AbstractLongOffsetPcodeExecutorState<byte[], S> {
|
||||
public abstract class AbstractBytesPcodeExecutorStatePiece<S extends BytesPcodeExecutorStateSpace<?>>
|
||||
extends AbstractLongOffsetPcodeExecutorStatePiece<byte[], byte[], S> {
|
||||
|
||||
/**
|
||||
* A memory buffer bound to a given space in this state
|
||||
*/
|
||||
protected class StateMemBuffer implements MemBufferAdapter {
|
||||
protected final Address address;
|
||||
protected final BytesPcodeExecutorStateSpace<B> source;
|
||||
protected final BytesPcodeExecutorStateSpace<?> source;
|
||||
|
||||
/**
|
||||
* Construct a buffer bound to the given space, at the given address
|
||||
@@ -48,7 +46,7 @@ public abstract class AbstractBytesPcodeExecutorState<B, S extends BytesPcodeExe
|
||||
* @param address the address
|
||||
* @param source the space
|
||||
*/
|
||||
public StateMemBuffer(Address address, BytesPcodeExecutorStateSpace<B> source) {
|
||||
public StateMemBuffer(Address address, BytesPcodeExecutorStateSpace<?> source) {
|
||||
this.address = address;
|
||||
this.source = source;
|
||||
}
|
||||
@@ -76,59 +74,44 @@ public abstract class AbstractBytesPcodeExecutorState<B, S extends BytesPcodeExe
|
||||
}
|
||||
}
|
||||
|
||||
protected final Map<AddressSpace, S> spaces = new HashMap<>();
|
||||
|
||||
protected final Language language;
|
||||
protected final AbstractSpaceMap<S> spaceMap = newSpaceMap();
|
||||
|
||||
/**
|
||||
* Construct a state for the given language
|
||||
*
|
||||
* @param language the langauge (used for its memory model)
|
||||
* @param language the language, used for its memory model and arithmetic
|
||||
*/
|
||||
public AbstractBytesPcodeExecutorState(Language language) {
|
||||
super(language, BytesPcodeArithmetic.forLanguage(language));
|
||||
this.language = language;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected long offsetToLong(byte[] offset) {
|
||||
return Utils.bytesToLong(offset, offset.length, language.isBigEndian());
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] longToOffset(AddressSpace space, long l) {
|
||||
return arithmetic.fromConst(l, space.getPointerSize());
|
||||
public AbstractBytesPcodeExecutorStatePiece(Language language) {
|
||||
this(language, BytesPcodeArithmetic.forLanguage(language));
|
||||
}
|
||||
|
||||
/**
|
||||
* If this state is a cache, get the object backing the given address space
|
||||
* Construct a state for the given language
|
||||
*
|
||||
* @param space the space
|
||||
* @return the backing object
|
||||
* @param language the language, used for its memory model
|
||||
* @param arithmetic the arithmetic
|
||||
*/
|
||||
protected B getBacking(AddressSpace space) {
|
||||
return null;
|
||||
public AbstractBytesPcodeExecutorStatePiece(Language language,
|
||||
PcodeArithmetic<byte[]> arithmetic) {
|
||||
super(language, arithmetic, arithmetic);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new space internally associated with the given address space, having the given
|
||||
* backing
|
||||
* A factory method for this state's space map.
|
||||
*
|
||||
* <p>
|
||||
* As the name implies, this often simply wraps {@code S}'s constructor
|
||||
* Because most of the special logic for extensions is placed in the "state space," i.e., an
|
||||
* object assigned to a particular address space in the state's language, this factory method
|
||||
* must provide the map to create and maintain those spaces. That map will in turn be the
|
||||
* factory of the spaces themselves, allowing extensions to provide additional read/write logic.
|
||||
*
|
||||
* @param space the address space
|
||||
* @param backing the backing, if applicable
|
||||
* @return the new space
|
||||
* @return the new space map
|
||||
*/
|
||||
protected abstract S newSpace(AddressSpace space, B backing);
|
||||
protected abstract AbstractSpaceMap<S> newSpaceMap();
|
||||
|
||||
@Override
|
||||
protected S getForSpace(AddressSpace space, boolean toWrite) {
|
||||
return spaces.computeIfAbsent(space, s -> {
|
||||
B backing = s.isUniqueSpace() ? null : getBacking(space);
|
||||
return newSpace(s, backing);
|
||||
});
|
||||
return spaceMap.getForSpace(space, toWrite);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -140,7 +123,7 @@ public abstract class AbstractBytesPcodeExecutorState<B, S extends BytesPcodeExe
|
||||
if (val.length < size) {
|
||||
Msg.warn(this, "Value is smaller than variable: " + val.length + " < " + size +
|
||||
". Zero extending");
|
||||
val = arithmetic.unaryOp(PcodeArithmetic.INT_ZEXT, size, val.length, val);
|
||||
val = arithmetic.unaryOp(PcodeOp.INT_ZEXT, size, val.length, val);
|
||||
}
|
||||
space.write(offset, val, 0, size);
|
||||
}
|
||||
@@ -156,7 +139,7 @@ public abstract class AbstractBytesPcodeExecutorState<B, S extends BytesPcodeExe
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemBuffer getConcreteBuffer(Address address) {
|
||||
public MemBuffer getConcreteBuffer(Address address, Purpose purpose) {
|
||||
return new StateMemBuffer(address, getForSpace(address.getAddressSpace(), false));
|
||||
}
|
||||
}
|
||||
+105
-20
@@ -15,12 +15,19 @@
|
||||
*/
|
||||
package ghidra.pcode.exec;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.pcode.exec.PcodeArithmetic.Purpose;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.lang.Language;
|
||||
|
||||
/**
|
||||
* An abstract executor state piece which internally uses {@code long} to address contents
|
||||
*
|
||||
* <p>
|
||||
* This also provides an internal mechanism for breaking the piece down into the spaces defined by a
|
||||
* language. It also provides for the special treatment of the {@code unique} space.
|
||||
*
|
||||
* @param <A> the type used to address contents, convertible to and from {@code long}
|
||||
* @param <T> the type of values stored
|
||||
* @param <S> the type of an execute state space, internally associated with an address space
|
||||
@@ -28,23 +35,108 @@ import ghidra.program.model.lang.Language;
|
||||
public abstract class AbstractLongOffsetPcodeExecutorStatePiece<A, T, S>
|
||||
implements PcodeExecutorStatePiece<A, T> {
|
||||
|
||||
/**
|
||||
* A map of address spaces to objects which store or cache state for that space
|
||||
*
|
||||
* @param <S> the type of object for each address space
|
||||
*/
|
||||
public abstract static class AbstractSpaceMap<S> {
|
||||
protected final Map<AddressSpace, S> spaces = new HashMap<>();
|
||||
|
||||
public abstract S getForSpace(AddressSpace space, boolean toWrite);
|
||||
|
||||
public Collection<S> values() {
|
||||
return spaces.values();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this when each S contains the complete state for the address space
|
||||
*
|
||||
* @param <S> the type of object for each address space
|
||||
*/
|
||||
public abstract static class SimpleSpaceMap<S> extends AbstractSpaceMap<S> {
|
||||
/**
|
||||
* Construct a new space internally associated with the given address space
|
||||
*
|
||||
* <p>
|
||||
* As the name implies, this often simply wraps {@code S}'s constructor
|
||||
*
|
||||
* @param space the address space
|
||||
* @return the new space
|
||||
*/
|
||||
protected abstract S newSpace(AddressSpace space);
|
||||
|
||||
@Override
|
||||
public S getForSpace(AddressSpace space, boolean toWrite) {
|
||||
return spaces.computeIfAbsent(space, s -> newSpace(s));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this when each S is possibly a cache to some other state (backing) object
|
||||
*
|
||||
* @param <B> the type of the object backing the cache for each address space
|
||||
* @param <S> the type of cache for each address space
|
||||
*/
|
||||
public abstract static class CacheingSpaceMap<B, S> extends AbstractSpaceMap<S> {
|
||||
/**
|
||||
* Get the object backing the cache for the given address space
|
||||
*
|
||||
* @param space the space
|
||||
* @return the backing object
|
||||
*/
|
||||
protected abstract B getBacking(AddressSpace space);
|
||||
|
||||
/**
|
||||
* Construct a new space internally associated with the given address space, having the
|
||||
* given backing
|
||||
*
|
||||
* <p>
|
||||
* As the name implies, this often simply wraps {@code S}'s constructor
|
||||
*
|
||||
* @param space the address space
|
||||
* @param backing the backing, if applicable. null for the unique space
|
||||
* @return the new space
|
||||
*/
|
||||
protected abstract S newSpace(AddressSpace space, B backing);
|
||||
|
||||
@Override
|
||||
public S getForSpace(AddressSpace space, boolean toWrite) {
|
||||
return spaces.computeIfAbsent(space,
|
||||
s -> newSpace(s, s.isUniqueSpace() ? null : getBacking(s)));
|
||||
}
|
||||
}
|
||||
|
||||
protected final Language language;
|
||||
protected final PcodeArithmetic<A> addressArithmetic;
|
||||
protected final PcodeArithmetic<T> arithmetic;
|
||||
protected final AddressSpace uniqueSpace;
|
||||
|
||||
/**
|
||||
* Construct a state piece for the given language and arithmetic
|
||||
*
|
||||
* @param language the langauge (used for its memory model)
|
||||
* @param language the language (used for its memory model)
|
||||
* @param arithmetic an arithmetic used to generate default values of {@code T}
|
||||
*/
|
||||
public AbstractLongOffsetPcodeExecutorStatePiece(Language language,
|
||||
PcodeArithmetic<T> arithmetic) {
|
||||
PcodeArithmetic<A> addressArithmetic, PcodeArithmetic<T> arithmetic) {
|
||||
this.language = language;
|
||||
this.addressArithmetic = addressArithmetic;
|
||||
this.arithmetic = arithmetic;
|
||||
uniqueSpace = language.getAddressFactory().getUniqueSpace();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PcodeArithmetic<A> getAddressArithmetic() {
|
||||
return addressArithmetic;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PcodeArithmetic<T> getArithmetic() {
|
||||
return arithmetic;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a value in the unique space
|
||||
*
|
||||
@@ -83,6 +175,7 @@ public abstract class AbstractLongOffsetPcodeExecutorStatePiece<A, T, S>
|
||||
* @param toWrite in case internal spaces are generated lazily, this indicates the space must be
|
||||
* present, because it is going to be written to.
|
||||
* @return the space, or {@code null}
|
||||
* @see AbstractSpaceMap
|
||||
*/
|
||||
protected abstract S getForSpace(AddressSpace space, boolean toWrite);
|
||||
|
||||
@@ -120,23 +213,14 @@ public abstract class AbstractLongOffsetPcodeExecutorStatePiece<A, T, S>
|
||||
return arithmetic.fromConst(0, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an offset of type {@code A} to {@code long}
|
||||
*
|
||||
* @param offset the offset as an {@code A}
|
||||
* @return the offset as a long
|
||||
*/
|
||||
protected abstract long offsetToLong(A offset);
|
||||
|
||||
@Override
|
||||
public void setVar(AddressSpace space, A offset, int size, boolean truncateAddressableUnit,
|
||||
T val) {
|
||||
setVar(space, offsetToLong(offset), size, truncateAddressableUnit, val);
|
||||
public void setVar(AddressSpace space, A offset, int size, boolean quantize, T val) {
|
||||
long lOffset = addressArithmetic.toLong(offset, Purpose.STORE);
|
||||
setVar(space, lOffset, size, quantize, val);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVar(AddressSpace space, long offset, int size, boolean truncateAddressableUnit,
|
||||
T val) {
|
||||
public void setVar(AddressSpace space, long offset, int size, boolean quantize, T val) {
|
||||
checkRange(space, offset, size);
|
||||
if (space.isConstantSpace()) {
|
||||
throw new IllegalArgumentException("Cannot write to constant space");
|
||||
@@ -146,17 +230,18 @@ public abstract class AbstractLongOffsetPcodeExecutorStatePiece<A, T, S>
|
||||
return;
|
||||
}
|
||||
S s = getForSpace(space, true);
|
||||
offset = truncateOffset(space, offset);
|
||||
offset = quantizeOffset(space, offset);
|
||||
setInSpace(s, offset, size, val);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getVar(AddressSpace space, A offset, int size, boolean truncateAddressableUnit) {
|
||||
return getVar(space, offsetToLong(offset), size, truncateAddressableUnit);
|
||||
public T getVar(AddressSpace space, A offset, int size, boolean quantize) {
|
||||
long lOffset = addressArithmetic.toLong(offset, Purpose.LOAD);
|
||||
return getVar(space, lOffset, size, quantize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getVar(AddressSpace space, long offset, int size, boolean truncateAddressableUnit) {
|
||||
public T getVar(AddressSpace space, long offset, int size, boolean quantize) {
|
||||
checkRange(space, offset, size);
|
||||
if (space.isConstantSpace()) {
|
||||
return arithmetic.fromConst(offset, size);
|
||||
@@ -168,7 +253,7 @@ public abstract class AbstractLongOffsetPcodeExecutorStatePiece<A, T, S>
|
||||
if (s == null) {
|
||||
return getFromNullSpace(size);
|
||||
}
|
||||
offset = truncateOffset(space, offset);
|
||||
offset = quantizeOffset(space, offset);
|
||||
return getFromSpace(s, offset, size);
|
||||
}
|
||||
}
|
||||
|
||||
-70
@@ -1,70 +0,0 @@
|
||||
/* ###
|
||||
* 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.exec;
|
||||
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
|
||||
/**
|
||||
* An executor state decorator which transforms the offset type
|
||||
*
|
||||
* @param <A> the offset type of the decorator
|
||||
* @param <B> the offset type of the delegate
|
||||
* @param <T> the type of values
|
||||
*/
|
||||
public abstract class AbstractOffsetTransformedPcodeExecutorState<A, B, T>
|
||||
implements PcodeExecutorStatePiece<A, T> {
|
||||
|
||||
private final PcodeExecutorStatePiece<B, T> state;
|
||||
|
||||
/**
|
||||
* Construct a decorator around the given delegate
|
||||
*
|
||||
* @param state the delegate
|
||||
*/
|
||||
public AbstractOffsetTransformedPcodeExecutorState(PcodeExecutorStatePiece<B, T> state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform an offset of type {@code A} to type {@code B}
|
||||
*
|
||||
* @param offset the offset as an {@code A}
|
||||
* @return the offset as a {@code B}
|
||||
*/
|
||||
protected abstract B transformOffset(A offset);
|
||||
|
||||
@Override
|
||||
public void setVar(AddressSpace space, A offset, int size, boolean truncateAddressableUnit,
|
||||
T val) {
|
||||
state.setVar(space, transformOffset(offset), size, truncateAddressableUnit, val);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVar(AddressSpace space, long offset, int size, boolean truncateAddressableUnit,
|
||||
T val) {
|
||||
state.setVar(space, offset, size, truncateAddressableUnit, val);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getVar(AddressSpace space, A offset, int size, boolean truncateAddressableUnit) {
|
||||
return state.getVar(space, transformOffset(offset), size, truncateAddressableUnit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getVar(AddressSpace space, long offset, int size, boolean truncateAddressableUnit) {
|
||||
return state.getVar(space, offset, size, truncateAddressableUnit);
|
||||
}
|
||||
}
|
||||
+32
-18
@@ -17,41 +17,55 @@ package ghidra.pcode.exec;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
import ghidra.pcode.opbehavior.BinaryOpBehavior;
|
||||
import ghidra.pcode.opbehavior.UnaryOpBehavior;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.lang.Endian;
|
||||
|
||||
/**
|
||||
* A rider arithmetic that reports the address of the control value
|
||||
* An auxiliary arithmetic that reports the address of the control value
|
||||
*
|
||||
* <p>
|
||||
* This is intended for use as the right side of a {@link PairedPcodeArithmetic}. Note that constant
|
||||
* and unique spaces are never returned. Furthermore, any computation performed on a value,
|
||||
* producing a temporary value, philosophically does not exist at any address in the state. Thus,
|
||||
* every operation in this arithmetic results in {@code null}. The accompanying state
|
||||
* {@link AddressOfPcodeExecutorState} does the real "address of" logic.
|
||||
* every operation in this arithmetic results in {@code null}. The accompanying state piece
|
||||
* {@link AddressOfPcodeExecutorStatePiece} does the real "address of" logic.
|
||||
*/
|
||||
public enum AddressOfPcodeArithmetic implements PcodeArithmetic<Address> {
|
||||
// NB: No temp value has a real address
|
||||
/**
|
||||
* The singleton instance.
|
||||
*/
|
||||
/** The singleton instance */
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public Address unaryOp(UnaryOpBehavior op, int sizeout, int sizein1, Address in1) {
|
||||
public Endian getEndian() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address binaryOp(BinaryOpBehavior op, int sizeout, int sizein1, Address in1, int sizein2,
|
||||
public Address unaryOp(int opcode, int sizeout, int sizein1, Address in1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address binaryOp(int opcode, int sizeout, int sizein1, Address in1, int sizein2,
|
||||
Address in2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address fromConst(long value, int size) {
|
||||
return null; // TODO: Do we care about Constant space?
|
||||
public Address modBeforeStore(int sizeout, int sizeinAddress, Address inAddress,
|
||||
int sizeinValue, Address inValue) {
|
||||
return inValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address modAfterLoad(int sizeout, int sizeinAddress, Address inAddress, int sizeinValue,
|
||||
Address inValue) {
|
||||
return inValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address fromConst(byte[] value) {
|
||||
return null; // TODO: Do we care about constant space?
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -60,17 +74,17 @@ public enum AddressOfPcodeArithmetic implements PcodeArithmetic<Address> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTrue(Address cond) {
|
||||
throw new AssertionError("Cannot decide branches using 'address of'");
|
||||
public Address fromConst(long value, int size) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigInteger toConcrete(Address value, boolean isContextreg) {
|
||||
throw new AssertionError("Should not attempt to concretize 'address of'");
|
||||
public byte[] toConcrete(Address value, Purpose purpose) {
|
||||
throw new ConcretionError("Cannot decide branches using 'address of'", purpose);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address sizeOf(Address value) {
|
||||
return fromConst(value.getAddressSpace().getSize() / 8, SIZEOF_SIZEOF);
|
||||
public long sizeOf(Address value) {
|
||||
return value.getAddressSpace().getSize() / 8;
|
||||
}
|
||||
}
|
||||
|
||||
+23
-20
@@ -18,13 +18,13 @@ package ghidra.pcode.exec;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import ghidra.pcode.utils.Utils;
|
||||
import ghidra.pcode.exec.PcodeArithmetic.Purpose;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.mem.MemBuffer;
|
||||
|
||||
/**
|
||||
* A rider state piece that reports the address of the control value
|
||||
* An auxiliary state piece that reports the address of the control value
|
||||
*
|
||||
* <p>
|
||||
* This is intended for use as the right side of a {@link PairedPcodeExecutorState} or
|
||||
@@ -33,47 +33,50 @@ import ghidra.program.model.mem.MemBuffer;
|
||||
* as the value, so that values transiting unique space can correctly have their source addresses
|
||||
* reported.
|
||||
*/
|
||||
public class AddressOfPcodeExecutorState
|
||||
public class AddressOfPcodeExecutorStatePiece
|
||||
implements PcodeExecutorStatePiece<byte[], Address> {
|
||||
private final boolean isBigEndian;
|
||||
private Map<Long, Address> unique = new HashMap<>();
|
||||
private final BytesPcodeArithmetic addressArithmetic;
|
||||
private final Map<Long, Address> unique = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Construct an "address of" state piece
|
||||
*
|
||||
* @param isBigEndian true if the control language is big endian
|
||||
*/
|
||||
public AddressOfPcodeExecutorState(boolean isBigEndian) {
|
||||
this.isBigEndian = isBigEndian;
|
||||
public AddressOfPcodeExecutorStatePiece(boolean isBigEndian) {
|
||||
this.addressArithmetic = BytesPcodeArithmetic.forEndian(isBigEndian);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] longToOffset(AddressSpace space, long l) {
|
||||
return Utils.longToBytes(l, space.getPointerSize(), isBigEndian);
|
||||
public PcodeArithmetic<byte[]> getAddressArithmetic() {
|
||||
return addressArithmetic;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVar(AddressSpace space, byte[] offset, int size,
|
||||
boolean truncateAddressableUnit, Address val) {
|
||||
public PcodeArithmetic<Address> getArithmetic() {
|
||||
return AddressOfPcodeArithmetic.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVar(AddressSpace space, byte[] offset, int size, boolean quantize, Address val) {
|
||||
if (!space.isUniqueSpace()) {
|
||||
return;
|
||||
}
|
||||
long off = Utils.bytesToLong(offset, offset.length, isBigEndian);
|
||||
unique.put(off, val);
|
||||
long lOffset = addressArithmetic.toLong(offset, Purpose.STORE);
|
||||
unique.put(lOffset, val);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address getVar(AddressSpace space, byte[] offset, int size,
|
||||
boolean truncateAddressableUnit) {
|
||||
long off = Utils.bytesToLong(offset, offset.length, isBigEndian);
|
||||
public Address getVar(AddressSpace space, byte[] offset, int size, boolean quantize) {
|
||||
long lOffset = addressArithmetic.toLong(offset, Purpose.LOAD);
|
||||
if (!space.isUniqueSpace()) {
|
||||
return space.getAddress(off);
|
||||
return space.getAddress(lOffset);
|
||||
}
|
||||
return unique.get(off);
|
||||
return unique.get(lOffset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemBuffer getConcreteBuffer(Address address) {
|
||||
throw new AssertionError("Cannot make 'address of' concrete buffers");
|
||||
public MemBuffer getConcreteBuffer(Address address, Purpose purpose) {
|
||||
throw new ConcretionError("Cannot make 'address of' concrete buffers", purpose);
|
||||
}
|
||||
}
|
||||
+94
-65
@@ -26,12 +26,17 @@ import java.util.stream.Stream;
|
||||
|
||||
import org.apache.commons.lang3.reflect.TypeUtils;
|
||||
|
||||
import ghidra.pcode.emu.linux.EmuLinuxAmd64SyscallUseropLibrary;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
import utilities.util.AnnotationUtilities;
|
||||
|
||||
/**
|
||||
* A userop library wherein Java methods are exported via a special annotation
|
||||
*
|
||||
* <p>
|
||||
* See {@code StandAloneEmuExampleScript} for an example of implementing a userop library. A more
|
||||
* complex example is {@link EmuLinuxAmd64SyscallUseropLibrary}.
|
||||
*
|
||||
* @param <T> the type of data processed by the library
|
||||
*/
|
||||
public abstract class AnnotatedPcodeUseropLibrary<T> implements PcodeUseropLibrary<T> {
|
||||
@@ -54,7 +59,7 @@ public abstract class AnnotatedPcodeUseropLibrary<T> implements PcodeUseropLibra
|
||||
opdef.posExecutor = pos;
|
||||
}
|
||||
},
|
||||
STATE(OpState.class, PcodeExecutorStatePiece.class) {
|
||||
STATE(OpState.class, PcodeExecutorState.class) {
|
||||
@Override
|
||||
int getPos(AnnotatedPcodeUseropDefinition<?> opdef) {
|
||||
return opdef.posState;
|
||||
@@ -88,8 +93,8 @@ public abstract class AnnotatedPcodeUseropLibrary<T> implements PcodeUseropLibra
|
||||
}
|
||||
};
|
||||
|
||||
static boolean processParameter(AnnotatedPcodeUseropDefinition<?> opdef, int i,
|
||||
Parameter p) {
|
||||
static boolean processParameter(AnnotatedPcodeUseropDefinition<?> opdef, Type declClsOpType,
|
||||
int i, Parameter p) {
|
||||
ParamAnnotProc only = null;
|
||||
for (ParamAnnotProc proc : ParamAnnotProc.values()) {
|
||||
if (proc.hasAnnot(p)) {
|
||||
@@ -105,7 +110,7 @@ public abstract class AnnotatedPcodeUseropLibrary<T> implements PcodeUseropLibra
|
||||
if (only == null) {
|
||||
return false;
|
||||
}
|
||||
only.processParameterPerAnnot(opdef, i, p);
|
||||
only.processParameterPerAnnot(opdef, declClsOpType, i, p);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -125,15 +130,43 @@ public abstract class AnnotatedPcodeUseropLibrary<T> implements PcodeUseropLibra
|
||||
return p.getAnnotation(annotCls) != null;
|
||||
}
|
||||
|
||||
void processParameterPerAnnot(AnnotatedPcodeUseropDefinition<?> opdef, int i,
|
||||
Parameter p) {
|
||||
Type getArgumentType(Type opType) {
|
||||
TypeVariable<?>[] typeParams = paramCls.getTypeParameters();
|
||||
if (typeParams.length == 0) {
|
||||
return paramCls;
|
||||
}
|
||||
if (typeParams.length == 1) {
|
||||
return TypeUtils.parameterize(paramCls, opType);
|
||||
}
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
void processParameterPerAnnot(AnnotatedPcodeUseropDefinition<?> opdef, Type declClsOpType,
|
||||
int i, Parameter p) {
|
||||
if (getPos(opdef) != -1) {
|
||||
throw new IllegalArgumentException(
|
||||
"Can only have one parameter with @" + annotCls.getSimpleName());
|
||||
}
|
||||
if (!p.getType().isAssignableFrom(paramCls)) {
|
||||
Type pType = p.getParameterizedType();
|
||||
Map<TypeVariable<?>, Type> typeArgs = TypeUtils.getTypeArguments(pType, paramCls);
|
||||
if (typeArgs == null) {
|
||||
throw new IllegalArgumentException("Parameter " + p.getName() + " with @" +
|
||||
annotCls.getSimpleName() + " must acccept " + paramCls.getSimpleName());
|
||||
annotCls.getSimpleName() + " must acccept " + getArgumentType(declClsOpType));
|
||||
}
|
||||
if (typeArgs.isEmpty()) {
|
||||
// Nothing
|
||||
}
|
||||
else if (typeArgs.size() == 1) {
|
||||
Type declMthOpType = typeArgs.get(paramCls.getTypeParameters()[0]);
|
||||
if (!Objects.equals(declClsOpType, declMthOpType)) {
|
||||
throw new IllegalArgumentException("Parameter " + p.getName() + " with @" +
|
||||
annotCls.getSimpleName() + " must acccept " +
|
||||
getArgumentType(declClsOpType));
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new AssertionError("Internal: paramCls for @" + annotCls.getSimpleName() +
|
||||
"should only have one type parameter <T>");
|
||||
}
|
||||
setPos(opdef, i);
|
||||
}
|
||||
@@ -148,8 +181,7 @@ public abstract class AnnotatedPcodeUseropLibrary<T> implements PcodeUseropLibra
|
||||
implements PcodeUseropDefinition<T> {
|
||||
|
||||
protected static <T> AnnotatedPcodeUseropDefinition<T> create(PcodeUserop annot,
|
||||
AnnotatedPcodeUseropLibrary<T> library, Class<T> opType, Lookup lookup,
|
||||
Method method) {
|
||||
AnnotatedPcodeUseropLibrary<T> library, Type opType, Lookup lookup, Method method) {
|
||||
if (annot.variadic()) {
|
||||
return new VariadicAnnotatedPcodeUseropDefinition<>(library, opType, lookup,
|
||||
method);
|
||||
@@ -168,8 +200,8 @@ public abstract class AnnotatedPcodeUseropLibrary<T> implements PcodeUseropLibra
|
||||
private int posLib = -1;
|
||||
private int posOut = -1;
|
||||
|
||||
public AnnotatedPcodeUseropDefinition(AnnotatedPcodeUseropLibrary<T> library,
|
||||
Class<T> opType, Lookup lookup, Method method) {
|
||||
public AnnotatedPcodeUseropDefinition(AnnotatedPcodeUseropLibrary<T> library, Type opType,
|
||||
Lookup lookup, Method method) {
|
||||
initStarting();
|
||||
this.method = method;
|
||||
try {
|
||||
@@ -181,22 +213,21 @@ public abstract class AnnotatedPcodeUseropLibrary<T> implements PcodeUseropLibra
|
||||
PcodeUserop.class.getSimpleName() +
|
||||
" annotation. Override getMethodLookup()");
|
||||
}
|
||||
|
||||
Class<?> rType = method.getReturnType();
|
||||
if (rType != void.class && !opType.isAssignableFrom(rType)) {
|
||||
Type declClsOpType = PcodeUseropLibrary.getOperandType(method.getDeclaringClass());
|
||||
Type rType = method.getGenericReturnType();
|
||||
if (rType != void.class && !TypeUtils.isAssignable(rType, declClsOpType)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Method " + method.getName() + " with @" +
|
||||
PcodeUserop.class.getSimpleName() +
|
||||
" annotation must return void or a type assignable to " +
|
||||
opType.getSimpleName());
|
||||
" annotation must return void or a type assignable to " + declClsOpType);
|
||||
}
|
||||
|
||||
Parameter[] params = method.getParameters();
|
||||
for (int i = 0; i < params.length; i++) {
|
||||
Parameter p = params[i];
|
||||
boolean processed = ParamAnnotProc.processParameter(this, i, p);
|
||||
boolean processed = ParamAnnotProc.processParameter(this, declClsOpType, i, p);
|
||||
if (!processed) {
|
||||
processNonAnnotatedParameter(opType, i, p);
|
||||
processNonAnnotatedParameter(declClsOpType, opType, i, p);
|
||||
}
|
||||
}
|
||||
initFinished();
|
||||
@@ -248,7 +279,7 @@ public abstract class AnnotatedPcodeUseropLibrary<T> implements PcodeUseropLibra
|
||||
// Optional override
|
||||
}
|
||||
|
||||
protected abstract void processNonAnnotatedParameter(Class<T> opType, int i,
|
||||
protected abstract void processNonAnnotatedParameter(Type declClsOpType, Type opType, int i,
|
||||
Parameter p);
|
||||
|
||||
protected void initFinished() {
|
||||
@@ -275,7 +306,7 @@ public abstract class AnnotatedPcodeUseropLibrary<T> implements PcodeUseropLibra
|
||||
private Set<Integer> posTs;
|
||||
|
||||
public FixedArgsAnnotatedPcodeUseropDefinition(AnnotatedPcodeUseropLibrary<T> library,
|
||||
Class<T> opType, Lookup lookup, Method method) {
|
||||
Type opType, Lookup lookup, Method method) {
|
||||
super(library, opType, lookup, method);
|
||||
}
|
||||
|
||||
@@ -286,17 +317,19 @@ public abstract class AnnotatedPcodeUseropLibrary<T> implements PcodeUseropLibra
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processNonAnnotatedParameter(Class<T> opType, int i, Parameter p) {
|
||||
if (p.getType().equals(Varnode.class)) {
|
||||
protected void processNonAnnotatedParameter(Type declClsOpType, Type opType, int i,
|
||||
Parameter p) {
|
||||
Type pType = p.getParameterizedType();
|
||||
if (TypeUtils.isAssignable(Varnode.class, pType)) {
|
||||
// Just use the Varnode by default
|
||||
}
|
||||
else if (p.getType().isAssignableFrom(opType)) {
|
||||
else if (TypeUtils.isAssignable(declClsOpType, pType)) {
|
||||
posTs.add(i);
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Input parameter " + p.getName() +
|
||||
" of userop " + method.getName() + " must be " +
|
||||
Varnode.class.getSimpleName() + " or accept " + opType.getSimpleName());
|
||||
Varnode.class.getSimpleName() + " or accept " + declClsOpType);
|
||||
}
|
||||
posIns.add(i);
|
||||
}
|
||||
@@ -342,34 +375,42 @@ public abstract class AnnotatedPcodeUseropLibrary<T> implements PcodeUseropLibra
|
||||
extends AnnotatedPcodeUseropDefinition<T> {
|
||||
|
||||
private int posIns;
|
||||
private Class<T> opType;
|
||||
private Class<?> opRawType;
|
||||
|
||||
public VariadicAnnotatedPcodeUseropDefinition(AnnotatedPcodeUseropLibrary<T> library,
|
||||
Class<T> opType, Lookup lookup, Method method) {
|
||||
Type opType, Lookup lookup, Method method) {
|
||||
super(library, opType, lookup, method);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initStarting() {
|
||||
posIns = -1;
|
||||
opType = null;
|
||||
opRawType = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processNonAnnotatedParameter(Class<T> opType, int i, Parameter p) {
|
||||
protected void processNonAnnotatedParameter(Type declClsOpType, Type opType, int i,
|
||||
Parameter p) {
|
||||
if (posIns != -1) {
|
||||
throw new IllegalArgumentException(
|
||||
"Only one non-annotated parameter is allowed to receive the inputs");
|
||||
}
|
||||
if (p.getType().equals(Varnode[].class)) {
|
||||
Type pType = p.getParameterizedType();
|
||||
Type eType = TypeUtils.getArrayComponentType(pType);
|
||||
if (eType == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Variadic userop must receive inputs as " + declClsOpType + "[] or " +
|
||||
Varnode.class.getSimpleName() + "[]");
|
||||
}
|
||||
if (pType.equals(Varnode[].class)) {
|
||||
// Just pass inVars as is
|
||||
}
|
||||
else if (p.getType().isAssignableFrom(Array.newInstance(opType, 0).getClass())) {
|
||||
this.opType = opType;
|
||||
else if (TypeUtils.isAssignable(declClsOpType, eType)) {
|
||||
this.opRawType = TypeUtils.getRawType(opType, getClass());
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException(
|
||||
"Variadic userop must receive inputs as T[] or " +
|
||||
"Variadic userop must receive inputs as " + declClsOpType + "[] or " +
|
||||
Varnode.class.getSimpleName() + "[]");
|
||||
}
|
||||
posIns = i;
|
||||
@@ -383,15 +424,19 @@ public abstract class AnnotatedPcodeUseropLibrary<T> implements PcodeUseropLibra
|
||||
}
|
||||
}
|
||||
|
||||
protected Object[] readVars(PcodeExecutorState<T> state, List<Varnode> vars) {
|
||||
Object[] vals = (Object[]) Array.newInstance(opRawType, vars.size());
|
||||
for (int i = 0; i < vals.length; i++) {
|
||||
vals[i] = state.getVar(vars.get(i));
|
||||
}
|
||||
return vals;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void placeInputs(PcodeExecutor<T> executor, List<Object> args,
|
||||
List<Varnode> inVars) {
|
||||
PcodeExecutorStatePiece<T, T> state = executor.getState();
|
||||
if (opType != null) {
|
||||
Stream<T> ts = inVars.stream().map(state::getVar);
|
||||
@SuppressWarnings("unchecked")
|
||||
Object valsArr = ts.toArray(l -> (T[]) Array.newInstance(opType, l));
|
||||
args.set(posIns, valsArr);
|
||||
if (opRawType != null) {
|
||||
args.set(posIns, readVars(executor.getState(), inVars));
|
||||
}
|
||||
else {
|
||||
args.set(posIns, inVars.toArray(Varnode[]::new));
|
||||
@@ -447,10 +492,8 @@ public abstract class AnnotatedPcodeUseropLibrary<T> implements PcodeUseropLibra
|
||||
* An annotation to receive the executor itself into a parameter
|
||||
*
|
||||
* <p>
|
||||
* The annotated parameter must have a type assignable from {@link PcodeExecutor} with parameter
|
||||
* {@code <T>} matching that of the actual executor. TODO: No "bind-time" check of the type
|
||||
* parameter is performed. An incorrect parameter will likely cause a {@link ClassCastException}
|
||||
* despite the lack of any compiler warnings.
|
||||
* The annotated parameter must have type {@link PcodeExecutor} with the same {@code <T>} as the
|
||||
* class declaring the method.
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.PARAMETER)
|
||||
@@ -461,10 +504,8 @@ public abstract class AnnotatedPcodeUseropLibrary<T> implements PcodeUseropLibra
|
||||
* An annotation to receive the executor's state into a parameter
|
||||
*
|
||||
* <p>
|
||||
* The annotated parameter must have a type assignable from {@link PcodeExecutorStatePiece} with
|
||||
* parameters {@code <T,T>} matching that of the executor. TODO: No "bind-time" check of the
|
||||
* type parameters is performed. An incorrect parameter will likely cause a
|
||||
* {@link ClassCastException} despite the lack of any compiler warnings.
|
||||
* The annotated parameter must have type {@link PcodeExecutorState} with the same {@code <T>}
|
||||
* as the class declaring the method.
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.PARAMETER)
|
||||
@@ -482,10 +523,8 @@ public abstract class AnnotatedPcodeUseropLibrary<T> implements PcodeUseropLibra
|
||||
* definition to receive the complete library.
|
||||
*
|
||||
* <p>
|
||||
* The annotated parameter must have a type assignable from {@link PcodeUseropLibrary} with
|
||||
* parameter {@code <T>} matching that of the executor. TODO: No "bind-time" check of the type
|
||||
* parameters is performed. An incorrect parameter will likely cause a
|
||||
* {@link ClassCastException} despite the lack of any compiler warnings.
|
||||
* The annotated parameter must have type {@link PcodeUseropLibrary} with the same {@code <T>}
|
||||
* as the class declaring the method.
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.PARAMETER)
|
||||
@@ -496,7 +535,7 @@ public abstract class AnnotatedPcodeUseropLibrary<T> implements PcodeUseropLibra
|
||||
* An annotation to receive the output varnode into a parameter
|
||||
*
|
||||
* <p>
|
||||
* The annotated parameter must have a type assignable from {@link Varnode}.
|
||||
* The annotated parameter must have type {@link Varnode}.
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.PARAMETER)
|
||||
@@ -512,7 +551,7 @@ public abstract class AnnotatedPcodeUseropLibrary<T> implements PcodeUseropLibra
|
||||
*/
|
||||
public AnnotatedPcodeUseropLibrary() {
|
||||
Lookup lookup = getMethodLookup();
|
||||
Class<T> opType = getOperandType();
|
||||
Type opType = getOperandType();
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
Class<? extends AnnotatedPcodeUseropLibrary<T>> cls = (Class) this.getClass();
|
||||
Set<Method> methods = CACHE_BY_CLASS.computeIfAbsent(cls, __ -> collectDefinitions(cls));
|
||||
@@ -527,18 +566,8 @@ public abstract class AnnotatedPcodeUseropLibrary<T> implements PcodeUseropLibra
|
||||
*
|
||||
* @return the type of data processed by the userop
|
||||
*/
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
protected Class<T> getOperandType() {
|
||||
Map<TypeVariable<?>, Type> args =
|
||||
TypeUtils.getTypeArguments(getClass(), AnnotatedPcodeUseropLibrary.class);
|
||||
if (args == null) {
|
||||
return (Class) Object.class;
|
||||
}
|
||||
Type type = args.get(AnnotatedPcodeUseropLibrary.class.getTypeParameters()[0]);
|
||||
if (!(type instanceof Class<?>)) {
|
||||
return (Class) Object.class;
|
||||
}
|
||||
return (Class) type;
|
||||
protected Type getOperandType() {
|
||||
return PcodeUseropLibrary.getOperandType(getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
-69
@@ -1,69 +0,0 @@
|
||||
/* ###
|
||||
* 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.exec;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
import ghidra.pcode.opbehavior.BinaryOpBehavior;
|
||||
import ghidra.pcode.opbehavior.UnaryOpBehavior;
|
||||
|
||||
/**
|
||||
* A p-code arithmetic that operates on {@link BigInteger} values
|
||||
*
|
||||
* <p>
|
||||
* Note: it appears this class is no longer used anywhere, which means it's probably not tested.
|
||||
*/
|
||||
@Deprecated(forRemoval = true) // TODO: Not getting used
|
||||
public enum BigIntegerPcodeArithmetic implements PcodeArithmetic<BigInteger> {
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public BigInteger unaryOp(UnaryOpBehavior op, int sizeout, int sizein1, BigInteger in1) {
|
||||
return op.evaluateUnary(sizeout, sizein1, in1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigInteger binaryOp(BinaryOpBehavior op, int sizeout, int sizein1, BigInteger in1,
|
||||
int sizein2, BigInteger in2) {
|
||||
return op.evaluateBinary(sizeout, sizein1, in1, in2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigInteger fromConst(long value, int size) {
|
||||
return BigInteger.valueOf(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigInteger fromConst(BigInteger value, int size, boolean isContextreg) {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTrue(BigInteger cond) {
|
||||
return !cond.equals(BigInteger.ZERO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigInteger toConcrete(BigInteger value, boolean isContextreg) {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigInteger sizeOf(BigInteger value) {
|
||||
// NOTE: Determining the minimum necessary size to contain it is not correct.
|
||||
throw new AssertionError("Size is not known");
|
||||
}
|
||||
}
|
||||
+42
-40
@@ -17,27 +17,27 @@ package ghidra.pcode.exec;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
import ghidra.pcode.opbehavior.BinaryOpBehavior;
|
||||
import ghidra.pcode.opbehavior.UnaryOpBehavior;
|
||||
import ghidra.pcode.opbehavior.*;
|
||||
import ghidra.pcode.utils.Utils;
|
||||
import ghidra.program.model.lang.Endian;
|
||||
import ghidra.program.model.lang.Language;
|
||||
|
||||
/**
|
||||
* A p-code arithmetic that operates on byte array values
|
||||
* A p-code arithmetic that operates on concrete byte array values
|
||||
*
|
||||
* <p>
|
||||
* The arithmetic interprets the arrays as big- or little-endian values, then performs the
|
||||
* arithmetic as specified by the p-code operation.
|
||||
* arithmetic as specified by the p-code operation. The implementation defers to {@link OpBehavior}.
|
||||
*/
|
||||
public enum BytesPcodeArithmetic implements PcodeArithmetic<byte[]> {
|
||||
/**
|
||||
* The instance which interprets arrays as big-endian values
|
||||
*/
|
||||
BIG_ENDIAN(true),
|
||||
BIG_ENDIAN(Endian.BIG),
|
||||
/**
|
||||
* The instance which interprets arrays as little-endian values
|
||||
*/
|
||||
LITTLE_ENDIAN(false);
|
||||
LITTLE_ENDIAN(Endian.LITTLE);
|
||||
|
||||
/**
|
||||
* Obtain the instance for the given endianness
|
||||
@@ -59,70 +59,72 @@ public enum BytesPcodeArithmetic implements PcodeArithmetic<byte[]> {
|
||||
return forEndian(language.isBigEndian());
|
||||
}
|
||||
|
||||
private final boolean isBigEndian;
|
||||
private final Endian endian;
|
||||
|
||||
private BytesPcodeArithmetic(boolean isBigEndian) {
|
||||
this.isBigEndian = isBigEndian;
|
||||
private BytesPcodeArithmetic(Endian endian) {
|
||||
this.endian = endian;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] unaryOp(UnaryOpBehavior op, int sizeout, int sizein1, byte[] in1) {
|
||||
public Endian getEndian() {
|
||||
return endian;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] unaryOp(int opcode, int sizeout, int sizein1, byte[] in1) {
|
||||
UnaryOpBehavior b = (UnaryOpBehavior) OpBehaviorFactory.getOpBehavior(opcode);
|
||||
boolean isBigEndian = endian.isBigEndian();
|
||||
if (sizein1 > 8 || sizeout > 8) {
|
||||
BigInteger in1Val = Utils.bytesToBigInteger(in1, in1.length, isBigEndian, false);
|
||||
BigInteger outVal = op.evaluateUnary(sizeout, sizein1, in1Val);
|
||||
BigInteger in1Val = Utils.bytesToBigInteger(in1, sizein1, isBigEndian, false);
|
||||
BigInteger outVal = b.evaluateUnary(sizeout, sizein1, in1Val);
|
||||
return Utils.bigIntegerToBytes(outVal, sizeout, isBigEndian);
|
||||
}
|
||||
else {
|
||||
long in1Val = Utils.bytesToLong(in1, sizein1, isBigEndian);
|
||||
long outVal = op.evaluateUnary(sizeout, sizein1, in1Val);
|
||||
return Utils.longToBytes(outVal, sizeout, isBigEndian);
|
||||
}
|
||||
long in1Val = Utils.bytesToLong(in1, sizein1, isBigEndian);
|
||||
long outVal = b.evaluateUnary(sizeout, sizein1, in1Val);
|
||||
return Utils.longToBytes(outVal, sizeout, isBigEndian);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] binaryOp(BinaryOpBehavior op, int sizeout, int sizein1, byte[] in1, int sizein2,
|
||||
public byte[] binaryOp(int opcode, int sizeout, int sizein1, byte[] in1, int sizein2,
|
||||
byte[] in2) {
|
||||
BinaryOpBehavior b = (BinaryOpBehavior) OpBehaviorFactory.getOpBehavior(opcode);
|
||||
boolean isBigEndian = endian.isBigEndian();
|
||||
if (sizein1 > 8 || sizein2 > 8 || sizeout > 8) {
|
||||
BigInteger in1Val = Utils.bytesToBigInteger(in1, sizein1, isBigEndian, false);
|
||||
BigInteger in2Val = Utils.bytesToBigInteger(in2, sizein2, isBigEndian, false);
|
||||
BigInteger outVal = op.evaluateBinary(sizeout, sizein1, in1Val, in2Val);
|
||||
BigInteger outVal = b.evaluateBinary(sizeout, sizein1, in1Val, in2Val);
|
||||
return Utils.bigIntegerToBytes(outVal, sizeout, isBigEndian);
|
||||
}
|
||||
else {
|
||||
long in1Val = Utils.bytesToLong(in1, sizein1, isBigEndian);
|
||||
long in2Val = Utils.bytesToLong(in2, sizein2, isBigEndian);
|
||||
long outVal = op.evaluateBinary(sizeout, sizein1, in1Val, in2Val);
|
||||
return Utils.longToBytes(outVal, sizeout, isBigEndian);
|
||||
}
|
||||
long in1Val = Utils.bytesToLong(in1, sizein1, isBigEndian);
|
||||
long in2Val = Utils.bytesToLong(in2, sizein2, isBigEndian);
|
||||
long outVal = b.evaluateBinary(sizeout, sizein1, in1Val, in2Val);
|
||||
return Utils.longToBytes(outVal, sizeout, isBigEndian);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] fromConst(long value, int size) {
|
||||
return Utils.longToBytes(value, size, isBigEndian);
|
||||
public byte[] modBeforeStore(int sizeout, int sizeinAddress, byte[] inAddress, int sizeinValue,
|
||||
byte[] inValue) {
|
||||
return inValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] fromConst(BigInteger value, int size, boolean isContextreg) {
|
||||
return Utils.bigIntegerToBytes(value, size, isBigEndian || isContextreg);
|
||||
public byte[] modAfterLoad(int sizeout, int sizeinAddress, byte[] inAddress, int sizeinValue,
|
||||
byte[] inValue) {
|
||||
return inValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTrue(byte[] cond) {
|
||||
for (byte b : cond) {
|
||||
if (b != 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
public byte[] fromConst(byte[] value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigInteger toConcrete(byte[] value, boolean isContextreg) {
|
||||
return Utils.bytesToBigInteger(value, value.length, isBigEndian || isContextreg, false);
|
||||
public byte[] toConcrete(byte[] value, Purpose purpose) {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] sizeOf(byte[] value) {
|
||||
return fromConst(value.length, SIZEOF_SIZEOF);
|
||||
public long sizeOf(byte[] value) {
|
||||
return value.length;
|
||||
}
|
||||
}
|
||||
|
||||
+6
-13
@@ -15,26 +15,19 @@
|
||||
*/
|
||||
package ghidra.pcode.exec;
|
||||
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.lang.Language;
|
||||
|
||||
/**
|
||||
* A plain concrete state suitable for simple emulation, without any backing objects
|
||||
* A state composing a single {@link BytesPcodeExecutorStatePiece}
|
||||
*/
|
||||
public class BytesPcodeExecutorState
|
||||
extends AbstractBytesPcodeExecutorState<Void, BytesPcodeExecutorStateSpace<Void>> {
|
||||
|
||||
public class BytesPcodeExecutorState extends DefaultPcodeExecutorState<byte[]> {
|
||||
/**
|
||||
* Construct a state for the given language
|
||||
* Create the state
|
||||
*
|
||||
* @param langauge the language (used for its memory model)
|
||||
* @param language the language (processor model)
|
||||
*/
|
||||
public BytesPcodeExecutorState(Language language) {
|
||||
super(language);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BytesPcodeExecutorStateSpace<Void> newSpace(AddressSpace space, Void backing) {
|
||||
return new BytesPcodeExecutorStateSpace<>(language, space, backing);
|
||||
super(new BytesPcodeExecutorStatePiece(language),
|
||||
BytesPcodeArithmetic.forLanguage(language));
|
||||
}
|
||||
}
|
||||
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/* ###
|
||||
* 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.exec;
|
||||
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.lang.Language;
|
||||
|
||||
/**
|
||||
* A plain concrete state piece without any backing objects
|
||||
*/
|
||||
public class BytesPcodeExecutorStatePiece
|
||||
extends AbstractBytesPcodeExecutorStatePiece<BytesPcodeExecutorStateSpace<Void>> {
|
||||
|
||||
/**
|
||||
* Construct a state for the given language
|
||||
*
|
||||
* @param langauge the language (used for its memory model)
|
||||
*/
|
||||
public BytesPcodeExecutorStatePiece(Language language) {
|
||||
super(language);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractSpaceMap<BytesPcodeExecutorStateSpace<Void>> newSpaceMap() {
|
||||
return new SimpleSpaceMap<>() {
|
||||
@Override
|
||||
protected BytesPcodeExecutorStateSpace<Void> newSpace(AddressSpace space) {
|
||||
return new BytesPcodeExecutorStateSpace<>(language, space, null);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -27,7 +27,7 @@ import ghidra.program.model.lang.Register;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
/**
|
||||
* A p-code executor state space for storing bytes, retrieved and set as arrays.
|
||||
* A p-code executor state space for storing and retrieving bytes as arrays
|
||||
*
|
||||
* @param <B> if this space is a cache, the type of object backing this space
|
||||
*/
|
||||
|
||||
+22
-10
@@ -15,19 +15,31 @@
|
||||
*/
|
||||
package ghidra.pcode.exec;
|
||||
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.pcode.exec.PcodeArithmetic.Purpose;
|
||||
|
||||
/**
|
||||
* A device in the type hierarchy that turns a suitable state piece into a state
|
||||
*
|
||||
* @param <T> the type of values and addresses in the state
|
||||
* @param <S> the type of an execute state space, internally associated with an address space
|
||||
* The emulator or a client attempted to concretize an abstract value
|
||||
*/
|
||||
public abstract class AbstractLongOffsetPcodeExecutorState<T, S>
|
||||
extends AbstractLongOffsetPcodeExecutorStatePiece<T, T, S>
|
||||
implements PcodeExecutorState<T> {
|
||||
public class ConcretionError extends PcodeExecutionException {
|
||||
private final Purpose purpose;
|
||||
|
||||
public AbstractLongOffsetPcodeExecutorState(Language language, PcodeArithmetic<T> arithmetic) {
|
||||
super(language, arithmetic);
|
||||
/**
|
||||
* Create the exception
|
||||
*
|
||||
* @param message a message for the client
|
||||
* @param purpose the reason why the emulator needs a concrete value
|
||||
*/
|
||||
public ConcretionError(String message, Purpose purpose) {
|
||||
super(message);
|
||||
this.purpose = purpose;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the reason why the emulator needs a concrete value
|
||||
*
|
||||
* @return the purpose
|
||||
*/
|
||||
public Purpose getPurpose() {
|
||||
return purpose;
|
||||
}
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
/* ###
|
||||
* 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.exec;
|
||||
|
||||
import ghidra.pcode.exec.PcodeArithmetic.Purpose;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.mem.MemBuffer;
|
||||
|
||||
/**
|
||||
* A p-code executor state formed from a piece whose address and value types are the same
|
||||
*
|
||||
* <p>
|
||||
* This class will also wire in the arithmetic's
|
||||
* {@link PcodeArithmetic#modBeforeStore(int, int, Object, int, Object)} and
|
||||
* {@link PcodeArithmetic#modAfterLoad(int, int, Object, int, Object)}, which is only possible when
|
||||
* the address and value type are guaranteed to match.
|
||||
*
|
||||
* @param <T> the type of values and addresses in the state
|
||||
*/
|
||||
public class DefaultPcodeExecutorState<T> implements PcodeExecutorState<T> {
|
||||
protected final PcodeExecutorStatePiece<T, T> piece;
|
||||
protected final PcodeArithmetic<T> arithmetic;
|
||||
|
||||
public DefaultPcodeExecutorState(PcodeExecutorStatePiece<T, T> piece,
|
||||
PcodeArithmetic<T> arithmetic) {
|
||||
this.piece = piece;
|
||||
this.arithmetic = arithmetic;
|
||||
}
|
||||
|
||||
public DefaultPcodeExecutorState(PcodeExecutorStatePiece<T, T> piece) {
|
||||
this(piece, piece.getArithmetic());
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getVar(AddressSpace space, T offset, int size, boolean quantize) {
|
||||
return piece.getVar(space, offset, size, quantize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVar(AddressSpace space, T offset, int size, boolean quantize, T val) {
|
||||
piece.setVar(space, offset, size, quantize, val);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PcodeArithmetic<T> getArithmetic() {
|
||||
return arithmetic;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemBuffer getConcreteBuffer(Address address, Purpose purpose) {
|
||||
return piece.getConcreteBuffer(address, purpose);
|
||||
}
|
||||
}
|
||||
+98
-42
@@ -15,29 +15,34 @@
|
||||
*/
|
||||
package ghidra.pcode.exec;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.apache.commons.lang3.tuple.ImmutablePair;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import ghidra.pcode.opbehavior.BinaryOpBehavior;
|
||||
import ghidra.pcode.opbehavior.UnaryOpBehavior;
|
||||
import ghidra.program.model.lang.Endian;
|
||||
import ghidra.program.model.pcode.PcodeOp;
|
||||
|
||||
/**
|
||||
* An arithmetic composed from two.
|
||||
*
|
||||
* <p>
|
||||
* The new arithmetic operates on tuples where each is subject to its respective arithmetic. One
|
||||
* exception is {@link #isTrue(Entry)}, which is typically used to control branches. This arithmetic
|
||||
* defers to left ("control") arithmetic.
|
||||
* exception is {@link #toConcrete(Pair, Purpose)}. This arithmetic defers to left ("control")
|
||||
* arithmetic. Thus, conventionally, when part of the pair represents the concrete value, it should
|
||||
* be the left.
|
||||
*
|
||||
* <p>
|
||||
* See {@link PairedPcodeExecutorStatePiece} regarding composing three or more elements. Generally,
|
||||
* it's recommended the client provide its own "record" type and the corresponding arithmetic and
|
||||
* state piece to manipulate it. Nesting pairs would work, but is not recommended.
|
||||
*
|
||||
* @param <L> the type of the left ("control") element
|
||||
* @param <R> the type of the right ("rider") element
|
||||
* @param <R> the type of the right ("auxiliary") element
|
||||
*/
|
||||
public class PairedPcodeArithmetic<L, R> implements PcodeArithmetic<Pair<L, R>> {
|
||||
private final PcodeArithmetic<L> leftArith;
|
||||
private final PcodeArithmetic<R> rightArith;
|
||||
private final Endian endian;
|
||||
|
||||
/**
|
||||
* Construct a composed arithmetic from the given two
|
||||
@@ -46,52 +51,103 @@ public class PairedPcodeArithmetic<L, R> implements PcodeArithmetic<Pair<L, R>>
|
||||
* @param rightArith the right ("rider") arithmetic
|
||||
*/
|
||||
public PairedPcodeArithmetic(PcodeArithmetic<L> leftArith, PcodeArithmetic<R> rightArith) {
|
||||
Endian lend = leftArith.getEndian();
|
||||
Endian rend = rightArith.getEndian();
|
||||
if (lend != null && rend != null && lend != rend) {
|
||||
throw new IllegalArgumentException("Arithmetics must agree in endianness");
|
||||
}
|
||||
this.endian = lend != null ? lend : rend;
|
||||
this.leftArith = leftArith;
|
||||
this.rightArith = rightArith;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<L, R> unaryOp(UnaryOpBehavior op, int sizeout, int sizein1, Pair<L, R> in1) {
|
||||
return new ImmutablePair<>(
|
||||
leftArith.unaryOp(op, sizeout, sizein1, in1.getLeft()),
|
||||
rightArith.unaryOp(op, sizeout, sizein1, in1.getRight()));
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (this.getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
PairedPcodeArithmetic<?, ?> that = (PairedPcodeArithmetic<?, ?>) obj;
|
||||
if (!Objects.equals(this.leftArith, that.leftArith)) {
|
||||
return false;
|
||||
}
|
||||
if (!Objects.equals(this.rightArith, that.rightArith)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<L, R> binaryOp(BinaryOpBehavior op, int sizeout, int sizein1, Pair<L, R> in1,
|
||||
int sizein2, Pair<L, R> in2) {
|
||||
return new ImmutablePair<>(
|
||||
leftArith.binaryOp(op, sizeout, sizein1, in1.getLeft(), sizein2, in2.getLeft()),
|
||||
rightArith.binaryOp(op, sizeout, sizein1, in1.getRight(), sizein2, in2.getRight()));
|
||||
public Endian getEndian() {
|
||||
return endian;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<L, R> fromConst(long value, int size) {
|
||||
return new ImmutablePair<>(leftArith.fromConst(value, size),
|
||||
rightArith.fromConst(value, size));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<L, R> fromConst(BigInteger value, int size, boolean isContextreg) {
|
||||
return new ImmutablePair<>(leftArith.fromConst(value, size, isContextreg),
|
||||
rightArith.fromConst(value, size, isContextreg));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTrue(Pair<L, R> cond) {
|
||||
return leftArith.isTrue(cond.getLeft());
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigInteger toConcrete(Pair<L, R> value, boolean isContextreg) {
|
||||
return leftArith.toConcrete(value.getLeft(), isContextreg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<L, R> sizeOf(Pair<L, R> value) {
|
||||
public Pair<L, R> unaryOp(int opcode, int sizeout, int sizein1,
|
||||
Pair<L, R> in1) {
|
||||
return Pair.of(
|
||||
leftArith.sizeOf(value.getLeft()),
|
||||
rightArith.sizeOf(value.getRight()));
|
||||
leftArith.unaryOp(opcode, sizeout, sizein1, in1.getLeft()),
|
||||
rightArith.unaryOp(opcode, sizeout, sizein1, in1.getRight()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<L, R> unaryOp(PcodeOp op, Pair<L, R> in1) {
|
||||
return Pair.of(
|
||||
leftArith.unaryOp(op, in1.getLeft()),
|
||||
rightArith.unaryOp(op, in1.getRight()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<L, R> binaryOp(int opcode, int sizeout, int sizein1,
|
||||
Pair<L, R> in1, int sizein2, Pair<L, R> in2) {
|
||||
return Pair.of(
|
||||
leftArith.binaryOp(opcode, sizeout, sizein1, in1.getLeft(), sizein2, in2.getLeft()),
|
||||
rightArith.binaryOp(opcode, sizeout, sizein1, in1.getRight(), sizein2, in2.getRight()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<L, R> binaryOp(PcodeOp op, Pair<L, R> in1, Pair<L, R> in2) {
|
||||
return Pair.of(
|
||||
leftArith.binaryOp(op, in1.getLeft(), in2.getLeft()),
|
||||
rightArith.binaryOp(op, in1.getRight(), in2.getRight()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<L, R> modBeforeStore(int sizeout, int sizeinAddress, Pair<L, R> inAddress,
|
||||
int sizeinValue, Pair<L, R> inValue) {
|
||||
return Pair.of(
|
||||
leftArith.modBeforeStore(sizeout, sizeinAddress, inAddress.getLeft(), sizeinValue,
|
||||
inValue.getLeft()),
|
||||
rightArith.modBeforeStore(sizeout, sizeinAddress, inAddress.getRight(), sizeinValue,
|
||||
inValue.getRight()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<L, R> modAfterLoad(int sizeout, int sizeinAddress, Pair<L, R> inAddress,
|
||||
int sizeinValue, Pair<L, R> inValue) {
|
||||
return Pair.of(
|
||||
leftArith.modAfterLoad(sizeout, sizeinAddress, inAddress.getLeft(), sizeinValue,
|
||||
inValue.getLeft()),
|
||||
rightArith.modAfterLoad(sizeout, sizeinAddress, inAddress.getRight(), sizeinValue,
|
||||
inValue.getRight()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<L, R> fromConst(byte[] value) {
|
||||
return Pair.of(leftArith.fromConst(value), rightArith.fromConst(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] toConcrete(Pair<L, R> value, Purpose purpose) {
|
||||
return leftArith.toConcrete(value.getLeft(), purpose);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long sizeOf(Pair<L, R> value) {
|
||||
return leftArith.sizeOf(value.getLeft());
|
||||
// TODO: Assert that the right agrees? Nah. Some aux types have no size.
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+43
-30
@@ -15,9 +15,9 @@
|
||||
*/
|
||||
package ghidra.pcode.exec;
|
||||
|
||||
import org.apache.commons.lang3.tuple.ImmutablePair;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import ghidra.pcode.exec.PcodeArithmetic.Purpose;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.mem.MemBuffer;
|
||||
@@ -26,57 +26,59 @@ import ghidra.program.model.mem.MemBuffer;
|
||||
* A paired executor state
|
||||
*
|
||||
* <p>
|
||||
* This composes two delegate states "left" and "write" creating a single state which instead stores
|
||||
* pairs of values, where the left component has the value type of the left state, and the right
|
||||
* component has the value type of the right state. Note that both states are addressed using only
|
||||
* the left "control" component. Otherwise, every operation on this state is decomposed into
|
||||
* This composes a delegate state and piece "left" and "write" creating a single state which instead
|
||||
* stores pairs of values, where the left component has the value type of the left state, and the
|
||||
* right component has the value type of the right state. Note that both states are addressed using
|
||||
* only the left "control" component. Otherwise, every operation on this state is decomposed into
|
||||
* operations upon the delegate states, and the final result composed from the results of those
|
||||
* operations.
|
||||
*
|
||||
* <p>
|
||||
* Where a response cannot be composed of both states, the paired state defers to the left. In this
|
||||
* way, the left state controls the machine, while the right is computed in tandem. The right never
|
||||
* directly controls the machine; however, by overriding
|
||||
* {@link #getVar(AddressSpace, Object, int, boolean)} and/or
|
||||
* {@link #setVar(AddressSpace, Object, int, boolean, Object)}, the right can affect the left and
|
||||
* indirectly control the machine.
|
||||
* directly controls the machine
|
||||
*
|
||||
* <p>
|
||||
* See {@link PairedPcodeExecutorStatePiece} regarding the composition of three or more pieces.
|
||||
*
|
||||
* @param <L> the type of values for the "left" state
|
||||
* @param <R> the type of values for the "right" state
|
||||
*/
|
||||
public class PairedPcodeExecutorState<L, R>
|
||||
extends AbstractOffsetTransformedPcodeExecutorState<Pair<L, R>, L, Pair<L, R>>
|
||||
implements PcodeExecutorState<Pair<L, R>> {
|
||||
public class PairedPcodeExecutorState<L, R> implements PcodeExecutorState<Pair<L, R>> {
|
||||
private final PairedPcodeExecutorStatePiece<L, L, R> piece;
|
||||
private final PcodeArithmetic<Pair<L, R>> arithmetic;
|
||||
|
||||
private final PcodeExecutorStatePiece<L, L> left;
|
||||
private final PcodeExecutorStatePiece<L, R> right;
|
||||
public PairedPcodeExecutorState(PairedPcodeExecutorStatePiece<L, L, R> piece) {
|
||||
this.piece = piece;
|
||||
this.arithmetic = piece.getArithmetic();
|
||||
}
|
||||
|
||||
/**
|
||||
* Compose a paired state from the given left and right states
|
||||
*
|
||||
* @param left the state backing the left side of paired values ("control")
|
||||
* @param right the state backing the right side of paired values ("rider")
|
||||
* @param right the state backing the right side of paired values ("auxiliary")
|
||||
*/
|
||||
public PairedPcodeExecutorState(PcodeExecutorStatePiece<L, L> left,
|
||||
public PairedPcodeExecutorState(PcodeExecutorState<L> left,
|
||||
PcodeExecutorStatePiece<L, R> right, PcodeArithmetic<Pair<L, R>> arithmetic) {
|
||||
this.piece =
|
||||
new PairedPcodeExecutorStatePiece<>(left, right, left.getArithmetic(), arithmetic);
|
||||
this.arithmetic = arithmetic;
|
||||
}
|
||||
|
||||
public PairedPcodeExecutorState(PcodeExecutorState<L> left,
|
||||
PcodeExecutorStatePiece<L, R> right) {
|
||||
super(new PairedPcodeExecutorStatePiece<>(left, right));
|
||||
this.left = left;
|
||||
this.right = right;
|
||||
this(left, right, new PairedPcodeArithmetic<>(left.getArithmetic(), right.getArithmetic()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<L, R> longToOffset(AddressSpace space, long l) {
|
||||
return new ImmutablePair<>(left.longToOffset(space, l), null);
|
||||
public PcodeArithmetic<Pair<L, R>> getArithmetic() {
|
||||
return arithmetic;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected L transformOffset(Pair<L, R> offset) {
|
||||
return offset.getLeft();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemBuffer getConcreteBuffer(Address address) {
|
||||
return left.getConcreteBuffer(address);
|
||||
public MemBuffer getConcreteBuffer(Address address, Purpose purpose) {
|
||||
return piece.getConcreteBuffer(address, purpose);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -85,7 +87,7 @@ public class PairedPcodeExecutorState<L, R>
|
||||
* @return the left state
|
||||
*/
|
||||
public PcodeExecutorStatePiece<L, L> getLeft() {
|
||||
return left;
|
||||
return piece.getLeft();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -94,6 +96,17 @@ public class PairedPcodeExecutorState<L, R>
|
||||
* @return the right state
|
||||
*/
|
||||
public PcodeExecutorStatePiece<L, R> getRight() {
|
||||
return right;
|
||||
return piece.getRight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVar(AddressSpace space, Pair<L, R> offset, int size, boolean quantize,
|
||||
Pair<L, R> val) {
|
||||
piece.setVar(space, offset.getLeft(), size, quantize, val);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<L, R> getVar(AddressSpace space, Pair<L, R> offset, int size, boolean quantize) {
|
||||
return piece.getVar(space, offset.getLeft(), size, quantize);
|
||||
}
|
||||
}
|
||||
|
||||
+45
-21
@@ -15,9 +15,9 @@
|
||||
*/
|
||||
package ghidra.pcode.exec;
|
||||
|
||||
import org.apache.commons.lang3.tuple.ImmutablePair;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import ghidra.pcode.exec.PcodeArithmetic.Purpose;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.mem.MemBuffer;
|
||||
@@ -26,11 +26,21 @@ import ghidra.program.model.mem.MemBuffer;
|
||||
* A paired executor state piece
|
||||
*
|
||||
* <p>
|
||||
* This compose two delegate pieces "left" and "right" creating a single piece which instead stores
|
||||
* pairs of values, where the left component has the value type of the left state, and the right
|
||||
* component has the value type of the right state. Both pieces must have the same address type.
|
||||
* Every operation on this piece is decomposed into operations upon the delegate pieces, and the
|
||||
* final result composed from the results of those operations.
|
||||
* This composes two delegate pieces "left" and "right" creating a single piece which stores pairs
|
||||
* of values, where the left component has the value type of the left piece, and the right component
|
||||
* has the value type of the right piece. Both pieces must have the same address type. Every
|
||||
* operation on this piece is decomposed into operations upon the delegate pieces, and the final
|
||||
* result composed from the results of those operations.
|
||||
*
|
||||
* <p>
|
||||
* To compose three or more states, first ask if it is really necessary. Second, consider
|
||||
* implementing the {@link PcodeExecutorStatePiece} interface directly. Third, use the Church-style
|
||||
* triple. In that third case, the implementor must decide which side has the nested tuple. Putting
|
||||
* it on the right keeps the concrete piece (conventionally on the left) in the most shallow
|
||||
* position, so it can be accessed efficiently. However, putting it on the left (implying it's in
|
||||
* the deepest position) keeps the concrete piece near the other pieces to which it's most closely
|
||||
* bound. The latter goal is only important when the paired arithmetics mix information between
|
||||
* their elements.
|
||||
*
|
||||
* @see PairedPcodeExecutorState
|
||||
* @param <A> the type of offset, usually the type of a controlling state
|
||||
@@ -42,36 +52,50 @@ public class PairedPcodeExecutorStatePiece<A, L, R>
|
||||
|
||||
private final PcodeExecutorStatePiece<A, L> left;
|
||||
private final PcodeExecutorStatePiece<A, R> right;
|
||||
private final PcodeArithmetic<A> addressArithmetic;
|
||||
private final PcodeArithmetic<Pair<L, R>> arithmetic;
|
||||
|
||||
public PairedPcodeExecutorStatePiece(PcodeExecutorStatePiece<A, L> left,
|
||||
PcodeExecutorStatePiece<A, R> right, PcodeArithmetic<A> addressArithmetic,
|
||||
PcodeArithmetic<Pair<L, R>> arithmetic) {
|
||||
this.left = left;
|
||||
this.right = right;
|
||||
this.addressArithmetic = addressArithmetic;
|
||||
this.arithmetic = arithmetic;
|
||||
}
|
||||
|
||||
public PairedPcodeExecutorStatePiece(PcodeExecutorStatePiece<A, L> left,
|
||||
PcodeExecutorStatePiece<A, R> right) {
|
||||
this.left = left;
|
||||
this.right = right;
|
||||
this(left, right, left.getAddressArithmetic(),
|
||||
new PairedPcodeArithmetic<>(left.getArithmetic(), right.getArithmetic()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public A longToOffset(AddressSpace space, long l) {
|
||||
return left.longToOffset(space, l);
|
||||
public PcodeArithmetic<A> getAddressArithmetic() {
|
||||
return addressArithmetic;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVar(AddressSpace space, A offset, int size,
|
||||
boolean truncateAddressableUnit, Pair<L, R> val) {
|
||||
left.setVar(space, offset, size, truncateAddressableUnit, val.getLeft());
|
||||
right.setVar(space, offset, size, truncateAddressableUnit, val.getRight());
|
||||
public PcodeArithmetic<Pair<L, R>> getArithmetic() {
|
||||
return arithmetic;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<L, R> getVar(AddressSpace space, A offset, int size,
|
||||
boolean truncateAddressableUnit) {
|
||||
return new ImmutablePair<>(
|
||||
left.getVar(space, offset, size, truncateAddressableUnit),
|
||||
right.getVar(space, offset, size, truncateAddressableUnit));
|
||||
public void setVar(AddressSpace space, A offset, int size, boolean quantize, Pair<L, R> val) {
|
||||
left.setVar(space, offset, size, quantize, val.getLeft());
|
||||
right.setVar(space, offset, size, quantize, val.getRight());
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemBuffer getConcreteBuffer(Address address) {
|
||||
return left.getConcreteBuffer(address);
|
||||
public Pair<L, R> getVar(AddressSpace space, A offset, int size, boolean quantize) {
|
||||
return Pair.of(
|
||||
left.getVar(space, offset, size, quantize),
|
||||
right.getVar(space, offset, size, quantize));
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemBuffer getConcreteBuffer(Address address, Purpose purpose) {
|
||||
return left.getConcreteBuffer(address, purpose);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -18,18 +18,26 @@ package ghidra.pcode.exec;
|
||||
import java.math.BigInteger;
|
||||
|
||||
import ghidra.pcode.opbehavior.*;
|
||||
import ghidra.pcode.utils.Utils;
|
||||
import ghidra.program.model.lang.Endian;
|
||||
import ghidra.program.model.pcode.PcodeOp;
|
||||
|
||||
/**
|
||||
* An interface that defines arithmetic p-code operations on values of type {@code T}.
|
||||
*
|
||||
* <p>
|
||||
* See {@link BytesPcodeArithmetic} for the typical pattern when implementing an arithmetic. There
|
||||
* are generally two cases: 1) Where endianness matters, 2) Where endianness does not matter. The
|
||||
* first is typical. The implementation should be an {@link Enum} with two constants, one for the
|
||||
* big endian implementation, and one for the little endian implementation. The class should also
|
||||
* provide static methods: {@code forEndian(boolean isBigEndian)} for getting the correct one based
|
||||
* on endianness, and {@code forLanguage(Language language)} for getting the correct one given a
|
||||
* language. If endianness does not matter, then the implementation should follow a singleton
|
||||
* pattern. See notes on {@link #getEndian()} for the endian-agnostic case.
|
||||
*
|
||||
* @param <T> the type of values operated on
|
||||
*/
|
||||
public interface PcodeArithmetic<T> {
|
||||
BinaryOpBehavior INT_ADD =
|
||||
(BinaryOpBehavior) OpBehaviorFactory.getOpBehavior(PcodeOp.INT_ADD);
|
||||
UnaryOpBehavior INT_ZEXT =
|
||||
(UnaryOpBehavior) OpBehaviorFactory.getOpBehavior(PcodeOp.INT_ZEXT);
|
||||
|
||||
/**
|
||||
* The number of bytes needed to encode the size (in bytes) of any value
|
||||
@@ -37,18 +45,46 @@ public interface PcodeArithmetic<T> {
|
||||
int SIZEOF_SIZEOF = 8;
|
||||
|
||||
/**
|
||||
* The arithmetic for operating on bytes in big-endian
|
||||
* Various reasons the emulator may require a concrete value
|
||||
*/
|
||||
PcodeArithmetic<byte[]> BYTES_BE = BytesPcodeArithmetic.BIG_ENDIAN;
|
||||
enum Purpose {
|
||||
/** The value is needed to parse an instruction */
|
||||
DECODE,
|
||||
/** The value is needed for disassembly context */
|
||||
CONTEXT,
|
||||
/** The value is needed to decide a conditional branch */
|
||||
CONDITION,
|
||||
/** The value will be used as the address of an indirect branch */
|
||||
BRANCH,
|
||||
/** The value will be used as the address of a value to load */
|
||||
LOAD,
|
||||
/** The value will be used as the address of a value to store */
|
||||
STORE,
|
||||
/** Some other reason, perhaps for userop library use */
|
||||
OTHER,
|
||||
/** The user or a tool is inspecting the value */
|
||||
INSPECT
|
||||
}
|
||||
|
||||
/**
|
||||
* The arithmetic for operating on bytes in little-endian
|
||||
* Get the endianness of this arithmetic
|
||||
*
|
||||
* <p>
|
||||
* Often T is a byte array, or at least represents one abstractly. Ideally, it is an array where
|
||||
* each element is an abstraction of a byte. If that is the case, then the arithmetic likely has
|
||||
* to interpret those bytes as integral values according to an endianness. This should return
|
||||
* that endianness.
|
||||
*
|
||||
* <p>
|
||||
* If the abstraction has no notion of endianness, return null. In that case, the both
|
||||
* {@link #fromConst(BigInteger, int, boolean)} and {@link #fromConst(long, int)} must be
|
||||
* overridden. Furthermore, unless {@link #toConcrete(Object, Purpose)} is guaranteed to throw
|
||||
* an exception, then {@link #toBigInteger(Object, Purpose)} and
|
||||
* {@link #toLong(Object, Purpose)} must also be overridden.
|
||||
*
|
||||
* @return the endianness or null
|
||||
*/
|
||||
PcodeArithmetic<byte[]> BYTES_LE = BytesPcodeArithmetic.LITTLE_ENDIAN;
|
||||
/**
|
||||
* The arithmetic for operating on {@link BigInteger}s.
|
||||
*/
|
||||
@Deprecated(forRemoval = true) // TODO: Not getting used
|
||||
PcodeArithmetic<BigInteger> BIGINT = BigIntegerPcodeArithmetic.INSTANCE;
|
||||
Endian getEndian();
|
||||
|
||||
/**
|
||||
* Apply a unary operator to the given input
|
||||
@@ -58,13 +94,35 @@ public interface PcodeArithmetic<T> {
|
||||
* size. For example, a {@link BigInteger} may have a minimum encoding size, but that does not
|
||||
* necessarily reflect the size of the variable from which is was read.
|
||||
*
|
||||
* @param op the behavior of the operator
|
||||
* @implNote {@link OpBehaviorFactory#getOpBehavior(int)} for the given opcode is guaranteed to
|
||||
* return a derivative of {@link UnaryOpBehavior}.
|
||||
*
|
||||
* @param opcode the p-code opcode
|
||||
* @param sizeout the size (in bytes) of the output variable
|
||||
* @param sizein1 the size (in bytes) of the input variable
|
||||
* @param in1 the input value
|
||||
* @return the output value
|
||||
*/
|
||||
T unaryOp(UnaryOpBehavior op, int sizeout, int sizein1, T in1);
|
||||
T unaryOp(int opcode, int sizeout, int sizein1, T in1);
|
||||
|
||||
/**
|
||||
* Apply a unary operator to the given input
|
||||
*
|
||||
* <p>
|
||||
* This provides the full p-code op, allowing deeper inspection of the code. For example, an
|
||||
* arithmetic may wish to distinguish immediate (constant) values from variables. By default,
|
||||
* this unpacks the details and defers to {@link #unaryOp(int, int, int, Object)}.
|
||||
*
|
||||
* @implNote {@link OpBehaviorFactory#getOpBehavior(int)} for the given opcode is guaranteed to
|
||||
* return a derivative of {@link UnaryOpBehavior}.
|
||||
*
|
||||
* @param op the operation
|
||||
* @param in1 the input value
|
||||
* @return the output value
|
||||
*/
|
||||
default T unaryOp(PcodeOp op, T in1) {
|
||||
return unaryOp(op.getOpcode(), op.getOutput().getSize(), op.getInput(0).getSize(), in1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a binary operator to the given inputs
|
||||
@@ -74,7 +132,11 @@ public interface PcodeArithmetic<T> {
|
||||
* size. For example, a {@link BigInteger} may have a minimum encoding size, but that does not
|
||||
* necessarily reflect the size of the variable from which is was read.
|
||||
*
|
||||
* @param op the behavior of the operator
|
||||
* @implNote {@link OpBehaviorFactory#getOpBehavior(int)} for the given opcode is guaranteed to
|
||||
* return a derivative of {@link BinaryOpBehavior}.
|
||||
*
|
||||
* @param op the operation
|
||||
* @param b the behavior of the operator
|
||||
* @param sizeout the size (in bytes) of the output variable
|
||||
* @param sizein1 the size (in bytes) of the first (left) input variable
|
||||
* @param in1 the first (left) input value
|
||||
@@ -82,7 +144,68 @@ public interface PcodeArithmetic<T> {
|
||||
* @param in2 the second (right) input value
|
||||
* @return the output value
|
||||
*/
|
||||
T binaryOp(BinaryOpBehavior op, int sizeout, int sizein1, T in1, int sizein2, T in2);
|
||||
T binaryOp(int opcode, int sizeout, int sizein1, T in1, int sizein2, T in2);
|
||||
|
||||
/**
|
||||
* Apply a binary operator to the given input
|
||||
*
|
||||
* <p>
|
||||
* This provides the full p-code op, allowing deeper inspection of the code. For example, an
|
||||
* arithmetic may wish to distinguish immediate (constant) values from variables. By default,
|
||||
* this unpacks the details and defers to {@link #binaryOp(int, int, int, Object, int, Object)}.
|
||||
*
|
||||
* @implNote {@link OpBehaviorFactory#getOpBehavior(int)} for the given opcode is guaranteed to
|
||||
* return a derivative of {@link BinaryOpBehavior}.
|
||||
*
|
||||
* @param op
|
||||
* @param in1
|
||||
* @param in2
|
||||
* @return
|
||||
*/
|
||||
default T binaryOp(PcodeOp op, T in1, T in2) {
|
||||
return binaryOp(op.getOpcode(), op.getOutput().getSize(), op.getInput(0).getSize(), in1,
|
||||
op.getInput(1).getSize(), in2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply any modifications before a value is stored
|
||||
*
|
||||
* <p>
|
||||
* This implements any abstractions associated with {@link PcodeOp#STORE}. This is called on the
|
||||
* address/offset and the value before the value is actually stored into the state.
|
||||
*
|
||||
* @param sizeout the size (in bytes) of the output variable
|
||||
* @param sizeinAddress the size (in bytes) of the variable used for indirection
|
||||
* @param inAddress the value used as the address (or offset)
|
||||
* @param sizeinValue the size (in bytes) of the variable to store
|
||||
* @param inValue the value to store
|
||||
* @return the modified value to store
|
||||
*/
|
||||
T modBeforeStore(int sizeout, int sizeinAddress, T inAddress, int sizeinValue, T inValue);
|
||||
|
||||
/**
|
||||
* Apply any modifications after a value is loaded
|
||||
*
|
||||
* <p>
|
||||
* This implements any abstractions associated with {@link PcodeOp#LOAD}. This is called on the
|
||||
* address/offset and the value after the value is actually loaded from the state.
|
||||
*
|
||||
* @param sizeout the size (in bytes) of the output variable
|
||||
* @param sizeinAddress the size (in bytes) of the variable used for indirection
|
||||
* @param inAddress the value used as the address (or offset)
|
||||
* @param sizeinValue the size (in bytes) of the variable loaded
|
||||
* @param inValue the value loaded
|
||||
* @return the modified value loaded
|
||||
*/
|
||||
T modAfterLoad(int sizeout, int sizeinAddress, T inAddress, int sizeinValue, T inValue);
|
||||
|
||||
/**
|
||||
* Convert the given constant concrete value to type {@code T} having the same size.
|
||||
*
|
||||
* @param value the constant value
|
||||
* @return the value as a {@code T}
|
||||
*/
|
||||
T fromConst(byte[] value);
|
||||
|
||||
/**
|
||||
* Convert the given constant concrete value to type {@code T} having the given size.
|
||||
@@ -96,7 +219,9 @@ public interface PcodeArithmetic<T> {
|
||||
* @param size the size (in bytes) of the variable into which the value is to be stored
|
||||
* @return the value as a {@code T}
|
||||
*/
|
||||
T fromConst(long value, int size);
|
||||
default T fromConst(long value, int size) {
|
||||
return fromConst(Utils.longToBytes(value, size, getEndian().isBigEndian()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given constant concrete value to type {@code T} having the given size.
|
||||
@@ -113,7 +238,10 @@ public interface PcodeArithmetic<T> {
|
||||
* big endian, no matter the machine language's endianness.
|
||||
* @return the value as a {@code T}
|
||||
*/
|
||||
T fromConst(BigInteger value, int size, boolean isContextreg);
|
||||
default T fromConst(BigInteger value, int size, boolean isContextreg) {
|
||||
return fromConst(
|
||||
Utils.bigIntegerToBytes(value, size, isContextreg || getEndian().isBigEndian()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given constant concrete value to type {@code T} having the given size.
|
||||
@@ -127,53 +255,89 @@ public interface PcodeArithmetic<T> {
|
||||
return fromConst(value, size, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert, if possible, the given abstract value to a concrete byte array
|
||||
*
|
||||
* @param value the abstract value
|
||||
* @param size the expected size (in bytes) of the array
|
||||
* @param the reason why the emulator needs a concrete value
|
||||
* @return the array
|
||||
* @throws ConcretionError if the value cannot be made concrete
|
||||
*/
|
||||
byte[] toConcrete(T value, Purpose purpose);
|
||||
|
||||
/**
|
||||
* Convert, if possible, the given abstract condition to a concrete boolean value
|
||||
*
|
||||
* @param cond the abstract condition
|
||||
* @param purpose probably {@link Purpose#CONDITION}
|
||||
* @return the boolean value
|
||||
*/
|
||||
boolean isTrue(T cond);
|
||||
default boolean isTrue(T cond, Purpose purpose) {
|
||||
byte[] concrete = toConcrete(cond, purpose);
|
||||
for (byte b : concrete) {
|
||||
if (b != 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert, if possible, the given abstract value to a concrete value
|
||||
* Convert, if possible, the given abstract value to a concrete big integer
|
||||
*
|
||||
* <p>
|
||||
* If the conversion is not possible, throw an exception. TODO: Decide on conventions of which
|
||||
* exception to throw and/or establish a hierarchy of checked exceptions.
|
||||
* If the conversion is not possible, throw an exception.
|
||||
*
|
||||
* @param value the abstract value
|
||||
* @param isContextreg true to indicate the value is from the disassembly context register. If
|
||||
* {@code T} represents bytes, and the value is the contextreg, then the bytes are in
|
||||
* big endian, no matter the machine language's endianness.
|
||||
* @param purpose the reason why the emulator needs a concrete value
|
||||
* @return the concrete value
|
||||
* @throws ConcretionError if the value cannot be made concrete
|
||||
*/
|
||||
BigInteger toConcrete(T value, boolean isContextreg);
|
||||
default BigInteger toBigInteger(T value, Purpose purpose) {
|
||||
byte[] concrete = toConcrete(value, purpose);
|
||||
return Utils.bytesToBigInteger(concrete, concrete.length,
|
||||
purpose == Purpose.CONTEXT || getEndian().isBigEndian(), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make concrete, if possible, the given abstract value
|
||||
* Convert, if possible, the given abstract value to a concrete long
|
||||
*
|
||||
* <p>
|
||||
* If the conversion is not possible, throw an exception. TODO: Decide on conventions of which
|
||||
* exception to throw and/or establish a hierarchy of checked exceptions.
|
||||
* If the conversion is not possible, throw an exception.
|
||||
*
|
||||
* @param value the abstract value
|
||||
* @param purpose the reason why the emulator needs a concrete value
|
||||
* @return the concrete value
|
||||
* @throws ConcretionError if the value cannot be made concrete
|
||||
*/
|
||||
default BigInteger toConcrete(T value) {
|
||||
return toConcrete(value, false);
|
||||
default long toLong(T value, Purpose purpose) {
|
||||
byte[] concrete = toConcrete(value, purpose);
|
||||
return Utils.bytesToLong(concrete, concrete.length,
|
||||
purpose == Purpose.CONTEXT || getEndian().isBigEndian());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the size in bytes, if possible, of the given abstract value
|
||||
*
|
||||
* <p>
|
||||
* If the abstract value does not conceptually have a size, throw an exception. Note the
|
||||
* returned size should itself have a size of {@link #SIZEOF_SIZEOF}. TODO: Establish
|
||||
* conventions for exceptions.
|
||||
* If the abstract value does not conceptually have a size, throw an exception.
|
||||
*
|
||||
* @param value the abstract value
|
||||
* @return the size in bytes
|
||||
*/
|
||||
T sizeOf(T value);
|
||||
long sizeOf(T value);
|
||||
|
||||
/**
|
||||
* Get the size in bytes, if possible, of the given abstract value, as an abstract value
|
||||
*
|
||||
* <p>
|
||||
* The returned size should itself has a size of {@link #SIZEOF_SIZEOF}.
|
||||
*
|
||||
* @param value the abstract value
|
||||
* @return the size in bytes, as an abstract value
|
||||
*/
|
||||
default T sizeOfAbstract(T value) {
|
||||
return fromConst(sizeOf(value), SIZEOF_SIZEOF);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import java.util.Map;
|
||||
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
|
||||
import ghidra.pcode.emu.PcodeEmulator;
|
||||
import ghidra.pcode.error.LowlevelError;
|
||||
import ghidra.pcode.exec.PcodeArithmetic.Purpose;
|
||||
import ghidra.pcode.exec.PcodeUseropLibrary.PcodeUseropDefinition;
|
||||
import ghidra.pcode.opbehavior.*;
|
||||
import ghidra.program.model.address.Address;
|
||||
@@ -41,7 +42,7 @@ import ghidra.program.model.pcode.Varnode;
|
||||
public class PcodeExecutor<T> {
|
||||
protected final SleighLanguage language;
|
||||
protected final PcodeArithmetic<T> arithmetic;
|
||||
protected final PcodeExecutorStatePiece<T, T> state;
|
||||
protected final PcodeExecutorState<T> state;
|
||||
protected final Register pc;
|
||||
protected final int pointerSize;
|
||||
|
||||
@@ -53,7 +54,7 @@ public class PcodeExecutor<T> {
|
||||
* @param state an implementation of load/store p-code ops
|
||||
*/
|
||||
public PcodeExecutor(SleighLanguage language, PcodeArithmetic<T> arithmetic,
|
||||
PcodeExecutorStatePiece<T, T> state) {
|
||||
PcodeExecutorState<T> state) {
|
||||
this.language = language;
|
||||
this.arithmetic = arithmetic;
|
||||
this.state = state;
|
||||
@@ -85,28 +86,61 @@ public class PcodeExecutor<T> {
|
||||
*
|
||||
* @return the state
|
||||
*/
|
||||
public PcodeExecutorStatePiece<T, T> getState() {
|
||||
public PcodeExecutorState<T> getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile and execute a line of Sleigh
|
||||
*
|
||||
* @param line the line, excluding the semicolon
|
||||
*/
|
||||
public void executeSleighLine(String line) {
|
||||
PcodeProgram program = SleighProgramCompiler.compileProgram(language,
|
||||
"line", List.of(line + ";"), PcodeUseropLibrary.NIL);
|
||||
execute(program, PcodeUseropLibrary.nil());
|
||||
}
|
||||
|
||||
/**
|
||||
* Begin execution of the given program
|
||||
*
|
||||
* @param program the program, e.g., from an injection, or a decoded instruction
|
||||
* @return the frame
|
||||
*/
|
||||
public PcodeFrame begin(PcodeProgram program) {
|
||||
return begin(program.code, program.useropNames);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a program using the given library
|
||||
*
|
||||
* @param program the program, e.g., from an injection, or a decoded instruction
|
||||
* @param library the library
|
||||
* @return the frame
|
||||
*/
|
||||
public PcodeFrame execute(PcodeProgram program, PcodeUseropLibrary<T> library) {
|
||||
return execute(program.code, program.useropNames, library);
|
||||
}
|
||||
|
||||
/**
|
||||
* Begin execution of a list of p-code ops
|
||||
*
|
||||
* @param code the ops
|
||||
* @param useropNames the map of userop numbers to names
|
||||
* @return the frame
|
||||
*/
|
||||
public PcodeFrame begin(List<PcodeOp> code, Map<Integer, String> useropNames) {
|
||||
return new PcodeFrame(language, code, useropNames);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a list of p-code ops
|
||||
*
|
||||
* @param code the ops
|
||||
* @param useropNames the map of userop numbers to names
|
||||
* @param library the library of userops
|
||||
* @return the frame
|
||||
*/
|
||||
public PcodeFrame execute(List<PcodeOp> code, Map<Integer, String> useropNames,
|
||||
PcodeUseropLibrary<T> library) {
|
||||
PcodeFrame frame = begin(code, useropNames);
|
||||
@@ -139,6 +173,11 @@ public class PcodeExecutor<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an unrecognized or unimplemented p-code op
|
||||
*
|
||||
* @param op the op
|
||||
*/
|
||||
protected void badOp(PcodeOp op) {
|
||||
switch (op.getOpcode()) {
|
||||
case PcodeOp.UNIMPLEMENTED:
|
||||
@@ -150,6 +189,13 @@ public class PcodeExecutor<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Step on p-code op
|
||||
*
|
||||
* @param op the op
|
||||
* @param frame the current frame
|
||||
* @param library the library, invoked in case of {@link PcodeOp#CALLOTHER}
|
||||
*/
|
||||
public void stepOp(PcodeOp op, PcodeFrame frame, PcodeUseropLibrary<T> library) {
|
||||
OpBehavior b = OpBehaviorFactory.getOpBehavior(op.getOpcode());
|
||||
if (b == null) {
|
||||
@@ -198,6 +244,12 @@ public class PcodeExecutor<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Step a single p-code op
|
||||
*
|
||||
* @param frame the frame whose next op to execute
|
||||
* @param library the userop library
|
||||
*/
|
||||
public void step(PcodeFrame frame, PcodeUseropLibrary<T> library) {
|
||||
try {
|
||||
stepOp(frame.nextOp(), frame, library);
|
||||
@@ -211,55 +263,97 @@ public class PcodeExecutor<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip a single p-code op
|
||||
*
|
||||
* @param frame the frame whose next op to skip
|
||||
*/
|
||||
public void skip(PcodeFrame frame) {
|
||||
frame.nextOp();
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that a varnode is constant and get its value as an integer.
|
||||
*
|
||||
* <p>
|
||||
* Here "constant" means a literal or immediate value. It does not read from the state.
|
||||
*
|
||||
* @param vn the varnode
|
||||
* @return the value
|
||||
*/
|
||||
protected int getIntConst(Varnode vn) {
|
||||
assert vn.getAddress().getAddressSpace().isConstantSpace();
|
||||
return (int) vn.getAddress().getOffset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given unary op
|
||||
*
|
||||
* @param op the op
|
||||
* @param b the op behavior
|
||||
*/
|
||||
public void executeUnaryOp(PcodeOp op, UnaryOpBehavior b) {
|
||||
Varnode in1Var = op.getInput(0);
|
||||
Varnode outVar = op.getOutput();
|
||||
T in1 = state.getVar(in1Var);
|
||||
T out = arithmetic.unaryOp(b, outVar.getSize(),
|
||||
in1Var.getSize(), in1);
|
||||
T out = arithmetic.unaryOp(op, in1);
|
||||
state.setVar(outVar, out);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given binary op
|
||||
*
|
||||
* @param op the op
|
||||
* @param b the op behavior
|
||||
*/
|
||||
public void executeBinaryOp(PcodeOp op, BinaryOpBehavior b) {
|
||||
Varnode in1Var = op.getInput(0);
|
||||
Varnode in2Var = op.getInput(1);
|
||||
Varnode outVar = op.getOutput();
|
||||
T in1 = state.getVar(in1Var);
|
||||
T in2 = state.getVar(in2Var);
|
||||
T out = arithmetic.binaryOp(b, outVar.getSize(),
|
||||
in1Var.getSize(), in1, in2Var.getSize(), in2);
|
||||
T out = arithmetic.binaryOp(op, in1, in2);
|
||||
state.setVar(outVar, out);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a load
|
||||
*
|
||||
* @param op the op
|
||||
*/
|
||||
public void executeLoad(PcodeOp op) {
|
||||
int spaceID = getIntConst(op.getInput(0));
|
||||
AddressSpace space = language.getAddressFactory().getAddressSpace(spaceID);
|
||||
T offset = state.getVar(op.getInput(1));
|
||||
Varnode inOffset = op.getInput(1);
|
||||
T offset = state.getVar(inOffset);
|
||||
Varnode outvar = op.getOutput();
|
||||
T out = state.getVar(space, offset, outvar.getSize(), true);
|
||||
state.setVar(outvar, out);
|
||||
}
|
||||
|
||||
public void executeStore(PcodeOp op) {
|
||||
int spaceID = getIntConst(op.getInput(0));
|
||||
AddressSpace space = language.getAddressFactory().getAddressSpace(spaceID);
|
||||
T offset = state.getVar(op.getInput(1));
|
||||
Varnode valVar = op.getInput(2);
|
||||
T val = state.getVar(valVar);
|
||||
state.setVar(space, offset, valVar.getSize(), true, val);
|
||||
T out = state.getVar(space, offset, outvar.getSize(), true);
|
||||
T mod = arithmetic.modAfterLoad(outvar.getSize(), inOffset.getSize(), offset,
|
||||
outvar.getSize(), out);
|
||||
state.setVar(outvar, mod);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when execution branches to a target address
|
||||
* Execute a store
|
||||
*
|
||||
* @param op the op
|
||||
*/
|
||||
public void executeStore(PcodeOp op) {
|
||||
int spaceID = getIntConst(op.getInput(0));
|
||||
AddressSpace space = language.getAddressFactory().getAddressSpace(spaceID);
|
||||
Varnode inOffset = op.getInput(1);
|
||||
T offset = state.getVar(inOffset);
|
||||
Varnode valVar = op.getInput(2);
|
||||
|
||||
T val = state.getVar(valVar);
|
||||
T mod = arithmetic.modBeforeStore(valVar.getSize(), inOffset.getSize(), offset,
|
||||
valVar.getSize(), val);
|
||||
state.setVar(space, offset, valVar.getSize(), true, mod);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension point: Called when execution branches to a target address
|
||||
*
|
||||
* <p>
|
||||
* NOTE: This is <em>not</em> called for the fall-through case
|
||||
@@ -267,16 +361,36 @@ public class PcodeExecutor<T> {
|
||||
* @param target the target address
|
||||
*/
|
||||
protected void branchToAddress(Address target) {
|
||||
// Extension point
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the state's pc to the given offset and finish the frame
|
||||
*
|
||||
* <p>
|
||||
* This implements only part of the p-code control flow semantics. An emulator must also
|
||||
* override {@link #branchToAddress(Address)}, so that it can update its internal program
|
||||
* counter. The emulator could just read the program counter from the state after <em>every</em>
|
||||
* completed frame, but receiving it "out of band" is faster.
|
||||
*
|
||||
* @param offset the offset (the new value of the program counter)
|
||||
* @param frame the frame to finish
|
||||
*/
|
||||
protected void branchToOffset(T offset, PcodeFrame frame) {
|
||||
state.setVar(pc.getAddressSpace(), pc.getOffset(), (pc.getBitLength() + 7) / 8, false,
|
||||
offset);
|
||||
state.setVar(pc, offset);
|
||||
frame.finishAsBranch();
|
||||
}
|
||||
|
||||
public void executeBranch(PcodeOp op, PcodeFrame frame) {
|
||||
/**
|
||||
* Perform the actual logic of a branch p-code op
|
||||
*
|
||||
* <p>
|
||||
* This is a separate method, so that overriding {@link #executeBranch(PcodeOp, PcodeFrame)}
|
||||
* does not implicitly modify {@link #executeConditionalBranch(PcodeOp, PcodeFrame)}.
|
||||
*
|
||||
* @param op the op
|
||||
* @param frame the frame
|
||||
*/
|
||||
protected void doExecuteBranch(PcodeOp op, PcodeFrame frame) {
|
||||
Address target = op.getInput(0).getAddress();
|
||||
if (target.isConstantAddress()) {
|
||||
frame.branch((int) target.getOffset());
|
||||
@@ -287,33 +401,101 @@ public class PcodeExecutor<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a branch
|
||||
*
|
||||
* <p>
|
||||
* This merely defers to {@link #doExecuteBranch(PcodeOp, PcodeFrame)}. To instrument the
|
||||
* operation, override this. To modify or instrument branching in general, override
|
||||
* {@link #doExecuteBranch(PcodeOp, PcodeFrame)}, {@link #branchToOffset(Object, PcodeFrame)},
|
||||
* and/or {@link #branchToAddress(Address)}.
|
||||
*
|
||||
* @param op the op
|
||||
* @param frame the frame
|
||||
*/
|
||||
public void executeBranch(PcodeOp op, PcodeFrame frame) {
|
||||
doExecuteBranch(op, frame);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a conditional branch
|
||||
*
|
||||
* @param op the op
|
||||
* @param frame the frame
|
||||
*/
|
||||
public void executeConditionalBranch(PcodeOp op, PcodeFrame frame) {
|
||||
Varnode condVar = op.getInput(1);
|
||||
T cond = state.getVar(condVar);
|
||||
if (arithmetic.isTrue(cond)) {
|
||||
executeBranch(op, frame);
|
||||
if (arithmetic.isTrue(cond, Purpose.CONDITION)) {
|
||||
doExecuteBranch(op, frame);
|
||||
}
|
||||
}
|
||||
|
||||
public void executeIndirectBranch(PcodeOp op, PcodeFrame frame) {
|
||||
/**
|
||||
* Perform the actual logic of an indirect branch p-code op
|
||||
*
|
||||
* <p>
|
||||
* This is a separate method, so that overriding
|
||||
* {@link #executeIndirectBranch(PcodeOp, PcodeFrame)} does not implicitly modify
|
||||
* {@link #executeIndirectCall(PcodeOp, PcodeFrame)} and
|
||||
* {@link #executeReturn(PcodeOp, PcodeFrame)}.
|
||||
*
|
||||
* @param op the op
|
||||
* @param frame the frame
|
||||
*/
|
||||
protected void doExecuteIndirectBranch(PcodeOp op, PcodeFrame frame) {
|
||||
T offset = state.getVar(op.getInput(0));
|
||||
branchToOffset(offset, frame);
|
||||
|
||||
long concrete = arithmetic.toConcrete(offset).longValue();
|
||||
long concrete = arithmetic.toLong(offset, Purpose.BRANCH);
|
||||
Address target = op.getSeqnum().getTarget().getNewAddress(concrete, true);
|
||||
branchToAddress(target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute an indirect branch
|
||||
*
|
||||
* <p>
|
||||
* This merely defers to {@link #doExecuteIndirectBranch(PcodeOp, PcodeFrame)}. To instrument
|
||||
* the operation, override this. To modify or instrument indirect branching in general, override
|
||||
* {@link #doExecuteIndirectBranch(PcodeOp, PcodeFrame)}.
|
||||
*
|
||||
* @param op the op
|
||||
* @param frame the frame
|
||||
*/
|
||||
public void executeIndirectBranch(PcodeOp op, PcodeFrame frame) {
|
||||
doExecuteIndirectBranch(op, frame);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a call
|
||||
*
|
||||
* @param op the op
|
||||
* @param frame the frame
|
||||
*/
|
||||
public void executeCall(PcodeOp op, PcodeFrame frame) {
|
||||
Address target = op.getInput(0).getAddress();
|
||||
branchToOffset(arithmetic.fromConst(target.getOffset(), pointerSize), frame);
|
||||
branchToAddress(target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute an indirect call
|
||||
*
|
||||
* @param op the op
|
||||
* @param frame the frame
|
||||
*/
|
||||
public void executeIndirectCall(PcodeOp op, PcodeFrame frame) {
|
||||
executeIndirectBranch(op, frame);
|
||||
doExecuteIndirectBranch(op, frame);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of a userop
|
||||
*
|
||||
* @param opNo the userop number
|
||||
* @param frame the frame
|
||||
* @return the name, or null if it is not defined
|
||||
*/
|
||||
public String getUseropName(int opNo, PcodeFrame frame) {
|
||||
if (opNo < language.getNumberOfUserDefinedOpNames()) {
|
||||
return language.getUserDefinedOpName(opNo);
|
||||
@@ -321,23 +503,52 @@ public class PcodeExecutor<T> {
|
||||
return frame.getUseropName(opNo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a userop call
|
||||
*
|
||||
* @param op the op
|
||||
* @param frame the frame
|
||||
* @param library the library of userops
|
||||
*/
|
||||
public void executeCallother(PcodeOp op, PcodeFrame frame, PcodeUseropLibrary<T> library) {
|
||||
int opNo = getIntConst(op.getInput(0));
|
||||
String opName = getUseropName(opNo, frame);
|
||||
if (opName == null) {
|
||||
throw new AssertionError(
|
||||
"Pcode userop " + opNo + " is not defined");
|
||||
throw new AssertionError("Pcode userop " + opNo + " is not defined");
|
||||
}
|
||||
PcodeUseropDefinition<T> opDef = library.getUserops().get(opName);
|
||||
if (opDef == null) {
|
||||
throw new SleighLinkException(
|
||||
"Sleigh userop '" + opName + "' is not in the library " + library);
|
||||
if (opDef != null) {
|
||||
opDef.execute(this, library, op.getOutput(),
|
||||
List.of(op.getInputs()).subList(1, op.getNumInputs()));
|
||||
return;
|
||||
}
|
||||
opDef.execute(this, library, op.getOutput(),
|
||||
List.of(op.getInputs()).subList(1, op.getNumInputs()));
|
||||
onMissingUseropDef(op, frame, opName, library);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension point: Behavior when a userop definition was not found in the library
|
||||
*
|
||||
* <p>
|
||||
* The default behavior is to throw a {@link SleighLinkException}.
|
||||
*
|
||||
* @param op the op
|
||||
* @param frame the frame
|
||||
* @param opName the name of the p-code userop
|
||||
* @param library the library
|
||||
*/
|
||||
protected void onMissingUseropDef(PcodeOp op, PcodeFrame frame, String opName,
|
||||
PcodeUseropLibrary<T> library) {
|
||||
throw new SleighLinkException(
|
||||
"Sleigh userop '" + opName + "' is not in the library " + library);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a return
|
||||
*
|
||||
* @param op the op
|
||||
* @param frame the frame
|
||||
*/
|
||||
public void executeReturn(PcodeOp op, PcodeFrame frame) {
|
||||
executeIndirectBranch(op, frame);
|
||||
doExecuteIndirectBranch(op, frame);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,16 +17,28 @@ package ghidra.pcode.exec;
|
||||
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
|
||||
/**
|
||||
* An interface that provides storage for values of type {@code T}, addressed by offsets of type
|
||||
* {@code T}.
|
||||
* An interface that provides storage for values of type {@code T}
|
||||
*
|
||||
* <p>
|
||||
* This is not much more than a stricter form of {@link PcodeExecutorStatePiece}, in that it
|
||||
* requires the value and address offset types to agree, so that a p-code executor or emulator can
|
||||
* perform loads and stores using indirect addresses. The typical pattern for implementing a state
|
||||
* is to compose it from pieces. See {@link PcodeExecutorStatePiece}.
|
||||
*
|
||||
* @param <T> the type of offsets and values
|
||||
*/
|
||||
public interface PcodeExecutorState<T> extends PcodeExecutorStatePiece<T, T> {
|
||||
|
||||
@Override
|
||||
default PcodeArithmetic<T> getAddressArithmetic() {
|
||||
return getArithmetic();
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this state as the control, paired with the given state as the rider.
|
||||
* Use this state as the control, paired with the given auxiliary state.
|
||||
*
|
||||
* <p>
|
||||
* <b>CAUTION:</b> Often, the default paired state is not quite sufficient. Consider
|
||||
|
||||
+65
-29
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package ghidra.pcode.exec;
|
||||
|
||||
import ghidra.pcode.exec.PcodeArithmetic.Purpose;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.Register;
|
||||
import ghidra.program.model.mem.MemBuffer;
|
||||
@@ -22,9 +23,17 @@ import ghidra.program.model.pcode.Varnode;
|
||||
|
||||
/**
|
||||
* An interface that provides storage for values of type {@code T}, addressed by offsets of type
|
||||
* {@code A}.
|
||||
* {@code A}
|
||||
*
|
||||
* <p>
|
||||
* The typical pattern for implementing a state is to compose it from one or more state pieces. Each
|
||||
* piece must use the same address type and arithmetic. If more than one piece is needed, they are
|
||||
* composed using {@link PairedPcodeExecutorStatePiece}. Once all the pieces are composed, the root
|
||||
* piece can be wrapped to make a state using {@link DefaultPcodeExecutorState} or
|
||||
* {@link PairedPcodeExecutorState}. The latter corrects the address type to be a pair so it matches
|
||||
* the type of values.
|
||||
*
|
||||
* @param <A> the type of offsets
|
||||
* @param <A> the type of address offsets
|
||||
* @param <T> the type of values
|
||||
*/
|
||||
public interface PcodeExecutorStatePiece<A, T> {
|
||||
@@ -47,17 +56,18 @@ public interface PcodeExecutorStatePiece<A, T> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given offset from {@code long} to type {@code A}
|
||||
* Get the arithmetic used to manipulate addresses of the type used by this state
|
||||
*
|
||||
* <p>
|
||||
* Note, is it unlikely (and discouraged) to encode the space in {@code A}. The reason the space
|
||||
* is given is to ensure the result has the correct size.
|
||||
*
|
||||
* @param space the space where the offset applies
|
||||
* @param l the offset
|
||||
* @return the same offset as type {@code A}
|
||||
* @return the address (or offset) arithmetic
|
||||
*/
|
||||
A longToOffset(AddressSpace space, long l);
|
||||
PcodeArithmetic<A> getAddressArithmetic();
|
||||
|
||||
/**
|
||||
* Get the arithmetic used to manipulate values of the type stored by this state
|
||||
*
|
||||
* @return the arithmetic
|
||||
*/
|
||||
PcodeArithmetic<T> getArithmetic();
|
||||
|
||||
/**
|
||||
* Set the value of a register variable
|
||||
@@ -87,27 +97,36 @@ public interface PcodeExecutorStatePiece<A, T> {
|
||||
* @param space the address space
|
||||
* @param offset the offset within the space
|
||||
* @param size the size of the variable
|
||||
* @param truncateAddressableUnit true to truncate to the language's "addressable unit"
|
||||
* @param quantize true to quantize to the language's "addressable unit"
|
||||
* @param val the value
|
||||
*/
|
||||
void setVar(AddressSpace space, A offset, int size, boolean truncateAddressableUnit, T val);
|
||||
void setVar(AddressSpace space, A offset, int size, boolean quantize, T val);
|
||||
|
||||
/**
|
||||
* Set the value of a variable
|
||||
*
|
||||
* <p>
|
||||
* This method is typically used for writing memory variables.
|
||||
*
|
||||
* @param space the address space
|
||||
* @param offset the offset within the space
|
||||
* @param size the size of the variable
|
||||
* @param truncateAddressableUnit true to truncate to the language's "addressable unit"
|
||||
* @param quantize true to quantize to the language's "addressable unit"
|
||||
* @param val the value
|
||||
*/
|
||||
default void setVar(AddressSpace space, long offset, int size, boolean truncateAddressableUnit,
|
||||
T val) {
|
||||
default void setVar(AddressSpace space, long offset, int size, boolean quantize, T val) {
|
||||
checkRange(space, offset, size);
|
||||
setVar(space, longToOffset(space, offset), size, truncateAddressableUnit, val);
|
||||
A aOffset = getAddressArithmetic().fromConst(offset, space.getPointerSize());
|
||||
setVar(space, aOffset, size, quantize, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of a variable
|
||||
*
|
||||
* @param address the address in memory
|
||||
* @param size the size of the variable
|
||||
* @param quantize true to quantize to the language's "addressable unit"
|
||||
* @param val the value
|
||||
*/
|
||||
default void setVar(Address address, int size, boolean quantize, T val) {
|
||||
setVar(address.getAddressSpace(), address.getOffset(), size, quantize, val);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -139,10 +158,10 @@ public interface PcodeExecutorStatePiece<A, T> {
|
||||
* @param space the address space
|
||||
* @param offset the offset within the space
|
||||
* @param size the size of the variable
|
||||
* @param truncateAddressableUnit true to truncate to the language's "addressable unit"
|
||||
* @param quantize true to quantize to the language's "addressable unit"
|
||||
* @return the value
|
||||
*/
|
||||
T getVar(AddressSpace space, A offset, int size, boolean truncateAddressableUnit);
|
||||
T getVar(AddressSpace space, A offset, int size, boolean quantize);
|
||||
|
||||
/**
|
||||
* Get the value of a variable
|
||||
@@ -153,30 +172,47 @@ public interface PcodeExecutorStatePiece<A, T> {
|
||||
* @param space the address space
|
||||
* @param offset the offset within the space
|
||||
* @param size the size of the variable
|
||||
* @param truncateAddressableUnit true to truncate to the language's "addressalbe unit"
|
||||
* @param quantize true to quantize to the language's "addressable unit"
|
||||
* @return the value
|
||||
*/
|
||||
default T getVar(AddressSpace space, long offset, int size, boolean truncateAddressableUnit) {
|
||||
default T getVar(AddressSpace space, long offset, int size, boolean quantize) {
|
||||
checkRange(space, offset, size);
|
||||
return getVar(space, longToOffset(space, offset), size, truncateAddressableUnit);
|
||||
A aOffset = getAddressArithmetic().fromConst(offset, space.getPointerSize());
|
||||
return getVar(space, aOffset, size, quantize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of a variable
|
||||
*
|
||||
* <p>
|
||||
* This method is typically used for reading memory variables.
|
||||
*
|
||||
* @param address the address of the variable
|
||||
* @param size the size of the variable
|
||||
* @param quantize true to quantize to the language's "addressable unit"
|
||||
* @return the value
|
||||
*/
|
||||
default T getVar(Address address, int size, boolean quantize) {
|
||||
return getVar(address.getAddressSpace(), address.getOffset(), size, quantize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind a buffer of concrete bytes at the given start address
|
||||
*
|
||||
* @param address the start address
|
||||
* @param purpose the reason why the emulator needs a concrete value
|
||||
* @return a buffer
|
||||
*/
|
||||
MemBuffer getConcreteBuffer(Address address);
|
||||
MemBuffer getConcreteBuffer(Address address, Purpose purpose);
|
||||
|
||||
/**
|
||||
* Truncate the given offset to the language's "addressable unit"
|
||||
* Quantize the given offset to the language's "addressable unit"
|
||||
*
|
||||
* @param space the space where the offset applies
|
||||
* @param offset the offset
|
||||
* @return the truncated offset
|
||||
* @return the quantized offset
|
||||
*/
|
||||
default long truncateOffset(AddressSpace space, long offset) {
|
||||
default long quantizeOffset(AddressSpace space, long offset) {
|
||||
return space.truncateAddressableWordOffset(offset) * space.getAddressableUnitSize();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,20 +15,24 @@
|
||||
*/
|
||||
package ghidra.pcode.exec;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.lang.reflect.TypeVariable;
|
||||
import java.util.*;
|
||||
|
||||
import org.apache.commons.lang3.reflect.TypeUtils;
|
||||
|
||||
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
|
||||
import ghidra.pcodeCPort.slghsymbol.UserOpSymbol;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
import ghidra.sleigh.grammar.Location;
|
||||
|
||||
/**
|
||||
* A "library" of p-code userops available to a p-code executor.
|
||||
* A "library" of p-code userops available to a p-code executor
|
||||
*
|
||||
* <p>
|
||||
* The library can provide definitions of p-code userops already declared by the executor's language
|
||||
* as well as completely new userops accessible to SLEIGH/p-code compiled for the executor. The
|
||||
* recommended way to implement a library is to extend {@link AnnotatedPcodeUseropLibrary}.
|
||||
* as well as completely new userops accessible to Sleigh/p-code later compiled for the executor.
|
||||
* The recommended way to implement a library is to extend {@link AnnotatedPcodeUseropLibrary}.
|
||||
*
|
||||
* @param <T> the type of values accepted by the p-code userops.
|
||||
*/
|
||||
@@ -45,6 +49,28 @@ public interface PcodeUseropLibrary<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the type {@code T} for the given class
|
||||
*
|
||||
* <p>
|
||||
* If the class does not implement {@link PcodeUseropLibrary}, this returns null. If it does,
|
||||
* but no arguments are given (i.e., it implements the raw type), this return {@link Object}.
|
||||
*
|
||||
* @param cls the class
|
||||
* @return the type, or null
|
||||
*/
|
||||
static Type getOperandType(Class<?> cls) {
|
||||
Map<TypeVariable<?>, Type> args =
|
||||
TypeUtils.getTypeArguments(cls, PcodeUseropLibrary.class);
|
||||
if (args == null) {
|
||||
return null;
|
||||
}
|
||||
if (args.isEmpty()) {
|
||||
return Object.class;
|
||||
}
|
||||
return args.get(PcodeUseropLibrary.class.getTypeParameters()[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* The empty userop library.
|
||||
*
|
||||
|
||||
+18
-5
@@ -19,6 +19,7 @@ import static org.junit.Assume.assumeFalse;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.event.*;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Function;
|
||||
|
||||
@@ -26,19 +27,30 @@ import javax.swing.JFrame;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.table.TableColumn;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.*;
|
||||
|
||||
import com.google.common.collect.Range;
|
||||
|
||||
import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn;
|
||||
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
|
||||
import ghidra.test.TestEnv;
|
||||
import ghidra.util.SystemUtilities;
|
||||
import ghidra.util.table.GhidraTable;
|
||||
|
||||
public class DemoRangeCellRendererTest {
|
||||
public class DemoRangeCellRendererTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
private TestEnv env;
|
||||
|
||||
@Before
|
||||
public void checkNotBatch() {
|
||||
public void setupDemo() throws IOException {
|
||||
assumeFalse(SystemUtilities.isInTestingBatchMode());
|
||||
env = new TestEnv();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDownDemo() {
|
||||
if (env != null) {
|
||||
env.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
protected static class MyRow {
|
||||
@@ -105,11 +117,12 @@ public class DemoRangeCellRendererTest {
|
||||
|
||||
@Test
|
||||
public void testDemoRangeCellRenderer() throws Throwable {
|
||||
new TestEnv().getTool();
|
||||
JFrame window = new JFrame();
|
||||
window.setLayout(new BorderLayout());
|
||||
|
||||
DefaultEnumeratedColumnTableModel<MyColumns, MyRow> model =
|
||||
new DefaultEnumeratedColumnTableModel<>("People", MyColumns.class);
|
||||
new DefaultEnumeratedColumnTableModel<>(env.getTool(), "People", MyColumns.class);
|
||||
GhidraTable table = new GhidraTable(model);
|
||||
GTableFilterPanel<MyRow> filterPanel = new GTableFilterPanel<>(table, model);
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user