diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFMacroEnumCreator.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFMacroEnumCreator.java index 87f6b3f8e1..b4ba6802b3 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFMacroEnumCreator.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFMacroEnumCreator.java @@ -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); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/navigation/GoToAddressLabelDialog.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/navigation/GoToAddressLabelDialog.java index 6a4caf7d52..49d86a698c 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/navigation/GoToAddressLabelDialog.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/navigation/GoToAddressLabelDialog.java @@ -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"; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/navigation/GoToQuery.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/navigation/GoToQuery.java index 0da0b9e1ca..2c51ae9466 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/navigation/GoToQuery.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/navigation/GoToQuery.java @@ -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; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/navigation/GoToServiceImpl.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/navigation/GoToServiceImpl.java index eb7ad3c0a5..5129cd3e0b 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/navigation/GoToServiceImpl.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/navigation/GoToServiceImpl.java @@ -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 diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/util/AddressEvaluatorTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/util/AddressEvaluatorTest.java index 2f4eba2a59..b34a809e0a 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/util/AddressEvaluatorTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/util/AddressEvaluatorTest.java @@ -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); } diff --git a/Ghidra/Framework/Generic/src/main/java/generic/expressions/BigIntegerExpressionValue.java b/Ghidra/Framework/Generic/src/main/java/generic/expressions/BigIntegerExpressionValue.java new file mode 100644 index 0000000000..84fa8607a3 --- /dev/null +++ b/Ghidra/Framework/Generic/src/main/java/generic/expressions/BigIntegerExpressionValue.java @@ -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!"); + } + + } + +} diff --git a/Ghidra/Framework/Generic/src/main/java/generic/expressions/ExpressionEvaluator.java b/Ghidra/Framework/Generic/src/main/java/generic/expressions/ExpressionEvaluator.java index 9691a4fdf2..683076f554 100644 --- a/Ghidra/Framework/Generic/src/main/java/generic/expressions/ExpressionEvaluator.java +++ b/Ghidra/Framework/Generic/src/main/java/generic/expressions/ExpressionEvaluator.java @@ -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 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 list, String token) { - try { - long value = NumericUtilities.parseHexLong(token); - list.add(new LongExpressionValue(value)); - return true; - } - catch (NumberFormatException e) { - // ignore - } - return false; - } } diff --git a/Ghidra/Framework/Generic/src/main/java/generic/expressions/LongExpressionValue.java b/Ghidra/Framework/Generic/src/main/java/generic/expressions/LongExpressionValue.java deleted file mode 100644 index 200a30e21b..0000000000 --- a/Ghidra/Framework/Generic/src/main/java/generic/expressions/LongExpressionValue.java +++ /dev/null @@ -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!"); - } - - } - -} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/AddressEvaluator.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/AddressEvaluator.java index 0663a1acb2..8875801690 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/AddressEvaluator.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/AddressEvaluator.java @@ -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) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/AddressExpressionValue.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/AddressExpressionValue.java index 143931d8a7..cec7694b04 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/AddressExpressionValue.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/AddressExpressionValue.java @@ -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: