Merge remote-tracking branch

'origin/GP-1954_win_x64_cspec_register_grouping' (Closes #2952,
Closes #1480)
This commit is contained in:
Ryan Kurtz 2022-05-13 01:08:25 -04:00
commit 4b600847eb
15 changed files with 767 additions and 230 deletions

View File

@ -25,6 +25,7 @@ src/decompile/datatests/forloop_withskip.xml||GHIDRA||||END|
src/decompile/datatests/impliedfield.xml||GHIDRA||||END|
src/decompile/datatests/indproto.xml||GHIDRA||||END|
src/decompile/datatests/loopcomment.xml||GHIDRA||||END|
src/decompile/datatests/mixfloatint.xml||GHIDRA||||END|
src/decompile/datatests/multiret.xml||GHIDRA||||END|
src/decompile/datatests/namespace.xml||GHIDRA||||END|
src/decompile/datatests/nestedoffset.xml||GHIDRA||||END|

View File

@ -1865,7 +1865,7 @@ int4 ActionReturnRecovery::apply(Funcdata &data)
int4 slot = trial.getSlot();
vn = op->getIn(slot);
if (ancestorReal.execute(op,slot,&trial,false))
if (data.ancestorOpUse(maxancestor,vn,op,trial,0))
if (data.ancestorOpUse(maxancestor,vn,op,trial,0,0))
trial.markActive(); // This varnode sees active use as a parameter
count += 1;
}

View File

@ -571,33 +571,38 @@ int4 ParamListStandard::characterizeAsParam(const Address &loc,int4 size) const
{
int4 index = loc.getSpace()->getIndex();
if (index >= resolverMap.size())
return 0;
return ParamEntry::no_containment;
ParamEntryResolver *resolver = resolverMap[index];
if (resolver == (ParamEntryResolver *)0)
return 0;
return ParamEntry::no_containment;
pair<ParamEntryResolver::const_iterator,ParamEntryResolver::const_iterator> iterpair;
iterpair = resolver->find(loc.getOffset());
int4 res = 0;
bool resContains = false;
bool resContainedBy = false;
while(iterpair.first != iterpair.second) {
const ParamEntry *testEntry = (*iterpair.first).getParamEntry();
if (testEntry->getMinSize() <= size && testEntry->justifiedContain(loc, size)==0)
return 1;
int4 off = testEntry->justifiedContain(loc, size);
if (off == 0)
return ParamEntry::contains_justified;
else if (off > 0)
resContains = true;
if (testEntry->isExclusion() && testEntry->containedBy(loc, size))
res = 2;
resContainedBy = true;
++iterpair.first;
}
if (res != 2 && iterpair.first != resolver->end()) {
if (resContains) return ParamEntry::contains_unjustified;
if (resContainedBy) return ParamEntry::contained_by;
if (iterpair.first != resolver->end()) {
iterpair.second = resolver->find_end(loc.getOffset() + (size-1));
while(iterpair.first != iterpair.second) {
const ParamEntry *testEntry = (*iterpair.first).getParamEntry();
if (testEntry->isExclusion() && testEntry->containedBy(loc, size)) {
res = 2;
break;
return ParamEntry::contained_by;
}
++iterpair.first;
}
}
return res;
return ParamEntry::no_containment;
}
/// Given the next data-type and the status of previously allocated slots,
@ -664,6 +669,34 @@ void ParamListStandard::assignMap(const vector<Datatype *> &proto,TypeFactory &t
}
}
/// From among the ParamEntrys matching the given \e group, return the one that best matches
/// the given \e metatype attribute. If there are no ParamEntrys in the group, null is returned.
/// \param grp is the given \e group number
/// \param prefType is the preferred \e metatype attribute to match
const ParamEntry *ParamListStandard::selectUnreferenceEntry(int4 grp,type_metatype prefType) const
{
int4 bestScore = -1;
const ParamEntry *bestEntry = (const ParamEntry *)0;
list<ParamEntry>::const_iterator iter;
for(iter=entry.begin();iter!=entry.end();++iter) {
const ParamEntry *curEntry = &(*iter);
if (curEntry->getGroup() != grp) continue;
int4 curScore;
if (curEntry->getType() == prefType)
curScore = 2;
else if (prefType == TYPE_UNKNOWN)
curScore = 1;
else
curScore = 0;
if (curScore > bestScore) {
bestScore = curScore;
bestEntry = curEntry;
}
}
return bestEntry;
}
/// Given a set of \b trials (putative Varnode parameters) as ParamTrial objects,
/// associate each trial with a model ParamEntry within \b this list. Trials for
/// for which there are no matching entries are marked as unused. Any holes
@ -673,8 +706,8 @@ void ParamListStandard::buildTrialMap(ParamActive *active) const
{
vector<const ParamEntry *> hitlist; // List of groups for which we have a representative
bool seenfloattrial = false;
bool seeninttrial = false;
int4 floatCount = 0;
int4 intCount = 0;
for(int4 i=0;i<active->getNumTrials();++i) {
ParamTrial &paramtrial(active->getTrial(i));
@ -686,10 +719,12 @@ void ParamListStandard::buildTrialMap(ParamActive *active) const
else {
paramtrial.setEntry( entrySlot, 0 ); // Keep track of entry recovered for this trial
if (entrySlot->getType() == TYPE_FLOAT)
seenfloattrial = true;
else
seeninttrial = true;
if (paramtrial.isActive()) {
if (entrySlot->getType() == TYPE_FLOAT)
floatCount += 1;
else
intCount += 1;
}
// Make sure we list that the entries group is marked
int4 grp = entrySlot->getGroup();
@ -701,22 +736,16 @@ void ParamListStandard::buildTrialMap(ParamActive *active) const
}
}
// Created unreferenced (unref) ParamTrial for any group that we don't have a representive for
// Created unreferenced (unref) ParamTrial for any group that we don't have a representative for
// if that group occurs before one where we do have a representative
for(int4 i=0;i<hitlist.size();++i) {
const ParamEntry *curentry = hitlist[i];
if (curentry == (const ParamEntry *)0) {
list<ParamEntry>::const_iterator iter;
for(iter=entry.begin();iter!=entry.end();++iter) {
curentry = &(*iter);
if (curentry->getGroup() == i) break; // Find first entry of the missing group
}
if ((!seenfloattrial)&&(curentry->getType()==TYPE_FLOAT))
continue; // Don't fill in unreferenced floats if we haven't seen any floats
if ((!seeninttrial)&&(curentry->getType()!=TYPE_FLOAT))
continue; // Don't fill in unreferenced int if all we have seen is floats
curentry = selectUnreferenceEntry(i, (floatCount > intCount) ? TYPE_FLOAT : TYPE_UNKNOWN);
if (curentry == (const ParamEntry *)0)
continue;
int4 sz = curentry->isExclusion() ? curentry->getSize() : curentry->getAlign();
int4 nextslot = 0;
Address addr = curentry->getAddrBySlot(nextslot,sz);
@ -798,30 +827,98 @@ void ParamListStandard::separateSections(ParamActive *active,int4 &oneStart,int4
twoStop = numtrials;
}
/// \brief Mark all the trials within the indicated groups as \e not \e used, except for one specified index
///
/// Only one trial within an exclusion group can have active use, mark all others as unused.
/// \param active is the set of trials, which must be sorted on group
/// \param groupUper is the biggest group number to be marked
/// \param groupStart is the index of the first trial in the smallest group to be marked
/// \param index is the specified trial index that is \e not to be marked
void ParamListStandard::markGroupNoUse(ParamActive *active,int4 groupUpper,int4 groupStart,int4 index)
{
int4 numTrials = active->getNumTrials();
for(int4 i=groupStart;i<numTrials;++i) { // Mark entries in the group range as definitely not used
if (i == index) continue; // The trial NOT to mark
ParamTrial &othertrial(active->getTrial(i));
if (othertrial.isDefinitelyNotUsed()) continue;
if (othertrial.getEntry()->getGroup() > groupUpper) break;
othertrial.markNoUse();
}
}
/// \brief From among multiple \e inactive trials, select the most likely to be active and mark others as not used
///
/// There can be at most one \e inactive trial in an exclusion group for the fill algorithms to work.
/// Score all the trials and pick the one that is the most likely to actually be an active param.
/// Mark all the others as definitely not used.
/// \param active is the sorted set of trials
/// \param group is the group number
/// \param groupStart is the index of the first trial in the group
/// \param prefType is a preferred entry to type to use in scoring
void ParamListStandard::markBestInactive(ParamActive *active,int4 group,int4 groupStart,type_metatype prefType)
{
int4 numTrials = active->getNumTrials();
int4 bestTrial = -1;
int4 bestScore = -1;
for(int4 i=groupStart;i<numTrials;++i) {
ParamTrial &trial(active->getTrial(i));
if (trial.isDefinitelyNotUsed()) continue;
const ParamEntry *entry = trial.getEntry();
int4 grp = entry->getGroup();
if (grp != group) break;
if (entry->getGroupSize() > 1) continue; // Covering multiple slots automatically give low score
int4 score = 0;
if (trial.hasAncestorRealistic()) {
score += 5;
if (trial.hasAncestorSolid())
score += 5;
}
if (entry->getType() == prefType)
score += 1;
if (score > bestScore) {
bestScore = score;
bestTrial = i;
}
}
if (bestTrial >= 0)
markGroupNoUse(active, group, groupStart, bestTrial);
}
/// \brief Enforce exclusion rules for the given set of parameter trials
///
/// If there are more than one active trials in a single group,
/// and if that group is an exclusion group, mark all but the first trial to \e inactive.
/// and if that group is an exclusion group, mark all but the first trial to \e defnouse.
/// \param active is the set of trials
void ParamListStandard::forceExclusionGroup(ParamActive *active) const
void ParamListStandard::forceExclusionGroup(ParamActive *active)
{
int4 curupper = -1;
bool exclusion = false;
int4 numtrials = active->getNumTrials();
for(int4 i=0;i<numtrials;++i) {
int4 numTrials = active->getNumTrials();
int4 curGroup = -1;
int4 groupStart = -1;
int4 inactiveCount = 0;
for(int4 i=0;i<numTrials;++i) {
ParamTrial &curtrial(active->getTrial(i));
if (curtrial.isActive()) {
int4 grp = curtrial.getEntry()->getGroup();
exclusion = curtrial.getEntry()->isExclusion();
if (grp <= curupper) { // If curtrial's group falls below highest group where we have seen an active
if (exclusion)
curtrial.markInactive(); // mark inactive if it is an exclusion group
}
else
curupper = grp + curtrial.getEntry()->getGroupSize() - 1; // This entry covers some number of groups
int4 grp = curtrial.getEntry()->getGroup();
if (grp != curGroup) {
if (inactiveCount > 1)
markBestInactive(active, curGroup, groupStart, TYPE_UNKNOWN);
curGroup = grp;
groupStart = i;
inactiveCount = 0;
}
if (curtrial.isDefinitelyNotUsed() || !curtrial.getEntry()->isExclusion())
continue;
else if (!curtrial.isActive())
inactiveCount += 1;
else if (curtrial.isActive()) {
int4 groupUpper = grp + curtrial.getEntry()->getGroupSize() - 1; // This entry covers some number of groups
markGroupNoUse(active, groupUpper, groupStart, i);
}
}
if (inactiveCount > 1)
markBestInactive(active, curGroup, groupStart, TYPE_UNKNOWN);
}
/// \brief Mark every trial above the first "definitely not used" as \e inactive.
@ -831,7 +928,7 @@ void ParamListStandard::forceExclusionGroup(ParamActive *active) const
/// \param active is the set of trials, which must already be ordered
/// \param start is the index of the first trial in the range to consider
/// \param stop is the index (+1) of the last trial in the range to consider
void ParamListStandard::forceNoUse(ParamActive *active, int4 start, int4 stop) const
void ParamListStandard::forceNoUse(ParamActive *active, int4 start, int4 stop)
{
bool seendefnouse = false;
@ -873,7 +970,7 @@ void ParamListStandard::forceNoUse(ParamActive *active, int4 start, int4 stop) c
/// \param start is the first index in the range of trials to consider
/// \param stop is the last index (+1) in the range of trials to consider
/// \param groupstart is the smallest group id in the particular section
void ParamListStandard::forceInactiveChain(ParamActive *active,int4 maxchain,int4 start,int4 stop,int4 groupstart) const
void ParamListStandard::forceInactiveChain(ParamActive *active,int4 maxchain,int4 start,int4 stop,int4 groupstart)
{
bool seenchain = false;
@ -881,7 +978,7 @@ void ParamListStandard::forceInactiveChain(ParamActive *active,int4 maxchain,int
int4 max = -1;
for(int4 i=start;i<stop;++i) {
ParamTrial &trial(active->getTrial(i));
if (trial.getEntry() == (const ParamEntry *)0) continue; // Already know not used
if (trial.isDefinitelyNotUsed()) continue; // Already know not used
if (!trial.isActive()) {
if (trial.isUnref()&&active->isRecoverSubcall()) {
// If there is no reference to the trial within the function, the only real possibility
@ -927,32 +1024,56 @@ void ParamListStandard::calcDelay(void)
}
}
/// \brief Internal method for adding a single address range to the ParamEntryResolvers
///
/// Specify the contiguous address range, the ParamEntry to map to it, and a position recording
/// the order in which ranges are added.
/// \param spc is address space of the memory range
/// \param first is the starting offset of the memory range
/// \param last is the ending offset of the memory range
/// \param paramEntry is the ParamEntry to associate with the memory range
/// \param position is the ordering position
void ParamListStandard::addResolverRange(AddrSpace *spc,uintb first,uintb last,ParamEntry *paramEntry,int4 position)
{
int4 index = spc->getIndex();
while(resolverMap.size() <= index) {
resolverMap.push_back((ParamEntryResolver *)0);
}
ParamEntryResolver *resolver = resolverMap[index];
if (resolver == (ParamEntryResolver *)0) {
resolver = new ParamEntryResolver();
resolverMap[spc->getIndex()] = resolver;
}
ParamEntryResolver::inittype initData(position,paramEntry);
resolver->insert(initData,first,last);
}
/// Enter all the ParamEntry objects into an interval map (based on address space)
void ParamListStandard::populateResolver(void)
{
int4 maxid = -1;
list<ParamEntry>::iterator iter;
for(iter=entry.begin();iter!=entry.end();++iter) {
int4 id = (*iter).getSpace()->getIndex();
if (id > maxid)
maxid = id;
}
resolverMap.resize(maxid+1, (ParamEntryResolver *)0);
int4 position = 0;
for(iter=entry.begin();iter!=entry.end();++iter) {
ParamEntry *paramEntry = &(*iter);
int4 spaceId = paramEntry->getSpace()->getIndex();
ParamEntryResolver *resolver = resolverMap[spaceId];
if (resolver == (ParamEntryResolver *)0) {
resolver = new ParamEntryResolver();
resolverMap[spaceId] = resolver;
AddrSpace *spc = paramEntry->getSpace();
if (spc->getType() == IPTR_JOIN) {
JoinRecord *joinRec = paramEntry->getJoinRecord();
for(int4 i=0;i<joinRec->numPieces();++i) {
// Individual pieces making up the join are mapped to the ParamEntry
const VarnodeData &vData(joinRec->getPiece(i));
uintb last = vData.offset + (vData.size - 1);
addResolverRange(vData.space,vData.offset,last,paramEntry,position);
position += 1;
}
}
else {
uintb first = paramEntry->getBase();
uintb last = first + (paramEntry->getSize() - 1);
addResolverRange(spc,first,last,paramEntry,position);
position += 1;
}
uintb first = paramEntry->getBase();
uintb last = first + (paramEntry->getSize() - 1);
ParamEntryResolver::inittype initData(position,paramEntry);
position += 1;
resolver->insert(initData,first,last);
}
}
@ -972,7 +1093,7 @@ void ParamListStandard::parsePentry(const Element *el,const AddrSpaceManager *ma
entry.emplace_back(groupid);
entry.back().restoreXml(el,manage,normalstack,grouped,entry);
if (splitFloat) {
if (entry.back().getType() == TYPE_FLOAT) {
if (!grouped && entry.back().getType() == TYPE_FLOAT) {
if (resourceTwoStart >= 0)
throw LowlevelError("parameter list floating-point entries must come first");
}
@ -3837,9 +3958,10 @@ vector<VarnodeData>::const_iterator FuncProto::trashEnd(void) const
/// If the input is locked, check if the location overlaps one of the current parameters.
/// Otherwise, check if the location overlaps an entry in the prototype model.
/// Return:
/// - 0 if the location neither contains or is contained by a parameter storage location
/// - 1 if the location is contained by a parameter storage location
/// - 2 if the location contains a parameter storage location
/// - no_containment - there is no containment between the range and any input parameter
/// - contains_unjustified - at least one parameter contains the range
/// - contains_justified - at least one parameter contains this range as its least significant bytes
/// - contained_by - no parameter contains this range, but the range contains at least one parameter
/// \param addr is the starting address of the given storage location
/// \param size is the number of bytes in the storage
/// \return the characterization code
@ -3851,7 +3973,8 @@ int4 FuncProto::characterizeAsInputParam(const Address &addr,int4 size) const
int4 num = numParams();
if (num > 0) {
bool locktest = false; // Have tested against locked symbol
int4 characterCode = 0;
bool resContains = false;
bool resContainedBy = false;
for(int4 i=0;i<num;++i) {
ProtoParameter *param = getParam(i);
if (!param->isTypeLocked()) continue;
@ -3859,17 +3982,58 @@ int4 FuncProto::characterizeAsInputParam(const Address &addr,int4 size) const
Address iaddr = param->getAddress();
// If the parameter already exists, the varnode must be justified in the parameter relative
// to the endianness of the space, irregardless of the forceleft flag
if (iaddr.justifiedContain(param->getSize(),addr,size,false)==0)
return 1;
int4 off = iaddr.justifiedContain(param->getSize(), addr, size, false);
if (off == 0)
return ParamEntry::contains_justified;
else if (off > 0)
resContains = true;
if (iaddr.containedBy(param->getSize(), addr, size))
characterCode = 2;
resContainedBy = true;
}
if (locktest) {
if (resContains) return ParamEntry::contains_unjustified;
if (resContainedBy) return ParamEntry::contained_by;
return ParamEntry::no_containment;
}
if (locktest) return characterCode;
}
}
return model->characterizeAsInputParam(addr, size);
}
/// \brief Decide whether a given storage location could be, or could hold, the return value
///
/// If the output is locked, check if the location overlaps the current return storage.
/// Otherwise, check if the location overlaps an entry in the prototype model.
/// Return:
/// - no_containment - there is no containment between the range and any output storage
/// - contains_unjustified - at least one output storage contains the range
/// - contains_justified - at least one output storage contains this range as its least significant bytes
/// - contained_by - no output storage contains this range, but the range contains at least one output storage
/// \param addr is the starting address of the given storage location
/// \param size is the number of bytes in the storage
/// \return the characterization code
int4 FuncProto::characterizeAsOutput(const Address &addr,int4 size) const
{
if (isOutputLocked()) {
ProtoParameter *outparam = getOutput();
if (outparam->getType()->getMetatype() == TYPE_VOID)
return ParamEntry::no_containment;
Address iaddr = outparam->getAddress();
// If the output is locked, the varnode must be justified in the location relative
// to the endianness of the space, irregardless of the forceleft flag
int4 off = iaddr.justifiedContain(outparam->getSize(),addr,size,false);
if (off == 0)
return ParamEntry::contains_justified;
else if (off > 0)
return ParamEntry::contains_unjustified;
if (iaddr.containedBy(outparam->getSize(),addr,size))
return ParamEntry::contained_by;
return ParamEntry::no_containment;
}
return model->characterizeAsOutput(addr, size);
}
/// \brief Decide whether a given storage location could be an input parameter
///
/// If the input is locked, check if the location matches one of the current parameters.
@ -3967,8 +4131,6 @@ bool FuncProto::unjustifiedInputParam(const Address &addr,int4 size,VarnodeData
return model->unjustifiedInputParam(addr,size,res);
}
/// \brief Pass-back the biggest input parameter contained within the given range
///
/// \param loc is the starting address of the given range
/// \param size is the number of bytes in the range
/// \param res will hold the parameter storage description being passed back
@ -4002,6 +4164,29 @@ bool FuncProto::getBiggestContainedInputParam(const Address &loc,int4 size,Varno
return model->getBiggestContainedInputParam(loc,size,res);
}
/// \param loc is the starting address of the given range
/// \param size is the number of bytes in the range
/// \param res will hold the output storage description being passed back
/// \return \b true if there is at least one possible output contained in the range
bool FuncProto::getBiggestContainedOutput(const Address &loc,int4 size,VarnodeData &res) const
{
if (isOutputLocked()) {
ProtoParameter *outparam = getOutput();
if (outparam->getType()->getMetatype() == TYPE_VOID)
return false;
Address iaddr = outparam->getAddress();
if (iaddr.containedBy(outparam->getSize(), loc, size)) {
res.space = iaddr.getSpace();
res.offset = iaddr.getOffset();
res.size = outparam->getSize();
return true;
}
return false;
}
return model->getBiggestContainedOutput(loc,size,res);
}
/// \brief Decide if \b this can be safely restricted to match another prototype
///
/// Do \b this and another given function prototype share enough of
@ -5043,7 +5228,7 @@ void FuncCallSpecs::checkInputTrialUse(Funcdata &data,AliasChecker &aliascheck)
trial.markNoUse();
}
else if (ancestorReal.execute(op,slot,&trial,false)) {
if (data.ancestorOpUse(maxancestor,vn,op,trial,0))
if (data.ancestorOpUse(maxancestor,vn,op,trial,0,0))
trial.markActive();
else
trial.markInactive();
@ -5053,7 +5238,7 @@ void FuncCallSpecs::checkInputTrialUse(Funcdata &data,AliasChecker &aliascheck)
}
else {
if (ancestorReal.execute(op,slot,&trial,true)) {
if (data.ancestorOpUse(maxancestor,vn,op,trial,0)) {
if (data.ancestorOpUse(maxancestor,vn,op,trial,0,0)) {
trial.markActive();
if (trial.hasCondExeEffect())
activeinput.markNeedsFinalCheck();

View File

@ -60,6 +60,12 @@ public:
is_grouped = 512, ///< This entry is grouped with other entries
overlapping = 0x100 ///< Overlaps an earlier entry (and doesn't consume additional resource slots)
};
enum {
no_containment, ///< Range neither contains nor is contained by a ParamEntry
contains_unjustified, ///< ParamEntry contains range, but the range does not cover the least significant bytes
contains_justified, ///< ParamEntry contains range, which covers the least significant bytes
contained_by ///< ParamEntry is contained by the range
};
private:
uint4 flags; ///< Boolean properties of the parameter
type_metatype type; ///< Data-type class that this entry must match
@ -85,6 +91,7 @@ public:
int4 getSize(void) const { return size; } ///< Get the size of the memory range in bytes.
int4 getMinSize(void) const { return minsize; } ///< Get the minimum size of a logical value contained in \b this
int4 getAlign(void) const { return alignment; } ///< Get the alignment of \b this entry
JoinRecord *getJoinRecord(void) const { return joinrec; }
type_metatype getType(void) const { return type; } ///< Get the data-type class associated with \b this
bool isExclusion(void) const { return (alignment==0); } ///< Return \b true if this holds a single parameter exclusively
bool isReverseStack(void) const { return ((flags & reverse_stack)!=0); } ///< Return \b true if parameters are allocated in reverse order
@ -167,11 +174,13 @@ public:
used = 2, ///< Trial is definitely used (final verdict)
defnouse = 4, ///< Trial is definitely not used
active = 8, ///< Trial looks active (hint that it is used)
unref = 16, ///< There is no direct reference to this parameter trial
killedbycall = 32, ///< Data in this location is unlikely to flow thru a func and still be a param
rem_formed = 64, ///< The trial is built out of a remainder operation
indcreate_formed = 128, ///< The trial is built out of an indirect creation
condexe_effect = 256 ///< This trial may be affected by conditional execution
unref = 0x10, ///< There is no direct reference to this parameter trial
killedbycall = 0x20, ///< Data in this location is unlikely to flow thru a func and still be a param
rem_formed = 0x40, ///< The trial is built out of a remainder operation
indcreate_formed = 0x80, ///< The trial is built out of an indirect creation
condexe_effect = 0x100, ///< This trial may be affected by conditional execution
ancestor_realistic = 0x200, ///< Trial has a realistic ancestor
ancestor_solid = 0x400 ///< Solid movement into the Varnode
};
private:
uint4 flags; ///< Boolean properties of the trial
@ -208,6 +217,10 @@ public:
bool isIndCreateFormed(void) const { return ((flags & indcreate_formed)!=0); } ///< Is \b this trial formed by \e indirect \e creation
void setCondExeEffect(void) { flags |= condexe_effect; } ///< Mark \b this trial as possibly affected by conditional execution
bool hasCondExeEffect(void) const { return ((flags & condexe_effect)!=0); } ///< Is \b this trial possibly affected by conditional execution
void setAncestorRealistic(void) { flags |= ancestor_realistic; } ///< Mark \b this as having a realistic ancestor
bool hasAncestorRealistic(void) const { return ((flags & ancestor_realistic)!=0); } ///< Does \b this have a realistic ancestor
void setAncestorSolid(void) { flags |= ancestor_solid; } ///< Mark \b this as showing solid movement into Varnode
bool hasAncestorSolid(void) const { return ((flags & ancestor_solid)!=0); } ///< Does \b this show solid movement into Varnode
int4 slotGroup(void) const { return entry->getSlot(addr,size-1); } ///< Get position of \b this within its parameter \e group
void setAddress(const Address &ad,int4 sz) { addr=ad; size=sz; } ///< Reset the memory range of \b this trial
ParamTrial splitHi(int4 sz) const; ///< Create a trial representing the first part of \b this
@ -403,10 +416,11 @@ public:
/// \brief Characterize whether the given range overlaps parameter storage
///
/// Does the range naturally fit inside a potential parameter entry from this list or does
/// it contain a parameter entry. Return one of three values indicating this characterization:
/// - 0 means there is no intersection between the range and any parameter in this list
/// - 1 means that at least one parameter contains the range in a properly justified manner
/// - 2 means no parameter contains the range, but the range contains at least one ParamEntry
/// it contain a parameter entry. Return one of four enumerations indicating this characterization:
/// - no_containment - there is no containment between the range and any parameter in this list
/// - contains_unjustified - at least one parameter contains the range
/// - contains_justified - at least one parameter contains this range as its least significant bytes
/// - contained_by - no parameter contains this range, but the range contains at least one parameter
/// \param loc is the starting address of the given range
/// \param size is the number of bytes in the given range
/// \return the characterization code
@ -514,12 +528,16 @@ protected:
AddrSpace *spacebase; ///< Address space containing relative offset parameters
const ParamEntry *findEntry(const Address &loc,int4 size) const; ///< Given storage location find matching ParamEntry
Address assignAddress(const Datatype *tp,vector<int4> &status) const; ///< Assign storage for given parameter data-type
const ParamEntry *selectUnreferenceEntry(int4 grp,type_metatype prefType) const; ///< Select entry to fill an unreferenced param
void buildTrialMap(ParamActive *active) const; ///< Build map from parameter trials to model ParamEntrys
void separateSections(ParamActive *active,int4 &oneStart,int4 &oneStop,int4 &twoStart,int4 &twoStop) const;
void forceExclusionGroup(ParamActive *active) const;
void forceNoUse(ParamActive *active,int4 start,int4 stop) const;
void forceInactiveChain(ParamActive *active,int4 maxchain,int4 start,int4 stop,int4 groupstart) const;
static void markGroupNoUse(ParamActive *active,int4 groupUpper,int4 groupStart,int4 index);
static void markBestInactive(ParamActive *active,int4 group,int4 groupStart,type_metatype prefType);
static void forceExclusionGroup(ParamActive *active);
static void forceNoUse(ParamActive *active,int4 start,int4 stop);
static void forceInactiveChain(ParamActive *active,int4 maxchain,int4 start,int4 stop,int4 groupstart);
void calcDelay(void); ///< Calculate the maximum heritage delay for any potential parameter in this list
void addResolverRange(AddrSpace *spc,uintb first,uintb last,ParamEntry *paramEntry,int4 position);
void populateResolver(void); ///< Build the ParamEntry resolver maps
void parsePentry(const Element *el,const AddrSpaceManager *manage,vector<EffectRecord> &effectlist,
int4 groupid,bool normalstack,bool autokill,bool splitFloat,bool grouped);
@ -738,13 +756,15 @@ public:
vector<EffectRecord>::const_iterator effectEnd(void) const { return effectlist.end(); } ///< Get an iterator to the last EffectRecord
vector<VarnodeData>::const_iterator trashBegin(void) const { return likelytrash.begin(); } ///< Get an iterator to the first \e likelytrash
vector<VarnodeData>::const_iterator trashEnd(void) const { return likelytrash.end(); } ///< Get an iterator to the last \e likelytrash
/// \brief Characterize whether the given range overlaps parameter storage
///
/// Does the range naturally fit inside a potential parameter entry from this model or does
/// it contain a parameter entry. Return one of three values indicating this characterization:
/// - 0 means there is no intersection between the range and any ParamEntry
/// - 1 means that at least one ParamEntry contains the range in a properly justified manner
/// - 2 means no ParamEntry contains the range, but the range contains at least one ParamEntry
/// it contain a parameter entry. Return one of four values indicating this characterization:
/// - no_containment - there is no containment between the range and any parameter in this list
/// - contains_unjustified - at least one parameter contains the range
/// - contains_justified - at least one parameter contains this range as its least significant bytes
/// - contained_by - no parameter contains this range, but the range contains at least one parameter
/// \param loc is the starting address of the given range
/// \param size is the number of bytes in the given range
/// \return the characterization code
@ -752,6 +772,21 @@ public:
return input->characterizeAsParam(loc, size);
}
/// \brief Characterize whether the given range overlaps output storage
///
/// Does the range naturally fit inside a potential output entry from this model or does
/// it contain an output entry. Return one of four values indicating this characterization:
/// - no_containment - there is no containment between the range and any parameter in this list
/// - contains_unjustified - at least one parameter contains the range
/// - contains_justified - at least one parameter contains this range as its least significant bytes
/// - contained_by - no parameter contains this range, but the range contains at least one parameter
/// \param loc is the starting address of the given range
/// \param size is the number of bytes in the given range
/// \return the characterization code
int4 characterizeAsOutput(const Address &loc,int4 size) const {
return output->characterizeAsParam(loc, size);
}
/// \brief Does the given storage location make sense as an input parameter
///
/// Within \b this model, decide if the storage location can be considered an input parameter.
@ -842,6 +877,16 @@ public:
return input->getBiggestContainedParam(loc, size, res);
}
/// \brief Pass-back the biggest possible output parameter contained within the given range
///
/// \param loc is the starting address of the given range
/// \param size is the number of bytes in the range
/// \param res will hold the storage description being passed back
/// \return \b true if there is at least one possible output parameter contained in the range
bool getBiggestContainedOutput(const Address &loc,int4 size,VarnodeData &res) const {
return output->getBiggestContainedParam(loc, size, res);
}
AddrSpace *getSpacebase(void) const { return input->getSpacebase(); } ///< Get the stack space associated with \b this model
bool isStackGrowsNegative(void) const { return stackgrowsnegative; } ///< Return \b true if the stack \e grows toward smaller addresses
bool hasThisPointer(void) const { return hasThis; } ///< Is \b this a model for (non-static) class methods
@ -1393,6 +1438,7 @@ public:
vector<VarnodeData>::const_iterator trashBegin(void) const; ///< Get iterator to front of \e likelytrash list
vector<VarnodeData>::const_iterator trashEnd(void) const; ///< Get iterator to end of \e likelytrash list
int4 characterizeAsInputParam(const Address &addr,int4 size) const;
int4 characterizeAsOutput(const Address &addr,int4 size) const;
bool possibleInputParam(const Address &addr,int4 size) const;
bool possibleOutputParam(const Address &addr,int4 size) const;
@ -1443,6 +1489,9 @@ public:
/// \brief Pass-back the biggest potential input parameter contained within the given range
bool getBiggestContainedInputParam(const Address &loc,int4 size,VarnodeData &res) const;
/// \brief Pass-back the biggest potential output storage location contained within the given range
bool getBiggestContainedOutput(const Address &loc,int4 size,VarnodeData &res) const;
bool isCompatible(const FuncProto &op2) const;
AddrSpace *getSpacebase(void) const { return model->getSpacebase(); } ///< Get the \e stack address space
void printRaw(const string &funcname,ostream &s) const;

View File

@ -59,11 +59,6 @@ class Funcdata {
baddata_present = 0x800, ///< Set if function flowed into bad data
double_precis_on = 0x1000 ///< Set if we are performing double precision recovery
};
enum {
traverse_actionalt = 1, ///< Alternate path traverses a solid action or \e non-incidental COPY
traverse_indirect = 2, ///< Main path traverses an INDIRECT
traverse_indirectalt = 4 ///< Alternate path traverses an INDIRECT
};
uint4 flags; ///< Boolean properties associated with \b this function
uint4 clean_up_index; ///< Creation index of first Varnode created after start of cleanup
uint4 high_level_index; ///< Creation index of first Varnode created after HighVariables are created
@ -124,7 +119,6 @@ class Funcdata {
void nodeSplitCloneVarnode(PcodeOp *op,PcodeOp *newop);
void nodeSplitRawDuplicate(BlockBasic *b,BlockBasic *bprime);
void nodeSplitInputPatch(BlockBasic *b,BlockBasic *bprime,int4 inedge);
static bool isAlternatePathValid(const Varnode *vn,uint4 flags);
static bool descendantsOutside(Varnode *vn);
static void saveVarnodeXml(ostream &s,VarnodeLocSet::const_iterator iter,VarnodeLocSet::const_iterator enditer);
static bool checkIndirectUse(Varnode *vn);
@ -374,7 +368,7 @@ public:
void mapGlobals(void); ///< Make sure there is a Symbol entry for all global Varnodes
bool checkCallDoubleUse(const PcodeOp *opmatch,const PcodeOp *op,const Varnode *vn,uint4 fl,const ParamTrial &trial) const;
bool onlyOpUse(const Varnode *invn,const PcodeOp *opmatch,const ParamTrial &trial,uint4 mainFlags) const;
bool ancestorOpUse(int4 maxlevel,const Varnode *invn,const PcodeOp *op,ParamTrial &trial,uint4 mainFlags) const;
bool ancestorOpUse(int4 maxlevel,const Varnode *invn,const PcodeOp *op,ParamTrial &trial,int4 offset,uint4 mainFlags) const;
bool syncVarnodesWithSymbols(const ScopeLocal *lm,bool typesyes);
Datatype *checkSymbolType(Varnode *vn); ///< Check for any delayed symbol data-type information on the given Varnode
void transferVarnodeProperties(Varnode *vn,Varnode *newVn,int4 lsbOffset);
@ -599,9 +593,9 @@ class AncestorRealistic {
seen_kill = 4 ///< Indicates the Varnode is killed by a call on at least path to MULTIEQUAL
};
PcodeOp *op; ///< Operation along the path to the Varnode
Varnode *vn; ///< Varnode input to \b op, along path
int4 slot; ///< vn = op->getIn(slot)
uint4 flags; ///< Boolean properties of the node
int4 offset; ///< Offset of the (eventual) trial value, within a possibly larger register
/// \brief Constructor given a Varnode read
///
@ -610,8 +604,20 @@ class AncestorRealistic {
State(PcodeOp *o,int4 s) {
op = o;
slot = s;
vn = op->getIn(slot);
flags = 0;
offset = 0;
}
/// \brief Constructor from old state pulled back through a CPUI_SUBPIECE
///
/// Data ultimately in SUBPIECE output is copied from a non-zero offset within the input Varnode. Note this offset
/// \param o is the CPUI_SUBPIECE
/// \param oldState is the old state being pulled back from
State(PcodeOp *o,const State &oldState) {
op = o;
slot = 0;
flags = 0;
offset = oldState.offset + (int4)op->getIn(1)->getOffset();
}
int4 getSolidSlot(void) const { return ((flags & seen_solid0)!=0) ? 0 : 1; } ///< Get slot associated with \e solid movement
void markSolid(int4 s) { flags |= (s==0) ? seen_solid0 : seen_solid1; } ///< Mark given slot as having \e solid movement
@ -641,8 +647,8 @@ class AncestorRealistic {
vn->setMark();
}
int4 enterNode(State &state); ///< Traverse into a new Varnode
int4 uponPop(State &state,int4 command); ///< Pop a Varnode from the traversal stack
int4 enterNode(void); ///< Traverse into a new Varnode
int4 uponPop(int4 command); ///< Pop a Varnode from the traversal stack
bool checkConditionalExe(State &state); ///< Check if current Varnode produced by conditional flow
public:
bool execute(PcodeOp *op,int4 slot,ParamTrial *t,bool allowFail);

View File

@ -1549,30 +1549,6 @@ void Funcdata::mapGlobals(void)
warningHeader("Globals starting with '_' overlap smaller symbols at the same address");
}
/// \brief Return \b true if the alternate path looks more valid than the main path.
///
/// Two different paths from a common Varnode each terminate at a CALL, CALLIND, or RETURN.
/// Evaluate which path most likely represents actual parameter/return value passing,
/// based on traversal information about each path.
/// \param vn is the Varnode terminating the \e alternate path
/// \param flags indicates traversals for both paths
/// \return \b true if the alternate path is preferred
bool Funcdata::isAlternatePathValid(const Varnode *vn,uint4 flags)
{
if ((flags & (traverse_indirect | traverse_indirectalt)) == traverse_indirect)
// If main path traversed an INDIRECT but the alternate did not
return true; // Main path traversed INDIRECT, alternate did not
if ((flags & (traverse_indirect | traverse_indirectalt)) == traverse_indirectalt)
return false; // Alternate path traversed INDIRECT, main did not
if ((flags & traverse_actionalt) != 0)
return true; // Alternate path traversed a dedicated COPY
if (vn->loneDescend() == (PcodeOp*)0) return false;
const PcodeOp *op = vn->getDef();
if (op == (PcodeOp*)0) return true;
return !op->isMarker(); // MULTIEQUAL or INDIRECT indicates multiple values
}
/// \brief Test for legitimate double use of a parameter trial
///
/// The given trial is a \e putative input to first CALL, but can also trace its data-flow
@ -1617,7 +1593,7 @@ bool Funcdata::checkCallDoubleUse(const PcodeOp *opmatch,const PcodeOp *op,const
if (curtrial.isActive())
return false;
}
else if (isAlternatePathValid(vn,fl))
else if (TraverseNode::isAlternatePathValid(vn,fl))
return false;
return true;
}
@ -1670,11 +1646,11 @@ bool Funcdata::onlyOpUse(const Varnode *invn,const PcodeOp *opmatch,const ParamT
res = false;
break;
case CPUI_INDIRECT:
curFlags |= Funcdata::traverse_indirectalt;
curFlags |= TraverseNode::indirectalt;
break;
case CPUI_COPY:
if ((op->getOut()->getSpace()->getType()!=IPTR_INTERNAL)&&!op->isIncidentalCopy()&&!vn->isIncidentalCopy()) {
curFlags |= Funcdata::traverse_actionalt;
curFlags |= TraverseNode::actionalt;
}
break;
case CPUI_RETURN:
@ -1684,21 +1660,34 @@ bool Funcdata::onlyOpUse(const Varnode *invn,const PcodeOp *opmatch,const ParamT
}
else if (activeoutput != (ParamActive *)0) { // Are we in the middle of analyzing returns
if (op->getIn(0) != vn) { // Unless we hold actual return value
if (!isAlternatePathValid(vn,curFlags))
if (!TraverseNode::isAlternatePathValid(vn,curFlags))
continue; // Don't consider this a "use"
}
}
res = false;
break;
case CPUI_MULTIEQUAL:
case CPUI_PIECE:
case CPUI_SUBPIECE:
case CPUI_INT_SEXT:
case CPUI_INT_ZEXT:
case CPUI_CAST:
break;
case CPUI_PIECE:
if (op->getIn(0) == vn) { // Concatenated as most significant piece
if ((curFlags & TraverseNode::lsb_truncated) != 0) {
// Original lsb has been truncated and replaced
continue; // No longer assume this is a possible use
}
curFlags |= TraverseNode::concat_high;
}
break;
case CPUI_SUBPIECE:
if (op->getIn(1)->getOffset() != 0) { // Throwing away least significant byte(s)
if ((curFlags & TraverseNode::concat_high) == 0) // If no previous concatenation has occurred
curFlags |= TraverseNode::lsb_truncated; // Byte(s) of original value have been thrown away
}
break;
default:
curFlags |= Funcdata::traverse_actionalt;
curFlags |= TraverseNode::actionalt;
break;
}
if (!res) break;
@ -1729,14 +1718,13 @@ bool Funcdata::onlyOpUse(const Varnode *invn,const PcodeOp *opmatch,const ParamT
/// \param invn is the given trial Varnode to test
/// \param op is the given CALL or RETURN
/// \param trial is the associated parameter trial object
/// \param offset is the offset within the current Varnode of the value ultimately copied into the trial
/// \param mainFlags describes traversals along the path from \e invn to \e op
/// \return \b true if the Varnode is only used for the CALL/RETURN
bool Funcdata::ancestorOpUse(int4 maxlevel,const Varnode *invn,
const PcodeOp *op,ParamTrial &trial,uint4 mainFlags) const
const PcodeOp *op,ParamTrial &trial,int4 offset,uint4 mainFlags) const
{
int4 i;
if (maxlevel==0) return false;
if (!invn->isWritten()) {
@ -1754,15 +1742,15 @@ bool Funcdata::ancestorOpUse(int4 maxlevel,const Varnode *invn,
// as an "only use"
if (def->isIndirectCreation())
return false;
return ancestorOpUse(maxlevel-1,def->getIn(0),op,trial,mainFlags | Funcdata::traverse_indirect);
return ancestorOpUse(maxlevel-1,def->getIn(0),op,trial,offset,mainFlags | TraverseNode::indirect);
case CPUI_MULTIEQUAL:
// Check if there is any ancestor whose only
// use is in this op
if (def->isMark()) return false; // Trim the loop
def->setMark(); // Mark that this MULTIEQUAL is on the path
// Note: onlyOpUse is using Varnode::setMark
for(i=0;i<def->numInput();++i) {
if (ancestorOpUse(maxlevel-1,def->getIn(i),op,trial, mainFlags)) {
for(int4 i=0;i<def->numInput();++i) {
if (ancestorOpUse(maxlevel-1,def->getIn(i),op,trial,offset,mainFlags)) {
def->clearMark();
return true;
}
@ -1771,17 +1759,23 @@ bool Funcdata::ancestorOpUse(int4 maxlevel,const Varnode *invn,
return false;
case CPUI_COPY:
if ((invn->getSpace()->getType()==IPTR_INTERNAL)||def->isIncidentalCopy()||def->getIn(0)->isIncidentalCopy()) {
return ancestorOpUse(maxlevel-1,def->getIn(0),op,trial,mainFlags);
return ancestorOpUse(maxlevel-1,def->getIn(0),op,trial,offset,mainFlags);
}
break;
case CPUI_PIECE:
// Concatenation tends to be artificial, so recurse through the least significant part
return ancestorOpUse(maxlevel-1,def->getIn(1),op,trial,mainFlags);
// Concatenation tends to be artificial, so recurse through piece corresponding later SUBPIECE
if (offset == 0)
return ancestorOpUse(maxlevel-1,def->getIn(1),op,trial,0,mainFlags); // Follow into least sig piece
if (offset == def->getIn(1)->getSize())
return ancestorOpUse(maxlevel-1,def->getIn(0),op,trial,0,mainFlags); // Follow into most sig piece
return false;
case CPUI_SUBPIECE:
{
int4 newOff = def->getIn(1)->getOffset();
// This is a rather kludgy way to get around where a DIV (or other similar) instruction
// causes a register that looks like the high precision piece of the function return
// to be set with the remainder as a side effect
if (def->getIn(1)->getOffset()==0) {
if (newOff==0) {
const Varnode *vn = def->getIn(0);
if (vn->isWritten()) {
const PcodeOp *remop = vn->getDef();
@ -1789,7 +1783,13 @@ bool Funcdata::ancestorOpUse(int4 maxlevel,const Varnode *invn,
trial.setRemFormed();
}
}
if (invn->getSpace()->getType() == IPTR_INTERNAL || def->isIncidentalCopy() ||
def->getIn(0)->isIncidentalCopy() ||
invn->overlap(*def->getIn(0)) == newOff) {
return ancestorOpUse(maxlevel-1,def->getIn(0),op,trial,offset + newOff,mainFlags);
}
break;
}
case CPUI_CALL:
case CPUI_CALLIND:
return false; // A call is never a good indication of a single op use
@ -1829,24 +1829,25 @@ bool AncestorRealistic::checkConditionalExe(State &state)
}
/// Analyze a new node that has just entered, during the depth-first traversal
/// \param state is the current node on the path, with associated state information
/// \return the command indicating the next traversal step: push (enter_node), or pop (pop_success, pop_fail, pop_solid...)
int4 AncestorRealistic::enterNode(State &state)
int4 AncestorRealistic::enterNode(void)
{
State &state(stateStack.back());
// If the node has already been visited, we truncate the traversal to prevent cycles.
// We always return success assuming the proper result will get returned along the first path
if (state.vn->isMark()) return pop_success;
if (!state.vn->isWritten()) {
if (state.vn->isInput()) {
if (state.vn->isUnaffected()) return pop_fail;
if (state.vn->isPersist()) return pop_success; // A global input, not active movement, but a valid possibility
if (!state.vn->isDirectWrite()) return pop_fail;
Varnode *stateVn = state.op->getIn(state.slot);
if (stateVn->isMark()) return pop_success;
if (!stateVn->isWritten()) {
if (stateVn->isInput()) {
if (stateVn->isUnaffected()) return pop_fail;
if (stateVn->isPersist()) return pop_success; // A global input, not active movement, but a valid possibility
if (!stateVn->isDirectWrite()) return pop_fail;
}
return pop_success; // Probably a normal parameter, not active movement, but valid
}
mark(state.vn); // Mark that the varnode has now been visited
PcodeOp *op = state.vn->getDef();
mark(stateVn); // Mark that the varnode has now been visited
PcodeOp *op = stateVn->getDef();
switch(op->code()) {
case CPUI_INDIRECT:
if (op->isIndirectCreation()) { // Backtracking is stopped by a call
@ -1867,7 +1868,7 @@ int4 AncestorRealistic::enterNode(State &state)
if (op->getOut()->getSpace()->getType()==IPTR_INTERNAL
|| op->isIncidentalCopy() || op->getIn(0)->isIncidentalCopy()
|| (op->getOut()->overlap(*op->getIn(0)) == (int4)op->getIn(1)->getOffset())) {
stateStack.push_back(State(op,0));
stateStack.push_back(State(op,state));
return enter_node; // Push into the new node
}
// For other SUBPIECES, do a minimal traversal to rule out unaffected or other invalid inputs,
@ -1882,6 +1883,7 @@ int4 AncestorRealistic::enterNode(State &state)
} while((op!=(PcodeOp *)0)&&((op->code() == CPUI_COPY)||(op->code()==CPUI_SUBPIECE)));
return pop_solid; // treat the COPY as a solid movement
case CPUI_COPY:
{
// Copies to a temporary, or between varnodes with same storage location, or otherwise incidental
// are viewed as just another node on the path to traverse
if (op->getOut()->getSpace()->getType()==IPTR_INTERNAL
@ -1892,24 +1894,46 @@ int4 AncestorRealistic::enterNode(State &state)
}
// For other COPIES, do a minimal traversal to rule out unaffected or other invalid inputs,
// but otherwise treat it as valid, active, movement into the parameter
do {
Varnode *vn = op->getIn(0);
Varnode *vn = op->getIn(0);
for(;;) {
if ((!vn->isMark())&&(vn->isInput())) {
if (!vn->isDirectWrite())
return pop_fail;
}
op = vn->getDef();
} while((op!=(PcodeOp *)0)&&((op->code() == CPUI_COPY)||(op->code()==CPUI_SUBPIECE)));
if (op == (PcodeOp *)0) break;
OpCode opc = op->code();
if (opc == CPUI_COPY || opc == CPUI_SUBPIECE)
vn = op->getIn(0);
else if (opc == CPUI_PIECE)
vn = op->getIn(1); // Follow least significant piece
else
break;
}
return pop_solid; // treat the COPY as a solid movement
}
case CPUI_MULTIEQUAL:
multiDepth += 1;
stateStack.push_back(State(op,0));
return enter_node; // Nothing to check, start traversing inputs of MULTIEQUAL
case CPUI_PIECE:
// If the trial is getting pieced together and then truncated in a register,
// this is evidence of artificial data-flow.
if (state.vn->getSize() > trial->getSize() && state.vn->getSpace()->getType() != IPTR_SPACEBASE)
return pop_fail;
if (stateVn->getSize() > trial->getSize()) { // Did we already pull-back from a SUBPIECE?
// If the trial is getting pieced together and then truncated in a register,
// this is evidence of artificial data-flow.
if (state.offset == 0 && op->getIn(1)->getSize() <= trial->getSize()) {
// Truncation corresponds to least significant piece, follow slot=1
stateStack.push_back(State(op,1));
return enter_node;
}
else if (state.offset == op->getIn(1)->getSize() && op->getIn(0)->getSize() <= trial->getSize()) {
// Truncation corresponds to most significant piece, follow slot=0
stateStack.push_back(State(op,0));
return enter_node;
}
if (stateVn->getSpace()->getType() != IPTR_SPACEBASE) {
return pop_fail;
}
}
return pop_solid;
default:
return pop_solid; // Any other LOAD or arithmetic/logical operation is viewed as solid movement
@ -1917,12 +1941,12 @@ int4 AncestorRealistic::enterNode(State &state)
}
/// Backtrack into a previously visited node
/// \param state is the node that needs to be popped from the stack
/// \param pop_command is the type of pop (pop_success, pop_fail, pop_failkill, pop_solid) being performed
/// \return the command to execute (push or pop) after the current pop
int4 AncestorRealistic::uponPop(State &state,int4 pop_command)
int4 AncestorRealistic::uponPop(int4 pop_command)
{
State &state(stateStack.back());
if (state.op->code() == CPUI_MULTIEQUAL) { // All the interesting action happens for MULTIEQUAL branch points
State &prevstate( stateStack[ stateStack.size()-2 ]); // State previous the one being popped
if (pop_command == pop_fail) { // For a pop_fail, we always pop and pass along the fail
@ -1957,7 +1981,6 @@ int4 AncestorRealistic::uponPop(State &state,int4 pop_command)
stateStack.pop_back();
return pop_command;
}
state.vn = state.op->getIn(state.slot); // Advance to next sibling
return enter_node;
}
else {
@ -1994,19 +2017,26 @@ bool AncestorRealistic::execute(PcodeOp *op,int4 slot,ParamTrial *t,bool allowFa
while(!stateStack.empty()) { // Continue until all paths have been exhausted
switch(command) {
case enter_node:
command = enterNode(stateStack.back());
command = enterNode();
break;
case pop_success:
case pop_solid:
case pop_fail:
case pop_failkill:
command = uponPop(stateStack.back(),command);
command = uponPop(command);
break;
}
}
for(int4 i=0;i<markedVn.size();++i) // Clean up marks we left along the way
markedVn[i]->clearMark();
if ((command != pop_success)&&(command != pop_solid))
return false;
return true;
if (command == pop_success) {
trial->setAncestorRealistic();
return true;
}
else if (command == pop_solid) {
trial->setAncestorRealistic();
trial->setAncestorSolid();
return true;
}
return false;
}

View File

@ -1178,6 +1178,63 @@ void Heritage::guardCallOverlappingInput(FuncCallSpecs *fc,const Address &addr,c
}
}
/// \brief Guard an address range that is larger than the possible output storage
///
/// A potential return value should look like an \b indirect \b creation at this stage,
/// but the range is even bigger. We split it up into 2 or 3 Varnodes, and make each one via
/// an INDIRECT. The piece corresponding to the potential return value is registered, and all
/// the pieces are concatenated to form a Varnode of the whole range.
/// \param fc is the call site potentially returning a value
/// \param addr is the starting address of the range
/// \param size is the size of the range in bytes
/// \param write is the set of new written Varnodes
/// \return \b true if the INDIRECTs were created
bool Heritage::guardCallOverlappingOutput(FuncCallSpecs *fc,const Address &addr,int4 size,vector<Varnode *> &write)
{
VarnodeData vData;
if (!fc->getBiggestContainedOutput(addr, size, vData))
return false;
ParamActive *active = fc->getActiveOutput();
Address truncAddr(vData.space,vData.offset);
if (active->whichTrial(truncAddr, size) >= 0)
return false; // Trial already exists
int4 sizeFront = (int4)(vData.offset - addr.getOffset());
int4 sizeBack = size - vData.size - sizeFront;
PcodeOp *indOp = fd->newIndirectCreation(fc->getOp(),truncAddr,vData.size,true);
Varnode *vnCollect = indOp->getOut();
PcodeOp *insertPoint = fc->getOp();
if (sizeFront != 0) {
PcodeOp *indOpFront = fd->newIndirectCreation(indOp,addr,sizeFront,false);
Varnode *newFront = indOpFront->getOut();
PcodeOp *concatFront = fd->newOp(2,indOp->getAddr());
int4 slotNew = vData.space->isBigEndian() ? 0 : 1;
fd->opSetOpcode(concatFront, CPUI_PIECE);
fd->opSetInput(concatFront,newFront,slotNew);
fd->opSetInput(concatFront,vnCollect,1-slotNew);
vnCollect = fd->newVarnodeOut(sizeFront+vData.size,addr,concatFront);
fd->opInsertAfter(concatFront, insertPoint);
insertPoint = concatFront;
}
if (sizeBack != 0) {
Address addrBack = truncAddr + vData.size;
PcodeOp *indOpBack = fd->newIndirectCreation(fc->getOp(),addrBack,sizeBack,false);
Varnode *newBack = indOpBack->getOut();
PcodeOp *concatBack = fd->newOp(2,indOp->getAddr());
int4 slotNew = vData.space->isBigEndian() ? 1 : 0;
fd->opSetOpcode(concatBack, CPUI_PIECE);
fd->opSetInput(concatBack,newBack,slotNew);
fd->opSetInput(concatBack,vnCollect,1-slotNew);
vnCollect = fd->newVarnodeOut(size,addr,concatBack);
fd->opInsertAfter(concatBack, insertPoint);
}
vnCollect->setActiveHeritage();
write.push_back(vnCollect);
active->registerTrial(truncAddr, vData.size);
return true;
}
/// \brief Guard CALL/CALLIND ops in preparation for renaming algorithm
///
/// For the given address range, we decide what the data-flow effect is
@ -1206,11 +1263,18 @@ void Heritage::guardCalls(uint4 fl,const Address &addr,int4 size,vector<Varnode
bool possibleoutput = false;
if (fc->isOutputActive()) {
ParamActive *active = fc->getActiveOutput();
if (fc->possibleOutputParam(addr,size)) {
if (active->whichTrial(addr,size)<0) { // If not already a trial
active->registerTrial(addr,size);
effecttype = EffectRecord::killedbycall; // A potential output is always killed by call
possibleoutput = true;
int4 outputCharacter = fc->characterizeAsOutput(addr, size);
if (outputCharacter != ParamEntry::no_containment) {
effecttype = EffectRecord::killedbycall; // A potential output is always killed by call
if (outputCharacter == ParamEntry::contained_by) {
if (guardCallOverlappingOutput(fc, addr, size, write))
effecttype = EffectRecord::unaffected; // Range is handled, don't do additional guarding
}
else {
if (active->whichTrial(addr,size)<0) { // If not already a trial
active->registerTrial(addr,size);
possibleoutput = true;
}
}
}
}
@ -1227,7 +1291,7 @@ void Heritage::guardCalls(uint4 fl,const Address &addr,int4 size,vector<Varnode
Address transAddr(spc,off); // Address relative to callee's stack
if (tryregister) {
int4 inputCharacter = fc->characterizeAsInputParam(transAddr,size);
if (inputCharacter == 1) { // Call could be using this range as an input parameter
if (inputCharacter == ParamEntry::contains_justified) { // Call could be using this range as an input parameter
ParamActive *active = fc->getActiveInput();
if (active->whichTrial(transAddr,size)<0) { // If not already a trial
PcodeOp *op = fc->getOp();
@ -1237,7 +1301,7 @@ void Heritage::guardCalls(uint4 fl,const Address &addr,int4 size,vector<Varnode
fd->opInsertInput(op,vn,op->numInput());
}
}
else if (inputCharacter == 2) // Call may be using part of this range as an input parameter
else if (inputCharacter == ParamEntry::contained_by) // Call may be using part of this range as an input parameter
guardCallOverlappingInput(fc, addr, transAddr, size);
}
}
@ -1334,6 +1398,43 @@ void Heritage::guardLoads(uint4 fl,const Address &addr,int4 size,vector<Varnode
}
}
/// \brief Guard data-flow at RETURN ops, where range properly contains potention return storage
///
/// The RETURN ops need to take a new input because of the potential of a return value,
/// but the range is too big so it must be truncated to fit.
/// \param addr is the starting address of the range
/// \param size is the size of the range in bytes
void Heritage::guardReturnsOverlapping(const Address &addr,int4 size)
{
VarnodeData vData;
list<PcodeOp *>::const_iterator iter,iterend;
if (!fd->getFuncProto().getBiggestContainedOutput(addr, size, vData))
return;
Address truncAddr(vData.space,vData.offset);
ParamActive *active = fd->getActiveOutput();
active->registerTrial(truncAddr,vData.size);
int4 offset = (int4)(vData.offset - addr.getOffset()); // Number of least significant bytes to truncate
if (vData.space->isBigEndian())
offset = (size - vData.size) - offset;
iterend = fd->endOp(CPUI_RETURN);
for(iter=fd->beginOp(CPUI_RETURN);iter!=iterend;++iter) {
PcodeOp *op = *iter;
if (op->isDead()) continue;
if (op->getHaltType() != 0) continue; // Special halt points cannot take return values
Varnode *invn = fd->newVarnode(size,addr);
PcodeOp *subOp = fd->newOp(2,op->getAddr());
fd->opSetOpcode(subOp, CPUI_SUBPIECE);
fd->opSetInput(subOp,invn,0);
fd->opSetInput(subOp,fd->newConstant(4, offset),1);
fd->opInsertBefore(subOp, op);
Varnode *retVal = fd->newVarnodeOut(vData.size,truncAddr,subOp);
invn->setActiveHeritage();
fd->opInsertInput(op,retVal,op->numInput());
}
}
/// \brief Guard global data-flow at RETURN ops in preparation for renaming
///
/// For the given global (persistent) address range, data-flow must persist up to
@ -1354,7 +1455,10 @@ void Heritage::guardReturns(uint4 fl,const Address &addr,int4 size,vector<Varnod
ParamActive *active = fd->getActiveOutput();
if (active != (ParamActive *)0) {
if (fd->getFuncProto().possibleOutputParam(addr,size)) {
int4 outputCharacter = fd->getFuncProto().characterizeAsOutput(addr, size);
if (outputCharacter == ParamEntry::contained_by)
guardReturnsOverlapping(addr, size);
else if (outputCharacter != ParamEntry::no_containment) {
active->registerTrial(addr,size);
iterend = fd->endOp(CPUI_RETURN);
for(iter=fd->beginOp(CPUI_RETURN);iter!=iterend;++iter) {

View File

@ -249,9 +249,11 @@ class Heritage {
void guard(const Address &addr,int4 size,vector<Varnode *> &read,vector<Varnode *> &write,vector<Varnode *> &inputvars);
void guardInput(const Address &addr,int4 size,vector<Varnode *> &input);
void guardCallOverlappingInput(FuncCallSpecs *fc,const Address &addr,const Address &transAddr,int4 size);
bool guardCallOverlappingOutput(FuncCallSpecs *fc,const Address &addr,int4 size,vector<Varnode *> &write);
void guardCalls(uint4 fl,const Address &addr,int4 size,vector<Varnode *> &write);
void guardStores(const Address &addr,int4 size,vector<Varnode *> &write);
void guardLoads(uint4 fl,const Address &addr,int4 size,vector<Varnode *> &write);
void guardReturnsOverlapping(const Address &addr,int4 size);
void guardReturns(uint4 fl,const Address &addr,int4 size,vector<Varnode *> &write);
static void buildRefinement(vector<int4> &refine,const Address &addr,int4 size,const vector<Varnode *> &vnlist);
void splitByRefinement(Varnode *vn,const Address &addr,const vector<int4> &refine,vector<Varnode *> &split);

View File

@ -9357,20 +9357,22 @@ int4 RulePiecePathology::applyOp(PcodeOp *op,Funcdata &data)
if (!isPathology(subOp->getIn(0),data)) return 0;
}
else if (opc == CPUI_INDIRECT) {
if (!subOp->isIndirectCreation()) return 0;
Varnode *retVn = op->getIn(1);
if (!retVn->isWritten()) return 0;
PcodeOp *callOp = retVn->getDef();
if (!callOp->isCall()) return 0;
FuncCallSpecs *fc = data.getCallSpecs(callOp);
if (fc == (FuncCallSpecs *)0) return 0;
if (!fc->isOutputLocked()) return 0;
Address addr = retVn->getAddr();
if (!subOp->isIndirectCreation()) return 0; // Indirect concatenation
Varnode *lsbVn = op->getIn(1);
if (!lsbVn->isWritten()) return 0;
PcodeOp *lsbOp = lsbVn->getDef();
if ((lsbOp->getEvalType() & (PcodeOp::binary | PcodeOp::unary)) == 0) { // from either a unary/binary operation
if (!lsbOp->isCall()) return 0; // or a CALL
FuncCallSpecs *fc = data.getCallSpecs(lsbOp);
if (fc == (FuncCallSpecs *)0) return 0;
if (!fc->isOutputLocked()) return 0; // with a locked output
}
Address addr = lsbVn->getAddr();
if (addr.getSpace()->isBigEndian())
addr = addr - vn->getSize();
else
addr = addr + retVn->getSize();
if (addr != vn->getAddr()) return 0;
addr = addr + lsbVn->getSize();
if (addr != vn->getAddr()) return 0; // into a contiguous register
}
else
return 0;

View File

@ -1632,6 +1632,30 @@ void VarnodeBank::verifyIntegrity(void) const
}
#endif
/// \brief Return \b true if the alternate path looks more valid than the main path.
///
/// Two different paths from a common Varnode each terminate at a CALL, CALLIND, or RETURN.
/// Evaluate which path most likely represents actual parameter/return value passing,
/// based on traversal information about each path.
/// \param vn is the Varnode terminating the \e alternate path
/// \param flags indicates traversals for both paths
/// \return \b true if the alternate path is preferred
bool TraverseNode::isAlternatePathValid(const Varnode *vn,uint4 flags)
{
if ((flags & (indirect | indirectalt)) == indirect)
// If main path traversed an INDIRECT but the alternate did not
return true; // Main path traversed INDIRECT, alternate did not
if ((flags & (indirect | indirectalt)) == indirectalt)
return false; // Alternate path traversed INDIRECT, main did not
if ((flags & actionalt) != 0)
return true; // Alternate path traversed a dedicated COPY
if (vn->loneDescend() == (PcodeOp*)0) return false;
const PcodeOp *op = vn->getDef();
if (op == (PcodeOp*)0) return true;
return !op->isMarker(); // MULTIEQUAL or INDIRECT indicates multiple values
}
/// Return true if \b vn1 contains the high part and \b vn2 the low part
/// of what was(is) a single value.
/// \param vn1 is the putative high Varnode

View File

@ -392,9 +392,17 @@ public:
/// \brief Node for a forward traversal of a Varnode expression
struct TraverseNode {
enum {
actionalt = 1, ///< Alternate path traverses a solid action or \e non-incidental COPY
indirect = 2, ///< Main path traverses an INDIRECT
indirectalt = 4, ///< Alternate path traverses an INDIRECT
lsb_truncated = 8, ///< Least significant byte(s) of original value have been truncated
concat_high = 0x10 ///< Original value has been concatented as \e most significant portion
};
const Varnode *vn; ///< Varnode at the point of traversal
uint4 flags; ///< Flags associated with the node
TraverseNode(const Varnode *v,uint4 f) { vn = v; flags = f; } ///< Constructor
static bool isAlternatePathValid(const Varnode *vn,uint4 flags);
};
bool contiguous_test(Varnode *vn1,Varnode *vn2); ///< Test if Varnodes are pieces of a whole

View File

@ -0,0 +1,83 @@
<decompilertest>
<binaryimage arch="x86:LE:64:default:windows">
<!--
Contrived example comparing a signed byte. The decompiler
should not use the 'char' data-type.
-->
<bytechunk space="ram" offset="0x140001058" readonly="true">
0f57db0f57c9f20f
2adaf2410f2ac9f20f58d8f20f58da0f
57d2f20f2a542428f20f58d90f57c9f2
0f2a4c2430f20f58daf20f58d90f28c3
c3cccccc0f28c8660f6ec20f5bc0f30f
58c1c3cc660f6ec1f30fe6c0f20f58c1
f20f2cc0c3cccccc660f6ed1f30fe6d2
66410f6ec0f20f58d1f30fe6c0f20f58
d0f20f58d3f20f2cc2c3cccc488bc448
8958084889681048897018574883ec50
f30f1005101201008bd10f2970e88bf1
0f2978d8e88bfffffff20f100dcf1101
008bce0f28f8e889fffffff20f101dcd
110100448d4601f20f100db91101008b
ce8be8e880fffffff20f1015c0110100
8d5602f20f1005ad1101008d4e03894c
2428448d4e01895424208bf88bd6e8f5
feffff0f57c9f20f11442420f30f5acf
448bcf488d0d46110100448bc50f28f0
66480f7ecae842000000488b5c246048
8b742470660f6ed5488b6c24680f5bd2
660f6ecff30f58d70f287c24300f5bc9
f30f58d10f5ad2f20f58d60f28742440
f20f2cc24883c4505fc3
</bytechunk>
<bytechunk space="ram" offset="0x1400122c0" readonly="true">
613a20256620623a20256420633a2025
6c6420643a2025670a00
</bytechunk>
<bytechunk space="ram" offset="0x1400122e0" readonly="true">
9a999999999901400000000000000840
00000000000014400000000000001c40
000000000000204000004040
</bytechunk>
<symbol space="ram" offset="0x140001058" name="dldlll"/>
<symbol space="ram" offset="0x140001094" name="fi"/>
<symbol space="ram" offset="0x1400010a4" name="id"/>
<symbol space="ram" offset="0x1400010b8" name="ldld"/>
<symbol space="ram" offset="0x1400010dc" name="main"/>
</binaryimage>
<script>
<com>option readonly on</com>
<com>map fun r0x1400011cc printf nocode</com>
<com>parse line extern void printf(char *,...);</com>
<com>lo fu dldlll</com>
<com>decompile</com>
<com>print C</com>
<com>lo fu fi</com>
<com>decompile</com>
<com>print C</com>
<com>lo fu id</com>
<com>decompile</com>
<com>print C</com>
<com>lo fu ldld</com>
<com>decompile</com>
<com>print C</com>
<com>parse line extern float8 dldlll(float8 a,int4 b,float8 c,int4 d,int4 e,int4 f);</com>
<com>parse line extern float4 fi(float4 a,int4 b);</com>
<com>parse line extern int4 id(int4 a,float8 b);</com>
<com>parse line extern int4 ldld(int4 a,float8 b,int4 c,float8 d);</com>
<com>lo fu main</com>
<com>decompile</com>
<com>print C</com>
<com>quit</com>
</script>
<stringmatch name="Mixed float/int #1" min="1" max="1">float8 dldlll\(float8 param_1,int4 param_2,float8 param_3,int4 param_4,int4 param_5,int4 param_6\)</stringmatch>
<stringmatch name="Mixed float/int #2" min="1" max="1">return.*param.*\+.*param.*\+.*param.*\+.*param.*\+.*param.*\+.*param.*;</stringmatch>
<stringmatch name="Mixed float/int #3" min="1" max="1">float4 fi\(float4 param_1,int4 param_2\)</stringmatch>
<stringmatch name="Mixed float/int #4" min="1" max="1">int4 id\(int4 param_1,float8 param_2\)</stringmatch>
<stringmatch name="Mixed float/int #5" min="1" max="1">int4 ldld\(int4 param_1,float8 param_2,int4 param_3,float8 param_4\)</stringmatch>
<stringmatch name="Mixed float/int #6" min="4" max="4">return.*param.*\+.*param.*;</stringmatch>
<stringmatch name="Mixed float/int #7" min="1" max="1">fVar. = fi\(3\.0,param_1\);</stringmatch>
<stringmatch name="Mixed float/int #8" min="1" max="1">uVar. = id\(param_1,2\.2\);</stringmatch>
<stringmatch name="Mixed float/int #9" min="1" max="1">uVar. = ldld\(param_1,3\.0,param_1 \+ 1,5\.0\);</stringmatch>
<stringmatch name="Mixed float/int #10" min="1" max="1">fVar. = dldlll\(7\.0,param_1,8\.0,param_1 \+ 1,param_1 \+ 2</stringmatch>
</decompilertest>

View File

@ -240,7 +240,7 @@ public class ParamListStandard implements ParamList {
pe.add(pentry);
pentry.restoreXml(parser, cspec, pe, grouped);
if (splitFloat) {
if (pentry.getType() == ParamEntry.TYPE_FLOAT) {
if (!grouped && pentry.getType() == ParamEntry.TYPE_FLOAT) {
if (resourceTwoStart >= 0) {
throw new XmlParseException(
"parameter list floating-point entries must come first");
@ -276,8 +276,8 @@ public class ParamListStandard implements ParamList {
// Check that all entries in the group are distinguishable
for (int i = 1; i < count; ++i) {
ParamEntry curEntry = pe.get(pe.size() - 1 - i);
for (int j = 0; j < i; ++i) {
ParamEntry.orderWithinGroup(pe.get(pe.size() - 1 - j), curEntry);
for (int j = 0; j < i; ++j) {
ParamEntry.orderWithinGroup(curEntry, pe.get(pe.size() - 1 - j));
}
}
parser.end(el);

View File

@ -27,6 +27,9 @@
<range space="ram"/>
</global>
<stackpointer register="RSP" space="ram"/>
<returnaddress>
<varnode space="stack" offset="0" size="8"/>
</returnaddress>
<default_proto>
<prototype name="__stdcall" extrapop="8" stackshift="8">
<!-- Derived from "System V Application Binary Interface AMD64 Architecture Processor Supplement" April 2016 -->

View File

@ -2,6 +2,7 @@
<!-- see: -->
<!-- https://docs.microsoft.com/en-us/cpp/build/x64-software-conventions#register-usage -->
<!-- https://docs.microsoft.com/en-us/cpp/build/x64-calling-convention -->
<!-- https://docs.microsoft.com/en-us/cpp/c-runtime-library/direction-flag -->
<!-- https://docs.microsoft.com/en-us/cpp/cpp/vectorcall -->
@ -36,37 +37,48 @@
<range space="ram"/>
</global>
<stackpointer register="RSP" space="ram"/>
<returnaddress>
<varnode space="stack" offset="0" size="8"/>
</returnaddress>
<default_proto>
<prototype name="__fastcall" extrapop="8" stackshift="8">
<input pointermax="8">
<group>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="XMM0_Qa"/>
</pentry>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="XMM1_Qa"/>
</pentry>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="XMM2_Qa"/>
</pentry>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="XMM3_Qa"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="RCX"/>
</pentry>
</group>
<group>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="XMM1_Qa"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="RDX"/>
</pentry>
</group>
<group>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="XMM2_Qa"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="R8"/>
</pentry>
</group>
<group>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="XMM3_Qa"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="R9"/>
</pentry>
<pentry minsize="1" maxsize="500" align="8">
<addr offset="40" space="stack"/>
</pentry>
</input>
</group>
<pentry minsize="1" maxsize="500" align="8">
<addr offset="40" space="stack"/>
</pentry>
</input>
<output>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="XMM0_Qa"/>
@ -88,6 +100,16 @@
<register name="R15"/>
<register name="DF"/>
<register name="GS_OFFSET"/>
<register name="XMM6"/>
<register name="XMM7"/>
<register name="XMM8"/>
<register name="XMM9"/>
<register name="XMM10"/>
<register name="XMM11"/>
<register name="XMM12"/>
<register name="XMM13"/>
<register name="XMM14"/>
<register name="XMM15"/>
</unaffected>
<killedbycall>
<register name="RAX"/>
@ -101,34 +123,42 @@
</default_proto>
<prototype name="__thiscall" extrapop="8" stackshift="8">
<input pointermax="8" thisbeforeretpointer="true">
<pentry minsize="4" maxsize="8" metatype="float">
<register name="XMM0_Qa"/>
</pentry>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="XMM1_Qa"/>
</pentry>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="XMM2_Qa"/>
</pentry>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="XMM3_Qa"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="RCX"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="RDX"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="R8"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="R9"/>
</pentry>
<group>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="XMM0_Qa"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="RCX"/>
</pentry>
</group>
<group>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="XMM1_Qa"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="RDX"/>
</pentry>
</group>
<group>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="XMM2_Qa"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="R8"/>
</pentry>
</group>
<group>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="XMM3_Qa"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="R9"/>
</pentry>
</group>
<pentry minsize="1" maxsize="500" align="8">
<addr offset="40" space="stack"/>
</pentry>
</input>
</pentry>
</input>
<output>
<pentry minsize="4" maxsize="8" metatype="float">
<register name="XMM0_Qa"/>
@ -150,6 +180,16 @@
<register name="R15"/>
<register name="DF"/>
<register name="GS_OFFSET"/>
<register name="XMM6"/>
<register name="XMM7"/>
<register name="XMM8"/>
<register name="XMM9"/>
<register name="XMM10"/>
<register name="XMM11"/>
<register name="XMM12"/>
<register name="XMM13"/>
<register name="XMM14"/>
<register name="XMM15"/>
</unaffected>
<killedbycall>
<register name="RAX"/>