GP-5240 Simplify late expressions where a SUBPIECE is taken of a PIECE

This commit is contained in:
caheckman 2025-01-06 21:51:28 +00:00
parent acf5ac688a
commit 53d8d182e3
15 changed files with 215 additions and 88 deletions

View file

@ -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|

View file

@ -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 {

View file

@ -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]);

View file

@ -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);

View file

@ -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") );

View file

@ -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

View file

@ -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.

View file

@ -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)

View file

@ -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);

View file

@ -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);

View file

@ -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
///

View file

@ -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

View file

@ -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

View file

@ -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();
}

View file

@ -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\(&amp;out-&gt;base,&amp;in-&gt;base\);</stringmatch>
<stringmatch name="Concat split #2" min="1" max="1">out-&gt;a = A;</stringmatch>
<stringmatch name="Concat split #3" min="1" max="1">out-&gt;b = B;</stringmatch>
<stringmatch name="Concat split #4" min="1" max="1">out-&gt;c = C;</stringmatch>
<stringmatch name="Concat split #5" min="1" max="1">out-&gt;d = D;</stringmatch>
<stringmatch name="Concat split #6" min="1" max="1">out-&gt;e = E;</stringmatch>
<stringmatch name="Concat split #7" min="0" max="0">CONCAT</stringmatch>
</decompilertest>