mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 17:59:46 +02:00
GP-4031 x86 System V ABI
This commit is contained in:
parent
362f571b19
commit
c674e1f2ec
20 changed files with 1668 additions and 185 deletions
|
@ -69,6 +69,24 @@ const ParamEntry *ParamEntry::findEntryByStorage(const list<ParamEntry> &entryLi
|
|||
return (const ParamEntry *)0;
|
||||
}
|
||||
|
||||
/// Check previous ParamEntry, if it exists, and compare storage class.
|
||||
/// If it is different, this is the first, and its flag gets set.
|
||||
/// \param curList is the list of previous ParamEntry
|
||||
void ParamEntry::resolveFirst(list<ParamEntry> &curList)
|
||||
|
||||
{
|
||||
list<ParamEntry>::const_iterator iter = curList.end();
|
||||
--iter;
|
||||
if (iter == curList.begin()) {
|
||||
flags |= first_storage;
|
||||
return;
|
||||
}
|
||||
--iter;
|
||||
if (type != (*iter).type) {
|
||||
flags |= first_storage;
|
||||
}
|
||||
}
|
||||
|
||||
/// If the ParamEntry is initialized with a \e join address, cache the join record and
|
||||
/// adjust the group and groupsize based on the ParamEntrys being overlapped
|
||||
/// \param curList is the current list of ParamEntry
|
||||
|
@ -536,6 +554,7 @@ void ParamEntry::decode(Decoder &decoder,bool normalstack,bool grouped,list<Para
|
|||
}
|
||||
if (grouped)
|
||||
flags |= is_grouped;
|
||||
resolveFirst(curList);
|
||||
resolveJoin(curList);
|
||||
resolveOverlap(curList);
|
||||
}
|
||||
|
@ -583,11 +602,44 @@ ParamListStandard::~ParamListStandard(void)
|
|||
}
|
||||
}
|
||||
|
||||
/// The entry must have a unique group.
|
||||
/// If no matching entry is found, the \b end iterator is returned.
|
||||
/// \param type is the storage class
|
||||
/// \return the first matching iterator
|
||||
list<ParamEntry>::const_iterator ParamListStandard::getFirstIter(type_class type) const
|
||||
|
||||
{
|
||||
list<ParamEntry>::const_iterator iter;
|
||||
for(iter=entry.begin();iter!=entry.end();++iter) {
|
||||
const ParamEntry &curEntry( *iter );
|
||||
if (curEntry.getType() == type && curEntry.getAllGroups().size() == 1)
|
||||
return iter;
|
||||
}
|
||||
return iter;
|
||||
}
|
||||
|
||||
/// If the stack entry is not present, null is returned
|
||||
/// \return the stack entry or null
|
||||
const ParamEntry *ParamListStandard::getStackEntry(void) const
|
||||
|
||||
{
|
||||
list<ParamEntry>::const_iterator iter = entry.end();
|
||||
if (iter != entry.begin()) {
|
||||
--iter; // Stack entry necessarily must be the last entry
|
||||
const ParamEntry &curEntry( *iter );
|
||||
if (!curEntry.isExclusion() && curEntry.getSpace()->getType() == IPTR_SPACEBASE) {
|
||||
return &(*iter);
|
||||
}
|
||||
}
|
||||
return (const ParamEntry *)0;
|
||||
}
|
||||
|
||||
/// Find the (first) entry containing the given memory range
|
||||
/// \param loc is the starting address of the range
|
||||
/// \param size is the number of bytes in the range
|
||||
/// \param just is \b true if the search enforces a justified match
|
||||
/// \return the pointer to the matching ParamEntry or null if no match exists
|
||||
const ParamEntry *ParamListStandard::findEntry(const Address &loc,int4 size) const
|
||||
const ParamEntry *ParamListStandard::findEntry(const Address &loc,int4 size,bool just) const
|
||||
|
||||
{
|
||||
int4 index = loc.getSpace()->getIndex();
|
||||
|
@ -602,7 +654,7 @@ const ParamEntry *ParamListStandard::findEntry(const Address &loc,int4 size) con
|
|||
const ParamEntry *testEntry = (*res.first).getParamEntry();
|
||||
++res.first;
|
||||
if (testEntry->getMinSize() > size) continue;
|
||||
if (testEntry->justifiedContain(loc,size)==0) // Make sure the range is properly justified in entry
|
||||
if (!just || testEntry->justifiedContain(loc,size)==0) // Make sure the range is properly justified in entry
|
||||
return testEntry;
|
||||
}
|
||||
return (const ParamEntry *)0;
|
||||
|
@ -779,7 +831,7 @@ void ParamListStandard::buildTrialMap(ParamActive *active) const
|
|||
|
||||
for(int4 i=0;i<active->getNumTrials();++i) {
|
||||
ParamTrial ¶mtrial(active->getTrial(i));
|
||||
const ParamEntry *entrySlot = findEntry(paramtrial.getAddress(),paramtrial.getSize());
|
||||
const ParamEntry *entrySlot = findEntry(paramtrial.getAddress(),paramtrial.getSize(),true);
|
||||
// Note: if a trial is "definitely not used" but there is a matching entry,
|
||||
// we still include it in the map
|
||||
if (entrySlot == (const ParamEntry *)0)
|
||||
|
@ -1241,9 +1293,9 @@ void ParamListStandard::fillinMap(ParamActive *active) const
|
|||
bool ParamListStandard::checkJoin(const Address &hiaddr,int4 hisize,const Address &loaddr,int4 losize) const
|
||||
|
||||
{
|
||||
const ParamEntry *entryHi = findEntry(hiaddr,hisize);
|
||||
const ParamEntry *entryHi = findEntry(hiaddr,hisize,true);
|
||||
if (entryHi == (const ParamEntry *)0) return false;
|
||||
const ParamEntry *entryLo = findEntry(loaddr,losize);
|
||||
const ParamEntry *entryLo = findEntry(loaddr,losize,true);
|
||||
if (entryLo == (const ParamEntry *)0) return false;
|
||||
if (entryHi->getGroup() == entryLo->getGroup()) {
|
||||
if (entryHi->isExclusion()||entryLo->isExclusion()) return false;
|
||||
|
@ -1270,9 +1322,9 @@ bool ParamListStandard::checkSplit(const Address &loc,int4 size,int4 splitpoint)
|
|||
{
|
||||
Address loc2 = loc + splitpoint;
|
||||
int4 size2 = size - splitpoint;
|
||||
const ParamEntry *entryNum = findEntry(loc,splitpoint);
|
||||
const ParamEntry *entryNum = findEntry(loc,splitpoint,true);
|
||||
if (entryNum == (const ParamEntry *)0) return false;
|
||||
entryNum = findEntry(loc2,size2);
|
||||
entryNum = findEntry(loc2,size2,true);
|
||||
if (entryNum == (const ParamEntry *)0) return false;
|
||||
return true;
|
||||
}
|
||||
|
@ -1280,13 +1332,13 @@ bool ParamListStandard::checkSplit(const Address &loc,int4 size,int4 splitpoint)
|
|||
bool ParamListStandard::possibleParam(const Address &loc,int4 size) const
|
||||
|
||||
{
|
||||
return ((const ParamEntry *)0 != findEntry(loc,size));
|
||||
return ((const ParamEntry *)0 != findEntry(loc,size,true));
|
||||
}
|
||||
|
||||
bool ParamListStandard::possibleParamWithSlot(const Address &loc,int4 size,int4 &slot,int4 &slotsize) const
|
||||
|
||||
{
|
||||
const ParamEntry *entryNum = findEntry(loc,size);
|
||||
const ParamEntry *entryNum = findEntry(loc,size,true);
|
||||
if (entryNum == (const ParamEntry *)0) return false;
|
||||
slot = entryNum->getSlot(loc,0);
|
||||
if (entryNum->isExclusion()) {
|
||||
|
@ -1473,7 +1525,7 @@ void ParamListRegister::fillinMap(ParamActive *active) const
|
|||
// Mark anything active as used
|
||||
for(int4 i=0;i<active->getNumTrials();++i) {
|
||||
ParamTrial ¶mtrial(active->getTrial(i));
|
||||
const ParamEntry *entrySlot = findEntry(paramtrial.getAddress(),paramtrial.getSize());
|
||||
const ParamEntry *entrySlot = findEntry(paramtrial.getAddress(),paramtrial.getSize(),true);
|
||||
if (entrySlot == (const ParamEntry *)0) // There may be no matching entry (if the model was recovered late)
|
||||
paramtrial.markNoUse();
|
||||
else {
|
||||
|
@ -1535,10 +1587,22 @@ void ParamListStandardOut::assignMap(const PrototypePieces &proto,TypeFactory &t
|
|||
}
|
||||
}
|
||||
|
||||
void ParamListStandardOut::fillinMap(ParamActive *active) const
|
||||
void ParamListStandardOut::initialize(void)
|
||||
|
||||
{
|
||||
useFillinFallback = true;
|
||||
list<ModelRule>::const_iterator iter;
|
||||
for(iter=modelRules.begin();iter!=modelRules.end();++iter) {
|
||||
if ((*iter).canAffectFillinOutput()) {
|
||||
useFillinFallback = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ParamListStandardOut::fillinMapFallback(ParamActive *active,bool firstOnly) const
|
||||
|
||||
{
|
||||
if (active->getNumTrials() == 0) return; // No trials to check
|
||||
const ParamEntry *bestentry = (const ParamEntry *)0;
|
||||
int4 bestcover = 0;
|
||||
type_class bestclass = TYPECLASS_PTR;
|
||||
|
@ -1547,6 +1611,9 @@ void ParamListStandardOut::fillinMap(ParamActive *active) const
|
|||
list<ParamEntry>::const_iterator iter;
|
||||
for(iter=entry.begin();iter!=entry.end();++iter) {
|
||||
const ParamEntry *curentry = &(*iter);
|
||||
if (firstOnly && !curentry->isFirstInClass() && curentry->isExclusion() && curentry->getAllGroups().size() == 1) {
|
||||
continue; // This is not the first entry in the storage class
|
||||
}
|
||||
bool putativematch = false;
|
||||
for(int4 j=0;j<active->getNumTrials();++j) { // Evaluate all trials in terms of current ParamEntry
|
||||
ParamTrial ¶mtrial(active->getTrial(j));
|
||||
|
@ -1616,6 +1683,50 @@ void ParamListStandardOut::fillinMap(ParamActive *active) const
|
|||
}
|
||||
}
|
||||
|
||||
void ParamListStandardOut::fillinMap(ParamActive *active) const
|
||||
|
||||
{
|
||||
if (active->getNumTrials() == 0) return; // No trials to check
|
||||
if (useFillinFallback) {
|
||||
fillinMapFallback(active,false);
|
||||
return;
|
||||
}
|
||||
for(int4 i=0;i<active->getNumTrials();++i) {
|
||||
ParamTrial &trial(active->getTrial(i));
|
||||
trial.setEntry((const ParamEntry *)0, 0);
|
||||
if (!trial.isActive()) continue;
|
||||
const ParamEntry *entry = findEntry(trial.getAddress(),trial.getSize(),false);
|
||||
if (entry == (const ParamEntry *)0) {
|
||||
trial.markNoUse();
|
||||
continue;
|
||||
}
|
||||
int4 res = entry->justifiedContain(trial.getAddress(),trial.getSize());
|
||||
if ((trial.isRemFormed() || trial.isIndCreateFormed()) && !entry->isFirstInClass()) {
|
||||
trial.markNoUse();
|
||||
continue;
|
||||
}
|
||||
trial.setEntry(entry,res);
|
||||
}
|
||||
active->sortTrials();
|
||||
list<ModelRule>::const_iterator iter;
|
||||
for(iter=modelRules.begin();iter!=modelRules.end();++iter) {
|
||||
if ((*iter).fillinOutputMap(active)) {
|
||||
for(int4 i=0;i<active->getNumTrials();++i) {
|
||||
ParamTrial &trial(active->getTrial(i));
|
||||
if (trial.isActive()) {
|
||||
trial.markUsed();
|
||||
}
|
||||
else {
|
||||
trial.markNoUse();
|
||||
trial.setEntry((const ParamEntry *)0,0);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
fillinMapFallback(active, true);
|
||||
}
|
||||
|
||||
bool ParamListStandardOut::possibleParam(const Address &loc,int4 size) const
|
||||
|
||||
{
|
||||
|
@ -1627,6 +1738,13 @@ bool ParamListStandardOut::possibleParam(const Address &loc,int4 size) const
|
|||
return false;
|
||||
}
|
||||
|
||||
void ParamListStandardOut::decode(Decoder &decoder,vector<EffectRecord> &effectlist,bool normalstack)
|
||||
|
||||
{
|
||||
ParamListStandard::decode(decoder,effectlist,normalstack);
|
||||
initialize();
|
||||
}
|
||||
|
||||
ParamList *ParamListStandardOut::clone(void) const
|
||||
|
||||
{
|
||||
|
@ -3615,6 +3733,8 @@ void FuncProto::setModel(ProtoModel *m)
|
|||
flags |= has_thisptr;
|
||||
if (m->isConstructor())
|
||||
flags |= is_constructor;
|
||||
if (m->isAutoKillByCall())
|
||||
flags |= auto_killbycall;
|
||||
model = m;
|
||||
}
|
||||
else {
|
||||
|
@ -4389,6 +4509,19 @@ void FuncProto::printRaw(const string &funcname,ostream &s) const
|
|||
s << ") extrapop=" << dec << extrapop;
|
||||
}
|
||||
|
||||
/// This assumes the storage location has already been determined to be contained
|
||||
/// in standard return value location.
|
||||
/// \return \b true if the location should be considered killed by call
|
||||
bool FuncProto::isAutoKillByCall(void) const
|
||||
|
||||
{
|
||||
if ((flags & auto_killbycall)!=0)
|
||||
return true; // The ProtoModel always does killbycall
|
||||
if (isOutputLocked())
|
||||
return true; // A locked output location is killbycall by definition
|
||||
return false;
|
||||
}
|
||||
|
||||
/// \brief Encode \b this to a stream as a \<prototype> element.
|
||||
///
|
||||
/// Save everything under the control of this prototype, which
|
||||
|
|
|
@ -88,12 +88,13 @@ public:
|
|||
smallsize_zext = 4, ///< Assume values that are below the max \b size are zero extended into this container
|
||||
smallsize_sext = 8, ///< Assume values that are below the max \b size are sign extended into this container
|
||||
// is_big_endian = 16, ///< Set if this value should be treated as big endian
|
||||
smallsize_inttype = 32, ///< Assume values that are below the max \b size are sign OR zero extended based on integer type
|
||||
smallsize_floatext = 64, ///< Assume values smaller than max \b size are floating-point extended to full size
|
||||
extracheck_high = 128, ///< Perform extra checks during parameter recovery on most sig portion of the double
|
||||
extracheck_low = 256, ///< Perform extra checks during parameter recovery on least sig portion of the double
|
||||
is_grouped = 512, ///< This entry is grouped with other entries
|
||||
overlapping = 0x100 ///< Overlaps an earlier entry (and doesn't consume additional resource slots)
|
||||
smallsize_inttype = 0x20, ///< Assume values that are below the max \b size are sign OR zero extended based on integer type
|
||||
smallsize_floatext = 0x40, ///< Assume values smaller than max \b size are floating-point extended to full size
|
||||
extracheck_high = 0x80, ///< Perform extra checks during parameter recovery on most sig portion of the double
|
||||
extracheck_low = 0x100, ///< Perform extra checks during parameter recovery on least sig portion of the double
|
||||
is_grouped = 0x200, ///< This entry is grouped with other entries
|
||||
overlapping = 0x400, ///< Overlaps an earlier entry (and doesn't consume additional resource slots)
|
||||
first_storage = 0x800 ///< Entry is first in its storage class
|
||||
};
|
||||
enum {
|
||||
no_containment, ///< Range neither contains nor is contained by a ParamEntry
|
||||
|
@ -113,6 +114,7 @@ private:
|
|||
int4 numslots; ///< (Maximum) number of slots that can store separate parameters
|
||||
JoinRecord *joinrec; ///< Non-null if this is logical variable from joined pieces
|
||||
static const ParamEntry *findEntryByStorage(const list<ParamEntry> &entryList,const VarnodeData &vn);
|
||||
void resolveFirst(list<ParamEntry> &curList); ///< Mark if \b this is the first ParamEntry in its storage class
|
||||
void resolveJoin(list<ParamEntry> &curList); ///< Make adjustments for a \e join ParamEntry
|
||||
void resolveOverlap(list<ParamEntry> &curList); ///< Make adjustments for ParamEntry that overlaps others
|
||||
|
||||
|
@ -132,6 +134,7 @@ public:
|
|||
bool isReverseStack(void) const { return ((flags & reverse_stack)!=0); } ///< Return \b true if parameters are allocated in reverse order
|
||||
bool isGrouped(void) const { return ((flags & is_grouped)!=0); } ///< Return \b true if \b this is grouped with other entries
|
||||
bool isOverlap(void) const { return ((flags & overlapping)!=0); } ///< Return \b true if \b this overlaps another entry
|
||||
bool isFirstInClass(void) const { return ((flags & first_storage)!=0); } ///< Return \b true if \b this is the first entry in the storage class
|
||||
bool subsumesDefinition(const ParamEntry &op2) const; ///< Does \b this subsume the definition of the given ParamEntry
|
||||
bool containedBy(const Address &addr,int4 sz) const; ///< Is this entry contained by the given range
|
||||
bool intersects(const Address &addr,int4 sz) const; ///< Does \b this intersect the given range in some way
|
||||
|
@ -554,6 +557,11 @@ public:
|
|||
/// \return the maximum number of passes across all parameters in \b this model
|
||||
virtual int4 getMaxDelay(void) const=0;
|
||||
|
||||
/// \brief Return \b true if ParamEntry locations should automatically be considered killed by call
|
||||
///
|
||||
/// \return \b true if automatically assume killbycall
|
||||
virtual bool isAutoKillByCall(void) const=0;
|
||||
|
||||
/// \brief Restore the model from an \<input> or \<output> element in the stream
|
||||
///
|
||||
/// \param decoder is the stream decoder
|
||||
|
@ -583,7 +591,7 @@ protected:
|
|||
vector<ParamEntryResolver *> resolverMap; ///< Map from space id to resolver
|
||||
list<ModelRule> modelRules; ///< Rules to apply when assigning addresses
|
||||
AddrSpace *spacebase; ///< Address space containing relative offset parameters
|
||||
const ParamEntry *findEntry(const Address &loc,int4 size) const; ///< Given storage location find matching ParamEntry
|
||||
const ParamEntry *findEntry(const Address &loc,int4 size,bool just) const; ///< Given storage location find matching ParamEntry
|
||||
const ParamEntry *selectUnreferenceEntry(int4 grp,type_class 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,vector<int4> &trialStart) const;
|
||||
|
@ -604,6 +612,8 @@ public:
|
|||
ParamListStandard(const ParamListStandard &op2); ///< Copy constructor
|
||||
virtual ~ParamListStandard(void);
|
||||
const list<ParamEntry> &getEntry(void) const { return entry; } ///< Get the list of parameter entries
|
||||
list<ParamEntry>::const_iterator getFirstIter(type_class type) const; ///< Get iterator to first entry in a storage class
|
||||
const ParamEntry *getStackEntry(void) const; ///< Get the stack entry
|
||||
uint4 assignAddressFallback(type_class resource,Datatype *tp,bool matchExact,vector<int4> &status,
|
||||
ParameterPieces ¶m) const;
|
||||
uint4 assignAddress(Datatype *dt,const PrototypePieces &proto,int4 pos,TypeFactory &tlst,
|
||||
|
@ -623,6 +633,7 @@ public:
|
|||
virtual bool isThisBeforeRetPointer(void) const { return thisbeforeret; }
|
||||
virtual void getRangeList(AddrSpace *spc,RangeList &res) const;
|
||||
virtual int4 getMaxDelay(void) const { return maxdelay; }
|
||||
virtual bool isAutoKillByCall(void) const { return false; }
|
||||
virtual void decode(Decoder &decoder,vector<EffectRecord> &effectlist,bool normalstack);
|
||||
virtual ParamList *clone(void) const;
|
||||
};
|
||||
|
@ -636,13 +647,19 @@ public:
|
|||
/// attempted again, and the return value is marked as a \e hidden return parameter
|
||||
/// to inform the input model.
|
||||
class ParamListStandardOut : public ParamListStandard {
|
||||
bool useFillinFallback; ///< If \b true, use fillinMapFallback
|
||||
void initialize(void); ///< Cache ModelRule information
|
||||
public:
|
||||
ParamListStandardOut(void) : ParamListStandard() {} ///< Constructor for use with decode()
|
||||
ParamListStandardOut(const ParamListStandardOut &op2) : ParamListStandard(op2) {} ///< Copy constructor
|
||||
ParamListStandardOut(const ParamListStandardOut &op2) : ParamListStandard(op2) {
|
||||
useFillinFallback = op2.useFillinFallback; } ///< Copy constructor
|
||||
void fillinMapFallback(ParamActive *active,bool firstOnly) const;
|
||||
virtual uint4 getType(void) const { return p_standard_out; }
|
||||
virtual void assignMap(const PrototypePieces &proto,TypeFactory &typefactory,vector<ParameterPieces> &res) const;
|
||||
virtual void fillinMap(ParamActive *active) const;
|
||||
virtual bool possibleParam(const Address &loc,int4 size) const;
|
||||
virtual bool isAutoKillByCall(void) const { return useFillinFallback; }
|
||||
virtual void decode(Decoder &decoder,vector<EffectRecord> &effectlist,bool normalstack);
|
||||
virtual ParamList *clone(void) const;
|
||||
};
|
||||
|
||||
|
@ -971,6 +988,11 @@ public:
|
|||
/// \return the maximum number of passes across all output parameters in \b this model
|
||||
int4 getMaxOutputDelay(void) const { return output->getMaxDelay(); }
|
||||
|
||||
/// \brief Does \b this model automatically consider potential output locations as killed by call
|
||||
///
|
||||
/// \return \b true if output locations should be considered killed by call
|
||||
bool isAutoKillByCall(void) const { return output->isAutoKillByCall(); }
|
||||
|
||||
/// \brief Is \b this a merged prototype model
|
||||
///
|
||||
/// \return \b true if \b this is a merged form of multiple independent prototype models
|
||||
|
@ -1323,7 +1345,8 @@ class FuncProto {
|
|||
is_constructor = 0x200, ///< Function is an (object-oriented) constructor
|
||||
is_destructor = 0x400, ///< Function is an (object-oriented) destructor
|
||||
has_thisptr= 0x800, ///< Function is a method with a 'this' pointer as an argument
|
||||
is_override = 0x1000 ///< Set if \b this prototype is created to override a single call site
|
||||
is_override = 0x1000, ///< Set if \b this prototype is created to override a single call site
|
||||
auto_killbycall = 0x2000 ///< Potential output storage should always be considered \e killed \e by \e call
|
||||
};
|
||||
ProtoModel *model; ///< Model of for \b this prototype
|
||||
ProtoStore *store; ///< Storage interface for parameters
|
||||
|
@ -1583,6 +1606,8 @@ public:
|
|||
/// \return the active set of flags for \b this prototype
|
||||
uint4 getComparableFlags(void) const { return (flags & (dotdotdot | is_constructor | is_destructor | has_thisptr )); }
|
||||
|
||||
bool isAutoKillByCall(void) const; ///< Is a potential output automatically considered \e killed \e by \e call
|
||||
|
||||
void encode(Encoder &encoder) const;
|
||||
void decode(Decoder &decoder,Architecture *glb);
|
||||
};
|
||||
|
|
|
@ -1426,7 +1426,8 @@ void Heritage::guardCalls(uint4 fl,const Address &addr,int4 size,vector<Varnode
|
|||
ParamActive *active = fc->getActiveOutput();
|
||||
int4 outputCharacter = fc->characterizeAsOutput(transAddr, size);
|
||||
if (outputCharacter != ParamEntry::no_containment) {
|
||||
effecttype = EffectRecord::killedbycall; // A potential output is always killed by call
|
||||
if (effecttype != EffectRecord::killedbycall && fc->isAutoKillByCall())
|
||||
effecttype = EffectRecord::killedbycall;
|
||||
if (outputCharacter == ParamEntry::contained_by) {
|
||||
if (tryOutputOverlapGuard(fc, addr, transAddr, size, write))
|
||||
effecttype = EffectRecord::unaffected; // Range is handled, don't do additional guarding
|
||||
|
|
|
@ -1266,6 +1266,6 @@ ElementId ELEM_VAL = ElementId("val",8);
|
|||
ElementId ELEM_VALUE = ElementId("value",9);
|
||||
ElementId ELEM_VOID = ElementId("void",10);
|
||||
|
||||
ElementId ELEM_UNKNOWN = ElementId("XMLunknown",285); // Number serves as next open index
|
||||
ElementId ELEM_UNKNOWN = ElementId("XMLunknown",286); // Number serves as next open index
|
||||
|
||||
} // End namespace ghidra
|
||||
|
|
|
@ -29,24 +29,150 @@ ElementId ELEM_POSITION = ElementId("position",280);
|
|||
ElementId ELEM_VARARGS = ElementId("varargs",281);
|
||||
ElementId ELEM_HIDDEN_RETURN = ElementId("hidden_return",282);
|
||||
ElementId ELEM_JOIN_PER_PRIMITIVE = ElementId("join_per_primitive",283);
|
||||
ElementId ELEM_JOIN_DUAL_CLASS = ElementId("join_dual_class",285);
|
||||
|
||||
/// \brief Extract an ordered list of primitive data-types making up the given data-type
|
||||
/// \brief Check that a big Primitive properly overlaps smaller Primitives
|
||||
///
|
||||
/// The primitive data-types are passed back in an array. If the given data-type is already
|
||||
/// primitive, it is passed back as is. Otherwise if it is composite, its components are
|
||||
/// recursively listed. If a maximum number of extracted primitives is exceeded, or if the
|
||||
/// primitives are not properly aligned, or if a non-primitive non-composite data-type is
|
||||
/// encountered, false is returned.
|
||||
/// If the big Primitive does not properly overlap the smaller Primitives starting at the given \b point,
|
||||
/// return -1. Otherwise, if the big Primitive is floating-point, add the overlapped primitives to the
|
||||
/// common refinement list, or if not a floating-point, add the big Primitive to the list.
|
||||
/// (Integer primitives are \e preferred over floating-point primitives in this way) Return the index of
|
||||
/// the next primitive after the overlap.
|
||||
/// \param res holds the common refinement list
|
||||
/// \param small is the list of Primitives that are overlapped
|
||||
/// \param point is the index of the first overlap
|
||||
/// \param big is the big overlapping Primitive
|
||||
/// \return the index of the next Primitive after the overlap or -1 if the overlap is invalid
|
||||
int4 PrimitiveExtractor::checkOverlap(vector<Primitive> &res,vector<Primitive> &small,int4 point,Primitive &big)
|
||||
|
||||
{
|
||||
int4 endOff = big.offset + big.dt->getAlignSize();
|
||||
// If big data-type is a float, let smaller primitives override it, otherwise we keep the big primitive
|
||||
bool useSmall = big.dt->getMetatype() == TYPE_FLOAT;
|
||||
while(point < small.size()) {
|
||||
int4 curOff = small[point].offset;
|
||||
if (curOff >= endOff) break;
|
||||
curOff += small[point].dt->getAlignSize();
|
||||
if (curOff > endOff)
|
||||
return -1; // Improper overlap of the end of big
|
||||
if (useSmall)
|
||||
res.push_back(small[point]);
|
||||
point += 1;
|
||||
}
|
||||
if (!useSmall) // If big data-type was preferred
|
||||
res.push_back(big); // use big Primitive in the refinement
|
||||
return point;
|
||||
}
|
||||
|
||||
/// \brief Overwrite \b first list with common refinement of \b first and \b second
|
||||
///
|
||||
/// Given two sets of overlapping Primitives, find a \e common \e refinement of the lists.
|
||||
/// If there is any partial overlap of two Primitives, \b false is returned.
|
||||
/// If the same primitive data-type occurs at the same offset, it is included in the refinement.
|
||||
/// Otherwise an integer data-type is preferred over a floating-point data-type, or a bigger
|
||||
/// primitive is preferred over smaller overlapping primitives.
|
||||
/// The final refinement replaces the \b first list.
|
||||
/// \param first is the first list of Primitives
|
||||
/// \param second is the second list
|
||||
/// \return \b true if a refinement was successfully constructed
|
||||
bool PrimitiveExtractor::commonRefinement(vector<Primitive> &first,vector<Primitive> &second)
|
||||
|
||||
{
|
||||
int4 firstPoint = 0;
|
||||
int4 secondPoint = 0;
|
||||
vector<Primitive> common;
|
||||
while(firstPoint < first.size() && secondPoint < second.size()) {
|
||||
Primitive &firstElement( first[firstPoint] );
|
||||
Primitive &secondElement( second[secondPoint] );
|
||||
if (firstElement.offset < secondElement.offset &&
|
||||
firstElement.offset + firstElement.dt->getAlignSize() <= secondElement.offset) {
|
||||
common.push_back(firstElement);
|
||||
firstPoint += 1;
|
||||
continue;
|
||||
}
|
||||
if (secondElement.offset < firstElement.offset &&
|
||||
secondElement.offset + secondElement.dt->getAlignSize() <= firstElement.offset) {
|
||||
common.push_back(secondElement);
|
||||
secondPoint += 1;
|
||||
continue;
|
||||
}
|
||||
if (firstElement.dt->getAlignSize() >= secondElement.dt->getAlignSize()) {
|
||||
secondPoint = checkOverlap(common,second,secondPoint,firstElement);
|
||||
if (secondPoint < 0) return false;
|
||||
firstPoint += 1;
|
||||
}
|
||||
else {
|
||||
firstPoint = checkOverlap(common,first,firstPoint,secondElement);
|
||||
if (firstPoint < 0) return false;
|
||||
secondPoint += 1;
|
||||
}
|
||||
}
|
||||
// Add any tail primitives from either list
|
||||
while(firstPoint < first.size()) {
|
||||
common.push_back(first[firstPoint]);
|
||||
firstPoint += 1;
|
||||
}
|
||||
while(secondPoint < second.size()) {
|
||||
common.push_back(second[secondPoint]);
|
||||
secondPoint += 1;
|
||||
}
|
||||
first.swap(common); // Replace first with the refinement
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Form a primitive list for each field of the union. Then, if possible, form a common refinement
|
||||
/// of all the primitive lists and add to the end of \b this extractor's list.
|
||||
/// \param dt is the union data-type
|
||||
/// \param max is the maximum number primitives allowed for \b this extraction
|
||||
/// \param offset is the starting offset of the union within the parent
|
||||
/// \return \b true if a common refinement was found and appended
|
||||
bool PrimitiveExtractor::handleUnion(TypeUnion *dt,int4 max,int4 offset)
|
||||
|
||||
{
|
||||
if ((flags & union_invalid) != 0)
|
||||
return false;
|
||||
int4 num = dt->numDepend();
|
||||
if (num == 0)
|
||||
return false;
|
||||
const TypeField *curField = dt->getField(0);
|
||||
PrimitiveExtractor common(curField->type,false,offset + curField->offset,max);
|
||||
if (!common.isValid())
|
||||
return false;
|
||||
for(int4 i=1;i<num;++i) {
|
||||
curField = dt->getField(i);
|
||||
PrimitiveExtractor next(curField->type,false,offset + curField->offset,max);
|
||||
if (!next.isValid())
|
||||
return false;
|
||||
if (!commonRefinement(common.primitives,next.primitives))
|
||||
return false;
|
||||
}
|
||||
if (primitives.size() + common.primitives.size() > max)
|
||||
return false;
|
||||
for(int4 i=0;i<common.primitives.size();++i)
|
||||
primitives.push_back(common.primitives[i]);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// An array of the primitive data-types, with their associated offsets, is constructed.
|
||||
/// If the given data-type is already primitive it is put in the array by itself. Otherwise
|
||||
/// if it is composite, its components are recursively added to the array.
|
||||
/// Boolean properties about the primitives encountered are recorded:
|
||||
/// - Are any of the primitives \b undefined
|
||||
/// - Are all the primitives properly aligned.
|
||||
///
|
||||
/// If a maximum number of extracted primitives is exceeded, or if an illegal
|
||||
/// data-type is encountered (\b void or other internal data-type) false is returned.
|
||||
/// \param dt is the given data-type to extract primitives from
|
||||
/// \param max is the maximum number of primitives to extract before giving up
|
||||
/// \param res will hold the list of primitives
|
||||
/// \param offset is the starting offset to associate with the first primitive
|
||||
/// \return \b true if all primitives were extracted
|
||||
bool DatatypeFilter::extractPrimitives(Datatype *dt,int4 max,vector<Datatype *> &res)
|
||||
bool PrimitiveExtractor::extract(Datatype *dt,int4 max,int4 offset)
|
||||
|
||||
{
|
||||
switch(dt->getMetatype()) {
|
||||
case TYPE_UNKNOWN:
|
||||
return false; // Do not consider undefined data-types as primitive
|
||||
flags |= unknown_element; ///< Mark that the data-type contains an unknown primitive
|
||||
// fallthru
|
||||
case TYPE_INT:
|
||||
case TYPE_UINT:
|
||||
case TYPE_BOOL:
|
||||
|
@ -54,46 +180,63 @@ bool DatatypeFilter::extractPrimitives(Datatype *dt,int4 max,vector<Datatype *>
|
|||
case TYPE_FLOAT:
|
||||
case TYPE_PTR:
|
||||
case TYPE_PTRREL:
|
||||
if (res.size() >= max)
|
||||
if (primitives.size() >= max)
|
||||
return false;
|
||||
res.push_back(dt);
|
||||
primitives.emplace_back(dt,offset);
|
||||
return true;
|
||||
case TYPE_ARRAY:
|
||||
{
|
||||
int4 numEls = ((TypeArray *)dt)->numElements();
|
||||
Datatype *base = ((TypeArray *)dt)->getBase();
|
||||
for(int4 i=0;i<numEls;++i) {
|
||||
if (!extractPrimitives(base,max,res))
|
||||
if (!extract(base,max,offset))
|
||||
return false;
|
||||
offset += base->getAlignSize();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case TYPE_UNION:
|
||||
return handleUnion((TypeUnion *)dt,max,offset);
|
||||
case TYPE_STRUCT:
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
TypeStruct *structPtr = (TypeStruct *)dt;
|
||||
int4 curOff = 0;
|
||||
vector<TypeField>::const_iterator enditer = structPtr->endField();
|
||||
int4 expectedOff = offset;
|
||||
for(vector<TypeField>::const_iterator iter=structPtr->beginField();iter!=enditer;++iter) {
|
||||
Datatype *compDt = (*iter).type;
|
||||
int4 nextOff = (*iter).offset;
|
||||
int4 align = dt->getAlignment();
|
||||
int4 rem = curOff % align;
|
||||
int4 curOff = (*iter).offset + offset;
|
||||
int4 align = compDt->getAlignment();
|
||||
if (curOff % align != 0)
|
||||
flags |= unaligned;
|
||||
int4 rem = expectedOff % align;
|
||||
if (rem != 0) {
|
||||
curOff += (align - rem);
|
||||
expectedOff += (align - rem);
|
||||
}
|
||||
if (curOff != nextOff) {
|
||||
return false;
|
||||
if (expectedOff != curOff) {
|
||||
flags |= extra_space;
|
||||
}
|
||||
curOff = nextOff + compDt->getAlignSize();
|
||||
if (!extractPrimitives(compDt,max,res))
|
||||
if (!extract(compDt,max,curOff))
|
||||
return false;
|
||||
expectedOff = curOff + compDt->getAlignSize();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// \param dt is data-type extract from
|
||||
/// \param unionIllegal is \b true if unions encountered during extraction are considered illegal
|
||||
/// \param offset is the starting offset to associate with the data-type
|
||||
/// \param max is the maximum number of primitives to extract before giving up
|
||||
PrimitiveExtractor::PrimitiveExtractor(Datatype *dt,bool unionIllegal,int offset,int4 max)
|
||||
|
||||
{
|
||||
flags = unionIllegal ? union_invalid : 0;
|
||||
if (!extract(dt,max,offset))
|
||||
flags |= invalid;
|
||||
}
|
||||
|
||||
/// \param decoder is the given stream decoder
|
||||
/// \return the new data-type filter instance
|
||||
DatatypeFilter *DatatypeFilter::decodeFilter(Decoder &decoder)
|
||||
|
@ -196,14 +339,15 @@ bool HomogeneousAggregate::filter(Datatype *dt) const
|
|||
type_metatype meta = dt->getMetatype();
|
||||
if (meta != TYPE_ARRAY && meta != TYPE_STRUCT)
|
||||
return false;
|
||||
vector<Datatype *> res;
|
||||
if (!extractPrimitives(dt, 4, res) || res.empty())
|
||||
PrimitiveExtractor primitives(dt,true,0,4);
|
||||
if (!primitives.isValid() || primitives.size() == 0 || primitives.containsUnknown()
|
||||
|| !primitives.isAligned() || primitives.containsHoles())
|
||||
return false;
|
||||
Datatype *base = res[0];
|
||||
Datatype *base = primitives.get(0).dt;
|
||||
if (base->getMetatype() != metaType)
|
||||
return false;
|
||||
for(int4 i=1;i<res.size();++i) {
|
||||
if (res[i] != base)
|
||||
for(int4 i=1;i<primitives.size();++i) {
|
||||
if (primitives.get(i).dt != base)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -332,6 +476,12 @@ void DatatypeMatchFilter::decode(Decoder &decoder)
|
|||
decoder.closeElement(elemId);
|
||||
}
|
||||
|
||||
bool AssignAction::fillinOutputMap(ParamActive *active) const
|
||||
|
||||
{
|
||||
return false; // Default implementation for an inactive action
|
||||
}
|
||||
|
||||
/// \brief Read the next model rule action element from the stream
|
||||
///
|
||||
/// Allocate the action object corresponding to the element and configure it.
|
||||
|
@ -366,6 +516,9 @@ AssignAction *AssignAction::decodeAction(Decoder &decoder,const ParamListStandar
|
|||
}
|
||||
action = new MultiMemberAssign(TYPECLASS_GENERAL,false,consumeMostSig,res);
|
||||
}
|
||||
else if (elemId == ELEM_JOIN_DUAL_CLASS) {
|
||||
action = new MultiSlotDualAssign(res);
|
||||
}
|
||||
else
|
||||
throw DecoderError("Expecting model rule action");
|
||||
action->decode(decoder);
|
||||
|
@ -397,15 +550,7 @@ AssignAction *AssignAction::decodeSideeffect(Decoder &decoder,const ParamListSta
|
|||
void GotoStack::initializeEntry(void)
|
||||
|
||||
{
|
||||
const list<ParamEntry> &entries(resource->getEntry());
|
||||
list<ParamEntry>::const_iterator iter;
|
||||
for(iter=entries.begin();iter!=entries.end();++iter) {
|
||||
const ParamEntry &entry( *iter );
|
||||
if (!entry.isExclusion() && entry.getSpace()->getType() == IPTR_SPACEBASE) {
|
||||
stackEntry = &entry;
|
||||
break;
|
||||
}
|
||||
}
|
||||
stackEntry = resource->getStackEntry();
|
||||
if (stackEntry == (const ParamEntry *)0)
|
||||
throw LowlevelError("Cannot find matching <pentry> for action: gotostack");
|
||||
}
|
||||
|
@ -416,12 +561,14 @@ GotoStack::GotoStack(const ParamListStandard *res,int4 val)
|
|||
: AssignAction(res)
|
||||
{
|
||||
stackEntry = (const ParamEntry *)0;
|
||||
fillinOutputActive = true;
|
||||
}
|
||||
|
||||
GotoStack::GotoStack(const ParamListStandard *res)
|
||||
: AssignAction(res)
|
||||
{
|
||||
stackEntry = (const ParamEntry *)0;
|
||||
fillinOutputActive = true;
|
||||
initializeEntry();
|
||||
}
|
||||
|
||||
|
@ -435,6 +582,23 @@ uint4 GotoStack::assignAddress(Datatype *dt,const PrototypePieces &proto,int4 po
|
|||
return success;
|
||||
}
|
||||
|
||||
bool GotoStack::fillinOutputMap(ParamActive *active) const
|
||||
|
||||
{
|
||||
int4 count = 0;
|
||||
for(int4 i=0;i<active->getNumTrials();++i) {
|
||||
ParamTrial &trial(active->getTrial(i));
|
||||
const ParamEntry *entry = trial.getEntry();
|
||||
if (entry == (const ParamEntry *)0) break;
|
||||
if (entry != stackEntry)
|
||||
return false;
|
||||
count += 1;
|
||||
if (count > 1)
|
||||
return false;
|
||||
}
|
||||
return (count == 1);
|
||||
}
|
||||
|
||||
void GotoStack::decode(Decoder &decoder)
|
||||
|
||||
{
|
||||
|
@ -477,20 +641,9 @@ void ConvertToPointer::decode(Decoder &decoder)
|
|||
void MultiSlotAssign::initializeEntries(void)
|
||||
|
||||
{
|
||||
const list<ParamEntry> &entries(resource->getEntry());
|
||||
firstIter = entries.end();
|
||||
list<ParamEntry>::const_iterator iter;
|
||||
for(iter=entries.begin();iter!=entries.end();++iter) {
|
||||
const ParamEntry &entry( *iter );
|
||||
if (firstIter == entries.end() && entry.isExclusion() && entry.getType() == resourceType &&
|
||||
entry.getAllGroups().size() == 1)
|
||||
firstIter = iter; // First matching resource size
|
||||
if (!entry.isExclusion() && entry.getSpace()->getType() == IPTR_SPACEBASE) {
|
||||
stackEntry = &entry;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (firstIter == entries.end())
|
||||
firstIter = resource->getFirstIter(resourceType);
|
||||
stackEntry = resource->getStackEntry();
|
||||
if (firstIter == resource->getEntry().end())
|
||||
throw LowlevelError("Could not find matching resources for action: join");
|
||||
if (consumeFromStack && stackEntry == (const ParamEntry *)0)
|
||||
throw LowlevelError("Cannot find matching <pentry> for action: join");
|
||||
|
@ -502,6 +655,7 @@ MultiSlotAssign::MultiSlotAssign(const ParamListStandard *res)
|
|||
: AssignAction(res)
|
||||
{
|
||||
resourceType = TYPECLASS_GENERAL; // Join general purpose registers
|
||||
fillinOutputActive = true;
|
||||
uint4 listType = res->getType();
|
||||
// Consume from stack on input parameters by default
|
||||
consumeFromStack = (listType != ParamList::p_register_out && listType != ParamList::p_standard_out);
|
||||
|
@ -520,6 +674,7 @@ MultiSlotAssign::MultiSlotAssign(type_class store,bool stack,bool mostSig,bool a
|
|||
: AssignAction(res)
|
||||
{
|
||||
resourceType = store;
|
||||
fillinOutputActive = true;
|
||||
consumeFromStack = stack;
|
||||
consumeMostSig = mostSig;
|
||||
enforceAlignment = align;
|
||||
|
@ -620,6 +775,55 @@ uint4 MultiSlotAssign::assignAddress(Datatype *dt,const PrototypePieces &proto,i
|
|||
return success;
|
||||
}
|
||||
|
||||
bool MultiSlotAssign::fillinOutputMap(ParamActive *active) const
|
||||
|
||||
{
|
||||
int4 count = 0;
|
||||
int4 curGroup = -1;
|
||||
int4 partial = -1;
|
||||
for(int4 i=0;i<active->getNumTrials();++i) {
|
||||
ParamTrial &trial(active->getTrial(i));
|
||||
const ParamEntry *entry = trial.getEntry();
|
||||
if (entry == (const ParamEntry *)0) break;
|
||||
if (entry->getType() != resourceType) // Trials must come from action's type_class
|
||||
return false;
|
||||
if (count == 0) {
|
||||
if (!entry->isFirstInClass())
|
||||
return false; // Trials must start on first entry of the type_class
|
||||
}
|
||||
else {
|
||||
if (entry->getGroup() != curGroup + 1) // Trials must be consecutive
|
||||
return false;
|
||||
}
|
||||
curGroup = entry->getGroup();
|
||||
if (trial.getSize() != entry->getSize()) {
|
||||
if (partial != -1)
|
||||
return false; // At most, one trial can be partial size
|
||||
partial = i;
|
||||
}
|
||||
count += 1;
|
||||
}
|
||||
if (partial != -1) {
|
||||
if (justifyRight) {
|
||||
if (partial != 0) return false;
|
||||
}
|
||||
else {
|
||||
if (partial != count - 1) return false;
|
||||
}
|
||||
ParamTrial &trial(active->getTrial(partial));
|
||||
if (justifyRight == consumeMostSig) {
|
||||
if (trial.getOffset() != 0)
|
||||
return false; // Partial entry must be least sig bytes
|
||||
}
|
||||
else {
|
||||
if (trial.getOffset() + trial.getSize() != trial.getEntry()->getSize()) {
|
||||
return false; // Partial entry must be most sig bytes
|
||||
}
|
||||
}
|
||||
}
|
||||
return (count > 0);
|
||||
}
|
||||
|
||||
void MultiSlotAssign::decode(Decoder &decoder)
|
||||
|
||||
{
|
||||
|
@ -648,6 +852,7 @@ MultiMemberAssign::MultiMemberAssign(type_class store,bool stack,bool mostSig,co
|
|||
resourceType = store;
|
||||
consumeFromStack = stack;
|
||||
consumeMostSig = mostSig;
|
||||
fillinOutputActive = true;
|
||||
}
|
||||
|
||||
uint4 MultiMemberAssign::assignAddress(Datatype *dt,const PrototypePieces &proto,int4 pos,TypeFactory &tlist,
|
||||
|
@ -655,12 +860,13 @@ uint4 MultiMemberAssign::assignAddress(Datatype *dt,const PrototypePieces &proto
|
|||
{
|
||||
vector<int4> tmpStatus = status;
|
||||
vector<VarnodeData> pieces;
|
||||
vector<Datatype *> primitives;
|
||||
if (!DatatypeFilter::extractPrimitives(dt,16,primitives) || primitives.empty())
|
||||
PrimitiveExtractor primitives(dt,false,0,16);
|
||||
if (!primitives.isValid() || primitives.size() == 0 || primitives.containsUnknown()
|
||||
|| !primitives.isAligned() || primitives.containsHoles())
|
||||
return fail;
|
||||
ParameterPieces param;
|
||||
for(int4 i=0;i<primitives.size();++i) {
|
||||
Datatype *curType = primitives[i];
|
||||
Datatype *curType = primitives.get(i).dt;
|
||||
if (resource->assignAddressFallback(resourceType, curType, !consumeFromStack, tmpStatus,param) == fail)
|
||||
return fail;
|
||||
pieces.push_back(VarnodeData());
|
||||
|
@ -687,6 +893,33 @@ uint4 MultiMemberAssign::assignAddress(Datatype *dt,const PrototypePieces &proto
|
|||
return success;
|
||||
}
|
||||
|
||||
bool MultiMemberAssign::fillinOutputMap(ParamActive *active) const
|
||||
|
||||
{
|
||||
int4 count = 0;
|
||||
int4 curGroup = -1;
|
||||
for(int4 i=0;i<active->getNumTrials();++i) {
|
||||
ParamTrial &trial(active->getTrial(i));
|
||||
const ParamEntry *entry = trial.getEntry();
|
||||
if (entry == (const ParamEntry *)0) break;
|
||||
if (entry->getType() != resourceType) // Trials must come from action's type_class
|
||||
return false;
|
||||
if (count == 0) {
|
||||
if (!entry->isFirstInClass())
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
if (entry->getGroup() != curGroup + 1) // Trials must be consecutive
|
||||
return false;
|
||||
}
|
||||
curGroup = entry->getGroup();
|
||||
if (trial.getOffset() != 0)
|
||||
return false; // Entry must be justified
|
||||
count += 1;
|
||||
}
|
||||
return (count > 0);
|
||||
}
|
||||
|
||||
void MultiMemberAssign::decode(Decoder &decoder)
|
||||
|
||||
{
|
||||
|
@ -701,10 +934,252 @@ void MultiMemberAssign::decode(Decoder &decoder)
|
|||
decoder.closeElement(elemId);
|
||||
}
|
||||
|
||||
/// Find the first ParamEntry matching the \b baseType, and the first matching \b altType.
|
||||
void MultiSlotDualAssign::initializeEntries(void)
|
||||
|
||||
{
|
||||
baseIter = resource->getFirstIter(baseType);
|
||||
altIter = resource->getFirstIter(altType);
|
||||
list<ParamEntry>::const_iterator enditer = resource->getEntry().end();
|
||||
if (baseIter == enditer || altIter == enditer)
|
||||
throw LowlevelError("Could not find matching resources for action: join_dual_class");
|
||||
tileSize = (*baseIter).getSize();
|
||||
if (tileSize != (*altIter).getSize())
|
||||
throw LowlevelError("Storage class register sizes do not match for action: join_dual_class");
|
||||
}
|
||||
|
||||
/// \brief Get the first unused ParamEntry that matches the given storage class
|
||||
///
|
||||
/// \param iter points to the starting entry to search
|
||||
/// \param storage is the given storage class to match
|
||||
/// \param status is the usage information for the entries
|
||||
/// \return the iterator to the unused ParamEntry
|
||||
list<ParamEntry>::const_iterator MultiSlotDualAssign::getFirstUnused(list<ParamEntry>::const_iterator iter,
|
||||
type_class storage,vector<int4> &status) const
|
||||
{
|
||||
list<ParamEntry>::const_iterator endIter = resource->getEntry().end();
|
||||
for(;iter != endIter; ++iter) {
|
||||
const ParamEntry &entry( *iter );
|
||||
if (!entry.isExclusion())
|
||||
break; // Reached end of resource list
|
||||
if (entry.getType() != storage || entry.getAllGroups().size() != 1)
|
||||
continue; // Not a single register from desired resource
|
||||
if (status[entry.getGroup()] != 0)
|
||||
continue; // Already consumed
|
||||
return iter;
|
||||
}
|
||||
return endIter;
|
||||
}
|
||||
|
||||
/// \brief Get the storage class to use for the specific section of the data-type
|
||||
///
|
||||
/// For the section starting at \b off extending through \b tileSize bytes, if any primitive overlaps
|
||||
/// the boundary of the section, return -1. Otherwise, if all the primitive data-types in the section
|
||||
/// match the alternate storage class, return 1, or if one or more does not match, return 0.
|
||||
/// The \b index of the first primitive after the start of the section is provided and is then updated
|
||||
/// to be the first primitive after the end of the section.
|
||||
/// \param primitives is the list of primitive data-types making up the data-type
|
||||
/// \param off is the starting offset of the section
|
||||
/// \param index is the index of the first primitive in the section
|
||||
/// \return 0 for a base tile, 1 for an alternate tile, -1 for boundary overlaps
|
||||
int4 MultiSlotDualAssign::getTileClass(const PrimitiveExtractor &primitives,int4 off,int4 &index) const
|
||||
|
||||
{
|
||||
int4 res = 1;
|
||||
int4 count = 0;
|
||||
int4 endBoundary = off + tileSize;
|
||||
while(index < primitives.size()) {
|
||||
const PrimitiveExtractor::Primitive &element( primitives.get(index) );
|
||||
if (element.offset < off) return -1;
|
||||
if (element.offset >= endBoundary) break;
|
||||
if (element.offset + element.dt->getSize() > endBoundary) return -1;
|
||||
count += 1;
|
||||
index += 1;
|
||||
type_class storage = metatype2typeclass(element.dt->getMetatype());
|
||||
if (storage != altType)
|
||||
res = 0;
|
||||
}
|
||||
if (count == 0) return -1; // Must be at least one primitive in section
|
||||
return res;
|
||||
}
|
||||
|
||||
/// Set default configuration
|
||||
/// \param res is the new resource set to associate with \b this action
|
||||
MultiSlotDualAssign::MultiSlotDualAssign(const ParamListStandard *res)
|
||||
: AssignAction(res)
|
||||
{
|
||||
fillinOutputActive = true;
|
||||
baseType = TYPECLASS_GENERAL; // Tile from general purpose registers
|
||||
altType = TYPECLASS_FLOAT; // Use specialized registers for floating-point components
|
||||
consumeMostSig = false;
|
||||
justifyRight = false;
|
||||
AddrSpace *spc = res->getSpacebase();
|
||||
if (spc != (AddrSpace *)0 && spc->isBigEndian()) {
|
||||
consumeMostSig = true;
|
||||
justifyRight = true;
|
||||
}
|
||||
tileSize = 0;
|
||||
}
|
||||
|
||||
MultiSlotDualAssign::MultiSlotDualAssign(type_class baseStore,type_class altStore,bool mostSig,bool justRight,
|
||||
const ParamListStandard *res)
|
||||
: AssignAction(res)
|
||||
{
|
||||
fillinOutputActive = true;
|
||||
baseType = baseStore;
|
||||
altType = altStore;
|
||||
consumeMostSig = mostSig;
|
||||
justifyRight = justRight;
|
||||
initializeEntries();
|
||||
}
|
||||
|
||||
uint4 MultiSlotDualAssign::assignAddress(Datatype *dt,const PrototypePieces &proto,int4 pos,TypeFactory &tlist,
|
||||
vector<int4> &status,ParameterPieces &res) const
|
||||
{
|
||||
PrimitiveExtractor primitives(dt,false,0,1024);
|
||||
if (!primitives.isValid() || primitives.size() == 0 || primitives.containsHoles())
|
||||
return fail;
|
||||
int4 primitiveIndex = 0;
|
||||
vector<int4> tmpStatus = status;
|
||||
vector<VarnodeData> pieces;
|
||||
int4 typeSize = dt->getSize();
|
||||
int4 sizeLeft = typeSize;
|
||||
list<ParamEntry>::const_iterator iterBase = baseIter;
|
||||
list<ParamEntry>::const_iterator iterAlt = altIter;
|
||||
list<ParamEntry>::const_iterator endIter = resource->getEntry().end();
|
||||
while(sizeLeft > 0) {
|
||||
list<ParamEntry>::const_iterator iter;
|
||||
int4 iterType = getTileClass(primitives, typeSize-sizeLeft, primitiveIndex);
|
||||
if (iterType < 0)
|
||||
return fail;
|
||||
if (iterType == 0) {
|
||||
iter = iterBase = getFirstUnused(iterBase, baseType, tmpStatus);
|
||||
}
|
||||
else {
|
||||
iter = iterAlt = getFirstUnused(iterAlt, altType, tmpStatus);
|
||||
}
|
||||
if (iter == endIter)
|
||||
return fail; // Out of the particular resource
|
||||
const ParamEntry &entry( *iter );
|
||||
int4 trialSize = entry.getSize();
|
||||
Address addr = entry.getAddrBySlot(tmpStatus[entry.getGroup()], trialSize,1);
|
||||
tmpStatus[entry.getGroup()] = -1; // Consume the register
|
||||
pieces.push_back(VarnodeData());
|
||||
pieces.back().space = addr.getSpace();
|
||||
pieces.back().offset = addr.getOffset();
|
||||
pieces.back().size = trialSize;
|
||||
sizeLeft -= trialSize;
|
||||
}
|
||||
if (sizeLeft < 0) { // Have odd data-type size
|
||||
if (justifyRight) {
|
||||
pieces.front().offset += -sizeLeft; // Initial bytes of first entry are padding
|
||||
pieces.front().size += sizeLeft;
|
||||
}
|
||||
else {
|
||||
pieces.back().size += sizeLeft;
|
||||
}
|
||||
}
|
||||
status = tmpStatus; // Commit resource usage for all the pieces
|
||||
res.flags = 0;
|
||||
res.type = dt;
|
||||
if (pieces.size() == 1) {
|
||||
res.addr = pieces[0].getAddr();
|
||||
return success;
|
||||
}
|
||||
if (!consumeMostSig) {
|
||||
vector<VarnodeData> reverse;
|
||||
for(int4 i=pieces.size()-1;i>=0;--i)
|
||||
reverse.push_back(pieces[i]);
|
||||
pieces.swap(reverse);
|
||||
}
|
||||
JoinRecord *joinRecord = tlist.getArch()->findAddJoin(pieces, 0);
|
||||
res.addr = joinRecord->getUnified().getAddr();
|
||||
return success;
|
||||
}
|
||||
|
||||
bool MultiSlotDualAssign::fillinOutputMap(ParamActive *active) const
|
||||
|
||||
{
|
||||
int4 count = 0;
|
||||
int4 curGroup = -1;
|
||||
int4 partial = -1;
|
||||
type_class resourceType = TYPECLASS_GENERAL;
|
||||
for(int4 i=0;i<active->getNumTrials();++i) {
|
||||
ParamTrial &trial(active->getTrial(i));
|
||||
const ParamEntry *entry = trial.getEntry();
|
||||
if (entry == (const ParamEntry *)0) break;
|
||||
if (count == 0) {
|
||||
resourceType = entry->getType();
|
||||
if (resourceType != baseType && resourceType != altType)
|
||||
return false;
|
||||
}
|
||||
else if (entry->getType() != resourceType) // Trials must come from action's type_class
|
||||
return false;
|
||||
if (count == 0) {
|
||||
if (!entry->isFirstInClass())
|
||||
return false; // Trials must start on first entry of the type_class
|
||||
}
|
||||
else {
|
||||
if (entry->getGroup() != curGroup + 1) // Trials must be consecutive
|
||||
return false;
|
||||
}
|
||||
curGroup = entry->getGroup();
|
||||
if (trial.getSize() != entry->getSize()) {
|
||||
if (partial != -1)
|
||||
return false; // At most, one trial can be partial size
|
||||
partial = i;
|
||||
}
|
||||
count += 1;
|
||||
}
|
||||
if (partial != -1) {
|
||||
if (justifyRight) {
|
||||
if (partial != 0) return false;
|
||||
}
|
||||
else {
|
||||
if (partial != count - 1) return false;
|
||||
}
|
||||
ParamTrial &trial(active->getTrial(partial));
|
||||
if (justifyRight == consumeMostSig) {
|
||||
if (trial.getOffset() != 0)
|
||||
return false; // Partial entry must be least sig bytes
|
||||
}
|
||||
else {
|
||||
if (trial.getOffset() + trial.getSize() != trial.getEntry()->getSize()) {
|
||||
return false; // Partial entry must be most sig bytes
|
||||
}
|
||||
}
|
||||
}
|
||||
return (count > 0);
|
||||
|
||||
}
|
||||
|
||||
void MultiSlotDualAssign::decode(Decoder &decoder)
|
||||
|
||||
{
|
||||
uint4 elemId = decoder.openElement(ELEM_JOIN_DUAL_CLASS);
|
||||
for(;;) {
|
||||
uint4 attribId = decoder.getNextAttributeId();
|
||||
if (attribId == 0) break;
|
||||
if (attribId == ATTRIB_REVERSEJUSTIFY) {
|
||||
if (decoder.readBool())
|
||||
justifyRight = !justifyRight;
|
||||
}
|
||||
else if (attribId == ATTRIB_STORAGE || attribId == ATTRIB_A) {
|
||||
baseType = string2typeclass(decoder.readString());
|
||||
}
|
||||
else if (attribId == ATTRIB_B) {
|
||||
altType = string2typeclass(decoder.readString());
|
||||
}
|
||||
}
|
||||
decoder.closeElement(elemId);
|
||||
initializeEntries(); // Need new firstIter
|
||||
}
|
||||
|
||||
ConsumeAs::ConsumeAs(type_class store,const ParamListStandard *res)
|
||||
: AssignAction(res)
|
||||
{
|
||||
resourceType = store;
|
||||
fillinOutputActive = true;
|
||||
}
|
||||
|
||||
uint4 ConsumeAs::assignAddress(Datatype *dt,const PrototypePieces &proto,int4 pos,TypeFactory &tlist,
|
||||
|
@ -713,6 +1188,27 @@ uint4 ConsumeAs::assignAddress(Datatype *dt,const PrototypePieces &proto,int4 po
|
|||
return resource->assignAddressFallback(resourceType, dt, true, status, res);
|
||||
}
|
||||
|
||||
bool ConsumeAs::fillinOutputMap(ParamActive *active) const
|
||||
|
||||
{
|
||||
int4 count = 0;
|
||||
for(int4 i=0;i<active->getNumTrials();++i) {
|
||||
ParamTrial &trial(active->getTrial(i));
|
||||
const ParamEntry *entry = trial.getEntry();
|
||||
if (entry == (const ParamEntry *)0) break;
|
||||
if (entry->getType() != resourceType) // Trials must come from action's type_class
|
||||
return false;
|
||||
if (!entry->isFirstInClass())
|
||||
return false;
|
||||
count += 1;
|
||||
if (count > 1)
|
||||
return false;
|
||||
if (trial.getOffset() != 0)
|
||||
return false; // Entry must be justified
|
||||
}
|
||||
return (count > 0);
|
||||
}
|
||||
|
||||
void ConsumeAs::decode(Decoder &decoder)
|
||||
|
||||
{
|
||||
|
@ -761,17 +1257,8 @@ void HiddenReturnAssign::decode(Decoder &decoder)
|
|||
void ConsumeExtra::initializeEntries(void)
|
||||
|
||||
{
|
||||
const list<ParamEntry> &entries(resource->getEntry());
|
||||
firstIter = entries.end();
|
||||
list<ParamEntry>::const_iterator iter;
|
||||
for(iter=entries.begin();iter!=entries.end();++iter) {
|
||||
const ParamEntry &entry( *iter );
|
||||
if (entry.isExclusion() && entry.getType() == resourceType && entry.getAllGroups().size() == 1) {
|
||||
firstIter = iter; // First matching resource size
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (firstIter == entries.end())
|
||||
firstIter = resource->getFirstIter(resourceType);
|
||||
if (firstIter == resource->getEntry().end())
|
||||
throw LowlevelError("Could not find matching resources for action: consumeextra");
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ namespace ghidra {
|
|||
class ParameterPieces;
|
||||
class ParamListStandard;
|
||||
class ParamEntry;
|
||||
class ParamActive;
|
||||
|
||||
extern ElementId ELEM_DATATYPE; ///< Marshaling element \<datatype>
|
||||
extern ElementId ELEM_CONSUME; ///< Marshaling element \<consume>
|
||||
|
@ -38,6 +39,43 @@ extern ElementId ELEM_POSITION; ///< Marshaling element \<position>
|
|||
extern ElementId ELEM_VARARGS; ///< Marshaling element \<varargs>
|
||||
extern ElementId ELEM_HIDDEN_RETURN; ///< Marshaling element \<hidden_return>
|
||||
extern ElementId ELEM_JOIN_PER_PRIMITIVE; ///< Marshaling element \<join_per_primitive>
|
||||
extern ElementId ELEM_JOIN_DUAL_CLASS; ///< Marshaling element \<join_dual_class>
|
||||
|
||||
/// \brief Class for extracting primitive elements of a data-type
|
||||
///
|
||||
/// This recursively collects the formal \e primitive data-types of a composite data-type,
|
||||
/// laying them out with their offsets in an array. Other boolean properties are collected.
|
||||
class PrimitiveExtractor {
|
||||
enum {
|
||||
unknown_element = 1, ///< Contains at least one TYPE_UNKNOWN primitive
|
||||
unaligned = 2, ///< At least one primitive is not properly aligned
|
||||
extra_space = 4, ///< Data-type contains empty space not attributable to alignment padding
|
||||
invalid = 8, ///< Data-type exceeded maximum or contained illegal elements
|
||||
union_invalid = 16 ///< Unions are treated as an illegal element
|
||||
};
|
||||
public:
|
||||
class Primitive {
|
||||
public:
|
||||
Datatype *dt; ///< Primitive data-type
|
||||
int4 offset; ///< Offset within container
|
||||
Primitive(Datatype *d,int4 off) { dt = d; offset = off; } ///< Constructor
|
||||
};
|
||||
private:
|
||||
vector<Primitive> primitives; ///< List of extracted primitives
|
||||
uint4 flags; ///< Boolean properties of the data-type
|
||||
int4 checkOverlap(vector<Primitive> &res,vector<Primitive> &small,int4 point,Primitive &big);
|
||||
bool commonRefinement(vector<Primitive> &first,vector<Primitive> &second);
|
||||
bool handleUnion(TypeUnion *dt,int4 max,int4 offset); ///< Add primitives representing a union data-type
|
||||
bool extract(Datatype *dt,int4 max,int4 offset); ///< Extract list of primitives from given data-type
|
||||
public:
|
||||
PrimitiveExtractor(Datatype *dt,bool unionIllegal,int4 offset,int4 max); ///< Constructor
|
||||
int4 size(void) const { return primitives.size(); } ///< Return the number of primitives extracted
|
||||
const Primitive &get(int4 i) const { return primitives[i]; } ///< Get a particular primitive
|
||||
bool isValid(void) const { return (flags & invalid) == 0; }
|
||||
bool containsUnknown(void) const { return (flags & unknown_element)!=0; } ///< Are there \b unknown elements
|
||||
bool isAligned(void) const { return (flags & unaligned)==0; } ///< Are all elements aligned
|
||||
bool containsHoles(void) const { return (flags & extra_space)!=0; } ///< Is there empty space that is not padding
|
||||
};
|
||||
|
||||
/// \brief A filter selecting a specific class of data-type
|
||||
///
|
||||
|
@ -63,7 +101,6 @@ public:
|
|||
/// \param decoder is the given stream decoder
|
||||
virtual void decode(Decoder &decoder)=0;
|
||||
|
||||
static bool extractPrimitives(Datatype *dt,int4 max,vector<Datatype *> &res);
|
||||
static DatatypeFilter *decodeFilter(Decoder &decoder); ///< Instantiate a filter from the given stream
|
||||
};
|
||||
|
||||
|
@ -208,8 +245,12 @@ public:
|
|||
};
|
||||
protected:
|
||||
const ParamListStandard *resource; ///< Resources to which this action applies
|
||||
bool fillinOutputActive; ///< If \b true, fillinOutputMap is active
|
||||
public:
|
||||
AssignAction(const ParamListStandard *res) {resource = res; } ///< Constructor
|
||||
AssignAction(const ParamListStandard *res) {resource = res; fillinOutputActive = false; } ///< Constructor
|
||||
|
||||
bool canAffectFillinOutput(void) const { return fillinOutputActive; } ///< Return \b true if fillinOutputMap is active
|
||||
|
||||
virtual ~AssignAction(void) {}
|
||||
|
||||
/// \brief Make a copy of \b this action
|
||||
|
@ -237,6 +278,14 @@ public:
|
|||
virtual uint4 assignAddress(Datatype *dt,const PrototypePieces &proto,int4 pos,TypeFactory &tlist,
|
||||
vector<int4> &status,ParameterPieces &res) const=0;
|
||||
|
||||
/// \brief Test if \b this action could produce return value storage matching the given set of trials
|
||||
///
|
||||
/// If there is a return value data-type that could be assigned storage matching the trials by \b this action,
|
||||
/// return \b true. The trials have their matching ParamEntry and offset already set and are already sorted.
|
||||
/// \param active is the given set of trials
|
||||
/// \return \b true if the trials could form a valid return value
|
||||
virtual bool fillinOutputMap(ParamActive *active) const;
|
||||
|
||||
/// \brief Configure any details of how \b this action should behave from the stream
|
||||
///
|
||||
/// \param decoder is the given stream decoder
|
||||
|
@ -255,6 +304,7 @@ public:
|
|||
virtual AssignAction *clone(const ParamListStandard *newResource) const { return new GotoStack(newResource); }
|
||||
virtual uint4 assignAddress(Datatype *dt,const PrototypePieces &proto,int4 pos,TypeFactory &tlist,
|
||||
vector<int4> &status,ParameterPieces &res) const;
|
||||
virtual bool fillinOutputMap(ParamActive *active) const;
|
||||
virtual void decode(Decoder &decoder);
|
||||
};
|
||||
|
||||
|
@ -292,6 +342,7 @@ public:
|
|||
return new MultiSlotAssign(resourceType,consumeFromStack,consumeMostSig,enforceAlignment,justifyRight,newResource); }
|
||||
virtual uint4 assignAddress(Datatype *dt,const PrototypePieces &proto,int4 pos,TypeFactory &tlist,
|
||||
vector<int4> &status,ParameterPieces &res) const;
|
||||
virtual bool fillinOutputMap(ParamActive *active) const;
|
||||
virtual void decode(Decoder &decoder);
|
||||
};
|
||||
|
||||
|
@ -310,6 +361,35 @@ public:
|
|||
return new MultiMemberAssign(resourceType,consumeFromStack,consumeMostSig,newResource); }
|
||||
virtual uint4 assignAddress(Datatype *dt,const PrototypePieces &proto,int4 pos,TypeFactory &tlist,
|
||||
vector<int4> &status,ParameterPieces &res) const;
|
||||
virtual bool fillinOutputMap(ParamActive *active) const;
|
||||
virtual void decode(Decoder &decoder);
|
||||
};
|
||||
|
||||
/// \brief Consume multiple registers from different storage classes to pass a data-type
|
||||
///
|
||||
/// This action is for calling conventions that can use both floating-point and general purpose registers
|
||||
/// when assigning storage for a single composite data-type, such as the X86-64 System V ABI
|
||||
class MultiSlotDualAssign : public AssignAction {
|
||||
type_class baseType; ///< Resource list from which to consume general tiles
|
||||
type_class altType; ///< Resource list from which to consume alternate tiles
|
||||
bool consumeMostSig; ///< True if resources are consumed starting with most significant bytes
|
||||
bool justifyRight; ///< True if initial bytes are padding for odd data-type sizes
|
||||
int4 tileSize; ///< Number of bytes in a tile
|
||||
list<ParamEntry>::const_iterator baseIter; ///< Iterator to first element in the base resource list
|
||||
list<ParamEntry>::const_iterator altIter; ///< Iterator to first element in alternate resource list
|
||||
void initializeEntries(void); ///< Cache specific ParamEntry needed by the action
|
||||
list<ParamEntry>::const_iterator getFirstUnused(list<ParamEntry>::const_iterator iter,type_class storage,
|
||||
vector<int4> &status) const;
|
||||
int4 getTileClass(const PrimitiveExtractor &primitives,int4 off,int4 &index) const;
|
||||
public:
|
||||
MultiSlotDualAssign(const ParamListStandard *res); ///< Constructor for use with decode
|
||||
MultiSlotDualAssign(type_class baseStore,type_class altStore,bool mostSig,bool justRight,
|
||||
const ParamListStandard *res); ///< Constructor
|
||||
virtual AssignAction *clone(const ParamListStandard *newResource) const {
|
||||
return new MultiSlotDualAssign(baseType,altType,consumeMostSig,justifyRight,newResource); }
|
||||
virtual uint4 assignAddress(Datatype *dt,const PrototypePieces &proto,int4 pos,TypeFactory &tlist,
|
||||
vector<int4> &status,ParameterPieces &res) const;
|
||||
virtual bool fillinOutputMap(ParamActive *active) const;
|
||||
virtual void decode(Decoder &decoder);
|
||||
};
|
||||
|
||||
|
@ -325,6 +405,7 @@ public:
|
|||
return new ConsumeAs(resourceType,newResource); }
|
||||
virtual uint4 assignAddress(Datatype *dt,const PrototypePieces &proto,int4 pos,TypeFactory &tlist,
|
||||
vector<int4> &status,ParameterPieces &res) const;
|
||||
virtual bool fillinOutputMap(ParamActive *active) const;
|
||||
virtual void decode(Decoder &decoder);
|
||||
};
|
||||
|
||||
|
@ -392,8 +473,26 @@ public:
|
|||
~ModelRule(void); ///< Destructor
|
||||
uint4 assignAddress(Datatype *dt,const PrototypePieces &proto,int4 pos,TypeFactory &tlist,
|
||||
vector<int4> &status,ParameterPieces &res) const;
|
||||
bool fillinOutputMap(ParamActive *active) const; ///< Test and mark the trial(s) that can be valid return value
|
||||
bool canAffectFillinOutput(void) const; ///< Return \b true if fillinOutputMap is active for \b this rule
|
||||
void decode(Decoder &decoder,const ParamListStandard *res); ///< Decode \b this rule from stream
|
||||
};
|
||||
|
||||
/// If the assign action could produce the trials as return value storage, return \b true
|
||||
/// \param active is the set of trials
|
||||
/// \return \b true if the trials form a return value
|
||||
inline bool ModelRule::fillinOutputMap(ParamActive *active) const
|
||||
|
||||
{
|
||||
return assign->fillinOutputMap(active);
|
||||
}
|
||||
|
||||
/// \return \b true if the assign action can affect fillinOutputMap()
|
||||
inline bool ModelRule::canAffectFillinOutput(void) const
|
||||
|
||||
{
|
||||
return assign->canAffectFillinOutput();
|
||||
}
|
||||
|
||||
} // End namespace ghidra
|
||||
#endif
|
||||
|
|
|
@ -1507,6 +1507,9 @@ void TypeStruct::setFields(const vector<TypeField> &fd,int4 fixedSize,int4 fixed
|
|||
size = calcSize;
|
||||
alignment = (fixedAlign < 1) ? calcAlign : fixedAlign;
|
||||
calcAlignSize();
|
||||
if (fixedSize <= 0) { // Unless specifically overridden
|
||||
size = alignSize; // pad out structure to with alignment bytes
|
||||
}
|
||||
}
|
||||
|
||||
/// Find the proper subfield given an offset. Return the index of that field
|
||||
|
|
|
@ -199,7 +199,63 @@ bool ParamStoreEnvironment::comparePiece(const VarnodeData &vData,ParameterPiece
|
|||
|
||||
TEST(paramstore_x64) {
|
||||
ProtoModel *model = theEnviron.getModel("x86:LE:64:default:gcc","__stdcall");
|
||||
ASSERT(theEnviron.test(model,"void func(int4 a,int4 b);","void,EDI,ESI"));
|
||||
ASSERT(theEnviron.test(model,"void func(int4,int4);","void,EDI,ESI"));
|
||||
ASSERT(theEnviron.test(model,"void func(float4,float4);","void,XMM0:4,XMM1:4"));
|
||||
ASSERT(theEnviron.test(model, "void func(int2 a,int4 b,int1 c);", "void,DI,ESI,DX:1"));
|
||||
ASSERT(theEnviron.test(model, "void func(int8,int8);", "void,RDI,RSI"));
|
||||
ASSERT(theEnviron.test(model, "void func(float8,float8);", "void,XMM0:8,XMM1:8"));
|
||||
ASSERT(theEnviron.test(model, "void func(int4,float4,int4,float4);", "void,EDI,XMM0:4,ESI,XMM1:4"));
|
||||
ASSERT(theEnviron.test(model, "void func(float4,int4,float4,int4);", "void,XMM0:4,EDI,XMM1:4,ESI"));
|
||||
ASSERT(theEnviron.test(model, "void func(int4,float8,float8,int4);", "void,EDI,XMM0:8,XMM1:8,ESI"));
|
||||
ASSERT(theEnviron.test(model, "void func(float8,int8,int8,float8);", "void,XMM0:8,RDI,RSI,XMM1:8"));
|
||||
ASSERT(theEnviron.test(model, "void func(float10);", "void,stack8:10"));
|
||||
ASSERT(theEnviron.test(model, "void func(float4,float10,float4);", "void,XMM0:4,stack8:10,XMM1:4"));
|
||||
theEnviron.parseType(model,"struct intfloatpair { int4 a; float4 b;};");
|
||||
ASSERT(theEnviron.test(model, "void func(intfloatpair);", "void,RDI"));
|
||||
theEnviron.parseType(model,"struct longfloatpair { int8 a; float4 b;};");
|
||||
ASSERT(theEnviron.test(model, "void func(int4,longfloatpair);", "void,EDI,join XMM0:8 RSI"));
|
||||
theEnviron.parseType(model,"struct longdoublepair { int8 a; float8 b;};");
|
||||
ASSERT(theEnviron.test(model, "void func(int4,longdoublepair);", "void,EDI,join XMM0:8 RSI"));
|
||||
theEnviron.parseType(model,"struct intdoublepair { int4 a; float8 b;};");
|
||||
ASSERT(theEnviron.test(model, "void func(int4,intdoublepair);", "void,EDI,join XMM0:8 RSI"));
|
||||
theEnviron.parseType(model,"struct floatintpair { float4 a; int4 b;};");
|
||||
ASSERT(theEnviron.test(model, "void func(int4,floatintpair);", "void,EDI,RSI"));
|
||||
theEnviron.parseType(model,"struct doubleintpair { float8 a; int4 b;};");
|
||||
ASSERT(theEnviron.test(model, "void func(int4,doubleintpair);", "void,EDI,join RSI XMM0:8"));
|
||||
theEnviron.parseType(model,"struct intintfloat { int4 a; int4 b; float4 c; };");
|
||||
ASSERT(theEnviron.test(model, "void func(int4,intintfloat);", "void,EDI,join XMM0:4 RSI"));
|
||||
theEnviron.parseType(model,"struct intintfloatfloat { int4 a; int4 b; float4 c; float4 d;};");
|
||||
ASSERT(theEnviron.test(model, "void func(int4,intintfloatfloat);", "void,EDI,join XMM0:8 RSI"));
|
||||
theEnviron.parseType(model,"struct intfloatfloatint { int4 a; float4 b; float4 c; int4 d;};");
|
||||
ASSERT(theEnviron.test(model, "void func(int4,intfloatfloatint);", "void,EDI,join RDX RSI"));
|
||||
theEnviron.parseType(model,"struct intfloatfloat { int4 a; float4 b; float4 c; };");
|
||||
ASSERT(theEnviron.test(model, "void func(int4,intfloatfloat);", "void,EDI,join XMM0:4 RSI"));
|
||||
theEnviron.parseType(model,"struct floatfloatpair { float4 a; float4 b; };");
|
||||
ASSERT(theEnviron.test(model, "void func(int4,floatfloatpair);", "void,EDI,XMM0:8"));
|
||||
theEnviron.parseType(model,"struct doublefloatpair { float8 a; float4 b; };");
|
||||
ASSERT(theEnviron.test(model, "void func(int4,doublefloatpair);", "void,EDI,join XMM1:8 XMM0:8"));
|
||||
theEnviron.parseType(model,"struct floatfloatfloat { float4 a; float4 b; float4 c; };");
|
||||
ASSERT(theEnviron.test(model, "void func(floatfloatfloat,int8);", "void,join XMM1:4 XMM0:8,RDI"));
|
||||
theEnviron.parseType(model,"struct intintintint { int4 a; int4 b; int4 c; int4 d; };");
|
||||
ASSERT(theEnviron.test(model, "void func(intintintint);", "void,join RSI RDI"));
|
||||
ASSERT(theEnviron.test(model, "void func(int4,intintintint);", "void,EDI,join RDX RSI"));
|
||||
theEnviron.parseType(model,"struct intintintintint { int4 a; int4 b; int4 c; int4 d; int4 e;};");
|
||||
ASSERT(theEnviron.test(model, "void func(intintintintint);", "void,stack8:20"));
|
||||
ASSERT(theEnviron.test(model, "void func(float4,float4,float4,float4,float4,float4,float4,float4,longfloatpair);",
|
||||
"void,XMM0:4,XMM1:4,XMM2:4,XMM3:4,XMM4:4,XMM5:4,XMM6:4,XMM7:4,stack8:16"));
|
||||
ASSERT(theEnviron.test(model, "void func(xunknown4,xunknown8);", "void,EDI,RSI"));
|
||||
ASSERT(theEnviron.test(model, "intintintint func(void);", "join RDX RAX"));
|
||||
ASSERT(theEnviron.test(model, "floatintpair func(void);", "RAX"));
|
||||
ASSERT(theEnviron.test(model, "longfloatpair func(void);", "join XMM0:8 RAX"));
|
||||
ASSERT(theEnviron.test(model, "longdoublepair func(void);", "join XMM0:8 RAX"));
|
||||
ASSERT(theEnviron.test(model, "doubleintpair func(void);", "join RAX XMM0:8"));
|
||||
ASSERT(theEnviron.test(model, "floatfloatfloat func(void);", "join XMM1:4 XMM0:8"));
|
||||
theEnviron.parseType(model, "struct doubledoublepair { float8 a; float8 b; };");
|
||||
ASSERT(theEnviron.test(model, "doubledoublepair func(void);", "join XMM1:8 XMM0:8"));
|
||||
ASSERT(theEnviron.test(model, "floatfloatpair func(void);", "XMM0:8"));
|
||||
ASSERT(theEnviron.test(model, "intintintintint func(void);", "RAX,RDI"));
|
||||
theEnviron.parseType(model, "struct doubleintintint { float8 a; int4 b; int4 c; int4 d; };");
|
||||
ASSERT(theEnviron.test(model, "doubleintintint func(void);", "RAX,RDI"));
|
||||
}
|
||||
|
||||
TEST(paramstore_aarch64_cdecl) {
|
||||
|
|
|
@ -409,6 +409,22 @@
|
|||
<attribute name="storage"/>
|
||||
</optional>
|
||||
</element>
|
||||
<element name="join_dual_class">
|
||||
<optional>
|
||||
<attribute name="reversejustify">
|
||||
<ref name="boolean_type"/>
|
||||
</attribute>
|
||||
</optional>
|
||||
<optional>
|
||||
<attribute name="storage"/>
|
||||
</optional>
|
||||
<optional>
|
||||
<attribute name="a"/>
|
||||
</optional>
|
||||
<optional>
|
||||
<attribute name="b"/>
|
||||
</optional>
|
||||
</element>
|
||||
</choice>
|
||||
<zeroOrMore>
|
||||
<element name="consume_extra">
|
||||
|
|
|
@ -127,6 +127,9 @@ public abstract class AssignAction {
|
|||
boolean consumeMostSig = res.getEntry(0).isBigEndian();
|
||||
action = new MultiMemberAssign(StorageClass.GENERAL, false, consumeMostSig, res);
|
||||
}
|
||||
else if (nm.equals(ELEM_JOIN_DUAL_CLASS.name())) {
|
||||
action = new MultiSlotDualAssign(res);
|
||||
}
|
||||
else {
|
||||
throw new XmlParseException("Unknown model rule action: " + nm);
|
||||
}
|
||||
|
|
|
@ -18,9 +18,8 @@ package ghidra.program.model.lang.protorules;
|
|||
import static ghidra.program.model.pcode.AttributeId.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.pcode.Encoder;
|
||||
import ghidra.program.model.pcode.PcodeDataTypeManager;
|
||||
import ghidra.xml.*;
|
||||
|
@ -66,79 +65,6 @@ public interface DatatypeFilter {
|
|||
*/
|
||||
public void restoreXml(XmlPullParser parser) throws XmlParseException;
|
||||
|
||||
/**
|
||||
* Extract an ordered list of primitive data-types making up the given data-type
|
||||
*
|
||||
* The primitive data-types are passed back in an ArrayList. If the given data-type is already
|
||||
* primitive, it is passed back as is. Otherwise if it is composite, its components are
|
||||
* recursively listed. If a maximum number of extracted primitives is exceeded, or if the
|
||||
* primitives are not properly aligned, or if a non-primitive non-composite data-type is
|
||||
* encountered, false is returned.
|
||||
* @param dt is the given data-type to extract primitives from
|
||||
* @param max is the maximum number of primitives to extract before giving up
|
||||
* @param res will hold the list of primitives
|
||||
* @return true if all primitives were extracted
|
||||
*/
|
||||
public static boolean extractPrimitives(DataType dt, int max, ArrayList<DataType> res) {
|
||||
if (dt instanceof TypeDef) {
|
||||
dt = ((TypeDef) dt).getBaseDataType();
|
||||
}
|
||||
int metaType = PcodeDataTypeManager.getMetatype(dt);
|
||||
switch (metaType) {
|
||||
case PcodeDataTypeManager.TYPE_UNKNOWN:
|
||||
return false; // Do not consider undefined data-types as primitive
|
||||
case PcodeDataTypeManager.TYPE_INT:
|
||||
case PcodeDataTypeManager.TYPE_UINT:
|
||||
case PcodeDataTypeManager.TYPE_BOOL:
|
||||
case PcodeDataTypeManager.TYPE_CODE:
|
||||
case PcodeDataTypeManager.TYPE_FLOAT:
|
||||
case PcodeDataTypeManager.TYPE_PTR:
|
||||
case PcodeDataTypeManager.TYPE_PTRREL:
|
||||
if (res.size() >= max) {
|
||||
return false;
|
||||
}
|
||||
res.add(dt);
|
||||
return true;
|
||||
case PcodeDataTypeManager.TYPE_ARRAY: {
|
||||
int numEls = ((Array) dt).getNumElements();
|
||||
DataType base = ((Array) dt).getDataType();
|
||||
for (int i = 0; i < numEls; ++i) {
|
||||
if (!extractPrimitives(base, max, res)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case PcodeDataTypeManager.TYPE_STRUCT:
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
Structure structPtr = (Structure) dt;
|
||||
boolean isPacked = structPtr.isPackingEnabled();
|
||||
int curOff = 0;
|
||||
DataTypeComponent[] components = structPtr.getDefinedComponents();
|
||||
for (DataTypeComponent component : components) {
|
||||
DataType compDT = component.getDataType();
|
||||
if (!isPacked) {
|
||||
int nextOff = component.getOffset();
|
||||
int align = dt.getAlignment();
|
||||
int rem = curOff % align;
|
||||
if (rem != 0) {
|
||||
curOff += (align - rem);
|
||||
}
|
||||
if (curOff != nextOff) {
|
||||
return false;
|
||||
}
|
||||
curOff = nextOff + compDT.getAlignedLength();
|
||||
}
|
||||
if (!extractPrimitives(compDT, max, res)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate a filter from the given stream.
|
||||
* @param parser is the given stream decoder
|
||||
|
|
|
@ -19,7 +19,6 @@ import static ghidra.program.model.pcode.AttributeId.*;
|
|||
import static ghidra.program.model.pcode.ElementId.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.pcode.Encoder;
|
||||
|
@ -67,17 +66,18 @@ public class HomogeneousAggregate extends SizeRestrictedFilter {
|
|||
if (meta != PcodeDataTypeManager.TYPE_ARRAY && meta != PcodeDataTypeManager.TYPE_STRUCT) {
|
||||
return false;
|
||||
}
|
||||
ArrayList<DataType> res = new ArrayList<>();
|
||||
if (!DatatypeFilter.extractPrimitives(dt, MAX_PRIMITIVES, res) || res.isEmpty()) {
|
||||
PrimitiveExtractor primitives = new PrimitiveExtractor(dt, true, 0, MAX_PRIMITIVES);
|
||||
if (!primitives.isValid() || primitives.size() == 0 || primitives.containsUnknown() ||
|
||||
!primitives.isAligned() || primitives.containsHoles()) {
|
||||
return false;
|
||||
}
|
||||
DataType base = res.get(0);
|
||||
DataType base = primitives.get(0).dt;
|
||||
int baseMeta = PcodeDataTypeManager.getMetatype(base);
|
||||
if (baseMeta != metaType) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 1; i < res.size(); ++i) {
|
||||
if (res.get(i) != base) {
|
||||
for (int i = 1; i < primitives.size(); ++i) {
|
||||
if (primitives.get(i).dt != base) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -77,12 +77,13 @@ public class MultiMemberAssign extends AssignAction {
|
|||
int[] tmpStatus = status.clone();
|
||||
ArrayList<Varnode> pieces = new ArrayList<>();
|
||||
ParameterPieces param = new ParameterPieces();
|
||||
ArrayList<DataType> primitives = new ArrayList<>();
|
||||
if (!DatatypeFilter.extractPrimitives(dt, 16, primitives) || primitives.isEmpty()) {
|
||||
PrimitiveExtractor primitives = new PrimitiveExtractor(dt, false, 0, 16);
|
||||
if (!primitives.isValid() || primitives.size() == 0 || primitives.containsUnknown() ||
|
||||
!primitives.isAligned() || primitives.containsHoles()) {
|
||||
return FAIL;
|
||||
}
|
||||
for (int i = 0; i < primitives.size(); ++i) {
|
||||
DataType curType = primitives.get(i);
|
||||
DataType curType = primitives.get(i).dt;
|
||||
if (resource.assignAddressFallback(resourceType, curType, !consumeFromStack, tmpStatus,
|
||||
param) == FAIL) {
|
||||
return FAIL;
|
||||
|
|
|
@ -0,0 +1,317 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.program.model.lang.protorules;
|
||||
|
||||
import static ghidra.program.model.pcode.AttributeId.*;
|
||||
import static ghidra.program.model.pcode.ElementId.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.data.DataTypeManager;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.lang.protorules.PrimitiveExtractor.Primitive;
|
||||
import ghidra.program.model.pcode.Encoder;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
import ghidra.util.exception.InvalidInputException;
|
||||
import ghidra.util.xml.SpecXmlUtils;
|
||||
import ghidra.xml.*;
|
||||
|
||||
/**
|
||||
* Consume multiple registers from different storage classes to pass a data-type
|
||||
*
|
||||
* This action is for calling conventions that can use both floating-point and general purpose registers
|
||||
* when assigning storage for a single composite data-type, such as the X86-64 System V ABI
|
||||
*/
|
||||
public class MultiSlotDualAssign extends AssignAction {
|
||||
private StorageClass baseType; // Resource list from which to consume general tiles
|
||||
private StorageClass altType; // Resource list from which to consume alternate tiles
|
||||
private boolean consumeMostSig; // True if resources are consumed starting with most significant bytes
|
||||
private boolean justifyRight; // True if initial bytes are padding for odd data-type sizes
|
||||
private int tileSize; // Number of bytes in a tile
|
||||
private int baseIter; // Iterator to first element in the base resource list
|
||||
private int altIter; // Iterator to first element in alternate resource list
|
||||
|
||||
/**
|
||||
* Find the first ParamEntry matching the baseType, and the first matching altType.
|
||||
* @throws InvalidInputException if the required elements are not available in the resource list
|
||||
*/
|
||||
private void initializeEntries() throws InvalidInputException {
|
||||
baseIter = -1;
|
||||
altIter = -1;
|
||||
for (int i = 0; i < resource.getNumParamEntry(); ++i) {
|
||||
ParamEntry entry = resource.getEntry(i);
|
||||
if (baseIter == -1 && entry.isExclusion() && entry.getType() == baseType &&
|
||||
entry.getAllGroups().length == 1) {
|
||||
baseIter = i; // First matching base resource type
|
||||
}
|
||||
if (altIter == -1 && entry.isExclusion() && entry.getType() == altType &&
|
||||
entry.getAllGroups().length == 1) {
|
||||
altIter = i; // First matching alt resource type
|
||||
}
|
||||
}
|
||||
if (baseIter == -1 || altIter == -1) {
|
||||
throw new InvalidInputException(
|
||||
"Could not find matching resources for action: join_dual_class");
|
||||
}
|
||||
tileSize = resource.getEntry(baseIter).getSize();
|
||||
if (tileSize != resource.getEntry(altIter).getSize()) {
|
||||
throw new InvalidInputException(
|
||||
"Storage class register sizes do not match for action: join_dual_class");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the first unused ParamEntry that matches the given storage class
|
||||
* @param iter points to the starting entry to search
|
||||
* @param storage is the given storage class to match
|
||||
* @param status is the usage information for the entries
|
||||
* @return the iterator to the unused ParamEntry
|
||||
*/
|
||||
private int getFirstUnused(int iter, StorageClass storage, int[] status) {
|
||||
int endIter = resource.getNumParamEntry();
|
||||
for (; iter != endIter; ++iter) {
|
||||
ParamEntry entry = resource.getEntry(iter);
|
||||
if (!entry.isExclusion()) {
|
||||
break; // Reached end of resource list
|
||||
}
|
||||
if (entry.getType() != storage || entry.getAllGroups().length != 1) {
|
||||
continue; // Not a single register from desired resource
|
||||
}
|
||||
if (status[entry.getGroup()] != 0) {
|
||||
continue; // Already consumed
|
||||
}
|
||||
return iter;
|
||||
}
|
||||
return endIter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the storage class to use for the specific section of the data-type
|
||||
*
|
||||
* For the section starting at -off- extending through -tileSize- bytes, if any primitive
|
||||
* overlaps the boundary of the section, return -1. Otherwise, if all the primitive data-types
|
||||
* in the section match the alternate storage class, return 1, or if one or more does not
|
||||
* match, return 0. The -index- of the first primitive after the start of the section is
|
||||
* provided and is then updated to be the first primitive after the end of the section.
|
||||
* @param primitives is the list of primitive data-types making up the data-type
|
||||
* @param off is the starting offset of the section
|
||||
* @param index is the index of the first primitive in the section
|
||||
* @return 0 for a base tile, 1 for an alternate tile, -1 for boundary overlaps
|
||||
*/
|
||||
private int getTileClass(PrimitiveExtractor primitives, int off, int[] index) {
|
||||
int res = 1;
|
||||
int count = 0;
|
||||
int endBoundary = off + tileSize;
|
||||
while (index[0] < primitives.size()) {
|
||||
Primitive element = primitives.get(index[0]);
|
||||
if (element.offset < off) {
|
||||
return -1;
|
||||
}
|
||||
if (element.offset >= endBoundary) {
|
||||
break;
|
||||
}
|
||||
if (element.offset + element.dt.getLength() > endBoundary) {
|
||||
return -1;
|
||||
}
|
||||
count += 1;
|
||||
index[0] += 1;
|
||||
StorageClass storage = ParamEntry.getBasicTypeClass(element.dt);
|
||||
if (storage != altType) {
|
||||
res = 0;
|
||||
}
|
||||
}
|
||||
if (count == 0) {
|
||||
return -1; // Must be at least one primitive in section
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for use with decode. Set default configuration.
|
||||
* @param res is the new resource set to associate with this action
|
||||
*/
|
||||
protected MultiSlotDualAssign(ParamListStandard res) {
|
||||
super(res);
|
||||
baseType = StorageClass.GENERAL; // Tile from general purpose registers
|
||||
altType = StorageClass.FLOAT; // Use specialized registers for floating-point components
|
||||
consumeMostSig = false;
|
||||
justifyRight = false;
|
||||
if (res.getEntry(0).isBigEndian()) {
|
||||
consumeMostSig = true;
|
||||
justifyRight = true;
|
||||
}
|
||||
tileSize = 0;
|
||||
}
|
||||
|
||||
public MultiSlotDualAssign(StorageClass baseStore, StorageClass altStore, boolean mostSig,
|
||||
boolean justRight, ParamListStandard res) throws InvalidInputException {
|
||||
super(res);
|
||||
baseType = baseStore;
|
||||
altType = altStore;
|
||||
consumeMostSig = mostSig;
|
||||
justifyRight = justRight;
|
||||
initializeEntries();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AssignAction clone(ParamListStandard newResource) throws InvalidInputException {
|
||||
return new MultiSlotDualAssign(baseType, altType, consumeMostSig, justifyRight,
|
||||
newResource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEquivalent(AssignAction op) {
|
||||
if (this.getClass() != op.getClass()) {
|
||||
return false;
|
||||
}
|
||||
MultiSlotDualAssign otherAction = (MultiSlotDualAssign) op;
|
||||
if (consumeMostSig != otherAction.consumeMostSig ||
|
||||
justifyRight != otherAction.justifyRight) {
|
||||
return false;
|
||||
}
|
||||
if (baseIter != otherAction.baseIter || altIter != otherAction.altIter) {
|
||||
return false;
|
||||
}
|
||||
if (baseType != otherAction.baseType || altType != otherAction.altType) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int assignAddress(DataType dt, PrototypePieces proto, int pos, DataTypeManager dtManager,
|
||||
int[] status, ParameterPieces res) {
|
||||
PrimitiveExtractor primitives = new PrimitiveExtractor(dt, false, 0, 1024);
|
||||
if (!primitives.isValid() || primitives.size() == 0 || primitives.containsHoles()) {
|
||||
return FAIL;
|
||||
}
|
||||
ParameterPieces param = new ParameterPieces();
|
||||
int[] primitiveIndex = new int[1];
|
||||
primitiveIndex[0] = 0;
|
||||
int[] tmpStatus = status.clone();
|
||||
ArrayList<Varnode> pieces = new ArrayList<>();
|
||||
int typeSize = dt.getLength();
|
||||
int sizeLeft = typeSize;
|
||||
int iterBase = baseIter;
|
||||
int iterAlt = altIter;
|
||||
int endIter = resource.getNumParamEntry();
|
||||
while (sizeLeft > 0) {
|
||||
int iter;
|
||||
int iterType = getTileClass(primitives, typeSize - sizeLeft, primitiveIndex);
|
||||
if (iterType < 0) {
|
||||
return FAIL;
|
||||
}
|
||||
if (iterType == 0) {
|
||||
iter = iterBase = getFirstUnused(iterBase, baseType, tmpStatus);
|
||||
}
|
||||
else {
|
||||
iter = iterAlt = getFirstUnused(iterAlt, altType, tmpStatus);
|
||||
}
|
||||
if (iter == endIter) {
|
||||
return FAIL; // Out of the particular resource
|
||||
}
|
||||
ParamEntry entry = resource.getEntry(iter);
|
||||
int trialSize = entry.getSize();
|
||||
entry.getAddrBySlot(tmpStatus[entry.getGroup()], trialSize, 1, param);
|
||||
tmpStatus[entry.getGroup()] = -1; // Consume the register
|
||||
Varnode vn = new Varnode(param.address, trialSize);
|
||||
pieces.add(vn);
|
||||
sizeLeft -= trialSize;
|
||||
}
|
||||
if (sizeLeft < 0) { // Have odd data-type size
|
||||
if (justifyRight) {
|
||||
// Initial bytes of first entry are padding
|
||||
Varnode vn = pieces.get(0);
|
||||
Address addr = vn.getAddress().add(-sizeLeft);
|
||||
int sz = vn.getSize() + sizeLeft;
|
||||
vn = new Varnode(addr, sz);
|
||||
pieces.set(0, vn);
|
||||
}
|
||||
else {
|
||||
int end = pieces.size() - 1;
|
||||
Varnode vn = pieces.get(end);
|
||||
int sz = vn.getSize() + sizeLeft;
|
||||
vn = new Varnode(vn.getAddress(), sz);
|
||||
pieces.set(end, vn);
|
||||
}
|
||||
}
|
||||
System.arraycopy(tmpStatus, 0, status, 0, tmpStatus.length); // Commit resource usage for all the pieces
|
||||
res.type = dt;
|
||||
if (pieces.size() == 1) {
|
||||
res.address = pieces.get(0).getAddress();
|
||||
return SUCCESS;
|
||||
}
|
||||
res.joinPieces = new Varnode[pieces.size()];
|
||||
if (!consumeMostSig) {
|
||||
for (int i = 0; i < res.joinPieces.length; ++i) {
|
||||
res.joinPieces[i] = pieces.get(pieces.size() - 1 - i);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < pieces.size(); ++i) {
|
||||
res.joinPieces[i] = pieces.get(i);
|
||||
}
|
||||
}
|
||||
res.address = Address.NO_ADDRESS;
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(Encoder encoder) throws IOException {
|
||||
encoder.openElement(ELEM_JOIN_DUAL_CLASS);
|
||||
if (resource.getEntry(0).isBigEndian() != justifyRight) {
|
||||
encoder.writeBool(ATTRIB_REVERSEJUSTIFY, true);
|
||||
}
|
||||
if (baseType != StorageClass.GENERAL) {
|
||||
encoder.writeString(ATTRIB_STORAGE, baseType.toString());
|
||||
}
|
||||
if (altType != StorageClass.FLOAT) {
|
||||
encoder.writeString(ATTRIB_B, altType.toString());
|
||||
}
|
||||
encoder.closeElement(ELEM_JOIN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreXml(XmlPullParser parser) throws XmlParseException {
|
||||
XmlElement elem = parser.start(ELEM_JOIN_DUAL_CLASS.name());
|
||||
for (Entry<String, String> attrib : elem.getAttributes().entrySet()) {
|
||||
String name = attrib.getKey();
|
||||
if (name.equals(ATTRIB_REVERSEJUSTIFY.name())) {
|
||||
if (SpecXmlUtils.decodeBoolean(attrib.getValue())) {
|
||||
justifyRight = !justifyRight;
|
||||
}
|
||||
}
|
||||
else if (name.equals(ATTRIB_STORAGE.name()) || name.equals(ATTRIB_A.name())) {
|
||||
baseType = StorageClass.getClass(attrib.getValue());
|
||||
}
|
||||
else if (name.equals(ATTRIB_B.name())) {
|
||||
altType = StorageClass.getClass(attrib.getValue());
|
||||
}
|
||||
}
|
||||
parser.end(elem);
|
||||
try {
|
||||
initializeEntries();
|
||||
}
|
||||
catch (InvalidInputException e) {
|
||||
throw new XmlParseException(e.getMessage());
|
||||
} // Need new firstIter
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,330 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.program.model.lang.protorules;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.pcode.PcodeDataTypeManager;
|
||||
|
||||
public class PrimitiveExtractor {
|
||||
|
||||
public static class Primitive {
|
||||
public DataType dt; // The primitive data-type
|
||||
public int offset; // Offset within the container
|
||||
|
||||
public Primitive(DataType d, int off) {
|
||||
dt = d;
|
||||
offset = off;
|
||||
}
|
||||
}
|
||||
|
||||
private ArrayList<Primitive> primitives; // List of extracted primitives
|
||||
private boolean valid; // Extraction was invalid
|
||||
private boolean aligned; // True if all primitives are properly aligned
|
||||
private boolean unknownElements; // True if at least one TYPE_UNKNOWN primitive
|
||||
private boolean extraSpace; // True if extra space not attributable to padding
|
||||
private boolean unionInvalid; // True if unions are treated as invalid primitive
|
||||
|
||||
/**
|
||||
* Check that a big Primitive properly overlaps smaller Primitives
|
||||
*
|
||||
* If the big Primitive does not properly overlap the smaller Primitives starting at the given
|
||||
* point, return -1. Otherwise, if the big Primitive is floating-point, add the overlapped
|
||||
* primitives to the common refinement list, or if not a floating-point, add the big Primitive
|
||||
* to the list. (Integer primitives are \e preferred over floating-point primitives in this way)
|
||||
* Return the index of the next primitive after the overlap.
|
||||
* @param res holds the common refinement list
|
||||
* @param small is the list of Primitives that are overlapped
|
||||
* @param point is the index of the first overlap
|
||||
* @param big is the big overlapping Primitive
|
||||
* @return the index of the next Primitive after the overlap or -1 if the overlap is invalid
|
||||
*/
|
||||
private int checkOverlap(ArrayList<Primitive> res, ArrayList<Primitive> small, int point,
|
||||
Primitive big) {
|
||||
int endOff = big.offset + big.dt.getAlignedLength();
|
||||
// If big data-type is a float, let smaller primitives override it, otherwise we keep the big primitive
|
||||
boolean useSmall =
|
||||
PcodeDataTypeManager.getMetatype(big.dt) == PcodeDataTypeManager.TYPE_FLOAT;
|
||||
while (point < small.size()) {
|
||||
int curOff = small.get(point).offset;
|
||||
if (curOff >= endOff) {
|
||||
break;
|
||||
}
|
||||
curOff += small.get(point).dt.getAlignedLength();
|
||||
if (curOff > endOff) {
|
||||
return -1; // Improper overlap of the end of big
|
||||
}
|
||||
if (useSmall) {
|
||||
res.add(small.get(point));
|
||||
}
|
||||
point += 1;
|
||||
}
|
||||
if (!useSmall) { // If big data-type was preferred
|
||||
res.add(big); // use big Primitive in the refinement
|
||||
}
|
||||
return point;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwrite first list with common refinement of first and second
|
||||
*
|
||||
* Given two sets of overlapping Primitives, find a \e common \e refinement of the lists.
|
||||
* If there is any partial overlap of two Primitives, \b false is returned.
|
||||
* If the same primitive data-type occurs at the same offset, it is included in the refinement.
|
||||
* Otherwise an integer data-type is preferred over a floating-point data-type, or a bigger
|
||||
* primitive is preferred over smaller overlapping primitives.
|
||||
* The final refinement replaces the \b first list.
|
||||
* @param first is the first list of Primitives
|
||||
* @param second is the second list
|
||||
* @return true if a refinement was successfully constructed
|
||||
*/
|
||||
private boolean commonRefinement(ArrayList<Primitive> first, ArrayList<Primitive> second) {
|
||||
int firstPoint = 0;
|
||||
int secondPoint = 0;
|
||||
ArrayList<Primitive> common = new ArrayList<>();
|
||||
while (firstPoint < first.size() && secondPoint < second.size()) {
|
||||
Primitive firstElement = first.get(firstPoint);
|
||||
Primitive secondElement = second.get(secondPoint);
|
||||
if (firstElement.offset < secondElement.offset &&
|
||||
firstElement.offset + firstElement.dt.getAlignedLength() <= secondElement.offset) {
|
||||
common.add(firstElement);
|
||||
firstPoint += 1;
|
||||
continue;
|
||||
}
|
||||
if (secondElement.offset < firstElement.offset &&
|
||||
secondElement.offset + secondElement.dt.getAlignedLength() <= firstElement.offset) {
|
||||
common.add(secondElement);
|
||||
secondPoint += 1;
|
||||
continue;
|
||||
}
|
||||
if (firstElement.dt.getAlignedLength() >= secondElement.dt.getAlignedLength()) {
|
||||
secondPoint = checkOverlap(common, second, secondPoint, firstElement);
|
||||
if (secondPoint < 0) {
|
||||
return false;
|
||||
}
|
||||
firstPoint += 1;
|
||||
}
|
||||
else {
|
||||
firstPoint = checkOverlap(common, first, firstPoint, secondElement);
|
||||
if (firstPoint < 0) {
|
||||
return false;
|
||||
}
|
||||
secondPoint += 1;
|
||||
}
|
||||
}
|
||||
// Add any tail primitives from either list
|
||||
while (firstPoint < first.size()) {
|
||||
common.add(first.get(firstPoint));
|
||||
firstPoint += 1;
|
||||
}
|
||||
while (secondPoint < second.size()) {
|
||||
common.add(second.get(secondPoint));
|
||||
secondPoint += 1;
|
||||
}
|
||||
first.clear();
|
||||
first.addAll(common); // Replace first with the refinement
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Form a primitive list for each field of the union. Then, if possible, form a common
|
||||
* refinement of all the primitive lists and add to the end of this extractor's list.
|
||||
* @param dt is the union data-type
|
||||
* @param max is the maximum number primitives allowed for \b this extraction
|
||||
* @param offset is the starting offset of the union within the parent
|
||||
* @return true if a common refinement was found and appended
|
||||
*/
|
||||
private boolean handleUnion(UnionDataType dt, int max, int offset) {
|
||||
if (unionInvalid) {
|
||||
return false;
|
||||
}
|
||||
int num = dt.getNumComponents();
|
||||
if (num == 0) {
|
||||
return false;
|
||||
}
|
||||
DataTypeComponent curField = dt.getComponent(0);
|
||||
|
||||
PrimitiveExtractor common = new PrimitiveExtractor(curField.getDataType(), false,
|
||||
offset + curField.getOffset(), max);
|
||||
if (!common.isValid()) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 1; i < num; ++i) {
|
||||
curField = dt.getComponent(i);
|
||||
|
||||
PrimitiveExtractor next = new PrimitiveExtractor(curField.getDataType(), false,
|
||||
offset + curField.getOffset(), max);
|
||||
if (!next.isValid()) {
|
||||
return false;
|
||||
}
|
||||
if (!commonRefinement(common.primitives, next.primitives)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (primitives.size() + common.primitives.size() > max) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < common.primitives.size(); ++i) {
|
||||
primitives.add(common.primitives.get(i));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* An array of the primitive data-types, with their associated offsets, is constructed.
|
||||
* If the given data-type is already primitive it is put in the array by itself. Otherwise
|
||||
* if it is composite, its components are recursively added to the array.
|
||||
* Boolean properties about the primitives encountered are recorded:
|
||||
* - Are any of the primitives \b undefined
|
||||
* - Are all the primitives properly aligned.
|
||||
*
|
||||
* If a maximum number of extracted primitives is exceeded, or if an illegal
|
||||
* data-type is encountered (\b void or other internal data-type) false is returned.
|
||||
* @param dt is the given data-type to extract primitives from
|
||||
* @param max is the maximum number of primitives to extract before giving up
|
||||
* @param offset is the starting offset to associate with the first primitive
|
||||
* @return true if all primitives were extracted
|
||||
*/
|
||||
private boolean extract(DataType dt, int max, int offset) {
|
||||
if (dt instanceof TypeDef) {
|
||||
dt = ((TypeDef) dt).getBaseDataType();
|
||||
}
|
||||
int metaType = PcodeDataTypeManager.getMetatype(dt);
|
||||
switch (metaType) {
|
||||
case PcodeDataTypeManager.TYPE_UNKNOWN:
|
||||
unknownElements = true;
|
||||
// fall-thru
|
||||
case PcodeDataTypeManager.TYPE_INT:
|
||||
case PcodeDataTypeManager.TYPE_UINT:
|
||||
case PcodeDataTypeManager.TYPE_BOOL:
|
||||
case PcodeDataTypeManager.TYPE_CODE:
|
||||
case PcodeDataTypeManager.TYPE_FLOAT:
|
||||
case PcodeDataTypeManager.TYPE_PTR:
|
||||
case PcodeDataTypeManager.TYPE_PTRREL:
|
||||
if (primitives.size() >= max) {
|
||||
return false;
|
||||
}
|
||||
primitives.add(new Primitive(dt, offset));
|
||||
return true;
|
||||
case PcodeDataTypeManager.TYPE_ARRAY: {
|
||||
int numEls = ((Array) dt).getNumElements();
|
||||
DataType base = ((Array) dt).getDataType();
|
||||
for (int i = 0; i < numEls; ++i) {
|
||||
if (!extract(base, max, offset)) {
|
||||
return false;
|
||||
}
|
||||
offset += base.getAlignedLength();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case PcodeDataTypeManager.TYPE_UNION:
|
||||
return handleUnion((UnionDataType) dt, max, offset);
|
||||
case PcodeDataTypeManager.TYPE_STRUCT:
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
Structure structPtr = (Structure) dt;
|
||||
boolean isPacked = structPtr.isPackingEnabled();
|
||||
DataTypeComponent[] components = structPtr.getDefinedComponents();
|
||||
int expectedOff = offset;
|
||||
for (DataTypeComponent component : components) {
|
||||
DataType compDT = component.getDataType();
|
||||
int curOff = component.getOffset() + offset;
|
||||
if (!isPacked) {
|
||||
int align = compDT.getAlignment();
|
||||
if (curOff % align != 0) {
|
||||
aligned = false;
|
||||
}
|
||||
int rem = expectedOff % align;
|
||||
if (rem != 0) {
|
||||
expectedOff += (align - rem);
|
||||
}
|
||||
if (expectedOff != curOff) {
|
||||
extraSpace = true;
|
||||
}
|
||||
}
|
||||
if (!extract(compDT, max, curOff)) {
|
||||
return false;
|
||||
}
|
||||
expectedOff = curOff + compDT.getAlignedLength();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param dt is data-type extract from
|
||||
* @param unionIllegal is true if unions encountered during extraction are considered illegal
|
||||
* @param offset is the starting offset to associate with the data-type
|
||||
* @param max is the maximum number of primitives to extract before giving up
|
||||
*/
|
||||
public PrimitiveExtractor(DataType dt, boolean unionIllegal, int offset, int max) {
|
||||
primitives = new ArrayList<>();
|
||||
valid = true;
|
||||
aligned = true;
|
||||
unknownElements = false;
|
||||
extraSpace = false;
|
||||
unionInvalid = unionIllegal;
|
||||
if (!extract(dt, max, offset)) {
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if all primitive elements were extracted
|
||||
*/
|
||||
public boolean isValid() {
|
||||
return valid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if any extracted element was unknown/undefined
|
||||
*/
|
||||
public boolean containsUnknown() {
|
||||
return unknownElements;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if all extracted elements are aligned
|
||||
*/
|
||||
public boolean isAligned() {
|
||||
return aligned;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if there is extra space in the data-type that is not alignment padding
|
||||
*/
|
||||
public boolean containsHoles() {
|
||||
return extraSpace;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the number of primitives extracted
|
||||
*/
|
||||
public int size() {
|
||||
return primitives.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the i-th extracted primitive and its offset
|
||||
* @param i is the index
|
||||
* @return the primitive and offset
|
||||
*/
|
||||
public Primitive get(int i) {
|
||||
return primitives.get(i);
|
||||
}
|
||||
}
|
|
@ -454,6 +454,7 @@ public record ElementId(String name, int id) {
|
|||
public static final ElementId ELEM_HIDDEN_RETURN = new ElementId("hidden_return", 282);
|
||||
public static final ElementId ELEM_JOIN_PER_PRIMITIVE =
|
||||
new ElementId("join_per_primitive", 283);
|
||||
public static final ElementId ELEM_JOIN_DUAL_CLASS = new ElementId("join_dual_class", 285);
|
||||
|
||||
public static final ElementId ELEM_UNKNOWN = new ElementId("XMLunknown", 285);
|
||||
public static final ElementId ELEM_UNKNOWN = new ElementId("XMLunknown", 286);
|
||||
}
|
||||
|
|
|
@ -541,14 +541,8 @@ public class PcodeDataTypeManager {
|
|||
private void encodeEnum(Encoder encoder, Enum type, int size) throws IOException {
|
||||
encoder.openElement(ELEM_TYPE);
|
||||
encodeNameIdAttributes(encoder, type);
|
||||
String metatype = type.isSigned() ? "int" : "uint";
|
||||
long[] keys = type.getValues();
|
||||
String metatype = "uint";
|
||||
for (long key : keys) {
|
||||
if (key < 0) {
|
||||
metatype = "int";
|
||||
break;
|
||||
}
|
||||
}
|
||||
encoder.writeString(ATTRIB_METATYPE, metatype);
|
||||
encoder.writeSignedInteger(ATTRIB_SIZE, type.getLength());
|
||||
encoder.writeBool(ATTRIB_ENUM, true);
|
||||
|
@ -1292,6 +1286,19 @@ public class PcodeDataTypeManager {
|
|||
if (tp instanceof Array) {
|
||||
return TYPE_ARRAY;
|
||||
}
|
||||
if (tp instanceof CharDataType) {
|
||||
return ((CharDataType) tp).isSigned() ? TYPE_INT : TYPE_UINT;
|
||||
}
|
||||
if (tp instanceof WideCharDataType || tp instanceof WideChar16DataType ||
|
||||
tp instanceof WideChar32DataType) {
|
||||
return TYPE_INT;
|
||||
}
|
||||
if (tp instanceof Enum) {
|
||||
return ((Enum) tp).isSigned() ? TYPE_INT : TYPE_UINT;
|
||||
}
|
||||
if (tp instanceof FunctionDefinition) {
|
||||
return TYPE_CODE;
|
||||
}
|
||||
return TYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
|
|
|
@ -80,17 +80,36 @@
|
|||
<pentry minsize="1" maxsize="500" align="8">
|
||||
<addr offset="8" space="stack"/>
|
||||
</pentry>
|
||||
<rule>
|
||||
<datatype name="any" maxsize="16"/>
|
||||
<join_dual_class/> <!-- Bind from registers if possible-->
|
||||
</rule>
|
||||
<rule>
|
||||
<datatype name="any"/>
|
||||
<goto_stack/>
|
||||
</rule>
|
||||
</input>
|
||||
<output>
|
||||
<pentry minsize="4" maxsize="8" metatype="float">
|
||||
<pentry minsize="1" maxsize="8" metatype="float">
|
||||
<register name="XMM0_Qa"/>
|
||||
</pentry>
|
||||
<pentry minsize="1" maxsize="8" metatype="float">
|
||||
<register name="XMM1_Qa"/>
|
||||
</pentry>
|
||||
<pentry minsize="1" maxsize="8">
|
||||
<register name="RAX"/>
|
||||
</pentry>
|
||||
<pentry minsize="9" maxsize="16">
|
||||
<addr space="join" piece1="RDX" piece2="RAX"/>
|
||||
<pentry minsize="1" maxsize="8">
|
||||
<register name="RDX"/>
|
||||
</pentry>
|
||||
<rule>
|
||||
<datatype name="any" maxsize="16"/>
|
||||
<join_dual_class/>
|
||||
</rule>
|
||||
<rule>
|
||||
<datatype name="any"/>
|
||||
<hidden_return/>
|
||||
</rule>
|
||||
</output>
|
||||
<killedbycall>
|
||||
<register name="RAX"/>
|
||||
|
|
|
@ -90,6 +90,8 @@ public class AbstractProtoModelTest extends AbstractGenericTest {
|
|||
dtManager.addDataType(new FloatDataType(), null);
|
||||
dtManager.addDataType(new DoubleDataType(), null);
|
||||
dtManager.addDataType(new Float16DataType(), null);
|
||||
dtManager.addDataType(new Undefined4DataType(), null);
|
||||
dtManager.addDataType(new Undefined8DataType(), null);
|
||||
}
|
||||
finally {
|
||||
dtManager.endTransaction(txID, true);
|
||||
|
|
|
@ -30,6 +30,63 @@ public class X64PrototypeModelTest extends AbstractProtoModelTest {
|
|||
public void testStdCall() throws Exception {
|
||||
PrototypeModel model = cspec.getCallingConvention("__stdcall");
|
||||
test(model, "void func(int a,int b)", "void,EDI,ESI");
|
||||
test(model, "void func(float,float)", "void,XMM0:4,XMM1:4");
|
||||
test(model, "void func(short a,int b,char c)", "void,DI,ESI,DX:1");
|
||||
test(model, "void func(long,long)", "void,RDI,RSI");
|
||||
test(model, "void func(double,double)", "void,XMM0:8,XMM1:8");
|
||||
test(model, "void func(int,float,int,float)", "void,EDI,XMM0:4,ESI,XMM1:4");
|
||||
test(model, "void func(float,int,float,int)", "void,XMM0:4,EDI,XMM1:4,ESI");
|
||||
test(model, "void func(int,double,double,int)", "void,EDI,XMM0:8,XMM1:8,ESI");
|
||||
test(model, "void func(double,long,long,double)", "void,XMM0:8,RDI,RSI,XMM1:8");
|
||||
test(model, "void func(long double)", "void,stack8:10");
|
||||
test(model, "void func(float,long double,float)", "void,XMM0:4,stack8:10,XMM1:4");
|
||||
parseStructure("intfloatpair", "int,float");
|
||||
test(model, "void func(intfloatpair)", "void,RDI");
|
||||
parseStructure("longfloatpair", "long,float");
|
||||
test(model, "void func(int,longfloatpair)", "void,EDI,join XMM0:8 RSI");
|
||||
parseStructure("longdoublepair", "long,double");
|
||||
test(model, "void func(int,longdoublepair)", "void,EDI,join XMM0:8 RSI");
|
||||
parseStructure("intdoublepair", "int,double");
|
||||
test(model, "void func(int,intdoublepair)", "void,EDI,join XMM0:8 RSI");
|
||||
parseStructure("floatintpair", "float,int");
|
||||
test(model, "void func(int,floatintpair)", "void,EDI,RSI");
|
||||
parseStructure("doubleintpair", "double,int");
|
||||
test(model, "void func(int,doubleintpair)", "void,EDI,join RSI XMM0:8");
|
||||
parseStructure("intintfloat", "int,int,float");
|
||||
test(model, "void func(int,intintfloat)", "void,EDI,join XMM0:4 RSI");
|
||||
parseStructure("intintfloatfloat", "int,int,float,float");
|
||||
test(model, "void func(int,intintfloatfloat)", "void,EDI,join XMM0:8 RSI");
|
||||
parseStructure("intfloatfloatint", "int,float,float,int");
|
||||
test(model, "void func(int,intfloatfloatint)", "void,EDI,join RDX RSI");
|
||||
parseStructure("intfloatfloat", "int,float,float");
|
||||
test(model, "void func(int,intfloatfloat)", "void,EDI,join XMM0:4 RSI");
|
||||
parseStructure("floatfloatpair", "float,float");
|
||||
test(model, "void func(int,floatfloatpair)", "void,EDI,XMM0:8");
|
||||
parseStructure("doublefloatpair", "double,float");
|
||||
test(model, "void func(int,doublefloatpair)", "void,EDI,join XMM1:8 XMM0:8");
|
||||
parseStructure("floatfloatfloat", "float,float,float");
|
||||
test(model, "void func(floatfloatfloat,long)", "void,join XMM1:4 XMM0:8,RDI");
|
||||
parseStructure("intintintint", "int,int,int,int");
|
||||
test(model, "void func(intintintint)", "void,join RSI RDI");
|
||||
test(model, "void func(int,intintintint)", "void,EDI,join RDX RSI");
|
||||
parseStructure("intintintintint", "int,int,int,int,int");
|
||||
test(model, "void func(intintintintint)", "void,stack8:20");
|
||||
test(model, "void func(float,float,float,float,float,float,float,float,longfloatpair)",
|
||||
"void,XMM0:4,XMM1:4,XMM2:4,XMM3:4,XMM4:4,XMM5:4,XMM6:4,XMM7:4,stack8:16");
|
||||
test(model, "void func(undefined4,undefined8)", "void,EDI,RSI");
|
||||
|
||||
test(model, "intintintint func(void)", "join RDX RAX");
|
||||
test(model, "floatintpair func(void)", "RAX");
|
||||
test(model, "longfloatpair func(void)", "join XMM0:8 RAX");
|
||||
test(model, "longdoublepair func(void)", "join XMM0:8 RAX");
|
||||
test(model, "doubleintpair func(void)", "join RAX XMM0:8");
|
||||
test(model, "floatfloatfloat func(void)", "join XMM1:4 XMM0:8");
|
||||
parseStructure("doubledoublepair", "double,double");
|
||||
test(model, "doubledoublepair func(void)", "join XMM1:8 XMM0:8");
|
||||
test(model, "floatfloatpair func(void)", "XMM0:8");
|
||||
test(model, "intintintintint func(void)", "RAX,RDI");
|
||||
parseStructure("doubleintintint", "double,int,int,int");
|
||||
test(model, "doubleintintint func(void)", "RAX,RDI");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue