mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-10-01 17:11:44 -09:00
GP-7119 Fixed address evaluator to handle unsigned long values properly
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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!");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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!");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user