mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 02:09:44 +02:00
Merge remote-tracking branch
'origin/GP-1954_win_x64_cspec_register_grouping' (Closes #2952, Closes #1480)
This commit is contained in:
commit
4b600847eb
15 changed files with 767 additions and 230 deletions
|
@ -1549,30 +1549,6 @@ void Funcdata::mapGlobals(void)
|
|||
warningHeader("Globals starting with '_' overlap smaller symbols at the same address");
|
||||
}
|
||||
|
||||
/// \brief Return \b true if the alternate path looks more valid than the main path.
|
||||
///
|
||||
/// Two different paths from a common Varnode each terminate at a CALL, CALLIND, or RETURN.
|
||||
/// Evaluate which path most likely represents actual parameter/return value passing,
|
||||
/// based on traversal information about each path.
|
||||
/// \param vn is the Varnode terminating the \e alternate path
|
||||
/// \param flags indicates traversals for both paths
|
||||
/// \return \b true if the alternate path is preferred
|
||||
bool Funcdata::isAlternatePathValid(const Varnode *vn,uint4 flags)
|
||||
|
||||
{
|
||||
if ((flags & (traverse_indirect | traverse_indirectalt)) == traverse_indirect)
|
||||
// If main path traversed an INDIRECT but the alternate did not
|
||||
return true; // Main path traversed INDIRECT, alternate did not
|
||||
if ((flags & (traverse_indirect | traverse_indirectalt)) == traverse_indirectalt)
|
||||
return false; // Alternate path traversed INDIRECT, main did not
|
||||
if ((flags & traverse_actionalt) != 0)
|
||||
return true; // Alternate path traversed a dedicated COPY
|
||||
if (vn->loneDescend() == (PcodeOp*)0) return false;
|
||||
const PcodeOp *op = vn->getDef();
|
||||
if (op == (PcodeOp*)0) return true;
|
||||
return !op->isMarker(); // MULTIEQUAL or INDIRECT indicates multiple values
|
||||
}
|
||||
|
||||
/// \brief Test for legitimate double use of a parameter trial
|
||||
///
|
||||
/// The given trial is a \e putative input to first CALL, but can also trace its data-flow
|
||||
|
@ -1617,7 +1593,7 @@ bool Funcdata::checkCallDoubleUse(const PcodeOp *opmatch,const PcodeOp *op,const
|
|||
if (curtrial.isActive())
|
||||
return false;
|
||||
}
|
||||
else if (isAlternatePathValid(vn,fl))
|
||||
else if (TraverseNode::isAlternatePathValid(vn,fl))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
@ -1670,11 +1646,11 @@ bool Funcdata::onlyOpUse(const Varnode *invn,const PcodeOp *opmatch,const ParamT
|
|||
res = false;
|
||||
break;
|
||||
case CPUI_INDIRECT:
|
||||
curFlags |= Funcdata::traverse_indirectalt;
|
||||
curFlags |= TraverseNode::indirectalt;
|
||||
break;
|
||||
case CPUI_COPY:
|
||||
if ((op->getOut()->getSpace()->getType()!=IPTR_INTERNAL)&&!op->isIncidentalCopy()&&!vn->isIncidentalCopy()) {
|
||||
curFlags |= Funcdata::traverse_actionalt;
|
||||
curFlags |= TraverseNode::actionalt;
|
||||
}
|
||||
break;
|
||||
case CPUI_RETURN:
|
||||
|
@ -1684,21 +1660,34 @@ bool Funcdata::onlyOpUse(const Varnode *invn,const PcodeOp *opmatch,const ParamT
|
|||
}
|
||||
else if (activeoutput != (ParamActive *)0) { // Are we in the middle of analyzing returns
|
||||
if (op->getIn(0) != vn) { // Unless we hold actual return value
|
||||
if (!isAlternatePathValid(vn,curFlags))
|
||||
if (!TraverseNode::isAlternatePathValid(vn,curFlags))
|
||||
continue; // Don't consider this a "use"
|
||||
}
|
||||
}
|
||||
res = false;
|
||||
break;
|
||||
case CPUI_MULTIEQUAL:
|
||||
case CPUI_PIECE:
|
||||
case CPUI_SUBPIECE:
|
||||
case CPUI_INT_SEXT:
|
||||
case CPUI_INT_ZEXT:
|
||||
case CPUI_CAST:
|
||||
break;
|
||||
case CPUI_PIECE:
|
||||
if (op->getIn(0) == vn) { // Concatenated as most significant piece
|
||||
if ((curFlags & TraverseNode::lsb_truncated) != 0) {
|
||||
// Original lsb has been truncated and replaced
|
||||
continue; // No longer assume this is a possible use
|
||||
}
|
||||
curFlags |= TraverseNode::concat_high;
|
||||
}
|
||||
break;
|
||||
case CPUI_SUBPIECE:
|
||||
if (op->getIn(1)->getOffset() != 0) { // Throwing away least significant byte(s)
|
||||
if ((curFlags & TraverseNode::concat_high) == 0) // If no previous concatenation has occurred
|
||||
curFlags |= TraverseNode::lsb_truncated; // Byte(s) of original value have been thrown away
|
||||
}
|
||||
break;
|
||||
default:
|
||||
curFlags |= Funcdata::traverse_actionalt;
|
||||
curFlags |= TraverseNode::actionalt;
|
||||
break;
|
||||
}
|
||||
if (!res) break;
|
||||
|
@ -1729,14 +1718,13 @@ bool Funcdata::onlyOpUse(const Varnode *invn,const PcodeOp *opmatch,const ParamT
|
|||
/// \param invn is the given trial Varnode to test
|
||||
/// \param op is the given CALL or RETURN
|
||||
/// \param trial is the associated parameter trial object
|
||||
/// \param offset is the offset within the current Varnode of the value ultimately copied into the trial
|
||||
/// \param mainFlags describes traversals along the path from \e invn to \e op
|
||||
/// \return \b true if the Varnode is only used for the CALL/RETURN
|
||||
bool Funcdata::ancestorOpUse(int4 maxlevel,const Varnode *invn,
|
||||
const PcodeOp *op,ParamTrial &trial,uint4 mainFlags) const
|
||||
const PcodeOp *op,ParamTrial &trial,int4 offset,uint4 mainFlags) const
|
||||
|
||||
{
|
||||
int4 i;
|
||||
|
||||
if (maxlevel==0) return false;
|
||||
|
||||
if (!invn->isWritten()) {
|
||||
|
@ -1754,15 +1742,15 @@ bool Funcdata::ancestorOpUse(int4 maxlevel,const Varnode *invn,
|
|||
// as an "only use"
|
||||
if (def->isIndirectCreation())
|
||||
return false;
|
||||
return ancestorOpUse(maxlevel-1,def->getIn(0),op,trial,mainFlags | Funcdata::traverse_indirect);
|
||||
return ancestorOpUse(maxlevel-1,def->getIn(0),op,trial,offset,mainFlags | TraverseNode::indirect);
|
||||
case CPUI_MULTIEQUAL:
|
||||
// Check if there is any ancestor whose only
|
||||
// use is in this op
|
||||
if (def->isMark()) return false; // Trim the loop
|
||||
def->setMark(); // Mark that this MULTIEQUAL is on the path
|
||||
// Note: onlyOpUse is using Varnode::setMark
|
||||
for(i=0;i<def->numInput();++i) {
|
||||
if (ancestorOpUse(maxlevel-1,def->getIn(i),op,trial, mainFlags)) {
|
||||
for(int4 i=0;i<def->numInput();++i) {
|
||||
if (ancestorOpUse(maxlevel-1,def->getIn(i),op,trial,offset,mainFlags)) {
|
||||
def->clearMark();
|
||||
return true;
|
||||
}
|
||||
|
@ -1771,17 +1759,23 @@ bool Funcdata::ancestorOpUse(int4 maxlevel,const Varnode *invn,
|
|||
return false;
|
||||
case CPUI_COPY:
|
||||
if ((invn->getSpace()->getType()==IPTR_INTERNAL)||def->isIncidentalCopy()||def->getIn(0)->isIncidentalCopy()) {
|
||||
return ancestorOpUse(maxlevel-1,def->getIn(0),op,trial,mainFlags);
|
||||
return ancestorOpUse(maxlevel-1,def->getIn(0),op,trial,offset,mainFlags);
|
||||
}
|
||||
break;
|
||||
case CPUI_PIECE:
|
||||
// Concatenation tends to be artificial, so recurse through the least significant part
|
||||
return ancestorOpUse(maxlevel-1,def->getIn(1),op,trial,mainFlags);
|
||||
// Concatenation tends to be artificial, so recurse through piece corresponding later SUBPIECE
|
||||
if (offset == 0)
|
||||
return ancestorOpUse(maxlevel-1,def->getIn(1),op,trial,0,mainFlags); // Follow into least sig piece
|
||||
if (offset == def->getIn(1)->getSize())
|
||||
return ancestorOpUse(maxlevel-1,def->getIn(0),op,trial,0,mainFlags); // Follow into most sig piece
|
||||
return false;
|
||||
case CPUI_SUBPIECE:
|
||||
{
|
||||
int4 newOff = def->getIn(1)->getOffset();
|
||||
// This is a rather kludgy way to get around where a DIV (or other similar) instruction
|
||||
// causes a register that looks like the high precision piece of the function return
|
||||
// to be set with the remainder as a side effect
|
||||
if (def->getIn(1)->getOffset()==0) {
|
||||
if (newOff==0) {
|
||||
const Varnode *vn = def->getIn(0);
|
||||
if (vn->isWritten()) {
|
||||
const PcodeOp *remop = vn->getDef();
|
||||
|
@ -1789,7 +1783,13 @@ bool Funcdata::ancestorOpUse(int4 maxlevel,const Varnode *invn,
|
|||
trial.setRemFormed();
|
||||
}
|
||||
}
|
||||
if (invn->getSpace()->getType() == IPTR_INTERNAL || def->isIncidentalCopy() ||
|
||||
def->getIn(0)->isIncidentalCopy() ||
|
||||
invn->overlap(*def->getIn(0)) == newOff) {
|
||||
return ancestorOpUse(maxlevel-1,def->getIn(0),op,trial,offset + newOff,mainFlags);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CPUI_CALL:
|
||||
case CPUI_CALLIND:
|
||||
return false; // A call is never a good indication of a single op use
|
||||
|
@ -1829,24 +1829,25 @@ bool AncestorRealistic::checkConditionalExe(State &state)
|
|||
}
|
||||
|
||||
/// Analyze a new node that has just entered, during the depth-first traversal
|
||||
/// \param state is the current node on the path, with associated state information
|
||||
/// \return the command indicating the next traversal step: push (enter_node), or pop (pop_success, pop_fail, pop_solid...)
|
||||
int4 AncestorRealistic::enterNode(State &state)
|
||||
int4 AncestorRealistic::enterNode(void)
|
||||
|
||||
{
|
||||
State &state(stateStack.back());
|
||||
// If the node has already been visited, we truncate the traversal to prevent cycles.
|
||||
// We always return success assuming the proper result will get returned along the first path
|
||||
if (state.vn->isMark()) return pop_success;
|
||||
if (!state.vn->isWritten()) {
|
||||
if (state.vn->isInput()) {
|
||||
if (state.vn->isUnaffected()) return pop_fail;
|
||||
if (state.vn->isPersist()) return pop_success; // A global input, not active movement, but a valid possibility
|
||||
if (!state.vn->isDirectWrite()) return pop_fail;
|
||||
Varnode *stateVn = state.op->getIn(state.slot);
|
||||
if (stateVn->isMark()) return pop_success;
|
||||
if (!stateVn->isWritten()) {
|
||||
if (stateVn->isInput()) {
|
||||
if (stateVn->isUnaffected()) return pop_fail;
|
||||
if (stateVn->isPersist()) return pop_success; // A global input, not active movement, but a valid possibility
|
||||
if (!stateVn->isDirectWrite()) return pop_fail;
|
||||
}
|
||||
return pop_success; // Probably a normal parameter, not active movement, but valid
|
||||
}
|
||||
mark(state.vn); // Mark that the varnode has now been visited
|
||||
PcodeOp *op = state.vn->getDef();
|
||||
mark(stateVn); // Mark that the varnode has now been visited
|
||||
PcodeOp *op = stateVn->getDef();
|
||||
switch(op->code()) {
|
||||
case CPUI_INDIRECT:
|
||||
if (op->isIndirectCreation()) { // Backtracking is stopped by a call
|
||||
|
@ -1867,7 +1868,7 @@ int4 AncestorRealistic::enterNode(State &state)
|
|||
if (op->getOut()->getSpace()->getType()==IPTR_INTERNAL
|
||||
|| op->isIncidentalCopy() || op->getIn(0)->isIncidentalCopy()
|
||||
|| (op->getOut()->overlap(*op->getIn(0)) == (int4)op->getIn(1)->getOffset())) {
|
||||
stateStack.push_back(State(op,0));
|
||||
stateStack.push_back(State(op,state));
|
||||
return enter_node; // Push into the new node
|
||||
}
|
||||
// For other SUBPIECES, do a minimal traversal to rule out unaffected or other invalid inputs,
|
||||
|
@ -1882,6 +1883,7 @@ int4 AncestorRealistic::enterNode(State &state)
|
|||
} while((op!=(PcodeOp *)0)&&((op->code() == CPUI_COPY)||(op->code()==CPUI_SUBPIECE)));
|
||||
return pop_solid; // treat the COPY as a solid movement
|
||||
case CPUI_COPY:
|
||||
{
|
||||
// Copies to a temporary, or between varnodes with same storage location, or otherwise incidental
|
||||
// are viewed as just another node on the path to traverse
|
||||
if (op->getOut()->getSpace()->getType()==IPTR_INTERNAL
|
||||
|
@ -1892,24 +1894,46 @@ int4 AncestorRealistic::enterNode(State &state)
|
|||
}
|
||||
// For other COPIES, do a minimal traversal to rule out unaffected or other invalid inputs,
|
||||
// but otherwise treat it as valid, active, movement into the parameter
|
||||
do {
|
||||
Varnode *vn = op->getIn(0);
|
||||
Varnode *vn = op->getIn(0);
|
||||
for(;;) {
|
||||
if ((!vn->isMark())&&(vn->isInput())) {
|
||||
if (!vn->isDirectWrite())
|
||||
return pop_fail;
|
||||
}
|
||||
op = vn->getDef();
|
||||
} while((op!=(PcodeOp *)0)&&((op->code() == CPUI_COPY)||(op->code()==CPUI_SUBPIECE)));
|
||||
if (op == (PcodeOp *)0) break;
|
||||
OpCode opc = op->code();
|
||||
if (opc == CPUI_COPY || opc == CPUI_SUBPIECE)
|
||||
vn = op->getIn(0);
|
||||
else if (opc == CPUI_PIECE)
|
||||
vn = op->getIn(1); // Follow least significant piece
|
||||
else
|
||||
break;
|
||||
}
|
||||
return pop_solid; // treat the COPY as a solid movement
|
||||
}
|
||||
case CPUI_MULTIEQUAL:
|
||||
multiDepth += 1;
|
||||
stateStack.push_back(State(op,0));
|
||||
return enter_node; // Nothing to check, start traversing inputs of MULTIEQUAL
|
||||
case CPUI_PIECE:
|
||||
// If the trial is getting pieced together and then truncated in a register,
|
||||
// this is evidence of artificial data-flow.
|
||||
if (state.vn->getSize() > trial->getSize() && state.vn->getSpace()->getType() != IPTR_SPACEBASE)
|
||||
return pop_fail;
|
||||
if (stateVn->getSize() > trial->getSize()) { // Did we already pull-back from a SUBPIECE?
|
||||
// If the trial is getting pieced together and then truncated in a register,
|
||||
// this is evidence of artificial data-flow.
|
||||
if (state.offset == 0 && op->getIn(1)->getSize() <= trial->getSize()) {
|
||||
// Truncation corresponds to least significant piece, follow slot=1
|
||||
stateStack.push_back(State(op,1));
|
||||
return enter_node;
|
||||
}
|
||||
else if (state.offset == op->getIn(1)->getSize() && op->getIn(0)->getSize() <= trial->getSize()) {
|
||||
// Truncation corresponds to most significant piece, follow slot=0
|
||||
stateStack.push_back(State(op,0));
|
||||
return enter_node;
|
||||
}
|
||||
if (stateVn->getSpace()->getType() != IPTR_SPACEBASE) {
|
||||
return pop_fail;
|
||||
}
|
||||
}
|
||||
return pop_solid;
|
||||
default:
|
||||
return pop_solid; // Any other LOAD or arithmetic/logical operation is viewed as solid movement
|
||||
|
@ -1917,12 +1941,12 @@ int4 AncestorRealistic::enterNode(State &state)
|
|||
}
|
||||
|
||||
/// Backtrack into a previously visited node
|
||||
/// \param state is the node that needs to be popped from the stack
|
||||
/// \param pop_command is the type of pop (pop_success, pop_fail, pop_failkill, pop_solid) being performed
|
||||
/// \return the command to execute (push or pop) after the current pop
|
||||
int4 AncestorRealistic::uponPop(State &state,int4 pop_command)
|
||||
int4 AncestorRealistic::uponPop(int4 pop_command)
|
||||
|
||||
{
|
||||
State &state(stateStack.back());
|
||||
if (state.op->code() == CPUI_MULTIEQUAL) { // All the interesting action happens for MULTIEQUAL branch points
|
||||
State &prevstate( stateStack[ stateStack.size()-2 ]); // State previous the one being popped
|
||||
if (pop_command == pop_fail) { // For a pop_fail, we always pop and pass along the fail
|
||||
|
@ -1957,7 +1981,6 @@ int4 AncestorRealistic::uponPop(State &state,int4 pop_command)
|
|||
stateStack.pop_back();
|
||||
return pop_command;
|
||||
}
|
||||
state.vn = state.op->getIn(state.slot); // Advance to next sibling
|
||||
return enter_node;
|
||||
}
|
||||
else {
|
||||
|
@ -1994,19 +2017,26 @@ bool AncestorRealistic::execute(PcodeOp *op,int4 slot,ParamTrial *t,bool allowFa
|
|||
while(!stateStack.empty()) { // Continue until all paths have been exhausted
|
||||
switch(command) {
|
||||
case enter_node:
|
||||
command = enterNode(stateStack.back());
|
||||
command = enterNode();
|
||||
break;
|
||||
case pop_success:
|
||||
case pop_solid:
|
||||
case pop_fail:
|
||||
case pop_failkill:
|
||||
command = uponPop(stateStack.back(),command);
|
||||
command = uponPop(command);
|
||||
break;
|
||||
}
|
||||
}
|
||||
for(int4 i=0;i<markedVn.size();++i) // Clean up marks we left along the way
|
||||
markedVn[i]->clearMark();
|
||||
if ((command != pop_success)&&(command != pop_solid))
|
||||
return false;
|
||||
return true;
|
||||
if (command == pop_success) {
|
||||
trial->setAncestorRealistic();
|
||||
return true;
|
||||
}
|
||||
else if (command == pop_solid) {
|
||||
trial->setAncestorRealistic();
|
||||
trial->setAncestorSolid();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue