mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 02:39:44 +02:00
Merge remote-tracking branch 'origin/GP-0_Dan_moveAppender'
This commit is contained in:
commit
0d0c1660f4
4 changed files with 0 additions and 0 deletions
|
@ -0,0 +1,258 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.util.pcode;
|
||||
|
||||
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.program.model.lang.Register;
|
||||
import ghidra.program.model.pcode.PcodeOp;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
/**
|
||||
* A base implementation of {@link Appender} suitable for most cases.
|
||||
*
|
||||
* @param <T> the type of output of the formatter
|
||||
*/
|
||||
public abstract class AbstractAppender<T> implements Appender<T> {
|
||||
protected final Language language;
|
||||
protected final boolean indent;
|
||||
|
||||
/**
|
||||
* Create a new appender.
|
||||
*
|
||||
* @param language the language of the p-code ops to format
|
||||
* @param indent whether or not to indent
|
||||
*/
|
||||
public AbstractAppender(Language language, boolean indent) {
|
||||
this.language = language;
|
||||
this.indent = indent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Language getLanguage() {
|
||||
return language;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendAddressWordOffcut(long wordOffset, long offcut) {
|
||||
appendString(stringifyWordOffcut(wordOffset, offcut));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendCharacter(char c) {
|
||||
if (c == '=') {
|
||||
appendString(" = "); // HACK
|
||||
}
|
||||
else {
|
||||
appendString(Character.toString(c));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendIndent() {
|
||||
if (indent) {
|
||||
appendCharacter(' ');
|
||||
appendCharacter(' ');
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendLabel(String label) {
|
||||
appendString(label);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendLineLabelRef(long label) {
|
||||
appendString(stringifyLineLabel(label));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendMnemonic(int opcode) {
|
||||
appendString(stringifyOpMnemonic(opcode));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendRawVarnode(AddressSpace space, long offset, long size) {
|
||||
appendString(stringifyRawVarnode(space, offset, size));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendRegister(Register register) {
|
||||
appendString(stringifyRegister(register));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendScalar(long value) {
|
||||
appendString(stringifyScalarValue(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendSpace(AddressSpace space) {
|
||||
appendString(stringifySpace(space));
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a plain string.
|
||||
*
|
||||
* <p>
|
||||
* By default, all append method delegate to this, so either it must be implemented, or every
|
||||
* other append method must be overridden to avoid ever invoking this method. The default
|
||||
* implementation throws an assertion error.
|
||||
*
|
||||
* @param string the string to append
|
||||
*/
|
||||
protected void appendString(String string) {
|
||||
throw new AssertionError(
|
||||
"Either this shouldn't happen, or you should accept the string");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendUnique(long offset) {
|
||||
appendString(stringifyUnique(offset));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendUserop(int id) {
|
||||
appendString(stringifyUserop(language, id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Covert the given line label to a string as it should be conventionally displayed.
|
||||
*
|
||||
* @param label the label number
|
||||
* @return the display string, e.g., {@code <L1>}
|
||||
*/
|
||||
protected String stringifyLineLabel(long label) {
|
||||
return "<" + label + ">";
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given opcode to a string as it should be conventionally displayed.
|
||||
*
|
||||
* @param opcode the opcode
|
||||
* @return the display string, i.e., its mnemonic
|
||||
*/
|
||||
protected String stringifyOpMnemonic(int opcode) {
|
||||
return PcodeOp.getMnemonic(opcode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given varnode to its raw conventional form.
|
||||
*
|
||||
* @param space the address space
|
||||
* @param offset the offset in the space
|
||||
* @param size the size in bytes
|
||||
* @return the raw display string
|
||||
*/
|
||||
protected String stringifyRawVarnode(AddressSpace space, long offset, long size) {
|
||||
return "(" + space.getName() + ", 0x" + Long.toHexString(offset) + ", " + size + ")";
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given register to a string as it should be conventionally displayed.
|
||||
*
|
||||
* @param register the register
|
||||
* @return the display string, i.e., its name
|
||||
*/
|
||||
protected String stringifyRegister(Register register) {
|
||||
return register.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given scalar to a string as it should be conventionally displayed.
|
||||
*
|
||||
* @param value the value
|
||||
* @return the display string, i.e., its decimal value if small, or hex value is large
|
||||
*/
|
||||
protected String stringifyScalarValue(long value) {
|
||||
if (value >= -64 && value <= 64) {
|
||||
return Long.toString(value);
|
||||
}
|
||||
else {
|
||||
return "0x" + Long.toHexString(value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given address space to a string as it should be conventionally displayed.
|
||||
*
|
||||
* @param space the address space
|
||||
* @return the display string, i.e., its name
|
||||
*/
|
||||
protected String stringifySpace(AddressSpace space) {
|
||||
if (space == null) {
|
||||
return "unknown";
|
||||
}
|
||||
return space.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a given unique variable to a string as it should be conventionally displayed.
|
||||
*
|
||||
* @param offset the variable's offset
|
||||
* @return the display string, e.g., {@code $U1234}
|
||||
*/
|
||||
protected String stringifyUnique(long offset) {
|
||||
return "$U" + Long.toHexString(offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup a given userop name
|
||||
*
|
||||
* @param language the language containing the userop
|
||||
* @param id the userop id
|
||||
* @return the display string, i.e., its name, or null if it doesn't exist
|
||||
*/
|
||||
protected String stringifyUseropUnchecked(Language language, int id) {
|
||||
if (!(language instanceof SleighLanguage)) {
|
||||
throw new RuntimeException("Expected Sleigh language for CALLOTHER op");
|
||||
}
|
||||
return ((SleighLanguage) language).getUserDefinedOpName(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a given userop to a string as it should be conventionally displayed.
|
||||
*
|
||||
* @param language the langauge containing the userop
|
||||
* @param id the userop id
|
||||
* @return the display string, i.e., its name or "unknown"
|
||||
*/
|
||||
protected String stringifyUserop(Language language, int id) {
|
||||
String pseudoOp = stringifyUseropUnchecked(language, id);
|
||||
if (pseudoOp == null) {
|
||||
Msg.error(this, "Pseudo-op index not found: " + id);
|
||||
pseudoOp = "unknown";
|
||||
}
|
||||
return pseudoOp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a given word-offcut style address to a string as it should be conventionally
|
||||
* displayed.
|
||||
*
|
||||
* @param wordOffset the offset of the word in memory
|
||||
* @param offcut the byte "offcut" within the word
|
||||
* @return the display string, e.g., {@code 0x1234.1}
|
||||
*/
|
||||
protected String stringifyWordOffcut(long wordOffset, long offcut) {
|
||||
String str = "0x" + Long.toHexString(wordOffset);
|
||||
if (offcut != 0) {
|
||||
str += "." + offcut;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,412 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.util.pcode;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.app.plugin.processors.sleigh.template.*;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.program.model.lang.Register;
|
||||
import ghidra.program.model.pcode.PcodeOp;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
/**
|
||||
* An abstract p-code formatter which can take a list of p-code ops or op templates and consistently
|
||||
* format them. The general pattern is to extend this class and specify another class which extends
|
||||
* an {@link AbstractAppender}. In most cases, it is only necessary to override
|
||||
* {@link #formatOpTemplate(Appender, OpTpl)}. Otherwise, most formatting logic is implemented by
|
||||
* the appender.
|
||||
*
|
||||
* @see {@link StringPcodeFormatter} for an example
|
||||
* @see {@link AbstractAppender}
|
||||
* @param <T> the type of this formatter's output, e.g., {@link String}
|
||||
* @param <A> the type of the appender
|
||||
*/
|
||||
public abstract class AbstractPcodeFormatter<T, A extends Appender<T>>
|
||||
implements PcodeFormatter<T> {
|
||||
|
||||
/**
|
||||
* A result instructing the formatter whether or not to continue
|
||||
*/
|
||||
protected enum FormatResult {
|
||||
CONTINUE, TERMINATE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the appender for a formatting invocation
|
||||
*
|
||||
* @param language the language of the p-code to format
|
||||
* @param indent indicates whether each line should be indented to accommodate line labels
|
||||
* @return the new appender
|
||||
*/
|
||||
protected abstract A createAppender(Language language, boolean indent);
|
||||
|
||||
/**
|
||||
* Check if this formatter is configured to display raw p-code
|
||||
*
|
||||
* @return true if displaying raw, false otherwise
|
||||
*/
|
||||
protected boolean isFormatRaw() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T formatTemplates(Language language, List<OpTpl> pcodeOpTemplates) {
|
||||
boolean indent = hasLabel(pcodeOpTemplates);
|
||||
A appender = createAppender(language, indent);
|
||||
|
||||
for (OpTpl template : pcodeOpTemplates) {
|
||||
if (FormatResult.TERMINATE == formatOpTemplate(appender, template)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return appender.finish();
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a single op template
|
||||
*
|
||||
* @param appender the appender to receive the formatted text
|
||||
* @param op the template to format
|
||||
* @return instructions to continue or terminate. The loop in
|
||||
* {@link #formatTemplates(Language, List)} is terminated if this method returns
|
||||
* {@link FormatResult#TERMINATE}.
|
||||
*/
|
||||
protected FormatResult formatOpTemplate(A appender, OpTpl op) {
|
||||
int opcode = op.getOpcode();
|
||||
if (PcodeOp.PTRADD == opcode) {
|
||||
appender.appendLineLabel(op.getInput()[0].getOffset().getReal());
|
||||
return FormatResult.CONTINUE;
|
||||
}
|
||||
|
||||
appender.appendIndent();
|
||||
|
||||
if (opcode >= PcodeOp.PCODE_MAX) {
|
||||
throw new RuntimeException("Unsupported opcode encountered: " + opcode);
|
||||
}
|
||||
VarnodeTpl output = op.getOutput();
|
||||
if (output != null) {
|
||||
formatOutput(appender, opcode, output);
|
||||
appender.appendCharacter('=');
|
||||
}
|
||||
appender.appendMnemonic(opcode);
|
||||
VarnodeTpl[] inputs = op.getInput();
|
||||
for (int i = 0; i < inputs.length; i++) {
|
||||
if (i > 0) {
|
||||
appender.appendCharacter(',');
|
||||
}
|
||||
appender.appendCharacter(' ');
|
||||
if (i == 0) {
|
||||
if (!isFormatRaw()) {
|
||||
if (opcode == PcodeOp.LOAD || opcode == PcodeOp.STORE) {
|
||||
formatMemoryInput(appender, inputs[0], inputs[1]);
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
if (opcode == PcodeOp.CALLOTHER) {
|
||||
formatCallOtherName(appender, inputs[0]);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (opcode == PcodeOp.BRANCH || opcode == PcodeOp.CBRANCH) {
|
||||
if (formatLabelInput(appender, inputs[i])) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
formatInput(appender, opcode, i, inputs[i]);
|
||||
}
|
||||
return FormatResult.CONTINUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format an output varnode
|
||||
*
|
||||
* @param appender the appender to receive the formatted text
|
||||
* @param opcode the op's opcode
|
||||
* @param output the varnode to format
|
||||
*/
|
||||
protected void formatOutput(A appender, int opcode, VarnodeTpl output) {
|
||||
formatVarnode(appender, opcode, -1, output);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format an input varnode
|
||||
*
|
||||
* @param appender the appender to receive the formatted text
|
||||
* @param opcode the op's opcode
|
||||
* @param opIndex the operand's index
|
||||
* @param input the varnode to format
|
||||
*/
|
||||
protected void formatInput(A appender, int opcode, int opIndex, VarnodeTpl input) {
|
||||
formatVarnode(appender, opcode, opIndex, input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a varnode
|
||||
*
|
||||
* @param appender the appender to receive the formatted text
|
||||
* @param opcode the op's opcode
|
||||
* @param opIndex the operand's index (-1 is output, 0 is first input)
|
||||
* @param vTpl the varnode to format
|
||||
*/
|
||||
protected void formatVarnode(A appender, int opcode, int opIndex, VarnodeTpl vTpl) {
|
||||
ConstTpl space = vTpl.getSpace();
|
||||
ConstTpl offset = vTpl.getOffset();
|
||||
ConstTpl size = vTpl.getSize();
|
||||
|
||||
if (space.getType() == ConstTpl.J_CURSPACE) {
|
||||
if (offset.getType() == ConstTpl.J_START) {
|
||||
appender.appendLabel("inst_start");
|
||||
}
|
||||
else if (offset.getType() == ConstTpl.J_NEXT) {
|
||||
appender.appendLabel("inst_next");
|
||||
}
|
||||
else if (offset.getType() == ConstTpl.J_NEXT2) {
|
||||
appender.appendLabel("inst_next2");
|
||||
}
|
||||
else {
|
||||
formatAddress(appender, null, offset, size);
|
||||
}
|
||||
}
|
||||
else if (space.getType() == ConstTpl.SPACEID) {
|
||||
if (isFormatRaw() && offset.getType() == ConstTpl.REAL &&
|
||||
size.getType() == ConstTpl.REAL) {
|
||||
formatVarnodeRaw(appender, space.getSpaceId(), offset, size);
|
||||
}
|
||||
else {
|
||||
formatVarnodeNice(appender, space.getSpaceId(), offset, size);
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new RuntimeException("Unsupported space template type: " + space.getType());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a varnode in nice (non-raw) form
|
||||
*
|
||||
* @param appender the appender to receive the formatted text
|
||||
* @param space the address space of the varnode
|
||||
* @param offset the offset in the address space
|
||||
* @param size the size in bytes
|
||||
*/
|
||||
protected void formatVarnodeNice(A appender, AddressSpace space, ConstTpl offset,
|
||||
ConstTpl size) {
|
||||
if (space.isConstantSpace()) {
|
||||
formatConstant(appender, offset, size);
|
||||
}
|
||||
else if (space.isUniqueSpace()) {
|
||||
formatUnique(appender, offset, size);
|
||||
}
|
||||
else {
|
||||
formatAddress(appender, space, offset, size);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a varnode in raw form
|
||||
*
|
||||
* @param appender the appender to receive the formatted text
|
||||
* @param space the address space of the varnode
|
||||
* @param offset the offset in the address space
|
||||
* @param size the size in bytes
|
||||
*/
|
||||
protected void formatVarnodeRaw(A appender, AddressSpace space, ConstTpl offset,
|
||||
ConstTpl size) {
|
||||
appender.appendRawVarnode(space, offset.getReal(), size.getReal());
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a unique variable
|
||||
*
|
||||
* @param appender the appender to receive the formatted text
|
||||
* @param offset the offset in unique space
|
||||
* @param size the size in bytes
|
||||
*/
|
||||
protected void formatUnique(A appender, ConstTpl offset, ConstTpl size) {
|
||||
if (offset.getType() != ConstTpl.REAL) {
|
||||
throw new RuntimeException("Unsupported unique offset type: " + offset.getType());
|
||||
}
|
||||
if (size.getType() != ConstTpl.REAL) {
|
||||
throw new RuntimeException("Unsupported unique size type: " + size.getType());
|
||||
}
|
||||
appender.appendUnique(offset.getReal());
|
||||
formatSize(appender, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a memory variable
|
||||
*
|
||||
* @param appender the appender to receive the formatted text
|
||||
* @param addrSpace the address space of the variable
|
||||
* @param offset the offset in the address space
|
||||
* @param size the size in bytes
|
||||
*/
|
||||
protected void formatAddress(A appender, AddressSpace addrSpace,
|
||||
ConstTpl offset, ConstTpl size) {
|
||||
if (offset.getType() != ConstTpl.REAL) {
|
||||
throw new RuntimeException("Unsupported address offset type: " + offset.getType());
|
||||
}
|
||||
|
||||
long offsetValue = offset.getReal();
|
||||
if (addrSpace == null) {
|
||||
appender.appendCharacter('*');
|
||||
appender.appendAddressWordOffcut(offsetValue, 0);
|
||||
if (size.getType() != ConstTpl.J_CURSPACE_SIZE) {
|
||||
formatSize(appender, size);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
long sizeValue = size.getReal();
|
||||
Register reg =
|
||||
appender.getLanguage().getRegister(addrSpace.getAddress(offsetValue), (int) sizeValue);
|
||||
if (reg != null) {
|
||||
appender.appendRegister(reg);
|
||||
if (reg.getMinimumByteSize() > sizeValue) {
|
||||
appender.appendCharacter(':');
|
||||
appender.appendScalar(sizeValue);
|
||||
}
|
||||
return;
|
||||
}
|
||||
appender.appendCharacter('*');
|
||||
appender.appendCharacter('[');
|
||||
appender.appendSpace(addrSpace);
|
||||
appender.appendCharacter(']');
|
||||
|
||||
long wordOffset = offsetValue / addrSpace.getAddressableUnitSize();
|
||||
long offcut = offsetValue % addrSpace.getAddressableUnitSize();
|
||||
appender.appendAddressWordOffcut(wordOffset, offcut);
|
||||
formatSize(appender, size);
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a constant
|
||||
*
|
||||
* @param appender the appender to receive the formatted text
|
||||
* @param offset the value of the constant
|
||||
* @param size the size in bytes
|
||||
*/
|
||||
protected void formatConstant(A appender, ConstTpl offset, ConstTpl size) {
|
||||
if (offset.getType() != ConstTpl.REAL) {
|
||||
throw new RuntimeException("Unsupported constant offset type: " + offset.getType());
|
||||
}
|
||||
long value = offset.getReal();
|
||||
appender.appendScalar(value);
|
||||
formatSize(appender, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a size indicator
|
||||
*
|
||||
* @param appender the appender to receive the formatted text
|
||||
* @param size the size in bytes
|
||||
*/
|
||||
protected void formatSize(A appender, ConstTpl size) {
|
||||
if (size.getType() != ConstTpl.REAL) {
|
||||
throw new RuntimeException("Unsupported address size type: " + size.getType());
|
||||
}
|
||||
if (size.getReal() != 0) {
|
||||
appender.appendCharacter(':');
|
||||
appender.appendScalar(size.getReal());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a p-code userop name (CALLOTHER)
|
||||
*
|
||||
* @param appender the appender to receive the formatted text
|
||||
* @param input0 the constant varnode giving the userop id
|
||||
*/
|
||||
protected void formatCallOtherName(A appender, VarnodeTpl input0) {
|
||||
if (!input0.getSpace().isConstSpace() || input0.getOffset().getType() != ConstTpl.REAL) {
|
||||
throw new RuntimeException("Expected constant input[0] for CALLOTHER pcode op");
|
||||
}
|
||||
|
||||
int id = (int) input0.getOffset().getReal();
|
||||
appender.appendCharacter('"');
|
||||
appender.appendUserop(id);
|
||||
appender.appendCharacter('"');
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to format a local label (e.g., {@code instr_next})
|
||||
*
|
||||
* @param appender the appender to receive the formatted text
|
||||
* @param input0 the relative jump varnode
|
||||
* @return true if the varnode was formatted, false if not
|
||||
*/
|
||||
protected boolean formatLabelInput(A appender, VarnodeTpl input0) {
|
||||
if (input0.getSpace().isConstSpace() &&
|
||||
input0.getOffset().getType() == ConstTpl.J_RELATIVE) {
|
||||
appender.appendLineLabelRef(input0.getOffset().getReal());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the memory location for a LOAD or STORE op
|
||||
*
|
||||
* @param appender the appender to receive the formatted text
|
||||
* @param input0 the const varnode giving the address space id
|
||||
* @param input1 the varnode giving the address offset
|
||||
*/
|
||||
protected void formatMemoryInput(A appender, VarnodeTpl input0, VarnodeTpl input1) {
|
||||
if (!input0.getSpace().isConstSpace() || input0.getOffset().getType() != ConstTpl.REAL) {
|
||||
throw new RuntimeException("Expected constant input[0] for LOAD/STORE pcode op");
|
||||
}
|
||||
int id = (int) input0.getOffset().getReal();
|
||||
AddressSpace space = appender.getLanguage().getAddressFactory().getAddressSpace(id);
|
||||
if (space == null) {
|
||||
Msg.error(this, "Address space id not found: " + id);
|
||||
}
|
||||
appender.appendSpace(space);
|
||||
appender.appendCharacter('(');
|
||||
formatVarnode(appender, -1, 0, input1);
|
||||
appender.appendCharacter(')');
|
||||
}
|
||||
|
||||
private static boolean hasLabel(List<OpTpl> pcodeOpTemplates) {
|
||||
for (OpTpl op : pcodeOpTemplates) {
|
||||
if (isLineLabel(op)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given template represents a line label
|
||||
*
|
||||
* <p>
|
||||
* The {@link PcodeOp#PTRADD} op is ordinarily only use in high p-code. We reuse (read "abuse")
|
||||
* it to hold a display slot for line labels later referred to in {@link PcodeOp#BRANCH} and
|
||||
* {@link PcodeOp#CBRANCH} ops. This method checks if the given op template is one of those
|
||||
* placeholders.
|
||||
*
|
||||
* @param template the op template
|
||||
* @return true if it's a line label
|
||||
*/
|
||||
protected static boolean isLineLabel(OpTpl template) {
|
||||
// Overloaded: PTRADD is high p-code
|
||||
return template.getOpcode() == PcodeOp.PTRADD;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,146 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.util.pcode;
|
||||
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.program.model.lang.Register;
|
||||
|
||||
/**
|
||||
* An appender to receive formatted p-code ops.
|
||||
*
|
||||
* <p>
|
||||
* Using {@link AbstractAppender} is highly recommended, as it makes available methods for
|
||||
* displaying elements according to established Ghidra conventions.
|
||||
*
|
||||
* @param <T> the type of the final formatted output
|
||||
*/
|
||||
interface Appender<T> {
|
||||
/**
|
||||
* Get the language of the p-code being formatted
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Language getLanguage();
|
||||
|
||||
/**
|
||||
* Append a line label, usually meant to be on its own line
|
||||
*
|
||||
* @param label the label number
|
||||
*/
|
||||
default void appendLineLabel(long label) {
|
||||
appendLineLabelRef(label);
|
||||
}
|
||||
|
||||
/**
|
||||
* Append indentation, usually meant for the beginning of a line
|
||||
*/
|
||||
default void appendIndent() {
|
||||
appendCharacter(' ');
|
||||
appendCharacter(' ');
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a reference to the given line label
|
||||
*
|
||||
* @param label the label number
|
||||
*/
|
||||
void appendLineLabelRef(long label);
|
||||
|
||||
/**
|
||||
* Append the given opcode
|
||||
*
|
||||
* @param opcode the op's opcode
|
||||
*/
|
||||
void appendMnemonic(int opcode);
|
||||
|
||||
/**
|
||||
* Append the the given userop
|
||||
*
|
||||
* @param id the userop id
|
||||
*/
|
||||
void appendUserop(int id);
|
||||
|
||||
/**
|
||||
* Append the given varnode in raw form
|
||||
*
|
||||
* @param space the address space
|
||||
* @param offset the offset in the space
|
||||
* @param size the size in bytes
|
||||
*/
|
||||
void appendRawVarnode(AddressSpace space, long offset, long size);
|
||||
|
||||
/**
|
||||
* Append a character
|
||||
*
|
||||
* <p>
|
||||
* <b>NOTE:</b> if extra spacing is desired, esp., surrounding the equals sign, it must be
|
||||
* appended manually.
|
||||
*
|
||||
* @param c the character
|
||||
*/
|
||||
void appendCharacter(char c);
|
||||
|
||||
/**
|
||||
* Append an address in word-offcut form
|
||||
*
|
||||
* @param wordOffset the word offset
|
||||
* @param offcut the byte within the word
|
||||
*/
|
||||
void appendAddressWordOffcut(long wordOffset, long offcut);
|
||||
|
||||
/**
|
||||
* Append a local label
|
||||
*
|
||||
* @param label the label name, e.g., {@code instr_next}
|
||||
*/
|
||||
void appendLabel(String label);
|
||||
|
||||
/**
|
||||
* Append a register
|
||||
*
|
||||
* @param register the register
|
||||
*/
|
||||
void appendRegister(Register register);
|
||||
|
||||
/**
|
||||
* Append a scalar value
|
||||
*
|
||||
* @param value the value
|
||||
*/
|
||||
void appendScalar(long value);
|
||||
|
||||
/**
|
||||
* Append an address space
|
||||
*
|
||||
* @param space the space
|
||||
*/
|
||||
void appendSpace(AddressSpace space);
|
||||
|
||||
/**
|
||||
* Append a unique variable
|
||||
*
|
||||
* @param offset the offset in unique space
|
||||
*/
|
||||
void appendUnique(long offset);
|
||||
|
||||
/**
|
||||
* Finish formatting and return the final result
|
||||
*
|
||||
* @return the final result
|
||||
*/
|
||||
T finish();
|
||||
}
|
|
@ -0,0 +1,153 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.util.pcode;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.app.plugin.processors.sleigh.template.*;
|
||||
import ghidra.program.model.address.AddressFactory;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.program.model.pcode.PcodeOp;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
|
||||
public interface PcodeFormatter<T> {
|
||||
/**
|
||||
* Format the p-code ops
|
||||
*
|
||||
* @param language the language generating the p-code
|
||||
* @param pcodeOps the p-code ops
|
||||
* @return the formatted result
|
||||
*/
|
||||
default T formatOps(Language language, List<PcodeOp> pcodeOps) {
|
||||
return formatOps(language, language.getAddressFactory(), pcodeOps);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the pcode ops with a specified {@link AddressFactory}. For use when the
|
||||
* pcode ops can reference program-specific address spaces.
|
||||
*
|
||||
* @param language the language generating the p-code
|
||||
* @param addrFactory addressFactory to use when generating pcodeop templates
|
||||
* @param pcodeOps p-code ops to format
|
||||
* @return the formatted result
|
||||
*
|
||||
*/
|
||||
default T formatOps(Language language, AddressFactory addrFactory, List<PcodeOp> pcodeOps) {
|
||||
return formatTemplates(language, getPcodeOpTemplates(addrFactory, pcodeOps));
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the p-code op templates
|
||||
*
|
||||
* @param language the language generating the p-code
|
||||
* @param pcodeOpTemplates the templates
|
||||
* @return the formatted result
|
||||
*/
|
||||
T formatTemplates(Language language, List<OpTpl> pcodeOpTemplates);
|
||||
|
||||
/**
|
||||
* Convert flattened p-code ops into templates.
|
||||
*
|
||||
* @param addrFactory the language's address factory
|
||||
* @param pcodeOps the p-code ops to convert
|
||||
* @return p-code op templates
|
||||
*/
|
||||
public static List<OpTpl> getPcodeOpTemplates(AddressFactory addrFactory,
|
||||
List<PcodeOp> pcodeOps) {
|
||||
ArrayList<OpTpl> list = new ArrayList<OpTpl>();
|
||||
HashMap<Integer, Integer> labelMap = new HashMap<Integer, Integer>(); // label offset to index map
|
||||
|
||||
for (PcodeOp pcodeOp : pcodeOps) {
|
||||
|
||||
int opcode = pcodeOp.getOpcode();
|
||||
|
||||
VarnodeTpl outputTpl = null;
|
||||
Varnode v = pcodeOp.getOutput();
|
||||
if (v != null) {
|
||||
outputTpl = getVarnodeTpl(addrFactory, v);
|
||||
}
|
||||
|
||||
Varnode[] inputs = pcodeOp.getInputs();
|
||||
VarnodeTpl[] inputTpls = new VarnodeTpl[inputs.length];
|
||||
for (int i = 0; i < inputs.length; i++) {
|
||||
|
||||
Varnode input = inputs[i];
|
||||
|
||||
if (i == 0 && (opcode == PcodeOp.BRANCH || opcode == PcodeOp.CBRANCH)) {
|
||||
// Handle internal branch destination represented by constant destination
|
||||
if (input.isConstant()) {
|
||||
int labelOffset = pcodeOp.getSeqnum().getTime() + (int) input.getOffset();
|
||||
int labelIndex;
|
||||
if (labelMap.containsKey(labelOffset)) {
|
||||
labelIndex = labelMap.get(labelOffset);
|
||||
}
|
||||
else {
|
||||
labelIndex = labelMap.size();
|
||||
labelMap.put(labelOffset, labelIndex);
|
||||
}
|
||||
ConstTpl offsetTpl = new ConstTpl(ConstTpl.J_RELATIVE, labelIndex);
|
||||
ConstTpl spaceTpl = new ConstTpl(addrFactory.getConstantSpace());
|
||||
ConstTpl sizeTpl = new ConstTpl(ConstTpl.REAL, 8);
|
||||
inputTpls[i] = new VarnodeTpl(spaceTpl, offsetTpl, sizeTpl);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
inputTpls[i] = getVarnodeTpl(addrFactory, input);
|
||||
}
|
||||
|
||||
list.add(new OpTpl(opcode, outputTpl, inputTpls));
|
||||
}
|
||||
|
||||
// Insert label templates from the bottom-up
|
||||
ArrayList<Integer> offsetList = new ArrayList<Integer>(labelMap.keySet());
|
||||
Collections.sort(offsetList);
|
||||
for (int i = offsetList.size() - 1; i >= 0; i--) {
|
||||
int labelOffset = offsetList.get(i);
|
||||
int labelIndex = labelMap.get(labelOffset);
|
||||
OpTpl labelTpl = getLabelOpTemplate(addrFactory, labelIndex);
|
||||
list.add(labelOffset, labelTpl);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create label OpTpl. Uses overloaded PcodeOp.PTRADD with input[0] = labelIndex
|
||||
*
|
||||
* @param addrFactory
|
||||
* @param labelIndex
|
||||
* @return label OpTpl
|
||||
*/
|
||||
private static OpTpl getLabelOpTemplate(AddressFactory addrFactory, int labelIndex) {
|
||||
ConstTpl offsetTpl = new ConstTpl(ConstTpl.REAL, labelIndex);
|
||||
ConstTpl spaceTpl = new ConstTpl(addrFactory.getConstantSpace());
|
||||
ConstTpl sizeTpl = new ConstTpl(ConstTpl.REAL, 8);
|
||||
VarnodeTpl input = new VarnodeTpl(spaceTpl, offsetTpl, sizeTpl);
|
||||
return new OpTpl(PcodeOp.PTRADD, null, new VarnodeTpl[] { input });
|
||||
}
|
||||
|
||||
private static VarnodeTpl getVarnodeTpl(AddressFactory addrFactory, Varnode v) {
|
||||
ConstTpl offsetTpl = new ConstTpl(ConstTpl.REAL, v.getOffset());
|
||||
AddressSpace addressSpace = addrFactory.getAddressSpace(v.getSpace());
|
||||
if (addressSpace == null) {
|
||||
throw new IllegalArgumentException("Unknown varnode space ID: " + v.getSpace());
|
||||
}
|
||||
ConstTpl spaceTpl = new ConstTpl(addressSpace);
|
||||
ConstTpl sizeTpl = new ConstTpl(ConstTpl.REAL, v.getSize());
|
||||
return new VarnodeTpl(spaceTpl, offsetTpl, sizeTpl);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue