Merge remote-tracking branch 'origin/GP-4666_DeindirectOutput' into patch

This commit is contained in:
Ryan Kurtz 2024-06-12 11:13:52 -04:00
commit 001a65357c
4 changed files with 164 additions and 107 deletions

View file

@ -4984,38 +4984,37 @@ int4 FuncCallSpecs::transferLockedInputParam(ProtoParameter *param)
return 0;
}
/// Return the p-code op whose output Varnode corresponds to the given parameter (return value)
/// \brief Return any outputs of \b this CALL that contain or are contained by the given return value parameter
///
/// The Varnode may be attached to the base CALL or CALLIND, but it also may be
/// attached to an INDIRECT preceding the CALL. The output Varnode may not exactly match
/// the dimensions of the given parameter. We return non-null if either:
/// The output Varnodes may be attached to the base CALL or CALLIND, but also may be
/// attached to an INDIRECT preceding the CALL. The output Varnodes may not exactly match
/// the dimensions of the given parameter. We pass back a Varnode if either:
/// - The parameter contains the Varnode (the easier case) OR if
/// - The Varnode properly contains the parameter
/// \param param is the given paramter (return value)
/// \param newoutput will hold any overlapping output Varnodes
/// \return the matching PcodeOp or NULL
PcodeOp *FuncCallSpecs::transferLockedOutputParam(ProtoParameter *param)
void FuncCallSpecs::transferLockedOutputParam(ProtoParameter *param,vector<Varnode *> &newoutput)
{
Varnode *vn = op->getOut();
if (vn != (Varnode *)0) {
if (param->getAddress().justifiedContain(param->getSize(),vn->getAddr(),vn->getSize(),false)==0)
return op;
if (vn->getAddr().justifiedContain(vn->getSize(),param->getAddress(),param->getSize(),false)==0)
return op;
return (PcodeOp *)0;
if (param->getAddress().justifiedContain(param->getSize(),vn->getAddr(),vn->getSize(),false)>=0)
newoutput.push_back(vn);
else if (vn->getAddr().justifiedContain(vn->getSize(),param->getAddress(),param->getSize(),false)>=0)
newoutput.push_back(vn);
}
PcodeOp *indop = op->previousOp();
while((indop!=(PcodeOp *)0)&&(indop->code()==CPUI_INDIRECT)) {
if (indop->isIndirectCreation()) {
vn = indop->getOut();
if (param->getAddress().justifiedContain(param->getSize(),vn->getAddr(),vn->getSize(),false)==0)
return indop;
if (vn->getAddr().justifiedContain(vn->getSize(),param->getAddress(),param->getSize(),false)==0)
return indop;
if (param->getAddress().justifiedContain(param->getSize(),vn->getAddr(),vn->getSize(),false)>=0)
newoutput.push_back(vn);
else if (vn->getAddr().justifiedContain(vn->getSize(),param->getAddress(),param->getSize(),false)>=0)
newoutput.push_back(vn);
}
indop = indop->previousOp();
}
return (PcodeOp *)0;
}
/// \brief List and/or create a Varnode for each input parameter of matching a source prototype
@ -5057,19 +5056,14 @@ bool FuncCallSpecs::transferLockedInput(vector<Varnode *> &newinput,const FuncPr
/// \param newoutput will hold the passed back Varnode
/// \param source is the source prototype
/// \return \b true if the passed back value is accurate
bool FuncCallSpecs::transferLockedOutput(Varnode *&newoutput,const FuncProto &source)
bool FuncCallSpecs::transferLockedOutput(vector<Varnode *> &newoutput,const FuncProto &source)
{
ProtoParameter *param = source.getOutput();
if (param->getType()->getMetatype() == TYPE_VOID) {
newoutput = (Varnode *)0;
return true;
}
PcodeOp *outop = transferLockedOutputParam(param);
if (outop == (PcodeOp *)0)
newoutput = (Varnode *)0;
else
newoutput = outop->getOut();
transferLockedOutputParam(param,newoutput);
return true;
}
@ -5126,103 +5120,136 @@ void FuncCallSpecs::commitNewInputs(Funcdata &data,vector<Varnode *> &newinput)
/// \brief Update output Varnode to \b this CALL to reflect the formal return value
///
/// The current return value must be locked and is presumably out of date
/// with the current CALL output. Unless the return value is \e void, the
/// output Varnode must exist and must be provided.
/// The Varnode is updated to reflect the return value,
/// which may involve truncating or extending. Any active trials are updated,
/// and the new Varnode is set as the CALL output.
/// The current return value must be locked and is presumably out of date with the current CALL output.
/// Unless the return value is \e void, the output Varnode must exist and must be provided.
/// The Varnode is created/updated to reflect the return value and is set as the CALL output.
/// Any other intersecting outputs are updated to be either truncations or extensions of this.
/// Any active trials are updated,
/// \param data is the calling function
/// \param newout is the provided old output Varnode (or NULL)
void FuncCallSpecs::commitNewOutputs(Funcdata &data,Varnode *newout)
/// \param newout is the list of intersecting outputs
void FuncCallSpecs::commitNewOutputs(Funcdata &data,vector<Varnode *> &newoutput)
{
if (!isOutputLocked()) return;
activeoutput.clear();
if (newout != (Varnode *)0) {
if (!newoutput.empty()) {
ProtoParameter *param = getOutput();
// We could conceivably truncate the output to the correct size to match the parameter
activeoutput.registerTrial(param->getAddress(),param->getSize());
PcodeOp *indop = newout->getDef();
if (newout->getSize() == 1 && param->getType()->getMetatype() == TYPE_BOOL && data.isTypeRecoveryOn())
if (param->getSize() == 1 && param->getType()->getMetatype() == TYPE_BOOL && data.isTypeRecoveryOn())
data.opMarkCalculatedBool(op);
if (newout->getSize() == param->getSize()) {
if (indop != op) {
data.opUnsetOutput(indop);
data.opUnlink(indop); // We know this is an indirect creation which is no longer used
Varnode *exactMatch = (Varnode *)0;
for(int4 i=0;i<newoutput.size();++i) {
if (newoutput[i]->getSize() == param->getSize()) {
exactMatch = newoutput[i];
break;
}
}
Varnode *realOut;
PcodeOp *indOp;
if (exactMatch != (Varnode *)0) {
// If we have a Varnode that exactly matches param, make sure it is the output of the CALL
indOp = exactMatch->getDef();
if (op != indOp) {
// If we reach here, we know -op- must have no output
data.opSetOutput(op,newout);
data.opSetOutput(op,exactMatch);
data.opUnlink(indOp); // We know this is an indirect creation which is no longer used
}
realOut = exactMatch;
}
else if (newout->getSize() < param->getSize()) {
// We know newout is properly justified within param
if (indop != op) {
data.opUninsert(indop);
data.opSetOpcode(indop,CPUI_SUBPIECE);
}
else {
indop = data.newOp(2,op->getAddr());
data.opSetOpcode(indop,CPUI_SUBPIECE);
data.opSetOutput(indop,newout); // Move -newout- from -op- to -indop-
}
Varnode *realout = data.newVarnodeOut(param->getSize(),param->getAddress(),op);
data.opSetInput(indop,realout,0);
data.opSetInput(indop,data.newConstant(4,0),1);
data.opInsertAfter(indop,op);
else {
// Otherwise, we create a Varnode matching param
data.opUnsetOutput(op);
realOut = data.newVarnodeOut(param->getSize(),param->getAddress(),op);
}
else { // param->getSize() < newout->getSize()
// We know param is justified contained in newout
VarnodeData vardata;
// Test whether the new prototype naturally extends its output
OpCode opc = assumedOutputExtension(param->getAddress(),param->getSize(),vardata);
Address hiaddr = newout->getAddr();
if (opc != CPUI_COPY) {
// If -newout- looks like a natural extension of the true output type, create the extension op
if (opc == CPUI_PIECE) { // Extend based on the datatype
if (param->getType()->getMetatype() == TYPE_INT)
opc = CPUI_INT_SEXT;
else
opc = CPUI_INT_ZEXT;
}
if (indop != op) {
data.opUninsert(indop);
data.opRemoveInput(indop,1);
data.opSetOpcode(indop,opc);
Varnode *outvn = data.newVarnodeOut(param->getSize(),param->getAddress(),op);
data.opSetInput(indop,outvn,0);
data.opInsertAfter(indop,op);
for(int4 i=0;i<newoutput.size();++i) {
Varnode *oldOut = newoutput[i];
if (oldOut == exactMatch) continue;
indOp = oldOut->getDef();
if (indOp == op)
indOp = (PcodeOp *)0;
if (oldOut->getSize() < param->getSize()) {
if (indOp != (PcodeOp *)0) {
data.opUninsert(indOp);
data.opSetOpcode(indOp,CPUI_SUBPIECE);
}
else {
PcodeOp *extop = data.newOp(1,op->getAddr());
data.opSetOpcode(extop,opc);
data.opSetOutput(extop,newout); // Move newout from -op- to -extop-
Varnode *outvn = data.newVarnodeOut(param->getSize(),param->getAddress(),op);
data.opSetInput(extop,outvn,0);
data.opInsertAfter(extop,op);
indOp = data.newOp(2,op->getAddr());
data.opSetOpcode(indOp,CPUI_SUBPIECE);
data.opSetOutput(indOp,oldOut); // Move oldOut from op to indOp
}
int4 overlap = oldOut->overlap(realOut->getAddr(),realOut->getSize());
data.opSetInput(indOp,realOut,0);
data.opSetInput(indOp,data.newConstant(4,(uintb)overlap),1);
data.opInsertAfter(indOp,op);
}
else { // If all else fails, concatenate in extra byte from something "indirectly created" by -op-
int4 hisz = newout->getSize() - param->getSize();
if (!newout->getAddr().getSpace()->isBigEndian())
hiaddr = hiaddr + param->getSize();
PcodeOp *newindop = data.newIndirectCreation(op,hiaddr,hisz,true);
if (indop != op) {
data.opUninsert(indop);
data.opSetOpcode(indop,CPUI_PIECE);
Varnode *outvn = data.newVarnodeOut(param->getSize(),param->getAddress(),op);
data.opSetInput(indop,newindop->getOut(),0);
data.opSetInput(indop,outvn,1);
data.opInsertAfter(indop,op);
else if (param->getSize() < oldOut->getSize()) {
int4 overlap = oldOut->getAddr().justifiedContain(oldOut->getSize(), param->getAddress(), param->getSize(), false);
VarnodeData vardata;
// Test whether the new prototype naturally extends its output
OpCode opc = assumedOutputExtension(param->getAddress(),param->getSize(),vardata);
if (opc != CPUI_COPY && overlap == 0) {
// If oldOut looks like a natural extension of the true output type, create the extension op
if (opc == CPUI_PIECE) { // Extend based on the data-type
if (param->getType()->getMetatype() == TYPE_INT)
opc = CPUI_INT_SEXT;
else
opc = CPUI_INT_ZEXT;
}
if (indOp != (PcodeOp *)0) {
data.opUninsert(indOp);
data.opRemoveInput(indOp,1);
data.opSetOpcode(indOp,opc);
data.opSetInput(indOp,realOut,0);
data.opInsertAfter(indOp,op);
}
else {
PcodeOp *extop = data.newOp(1,op->getAddr());
data.opSetOpcode(extop,opc);
data.opSetOutput(extop,oldOut); // Move newout from -op- to -extop-
data.opSetInput(extop,realOut,0);
data.opInsertAfter(extop,op);
}
}
else {
PcodeOp *concatop = data.newOp(2,op->getAddr());
data.opSetOpcode(concatop,CPUI_PIECE);
data.opSetOutput(concatop,newout); // Move newout from -op- to -concatop-
Varnode *outvn = data.newVarnodeOut(param->getSize(),param->getAddress(),op);
data.opSetInput(concatop,newindop->getOut(),0);
data.opSetInput(concatop,outvn,1);
data.opInsertAfter(concatop,op);
else { // If all else fails, concatenate in extra byte from something "indirectly created" by -op-
if (indOp != (PcodeOp *)0) {
data.opUnlink(indOp);
}
int4 mostSigSize = oldOut->getSize() - overlap - realOut->getSize();
PcodeOp *lastOp = op;
if (overlap != 0) { // We need to append less significant bytes to realOut for this oldOut
Address loAddr = oldOut->getAddr();
if (loAddr.isBigEndian())
loAddr = loAddr + (oldOut->getSize() - overlap);
PcodeOp *newIndOp = data.newIndirectCreation(op,loAddr,overlap,true);
PcodeOp *concatOp = data.newOp(2,op->getAddr());
data.opSetOpcode(concatOp,CPUI_PIECE);
data.opSetInput(concatOp,realOut,0); // Most significant part
data.opSetInput(concatOp,newIndOp->getOut(),1); // Least sig
data.opInsertAfter(concatOp,op);
if (mostSigSize != 0) {
if (loAddr.isBigEndian())
data.newVarnodeOut(overlap+realOut->getSize(),realOut->getAddr(),concatOp);
else
data.newVarnodeOut(overlap+realOut->getSize(),loAddr,concatOp);
}
lastOp = concatOp;
}
if (mostSigSize != 0) { // We need to append more significant bytes to realOut for this oldOut
Address hiAddr = oldOut->getAddr();
if (!hiAddr.isBigEndian())
hiAddr = hiAddr + (realOut->getSize() + overlap);
PcodeOp *newIndOp = data.newIndirectCreation(op,hiAddr,mostSigSize,true);
PcodeOp *concatOp = data.newOp(2,op->getAddr());
data.opSetOpcode(concatOp,CPUI_PIECE);
data.opSetInput(concatOp,newIndOp->getOut(),0);
data.opSetInput(concatOp,lastOp->getOut(),1);
data.opInsertAfter(concatOp,lastOp);
lastOp = concatOp;
}
data.opSetOutput(lastOp,oldOut); // We have completed the redefinition of this oldOut
}
}
}
@ -5307,7 +5334,8 @@ void FuncCallSpecs::doInputJoin(int4 slot1,bool ishislot)
/// \param newinput will hold the new list of input Varnodes for the CALL
/// \param newoutput will hold the new output Varnode or NULL
/// \return \b true if \b this can be fully converted
bool FuncCallSpecs::lateRestriction(const FuncProto &restrictedProto,vector<Varnode *> &newinput,Varnode *&newoutput)
bool FuncCallSpecs::lateRestriction(const FuncProto &restrictedProto,vector<Varnode *> &newinput,
vector<Varnode *> &newoutput)
{
if (!hasModel()) {
@ -5357,7 +5385,7 @@ void FuncCallSpecs::deindirect(Funcdata &data,Funcdata *newfd)
// Try our best to merge existing prototype
// with the one we have just been handed
vector<Varnode *> newinput;
Varnode *newoutput;
vector<Varnode *> newoutput;
FuncProto &newproto( newfd->getFuncProto() );
if ((!newproto.isNoReturn())&&(!newproto.isInline())) {
if (isOverride()) // If we are overridden at the call-site
@ -5387,7 +5415,7 @@ void FuncCallSpecs::forceSet(Funcdata &data,const FuncProto &fp)
{
vector<Varnode *> newinput;
Varnode *newoutput;
vector<Varnode *> newoutput;
// Copy the recovered prototype into the override manager so that
// future restarts don't have to rediscover it

View file

@ -1657,11 +1657,11 @@ class FuncCallSpecs : public FuncProto {
Varnode *getSpacebaseRelative(void) const; ///< Get the active stack-pointer Varnode at \b this call site
Varnode *buildParam(Funcdata &data,Varnode *vn,ProtoParameter *param,Varnode *stackref);
int4 transferLockedInputParam(ProtoParameter *param);
PcodeOp *transferLockedOutputParam(ProtoParameter *param);
void transferLockedOutputParam(ProtoParameter *param,vector<Varnode *> &newoutput);
bool transferLockedInput(vector<Varnode *> &newinput,const FuncProto &source);
bool transferLockedOutput(Varnode *&newoutput,const FuncProto &source);
bool transferLockedOutput(vector<Varnode *> &newoutput,const FuncProto &source);
void commitNewInputs(Funcdata &data,vector<Varnode *> &newinput);
void commitNewOutputs(Funcdata &data,Varnode *newout);
void commitNewOutputs(Funcdata &data,vector<Varnode *> &newoutput);
void collectOutputTrialVarnodes(vector<Varnode *> &trialvn);
void setStackPlaceholderSlot(int4 slot) { stackPlaceholderSlot = slot;
if (isinputactive) activeinput.setPlaceholderSlot(); } ///< Set the slot of the stack-pointer placeholder
@ -1702,7 +1702,7 @@ public:
bool checkInputJoin(int4 slot1,bool ishislot,Varnode *vn1,Varnode *vn2) const;
void doInputJoin(int4 slot1,bool ishislot);
bool lateRestriction(const FuncProto &restrictedProto,vector<Varnode *> &newinput,Varnode *&newoutput);
bool lateRestriction(const FuncProto &restrictedProto,vector<Varnode *> &newinput,vector<Varnode *> &newoutput);
void deindirect(Funcdata &data,Funcdata *newfd);
void forceSet(Funcdata &data,const FuncProto &fp);
void insertPcode(Funcdata &data);