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 @@ + + + + + + +