From e908ab6fbf4311f99fa86d50b60aa4802a6e3fd6 Mon Sep 17 00:00:00 2001 From: dev747368 <48332326+dev747368@users.noreply.github.com> Date: Mon, 11 Aug 2025 11:21:28 -0400 Subject: [PATCH] DWARF expression handling refactor Cleanup logic of expression evaluation, stub out resolution of register values to a callback in case we want to use constant propagation to try to allow successful calculations, and add support for default static values for treating an arch's stack frame register (e.g. RBP) like the static CFA value we already have support for. Add option to decorate params and local vars with their DWARF storage location info. Handle arrays with unspecified element type. --- .../util/bin/format/dwarf/DIEAggregate.java | 23 +- .../format/dwarf/DWARFDataTypeImporter.java | 19 +- .../format/dwarf/DWARFDataTypeManager.java | 17 + .../util/bin/format/dwarf/DWARFFunction.java | 28 +- .../format/dwarf/DWARFFunctionImporter.java | 81 +- .../bin/format/dwarf/DWARFImportOptions.java | 27 + .../bin/format/dwarf/DWARFImportSummary.java | 33 +- .../util/bin/format/dwarf/DWARFLocation.java | 17 +- .../util/bin/format/dwarf/DWARFProgram.java | 54 +- .../format/dwarf/DWARFRegisterMappings.java | 40 +- .../dwarf/DWARFRegisterMappingsManager.java | 29 +- .../app/util/bin/format/dwarf/DWARFUtil.java | 10 +- .../util/bin/format/dwarf/DWARFVariable.java | 166 +-- .../util/bin/format/dwarf/DebugInfoEntry.java | 24 +- .../util/bin/format/dwarf/StringTable.java | 3 +- .../dwarf/attribs/DWARFBlobAttribute.java | 16 +- .../dwarf/expression/DWARFExpression.java | 320 ++--- .../expression/DWARFExpressionEvaluator.java | 1155 +++++++++-------- .../expression/DWARFExpressionException.java | 23 +- .../DWARFExpressionInstruction.java | 233 ++++ .../expression/DWARFExpressionOpCode.java | 285 ++++ .../expression/DWARFExpressionOpCodes.java | 298 ----- .../DWARFExpressionOperandType.java | 18 +- .../expression/DWARFExpressionOperation.java | 102 -- ...WARFExpressionTerminalDerefException.java} | 28 +- ...DWARFExpressionUnsupportedOpException.java | 31 + ...ava => DWARFExpressionValueException.java} | 20 +- .../ParamSpillDWARFFunctionFixup.java | 24 +- ...StorageVerificationDWARFFunctionFixup.java | 2 +- .../golang/GolangDWARFFunctionFixup.java | 15 +- .../dwarf/DWARFFunctionImporterTest.java | 71 +- .../dwarf/DWARFStaticVarImporterTest.java | 73 +- .../app/util/bin/format/dwarf/DIECreator.java | 11 +- .../util/bin/format/dwarf/DWARFTestBase.java | 79 +- .../bin/format/dwarf/StringTableTest.java | 42 +- .../attribs/DWARFAttributeFactoryTest.java | 123 +- .../DWARFExpressionEvaluatorTest.java | 349 ++--- .../dwarf/expression/DWARFExpressionTest.java | 237 ++++ .../ghidra/program/model/data/LEB128.java | 90 +- .../ghidra/program/model/data/LEB128Test.java | 139 +- .../x86/data/languages/x86-64.dwarf | 19 + .../Processors/x86/data/languages/x86.dwarf | 19 + 42 files changed, 2517 insertions(+), 1876 deletions(-) create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionInstruction.java create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionOpCode.java delete mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionOpCodes.java delete mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionOperation.java rename Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/{DWARFExpressionResult.java => DWARFExpressionTerminalDerefException.java} (52%) create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionUnsupportedOpException.java rename Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/{DWARFExpressionEvaluatorContext.java => DWARFExpressionValueException.java} (65%) create mode 100644 Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionTest.java diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DIEAggregate.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DIEAggregate.java index d176c0b0ae..3734fe59f4 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DIEAggregate.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DIEAggregate.java @@ -24,7 +24,8 @@ import java.util.*; import org.apache.commons.lang3.ArrayUtils; import ghidra.app.util.bin.format.dwarf.attribs.*; -import ghidra.app.util.bin.format.dwarf.expression.*; +import ghidra.app.util.bin.format.dwarf.expression.DWARFExpressionEvaluator; +import ghidra.app.util.bin.format.dwarf.expression.DWARFExpressionException; import ghidra.app.util.bin.format.dwarf.line.DWARFFile; import ghidra.app.util.bin.format.dwarf.line.DWARFLine; import ghidra.util.Msg; @@ -544,12 +545,9 @@ public class DIEAggregate { return assertValidInt(dnum.getValue()); } else if (attr instanceof DWARFBlobAttribute dblob) { - byte[] exprBytes = dblob.getBytes(); DWARFExpressionEvaluator evaluator = new DWARFExpressionEvaluator(getCompilationUnit()); - DWARFExpression expr = evaluator.readExpr(exprBytes); - - evaluator.evaluate(expr, 0); - return assertValidInt(evaluator.pop()); + evaluator.evaluate(dblob.getBytes(), 0); + return assertValidInt(evaluator.popLong()); } else { throw new IOException("Not integer attribute: %s".formatted(attr)); @@ -578,13 +576,10 @@ public class DIEAggregate { return dnum.getUnsignedValue(); } else if (attr instanceof DWARFBlobAttribute dblob) { - byte[] exprBytes = dblob.getBytes(); DWARFExpressionEvaluator evaluator = new DWARFExpressionEvaluator(attrInfo.die().getCompilationUnit()); - DWARFExpression expr = evaluator.readExpr(exprBytes); - - evaluator.evaluate(expr, 0); - return evaluator.pop(); + evaluator.evaluate(dblob.getBytes(), 0); + return evaluator.popLong(); } else { throw new IOException("Not integer attribute: %s".formatted(attr)); @@ -627,15 +622,13 @@ public class DIEAggregate { return dnum.getUnsignedIntExact(); } else if (attr instanceof DWARFBlobAttribute dblob) { - byte[] exprBytes = dblob.getBytes(); DWARFExpressionEvaluator evaluator = new DWARFExpressionEvaluator(getCompilationUnit()); - DWARFExpression expr = evaluator.readExpr(exprBytes); // DW_AT_data_member_location expects the address of the containing object // to be on the stack before evaluation starts. We don't have that so we // fake it with zero. - evaluator.evaluate(expr, 0); - return assertValidUInt(evaluator.pop()); + evaluator.evaluate(dblob.getBytes(), 0); + return assertValidUInt(evaluator.popLong()); } else { throw new DWARFException("DWARF attribute form not valid for data member offset: %s" diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFDataTypeImporter.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFDataTypeImporter.java index 3f533dfa78..ca4d6d7f31 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFDataTypeImporter.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFDataTypeImporter.java @@ -120,8 +120,8 @@ public class DWARFDataTypeImporter { * @param defaultValue value to return if the specified DIEA is null or there is a problem * with the DWARF debug data. * @return a {@link DWARFDataType} wrapper around the new Ghidra {@link DataType}. - * @throws IOException - * @throws DWARFExpressionException + * @throws IOException if error + * @throws DWARFExpressionException if error with dwarf expression */ public DWARFDataType getDataType(DIEAggregate diea, DWARFDataType defaultValue) throws IOException, DWARFExpressionException { @@ -342,12 +342,8 @@ public class DWARFDataTypeImporter { return result; } - /** + /* * Gets the corresponding Ghidra base type. - * - * @param diea - * @throws IOException - * @throws DWARFExpressionException */ private DWARFDataType makeDataTypeForBaseType(DIEAggregate diea) throws IOException, DWARFExpressionException { @@ -1084,10 +1080,15 @@ public class DWARFDataTypeImporter { if (self != null) { return self; } - DataType elementDT = fixupDataTypeInconsistencies(elementType); + if (elementType == voidDDT) { + // there was no info about the array's element, cheese something else + return new DWARFDataType(dwarfDTM.getUnspecifiedArrayType(), null, diea.getOffset()); + } + + DataType elementDT = fixupDataTypeInconsistencies(elementType); long explictArraySize = diea.getUnsignedLong(DW_AT_byte_size, -1); - if (elementType.dataType.isZeroLength() || explictArraySize == 0) { + if (DWARFUtil.isZeroByteDataType(elementType.dataType) || explictArraySize == 0) { // don't bother checking range info, we are going to force a zero-element array DataType zeroLenArray = new ArrayDataType(elementDT, 0, -1, dataTypeManager); return new DWARFDataType(zeroLenArray, null, diea.getOffset()); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFDataTypeManager.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFDataTypeManager.java index 7ae6ba572e..4adad6ff3a 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFDataTypeManager.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFDataTypeManager.java @@ -328,6 +328,23 @@ public class DWARFDataTypeManager { return baseDataTypeVoid; } + public DataType getUnspecifiedArrayType() { + DataTypePath dtp = new DataTypePath(DWARFProgram.DWARF_ROOT_CATPATH, ".unknown_array"); + + DataType dt = dataTypeManager.getDataType(dtp); + if (dt == null) { + StructureDataType unspecifiedElementArray = new StructureDataType(dtp.getCategoryPath(), + dtp.getDataTypeName(), 0, dataTypeManager); + unspecifiedElementArray.setToDefaultPacking(); + unspecifiedElementArray.setDescription( + "Zero length data type for arrays that do not have element info"); + + dt = dataTypeManager.resolve(unspecifiedElementArray, + DataTypeConflictHandler.DEFAULT_HANDLER); + } + return dt; + } + /** * Returns a DWARF base data type based on its name, or null if it does not exist. * diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFFunction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFFunction.java index e701e2d172..b614a1dfd6 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFFunction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFFunction.java @@ -24,6 +24,7 @@ import java.util.stream.Collectors; import java.util.stream.StreamSupport; import ghidra.app.cmd.label.SetLabelPrimaryCmd; +import ghidra.app.util.bin.format.dwarf.expression.DWARFExpressionEvaluator; import ghidra.app.util.bin.format.dwarf.expression.DWARFExpressionException; import ghidra.app.util.bin.format.dwarf.funcfixup.DWARFFunctionFixup; import ghidra.program.database.function.OverlappingFunctionException; @@ -46,9 +47,10 @@ public class DWARFFunction { public Namespace namespace; private DWARFRangeList dwarfBody; public Address address; - public long frameBase; // TODO: change this to preserve the func's frameBase expr instead of value public Function function; // ghidra function + public DWARFLocation funcEntryFrameBaseLoc; + public String callingConventionName; public DWARFVariable retval; @@ -70,10 +72,8 @@ public class DWARFFunction { * @param diea DW_TAG_subprogram {@link DIEAggregate} * @return new {@link DWARFFunction}, or null if invalid DWARF information * @throws IOException if error accessing attribute values - * @throws DWARFExpressionException if error accessing attribute values */ - public static DWARFFunction read(DIEAggregate diea) - throws IOException, DWARFExpressionException { + public static DWARFFunction read(DIEAggregate diea) throws IOException { if (diea.isDanglingDeclaration()) { return null; } @@ -100,7 +100,21 @@ public class DWARFFunction { DWARFLocation frameLoc = frameBaseLocs.getLocationContaining(dfunc.getEntryPc()); // get the framebase register, find where the frame is finally setup. if (frameLoc != null) { - dfunc.frameBase = frameLoc.evaluate(diea.getCompilationUnit()).pop(); + try { + DWARFExpressionEvaluator evaluator = + new DWARFExpressionEvaluator(diea.getCompilationUnit()); + if (prog.getImportOptions().isUseStaticStackFrameRegisterValue()) { + evaluator.setValReader(evaluator.withStaticStackRegisterValues(null, + prog.getRegisterMappings().getStackFrameRegisterOffset())); + } + evaluator.evaluate(frameLoc.getExpr()); + frameLoc.setResolvedValue(evaluator.popVarnode()); + dfunc.funcEntryFrameBaseLoc = frameLoc; + } + catch (DWARFExpressionException e) { + // ignore, any location expressions that use DW_OP_fbreg will fail + prog.getImportSummary().addProblematicDWARFExpression(e.getExpression()); + } } } @@ -313,7 +327,7 @@ public class DWARFFunction { VariableUtilities.checkVariableConflict(function, var, varStorage, true); function.addLocalVariable(var, SourceType.IMPORTED); } - catch (InvalidInputException | DuplicateNameException e) { + catch (InvalidInputException | DuplicateNameException | IllegalArgumentException e) { getProgram() .logWarningAt(function.getEntryPoint().add(dvar.lexicalOffset), function.getName(), @@ -457,7 +471,7 @@ public class DWARFFunction { function.setVarArgs(varArg); function.setNoReturn(noReturn); } - catch (InvalidInputException | DuplicateNameException e) { + catch (InvalidInputException | IllegalArgumentException | DuplicateNameException e) { Msg.error(this, "Error updating function %s@%s with params: %s".formatted( function.getName(), function.getEntryPoint().toString(), e.getMessage())); Msg.error(this, "DIE info: " + diea.toString()); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFFunctionImporter.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFFunctionImporter.java index 1705fa0e50..2821ed84e3 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFFunctionImporter.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFFunctionImporter.java @@ -15,19 +15,26 @@ */ package ghidra.app.util.bin.format.dwarf; +import static ghidra.app.util.bin.format.dwarf.attribs.DWARFAttribute.*; + import java.io.IOException; -import java.util.*; +import java.util.HashSet; +import java.util.Set; import ghidra.app.util.bin.format.dwarf.DWARFFunction.CommitMode; +import ghidra.app.util.bin.format.dwarf.expression.DWARFExpression; import ghidra.app.util.bin.format.dwarf.expression.DWARFExpressionException; +import ghidra.app.util.viewer.field.AddressAnnotatedStringHandler; import ghidra.program.model.address.*; import ghidra.program.model.data.*; import ghidra.program.model.data.DataUtilities.ClearDataMode; import ghidra.program.model.listing.*; +import ghidra.program.model.pcode.Varnode; import ghidra.program.model.symbol.*; import ghidra.program.model.util.CodeUnitInsertionException; import ghidra.util.Msg; import ghidra.util.exception.*; +import ghidra.util.table.field.AddressBasedLocation; import ghidra.util.task.TaskMonitor; /** @@ -134,20 +141,6 @@ public class DWARFFunctionImporter { Msg.info(this, "DIE info:\n" + diea.toString()); } } - logImportErrorSummary(); - - - } - - private void logImportErrorSummary() { - if (!importSummary.unknownRegistersEncountered.isEmpty()) { - Msg.error(this, "Found %d unknown registers referenced in DWARF expression operands:" - .formatted(importSummary.unknownRegistersEncountered.size())); - List sortedUnknownRegs = - new ArrayList<>(importSummary.unknownRegistersEncountered); - Collections.sort(sortedUnknownRegs); - Msg.error(this, " unknown registers: %s".formatted(sortedUnknownRegs)); - } } private void markAllChildrenAsProcessed(DebugInfoEntry die) { @@ -269,6 +262,33 @@ public class DWARFFunctionImporter { appendPlateComment(dfunc.address, "DWARF signature update mode: ", dfunc.signatureCommitMode.toString()); } + if (importOptions.isShowVariableStorageInfo()) { + try { + DWARFLocationList frameBaseLocs = dfunc.diea.getLocationList(DW_AT_frame_base); + if (!frameBaseLocs.isEmpty()) { + DWARFLocation frameLoc = + frameBaseLocs.getLocationContaining(dfunc.getEntryPc()); + // get the framebase register, find where the frame is finally setup. + if (frameLoc != null) { + DWARFCompilationUnit cu = dfunc.diea.getCompilationUnit(); + DWARFExpression expr = DWARFExpression.read(frameLoc.getExpr(), cu); + Varnode frameBaseVal = dfunc.funcEntryFrameBaseLoc != null + ? dfunc.funcEntryFrameBaseLoc.getResolvedValue() + : null; + AddressBasedLocation abl = frameBaseVal != null + ? new AddressBasedLocation(currentProgram, + frameBaseVal.getAddress()) + : null; + String fbDestStr = abl != null ? abl.toString() : "???"; + appendPlateComment(dfunc.address, "DWARF frame base: ", + expr.toString(cu) + "=" + fbDestStr); + } + } + } + catch (DWARFExpressionException | IOException e) { + // skip + } + } if (dfunc.name.isNameModified()) { appendPlateComment(dfunc.address, "DWARF original name: ", @@ -283,6 +303,25 @@ public class DWARFFunctionImporter { appendPlateComment(dfunc.address, "DWARF original prototype: ", origFuncDefStr); } + if (dfunc.getBody().getNumAddressRanges() > 1) { + String mainFuncAnnotate = AddressAnnotatedStringHandler + .createAddressAnnotationString(dfunc.address.getOffset(), dfunc.name.getName()); + int rngNum = 0; + for (AddressRange rng : dfunc.getBody().getAddressRanges()) { + String rngMinAnnotate = AddressAnnotatedStringHandler.createAddressAnnotationString( + rng.getMinAddress().getOffset(), rng.getMinAddress().toString()); + String comment = rngMinAnnotate + " (" + rng.getLength() + " bytes)"; + appendPlateComment(dfunc.address, "DWARF func body range[" + rngNum + "]: ", + comment); + if (rngNum != 0) { + appendPlateComment(rng.getMinAddress(), "DWARF: ", + mainFuncAnnotate + " disjoint block " + rngNum); + } + rngNum++; + } + + } + } @@ -299,13 +338,23 @@ public class DWARFFunctionImporter { if (offsetFromFuncStart >= 0) { DWARFVariable localVar = DWARFVariable.readLocalVariable(childDIEA, dfunc, offsetFromFuncStart); - if (localVar != null) { + if (!localVar.isMissingStorage()) { if (prog.getImportOptions().isImportLocalVariables() || localVar.isRamStorage()) { // only retain the local var if option is turned on, or global/static variable dfunc.localVars.add(localVar); } } + else { + String s = "%s %s@[%s]".formatted(localVar.type.getName(), + localVar.name.getName(), + localVar.comment != null && !localVar.comment.isEmpty() + ? localVar.comment + : "???"); + DWARFUtil.appendComment(currentProgram, + dfunc.address.add(offsetFromFuncStart), CommentType.PRE, + "Unresolved local var: ", s, "\n"); + } } break; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFImportOptions.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFImportOptions.java index 5cdadc7cac..4604703b72 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFImportOptions.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFImportOptions.java @@ -95,6 +95,10 @@ public class DWARFImportOptions { Charset to use when decoding debug strings (symbols, filenames, etc). Default is utf-8. Typical values will be 'ascii' or 'utf-8'."""; + private static final String OPTION_SHOW_VARIABLE_STORAGE_INFO = "Output Storage Info"; + private static final String OPTION_SHOW_VARIABLE_STORAGE_DESC = + "Add DWARF storage info for parameters and variables to EOL comments."; + //================================================================================================== // Old Option Names - Should stick around for multiple major versions after 10.2 //================================================================================================== @@ -134,6 +138,8 @@ public class DWARFImportOptions { private long maxSourceMapEntryLength = 2000; private boolean copyExternalDebugFileSymbols = true; private String charsetName = ""; + private boolean showVariableStorageInfo = false; + private boolean useStaticStackFrameRegisterValue = true; /** * Create new instance @@ -480,6 +486,22 @@ public class DWARFImportOptions { this.charsetName = charsetName; } + public boolean isShowVariableStorageInfo() { + return showVariableStorageInfo; + } + + public void setShowVariableStorageInfo(boolean showVariableStorageInfo) { + this.showVariableStorageInfo = showVariableStorageInfo; + } + + public boolean isUseStaticStackFrameRegisterValue() { + return useStaticStackFrameRegisterValue; + } + + public void setUseStaticStackFrameRegisterValue(boolean useStaticStackFrameRegisterValue) { + this.useStaticStackFrameRegisterValue = useStaticStackFrameRegisterValue; + } + /** * See {@link Analyzer#registerOptions(Options, ghidra.program.model.listing.Program)} * @@ -528,6 +550,9 @@ public class DWARFImportOptions { options.registerOption(OPTION_CHARSET_NAME, getCharsetName(), null, OPTION_CHARSET_NAME_DESC); + + options.registerOption(OPTION_SHOW_VARIABLE_STORAGE_INFO, isShowVariableStorageInfo(), + null, OPTION_SHOW_VARIABLE_STORAGE_DESC); } /** @@ -560,5 +585,7 @@ public class DWARFImportOptions { setCopyExternalDebugFileSymbols(options.getBoolean(OPTION_COPY_EXTERNAL_DEBUG_FILE_SYMBOLS, isCopyExternalDebugFileSymbols())); setCharsetName(options.getString(OPTION_CHARSET_NAME, getCharsetName())); + setShowVariableStorageInfo(options.getBoolean(OPTION_SHOW_VARIABLE_STORAGE_INFO, + isShowVariableStorageInfo())); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFImportSummary.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFImportSummary.java index bc23c7b138..1445850acb 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFImportSummary.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFImportSummary.java @@ -17,6 +17,7 @@ package ghidra.app.util.bin.format.dwarf; import java.util.*; +import ghidra.app.util.bin.format.dwarf.expression.DWARFExpression; import ghidra.util.Msg; /** @@ -33,11 +34,7 @@ public class DWARFImportSummary { int funcsUpdated; int funcSignaturesAdded; int globalVarsAdded; - Set unknownRegistersEncountered = new HashSet<>(); Set relocationErrorVarDefs = new HashSet<>(); - int varFitError; - int varDynamicRegisterError; - int varDWARFExpressionValue; int exprReadError; Set typeRemappings = new HashSet<>(); int paramZeroLenDataType; @@ -50,6 +47,7 @@ public class DWARFImportSummary { List compNames = new ArrayList<>(); Set producers = new HashSet<>(); Set sourceLangs = new HashSet<>(); + Map failedExpressions = new HashMap<>(); /** * Writes summary information to the {@link Msg} log. @@ -114,22 +112,11 @@ public class DWARFImportSummary { } } - if (varFitError > 0) { - Msg.error(this, - "DWARF variable definitions that failed because the data type was too large for the defined register location: " + - varFitError); - } - - if (varDynamicRegisterError > 0) { - Msg.error(this, - "DWARF variable definitions that failed because they depended on the dynamic value of a register: " + - varDynamicRegisterError); - } - - if (varDWARFExpressionValue > 0) { - Msg.error(this, - "DWARF variable definitions that failed because they are computed pseudo variables: " + - varDWARFExpressionValue); + if (!failedExpressions.isEmpty()) { + Msg.error(this, "DWARF un-recoverable expressions:"); + for (Map.Entry entry : failedExpressions.entrySet()) { + Msg.error(this, " %s -> %d".formatted(entry.getKey(), entry.getValue())); + } } if (paramZeroLenDataType > 0) { @@ -168,6 +155,12 @@ public class DWARFImportSummary { sourceLangs.add(DWARFUtil.toString(DWARFSourceLanguage.class, lang)); } } + } + void addProblematicDWARFExpression(DWARFExpression expr) { + if (expr != null) { + expr = expr.toGenericForm(); + failedExpressions.compute(expr, (prevexpr, count) -> count != null ? count + 1 : 1); + } } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFLocation.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFLocation.java index fddae01ef5..fd60ae8652 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFLocation.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFLocation.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -17,7 +17,7 @@ package ghidra.app.util.bin.format.dwarf; import java.util.Arrays; -import ghidra.app.util.bin.format.dwarf.expression.*; +import ghidra.program.model.pcode.Varnode; /** * Represents the location of an item that is only valid for a certain range of program-counter @@ -28,6 +28,7 @@ import ghidra.app.util.bin.format.dwarf.expression.*; public class DWARFLocation { private DWARFRange addressRange; private byte[] expr; + private Varnode resolvedValue; /** * Create a Location given an address range and location expression. @@ -65,13 +66,17 @@ public class DWARFLocation { return isWildcard() || addressRange.contains(addr); } - public DWARFExpressionResult evaluate(DWARFCompilationUnit cu) throws DWARFExpressionException { - DWARFExpressionEvaluator evaluator = new DWARFExpressionEvaluator(cu); - return evaluator.evaluate(evaluator.readExpr(this.expr)); + public Varnode getResolvedValue() { + return resolvedValue; + } + + public void setResolvedValue(Varnode resolvedValue) { + this.resolvedValue = resolvedValue; } @Override public String toString() { return "DWARFLocation: range: %s, expr: %s".formatted(addressRange, Arrays.toString(expr)); } + } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFProgram.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFProgram.java index 34ba826eb0..e917ecc9bc 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFProgram.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFProgram.java @@ -23,6 +23,7 @@ import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.*; import java.util.Map.Entry; +import java.util.function.Predicate; import org.apache.commons.collections4.ListValuedMap; import org.apache.commons.collections4.multimap.ArrayListValuedHashMap; @@ -684,7 +685,8 @@ public class DWARFProgram implements Closeable { name = "lexical_block" + getLexicalBlockNameWorker(diea.getHeadFragment()); break; case DW_TAG_formal_parameter: - name = "param_%d".formatted(diea.getHeadFragment().getPositionInParent()); + name = "param_%d".formatted(getPositionInParent(diea.getHeadFragment(), + dietag -> dietag == DW_TAG_formal_parameter)); isAnon = true; break; case DW_TAG_subprogram: @@ -765,14 +767,18 @@ public class DWARFProgram implements Closeable { return "%s.dwarf_%x".formatted(baseName, diea.getOffset()); } - private static String getLexicalBlockNameWorker(DebugInfoEntry die) { - if (die.getTag() == DW_TAG_lexical_block || die.getTag() == DW_TAG_inlined_subroutine) { + private String getLexicalBlockNameWorker(DebugInfoEntry die) { + if (isLexicalBlockTag(die.getTag())) { return "%s_%d".formatted(getLexicalBlockNameWorker(die.getParent()), - die.getPositionInParent()); + getPositionInParent(die, this::isLexicalBlockTag)); } return ""; } + private boolean isLexicalBlockTag(DWARFTag tag) { + return tag == DW_TAG_lexical_block || tag == DW_TAG_inlined_subroutine; + } + private String getReferringMemberFieldNames(List referringMembers) { if (referringMembers == null || referringMembers.isEmpty()) { return ""; @@ -787,7 +793,8 @@ public class DWARFProgram implements Closeable { } String memberName = referringMember.getName(); if (memberName == null) { - int positionInParent = referringMember.getHeadFragment().getPositionInParent(); + int positionInParent = + getPositionInParent(referringMember.getHeadFragment(), x -> true); if (positionInParent == -1) { continue; } @@ -886,7 +893,7 @@ public class DWARFProgram implements Closeable { * @param dieIndex index of a DIE record * @return index of the parent of specified DIE, or -1 if no parent (eg. root DIE) */ - public int getParentIndex(int dieIndex) { + private int getParentIndex(int dieIndex) { return parentIndexes[dieIndex]; } @@ -929,7 +936,7 @@ public class DWARFProgram implements Closeable { * @param dieIndex index of a DIE record * @return list of DIE indexes that are children of the specified DIE */ - public IntArrayList getDIEChildIndexes(int dieIndex) { + private IntArrayList getDIEChildIndexes(int dieIndex) { IntArrayList result = new IntArrayList(true); if (dieIndex >= 0) { int parentSiblingIndex = siblingIndexes[dieIndex]; @@ -941,6 +948,18 @@ public class DWARFProgram implements Closeable { return result; } + public int getChildCount(int dieIndex) { + int result = 0; + if (dieIndex >= 0) { + int parentSiblingIndex = siblingIndexes[dieIndex]; + for (int index = dieIndex + 1; index < parentSiblingIndex; index = + siblingIndexes[index]) { + result++; + } + } + return result; + } + private DWARFCompilationUnit getCompilationUnitForDIE(int dieIndex) { Entry entry = compUnitDieIndex.ceilingEntry(dieIndex); return entry != null ? entry.getValue() : null; @@ -1410,6 +1429,27 @@ public class DWARFProgram implements Closeable { this.debugStrings = st; } + private int getPositionInParent(DebugInfoEntry die, Predicate dwTagFilter) { + int dieIndex = die.getIndex(); + int parentIndex = getParentIndex(dieIndex); + if (parentIndex < 0) { + return -1; + } + IntArrayList childIndexes = getDIEChildIndexes(parentIndex); + for (int i = 0, positionNum = 0; i < childIndexes.size(); i++) { + int childDIEIndex = childIndexes.get(i); + if (childDIEIndex == dieIndex) { + return positionNum; + } + DebugInfoEntry childDIE = getDIEByIndex(childDIEIndex); + if (childDIE != null && dwTagFilter.test(childDIE.getTag())) { + positionNum++; + } + } + // only way to get here is if our in-memory indexes are corrupt / incorrect + throw new RuntimeException("DWARF DIE index failure."); + } + //--------------------------------------------------------------------------------------------- private class DIEAggregateIterator implements Iterator, Iterable { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFRegisterMappings.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFRegisterMappings.java index 6b734c4c26..73eb3022d6 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFRegisterMappings.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFRegisterMappings.java @@ -15,7 +15,6 @@ */ package ghidra.app.util.bin.format.dwarf; -import java.util.Collections; import java.util.Map; import ghidra.program.model.lang.Register; @@ -75,39 +74,68 @@ import ghidra.program.model.lang.Register; public class DWARFRegisterMappings { public static final DWARFRegisterMappings DUMMY = - new DWARFRegisterMappings(Collections.emptyMap(), 0, -1, false); + new DWARFRegisterMappings(Map.of(), null, -1, null, 0, false); /* * Maps DWARF register number to Ghidra architecture registers. */ private final Map dwarfRegisterMap; - private final long callFrameCFA; + private final Integer callFrameCFA; private final int stackPointerIndex; private final boolean useFormalParameterStorage; - public DWARFRegisterMappings(Map regmap, long callFrameCFA, - int stackPointerIndex, boolean useFPS) { + private Register stackFrameRegister; + + private int stackFrameRegisterOffset; + + public DWARFRegisterMappings(Map regmap, Integer callFrameCFA, + int stackPointerIndex, Register stackFrameRegister, int stackFrameRegisterOffset, + boolean useFPS) { this.dwarfRegisterMap = regmap; this.callFrameCFA = callFrameCFA; this.stackPointerIndex = stackPointerIndex; + this.stackFrameRegister = stackFrameRegister; this.useFormalParameterStorage = useFPS; + this.stackFrameRegisterOffset = stackFrameRegisterOffset; } public Register getGhidraReg(int dwarfRegNum) { return dwarfRegisterMap.get(dwarfRegNum); } - public long getCallFrameCFA() { + /** + * 'Static' value for a function's CFA value (instead of trying to extract it from the func's + * CIE metadata). + * + * @return cfa static stack offset + */ + public int getCallFrameCFA() { return callFrameCFA; } + public boolean hasStaticCFA() { + return callFrameCFA != null; + } + public int getDWARFStackPointerRegNum() { return stackPointerIndex; } + public Register getStackRegister() { + return stackPointerIndex != -1 ? getGhidraReg(stackPointerIndex) : null; + } + + public Register getStackFrameRegister() { + return stackFrameRegister; + } + + public int getStackFrameRegisterOffset() { + return stackFrameRegisterOffset; + } + public boolean isUseFormalParameterStorage() { return useFormalParameterStorage; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFRegisterMappingsManager.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFRegisterMappingsManager.java index 59c82d7358..76a627a2a3 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFRegisterMappingsManager.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFRegisterMappingsManager.java @@ -132,15 +132,31 @@ public class DWARFRegisterMappingsManager { Map regmap = new HashMap<>(); int spi; - long cfa; + Integer cfa = null; // null == not set + Register stackFrameRegister = null; + int stackFrameRegisterOffset = 0; boolean useFPS; try { spi = readMappingsElem(regMappingsElem, lang, regmap); Element callFrameElem = rootElem.getChild("call_frame_cfa"); - cfa = (callFrameElem != null) - ? XmlUtilities.parseOptionalBoundedLongAttr(callFrameElem, "value", 0, 0, - Long.MAX_VALUE) - : 0; + if (callFrameElem != null) { + cfa = XmlUtilities.parseOptionalBoundedIntAttr(callFrameElem, "value", 0, 0, + Integer.MAX_VALUE); + } + + Element stackFrameElem = rootElem.getChild("stack_frame"); + if (stackFrameElem != null) { + String stackFrameRegisterName = + stackFrameElem.getAttributeValue("register"); + stackFrameRegister = + stackFrameRegisterName != null && !stackFrameRegisterName.isEmpty() + ? lang.getRegister(stackFrameRegisterName) + : null; + if (stackFrameRegister != null) { + stackFrameRegisterOffset = XmlUtilities.parseBoundedIntAttr(stackFrameElem, + "offset", Integer.MIN_VALUE, Integer.MAX_VALUE); + } + } Element useFormalParameterStorageElem = rootElem.getChild("use_formal_parameter_storage"); @@ -151,7 +167,8 @@ public class DWARFRegisterMappingsManager { nfe); } - return new DWARFRegisterMappings(regmap, cfa, spi, useFPS); + return new DWARFRegisterMappings(regmap, cfa, spi, stackFrameRegister, + stackFrameRegisterOffset, useFPS); } /* diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFUtil.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFUtil.java index bef803deb6..8e08f87f4d 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFUtil.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFUtil.java @@ -465,7 +465,7 @@ public class DWARFUtil { * @param lang {@link Language} to query * @param name name of the value * @return String value - * @throws IOException + * @throws IOException if invalid language or multiple values with same name */ public static String getLanguageExternalNameValue(Language lang, String name) throws IOException { @@ -552,8 +552,8 @@ public class DWARFUtil { if (VoidDataType.dataType.isEquivalent(dt)) { return true; } - if (!dt.isZeroLength() && dt instanceof Array) { - dt = DataTypeUtilities.getArrayBaseDataType((Array) dt); + if (!dt.isZeroLength() && dt instanceof Array array) { + dt = DataTypeUtilities.getArrayBaseDataType(array); } return dt.isZeroLength(); } @@ -567,4 +567,8 @@ public class DWARFUtil { varnode.getAddress().getAddressSpace().getType() == AddressSpace.TYPE_STACK; } + public static boolean isConstVarnode(Varnode varnode) { + return varnode != null && + varnode.getAddress().getAddressSpace().getType() == AddressSpace.TYPE_CONSTANT; + } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFVariable.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFVariable.java index 0e473d807a..8e19480602 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFVariable.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFVariable.java @@ -15,7 +15,6 @@ */ package ghidra.app.util.bin.format.dwarf; -import static ghidra.app.util.bin.format.dwarf.DWARFTag.*; import static ghidra.app.util.bin.format.dwarf.attribs.DWARFAttribute.*; import java.io.IOException; @@ -73,8 +72,8 @@ public class DWARFVariable { * @param diea {@link DIEAggregate} DW_TAG_variable * @param dfunc {@link DWARFFunction} that this local var belongs to * @param offsetFromFuncStart offset from start of containing function - * @return new DWARFVariable that represents a local var, or null if - * error reading storage info + * @return new DWARFVariable that represents a local var, never null. Check + * {@link #isMissingStorage()} to determine if there was an error getting storage info */ public static DWARFVariable readLocalVariable(DIEAggregate diea, DWARFFunction dfunc, long offsetFromFuncStart) { @@ -82,7 +81,9 @@ public class DWARFVariable { DWARFVariable dvar = new DWARFVariable(dfunc, diea); dvar.lexicalOffset = offsetFromFuncStart; - return dvar.readLocalVariableStorage(diea) ? dvar : null; + dvar.readLocalVariableStorage(diea); + + return dvar; } /** @@ -111,7 +112,7 @@ public class DWARFVariable { public DWARFSourceInfo sourceInfo; private List storage = new ArrayList<>(); private Varnode stackStorage; // any stack storage is forced to be last in the storage list - private String comment; + public String comment; private DWARFVariable(DWARFProgram program, DWARFFunction dfunc, DataType type) { this.program = program; @@ -128,6 +129,19 @@ public class DWARFVariable { this.sourceInfo = DWARFSourceInfo.create(diea); } + public void setStorage(Varnode varnode) { + clearStorage(); + if (varnode.getSize() == 0) { + // TODO: size probably needs to drive register adjustments + varnode = new Varnode(varnode.getAddress(), type.getLength()); + } + if ( DWARFUtil.isStackVarnode(varnode)) { + stackStorage = varnode; + } else { + storage.add(varnode); + } + } + /** * Assign storage for this variable in a ram data location. * @@ -255,7 +269,7 @@ public class DWARFVariable { if (paramLoc == null) { return false; } - return readStorage(diea, paramLoc); + return readStorage(diea, paramLoc, false); } catch (IOException e) { diea.getProgram().getImportSummary().exprReadError++; @@ -277,7 +291,7 @@ public class DWARFVariable { lexicalOffset = location.getOffset(dfunc.getEntryPc()); } - return readStorage(diea, location); + return readStorage(diea, location, true); } catch (IOException e) { diea.getProgram().getImportSummary().exprReadError++; @@ -298,17 +312,15 @@ public class DWARFVariable { DWARFExpressionEvaluator exprEvaluator = new DWARFExpressionEvaluator(diea.getCompilationUnit()); - DWARFExpression expr = exprEvaluator.readExpr(location.getExpr()); + exprEvaluator.evaluate(location.getExpr()); + Varnode res = exprEvaluator.popVarnode(); - exprEvaluator.evaluate(expr); - if (exprEvaluator.getRawLastRegister() != -1) { + if (!res.isAddress()) { Msg.warn(this, "DWARF: bad location for global variable %s: %s" - .formatted(getDeclInfoString(), expr.toString())); + .formatted(getDeclInfoString(), exprEvaluator.getExpr().toString())); return false; } - - long res = exprEvaluator.pop(); - if (res == 0) { + if (res.getAddress().getOffset() == 0) { if (diea.hasAttribute(DWARFAttribute.DW_AT_const_value)) { // skip without complaining global vars with a const value and bad location expression return false; @@ -322,17 +334,21 @@ public class DWARFVariable { return false; } - setRamStorage(res); + setStorage(res); return true; } - catch (DWARFExpressionException | UnsupportedOperationException - | IndexOutOfBoundsException | IOException ex) { + catch (DWARFExpressionException e) { + prog.getImportSummary().addProblematicDWARFExpression(e.getExpression()); + return false; + } + catch (IOException e) { prog.getImportSummary().exprReadError++; return false; } } - private boolean readStorage(DIEAggregate diea, DWARFLocation location) { + private boolean readStorage(DIEAggregate diea, DWARFLocation location, + boolean allowDerefFixup) { if (location == null) { return false; @@ -342,97 +358,55 @@ public class DWARFVariable { DWARFProgram prog = diea.getProgram(); DWARFImportSummary importSummary = prog.getImportSummary(); + DWARFCompilationUnit cu = diea.getCompilationUnit(); + DWARFExpressionEvaluator exprEvaluator = new DWARFExpressionEvaluator(cu); + if (dfunc.funcEntryFrameBaseLoc != null && + dfunc.funcEntryFrameBaseLoc.getResolvedValue() != null && + dfunc.funcEntryFrameBaseLoc.contains(dfunc.getEntryPc() + lexicalOffset)) { + exprEvaluator.setFrameBaseVal(dfunc.funcEntryFrameBaseLoc.getResolvedValue()); + } + + DWARFExpression expr = null; try { - DWARFExpressionEvaluator exprEvaluator = - new DWARFExpressionEvaluator(diea.getCompilationUnit()); - exprEvaluator.setFrameBase(dfunc.frameBase); + expr = DWARFExpression.read(location.getExpr(), cu); + if (expr.isEmpty()) { + return false; + } - DWARFExpression expr = exprEvaluator.readExpr(location.getExpr()); + if (prog.getImportOptions().isUseStaticStackFrameRegisterValue()) { + exprEvaluator.setValReader(exprEvaluator.withStaticStackRegisterValues(null, + prog.getRegisterMappings().getStackFrameRegisterOffset())); + } + if (prog.getImportOptions().isShowVariableStorageInfo()) { + comment = expr.toString(cu); + } + exprEvaluator.evaluate(expr); - long res = exprEvaluator.pop(); + + Varnode storageLoc = exprEvaluator.popVarnode(); - // check expression eval result. Use early return for errors, leaving storage unset. - // Success return is at bottom of if/else chain of checks. - - if (exprEvaluator.isDwarfStackValue()) { - // result is a value (not a location) left on the expr stack, which is not supported - importSummary.varDWARFExpressionValue++; - return false; - } - - if (exprEvaluator.useUnknownRegister()) { - // This is a deref of a register (excluding the stack pointer) - // If the offset of the deref was 0, we can cheese it into a ghidra register location - // by changing the datatype to a pointer-to-original-datatype, otherwise - // its not usable in ghidra - - if (!exprEvaluator.isRegisterLocation()) { - importSummary.varDynamicRegisterError++; - return false; - } - - if (exprEvaluator.getLastRegister() != null) { - type = prog.getDwarfDTM().getPtrTo(type); - setRegisterStorage(List.of(exprEvaluator.getLastRegister())); - } - } - else if (exprEvaluator.isStackRelative()) { - if (exprEvaluator.isDeref()) { - type = prog.getDwarfDTM().getPtrTo(type); - } - setStackStorage(res); - } - else if (exprEvaluator.isRegisterLocation()) { - // The DWARF expression evaluated to a simple register. If we have a mapping - // for it in the "processor.dwarf" register mapping file, try to create - // a variable, otherwise log the unknown register for later logging. - Register reg = exprEvaluator.getLastRegister(); - if (reg == null) { - // The DWARF register did not have a mapping to a Ghidra register, so - // log it to be displayed in an error summary at end of import phase. - importSummary.unknownRegistersEncountered - .add(exprEvaluator.getRawLastRegister()); - return false; - } - if ((type.getLength() > reg.getMinimumByteSize())) { - importSummary.varFitError++; - program.logWarningAt(dfunc.address, dfunc.name.getName(), - "%s %s [%s, size=%d] can not fit into specified register %s, size=%d" - .formatted(getVarTypeName(diea), name.getName(), type.getName(), - type.getLength(), reg.getName(), reg.getMinimumByteSize())); - return false; - } - setRegisterStorage(List.of(reg)); - } - else if (exprEvaluator.getRawLastRegister() == -1 && res != 0) { - // static global variable location - setRamStorage(res); - } - else { - Msg.error(this, - "%s location error for function %s@%s, %s: %s, DWARF DIE: %s, unsupported location information." - .formatted(getVarTypeName(diea), dfunc.name.getName(), dfunc.address, - name.getName(), - DWARFExpression.exprToString(location.getExpr(), diea), - diea.getHexOffset())); - return false; - } + setStorage(storageLoc); return true; } - catch (DWARFExpressionException | UnsupportedOperationException - | IndexOutOfBoundsException ex) { - importSummary.exprReadError++; + catch (DWARFExpressionException e) { + if (allowDerefFixup && e instanceof DWARFExpressionTerminalDerefException derefExcept) { + type = type.getDataTypeManager().getPointer(type); + setStorage(derefExcept.getVarnode()); + return true; + } + + if (e instanceof DWARFExpressionValueException && expr != null) { + comment = expr.toString(cu); + } + + importSummary.addProblematicDWARFExpression(e.getExpression()); return false; } } - private String getVarTypeName(DIEAggregate diea) { - return diea.getTag() == DW_TAG_formal_parameter ? "Parameter" : "Variable"; - } - public int getStorageSize() { return getVarnodes().stream().mapToInt(Varnode::getSize).sum(); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DebugInfoEntry.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DebugInfoEntry.java index 7e60691ca6..38ee729c27 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DebugInfoEntry.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DebugInfoEntry.java @@ -22,7 +22,6 @@ import ghidra.app.util.bin.BinaryReader; import ghidra.app.util.bin.format.dwarf.attribs.*; import ghidra.app.util.bin.format.dwarf.attribs.DWARFAttribute.AttrDef; import ghidra.program.model.data.LEB128; -import ghidra.util.datastruct.IntArrayList; /** * A DWARF Debug Info Entry is a collection of {@link DWARFAttributeValue attributes} @@ -245,27 +244,6 @@ public class DebugInfoEntry { return abbreviation == null; } - /** - * Returns the ordinal position of this DIE record in its parent's list of children. - * - * @return index of ourself in our parent, or -1 if root DIE - */ - public int getPositionInParent() { - DWARFProgram dprog = getProgram(); - int parentIndex = dprog.getParentIndex(dieIndex); - if (parentIndex < 0) { - return -1; - } - IntArrayList childIndexes = dprog.getDIEChildIndexes(parentIndex); - for (int i = 0; i < childIndexes.size(); i++) { - if (childIndexes.get(i) == dieIndex) { - return i; - } - } - // only way to get here is if our in-memory indexes are corrupt / incorrect - throw new RuntimeException("DWARF DIE index failure."); - } - public DWARFCompilationUnit getCompilationUnit() { return compilationUnit; } @@ -302,7 +280,7 @@ public class DebugInfoEntry { DWARFTag tag = getTag(); int tagNum = tag != null ? tag.getId() : 0; int abbrNum = abbreviation != null ? abbreviation.getAbbreviationCode() : 0; - int childCount = getProgram().getDIEChildIndexes(dieIndex).size(); + int childCount = getProgram().getChildCount(dieIndex); buffer.append("<%d><%x>: %s [abbrev %d, tag %d, index %d, children %d]\n".formatted( getDepth(), offset, tag, abbrNum, tagNum, dieIndex, childCount)); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/StringTable.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/StringTable.java index d673fcdd13..21b6104a27 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/StringTable.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/StringTable.java @@ -32,6 +32,7 @@ public class StringTable { * Creates a StringTable instance, if the supplied BinaryReader is non-null. * * @param reader BinaryReader + * @param charset {@link Charset} of strings in table * @return new instance, or null if reader is null */ public static StringTable of(BinaryReader reader, Charset charset) { @@ -49,7 +50,7 @@ public class StringTable { * Creates a StringTable * * @param reader {@link BinaryReader} .debug_str or .debug_line_str - * @param charset {@link Charset} of strings + * @param charset {@link Charset} of strings in table */ public StringTable(BinaryReader reader, Charset charset) { this.reader = reader; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/attribs/DWARFBlobAttribute.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/attribs/DWARFBlobAttribute.java index 2680fd43a7..7c1de70999 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/attribs/DWARFBlobAttribute.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/attribs/DWARFBlobAttribute.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -15,8 +15,6 @@ */ package ghidra.app.util.bin.format.dwarf.attribs; -import ghidra.app.util.bin.format.dwarf.DWARFCompilationUnit; -import ghidra.app.util.bin.format.dwarf.expression.*; import ghidra.util.NumericUtilities; /** @@ -38,16 +36,6 @@ public class DWARFBlobAttribute extends DWARFAttributeValue { return bytes.length; } - public DWARFExpressionEvaluator evaluateExpression(DWARFCompilationUnit cu) - throws DWARFExpressionException { - - DWARFExpressionEvaluator exprEvaluator = new DWARFExpressionEvaluator(cu); - DWARFExpression expr = exprEvaluator.readExpr(bytes); - exprEvaluator.evaluate(expr); - - return exprEvaluator; - } - @Override public String toString() { return "%s : %s = [%d]%s".formatted(getAttributeName(), getAttributeForm(), bytes.length, diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpression.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpression.java index cbd0a7badb..2f7924bed4 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpression.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpression.java @@ -15,42 +15,40 @@ */ package ghidra.app.util.bin.format.dwarf.expression; +import static ghidra.app.util.bin.format.dwarf.expression.DWARFExpressionOpCode.*; + import java.io.IOException; -import java.util.ArrayList; -import java.util.List; +import java.util.*; import ghidra.app.util.bin.*; -import ghidra.app.util.bin.format.dwarf.DIEAggregate; -import ghidra.program.model.data.LEB128; -import ghidra.util.NumericUtilities; +import ghidra.app.util.bin.format.dwarf.DWARFCompilationUnit; +import ghidra.app.util.bin.format.dwarf.DWARFRegisterMappings; /** - * A {@link DWARFExpression} is an immutable list of {@link DWARFExpressionOperation operations} and some factory methods to read - * an expression from its binary representation. + * A {@link DWARFExpression} is an immutable list of {@link DWARFExpressionInstruction operations} + * and some factory methods to read an expression from its binary representation. *

* Use a {@link DWARFExpressionEvaluator} to execute a {@link DWARFExpression}. */ public class DWARFExpression { - static long EMPTY_OPERANDS_VALUE[] = {}; public static final int MAX_SANE_EXPR = 256; - private final List operations; - - private final int lastActiveOpIndex; - - public static String exprToString(byte[] exprBytes, DIEAggregate diea) { - try { - DWARFExpression expr = - new DWARFExpressionEvaluator(diea.getCompilationUnit()).readExpr(exprBytes); - return expr.toString(); - } - catch (DWARFExpressionException e) { - return "Unable to parse DWARF expression. Raw bytes: " + - NumericUtilities.convertBytesToString(exprBytes, " "); - } + /** + * Deserializes a {@link DWARFExpression} from its raw bytes. + * + * @param exprBytes bytes containing the expression + * @param cu the {@link DWARFCompilationUnit} that contained the expression + * @return new {@link DWARFExpression}, never null + * @throws DWARFExpressionException if error reading the expression, check + * {@link DWARFExpressionException#getExpression()} for the partial results of the read + */ + public static DWARFExpression read(byte[] exprBytes, DWARFCompilationUnit cu) + throws DWARFExpressionException { + return read(exprBytes, cu.getPointerSize(), cu.getProgram().isLittleEndian(), + cu.getIntSize()); } - public static DWARFExpression read(byte[] exprBytes, byte addrSize, boolean isLittleEndian, + private static DWARFExpression read(byte[] exprBytes, byte addrSize, boolean isLittleEndian, int intSize) throws DWARFExpressionException { ByteProvider provider = new ByteArrayProvider(exprBytes); BinaryReader reader = new BinaryReader(provider, isLittleEndian); @@ -58,147 +56,90 @@ public class DWARFExpression { return read(reader, addrSize, intSize); } - public static DWARFExpression read(BinaryReader reader, byte addrSize, int intSize) + private static DWARFExpression read(BinaryReader reader, byte addrSize, int intSize) throws DWARFExpressionException { - List operations = new ArrayList<>(); + List instructions = new ArrayList<>(); try { - long opcodeoffset; - boolean invalidOpCodeEncountered = false; - - while ((opcodeoffset = reader.getPointerIndex()) < reader.length()) { - int opcode = reader.readNextUnsignedByte(); - if (!DWARFExpressionOpCodes.isValidOpcode(opcode)) { - // consume the remainder of the bytes in the expression because - // we've hit an invalid opcode and can not proceed any further. - int bytesLeft = (int) (reader.length() - reader.getPointerIndex()); - operations.add(new DWARFExpressionOperation(opcode, - DWARFExpressionOpCodes.BLOBONLY_OPERANDTYPES, new long[] { 0 }, - readSizedBlobOperand(reader, bytesLeft), (int) opcodeoffset)); - invalidOpCodeEncountered = true; - } - else { - DWARFExpressionOperandType[] operandTypes = - DWARFExpressionOpCodes.getOperandTypesFor(opcode); - - long[] operandValues = - (operandTypes.length != 0) ? new long[operandTypes.length] - : EMPTY_OPERANDS_VALUE; - byte[] blob = null; - for (int i = 0; i < operandTypes.length; i++) { - DWARFExpressionOperandType optype = operandTypes[i]; - if (optype == DWARFExpressionOperandType.SIZED_BLOB) { - blob = readSizedBlobOperand(reader, operandValues[i - 1]); - } - else { - operandValues[i] = readOperandValue(optype, reader, addrSize, intSize); - } - } - - DWARFExpressionOperation op = new DWARFExpressionOperation(opcode, operandTypes, - operandValues, blob, (int) opcodeoffset); - operations.add(op); + while (reader.hasNext()) { + DWARFExpressionInstruction instr = + DWARFExpressionInstruction.read(reader, addrSize, intSize); + instructions.add(instr); + if (instr.getOpCode() == DW_OP_unknown_opcode) { + throw new IOException("Unknown DWARF opcode(s) encountered"); } } - if (invalidOpCodeEncountered) { - throw new IOException("Unknown DWARF opcode(s) encountered"); - } - - return new DWARFExpression(operations); + return new DWARFExpression(instructions); } catch (IOException ioe) { - DWARFExpression badExpr = new DWARFExpression(operations); - String s = badExpr.toString(); + DWARFExpression badExpr = new DWARFExpression(instructions); throw new DWARFExpressionException( "Error reading DWARF expression, partial expression is: ", badExpr, -1, ioe); } } - private static long readOperandValue(DWARFExpressionOperandType operandType, - BinaryReader reader, byte addrSize, int intSize) throws IOException { - try { - switch (operandType) { - case ADDR: - return reader.readNextUnsignedValue(addrSize); - case S_BYTE: - return reader.readNextByte(); - case S_SHORT: - return reader.readNextShort(); - case S_INT: - return reader.readNextInt(); - case S_LONG: - return reader.readNextLong(); - case U_BYTE: - return reader.readNextUnsignedByte(); - case U_SHORT: - return reader.readNextUnsignedShort(); - case U_INT: - return reader.readNextUnsignedInt(); - case U_LONG: - return reader.readNextLong(); /* & there is no mask for ulong */ - case S_LEB128: - return reader.readNext(LEB128::signed); - case U_LEB128: - return reader.readNext(LEB128::unsigned); - case SIZED_BLOB: - throw new IOException("Can't read SIZED_BLOB as a Long value"); - case DWARF_INT: - return reader.readNextUnsignedValue(intSize); - } - } - catch (ArrayIndexOutOfBoundsException aioob) { - throw new IOException("Not enough bytes to read " + operandType); - } - throw new IOException("Unknown DWARFExpressionOperandType " + operandType); - } - - private static byte[] readSizedBlobOperand(BinaryReader reader, long previousOperandValue) - throws IOException { - return reader.readNextByteArray((int) previousOperandValue); - } - - private DWARFExpression(List operations) { - this.operations = operations; - this.lastActiveOpIndex = findLastActiveOpIndex(); - } - - public DWARFExpressionOperation getOp(int i) { - return operations.get(i); - } - - public int getOpCount() { - return operations.size(); - } + private final List instructions; /** - * Returns the index of the last operation that is not a NOP. - * @return + * Private constructor for {@link DWARFExpression}... use one of the static + * {@link #read(byte[], DWARFCompilationUnit) read} methods to create an instance. + * + * @param instructions list of instructions */ - public int getLastActiveOpIndex() { - return lastActiveOpIndex; - } - - private int findLastActiveOpIndex() { - for (int i = operations.size() - 1; i >= 0; i--) { - if (operations.get(i).getOpCode() != DWARFExpressionOpCodes.DW_OP_nop) { - return i; - } - } - return operations.size() - 1; + private DWARFExpression(List instructions) { + this.instructions = instructions; } /** - * Finds the index of an {@link DWARFExpressionOperation operation} by its offset + * Converts this {@link DWARFExpression} into a generic form, lacking any operand values. + *

+ * Useful for aggregating statistics about unsupported/problematic expressions encountered in + * a binary. + * + * @return new {@link DWARFExpression} instance where each instruction has been stripped of all + * operands + */ + public DWARFExpression toGenericForm() { + List genericInstrs = + instructions.stream().map(DWARFExpressionInstruction::toGenericForm).toList(); + return new DWARFExpression(genericInstrs); + } + + /** + * {@return the requested instruction} + * @param i instruction index + */ + public DWARFExpressionInstruction getInstruction(int i) { + return instructions.get(i); + } + + /** + * {@return number of instructions in this expression} + */ + public int getInstructionCount() { + return instructions.size(); + } + + /** + * {@return true if there are no instructions} + */ + public boolean isEmpty() { + return instructions.isEmpty(); + } + + /** + * Finds the index of an {@link DWARFExpressionInstruction operation} by its offset * from the beginning of the expression. * - * @param offset - * @return -1 if there is no op at the specified offset + * @param offset byte offset of instruction to find + * @return index of instruction at specified byte offset, or -1 if there is no instruction + * at the specified offset */ - public int findOpByOffset(long offset) { - for (int i = 0; i < operations.size(); i++) { - DWARFExpressionOperation op = getOp(i); - if (op.getOffset() == offset) { + public int findInstructionByOffset(long offset) { + for (int i = 0; i < instructions.size(); i++) { + DWARFExpressionInstruction instr = getInstruction(i); + if (instr.getOffset() == offset) { return i; } } @@ -207,64 +148,54 @@ public class DWARFExpression { @Override public String toString() { - return toString(-1, false, false); + return toString(-1, false, false, null); } - public String toString(int caretPosition, boolean newlines, boolean offsets) { - StringBuilder sb = new StringBuilder(); - for (int step = 0; step < operations.size(); step++) { - DWARFExpressionOperation op = operations.get(step); + public String toString(DWARFCompilationUnit cu) { + return toString(-1, false, false, cu.getProgram().getRegisterMappings()); + } - if (step != 0) { - sb.append("; "); - if (newlines) { - sb.append('\n'); - } + /** + * Returns a formatted string representing this expression. + * + * @param caretPosition index of which instruction to highlight as being the current + * instruction, or -1 to not highlight any instruction + * @param newlines boolean flag, if true each instruction will be on its own line + * @param offsets boolean flag, if true the byte offset in the expression will be listed + * next to each instruction + * @param regMapping mapping of dwarf to ghidra registers + * @return formatted string + */ + public String toString(int caretPosition, boolean newlines, boolean offsets, + DWARFRegisterMappings regMapping) { + + StringBuilder sb = new StringBuilder(); + for (int instrIndex = 0; instrIndex < instructions.size(); instrIndex++) { + DWARFExpressionInstruction instr = instructions.get(instrIndex); + + if (instrIndex != 0) { + sb.append(newlines ? "\n" : "; "); } if (offsets) { - sb.append(String.format("%3d [%03x]: ", step, op.getOffset())); + sb.append("%3d [%03x]: ".formatted(instrIndex, instr.getOffset())); } - if (caretPosition == step) { + if (caretPosition == instrIndex) { sb.append(" ==> ["); } - int opcode = op.getOpCode(); - if (DWARFExpressionOpCodes.isValidOpcode(opcode)) { - sb.append(DWARFExpressionOpCodes.toString(opcode)); - } - else { - if (opcode >= DWARFExpressionOpCodes.DW_OP_lo_user && - opcode <= DWARFExpressionOpCodes.DW_OP_hi_user) { - int relOpCode = opcode - DWARFExpressionOpCodes.DW_OP_lo_user; - sb.append( - DWARFExpressionOpCodes.toString(DWARFExpressionOpCodes.DW_OP_lo_user) + - "+" + relOpCode + "[" + opcode + "]"); - } - else { - sb.append("DW_OP_UNKNOWN[" + opcode + "]"); - } - } - for (int operandIndex = 0; operandIndex < op.operands.length; operandIndex++) { + sb.append(instr.getOpCode().toString(regMapping)); + for (int operandIndex = 0; operandIndex < instr.getOperandCount(); operandIndex++) { if (operandIndex == 0) { sb.append(':'); } sb.append(' '); - DWARFExpressionOperandType operandType = op.operandTypes[operandIndex]; - if (operandType != DWARFExpressionOperandType.SIZED_BLOB) { - long operandValue = op.operands[operandIndex]; - - sb.append(DWARFExpressionOperandType.valueToString(operandValue, operandType)); - } - else { - sb.append(NumericUtilities.convertBytesToString(op.blob, " ")); - } + sb.append(instr.getOperandRepresentation(operandIndex)); } - if (caretPosition == step) { + if (caretPosition == instrIndex) { sb.append(" ] <=="); } - if (opcode == DWARFExpressionOpCodes.DW_OP_bra || - opcode == DWARFExpressionOpCodes.DW_OP_skip) { - long destOffset = op.getOperandValue(0) + op.getOffset(); - int destIndex = findOpByOffset(destOffset); + if (instr.opcode == DW_OP_bra || instr.opcode == DW_OP_skip) { + long destOffset = instr.getOperandValue(0) + instr.getOffset(); + int destIndex = findInstructionByOffset(destOffset); sb.append(String.format(" /* dest index: %d, offset: %03x */", destIndex, (int) destOffset)); } @@ -272,4 +203,23 @@ public class DWARFExpression { return sb.toString(); } + + @Override + public int hashCode() { + return Objects.hash(instructions); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof DWARFExpression)) { + return false; + } + DWARFExpression other = (DWARFExpression) obj; + return Objects.equals(instructions, other.instructions); + } + + } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionEvaluator.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionEvaluator.java index 210d480e07..f7c21389a3 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionEvaluator.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionEvaluator.java @@ -15,25 +15,25 @@ */ package ghidra.app.util.bin.format.dwarf.expression; -import static ghidra.app.util.bin.format.dwarf.expression.DWARFExpressionOpCodes.*; +import static ghidra.app.util.bin.format.dwarf.expression.DWARFExpressionOpCode.*; import java.io.IOException; -import java.util.ArrayDeque; -import java.util.Objects; +import java.util.*; import ghidra.app.util.bin.format.dwarf.*; import ghidra.app.util.bin.format.dwarf.attribs.DWARFForm; +import ghidra.program.model.address.Address; +import ghidra.program.model.lang.Language; import ghidra.program.model.lang.Register; +import ghidra.program.model.pcode.Varnode; +import ghidra.program.model.scalar.Scalar; /** - * Evaluates a subset of DWARF expression opcodes. + * Evaluates a {@link DWARFExpression}. *

- * Limitations:

- * Can not access memory during evaluation of expressions.
- * Some opcodes must be the last operation in the expression (deref, regX)
- * Can only specify offset from register for framebase and stack relative
- *

- * Result can be a numeric value (ie. static address) or a register 'name' or a stack based offset. + * If an instruction needs a value in a register or memory location, the current {@link ValueReader} + * callback will be called to fetch the value. The default implementation is to throw an exception, + * but future work may plug in a constant propagation callback. */ public class DWARFExpressionEvaluator { @@ -42,8 +42,23 @@ public class DWARFExpressionEvaluator { */ private static final int DEFAULT_MAX_STEP_COUNT = 1000; + public interface ValueReader { + Object getValue(Varnode vn) throws DWARFExpressionValueException; + + ValueReader DUMMY = new ValueReader() { + @Override + public Object getValue(Varnode vn) throws DWARFExpressionValueException { + throw new DWARFExpressionValueException(vn); + } + }; + + } + private final DWARFProgram dprog; private final DWARFCompilationUnit cu; + private final Language lang; + + private ValueReader valReader = ValueReader.DUMMY; private int maxStepCount = DEFAULT_MAX_STEP_COUNT; @@ -52,519 +67,69 @@ public class DWARFExpressionEvaluator { /** * The subprogram's DW_AT_frame_base value */ - private long frameOffset = -1; - private int lastRegister = -1; - /** - * The value at the top of the stack is a framebase offset - */ - private boolean lastStackRelative; + private Varnode frameBaseVal; - /** - * Indicates that the result of the expression is held in register {@link #lastRegister} - */ - private boolean registerLoc; - - /** - * Indicates that the result of the expression is pointed to by the value in - * register {{@link #lastRegister} (ie. lastRegister is a pointer to the result) - */ - private boolean isDeref; - - private boolean dwarfStackValue;// true if dwarf says that value does not exist in memory - private boolean useUnknownRegister; - private ArrayDeque stack = new ArrayDeque(); + private List stack = new ArrayList<>(); private DWARFExpression expr; - private DWARFExpressionOperation currentOp; - private int currentOpIndex = -1; + private DWARFExpressionInstruction instr; + private int instrIndex = -1; + private int stepCount = 0; public DWARFExpressionEvaluator(DWARFCompilationUnit cu) { this.cu = cu; this.dprog = cu.getProgram(); this.registerMappings = Objects.requireNonNullElse(dprog.getRegisterMappings(), DWARFRegisterMappings.DUMMY); + this.lang = dprog.getGhidraProgram().getLanguage(); } public DWARFCompilationUnit getDWARFCompilationUnit() { return cu; } - public void setFrameBase(long fb) { - this.frameOffset = fb; + public DWARFExpression getExpr() { + return expr; } - public void push(long l) { - stack.push(l); - lastRegister = -1; - lastStackRelative = false; - registerLoc = false; + public boolean isEmpty() { + return stack.isEmpty(); } - public long peek() throws DWARFExpressionException { - if (stack.isEmpty()) { - throw new DWARFExpressionException("DWARF expression stack empty"); - } - return stack.peek().longValue(); + public int getPtrSize() { + return cu.getPointerSize(); } - public long pop() throws DWARFExpressionException { - if (stack.isEmpty()) { - throw new DWARFExpressionException("DWARF expression stack empty"); - } - return stack.pop().longValue(); + public void setFrameBaseStackLocation(int offset) { + this.frameBaseVal = newStackVarnode(offset, 0); } - /** - * Returns the {@link Register register} that holds the contents of the object that the - * {@link DWARFExpression expression} points to. - *

- * Note, you should check {@link #isDeref()} to see if the register is just a pointer - * to the object instead of the object itself. - * - * @return - */ - public Register getTerminalRegister() { - return registerMappings.getGhidraReg(lastRegister); + public void setFrameBaseVal(Varnode frameBaseVal) { + this.frameBaseVal = frameBaseVal; } - public boolean isDeref() { - return isDeref; + public void setValReader(ValueReader valReader) { + this.valReader = valReader; } - public DWARFExpression readExpr(byte[] exprBytes) throws DWARFExpressionException { - DWARFExpression tmp = DWARFExpression.read(exprBytes, cu.getPointerSize(), - dprog.isLittleEndian(), cu.getIntSize()); - return tmp; - } - - public DWARFExpressionResult evaluate(byte[] exprBytes) throws DWARFExpressionException { - return evaluate(readExpr(exprBytes)); - } - - /** - * @param _expr - * @param stackArgs - pushed 0..N, so stackArgs[0] will be deepest, stackArgs[N] will be topmost. - * @return - * @throws DWARFExpressionException - */ - public DWARFExpressionResult evaluate(DWARFExpression _expr, long... stackArgs) - throws DWARFExpressionException { - for (long l : stackArgs) { - push(l); - } - return evaluate(_expr); - } - - public DWARFExpressionResult evaluate(DWARFExpression _expr) throws DWARFExpressionException { - this.expr = _expr; - currentOp = null; - int stepCount = 0; - for (currentOpIndex = - 0; currentOpIndex < expr.getOpCount(); currentOpIndex++, stepCount++) { - currentOp = expr.getOp(currentOpIndex); - - try { - if (stepCount >= maxStepCount) { - throw new DWARFExpressionException( - "Excessive expression run length, terminating after " + stepCount + - " operations"); + public ValueReader withStaticStackRegisterValues(Integer stackOffset, + Integer stackFrameOffset) { + return new ValueReader() { + @Override + public Varnode getValue(Varnode vn) throws DWARFExpressionValueException { + Register reg; + if (vn.isRegister() && (reg = lang.getRegister(vn.getAddress(), 0)) != null) { + if (reg == registerMappings.getStackFrameRegister() && + stackFrameOffset != null) { + return newStackVarnode(stackFrameOffset, 0); + } + if (reg == registerMappings.getStackRegister() && stackOffset != null) { + return newStackVarnode(stackOffset, 0); + } } - if (Thread.currentThread().isInterrupted()) { - throw new DWARFExpressionException( - "Thread interrupted while evaluating DWARF expression, terminating after " + - stepCount + " operations"); - } - - _preValidateCurrentOp(); - _evaluateCurrentOp(); + throw new DWARFExpressionValueException(vn); } - catch (DWARFExpressionException dee) { - if (dee.getExpression() == null) { - dee.setExpression(expr); - dee.setStep(currentOpIndex); - } - throw dee; - } - } - - return new DWARFExpressionResult(stack); - } - - public String getStackAsString() { - StringBuilder sb = new StringBuilder(); - - int stackindex = 0; - for (Long stackElement : stack) { - sb.append(String.format("%3d: [%08x] %d\n", stackindex, stackElement, stackElement)); - stackindex++; - } - return sb.toString(); - } - - private void _preValidateCurrentOp() throws DWARFExpressionException { - // throw a DWARFExpressionException if op not valid in this context - int opcode = currentOp.getOpCode(); - boolean isLastOperation = (currentOpIndex == expr.getLastActiveOpIndex()); - - switch (opcode) { - case DW_OP_fbreg: - if (frameOffset == -1) { - throw new DWARFExpressionException( - "Frame base has not been set, DW_OP_fbreg can not be evaluated"); - } - break; - case DW_OP_deref: - if (!(registerLoc || lastStackRelative)) { - throw new DWARFExpressionException( - "Can not evaluate DW_OP_deref for non-register location"); - } - if (!isLastOperation) { - throw new DWARFExpressionException( - "Non-terminal DW_OP_deref can't be evaluated"); - } - - break; - default: - if (((opcode >= DW_OP_reg0 && opcode <= DW_OP_reg31) || (opcode == DW_OP_regx)) && - (!isLastOperation)) { - throw new DWARFExpressionException( - "Non-terminal DW_OP_reg? can't be evaluated"); - } - } - - } - - private void _evaluateCurrentOp() throws DWARFExpressionException { - int opcode = currentOp.getOpCode(); - if (DWARFExpressionOpCodes.UNSUPPORTED_OPCODES.contains(opcode)) { - throw new DWARFExpressionException( - "Can not evaluate unsupported opcode " + DWARFExpressionOpCodes.toString(opcode)); - } - - if (opcode >= DW_OP_lit0 && opcode <= DW_OP_lit31) { - push(currentOp.getRelativeOpCodeOffset(DW_OP_lit0)); - } - else if (opcode >= DW_OP_breg0 && opcode <= DW_OP_breg31) { - // Retrieve value held in register X and add offset from operand and push result on stack. - // Fake it using zero as register value. - // Mainly only useful if offset is zero or if non-zero the register happens to - // be the stack pointer. - long offset = currentOp.getOperandValue(0); - push(0 /*fake register value */ + offset); - lastRegister = currentOp.getRelativeOpCodeOffset(DW_OP_breg0); - - if (lastRegister == registerMappings.getDWARFStackPointerRegNum()) { - lastStackRelative = true; - } - else { - useUnknownRegister = true; - if (offset == 0) { - // if offset is 0, we can represent the location as a ghidra register location - // also implies a deref by the user of this location info - registerLoc = true; - } - } - - } - else if (opcode >= DW_OP_reg0 && opcode <= DW_OP_reg31) { - push(0);// TODO: not sure why we are pushing a zero on stack, not part of DWARF std. - lastRegister = currentOp.getRelativeOpCodeOffset(DW_OP_reg0); - registerLoc = true; - } - else if (opcode == DW_OP_regx) { - push(0);// TODO: not sure why we are pushing a zero on stack, not part of DWARF std. - lastRegister = (int) currentOp.getOperandValue(0); - registerLoc = true; - } - else { - switch (opcode) { - case DW_OP_addr: - case DW_OP_const1u: - case DW_OP_const2u: - case DW_OP_const4u: - case DW_OP_const8u: - case DW_OP_const1s: - case DW_OP_const2s: - case DW_OP_const4s: - case DW_OP_const8s: - case DW_OP_constu: - case DW_OP_consts: - push(currentOp.getOperandValue(0)); - break; - // Register Based Addressing - case DW_OP_fbreg: - push(frameOffset + currentOp.getOperandValue(0)); - lastStackRelative = true; - break; - // Stack Operations - case DW_OP_dup: - push(peek()); - break; - case DW_OP_drop: - pop(); - break; - case DW_OP_pick: { - long index = currentOp.getOperandValue(0); - if (index >= stack.size()) { - throw new DWARFExpressionException( - "Invalid index for DW_OP_pick: " + index); - } - dw_op_pick((int) index); - - break; - } - case DW_OP_over: { - if (stack.size() < 2) { - throw new DWARFExpressionException( - "Not enough items on stack[size=" + stack.size() + "] for DW_OP_over"); - } - dw_op_pick(1); - - break; - } - case DW_OP_swap: { - long firstValue = pop(); - long secondValue = pop(); - push(firstValue); - push(secondValue); - break; - } - case DW_OP_rot: { - long firstValue = pop(); - long secondValue = pop(); - long thirdValue = pop(); - push(firstValue); - push(thirdValue); - push(secondValue); - break; - } - case DW_OP_deref: { - isDeref = true; - // Real deref should pop the top value from stack, deref it, - // and push value found at that address on stack. - // Since we can only handle the subset of deref usages that are - // register or framebased, leave the stack alone so that the - // register or framebase offset can be accessed. - break; - } - - case DW_OP_call_frame_cfa: { - push(registerMappings.getCallFrameCFA()); - lastStackRelative = true; - break; - } - - // Arithmetic and Logical Operations - case DW_OP_abs: { - push(Math.abs(pop())); - break; - } - case DW_OP_and: {// bitwise and - long firstValue = pop(); - long secondValue = pop(); - push(firstValue & secondValue); - break; - } - case DW_OP_div: { - long firstValue = pop(); - long secondValue = pop(); - if (firstValue == 0) { - throw new DWARFExpressionException("Divide by zero"); - } - push(secondValue / firstValue); - break; - } - case DW_OP_minus: { - long firstValue = pop(); - long secondValue = pop(); - push(secondValue - firstValue); - break; - } - case DW_OP_mod: { - long firstValue = pop(); - long secondValue = pop(); - if (firstValue == 0) { - throw new DWARFExpressionException("Divide by zero"); - } - push(secondValue % firstValue); - break; - } - case DW_OP_mul: { - long firstValue = pop(); - long secondValue = pop(); - push(firstValue * secondValue); - break; - } - case DW_OP_neg: { - long firstValue = pop(); - push(-firstValue); - break; - } - case DW_OP_not: {// bitwise neg - long firstValue = pop(); - push(~firstValue); - break; - } - case DW_OP_or: {// bitwise or - long firstValue = pop(); - long secondValue = pop(); - push(firstValue | secondValue); - break; - } - case DW_OP_plus: { - long firstValue = pop(); - long secondValue = pop(); - push(firstValue + secondValue); - break; - } - case DW_OP_plus_uconst: { - long firstValue = pop(); - long value = currentOp.getOperandValue(0); - push(firstValue + value); - break; - } - case DW_OP_shl: { - long firstValue = pop(); - long secondValue = pop(); - push(secondValue << firstValue); - break; - } - case DW_OP_shr: { - long firstValue = pop(); - long secondValue = pop(); - push(secondValue >>> firstValue); - break; - } - case DW_OP_shra: { - long firstValue = pop(); - long secondValue = pop(); - push(secondValue >> firstValue); - break; - } - case DW_OP_xor: { - long firstValue = pop(); - long secondValue = pop(); - push(firstValue ^ secondValue); - break; - } - // Control Flow Operations, values treated as signed for comparison - case DW_OP_le: { - long firstValue = pop(); - long secondValue = pop(); - push((secondValue <= firstValue) ? 1L : 0L); - break; - } - case DW_OP_ge: { - long firstValue = pop(); - long secondValue = pop(); - push((secondValue >= firstValue) ? 1L : 0L); - break; - } - case DW_OP_eq: { - long firstValue = pop(); - long secondValue = pop(); - push((secondValue == firstValue) ? 1L : 0L); - break; - } - case DW_OP_lt: { - long firstValue = pop(); - long secondValue = pop(); - push((secondValue < firstValue) ? 1L : 0L); - break; - } - case DW_OP_gt: { - long firstValue = pop(); - long secondValue = pop(); - push((secondValue > firstValue) ? 1L : 0L); - break; - } - case DW_OP_ne: { - long firstValue = pop(); - long secondValue = pop(); - push((secondValue != firstValue) ? 1L : 0L); - break; - } - case DW_OP_skip: { - long destOffset = currentOp.getOperandValue(0) + currentOp.getOffset(); - int newStep = expr.findOpByOffset(destOffset); - if (newStep == -1) { - throw new DWARFExpressionException("Invalid skip offset " + destOffset); - } - currentOpIndex = newStep - 1;// 1 before the target op index because the for() loop will ++ the index value - break; - } - case DW_OP_bra: { - long destOffset = currentOp.getOperandValue(0) + currentOp.getOffset(); - long firstValue = pop(); - if (firstValue != 0) { - int newStep = expr.findOpByOffset(destOffset); - if (newStep == -1) { - throw new DWARFExpressionException("Invalid bra offset " + destOffset); - } - currentOpIndex = newStep - 1;// 1 before the target op index because the for() loop will ++ the index value - } - break; - } - - // Special Operations - case DW_OP_nop: { - break; - } - case DW_OP_stack_value: - // This op is a flag to the debugger that the requested value does not exist in memory - // (on the host) but that the result of this expression gives you value - dwarfStackValue = true; - break; - - case DW_OP_addrx: - try { - long addr = dprog.getAddress(DWARFForm.DW_FORM_addrx, - currentOp.getOperandValue(0), cu); - push(addr); - break; - } - catch (IOException e) { - throw new DWARFExpressionException( - "Invalid indirect address index: " + currentOp.getOperandValue(0)); - } - case DW_OP_constx: // same as addrx, but different relocation-able specifications - try { - long addr = dprog.getAddress(DWARFForm.DW_FORM_addrx, - currentOp.getOperandValue(0), cu); - push(addr); - break; - } - catch (IOException e) { - throw new DWARFExpressionException( - "Invalid indirect address index: " + currentOp.getOperandValue(0)); - } - - default: - throw new DWARFExpressionException("Unimplemented DWARF expression opcode " + - DWARFExpressionOpCodes.toString(opcode)); - } - } - } - - private void dw_op_pick(int index) { - int stackindex = 0; - for (Long stackElement : stack) { - if (stackindex == index) { - push(stackElement); - break; - } - stackindex++; - } - } - - @Override - public String toString() { - return "DWARFExpressionEvaluator [frameOffset=" + frameOffset + ", lastRegister=" + - lastRegister + - ", lastStackRelative=" + lastStackRelative + ", registerLoc=" + registerLoc + - ", isDeref=" + isDeref + ", dwarfStackValue=" + dwarfStackValue + - ", useUnknownRegister=" + useUnknownRegister + "]\nStack:\n" + getStackAsString() + - "\n" + (expr != null ? expr.toString(currentOpIndex, true, true) : "no expr"); + }; } public int getMaxStepCount() { @@ -575,28 +140,606 @@ public class DWARFExpressionEvaluator { this.maxStepCount = maxStepCount; } - public boolean isDwarfStackValue() { - return this.dwarfStackValue; + public void push(Address addr) { + push(new Varnode(addr, 0)); } - public boolean useUnknownRegister() { - return useUnknownRegister; + public void push(Register reg) { + push(new Varnode(reg.getAddress(), reg.getMinimumByteSize())); } - public boolean isRegisterLocation() { - return registerLoc; + public void push(boolean b) { + push(b ? 1L : 0L); } - public Register getLastRegister() { - return registerMappings.getGhidraReg(lastRegister); + public void push(long l) { + push(new Scalar(getPtrSize() * 8, l)); } - public int getRawLastRegister() { - return lastRegister; + public void push(Object val) { + stack.addLast(val); } - public boolean isStackRelative() { - return lastStackRelative; + /** + * Peek at the top value of the stack. + * + * @return top value of the stack + * @throws DWARFExpressionException if stack is empty + */ + public Object peek() throws DWARFExpressionException { + if (stack.isEmpty()) { + throw new DWARFExpressionException("DWARF expression stack empty"); + } + return stack.getLast(); } + /** + * Pop the top value off the stack. + * + * @return top value of the stack + * @throws DWARFExpressionException if stack is empty + */ + public Object pop() throws DWARFExpressionException { + if (stack.isEmpty()) { + throw new DWARFExpressionException("DWARF expression stack empty"); + } + return stack.removeLast(); + } + + /** + * Pop the top value off the stack, and coerce it into a scalar. + * + * @return top value of the stack, as a scalar + * @throws DWARFExpressionException if stack is empty or value can not be used as a scalar + */ + public Scalar popScalar() throws DWARFExpressionException { + return stackValueToScalar(pop()); + } + + private Scalar stackValueToScalar(Object val) throws DWARFExpressionException { + switch (val) { + case Scalar s: + return s; + case Varnode varnode: + if (varnode.isRegister()) { + // try to deref the register and hopefully get a const varnode + return stackValueToScalar(valReader.getValue(varnode)); + } + if (DWARFUtil.isConstVarnode(varnode)) { + return new Scalar(varnode.getSize() * 8, varnode.getOffset()); + } + // fall thru, throw exception + default: + } + throw new DWARFExpressionException( + "Unable to convert stack value to scalar: %s".formatted(val)); + } + + /** + * Pop the top value off the stack, and coerce it into a varnode. + * + * @return top value of the stack, as a varnode + * @throws DWARFExpressionException if stack is empty or value can not be used as a varnode + */ + public Varnode popVarnode() throws DWARFExpressionException { + Object tmp = pop(); + return switch (tmp) { + case Scalar s when s.bitLength() == cu.getPointerSize() * 8 -> newAddrVarnode( + s.getUnsignedValue()); + case Varnode varnode -> varnode; + default -> throw new DWARFExpressionException( + "Unable to convert DWARF expression stack value %s to address".formatted(tmp)); + }; + } + + /** + * Pop the top value off the stack, and coerce it into a scalar long. + * + * @return top value of the stack, as a scalar long + * @throws DWARFExpressionException if stack is empty or value can not be used as a long + */ + public long popLong() throws DWARFExpressionException { + Scalar s = popScalar(); + return s.getValue(); + } + + /** + * Executes the instructions found in the expression. + * + * @param exprBytes raw bytes of the expression + * @throws DWARFExpressionException if error + */ + public void evaluate(byte[] exprBytes) throws DWARFExpressionException { + evaluate(DWARFExpression.read(exprBytes, cu)); + } + + /** + * Executes the instructions found in the expression. + * + * @param exprBytes raw bytes of the expression + * @param stackArgs any values to push onto the stack before execution + * @throws DWARFExpressionException if error + */ + public void evaluate(byte[] exprBytes, long... stackArgs) throws DWARFExpressionException { + evaluate(DWARFExpression.read(exprBytes, cu), stackArgs); + } + + /** + * Sets the current expression. + * + * @param expr {@link DWARFExpression} + */ + public void setExpression(DWARFExpression expr) { + this.expr = expr; + instr = null; + instrIndex = 0; + stepCount = 0; + } + + /** + * {@return true if there are instructions that can be evaluated} + */ + public boolean hasNext() { + return instrIndex < expr.getInstructionCount(); + } + + /** + * Evaluates the next instruction in the expression. + * + * @return true if there are more instructions + * @throws DWARFExpressionException if error + */ + public boolean step() throws DWARFExpressionException { + if (hasNext()) { + try { + evaluateInstruction(expr.getInstruction(instrIndex)); + instrIndex++; + stepCount++; + } + catch (DWARFExpressionException dee) { + if (dee.getExpression() == null) { + dee.setExpression(expr); + dee.setInstructionIndex(instrIndex); + } + throw dee; + } + } + + return hasNext(); + } + + /** + * Executes the instructions found in the expression. + * + * @param expr {@link DWARFException} to evaluate + * @param stackArgs - pushed 0..N, so stackArgs[0] will be deepest, stackArgs[N] will be topmost. + * @throws DWARFExpressionException if error + */ + public void evaluate(DWARFExpression expr, long... stackArgs) + throws DWARFExpressionException { + for (long l : stackArgs) { + push(l); + } + evaluate(expr); + } + + public void evaluate(DWARFExpression expr) throws DWARFExpressionException { + setExpression(expr); + while (hasNext()) { + if (stepCount >= maxStepCount) { + throw new DWARFExpressionException( + "Excessive expression run length, terminating after %d operations" + .formatted(stepCount)); + } + if (Thread.currentThread().isInterrupted()) { + throw new DWARFExpressionException( + "Thread interrupted while evaluating DWARF expression, terminating after %d operations" + .formatted(stepCount)); + } + step(); + } + } + + private Register getReg(int dwarfRegNum) throws DWARFExpressionException { + Register reg = registerMappings.getGhidraReg(dwarfRegNum); + if (reg == null) { + throw new DWARFExpressionException( + "Unknown/unmapped DWARF register: %d".formatted(dwarfRegNum)); + } + return reg; + } + + private void evaluateInstruction(DWARFExpressionInstruction _instr) + throws DWARFExpressionException { + this.instr = _instr; + if (DWARFExpressionOpCode.isInRange(instr.opcode, DW_OP_lit0, DW_OP_lit31)) { + push(instr.opcode.getRelativeOpCodeOffset(DW_OP_lit0)); + } + else if (DWARFExpressionOpCode.isInRange(instr.opcode, DW_OP_breg0, DW_OP_breg31)) { + // Retrieve address held in register X and add offset from operand0 and push result on stack. + Register register = getReg(instr.opcode.getRelativeOpCodeOffset(DW_OP_breg0)); + long offset = instr.getOperandValue(0); + Object regVal = valReader.getValue(newRegisterVarnode(register)); + if (regVal instanceof Varnode regVN && + (DWARFUtil.isStackVarnode(regVN) || regVN.isConstant())) { + push(new Varnode(regVN.getAddress().add(offset), 0)); + } + else if (regVal instanceof Scalar s) { + push(s.getValue() + offset); + } + else { + throw new DWARFExpressionException("Unable to deref register value " + regVal); + } + } + else if (DWARFExpressionOpCode.isInRange(instr.opcode, DW_OP_reg0, DW_OP_reg31)) { + Register register = getReg(instr.opcode.getRelativeOpCodeOffset(DW_OP_reg0)); + Object regVal = valReader.getValue(newRegisterVarnode(register)); + push(regVal); + } + else { + switch (instr.opcode) { + case DW_OP_addr: + push(dprog.getDataAddress(instr.getOperandValue(0))); + break; + + case DW_OP_const1u: + case DW_OP_const2u: + case DW_OP_const4u: + case DW_OP_const8u: + case DW_OP_const1s: + case DW_OP_const2s: + case DW_OP_const4s: + case DW_OP_const8s: + case DW_OP_constu: + case DW_OP_consts: + push(instr.getOperandValue(0)); + break; + + // Register Based Addressing + case DW_OP_regx: + Register register = getReg((int) instr.getOperandValue(0)); + push(register); + break; + case DW_OP_fbreg: { + if (frameBaseVal == null) { + throw new DWARFExpressionException( + "Frame base has not been set, DW_OP_fbreg can not be evaluated"); + } + long fbOffset = instr.getOperandValue(0); + push(new Varnode(frameBaseVal.getAddress().add(fbOffset), 0)); + } + // Stack Operations + case DW_OP_dup: + push(peek()); + break; + case DW_OP_drop: + pop(); + break; + case DW_OP_pick: { + int index = (int) instr.getOperandValue(0); + if (index >= stack.size()) { + throw new DWARFExpressionException( + "Invalid index for DW_OP_pick: " + index); + } + Object elem = stack.get(stack.size() - index - 1); + push(elem); + break; + } + case DW_OP_over: { + if (stack.size() < 2) { + throw new DWARFExpressionException( + "Not enough items on stack[size=%d] for DW_OP_over" + .formatted(stack.size())); + } + push(stack.get(stack.size() - 2)); + break; + } + case DW_OP_swap: { + Object firstValue = pop(); + Object secondValue = pop(); + push(firstValue); + push(secondValue); + break; + } + case DW_OP_rot: { + Object firstValue = pop(); + Object secondValue = pop(); + Object thirdValue = pop(); + push(firstValue); + push(thirdValue); + push(secondValue); + break; + } + case DW_OP_deref: { + // Treat top stack value as a location, deref it and fetch a ptrSize'd value + // and push it on stack + + if (instrIndex == expr.getInstructionCount() - 1) { + // If this was the last instruction, throw a special exception that lets + // the caller figure out what happened and accommodate this in some + // situations. + // TODO: trailing NOPs were skipped when checking this condition previously... dunno if needed for real + Varnode location = popVarnode(); + throw new DWARFExpressionTerminalDerefException(instr, location); + } + throw new DWARFExpressionUnsupportedOpException(instr); + } + + case DW_OP_call_frame_cfa: { + if (!registerMappings.hasStaticCFA()) { + throw new DWARFExpressionException( + "CFA not specified in DWARF register mappings for this arch"); + } + push(newStackVarnode(registerMappings.getCallFrameCFA(), 0)); + break; + } + + // Arithmetic and Logical Operations + case DW_OP_abs: { + Scalar val = popScalar(); + Scalar absVal = new Scalar(val.bitLength(), Math.abs(val.getSignedValue())); + push(absVal); + break; + } + case DW_OP_and: {// bitwise and + Scalar firstValue = popScalar(); + Scalar secondValue = popScalar(); + long tmp = firstValue.getUnsignedValue() & secondValue.getUnsignedValue(); + int bitCount = Math.max(firstValue.bitLength(), secondValue.bitLength()); + push(new Scalar(bitCount, tmp)); + break; + } + case DW_OP_div: { + Scalar firstValue = popScalar(); + Scalar secondValue = popScalar(); + if (firstValue.getValue() == 0) { + throw new DWARFExpressionException("Divide by zero"); + } + long tmp = secondValue.getValue() / firstValue.getValue(); + push(new Scalar(secondValue.bitLength(), tmp)); + break; + } + case DW_OP_minus: { + Scalar firstValue = popScalar(); + Scalar secondValue = popScalar(); + long tmp = secondValue.getValue() - firstValue.getValue(); + int bitCount = Math.max(firstValue.bitLength(), secondValue.bitLength()); + push(new Scalar(bitCount, tmp)); + break; + } + case DW_OP_mod: { + Scalar firstValue = popScalar(); + Scalar secondValue = popScalar(); + if (firstValue.getValue() == 0) { + throw new DWARFExpressionException("Divide by zero"); + } + long tmp = secondValue.getValue() % firstValue.getValue(); + push(new Scalar(secondValue.bitLength(), tmp)); + break; + } + case DW_OP_mul: { + Scalar firstValue = popScalar(); + Scalar secondValue = popScalar(); + long tmp = secondValue.getValue() * firstValue.getValue(); + int bitCount = Math.max(firstValue.bitLength(), secondValue.bitLength()); + push(new Scalar(bitCount, tmp)); + break; + } + case DW_OP_neg: { + Scalar firstValue = popScalar(); + long tmp = -firstValue.getSignedValue(); + push(new Scalar(firstValue.bitLength(), tmp)); + break; + } + case DW_OP_not: {// bitwise neg + Scalar firstValue = popScalar(); + long tmp = ~firstValue.getValue(); + push(new Scalar(firstValue.bitLength(), tmp)); + break; + } + case DW_OP_or: {// bitwise or + Scalar firstValue = popScalar(); + Scalar secondValue = popScalar(); + long tmp = secondValue.getValue() | firstValue.getValue(); + int bitCount = Math.max(firstValue.bitLength(), secondValue.bitLength()); + push(new Scalar(bitCount, tmp)); + break; + } + case DW_OP_plus: { + Scalar firstValue = popScalar(); + Scalar secondValue = popScalar(); + long tmp = secondValue.getValue() + firstValue.getValue(); + int bitCount = Math.max(firstValue.bitLength(), secondValue.bitLength()); + push(new Scalar(bitCount, tmp)); + break; + } + case DW_OP_plus_uconst: { + Scalar firstValue = popScalar(); + long opValue = instr.getOperandValue(0); + long tmp = firstValue.getValue() + opValue; + push(new Scalar(firstValue.bitLength(), tmp)); + break; + } + case DW_OP_shl: { + Scalar firstValue = popScalar(); + Scalar secondValue = popScalar(); + long tmp = secondValue.getValue() << firstValue.getValue(); + push(new Scalar(secondValue.bitLength(), tmp)); + break; + } + case DW_OP_shr: { + Scalar firstValue = popScalar(); + Scalar secondValue = popScalar(); + long tmp = secondValue.getValue() >>> firstValue.getValue(); + push(new Scalar(secondValue.bitLength(), tmp)); + break; + } + case DW_OP_shra: { + Scalar firstValue = popScalar(); + Scalar secondValue = popScalar(); + long tmp = secondValue.getValue() >> firstValue.getValue(); + push(new Scalar(secondValue.bitLength(), tmp)); + break; + } + case DW_OP_xor: { + Scalar firstValue = popScalar(); + Scalar secondValue = popScalar(); + long tmp = secondValue.getValue() ^ firstValue.getValue(); + int bitCount = Math.max(firstValue.bitLength(), secondValue.bitLength()); + push(new Scalar(bitCount, tmp)); + break; + } + // Control Flow Operations, values treated as signed for comparison + case DW_OP_le: { + Scalar firstValue = popScalar(); + Scalar secondValue = popScalar(); + push(secondValue.getSignedValue() <= firstValue.getSignedValue()); + break; + } + case DW_OP_ge: { + Scalar firstValue = popScalar(); + Scalar secondValue = popScalar(); + push(secondValue.getSignedValue() >= firstValue.getSignedValue()); + break; + } + case DW_OP_eq: { + Scalar firstValue = popScalar(); + Scalar secondValue = popScalar(); + push(secondValue.getValue() == firstValue.getValue()); + break; + } + case DW_OP_lt: { + Scalar firstValue = popScalar(); + Scalar secondValue = popScalar(); + push(secondValue.getSignedValue() < firstValue.getSignedValue()); + break; + } + case DW_OP_gt: { + Scalar firstValue = popScalar(); + Scalar secondValue = popScalar(); + push(secondValue.getSignedValue() > firstValue.getSignedValue()); + break; + } + case DW_OP_ne: { + Scalar firstValue = popScalar(); + Scalar secondValue = popScalar(); + push(secondValue.getSignedValue() != firstValue.getSignedValue()); + break; + } + case DW_OP_skip: { + long destOffset = instr.getOperandValue(0) + instr.getOffset(); + int newInstrIndex = expr.findInstructionByOffset(destOffset); + if (newInstrIndex == -1) { + throw new DWARFExpressionException("Invalid skip offset " + destOffset); + } + instrIndex = newInstrIndex - 1;// 1 before the target op index because the for() loop will ++ the index value + break; + } + case DW_OP_bra: { + long destOffset = instr.getOperandValue(0) + instr.getOffset(); + Scalar firstValue = popScalar(); + if (firstValue.getValue() != 0) { + int newInstrIndex = expr.findInstructionByOffset(destOffset); + if (newInstrIndex == -1) { + throw new DWARFExpressionException("Invalid bra offset " + destOffset); + } + instrIndex = newInstrIndex - 1;// 1 before the target op index because the for() loop will ++ the index value + } + break; + } + + // Special Operations + case DW_OP_nop: { + break; + } + case DW_OP_stack_value: + // This op is a flag to the debugger that the requested value does not exist in memory + // (on the host) but that the result of this expression gives you the value + throw new DWARFExpressionUnsupportedOpException(instr); + case DW_OP_addrx: + try { + long addr = dprog.getAddress(DWARFForm.DW_FORM_addrx, + instr.getOperandValue(0), cu); + push(addr); + break; + } + catch (IOException e) { + throw new DWARFExpressionException( + "Invalid indirect address index: " + instr.getOperandValue(0)); + } + case DW_OP_constx: // same as addrx, but different relocation-able specifications + try { + long addr = dprog.getAddress(DWARFForm.DW_FORM_addrx, + instr.getOperandValue(0), cu); + push(addr); + break; + } + catch (IOException e) { + throw new DWARFExpressionException( + "Invalid indirect address index: " + instr.getOperandValue(0)); + } + + default: + throw new DWARFExpressionUnsupportedOpException(instr); + + } + } + } + + private Varnode newStackVarnode(long offset, int size) { + return new Varnode(dprog.getStackSpace().getAddress(offset), size); + } + + private Varnode newRegisterVarnode(Register reg) { + return new Varnode(reg.getAddress(), reg.getMinimumByteSize()); + } + + private Varnode newAddrVarnode(long l) { + return new Varnode(dprog.getDataAddress(l), cu.getPointerSize()); + } + + @Override + public String toString() { + return """ + DWARFExpressionEvaluator + frameBaseVal = %s + stepCount = %d + status: %s + + Stack: + %s + Instructions: + %s + """.formatted( // + frameBaseVal != null ? frameBaseVal.toString() : "not set", + stepCount, + getStatusString(), + getStackAsString().indent(2), + expr != null + ? expr.toString(instrIndex, true, true, dprog.getRegisterMappings()).indent(2) + : " no expr"); + } + + private String getStackAsString() { + StringBuilder sb = new StringBuilder(); + + int stackindex = 0; + for (Object stackVal : stack.reversed()) { + sb.append("%3d: %s\n".formatted(stackindex, stackVal)); + stackindex++; + } + return sb.toString(); + } + + private String getStatusString() { + if (instrIndex == -1) { + return "Not started"; + } + else if (expr != null && instrIndex == expr.getInstructionCount()) { + return "Finished"; + } + return "Running"; + } + + } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionException.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionException.java index 387c705820..d9e8a467b6 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionException.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionException.java @@ -20,26 +20,22 @@ package ghidra.app.util.bin.format.dwarf.expression; * or when they are {@link DWARFExpressionEvaluator evaluated.} *

* Use this class when you want to pass the {@link DWARFExpression expression} and - * the opcode / step in the expression that caused the problem back up the call chain. + * the location in the expression that caused the problem back up the call chain. */ public class DWARFExpressionException extends Exception { private DWARFExpression expr; - private int step = -1; + private int instrIndex = -1; public DWARFExpressionException() { super(); } - public DWARFExpressionException(String message, DWARFExpression expr, int step) { - this(message, expr, step, null); - } - - public DWARFExpressionException(String message, DWARFExpression expr, int step, + public DWARFExpressionException(String message, DWARFExpression expr, int instrIndex, Throwable cause) { super(message, cause); this.expr = expr; - this.step = step; + this.instrIndex = instrIndex; } public DWARFExpressionException(String message, Throwable cause) { @@ -62,17 +58,18 @@ public class DWARFExpressionException extends Exception { this.expr = expr; } - public void setStep(int step) { - this.step = step; + public void setInstructionIndex(int instrIndex) { + this.instrIndex = instrIndex; } - public int getStep() { - return step; + public int getInstructionIndex() { + return instrIndex; } @Override public String getMessage() { - return super.getMessage() + (expr != null ? "\n" + expr.toString(step, false, false) : ""); + return super.getMessage() + + (expr != null ? "\n" + expr.toString(instrIndex, false, false, null) : ""); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionInstruction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionInstruction.java new file mode 100644 index 0000000000..5cb23b158f --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionInstruction.java @@ -0,0 +1,233 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.util.bin.format.dwarf.expression; + +import static ghidra.app.util.bin.format.dwarf.expression.DWARFExpressionOpCode.*; +import static ghidra.app.util.bin.format.dwarf.expression.DWARFExpressionOperandType.*; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Objects; + +import ghidra.app.util.bin.BinaryReader; +import ghidra.program.model.data.LEB128; +import ghidra.util.NumericUtilities; + +/** + * An immutable representation of a single {@link DWARFExpression} instruction and its operands. + *

+ * An instruction can take 0, 1, or 2 operands, only the last can be a blob. + */ +public class DWARFExpressionInstruction { + + /** + * Reads a single instruction from the stream. + * + * @param reader {@link BinaryReader} stream + * @param addrSize size of pointers + * @param intSize size of ints + * @return new {@link DWARFExpressionInstruction}, never null. Problematic instructions + * will have an opcode of {@link DWARFExpressionOpCode#DW_OP_unknown_opcode DW_OP_unknown_opcode} + * and will contain the remainder of the stream as its blob operand + * @throws IOException if error reading a primitive value from the stream + */ + public static DWARFExpressionInstruction read(BinaryReader reader, byte addrSize, int intSize) + throws IOException { + long opcodeoffset = reader.getPointerIndex(); + int opcode = reader.readNextUnsignedByte(); + DWARFExpressionOpCode op = DWARFExpressionOpCode.parse(opcode); + if (op == null) { + // back up so the raw opcode byte is included and + // consume the remainder of the bytes in the expression because + // we've hit an invalid/unknown opcode and can not proceed any further. + reader.setPointerIndex(opcodeoffset); + int bytesLeft = (int) (reader.length() - reader.getPointerIndex()); + byte[] remainingBytes = readSizedBlobOperand(reader, bytesLeft); + + return new DWARFExpressionInstruction(DW_OP_unknown_opcode, + new DWARFExpressionOperandType[] { SIZED_BLOB }, EMPTY_OPERANDS_VALUE, + remainingBytes, (int) opcodeoffset); + } + else { + DWARFExpressionOperandType[] operandTypes = op.getOperandTypes(); + + long[] operandValues = + (operandTypes.length != 0) ? new long[operandTypes.length] : EMPTY_OPERANDS_VALUE; + byte[] blob = null; + for (int i = 0; i < operandTypes.length; i++) { + DWARFExpressionOperandType optype = operandTypes[i]; + if (optype == SIZED_BLOB) { + blob = readSizedBlobOperand(reader, operandValues[i - 1]); + } + else { + operandValues[i] = readOperandValue(optype, reader, addrSize, intSize); + } + } + + return new DWARFExpressionInstruction(op, operandTypes, operandValues, blob, + (int) opcodeoffset); + } + + } + + protected final DWARFExpressionOpCode opcode; + protected final int offset; + protected final DWARFExpressionOperandType[] operandTypes; + protected final long operands[]; + protected final byte[] blob; + + /** + * Create a new DWARF expression instruction. + * + * @param op enum opcode, ie. DW_OP_not from {@link DWARFExpressionOpCode} + * @param operandTypes 'datatype' of each operands + * @param operands value of the operands, pre-converted into longs. + * @param blob if an operand is a byte array (ie. for DW_OP_implicit_value), this is the bytes + * @param offset byte offset of this operation from the start of the DWARF expression. + */ + public DWARFExpressionInstruction(DWARFExpressionOpCode op, + DWARFExpressionOperandType[] operandTypes, long[] operands, byte[] blob, int offset) { + this.opcode = op; + this.operandTypes = operandTypes; + this.operands = operands; + this.blob = blob; + this.offset = offset; + } + + /** + * {@return a new instruction instance that is a copy of this instruction, but has had all + * it's operands removed} + */ + public DWARFExpressionInstruction toGenericForm() { + return new DWARFExpressionInstruction(opcode, DW_OP_unknown_opcode.getOperandTypes(), + EMPTY_OPERANDS_VALUE, null, 0); + } + + /** + * {@return {@link DWARFExpressionOpCode} of this instruction} + */ + public DWARFExpressionOpCode getOpCode() { + return opcode; + } + + /** + * {@return the specified operand's value. Not valid for blob operands} + * + * @param opindex which operand to fetch. + */ + public long getOperandValue(int opindex) { + return operands[opindex]; + } + + /** + * {@return number of operands this instruction has} + */ + public int getOperandCount() { + return operandTypes.length; + } + + /** + * {@return the byte array that contains the bytes of the blob operand} + */ + public byte[] getBlob() { + return blob; + } + + /** + * {@return offset of this opcode, relative to the start of the {@link DWARFExpression}} + */ + public int getOffset() { + return offset; + } + + @Override + public String toString() { + return opcode.toString() + (operands.length > 0 ? " " + Arrays.toString(operands) : "") + + (blob != null ? " blob: [" + NumericUtilities.convertBytesToString(blob) + "]" : ""); + } + + /** + * {@return formatted string representation of the specified operand, patterned after readelf's + * format} + * + * @param opIndex operand index + */ + public String getOperandRepresentation(int opIndex) { + return switch (operandTypes[opIndex]) { + case ADDR -> Long.toHexString(operands[opIndex]); + case S_BYTE, S_SHORT, S_INT, S_LONG, S_LEB128 -> // force a leading "+" for positive + (operands[opIndex] > 0 ? "+" : "") + Long.toString(operands[opIndex]); + case U_BYTE, U_SHORT, U_INT, U_LONG, U_LEB128, DWARF_INT -> Long + .toUnsignedString(operands[opIndex]); + case SIZED_BLOB -> NumericUtilities.convertBytesToString(blob, " "); + }; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + Arrays.hashCode(blob); + result = prime * result + Arrays.hashCode(operandTypes); + result = prime * result + Arrays.hashCode(operands); + result = prime * result + Objects.hash(offset, opcode); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof DWARFExpressionInstruction)) { + return false; + } + DWARFExpressionInstruction other = (DWARFExpressionInstruction) obj; + return Arrays.equals(blob, other.blob) && offset == other.offset && + opcode == other.opcode && Arrays.equals(operandTypes, other.operandTypes) && + Arrays.equals(operands, other.operands); + } + + //-------------------------------------------------------------------------------------------- + + private static final long EMPTY_OPERANDS_VALUE[] = {}; + + private static byte[] readSizedBlobOperand(BinaryReader reader, long blobSize) + throws IOException { + return reader.readNextByteArray((int) blobSize); + } + + private static long readOperandValue(DWARFExpressionOperandType operandType, + BinaryReader reader, byte addrSize, int intSize) throws IOException { + return switch (operandType) { + case ADDR -> reader.readNextUnsignedValue(addrSize); + case S_BYTE -> reader.readNextByte(); + case S_SHORT -> reader.readNextShort(); + case S_INT -> reader.readNextInt(); + case S_LONG -> reader.readNextLong(); + case U_BYTE -> reader.readNextUnsignedByte(); + case U_SHORT -> reader.readNextUnsignedShort(); + case U_INT -> reader.readNextUnsignedInt(); + case U_LONG -> reader.readNextLong(); /* & there is no mask for ulong */ + case S_LEB128 -> reader.readNext(LEB128::signed); + case U_LEB128 -> reader.readNext(LEB128::unsigned); + case SIZED_BLOB -> throw new IOException("Can't read SIZED_BLOB as a Long value"); + case DWARF_INT -> reader.readNextUnsignedValue(intSize); + default -> throw new IOException("Unknown DWARFExpressionOperandType " + operandType); + }; + } + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionOpCode.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionOpCode.java new file mode 100644 index 0000000000..22c1559c68 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionOpCode.java @@ -0,0 +1,285 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.util.bin.format.dwarf.expression; + +import static ghidra.app.util.bin.format.dwarf.expression.DWARFExpressionOperandType.*; + +import java.util.Arrays; + +import ghidra.app.util.bin.format.dwarf.DWARFRegisterMappings; +import ghidra.program.model.lang.Register; + +/** + * DWARF expression opcodes, and their expected operands. + */ +public enum DWARFExpressionOpCode { + DW_OP_unknown_opcode(0), // special value, not a real DWARF opcode + DW_OP_addr(0x3, ADDR), + DW_OP_deref(0x6), + DW_OP_const1u(0x8, U_BYTE), + DW_OP_const1s(0x9, S_BYTE), + DW_OP_const2u(0xa, U_SHORT), + DW_OP_const2s(0xb, S_SHORT), + DW_OP_const4u(0xc, U_INT), + DW_OP_const4s(0xd, S_INT), + DW_OP_const8u(0xe, U_LONG), + DW_OP_const8s(0xf, S_LONG), + DW_OP_constu(0x10, U_LEB128), + DW_OP_consts(0x11, S_LEB128), + DW_OP_dup(0x12), + DW_OP_drop(0x13), + DW_OP_over(0x14), + DW_OP_pick(0x15, U_BYTE), + DW_OP_swap(0x16), + DW_OP_rot(0x17), + DW_OP_xderef(0x18), + DW_OP_abs(0x19), + DW_OP_and(0x1a), + DW_OP_div(0x1b), + DW_OP_minus(0x1c), + DW_OP_mod(0x1d), + DW_OP_mul(0x1e), + DW_OP_neg(0x1f), + DW_OP_not(0x20), + DW_OP_or(0x21), + DW_OP_plus(0x22), + DW_OP_plus_uconst(0x23, U_LEB128), + DW_OP_shl(0x24), + DW_OP_shr(0x25), + DW_OP_shra(0x26), + DW_OP_xor(0x27), + DW_OP_bra(0x28, S_SHORT), + DW_OP_eq(0x29), + DW_OP_ge(0x2a), + DW_OP_gt(0x2b), + DW_OP_le(0x2c), + DW_OP_lt(0x2d), + DW_OP_ne(0x2e), + DW_OP_skip(0x2f, S_SHORT), + DW_OP_lit0(0x30), + DW_OP_lit1(0x31), + DW_OP_lit2(0x32), + DW_OP_lit3(0x33), + DW_OP_lit4(0x34), + DW_OP_lit5(0x35), + DW_OP_lit6(0x36), + DW_OP_lit7(0x37), + DW_OP_lit8(0x38), + DW_OP_lit9(0x39), + DW_OP_lit10(0x3a), + DW_OP_lit11(0x3b), + DW_OP_lit12(0x3c), + DW_OP_lit13(0x3d), + DW_OP_lit14(0x3e), + DW_OP_lit15(0x3f), + DW_OP_lit16(0x40), + DW_OP_lit17(0x41), + DW_OP_lit18(0x42), + DW_OP_lit19(0x43), + DW_OP_lit20(0x44), + DW_OP_lit21(0x45), + DW_OP_lit22(0x46), + DW_OP_lit23(0x47), + DW_OP_lit24(0x48), + DW_OP_lit25(0x49), + DW_OP_lit26(0x4a), + DW_OP_lit27(0x4b), + DW_OP_lit28(0x4c), + DW_OP_lit29(0x4d), + DW_OP_lit30(0x4e), + DW_OP_lit31(0x4f), + DW_OP_reg0(0x50), + DW_OP_reg1(0x51), + DW_OP_reg2(0x52), + DW_OP_reg3(0x53), + DW_OP_reg4(0x54), + DW_OP_reg5(0x55), + DW_OP_reg6(0x56), + DW_OP_reg7(0x57), + DW_OP_reg8(0x58), + DW_OP_reg9(0x59), + DW_OP_reg10(0x5a), + DW_OP_reg11(0x5b), + DW_OP_reg12(0x5c), + DW_OP_reg13(0x5d), + DW_OP_reg14(0x5e), + DW_OP_reg15(0x5f), + DW_OP_reg16(0x60), + DW_OP_reg17(0x61), + DW_OP_reg18(0x62), + DW_OP_reg19(0x63), + DW_OP_reg20(0x64), + DW_OP_reg21(0x65), + DW_OP_reg22(0x66), + DW_OP_reg23(0x67), + DW_OP_reg24(0x68), + DW_OP_reg25(0x69), + DW_OP_reg26(0x6a), + DW_OP_reg27(0x6b), + DW_OP_reg28(0x6c), + DW_OP_reg29(0x6d), + DW_OP_reg30(0x6e), + DW_OP_reg31(0x6f), + DW_OP_breg0(0x70, S_LEB128), + DW_OP_breg1(0x71, S_LEB128), + DW_OP_breg2(0x72, S_LEB128), + DW_OP_breg3(0x73, S_LEB128), + DW_OP_breg4(0x74, S_LEB128), + DW_OP_breg5(0x75, S_LEB128), + DW_OP_breg6(0x76, S_LEB128), + DW_OP_breg7(0x77, S_LEB128), + DW_OP_breg8(0x78, S_LEB128), + DW_OP_breg9(0x79, S_LEB128), + DW_OP_breg10(0x7a, S_LEB128), + DW_OP_breg11(0x7b, S_LEB128), + DW_OP_breg12(0x7c, S_LEB128), + DW_OP_breg13(0x7d, S_LEB128), + DW_OP_breg14(0x7e, S_LEB128), + DW_OP_breg15(0x7f, S_LEB128), + DW_OP_breg16(0x80, S_LEB128), + DW_OP_breg17(0x81, S_LEB128), + DW_OP_breg18(0x82, S_LEB128), + DW_OP_breg19(0x83, S_LEB128), + DW_OP_breg20(0x84, S_LEB128), + DW_OP_breg21(0x85, S_LEB128), + DW_OP_breg22(0x86, S_LEB128), + DW_OP_breg23(0x87, S_LEB128), + DW_OP_breg24(0x88, S_LEB128), + DW_OP_breg25(0x89, S_LEB128), + DW_OP_breg26(0x8a, S_LEB128), + DW_OP_breg27(0x8b, S_LEB128), + DW_OP_breg28(0x8c, S_LEB128), + DW_OP_breg29(0x8d, S_LEB128), + DW_OP_breg30(0x8e, S_LEB128), + DW_OP_breg31(0x8f, S_LEB128), + DW_OP_regx(0x90, U_LEB128), + DW_OP_fbreg(0x91, S_LEB128), + DW_OP_bregx(0x92, U_LEB128, S_LEB128), + DW_OP_piece(0x93, U_LEB128), + DW_OP_deref_size(0x94, U_BYTE), + DW_OP_xderef_size(0x95, U_BYTE), + DW_OP_nop(0x96), + DW_OP_push_object_address(0x97), + DW_OP_call2(0x98, U_SHORT), + DW_OP_call4(0x99, U_INT), + DW_OP_call_ref(0x9a, DWARF_INT), + DW_OP_form_tls_address(0x9b), + DW_OP_call_frame_cfa(0x9c), + DW_OP_bit_piece(0x9d, U_LEB128, U_LEB128), + DW_OP_implicit_value(0x9e, U_LEB128, SIZED_BLOB), + DW_OP_stack_value(0x9f), + + // DWARF5 + DW_OP_implicit_pointer(0xa0, DWARF_INT, S_LEB128), + DW_OP_addrx(0xa1, U_LEB128), + DW_OP_constx(0xa2, U_LEB128), + DW_OP_entry_value(0xa3, U_LEB128, SIZED_BLOB), + DW_OP_const_type(0xa4, U_LEB128, U_BYTE, SIZED_BLOB), + DW_OP_regval_type(0xa5, U_LEB128, U_LEB128), + DW_OP_deref_type(0xa6, U_BYTE, U_LEB128), + DW_OP_xderef_type(0xa7, U_BYTE, U_LEB128), + DW_OP_convert(0xa8, U_LEB128), + DW_OP_reinterpret(0xa9, U_LEB128); + + private static final int DW_OP_lo_user = 0xe0; + private static final int DW_OP_hi_user = 0xff; + + private final int opcode; + private final DWARFExpressionOperandType[] operandTypes; + + DWARFExpressionOpCode(int opcode) { + this.opcode = opcode; + this.operandTypes = DWARFExpressionOperandType.EMPTY_TYPELIST; + } + + DWARFExpressionOpCode(int opcode, DWARFExpressionOperandType... operandTypes) { + this.opcode = opcode; + this.operandTypes = operandTypes; + } + + /** + * {@return this opcode's raw numeric value} + */ + public byte getOpCodeValue() { + return (byte) opcode; + } + + /** + * {@return the expected operand types that an instruction would have for this opcode} + */ + public DWARFExpressionOperandType[] getOperandTypes() { + return operandTypes; + } + + private static DWARFExpressionOpCode[] lookupvals = values(); + private static int[] opcodes = getAllOpcodes(); + + private static int[] getAllOpcodes() { + int[] results = new int[lookupvals.length]; + for (int i = 0; i < results.length; i++) { + results[i] = lookupvals[i].opcode; + } + return results; + } + + /** + * {@return true if the specified opcode is in the range (inclusive) of the lo..hi opcodes} + * @param op opcode to test + * @param lo lowest opcode + * @param hi highest opcode + */ + public static boolean isInRange(DWARFExpressionOpCode op, DWARFExpressionOpCode lo, + DWARFExpressionOpCode hi) { + return lo.opcode <= op.opcode && op.opcode <= hi.opcode; + } + + /** + * Calculates the relative opcode number of this opcode, as compared to a base opcode. + *

+ * Example: if this opcode was DW_OP_reg12 (0x5c), and the base op code was DW_OP_reg0 (0x50), + * the result value would be 12. + * + * @param baseOp base opcode that this opcode is being compared to + * @return numeric difference between this opcode and the base opcode + */ + public int getRelativeOpCodeOffset(DWARFExpressionOpCode baseOp) { + return opcode - baseOp.opcode; + } + + public String toString(DWARFRegisterMappings regMapping) { + int regIdx = -1; + if (isInRange(this, DW_OP_reg0, DW_OP_reg31)) { + regIdx = getRelativeOpCodeOffset(DW_OP_reg0); + } + else if (isInRange(this, DW_OP_breg0, DW_OP_breg31)) { + regIdx = getRelativeOpCodeOffset(DW_OP_breg0); + } + Register reg = regIdx >= 0 && regMapping != null ? regMapping.getGhidraReg(regIdx) : null; + return this.toString() + (reg != null ? "(" + reg.getName() + ")" : ""); + } + + /** + * {@return the matching {@link DWARFExpressionOpCode} enum member, or null if unknown opcode} + * + * @param opcode numeric value of opcode (currently defined by DWARF as uint8) + */ + public static DWARFExpressionOpCode parse(int opcode) { + // NOTE: the order of this enum's opcode values must be defined in ascending order for this + // binarysearch to function + int opcodeIdx = Arrays.binarySearch(opcodes, opcode); + return opcodeIdx >= 0 ? lookupvals[opcodeIdx] : null; + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionOpCodes.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionOpCodes.java deleted file mode 100644 index 24c764ee8a..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionOpCodes.java +++ /dev/null @@ -1,298 +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 ghidra.app.util.bin.format.dwarf.expression; - -import static ghidra.app.util.bin.format.dwarf.expression.DWARFExpressionOperandType.*; - -import java.lang.reflect.Field; -import java.util.*; - -import ghidra.app.util.bin.format.dwarf.DWARFUtil; - -/** - * DWARF expression opcode consts from www.dwarfstd.org/doc/DWARF4.pdf - */ -public class DWARFExpressionOpCodes { - public static final int DW_OP_addr = 0x3; - public static final int DW_OP_deref = 0x6; - public static final int DW_OP_const1u = 0x8; - public static final int DW_OP_const1s = 0x9; - public static final int DW_OP_const2u = 0xa; - public static final int DW_OP_const2s = 0xb; - public static final int DW_OP_const4u = 0xc; - public static final int DW_OP_const4s = 0xd; - public static final int DW_OP_const8u = 0xe; - public static final int DW_OP_const8s = 0xf; - public static final int DW_OP_constu = 0x10; - public static final int DW_OP_consts = 0x11; - public static final int DW_OP_dup = 0x12; - public static final int DW_OP_drop = 0x13; - public static final int DW_OP_over = 0x14; - public static final int DW_OP_pick = 0x15; - public static final int DW_OP_swap = 0x16; - public static final int DW_OP_rot = 0x17; - public static final int DW_OP_xderef = 0x18; - public static final int DW_OP_abs = 0x19; - public static final int DW_OP_and = 0x1a; - public static final int DW_OP_div = 0x1b; - public static final int DW_OP_minus = 0x1c; - public static final int DW_OP_mod = 0x1d; - public static final int DW_OP_mul = 0x1e; - public static final int DW_OP_neg = 0x1f; - public static final int DW_OP_not = 0x20; - public static final int DW_OP_or = 0x21; - public static final int DW_OP_plus = 0x22; - public static final int DW_OP_plus_uconst = 0x23; - public static final int DW_OP_shl = 0x24; - public static final int DW_OP_shr = 0x25; - public static final int DW_OP_shra = 0x26; - public static final int DW_OP_xor = 0x27; - public static final int DW_OP_bra = 0x28; - public static final int DW_OP_eq = 0x29; - public static final int DW_OP_ge = 0x2a; - public static final int DW_OP_gt = 0x2b; - public static final int DW_OP_le = 0x2c; - public static final int DW_OP_lt = 0x2d; - public static final int DW_OP_ne = 0x2e; - public static final int DW_OP_skip = 0x2f; - public static final int DW_OP_lit0 = 0x30; - public static final int DW_OP_lit1 = 0x31; - public static final int DW_OP_lit2 = 0x32; - public static final int DW_OP_lit3 = 0x33; - public static final int DW_OP_lit4 = 0x34; - public static final int DW_OP_lit5 = 0x35; - public static final int DW_OP_lit6 = 0x36; - public static final int DW_OP_lit7 = 0x37; - public static final int DW_OP_lit8 = 0x38; - public static final int DW_OP_lit9 = 0x39; - public static final int DW_OP_lit10 = 0x3a; - public static final int DW_OP_lit11 = 0x3b; - public static final int DW_OP_lit12 = 0x3c; - public static final int DW_OP_lit13 = 0x3d; - public static final int DW_OP_lit14 = 0x3e; - public static final int DW_OP_lit15 = 0x3f; - public static final int DW_OP_lit16 = 0x40; - public static final int DW_OP_lit17 = 0x41; - public static final int DW_OP_lit18 = 0x42; - public static final int DW_OP_lit19 = 0x43; - public static final int DW_OP_lit20 = 0x44; - public static final int DW_OP_lit21 = 0x45; - public static final int DW_OP_lit22 = 0x46; - public static final int DW_OP_lit23 = 0x47; - public static final int DW_OP_lit24 = 0x48; - public static final int DW_OP_lit25 = 0x49; - public static final int DW_OP_lit26 = 0x4a; - public static final int DW_OP_lit27 = 0x4b; - public static final int DW_OP_lit28 = 0x4c; - public static final int DW_OP_lit29 = 0x4d; - public static final int DW_OP_lit30 = 0x4e; - public static final int DW_OP_lit31 = 0x4f; - public static final int DW_OP_reg0 = 0x50; - public static final int DW_OP_reg1 = 0x51; - public static final int DW_OP_reg2 = 0x52; - public static final int DW_OP_reg3 = 0x53; - public static final int DW_OP_reg4 = 0x54; - public static final int DW_OP_reg5 = 0x55; - public static final int DW_OP_reg6 = 0x56; - public static final int DW_OP_reg7 = 0x57; - public static final int DW_OP_reg8 = 0x58; - public static final int DW_OP_reg9 = 0x59; - public static final int DW_OP_reg10 = 0x5a; - public static final int DW_OP_reg11 = 0x5b; - public static final int DW_OP_reg12 = 0x5c; - public static final int DW_OP_reg13 = 0x5d; - public static final int DW_OP_reg14 = 0x5e; - public static final int DW_OP_reg15 = 0x5f; - public static final int DW_OP_reg16 = 0x60; - public static final int DW_OP_reg17 = 0x61; - public static final int DW_OP_reg18 = 0x62; - public static final int DW_OP_reg19 = 0x63; - public static final int DW_OP_reg20 = 0x64; - public static final int DW_OP_reg21 = 0x65; - public static final int DW_OP_reg22 = 0x66; - public static final int DW_OP_reg23 = 0x67; - public static final int DW_OP_reg24 = 0x68; - public static final int DW_OP_reg25 = 0x69; - public static final int DW_OP_reg26 = 0x6a; - public static final int DW_OP_reg27 = 0x6b; - public static final int DW_OP_reg28 = 0x6c; - public static final int DW_OP_reg29 = 0x6d; - public static final int DW_OP_reg30 = 0x6e; - public static final int DW_OP_reg31 = 0x6f; - public static final int DW_OP_breg0 = 0x70; - public static final int DW_OP_breg1 = 0x71; - public static final int DW_OP_breg2 = 0x72; - public static final int DW_OP_breg3 = 0x73; - public static final int DW_OP_breg4 = 0x74; - public static final int DW_OP_breg5 = 0x75; - public static final int DW_OP_breg6 = 0x76; - public static final int DW_OP_breg7 = 0x77; - public static final int DW_OP_breg8 = 0x78; - public static final int DW_OP_breg9 = 0x79; - public static final int DW_OP_breg10 = 0x7a; - public static final int DW_OP_breg11 = 0x7b; - public static final int DW_OP_breg12 = 0x7c; - public static final int DW_OP_breg13 = 0x7d; - public static final int DW_OP_breg14 = 0x7e; - public static final int DW_OP_breg15 = 0x7f; - public static final int DW_OP_breg16 = 0x80; - public static final int DW_OP_breg17 = 0x81; - public static final int DW_OP_breg18 = 0x82; - public static final int DW_OP_breg19 = 0x83; - public static final int DW_OP_breg20 = 0x84; - public static final int DW_OP_breg21 = 0x85; - public static final int DW_OP_breg22 = 0x86; - public static final int DW_OP_breg23 = 0x87; - public static final int DW_OP_breg24 = 0x88; - public static final int DW_OP_breg25 = 0x89; - public static final int DW_OP_breg26 = 0x8a; - public static final int DW_OP_breg27 = 0x8b; - public static final int DW_OP_breg28 = 0x8c; - public static final int DW_OP_breg29 = 0x8d; - public static final int DW_OP_breg30 = 0x8e; - public static final int DW_OP_breg31 = 0x8f; - public static final int DW_OP_regx = 0x90; - public static final int DW_OP_fbreg = 0x91; - public static final int DW_OP_bregx = 0x92; - public static final int DW_OP_piece = 0x93; - public static final int DW_OP_deref_size = 0x94; - public static final int DW_OP_xderef_size = 0x95; - public static final int DW_OP_nop = 0x96; - public static final int DW_OP_push_object_address = 0x97; - public static final int DW_OP_call2 = 0x98; - public static final int DW_OP_call4 = 0x99; - public static final int DW_OP_call_ref = 0x9a; - public static final int DW_OP_form_tls_address = 0x9b; - public static final int DW_OP_call_frame_cfa = 0x9c; - public static final int DW_OP_bit_piece = 0x9d; - public static final int DW_OP_implicit_value = 0x9e; - public static final int DW_OP_stack_value = 0x9f; - - // DWARF5 - public static final int DW_OP_implicit_pointer = 0xa0; - public static final int DW_OP_addrx = 0xa1; - public static final int DW_OP_constx = 0xa2; - public static final int DW_OP_entry_value = 0xa3; - public static final int DW_OP_const_type = 0xa4; - public static final int DW_OP_regval_type = 0xa5; - public static final int DW_OP_deref_type = 0xa6; - public static final int DW_OP_xderef_type = 0xa7; - public static final int DW_OP_convert = 0xa8; - public static final int DW_OP_reinterpret = 0xa9; - - public static final int DW_OP_lo_user = 0xe0; - public static final int DW_OP_hi_user = 0xff; - - public static boolean isValidOpcode(int opcode) { - Field field = DWARFUtil.getStaticFinalFieldWithValue(DWARFExpressionOpCodes.class, opcode); - return field != null && field.getName().startsWith("DW_OP_"); - } - - /** - * These opcodes are known, but can not be evaluated in the current Ghidra DWARF code - */ - public static final int[] UNSUPPORTED_OPCODES_LIST = - { DW_OP_deref_size, DW_OP_xderef, DW_OP_xderef_size, DW_OP_push_object_address, - DW_OP_form_tls_address, DW_OP_call2, DW_OP_call4, DW_OP_call_ref, DW_OP_implicit_value, - DW_OP_implicit_pointer, DW_OP_entry_value, DW_OP_const_type, DW_OP_regval_type, - DW_OP_deref_type, DW_OP_xderef_type, DW_OP_convert, DW_OP_reinterpret }; - - /** - * These opcodes are known, but can not be evaluated in the current Ghidra DWARF code. - */ - public static final Set UNSUPPORTED_OPCODES = new HashSet<>(); - - static { - for (int opcode : UNSUPPORTED_OPCODES) { - UNSUPPORTED_OPCODES.add(opcode); - } - } - - /** - * Map of opcode to its expected operand types. If the opcode isn't found in this map, - * it is assumed to not take any operands. - * Even if Ghidra can't evaluate a DWARF opCode, we should still keep it in this - * map so we can parse the expression and display it as a string. - */ - static final Map OPtoOperandTypes = new HashMap<>(); - - static { - addOperandTypeMapping(DW_OP_addr, ADDR); - addOperandTypeMapping(DW_OP_const1u, U_BYTE); - addOperandTypeMapping(DW_OP_const1s, S_BYTE); - addOperandTypeMapping(DW_OP_const2u, U_SHORT); - addOperandTypeMapping(DW_OP_const2s, S_SHORT); - addOperandTypeMapping(DW_OP_const4u, U_INT); - addOperandTypeMapping(DW_OP_const4s, S_INT); - addOperandTypeMapping(DW_OP_const8u, U_LONG); - addOperandTypeMapping(DW_OP_const8s, S_LONG); - addOperandTypeMapping(DW_OP_constu, U_LEB128); - addOperandTypeMapping(DW_OP_consts, S_LEB128); - addOperandTypeMapping(DW_OP_pick, U_BYTE); - addOperandTypeMapping(DW_OP_plus_uconst, U_LEB128); - addOperandTypeMapping(DW_OP_skip, S_SHORT); - addOperandTypeMapping(DW_OP_bra, S_SHORT); - addOperandTypeMapping(DW_OP_breg0, DW_OP_breg31, S_LEB128); - addOperandTypeMapping(DW_OP_regx, U_LEB128); - addOperandTypeMapping(DW_OP_fbreg, S_LEB128); - addOperandTypeMapping(DW_OP_bregx, U_LEB128, S_LEB128); - addOperandTypeMapping(DW_OP_piece, U_LEB128); - addOperandTypeMapping(DW_OP_deref_size, U_BYTE); - addOperandTypeMapping(DW_OP_xderef_size, U_BYTE); - addOperandTypeMapping(DW_OP_call2, U_SHORT); - addOperandTypeMapping(DW_OP_call4, U_INT); - addOperandTypeMapping(DW_OP_call_ref, DWARF_INT);// U_INT OR U_LONG depending on DWARF32 or DWARF64 - addOperandTypeMapping(DW_OP_bit_piece, U_LEB128, U_LEB128); - addOperandTypeMapping(DW_OP_implicit_value, U_LEB128, SIZED_BLOB); - - // dwarf5 - addOperandTypeMapping(DW_OP_implicit_pointer, DWARF_INT, S_LEB128); - addOperandTypeMapping(DW_OP_addrx, U_LEB128); - addOperandTypeMapping(DW_OP_constx, U_LEB128); - addOperandTypeMapping(DW_OP_entry_value, U_LEB128, SIZED_BLOB); - addOperandTypeMapping(DW_OP_const_type, U_LEB128, U_BYTE, SIZED_BLOB); - addOperandTypeMapping(DW_OP_regval_type, U_LEB128, U_LEB128); - addOperandTypeMapping(DW_OP_deref_type, U_BYTE, U_LEB128); - addOperandTypeMapping(DW_OP_xderef_type, U_BYTE, U_LEB128); - addOperandTypeMapping(DW_OP_convert, U_LEB128); - addOperandTypeMapping(DW_OP_reinterpret, U_LEB128); - } - - public static final DWARFExpressionOperandType[] EMPTY_OPERANDTYPES = {}; - public static final DWARFExpressionOperandType[] BLOBONLY_OPERANDTYPES = - { DWARFExpressionOperandType.SIZED_BLOB }; - - private static void addOperandTypeMapping(int opcode, - DWARFExpressionOperandType... operandTypes) { - OPtoOperandTypes.put(opcode, operandTypes); - } - - private static void addOperandTypeMapping(int opcodeLow, int opcodeHigh, - DWARFExpressionOperandType... operandTypes) { - for (int i = opcodeLow; i <= opcodeHigh; i++) { - OPtoOperandTypes.put(i, operandTypes); - } - } - - public static DWARFExpressionOperandType[] getOperandTypesFor(int opcode) { - DWARFExpressionOperandType[] results = OPtoOperandTypes.get(opcode); - return results != null ? results : EMPTY_OPERANDTYPES; - } - - public static String toString(int opcode) { - return DWARFUtil.toString(DWARFExpressionOpCodes.class, opcode); - } -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionOperandType.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionOperandType.java index 9712e4789a..13eacf3efb 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionOperandType.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionOperandType.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -17,26 +17,24 @@ package ghidra.app.util.bin.format.dwarf.expression; /** * Enumeration that represents the different type of operands that a - * {@link DWARFExpressionOpCodes opcode} can take. + * {@link DWARFExpressionOpCode opcode} can take. */ public enum DWARFExpressionOperandType { U_LEB128, // UNSIGNED LEB128 (variable len) S_LEB128, // SIGNED LEB128 (variable len) S_BYTE, // SIGNED BYTE (1 byte) - S_SHORT, // SIGNED SHORT (2 bytes) + S_SHORT, // SIGNED SHORT (2 bytes) S_INT, // SIGNED INT (4 bytes) S_LONG, // SIGNED LONG (8 bytes) U_BYTE, // UNSIGNED BYTE (1 byte) - U_SHORT, // UNSIGNED SHORT (2 bytes) + U_SHORT, // UNSIGNED SHORT (2 bytes) U_INT, // UNSIGNED INT (4 bytes) U_LONG, // UNSIGNED LONG (8 bytes) ADDR, // ADDRESS (1, 2, 4, 8 from DWARFCompilationUnit.pointerSize) SIZED_BLOB, // raw bytes (length specified by other operand) DWARF_INT; // U_INT or U_LONG based on dwarf native size - public static String valueToString(long value, DWARFExpressionOperandType operandType) { - return operandType == U_LONG || operandType == ADDR || operandType == DWARF_INT - ? Long.toUnsignedString(value, 16) - : Long.toString(value, 16); - } + // This is here instead of DWARFExpressionOpCode to satisfy initialization order dependence + static final DWARFExpressionOperandType[] EMPTY_TYPELIST = new DWARFExpressionOperandType[0]; + } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionOperation.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionOperation.java deleted file mode 100644 index a882646321..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionOperation.java +++ /dev/null @@ -1,102 +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 ghidra.app.util.bin.format.dwarf.expression; - -import java.util.Arrays; - -/** - * An immutable representation of a single {@link DWARFExpression} instruction and its operands. - *

- * A DWARF expression operation can take 0, 1, or 2 operands. - */ -class DWARFExpressionOperation { - protected final int offset; - protected final int opcode; - protected final DWARFExpressionOperandType[] operandTypes; - protected final long operands[]; - protected final byte[] blob; - - /** - * Create a new DWARF expression opcode element. - * - * @param opcode numeric value of the opcode, ie. DW_OP_not from {@link DWARFExpressionOpCodes} - * @param operandTypes 'datatype' of the operands - * @param operands value of the operands, pre-converted into longs. - * @param blob if an operand is a byte array (ie. for DW_OP_implicit_value), this is the bytes - * @param offset byte offset of this operation from the start of the DWARF expression. - */ - public DWARFExpressionOperation(int opcode, DWARFExpressionOperandType[] operandTypes, - long[] operands, byte[] blob, int offset) { - this.opcode = opcode; - this.operandTypes = operandTypes; - this.operands = operands; - this.blob = blob; - this.offset = offset; - } - - /** - * See {@link DWARFExpressionOpCodes} for list of opcodes. - * @return - */ - public int getOpCode() { - return opcode; - } - - /** - * Get the operand value. - * - * @param opindex which operand to fetch. - * @return value of operand as a long. - */ - public long getOperandValue(int opindex) { - return operands[opindex]; - } - - /** - * Calculates the relative opcode number of this opcode, as compared to a base opcode. - *

- * Ie. If this opcode was DW_OP_reg12 (0x5c), and the base op code was DW_OP_reg0 (0x50), - * the result value would be 12. - * - * @param baseOpCode Ordinal value of the opcode that this opcode is being compared ot. - * @return numeric difference between this opcode and the base opcode. - */ - public int getRelativeOpCodeOffset(int baseOpCode) { - return opcode - baseOpCode; - } - - /** - * Return the byte array that contains the bytes of the blob operand. - * - * @return byte array - */ - public byte[] getBlob() { - return blob; - } - - /** - * The offset of this opcode, relative to the start of the {@link DWARFExpression}. - * @return - */ - public int getOffset() { - return offset; - } - - @Override - public String toString() { - return DWARFExpressionOpCodes.toString(opcode) + " " + Arrays.toString(operands); - } -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionResult.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionTerminalDerefException.java similarity index 52% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionResult.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionTerminalDerefException.java index 2e40c7156b..bcb4b2d99d 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionResult.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionTerminalDerefException.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -15,23 +15,19 @@ */ package ghidra.app.util.bin.format.dwarf.expression; -import java.util.ArrayDeque; +import ghidra.program.model.pcode.Varnode; -/** - * The result of executing a {@link DWARFExpression} with a {@link DWARFExpressionEvaluator}. - *

- * Currently only holds the stack results, but future improvements should - * migrate result values (ie. stuff like {@link DWARFExpressionEvaluator#isDeref()}) - * from {@link DWARFExpressionEvaluator} to here. - */ -public class DWARFExpressionResult { - private ArrayDeque stack = new ArrayDeque(); +public class DWARFExpressionTerminalDerefException extends DWARFExpressionUnsupportedOpException { - public DWARFExpressionResult(ArrayDeque stack) { - this.stack = stack; + private Varnode varnode; + + public DWARFExpressionTerminalDerefException(DWARFExpressionInstruction op, Varnode varnode) { + super(op); + this.varnode = varnode; } - public long pop() { - return stack.pop(); + + public Varnode getVarnode() { + return varnode; } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionUnsupportedOpException.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionUnsupportedOpException.java new file mode 100644 index 0000000000..6968eaf2c7 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionUnsupportedOpException.java @@ -0,0 +1,31 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.util.bin.format.dwarf.expression; + +public class DWARFExpressionUnsupportedOpException extends DWARFExpressionException { + + private DWARFExpressionInstruction instr; + + public DWARFExpressionUnsupportedOpException(DWARFExpressionInstruction instr) { + super("Unsupported instruction %s".formatted(instr)); + this.instr = instr; + } + + public DWARFExpressionInstruction getInstruction() { + return instr; + } + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionEvaluatorContext.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionValueException.java similarity index 65% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionEvaluatorContext.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionValueException.java index 2d2738bce3..9efbdcc2b4 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionEvaluatorContext.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionValueException.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -15,6 +15,18 @@ */ package ghidra.app.util.bin.format.dwarf.expression; -import ghidra.app.util.bin.format.dwarf.DWARFCompilationUnit; +import ghidra.program.model.pcode.Varnode; -public record DWARFExpressionEvaluatorContext(DWARFCompilationUnit cu) {} +public class DWARFExpressionValueException extends DWARFExpressionException { + + private Varnode vn; + + public DWARFExpressionValueException(Varnode vn) { + super("Unable to access value of %s".formatted(vn)); + this.vn = vn; + } + + public Varnode getVarnode() { + return vn; + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/funcfixup/ParamSpillDWARFFunctionFixup.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/funcfixup/ParamSpillDWARFFunctionFixup.java index ad9b746c27..eb7b801e86 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/funcfixup/ParamSpillDWARFFunctionFixup.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/funcfixup/ParamSpillDWARFFunctionFixup.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -36,16 +36,20 @@ public class ParamSpillDWARFFunctionFixup implements DWARFFunctionFixup { continue; } long paramStackOffset = param.getStackOffset(); - if (dfunc.isInLocalVarStorageArea(paramStackOffset) && - dfunc.getLocalVarByOffset(paramStackOffset) == null) { + if (dfunc.isInLocalVarStorageArea(paramStackOffset)) { + if (dfunc.getLocalVarByOffset(paramStackOffset) == null) { + DWARFVariable paramSpill = DWARFVariable.fromDataType(dfunc, param.type); + String paramName = param.name.getName(); + paramSpill.name = + param.name.replaceName(paramName + "_local", paramName + "_local"); + paramSpill.setStackStorage(paramStackOffset); + paramSpill.comment = param.comment; + + dfunc.localVars.add(paramSpill); + } - DWARFVariable paramSpill = DWARFVariable.fromDataType(dfunc, param.type); - String paramName = param.name.getName(); - paramSpill.name = - param.name.replaceName(paramName + "_local", paramName + "_local"); - paramSpill.setStackStorage(paramStackOffset); - dfunc.localVars.add(paramSpill); param.clearStorage(); + param.comment = null; } } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/funcfixup/StorageVerificationDWARFFunctionFixup.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/funcfixup/StorageVerificationDWARFFunctionFixup.java index 0aec2e4fdf..03c61543b8 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/funcfixup/StorageVerificationDWARFFunctionFixup.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/funcfixup/StorageVerificationDWARFFunctionFixup.java @@ -34,7 +34,7 @@ public class StorageVerificationDWARFFunctionFixup implements DWARFFunctionFixup DWARFRegisterMappings regMappings = dfunc.getProgram().getRegisterMappings(); boolean ignoreStorage = dfunc.getProgram().getImportOptions().isIgnoreParamStorage() || (regMappings != null && regMappings.isUseFormalParameterStorage()); - boolean isEmptySignature = dfunc.params.isEmpty() && dfunc.retval.isVoidType(); + boolean isEmptySignature = dfunc.params.isEmpty() && dfunc.retval.isMissingStorage(); if (regMappings == null || ignoreStorage || isEmptySignature) { dfunc.signatureCommitMode = CommitMode.FORMAL; return; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/GolangDWARFFunctionFixup.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/GolangDWARFFunctionFixup.java index 55c1194b07..bc0b80420d 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/GolangDWARFFunctionFixup.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/GolangDWARFFunctionFixup.java @@ -102,9 +102,12 @@ public class GolangDWARFFunctionFixup implements DWARFFunctionFixup { storageAllocator.setAbi0Mode(); } - dfunc.callingConventionName = - storageAllocator.isAbi0Mode() ? GoConstants.GOLANG_ABI0_CALLINGCONVENTION_NAME - : GoConstants.GOLANG_ABI_INTERNAL_CALLINGCONVENTION_NAME; + String ccName = storageAllocator.isAbi0Mode() + ? GoConstants.GOLANG_ABI0_CALLINGCONVENTION_NAME + : GoConstants.GOLANG_ABI_INTERNAL_CALLINGCONVENTION_NAME; + if (goBinary.hasCallingConvention(ccName)) { + dfunc.callingConventionName = ccName; + } GoFunctionMultiReturn multiReturnInfo = fixupFormalFuncDef(dfunc, storageAllocator, dtm); fixupCustomStorage(dfunc, storageAllocator, dtm, multiReturnInfo); @@ -164,7 +167,7 @@ public class GolangDWARFFunctionFixup implements DWARFFunctionFixup { // WARNING: this code should be kept in sync with GoFunctionFixup Program program = goBinary.getProgram(); - + // Allocate custom storage for each parameter List spillVars = new ArrayList<>(); for (DWARFVariable dvar : dfunc.params) { @@ -208,8 +211,8 @@ public class GolangDWARFFunctionFixup implements DWARFFunctionFixup { // because we will do it manually for (DataTypeComponent dtc : multiReturn.getComponentsInOriginalOrder()) { allocateReturnStorage(dfunc, dfunc.retval, - dtc.getFieldName() + "_return_result_alias", - dtc.getDataType(), storageAllocator, false); + dtc.getFieldName() + "_return_result_alias", dtc.getDataType(), + storageAllocator, false); } if (!program.getMemory().isBigEndian()) { diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf/DWARFFunctionImporterTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf/DWARFFunctionImporterTest.java index af00c4c1d1..4f8c9db5e3 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf/DWARFFunctionImporterTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf/DWARFFunctionImporterTest.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -15,6 +15,9 @@ */ package ghidra.app.util.bin.format.dwarf; +import static ghidra.app.util.bin.format.dwarf.DWARFSourceLanguage.*; +import static ghidra.app.util.bin.format.dwarf.attribs.DWARFAttribute.*; +import static ghidra.app.util.bin.format.dwarf.expression.DWARFExpressionOpCode.*; import static org.junit.Assert.*; import java.io.IOException; @@ -23,8 +26,6 @@ import java.util.List; import org.junit.Test; import ghidra.app.util.NamespaceUtils; -import ghidra.app.util.bin.format.dwarf.attribs.DWARFAttribute; -import ghidra.app.util.bin.format.dwarf.expression.DWARFExpressionOpCodes; import ghidra.program.database.function.OverlappingFunctionException; import ghidra.program.model.address.AddressSet; import ghidra.program.model.data.*; @@ -42,12 +43,11 @@ public class DWARFFunctionImporterTest extends DWARFTestBase { // test that Ghidra functions in a Rust compilation unit do have their info set // if they look like they have normal param info - addCompUnit(DWARFSourceLanguage.DW_LANG_Rust); + addCompUnit(DW_LANG_Rust); DebugInfoEntry intDIE = addInt(); DebugInfoEntry fooDIE = newSubprogram("foo", intDIE, 0x410, 10).create(); - newFormalParam(fooDIE, "param1", intDIE, DWARFExpressionOpCodes.DW_OP_fbreg, 0x6c) - .create(); + newFormalParam(fooDIE, "param1", intDIE, instr(DW_OP_fbreg, 0x6c)).create(); importFunctions(); @@ -66,9 +66,8 @@ public class DWARFFunctionImporterTest extends DWARFTestBase { } @Test - public void testRustMethod_SetsRustCC() - throws CancelledException, IOException, DWARFException { - addCompUnit(DWARFSourceLanguage.DW_LANG_Rust); + public void testRustMethod_SetsRustCC() throws CancelledException, IOException, DWARFException { + addCompUnit(DW_LANG_Rust); DebugInfoEntry intDIE = addInt(); newSubprogram("foo", intDIE, 0x410, 10).create(); @@ -98,8 +97,7 @@ public class DWARFFunctionImporterTest extends DWARFTestBase { newMember(nestedStructDIE, "blah1", intDIE, 0).create(); DebugInfoEntry fooDIE = newSubprogram("foo", intDIE, 0x410, 10).setParent(nestedStructDIE).create(); - newFormalParam(fooDIE, "this", nestedStructPtrDIE, DWARFExpressionOpCodes.DW_OP_fbreg, 0x6c) - .create(); + newFormalParam(fooDIE, "this", nestedStructPtrDIE, instr(DW_OP_fbreg, 0x6c)).create(); newMember(struct1DIE, "f1", intDIE, 0).create(); newMember(struct1DIE, "f2", floatDIE, 10).create(); @@ -128,7 +126,7 @@ public class DWARFFunctionImporterTest extends DWARFTestBase { public void testNoReturnFlag_True() throws CancelledException, IOException, DWARFException { DebugInfoEntry intDIE = addInt(); DIECreator func = newSubprogram("foo", intDIE, 0x410, 10); - func.addBoolean(DWARFAttribute.DW_AT_noreturn, true); + func.addBoolean(DW_AT_noreturn, true); func.create(); importFunctions(); @@ -160,7 +158,7 @@ public class DWARFFunctionImporterTest extends DWARFTestBase { DebugInfoEntry intDIE = addInt(); DebugInfoEntry fooDIE = newSubprogram("foo", intDIE, 0x410, 10).create(); - newFormalParam(fooDIE, "param1", intDIE, DWARFExpressionOpCodes.DW_OP_fbreg, 0x6c) // fbreg -14, func local variable area + newFormalParam(fooDIE, "param1", intDIE, instr(DW_OP_fbreg, 0x6c)) // fbreg -14, func local variable area .create(); importFunctions(); @@ -190,8 +188,10 @@ public class DWARFFunctionImporterTest extends DWARFTestBase { // TODO: need to also test location info from a debug_loc sequence that specifies a lexical offset DebugInfoEntry intDIE = addInt(); - DebugInfoEntry fooDIE = newSubprogram("foo", intDIE, 0x410, 10).create(); - newFormalParam(fooDIE, "param1", intDIE, DWARFExpressionOpCodes.DW_OP_fbreg, 0x8) // fbreg +8, caller stack area + DebugInfoEntry fooDIE = newSubprogram("foo", intDIE, 0x410, 10) + .addBlockBytes(DW_AT_frame_base, instr(DW_OP_call_frame_cfa)) + .create(); + newFormalParam(fooDIE, "param1", intDIE, instr(DW_OP_fbreg, 0x8)) // fbreg +8, caller stack area .create(); importFunctions(); @@ -208,9 +208,8 @@ public class DWARFFunctionImporterTest extends DWARFTestBase { assertEquals(fooParams.length, 1); assertEquals("param1", fooParams[0].getName()); assertEquals("int", fooParams[0].getDataType().getName()); - assertTrue(fooParams[0].isStackVariable()); - assertEquals(8, fooParams[0].getStackOffset()); + assertEquals(16 /* x86-64 static cfa 8 + fbreg 8 */, fooParams[0].getStackOffset()); } @Test @@ -222,8 +221,7 @@ public class DWARFFunctionImporterTest extends DWARFTestBase { DebugInfoEntry struct1DIE = newStruct("mystruct", 100).create(); DebugInfoEntry fooDIE = newSubprogram("foo", intDIE, 0x410, 10).setParent(struct1DIE).create(); - newFormalParam(fooDIE, "this", struct1PtrDIE, DWARFExpressionOpCodes.DW_OP_fbreg, 0x6c) - .create(); + newFormalParam(fooDIE, "this", struct1PtrDIE, instr(DW_OP_fbreg, 0x6c)).create(); newMember(struct1DIE, "f1", intDIE, 0).create(); newMember(struct1DIE, "f2", floatDIE, 10).create(); @@ -245,8 +243,8 @@ public class DWARFFunctionImporterTest extends DWARFTestBase { DebugInfoEntry struct1DIE = newStruct("mystruct", 100).create(); DebugInfoEntry fooDIE = newSubprogram("foo", intDIE, 0x410, 10).setParent(struct1DIE).create(); - newFormalParam(fooDIE, null, struct1PtrDIE, DWARFExpressionOpCodes.DW_OP_fbreg, 0x6c) - .addBoolean(DWARFAttribute.DW_AT_artificial, true) + newFormalParam(fooDIE, null, struct1PtrDIE, instr(DW_OP_fbreg, 0x6c)) + .addBoolean(DW_AT_artificial, true) .create(); newMember(struct1DIE, "f1", intDIE, 0).create(); @@ -268,15 +266,14 @@ public class DWARFFunctionImporterTest extends DWARFTestBase { DebugInfoEntry struct1PtrDIE = addFwdPtr(1); DebugInfoEntry struct1DIE = newStruct("mystruct", 100).create(); long formalParamDIEOffset = dwarfProg.getRelativeDIEOffset(2); - DebugInfoEntry fooDIE = - newSubprogram("foo", intDIE, 0x410, 10) - .addRef(DWARFAttribute.DW_AT_object_pointer, formalParamDIEOffset) - .setParent(struct1DIE) - .create(); + DebugInfoEntry fooDIE = newSubprogram("foo", intDIE, 0x410, 10) + .addRef(DW_AT_object_pointer, formalParamDIEOffset) + .setParent(struct1DIE) + .create(); // give the param a non-this name to defeat the logic in DWARFUtil.isThisParam() - newFormalParam(fooDIE, "not_the_normal_this_name", struct1PtrDIE, - DWARFExpressionOpCodes.DW_OP_fbreg, 0x6c).create(); + newFormalParam(fooDIE, "not_the_normal_this_name", struct1PtrDIE, instr(DW_OP_fbreg, 0x6c)) + .create(); newMember(struct1DIE, "f1", intDIE, 0).create(); newMember(struct1DIE, "f2", floatDIE, 10).create(); @@ -306,8 +303,8 @@ public class DWARFFunctionImporterTest extends DWARFTestBase { .create(); // give the param a non-this name to defeat the logic in DWARFUtil.isThisParam() - newFormalParam(fooDIE, "not_the_normal_this_name", struct1PtrDIE, - DWARFExpressionOpCodes.DW_OP_fbreg, 0x6c).create(); + newFormalParam(fooDIE, "not_the_normal_this_name", struct1PtrDIE, instr(DW_OP_fbreg, 0x6c)) + .create(); newMember(struct1DIE, "f1", intDIE, 0).create(); newMember(struct1DIE, "f2", floatDIE, 10).create(); @@ -331,8 +328,7 @@ public class DWARFFunctionImporterTest extends DWARFTestBase { DebugInfoEntry fooDIE = newSubprogram("foo", intDIE, 0x410, 10).setParent(struct1DIE).create(); - newFormalParam(fooDIE, null, struct1PtrDIE, DWARFExpressionOpCodes.DW_OP_fbreg, 0x6c) - .create(); + newFormalParam(fooDIE, null, struct1PtrDIE, instr(DW_OP_fbreg, 0x6c)).create(); newMember(struct1DIE, "f1", intDIE, 0).create(); newMember(struct1DIE, "f2", floatDIE, 10).create(); @@ -347,8 +343,8 @@ public class DWARFFunctionImporterTest extends DWARFTestBase { @Test public void testParamNameConflictsWithLocalVar() - throws CancelledException, IOException, DWARFException, - InvalidInputException, OverlappingFunctionException, DuplicateNameException { + throws CancelledException, IOException, DWARFException, InvalidInputException, + OverlappingFunctionException, DuplicateNameException { Function initialFoo = program.getListing() .createFunction("foo", addr(0x410), new AddressSet(addr(0x410), addr(0x411)), SourceType.DEFAULT); @@ -356,7 +352,7 @@ public class DWARFFunctionImporterTest extends DWARFTestBase { DataType intDT = IntegerDataType.getSignedDataType(4, program.getDataTypeManager()); initialFoo.addLocalVariable(new LocalVariableImpl("testxyz", intDT, -16, program), SourceType.USER_DEFINED); - + DebugInfoEntry intDIE = addInt(); DebugInfoEntry fooDIE = newSubprogram("foo", intDIE, 0x410, 10).create(); newFormalParam(fooDIE, "param1", intDIE).create(); @@ -374,8 +370,7 @@ public class DWARFFunctionImporterTest extends DWARFTestBase { } @Test - public void testParamNameBadChars() - throws CancelledException, IOException, DWARFException { + public void testParamNameBadChars() throws CancelledException, IOException, DWARFException { DebugInfoEntry intDIE = addInt(); DebugInfoEntry fooDIE = newSubprogram("foo", intDIE, 0x410, 10).create(); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf/DWARFStaticVarImporterTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf/DWARFStaticVarImporterTest.java index 85864100ae..ed0b9253b2 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf/DWARFStaticVarImporterTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf/DWARFStaticVarImporterTest.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -15,7 +15,9 @@ */ package ghidra.app.util.bin.format.dwarf; +import static ghidra.app.util.bin.format.dwarf.DWARFTag.*; import static ghidra.app.util.bin.format.dwarf.attribs.DWARFAttribute.*; +import static ghidra.app.util.bin.format.dwarf.expression.DWARFExpressionOpCode.*; import static org.junit.Assert.*; import java.io.IOException; @@ -24,7 +26,6 @@ import java.util.Set; import org.junit.Test; -import ghidra.app.util.bin.format.dwarf.expression.DWARFExpressionOpCodes; import ghidra.program.model.data.*; import ghidra.program.model.listing.CodeUnit; import ghidra.program.model.listing.Data; @@ -37,56 +38,48 @@ public class DWARFStaticVarImporterTest extends DWARFTestBase { @Test public void testIntStaticVar() throws CancelledException, IOException, DWARFException { DebugInfoEntry intDIE = addInt(); - new DIECreator(dwarfProg, DWARFTag.DW_TAG_variable) - .addString(DW_AT_name, "static_var1") + new DIECreator(dwarfProg, DW_TAG_variable).addString(DW_AT_name, "static_var1") .addRef(DW_AT_type, intDIE) - .addBlock(DW_AT_location, DWARFExpressionOpCodes.DW_OP_addr, 0x10, 0x4, 0, 0, 0, 0, - 0, 0) + .addBlockBytes(DW_AT_location, instr(DW_OP_addr, 0x10, 0x4, 0, 0, 0, 0, 0, 0)) .create(); importFunctions(); - CodeUnit cu = program.getListing().getCodeUnitAt(addr(0x410)); - assertNotNull(cu); - assertEquals("static_var1", cu.getLabel()); - assertEquals(4, cu.getLength()); - assertTrue(((Data) cu).getDataType() instanceof IntegerDataType); + CodeUnit codeunit = program.getListing().getCodeUnitAt(addr(0x410)); + assertNotNull(codeunit); + assertEquals("static_var1", codeunit.getLabel()); + assertEquals(4, codeunit.getLength()); + assertTrue(((Data) codeunit).getDataType() instanceof IntegerDataType); } @Test public void testZeroLenGlobalVar() throws CancelledException, IOException, DWARFException { DebugInfoEntry emptyStructDIE = newStruct("emptystruct", 0).create(); - new DIECreator(dwarfProg, DWARFTag.DW_TAG_variable) - .addString(DW_AT_name, "static_var1") + new DIECreator(dwarfProg, DW_TAG_variable).addString(DW_AT_name, "static_var1") .addRef(DW_AT_type, emptyStructDIE) - .addBlock(DW_AT_location, DWARFExpressionOpCodes.DW_OP_addr, 0x10, 0x4, 0, 0, 0, 0, - 0, 0) + .addBlockBytes(DW_AT_location, instr(DW_OP_addr, 0x10, 0x4, 0, 0, 0, 0, 0, 0)) .create(); importFunctions(); - CodeUnit cu = program.getListing().getCodeUnitAt(addr(0x410)); - assertNotNull(cu); - assertEquals("static_var1", cu.getLabel()); - assertEquals(1, cu.getLength()); - DataType dataType = ((Data) cu).getDataType(); + CodeUnit codeunit = program.getListing().getCodeUnitAt(addr(0x410)); + assertNotNull(codeunit); + assertEquals("static_var1", codeunit.getLabel()); + assertEquals(1, codeunit.getLength()); + DataType dataType = ((Data) codeunit).getDataType(); assertTrue(dataType instanceof Undefined || dataType instanceof DefaultDataType); } @Test public void test2ZeroLenGlobalVar() throws CancelledException, IOException, DWARFException { DebugInfoEntry emptyStructDIE = newStruct("emptystruct", 0).create(); - new DIECreator(dwarfProg, DWARFTag.DW_TAG_variable) - .addString(DW_AT_name, "static_var1") + new DIECreator(dwarfProg, DW_TAG_variable).addString(DW_AT_name, "static_var1") .addRef(DW_AT_type, emptyStructDIE) - .addBlock(DW_AT_location, DWARFExpressionOpCodes.DW_OP_addr, 0x10, 0x4, 0, 0, 0, 0, - 0, 0) + .addBlockBytes(DW_AT_location, instr(DW_OP_addr, 0x10, 0x4, 0, 0, 0, 0, 0, 0)) .create(); - new DIECreator(dwarfProg, DWARFTag.DW_TAG_variable) - .addString(DW_AT_name, "static_var2") + new DIECreator(dwarfProg, DW_TAG_variable).addString(DW_AT_name, "static_var2") .addRef(DW_AT_type, emptyStructDIE) - .addBlock(DW_AT_location, DWARFExpressionOpCodes.DW_OP_addr, 0x10, 0x4, 0, 0, 0, 0, - 0, 0) + .addBlockBytes(DW_AT_location, instr(DW_OP_addr, 0x10, 0x4, 0, 0, 0, 0, 0, 0)) .create(); importFunctions(); @@ -101,17 +94,13 @@ public class DWARFStaticVarImporterTest extends DWARFTestBase { throws CancelledException, IOException, DWARFException { DebugInfoEntry emptyStructDIE = newStruct("emptystruct", 0).create(); DebugInfoEntry intDIE = addInt(); - new DIECreator(dwarfProg, DWARFTag.DW_TAG_variable) - .addString(DW_AT_name, "static_var1") + new DIECreator(dwarfProg, DW_TAG_variable).addString(DW_AT_name, "static_var1") .addRef(DW_AT_type, intDIE) - .addBlock(DW_AT_location, DWARFExpressionOpCodes.DW_OP_addr, 0x10, 0x4, 0, 0, 0, 0, - 0, 0) + .addBlockBytes(DW_AT_location, instr(DW_OP_addr, 0x10, 0x4, 0, 0, 0, 0, 0, 0)) .create(); - new DIECreator(dwarfProg, DWARFTag.DW_TAG_variable) - .addString(DW_AT_name, "static_var2") + new DIECreator(dwarfProg, DW_TAG_variable).addString(DW_AT_name, "static_var2") .addRef(DW_AT_type, emptyStructDIE) - .addBlock(DW_AT_location, DWARFExpressionOpCodes.DW_OP_addr, 0x10, 0x4, 0, 0, 0, 0, - 0, 0) + .addBlockBytes(DW_AT_location, instr(DW_OP_addr, 0x10, 0x4, 0, 0, 0, 0, 0, 0)) .create(); importFunctions(); @@ -120,11 +109,11 @@ public class DWARFStaticVarImporterTest extends DWARFTestBase { assertTrue(labelNames.contains("static_var1")); assertTrue(labelNames.contains("static_var2")); - CodeUnit cu = program.getListing().getCodeUnitAt(addr(0x410)); - assertNotNull(cu); - assertEquals("static_var1", cu.getLabel()); - assertEquals(4, cu.getLength()); - assertTrue(((Data) cu).getDataType() instanceof IntegerDataType); + CodeUnit codeunit = program.getListing().getCodeUnitAt(addr(0x410)); + assertNotNull(codeunit); + assertEquals("static_var1", codeunit.getLabel()); + assertEquals(4, codeunit.getLength()); + assertTrue(((Data) codeunit).getDataType() instanceof IntegerDataType); } private Set getLabelNames(Symbol[] symbols) { diff --git a/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf/DIECreator.java b/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf/DIECreator.java index 51fe051395..e31bb859d9 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf/DIECreator.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf/DIECreator.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -84,8 +84,13 @@ public class DIECreator { for (int i = 0; i < bytes.length; i++) { bytes[i] = (byte) intBytes[i]; } + addBlockBytes(attribute, bytes); + return this; + } + + public DIECreator addBlockBytes(DWARFAttribute attribute, byte[] blockBytes) { AttrDef attrSpec = new AttrDef(attribute, attribute.getId(), DW_FORM_block1, 0); - add(attrSpec, new DWARFBlobAttribute(bytes, attrSpec)); + add(attrSpec, new DWARFBlobAttribute(blockBytes, attrSpec)); return this; } diff --git a/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf/DWARFTestBase.java b/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf/DWARFTestBase.java index 5ef82fce88..6820f1cbcb 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf/DWARFTestBase.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf/DWARFTestBase.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -29,6 +29,7 @@ import ghidra.app.plugin.core.analysis.AutoAnalysisManager; import ghidra.app.services.DataTypeManagerService; import ghidra.app.util.bin.BinaryReader; import ghidra.app.util.bin.ByteArrayProvider; +import ghidra.app.util.bin.format.dwarf.expression.*; import ghidra.app.util.bin.format.dwarf.sectionprovider.NullSectionProvider; import ghidra.program.database.ProgramBuilder; import ghidra.program.database.ProgramDB; @@ -63,7 +64,6 @@ public class DWARFTestBase extends AbstractGhidraHeadedIntegrationTest { protected CategoryPath uncatCP; protected CategoryPath dwarfRootCP; - @Before public void setUp() throws Exception { program = createProgram(); @@ -110,13 +110,13 @@ public class DWARFTestBase extends AbstractGhidraHeadedIntegrationTest { program.endTransaction(transactionID, true); } - protected BinaryReader br(byte... bytes) { - return new BinaryReader(new ByteArrayProvider(bytes), dwarfProg.isLittleEndian()); + protected BinaryReader br(int... intBytes) { + return new BinaryReader(new ByteArrayProvider(bytes(intBytes)), dwarfProg.isLittleEndian()); } protected void buildMockDIEIndexes() throws CancelledException, DWARFException { dwarfProg.buildMockDIEIndexes(); - dwarfProg.dumpDIEs(System.out); + //dwarfProg.dumpDIEs(System.out); } protected void importAllDataTypes() throws CancelledException, IOException, DWARFException { @@ -280,10 +280,9 @@ public class DWARFTestBase extends AbstractGhidraHeadedIntegrationTest { protected DebugInfoEntry addFwdPtr(int fwdRecordOffset) { ensureCompUnit(); - long absOffset = - dwarfProg.getRelativeDIEOffset(fwdRecordOffset + /* the ptr die we are about to add */ 1); - return new DIECreator(dwarfProg, DW_TAG_pointer_type) - .addRef(DW_AT_type, absOffset) + long absOffset = dwarfProg + .getRelativeDIEOffset(fwdRecordOffset + /* the ptr die we are about to add */ 1); + return new DIECreator(dwarfProg, DW_TAG_pointer_type).addRef(DW_AT_type, absOffset) .create(); } @@ -360,6 +359,21 @@ public class DWARFTestBase extends AbstractGhidraHeadedIntegrationTest { .addUInt(DW_AT_high_pc, length); } + protected DIECreator newFormalParam(DebugInfoEntry subprogram, String paramName, + DebugInfoEntry paramDataType, byte[] locationExpr) { + ensureCompUnit(); + DIECreator param = new DIECreator(dwarfProg, DW_TAG_formal_parameter) // + .addRef(DW_AT_type, paramDataType) + .setParent(subprogram); + if (locationExpr.length > 0) { + param.addBlockBytes(DW_AT_location, locationExpr); + } + if (paramName != null) { + param.addString(DW_AT_name, paramName); + } + return param; + } + protected DIECreator newFormalParam(DebugInfoEntry subprogram, String paramName, DebugInfoEntry paramDataType, int... locationExpr) { ensureCompUnit(); @@ -396,4 +410,49 @@ public class DWARFTestBase extends AbstractGhidraHeadedIntegrationTest { } assertNotEquals(0, component.getLength()); } + + protected DWARFExpression expr(byte[]... instructions) throws DWARFExpressionException { + return DWARFExpression.read(exprBytes(instructions), cu); + } + + public static byte[] exprBytes(byte[]... instructions) { + int totalBytes = 0; + for (byte[] instrBytes : instructions) { + totalBytes += instrBytes.length; + } + byte[] exprBytes = new byte[totalBytes]; + int offset = 0; + for (byte[] instrBytes : instructions) { + System.arraycopy(instrBytes, 0, exprBytes, offset, instrBytes.length); + offset += instrBytes.length; + } + return exprBytes; + } + + public static byte[] instr(DWARFExpressionOpCode opcode, int... operandBytes) { + byte[] result = new byte[1 + operandBytes.length]; + result[0] = opcode.getOpCodeValue(); + for (int i = 0; i < operandBytes.length; i++) { + result[i + 1] = (byte) operandBytes[i]; + } + return result; + } + + public static int[] uleb128(long val) { + // return int[] to match instr(...) + return bytesToInts(LEB128.encode(val, false)); + } + + public static int[] sleb128(long val) { + // return int[] to match instr(...) + return bytesToInts(LEB128.encode(val, true)); + } + + public static int[] bytesToInts(byte[] bytes) { + int[] result = new int[bytes.length]; + for (int i = 0; i < bytes.length; i++) { + result[i] = bytes[i]; + } + return result; + } } diff --git a/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf/StringTableTest.java b/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf/StringTableTest.java index c908af0567..98e1ca4eea 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf/StringTableTest.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf/StringTableTest.java @@ -31,8 +31,8 @@ import ghidra.app.util.bin.ByteArrayProvider; */ public class StringTableTest extends AbstractGenericTest { - private BinaryReader br(byte... bytes) { - return new BinaryReader(new ByteArrayProvider(bytes), true); + private BinaryReader br(int... intBytes) { + return new BinaryReader(new ByteArrayProvider(bytes(intBytes)), true); } @@ -40,9 +40,9 @@ public class StringTableTest extends AbstractGenericTest { public void testStr() throws IOException { // @formatter:off BinaryReader br = br( - /* str1 */ (byte) 'a', (byte) 'b', (byte) 0, - /* str2 */ (byte) 'c', (byte) 0, - /* str3 */ (byte) 'x', (byte) 'y', (byte) '\n', (byte) 0 + /* str1 */ 'a', 'b', 0, + /* str2 */ 'c', 0, + /* str3 */ 'x', 'y', '\n', 0 ); // @formatter:on StringTable st = new StringTable(br, StandardCharsets.US_ASCII); @@ -54,13 +54,27 @@ public class StringTableTest extends AbstractGenericTest { assertEquals("xy\n", st.getStringAtOffset(5)); } + @Test + public void testUtf8() throws IOException { + // @formatter:off + BinaryReader br = br( + /* str1 */ 0xc2, 0xbb, 'a', 'b', 'c', 0, + /* str2 */ 0xe3, 0x91, 0xad, '1', '2', '3', 0 + ); + // @formatter:on + StringTable st = new StringTable(br, StandardCharsets.UTF_8); + + assertEquals("\u00bbabc", st.getStringAtOffset(0)); + assertEquals("\u346d123", st.getStringAtOffset(6)); + } + @Test public void testOffcutStr() throws IOException { // @formatter:off BinaryReader br = br( - /* str1 */ (byte) 'a', (byte) 'b', (byte) 0, - /* str2 */ (byte) 'c', (byte) 0, - /* str3 */ (byte) 'x', (byte) 'y', (byte) '\n', (byte) 0 + /* str1 */ 'a', 'b', 0, + /* str2 */ 'c', 0, + /* str3 */ 'x', 'y', '\n', 0 ); // @formatter:on StringTable st = new StringTable(br, StandardCharsets.US_ASCII); @@ -75,9 +89,9 @@ public class StringTableTest extends AbstractGenericTest { public void testTrailingOffcutStr() { // @formatter:off BinaryReader br = br( - /* str1 */ (byte) 'a', (byte) 'b', (byte) 0, - /* str2 */ (byte) 'c', (byte) 0, - /* str3 */ (byte) 'x', (byte) 'y', (byte) '\n', (byte) 0 + /* str1 */ 'a', 'b', 0, + /* str2 */ 'c', 0, + /* str3 */ 'x', 'y', '\n', 0 ); // @formatter:on StringTable st = new StringTable(br, StandardCharsets.US_ASCII); @@ -95,9 +109,9 @@ public class StringTableTest extends AbstractGenericTest { public void testNegOffset() { // @formatter:off BinaryReader br = br( - /* str1 */ (byte) 'a', (byte) 'b', (byte) 0, - /* str2 */ (byte) 'c', (byte) 0, - /* str3 */ (byte) 'x', (byte) 'y', (byte) '\n', (byte) 0 + /* str1 */ 'a', 'b', 0, + /* str2 */ 'c', 0, + /* str3 */ 'x', 'y', '\n', 0 ); // @formatter:on StringTable st = new StringTable(br, StandardCharsets.US_ASCII); diff --git a/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf/attribs/DWARFAttributeFactoryTest.java b/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf/attribs/DWARFAttributeFactoryTest.java index a8451ffe93..f98c0e7c5f 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf/attribs/DWARFAttributeFactoryTest.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf/attribs/DWARFAttributeFactoryTest.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -53,10 +53,10 @@ public class DWARFAttributeFactoryTest extends DWARFTestBase { public void testStr() throws IOException { // @formatter:off BinaryReader br = br( - /* str1 */ (byte) 'a', (byte) 'b', (byte) 0, - /* str2 */ (byte) 'c', (byte) 0, - /* str3 */ (byte) 'x', (byte) 'y', (byte) '\n', (byte) 0, - /* guard byte for test */ (byte) 0xff); + /* str1 */ 'a', 'b', 0, + /* str2 */ 'c', 0, + /* str3 */ 'x', 'y', '\n', 0, + /* guard byte for test */ 0xff); // @formatter:on DWARFAttributeValue result = read(br, DW_AT_name, DW_FORM_string); assertTrue("Should be string", result instanceof DWARFStringAttribute); @@ -81,10 +81,10 @@ public class DWARFAttributeFactoryTest extends DWARFTestBase { // @formatter:off BinaryReader br = br( - /* ref to str2 */ (byte) 0, (byte) 0, (byte) 0, (byte) 100, - /* ref to str1 */ (byte) 0, (byte) 0, (byte) 0, (byte) 1, - /* ref to str2 ofcut */ (byte) 0, (byte) 0, (byte) 0, (byte) 101, - /* guard byte for test */ (byte) 0xff); + /* ref to str2 */ 0, 0, 0, 100, + /* ref to str1 */ 0, 0, 0, 1, + /* ref to str2 ofcut */ 0, 0, 0, 101, + /* guard byte for test */ 0xff); // @formatter:on DWARFAttributeValue result = read(br, DW_AT_name, DW_FORM_strp); @@ -110,9 +110,9 @@ public class DWARFAttributeFactoryTest extends DWARFTestBase { // @formatter:off BinaryReader br = br( - /* str1 */ (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 100, - /* str2 */ (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 1, - /* guard byte for test */ (byte) 0xff); + /* str1 */ 0, 0, 0, 0, 0, 0, 0, 100, + /* str2 */ 0, 0, 0, 0, 0, 0, 0, 1, + /* guard byte for test */ 0xff); // @formatter:on setCompUnit(dwarfProg.addCompUnit(DWARFSourceLanguage.DW_LANG_C, 8 /* dwarf64 */)); @@ -130,7 +130,7 @@ public class DWARFAttributeFactoryTest extends DWARFTestBase { @Test public void testData1() throws IOException { - BinaryReader br = br((byte) 55, (byte) 0xfe); + BinaryReader br = br(55, 0xfe); DWARFAttributeValue result = read(br, DW_AT_byte_size, DW_FORM_data1); assertTrue("Should be const", result instanceof DWARFNumericAttribute); @@ -144,7 +144,7 @@ public class DWARFAttributeFactoryTest extends DWARFTestBase { @Test public void testData2() throws IOException { - BinaryReader br = br((byte) 0, (byte) 55, (byte) 0xff, (byte) 0xfe); + BinaryReader br = br(0, 55, 0xff, 0xfe); DWARFAttributeValue result = read(br, DW_AT_byte_size, DW_FORM_data2); assertTrue("Should be const", result instanceof DWARFNumericAttribute); @@ -158,8 +158,7 @@ public class DWARFAttributeFactoryTest extends DWARFTestBase { @Test public void testData4() throws IOException { - BinaryReader br = br((byte) 0, (byte) 0, (byte) 0, (byte) 55, (byte) 0xff, (byte) 0xff, - (byte) 0xff, (byte) 0xfe); + BinaryReader br = br(0, 0, 0, 55, 0xff, 0xff, 0xff, 0xfe); DWARFAttributeValue result = read(br, DW_AT_byte_size, DW_FORM_data4); assertTrue("Should be const", result instanceof DWARFNumericAttribute); @@ -176,8 +175,8 @@ public class DWARFAttributeFactoryTest extends DWARFTestBase { public void testData8() throws IOException { // @formatter:off BinaryReader br = br( - (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 55, - (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xfe); + 0, 0, 0, 0, 0, 0, 0, 55, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe); // @formatter:on DWARFAttributeValue result = read(br, DW_AT_byte_size, DW_FORM_data8); @@ -195,7 +194,7 @@ public class DWARFAttributeFactoryTest extends DWARFTestBase { @Test public void testSData() throws IOException { - BinaryReader br = br((byte) 0, (byte) 55, (byte) 0xff, (byte) 0x7e); + BinaryReader br = br(0, 55, 0xff, 0x7e); DWARFAttributeValue result = read(br, DW_AT_byte_size, DW_FORM_sdata); assertTrue("Should be const", result instanceof DWARFNumericAttribute); @@ -212,7 +211,7 @@ public class DWARFAttributeFactoryTest extends DWARFTestBase { @Test public void testUData() throws IOException { - BinaryReader br = br((byte) 0, (byte) 55, (byte) 0xff, (byte) 0x7e); + BinaryReader br = br(0, 55, 0xff, 0x7e); DWARFAttributeValue result = read(br, DW_AT_byte_size, DW_FORM_udata); assertTrue("Should be const", result instanceof DWARFNumericAttribute); @@ -231,8 +230,8 @@ public class DWARFAttributeFactoryTest extends DWARFTestBase { public void testAddr() throws IOException { // @formatter:off BinaryReader br = br( - (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 55, - (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xfe); + 0, 0, 0, 0, 0, 0, 0, 55, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe); // @formatter:on DWARFAttributeValue result = read(br, DW_AT_byte_size, DW_FORM_addr); @@ -247,7 +246,7 @@ public class DWARFAttributeFactoryTest extends DWARFTestBase { @Test public void testBlock1() throws IOException { - BinaryReader br = br((byte) 1, (byte) 0x55, (byte) 0); + BinaryReader br = br(1, 0x55, 0); DWARFAttributeValue result = read(br, DW_AT_byte_size, DW_FORM_block1); assertTrue("Should be block", result instanceof DWARFBlobAttribute); @@ -258,8 +257,8 @@ public class DWARFAttributeFactoryTest extends DWARFTestBase { assertTrue("Should be block", result instanceof DWARFBlobAttribute); assertEquals("should be 0", 0, ((DWARFBlobAttribute) result).getLength()); - byte[] bytes = new byte[1 + 255 /* max_ubyte */]; - bytes[0] = (byte) 0xff; + int[] bytes = new int[1 + 255 /* max_ubyte */]; + bytes[0] = 0xff; result = read(br(bytes), DW_AT_byte_size, DW_FORM_block1); assertTrue("Should be block", result instanceof DWARFBlobAttribute); assertEquals("should be 255", 255, ((DWARFBlobAttribute) result).getLength()); @@ -267,7 +266,7 @@ public class DWARFAttributeFactoryTest extends DWARFTestBase { @Test public void testBlock2() throws IOException { - BinaryReader br = br((byte) 0, (byte) 1, (byte) 0x55, (byte) 0, (byte) 0); + BinaryReader br = br(0, 1, 0x55, 0, 0); DWARFAttributeValue result = read(br, DW_AT_byte_size, DW_FORM_block2); assertTrue("Should be block", result instanceof DWARFBlobAttribute); @@ -278,9 +277,9 @@ public class DWARFAttributeFactoryTest extends DWARFTestBase { assertTrue("Should be block", result instanceof DWARFBlobAttribute); assertEquals("should be 0", 0, ((DWARFBlobAttribute) result).getLength()); - byte[] bytes = new byte[2 + 0xffff /* max_ushort */]; - bytes[0] = (byte) 0xff; - bytes[1] = (byte) 0xff; + int[] bytes = new int[2 + 0xffff /* max_ushort */]; + bytes[0] = 0xff; + bytes[1] = 0xff; result = read(br(bytes), DW_AT_byte_size, DW_FORM_block2); assertTrue("Should be block", result instanceof DWARFBlobAttribute); assertEquals("should be 64k", 0xffff, ((DWARFBlobAttribute) result).getLength()); @@ -288,8 +287,7 @@ public class DWARFAttributeFactoryTest extends DWARFTestBase { @Test public void testBlock4() throws IOException { - BinaryReader br = br((byte) 0, (byte) 0, (byte) 0, (byte) 1, (byte) 0x55, (byte) 0, - (byte) 0, (byte) 0, (byte) 0); + BinaryReader br = br(0, 0, 0, 1, 0x55, 0, 0, 0, 0); DWARFAttributeValue result = read(br, DW_AT_byte_size, DW_FORM_block4); assertTrue("Should be block", result instanceof DWARFBlobAttribute); @@ -301,24 +299,24 @@ public class DWARFAttributeFactoryTest extends DWARFTestBase { assertEquals("should be 0", 0, ((DWARFBlobAttribute) result).getLength()); // Test max block4 sized chunk - byte[] bytes = new byte[4 + DWARFForm.MAX_BLOCK4_SIZE]; + int[] bytes = new int[4 + DWARFForm.MAX_BLOCK4_SIZE]; //DWARFAttributeFactory.MAX_BLOCK4_SIZE == 0x00_10_00_00 - bytes[0] = (byte) 0x00; - bytes[1] = (byte) 0x10; - bytes[2] = (byte) 0x00; - bytes[3] = (byte) 0x00; + bytes[0] = 0x00; + bytes[1] = 0x10; + bytes[2] = 0x00; + bytes[3] = 0x00; result = read(br(bytes), DW_AT_byte_size, DW_FORM_block4); assertTrue("Should be block", result instanceof DWARFBlobAttribute); assertEquals("should be MAX_BLOCK4_SIZE", DWARFForm.MAX_BLOCK4_SIZE, ((DWARFBlobAttribute) result).getLength()); // Test block4 size that is larger than max - bytes = new byte[4 + DWARFForm.MAX_BLOCK4_SIZE + 1]; + bytes = new int[4 + DWARFForm.MAX_BLOCK4_SIZE + 1]; //DWARFAttributeFactory.MAX_BLOCK4_SIZE == 0x00_10_00_00 + 1 == 0x00_10_00_01 - bytes[0] = (byte) 0x00; - bytes[1] = (byte) 0x10; - bytes[2] = (byte) 0x00; - bytes[3] = (byte) 0x01; + bytes[0] = 0x00; + bytes[1] = 0x10; + bytes[2] = 0x00; + bytes[3] = 0x01; try { result = read(br(bytes), DW_AT_byte_size, DW_FORM_block4); fail( @@ -331,7 +329,7 @@ public class DWARFAttributeFactoryTest extends DWARFTestBase { @Test public void testBlock() throws IOException { - BinaryReader br = br((byte) 1, (byte) 0x55, (byte) 0); + BinaryReader br = br(1, 0x55, 0); DWARFAttributeValue result = read(br, DW_AT_byte_size, DW_FORM_block); assertTrue("Should be block", result instanceof DWARFBlobAttribute); @@ -345,7 +343,7 @@ public class DWARFAttributeFactoryTest extends DWARFTestBase { @Test public void testExprLoc() throws IOException { - BinaryReader br = br((byte) 1, (byte) 0x55, (byte) 0); + BinaryReader br = br(1, 0x55, 0); DWARFAttributeValue result = read(br, DW_AT_byte_size, DW_FORM_exprloc); assertTrue("Should be exprloc", result instanceof DWARFBlobAttribute); @@ -359,7 +357,7 @@ public class DWARFAttributeFactoryTest extends DWARFTestBase { @Test public void testFlag() throws IOException { - BinaryReader br = br((byte) 55, (byte) 0x00); + BinaryReader br = br(55, 0x00); DWARFAttributeValue result = read(br, DW_AT_byte_size, DW_FORM_flag); assertTrue("Should be flag", result instanceof DWARFBooleanAttribute); @@ -372,7 +370,7 @@ public class DWARFAttributeFactoryTest extends DWARFTestBase { @Test public void testFlagPresent() throws IOException { - BinaryReader br = br(new byte[] {} /* no bytes needed for flag_present */); + BinaryReader br = br(new int[] {} /* no bytes needed for flag_present */); DWARFAttributeValue result = read(br, DW_AT_byte_size, DW_FORM_flag_present); assertTrue("Should be flag", result instanceof DWARFBooleanAttribute); @@ -381,7 +379,7 @@ public class DWARFAttributeFactoryTest extends DWARFTestBase { @Test public void testRef1() throws IOException { - BinaryReader br = br((byte) 55, (byte) 0xfe); + BinaryReader br = br(55, 0xfe); DWARFAttributeValue result = read(br, DW_AT_byte_size, DW_FORM_ref1); assertTrue("Should be ref", result instanceof DWARFNumericAttribute); @@ -396,7 +394,7 @@ public class DWARFAttributeFactoryTest extends DWARFTestBase { @Test public void testRef2() throws IOException { - BinaryReader br = br((byte) 0, (byte) 55, (byte) 0xff, (byte) 0xfe); + BinaryReader br = br(0, 55, 0xff, 0xfe); DWARFAttributeValue result = read(br, DW_AT_byte_size, DW_FORM_ref2); assertTrue("Should be ref", result instanceof DWARFNumericAttribute); @@ -411,8 +409,7 @@ public class DWARFAttributeFactoryTest extends DWARFTestBase { @Test public void testRef4() throws IOException { - BinaryReader br = br((byte) 0, (byte) 0, (byte) 0, (byte) 55, (byte) 0xff, (byte) 0xff, - (byte) 0xff, (byte) 0xfe); + BinaryReader br = br(0, 0, 0, 55, 0xff, 0xff, 0xff, 0xfe); DWARFAttributeValue result = read(br, DW_AT_byte_size, DW_FORM_ref4); assertTrue("Should be ref", result instanceof DWARFNumericAttribute); @@ -429,8 +426,8 @@ public class DWARFAttributeFactoryTest extends DWARFTestBase { public void testSecOffset() throws IOException { // @formatter:off BinaryReader br = br( - (byte) 0, (byte) 0, (byte) 0, (byte) 55, - (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 56 + 0, 0, 0, 55, + 0, 0, 0, 0, 0, 0, 0, 56 ); // @formatter:on @@ -448,8 +445,8 @@ public class DWARFAttributeFactoryTest extends DWARFTestBase { public void testRef8() throws IOException { // @formatter:off BinaryReader br = br( - (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 55, - (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xfe); + 0, 0, 0, 0, 0, 0, 0, 55, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe); // @formatter:on DWARFAttributeValue result = read(br, DW_AT_byte_size, DW_FORM_ref8); @@ -466,7 +463,7 @@ public class DWARFAttributeFactoryTest extends DWARFTestBase { @Test public void testRefUData() throws IOException { - BinaryReader br = br((byte) 55, (byte) 0xff, (byte) 0x7e); + BinaryReader br = br(55, 0xff, 0x7e); DWARFAttributeValue result = read(br, DW_AT_byte_size, DW_FORM_ref_udata); assertTrue("Should be ref", result instanceof DWARFNumericAttribute); @@ -483,10 +480,10 @@ public class DWARFAttributeFactoryTest extends DWARFTestBase { public void testRefAddr() throws IOException { // @formatter:off BinaryReader br = br( - (byte) 0, (byte) 0, (byte) 0, (byte) 55, - (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, - (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 55, - (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xfe + 0, 0, 0, 55, + 0xff, 0xff, 0xff, 0xff, + 0, 0, 0, 0, 0, 0, 0, 55, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe ); // @formatter:on @@ -516,10 +513,10 @@ public class DWARFAttributeFactoryTest extends DWARFTestBase { public void testIndirect() throws IOException { // @formatter:off BinaryReader br = br( - (byte)DW_FORM_data1.getId(), - (byte) 55, - (byte)DW_FORM_ref4.getId(), - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0xaa + DW_FORM_data1.getId(), + 55, + DW_FORM_ref4.getId(), + 0x00, 0x00, 0x00, 0xaa ); // @formatter:on diff --git a/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionEvaluatorTest.java b/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionEvaluatorTest.java index b04c5b60a3..7a6371751e 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionEvaluatorTest.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionEvaluatorTest.java @@ -15,14 +15,16 @@ */ package ghidra.app.util.bin.format.dwarf.expression; -import static ghidra.app.util.bin.format.dwarf.expression.DWARFExpressionOpCodes.*; +import static ghidra.app.util.bin.format.dwarf.expression.DWARFExpressionOpCode.*; import static org.junit.Assert.*; import java.io.IOException; -import org.junit.*; +import org.junit.Before; +import org.junit.Test; import ghidra.app.util.bin.format.dwarf.DWARFTestBase; +import ghidra.program.model.scalar.Scalar; public class DWARFExpressionEvaluatorTest extends DWARFTestBase { @@ -36,190 +38,41 @@ public class DWARFExpressionEvaluatorTest extends DWARFTestBase { evaluator = new DWARFExpressionEvaluator(cu); } - /** - * Test {@link DWARFExpressionEvaluator} by executing a expr that calculates - * the fibonacci series. - * - * @throws IOException - * @throws DWARFExpressionException - */ @Test public void fibTest() throws IOException, DWARFExpressionException { - // calculates the Nth fibonacci number + // Test by executing a expr that calculates the fibonacci series. + // Calculates the Nth fibonacci number, with N being pushed on stack as 'arg' to the + // expression, result left on stack. // @formatter:off - DWARFExpression expr = evaluator.readExpr( - new byte[] { - DW_OP_lit0, - DW_OP_lit1, - DW_OP_rot, - DW_OP_rot, - DW_OP_lit1, - DW_OP_minus, - DW_OP_dup, - DW_OP_lit0, - DW_OP_eq, - DW_OP_bra, 0xc, 0, - DW_OP_rot, - DW_OP_dup, - DW_OP_rot, - DW_OP_plus, - DW_OP_rot, - DW_OP_rot, - DW_OP_skip, (byte)0xf2, (byte) 0xff, - DW_OP_drop, - DW_OP_swap, - DW_OP_drop - }); + DWARFExpression expr = expr( + instr(DW_OP_lit0), + instr(DW_OP_lit1), + instr(DW_OP_rot), + instr(DW_OP_rot), + instr(DW_OP_lit1), + instr(DW_OP_minus), + instr(DW_OP_dup), + instr(DW_OP_lit0), + instr(DW_OP_eq), + instr(DW_OP_bra, 0xc, 0), + instr(DW_OP_rot), + instr(DW_OP_dup), + instr(DW_OP_rot), + instr(DW_OP_plus), + instr(DW_OP_rot), + instr(DW_OP_rot), + instr(DW_OP_skip, 0xf2, 0xff), + instr(DW_OP_drop), + instr(DW_OP_swap), + instr(DW_OP_drop) + ); // @formatter:on - long result = evaluator.evaluate(expr, 19).pop(); + evaluator.evaluate(expr, 19); + long result = evaluator.popLong(); assertEquals("Fibonacci[19] should be 4181", 4181, result); } - /** - * Test reading (but not executing) an expression that has every opcode - * that takes operands. Operands that are signed vs unsigned are present in - * byte patterns that exercise high-bit set vs. not set. - * @throws IOException - */ - @Test - public void testReadingAllOpCodesWithArgs() throws DWARFExpressionException { - // @formatter:off - DWARFExpression expr = - evaluator.readExpr(new byte[] { - /* 0 */ DW_OP_addr, 1, 2, 3, 4, 5, 6, 7, 8, - - /* 1 */ DW_OP_const1u, (byte)0x55, - /* 2 */ DW_OP_const1u, (byte)0xfe, - - /* 3 */ DW_OP_const1s, (byte)0x55, - /* 4 */ DW_OP_const1s, (byte) 0xfe, // -2 - - /* 5 */ DW_OP_const2u, (byte)0x55, (byte)0x55, - /* 6 */ DW_OP_const2u, (byte)0xf0, (byte)0xf0, - - /* 7 */ DW_OP_const2s, (byte)0x55, (byte)0x55, - /* 8 */ DW_OP_const2s, (byte)0xf0, (byte)0xf0, // -3856 - - /* 9 */ DW_OP_const4u, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, - /* 10 */ DW_OP_const4u, (byte)0xf0, (byte)0xf0, (byte)0xf0, (byte)0xf0, - - /* 11 */ DW_OP_const4s, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, - /* 12 */ DW_OP_const4s, (byte) 0xf0, (byte) 0xf0, (byte) 0xf0, (byte) 0xf0, // -252645136 - - /* 13 */ DW_OP_const8u, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, - /* 14 */ DW_OP_const8u, (byte)0xf0, (byte)0xf0, (byte)0xf0, (byte)0xf0, (byte)0xf0, (byte)0xf0, (byte)0xf0, (byte)0xf0, - - /* 15 */ DW_OP_const8s, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, - /* 16 */ DW_OP_const8s, (byte)0xf0, (byte)0xf0, (byte)0xf0, (byte)0xf0, (byte)0xf0, (byte)0xf0, (byte)0xf0, (byte)0xf0, - - /* 17 */ DW_OP_constu, (byte)0x55, - /* 18 */ DW_OP_constu, (byte)0x80, (byte)0x01, // == 128 - /* 19 */ DW_OP_constu, (byte)0x80, (byte)0x7f, // == 16256 - - /* 20 */ DW_OP_consts, (byte)0x33, - /* 21 */ DW_OP_consts, (byte)0x80, (byte)0x01, // == 128 - /* 22 */ DW_OP_consts, (byte)0x80, (byte)0x7f, // == -128 - - /* 23 */ DW_OP_pick, (byte)0x04, - /* 24 */ DW_OP_pick, (byte)0xf0, - - /* 25 */ DW_OP_plus_uconst, (byte)0x80, (byte)0x01, - /* 26 */ DW_OP_plus_uconst, (byte)0xbf, (byte)0x01, // == 191 - - /* 27 */ DW_OP_skip, (byte)0x05, (byte)0x05, - /* 28 */ DW_OP_skip, (byte)0xf0, (byte)0xf0, - - /* 29 */ DW_OP_bra, (byte)0x05, (byte)0x05, - /* 30 */ DW_OP_bra, (byte)0xf0, (byte)0xf0, - - /* 31 */ DW_OP_breg0, (byte) 0x0a, - /* 32 */ DW_OP_breg0, (byte)0x80, (byte)0x01, // == ???? - - /* 33 */ (byte)DW_OP_breg31, (byte)0x55, - /* 34 */ (byte)DW_OP_breg31, (byte)0x80, (byte)0x01, // == ???? - - /* 35 */ (byte)DW_OP_regx, (byte)0x55, - /* 36 */ (byte)DW_OP_regx, (byte)0x80, (byte)0x01, // == ???? - - /* 37 */ (byte)DW_OP_fbreg, (byte)0x55, - /* 38 */ (byte)DW_OP_fbreg, (byte)0x80, (byte)0x01, // == ???? - - /* 39 */ (byte)DW_OP_bregx, (byte)0x55, (byte)0x44, - /* 40 */ (byte)DW_OP_bregx, (byte)0x55, (byte)0x80, (byte)0x01, - - /* 41 */ (byte)DW_OP_piece, (byte)0x55, - /* 42 */ (byte)DW_OP_piece, (byte)0x80, (byte)0x01, // == 191 - - /* 43 */ (byte)DW_OP_deref_size, (byte)0x55, - /* 44 */ (byte)DW_OP_deref_size, (byte)0xf0, - - /* 45 */ (byte)DW_OP_xderef_size, (byte)0x55, - /* 46 */ (byte)DW_OP_xderef_size, (byte)0xf0, - - /* 47 */ (byte)DW_OP_call2, (byte)0x55, (byte)0x55, - /* 48 */ (byte)DW_OP_call2, (byte)0xf0, (byte)0xf0, - - /* 49 */ (byte)DW_OP_call4, (byte)0x55, (byte)0x55, (byte)0x55, (byte)0x55, - /* 50 */ (byte)DW_OP_call4, (byte)0xf0, (byte)0xf0, (byte)0xf0, (byte)0xf0, - - /* 51 */ (byte)DW_OP_bit_piece, (byte)0x55, (byte)0x55, - /* 52 */ (byte)DW_OP_bit_piece, (byte)0x80, (byte)0x01, (byte)0x81, (byte)0x01, - - /* 53 */ (byte) DW_OP_call_ref, 4, 3, 2, 1, - - /* 54 */ (byte) DW_OP_implicit_value, (byte) 0x05, 1, 2, 3, 4, 5, // - - /* 55 */ (byte) DW_OP_implicit_pointer, 1, 0, 0, 0, 2, - - /* 56 */ (byte) DW_OP_addrx, 0 - - - }); - // @formatter:on - - assertEquals(4, evaluator.getDWARFCompilationUnit().getIntSize()); - assertNotNull("Did not successfully instantiate DWARFExpression", expr); - assertEquals("Did not read all opcodes", 57, expr.getOpCount()); - - assertEquals(0x55, expr.getOp(1).getOperandValue(0)); - assertEquals(0xfe, expr.getOp(2).getOperandValue(0)); - - assertEquals(0x55, expr.getOp(3).getOperandValue(0)); - assertEquals(-2, expr.getOp(4).getOperandValue(0)); - - assertEquals(0x5555, expr.getOp(5).getOperandValue(0)); - assertEquals(0xf0f0, expr.getOp(6).getOperandValue(0)); - - assertEquals(0x5555, expr.getOp(7).getOperandValue(0)); - assertEquals(-3856, expr.getOp(8).getOperandValue(0)); - - assertEquals(0x55555555, expr.getOp(9).getOperandValue(0)); - assertEquals(0xf0f0f0f0L, expr.getOp(10).getOperandValue(0)); - - assertEquals(0x55555555, expr.getOp(11).getOperandValue(0)); - assertEquals(-252645136, expr.getOp(12).getOperandValue(0)); - - assertEquals(0x5555555555555555L, expr.getOp(13).getOperandValue(0)); - assertEquals(0xf0f0f0f0f0f0f0f0L, expr.getOp(14).getOperandValue(0)); - - assertEquals(0x5555555555555555L, expr.getOp(15).getOperandValue(0)); - assertEquals(0xf0f0f0f0f0f0f0f0L, expr.getOp(16).getOperandValue(0)); - - assertEquals(0x55, expr.getOp(17).getOperandValue(0)); - assertEquals(128, expr.getOp(18).getOperandValue(0)); - assertEquals(16256, expr.getOp(19).getOperandValue(0)); - - assertEquals(0x33, expr.getOp(20).getOperandValue(0)); - assertEquals(128, expr.getOp(21).getOperandValue(0)); - assertEquals(-128, expr.getOp(22).getOperandValue(0)); - - assertEquals(5, expr.getOp(54).getOperandValue(0)); - - assertEquals(1, expr.getOp(55).getOperandValue(0)); - assertEquals(2, expr.getOp(55).getOperandValue(1)); - } - @Test public void test_DW_OP_pick() throws DWARFExpressionException { @@ -229,8 +82,8 @@ public class DWARFExpressionEvaluatorTest extends DWARFTestBase { } for (int i = 0; i < count; i++) { long expected = (count - i - 1) * 3; - evaluator.evaluate(new byte[] { DW_OP_pick, (byte) i }); - long result = evaluator.pop(); + evaluator.evaluate(instr(DW_OP_pick, i)); + long result = evaluator.popLong(); assertEquals(expected, result); } @@ -244,7 +97,7 @@ public class DWARFExpressionEvaluatorTest extends DWARFTestBase { } try { - evaluator.evaluate(new byte[] { DW_OP_pick, (byte) (count + 1) }); + evaluator.evaluate(instr(DW_OP_pick, (byte) (count + 1))); fail("Should not get here"); } catch (DWARFExpressionException e) { @@ -256,13 +109,13 @@ public class DWARFExpressionEvaluatorTest extends DWARFTestBase { public void test_DW_OP_over() throws DWARFExpressionException { evaluator.push(10); evaluator.push(20); - evaluator.evaluate(new byte[] { DW_OP_over }); - assertEquals(10, evaluator.pop()); + evaluator.evaluate(instr(DW_OP_over)); + assertEquals(10, evaluator.popLong()); } @Test public void test_DW_OP_over_OOB() throws DWARFExpressionException { - DWARFExpression expr = evaluator.readExpr(new byte[] { DW_OP_over }); + DWARFExpression expr = expr(instr(DW_OP_over)); try { evaluator.evaluate(expr); @@ -284,49 +137,37 @@ public class DWARFExpressionEvaluatorTest extends DWARFTestBase { @Test public void test_DW_OP_deref() throws DWARFExpressionException { - DWARFExpression expr = - evaluator.readExpr(new byte[] { (byte) DW_OP_fbreg, 0x48, DW_OP_deref }); - evaluator.setFrameBase(0); - evaluator.evaluate(expr); - assertTrue(evaluator.isDeref()); + try { + evaluator.setFrameBaseStackLocation(0); + evaluator.evaluate(expr(instr(DW_OP_fbreg, 0x48), instr(DW_OP_deref))); + fail(); + } + catch (DWARFExpressionUnsupportedOpException e) { + assertEquals(DW_OP_deref, e.getInstruction().getOpCode()); + } } - /** - * Test to ensure that non-terminal DW_OP_deref opcodes trigger an exception. - * @throws IOException - */ @Test public void test_DW_OP_deref_nonterm() throws DWARFExpressionException { - DWARFExpression expr = - evaluator.readExpr(new byte[] { (byte) DW_OP_fbreg, 0x48, DW_OP_deref, DW_OP_dup }); - + // Test to ensure that non-terminal DW_OP_deref opcodes trigger an exception. try { - evaluator.setFrameBase(0); - evaluator.evaluate(expr); + evaluator.setFrameBaseStackLocation(0); + evaluator + .evaluate(expr(instr(DW_OP_fbreg, 0x48), instr(DW_OP_deref), instr(DW_OP_dup))); fail("Should not get here"); } - catch (DWARFExpressionException dee) { - // good + catch (DWARFExpressionUnsupportedOpException e) { + assertEquals(DW_OP_deref, e.getInstruction().getOpCode()); } } - /** - * Test to ensure that non-terminal DW_OP_reg[?] opcodes trigger an exception - * when evaluating. - * - * @throws IOException - * @throws DWARFExpressionException - */ @Test - public void test_DW_OP_regx_nonterm() throws IOException, DWARFExpressionException { - - DWARFExpression expr1 = evaluator.readExpr(new byte[] { (byte) DW_OP_reg0, DW_OP_dup }); - DWARFExpression expr2 = - evaluator.readExpr(new byte[] { (byte) DW_OP_regx, (byte) 0x01, DW_OP_dup }); - + public void test_DW_OP_regx_nonterm() { + // Test to ensure that non-terminal DW_OP_reg[?] opcodes trigger an exception + // when evaluating. try { - evaluator.evaluate(expr1); + evaluator.evaluate(expr(instr(DW_OP_reg0), instr(DW_OP_neg))); fail("Should not get here"); } catch (DWARFExpressionException dee) { @@ -334,7 +175,7 @@ public class DWARFExpressionEvaluatorTest extends DWARFTestBase { } try { - evaluator.evaluate(expr2); + evaluator.evaluate(expr(instr(DW_OP_regx, 0x01), instr(DW_OP_neg))); fail("Should not get here"); } catch (DWARFExpressionException dee) { @@ -342,19 +183,29 @@ public class DWARFExpressionEvaluatorTest extends DWARFTestBase { } } - /** - * Test to ensure that endless loops or excessive runtime are prevented by - * {@link DWARFExpressionEvaluator#setMaxStepCount(int) maxStepCount} - * - * @throws IOException - */ + @Test + public void test_DW_OP_regx_callback() throws DWARFExpressionException { + evaluator.setValReader(vn -> new Scalar(64, 0x10000)); + evaluator.evaluate(expr(instr(DW_OP_reg0), instr(DW_OP_neg))); + long result = evaluator.popLong(); + assertEquals(-0x10000, result); + } + + @Test + public void test_DW_OP_breg_callback() throws DWARFExpressionException { + evaluator.setValReader(vn -> new Scalar(64, 0x10000)); + evaluator.evaluate(expr(instr(DW_OP_breg0, sleb128(-100)), instr(DW_OP_neg))); + long result = evaluator.popLong(); + assertEquals(-(0x10000 - 100), result); + } + @Test(timeout = 10000) - public void testExcessiveExprLength() throws DWARFExpressionException { - // Endless loop: nop, skip -1. - DWARFExpression expr = evaluator.readExpr( - new byte[] { (byte) DW_OP_nop, (byte) DW_OP_skip, (byte) 0xff, (byte) 0xff, }); + public void testExcessiveExprLength() { + // Test to ensure that endless loops or excessive runtime are prevented by + // DWARFExpressionEvaluator.setMaxStepCount(int) maxStepCount try { - evaluator.evaluate(expr); + // Endless loop: nop, skip -1. + evaluator.evaluate(expr(instr(DW_OP_nop), instr(DW_OP_skip, 0xff, 0xff))); fail( "DWARFExpressionEvaluator should have thrown an exception because of the length of the expression, " + "but you are probably not reading this message because junit can't get here because of the endless loop in the expr."); @@ -362,21 +213,12 @@ public class DWARFExpressionEvaluatorTest extends DWARFTestBase { catch (DWARFExpressionException dee) { // good } - } - /** - * Test to ensure that endless loops are ended when the thread is interrupted. - * - * @throws IOException - */ @Test(timeout = 10000) - public void testThreadIntr() throws DWARFExpressionException { - // Endless loop: nop, skip -1. - DWARFExpression expr = evaluator.readExpr( - new byte[] { (byte) DW_OP_nop, (byte) DW_OP_skip, (byte) 0xff, (byte) 0xff, }); - - final Thread junitThread = Thread.currentThread(); + public void testThreadIntr() { + // Test to ensure that endless loops are ended when the thread is interrupted. + Thread junitThread = Thread.currentThread(); Thread intrThread = new Thread(() -> { try { Thread.sleep(500); @@ -390,7 +232,10 @@ public class DWARFExpressionEvaluatorTest extends DWARFTestBase { try { evaluator.setMaxStepCount(Integer.MAX_VALUE); - evaluator.evaluate(expr); + + // Endless loop: nop, skip -1. + evaluator.evaluate(expr(instr(DW_OP_nop), instr(DW_OP_skip, 0xff, 0xff))); + fail( "DWARFExpressionEvaluator should have thrown an exception because it recieved an interrupt, " + "but you are probably not reading this message because junit can't get here because of the endless loop in the expr."); @@ -401,27 +246,10 @@ public class DWARFExpressionEvaluatorTest extends DWARFTestBase { } @Test - public void testBadExpr() { - try { - DWARFExpression expr = - evaluator.readExpr(new byte[] { DW_OP_addr, 1, 2, 3, 4, 5, 6, 7, 8, DW_OP_const1u, - (byte) 0x55, DW_OP_const1u, (byte) 0xfe, DW_OP_addr, 1, 2 /* truncated */ }); - fail( - "readExpr should have thrown an exception because the expr's final op was truncated: " + - expr.toString()); - } - catch (DWARFExpressionException dee) { - // Should have been able to read 3 of the operations before failing - Assert.assertEquals(dee.getExpression().getOpCount(), 3); - } - } - - @Test - public void testAddrx() throws DWARFExpressionException { + public void testAddrx() { // test that OP_addrx fails with invalid index. Needs real test - DWARFExpression expr = evaluator.readExpr(new byte[] { (byte) DW_OP_addrx, 0 }); try { - evaluator.evaluate(expr); + evaluator.evaluate(expr(instr(DW_OP_addrx, 0))); fail(); } catch (DWARFExpressionException dee) { @@ -430,11 +258,10 @@ public class DWARFExpressionEvaluatorTest extends DWARFTestBase { } @Test - public void testConstx() throws DWARFExpressionException { + public void testConstx() { // test that OP_constx fails with invalid index. Needs real test - DWARFExpression expr = evaluator.readExpr(new byte[] { (byte) DW_OP_constx, 0 }); try { - evaluator.evaluate(expr); + evaluator.evaluate(expr(instr(DW_OP_constx, 0))); fail(); } catch (DWARFExpressionException dee) { diff --git a/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionTest.java b/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionTest.java new file mode 100644 index 0000000000..935fccc1a5 --- /dev/null +++ b/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpressionTest.java @@ -0,0 +1,237 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.util.bin.format.dwarf.expression; + +import static ghidra.app.util.bin.format.dwarf.expression.DWARFExpressionOpCode.*; +import static org.junit.Assert.*; + +import org.junit.*; + +import ghidra.app.util.bin.format.dwarf.DWARFTestBase; + +public class DWARFExpressionTest extends DWARFTestBase { + + DWARFExpressionEvaluator evaluator; + + @Override + @Before + public void setUp() throws Exception { + super.setUp(); + ensureCompUnit(); + evaluator = new DWARFExpressionEvaluator(cu); + } + + /** + * Test reading (but not executing) an expression that has every opcode + * that takes operands. Operands that are signed vs unsigned are present in + * byte patterns that exercise high-bit set vs. not set. + * @throws DWARFExpressionException if error + */ + @Test + public void testReadingAllOpCodesWithArgs() throws DWARFExpressionException { + // @formatter:off + DWARFExpression expr = expr( + /* 0 */ instr(DW_OP_addr, 1, 2, 3, 4, 5, 6, 7, 8), + + /* 1 */ instr(DW_OP_const1u, 0x55), + /* 2 */ instr(DW_OP_const1u, 0xfe), + + /* 3 */ instr(DW_OP_const1s, 0x55), + /* 4 */ instr(DW_OP_const1s, 0xfe), // -2 + + /* 5 */ instr(DW_OP_const2u, 0x55, 0x55), + /* 6 */ instr(DW_OP_const2u, 0xf0, 0xf0), + + /* 7 */ instr(DW_OP_const2s, 0x55, 0x55), + /* 8 */ instr(DW_OP_const2s, 0xf0, 0xf0), // -3856 + + /* 9 */ instr(DW_OP_const4u, 0x55, 0x55, 0x55, 0x55), + /* 10 */ instr(DW_OP_const4u, 0xf0, 0xf0, 0xf0, 0xf0), + + /* 11 */ instr(DW_OP_const4s, 0x55, 0x55, 0x55, 0x55), + /* 12 */ instr(DW_OP_const4s, 0xf0, 0xf0, 0xf0, 0xf0), // -252645136 + + /* 13 */ instr(DW_OP_const8u, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55), + /* 14 */ instr(DW_OP_const8u, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0), + + /* 15 */ instr(DW_OP_const8s, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55), + /* 16 */ instr(DW_OP_const8s, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0), + + /* 17 */ instr(DW_OP_constu, 0x55), + /* 18 */ instr(DW_OP_constu, uleb128(128)), + /* 19 */ instr(DW_OP_constu, uleb128(16256)), + + /* 20 */ instr(DW_OP_consts, sleb128(0x33)), + /* 21 */ instr(DW_OP_consts, sleb128(128)), + /* 22 */ instr(DW_OP_consts, sleb128(-128)), + + /* 23 */ instr(DW_OP_pick, 0x04), + /* 24 */ instr(DW_OP_pick, 0xf0), + + /* 25 */ instr(DW_OP_plus_uconst, uleb128(128)), + /* 26 */ instr(DW_OP_plus_uconst, uleb128(191)), + + /* 27 */ instr(DW_OP_skip, 0x05, 0x05), + /* 28 */ instr(DW_OP_skip, 0xf0, 0xf0), + + /* 29 */ instr(DW_OP_bra, 0x05, 0x05), + /* 30 */ instr(DW_OP_bra, 0xf0, 0xf0), + + /* 31 */ instr(DW_OP_breg0, sleb128(10)), + /* 32 */ instr(DW_OP_breg0, sleb128(128)), + + /* 33 */ instr(DW_OP_breg31, sleb128(12)), + /* 34 */ instr(DW_OP_breg31, sleb128(128)), + + /* 35 */ instr(DW_OP_regx, sleb128(12)), + /* 36 */ instr(DW_OP_regx, sleb128(128)), + + /* 37 */ instr(DW_OP_fbreg, sleb128(8)), + /* 38 */ instr(DW_OP_fbreg, sleb128(85)), + + /* 39 */ instr(DW_OP_bregx, 0x55, 0x44), + /* 40 */ instr(DW_OP_bregx, 0x55, 0x80, 0x01), + + /* 41 */ instr(DW_OP_piece, uleb128(128)), + /* 42 */ instr(DW_OP_piece, uleb128(191)), + + /* 43 */ instr(DW_OP_deref_size, 0x55), + /* 44 */ instr(DW_OP_deref_size, 0xf0), + + /* 45 */ instr(DW_OP_xderef_size, 0x55), + /* 46 */ instr(DW_OP_xderef_size, 0xf0), + + /* 47 */ instr(DW_OP_call2, 0x55, 0x55), + /* 48 */ instr(DW_OP_call2, 0xf0, 0xf0), + + /* 49 */ instr(DW_OP_call4, 0x55, 0x55, 0x55, 0x55), + /* 50 */ instr(DW_OP_call4, 0xf0, 0xf0, 0xf0, 0xf0), + + /* 51 */ instr(DW_OP_bit_piece, 0x55, 0x55), + /* 52 */ instr(DW_OP_bit_piece, 0x80, 0x01, 0x81, 0x01), + + /* 53 */ instr(DW_OP_call_ref, 4, 3, 2, 1), + + /* 54 */ instr( DW_OP_implicit_value, 0x05, 1, 2, 3, 4, 5), + + /* 55 */ instr( DW_OP_implicit_pointer, 1, 0, 0, 0, 2), + + /* 56 */ instr( DW_OP_addrx, 0) + ); + // @formatter:on + + assertEquals(4, evaluator.getDWARFCompilationUnit().getIntSize()); + assertNotNull("Did not successfully instantiate DWARFExpression", expr); + assertEquals("Did not read all instructions", 57, expr.getInstructionCount()); + + assertEquals(0x55, expr.getInstruction(1).getOperandValue(0)); + assertEquals(0xfe, expr.getInstruction(2).getOperandValue(0)); + + assertEquals(0x55, expr.getInstruction(3).getOperandValue(0)); + assertEquals(-2, expr.getInstruction(4).getOperandValue(0)); + + assertEquals(0x5555, expr.getInstruction(5).getOperandValue(0)); + assertEquals(0xf0f0, expr.getInstruction(6).getOperandValue(0)); + + assertEquals(0x5555, expr.getInstruction(7).getOperandValue(0)); + assertEquals(-3856, expr.getInstruction(8).getOperandValue(0)); + + assertEquals(0x55555555, expr.getInstruction(9).getOperandValue(0)); + assertEquals(0xf0f0f0f0L, expr.getInstruction(10).getOperandValue(0)); + + assertEquals(0x55555555, expr.getInstruction(11).getOperandValue(0)); + assertEquals(-252645136, expr.getInstruction(12).getOperandValue(0)); + + assertEquals(0x5555555555555555L, expr.getInstruction(13).getOperandValue(0)); + assertEquals(0xf0f0f0f0f0f0f0f0L, expr.getInstruction(14).getOperandValue(0)); + + assertEquals(0x5555555555555555L, expr.getInstruction(15).getOperandValue(0)); + assertEquals(0xf0f0f0f0f0f0f0f0L, expr.getInstruction(16).getOperandValue(0)); + + assertEquals(0x55, expr.getInstruction(17).getOperandValue(0)); + assertEquals(128, expr.getInstruction(18).getOperandValue(0)); + assertEquals(16256, expr.getInstruction(19).getOperandValue(0)); + + assertEquals(0x33, expr.getInstruction(20).getOperandValue(0)); + assertEquals(128, expr.getInstruction(21).getOperandValue(0)); + assertEquals(-128, expr.getInstruction(22).getOperandValue(0)); + + assertEquals(5, expr.getInstruction(54).getOperandValue(0)); + + assertEquals(1, expr.getInstruction(55).getOperandValue(0)); + assertEquals(2, expr.getInstruction(55).getOperandValue(1)); + } + + @Test + public void testBadExpr() { + try { + DWARFExpression expr = expr( + instr(DW_OP_addr, 1, 2, 3, 4, 5, 6, 7, 8), // instr 0 + instr(DW_OP_const1u, 0x55), // instr 1 + instr(DW_OP_const1u, 0xfe), // instr 2 + instr(DW_OP_addr, 1, 2) // instr 3, truncated + ); + fail( + "readExpr should have thrown an exception because the expr's final op was truncated: " + + expr.toString()); + } + catch (DWARFExpressionException dee) { + // Should have been able to read 3 of the operations before failing + Assert.assertEquals(dee.getExpression().getInstructionCount(), 3); + } + } + + @Test + public void testUnknownOpCode() { + try { + DWARFExpression expr = expr(new byte[] { (byte) 0xf0, 1, 2, 3 }); + fail( + "readExpr should have thrown an exception because the expr's final op was truncated: " + + expr.toString()); + } + catch (DWARFExpressionException dee) { + DWARFExpression expr = dee.getExpression(); + assertEquals(1, expr.getInstructionCount()); + assertEquals(DW_OP_unknown_opcode, expr.getInstruction(0).getOpCode()); + } + } + + @Test + public void test_DW_OP_addr_ExprRep() throws DWARFExpressionException { + assertEquals("DW_OP_addr: 807060504030201", + expr(instr(DW_OP_addr, 1, 2, 3, 4, 5, 6, 7, 8)).toString()); + assertEquals("DW_OP_addr: d0c0b0a", + expr(instr(DW_OP_addr, 0xa, 0xb, 0xc, 0xd, 0, 0, 0, 0)).toString()); + } + + @Test + public void test_DW_OP_fbreg_ExprRep() throws DWARFExpressionException { + // instructions with operands that are signed should show a "+" for positive values + assertEquals("DW_OP_fbreg: -48", expr(instr(DW_OP_fbreg, sleb128(-48))).toString()); + assertEquals("DW_OP_fbreg: -120", expr(instr(DW_OP_fbreg, sleb128(-120))).toString()); + assertEquals("DW_OP_fbreg: +120", expr(instr(DW_OP_fbreg, sleb128(120))).toString()); + assertEquals("DW_OP_fbreg: 0", expr(instr(DW_OP_fbreg, sleb128(0))).toString()); + } + + @Test + public void test_DW_OP_const_ExprRep() throws DWARFExpressionException { + assertEquals("DW_OP_const1s: -1", expr(instr(DW_OP_const1s, 0xff)).toString()); + assertEquals("DW_OP_const1s: +5", expr(instr(DW_OP_const1s, 0x5)).toString()); + assertEquals("DW_OP_const1s: 0", expr(instr(DW_OP_const1s, 0)).toString()); + + assertEquals("DW_OP_const1u: 255", expr(instr(DW_OP_const1u, 0xff)).toString()); + } +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/LEB128.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/LEB128.java index c60284d3ba..8fa75d95f1 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/LEB128.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/LEB128.java @@ -17,8 +17,10 @@ package ghidra.program.model.data; import java.io.*; +import ghidra.util.exception.AssertException; + /** - * Logic for reading LEB128 values. + * Logic for reading/writing LEB128 values. *

* LEB128 is a variable length integer encoding that uses 7 bits per byte, with the high bit * being reserved as a continuation flag, with the least significant bytes coming first @@ -150,4 +152,90 @@ public class LEB128 { return read(is, isSigned); } + /** + * Encodes a value into a sequence of LEB128 bytes. + * + * @param value to encode + * @param isSigned boolean flag, if true value is encoded as a signed value, if false value is + * encoded as an unsigned value + * @return byte array containing the LEB128 bytes of the value (max 10) + */ + public static byte[] encode(long value, boolean isSigned) { + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(MAX_SUPPORTED_LENGTH); + write(value, baos, isSigned); + return baos.toByteArray(); + } + catch (IOException e) { + // should not be able to happen using ByteArrayOutputStream. + throw new AssertException(e); + } + } + + /** + * Writes a value to the stream as a sequence of LEB128 bytes. + * + * @param value to write + * @param os {@link OutputStream} to write to + * @param isSigned boolean flag, if true value is encoded as a signed value, if false value is + * encoded as an unsigned value + * @return count of bytes written to stream + * @throws IOException if error writing to stream + */ + public static int write(long value, OutputStream os, boolean isSigned) throws IOException { + return isSigned ? writeSigned(value, os) : writeUnsigned(value, os); + } + + /** + * Writes a value to the stream as a sequence of LEB128 bytes. + * + * @param value to write + * @param os {@link OutputStream} to write to + * @return count of bytes written to stream + * @throws IOException if error writing to stream + */ + public static int writeUnsigned(long value, OutputStream os) throws IOException { + int size = 0; + boolean done; + do { + int b = (int) (value & 0x7f); + value = value >>> 7; + done = value == 0; + if (value != 0) { + b |= 0x80; + } + os.write(b); + size++; + } + while (!done); + return size; + } + + /** + * Writes a value to the stream as a sequence of LEB128 bytes. + * + * @param value to write + * @param os {@link OutputStream} to write to + * @return count of bytes written to stream + * @throws IOException if error writing to stream + */ + public static int writeSigned(long value, OutputStream os) throws IOException { + long endingVal = value < 0 ? -1 : 0; + int hiBit = value < 0 ? 0x40 : 0; + int size = 0; + boolean more; + do { + int b = (int) (value & 0x7f); + value = value >> 7; + more = value != endingVal || ((b & 0x40) != hiBit); + if (more) { + b |= 0x80; + } + os.write(b); + size++; + } + while (more); + return size; + } + } diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/LEB128Test.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/LEB128Test.java index 088dd5c22a..a34d722cc6 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/LEB128Test.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/LEB128Test.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -15,11 +15,11 @@ */ package ghidra.program.model.data; -import static org.junit.Assert.assertEquals; - -import java.util.List; +import static org.junit.Assert.*; import java.io.*; +import java.util.List; +import java.util.stream.LongStream; import org.junit.Assert; import org.junit.Test; @@ -57,38 +57,24 @@ public class LEB128Test extends AbstractGTest { te(0xf_ffff_ffffL, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01), // more than 32 bits to test shifting > 32bits // 1 byte - te(1L, 0x01), - te(63L, 0x3f), - te(64L, 0x40), + te(1L, 0x01), te(63L, 0x3f), te(64L, 0x40), // 1 byte to 2 byte transition - te(125L, 0x7d), - te(126L, 0x7e), - te(127L, 0x7f), - te(128L, 0x80, 0x01), - te(129L, 0x81, 0x01), - te(130L, 0x82, 0x01), - te(131L, 0x83, 0x01), + te(125L, 0x7d), te(126L, 0x7e), te(127L, 0x7f), te(128L, 0x80, 0x01), te(129L, 0x81, 0x01), + te(130L, 0x82, 0x01), te(131L, 0x83, 0x01), - te(254L, 0xfe, 0x01), - te(255L, 0xff, 0x01), - te(256L, 0x80, 0x02), - te(257L, 0x81, 0x02), + te(254L, 0xfe, 0x01), te(255L, 0xff, 0x01), te(256L, 0x80, 0x02), te(257L, 0x81, 0x02), // 2 byte to 3 byte transition - te(16382L, 0xfe, 0x7f), - te(16383L, 0xff, 0x7f), - te(16384L, 0x80, 0x80, 0x01), + te(16382L, 0xfe, 0x7f), te(16383L, 0xff, 0x7f), te(16384L, 0x80, 0x80, 0x01), te(16385L, 0x81, 0x80, 0x01), // 3 byte to 4 byte transition - te(2097151L, 0xff, 0xff, 0x7f), - te(2097152L, 0x80, 0x80, 0x80, 0x01), + te(2097151L, 0xff, 0xff, 0x7f), te(2097152L, 0x80, 0x80, 0x80, 0x01), te(2097153L, 0x81, 0x80, 0x80, 0x01), // 4 byte to 5 byte transition - te(268435455L, 0xff, 0xff, 0xff, 0x7f), - te(268435456L, 0x80, 0x80, 0x80, 0x80, 0x01), + te(268435455L, 0xff, 0xff, 0xff, 0x7f), te(268435456L, 0x80, 0x80, 0x80, 0x80, 0x01), te(268435457L, 0x81, 0x80, 0x80, 0x80, 0x01), // 5 byte to 6 byte transition @@ -103,51 +89,29 @@ public class LEB128Test extends AbstractGTest { te(-2130303778817L, 0xff, 0xff, 0xff, 0xff, 0xff, 0x41), // 1 byte positive stuff - te(0L, 0x00), - te(1L, 0x01), + te(0L, 0x00), te(1L, 0x01), // 1 byte to 2 byte transition (positive) - te(63L, 0x3f), - te(64L, 0xc0, 0x00), - te(65L, 0xc1, 0x00), - te(66L, 0xc2, 0x00), + te(63L, 0x3f), te(64L, 0xc0, 0x00), te(65L, 0xc1, 0x00), te(66L, 0xc2, 0x00), - te(126L, 0xfe, 0x00), - te(127L, 0xff, 0x00), - te(128L, 0x80, 0x01), - te(129L, 0x81, 0x01), + te(126L, 0xfe, 0x00), te(127L, 0xff, 0x00), te(128L, 0x80, 0x01), te(129L, 0x81, 0x01), - te(254L, 0xfe, 0x01), - te(255L, 0xff, 0x01), - te(256L, 0x80, 0x02), - te(257L, 0x81, 0x02), + te(254L, 0xfe, 0x01), te(255L, 0xff, 0x01), te(256L, 0x80, 0x02), te(257L, 0x81, 0x02), // 2 byte to 3 byte transition - te(8190L, 0xfe, 0x3f), - te(8191L, 0xff, 0x3f), - te(8192L, 0x80, 0xc0, 0x00), + te(8190L, 0xfe, 0x3f), te(8191L, 0xff, 0x3f), te(8192L, 0x80, 0xc0, 0x00), te(8193L, 0x81, 0xc0, 0x00), // 1 byte negative stuff - te(-1L, 0x7f), - te(-2L, 0x7e), - te(-3L, 0x7d), - te(-4L, 0x7c), - te(-5L, 0x7b), - te(-6L, 0x7a), + te(-1L, 0x7f), te(-2L, 0x7e), te(-3L, 0x7d), te(-4L, 0x7c), te(-5L, 0x7b), te(-6L, 0x7a), // 1 byte to 2 byte transition (negative) - te(-64L, 0x40), - te(-65L, 0xbf, 0x7f), + te(-64L, 0x40), te(-65L, 0xbf, 0x7f), - te(-127, 0x81, 0x7f), - te(-128, 0x80, 0x7f), - te(-129, 0xff, 0x7e), + te(-127, 0x81, 0x7f), te(-128, 0x80, 0x7f), te(-129, 0xff, 0x7e), // 2 byte to 3 byte transition (negative) - te(-8191L, 0x81, 0x40), - te(-8192L, 0x80, 0x40), - te(-8193L, 0xff, 0xbf, 0x7f), + te(-8191L, 0x81, 0x40), te(-8192L, 0x80, 0x40), te(-8193L, 0xff, 0xbf, 0x7f), te(-8194L, 0xfe, 0xbf, 0x7f) ); @@ -169,13 +133,13 @@ public class LEB128Test extends AbstractGTest { InputStream is = is(te.bytes); long actualValue = LEB128.read(is, signed); int remainder = is.available(); - assertEquals(String.format( - "%s[%d] failed: leb128(%s) != %d. Expected=%d / %x, actual=%d / %x", - name, i, NumericUtilities.convertBytesToString(te.bytes), te.expectedValue, - te.expectedValue, te.expectedValue, actualValue, actualValue), te.expectedValue, - actualValue); - assertEquals(String.format("%s[%d] failed: left-over bytes: %d", name, i, remainder), - 0, is.available()); + assertEquals( + String.format("%s[%d] failed: leb128(%s) != %d. Expected=%d / %x, actual=%d / %x", + name, i, NumericUtilities.convertBytesToString(te.bytes), te.expectedValue, + te.expectedValue, te.expectedValue, actualValue, actualValue), + te.expectedValue, actualValue); + assertEquals(String.format("%s[%d] failed: left-over bytes: %d", name, i, remainder), 0, + is.available()); } } @@ -212,4 +176,51 @@ public class LEB128Test extends AbstractGTest { Assert.assertEquals(bytes.length - 10, is.available()); } + @Test + public void testEncode() { + // positive unsigned + LongStream.range(0, 65536 + 10).forEach(this::assertRoundTripUnsigned); + LongStream.range(Integer.MAX_VALUE - 1000, Integer.MAX_VALUE + 1000L) + .forEach(this::assertRoundTripUnsigned); + LongStream.range(Long.MAX_VALUE - 1000, Long.MAX_VALUE) + .forEach(this::assertRoundTripUnsigned); + + // positive signed + LongStream.range(0, 65536 + 10).forEach(this::assertRoundTripSigned); + LongStream.range(Integer.MAX_VALUE - 1000L, Integer.MAX_VALUE + 1000L) + .forEach(this::assertRoundTripSigned); + LongStream.range(Long.MAX_VALUE - 1000L, Long.MAX_VALUE) + .forEach(this::assertRoundTripSigned); + + // negative signed + LongStream.range(-65536 - 10, 10).forEach(this::assertRoundTripSigned); + LongStream.range(Integer.MIN_VALUE - 1000L, Integer.MIN_VALUE + 1000L) + .forEach(this::assertRoundTripSigned); + LongStream.range(Long.MIN_VALUE, Long.MIN_VALUE + 1000L) + .forEach(this::assertRoundTripSigned); + + } + + private void assertRoundTripUnsigned(long l) { + assertRoundTrip(l, false); + } + + private void assertRoundTripSigned(long l) { + assertRoundTrip(l, true); + } + + private void assertRoundTrip(long l, boolean isSigned) { + try { + byte[] bytes = LEB128.encode(l, isSigned); + long decodeResult = LEB128.decode(bytes, 0, isSigned); + assertEquals( + "%d (0x%x) encoded to %s returned %d (0x%x)".formatted(l, l, + NumericUtilities.convertBytesToString(bytes), decodeResult, decodeResult), + l, decodeResult); + } + catch (IOException e) { + fail(e.getMessage()); + } + } + } diff --git a/Ghidra/Processors/x86/data/languages/x86-64.dwarf b/Ghidra/Processors/x86/data/languages/x86-64.dwarf index c821818431..8e4e3979ef 100644 --- a/Ghidra/Processors/x86/data/languages/x86-64.dwarf +++ b/Ghidra/Processors/x86/data/languages/x86-64.dwarf @@ -30,5 +30,24 @@ + + + + + + + diff --git a/Ghidra/Processors/x86/data/languages/x86.dwarf b/Ghidra/Processors/x86/data/languages/x86.dwarf index 154617ec38..3877fd1f49 100644 --- a/Ghidra/Processors/x86/data/languages/x86.dwarf +++ b/Ghidra/Processors/x86/data/languages/x86.dwarf @@ -30,5 +30,24 @@ + + + + + + +