mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-02-07 19:20:14 +00:00
Merge remote-tracking branch
'origin/GP-1954_win_x64_cspec_register_grouping' (Closes #2952, Closes #1480)
This commit is contained in:
commit
4b600847eb
@ -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|
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 ¶mtrial(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();
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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>
|
@ -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);
|
||||
|
@ -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 -->
|
||||
|
@ -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"/>
|
||||
|
Loading…
Reference in New Issue
Block a user