DWARF expression handling refactor

Cleanup logic of expression evaluation, stub out resolution of register
values to a callback in case we want to use constant propagation to try
to allow successful calculations, and add support for default static
values for treating an arch's stack frame register (e.g. RBP) like the
static CFA value we already have support for.

Add option to decorate params and local vars with their DWARF storage
location info.

Handle arrays with unspecified element type.
This commit is contained in:
dev747368 2025-08-11 11:21:28 -04:00
parent 483cd9a799
commit e908ab6fbf
42 changed files with 2517 additions and 1876 deletions

View file

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

View file

@ -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());

View file

@ -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.
*

View file

@ -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());

View file

@ -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<Integer> 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;
}

View file

@ -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()));
}
}

View file

@ -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<Integer> unknownRegistersEncountered = new HashSet<>();
Set<String> relocationErrorVarDefs = new HashSet<>();
int varFitError;
int varDynamicRegisterError;
int varDWARFExpressionValue;
int exprReadError;
Set<String> typeRemappings = new HashSet<>();
int paramZeroLenDataType;
@ -50,6 +47,7 @@ public class DWARFImportSummary {
List<String> compNames = new ArrayList<>();
Set<String> producers = new HashSet<>();
Set<String> sourceLangs = new HashSet<>();
Map<DWARFExpression, Integer> 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<DWARFExpression, Integer> 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);
}
}
}

View file

@ -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));
}
}

View file

@ -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<DIEAggregate> 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<Integer, DWARFCompilationUnit> 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<DWARFTag> 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<DIEAggregate>, Iterable<DIEAggregate> {

View file

@ -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<Integer, Register> dwarfRegisterMap;
private final long callFrameCFA;
private final Integer callFrameCFA;
private final int stackPointerIndex;
private final boolean useFormalParameterStorage;
public DWARFRegisterMappings(Map<Integer, Register> regmap, long callFrameCFA,
int stackPointerIndex, boolean useFPS) {
private Register stackFrameRegister;
private int stackFrameRegisterOffset;
public DWARFRegisterMappings(Map<Integer, Register> 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;
}

View file

@ -132,15 +132,31 @@ public class DWARFRegisterMappingsManager {
Map<Integer, Register> 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);
}
/*

View file

@ -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;
}
}

View file

@ -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 <strong>null</strong> 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<Varnode> 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();
// check expression eval result. Use early return for errors, leaving storage unset.
// Success return is at bottom of if/else chain of checks.
Varnode storageLoc = exprEvaluator.popVarnode();
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();
}

View file

@ -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));

View file

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

View file

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

View file

@ -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.
* <p>
* 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<DWARFExpressionOperation> 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<DWARFExpressionOperation> operations = new ArrayList<>();
List<DWARFExpressionInstruction> 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<DWARFExpressionOperation> 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<DWARFExpressionInstruction> 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<DWARFExpressionInstruction> 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.
* <p>
* 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<DWARFExpressionInstruction> 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);
}
}

View file

@ -20,26 +20,22 @@ package ghidra.app.util.bin.format.dwarf.expression;
* or when they are {@link DWARFExpressionEvaluator evaluated.}
* <p>
* 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) : "");
}
}

View file

@ -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.
* <p>
* 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);
};
}
}

View file

@ -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.
* <p>
* 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;
}
}

View file

@ -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<Integer> 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<Integer, DWARFExpressionOperandType[]> 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);
}
}

View file

@ -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];
}

View file

@ -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.
* <p>
* 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.
* <p>
* 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);
}
}

View file

@ -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}.
* <p>
* 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<Long> stack = new ArrayDeque<Long>();
public class DWARFExpressionTerminalDerefException extends DWARFExpressionUnsupportedOpException {
public DWARFExpressionResult(ArrayDeque<Long> 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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}
}

View file

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

View file

@ -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);
@ -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()) {

View file

@ -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);
@ -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();

View file

@ -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<String> getLabelNames(Symbol[] symbols) {

View file

@ -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;
}

View file

@ -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;
}
}

View file

@ -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);

View file

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

View file

@ -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) {

View file

@ -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());
}
}

View file

@ -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.
* <p>
* 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;
}
}

View file

@ -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());
}
}
}

View file

@ -30,5 +30,24 @@
<!-- <register_mapping dwarf="68" ghidra="XMM16" auto_count="16"/> **not implemented yet** --> <!-- XMM16..XMM31 -->
<!-- <register_mapping dwarf="118" ghidra="K0" auto_count="8"/> **not implemented yet** -->
</register_mappings>
<!--
call_frame_cfa and stack_frame allow specifying static values for DWARF expressions that
calculate stack locations of params or variables, typically used in a func's
DW_AT_frame_base attribute (later referenced via a DW_OP_fbreg instruction),
or in param/variable DW_AT_location attributes.
Using these values is controlled by dwarf import options, but not settable by the user currently.
-->
<!--
call_frame_cfa specifies the static offset of the func's CFA, which
technically should be looked up in the func's CIE structs.
-->
<call_frame_cfa value="8"/>
<!--
stack_frame allows dwarf expressions that reference RBP to be converted to a ghidra stack
location without evaluating the actual RBP value via symbolic propagation.
-->
<stack_frame register="RBP" offset="-8" />
</dwarf>

View file

@ -30,5 +30,24 @@
<register_mapping dwarf="48" ghidra="TR"/>
<register_mapping dwarf="49" ghidra="LDTR"/>
</register_mappings>
<!--
call_frame_cfa and stack_frame allow specifying static values for DWARF expressions that
calculate stack locations of params or variables, typically used in a func's
DW_AT_frame_base attribute (later referenced via a DW_OP_fbreg instruction),
or in param/variable DW_AT_location attributes.
Using these values is controlled by dwarf import options, but not settable by the user currently.
-->
<!--
call_frame_cfa specifies the static offset of the func's CFA, which
technically should be looked up in the func's CIE structs.
-->
<call_frame_cfa value="4"/>
<!--
stack_frame allows dwarf expressions that reference EBP to be converted to a ghidra stack
location without evaluating the actual EBP value via symbolic propagation.
-->
<stack_frame register="EBP" offset="-4" />
</dwarf>