GP-2291 Support for unions through partial containers

This commit is contained in:
caheckman 2022-08-24 11:46:46 -04:00
parent d3efd60d54
commit cb9c12894e
21 changed files with 678 additions and 182 deletions

View file

@ -35,6 +35,7 @@ src/decompile/datatests/noforloop_globcall.xml||GHIDRA||||END|
src/decompile/datatests/noforloop_iterused.xml||GHIDRA||||END| src/decompile/datatests/noforloop_iterused.xml||GHIDRA||||END|
src/decompile/datatests/offsetarray.xml||GHIDRA||||END| src/decompile/datatests/offsetarray.xml||GHIDRA||||END|
src/decompile/datatests/packstructaccess.xml||GHIDRA||||END| src/decompile/datatests/packstructaccess.xml||GHIDRA||||END|
src/decompile/datatests/partialunion.xml||GHIDRA||||END|
src/decompile/datatests/pointercmp.xml||GHIDRA||||END| src/decompile/datatests/pointercmp.xml||GHIDRA||||END|
src/decompile/datatests/pointerrel.xml||GHIDRA||||END| src/decompile/datatests/pointerrel.xml||GHIDRA||||END|
src/decompile/datatests/pointersub.xml||GHIDRA||||END| src/decompile/datatests/pointersub.xml||GHIDRA||||END|

View file

@ -2334,6 +2334,8 @@ int4 ActionSetCasts::resolveUnion(PcodeOp *op,int4 slot,Funcdata &data)
Datatype *dt = vn->getHigh()->getType(); Datatype *dt = vn->getHigh()->getType();
if (!dt->needsResolution()) if (!dt->needsResolution())
return 0; return 0;
if (dt != vn->getType())
dt->resolveInFlow(op, slot); // Last chance to resolve data-type based on flow
const ResolvedUnion *resUnion = data.getUnionField(dt, op,slot); const ResolvedUnion *resUnion = data.getUnionField(dt, op,slot);
if (resUnion != (ResolvedUnion*)0 && resUnion->getFieldNum() >= 0) { if (resUnion != (ResolvedUnion*)0 && resUnion->getFieldNum() >= 0) {
// Insert specific placeholder indicating which field is accessed // Insert specific placeholder indicating which field is accessed
@ -2383,8 +2385,12 @@ int4 ActionSetCasts::castOutput(PcodeOp *op,Funcdata &data,CastStrategy *castStr
// Short circuit more sophisticated casting tests. If they are the same type, there is no cast // Short circuit more sophisticated casting tests. If they are the same type, there is no cast
return 0; return 0;
} }
if (outHighType->needsResolution()) Datatype *outHighResolve = outHighType;
outHighType = outHighType->findResolve(op, -1); // Finish fetching DefFacing data-type if (outHighType->needsResolution()) {
if (outHighType != outvn->getType())
outHighType->resolveInFlow(op, -1); // Last chance to resolve data-type based on flow
outHighResolve = outHighType->findResolve(op, -1); // Finish fetching DefFacing data-type
}
if (outvn->isImplied()) { if (outvn->isImplied()) {
// implied varnode must have parse type // implied varnode must have parse type
if (outvn->isTypeLock()) { if (outvn->isTypeLock()) {
@ -2392,25 +2398,25 @@ int4 ActionSetCasts::castOutput(PcodeOp *op,Funcdata &data,CastStrategy *castStr
// The Varnode input to a CPUI_RETURN is marked as implied but // The Varnode input to a CPUI_RETURN is marked as implied but
// casting should act as if it were explicit // casting should act as if it were explicit
if (outOp == (PcodeOp *)0 || outOp->code() != CPUI_RETURN) { if (outOp == (PcodeOp *)0 || outOp->code() != CPUI_RETURN) {
force = !isOpIdentical(outHighType, tokenct); force = !isOpIdentical(outHighResolve, tokenct);
} }
} }
else if (outHighType->getMetatype() != TYPE_PTR) { // If implied varnode has an atomic (non-pointer) type else if (outHighResolve->getMetatype() != TYPE_PTR) { // If implied varnode has an atomic (non-pointer) type
outvn->updateType(tokenct,false,false); // Ignore it in favor of the token type outvn->updateType(tokenct,false,false); // Ignore it in favor of the token type
outHighType = outvn->getHighTypeDefFacing(); outHighResolve = outvn->getHighTypeDefFacing();
} }
else if (tokenct->getMetatype() == TYPE_PTR) { // If the token is a pointer AND implied varnode is pointer else if (tokenct->getMetatype() == TYPE_PTR) { // If the token is a pointer AND implied varnode is pointer
outct = ((TypePointer *)outHighType)->getPtrTo(); outct = ((TypePointer *)outHighResolve)->getPtrTo();
type_metatype meta = outct->getMetatype(); type_metatype meta = outct->getMetatype();
// Preserve implied pointer if it points to a composite // Preserve implied pointer if it points to a composite
if ((meta!=TYPE_ARRAY)&&(meta!=TYPE_STRUCT)&&(meta!=TYPE_UNION)) { if ((meta!=TYPE_ARRAY)&&(meta!=TYPE_STRUCT)&&(meta!=TYPE_UNION)) {
outvn->updateType(tokenct,false,false); // Otherwise ignore it in favor of the token type outvn->updateType(tokenct,false,false); // Otherwise ignore it in favor of the token type
outHighType = outvn->getHighTypeDefFacing(); outHighResolve = outvn->getHighTypeDefFacing();
} }
} }
} }
if (!force) { if (!force) {
outct = outHighType; // Type of result outct = outHighResolve; // Type of result
ct = castStrategy->castStandard(outct,tokenct,false,true); ct = castStrategy->castStandard(outct,tokenct,false,true);
if (ct == (Datatype *)0) return 0; if (ct == (Datatype *)0) return 0;
} }
@ -2427,8 +2433,10 @@ int4 ActionSetCasts::castOutput(PcodeOp *op,Funcdata &data,CastStrategy *castStr
data.opSetInput(newop,vn,0); data.opSetInput(newop,vn,0);
data.opSetOutput(op,vn); data.opSetOutput(op,vn);
data.opInsertAfter(newop,op); // Cast comes AFTER this operation data.opInsertAfter(newop,op); // Cast comes AFTER this operation
if (tokenct->needsResolution())
data.forceFacingType(tokenct, -1, newop, 0);
if (outHighType->needsResolution()) if (outHighType->needsResolution())
data.forceFacingType(outHighType, -1, newop, -1); data.inheritWriteResolution(outHighType, newop, op);
return 1; return 1;
} }
@ -4483,6 +4491,12 @@ bool ActionInferTypes::propagateTypeEdge(TypeFactory *typegrp,PcodeOp *op,int4 i
{ {
Varnode *invn,*outvn; Varnode *invn,*outvn;
invn = (inslot==-1) ? op->getOut() : op->getIn(inslot);
Datatype *alttype = invn->getTempType();
if (alttype->needsResolution()) {
// Always give incoming data-type a chance to resolve, even if it would not otherwise propagate
alttype = alttype->resolveInFlow(op, inslot);
}
if (inslot == outslot) return false; // don't backtrack if (inslot == outslot) return false; // don't backtrack
if (outslot < 0) if (outslot < 0)
outvn = op->getOut(); outvn = op->getOut();
@ -4490,11 +4504,6 @@ bool ActionInferTypes::propagateTypeEdge(TypeFactory *typegrp,PcodeOp *op,int4 i
outvn = op->getIn(outslot); outvn = op->getIn(outslot);
if (outvn->isAnnotation()) return false; if (outvn->isAnnotation()) return false;
} }
invn = (inslot==-1) ? op->getOut() : op->getIn(inslot);
Datatype *alttype = invn->getTempType();
if (alttype->needsResolution()) {
alttype = alttype->resolveInFlow(op, inslot);
}
if (outvn->isTypeLock()) return false; // Can't propagate through typelock if (outvn->isTypeLock()) return false; // Can't propagate through typelock
if (outvn->stopsUpPropagation() && outslot >= 0) return false; // Propagation is blocked if (outvn->stopsUpPropagation() && outslot >= 0) return false; // Propagation is blocked

View file

@ -1525,6 +1525,8 @@ SymbolEntry *Scope::addSymbol(const string &nm,Datatype *ct,
{ {
Symbol *sym; Symbol *sym;
if (ct->hasStripped())
ct = ct->getStripped();
sym = new Symbol(owner,nm,ct); sym = new Symbol(owner,nm,ct);
addSymbolInternal(sym); addSymbolInternal(sym);
return addMapPoint(sym,addr,usepoint); return addMapPoint(sym,addr,usepoint);
@ -1713,6 +1715,29 @@ Symbol *Scope::addEquateSymbol(const string &nm,uint4 format,uintb value,const A
return sym; return sym;
} }
/// \brief Create a symbol forcing a field interpretation for a specific access to a variable with \e union data-type
///
/// The symbol is attached to a specific Varnode and a PcodeOp that reads or writes to it. The Varnode,
/// in the context of the PcodeOp, is forced to have the data-type of the selected field, and field's name is used
/// to represent the Varnode in output.
/// \param nm is the name of the symbol
/// \param dt is the union data-type containing the field to force
/// \param fieldNum is the index of the desired field, or -1 if the whole union should be forced
/// \param addr is the address of the p-code op reading/writing the Varnode
/// \param hash is the dynamic hash identifying the Varnode
/// \return the new UnionFacetSymbol
Symbol *Scope::addUnionFacetSymbol(const string &nm,Datatype *dt,int4 fieldNum,const Address &addr,uint8 hash)
{
Symbol *sym = new UnionFacetSymbol(owner,nm,dt,fieldNum);
addSymbolInternal(sym);
RangeList rnglist;
if (!addr.isInvalid())
rnglist.insertRange(addr.getSpace(),addr.getOffset(),addr.getOffset());
addDynamicMapInternal(sym,Varnode::mapped,hash,0,1,rnglist);
return sym;
}
/// Create default name given information in the Symbol and possibly a representative Varnode. /// Create default name given information in the Symbol and possibly a representative Varnode.
/// This method extracts the crucial properties and then uses the buildVariableName method to /// This method extracts the crucial properties and then uses the buildVariableName method to
/// construct the actual name. /// construct the actual name.
@ -2129,6 +2154,8 @@ void ScopeInternal::renameSymbol(Symbol *sym,const string &newname)
void ScopeInternal::retypeSymbol(Symbol *sym,Datatype *ct) void ScopeInternal::retypeSymbol(Symbol *sym,Datatype *ct)
{ {
if (ct->hasStripped())
ct = ct->getStripped();
if ((sym->type->getSize() == ct->getSize())||(sym->mapentry.empty())) { if ((sym->type->getSize() == ct->getSize())||(sym->mapentry.empty())) {
// If size is the same, or no mappings nothing special to do // If size is the same, or no mappings nothing special to do
sym->type = ct; sym->type = ct;

View file

@ -762,6 +762,7 @@ public:
LabSymbol *addCodeLabel(const Address &addr,const string &nm); LabSymbol *addCodeLabel(const Address &addr,const string &nm);
Symbol *addDynamicSymbol(const string &nm,Datatype *ct,const Address &caddr,uint8 hash); Symbol *addDynamicSymbol(const string &nm,Datatype *ct,const Address &caddr,uint8 hash);
Symbol *addEquateSymbol(const string &nm,uint4 format,uintb value,const Address &addr,uint8 hash); Symbol *addEquateSymbol(const string &nm,uint4 format,uintb value,const Address &addr,uint8 hash);
Symbol *addUnionFacetSymbol(const string &nm,Datatype *dt,int4 fieldNum,const Address &addr,uint8 hash);
string buildDefaultName(Symbol *sym,int4 &base,Varnode *vn) const; ///< Create a default name for the given Symbol string buildDefaultName(Symbol *sym,int4 &base,Varnode *vn) const; ///< Create a default name for the given Symbol
bool isReadOnly(const Address &addr,int4 size,const Address &usepoint) const; bool isReadOnly(const Address &addr,int4 size,const Address &usepoint) const;
void printBounds(ostream &s) const { rangetree.printBounds(s); } ///< Print a description of \b this Scope's \e owned memory ranges void printBounds(ostream &s) const { rangetree.printBounds(s); } ///< Print a description of \b this Scope's \e owned memory ranges

View file

@ -858,50 +858,31 @@ bool Funcdata::syncVarnodesWithSymbols(const ScopeLocal *lm,bool typesyes)
return updateoccurred; return updateoccurred;
} }
/// If the Varnode is a partial Symbol with \e union data-type, the best description of the Varnode's /// If the Varnode is a partial of a Symbol with a \e union data-type component, we assign
/// data-type is delayed until data-type propagation is started. /// a partial union data-type (TypePartialUnion) to the Varnode, so that facing resolutions
/// We attempt to resolve this description and also lay down any facing resolutions for the Varnode /// can be provided.
/// \param vn is the given Varnode /// \param vn is the given Varnode
/// \return the best data-type or null /// \return the partial data-type or null
Datatype *Funcdata::checkSymbolType(Varnode *vn) Datatype *Funcdata::checkSymbolType(Varnode *vn)
{ {
if (vn->isTypeLock()) return vn->getType(); if (vn->isTypeLock()) return vn->getType();
SymbolEntry *entry = vn->getSymbolEntry(); SymbolEntry *entry = vn->getSymbolEntry();
Symbol *sym = entry->getSymbol(); Symbol *sym = entry->getSymbol();
if (sym->getType()->getMetatype() != TYPE_UNION) Datatype *curType = sym->getType();
if (curType->getSize() == vn->getSize())
return (Datatype *)0; return (Datatype *)0;
TypeUnion *unionType = (TypeUnion *)sym->getType(); int4 curOff = (vn->getAddr().getOffset() - entry->getAddr().getOffset()) + entry->getOffset();
int4 off = (int4)(vn->getOffset() - entry->getAddr().getOffset()) + entry->getOffset(); // Drill down until we hit something that isn't a containing structure
if (off == 0 && unionType->getSize() == vn->getSize()) while(curType != (Datatype *)0 && curType->getMetatype() == TYPE_STRUCT && curType->getSize() > vn->getSize()) {
return (Datatype *)0; uintb newOff;
const TypeField *finalField = (const TypeField *)0; curType = curType->getSubType(curOff, &newOff);
uintb finalOff = 0; curOff = newOff;
list<PcodeOp *>::const_iterator iter; }
for(iter=vn->beginDescend();iter!=vn->endDescend();++iter) { if (curType == (Datatype *)0 || curType->getSize() <= vn->getSize() || curType->getMetatype() != TYPE_UNION)
PcodeOp *op = *iter;
const TypeField *field = unionType->resolveTruncation(off, op, op->getSlot(vn),off);
if (field != (const TypeField *)0) {
finalField = field;
finalOff = off;
}
}
if (vn->isWritten()) {
const TypeField *field = unionType->resolveTruncation(off, vn->getDef(), -1, off);
if (field != (const TypeField *)0) {
finalField = field;
finalOff = off;
}
}
if (finalField != (const TypeField *)0) { // If any use of the Varnode resolves to a specific field
// Try to truncate down to a final data-type to assign to the Varnode
Datatype *ct = finalField->type;
while(ct != (Datatype *)0 && (finalOff != 0 || ct->getSize() != vn->getSize())) {
ct = ct->getSubType(finalOff, &finalOff);
}
return ct;
}
return (Datatype *)0; return (Datatype *)0;
// If we hit a containing union
return glb->types->getTypePartialUnion((TypeUnion *)curType, curOff, vn->getSize());
} }
/// A Varnode overlaps the given SymbolEntry. Make sure the Varnode is part of the variable /// A Varnode overlaps the given SymbolEntry. Make sure the Varnode is part of the variable

View file

@ -57,6 +57,7 @@ void IfaceDecompCapability::registerCommands(IfaceStatus *status)
status->registerCom(new IfcMapexternalref(),"map","externalref"); status->registerCom(new IfcMapexternalref(),"map","externalref");
status->registerCom(new IfcMaplabel(),"map","label"); status->registerCom(new IfcMaplabel(),"map","label");
status->registerCom(new IfcMapconvert(),"map","convert"); status->registerCom(new IfcMapconvert(),"map","convert");
status->registerCom(new IfcMapunionfacet(), "map", "unionfacet");
status->registerCom(new IfcPrintdisasm(),"disassemble"); status->registerCom(new IfcPrintdisasm(),"disassemble");
status->registerCom(new IfcDecompile(),"decompile"); status->registerCom(new IfcDecompile(),"decompile");
status->registerCom(new IfcDump(),"dump"); status->registerCom(new IfcDump(),"dump");
@ -711,6 +712,39 @@ void IfcMapconvert::execute(istream &s)
dcp->fd->getScopeLocal()->addEquateSymbol("", format, value, addr, hash); dcp->fd->getScopeLocal()->addEquateSymbol("", format, value, addr, hash);
} }
/// \class IfcMapunionfacet
/// \brief Create a union field forcing directive: `map facet <union> <fieldnum> <address> <hash>`
///
/// Creates a \e facet directive that associates a given field of a \e union data-type with
/// a varnode in the context of a specific p-code op accessing it. The varnode and p-code op
/// are specified by dynamic hash.
void IfcMapunionfacet::execute(istream &s)
{
Datatype *ct;
string unionName;
int4 fieldNum;
int4 size;
uint8 hash;
if (dcp->fd == (Funcdata *)0)
throw IfaceExecutionError("No function loaded");
s >> ws >> unionName;
ct = dcp->conf->types->findByName(unionName);
if (ct == (Datatype *)0 || ct->getMetatype() != TYPE_UNION)
throw IfaceParseError("Bad union data-type: " + unionName);
s >> ws >> dec >> fieldNum;
if (fieldNum < -1 || fieldNum >= ct->numDepend())
throw IfaceParseError("Bad field index");
Address addr = parse_machaddr(s,size,*dcp->conf->types); // Read pc address of hash
s >> hex >> hash; // Parse the hash value
ostringstream s2;
s2 << "unionfacet" << dec << (fieldNum + 1) << '_' << hex << addr.getOffset();
Symbol *sym = dcp->fd->getScopeLocal()->addUnionFacetSymbol(s2.str(), ct, fieldNum, addr, hash);
dcp->fd->getScopeLocal()->setAttribute(sym, Varnode::typelock | Varnode::namelock);
}
/// \class IfcPrintdisasm /// \class IfcPrintdisasm
/// \brief Print disassembly of a memory range: `disassemble [<address1> <address2>]` /// \brief Print disassembly of a memory range: `disassemble [<address1> <address2>]`
/// ///

View file

@ -181,6 +181,11 @@ public:
virtual void execute(istream &s); virtual void execute(istream &s);
}; };
class IfcMapunionfacet : public IfaceDecompCommand {
public:
virtual void execute(istream &s);
};
class IfcPrintdisasm : public IfaceDecompCommand { class IfcPrintdisasm : public IfaceDecompCommand {
public: public:
virtual void execute(istream &s); virtual void execute(istream &s);

View file

@ -345,23 +345,30 @@ void Merge::mergeByDatatype(VarnodeLocSet::const_iterator startiter,VarnodeLocSe
/// output is created. /// output is created.
/// \param inVn is the given input Varnode for the new COPY /// \param inVn is the given input Varnode for the new COPY
/// \param addr is the address associated with the new COPY /// \param addr is the address associated with the new COPY
/// \param trimOp is an exemplar PcodeOp whose read is being trimmed
/// \return the newly allocated COPY /// \return the newly allocated COPY
PcodeOp *Merge::allocateCopyTrim(Varnode *inVn,const Address &addr) PcodeOp *Merge::allocateCopyTrim(Varnode *inVn,const Address &addr,PcodeOp *trimOp)
{ {
PcodeOp *copyOp = data.newOp(1,addr); PcodeOp *copyOp = data.newOp(1,addr);
data.opSetOpcode(copyOp,CPUI_COPY); data.opSetOpcode(copyOp,CPUI_COPY);
Datatype *ct = inVn->getType(); Datatype *ct = inVn->getType();
Varnode *outVn = data.newUnique(inVn->getSize(),ct);
data.opSetOutput(copyOp,outVn);
data.opSetInput(copyOp,inVn,0);
copyTrims.push_back(copyOp);
if (ct->needsResolution()) { // If the data-type needs resolution if (ct->needsResolution()) { // If the data-type needs resolution
if (inVn->isWritten()) { if (inVn->isWritten()) {
int4 fieldNum = data.inheritWriteResolution(ct, copyOp, inVn->getDef()); int4 fieldNum = data.inheritWriteResolution(ct, copyOp, inVn->getDef());
data.forceFacingType(ct, fieldNum, copyOp, 0); data.forceFacingType(ct, fieldNum, copyOp, 0);
} }
else {
int4 slot = trimOp->getSlot(inVn);
const ResolvedUnion *resUnion = data.getUnionField(ct, trimOp, slot);
int4 fieldNum = (resUnion == (const ResolvedUnion *)0) ? -1 : resUnion->getFieldNum();
data.forceFacingType(ct, fieldNum, copyOp, 0);
} }
}
Varnode *outVn = data.newUnique(inVn->getSize(),ct);
data.opSetOutput(copyOp,outVn);
data.opSetInput(copyOp,inVn,0);
copyTrims.push_back(copyOp);
return copyOp; return copyOp;
} }
@ -397,7 +404,7 @@ void Merge::snipReads(Varnode *vn,list<PcodeOp *> &markedop)
else else
afterop = vn->getDef(); afterop = vn->getDef();
} }
copyop = allocateCopyTrim(vn, pc); copyop = allocateCopyTrim(vn, pc, markedop.front());
if (afterop == (PcodeOp *)0) if (afterop == (PcodeOp *)0)
data.opInsertBegin(copyop,bl); data.opInsertBegin(copyop,bl);
else else
@ -565,8 +572,15 @@ void Merge::trimOpOutput(PcodeOp *op)
else else
afterop = op; afterop = op;
vn = op->getOut(); vn = op->getOut();
uniq = data.newUnique(vn->getSize(),vn->getTypeDefFacing()); Datatype *ct = vn->getType();
copyop = data.newOp(1,op->getAddr()); copyop = data.newOp(1,op->getAddr());
if (ct->needsResolution()) {
int4 fieldNum = data.inheritWriteResolution(ct, copyop, op);
data.forceFacingType(ct, fieldNum, copyop, 0);
if (ct->getMetatype() == TYPE_PARTIALUNION)
ct = vn->getTypeDefFacing();
}
uniq = data.newUnique(vn->getSize(),ct);
data.opSetOutput(op,uniq); // Output of op is now stubby uniq data.opSetOutput(op,uniq); // Output of op is now stubby uniq
data.opSetOpcode(copyop,CPUI_COPY); data.opSetOpcode(copyop,CPUI_COPY);
data.opSetOutput(copyop,vn); // Original output is bumped forward slightly data.opSetOutput(copyop,vn); // Original output is bumped forward slightly
@ -596,7 +610,7 @@ void Merge::trimOpInput(PcodeOp *op,int4 slot)
else else
pc = op->getAddr(); pc = op->getAddr();
vn = op->getIn(slot); vn = op->getIn(slot);
copyop = allocateCopyTrim(vn, pc); copyop = allocateCopyTrim(vn, pc, op);
data.opSetInput(op,copyop->getOut(),slot); data.opSetInput(op,copyop->getOut(),slot);
if (op->code() == CPUI_MULTIEQUAL) if (op->code() == CPUI_MULTIEQUAL)
data.opInsertEnd(copyop,(BlockBasic *)op->getParent()->getIn(slot)); data.opInsertEnd(copyop,(BlockBasic *)op->getParent()->getIn(slot));
@ -752,7 +766,7 @@ void Merge::snipIndirect(PcodeOp *indop)
// an instance of the output high must // an instance of the output high must
// all intersect so the varnodes must all be // all intersect so the varnodes must all be
// traceable via COPY to the same root // traceable via COPY to the same root
snipop = allocateCopyTrim(refvn, op->getAddr()); snipop = allocateCopyTrim(refvn, op->getAddr(), correctable.front());
data.opInsertBefore(snipop,op); data.opInsertBefore(snipop,op);
list<PcodeOp *>::iterator oiter; list<PcodeOp *>::iterator oiter;
int4 i,slot; int4 i,slot;
@ -789,7 +803,7 @@ void Merge::mergeIndirect(PcodeOp *indop)
PcodeOp *newop; PcodeOp *newop;
newop = allocateCopyTrim(invn0, indop->getAddr()); newop = allocateCopyTrim(invn0, indop->getAddr(), indop);
SymbolEntry *entry = outvn->getSymbolEntry(); SymbolEntry *entry = outvn->getSymbolEntry();
if (entry != (SymbolEntry *)0 && entry->getSymbol()->getType()->needsResolution()) { if (entry != (SymbolEntry *)0 && entry->getSymbol()->getType()->needsResolution()) {
data.inheritWriteResolution(entry->getSymbol()->getType(), newop, indop); data.inheritWriteResolution(entry->getSymbol()->getType(), newop, indop);
@ -1063,20 +1077,28 @@ void Merge::buildDominantCopy(HighVariable *high,vector<PcodeOp *> &copy,int4 po
for(int4 i=0;i<size;++i) for(int4 i=0;i<size;++i)
blockSet.push_back(copy[pos+i]->getParent()); blockSet.push_back(copy[pos+i]->getParent());
BlockBasic *domBl = (BlockBasic *)FlowBlock::findCommonBlock(blockSet); BlockBasic *domBl = (BlockBasic *)FlowBlock::findCommonBlock(blockSet);
Varnode *rootVn = copy[pos]->getIn(0); PcodeOp *domCopy = copy[pos];
Varnode *rootVn = domCopy->getIn(0);
Varnode *domVn = domCopy->getOut();
bool domCopyIsNew; bool domCopyIsNew;
PcodeOp *domCopy; if (domBl == domCopy->getParent()) {
Varnode *domVn;
if (domBl == copy[pos]->getParent()) {
domCopyIsNew = false; domCopyIsNew = false;
domCopy = copy[pos];
domVn = domCopy->getOut();
} }
else { else {
domCopyIsNew = true; domCopyIsNew = true;
PcodeOp *oldCopy = domCopy;
domCopy = data.newOp(1,domBl->getStop()); domCopy = data.newOp(1,domBl->getStop());
data.opSetOpcode(domCopy, CPUI_COPY); data.opSetOpcode(domCopy, CPUI_COPY);
domVn = data.newUnique(rootVn->getSize(), rootVn->getType()); Datatype *ct = rootVn->getType();
if (ct->needsResolution()) {
const ResolvedUnion *resUnion = data.getUnionField(ct, oldCopy, 0);
int4 fieldNum = (resUnion == (const ResolvedUnion *)0) ? -1 : resUnion->getFieldNum();
data.forceFacingType(ct, fieldNum, domCopy, 0);
data.forceFacingType(ct, fieldNum, domCopy, -1);
if (ct->getMetatype() == TYPE_PARTIALUNION)
ct = rootVn->getTypeReadFacing(oldCopy);
}
domVn = data.newUnique(rootVn->getSize(), ct);
data.opSetOutput(domCopy,domVn); data.opSetOutput(domCopy,domVn);
data.opSetInput(domCopy,rootVn,0); data.opSetInput(domCopy,rootVn,0);
data.opInsertEnd(domCopy, domBl); data.opInsertEnd(domCopy, domBl);

View file

@ -96,7 +96,7 @@ class Merge {
void collectCovering(vector<Varnode *> &vlist,HighVariable *high,PcodeOp *op); void collectCovering(vector<Varnode *> &vlist,HighVariable *high,PcodeOp *op);
bool collectCorrectable(const vector<Varnode *> &vlist,list<PcodeOp *> &oplist,vector<int4> &slotlist, bool collectCorrectable(const vector<Varnode *> &vlist,list<PcodeOp *> &oplist,vector<int4> &slotlist,
PcodeOp *op); PcodeOp *op);
PcodeOp *allocateCopyTrim(Varnode *inVn,const Address &addr); PcodeOp *allocateCopyTrim(Varnode *inVn,const Address &addr,PcodeOp *trimOp);
void snipReads(Varnode *vn,list<PcodeOp *> &markedop); void snipReads(Varnode *vn,list<PcodeOp *> &markedop);
void snipIndirect(PcodeOp *indop); void snipIndirect(PcodeOp *indop);
void eliminateIntersect(Varnode *vn,const vector<BlockVarnode> &blocksort); void eliminateIntersect(Varnode *vn,const vector<BlockVarnode> &blocksort);

View file

@ -876,7 +876,7 @@ void PrintC::opPtrsub(const PcodeOp *op)
fieldtype = fld->type; fieldtype = fld->type;
} }
else { // TYPE_STRUCT else { // TYPE_STRUCT
const TypeField *fld = ((TypeStruct*)ct)->resolveTruncation((int4)suboff,0,&newoff); const TypeField *fld = ct->findTruncation((int4)suboff,0,op,0,newoff);
if (fld == (const TypeField*)0) { if (fld == (const TypeField*)0) {
if (ct->getSize() <= suboff) { if (ct->getSize() <= suboff) {
clear(); clear();
@ -1691,6 +1691,7 @@ void PrintC::pushConstant(uintb val,const Datatype *ct,
case TYPE_STRUCT: case TYPE_STRUCT:
case TYPE_UNION: case TYPE_UNION:
case TYPE_PARTIALSTRUCT: case TYPE_PARTIALSTRUCT:
case TYPE_PARTIALUNION:
break; break;
} }
// Default printing // Default printing
@ -1863,7 +1864,7 @@ void PrintC::pushPartialSymbol(const Symbol *sym,int4 off,int4 sz,
break; // Turns out we don't resolve to the field break; // Turns out we don't resolve to the field
} }
const TypeField *field; const TypeField *field;
field = ((TypeStruct *)ct)->resolveTruncation(off,sz,&off); field = ct->findTruncation(off,sz,op,inslot,off);
if (field != (const TypeField *)0) { if (field != (const TypeField *)0) {
stack.emplace_back(); stack.emplace_back();
PartialSymbolEntry &entry( stack.back() ); PartialSymbolEntry &entry( stack.back() );
@ -1894,7 +1895,7 @@ void PrintC::pushPartialSymbol(const Symbol *sym,int4 off,int4 sz,
} }
else if (ct->getMetatype() == TYPE_UNION) { else if (ct->getMetatype() == TYPE_UNION) {
const TypeField *field; const TypeField *field;
field = ((TypeUnion *)ct)->findTruncation(off,op,inslot,off); field = ct->findTruncation(off,sz,op,inslot,off);
if (field != (const TypeField*)0) { if (field != (const TypeField*)0) {
stack.emplace_back(); stack.emplace_back();
PartialSymbolEntry &entry(stack.back()); PartialSymbolEntry &entry(stack.back());

View file

@ -18,9 +18,9 @@
/// The base propagation ordering associated with each meta-type. /// The base propagation ordering associated with each meta-type.
/// The array elements correspond to the ordering of #type_metatype. /// The array elements correspond to the ordering of #type_metatype.
sub_metatype Datatype::base2sub[14] = { sub_metatype Datatype::base2sub[15] = {
SUB_UNION, SUB_STRUCT, SUB_PARTIALSTRUCT, SUB_ARRAY, SUB_PTRREL, SUB_PTR, SUB_FLOAT, SUB_CODE, SUB_BOOL, SUB_PARTIALUNION, SUB_PARTIALSTRUCT, SUB_UNION, SUB_STRUCT, SUB_ARRAY, SUB_PTRREL, SUB_PTR, SUB_FLOAT, SUB_CODE,
SUB_UINT_PLAIN, SUB_INT_PLAIN, SUB_UNKNOWN, SUB_SPACEBASE, SUB_VOID SUB_BOOL, SUB_UINT_PLAIN, SUB_INT_PLAIN, SUB_UNKNOWN, SUB_SPACEBASE, SUB_VOID
}; };
AttributeId ATTRIB_ALIGNMENT = AttributeId("alignment",47); AttributeId ATTRIB_ALIGNMENT = AttributeId("alignment",47);
@ -142,6 +142,24 @@ void Datatype::printRaw(ostream &s) const
s << "unkbyte" << dec << size; s << "unkbyte" << dec << size;
} }
/// \brief Find an immediate subfield of \b this data-type
///
/// Given a byte range within \b this data-type, determine the field it is contained in
/// and pass back the renormalized offset. This method applies to TYPE_STRUCT, TYPE_UNION, and
/// TYPE_PARTIALUNION, data-types that have field components. For TYPE_UNION and TYPE_PARTIALUNION, the
/// field may depend on the p-code op extracting or writing the value.
/// \param off is the byte offset into \b this
/// \param sz is the size of the byte range
/// \param op is the PcodeOp reading/writing the data-type
/// \param slot is the index of the Varnode being accessed, -1 for the output, >=0 for an input
/// \param newoff points to the renormalized offset to pass back
/// \return the containing field or NULL if the range is not contained
const TypeField *Datatype::findTruncation(int4 off,int4 sz,const PcodeOp *op,int4 slot,int4 &newoff) const
{
return (const TypeField *)0;
}
/// Given an offset into \b this data-type, return the component data-type at that offset. /// Given an offset into \b this data-type, return the component data-type at that offset.
/// Also, pass back a "renormalized" offset suitable for recursize getSubType() calls: /// Also, pass back a "renormalized" offset suitable for recursize getSubType() calls:
/// i.e. if the original offset hits the exact start of the sub-type, 0 is passed back. /// i.e. if the original offset hits the exact start of the sub-type, 0 is passed back.
@ -231,7 +249,10 @@ void metatype2string(type_metatype metatype,string &res)
res = "array"; res = "array";
break; break;
case TYPE_PARTIALSTRUCT: case TYPE_PARTIALSTRUCT:
res = "part"; res = "partstruct";
break;
case TYPE_PARTIALUNION:
res = "partunion";
break; break;
case TYPE_STRUCT: case TYPE_STRUCT:
res = "struct"; res = "struct";
@ -275,10 +296,12 @@ type_metatype string2metatype(const string &metastring)
case 'p': case 'p':
if (metastring=="ptr") if (metastring=="ptr")
return TYPE_PTR; return TYPE_PTR;
else if (metastring=="part")
return TYPE_PARTIALSTRUCT;
else if (metastring=="ptrrel") else if (metastring=="ptrrel")
return TYPE_PTRREL; return TYPE_PTRREL;
else if (metastring=="partunion")
return TYPE_PARTIALUNION;
else if (metastring=="partstruct")
return TYPE_PARTIALSTRUCT;
break; break;
case 'a': case 'a':
if (metastring=="array") if (metastring=="array")
@ -461,6 +484,24 @@ int4 Datatype::findCompatibleResolve(Datatype *ct) const
return -1; return -1;
} }
/// \brief Resolve which union field is being used for a given PcodeOp when a truncation is involved
///
/// This method applies to the TYPE_UNION and TYPE_PARTIALUNION data-types, when a Varnode is backed
/// by a larger Symbol with a union data-type, or if the Varnode is produced by a CPUI_SUBPIECE where
/// the input Varnode has a union data-type.
/// Scoring is done to compute the best field and the result is cached with the function.
/// The record of the best field is returned or null if there is no appropriate field
/// \param offset is the byte offset into the union we are truncating to
/// \param op is either the PcodeOp reading the truncated Varnode or the CPUI_SUBPIECE doing the truncation
/// \param slot is either the input slot of the reading PcodeOp or the artificial SUBPIECE slot: 1
/// \param newoff is used to pass back how much offset is left to resolve
/// \return the field of the union best associated with the truncation or null
const TypeField *Datatype::resolveTruncation(int4 offset,PcodeOp *op,int4 slot,int4 &newoff)
{
return (const TypeField *)0;
}
/// Restore the basic properties (name,size,id) of a data-type from an XML element /// Restore the basic properties (name,size,id) of a data-type from an XML element
/// Properties are read from the attributes of the element /// Properties are read from the attributes of the element
/// \param decoder is the stream decoder /// \param decoder is the stream decoder
@ -1332,13 +1373,7 @@ int4 TypeStruct::getLowerBoundField(int4 off) const
return -1; return -1;
} }
/// Given a byte range within \b this data-type, determine the field it is contained in const TypeField *TypeStruct::findTruncation(int4 off,int4 sz,const PcodeOp *op,int4 slot,int4 &newoff) const
/// and pass back the renormalized offset.
/// \param off is the byte offset into \b this
/// \param sz is the size of the byte range
/// \param newoff points to the renormalized offset to pass back
/// \return the containing field or NULL if the range is not contained
const TypeField *TypeStruct::resolveTruncation(int4 off,int4 sz,int4 *newoff) const
{ {
int4 i; int4 i;
@ -1350,7 +1385,7 @@ const TypeField *TypeStruct::resolveTruncation(int4 off,int4 sz,int4 *newoff) co
noff = off - curfield.offset; noff = off - curfield.offset;
if (noff+sz > curfield.type->getSize()) // Requested piece spans more than one field if (noff+sz > curfield.type->getSize()) // Requested piece spans more than one field
return (const TypeField *)0; return (const TypeField *)0;
*newoff = noff; newoff = noff;
return &curfield; return &curfield;
} }
@ -1785,17 +1820,6 @@ Datatype* TypeUnion::findResolve(const PcodeOp *op,int4 slot)
return this; return this;
} }
/// \brief Resolve which union field is being used for a given PcodeOp when a truncation is involved
///
/// This is used either when a Varnode is backed by a larger Symbol with a union data-type,
/// or if the Varnode is produced by a CPUI_SUBPIECE where the input Varnode has a union data-type.
/// Scoring is done to compute the best field and the result is cached with the function.
/// The record of the best field is returned or null if there is no appropriate field
/// \param offset is the byte offset into the union we are truncating to
/// \param op is either the PcodeOp reading the truncated Varnode or the CPUI_SUBPIECE doing the truncation
/// \param slot is either the input slot of the reading PcodeOp or the artificial SUBPIECE slot: 1
/// \param newoff is used to pass back how much offset is left to resolve
/// \return the field of the union best associated with the truncation or null
const TypeField *TypeUnion::resolveTruncation(int4 offset,PcodeOp *op,int4 slot,int4 &newoff) const TypeField *TypeUnion::resolveTruncation(int4 offset,PcodeOp *op,int4 slot,int4 &newoff)
{ {
@ -1826,23 +1850,23 @@ const TypeField *TypeUnion::resolveTruncation(int4 offset,PcodeOp *op,int4 slot,
return (const TypeField *)0; return (const TypeField *)0;
} }
/// \brief Return a precalculated field associated with a truncation
///
/// This is the \e const version of resolveTruncation(). No new scoring is done, but if a cached result
/// is available, return it.
/// \param offset is the byte offset of the truncation /// \param offset is the byte offset of the truncation
/// \param sz is the number of bytes in the resulting truncation
/// \param op is the PcodeOp reading the truncated value /// \param op is the PcodeOp reading the truncated value
/// \param slot is the input slot being read /// \param slot is the input slot being read
/// \param newoff is used to pass back any remaining offset into the field which still must be resolved /// \param newoff is used to pass back any remaining offset into the field which still must be resolved
/// \return the field to use with truncation or null if there is no appropriate field /// \return the field to use with truncation or null if there is no appropriate field
const TypeField *TypeUnion::findTruncation(int4 offset,const PcodeOp *op,int4 slot,int4 &newoff) const const TypeField *TypeUnion::findTruncation(int4 offset,int4 sz,const PcodeOp *op,int4 slot,int4 &newoff) const
{ {
// No new scoring is done, but if a cached result is available, return it.
const Funcdata *fd = op->getParent()->getFuncdata(); const Funcdata *fd = op->getParent()->getFuncdata();
const ResolvedUnion *res = fd->getUnionField(this, op, slot); const ResolvedUnion *res = fd->getUnionField(this, op, slot);
if (res != (ResolvedUnion *)0 && res->getFieldNum() >= 0) { if (res != (ResolvedUnion *)0 && res->getFieldNum() >= 0) {
const TypeField *field = getField(res->getFieldNum()); const TypeField *field = getField(res->getFieldNum());
newoff = offset - field->offset; newoff = offset - field->offset;
if (newoff + sz > field->type->getSize())
return (const TypeField *)0; // Truncation spans more than one field
return field; return field;
} }
return (const TypeField *)0; return (const TypeField *)0;
@ -1870,6 +1894,142 @@ int4 TypeUnion::findCompatibleResolve(Datatype *ct) const
return -1; return -1;
} }
TypePartialUnion::TypePartialUnion(const TypePartialUnion &op)
: Datatype(op)
{
stripped = op.stripped;
container = op.container;
offset = op.offset;
}
TypePartialUnion::TypePartialUnion(TypeUnion *contain,int4 off,int4 sz,Datatype *strip)
: Datatype(sz,TYPE_PARTIALUNION)
{
flags |= (needs_resolution | has_stripped);
stripped = strip;
container = contain;
offset = off;
}
void TypePartialUnion::printRaw(ostream &s) const
{
container->printRaw(s);
s << "[off=" << dec << offset << ",sz=" << size << ']';
}
const TypeField *TypePartialUnion::findTruncation(int4 off,int4 sz,const PcodeOp *op,int4 slot,int4 &newoff) const
{
return container->findTruncation(off + offset, sz, op, slot, newoff);
}
int4 TypePartialUnion::numDepend(void)
{
return container->numDepend();
}
Datatype *TypePartialUnion::getDepend(int4 index)
{
// Treat dependents as coming from the underlying union
Datatype *res = container->getDepend(index);
if (res->getSize() != size) // But if the size doesn't match
return stripped; // Return the stripped data-type
return res;
}
int4 TypePartialUnion::compare(const Datatype &op,int4 level) const
{
int4 res = Datatype::compare(op,level);
if (res != 0) return res;
// Both must be partial unions
TypePartialUnion *tp = (TypePartialUnion *) &op;
if (offset != tp->offset) return (offset < tp->offset) ? -1 : 1;
level -= 1;
if (level < 0) {
if (id == op.getId()) return 0;
return (id < op.getId()) ? -1 : 1;
}
return container->compare(*tp->container,level); // Compare the underlying union
}
int4 TypePartialUnion::compareDependency(const Datatype &op) const
{
if (submeta != op.getSubMeta()) return (submeta < op.getSubMeta()) ? -1 : 1;
TypePartialUnion *tp = (TypePartialUnion *) &op; // Both must be partial unions
if (container != tp->container) return (container < tp->container) ? -1 : 1; // Compare absolute pointers
if (offset != tp->offset) return (offset < tp->offset) ? -1 : 1;
return (op.getSize()-size);
}
void TypePartialUnion::encode(Encoder &encoder) const
{
encoder.openElement(ELEM_TYPE);
encodeBasic(metatype,encoder);
encoder.writeSignedInteger(ATTRIB_OFFSET, offset);
container->encodeRef(encoder);
encoder.closeElement(ELEM_TYPE);
}
Datatype *TypePartialUnion::resolveInFlow(PcodeOp *op,int4 slot)
{
Datatype *curType = container;
int4 curOff = offset;
while(curType != (Datatype *)0 && curType->getSize() > size) {
if (curType->getMetatype() == TYPE_UNION) {
const TypeField *field = curType->resolveTruncation(curOff, op, slot, curOff);
curType = (field == (const TypeField *)0) ? (Datatype *)0 : field->type;
}
else {
uintb newOff;
curType = curType->getSubType(curOff, &newOff);
curOff = newOff;
}
}
if (curType != (Datatype *)0 && curType->getSize() == size)
return curType;
return stripped;
}
Datatype* TypePartialUnion::findResolve(const PcodeOp *op,int4 slot)
{
Datatype *curType = container;
int4 curOff = offset;
while(curType != (Datatype *)0 && curType->getSize() > size) {
if (curType->getMetatype() == TYPE_UNION) {
Datatype *newType = curType->findResolve(op, slot);
curType = (newType == curType) ? (Datatype *)0 : newType;
}
else {
uintb newOff;
curType = curType->getSubType(curOff, &newOff);
curOff = newOff;
}
}
if (curType != (Datatype *)0 && curType->getSize() == size)
return curType;
return stripped;
}
int4 TypePartialUnion::findCompatibleResolve(Datatype *ct) const
{
return container->findCompatibleResolve(ct);
}
const TypeField *TypePartialUnion::resolveTruncation(int4 off,PcodeOp *op,int4 slot,int4 &newoff)
{
return container->resolveTruncation(off + offset, op, slot, newoff);
}
/// Parse a \<type> element with children describing the data-type being pointed to /// Parse a \<type> element with children describing the data-type being pointed to
/// and the parent data-type. /// and the parent data-type.
/// \param decoder is the stream decoder /// \param decoder is the stream decoder
@ -3222,6 +3382,14 @@ TypeUnion *TypeFactory::getTypeUnion(const string &n)
return (TypeUnion *) findAdd(tmp); return (TypeUnion *) findAdd(tmp);
} }
TypePartialUnion *TypeFactory::getTypePartialUnion(TypeUnion *contain,int4 off,int4 sz)
{
Datatype *strip = getBase(sz, TYPE_UNKNOWN);
TypePartialUnion tpu(contain,off,sz,strip);
return (TypePartialUnion *) findAdd(tpu);
}
/// The created enumeration will have no named values and a default configuration /// The created enumeration will have no named values and a default configuration
/// Named values must be added later. /// Named values must be added later.
/// \param n is the name of the enumeration /// \param n is the name of the enumeration

View file

@ -73,50 +73,52 @@ extern void print_data(ostream &s,uint1 *buffer,int4 size,const Address &baseadd
//extern bool print_string(ostream &s,uint1 *buffer,int4 size); //extern bool print_string(ostream &s,uint1 *buffer,int4 size);
/// The core meta-types supported by the decompiler. These are sizeless templates /// The core meta-types supported by the decompiler. These are sizeless templates
/// for the elements making up the type algebra. /// for the elements making up the type algebra. Index is important for Datatype::base2sub array.
enum type_metatype { enum type_metatype {
TYPE_VOID = 13, ///< Standard "void" type, absence of type TYPE_VOID = 14, ///< Standard "void" type, absence of type
TYPE_SPACEBASE = 12, ///< Placeholder for symbol/type look-up calculations TYPE_SPACEBASE = 13, ///< Placeholder for symbol/type look-up calculations
TYPE_UNKNOWN = 11, ///< An unknown low-level type. Treated as an unsigned integer. TYPE_UNKNOWN = 12, ///< An unknown low-level type. Treated as an unsigned integer.
TYPE_INT = 10, ///< Signed integer. Signed is considered less specific than unsigned in C TYPE_INT = 11, ///< Signed integer. Signed is considered less specific than unsigned in C
TYPE_UINT = 9, ///< Unsigned integer TYPE_UINT = 10, ///< Unsigned integer
TYPE_BOOL = 8, ///< Boolean TYPE_BOOL = 9, ///< Boolean
TYPE_CODE = 7, ///< Data is actual executable code TYPE_CODE = 8, ///< Data is actual executable code
TYPE_FLOAT = 6, ///< Floating-point TYPE_FLOAT = 7, ///< Floating-point
TYPE_PTR = 5, ///< Pointer data-type TYPE_PTR = 6, ///< Pointer data-type
TYPE_PTRREL = 4, ///< Pointer relative to another data-type (specialization of TYPE_PTR) TYPE_PTRREL = 5, ///< Pointer relative to another data-type (specialization of TYPE_PTR)
TYPE_ARRAY = 3, ///< Array data-type, made up of a sequence of "element" datatype TYPE_ARRAY = 4, ///< Array data-type, made up of a sequence of "element" datatype
TYPE_PARTIALSTRUCT = 2, ///< Part of a structure, stored separately from the whole TYPE_STRUCT = 3, ///< Structure data-type, made up of component datatypes
TYPE_STRUCT = 1, ///< Structure data-type, made up of component datatypes TYPE_UNION = 2, ///< An overlapping union of multiple datatypes
TYPE_UNION = 0 ///< An overlapping union of multiple datatypes TYPE_PARTIALSTRUCT = 1, ///< Part of a structure, stored separately from the whole
TYPE_PARTIALUNION = 0 ///< Part of a union
}; };
/// Specializations of the core meta-types. Each enumeration is associated with a specific #type_metatype. /// Specializations of the core meta-types. Each enumeration is associated with a specific #type_metatype.
/// Ordering is important: The lower the number, the more \b specific the data-type, affecting propagation. /// Ordering is important: The lower the number, the more \b specific the data-type, affecting propagation.
enum sub_metatype { enum sub_metatype {
SUB_VOID = 21, ///< Compare as a TYPE_VOID SUB_VOID = 22, ///< Compare as a TYPE_VOID
SUB_SPACEBASE = 20, ///< Compare as a TYPE_SPACEBASE SUB_SPACEBASE = 21, ///< Compare as a TYPE_SPACEBASE
SUB_UNKNOWN = 19, ///< Compare as a TYPE_UNKNOWN SUB_UNKNOWN = 20, ///< Compare as a TYPE_UNKNOWN
SUB_INT_CHAR = 18, ///< Signed 1-byte character, sub-type of TYPE_INT SUB_INT_CHAR = 19, ///< Signed 1-byte character, sub-type of TYPE_INT
SUB_UINT_CHAR = 17, ///< Unsigned 1-byte character, sub-type of TYPE_UINT SUB_UINT_CHAR = 18, ///< Unsigned 1-byte character, sub-type of TYPE_UINT
SUB_INT_PLAIN = 16, ///< Compare as a plain TYPE_INT SUB_INT_PLAIN = 17, ///< Compare as a plain TYPE_INT
SUB_UINT_PLAIN = 15, ///< Compare as a plain TYPE_UINT SUB_UINT_PLAIN = 16, ///< Compare as a plain TYPE_UINT
SUB_INT_ENUM = 14, ///< Signed enum, sub-type of TYPE_INT SUB_INT_ENUM = 15, ///< Signed enum, sub-type of TYPE_INT
SUB_UINT_ENUM = 13, ///< Unsigned enum, sub-type of TYPE_UINT SUB_UINT_ENUM = 14, ///< Unsigned enum, sub-type of TYPE_UINT
SUB_INT_UNICODE = 12, ///< Signed wide character, sub-type of TYPE_INT SUB_INT_UNICODE = 13, ///< Signed wide character, sub-type of TYPE_INT
SUB_UINT_UNICODE = 11, ///< Unsigned wide character, sub-type of TYPE_UINT SUB_UINT_UNICODE = 12, ///< Unsigned wide character, sub-type of TYPE_UINT
SUB_BOOL = 10, ///< Compare as TYPE_BOOL SUB_BOOL = 11, ///< Compare as TYPE_BOOL
SUB_CODE = 9, ///< Compare as TYPE_CODE SUB_CODE = 10, ///< Compare as TYPE_CODE
SUB_FLOAT = 8, ///< Compare as TYPE_FLOAT SUB_FLOAT = 9, ///< Compare as TYPE_FLOAT
SUB_PTRREL_UNK = 7, ///< Pointer to unknown field of struct, sub-type of TYPE_PTR SUB_PTRREL_UNK = 8, ///< Pointer to unknown field of struct, sub-type of TYPE_PTR
SUB_PTR = 6, ///< Compare as TYPE_PTR SUB_PTR = 7, ///< Compare as TYPE_PTR
SUB_PTRREL = 5, ///< Pointer relative to another data-type, sub-type of TYPE_PTR SUB_PTRREL = 6, ///< Pointer relative to another data-type, sub-type of TYPE_PTR
SUB_PTR_STRUCT = 4, ///< Pointer into struct, sub-type of TYPE_PTR SUB_PTR_STRUCT = 5, ///< Pointer into struct, sub-type of TYPE_PTR
SUB_ARRAY = 3, ///< Compare as TYPE_ARRAY SUB_ARRAY = 4, ///< Compare as TYPE_ARRAY
SUB_PARTIALSTRUCT = 2, ///< Compare as TYPE_PARTIALSTRUCT SUB_PARTIALSTRUCT = 3, ///< Compare as TYPE_PARTIALSTRUCT
SUB_STRUCT = 1, ///< Compare as TYPE_STRUCT SUB_STRUCT = 2, ///< Compare as TYPE_STRUCT
SUB_UNION = 0 ///< Compare as TYPE_UNION SUB_UNION = 1, ///< Compare as TYPE_UNION
SUB_PARTIALUNION = 0 ///< Compare as a TYPE_PARTIALUNION
}; };
/// Convert type \b meta-type to name /// Convert type \b meta-type to name
extern void metatype2string(type_metatype metatype,string &res); extern void metatype2string(type_metatype metatype,string &res);
@ -128,6 +130,7 @@ class Architecture; // Forward declarations
class PcodeOp; class PcodeOp;
class Scope; class Scope;
class TypeFactory; class TypeFactory;
class TypeField;
struct DatatypeCompare; struct DatatypeCompare;
/// \brief The base datatype class for the decompiler. /// \brief The base datatype class for the decompiler.
@ -135,7 +138,7 @@ struct DatatypeCompare;
/// Used for symbols, function prototypes, type propagation etc. /// Used for symbols, function prototypes, type propagation etc.
class Datatype { class Datatype {
protected: protected:
static sub_metatype base2sub[14]; static sub_metatype base2sub[15];
/// Boolean properties of datatypes /// Boolean properties of datatypes
enum { enum {
coretype = 1, ///< This is a basic type which will never be redefined coretype = 1, ///< This is a basic type which will never be redefined
@ -200,6 +203,7 @@ public:
const string &getName(void) const { return name; } ///< Get the type name const string &getName(void) const { return name; } ///< Get the type name
Datatype *getTypedef(void) const { return typedefImm; } ///< Get the data-type immediately typedefed by \e this (or null) Datatype *getTypedef(void) const { return typedefImm; } ///< Get the data-type immediately typedefed by \e this (or null)
virtual void printRaw(ostream &s) const; ///< Print a description of the type to stream virtual void printRaw(ostream &s) const; ///< Print a description of the type to stream
virtual const TypeField *findTruncation(int4 off,int4 sz,const PcodeOp *op,int4 slot,int4 &newoff) const;
virtual Datatype *getSubType(uintb off,uintb *newoff) const; ///< Recover component data-type one-level down virtual Datatype *getSubType(uintb off,uintb *newoff) const; ///< Recover component data-type one-level down
virtual Datatype *nearestArrayedComponentForward(uintb off,uintb *newoff,int4 *elSize) const; virtual Datatype *nearestArrayedComponentForward(uintb off,uintb *newoff,int4 *elSize) const;
virtual Datatype *nearestArrayedComponentBackward(uintb off,uintb *newoff,int4 *elSize) const; virtual Datatype *nearestArrayedComponentBackward(uintb off,uintb *newoff,int4 *elSize) const;
@ -214,6 +218,7 @@ public:
virtual Datatype *resolveInFlow(PcodeOp *op,int4 slot); ///< Tailor data-type propagation based on Varnode use virtual Datatype *resolveInFlow(PcodeOp *op,int4 slot); ///< Tailor data-type propagation based on Varnode use
virtual Datatype* findResolve(const PcodeOp *op,int4 slot); ///< Find a previously resolved sub-type virtual Datatype* findResolve(const PcodeOp *op,int4 slot); ///< Find a previously resolved sub-type
virtual int4 findCompatibleResolve(Datatype *ct) const; ///< Find a resolution compatible with the given data-type virtual int4 findCompatibleResolve(Datatype *ct) const; ///< Find a resolution compatible with the given data-type
virtual const TypeField *resolveTruncation(int4 offset,PcodeOp *op,int4 slot,int4 &newoff);
int4 typeOrder(const Datatype &op) const { if (this==&op) return 0; return compare(op,10); } ///< Order this with -op- datatype int4 typeOrder(const Datatype &op) const { if (this==&op) return 0; return compare(op,10); } ///< Order this with -op- datatype
int4 typeOrderBool(const Datatype &op) const; ///< Order \b this with -op-, treating \e bool data-type as special int4 typeOrderBool(const Datatype &op) const; ///< Order \b this with -op-, treating \e bool data-type as special
void encodeRef(Encoder &encoder) const; ///< Encode a reference of \b this to a stream void encodeRef(Encoder &encoder) const; ///< Encode a reference of \b this to a stream
@ -434,7 +439,7 @@ public:
TypeStruct(void) : Datatype(0,TYPE_STRUCT) { flags |= type_incomplete; } ///< Construct incomplete/empty TypeStruct TypeStruct(void) : Datatype(0,TYPE_STRUCT) { flags |= type_incomplete; } ///< Construct incomplete/empty TypeStruct
vector<TypeField>::const_iterator beginField(void) const { return field.begin(); } ///< Beginning of fields vector<TypeField>::const_iterator beginField(void) const { return field.begin(); } ///< Beginning of fields
vector<TypeField>::const_iterator endField(void) const { return field.end(); } ///< End of fields vector<TypeField>::const_iterator endField(void) const { return field.end(); } ///< End of fields
const TypeField *resolveTruncation(int4 off,int4 sz,int4 *newoff) const; ///< Get field based on offset virtual const TypeField *findTruncation(int4 off,int4 sz,const PcodeOp *op,int4 slot,int4 &newoff) const;
virtual Datatype *getSubType(uintb off,uintb *newoff) const; virtual Datatype *getSubType(uintb off,uintb *newoff) const;
virtual Datatype *nearestArrayedComponentForward(uintb off,uintb *newoff,int4 *elSize) const; virtual Datatype *nearestArrayedComponentForward(uintb off,uintb *newoff,int4 *elSize) const;
virtual Datatype *nearestArrayedComponentBackward(uintb off,uintb *newoff,int4 *elSize) const; virtual Datatype *nearestArrayedComponentBackward(uintb off,uintb *newoff,int4 *elSize) const;
@ -464,6 +469,7 @@ public:
TypeUnion(const TypeUnion &op); ///< Construct from another TypeUnion TypeUnion(const TypeUnion &op); ///< Construct from another TypeUnion
TypeUnion(void) : Datatype(0,TYPE_UNION) { flags |= (type_incomplete | needs_resolution); } ///< Construct incomplete TypeUnion TypeUnion(void) : Datatype(0,TYPE_UNION) { flags |= (type_incomplete | needs_resolution); } ///< Construct incomplete TypeUnion
const TypeField *getField(int4 i) const { return &field[i]; } ///< Get the i-th field of the union const TypeField *getField(int4 i) const { return &field[i]; } ///< Get the i-th field of the union
virtual const TypeField *findTruncation(int4 offset,int4 sz,const PcodeOp *op,int4 slot,int4 &newoff) const;
// virtual Datatype *getSubType(uintb off,uintb *newoff) const; // virtual Datatype *getSubType(uintb off,uintb *newoff) const;
virtual int4 numDepend(void) const { return field.size(); } virtual int4 numDepend(void) const { return field.size(); }
virtual Datatype *getDepend(int4 index) const { return field[index].type; } virtual Datatype *getDepend(int4 index) const { return field[index].type; }
@ -474,11 +480,43 @@ public:
virtual Datatype *resolveInFlow(PcodeOp *op,int4 slot); virtual Datatype *resolveInFlow(PcodeOp *op,int4 slot);
virtual Datatype* findResolve(const PcodeOp *op,int4 slot); virtual Datatype* findResolve(const PcodeOp *op,int4 slot);
virtual int4 findCompatibleResolve(Datatype *ct) const; virtual int4 findCompatibleResolve(Datatype *ct) const;
const TypeField *resolveTruncation(int4 offset,PcodeOp *op,int4 slot,int4 &newoff); virtual const TypeField *resolveTruncation(int4 offset,PcodeOp *op,int4 slot,int4 &newoff);
const TypeField *findTruncation(int4 offset,const PcodeOp *op,int4 slot,int4 &newoff) const;
}; };
/// The other data, the \b container, is typically a TypeStruct or TypeArray. Even though \b this pointer /// \brief An internal data-type for holding information about a variable's relative position within a union data-type
///
/// This is a data-type that can be assigned to a Varnode offset into a Symbol, where either the Symbol itself or
/// a sub-field is a TypeUnion. In these cases, we know the Varnode is properly contained within a TypeUnion,
/// but the lack of context prevents us from deciding which field of the TypeUnion applies (and possibly
/// the sub-field of the field).
class TypePartialUnion : public Datatype {
protected:
friend class TypeFactory;
Datatype *stripped; ///< The \e undefined data-type to use if a formal data-type is required.
TypeUnion *container; ///< Union data-type containing \b this partial data-type
int4 offset; ///< Offset (in bytes) into the \e container union
public:
TypePartialUnion(const TypePartialUnion &op); ///< Construct from another TypePartialUnion
TypePartialUnion(TypeUnion *contain,int4 off,int4 sz,Datatype *strip); ///< Constructor
TypeUnion *getParentUnion(void) const { return container; } ///< Get the union which \b this is part of
virtual void printRaw(ostream &s) const; ///< Print a description of the type to stream
virtual const TypeField *findTruncation(int4 off,int4 sz,const PcodeOp *op,int4 slot,int4 &newoff) const;
virtual int4 numDepend(void);
virtual Datatype *getDepend(int4 index);
virtual int4 compare(const Datatype &op,int4 level) const;
virtual int4 compareDependency(const Datatype &op) const;
virtual Datatype *clone(void) const { return new TypePartialUnion(*this); }
virtual void encode(Encoder &encoder) const;
virtual Datatype *getStripped(void) const { return stripped; }
virtual Datatype *resolveInFlow(PcodeOp *op,int4 slot);
virtual Datatype* findResolve(const PcodeOp *op,int4 slot);
virtual int4 findCompatibleResolve(Datatype *ct) const;
virtual const TypeField *resolveTruncation(int4 off,PcodeOp *op,int4 slot,int4 &newoff);
};
/// \brief Relative pointer: A pointer with a fixed offset into a specific structure or other data-type
///
/// The other data-type, the \b container, is typically a TypeStruct or TypeArray. Even though \b this pointer
/// does not point directly to the start of the container, it is possible to access the container through \b this, /// does not point directly to the start of the container, it is possible to access the container through \b this,
/// as the distance (the \b offset) to the start of the container is explicitly known. /// as the distance (the \b offset) to the start of the container is explicitly known.
class TypePointerRel : public TypePointer { class TypePointerRel : public TypePointer {
@ -643,6 +681,7 @@ public:
TypeArray *getTypeArray(int4 as,Datatype *ao); ///< Construct an array data-type TypeArray *getTypeArray(int4 as,Datatype *ao); ///< Construct an array data-type
TypeStruct *getTypeStruct(const string &n); ///< Create an (empty) structure TypeStruct *getTypeStruct(const string &n); ///< Create an (empty) structure
TypeUnion *getTypeUnion(const string &n); ///< Create an (empty) union TypeUnion *getTypeUnion(const string &n); ///< Create an (empty) union
TypePartialUnion *getTypePartialUnion(TypeUnion *contain,int4 off,int4 sz); ///< Create a partial union
TypeEnum *getTypeEnum(const string &n); ///< Create an (empty) enumeration TypeEnum *getTypeEnum(const string &n); ///< Create an (empty) enumeration
TypeSpacebase *getTypeSpacebase(AddrSpace *id,const Address &addr); ///< Create a "spacebase" type TypeSpacebase *getTypeSpacebase(AddrSpace *id,const Address &addr); ///< Create a "spacebase" type
TypeCode *getTypeCode(ProtoModel *model,Datatype *outtype, TypeCode *getTypeCode(ProtoModel *model,Datatype *outtype,

View file

@ -1791,7 +1791,18 @@ TypeOpMulti::TypeOpMulti(TypeFactory *t) : TypeOp(t,CPUI_MULTIEQUAL,"?")
Datatype *TypeOpMulti::propagateType(Datatype *alttype,PcodeOp *op,Varnode *invn,Varnode *outvn, Datatype *TypeOpMulti::propagateType(Datatype *alttype,PcodeOp *op,Varnode *invn,Varnode *outvn,
int4 inslot,int4 outslot) int4 inslot,int4 outslot)
{ {
if ((inslot!=-1)&&(outslot!=-1)) return (Datatype *)0; // Must propagate input <-> output if ((inslot!=-1)&&(outslot!=-1)) {
if (invn == outvn && outvn->getTempType()->needsResolution()) {
// If same Varnode occupies two input slots of the MULTIEQUAL
// the second input slot should inherit the resolution of the first
Funcdata *fd = op->getParent()->getFuncdata();
Datatype *unionType = outvn->getTempType();
const ResolvedUnion *res = fd->getUnionField(unionType, op, inslot);
if (res != (const ResolvedUnion *)0)
fd->setUnionField(unionType, op, outslot, *res);
}
return (Datatype *)0; // Must propagate input <-> output
}
Datatype *newtype; Datatype *newtype;
if (invn->isSpacebase()) { if (invn->isSpacebase()) {
AddrSpace *spc = tlst->getArch()->getDefaultDataSpace(); AddrSpace *spc = tlst->getArch()->getDefaultDataSpace();
@ -1939,15 +1950,16 @@ Datatype *TypeOpSubpiece::propagateType(Datatype *alttype,PcodeOp *op,Varnode *i
int4 byteOff; int4 byteOff;
int4 newoff; int4 newoff;
const TypeField *field; const TypeField *field;
if (alttype->getMetatype() == TYPE_UNION) { type_metatype meta = alttype->getMetatype();
if (meta == TYPE_UNION || meta == TYPE_PARTIALUNION) {
// NOTE: We use an artificial slot here to store the field being truncated to // NOTE: We use an artificial slot here to store the field being truncated to
// as the facing data-type for slot 0 is already to the parent (this TYPE_UNION) // as the facing data-type for slot 0 is already to the parent (this TYPE_UNION)
byteOff = computeByteOffsetForComposite(op); byteOff = computeByteOffsetForComposite(op);
field = ((TypeUnion *)alttype)->resolveTruncation(byteOff,op,1,newoff); field = alttype->resolveTruncation(byteOff,op,1,newoff);
} }
else if (alttype->getMetatype() == TYPE_STRUCT) { else if (alttype->getMetatype() == TYPE_STRUCT) {
int4 byteOff = computeByteOffsetForComposite(op); int4 byteOff = computeByteOffsetForComposite(op);
field = ((TypeStruct *)alttype)->resolveTruncation(byteOff, outvn->getSize(), &newoff); field = alttype->findTruncation(byteOff, outvn->getSize(), op, 1, newoff);
} }
else else
return (Datatype *)0; return (Datatype *)0;
@ -1972,22 +1984,13 @@ const TypeField *TypeOpSubpiece::testExtraction(bool useHigh,const PcodeOp *op,D
{ {
const Varnode *vn = op->getIn(0); const Varnode *vn = op->getIn(0);
Datatype *ct = useHigh ? vn->getHigh()->getType() : vn->getType(); Datatype *ct = useHigh ? vn->getHighTypeReadFacing(op) : vn->getTypeReadFacing(op);
if (ct->getMetatype() == TYPE_STRUCT) { type_metatype meta = ct->getMetatype();
if (meta != TYPE_STRUCT && meta != TYPE_UNION && meta != TYPE_PARTIALUNION)
return (const TypeField *)0;
parent = ct; parent = ct;
int4 byteOff = computeByteOffsetForComposite(op); int4 byteOff = computeByteOffsetForComposite(op);
return ((TypeStruct *)ct)->resolveTruncation(byteOff,op->getOut()->getSize(),&offset); return ct->findTruncation(byteOff,op->getOut()->getSize(),op,1,offset); // Use artificial slot
}
else if (ct->getMetatype() == TYPE_UNION) {
const Funcdata *fd = op->getParent()->getFuncdata();
const ResolvedUnion *res = fd->getUnionField(ct, op, 1); // Use artificial slot
if (res != (const ResolvedUnion *)0 && res->getFieldNum() >= 0) {
parent = ct;
offset = 0;
return ((TypeUnion *)ct)->getField(res->getFieldNum());
}
}
return (const TypeField *)0;
} }
/// \brief Compute the byte offset into an assumed composite data-type produced by the given CPUI_SUBPIECE /// \brief Compute the byte offset into an assumed composite data-type produced by the given CPUI_SUBPIECE

View file

@ -39,6 +39,8 @@ ResolvedUnion::ResolvedUnion(Datatype *parent)
ResolvedUnion::ResolvedUnion(Datatype *parent,int4 fldNum,TypeFactory &typegrp) ResolvedUnion::ResolvedUnion(Datatype *parent,int4 fldNum,TypeFactory &typegrp)
{ {
if (parent->getMetatype() == TYPE_PARTIALUNION)
parent = ((TypePartialUnion *)parent)->getParentUnion();
baseType = parent; baseType = parent;
fieldNum = fldNum; fieldNum = fldNum;
lock = false; lock = false;
@ -67,6 +69,8 @@ ResolveEdge::ResolveEdge(const Datatype *parent,const PcodeOp *op,int4 slot)
typeId = ((TypePointer *)parent)->getPtrTo()->getId(); // Strip pointer typeId = ((TypePointer *)parent)->getPtrTo()->getId(); // Strip pointer
encoding += 0x1000; // Encode the fact that a pointer is getting accessed encoding += 0x1000; // Encode the fact that a pointer is getting accessed
} }
else if (parent->getMetatype() == TYPE_PARTIALUNION)
typeId = ((TypePartialUnion *)parent)->getParentUnion()->getId();
else else
typeId = parent->getId(); typeId = parent->getId();
} }

View file

@ -159,7 +159,7 @@ void HighVariable::updateType(void) const
vn = getTypeRepresentative(); vn = getTypeRepresentative();
type = vn->getType(); type = vn->getType();
if (type->hasStripped()) if (type->hasStripped() && type->getMetatype() != TYPE_PARTIALUNION)
type = type->getStripped(); type = type->getStripped();
// Update lock flags // Update lock flags

View file

@ -0,0 +1,47 @@
<decompilertest>
<binaryimage arch="x86:LE:64:default:gcc">
<!--
Contrived examples of varnodes labeled as partial unions
-->
<bytechunk space="ram" offset="0x10068a" readonly="true">
554889e5f30f
1145fcf30f1045fcf30f110578092000
8b05760920005dc3
</bytechunk>
<bytechunk space="ram" offset="0x1006a8" readonly="true">
554889e5897dfc89
75f8837dfc01750b8b45f889055b0920
00eb33837dfc02750e8b45f883c00789
0543092000eb1f837dfc0475198b45f8
890536092000f30f100516010000f30f
110522092000905dc3
</bytechunk>
<bytechunk space="ram" offset="0x100804" readonly="true">
0000003f
</bytechunk>
<symbol space="ram" offset="0x10068a" name="partialunion"/>
<symbol space="ram" offset="0x1006a8" name="partial1"/>
</binaryimage>
<script>
<com>option readonly on</com>
<com>parse line struct astruct { int4 aval1; int4 aval2; int4 aval3; };</com>
<com>parse line struct bstruct { float4 bval1; int4 bval2; float4 bval3; };</com>
<com>parse line union structunion { astruct a; bstruct b; };</com>
<com>parse line extern int4 partialunion(float4 val);</com>
<com>map addr r0x301018 structunion globvar</com>
<com>lo fu partialunion</com>
<com>dec</com>
<com>print C</com>
<com>lo fu partial1</com>
<com>map unionfacet structunion 1 r0x1006ee 20603f6a8a0b89</com>
<com>dec</com>
<com>print C</com>
<com>quit</com>
</script>
<stringmatch name="Partial union #1" min="1" max="1">globvar\.b\.bval1 = val;</stringmatch>
<stringmatch name="Partial union #2" min="1" max="1">return globvar\.a\.aval2;</stringmatch>
<stringmatch name="Partial union #3" min="1" max="1">Var1 = globvar\.a\.aval2;</stringmatch>
<stringmatch name="Partial union #4" min="1" max="1">globvar\.a\.aval1 = param_2 \+ 7;</stringmatch>
<stringmatch name="Partial union #5" min="1" max="1">globvar\.b\.bval1 = 0\.5;</stringmatch>
<stringmatch name="Partial union #6" min="1" max="1">globvar\.a\.aval2 = .Var1;</stringmatch>
</decompilertest>

View file

@ -31,6 +31,7 @@ import ghidra.program.model.lang.*;
import ghidra.program.model.listing.Function; import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.program.model.pcode.*; import ghidra.program.model.pcode.*;
import ghidra.util.Msg;
import ghidra.util.task.CancelledListener; import ghidra.util.task.CancelledListener;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
@ -775,14 +776,9 @@ public class DecompInterface {
AddressXML.encode(activeSet.mainQuery, funcEntry); AddressXML.encode(activeSet.mainQuery, funcEntry);
decompProcess.sendCommandTimeout("decompileAt", timeoutSecs, activeSet); decompProcess.sendCommandTimeout("decompileAt", timeoutSecs, activeSet);
decompileMessage = decompCallback.getNativeMessage(); decompileMessage = decompCallback.getNativeMessage();
if (debug != null) {
XmlEncode xmlEncode = new XmlEncode();
options.encode(xmlEncode, this);
debug.shutdown(pcodelanguage, xmlEncode.toString());
debug = null;
}
} }
catch (Exception ex) { catch (Exception ex) {
decoder.clear(); // Clear any partial result
decompileMessage = "Exception while decompiling " + func.getEntryPoint() + ": " + decompileMessage = "Exception while decompiling " + func.getEntryPoint() + ": " +
ex.getMessage() + '\n'; ex.getMessage() + '\n';
} }
@ -792,6 +788,17 @@ public class DecompInterface {
} }
} }
try {
if (debug != null) {
XmlEncode xmlEncode = new XmlEncode();
options.encode(xmlEncode, this);
debug.shutdown(pcodelanguage, xmlEncode.toString());
debug = null;
}
}
catch (IOException e) {
Msg.error(debug, "Could not dump debug info");
}
DecompileProcess.DisposeState processState; DecompileProcess.DisposeState processState;
if (decompProcess != null) { if (decompProcess != null) {
processState = decompProcess.getDisposeState(); processState = decompProcess.getDisposeState();

View file

@ -93,6 +93,12 @@ public class ForceUnionAction extends AbstractDecompilerAction {
if (innerType instanceof Pointer) { if (innerType instanceof Pointer) {
innerType = ((Pointer) innerType).getDataType(); innerType = ((Pointer) innerType).getDataType();
} }
else if (innerType instanceof PartialUnion) {
innerType = ((PartialUnion) innerType).getParent();
if (innerType instanceof TypeDef) {
innerType = ((TypeDef) innerType).getBaseDataType();
}
}
if (innerType == unionDt) { if (innerType == unionDt) {
return dt; return dt;
} }
@ -168,17 +174,26 @@ public class ForceUnionAction extends AbstractDecompilerAction {
/** /**
* Build a list of all the union field names for the user to select from, when determining * Build a list of all the union field names for the user to select from, when determining
* which data-type to force. Two lists are produced. The first contains every possible * which data-type to force. Two lists are produced. The first contains every possible
* field name. The second list is filtered by the size of the Varnode being forced, * field name. The second list is filtered by the size and offset of the Varnode being forced.
* which must match the size of the selected field data-type.
* @param allFields will hold the unfiltered list of names * @param allFields will hold the unfiltered list of names
* @param size is the size of the Varnode to filter on
* @return the filtered list of names * @return the filtered list of names
*/ */
private String[] buildFieldOptions(ArrayList<String> allFields, int size) { private String[] buildFieldOptions(ArrayList<String> allFields) {
int size = accessVn.getSize();
int startOff = 0;
boolean exactMatch = true;
if (parentDt instanceof Pointer) {
size = 0;
}
if (parentDt instanceof PartialUnion) {
startOff = ((PartialUnion) parentDt).getOffset();
exactMatch = false;
}
int endOff = startOff + size;
DataTypeComponent[] components = unionDt.getDefinedComponents(); DataTypeComponent[] components = unionDt.getDefinedComponents();
ArrayList<String> res = new ArrayList<>(); ArrayList<String> res = new ArrayList<>();
allFields.add("(no field)"); allFields.add("(no field)");
if (size == 0 || unionDt.getLength() == size) { if (size == 0 || !exactMatch || size == parentDt.getLength()) {
res.add("(no field)"); res.add("(no field)");
} }
for (DataTypeComponent component : components) { for (DataTypeComponent component : components) {
@ -187,7 +202,11 @@ public class ForceUnionAction extends AbstractDecompilerAction {
nm = component.getDefaultFieldName(); nm = component.getDefaultFieldName();
} }
allFields.add(nm); allFields.add(nm);
if (size == 0 || component.getDataType().getLength() == size) { int compStart = component.getOffset();
int compEnd = compStart + component.getLength();
if (size == 0 || (exactMatch && startOff == compStart && endOff == compEnd) ||
(!exactMatch && startOff >= compStart && endOff <= compEnd)) {
res.add(nm); res.add(nm);
} }
} }
@ -206,12 +225,8 @@ public class ForceUnionAction extends AbstractDecompilerAction {
* @return the index of the selected field or -1 if "no field" was selected * @return the index of the selected field or -1 if "no field" was selected
*/ */
private boolean selectFieldNumber(String defaultFieldName) { private boolean selectFieldNumber(String defaultFieldName) {
int size = 0;
if (!(parentDt instanceof Pointer)) {
size = accessVn.getSize();
}
ArrayList<String> allFields = new ArrayList<>(); ArrayList<String> allFields = new ArrayList<>();
String[] choices = buildFieldOptions(allFields, size); String[] choices = buildFieldOptions(allFields);
if (choices.length < 2) { // If only one field fits the Varnode if (choices.length < 2) { // If only one field fits the Varnode
OkDialog.show("No Field Choices", "Only one field fits the selected variable"); OkDialog.show("No Field Choices", "Only one field fits the selected variable");
return false; return false;

View file

@ -742,7 +742,7 @@ public class HighFunctionDBUtil {
* pieces for building the dynamic LocalVariable. This method clears out any preexisting * pieces for building the dynamic LocalVariable. This method clears out any preexisting
* union facet with the same dynamic hash and firstUseOffset. * union facet with the same dynamic hash and firstUseOffset.
* @param function is the function affected by the union facet * @param function is the function affected by the union facet
* @param dt is the parent data-type, either the union or a pointer to it * @param dt is the parent data-type; a union, a pointer to a union, or a partial union
* @param fieldNum is the ordinal of the desired union field * @param fieldNum is the ordinal of the desired union field
* @param addr is the first use address of the facet * @param addr is the first use address of the facet
* @param hash is the dynamic hash * @param hash is the dynamic hash
@ -752,6 +752,9 @@ public class HighFunctionDBUtil {
*/ */
public static void writeUnionFacet(Function function, DataType dt, int fieldNum, Address addr, public static void writeUnionFacet(Function function, DataType dt, int fieldNum, Address addr,
long hash, SourceType source) throws InvalidInputException, DuplicateNameException { long hash, SourceType source) throws InvalidInputException, DuplicateNameException {
if (dt instanceof PartialUnion) {
dt = ((PartialUnion) dt).getParent();
}
int firstUseOffset = (int) addr.subtract(function.getEntryPoint()); int firstUseOffset = (int) addr.subtract(function.getEntryPoint());
String symbolName = UnionFacetSymbol.buildSymbolName(fieldNum, addr); String symbolName = UnionFacetSymbol.buildSymbolName(fieldNum, addr);
boolean nameCollision = false; boolean nameCollision = false;

View file

@ -0,0 +1,122 @@
/* ###
* 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.
*/
package ghidra.program.model.pcode;
import javax.help.UnsupportedOperationException;
import ghidra.docking.settings.Settings;
import ghidra.docking.settings.SettingsDefinition;
import ghidra.program.model.data.*;
import ghidra.program.model.mem.MemBuffer;
/**
* A data-type representing an unspecified piece of a parent Union data-type. This is used
* internally by the decompiler to label Varnodes representing partial symbols, where the
* part is known to be contained in a Union data-type. Within the isolated context of a Varnode,
* its not possible to resolve to a specific field of the Union because the Varnode may be used
* in multiple ways.
*/
public class PartialUnion extends AbstractDataType {
private DataType unionDataType; // Either a Union or a Typedef of a Union
private int offset; // Offset in bytes of partial within parent
private int size; // Number of bytes in partial
PartialUnion(DataTypeManager dtm, DataType parent, int off, int sz) {
super(CategoryPath.ROOT, "partialunion", dtm);
unionDataType = parent;
offset = off;
size = sz;
}
/**
* @return the Union data-type of which this is a part
*/
public DataType getParent() {
return unionDataType;
}
/**
* @return the offset, in bytes, of this part within its parent Union
*/
public int getOffset() {
return offset;
}
@Override
public DataType clone(DataTypeManager dtm) {
// Internal to the PcodeDataTypeManager
throw new UnsupportedOperationException("may not be cloned");
}
@Override
public int getLength() {
return size;
}
@Override
public String getDescription() {
return "Partial Union (internal)";
}
@Override
public Object getValue(MemBuffer buf, Settings settings, int length) {
return null; // Should not be placed on memory
}
@Override
public String getRepresentation(MemBuffer buf, Settings settings, int length) {
return null; // Should not be placed on memory
}
@Override
public SettingsDefinition[] getSettingsDefinitions() {
return unionDataType.getSettingsDefinitions();
}
@Override
public Settings getDefaultSettings() {
return unionDataType.getDefaultSettings();
}
@Override
public DataType copy(DataTypeManager dtm) {
// Internal to the PcodeDataTypeManager
throw new UnsupportedOperationException("may not be copied");
}
@Override
public Class<?> getValueClass(Settings settings) {
return unionDataType.getValueClass(settings);
}
@Override
public boolean isEquivalent(DataType dt) {
if (dt == null || !(dt instanceof PartialUnion)) {
return false;
}
PartialUnion op = (PartialUnion) dt;
if (offset != op.offset || size != op.size) {
return false;
}
return unionDataType.isEquivalent(op.unionDataType);
}
@Override
public int getAlignment() {
return 0;
}
}

View file

@ -251,6 +251,13 @@ public class PcodeDataTypeManager {
decoder.closeElement(el); decoder.closeElement(el);
return AbstractFloatDataType.getFloatDataType(size, progDataTypes); return AbstractFloatDataType.getFloatDataType(size, progDataTypes);
} }
else if (meta.equals("partunion")) {
int size = (int) decoder.readSignedInteger(ATTRIB_SIZE);
int offset = (int) decoder.readSignedInteger(ATTRIB_OFFSET);
DataType dt = decodeDataType(decoder);
decoder.closeElement(el);
return new PartialUnion(progDataTypes, dt, offset, size);
}
else { // We typically reach here if the decompiler invents a new type else { // We typically reach here if the decompiler invents a new type
// probably an unknown with a non-standard size // probably an unknown with a non-standard size
int size = (int) decoder.readSignedInteger(ATTRIB_SIZE); int size = (int) decoder.readSignedInteger(ATTRIB_SIZE);