/* ### * 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. */ #include "unionresolve.hh" #include "funcdata.hh" namespace ghidra { /// The original parent must either be a union, a partial union, a structure with a single field, /// an array with a single element, or a pointer to one of these data-types. /// The object is set up initially to resolve to the parent. /// \param parent is the original parent data-type ResolvedUnion::ResolvedUnion(Datatype *parent) { baseType = parent; if (baseType->getMetatype() == TYPE_PTR) baseType = ((TypePointer *)baseType)->getPtrTo(); resolve = parent; fieldNum = -1; lock = false; } /// The original parent must be a union or structure. /// \param parent is the original parent /// \param fldNum is the index of the particular field to resolve to (or -1 to resolve to parent) /// \param typegrp is a TypeFactory used to construct the resolved data-type of the field ResolvedUnion::ResolvedUnion(Datatype *parent,int4 fldNum,TypeFactory &typegrp) { if (parent->getMetatype() == TYPE_PARTIALUNION) parent = ((TypePartialUnion *)parent)->getParentUnion(); baseType = parent; fieldNum = fldNum; lock = false; if (fldNum < 0) resolve = parent; else { if (parent->getMetatype() == TYPE_PTR) { TypePointer *pointer = (TypePointer *)parent; Datatype *field = pointer->getPtrTo()->getDepend(fldNum); resolve = typegrp.getTypePointer(parent->getSize(),field,pointer->getWordSize()); } else resolve = parent->getDepend(fldNum); } } /// \param parent is a parent data-type that needs to be resolved /// \param op is the PcodeOp reading/writing the \b parent data-type /// \param slot is the slot (>=0 for input, -1 for output) accessing the \b parent ResolveEdge::ResolveEdge(const Datatype *parent,const PcodeOp *op,int4 slot) { opTime = op->getTime(); encoding = slot; if (parent->getMetatype() == TYPE_PTR) { typeId = ((TypePointer *)parent)->getPtrTo()->getId(); // Strip pointer encoding += 0x1000; // Encode the fact that a pointer is getting accessed } else if (parent->getMetatype() == TYPE_PARTIALUNION) typeId = ((TypePartialUnion *)parent)->getParentUnion()->getId(); else typeId = parent->getId(); } const int4 ScoreUnionFields::threshold = 256; const int4 ScoreUnionFields::maxPasses = 6; const int4 ScoreUnionFields::maxTrials = 1024; /// If the \b op is adding a constant size or a multiple of a constant size to the given input slot, where the /// size is at least as large as the union, return \b true. /// \param op is the given PcodeOp /// \param inslot is given input slot /// \return \b true if \b op is doing array arithmetic with elements at least as large as the union bool ScoreUnionFields::testArrayArithmetic(PcodeOp *op,int4 inslot) { if (op->code() == CPUI_INT_ADD) { Varnode *vn = op->getIn(1 - inslot); if (vn->isConstant()) { if (vn->getOffset() >= result.baseType->getSize()) return true; // Array with union elements } else if (vn->isWritten()) { PcodeOp *multOp = vn->getDef(); if (multOp->code() == CPUI_INT_MULT) { Varnode *vn2 = multOp->getIn(1); if (vn2->isConstant() && vn2->getOffset() >= result.baseType->getSize()) return true;// Array with union elements } } } else if (op->code() == CPUI_PTRADD) { Varnode *vn = op->getIn(2); if (vn->getOffset() >= result.baseType->getSize()) return true; } return false; } /// Identify cases where we know the union shouldn't be resolved to a field. /// \param op is the PcodeOp manipulating the union variable /// \param inslot is -1 if the union is the output, >=0 if the union is an input to the op /// \param parent is the parent union or pointer to union /// \return \b true if the union should \e not be resolved to a field bool ScoreUnionFields::testSimpleCases(PcodeOp *op,int4 inslot,Datatype *parent) { if (op->isMarker()) return true; // Propagate raw union across MULTIEQUAL and INDIRECT if (parent->getMetatype() == TYPE_PTR) { if (inslot < 0) return true; // Don't resolve pointers "up", there's only 1 possibility for assignment if (testArrayArithmetic(op, inslot)) return true; } if (op->code() != CPUI_COPY) return false; // A more complicated case if (inslot < 0) return false; // Generally we don't want to propagate union backward thru COPY if (op->getOut()->isTypeLock()) return false; // Do the full scoring return true; // Assume we don't have to extract a field if copying } /// A trial that encounters a locked data-type does not propagate through it but scores /// the trial data-type against the locked data-type. /// \param ct is the trial data-type /// \param lockType is the locked data-type /// \return the score int4 ScoreUnionFields::scoreLockedType(Datatype *ct,Datatype *lockType) { int score = 0; if (lockType == ct) score += 5; // Perfect match while(ct->getMetatype() == TYPE_PTR) { if (lockType->getMetatype() != TYPE_PTR) break; score += 5; ct = ((TypePointer *)ct)->getPtrTo(); lockType = ((TypePointer *)lockType)->getPtrTo(); } type_metatype ctMeta = ct->getMetatype(); type_metatype vnMeta = lockType->getMetatype(); if (ctMeta == vnMeta) { if (ctMeta == TYPE_STRUCT || ctMeta == TYPE_UNION || ctMeta == TYPE_ARRAY || ctMeta == TYPE_CODE) score += 10; else score += 3; } else { if ((ctMeta == TYPE_INT && vnMeta == TYPE_UINT)||(ctMeta == TYPE_UINT && vnMeta == TYPE_INT)) score -= 1; else score -= 5; if (ct->getSize() != lockType->getSize()) score -= 2; } return score; } /// Look up the call-specs for the given CALL. If the inputs are locked, find the corresponding /// parameter and score the trial data-type against it. /// \param ct is the trial data-type /// \param callOp is the CALL /// \param paramSlot is the input slot of the trial data-type /// \return the score int4 ScoreUnionFields::scoreParameter(Datatype *ct,const PcodeOp *callOp,int4 paramSlot) { const Funcdata *fd = callOp->getParent()->getFuncdata(); FuncCallSpecs *fc = fd->getCallSpecs(callOp); if (fc != (FuncCallSpecs *)0 && fc->isInputLocked() && fc->numParams() > paramSlot) { return scoreLockedType(ct,fc->getParam(paramSlot)->getType()); } type_metatype meta = ct->getMetatype(); if (meta == TYPE_ARRAY || meta == TYPE_STRUCT || meta == TYPE_UNION || meta == TYPE_CODE) return -1; // Vaguely unlikely thing to pass as a param return 0; } /// Look up the call-specs for the given CALL. If the output is locked, /// score the trial data-type against it. /// \param ct is the trial data-type /// \param callOp is the CALL /// \return the score int4 ScoreUnionFields::scoreReturnType(Datatype *ct,const PcodeOp *callOp) { const Funcdata *fd = callOp->getParent()->getFuncdata(); FuncCallSpecs *fc = fd->getCallSpecs(callOp); if (fc != (FuncCallSpecs *)0 && fc->isOutputLocked()) { return scoreLockedType(ct,fc->getOutputType()); } type_metatype meta = ct->getMetatype(); if (meta == TYPE_ARRAY || meta == TYPE_STRUCT || meta == TYPE_UNION || meta == TYPE_CODE) return -1; // Vaguely unlikely thing to return from a function return 0; } /// Test if the data-type is a pointer and if the pointed-to data-type is /// compatible with the size of the value being loaded or stored. A \b score is /// passed back for how closely the data-type fits this scenario, and if it /// does we return the data-type of the pointer value. /// \param ct is the trial data-type /// \param vn is the Varnode holding the value being loaded or stored /// \param score is used to pass back the score /// \return the data-type of the value or null Datatype *ScoreUnionFields::derefPointer(Datatype *ct,Varnode *vn,int4 &score) { Datatype *resType = (Datatype *)0; score = 0; if (ct->getMetatype() == TYPE_PTR) { Datatype *ptrto = ((TypePointer *)ct)->getPtrTo(); while(ptrto != (Datatype *)0 && ptrto->getSize() > vn->getSize()) { int8 newoff; ptrto = ptrto->getSubType(0, &newoff); } if (ptrto != (Datatype *)0 && ptrto->getSize() == vn->getSize()) { score = 10; resType = ptrto; } } else score = -10; return resType; } /// If the Varnode has already been visited, no new trials are created /// \param vn is the given Varnode /// \param ct is the data-type to associate with the trial /// \param scoreIndex is the field index to score the trial against /// \param isArray is \b true if the data-type to fit is a pointer to an array void ScoreUnionFields::newTrialsDown(Varnode *vn,Datatype *ct,int4 scoreIndex,bool isArray) { VisitMark mark(vn,scoreIndex); if (!visited.insert(mark).second) return; // Already visited this Varnode if (vn->isTypeLock()) { scores[scoreIndex] += scoreLockedType(ct, vn->getType()); return; // Don't propagate through locked Varnode } list::const_iterator piter; for(piter = vn->beginDescend();piter != vn->endDescend();++piter) { PcodeOp *op = *piter; trialNext.emplace_back(op,op->getSlot(vn),ct,scoreIndex,isArray); } } /// If the input slot is a Varnode that has already been visited, no new trial is created /// \param op is the PcodeOp with the given slot /// \param slot is the index of the given input slot /// \param ct is the data-type to associate with the trial /// \param scoreIndex is the field index to score the trial against /// \param isArray is \b true if the data-type to fit is a pointer to an array void ScoreUnionFields::newTrials(PcodeOp *op,int4 slot,Datatype *ct,int4 scoreIndex,bool isArray) { Varnode *vn = op->getIn(slot); VisitMark mark(vn,scoreIndex); if (!visited.insert(mark).second) return; // Already visited this Varnode if (vn->isTypeLock()) { scores[scoreIndex] += scoreLockedType(ct, vn->getType()); return; // Don't propagate through locked Varnode } trialNext.emplace_back(vn,ct,scoreIndex,isArray); // Try to fit up list::const_iterator iter; for(iter=vn->beginDescend();iter!=vn->endDescend();++iter) { PcodeOp *readOp = *iter; int4 inslot = readOp->getSlot(vn); if (readOp == op && inslot == slot) continue; // Don't go down PcodeOp we came from trialNext.emplace_back(readOp,inslot,ct, scoreIndex,isArray); } } /// The trial's data-type is fitted to its PcodeOp as the incoming Varnode and a /// score is computed and added to the score for the trial's union field. The fitting may /// produce a new data-type which indicates scoring for the trial recurses into the output. /// This method builds trials for any new data-type unless \b lastLevel is \b true /// Varnode of its PcodeOp. /// \param trial is the given trial /// \param lastLevel is \b true if the method should skip building new trials void ScoreUnionFields::scoreTrialDown(const Trial &trial,bool lastLevel) { if (trial.direction == Trial::fit_up) return; // Trial doesn't push in this direction Datatype *resType = (Datatype *)0; // Assume by default we don't propagate type_metatype meta = trial.fitType->getMetatype(); int4 score = 0; switch(trial.op->code()) { case CPUI_COPY: case CPUI_MULTIEQUAL: case CPUI_INDIRECT: resType = trial.fitType; // No score, but we can propagate break; case CPUI_LOAD: resType = derefPointer(trial.fitType, trial.op->getOut(), score); break; case CPUI_STORE: if (trial.inslot == 1) { Datatype *ptrto = derefPointer(trial.fitType,trial.op->getIn(2),score); if (ptrto != (Datatype*)0) { if (!lastLevel) newTrials(trial.op,2,ptrto,trial.scoreIndex,trial.array); // Propagate to value being STOREd } } else if (trial.inslot == 2) { if (meta == TYPE_CODE) score = -5; else score = 1; } break; case CPUI_CBRANCH: if (meta == TYPE_BOOL) score = 10; else score = -10; break; case CPUI_BRANCHIND: if (meta == TYPE_PTR || meta == TYPE_ARRAY || meta == TYPE_STRUCT || meta == TYPE_UNION || meta == TYPE_CODE || meta == TYPE_FLOAT) score = -5; else score = 1; break; case CPUI_CALL: case CPUI_CALLOTHER: if (trial.inslot > 0) score = scoreParameter(trial.fitType, trial.op, trial.inslot-1); break; case CPUI_CALLIND: if (trial.inslot == 0) { if (meta == TYPE_PTR) { Datatype *ptrto = ((TypePointer*)trial.fitType)->getPtrTo(); if (ptrto->getMetatype() == TYPE_CODE) { score = 10; } else { score = -10; } } } else { score = scoreParameter(trial.fitType, trial.op, trial.inslot-1); } break; case CPUI_RETURN: // We could check for locked return data-type if (meta == TYPE_ARRAY || meta == TYPE_STRUCT || meta == TYPE_UNION || meta == TYPE_CODE) score = -1; break; case CPUI_INT_EQUAL: case CPUI_INT_NOTEQUAL: if (meta == TYPE_ARRAY || meta == TYPE_STRUCT || meta == TYPE_UNION || meta == TYPE_CODE || meta == TYPE_FLOAT) score = -1; else score = 1; break; case CPUI_INT_SLESS: case CPUI_INT_SLESSEQUAL: case CPUI_INT_SCARRY: case CPUI_INT_SBORROW: if (meta == TYPE_ARRAY || meta == TYPE_STRUCT || meta == TYPE_UNION || meta == TYPE_CODE || meta == TYPE_FLOAT) score = -5; else if (meta == TYPE_PTR || meta == TYPE_UNKNOWN || meta == TYPE_UINT || meta == TYPE_BOOL) score = -1; else score = 5; break; case CPUI_INT_LESS: case CPUI_INT_LESSEQUAL: case CPUI_INT_CARRY: if (meta == TYPE_ARRAY || meta == TYPE_STRUCT || meta == TYPE_UNION || meta == TYPE_CODE || meta == TYPE_FLOAT) score = -5; else if (meta == TYPE_PTR || meta == TYPE_UNKNOWN || meta == TYPE_UINT) score = 5; else if (meta == TYPE_INT) score = -5; break; case CPUI_INT_ZEXT: if (meta == TYPE_UINT) score = 2; else if (meta == TYPE_INT || meta == TYPE_BOOL) score = 1; else if (meta == TYPE_UNKNOWN) score = 0; else // struct,union,ptr,array,code,float score = -5; break; case CPUI_INT_SEXT: if (meta == TYPE_INT) score = 2; else if (meta == TYPE_UINT || meta == TYPE_BOOL) score = 1; else if (meta == TYPE_UNKNOWN) score = 0; else // struct,union,ptr,array,code,float score = -5; break; case CPUI_INT_ADD: case CPUI_INT_SUB: case CPUI_PTRSUB: if (meta == TYPE_PTR) { if (trial.inslot >= 0) { Varnode *vn = trial.op->getIn(1-trial.inslot); if (vn->isConstant()) { TypePointer *baseType = (TypePointer *)trial.fitType; int8 off = vn->getOffset(); int8 parOff; TypePointer *par; resType = baseType->downChain(off,par,parOff,trial.array,typegrp); if (resType != (Datatype*)0) score = 5; } else { if (trial.array) { score = 1; int4 elSize = 1; if (vn->isWritten()) { PcodeOp *multOp = vn->getDef(); if (multOp->code() == CPUI_INT_MULT) { Varnode *multVn = multOp->getIn(1); if (multVn->isConstant()) elSize = (int4)multVn->getOffset(); } } TypePointer *baseType = (TypePointer *)trial.fitType; if (baseType->getPtrTo()->getAlignSize() == elSize) { score = 5; resType = trial.fitType; } } else score = 5; // Indexing into something that is not an array } } } else if (meta == TYPE_ARRAY || meta == TYPE_STRUCT || meta == TYPE_UNION || meta == TYPE_CODE || meta == TYPE_FLOAT) score = -5; else score = 1; break; case CPUI_INT_2COMP: if (meta == TYPE_ARRAY || meta == TYPE_STRUCT || meta == TYPE_UNION || meta == TYPE_CODE || meta == TYPE_FLOAT) score = -5; else if (meta == TYPE_PTR || meta == TYPE_UNKNOWN || meta == TYPE_BOOL) score = -1; else if (meta == TYPE_INT) score = 5; break; case CPUI_INT_NEGATE: case CPUI_INT_XOR: case CPUI_INT_AND: case CPUI_INT_OR: case CPUI_POPCOUNT: case CPUI_LZCOUNT: if (meta == TYPE_ARRAY || meta == TYPE_STRUCT || meta == TYPE_UNION || meta == TYPE_CODE || meta == TYPE_FLOAT) score = -5; else if (meta == TYPE_PTR || meta == TYPE_BOOL) score = -1; else if (meta == TYPE_UINT || meta == TYPE_UNKNOWN) score = 2; break; case CPUI_INT_LEFT: case CPUI_INT_RIGHT: if (trial.inslot == 0) { if (meta == TYPE_ARRAY || meta == TYPE_STRUCT || meta == TYPE_UNION || meta == TYPE_CODE || meta == TYPE_FLOAT) score = -5; else if (meta == TYPE_PTR || meta == TYPE_BOOL) score = -1; else if (meta == TYPE_UINT || meta == TYPE_UNKNOWN) score = 2; } else { if (meta == TYPE_ARRAY || meta == TYPE_STRUCT || meta == TYPE_UNION || meta == TYPE_CODE || meta == TYPE_FLOAT || meta == TYPE_PTR) score = -5; else score = 1; } break; case CPUI_INT_SRIGHT: if (trial.inslot == 0) { if (meta == TYPE_ARRAY || meta == TYPE_STRUCT || meta == TYPE_UNION || meta == TYPE_CODE || meta == TYPE_FLOAT) score = -5; else if (meta == TYPE_PTR || meta == TYPE_BOOL || meta == TYPE_UINT || meta == TYPE_UNKNOWN) score = -1; else score = 2; } else { if (meta == TYPE_ARRAY || meta == TYPE_STRUCT || meta == TYPE_UNION || meta == TYPE_CODE || meta == TYPE_FLOAT || meta == TYPE_PTR) score = -5; else score = 1; } break; case CPUI_INT_MULT: if (meta == TYPE_ARRAY || meta == TYPE_STRUCT || meta == TYPE_UNION || meta == TYPE_CODE || meta == TYPE_FLOAT) score = -10; else if (meta == TYPE_PTR || meta == TYPE_BOOL) score = -2; else score = 5; break; case CPUI_INT_DIV: case CPUI_INT_REM: if (meta == TYPE_ARRAY || meta == TYPE_STRUCT || meta == TYPE_UNION || meta == TYPE_CODE || meta == TYPE_FLOAT) score = -10; else if (meta == TYPE_PTR || meta == TYPE_BOOL) score = -2; else if (meta == TYPE_UINT || meta == TYPE_UNKNOWN) score = 5; break; case CPUI_INT_SDIV: case CPUI_INT_SREM: if (meta == TYPE_ARRAY || meta == TYPE_STRUCT || meta == TYPE_UNION || meta == TYPE_CODE || meta == TYPE_FLOAT) score = -10; else if (meta == TYPE_PTR || meta == TYPE_BOOL) score = -2; else if (meta == TYPE_INT) score = 5; break; case CPUI_BOOL_NEGATE: case CPUI_BOOL_AND: case CPUI_BOOL_XOR: case CPUI_BOOL_OR: if (meta == TYPE_BOOL) score = 10; else if (meta == TYPE_INT || meta == TYPE_UINT || meta == TYPE_UNKNOWN) score = -1; else score = -10; break; case CPUI_FLOAT_EQUAL: case CPUI_FLOAT_NOTEQUAL: case CPUI_FLOAT_LESS: case CPUI_FLOAT_LESSEQUAL: case CPUI_FLOAT_NAN: case CPUI_FLOAT_ADD: case CPUI_FLOAT_DIV: case CPUI_FLOAT_MULT: case CPUI_FLOAT_SUB: case CPUI_FLOAT_NEG: case CPUI_FLOAT_ABS: case CPUI_FLOAT_SQRT: case CPUI_FLOAT_FLOAT2FLOAT: case CPUI_FLOAT_TRUNC: case CPUI_FLOAT_CEIL: case CPUI_FLOAT_FLOOR: case CPUI_FLOAT_ROUND: if (meta == TYPE_FLOAT) score = 10; else score = -10; break; case CPUI_FLOAT_INT2FLOAT: if (meta == TYPE_ARRAY || meta == TYPE_STRUCT || meta == TYPE_UNION || meta == TYPE_CODE || meta == TYPE_FLOAT) score = -10; else if (meta == TYPE_PTR) score = -5; else if (meta == TYPE_INT) score = 5; break; case CPUI_PIECE: if (meta == TYPE_ARRAY || meta == TYPE_STRUCT || meta == TYPE_UNION || meta == TYPE_CODE || meta == TYPE_FLOAT) score = -5; break; case CPUI_SUBPIECE: { int4 offset = TypeOpSubpiece::computeByteOffsetForComposite(trial.op); resType = scoreTruncation(trial.fitType, trial.op->getOut(), offset, trial.scoreIndex); break; } case CPUI_PTRADD: if (meta == TYPE_PTR) { if (trial.inslot == 0) { Datatype *ptrto = ((TypePointer *)trial.fitType)->getPtrTo(); if (ptrto->getAlignSize() == trial.op->getIn(2)->getOffset()) { score = 10; resType = trial.fitType; } } else { score = -10; } } else if (meta == TYPE_ARRAY || meta == TYPE_STRUCT || meta == TYPE_UNION || meta == TYPE_CODE || meta == TYPE_FLOAT) score = -5; else score = 1; break; case CPUI_SEGMENTOP: if (trial.inslot == 2) { if (meta == TYPE_PTR) score = 5; else if (meta == TYPE_ARRAY || meta == TYPE_STRUCT || meta == TYPE_UNION || meta == TYPE_CODE || meta == TYPE_FLOAT) score = -5; else score = -1; } else { if (meta == TYPE_ARRAY || meta == TYPE_STRUCT || meta == TYPE_UNION || meta == TYPE_CODE || meta == TYPE_FLOAT || meta == TYPE_PTR) score = -2; } break; default: score = -10; // Doesn't fit break; } scores[trial.scoreIndex] += score; if (resType != (Datatype *)0 && !lastLevel) newTrialsDown(trial.op->getOut(), resType, trial.scoreIndex, trial.array); } void ScoreUnionFields::scoreTrialUp(const Trial &trial,bool lastLevel) { if (trial.direction == Trial::fit_down) return; // Trial doesn't push in this direction int score = 0; if (!trial.vn->isWritten()) { if (trial.vn->isConstant()) scoreConstantFit(trial); return; // Nothing to propagate up through } Datatype *resType = (Datatype *)0; // Assume by default we don't propagate int4 newslot = 0; type_metatype meta = trial.fitType->getMetatype(); PcodeOp *def = trial.vn->getDef(); switch(def->code()) { case CPUI_COPY: case CPUI_MULTIEQUAL: case CPUI_INDIRECT: resType = trial.fitType; // No score, but we can propagate newslot = 0; break; case CPUI_LOAD: resType = typegrp.getTypePointer(def->getIn(1)->getSize(),trial.fitType,1); newslot = 1; // No score, but we can propagate break; case CPUI_CALL: case CPUI_CALLOTHER: case CPUI_CALLIND: score = scoreReturnType(trial.fitType, def); break; case CPUI_INT_EQUAL: case CPUI_INT_NOTEQUAL: case CPUI_INT_SLESS: case CPUI_INT_SLESSEQUAL: case CPUI_INT_SCARRY: case CPUI_INT_SBORROW: case CPUI_INT_LESS: case CPUI_INT_LESSEQUAL: case CPUI_INT_CARRY: case CPUI_BOOL_NEGATE: case CPUI_BOOL_AND: case CPUI_BOOL_XOR: case CPUI_BOOL_OR: case CPUI_FLOAT_EQUAL: case CPUI_FLOAT_NOTEQUAL: case CPUI_FLOAT_LESS: case CPUI_FLOAT_LESSEQUAL: case CPUI_FLOAT_NAN: if (meta == TYPE_BOOL) score = 10; else if (trial.fitType->getSize() == 1) score = 1; else score = -10; break; case CPUI_INT_ADD: case CPUI_INT_SUB: case CPUI_PTRSUB: if (meta == TYPE_PTR) { score = 5; // Don't try to back up further } else if (meta == TYPE_ARRAY || meta == TYPE_STRUCT || meta == TYPE_UNION || meta == TYPE_CODE || meta == TYPE_FLOAT) score = -5; else score = 1; break; case CPUI_INT_2COMP: if (meta == TYPE_ARRAY || meta == TYPE_STRUCT || meta == TYPE_UNION || meta == TYPE_CODE || meta == TYPE_FLOAT) score = -5; else if (meta == TYPE_PTR || meta == TYPE_UNKNOWN || meta == TYPE_BOOL) score = -1; else if (meta == TYPE_INT) score = 5; break; case CPUI_INT_NEGATE: case CPUI_INT_XOR: case CPUI_INT_AND: case CPUI_INT_OR: case CPUI_POPCOUNT: case CPUI_LZCOUNT: if (meta == TYPE_ARRAY || meta == TYPE_STRUCT || meta == TYPE_UNION || meta == TYPE_CODE || meta == TYPE_FLOAT) score = -5; else if (meta == TYPE_PTR || meta == TYPE_BOOL) score = -1; else if (meta == TYPE_UINT || meta == TYPE_UNKNOWN) score = 2; break; case CPUI_INT_LEFT: case CPUI_INT_RIGHT: if (meta == TYPE_ARRAY || meta == TYPE_STRUCT || meta == TYPE_UNION || meta == TYPE_CODE || meta == TYPE_FLOAT) score = -5; else if (meta == TYPE_PTR || meta == TYPE_BOOL) score = -1; else if (meta == TYPE_UINT || meta == TYPE_UNKNOWN) score = 2; break; case CPUI_INT_SRIGHT: if (meta == TYPE_ARRAY || meta == TYPE_STRUCT || meta == TYPE_UNION || meta == TYPE_CODE || meta == TYPE_FLOAT) score = -5; else if (meta == TYPE_PTR || meta == TYPE_BOOL || meta == TYPE_UINT || meta == TYPE_UNKNOWN) score = -1; else score = 2; break; case CPUI_INT_MULT: if (meta == TYPE_ARRAY || meta == TYPE_STRUCT || meta == TYPE_UNION || meta == TYPE_CODE || meta == TYPE_FLOAT) score = -10; else if (meta == TYPE_PTR || meta == TYPE_BOOL) score = -2; else score = 5; break; case CPUI_INT_DIV: case CPUI_INT_REM: if (meta == TYPE_ARRAY || meta == TYPE_STRUCT || meta == TYPE_UNION || meta == TYPE_CODE || meta == TYPE_FLOAT) score = -10; else if (meta == TYPE_PTR || meta == TYPE_BOOL) score = -2; else if (meta == TYPE_UINT || meta == TYPE_UNKNOWN) score = 5; break; case CPUI_INT_SDIV: case CPUI_INT_SREM: if (meta == TYPE_ARRAY || meta == TYPE_STRUCT || meta == TYPE_UNION || meta == TYPE_CODE || meta == TYPE_FLOAT) score = -10; else if (meta == TYPE_PTR || meta == TYPE_BOOL) score = -2; else if (meta == TYPE_INT) score = 5; break; case CPUI_FLOAT_ADD: case CPUI_FLOAT_DIV: case CPUI_FLOAT_MULT: case CPUI_FLOAT_SUB: case CPUI_FLOAT_NEG: case CPUI_FLOAT_ABS: case CPUI_FLOAT_SQRT: case CPUI_FLOAT_FLOAT2FLOAT: case CPUI_FLOAT_CEIL: case CPUI_FLOAT_FLOOR: case CPUI_FLOAT_ROUND: case CPUI_FLOAT_INT2FLOAT: if (meta == TYPE_FLOAT) score = 10; else score = -10; break; case CPUI_FLOAT_TRUNC: if (meta == TYPE_INT || meta == TYPE_UINT) score = 2; else score = -2; break; case CPUI_PIECE: if (meta == TYPE_FLOAT || meta == TYPE_BOOL) score = -5; else if (meta == TYPE_CODE || meta == TYPE_PTR) score = -2; break; case CPUI_SUBPIECE: if (meta == TYPE_INT || meta == TYPE_UINT || meta == TYPE_BOOL) { if (def->getIn(1)->getOffset() == 0) score = 3; // Likely truncation else score = 1; } else score = -5; break; case CPUI_PTRADD: if (meta == TYPE_PTR) { Datatype *ptrto = ((TypePointer *)trial.fitType)->getPtrTo(); if (ptrto->getAlignSize() == def->getIn(2)->getOffset()) score = 10; else score = 2; } else if (meta == TYPE_ARRAY || meta == TYPE_STRUCT || meta == TYPE_UNION || meta == TYPE_CODE || meta == TYPE_FLOAT) score = -5; else score = 1; break; default: score = -10; // Datatype doesn't fit break; } scores[trial.scoreIndex] += score; if (resType != (Datatype *)0 && !lastLevel) { newTrials(def, newslot, resType, trial.scoreIndex, trial.array); } } /// The truncation may be an explicit CPUI_SUBPIECE, or it may be implied. /// A score is computed for fitting a given data-type to the truncation, and a possible /// data-type to recurse is also computed. /// \param ct is the given data-type to truncate /// \param vn is the Varnode the truncation will fit into /// \param offset is the number of bytes truncated off the start of the data-type /// \param scoreIndex is the field being scored /// \return the data-type to recurse or null Datatype *ScoreUnionFields::scoreTruncation(Datatype *ct,Varnode *vn,int4 offset,int4 scoreIndex) { int4 score; if (ct->getMetatype() == TYPE_UNION) { TypeUnion *unionDt = (TypeUnion *)ct; ct = (Datatype *)0; // Don't recurse a data-type from truncation of a union score = -10; // Negative score if the union has no field matching the size int4 num = unionDt->numDepend(); for(int4 i=0;igetField(i); if (field->offset == offset && field->type->getSize() == vn->getSize()) { score = 10; if (result.getBase() == unionDt) score += 5; break; } } } else { score = 10; // If we can find a size match for the truncation int8 curOff = offset; while(ct != (Datatype*)0 && (curOff != 0 || ct->getSize() != vn->getSize())) { if (ct->getMetatype() == TYPE_INT || ct->getMetatype() == TYPE_UINT) { if (ct->getSize() >= vn->getSize() + curOff) { score = 1; // Size doesn't match, but still possibly a reasonable operation break; } } ct = ct->getSubType(curOff,&curOff); } if (ct == (Datatype *)0) score = -10; } scores[scoreIndex] += score; return ct; } /// Assume the constant has no data-type of its own to match against. /// Evaluate if the constant looks like an integer or pointer etc. and score the trial data-type against that. /// \param trial is the trial of the constant Varnode void ScoreUnionFields::scoreConstantFit(const Trial &trial) { int4 size = trial.vn->getSize(); uintb val = trial.vn->getOffset(); type_metatype meta = trial.fitType->getMetatype(); int4 score = 0; if (meta == TYPE_BOOL) { score = (size == 1 && val < 2) ? 2 : -2; } else if (meta == TYPE_FLOAT) { score = -1; const FloatFormat *format = typegrp.getArch()->translate->getFloatFormat(size); if (format != (const FloatFormat *)0) { int4 exp = format->extractExponentCode(val); if (exp < 7 && exp > -4) // Check for common exponent range score = 2; } } else if (meta == TYPE_INT || meta == TYPE_UINT || meta == TYPE_PTR) { if (val == 0) { score = 2; // Zero is equally valid as pointer or integer } else { AddrSpace *spc = typegrp.getArch()->getDefaultDataSpace(); bool looksLikePointer = false; if (val >= spc->getPointerLowerBound() && val <= spc->getPointerUpperBound()) { if (bit_transitions(val,size) >= 3) { looksLikePointer = true; } } if (meta == TYPE_PTR) { score = looksLikePointer ? 2 : -2; } else { score = looksLikePointer ? 1 : 2; } } } else score = -2; scores[trial.scoreIndex] += score; } /// Run through each trial in the current list and compute a score. If the trial recurses and this is /// \e not the final pass, build new trials for the recursion. /// \param lastPass is \b true if this is the last pass void ScoreUnionFields::runOneLevel(bool lastPass) { list::const_iterator iter; for(iter=trialCurrent.begin();iter!=trialCurrent.end();++iter) { trialCount += 1; if (trialCount > maxTrials) return; // Absolute number of trials reached const Trial &trial(*iter); scoreTrialDown(trial,lastPass); scoreTrialUp(trial,lastPass); } } void ScoreUnionFields::computeBestIndex(void) { int4 bestScore = scores[0]; int4 bestIndex = 0; for(int4 i=1;i bestScore) { bestScore = scores[i]; bestIndex = i; } } result.fieldNum = bestIndex - 1; // Renormalize score index to field index result.resolve = fields[bestIndex]; } /// Try to fit each possible field over multiple levels of the data-flow. /// Return the index of the highest scoring field or -1 if the union data-type /// itself is the best fit. void ScoreUnionFields::run(void) { trialCount = 0; for(int4 pass=0;pass < maxPasses;++pass) { if (trialCurrent.empty()) break; if (trialCount > threshold) break; // Threshold reached, don't score any more trials if (pass + 1 == maxPasses) runOneLevel(true); else { runOneLevel(false); trialCurrent.swap(trialNext); trialNext.clear(); } } } /// \brief Score a given data-type involving a union against data-flow /// /// The data-type must either be a union or a pointer to union. /// Set up the initial set of trials based on the given data-flow edge (PcodeOp and slot). /// \param tgrp is the TypeFactory owning the data-types /// \param parentType is the given data-type to score /// \param op is PcodeOp of the given data-flow edge /// \param slot is slot of the given data-flow edge ScoreUnionFields::ScoreUnionFields(TypeFactory &tgrp,Datatype *parentType,PcodeOp *op,int4 slot) : typegrp(tgrp), result(parentType) { if (testSimpleCases(op, slot, parentType)) return; int4 wordSize = (parentType->getMetatype() == TYPE_PTR) ? ((TypePointer *)parentType)->getWordSize() : 0; int4 numFields = result.baseType->numDepend(); scores.resize(numFields + 1,0); fields.resize(numFields + 1,(Datatype *)0); Varnode *vn; if (slot < 0) { vn = op->getOut(); if (vn->getSize() != parentType->getSize()) scores[0] -= 10; // Data-type does not even match size of Varnode else trialCurrent.emplace_back(vn,parentType,0,false); } else { vn = op->getIn(slot); if (vn->getSize() != parentType->getSize()) scores[0] -= 10; else trialCurrent.emplace_back(op,slot,parentType,0,false); } fields[0] = parentType; visited.insert(VisitMark(vn,0)); for(int4 i=0;igetDepend(i); bool isArray = false; if (wordSize != 0) { if (fieldType->getMetatype() == TYPE_ARRAY) isArray = true; fieldType = typegrp.getTypePointerStripArray(parentType->getSize(),fieldType,wordSize); } if (vn->getSize() != fieldType->getSize()) scores[i+1] -= 10; // Data-type does not even match size of Varnode, don't create trial else if (slot < 0) { trialCurrent.emplace_back(vn,fieldType,i+1,isArray); } else { trialCurrent.emplace_back(op,slot,fieldType,i+1,isArray); } fields[i+1] = fieldType; visited.insert(VisitMark(vn,i+1)); } run(); computeBestIndex(); } /// \brief Score a union data-type against data-flow, where there is a SUBPIECE /// /// A truncation is fit to each union field before doing the fit against data-flow. /// Only fields that match the offset and the truncation size (of the SUBPIECE) are scored further. /// If there is a good fit, the scoring for that field recurses into the given data-flow edge. /// This is only used where there is a SUBPIECE and the base scoring indicates the whole union is /// the best match for the input. /// \param tgrp is the TypeFactory owning the data-types /// \param unionType is the data-type to score, which must be a TypeUnion /// \param offset is the given starting offset of the truncation /// \param op is the SUBPIECE op ScoreUnionFields::ScoreUnionFields(TypeFactory &tgrp,TypeUnion *unionType,int4 offset,PcodeOp *op) :typegrp(tgrp), result(unionType) { Varnode *vn = op->getOut(); int numFields = unionType->numDepend(); scores.resize(numFields + 1, 0); fields.resize(numFields + 1, (Datatype *)0); fields[0] = unionType; scores[0] = -10; for(int4 i=0;igetField(i); fields[i+1] = unionField->type; if (unionField->type->getSize() != vn->getSize() || unionField->offset != offset) { scores[i+1] = -10; continue; } newTrialsDown(vn, unionField->type, i+1, false); } trialCurrent.swap(trialNext); if (trialCurrent.size() > 1) run(); computeBestIndex(); } /// \brief Score a union data-type against data-flow, where there is an implied truncation /// /// A truncation is fit to each union field before doing the fit against data-flow, starting with /// the given PcodeOp and input slot. /// \param tgrp is the TypeFactory owning the data-types /// \param unionType is the data-type to score, which must be a TypeUnion /// \param offset is the given starting offset of the truncation /// \param op is the PcodeOp initially reading/writing the union /// \param slot is the -1 if the op is writing, >= 0 if reading ScoreUnionFields::ScoreUnionFields(TypeFactory &tgrp,TypeUnion *unionType,int4 offset,PcodeOp *op,int4 slot) :typegrp(tgrp), result(unionType) { Varnode *vn = (slot < 0) ? op->getOut() : op->getIn(slot); int numFields = unionType->numDepend(); scores.resize(numFields + 1, 0); fields.resize(numFields + 1, (Datatype *)0); fields[0] = unionType; scores[0] = -10; // Assume the untruncated entire union is not a good fit for(int4 i=0;igetField(i); fields[i+1] = unionField->type; // Score the implied truncation Datatype *ct = scoreTruncation(unionField->type,vn,offset-unionField->offset,i+1); if (ct != (Datatype *)0) { if (slot < 0) trialCurrent.emplace_back(vn,ct,i+1,false); // Try to flow backward else trialCurrent.emplace_back(op,slot,ct,i+1,false); // Flow downward visited.insert(VisitMark(vn,i+1)); } } if (trialCurrent.size() > 1) run(); computeBestIndex(); } } // End namespace ghidra