GP-794 Fix handling of problematic DWARF function parameters.

This commit is contained in:
dev747368 2021-03-24 15:25:58 -04:00 committed by ghidra1
parent c6de9dc493
commit 763092487b

View File

@ -15,10 +15,11 @@
*/
package ghidra.app.util.bin.format.dwarf4.next;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;
import java.io.IOException;
import ghidra.app.cmd.comments.AppendCommentCmd;
import ghidra.app.cmd.label.SetLabelPrimaryCmd;
import ghidra.app.util.bin.format.dwarf4.*;
@ -202,26 +203,26 @@ public class DWARFFunctionImporter {
return;
}
DWARFFunction function = new DWARFFunction(prog.getName(diea));
function.namespace = function.dni.getParentNamespace(currentProgram);
DWARFFunction dfunc = new DWARFFunction(prog.getName(diea));
dfunc.namespace = dfunc.dni.getParentNamespace(currentProgram);
Number lowPC = diea.getLowPC(0);
function.address = toAddr(lowPC);
function.highAddress =
dfunc.address = toAddr(lowPC);
dfunc.highAddress =
diea.hasAttribute(DWARFAttribute.DW_AT_high_pc) ? toAddr(diea.getHighPC()) : null;
String previousFunctionProcessed = functionsProcessed.get(function.address);
String previousFunctionProcessed = functionsProcessed.get(dfunc.address);
if (previousFunctionProcessed != null) {
// Msg.info(this, "Duplicate function defintion found for " + dni.getCategoryPath() +
// " at " + function.address + " in DIE " + diea.getHexOffset() + ", skipping");
markAllChildrenAsProcessed(diea.getHeadFragment());
return;
}
functionsProcessed.put(function.address,
function.dni.getNamespacePath() + " DIE: " + diea.getHexOffset());
functionsProcessed.put(dfunc.address,
dfunc.dni.getNamespacePath() + " DIE: " + diea.getHexOffset());
// Check if the function is an external function
function.isExternal = diea.getBool(DWARFAttribute.DW_AT_external, false);
dfunc.isExternal = diea.getBool(DWARFAttribute.DW_AT_external, false);
// Retrieve the frame base if it exists
DWARFLocation frameLoc = null;
@ -229,9 +230,9 @@ public class DWARFFunctionImporter {
List<DWARFLocation> frameBase = diea.getAsLocation(DWARFAttribute.DW_AT_frame_base);
// get the framebase register, find where the frame is finally set
// up.
frameLoc = getTopLocation(frameBase, function.address.getOffset());
frameLoc = getTopLocation(frameBase, dfunc.address.getOffset());
if (frameLoc != null) {
function.frameBase = (int) diea.evaluateLocation(frameLoc);
dfunc.frameBase = (int) diea.evaluateLocation(frameLoc);
}
}
@ -245,26 +246,134 @@ public class DWARFFunctionImporter {
// function passing a pointer to the callee function where the object is
// then operated on.
DIEAggregate typeRef = diea.getTypeRef();
if (typeRef != null) {
function.retval = new DWARFVariable();
function.retval.type = dwarfDTM.getDataType(typeRef, dwarfDTM.getVoidType());
}
DataType formalReturnType = (typeRef != null)
? dwarfDTM.getDataType(typeRef, DataType.DEFAULT)
: dwarfDTM.getVoidType();
dfunc.retval = new DWARFVariable();
dfunc.retval.type = formalReturnType;
boolean formalParamsOnly = false;
boolean skipFuncSignature = false;
List<Parameter> formalParams = new ArrayList<>();
for (DebugInfoEntry childEntry : diea.getHeadFragment().getChildren(
DWARFTag.DW_TAG_formal_parameter)) {
DIEAggregate childDIEA = prog.getAggregate(childEntry);
DWARFVariable var = processVariable(childDIEA, function, null, -1);
if (var != null) {
function.params.add(var);
Parameter formalParam = createFormalParameter(childDIEA);
if (formalParam == null) {
skipFuncSignature = true;
break;
}
formalParams.add(formalParam);
if (!formalParamsOnly) {
DWARFVariable var = processVariable(childDIEA, dfunc, null, -1);
if (var == null) {
// we had an error, can't rely on detailed param data, fallback to
// formal params
formalParamsOnly = true;
dfunc.params.clear();
}
else {
dfunc.params.add(var);
}
}
}
function.varArg =
dfunc.varArg =
!diea.getHeadFragment().getChildren(DWARFTag.DW_TAG_unspecified_parameters).isEmpty();
processFuncChildren(diea, function);
outputFunction(function, diea);
processFuncChildren(diea, dfunc);
Function gfunc = createFunction(dfunc, diea);
if (gfunc != null) {
if (formalParams.isEmpty() && dfunc.localVarErrors) {
// if there were no defined parameters and we had problems decoding local variables,
// don't force the method to have an empty param signature because there are other
// issues afoot.
skipFuncSignature = true;
}
if (skipFuncSignature) {
Msg.error(this,
"Failed to get function signature information, leaving undefined: " +
gfunc.getName() + "@" + gfunc.getEntryPoint());
Msg.debug(this, "DIE info: " + diea.toString());
return;
}
if (formalParamsOnly) {
updateFunctionSignatureWithFormalParams(gfunc, formalParams,
formalReturnType, dfunc.varArg, diea);
}
else {
updateFunctionSignatureWithDetailParams(gfunc, dfunc, diea);
}
}
}
private void updateFunctionSignatureWithFormalParams(Function gfunc, List<Parameter> params,
DataType returnType, boolean varArgs, DIEAggregate diea) {
try {
ReturnParameterImpl returnVar = new ReturnParameterImpl(returnType, currentProgram);
try {
gfunc.setVarArgs(varArgs);
gfunc.updateFunction(null, returnVar, params,
FunctionUpdateType.DYNAMIC_STORAGE_ALL_PARAMS, true, SourceType.IMPORTED);
}
catch (DuplicateNameException e) {
// try again after adjusting param names
setUniqueParameterNames(gfunc, params);
gfunc.updateFunction(null, returnVar, params,
FunctionUpdateType.DYNAMIC_STORAGE_ALL_PARAMS, true, SourceType.IMPORTED);
}
}
catch (InvalidInputException | DuplicateNameException e) {
Msg.error(this,
"Error updating function " + gfunc.getName() + " with formal params at " +
gfunc.getEntryPoint().toString() + ": " + e.getMessage());
Msg.error(this, "DIE info: " + diea.toString());
}
}
private void updateFunctionSignatureWithDetailParams(Function gfunc, DWARFFunction dfunc,
DIEAggregate diea) {
try {
CompilerSpec compilerSpec = currentProgram.getCompilerSpec();
PrototypeModel convention = null;
Variable returnVariable;
List<Parameter> params = new ArrayList<>();
returnVariable = buildReturnVariable(dfunc.retval);
for (int i = 0; i < dfunc.params.size(); ++i) {
Parameter curparam = buildParameter(gfunc, i, dfunc.params.get(i), diea);
params.add(curparam);
if (i == 0 && checkThisParameter(dfunc.params.get(0), diea)) {
convention = compilerSpec.matchConvention(GenericCallingConvention.thiscall);
}
}
for (int i = 0; i < dfunc.local.size(); ++i) {
commitLocal(gfunc, dfunc.local.get(i));
}
if (dfunc.retval != null || params.size() > 0) {
// Add the function signature definition into the data type manager
// TODO: createFunctionDefinition(dfunc, infopath);
// NOTE: Storage is computed above for the purpose of identifying
// a best fit calling convention. The commitPrototype method currently
// always employs dynamic storage.
commitPrototype(gfunc, returnVariable, params, convention);
gfunc.setVarArgs(dfunc.varArg);
}
}
catch (InvalidInputException | DuplicateNameException iie) {
Msg.error(this, "Error updating function " + dfunc.dni.getName() + " at " +
dfunc.address.toString() + ": " + iie.getMessage());
}
}
private void processFuncChildren(DIEAggregate diea, DWARFFunction dfunc)
@ -305,6 +414,19 @@ public class DWARFFunctionImporter {
}
}
private Parameter createFormalParameter(DIEAggregate diea) {
String name = diea.getString(DWARFAttribute.DW_AT_name, null);
DataType dt = dwarfDTM.getDataType(diea.getTypeRef(), dwarfDTM.getVoidType());
try {
return new ParameterImpl(name, dt, currentProgram);
}
catch (InvalidInputException e) {
Msg.debug(this, "Failed to create parameter for " + diea.toString());
}
return null;
}
/**
* Creates a new {@link DWARFVariable} from the specified {@link DIEAggregate DIEA} and
* as a child of the specified function (if not null).
@ -349,6 +471,9 @@ public class DWARFFunctionImporter {
DWARFLocation topLocation = getTopLocation(locList, funcAddr);
if (topLocation == null) {
if (dfunc != null) {
dfunc.localVarErrors = true;
}
return null;
}
@ -368,12 +493,18 @@ public class DWARFFunctionImporter {
catch (DWARFExpressionException | UnsupportedOperationException
| IndexOutOfBoundsException ex) {
importSummary.exprReadError++;
if (dfunc != null) {
dfunc.localVarErrors = true;
}
return null;
}
if (exprEvaluator.isDwarfStackValue()) {
importSummary.varDWARFExpressionValue++;
if (dfunc != null) {
dfunc.localVarErrors = true;
}
return null;
}
else if (exprEvaluator.useUnknownRegister() && exprEvaluator.isRegisterLocation()) {
@ -386,6 +517,9 @@ public class DWARFFunctionImporter {
}
else if (exprEvaluator.useUnknownRegister()) {
importSummary.varDynamicRegisterError++;
if (dfunc != null) {
dfunc.localVarErrors = true;
}
return null;
}
else if (exprEvaluator.isStackRelative()) {
@ -420,6 +554,9 @@ public class DWARFFunctionImporter {
" can not fit into specified register " + dvar.reg.getName() +
", size=" + dvar.reg.getMinimumByteSize() +
", skipping. DWARF DIE: " + diea.getHexOffset());
if (dfunc != null) {
dfunc.localVarErrors = true;
}
return null;
}
@ -430,6 +567,9 @@ public class DWARFFunctionImporter {
// 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());
if (dfunc != null) {
dfunc.localVarErrors = true;
}
return null;
}
}
@ -1030,75 +1170,7 @@ public class DWARFFunctionImporter {
return false;
}
private void outputFunction(DWARFFunction dfunc, DIEAggregate diea) {
try {
Function function = createFunction(dfunc);
if (function == null) {
Msg.error(this, "DWARF DIE: " + diea.getHexOffset());
return;
}
DWARFSourceInfo sourceInfo = DWARFSourceInfo.create(diea);
if (sourceInfo != null) {
// Move the function into the program tree of the file
moveIntoFragment(function.getName(), dfunc.address,
dfunc.highAddress != null ? dfunc.highAddress : dfunc.address.add(1),
sourceInfo.getFilename());
if (importOptions.isOutputSourceLocationInfo()) {
appendComment(dfunc.address, CodeUnit.PLATE_COMMENT,
sourceInfo.getDescriptionStr(), "\n");
}
}
if (importOptions.isOutputDIEInfo()) {
appendComment(dfunc.address, CodeUnit.PLATE_COMMENT,
"DWARF DIE: " + diea.getHexOffset(), "\n");
}
DWARFNameInfo dni = prog.getName(diea);
if (dni.isNameModified()) {
appendComment(dfunc.address, CodeUnit.PLATE_COMMENT,
"Original name: " + dni.getOriginalName(), "\n");
}
CompilerSpec compilerSpec = currentProgram.getCompilerSpec();
PrototypeModel convention = null;
Variable returnVariable;
ArrayList<Parameter> params = new ArrayList<>();
// boolean specifyStorage = evaluateParameterStorage(dfunc);
returnVariable = buildReturnVariable(dfunc.retval);
for (int i = 0; i < dfunc.params.size(); ++i) {
Parameter curparam = buildParameter(function, i, dfunc.params.get(i), diea);
params.add(curparam);
if (i == 0 && checkThisParameter(dfunc.params.get(0), diea)) {
convention = compilerSpec.matchConvention(GenericCallingConvention.thiscall);
}
}
for (int i = 0; i < dfunc.local.size(); ++i) {
commitLocal(function, dfunc.local.get(i));
}
if (dfunc.retval != null || params.size() > 0) {
// Add the function signature definition into the data type manager
// TODO: createFunctionDefinition(dfunc, infopath);
// NOTE: Storage is computed above for the purpose of identifying
// a best fit calling convention. The commitPrototype method currently
// always employs dynamic storage.
commitPrototype(function, returnVariable, params, convention);
function.setVarArgs(dfunc.varArg);
}
}
catch (InvalidInputException | DuplicateNameException iie) {
Msg.error(this, "Error updating function " + dfunc.dni.getName() + " at " +
dfunc.address.toString() + ": " + iie.getMessage());
}
}
private Function createFunction(DWARFFunction dfunc) {
private Function createFunction(DWARFFunction dfunc, DIEAggregate diea) {
try {
// create a new symbol if one does not exist (symbol table will figure this out)
SymbolTable symbolTable = currentProgram.getSymbolTable();
@ -1129,6 +1201,30 @@ public class DWARFFunctionImporter {
function = currentProgram.getFunctionManager().createFunction(null, dfunc.address,
new AddressSet(dfunc.address), SourceType.IMPORTED);
}
DWARFSourceInfo sourceInfo = DWARFSourceInfo.create(diea);
if (sourceInfo != null) {
// Move the function into the program tree of the file
moveIntoFragment(function.getName(), dfunc.address,
dfunc.highAddress != null ? dfunc.highAddress : dfunc.address.add(1),
sourceInfo.getFilename());
if (importOptions.isOutputSourceLocationInfo()) {
appendComment(dfunc.address, CodeUnit.PLATE_COMMENT,
sourceInfo.getDescriptionStr(), "\n");
}
}
if (importOptions.isOutputDIEInfo()) {
appendComment(dfunc.address, CodeUnit.PLATE_COMMENT,
"DWARF DIE: " + diea.getHexOffset(), "\n");
}
DWARFNameInfo dni = prog.getName(diea);
if (dni.isNameModified()) {
appendComment(dfunc.address, CodeUnit.PLATE_COMMENT,
"Original name: " + dni.getOriginalName(), "\n");
}
return function;
}
catch (OverlappingFunctionException e) {
@ -1152,14 +1248,14 @@ public class DWARFFunctionImporter {
* @throws InvalidInputException invalid parameter name
* @throws DuplicateNameException (should not occur on non-DB parameter)
*/
private void setUniqueParameterNames(Function function, Parameter[] parameters)
private void setUniqueParameterNames(Function function, List<Parameter> parameters)
throws DuplicateNameException, InvalidInputException {
SymbolTable symbolTable = currentProgram.getSymbolTable();
// Create a set containing all the unique parameter names determined so far so they can
// be avoided as additional parameter names are determined.
Set<String> namesSoFar = new HashSet<>();
for (int ordinal = 0; ordinal < parameters.length; ordinal++) {
Parameter parameter = parameters[ordinal];
for (int ordinal = 0; ordinal < parameters.size(); ordinal++) {
Parameter parameter = parameters.get(ordinal);
String baseName = parameter.getName();
if (ordinal == 0 && Function.THIS_PARAM_NAME.equals(baseName)) {
continue;
@ -1233,14 +1329,13 @@ public class DWARFFunctionImporter {
}
private void commitPrototype(Function function, Variable returnVariable,
ArrayList<Parameter> params, PrototypeModel protoModel)
List<Parameter> params, PrototypeModel protoModel)
throws InvalidInputException, DuplicateNameException {
Parameter[] paramarray = new Parameter[params.size()];
params.toArray(paramarray);
CompilerSpec compilerSpec = currentProgram.getCompilerSpec();
if (protoModel == null) {
Parameter[] paramarray = params.toArray(Parameter[]::new);
protoModel = compilerSpec.findBestCallingConvention(paramarray);
}
@ -1249,7 +1344,7 @@ public class DWARFFunctionImporter {
FunctionUpdateType.DYNAMIC_STORAGE_ALL_PARAMS, true, SourceType.IMPORTED);
}
catch (DuplicateNameException e) {
setUniqueParameterNames(function, paramarray);
setUniqueParameterNames(function, params);
function.updateFunction(protoModel.getName(), returnVariable, params,
FunctionUpdateType.DYNAMIC_STORAGE_ALL_PARAMS, true, SourceType.IMPORTED);
}
@ -1361,9 +1456,10 @@ public class DWARFFunctionImporter {
public DWARFVariable retval;
public boolean isExternal;
public long frameBase;
public ArrayList<DWARFVariable> params = new ArrayList<>();
public ArrayList<DWARFVariable> local = new ArrayList<>();
public List<DWARFVariable> params = new ArrayList<>();
public List<DWARFVariable> local = new ArrayList<>();
public boolean varArg;
public boolean localVarErrors; // set to true if problem w/local var decoding
public DWARFFunction(DWARFNameInfo dni) {
this.dni = dni;