mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 17:59:46 +02:00
Merge remote-tracking branch
'origin/GP-4069_dev747368_dwarf_expression_eval--SQUASHED' (Closes #5982, Closes #6974, Closes #2322, Closes #5311)
This commit is contained in:
commit
29b8cdf394
42 changed files with 2517 additions and 1876 deletions
|
@ -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"
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 (!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 (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 (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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,95 +358,53 @@ 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++;
|
||||
return false;
|
||||
}
|
||||
catch (DWARFExpressionException e) {
|
||||
if (allowDerefFixup && e instanceof DWARFExpressionTerminalDerefException derefExcept) {
|
||||
type = type.getDataTypeManager().getPointer(type);
|
||||
setStorage(derefExcept.getVarnode());
|
||||
return true;
|
||||
}
|
||||
|
||||
private String getVarTypeName(DIEAggregate diea) {
|
||||
return diea.getTag() == DW_TAG_formal_parameter ? "Parameter" : "Variable";
|
||||
if (e instanceof DWARFExpressionValueException && expr != null) {
|
||||
comment = expr.toString(cu);
|
||||
}
|
||||
|
||||
importSummary.addProblematicDWARFExpression(e.getExpression());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public int getStorageSize() {
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
if (invalidOpCodeEncountered) {
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
public String toString(DWARFCompilationUnit cu) {
|
||||
return toString(-1, false, false, cu.getProgram().getRegisterMappings());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 step = 0; step < operations.size(); step++) {
|
||||
DWARFExpressionOperation op = operations.get(step);
|
||||
for (int instrIndex = 0; instrIndex < instructions.size(); instrIndex++) {
|
||||
DWARFExpressionInstruction instr = instructions.get(instrIndex);
|
||||
|
||||
if (step != 0) {
|
||||
sb.append("; ");
|
||||
if (newlines) {
|
||||
sb.append('\n');
|
||||
}
|
||||
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));
|
||||
sb.append(instr.getOperandRepresentation(operandIndex));
|
||||
}
|
||||
else {
|
||||
sb.append(NumericUtilities.convertBytesToString(op.blob, " "));
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -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) : "");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -17,7 +17,7 @@ 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)
|
||||
|
@ -34,9 +34,7 @@ public enum DWARFExpressionOperandType {
|
|||
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];
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
param.clearStorage();
|
||||
param.comment = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -102,9 +102,12 @@ public class GolangDWARFFunctionFixup implements DWARFFunctionFixup {
|
|||
storageAllocator.setAbi0Mode();
|
||||
}
|
||||
|
||||
dfunc.callingConventionName =
|
||||
storageAllocator.isAbi0Mode() ? GoConstants.GOLANG_ABI0_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()) {
|
||||
|
|
|
@ -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)
|
||||
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();
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
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());
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue