mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 09:49:23 +02:00
Merge remote-tracking branch 'origin/GP-5240_SplitPieceLate'
(Closes #7277)
This commit is contained in:
commit
a6256817d4
15 changed files with 215 additions and 88 deletions
|
@ -15,6 +15,7 @@ src/decompile/cpp/Makefile||GHIDRA||||END|
|
|||
src/decompile/datatests/boolless.xml||GHIDRA||||END|
|
||||
src/decompile/datatests/ccmp.xml||GHIDRA||||END|
|
||||
src/decompile/datatests/concat.xml||GHIDRA||||END|
|
||||
src/decompile/datatests/concatsplit.xml||GHIDRA||||END|
|
||||
src/decompile/datatests/condconst.xml||GHIDRA||||END|
|
||||
src/decompile/datatests/condmulti.xml||GHIDRA||||END|
|
||||
src/decompile/datatests/convert.xml||GHIDRA||||END|
|
||||
|
|
|
@ -46,13 +46,14 @@ bool CastStrategy::markExplicitUnsigned(PcodeOp *op,int4 slot) const
|
|||
if (!vn->isConstant()) return false;
|
||||
Datatype *dt = vn->getHighTypeReadFacing(op);
|
||||
type_metatype meta = dt->getMetatype();
|
||||
if ((meta != TYPE_UINT)&&(meta != TYPE_UNKNOWN)) return false;
|
||||
if (meta != TYPE_UINT && meta != TYPE_UNKNOWN && meta != TYPE_PARTIALSTRUCT && meta != TYPE_PARTIALUNION)
|
||||
return false;
|
||||
if (dt->isCharPrint()) return false;
|
||||
if (dt->isEnumType()) return false;
|
||||
if ((op->numInput() == 2) && !inheritsFirstParamOnly) {
|
||||
Varnode *firstvn = op->getIn(1-slot);
|
||||
meta = firstvn->getHighTypeReadFacing(op)->getMetatype();
|
||||
if ((meta == TYPE_UINT)||(meta == TYPE_UNKNOWN))
|
||||
if (meta == TYPE_UINT || meta == TYPE_UNKNOWN || meta == TYPE_PARTIALSTRUCT || meta == TYPE_PARTIALUNION)
|
||||
return false; // Other side of the operation will force the unsigned
|
||||
}
|
||||
// Check if type is going to get forced anyway
|
||||
|
@ -85,7 +86,9 @@ bool CastStrategy::markExplicitLongSize(PcodeOp *op,int4 slot) const
|
|||
if (vn->getSize() <= promoteSize) return false;
|
||||
Datatype *dt = vn->getHigh()->getType();
|
||||
type_metatype meta = dt->getMetatype();
|
||||
if ((meta != TYPE_UINT)&&(meta != TYPE_INT)&&(meta != TYPE_UNKNOWN)) return false;
|
||||
if (meta != TYPE_UINT && meta != TYPE_INT && meta != TYPE_UNKNOWN &&
|
||||
meta != TYPE_PARTIALSTRUCT && meta != TYPE_PARTIALUNION)
|
||||
return false;
|
||||
uintb off = vn->getOffset();
|
||||
if (meta == TYPE_INT && signbit_negative(off, vn->getSize())) {
|
||||
off = uintb_negate(off, vn->getSize());
|
||||
|
@ -138,8 +141,9 @@ int4 CastStrategyC::localExtensionType(const Varnode *vn,const PcodeOp *op) cons
|
|||
|
||||
{
|
||||
type_metatype meta = vn->getHighTypeReadFacing(op)->getMetatype();
|
||||
int4 natural; // 1= natural zero extension, 2= natural sign extension
|
||||
if ((meta == TYPE_UINT)||(meta == TYPE_BOOL)||(meta == TYPE_UNKNOWN))
|
||||
int4 natural;
|
||||
if (meta == TYPE_UINT || meta == TYPE_BOOL || meta == TYPE_UNKNOWN ||
|
||||
meta == TYPE_PARTIALSTRUCT || meta == TYPE_PARTIALUNION)
|
||||
natural = UNSIGNED_EXTENSION;
|
||||
else if (meta == TYPE_INT)
|
||||
natural = SIGNED_EXTENSION;
|
||||
|
@ -331,20 +335,23 @@ Datatype *CastStrategyC::castStandard(Datatype *reqtype,Datatype *curtype,
|
|||
}
|
||||
switch(reqbase->getMetatype()) {
|
||||
case TYPE_UNKNOWN:
|
||||
case TYPE_PARTIALSTRUCT: // As they are ultimately stripped, treat partials as undefined
|
||||
case TYPE_PARTIALUNION:
|
||||
return (Datatype *)0;
|
||||
case TYPE_UINT:
|
||||
if (!care_uint_int) {
|
||||
type_metatype meta = curbase->getMetatype();
|
||||
// Note: meta can be TYPE_UINT if curbase is typedef/enumerated
|
||||
if ((meta==TYPE_UNKNOWN)||(meta==TYPE_INT)||(meta==TYPE_UINT)||(meta==TYPE_BOOL))
|
||||
if (meta==TYPE_UNKNOWN || meta==TYPE_INT || meta==TYPE_UINT || meta==TYPE_BOOL ||
|
||||
meta==TYPE_PARTIALSTRUCT || meta==TYPE_PARTIALUNION)
|
||||
return (Datatype *)0;
|
||||
}
|
||||
else {
|
||||
type_metatype meta = curbase->getMetatype();
|
||||
if ((meta == TYPE_UINT)||(meta==TYPE_BOOL)) // Can be TYPE_UINT for typedef/enumerated
|
||||
return (Datatype *)0;
|
||||
if (isptr && (meta==TYPE_UNKNOWN)) // Don't cast pointers to unknown
|
||||
return (Datatype *)0;
|
||||
if (isptr && (meta==TYPE_UNKNOWN || meta==TYPE_PARTIALSTRUCT || meta==TYPE_PARTIALUNION))
|
||||
return (Datatype *)0; // Don't cast pointers to unknown
|
||||
}
|
||||
if ((!care_ptr_uint)&&(curbase->getMetatype()==TYPE_PTR))
|
||||
return (Datatype *)0;
|
||||
|
@ -353,15 +360,16 @@ Datatype *CastStrategyC::castStandard(Datatype *reqtype,Datatype *curtype,
|
|||
if (!care_uint_int) {
|
||||
type_metatype meta = curbase->getMetatype();
|
||||
// Note: meta can be TYPE_INT if curbase is an enumerated type
|
||||
if ((meta==TYPE_UNKNOWN)||(meta==TYPE_INT)||(meta==TYPE_UINT)||(meta==TYPE_BOOL))
|
||||
if (meta==TYPE_UNKNOWN || meta==TYPE_INT || meta==TYPE_UINT || meta==TYPE_BOOL ||
|
||||
meta==TYPE_PARTIALSTRUCT || meta==TYPE_PARTIALUNION)
|
||||
return (Datatype *)0;
|
||||
}
|
||||
else {
|
||||
type_metatype meta = curbase->getMetatype();
|
||||
if ((meta == TYPE_INT)||(meta == TYPE_BOOL))
|
||||
return (Datatype *)0; // Can be TYPE_INT for typedef/enumerated/char
|
||||
if (isptr && (meta==TYPE_UNKNOWN)) // Don't cast pointers to unknown
|
||||
return (Datatype *)0;
|
||||
if (isptr && (meta==TYPE_UNKNOWN || meta==TYPE_PARTIALSTRUCT || meta==TYPE_PARTIALUNION))
|
||||
return (Datatype *)0; // Don't cast pointers to unknown
|
||||
}
|
||||
break;
|
||||
case TYPE_CODE:
|
||||
|
@ -402,24 +410,19 @@ bool CastStrategyC::isSubpieceCast(Datatype *outtype,Datatype *intype,uint4 offs
|
|||
{
|
||||
if (offset != 0) return false;
|
||||
type_metatype inmeta = intype->getMetatype();
|
||||
if ((inmeta!=TYPE_INT)&&
|
||||
(inmeta!=TYPE_UINT)&&
|
||||
(inmeta!=TYPE_UNKNOWN)&&
|
||||
(inmeta!=TYPE_PTR))
|
||||
if (inmeta!=TYPE_INT && inmeta!=TYPE_UINT && inmeta!=TYPE_UNKNOWN && inmeta!=TYPE_PTR &&
|
||||
inmeta!=TYPE_PARTIALSTRUCT && inmeta!=TYPE_PARTIALUNION)
|
||||
return false;
|
||||
type_metatype outmeta = outtype->getMetatype();
|
||||
if ((outmeta!=TYPE_INT)&&
|
||||
(outmeta!=TYPE_UINT)&&
|
||||
(outmeta!=TYPE_UNKNOWN)&&
|
||||
(outmeta!=TYPE_PTR)&&
|
||||
(outmeta!=TYPE_FLOAT))
|
||||
if (outmeta!=TYPE_INT && outmeta!=TYPE_UINT && outmeta!=TYPE_UNKNOWN &&
|
||||
outmeta!=TYPE_PTR && outmeta!=TYPE_FLOAT)
|
||||
return false;
|
||||
if (inmeta==TYPE_PTR) {
|
||||
if (outmeta == TYPE_PTR) {
|
||||
if (outtype->getSize() < intype->getSize())
|
||||
return true; // Cast from far pointer to near pointer
|
||||
}
|
||||
if ((outmeta!=TYPE_INT) && (outmeta!=TYPE_UINT))
|
||||
if (outmeta!=TYPE_INT && outmeta!=TYPE_UINT)
|
||||
return false; //other casts don't make sense for pointers
|
||||
}
|
||||
return true;
|
||||
|
@ -477,12 +480,15 @@ Datatype *CastStrategyJava::castStandard(Datatype *reqtype,Datatype *curtype,
|
|||
if (reqbase->getSize() != curbase->getSize()) return reqtype; // Always cast change in size
|
||||
switch(reqbase->getMetatype()) {
|
||||
case TYPE_UNKNOWN:
|
||||
case TYPE_PARTIALSTRUCT: // As they are ultimately stripped, treat partials as undefined
|
||||
case TYPE_PARTIALUNION:
|
||||
return (Datatype *)0;
|
||||
case TYPE_UINT:
|
||||
if (!care_uint_int) {
|
||||
type_metatype meta = curbase->getMetatype();
|
||||
// Note: meta can be TYPE_UINT if curbase is typedef/enumerated
|
||||
if ((meta==TYPE_UNKNOWN)||(meta==TYPE_INT)||(meta==TYPE_UINT)||(meta==TYPE_BOOL))
|
||||
if (meta==TYPE_UNKNOWN || meta==TYPE_INT || meta==TYPE_UINT || meta==TYPE_BOOL ||
|
||||
meta==TYPE_PARTIALSTRUCT || meta==TYPE_PARTIALUNION)
|
||||
return (Datatype *)0;
|
||||
}
|
||||
else {
|
||||
|
@ -495,7 +501,8 @@ Datatype *CastStrategyJava::castStandard(Datatype *reqtype,Datatype *curtype,
|
|||
if (!care_uint_int) {
|
||||
type_metatype meta = curbase->getMetatype();
|
||||
// Note: meta can be TYPE_INT if curbase is an enumerated type
|
||||
if ((meta==TYPE_UNKNOWN)||(meta==TYPE_INT)||(meta==TYPE_UINT)||(meta==TYPE_BOOL))
|
||||
if (meta==TYPE_UNKNOWN || meta==TYPE_INT || meta==TYPE_UINT || meta==TYPE_BOOL ||
|
||||
meta==TYPE_PARTIALSTRUCT || meta==TYPE_PARTIALUNION)
|
||||
return (Datatype *)0;
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -807,33 +807,6 @@ void HeapSequence::gatherIndirectPairs(vector<PcodeOp *> &indirects,vector<Varno
|
|||
indirects[i]->clearMark();
|
||||
}
|
||||
|
||||
/// \brief Remove the given PcodeOp and any other ops that uniquely produce its inputs
|
||||
///
|
||||
/// The given PcodeOp is always removed. PcodeOps are recursively removed, if the only data-flow
|
||||
/// path of their output is to the given op, and they are not a CALL or are otherwise special.
|
||||
/// \param op is the given PcodeOp to remove
|
||||
/// \param scratch is scratch space for holding
|
||||
void HeapSequence::removeRecursive(PcodeOp *op,vector<PcodeOp *> &scratch)
|
||||
|
||||
{
|
||||
scratch.clear();
|
||||
scratch.push_back(op);
|
||||
int4 pos = 0;
|
||||
while(pos < scratch.size()) {
|
||||
op = scratch[pos];
|
||||
pos += 1;
|
||||
for(int4 i=0;i<op->numInput();++i) {
|
||||
Varnode *vn = op->getIn(i);
|
||||
if (!vn->isWritten() || vn->isAutoLive()) continue;
|
||||
if (vn->loneDescend() == (PcodeOp *)0) continue;
|
||||
PcodeOp *defOp = vn->getDef();
|
||||
if (defOp->isCall() || defOp->isIndirectSource()) continue;
|
||||
scratch.push_back(defOp);
|
||||
}
|
||||
data.opDestroy(op);
|
||||
}
|
||||
}
|
||||
|
||||
/// If the STORE pointer no longer has any other uses, remove the PTRADD producing it, recursively,
|
||||
/// up to the base pointer. INDIRECT ops surrounding any STORE that is removed are replaced with
|
||||
/// INDIRECTs around the user-op replacing the STOREs.
|
||||
|
@ -847,7 +820,7 @@ void HeapSequence::removeStoreOps(PcodeOp *replaceOp)
|
|||
gatherIndirectPairs(indirects, indirectPairs);
|
||||
for(int4 i=0;i<moveOps.size();++i) {
|
||||
PcodeOp *op = moveOps[i].op;
|
||||
removeRecursive(op, scratch);
|
||||
data.opDestroyRecursive(op, scratch);
|
||||
}
|
||||
for(int4 i=0;i<indirects.size();++i) {
|
||||
data.opDestroy(indirects[i]);
|
||||
|
|
|
@ -99,7 +99,6 @@ class HeapSequence : public ArraySequence {
|
|||
bool collectStoreOps(void); ///< Collect ops STOREing into a memory region from the same root pointer
|
||||
PcodeOp *buildStringCopy(void); ///< Build the strncpy,wcsncpy, or memcpy function with string as input
|
||||
void gatherIndirectPairs(vector<PcodeOp *> &indirects,vector<Varnode *> &pairs);
|
||||
void removeRecursive(PcodeOp *op,vector<PcodeOp *> &scratch);
|
||||
void removeStoreOps(PcodeOp *replaceOp); ///< Remove all STORE ops from the basic block
|
||||
public:
|
||||
HeapSequence(Funcdata &fdata,Datatype *ct,PcodeOp *root);
|
||||
|
|
|
@ -5600,6 +5600,7 @@ void ActionDatabase::universalAction(Architecture *conf)
|
|||
actcleanup->addRule( new RuleMultNegOne("cleanup") );
|
||||
actcleanup->addRule( new RuleAddUnsigned("cleanup") );
|
||||
actcleanup->addRule( new Rule2Comp2Sub("cleanup") );
|
||||
actcleanup->addRule( new RuleDumptyHumpLate("cleanup") );
|
||||
actcleanup->addRule( new RuleSubRight("cleanup") );
|
||||
actcleanup->addRule( new RuleFloatSignCleanup("cleanup") );
|
||||
actcleanup->addRule( new RuleExpandLoad("cleanup") );
|
||||
|
|
|
@ -464,6 +464,7 @@ public:
|
|||
void opUninsert(PcodeOp *op); ///< Remove the given PcodeOp from its basic block
|
||||
void opUnlink(PcodeOp *op); ///< Unset inputs/output and remove given PcodeOP from its basic block
|
||||
void opDestroy(PcodeOp *op); ///< Remove given PcodeOp and destroy its Varnode operands
|
||||
void opDestroyRecursive(PcodeOp *op,vector<PcodeOp *> &scratch); ///< Remove a PcodeOp and recursively remove ops producing its inputs
|
||||
void opDestroyRaw(PcodeOp *op); ///< Remove the given \e raw PcodeOp
|
||||
void opDeadAndGone(PcodeOp *op) { obank.destroy(op); } ///< Free resources for the given \e dead PcodeOp
|
||||
void opSetAllInput(PcodeOp *op,const vector<Varnode *> &vvec); ///< Set all input Varnodes for the given PcodeOp simultaneously
|
||||
|
|
|
@ -221,6 +221,31 @@ void Funcdata::opDestroy(PcodeOp *op)
|
|||
}
|
||||
}
|
||||
|
||||
/// The given PcodeOp is always removed. PcodeOps are recursively removed, if the only data-flow
|
||||
/// path of their output is to the given op, and they are not a CALL or are otherwise special.
|
||||
/// \param op is the given PcodeOp to remove
|
||||
/// \param scratch is scratch space for holding PcodeOps being examined
|
||||
void Funcdata::opDestroyRecursive(PcodeOp *op,vector<PcodeOp *> &scratch)
|
||||
|
||||
{
|
||||
scratch.clear();
|
||||
scratch.push_back(op);
|
||||
int4 pos = 0;
|
||||
while(pos < scratch.size()) {
|
||||
op = scratch[pos];
|
||||
pos += 1;
|
||||
for(int4 i=0;i<op->numInput();++i) {
|
||||
Varnode *vn = op->getIn(i);
|
||||
if (!vn->isWritten() || vn->isAutoLive()) continue;
|
||||
if (vn->loneDescend() == (PcodeOp *)0) continue;
|
||||
PcodeOp *defOp = vn->getDef();
|
||||
if (defOp->isCall() || defOp->isIndirectSource()) continue;
|
||||
scratch.push_back(defOp);
|
||||
}
|
||||
opDestroy(op);
|
||||
}
|
||||
}
|
||||
|
||||
/// This is a specialized routine for deleting an op during flow generation that has
|
||||
/// been replaced by something else. The op is expected to be \e dead with none of its inputs
|
||||
/// or outputs linked to anything else. Both the PcodeOp and all the input/output Varnodes are destroyed.
|
||||
|
|
|
@ -849,6 +849,16 @@ void PrintC::opSubpiece(const PcodeOp *op)
|
|||
if (ct->isPieceStructured()) {
|
||||
int8 offset;
|
||||
int8 byteOff = TypeOpSubpiece::computeByteOffsetForComposite(op);
|
||||
Symbol *sym = vn->getHigh()->getSymbol();
|
||||
if (sym != (Symbol *)0 && vn->isExplicit()) {
|
||||
int4 sz = op->getOut()->getSize();
|
||||
int4 suboff = vn->getHigh()->getSymbolOffset();
|
||||
if (suboff > 0)
|
||||
byteOff += suboff;
|
||||
int4 slot = ct->needsResolution() ? 1 : 0; // Use artificial slot for initial resolution
|
||||
pushPartialSymbol(sym, byteOff, sz, op->getOut(), op, slot, true);
|
||||
return;
|
||||
}
|
||||
const TypeField *field = ct->findTruncation(byteOff,op->getOut()->getSize(),op,1,offset); // Use artificial slot
|
||||
if (field != (const TypeField*)0 && offset == 0) { // A formal structure field
|
||||
pushOp(&object_member,op);
|
||||
|
@ -856,16 +866,6 @@ void PrintC::opSubpiece(const PcodeOp *op)
|
|||
pushAtom(Atom(field->name,fieldtoken,EmitMarkup::no_color,ct,field->ident,op));
|
||||
return;
|
||||
}
|
||||
else if (vn->isExplicit() && vn->getHigh()->getSymbolOffset() == -1) { // An explicit, entire, structured object
|
||||
Symbol *sym = vn->getHigh()->getSymbol();
|
||||
if (sym != (Symbol *)0) {
|
||||
int4 sz = op->getOut()->getSize();
|
||||
int4 off = (int4)op->getIn(1)->getOffset();
|
||||
off = vn->getSpace()->isBigEndian() ? vn->getSize() - (sz + off) : off;
|
||||
pushPartialSymbol(sym, off, sz, vn, op, -1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Fall thru to functional printing
|
||||
}
|
||||
}
|
||||
|
@ -1089,7 +1089,7 @@ void PrintC::opPtrsub(const PcodeOp *op)
|
|||
// we can't use a cast in its description, so turn off
|
||||
// casting when printing the partial symbol
|
||||
// Datatype *exttype = ((mods & print_store_value)!=0) ? (Datatype *)0 : ct;
|
||||
pushPartialSymbol(symbol,off,0,(Varnode *)0,op,-1);
|
||||
pushPartialSymbol(symbol,off,0,(Varnode *)0,op,-1,false);
|
||||
}
|
||||
}
|
||||
if (arrayvalue)
|
||||
|
@ -1883,7 +1883,7 @@ void PrintC::pushAnnotation(const Varnode *vn,const PcodeOp *op)
|
|||
pushSymbol(entry->getSymbol(),vn,op);
|
||||
else {
|
||||
int4 symboloff = vn->getOffset() - entry->getFirst();
|
||||
pushPartialSymbol(entry->getSymbol(),symboloff,size,vn,op,-1);
|
||||
pushPartialSymbol(entry->getSymbol(),symboloff,size,vn,op,-1,false);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -1946,7 +1946,7 @@ void PrintC::pushUnnamedLocation(const Address &addr,
|
|||
|
||||
void PrintC::pushPartialSymbol(const Symbol *sym,int4 off,int4 sz,
|
||||
const Varnode *vn,const PcodeOp *op,
|
||||
int4 inslot)
|
||||
int4 slot,bool allowCast)
|
||||
{
|
||||
// We need to print "bottom up" in order to get parentheses right
|
||||
// I.e. we want to print globalstruct.arrayfield[0], rather than
|
||||
|
@ -1965,12 +1965,12 @@ void PrintC::pushPartialSymbol(const Symbol *sym,int4 off,int4 sz,
|
|||
bool succeeded = false;
|
||||
if (ct->getMetatype()==TYPE_STRUCT) {
|
||||
if (ct->needsResolution() && ct->getSize() == sz) {
|
||||
Datatype *outtype = ct->findResolve(op, inslot);
|
||||
Datatype *outtype = ct->findResolve(op, slot);
|
||||
if (outtype == ct)
|
||||
break; // Turns out we don't resolve to the field
|
||||
}
|
||||
const TypeField *field;
|
||||
field = ct->findTruncation(off,sz,op,inslot,newoff);
|
||||
field = ct->findTruncation(off,sz,op,slot,newoff);
|
||||
if (field != (const TypeField *)0) {
|
||||
off = newoff;
|
||||
stack.emplace_back();
|
||||
|
@ -2000,7 +2000,7 @@ void PrintC::pushPartialSymbol(const Symbol *sym,int4 off,int4 sz,
|
|||
}
|
||||
else if (ct->getMetatype() == TYPE_UNION) {
|
||||
const TypeField *field;
|
||||
field = ct->findTruncation(off,sz,op,inslot,newoff);
|
||||
field = ct->findTruncation(off,sz,op,slot,newoff);
|
||||
if (field != (const TypeField*)0) {
|
||||
off = newoff;
|
||||
stack.emplace_back();
|
||||
|
@ -2015,7 +2015,7 @@ void PrintC::pushPartialSymbol(const Symbol *sym,int4 off,int4 sz,
|
|||
else if (ct->getSize() == sz)
|
||||
break; // Turns out we don't need to resolve the field
|
||||
}
|
||||
else if (inslot >= 0) {
|
||||
else if (allowCast) {
|
||||
Datatype *outtype = vn->getHigh()->getType();
|
||||
AddrSpace *spc = sym->getFirstWholeMap()->getAddr().getSpace();
|
||||
if (spc == (AddrSpace *)0)
|
||||
|
|
|
@ -211,7 +211,7 @@ protected:
|
|||
virtual void pushUnnamedLocation(const Address &addr,
|
||||
const Varnode *vn,const PcodeOp *op);
|
||||
virtual void pushPartialSymbol(const Symbol *sym,int4 off,int4 sz,
|
||||
const Varnode *vn,const PcodeOp *op,int4 inslot);
|
||||
const Varnode *vn,const PcodeOp *op,int4 slot,bool allowCast);
|
||||
virtual void pushMismatchSymbol(const Symbol *sym,int4 off,int4 sz,
|
||||
const Varnode *vn,const PcodeOp *op);
|
||||
virtual void pushImpliedField(const Varnode *vn,const PcodeOp *op);
|
||||
|
|
|
@ -254,7 +254,7 @@ void PrintLanguage::pushSymbolDetail(const Varnode *vn,const PcodeOp *op,bool is
|
|||
}
|
||||
if (symboloff + vn->getSize() <= sym->getType()->getSize()) {
|
||||
int4 inslot = isRead ? op->getSlot(vn) : -1;
|
||||
pushPartialSymbol(sym,symboloff,vn->getSize(),vn,op,inslot);
|
||||
pushPartialSymbol(sym,symboloff,vn->getSize(),vn,op,inslot,isRead);
|
||||
}
|
||||
else
|
||||
pushMismatchSymbol(sym,symboloff,vn->getSize(),vn,op);
|
||||
|
|
|
@ -365,14 +365,20 @@ protected:
|
|||
/// \brief Push a variable that represents only part of a symbol onto the RPN stack
|
||||
///
|
||||
/// Generally \e member syntax specifying a field within a structure gets emitted.
|
||||
/// Nested structures may result in multiple fields being emitted to get to the final size.
|
||||
/// If the final size requires truncating a data-type that is not a structure, this method
|
||||
/// can optionally emit a final cast to represent the truncation, otherwise an artificial
|
||||
/// field representing the truncation is emitted. Any \e union encountered is resolved using
|
||||
/// the given PcodeOp and slot.
|
||||
/// \param sym is the root Symbol
|
||||
/// \param off is the byte offset, within the Symbol, of the partial variable
|
||||
/// \param sz is the number of bytes in the partial variable
|
||||
/// \param vn is the Varnode holding the partial value
|
||||
/// \param op is a PcodeOp associate with the Varnode
|
||||
/// \param inslot is the input slot of \b vn with \b op, or -1 if \b op writes \b vn
|
||||
/// \param slot is the slot to use (relative to \b op) for any data-type requiring resolution
|
||||
/// \param allowCast is \b true if a final truncation should be printed as a cast
|
||||
virtual void pushPartialSymbol(const Symbol *sym,int4 off,int4 sz,
|
||||
const Varnode *vn,const PcodeOp *op,int4 inslot)=0;
|
||||
const Varnode *vn,const PcodeOp *op,int4 slot,bool allowCast)=0;
|
||||
|
||||
/// \brief Push an identifier for a variable that mismatches with its Symbol
|
||||
///
|
||||
|
|
|
@ -2983,6 +2983,70 @@ int4 RuleSplitStore::applyOp(PcodeOp *op,Funcdata &data)
|
|||
return 0;
|
||||
}
|
||||
|
||||
void RuleDumptyHumpLate::getOpList(vector<uint4> &oplist) const
|
||||
|
||||
{
|
||||
oplist.push_back(CPUI_SUBPIECE);
|
||||
}
|
||||
|
||||
int4 RuleDumptyHumpLate::applyOp(PcodeOp *op,Funcdata &data)
|
||||
|
||||
{
|
||||
Varnode *vn = op->getIn(0);
|
||||
if (!vn->isWritten()) return 0;
|
||||
PcodeOp *pieceOp = vn->getDef();
|
||||
if (pieceOp->code() != CPUI_PIECE) return 0;
|
||||
Varnode *out = op->getOut();
|
||||
int4 outSize = out->getSize();
|
||||
int4 trunc = (int4)op->getIn(1)->getOffset();
|
||||
for(;;) {
|
||||
// Try to backtrack thru PIECE to the component vn is being truncated from
|
||||
Varnode *trialVn = pieceOp->getIn(1); // Assume the least significant component
|
||||
int4 trialTrunc = trunc;
|
||||
if (trunc >= trialVn->getSize()) { // Test for truncation from the most significant part
|
||||
trialTrunc -= trialVn->getSize(); // How much is truncated
|
||||
trialVn = pieceOp->getIn(0);
|
||||
}
|
||||
if (outSize + trialTrunc > trialVn->getSize())
|
||||
break; // vn crosses both components
|
||||
vn = trialVn; // Commit to this component
|
||||
trunc = trialTrunc;
|
||||
if (vn->getSize() == outSize)
|
||||
break; // Found matching component
|
||||
if (!vn->isWritten())
|
||||
break;
|
||||
pieceOp = vn->getDef();
|
||||
if (pieceOp->code() != CPUI_PIECE)
|
||||
break;
|
||||
}
|
||||
if (vn == op->getIn(0))
|
||||
return 0; // Didn't backtrack thru any PIECE
|
||||
if (vn->isWritten() && vn->getDef()->code() == CPUI_COPY)
|
||||
vn = vn->getDef()->getIn(0);
|
||||
PcodeOp *removeOp;
|
||||
if (outSize != vn->getSize()) { // Component does not match size exactly. Preserve SUBPIECE.
|
||||
removeOp = op->getIn(0)->getDef();
|
||||
if (op->getIn(1)->getOffset() != trunc)
|
||||
data.opSetInput(op, data.newConstant(4, trunc), 1);
|
||||
data.opSetInput(op, vn, 0);
|
||||
}
|
||||
else if (out->isAutoLive()) { // Exact match but output address fixed. Change SUBPIECE to COPY.
|
||||
removeOp = op->getIn(0)->getDef();
|
||||
data.opRemoveInput(op, 1);
|
||||
data.opSetOpcode(op, CPUI_COPY);
|
||||
data.opSetInput(op, vn, 0);
|
||||
}
|
||||
else { // Exact match. Completely replace output with component.
|
||||
removeOp = op;
|
||||
data.totalReplace(out, vn);
|
||||
}
|
||||
if (removeOp->getOut()->hasNoDescend() && !removeOp->getOut()->isAutoLive()) {
|
||||
vector<PcodeOp *> scratch;
|
||||
data.opDestroyRecursive(removeOp, scratch);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/// This method distinguishes between a floating-point variable with \e full precision, where all the
|
||||
/// storage can vary (or is unknown), versus a value that is extended from a floating-point variable with
|
||||
/// smaller storage. Within the data-flow above the given Varnode, we search for the maximum
|
||||
|
|
|
@ -353,6 +353,24 @@ public:
|
|||
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
||||
};
|
||||
|
||||
/// \brief Simplify join and break apart based on data-types
|
||||
///
|
||||
/// Simplify expressions like:
|
||||
/// - `sub( concat(V,W), 0) => W`
|
||||
/// - `sub( concat(V,W), c) => V`
|
||||
///
|
||||
/// preserving the data-types and removing the SUBPIECE and PIECE operations that are discarded.
|
||||
class RuleDumptyHumpLate : public Rule {
|
||||
public:
|
||||
RuleDumptyHumpLate(const string &g) : Rule( g, 0, "dumptyhumplate") {} ///< Constructor
|
||||
virtual Rule *clone(const ActionGroupList &grouplist) const {
|
||||
if (!grouplist.contains(getGroup())) return (Rule *)0;
|
||||
return new RuleDumptyHumpLate(getGroup());
|
||||
}
|
||||
virtual void getOpList(vector<uint4> &oplist) const;
|
||||
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
||||
};
|
||||
|
||||
/// \brief Class for tracing changes of precision in floating point variables
|
||||
///
|
||||
/// It follows the flow of a logical lower precision value stored in higher precision locations
|
||||
|
|
|
@ -303,18 +303,18 @@ void HighVariable::stripType(void) const
|
|||
{
|
||||
if (!type->hasStripped())
|
||||
return;
|
||||
if (type->getMetatype() == TYPE_PARTIALUNION) {
|
||||
if (symbol != (Symbol *)0 && symboloffset != -1) {
|
||||
type_metatype meta = symbol->getType()->getMetatype();
|
||||
if (meta != TYPE_STRUCT && meta != TYPE_UNION) // If partial union does not have a bigger backing symbol
|
||||
type = type->getStripped(); // strip the partial union
|
||||
type_metatype meta = type->getMetatype();
|
||||
if (meta == TYPE_PARTIALUNION || meta == TYPE_PARTIALSTRUCT) {
|
||||
if (symbol != (Symbol *)0 && symboloffset != -1) { // If there is a bigger backing symbol
|
||||
type_metatype submeta = symbol->getType()->getMetatype();
|
||||
if (submeta == TYPE_STRUCT || submeta == TYPE_UNION)
|
||||
return; // Don't strip the partial union
|
||||
}
|
||||
}
|
||||
else if (type->isEnumType()) {
|
||||
if (inst.size() != 1 || !inst[0]->isConstant()) // Only preserve partial enum on a constant
|
||||
type = type->getStripped();
|
||||
if (inst.size() == 1 && inst[0]->isConstant()) // Only preserve partial enum on a constant
|
||||
return;
|
||||
}
|
||||
else
|
||||
type = type->getStripped();
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
<decompilertest>
|
||||
<binaryimage arch="x86:LE:64:default:gcc">
|
||||
<!--
|
||||
Example where field assignments are packed into registers before begin written to the structure.
|
||||
-->
|
||||
<bytechunk space="ram" offset="0x100000" readonly="true">
|
||||
660f6ec266410f6ec8660f6ed94883ec
|
||||
2866410f6ed1660f62c348897c240866
|
||||
0f62ca660f6cc10f29442410e82f0000
|
||||
00488b442408660f6f4424108b542430
|
||||
0f1140148950244883c428c3
|
||||
</bytechunk>
|
||||
<symbol space="ram" offset="0x100000" name="test_split"/>
|
||||
<symbol space="ram" offset="0x100060" name="create"/>
|
||||
</binaryimage>
|
||||
<script>
|
||||
<com>parse line struct mystruct { int4 base; int4 unused1; int4 unused2; int4 unused3; int4 unused4; int4 a; int4 b; int4 c; int4 d; int4 e; };</com>
|
||||
<com>parse line extern void create(int4 *out,int4 *in);</com>
|
||||
<com>parse line extern mystruct *test_split(mystruct *out,mystruct *in,int4 A,int4 B,int4 C,int4 D,int4 E);</com>
|
||||
<com>lo fu test_split</com>
|
||||
<com>decompile</com>
|
||||
<com>print C</com>
|
||||
<com>quit</com>
|
||||
</script>
|
||||
<stringmatch name="Concat split #1" min="1" max="1">create\(&out->base,&in->base\);</stringmatch>
|
||||
<stringmatch name="Concat split #2" min="1" max="1">out->a = A;</stringmatch>
|
||||
<stringmatch name="Concat split #3" min="1" max="1">out->b = B;</stringmatch>
|
||||
<stringmatch name="Concat split #4" min="1" max="1">out->c = C;</stringmatch>
|
||||
<stringmatch name="Concat split #5" min="1" max="1">out->d = D;</stringmatch>
|
||||
<stringmatch name="Concat split #6" min="1" max="1">out->e = E;</stringmatch>
|
||||
<stringmatch name="Concat split #7" min="0" max="0">CONCAT</stringmatch>
|
||||
</decompilertest>
|
Loading…
Add table
Add a link
Reference in a new issue