Merge remote-tracking branch 'origin/GP-5196_ghidragon_address_expression_and_symbol_names_in_address_input--SQUASHED'

This commit is contained in:
Ryan Kurtz 2025-01-02 03:45:41 -05:00
commit 4475cedf18
61 changed files with 2902 additions and 1445 deletions

View file

@ -4,9 +4,9 @@
* 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.
@ -15,656 +15,270 @@
*/
package ghidra.program.util;
import java.util.*;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.List;
import generic.expressions.*;
import ghidra.app.util.NamespaceUtils;
import ghidra.app.util.SymbolPath;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program;
import ghidra.program.model.symbol.Symbol;
import ghidra.program.model.symbol.SymbolTable;
import ghidra.util.NumericUtilities;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.model.symbol.*;
/**
* The <CODE>AddressEvaluator</CODE> class provides a way to evaluate a string
* that represents an address and resolve it to an address for a particular program.
* Class for evaluating expressions as an Address. See
* {@link ExpressionOperator} for the full list of supported operators. All values are interpreted
* as longs or symbols that resolve to an address.
* <P>
* ExpressionEvaluators can operate in either decimal or hex mode. If in hex mode, all numbers are
* assumed to be hexadecimal values. In decimal mode, numbers are assumed to be decimal values, but
* hexadecimal values can still be specified by prefixing them with "0x".
* <P>
* There are also two convenience static methods that can be called to evaluate address expressions.
* These methods will either return an Address as the result or null if there was an error
* evaluating the expression. To get error messages related to parsing the expression, instantiate
* an AddressEvaluator and call {@link #parseAsAddress(String)} which will throw a
* {@link ExpressionException} when the expression can't be evaluated.
*/
public class AddressEvaluator {
public class AddressEvaluator extends ExpressionEvaluator {
private static final String TOKEN_CHARS = "+-*/()<>|^&~ =";
private Reference<Program> programReference;
private AddressFactory addressFactory;
private AddressSpace preferredSpace;
/**
* Gets a legitimate address for the specified program as indicated by the string.
* Gets a valid address for the specified program as indicated by the input expression.
* @param p the program to use for determining the address.
* @param baseAddr the base address to use for relative addressing.
* @param s string representation of the address desired.
* @param inputExpression string representation of the address desired.
* @return the address. Otherwise, return null if the string fails to evaluate
* to a unique legitimate address.
*/
public static Address evaluate(Program p, Address baseAddr, String s) {
AddressFactory af = p.getAddressFactory();
SymbolTable st = p.getSymbolTable();
List<Object> list = new ArrayList<Object>();
if (baseAddr != null) {
list.add(baseAddr);
}
if (!parseToList(s, af, st, list)) {
return null;
}
Object obj = eval(list);
if (obj instanceof Address) {
return (Address) obj;
}
else if (obj instanceof Long) {
try {
return af.getDefaultAddressSpace().getAddress(((Long) obj).longValue(), true);
}
catch (Exception e) {
// ignore
}
}
return null;
}
public static Long evaluateToLong(String s) {
List<Object> list = new ArrayList<Object>();
if (!parseToList(s, null, null, list)) {
return null;
}
Object obj = eval(list);
if (obj instanceof Address) {
return ((Address) obj).getOffset();
}
else if (obj instanceof Long) {
return (Long) obj;
}
return null;
}
protected static boolean parseToList(String s, AddressFactory af, SymbolTable st,
List<Object> list) {
StringTokenizer parser = new StringTokenizer(s, TOKEN_CHARS, true);
String lookahead = null;
while (lookahead != null || parser.hasMoreTokens()) {
String tok = null;
if (lookahead != null) {
tok = lookahead;
lookahead = null;
}
else {
tok = parser.nextToken();
}
if (tok.equals(" ")) {
continue;
}
// = must be followed by =, others can be followed
if ("=!<>|&".contains(tok)) {
lookahead = parser.nextToken();
tok = checkDoubleToken(tok, lookahead);
// if tok is now longer, consumed lookahead
if (tok.length() > 1) {
lookahead = null;
}
}
Object obj = Operator.getOperator(tok);
if (obj == null) {
obj = getValueObject(st, af, tok);
}
if (obj == null) {
return false;
}
list.add(obj);
}
return true;
}
private static String checkDoubleToken(String tok, String lookahead) {
switch (tok) {
case "=":
if (lookahead.equals("=")) {
return "==";
}
break;
case "<":
if (lookahead.equals("=")) {
return "<=";
}
if (lookahead.equals("<")) {
return "<<";
}
break;
case ">":
if (lookahead.equals("=")) {
return ">=";
}
if (lookahead.equals(">")) {
return ">>";
}
break;
case "!":
if (lookahead.equals("=")) {
return "!=";
}
break;
case "|":
if (lookahead.equals("|")) {
return "||";
}
break;
case "&":
if (lookahead.equals("&")) {
return "&&";
}
break;
}
return tok;
public static Address evaluate(Program p, String inputExpression) {
return evaluate(p, null, inputExpression);
}
/**
* Gets a legitimate address for the specified program as indicated by the string.
* Gets a valid address for the specified program as indicated by the input expression.
* @param p the program to use for determining the address.
* @param s string representation of the address desired.
* @param baseAddr the base address to use for relative addressing.
* @param inputExpression string representation of the address desired.
* @return the address. Otherwise, return null if the string fails to evaluate
* to a legitimate address.
* to a unique legitimate address.
*/
public static Address evaluate(Program p, String s) {
return evaluate(p, null, s);
public static Address evaluate(Program p, Address baseAddr, String inputExpression) {
AddressEvaluator evaluator = new AddressEvaluator(p, true);
try {
return evaluator.parseAsAddress(inputExpression);
}
catch (ExpressionException e) {
return null;
}
}
/**
* Utility method for creating an Address object from a byte array. The Address object may or may not
* be a legitimate Address in the program's address space. This method is meant to provide a way of
* creating an Address object from a sequence of bytes that can be used for additional tests and
* comparisons.
*
* @param p - program being analyzed.
* @param addrBytes - byte array to use containing the values the address will be constructed from.
* @return - Address object constructed from the addrBytes array. Returns null if the program is null,
* addrBytes is null, or the length of addrBytes does not match the default Pointer size or does not contain
* a valid offset.
*
* Constructs an AddressEvalutor for the given program and in the specified hex/decimal mode.
* @param program the program to use to evaluate expressions into valid addresses.
* @param assumeHex if true, all numeric values are assumed to be hexadecimal numbers.
*/
public static Address evaluate(Program p, byte[] addrBytes) {
boolean isBigEndian = p.getMemory().isBigEndian();
int ptrSize = p.getDefaultPointerSize();
int index = 0;
long offset = 0;
// Make sure correct # of bytes were passed
if (addrBytes == null || addrBytes.length != ptrSize) {
return null;
}
/*
* Make sure we account for endianness of the program.
* Computing the number of bits to shift the current byte value
* is different for Little vs. Big Endian. Need to multiply by
* 8 to shift in 1-byte increments.
*/
if (isBigEndian) {
index = 0;
while (index < addrBytes.length) {
offset += (addrBytes[index] & 0xff) << ((addrBytes.length - index - 1) * 8);
index++;
}
}
else {
// Program is LittleEndian
index = addrBytes.length - 1;
while (index >= 0) {
offset += ((addrBytes[index] & 0xff) << (index * 8));
index--;
}
}
AddressSpace space = p.getAddressFactory().getDefaultAddressSpace();
try {
return space.getAddress(offset, true);
}
catch (AddressOutOfBoundsException e) {
return null;
}
public AddressEvaluator(Program program, boolean assumeHex) {
this(program, null, assumeHex);
}
private static Object getValueObject(SymbolTable st, AddressFactory af, String tok) {
/**
* Constructs an AdddressEvaluator without a full program. This version will not be able to
* evaluate symbol or memory block names. This is mostly for backwards compatibility.
* @param factory the address factory for creating addresses
* @param assumeHex if true, all numeric values are assumed to be hexadecimal numbers.
*/
public AddressEvaluator(AddressFactory factory, boolean assumeHex) {
this(factory, null, assumeHex);
}
if (st == null || af == null) {
return getValueObject(tok);
}
/**
* Constructs an AddressEvalutor for the given program and in the specified hex/decimal mode.
* @param program the program to use to evaluate expressions into valid addresses.
* @param defaultSpace The address space to use when converting long values into addresses. If
* this value is null, then the default address space will be used.
* @param assumeHex if true, all numeric values are assumed to be hexadecimal numbers.
*/
public AddressEvaluator(Program program, AddressSpace defaultSpace, boolean assumeHex) {
this(program.getAddressFactory(), defaultSpace, assumeHex);
this.programReference = new WeakReference<>(program);
}
try {
return NumericUtilities.parseHexLong(tok);
private AddressEvaluator(AddressFactory factory, AddressSpace defaultSpace, boolean assumeHex) {
super(assumeHex);
this.addressFactory = factory;
this.preferredSpace = defaultSpace;
}
/**
* Evaluates the given input expression as an address.
* @param input the expression to evaluate
* @return the Address the expression evaluates to
* @throws ExpressionException if the input expression can't be evaluated to a valid, unique
* address.
*/
public Address parseAsAddress(String input) throws ExpressionException {
return this.parseAsRelativeAddress(input, null);
}
/**
* Evaluates the given input expression as a relative offset that will be added to the given
* base address.
* @param input the expression to evaluate as an offset
* @param baseAddress the base address the evaluted expression will be added to to get the
* resulting address.
* @return the Address after the evaluated offset is added to the given base address.
* @throws ExpressionException if the input expression can't be evaluated to a valid, unique
* address.
*/
public Address parseAsRelativeAddress(String input, Address baseAddress)
throws ExpressionException {
ExpressionValue expressionValue = baseAddress == null ? parse(input)
: parse(input, new AddressExpressionValue(baseAddress));
if (expressionValue instanceof AddressExpressionValue addressValue) {
return validateAddressSpace(addressValue.getAddress());
}
catch (NumberFormatException e) {
// ignore
if (expressionValue instanceof LongExpressionValue longValue) {
long offset = longValue.getLongValue();
AddressSpace space = getAddressSpace();
try {
return space.getAddressInThisSpaceOnly(offset);
}
catch (AddressOutOfBoundsException e) {
throw new ExpressionException(e.getMessage());
}
}
Address address = af.getAddress(tok);
if (address != null) {
throw new ExpressionException("Expression did not evalute to a long! Got a " +
expressionValue.getClass() + " instead.");
}
/**
* Returns the {@link AddressFactory} being used by this address evaluator
* @return the {@link AddressFactory} being used by this address evaluator
*/
public AddressFactory getAddressFactory() {
return addressFactory;
}
/**
* Sets the {@link AddressSpace} to be used to convert long values into addresses.
* @param space the address space to convert long values into addresses
*/
public void setPreferredAddressSpace(AddressSpace space) {
this.preferredSpace = space;
}
// checks if the given address's address space is compatible with the preferred address space
private Address validateAddressSpace(Address address) throws ExpressionException {
if (preferredSpace == null) {
return address;
}
List<Symbol> globalSymbols = st.getLabelOrFunctionSymbols(tok, null);
if (globalSymbols.size() == 1) {
return globalSymbols.get(0).getAddress();
AddressSpace space = address.getAddressSpace();
if (space.equals(preferredSpace)) {
return address;
}
return getValueObject(tok);
if (isOverlayRelated(space, preferredSpace)) {
return preferredSpace.getAddress(address.getOffset());
}
throw new ExpressionException("Selected address space is not compatible with expression!");
}
private static Object getValueObject(String strValue) {
try {
int start = 0;
int radix = 10;
if (strValue.indexOf("0x") == 0) {
start = 2;
radix = 16;
}
strValue = strValue.toLowerCase();
if (strValue.endsWith("ull") || strValue.endsWith("llu")) {
strValue = strValue.substring(start, strValue.length() - 3);
}
else if (strValue.endsWith("ul") || strValue.endsWith("lu") || strValue.endsWith("ll")) {
strValue = strValue.substring(start, strValue.length() - 2);
}
else if (strValue.endsWith("l") || strValue.endsWith("u")) {
strValue = strValue.substring(start, strValue.length() - 1);
}
else {
strValue = strValue.substring(start);
}
return (radix == 10) ? NumericUtilities.parseLong(strValue)
: NumericUtilities.parseHexLong(strValue);
}
catch (RuntimeException e) {
// ignore
}
return null;
private boolean isOverlayRelated(AddressSpace space1, AddressSpace space2) {
AddressSpace base1 = getBaseSpace(space1);
AddressSpace base2 = getBaseSpace(space2);
return base1.equals(base2);
}
private static Object eval(List<Object> list) {
// first evaluate any grouped expressions
boolean done = false;
while (!done) {
done = true;
for (int i = 0; i < list.size(); i++) {
if (list.get(i) == Operator.LEFT_PAREN) {
done = false;
int end = findMatchingParen(list, i);
if (end < 0) {
return null;
}
Object obj = eval(list.subList(i + 1, end));
if (obj == null) {
return null;
}
list.subList(i, i + 2).clear();
list.set(i, obj);
}
}
private AddressSpace getBaseSpace(AddressSpace space) {
if (space instanceof OverlayAddressSpace overlaySpace) {
return overlaySpace.getOverlayedSpace();
}
//check for leading Minus
if (list.size() > 1 && list.get(0) == Operator.MINUS) {
Object obj = list.get(1);
if (obj instanceof Long) {
obj = -((Long) obj).longValue();
list.remove(0);
list.set(0, obj);
}
}
//check for leading ~
if (list.size() > 1 && list.get(0) == Operator.NOT) {
Object obj = list.get(1);
if (obj instanceof Long) {
obj = ~((Long) obj).longValue();
list.remove(0);
list.set(0, obj);
}
}
//check for trailing leading ~
if (list.size() > 3 && list.get(2) == Operator.NOT) {
Object obj = list.get(3);
if (obj instanceof Long) {
obj = ~((Long) obj).longValue();
list.remove(2);
list.set(2, obj);
}
}
// evaluate all SHIFT because they have precedence
if (!evaluateOperator(list, Operator.RIGHTSHIFT, Operator.LEFTSHIFT)) {
return null;
}
// evaluate all TIMES because they have precedence
if (!evaluateOperator(list, Operator.TIMES, Operator.DIVIDE)) {
return null;
}
// evaluate Plus and Minus, same precedence, but do plus then minus
if (!evaluateOperator(list, Operator.PLUS, Operator.MINUS)) {
return null;
}
// evaluate & ^ |
if (!evaluateOperator(list, Operator.AND, null)) {
return null;
}
if (!evaluateOperator(list, Operator.XOR, null)) {
return null;
}
if (!evaluateOperator(list, Operator.OR, null)) {
return null;
}
if (!evaluateOperator(list, Operator.EQUALS, Operator.NOTEQUALS)) {
return null;
}
if (!evaluateOperator(list, Operator.LESS, Operator.GREATER)) {
return null;
}
if (!evaluateOperator(list, Operator.LESSEQUALS, Operator.GREATEREQUALS)) {
return null;
}
if (!evaluateOperator(list, Operator.LOG_AND, null)) {
return null;
}
if (!evaluateOperator(list, Operator.LOG_OR, null)) {
return null;
}
if (list.size() != 1) {
return null;
}
return list.get(0);
return space;
}
private static boolean evaluateOperator(List<Object> list, Operator op1, Operator op2) {
boolean done;
done = false;
while (!done) {
done = true;
for (int i = 0; i < list.size(); i++) {
Object obj = list.get(i);
if (obj == op1 || obj == op2) {
done = false;
if (i == 0 || i == list.size() - 1) {
return false;
}
Object value = computeValue(list.get(i - 1), (Operator) obj, list.get(i + 1));
if (value == null) {
return false;
}
list.subList(i, i + 2).clear();
list.set(i - 1, value);
}
}
private AddressSpace getAddressSpace() {
if (preferredSpace != null) {
return preferredSpace;
}
return true;
}
private static Object computeValue(Object v1, Operator op, Object v2) {
if (op == Operator.TIMES) {
if ((v1 instanceof Long) && (v2 instanceof Long)) {
return ((Long) v1).longValue() * ((Long) v2).longValue();
}
}
if (op == Operator.DIVIDE) {
if ((v1 instanceof Long) && (v2 instanceof Long)) {
return ((Long) v1).longValue() / ((Long) v2).longValue();
}
}
else if (op == Operator.AND) {
if ((v1 instanceof Long) && (v2 instanceof Long)) {
return ((Long) v1).longValue() & ((Long) v2).longValue();
}
}
else if (op == Operator.XOR) {
if ((v1 instanceof Long) && (v2 instanceof Long)) {
return ((Long) v1).longValue() ^ ((Long) v2).longValue();
}
}
else if (op == Operator.OR) {
if ((v1 instanceof Long) && (v2 instanceof Long)) {
return ((Long) v1).longValue() | ((Long) v2).longValue();
}
}
else if (op == Operator.LEFTSHIFT) {
if ((v1 instanceof Long) && (v2 instanceof Long)) {
return ((Long) v1).longValue() << ((Long) v2).longValue();
}
}
else if (op == Operator.RIGHTSHIFT) {
if ((v1 instanceof Long) && (v2 instanceof Long)) {
return ((Long) v1).longValue() >> ((Long) v2).longValue();
}
}
else if (op == Operator.PLUS) {
if ((v1 instanceof Long) && (v2 instanceof Long)) {
return ((Long) v1).longValue() + ((Long) v2).longValue();
}
else if ((v1 instanceof Address) && (v2 instanceof Long)) {
return ((Address) v1).addWrap(((Long) v2).longValue());
}
else if ((v1 instanceof Long) && (v2 instanceof Address)) {
return ((Address) v2).addWrap(((Long) v1).longValue());
}
}
else if (op == Operator.NOT) {
if (v2 instanceof Long) {
return ~(((Long) v2).longValue());
}
else if (v2 instanceof Address) {
return ((Address) v2).getNewAddress(~(((Long) v2).longValue()));
}
}
else if (op == Operator.MINUS) {
if ((v1 instanceof Long) && (v2 instanceof Long)) {
return ((Long) v1).longValue() - ((Long) v2).longValue();
}
else if ((v1 instanceof Address) && (v2 instanceof Long)) {
return ((Address) v1).subtractWrap(((Long) v2).longValue());
}
else if ((v1 instanceof Address) && (v2 instanceof Address)) {
return ((Address) v1).subtract((Address) v2);
}
}
else if (op == Operator.EQUALS) {
Long diff = getDifference(v1, v2);
if (diff != null) {
return diff == 0L ? 1L : 0L;
}
}
else if (op == Operator.NOTEQUALS) {
Long diff = getDifference(v1, v2);
if (diff != null) {
return diff != 0L ? 1L : 0L;
}
}
else if (op == Operator.LESSEQUALS) {
Long diff = getDifference(v1, v2);
if (diff != null) {
return diff <= 0L ? 1L : 0L;
}
}
else if (op == Operator.GREATEREQUALS) {
Long diff = getDifference(v1, v2);
if (diff != null) {
return diff >= 0L ? 1L : 0L;
}
}
else if (op == Operator.LESS) {
Long diff = getDifference(v1, v2);
if (diff != null) {
return diff < 0L ? 1L : 0L;
}
}
else if (op == Operator.GREATER) {
Long diff = getDifference(v1, v2);
if (diff != null) {
return diff > 0L ? 1L : 0L;
}
}
else if (op == Operator.LOG_AND) {
if ((v1 instanceof Long) && (v2 instanceof Long)) {
boolean test = (((Long) v1).longValue()) != 0 && (((Long) v2).longValue()) != 0;
return test ? 1L : 0L;
}
}
else if (op == Operator.LOG_OR) {
if ((v1 instanceof Long) && (v2 instanceof Long)) {
boolean test = (((Long) v1).longValue()) != 0 || (((Long) v2).longValue()) != 0;
return test ? 1L : 0L;
}
}
return null;
}
private static Long getDifference(Object v1, Object v2) {
if ((v1 instanceof Address) && (v2 instanceof Long)) {
return ((Address) v1).subtractWrap(((Long) v2).longValue()).getOffset();
}
else if ((v1 instanceof Address) && (v2 instanceof Address)) {
return ((Address) v1).subtract((Address) v2);
}
else if ((v1 instanceof Long) && (v2 instanceof Long)) {
return ((Long) v1).longValue() - ((Long) v2).longValue();
}
return null;
}
private static int findMatchingParen(List<Object> list, int index) {
int depth = 1;
for (int j = index + 1; j < list.size(); j++) {
Object obj = list.get(j);
if (obj == Operator.LEFT_PAREN) {
depth++;
}
else if (obj == Operator.RIGHT_PAREN) {
if (--depth == 0) {
return j;
}
}
}
return -1;
}
}
class Operator {
static Operator PLUS = new Operator("+");
static Operator MINUS = new Operator("-");
static Operator TIMES = new Operator("*");
static Operator DIVIDE = new Operator("/");
static Operator AND = new Operator("&");
static Operator OR = new Operator("|");
static Operator NOT = new Operator("~");
static Operator XOR = new Operator("^");
static Operator LEFTSHIFT = new Operator("<<");
static Operator RIGHTSHIFT = new Operator(">>");
static Operator LEFT_PAREN = new Operator("(");
static Operator RIGHT_PAREN = new Operator(")");
static Operator LOG_OR = new Operator("||");
static Operator LOG_AND = new Operator("&&");
static Operator EQUALS = new Operator("==");
static Operator NOTEQUALS = new Operator("!=");
static Operator LESS = new Operator("<");
static Operator GREATER = new Operator(">");
static Operator LESSEQUALS = new Operator("<=");
static Operator GREATEREQUALS = new Operator(">=");
final String name;
private Operator(String name) {
this.name = name;
return addressFactory.getDefaultAddressSpace();
}
@Override
public String toString() {
return name;
protected ExpressionValue evaluateSymbol(String input) {
Address address = addressFactory.getAddress(input);
if (address != null) {
return new AddressExpressionValue(address);
}
Program program = getProgram();
if (program != null) {
return getAddressForProgram(program, input);
}
return null;
}
/**
* Gets the static object implementation of an operator.
* @param tok token string for the operator
* @return the static operator object.
*/
public static Operator getOperator(String tok) {
if (tok.equals("+")) {
return PLUS;
private ExpressionValue getAddressForProgram(Program program, String input) {
Address address = getAddressForSymbol(program, input);
if (address == null) {
address = getAddressFromMemoryMap(program, input);
}
if (tok.equals("&")) {
return AND;
return address == null ? null : new AddressExpressionValue(address);
}
private Address getAddressFromMemoryMap(Program program, String input) {
Memory memory = program.getMemory();
MemoryBlock block = memory.getBlock(input);
if (block != null) {
return block.getStart();
}
if (tok.equals("|")) {
return OR;
return null;
}
private Address getAddressForSymbol(Program program, String input) {
SymbolPath symbolPath = new SymbolPath(input);
String symbolName = symbolPath.getName();
SymbolPath parent = symbolPath.getParent();
Namespace namespace = null;
if (parent != null) {
namespace = getParentNamespace(program, parent);
if (namespace == null) {
// there was a namespace specified, but not uniquely found, so can't resolve.
return null;
}
}
if (tok.equals("^")) {
return XOR;
SymbolTable symbolTable = program.getSymbolTable();
List<Symbol> symbols = symbolTable.getLabelOrFunctionSymbols(symbolName, namespace);
if (symbols.size() == 1) {
return symbols.get(0).getAddress();
}
else if (tok.equals("-")) {
return MINUS;
return null;
}
private Namespace getParentNamespace(Program program, SymbolPath path) {
if (path == null) {
return null;
}
else if (tok.equals("~")) {
return NOT;
List<Namespace> spaces = NamespaceUtils.getNamespaceByPath(program, null, path.getPath());
if (spaces.size() == 1) {
return spaces.get(0);
}
else if (tok.equals("*")) {
return TIMES;
}
else if (tok.equals("/")) {
return DIVIDE;
}
else if (tok.equals(")")) {
return RIGHT_PAREN;
}
else if (tok.equals("(")) {
return LEFT_PAREN;
}
else if (tok.equals("<<")) {
return LEFTSHIFT;
}
else if (tok.equals(">>")) {
return RIGHTSHIFT;
}
else if (tok.equals("==")) {
return EQUALS;
}
else if (tok.equals("!=")) {
return NOTEQUALS;
}
else if (tok.equals("<")) {
return LESS;
}
else if (tok.equals(">")) {
return GREATER;
}
else if (tok.equals("<=")) {
return LESSEQUALS;
}
else if (tok.equals(">=")) {
return GREATEREQUALS;
}
else if (tok.equals("||")) {
return LOG_OR;
}
else if (tok.equals("&&")) {
return LOG_AND;
return null;
}
private Program getProgram() {
if (programReference != null) {
return programReference.get();
}
return null;
}

View file

@ -0,0 +1,167 @@
/* ###
* 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.program.util;
import generic.expressions.*;
import ghidra.program.model.address.Address;
/**
* Address operand values. See {@link ExpressionValue}. Defines supported operators and other
* operands for expression values that are addresses.
*/
public class AddressExpressionValue implements ExpressionValue {
private Address value;
public AddressExpressionValue(Address address) {
this.value = address;
}
@Override
public ExpressionValue applyUnaryOperator(ExpressionOperator operator) throws ExpressionException {
long offset = value.getOffset();
switch (operator) {
case BITWISE_NOT:
return addressExpressionOf(~offset);
case UNARY_MINUS:
return addressExpressionOf(-offset);
case UNARY_PLUS:
return this;
default:
throw new ExpressionException(
"Unary Operator " + operator + " not supported by Long values!");
}
}
private AddressExpressionValue addressExpressionOf(long offset) {
Address address = value.getNewAddress(offset);
return new AddressExpressionValue(addressOf(offset));
}
private AddressExpressionValue addressExpressionOf(Address address) {
return new AddressExpressionValue(address);
}
private Address addressOf(long offset) {
return value.getNewAddress(offset);
}
@Override
public ExpressionValue applyBinaryOperator(ExpressionOperator operator, ExpressionValue operand)
throws ExpressionException {
if (operand instanceof LongExpressionValue longOperand) {
return applyBinaryOperator(operator, longOperand);
}
if (operand instanceof AddressExpressionValue addressOperand) {
return applyBinaryOperator(operator, addressOperand);
}
throw new ExpressionException("Unsupported operand type for Long: " + value);
}
private ExpressionValue applyBinaryOperator(ExpressionOperator operator,
LongExpressionValue expressionValue) throws ExpressionException {
long otherValue = expressionValue.getLongValue();
long offset = value.getOffset();
int compareResult = Long.compareUnsigned(offset, otherValue);
switch (operator) {
case BITWISE_AND:
return addressExpressionOf(offset & otherValue);
case BITWISE_OR:
return addressExpressionOf(offset | otherValue);
case BITWISE_XOR:
return addressExpressionOf(offset ^ otherValue);
case DIVIDE:
return addressExpressionOf(offset / otherValue);
case SUBTRACT:
return addressExpressionOf(value.subtract(otherValue));
case ADD:
return addressExpressionOf(value.add(otherValue));
case MULTIPLY:
return addressExpressionOf(offset * otherValue);
case SHIFT_LEFT:
return addressExpressionOf(offset << otherValue);
case SHIFT_RIGHT:
return addressExpressionOf(offset >> otherValue);
case EQUALS:
return booleanExpression(compareResult == 0);
case GREATER_THAN:
return booleanExpression(compareResult > 0);
case LESS_THAN:
return booleanExpression(compareResult < 0);
case GREATER_THAN_OR_EQUAL:
return booleanExpression(compareResult >= 0);
case LESS_THAN_OR_EQUAL:
return booleanExpression(compareResult <= 0);
default:
throw new ExpressionException(
"Binary Operator \"" + operator +
"\" with Long operands not supported by Address values!");
}
}
private ExpressionValue booleanExpression(boolean b) {
return new LongExpressionValue(b ? 1 : 0);
}
private ExpressionValue applyBinaryOperator(ExpressionOperator operator,
AddressExpressionValue expressionValue) throws ExpressionException {
Address otherValue = expressionValue.getAddress();
long otherValueOffset = otherValue.getOffset();
long offset = value.getOffset();
int compareResult = value.compareTo(otherValue);
switch (operator) {
case BITWISE_AND:
return new LongExpressionValue(offset & otherValueOffset);
case BITWISE_OR:
return new LongExpressionValue(offset | otherValueOffset);
case BITWISE_XOR:
return new LongExpressionValue(offset ^ otherValueOffset);
case SUBTRACT:
return new LongExpressionValue(value.subtract(otherValue));
case ADD:
return new LongExpressionValue(offset + otherValueOffset);
case EQUALS:
return booleanExpression(compareResult == 0);
case GREATER_THAN:
return booleanExpression(compareResult > 0);
case LESS_THAN:
return booleanExpression(compareResult < 0);
case GREATER_THAN_OR_EQUAL:
return booleanExpression(compareResult >= 0);
case LESS_THAN_OR_EQUAL:
return booleanExpression(compareResult <= 0);
default:
throw new ExpressionException(
"Binary Operator \"" + operator +
"\" with Long operands not supported by Address values!");
}
}
public Address getAddress() {
return value;
}
@Override
public String toString() {
return value.toString();
}
}