GP-7119 Fixed address evaluator to handle unsigned long values properly

This commit is contained in:
ghidragon
2026-09-02 17:56:16 -04:00
parent b33536d0ba
commit e9cb427dcd
10 changed files with 463 additions and 236 deletions

View File

@@ -106,7 +106,7 @@ public class DWARFMacroEnumCreator {
ExpressionEvaluator evaluator =
new ExpressionEvaluator(s -> macrosToValues.get(s));
long value = evaluator.parseAsLong(macroInfo.definition());
macrosToValues.put(symbolName, new LongExpressionValue(value));
macrosToValues.put(symbolName, new BigIntegerExpressionValue(value));
EnumDataType enumDT =
new EnumDataType(catPath, "define_" + symbolName, 8,
dtManager);

View File

@@ -15,11 +15,20 @@
*/
package ghidra.app.util.navigation;
import java.awt.*;
import java.awt.BorderLayout;
import java.awt.Cursor;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.LinkedList;
import java.util.List;
import javax.swing.*;
import javax.swing.BorderFactory;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JCheckBox;
import javax.swing.JPanel;
import javax.swing.border.Border;
import docking.DockingWindowManager;
@@ -30,7 +39,9 @@ import docking.widgets.checkbox.GCheckBox;
import docking.widgets.combobox.GhidraComboBox;
import ghidra.GhidraOptions;
import ghidra.app.nav.Navigatable;
import ghidra.app.services.*;
import ghidra.app.services.GoToService;
import ghidra.app.services.GoToServiceListener;
import ghidra.app.services.QueryData;
import ghidra.app.util.HelpTopics;
import ghidra.framework.options.Options;
import ghidra.framework.options.SaveState;
@@ -145,7 +156,7 @@ public class GoToAddressLabelDialog extends ReusableDialogComponentProvider
public void gotoFailed(Exception exc) {
navigatable = null;
setDialogEnabled(true);
setStatusText("ERROR: " + exc.getMessage());
setStatusText(exc.getMessage());
initializeContents();
}
@@ -191,6 +202,12 @@ public class GoToAddressLabelDialog extends ReusableDialogComponentProvider
inner.add(hyperlink, gbc);
comboBox = new GhidraComboBox<>();
comboBox.addEditorKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
clearStatusText();
}
});
comboBox.setEditable(true);
String comboName = "Go To Address or Label Text Field / Combobox";

View File

@@ -15,21 +15,28 @@
*/
package ghidra.app.util.navigation;
import java.util.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import generic.expressions.ExpressionException;
import ghidra.app.nav.Navigatable;
import ghidra.app.plugin.core.gotoquery.GoToQueryResultsTableModel;
import ghidra.app.plugin.core.navigation.NavigationOptions;
import ghidra.app.plugin.core.table.TableComponentProvider;
import ghidra.app.services.*;
import ghidra.app.services.GoToService;
import ghidra.app.services.ProgramManager;
import ghidra.app.services.QueryData;
import ghidra.app.util.SearchConstants;
import ghidra.app.util.query.TableService;
import ghidra.framework.options.Options;
import ghidra.framework.plugintool.Plugin;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.*;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressFormatException;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.Memory;
import ghidra.program.util.AddressEvaluator;
@@ -79,7 +86,7 @@ public class GoToQuery {
this.monitor = monitor;
}
public boolean processQuery() {
public boolean processQuery() throws ExpressionException {
// Queries can be of several different types. Handle all the non-symbol types first since
// they are faster to try, as they don't require searching through all the program's
// symbols.
@@ -88,7 +95,7 @@ public class GoToQuery {
return true;
}
if (processAddressExpression()) {
if (processAddressExpression(false)) {
return true;
}
@@ -96,9 +103,14 @@ public class GoToQuery {
return true;
}
// none of the specialized query handlers matched, so try to process the query
// as a symbol (label, function name, variable name, etc.)
return processSymbols();
if (processSymbols()) {
return true;
}
// try processing the expression again, this time allowing it to possibly throw an
// exception if it looks like an expression, but doesn't evaluate properly.
return processAddressExpression(true);
}
private boolean processFileOffset() {
@@ -122,7 +134,7 @@ public class GoToQuery {
return false;
}
private boolean processAddressExpression() {
private boolean processAddressExpression(boolean allowExceptions) throws ExpressionException {
String queryInput = queryData.getQueryString();
if (!isAddressExpression(queryInput)) {
return false;
@@ -131,11 +143,21 @@ public class GoToQuery {
// checking for leading "+" or "-", ignoring spaces.
boolean relative = queryInput.matches("^\\s*[+-].*");
Address baseAddr = relative ? fromAddress : null;
ExpressionException exception = null;
for (Program program : getSearchPrograms()) {
Address evalAddr = AddressEvaluator.evaluate(program, baseAddr, queryInput);
if (evalAddr != null) {
Address evalAddr;
try {
evalAddr = AddressEvaluator.parseRelative(program, baseAddr, queryInput);
return goTo(program, new ProgramLocation(program, evalAddr));
}
catch (ExpressionException e) {
exception = e;
}
}
if (allowExceptions && exception != null) {
throw exception;
}
return false;
}

View File

@@ -15,9 +15,13 @@
*/
package ghidra.app.util.navigation;
import generic.expressions.ExpressionException;
import ghidra.app.nav.Navigatable;
import ghidra.app.plugin.core.gotoquery.GoToHelper;
import ghidra.app.services.*;
import ghidra.app.services.GoToOverrideService;
import ghidra.app.services.GoToService;
import ghidra.app.services.GoToServiceListener;
import ghidra.app.services.QueryData;
import ghidra.framework.plugintool.Plugin;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program;
@@ -137,11 +141,22 @@ public class GoToServiceImpl implements GoToService {
GoToQuery query = new GoToQuery(navigatable, plugin, this, queryData, fromAddr,
helper.getOptions(), monitor);
boolean result = query.processQuery();
if (listener != null) {
listener.gotoCompleted(queryData.getQueryString(), result);
boolean result;
try {
result = query.processQuery();
if (listener != null) {
listener.gotoCompleted(queryData.getQueryString(), result);
}
return result;
}
return result;
catch (ExpressionException e) {
// The exception has more information about the failure. Pass that to the listener
// if the user has supplied one.
if (listener != null) {
listener.gotoFailed(e);
}
}
return false;
}
@Override

View File

@@ -15,10 +15,14 @@
*/
package ghidra.program.util;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import org.junit.*;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import generic.expressions.ExpressionException;
import generic.test.AbstractGenericTest;
import ghidra.program.database.ProgramBuilder;
import ghidra.program.model.address.Address;
@@ -52,14 +56,24 @@ public class AddressEvaluatorTest extends AbstractGenericTest {
}
@Test
public void testLongValueExpression() {
public void testLongValueExpressionAbsolute() {
assertEval(addr("0x19"), "(2+3)*5");
assertEval(addr("0x11"), "2+3*5");
assertEval(addr("0x11"), "2+(3*5)");
assertEval(addr("0x3"), "0-5+8");
assertEval(addr("0x3"), "-5+8");
assertEval(addr("0xfffffffB"), "-5");
assertEval(addr("0x11"), "3+(5+(3*2)+(3))");
assertOutOfBounds("Address out of bounds. Expression evaluated to a negative value: -1",
"-1");
}
@Test
public void testRelativeAddSubtract() {
assertEvalRelative(addr("0x50"), addr("0x20"), "+30");
assertEvalRelative(addr("0x20"), addr("0x70"), "-50");
assertOutOfBoundsRelative(addr("0xffffffff"), "+1");
assertOutOfBoundsRelative(addr("0x0"), "-1");
}
@Test
@@ -80,8 +94,6 @@ public class AddressEvaluatorTest extends AbstractGenericTest {
assertEval(addr("0xff00"), "0xffff ^ 0xff");
assertEval(addr("0x123f"), "0xffff & 0x123f");
assertEval(addr("0x1234"), "0x1200 | 0x0034");
assertEval(addr("0xffffffff"), "~ 0x0");
assertEval(addr("0x1201"), "0x1200 | ~(0xfffffffe)");
assertEval(addr("0x480"), "0x1200 >> 2");
assertEval(addr("0x1200"), "0x480 << 2");
assertEval(addr("0x103"), "0x100 | 0x1 | ~(~0x2)");
@@ -156,6 +168,30 @@ public class AddressEvaluatorTest extends AbstractGenericTest {
assertEquals(addr, AddressEvaluator.evaluate(program, input));
}
private void assertEvalRelative(Address expected, Address base, String input) {
assertEquals(expected, AddressEvaluator.evaluate(program, base, input));
}
private void assertOutOfBounds(String errMsg, String input) {
try {
AddressEvaluator.parse(program, input);
fail("Expected Expression exception");
}
catch (ExpressionException e) {
assertEquals(errMsg, e.getMessage());
}
}
private void assertOutOfBoundsRelative(Address base, String input) {
try {
AddressEvaluator.parseRelative(program, base, input);
fail("Expected Expression exception");
}
catch (ExpressionException e) {
assertEquals("Address out of bounds", e.getMessage());
}
}
private Address addr(String address) {
return program.getAddressFactory().getAddress(address);
}

View File

@@ -0,0 +1,134 @@
/* ###
* 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 generic.expressions;
import java.math.BigInteger;
/**
* Long operand values. See {@link ExpressionValue}. Defines supported operators and other
* operands for expression values that are long values.
*/
public class BigIntegerExpressionValue implements ExpressionValue {
private final BigInteger value;
public BigIntegerExpressionValue(BigInteger value) {
this.value = value;
}
public BigIntegerExpressionValue(long longValue) {
this.value = BigInteger.valueOf(longValue);
}
public BigInteger getValue() {
return value;
}
@Override
public String toString() {
return value.toString();
}
public String toHexString() {
String valueString = value.toString(16);
if (valueString.startsWith("-")) {
return "-0x" + valueString.substring(1);
}
return "0x" + valueString;
}
@Override
public ExpressionValue applyUnaryOperator(ExpressionOperator operator)
throws ExpressionException {
switch (operator) {
case BITWISE_NOT:
return new BigIntegerExpressionValue(value.not());
case LOGICAL_NOT:
boolean b = value.equals(BigInteger.ZERO);
return new BigIntegerExpressionValue(b ? BigInteger.ONE : BigInteger.ZERO);
case UNARY_MINUS:
return new BigIntegerExpressionValue(value.negate());
case UNARY_PLUS:
return this;
default:
throw new ExpressionException(
"Unary Operator " + operator + " not supported by Long values!");
}
}
@Override
public ExpressionValue applyBinaryOperator(ExpressionOperator operator, ExpressionValue operand)
throws ExpressionException {
if (!(operand instanceof BigIntegerExpressionValue bigIntegerOperand)) {
throw new ExpressionException("Unsupported operand type for BigInteger: " + value);
}
BigInteger otherValue = bigIntegerOperand.getValue();
switch (operator) {
case BITWISE_AND:
return new BigIntegerExpressionValue(value.and(otherValue));
case BITWISE_OR:
return new BigIntegerExpressionValue(value.or(otherValue));
case BITWISE_XOR:
return new BigIntegerExpressionValue(value.xor(otherValue));
case DIVIDE:
return new BigIntegerExpressionValue(value.divide(otherValue));
case EQUALS:
return new BigIntegerExpressionValue(
value.equals(otherValue) ? BigInteger.ONE : BigInteger.ZERO);
case GREATER_THAN:
return new BigIntegerExpressionValue(
value.compareTo(otherValue) > 0 ? BigInteger.ONE : BigInteger.ZERO);
case GREATER_THAN_OR_EQUAL:
return new BigIntegerExpressionValue(
value.compareTo(otherValue) >= 0 ? BigInteger.ONE : BigInteger.ZERO);
case SHIFT_LEFT:
return new BigIntegerExpressionValue(value.shiftLeft(otherValue.intValue()));
case LESS_THAN:
return new BigIntegerExpressionValue(
value.compareTo(otherValue) < 0 ? BigInteger.ONE : BigInteger.ZERO);
case LESS_THAN_OR_EQUAL:
return new BigIntegerExpressionValue(
value.compareTo(otherValue) <= 0 ? BigInteger.ONE : BigInteger.ZERO);
case LOGICAL_AND:
boolean b1 = !value.equals(BigInteger.ZERO);
boolean b2 = !otherValue.equals(BigInteger.ZERO);
boolean result = b1 && b2;
return new BigIntegerExpressionValue(result ? BigInteger.ONE : BigInteger.ZERO);
case LOGICAL_OR:
b1 = !value.equals(BigInteger.ZERO);
b2 = !otherValue.equals(BigInteger.ZERO);
result = b1 || b2;
return new BigIntegerExpressionValue(result ? BigInteger.ONE : BigInteger.ZERO);
case SUBTRACT:
return new BigIntegerExpressionValue(value.subtract(otherValue));
case NOT_EQUALS:
return new BigIntegerExpressionValue(
value.equals(otherValue) ? BigInteger.ONE : BigInteger.ZERO);
case ADD:
return new BigIntegerExpressionValue(value.add(otherValue));
case SHIFT_RIGHT:
return new BigIntegerExpressionValue(value.shiftRight(otherValue.intValue()));
case MULTIPLY:
return new BigIntegerExpressionValue(value.multiply(otherValue));
default:
throw new ExpressionException(
"Binary Operator \"" + operator + "\" not supported by Long values!");
}
}
}

View File

@@ -15,14 +15,18 @@
*/
package generic.expressions;
import static generic.expressions.ExpressionGrouper.*;
import static generic.expressions.ExpressionGrouper.LEFT_PAREN;
import static generic.expressions.ExpressionGrouper.RIGHT_PAREN;
import java.util.*;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.function.Function;
import java.util.stream.Collectors;
import ghidra.util.NumericUtilities;
/**
* Class for evaluating numeric expressions. See
* {@link ExpressionOperator} for the full list of supported operators. All values are interpreted
@@ -118,13 +122,28 @@ public class ExpressionEvaluator {
*/
public long parseAsLong(String input) throws ExpressionException {
ExpressionValue expressionValue = parse(input);
if (expressionValue instanceof LongExpressionValue longValue) {
return longValue.getLongValue();
if (expressionValue instanceof BigIntegerExpressionValue bitIntValue) {
return bitIntValue.getValue().longValue();
}
throw new ExpressionException("Expression did not evalute to a long! Got a " +
expressionValue.getClass() + " instead.");
}
/**
* Parses the given expression input, expecting the result to be BigInteger value.
* @param input the expression string
* @return the long value result.
* @throws ExpressionException if the expression could not be evaluated to a BigInteger
*/
public BigInteger parseAsBigInteger(String input) throws ExpressionException {
ExpressionValue expressionValue = parse(input);
if (expressionValue instanceof BigIntegerExpressionValue bigIntValue) {
return bigIntValue.getValue();
}
throw new ExpressionException("Expression did not evalute to a BigInteger! Got a " +
expressionValue.getClass() + " instead.");
}
/**
* Changes the hex/decimal mode.
* @param b if true, all numbers will be assumed to be hexadecimal
@@ -457,11 +476,8 @@ public class ExpressionEvaluator {
}
private boolean processNumber(List<ExpressionElement> list, String token) {
int radix = 10;
int radix = assumeHex ? 16 : 10;
if (assumeHex && processAsHexNumber(list, token)) {
return true;
}
token = toLowerAndRemoveEndNumberDecorators(token);
if (token.startsWith("0x")) {
radix = 16;
@@ -469,10 +485,8 @@ public class ExpressionEvaluator {
}
try {
long value = (radix == 10) ? NumericUtilities.parseLong(token)
: NumericUtilities.parseHexLong(token);
list.add(new LongExpressionValue(value));
BigInteger value = new BigInteger(token, radix);
list.add(new BigIntegerExpressionValue(value));
return true;
}
catch (Exception e) {
@@ -494,16 +508,4 @@ public class ExpressionEvaluator {
return token;
}
// parses values as a hex value (e.g. parsing "10" returns 16 instead of 10)
private boolean processAsHexNumber(List<ExpressionElement> list, String token) {
try {
long value = NumericUtilities.parseHexLong(token);
list.add(new LongExpressionValue(value));
return true;
}
catch (NumberFormatException e) {
// ignore
}
return false;
}
}

View File

@@ -1,110 +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 generic.expressions;
/**
* Long operand values. See {@link ExpressionValue}. Defines supported operators and other
* operands for expression values that are long values.
*/
public class LongExpressionValue implements ExpressionValue {
private final long value;
public LongExpressionValue(long value) {
this.value = value;
}
public long getLongValue() {
return value;
}
@Override
public String toString() {
return Long.toString(value);
}
@Override
public ExpressionValue applyUnaryOperator(ExpressionOperator operator) throws ExpressionException {
switch (operator) {
case BITWISE_NOT:
return new LongExpressionValue(~value);
case LOGICAL_NOT:
return new LongExpressionValue(value == 0 ? 1 : 0);
case UNARY_MINUS:
return new LongExpressionValue(-value);
case UNARY_PLUS:
return this;
default:
throw new ExpressionException(
"Unary Operator " + operator + " not supported by Long values!");
}
}
@Override
public ExpressionValue applyBinaryOperator(ExpressionOperator operator, ExpressionValue operand)
throws ExpressionException {
if (!(operand instanceof LongExpressionValue longOperand)) {
throw new ExpressionException("Unsupported operand type for Long: " + value);
}
long otherValue = longOperand.value;
switch (operator) {
case BITWISE_AND:
return new LongExpressionValue(value & otherValue);
case BITWISE_OR:
return new LongExpressionValue(value | otherValue);
case BITWISE_XOR:
return new LongExpressionValue(value ^ otherValue);
case DIVIDE:
return new LongExpressionValue(value / otherValue);
case EQUALS:
return new LongExpressionValue(value == otherValue ? 1 : 0);
case GREATER_THAN:
return new LongExpressionValue(value > otherValue ? 1 : 0);
case GREATER_THAN_OR_EQUAL:
return new LongExpressionValue(value >= otherValue ? 1 : 0);
case SHIFT_LEFT:
return new LongExpressionValue(value << otherValue);
case LESS_THAN:
return new LongExpressionValue(value < otherValue ? 1 : 0);
case LESS_THAN_OR_EQUAL:
return new LongExpressionValue(value <= otherValue ? 1 : 0);
case LOGICAL_AND:
int b1 = value == 0 ? 0 : 1;
int b2 = otherValue == 0 ? 0 : 1;
return new LongExpressionValue(b1 & b2);
case LOGICAL_OR:
b1 = value == 0 ? 0 : 1;
b2 = otherValue == 0 ? 0 : 1;
return new LongExpressionValue(b1 | b2);
case SUBTRACT:
return new LongExpressionValue(value - otherValue);
case NOT_EQUALS:
return new LongExpressionValue(value == otherValue ? 0 : 1);
case ADD:
return new LongExpressionValue(value + otherValue);
case SHIFT_RIGHT:
return new LongExpressionValue(value >> otherValue);
case MULTIPLY:
return new LongExpressionValue(value * otherValue);
default:
throw new ExpressionException(
"Binary Operator \"" + operator + "\" not supported by Long values!");
}
}
}

View File

@@ -19,14 +19,23 @@ import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.List;
import generic.expressions.*;
import generic.expressions.BigIntegerExpressionValue;
import generic.expressions.ExpressionEvaluator;
import generic.expressions.ExpressionException;
import generic.expressions.ExpressionOperator;
import generic.expressions.ExpressionValue;
import ghidra.app.util.NamespaceUtils;
import ghidra.app.util.SymbolPath;
import ghidra.program.model.address.*;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressFactory;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.address.OverlayAddressSpace;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.model.symbol.*;
import ghidra.program.model.symbol.Namespace;
import ghidra.program.model.symbol.Symbol;
import ghidra.program.model.symbol.SymbolTable;
/**
* Class for evaluating expressions as an Address. See
@@ -57,11 +66,18 @@ public class AddressEvaluator extends ExpressionEvaluator {
* to a unique legitimate address.
*/
public static Address evaluate(Program p, String inputExpression) {
return evaluate(p, null, inputExpression);
AddressEvaluator evaluator = new AddressEvaluator(p, true);
try {
return evaluator.parseAsAddress(inputExpression);
}
catch (ExpressionException e) {
return null;
}
}
/**
* Gets a valid address for the specified program as indicated by the input expression.
* Gets a valid address for the specified program as indicated by the input expression or null if
* the expression could not be evaluated for any reason.
* @param p the program to use for determining the address.
* @param baseAddr the base address to use for relative addressing.
* @param inputExpression string representation of the address desired.
@@ -78,6 +94,34 @@ public class AddressEvaluator extends ExpressionEvaluator {
}
}
/**
* 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 inputExpression string representation of the address desired
* @return a valid address in the program that the expression evaluated to
* @throws ExpressionException if the expression could be evaluated to an address
*/
public static Address parseRelative(Program p, Address baseAddr, String inputExpression)
throws ExpressionException {
AddressEvaluator evaluator = new AddressEvaluator(p, true);
return evaluator.parseAsRelativeAddress(inputExpression, baseAddr);
}
/**
* 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 inputExpression string representation of the address desired
* @return a valid address in the program that the expression evaluated to
* @throws ExpressionException if the expression could be evaluated to an address
*/
public static Address parse(Program p, String inputExpression)
throws ExpressionException {
AddressEvaluator evaluator = new AddressEvaluator(p, true);
return evaluator.parseAsAddress(inputExpression);
}
/**
* 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.
@@ -123,14 +167,15 @@ public class AddressEvaluator extends ExpressionEvaluator {
* address.
*/
public Address parseAsAddress(String input) throws ExpressionException {
return this.parseAsRelativeAddress(input, null);
ExpressionValue expressionValue = parse(input);
return toAddress(expressionValue);
}
/**
* 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
* @param baseAddress the base address the evaluated 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
@@ -138,25 +183,9 @@ public class AddressEvaluator extends ExpressionEvaluator {
*/
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());
}
if (expressionValue instanceof LongExpressionValue longValue) {
long offset = longValue.getLongValue();
AddressSpace space = getAddressSpace();
try {
return space.getAddressInThisSpaceOnly(offset*space.getAddressableUnitSize());
}
catch (AddressOutOfBoundsException e) {
throw new ExpressionException(e.getMessage());
}
}
throw new ExpressionException("Expression did not evalute to a long! Got a " +
expressionValue.getClass() + " instead.");
ExpressionValue expressionValue = parse(input, new AddressExpressionValue(baseAddress));
return toAddress(expressionValue);
}
/**
@@ -175,6 +204,21 @@ public class AddressEvaluator extends ExpressionEvaluator {
this.preferredSpace = space;
}
private Address toAddress(ExpressionValue expressionValue) throws ExpressionException {
if (expressionValue instanceof AddressExpressionValue addressValue) {
return validateAddressSpace(addressValue.getAddress());
}
if (expressionValue instanceof BigIntegerExpressionValue bigValue) {
AddressExpressionValue addressValue =
new AddressExpressionValue(getAddressSpace(), bigValue.getValue());
return validateAddressSpace(addressValue.getAddress());
}
throw new ExpressionException("Expression did not evalute to a long! Got a " +
expressionValue.getClass() + " instead.");
}
// 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) {

View File

@@ -15,8 +15,15 @@
*/
package ghidra.program.util;
import generic.expressions.*;
import java.math.BigInteger;
import generic.expressions.BigIntegerExpressionValue;
import generic.expressions.ExpressionException;
import generic.expressions.ExpressionOperator;
import generic.expressions.ExpressionValue;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressOutOfBoundsException;
import ghidra.program.model.address.AddressSpace;
/**
* Address operand values. See {@link ExpressionValue}. Defines supported operators and other
@@ -29,8 +36,13 @@ public class AddressExpressionValue implements ExpressionValue {
this.value = address;
}
public AddressExpressionValue(AddressSpace space, BigInteger value) throws ExpressionException {
this.value = toAddress(space, value);
}
@Override
public ExpressionValue applyUnaryOperator(ExpressionOperator operator) throws ExpressionException {
public ExpressionValue applyUnaryOperator(ExpressionOperator operator)
throws ExpressionException {
long offset = value.getOffset();
switch (operator) {
case BITWISE_NOT:
@@ -46,10 +58,53 @@ public class AddressExpressionValue implements ExpressionValue {
}
private AddressExpressionValue addressExpressionOf(long offset) {
Address address = value.getNewAddress(offset);
return new AddressExpressionValue(addressOf(offset));
}
private AddressExpressionValue addressExpressionOf(BigInteger bigIntValue)
throws ExpressionException {
Address address = toAddress(value.getAddressSpace(), bigIntValue);
return new AddressExpressionValue(address);
}
private Address toAddress(AddressSpace space, BigInteger value) throws ExpressionException {
if (value.signum() < 0) {
// a negative value is less than than min address
throw new ExpressionException(
"Address out of bounds. Expression evaluated to a negative value: " + value);
}
if (isBiggerThanMaxAddress(space, value)) {
throw new ExpressionException(
"Address out of bounds. Expression evaluated to a value larger than max address: " +
value);
}
return toAddress(value.longValue(), space);
}
private Address toAddress(long value, AddressSpace space) throws ExpressionException {
try {
return space.getAddressInThisSpaceOnly(value);
}
catch (AddressOutOfBoundsException e) {
throw new ExpressionException(e.getMessage());
}
}
private boolean isBiggerThanMaxAddress(AddressSpace space, BigInteger value) {
BigInteger byteOffset = computeByteOffset(space, value);
Address maxAddress = space.getMaxAddress();
long unsignedOffset = maxAddress.getOffset();
BigInteger max = new BigInteger(Long.toUnsignedString(unsignedOffset));
return byteOffset.compareTo(max) > 0;
}
private BigInteger computeByteOffset(AddressSpace space, BigInteger value) {
int bytesPerAddress = space.getAddressableUnitSize(); // this is 1 for most programs
return value.multiply(BigInteger.valueOf(bytesPerAddress));
}
private AddressExpressionValue addressExpressionOf(Address address) {
return new AddressExpressionValue(address);
}
@@ -62,8 +117,8 @@ public class AddressExpressionValue implements ExpressionValue {
public ExpressionValue applyBinaryOperator(ExpressionOperator operator, ExpressionValue operand)
throws ExpressionException {
if (operand instanceof LongExpressionValue longOperand) {
return applyBinaryOperator(operator, longOperand);
if (operand instanceof BigIntegerExpressionValue bigIntValue) {
return applyBinaryOperator(operator, bigIntValue);
}
if (operand instanceof AddressExpressionValue addressOperand) {
@@ -74,50 +129,62 @@ public class AddressExpressionValue implements ExpressionValue {
}
private ExpressionValue applyBinaryOperator(ExpressionOperator operator,
LongExpressionValue expressionValue) throws ExpressionException {
long otherValue = expressionValue.getLongValue();
long offset = value.getOffset();
int compareResult = Long.compareUnsigned(offset, otherValue);
BigIntegerExpressionValue expressionValue) throws ExpressionException {
BigInteger otherValue = expressionValue.getValue();
BigInteger myValue = new BigInteger(Long.toUnsignedString(value.getOffset()));
int compareResult = myValue.compareTo(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);
try {
default:
throw new ExpressionException(
"Binary Operator \"" + operator +
"\" with Long operands not supported by Address values!");
switch (operator) {
case BITWISE_AND:
return addressExpressionOf(myValue.and(otherValue));
case BITWISE_OR:
return addressExpressionOf(myValue.or(otherValue));
case BITWISE_XOR:
return addressExpressionOf(myValue.xor(otherValue));
case DIVIDE:
return addressExpressionOf(myValue.divide(otherValue));
case SUBTRACT:
return addressExpressionOf(value.subtract(otherValue.longValueExact()));
case ADD:
return addressExpressionOf(value.add(otherValue.longValueExact()));
case MULTIPLY:
return addressExpressionOf(myValue.multiply(otherValue));
case SHIFT_LEFT:
return addressExpressionOf(myValue.shiftLeft(otherValue.intValueExact()));
case SHIFT_RIGHT:
return addressExpressionOf(myValue.shiftRight(otherValue.intValueExact()));
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!");
}
}
catch (Exception e) {
throw new ExpressionException("Address out of bounds");
}
}
private ExpressionValue booleanExpression(boolean b) {
return new LongExpressionValue(b ? 1 : 0);
return new BigIntegerExpressionValue(b ? 1 : 0);
}
private ExpressionValue unsignedValue(long unsignedLong) {
BigInteger value = new BigInteger(Long.toUnsignedString(unsignedLong));
return new BigIntegerExpressionValue(value);
}
private ExpressionValue applyBinaryOperator(ExpressionOperator operator,
@@ -129,15 +196,15 @@ public class AddressExpressionValue implements ExpressionValue {
switch (operator) {
case BITWISE_AND:
return new LongExpressionValue(offset & otherValueOffset);
return unsignedValue(offset & otherValueOffset);
case BITWISE_OR:
return new LongExpressionValue(offset | otherValueOffset);
return unsignedValue(offset | otherValueOffset);
case BITWISE_XOR:
return new LongExpressionValue(offset ^ otherValueOffset);
return unsignedValue(offset ^ otherValueOffset);
case SUBTRACT:
return new LongExpressionValue(value.subtract(otherValue));
return new BigIntegerExpressionValue(value.subtract(otherValue));
case ADD:
return new LongExpressionValue(offset + otherValueOffset);
return new BigIntegerExpressionValue(offset + otherValueOffset);
case EQUALS:
return booleanExpression(compareResult == 0);
case GREATER_THAN: