mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-10-23 13:41:04 +00:00
GP-3938 PrototypeModel rules
This commit is contained in:
parent
dae07c1900
commit
191371675a
|
@ -71,6 +71,7 @@ model {
|
|||
include "cpool.cc"
|
||||
include "comment.cc"
|
||||
include "stringmanage.cc"
|
||||
include "modelrules.cc"
|
||||
include "fspec.cc"
|
||||
include "action.cc"
|
||||
include "loadimage.cc"
|
||||
|
|
|
@ -56,6 +56,7 @@ src/decompile/datatests/pointersub.xml||GHIDRA||||END|
|
|||
src/decompile/datatests/promotecompare.xml||GHIDRA||||END|
|
||||
src/decompile/datatests/ptrtoarray.xml||GHIDRA||||END|
|
||||
src/decompile/datatests/readvolatile.xml||GHIDRA||||END|
|
||||
src/decompile/datatests/retspecial.xml||GHIDRA||||END|
|
||||
src/decompile/datatests/retstruct.xml||GHIDRA||||END|
|
||||
src/decompile/datatests/sbyte.xml||GHIDRA||||END|
|
||||
src/decompile/datatests/skipnext2.xml||GHIDRA||||END|
|
||||
|
|
|
@ -79,7 +79,7 @@ EXTERNAL_CONSOLEEXT_NAMES=$(subst .cc,,$(notdir $(EXTERNAL_CONSOLEEXT_SOURCE)))
|
|||
CORE= xml marshal space float address pcoderaw translate opcodes globalcontext
|
||||
# Additional core files for any projects that decompile
|
||||
DECCORE=capability architecture options graph cover block cast typeop database cpool \
|
||||
comment stringmanage fspec action loadimage grammar varnode op \
|
||||
comment stringmanage modelrules fspec action loadimage grammar varnode op \
|
||||
type variable varmap jumptable emulate emulateutil flow userop \
|
||||
funcdata funcdata_block funcdata_op funcdata_varnode unionresolve pcodeinject \
|
||||
heritage prefersplit rangeutil ruleaction subflow blockaction merge double \
|
||||
|
|
|
@ -165,7 +165,7 @@ bool ParamEntry::groupOverlap(const ParamEntry &op2) const
|
|||
bool ParamEntry::subsumesDefinition(const ParamEntry &op2) const
|
||||
|
||||
{
|
||||
if ((type!=TYPE_UNKNOWN)&&(op2.type != type)) return false;
|
||||
if ((type!=TYPECLASS_GENERAL)&&(op2.type != type)) return false;
|
||||
if (spaceid != op2.spaceid) return false;
|
||||
if (op2.addressbase < addressbase) return false;
|
||||
if ((op2.addressbase+op2.size-1) > (addressbase+size-1)) return false;
|
||||
|
@ -467,7 +467,7 @@ void ParamEntry::decode(Decoder &decoder,bool normalstack,bool grouped,list<Para
|
|||
|
||||
{
|
||||
flags = 0;
|
||||
type = TYPE_UNKNOWN;
|
||||
type = TYPECLASS_GENERAL;
|
||||
size = minsize = -1; // Must be filled in
|
||||
alignment = 0; // default
|
||||
numslots = 1;
|
||||
|
@ -488,8 +488,8 @@ void ParamEntry::decode(Decoder &decoder,bool normalstack,bool grouped,list<Para
|
|||
else if (attribId == ATTRIB_MAXSIZE) {
|
||||
size = decoder.readSignedInteger();
|
||||
}
|
||||
else if (attribId == ATTRIB_METATYPE)
|
||||
type = string2metatype(decoder.readString());
|
||||
else if (attribId == ATTRIB_STORAGE || attribId == ATTRIB_METATYPE)
|
||||
type = string2typeclass(decoder.readString());
|
||||
else if (attribId == ATTRIB_EXTENSION) {
|
||||
flags &= ~((uint4)(smallsize_zext | smallsize_sext | smallsize_inttype));
|
||||
string ext = decoder.readString();
|
||||
|
@ -550,7 +550,7 @@ void ParamEntry::orderWithinGroup(const ParamEntry &entry1,const ParamEntry &ent
|
|||
if (entry2.minsize > entry1.size || entry1.minsize > entry2.size)
|
||||
return;
|
||||
if (entry1.type != entry2.type) {
|
||||
if (entry1.type == TYPE_UNKNOWN) {
|
||||
if (entry1.type == TYPECLASS_GENERAL) {
|
||||
throw LowlevelError("<pentry> tags with a specific type must come before the general type");
|
||||
}
|
||||
return;
|
||||
|
@ -565,9 +565,11 @@ ParamListStandard::ParamListStandard(const ParamListStandard &op2)
|
|||
entry = op2.entry;
|
||||
spacebase = op2.spacebase;
|
||||
maxdelay = op2.maxdelay;
|
||||
pointermax = op2.pointermax;
|
||||
thisbeforeret = op2.thisbeforeret;
|
||||
resourceStart = op2.resourceStart;
|
||||
for(list<ModelRule>::const_iterator iter=op2.modelRules.begin();iter!=op2.modelRules.end();++iter) {
|
||||
modelRules.emplace_back(*iter,&op2);
|
||||
}
|
||||
populateResolver();
|
||||
}
|
||||
|
||||
|
@ -645,75 +647,100 @@ int4 ParamListStandard::characterizeAsParam(const Address &loc,int4 size) const
|
|||
return ParamEntry::no_containment;
|
||||
}
|
||||
|
||||
/// Given the next data-type and the status of previously allocated slots,
|
||||
/// \brief Assign storage for given parameter class, using the fallback assignment algorithm
|
||||
///
|
||||
/// Given a resource list, a data-type, and the status of previously allocated slots,
|
||||
/// select the storage location for the parameter. The status array is
|
||||
/// indexed by \e group: a positive value indicates how many \e slots have been allocated
|
||||
/// from that group, and a -1 indicates the group/resource is fully consumed.
|
||||
/// \param tp is the data-type of the next parameter
|
||||
/// If an Address can be assigned to the parameter, it and other details are passed back in the
|
||||
/// ParameterPieces object and the \e success code is returned. Otherwise, the \e fail code is returned.
|
||||
/// \param resource is the resource list to allocate from
|
||||
/// \param tp is the data-type of the parameter
|
||||
/// \param matchExact is \b false if TYPECLASS_GENERAL is considered a match for any storage class
|
||||
/// \param status is an array marking how many \e slots have already been consumed in a group
|
||||
/// \return the newly assigned address for the parameter
|
||||
Address ParamListStandard::assignAddress(const Datatype *tp,vector<int4> &status) const
|
||||
|
||||
/// \param param will hold the address of the newly assigned parameter
|
||||
/// \return either \e success or \e fail
|
||||
uint4 ParamListStandard::assignAddressFallback(type_class resource,Datatype *tp,bool matchExact,
|
||||
vector<int4> &status,ParameterPieces ¶m) const
|
||||
{
|
||||
list<ParamEntry>::const_iterator iter;
|
||||
for(iter=entry.begin();iter!=entry.end();++iter) {
|
||||
const ParamEntry &curEntry( *iter );
|
||||
int4 grp = curEntry.getGroup();
|
||||
if (status[grp]<0) continue;
|
||||
if ((curEntry.getType() != TYPE_UNKNOWN) && tp->getMetatype() != curEntry.getType())
|
||||
continue; // Wrong type
|
||||
if (resource != curEntry.getType()) {
|
||||
if (matchExact || curEntry.getType() != TYPECLASS_GENERAL)
|
||||
continue; // Wrong type
|
||||
}
|
||||
|
||||
Address res = curEntry.getAddrBySlot(status[grp],tp->getAlignSize(),tp->getAlignment());
|
||||
if (res.isInvalid()) continue; // If -tp- doesn't fit an invalid address is returned
|
||||
param.addr = curEntry.getAddrBySlot(status[grp],tp->getAlignSize(),tp->getAlignment());
|
||||
if (param.addr.isInvalid()) continue; // If -tp- doesn't fit an invalid address is returned
|
||||
if (curEntry.isExclusion()) {
|
||||
const vector<int4> &groupSet(curEntry.getAllGroups());
|
||||
for(int4 j=0;j<groupSet.size();++j) // For an exclusion entry
|
||||
status[groupSet[j]] = -1; // some number of groups are taken up
|
||||
}
|
||||
return res;
|
||||
param.type = tp;
|
||||
param.flags = 0;
|
||||
return AssignAction::success;
|
||||
}
|
||||
return Address(); // Return invalid address to indicated we could not assign anything
|
||||
return AssignAction::fail; // Unable to make an assignment
|
||||
}
|
||||
|
||||
void ParamListStandard::assignMap(const vector<Datatype *> &proto,TypeFactory &typefactory,vector<ParameterPieces> &res) const
|
||||
/// \brief Fill in the Address and other details for the given parameter
|
||||
///
|
||||
/// Attempt to apply a ModelRule first. If these do not succeed, use the fallback assignment algorithm.
|
||||
/// \param dt is the data-type assigned to the parameter
|
||||
/// \param proto is the description of the function prototype
|
||||
/// \param pos is the position of the parameter to assign (pos=-1 for output, pos >=0 for input)
|
||||
/// \param tlist is the data-type factory for (possibly) transforming the parameter's data-type
|
||||
/// \param status is the consumed resource status array
|
||||
/// \param res is parameter description to be filled in
|
||||
/// \return the response code
|
||||
uint4 ParamListStandard::assignAddress(Datatype *dt,const PrototypePieces &proto,int4 pos,TypeFactory &tlist,
|
||||
vector<int4> &status,ParameterPieces &res) const
|
||||
|
||||
{
|
||||
for(list<ModelRule>::const_iterator iter=modelRules.begin();iter!=modelRules.end();++iter) {
|
||||
uint4 responseCode = (*iter).assignAddress(dt, proto, pos, tlist, status, res);
|
||||
if (responseCode != AssignAction::fail)
|
||||
return responseCode;
|
||||
}
|
||||
type_class store = metatype2typeclass(dt->getMetatype());
|
||||
return assignAddressFallback(store,dt,false,status,res);
|
||||
}
|
||||
|
||||
void ParamListStandard::assignMap(const PrototypePieces &proto,TypeFactory &typefactory,vector<ParameterPieces> &res) const
|
||||
|
||||
{
|
||||
vector<int4> status(numgroup,0);
|
||||
|
||||
if (res.size() == 2) { // Check for hidden parameters defined by the output list
|
||||
res.back().addr = assignAddress(res.back().type,status); // Reserve first param for hidden ret value
|
||||
res.back().flags |= ParameterPieces::hiddenretparm;
|
||||
if (res.back().addr.isInvalid())
|
||||
Datatype *dt = res.back().type;
|
||||
type_class store;
|
||||
if ((res.back().flags & ParameterPieces::hiddenretparm) != 0)
|
||||
store = TYPECLASS_HIDDENRET;
|
||||
else
|
||||
store = metatype2typeclass(dt->getMetatype());
|
||||
// Reserve first param for hidden return pointer
|
||||
if (assignAddressFallback(store,dt,false,status,res.back()) == AssignAction::fail)
|
||||
throw ParamUnassignedError("Cannot assign parameter address for " + res.back().type->getName());
|
||||
res.back().flags |= ParameterPieces::hiddenretparm;
|
||||
}
|
||||
for(int4 i=1;i<proto.size();++i) {
|
||||
for(int4 i=0;i<proto.intypes.size();++i) {
|
||||
res.emplace_back();
|
||||
if ((pointermax != 0) && (proto[i]->getSize() > pointermax)) { // Datatype is too big
|
||||
// Assume datatype is stored elsewhere and only the pointer is passed
|
||||
AddrSpace *spc = spacebase;
|
||||
if (spc == (AddrSpace*)0) spc = typefactory.getArch()->getDefaultDataSpace();
|
||||
int4 pointersize = spc->getAddrSize();
|
||||
int4 wordsize = spc->getWordSize();
|
||||
Datatype *pointertp = typefactory.getTypePointer(pointersize,proto[i],wordsize);
|
||||
res.back().addr = assignAddress(pointertp,status);
|
||||
res.back().type = pointertp;
|
||||
res.back().flags = ParameterPieces::indirectstorage;
|
||||
}
|
||||
else {
|
||||
res.back().addr = assignAddress(proto[i],status);
|
||||
res.back().type = proto[i];
|
||||
res.back().flags = 0;
|
||||
}
|
||||
if (res.back().addr.isInvalid())
|
||||
throw ParamUnassignedError("Cannot assign parameter address for " + proto[i]->getName());
|
||||
Datatype *dt = proto.intypes[i];
|
||||
if (assignAddress(dt,proto,i,typefactory,status,res.back()) == AssignAction::fail)
|
||||
throw ParamUnassignedError("Cannot assign parameter address for " + dt->getName());
|
||||
}
|
||||
}
|
||||
|
||||
/// From among the ParamEntrys matching the given \e group, return the one that best matches
|
||||
/// the given \e metatype attribute. If there are no ParamEntrys in the group, null is returned.
|
||||
/// \param grp is the given \e group number
|
||||
/// \param prefType is the preferred \e metatype attribute to match
|
||||
const ParamEntry *ParamListStandard::selectUnreferenceEntry(int4 grp,type_metatype prefType) const
|
||||
/// \param prefType is the preferred \e storage \e class attribute to match
|
||||
const ParamEntry *ParamListStandard::selectUnreferenceEntry(int4 grp,type_class prefType) const
|
||||
|
||||
{
|
||||
int4 bestScore = -1;
|
||||
|
@ -725,7 +752,7 @@ const ParamEntry *ParamListStandard::selectUnreferenceEntry(int4 grp,type_metaty
|
|||
int4 curScore;
|
||||
if (curEntry->getType() == prefType)
|
||||
curScore = 2;
|
||||
else if (prefType == TYPE_UNKNOWN)
|
||||
else if (prefType == TYPECLASS_GENERAL)
|
||||
curScore = 1;
|
||||
else
|
||||
curScore = 0;
|
||||
|
@ -760,7 +787,7 @@ void ParamListStandard::buildTrialMap(ParamActive *active) const
|
|||
paramtrial.setEntry( entrySlot, 0 ); // Keep track of entry recovered for this trial
|
||||
|
||||
if (paramtrial.isActive()) {
|
||||
if (entrySlot->getType() == TYPE_FLOAT)
|
||||
if (entrySlot->getType() == TYPECLASS_FLOAT)
|
||||
floatCount += 1;
|
||||
else
|
||||
intCount += 1;
|
||||
|
@ -783,7 +810,7 @@ void ParamListStandard::buildTrialMap(ParamActive *active) const
|
|||
const ParamEntry *curentry = hitlist[i];
|
||||
|
||||
if (curentry == (const ParamEntry *)0) {
|
||||
curentry = selectUnreferenceEntry(i, (floatCount > intCount) ? TYPE_FLOAT : TYPE_UNKNOWN);
|
||||
curentry = selectUnreferenceEntry(i, (floatCount > intCount) ? TYPECLASS_FLOAT : TYPECLASS_GENERAL);
|
||||
if (curentry == (const ParamEntry *)0)
|
||||
continue;
|
||||
int4 sz = curentry->isExclusion() ? curentry->getSize() : curentry->getAlign();
|
||||
|
@ -890,7 +917,7 @@ void ParamListStandard::markGroupNoUse(ParamActive *active,int4 activeTrial,int4
|
|||
/// \param group is the group number
|
||||
/// \param groupStart is the index of the first trial in the group
|
||||
/// \param prefType is a preferred entry to type to use in scoring
|
||||
void ParamListStandard::markBestInactive(ParamActive *active,int4 group,int4 groupStart,type_metatype prefType)
|
||||
void ParamListStandard::markBestInactive(ParamActive *active,int4 group,int4 groupStart,type_class prefType)
|
||||
|
||||
{
|
||||
int4 numTrials = active->getNumTrials();
|
||||
|
@ -939,7 +966,7 @@ void ParamListStandard::forceExclusionGroup(ParamActive *active)
|
|||
int4 grp = curtrial.getEntry()->getGroup();
|
||||
if (grp != curGroup) {
|
||||
if (inactiveCount > 1)
|
||||
markBestInactive(active, curGroup, groupStart, TYPE_UNKNOWN);
|
||||
markBestInactive(active, curGroup, groupStart, TYPECLASS_GENERAL);
|
||||
curGroup = grp;
|
||||
groupStart = i;
|
||||
inactiveCount = 0;
|
||||
|
@ -952,7 +979,7 @@ void ParamListStandard::forceExclusionGroup(ParamActive *active)
|
|||
}
|
||||
}
|
||||
if (inactiveCount > 1)
|
||||
markBestInactive(active, curGroup, groupStart, TYPE_UNKNOWN);
|
||||
markBestInactive(active, curGroup, groupStart, TYPECLASS_GENERAL);
|
||||
}
|
||||
|
||||
/// \brief Mark every trial above the first "definitely not used" as \e inactive.
|
||||
|
@ -1123,17 +1150,17 @@ void ParamListStandard::populateResolver(void)
|
|||
void ParamListStandard::parsePentry(Decoder &decoder,vector<EffectRecord> &effectlist,
|
||||
int4 groupid,bool normalstack,bool autokill,bool splitFloat,bool grouped)
|
||||
{
|
||||
type_metatype lastMeta = TYPE_UNION;
|
||||
type_class lastClass = TYPECLASS_CLASS4;
|
||||
if (!entry.empty()) {
|
||||
lastMeta = entry.back().isGrouped() ? TYPE_UNKNOWN : entry.back().getType();
|
||||
lastClass = entry.back().isGrouped() ? TYPECLASS_GENERAL : entry.back().getType();
|
||||
}
|
||||
entry.emplace_back(groupid);
|
||||
entry.back().decode(decoder,normalstack,grouped,entry);
|
||||
if (splitFloat) {
|
||||
type_metatype currentMeta = grouped ? TYPE_UNKNOWN : entry.back().getType();
|
||||
if (lastMeta != currentMeta) {
|
||||
if (lastMeta > currentMeta)
|
||||
throw LowlevelError("parameter list entries must be ordered by metatype");
|
||||
type_class currentClass = grouped ? TYPECLASS_GENERAL : entry.back().getType();
|
||||
if (lastClass != currentClass) {
|
||||
if (lastClass < currentClass)
|
||||
throw LowlevelError("parameter list entries must be ordered by storage class");
|
||||
resourceStart.push_back(groupid);
|
||||
}
|
||||
}
|
||||
|
@ -1351,7 +1378,7 @@ void ParamListStandard::decode(Decoder &decoder,vector<EffectRecord> &effectlist
|
|||
{
|
||||
numgroup = 0;
|
||||
spacebase = (AddrSpace *)0;
|
||||
pointermax = 0;
|
||||
int4 pointermax = 0;
|
||||
thisbeforeret = false;
|
||||
bool splitFloat = true; // True if we should split FLOAT entries into their own resource section
|
||||
bool autokilledbycall = false;
|
||||
|
@ -1381,11 +1408,30 @@ void ParamListStandard::decode(Decoder &decoder,vector<EffectRecord> &effectlist
|
|||
else if (subId == ELEM_GROUP) {
|
||||
parseGroup(decoder, effectlist, numgroup, normalstack, autokilledbycall, splitFloat);
|
||||
}
|
||||
else if (subId == ELEM_RULE) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for(;;) {
|
||||
uint4 subId = decoder.peekElement();
|
||||
if (subId == 0) break;
|
||||
if (subId == ELEM_RULE) {
|
||||
modelRules.emplace_back();
|
||||
modelRules.back().decode(decoder, this);
|
||||
}
|
||||
else {
|
||||
throw LowlevelError("<pentry> and <group> elements must come before any <modelrule>");
|
||||
}
|
||||
}
|
||||
decoder.closeElement(elemId);
|
||||
resourceStart.push_back(numgroup);
|
||||
calcDelay();
|
||||
populateResolver();
|
||||
if (pointermax > 0) { // Add a ModelRule at the end that converts too big data-types to pointers
|
||||
SizeRestrictedFilter typeFilter(pointermax+1,0);
|
||||
ConvertToPointer action(this);
|
||||
modelRules.emplace_back(typeFilter,action,this);
|
||||
}
|
||||
}
|
||||
|
||||
ParamList *ParamListStandard::clone(void) const
|
||||
|
@ -1395,27 +1441,102 @@ ParamList *ParamListStandard::clone(void) const
|
|||
return res;
|
||||
}
|
||||
|
||||
void ParamListRegisterOut::assignMap(const vector<Datatype *> &proto,TypeFactory &typefactory,vector<ParameterPieces> &res) const
|
||||
void ParamListRegisterOut::assignMap(const PrototypePieces &proto,TypeFactory &typefactory,vector<ParameterPieces> &res) const
|
||||
|
||||
{
|
||||
vector<int4> status(numgroup,0);
|
||||
res.emplace_back();
|
||||
if (proto[0]->getMetatype() != TYPE_VOID) {
|
||||
res.back().addr = assignAddress(proto[0],status);
|
||||
if (proto.outtype->getMetatype() != TYPE_VOID) {
|
||||
assignAddress(proto.outtype,proto,-1,typefactory,status,res.back());
|
||||
if (res.back().addr.isInvalid())
|
||||
throw ParamUnassignedError("Cannot assign parameter address for " + proto[0]->getName());
|
||||
throw ParamUnassignedError("Cannot assign parameter address for " + proto.outtype->getName());
|
||||
}
|
||||
else {
|
||||
res.back().type = proto.outtype;
|
||||
res.back().flags = 0;
|
||||
}
|
||||
res.back().type = proto[0];
|
||||
res.back().flags = 0;
|
||||
}
|
||||
|
||||
void ParamListRegisterOut::fillinMap(ParamActive *active) const
|
||||
ParamList *ParamListRegisterOut::clone(void) const
|
||||
|
||||
{
|
||||
ParamList *res = new ParamListRegisterOut(*this);
|
||||
return res;
|
||||
}
|
||||
|
||||
void ParamListRegister::fillinMap(ParamActive *active) const
|
||||
|
||||
{
|
||||
if (active->getNumTrials() == 0) return; // No trials to check
|
||||
|
||||
// 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());
|
||||
if (entrySlot == (const ParamEntry *)0) // There may be no matching entry (if the model was recovered late)
|
||||
paramtrial.markNoUse();
|
||||
else {
|
||||
paramtrial.setEntry( entrySlot,0 ); // Keep track of entry recovered for this trial
|
||||
if (paramtrial.isActive())
|
||||
paramtrial.markUsed();
|
||||
}
|
||||
}
|
||||
active->sortTrials();
|
||||
}
|
||||
|
||||
ParamList *ParamListRegister::clone(void) const
|
||||
|
||||
{
|
||||
ParamList *res = new ParamListRegister( *this );
|
||||
return res;
|
||||
}
|
||||
|
||||
void ParamListStandardOut::assignMap(const PrototypePieces &proto,TypeFactory &typefactory,vector<ParameterPieces> &res) const
|
||||
|
||||
{
|
||||
vector<int4> status(numgroup,0);
|
||||
|
||||
res.emplace_back();
|
||||
if (proto.outtype->getMetatype() == TYPE_VOID) {
|
||||
res.back().type = proto.outtype;
|
||||
res.back().flags = 0;
|
||||
return; // Leave the address as invalid
|
||||
}
|
||||
uint4 responseCode = assignAddress(proto.outtype,proto,-1,typefactory,status,res.back());
|
||||
if (responseCode != AssignAction::success) { // Could not assign an address (too big)
|
||||
AddrSpace *spc = spacebase;
|
||||
if (spc == (AddrSpace *)0)
|
||||
spc = typefactory.getArch()->getDefaultDataSpace();
|
||||
int4 pointersize = spc->getAddrSize();
|
||||
int4 wordsize = spc->getWordSize();
|
||||
Datatype *pointertp = typefactory.getTypePointer(pointersize, proto.outtype, wordsize);
|
||||
if (responseCode == AssignAction::hiddenret_specialreg_void) {
|
||||
res.back().type = typefactory.getTypeVoid();
|
||||
res.back().flags = 0;
|
||||
}
|
||||
else {
|
||||
if (assignAddressFallback(TYPECLASS_PTR,pointertp,false,status,res.back()) == AssignAction::fail)
|
||||
throw ParamUnassignedError("Cannot assign return value as a pointer");
|
||||
res.back().flags = ParameterPieces::indirectstorage;
|
||||
}
|
||||
|
||||
res.emplace_back(); // Add extra storage location in the input params
|
||||
res.back().type = pointertp; // that holds a pointer to where the return value should be stored
|
||||
// leave its address invalid, to be filled in by the input list assignMap
|
||||
// Encode whether or not hidden return should be drawn from TYPECLASS_HIDDENRET
|
||||
bool isSpecial = (responseCode == AssignAction::hiddenret_specialreg ||
|
||||
responseCode == AssignAction::hiddenret_specialreg_void);
|
||||
res.back().flags = isSpecial ? ParameterPieces::hiddenretparm : 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ParamListStandardOut::fillinMap(ParamActive *active) const
|
||||
|
||||
{
|
||||
if (active->getNumTrials() == 0) return; // No trials to check
|
||||
const ParamEntry *bestentry = (const ParamEntry *)0;
|
||||
int4 bestcover = 0;
|
||||
type_metatype bestmetatype = TYPE_PTR;
|
||||
type_class bestclass = TYPECLASS_PTR;
|
||||
|
||||
// Find entry which is best covered by the active trials
|
||||
list<ParamEntry>::const_iterator iter;
|
||||
|
@ -1457,10 +1578,10 @@ void ParamListRegisterOut::fillinMap(ParamActive *active) const
|
|||
k = 0; // Don't use this entry
|
||||
// Prefer a more generic type restriction if we have it
|
||||
// prefer the larger coverage
|
||||
if ((k==active->getNumTrials())&&((curentry->getType() > bestmetatype)||(offmatch > bestcover))) {
|
||||
if ((k==active->getNumTrials())&&((curentry->getType() < bestclass)||(offmatch > bestcover))) {
|
||||
bestentry = curentry;
|
||||
bestcover = offmatch;
|
||||
bestmetatype = curentry->getType();
|
||||
bestclass = curentry->getType();
|
||||
}
|
||||
}
|
||||
if (bestentry==(const ParamEntry *)0) {
|
||||
|
@ -1490,7 +1611,7 @@ void ParamListRegisterOut::fillinMap(ParamActive *active) const
|
|||
}
|
||||
}
|
||||
|
||||
bool ParamListRegisterOut::possibleParam(const Address &loc,int4 size) const
|
||||
bool ParamListStandardOut::possibleParam(const Address &loc,int4 size) const
|
||||
|
||||
{
|
||||
list<ParamEntry>::const_iterator iter;
|
||||
|
@ -1501,92 +1622,6 @@ bool ParamListRegisterOut::possibleParam(const Address &loc,int4 size) const
|
|||
return false;
|
||||
}
|
||||
|
||||
ParamList *ParamListRegisterOut::clone(void) const
|
||||
|
||||
{
|
||||
ParamList *res = new ParamListRegisterOut(*this);
|
||||
return res;
|
||||
}
|
||||
|
||||
void ParamListRegister::fillinMap(ParamActive *active) const
|
||||
|
||||
{
|
||||
if (active->getNumTrials() == 0) return; // No trials to check
|
||||
|
||||
// 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());
|
||||
if (entrySlot == (const ParamEntry *)0) // There may be no matching entry (if the model was recovered late)
|
||||
paramtrial.markNoUse();
|
||||
else {
|
||||
paramtrial.setEntry( entrySlot,0 ); // Keep track of entry recovered for this trial
|
||||
if (paramtrial.isActive())
|
||||
paramtrial.markUsed();
|
||||
}
|
||||
}
|
||||
active->sortTrials();
|
||||
}
|
||||
|
||||
ParamList *ParamListRegister::clone(void) const
|
||||
|
||||
{
|
||||
ParamList *res = new ParamListRegister( *this );
|
||||
return res;
|
||||
}
|
||||
|
||||
void ParamListStandardOut::assignMap(const vector<Datatype *> &proto,TypeFactory &typefactory,vector<ParameterPieces> &res) const
|
||||
|
||||
{
|
||||
vector<int4> status(numgroup,0);
|
||||
|
||||
res.emplace_back();
|
||||
res.back().type = proto[0];
|
||||
res.back().flags = 0;
|
||||
if (proto[0]->getMetatype() == TYPE_VOID) {
|
||||
return; // Leave the address as invalid
|
||||
}
|
||||
res.back().addr = assignAddress(proto[0],status);
|
||||
if (res.back().addr.isInvalid()) { // Could not assign an address (too big)
|
||||
AddrSpace *spc = spacebase;
|
||||
if (spc == (AddrSpace *)0)
|
||||
spc = typefactory.getArch()->getDefaultDataSpace();
|
||||
int4 pointersize = spc->getAddrSize();
|
||||
int4 wordsize = spc->getWordSize();
|
||||
Datatype *pointertp = typefactory.getTypePointer(pointersize, proto[0], wordsize);
|
||||
res.back().addr = assignAddress(pointertp,status);
|
||||
if (res.back().addr.isInvalid())
|
||||
throw ParamUnassignedError("Cannot assign return value as a pointer");
|
||||
res.back().type = pointertp;
|
||||
res.back().flags = ParameterPieces::indirectstorage;
|
||||
|
||||
res.emplace_back(); // Add extra storage location in the input params
|
||||
res.back().type = pointertp; // that holds a pointer to where the return value should be stored
|
||||
// leave its address invalid, to be filled in by the input list assignMap
|
||||
res.back().flags = ParameterPieces::hiddenretparm; // Mark it as special
|
||||
}
|
||||
}
|
||||
|
||||
void ParamListStandardOut::decode(Decoder &decoder,vector<EffectRecord> &effectlist,bool normalstack)
|
||||
|
||||
{
|
||||
ParamListRegisterOut::decode(decoder,effectlist,normalstack);
|
||||
// Check for double precision entries
|
||||
list<ParamEntry>::iterator iter;
|
||||
ParamEntry *previous1 = (ParamEntry *)0;
|
||||
ParamEntry *previous2 = (ParamEntry *)0;
|
||||
for(iter=entry.begin();iter!=entry.end();++iter) {
|
||||
ParamEntry &curEntry(*iter);
|
||||
if (previous1 != (ParamEntry *)0) {
|
||||
ParamEntry::orderWithinGroup(*previous1, curEntry);
|
||||
if (previous2 != (ParamEntry *)0)
|
||||
ParamEntry::orderWithinGroup(*previous2, curEntry);
|
||||
}
|
||||
previous2 = previous1;
|
||||
previous1 = &curEntry;
|
||||
}
|
||||
}
|
||||
|
||||
ParamList *ParamListStandardOut::clone(void) const
|
||||
|
||||
{
|
||||
|
@ -1980,6 +2015,20 @@ void FspecSpace::decode(Decoder &decoder)
|
|||
throw LowlevelError("Should never decode fspec space from stream");
|
||||
}
|
||||
|
||||
/// Swap any data-type and flags, but leave the storage address intact.
|
||||
/// This assumes the two parameters are the same size.
|
||||
/// \param op is the other parameter to swap with \b this.
|
||||
void ParameterPieces::swapMarkup(ParameterPieces &op)
|
||||
|
||||
{
|
||||
uint4 tmpFlags = flags;
|
||||
Datatype *tmpType = type;
|
||||
flags = op.flags;
|
||||
type = op.type;
|
||||
op.flags = tmpFlags;
|
||||
op.type = tmpType;
|
||||
}
|
||||
|
||||
/// The type is set to \e unknown_effect
|
||||
/// \param addr is the start of the memory range
|
||||
/// \param size is the number of bytes in the memory range
|
||||
|
@ -2186,27 +2235,25 @@ bool ProtoModel::isCompatible(const ProtoModel *op2) const
|
|||
|
||||
/// \brief Calculate input and output storage locations given a function prototype
|
||||
///
|
||||
/// The data-types of the function prototype are passed in as an ordered list, with the
|
||||
/// first data-type corresponding to the \e return \e value and all remaining
|
||||
/// data-types corresponding to the input parameters. Based on \b this model, a storage location
|
||||
/// is selected for each (input and output) parameter and passed back to the caller.
|
||||
/// The passed back storage locations are ordered similarly, with the output storage
|
||||
/// as the first entry. The model has the option of inserting a \e hidden return value
|
||||
/// pointer in the input storage locations.
|
||||
/// The data-types of the function prototype are passed in. Based on \b this model, a
|
||||
/// location is selected for each (input and output) parameter and passed back to the
|
||||
/// caller. The passed back storage locations are ordered with the output storage
|
||||
/// as the first entry, followed by the input storage locations. The model has the option
|
||||
/// of inserting a \e hidden return value pointer in the input storage locations.
|
||||
///
|
||||
/// A \b void return type is indicated by the formal TYPE_VOID in the (either) list.
|
||||
/// A \b void return type is indicated by the formal TYPE_VOID.
|
||||
/// If the model can't map the specific output prototype, the caller has the option of whether
|
||||
/// an exception (ParamUnassignedError) is thrown. If they choose not to throw,
|
||||
/// the unmapped return value is assumed to be \e void.
|
||||
/// \param typelist is the list of data-types from the function prototype
|
||||
/// \param proto is the data-types associated with the function prototype
|
||||
/// \param res will hold the storage locations for each parameter
|
||||
/// \param ignoreOutputError is \b true if problems assigning the output parameter are ignored
|
||||
void ProtoModel::assignParameterStorage(const vector<Datatype *> &typelist,vector<ParameterPieces> &res,bool ignoreOutputError)
|
||||
void ProtoModel::assignParameterStorage(const PrototypePieces &proto,vector<ParameterPieces> &res,bool ignoreOutputError)
|
||||
|
||||
{
|
||||
if (ignoreOutputError) {
|
||||
try {
|
||||
output->assignMap(typelist,*glb->types,res);
|
||||
output->assignMap(proto,*glb->types,res);
|
||||
}
|
||||
catch(ParamUnassignedError &err) {
|
||||
res.clear();
|
||||
|
@ -2217,9 +2264,23 @@ void ProtoModel::assignParameterStorage(const vector<Datatype *> &typelist,vecto
|
|||
}
|
||||
}
|
||||
else {
|
||||
output->assignMap(typelist,*glb->types,res);
|
||||
output->assignMap(proto,*glb->types,res);
|
||||
}
|
||||
input->assignMap(proto,*glb->types,res);
|
||||
|
||||
if (hasThis && res.size() > 1) {
|
||||
int4 thisIndex = 1;
|
||||
if ((res[1].flags & ParameterPieces::hiddenretparm) != 0 && res.size() > 2) {
|
||||
if (input->isThisBeforeRetPointer()) {
|
||||
// pointer has been bumped by auto-return-storage
|
||||
res[1].swapMarkup(res[2]); // must swap markup for slots 1 and 2
|
||||
}
|
||||
else {
|
||||
thisIndex = 2;
|
||||
}
|
||||
}
|
||||
res[thisIndex].flags |= ParameterPieces::isthis;
|
||||
}
|
||||
input->assignMap(typelist,*glb->types,res);
|
||||
}
|
||||
|
||||
/// \brief Look up an effect from the given EffectRecord list
|
||||
|
@ -3198,11 +3259,12 @@ void ProtoStoreInternal::decode(Decoder &decoder,ProtoModel *model)
|
|||
{
|
||||
Architecture *glb = model->getArch();
|
||||
vector<ParameterPieces> pieces;
|
||||
vector<string> namelist;
|
||||
PrototypePieces proto;
|
||||
proto.model = model;
|
||||
proto.firstVarArgSlot = -1;
|
||||
bool addressesdetermined = true;
|
||||
|
||||
pieces.push_back( ParameterPieces() ); // Push on placeholder for output pieces
|
||||
namelist.push_back("ret");
|
||||
pieces.back().type = outparam->getType();
|
||||
pieces.back().flags = 0;
|
||||
if (outparam->isTypeLocked())
|
||||
|
@ -3245,7 +3307,7 @@ void ProtoStoreInternal::decode(Decoder &decoder,ProtoModel *model)
|
|||
}
|
||||
}
|
||||
if ((flags & ParameterPieces::hiddenretparm) == 0)
|
||||
namelist.push_back(name);
|
||||
proto.innames.push_back(name);
|
||||
pieces.emplace_back();
|
||||
ParameterPieces &curparam( pieces.back() );
|
||||
curparam.addr = Address::decode(decoder);
|
||||
|
@ -3260,11 +3322,11 @@ void ProtoStoreInternal::decode(Decoder &decoder,ProtoModel *model)
|
|||
if (!addressesdetermined) {
|
||||
// If addresses for parameters are not provided, use
|
||||
// the model to derive them from type info
|
||||
vector<Datatype *> typelist;
|
||||
for(int4 i=0;i<pieces.size();++i) // Save off the decoded types
|
||||
typelist.push_back( pieces[i].type );
|
||||
proto.outtype = pieces[0].type;
|
||||
for(int4 i=1;i<pieces.size();++i) // Save off the decoded types
|
||||
proto.intypes.push_back( pieces[i].type );
|
||||
vector<ParameterPieces> addrPieces;
|
||||
model->assignParameterStorage(typelist,addrPieces,true);
|
||||
model->assignParameterStorage(proto,addrPieces,true);
|
||||
addrPieces.swap(pieces);
|
||||
uint4 k = 0;
|
||||
for(uint4 i=0;i<pieces.size();++i) {
|
||||
|
@ -3279,14 +3341,14 @@ void ProtoStoreInternal::decode(Decoder &decoder,ProtoModel *model)
|
|||
curparam = setOutput(pieces[0]);
|
||||
curparam->setTypeLock((pieces[0].flags & ParameterPieces::typelock)!=0);
|
||||
}
|
||||
uint4 j=1;
|
||||
uint4 j=0;
|
||||
for(uint4 i=1;i<pieces.size();++i) {
|
||||
if ((pieces[i].flags&ParameterPieces::hiddenretparm)!=0) {
|
||||
curparam = setInput(i-1,"rethidden",pieces[i]);
|
||||
curparam->setTypeLock((pieces[0].flags & ParameterPieces::typelock)!=0); // Has output's typelock
|
||||
continue; // increment i but not j
|
||||
}
|
||||
curparam = setInput(i-1,namelist[j],pieces[i]);
|
||||
curparam = setInput(i-1,proto.innames[j],pieces[i]);
|
||||
curparam->setTypeLock((pieces[i].flags & ParameterPieces::typelock)!=0);
|
||||
curparam->setNameLock((pieces[i].flags & ParameterPieces::namelock)!=0);
|
||||
j = j + 1;
|
||||
|
@ -3436,37 +3498,36 @@ void FuncProto::paramShift(int4 paramshift)
|
|||
if ((model == (ProtoModel *)0)||(store == (ProtoStore *)0))
|
||||
throw LowlevelError("Cannot parameter shift without a model");
|
||||
|
||||
vector<string> nmlist;
|
||||
vector<Datatype *> typelist;
|
||||
bool isdotdotdot = false;
|
||||
PrototypePieces proto;
|
||||
proto.model = model;
|
||||
proto.firstVarArgSlot = -1;
|
||||
TypeFactory *typefactory = model->getArch()->types;
|
||||
|
||||
if (isOutputLocked())
|
||||
typelist.push_back( getOutputType() );
|
||||
proto.outtype = getOutputType();
|
||||
else
|
||||
typelist.push_back( typefactory->getTypeVoid() );
|
||||
nmlist.push_back("");
|
||||
proto.outtype = typefactory->getTypeVoid();
|
||||
|
||||
Datatype *extra = typefactory->getBase(4,TYPE_UNKNOWN); // The extra parameters have this type
|
||||
for(int4 i=0;i<paramshift;++i) {
|
||||
nmlist.push_back("");
|
||||
typelist.push_back(extra);
|
||||
proto.innames.push_back("");
|
||||
proto.intypes.push_back(extra);
|
||||
}
|
||||
|
||||
if (isInputLocked()) { // Copy in the original parameter types
|
||||
int4 num = numParams();
|
||||
for(int4 i=0;i<num;++i) {
|
||||
ProtoParameter *param = getParam(i);
|
||||
nmlist.push_back(param->getName());
|
||||
typelist.push_back( param->getType() );
|
||||
proto.innames.push_back(param->getName());
|
||||
proto.intypes.push_back( param->getType() );
|
||||
}
|
||||
}
|
||||
else
|
||||
isdotdotdot = true;
|
||||
proto.firstVarArgSlot = paramshift;
|
||||
|
||||
// Reassign the storage locations for this new parameter list
|
||||
vector<ParameterPieces> pieces;
|
||||
model->assignParameterStorage(typelist,pieces,false);
|
||||
model->assignParameterStorage(proto,pieces,false);
|
||||
|
||||
delete store;
|
||||
|
||||
|
@ -3474,17 +3535,17 @@ void FuncProto::paramShift(int4 paramshift)
|
|||
store = new ProtoStoreInternal(typefactory->getTypeVoid());
|
||||
|
||||
store->setOutput(pieces[0]);
|
||||
uint4 j=1;
|
||||
uint4 j=0;
|
||||
for(uint4 i=1;i<pieces.size();++i) {
|
||||
if ((pieces[i].flags & ParameterPieces::hiddenretparm) != 0) {
|
||||
store->setInput(i-1,"rethidden",pieces[i]);
|
||||
continue; // increment i but not j
|
||||
}
|
||||
store->setInput(j,nmlist[j],pieces[i]);
|
||||
store->setInput(j,proto.innames[j],pieces[i]);
|
||||
j = j + 1;
|
||||
}
|
||||
setInputLock(true);
|
||||
setDotdotdot(isdotdotdot);
|
||||
setDotdotdot(proto.firstVarArgSlot >= 0);
|
||||
}
|
||||
|
||||
/// \brief If \b this has a \e merged model, pick the most likely model (from the merged set)
|
||||
|
@ -3571,15 +3632,7 @@ void FuncProto::setPieces(const PrototypePieces &pieces)
|
|||
{
|
||||
if (pieces.model != (ProtoModel *)0)
|
||||
setModel(pieces.model);
|
||||
vector<Datatype *> typelist;
|
||||
vector<string> nmlist;
|
||||
typelist.push_back(pieces.outtype);
|
||||
nmlist.push_back("");
|
||||
for(int4 i=0;i<pieces.intypes.size();++i) {
|
||||
typelist.push_back(pieces.intypes[i]);
|
||||
nmlist.push_back(pieces.innames[i]);
|
||||
}
|
||||
updateAllTypes(nmlist,typelist,pieces.dotdotdot);
|
||||
updateAllTypes(pieces);
|
||||
setInputLock(true);
|
||||
setOutputLock(true);
|
||||
setModelLock(true);
|
||||
|
@ -3600,7 +3653,7 @@ void FuncProto::getPieces(PrototypePieces &pieces) const
|
|||
pieces.intypes.push_back(param->getType());
|
||||
pieces.innames.push_back(param->getName());
|
||||
}
|
||||
pieces.dotdotdot = isDotdotdot();
|
||||
pieces.firstVarArgSlot = isDotdotdot() ? num : -1;
|
||||
}
|
||||
|
||||
/// Input parameters are set based on an existing function Scope
|
||||
|
@ -3924,32 +3977,30 @@ void FuncProto::updateOutputNoTypes(const vector<Varnode *> &triallist,TypeFacto
|
|||
/// the first entry corresponds to the output parameter (return value) and the remaining
|
||||
/// entries correspond to input parameters. Storage locations and hidden return parameters are
|
||||
/// calculated, creating a complete function protototype. Existing locks are overridden.
|
||||
/// \param namelist is the list of parameter names
|
||||
/// \param typelist is the list of data-types
|
||||
/// \param dtdtdt is \b true if the new prototype accepts variable argument lists
|
||||
void FuncProto::updateAllTypes(const vector<string> &namelist,const vector<Datatype *> &typelist,
|
||||
bool dtdtdt)
|
||||
/// \param proto is the list of names, data-types, and other attributes
|
||||
void FuncProto::updateAllTypes(const PrototypePieces &proto)
|
||||
|
||||
{
|
||||
setModel(model); // This resets extrapop
|
||||
store->clearAllInputs();
|
||||
store->clearOutput();
|
||||
flags &= ~((uint4)voidinputlock);
|
||||
setDotdotdot(dtdtdt);
|
||||
setDotdotdot(proto.firstVarArgSlot >= 0);
|
||||
|
||||
vector<ParameterPieces> pieces;
|
||||
|
||||
// Calculate what memory locations hold each type
|
||||
try {
|
||||
model->assignParameterStorage(typelist,pieces,false);
|
||||
model->assignParameterStorage(proto,pieces,false);
|
||||
store->setOutput(pieces[0]);
|
||||
uint4 j=1;
|
||||
uint4 j=0;
|
||||
for(uint4 i=1;i<pieces.size();++i) {
|
||||
if ((pieces[i].flags & ParameterPieces::hiddenretparm) != 0) {
|
||||
store->setInput(i-1,"rethidden",pieces[i]);
|
||||
continue; // increment i but not j
|
||||
}
|
||||
store->setInput(i-1,namelist[j],pieces[i]);
|
||||
string nm = (j >= proto.innames.size()) ? "" : proto.innames[j];
|
||||
store->setInput(i-1,nm,pieces[i]);
|
||||
j = j + 1;
|
||||
}
|
||||
}
|
||||
|
@ -4254,11 +4305,13 @@ Address FuncProto::getThisPointerStorage(Datatype *dt)
|
|||
{
|
||||
if (!model->hasThisPointer())
|
||||
return Address();
|
||||
vector<Datatype *> typelist;
|
||||
typelist.push_back(getOutputType());
|
||||
typelist.push_back(dt);
|
||||
PrototypePieces proto;
|
||||
proto.model = model;
|
||||
proto.firstVarArgSlot = -1;
|
||||
proto.outtype = getOutputType();
|
||||
proto.intypes.push_back(dt);
|
||||
vector<ParameterPieces> res;
|
||||
model->assignParameterStorage(typelist, res, true);
|
||||
model->assignParameterStorage(proto, res, true);
|
||||
for(int4 i=1;i<res.size();++i) {
|
||||
if ((res[i].flags & ParameterPieces::hiddenretparm) != 0) continue;
|
||||
return res[i].addr;
|
||||
|
|
|
@ -19,11 +19,12 @@
|
|||
#ifndef __FSPEC_HH__
|
||||
#define __FSPEC_HH__
|
||||
|
||||
#include "op.hh"
|
||||
#include "modelrules.hh"
|
||||
#include "rangemap.hh"
|
||||
|
||||
namespace ghidra {
|
||||
|
||||
class ProtoModel;
|
||||
class JoinRecord;
|
||||
|
||||
extern AttributeId ATTRIB_CUSTOM; ///< Marshaling attribute "custom"
|
||||
|
@ -102,7 +103,7 @@ public:
|
|||
};
|
||||
private:
|
||||
uint4 flags; ///< Boolean properties of the parameter
|
||||
type_metatype type; ///< Data-type class that this entry must match
|
||||
type_class type; ///< Data-type storage class that this entry must match
|
||||
vector<int4> groupSet; ///< Group(s) \b this entry belongs to
|
||||
AddrSpace *spaceid; ///< Address space containing the range
|
||||
uintb addressbase; ///< Starting offset of the range
|
||||
|
@ -126,7 +127,7 @@ public:
|
|||
int4 getMinSize(void) const { return minsize; } ///< Get the minimum size of a logical value contained in \b this
|
||||
int4 getAlign(void) const { return alignment; } ///< Get the alignment of \b this entry
|
||||
JoinRecord *getJoinRecord(void) const { return joinrec; } ///< Get record describing joined pieces (or null if only 1 piece)
|
||||
type_metatype getType(void) const { return type; } ///< Get the data-type class associated with \b this
|
||||
type_class getType(void) const { return type; } ///< Get the data-type class associated with \b this
|
||||
bool isExclusion(void) const { return (alignment==0); } ///< Return \b true if this holds a single parameter exclusively
|
||||
bool isReverseStack(void) const { return ((flags & reverse_stack)!=0); } ///< Return \b true if parameters are allocated in reverse order
|
||||
bool isGrouped(void) const { return ((flags & is_grouped)!=0); } ///< Return \b true if \b this is grouped with other entries
|
||||
|
@ -362,6 +363,17 @@ struct ParameterPieces {
|
|||
Address addr; ///< Storage address of the parameter
|
||||
Datatype *type; ///< The datatype of the parameter
|
||||
uint4 flags; ///< additional attributes of the parameter
|
||||
void swapMarkup(ParameterPieces &op); ///< Swap data-type markup between \b this and another parameter
|
||||
};
|
||||
|
||||
/// \brief Raw components of a function prototype (obtained from parsing source code)
|
||||
struct PrototypePieces {
|
||||
ProtoModel *model; ///< (Optional) model on which prototype is based
|
||||
string name; ///< Identifier (function name) associated with prototype
|
||||
Datatype *outtype; ///< Return data-type
|
||||
vector<Datatype *> intypes; ///< Input data-types
|
||||
vector<string> innames; ///< Identifiers for input types
|
||||
int4 firstVarArgSlot; ///< First position of a variable argument, or -1 if not varargs
|
||||
};
|
||||
|
||||
/// \brief Description of the indirect effect a sub-function has on a memory range
|
||||
|
@ -421,7 +433,7 @@ public:
|
|||
/// \param proto is the ordered list of data-types
|
||||
/// \param typefactory is the TypeFactory (for constructing pointers)
|
||||
/// \param res will contain the storage locations corresponding to the datatypes
|
||||
virtual void assignMap(const vector<Datatype *> &proto,TypeFactory &typefactory,vector<ParameterPieces> &res) const=0;
|
||||
virtual void assignMap(const PrototypePieces &proto,TypeFactory &typefactory,vector<ParameterPieces> &res) const=0;
|
||||
|
||||
/// \brief Given an unordered list of storage locations, calculate a function prototype
|
||||
///
|
||||
|
@ -520,6 +532,14 @@ public:
|
|||
/// \return the stack address space, if \b this models parameters passed on the stack, NULL otherwise
|
||||
virtual AddrSpace *getSpacebase(void) const=0;
|
||||
|
||||
/// \brief Return \b true if \b this pointer occurs before an indirect return pointer
|
||||
///
|
||||
/// The \e automatic parameters: \b this parameter and the \e hidden return value pointer both
|
||||
/// tend to be allocated from the initial general purpose registers reserved for parameter passing.
|
||||
/// This method returns \b true if the \b this parameter is allocated first.
|
||||
/// \return \b false if the \e hidden return value pointer is allocated first
|
||||
virtual bool isThisBeforeRetPointer(void) const=0;
|
||||
|
||||
/// \brief For a given address space, collect all the parameter locations within that space
|
||||
///
|
||||
/// Pass back the memory ranges for any parameter that is stored in the given address space.
|
||||
|
@ -558,19 +578,18 @@ class ParamListStandard : public ParamList {
|
|||
protected:
|
||||
int4 numgroup; ///< Number of \e groups in this parameter convention
|
||||
int4 maxdelay; ///< Maximum heritage delay across all parameters
|
||||
int4 pointermax; ///< If non-zero, maximum size of a data-type before converting to a pointer
|
||||
bool thisbeforeret; ///< Does a \b this parameter come before a hidden return parameter
|
||||
vector<int4> resourceStart; ///< The starting group for each resource section
|
||||
list<ParamEntry> entry; ///< The ordered list of parameter entries
|
||||
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
|
||||
Address assignAddress(const Datatype *tp,vector<int4> &status) const; ///< Assign storage for given parameter data-type
|
||||
const ParamEntry *selectUnreferenceEntry(int4 grp,type_metatype prefType) const; ///< Select entry to fill an unreferenced param
|
||||
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;
|
||||
static void markGroupNoUse(ParamActive *active,int4 activeTrial,int4 trialStart);
|
||||
static void markBestInactive(ParamActive *active,int4 group,int4 groupStart,type_metatype prefType);
|
||||
static void markBestInactive(ParamActive *active,int4 group,int4 groupStart,type_class prefType);
|
||||
static void forceExclusionGroup(ParamActive *active);
|
||||
static void forceNoUse(ParamActive *active,int4 start,int4 stop);
|
||||
static void forceInactiveChain(ParamActive *active,int4 maxchain,int4 start,int4 stop,int4 groupstart);
|
||||
|
@ -586,8 +605,12 @@ public:
|
|||
ParamListStandard(const ParamListStandard &op2); ///< Copy constructor
|
||||
virtual ~ParamListStandard(void);
|
||||
const list<ParamEntry> &getEntry(void) const { return entry; } ///< Get the list of parameter entries
|
||||
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,
|
||||
vector<int4> &status,ParameterPieces &res) const;
|
||||
virtual uint4 getType(void) const { return p_standard; }
|
||||
virtual void assignMap(const vector<Datatype *> &proto,TypeFactory &typefactory,vector<ParameterPieces> &res) const;
|
||||
virtual void assignMap(const PrototypePieces &proto,TypeFactory &typefactory,vector<ParameterPieces> &res) const;
|
||||
virtual void fillinMap(ParamActive *active) const;
|
||||
virtual bool checkJoin(const Address &hiaddr,int4 hisize,const Address &loaddr,int4 losize) const;
|
||||
virtual bool checkSplit(const Address &loc,int4 size,int4 splitpoint) const;
|
||||
|
@ -598,12 +621,32 @@ public:
|
|||
virtual bool unjustifiedContainer(const Address &loc,int4 size,VarnodeData &res) const;
|
||||
virtual OpCode assumedExtension(const Address &addr,int4 size,VarnodeData &res) const;
|
||||
virtual AddrSpace *getSpacebase(void) const { return spacebase; }
|
||||
virtual bool isThisBeforeRetPointer(void) const { return thisbeforeret; }
|
||||
virtual void getRangeList(AddrSpace *spc,RangeList &res) const;
|
||||
virtual int4 getMaxDelay(void) const { return maxdelay; }
|
||||
virtual void decode(Decoder &decoder,vector<EffectRecord> &effectlist,bool normalstack);
|
||||
virtual ParamList *clone(void) const;
|
||||
};
|
||||
|
||||
/// \brief A standard model for returning output parameters from a function
|
||||
///
|
||||
/// This has a more involved assignment strategy than its parent class.
|
||||
/// Entries in the resource list are treated as a \e group, meaning that only one can
|
||||
/// fit the desired storage size and type attributes of the return value. If no entry
|
||||
/// fits, the return value is converted to a pointer data-type, storage allocation is
|
||||
/// attempted again, and the return value is marked as a \e hidden return parameter
|
||||
/// to inform the input model.
|
||||
class ParamListStandardOut : public ParamListStandard {
|
||||
public:
|
||||
ParamListStandardOut(void) : ParamListStandard() {} ///< Constructor for use with decode()
|
||||
ParamListStandardOut(const ParamListStandardOut &op2) : ParamListStandard(op2) {} ///< Copy constructor
|
||||
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 ParamList *clone(void) const;
|
||||
};
|
||||
|
||||
/// \brief A model for passing back return values from a function
|
||||
///
|
||||
/// This is a resource list of potential storage locations for a return value,
|
||||
|
@ -611,14 +654,12 @@ public:
|
|||
/// for selecting a storage location. When assigning based on data-type (assignMap), the first list
|
||||
/// entry that fits is chosen. When assigning from a set of actively used locations (fillinMap),
|
||||
/// this class chooses the location that is the closest fitting match to an entry in the resource list.
|
||||
class ParamListRegisterOut : public ParamListStandard {
|
||||
class ParamListRegisterOut : public ParamListStandardOut {
|
||||
public:
|
||||
ParamListRegisterOut(void) : ParamListStandard() {} ///< Constructor
|
||||
ParamListRegisterOut(const ParamListRegisterOut &op2) : ParamListStandard(op2) {} ///< Copy constructor
|
||||
ParamListRegisterOut(void) : ParamListStandardOut() {} ///< Constructor
|
||||
ParamListRegisterOut(const ParamListRegisterOut &op2) : ParamListStandardOut(op2) {} ///< Copy constructor
|
||||
virtual uint4 getType(void) const { return p_register_out; }
|
||||
virtual void assignMap(const vector<Datatype *> &proto,TypeFactory &typefactory,vector<ParameterPieces> &res) const;
|
||||
virtual void fillinMap(ParamActive *active) const;
|
||||
virtual bool possibleParam(const Address &loc,int4 size) const;
|
||||
virtual void assignMap(const PrototypePieces &proto,TypeFactory &typefactory,vector<ParameterPieces> &res) const;
|
||||
virtual ParamList *clone(void) const;
|
||||
};
|
||||
|
||||
|
@ -638,24 +679,6 @@ public:
|
|||
virtual ParamList *clone(void) const;
|
||||
};
|
||||
|
||||
/// \brief A standard model for returning output parameters from a function
|
||||
///
|
||||
/// This has a more involved assignment strategy than its parent class.
|
||||
/// Entries in the resource list are treated as a \e group, meaning that only one can
|
||||
/// fit the desired storage size and type attributes of the return value. If no entry
|
||||
/// fits, the return value is converted to a pointer data-type, storage allocation is
|
||||
/// attempted again, and the return value is marked as a \e hidden return parameter
|
||||
/// to inform the input model.
|
||||
class ParamListStandardOut : public ParamListRegisterOut {
|
||||
public:
|
||||
ParamListStandardOut(void) : ParamListRegisterOut() {} ///< Constructor for use with decode()
|
||||
ParamListStandardOut(const ParamListStandardOut &op2) : ParamListRegisterOut(op2) {} ///< Copy constructor
|
||||
virtual uint4 getType(void) const { return p_standard_out; }
|
||||
virtual void assignMap(const vector<Datatype *> &proto,TypeFactory &typefactory,vector<ParameterPieces> &res) const;
|
||||
virtual void decode(Decoder &decoder,vector<EffectRecord> &effectlist,bool normalstack);
|
||||
virtual ParamList *clone(void) const;
|
||||
};
|
||||
|
||||
/// \brief A union of other input parameter passing models
|
||||
///
|
||||
/// This model is viewed as a union of a constituent set of resource lists.
|
||||
|
@ -671,7 +694,7 @@ public:
|
|||
void foldIn(const ParamListStandard &op2); ///< Add another model to the union
|
||||
void finalize(void) { populateResolver(); } ///< Fold-ins are finished, finalize \b this
|
||||
virtual uint4 getType(void) const { return p_merged; }
|
||||
virtual void assignMap(const vector<Datatype *> &proto,TypeFactory &typefactory,vector<ParameterPieces> &res) const {
|
||||
virtual void assignMap(const PrototypePieces &proto,TypeFactory &typefactory,vector<ParameterPieces> &res) const {
|
||||
throw LowlevelError("Cannot assign prototype before model has been resolved"); }
|
||||
virtual void fillinMap(ParamActive *active) const {
|
||||
throw LowlevelError("Cannot determine prototype before model has been resolved"); }
|
||||
|
@ -752,7 +775,7 @@ public:
|
|||
void deriveOutputMap(ParamActive *active) const {
|
||||
output->fillinMap(active); }
|
||||
|
||||
void assignParameterStorage(const vector<Datatype *> &typelist,vector<ParameterPieces> &res,bool ignoreOutputError);
|
||||
void assignParameterStorage(const PrototypePieces &proto,vector<ParameterPieces> &res,bool ignoreOutputError);
|
||||
|
||||
/// \brief Check if the given two input storage locations can represent a single logical parameter
|
||||
///
|
||||
|
@ -1276,16 +1299,6 @@ public:
|
|||
virtual void decode(Decoder &decoder,ProtoModel *model);
|
||||
};
|
||||
|
||||
/// \brief Raw components of a function prototype (obtained from parsing source code)
|
||||
struct PrototypePieces {
|
||||
ProtoModel *model; ///< (Optional) model on which prototype is based
|
||||
string name; ///< Identifier (function name) associated with prototype
|
||||
Datatype *outtype; ///< Return data-type
|
||||
vector<Datatype *> intypes; ///< Input data-types
|
||||
vector<string> innames; ///< Identifiers for input types
|
||||
bool dotdotdot; ///< True if prototype takes variable arguments
|
||||
};
|
||||
|
||||
/// \brief A \b function \b prototype
|
||||
///
|
||||
/// A description of the parameters and return value for a specific function.
|
||||
|
@ -1484,7 +1497,7 @@ public:
|
|||
void updateInputNoTypes(Funcdata &data,const vector<Varnode *> &triallist,ParamActive *activeinput);
|
||||
void updateOutputTypes(const vector<Varnode *> &triallist);
|
||||
void updateOutputNoTypes(const vector<Varnode *> &triallist,TypeFactory *factory);
|
||||
void updateAllTypes(const vector<string> &namelist,const vector<Datatype *> &typelist,bool dtdtdt);
|
||||
void updateAllTypes(const PrototypePieces &proto);
|
||||
ProtoParameter *getParam(int4 i) const { return store->getInput(i); } ///< Get the i-th input parameter
|
||||
void setParam(int4 i,const string &name,const ParameterPieces &piece) { store->setInput(i, name, piece); } ///< Set parameter storage directly
|
||||
void removeParam(int4 i) { store->clearInput(i); } ///< Remove the i-th input parameter
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -719,18 +719,22 @@ bool FunctionModifier::isValid(void) const
|
|||
Datatype *FunctionModifier::modType(Datatype *base,const TypeDeclarator *decl,Architecture *glb) const
|
||||
|
||||
{
|
||||
vector<Datatype *> intypes;
|
||||
PrototypePieces proto;
|
||||
|
||||
if (base == (Datatype *)0)
|
||||
proto.outtype = glb->types->getTypeVoid();
|
||||
else
|
||||
proto.outtype = base;
|
||||
// Varargs is encoded as extra null pointer in paramlist
|
||||
bool dotdotdot = false;
|
||||
proto.firstVarArgSlot = -1;
|
||||
if ((!paramlist.empty())&&(paramlist.back() == (TypeDeclarator *)0)) {
|
||||
dotdotdot = true;
|
||||
proto.firstVarArgSlot = paramlist.size() - 1;
|
||||
}
|
||||
|
||||
getInTypes(intypes,glb);
|
||||
getInTypes(proto.intypes,glb);
|
||||
|
||||
ProtoModel *protomodel = decl->getModel(glb);
|
||||
return glb->types->getTypeCode(protomodel,base,intypes,dotdotdot);
|
||||
proto.model = decl->getModel(glb);
|
||||
return glb->types->getTypeCode(proto);
|
||||
}
|
||||
|
||||
TypeDeclarator::~TypeDeclarator(void)
|
||||
|
@ -781,7 +785,7 @@ bool TypeDeclarator::getPrototype(PrototypePieces &pieces,Architecture *glb) con
|
|||
fmod->getInTypes(pieces.intypes,glb);
|
||||
pieces.innames.clear();
|
||||
fmod->getInNames(pieces.innames);
|
||||
pieces.dotdotdot = fmod->isDotdotdot();
|
||||
pieces.firstVarArgSlot = fmod->isDotdotdot() ? pieces.intypes.size() : -1;
|
||||
|
||||
// Construct the output type
|
||||
pieces.outtype = basetype;
|
||||
|
|
|
@ -1146,8 +1146,9 @@ AttributeId ATTRIB_TYPELOCK = AttributeId("typelock",23);
|
|||
AttributeId ATTRIB_VAL = AttributeId("val",24);
|
||||
AttributeId ATTRIB_VALUE = AttributeId("value",25);
|
||||
AttributeId ATTRIB_WORDSIZE = AttributeId("wordsize",26);
|
||||
AttributeId ATTRIB_STORAGE = AttributeId("storage",149);
|
||||
|
||||
AttributeId ATTRIB_UNKNOWN = AttributeId("XMLunknown",149); // Number serves as next open index
|
||||
AttributeId ATTRIB_UNKNOWN = AttributeId("XMLunknown",150); // Number serves as next open index
|
||||
|
||||
ElementId ELEM_DATA = ElementId("data",1);
|
||||
ElementId ELEM_INPUT = ElementId("input",2);
|
||||
|
@ -1160,6 +1161,6 @@ ElementId ELEM_VAL = ElementId("val",8);
|
|||
ElementId ELEM_VALUE = ElementId("value",9);
|
||||
ElementId ELEM_VOID = ElementId("void",10);
|
||||
|
||||
ElementId ELEM_UNKNOWN = ElementId("XMLunknown",273); // Number serves as next open index
|
||||
ElementId ELEM_UNKNOWN = ElementId("XMLunknown",284); // Number serves as next open index
|
||||
|
||||
} // End namespace ghidra
|
||||
|
|
|
@ -675,6 +675,7 @@ extern AttributeId ATTRIB_TYPELOCK; ///< Marshaling attribute "typelock"
|
|||
extern AttributeId ATTRIB_VAL; ///< Marshaling attribute "val"
|
||||
extern AttributeId ATTRIB_VALUE; ///< Marshaling attribute "value"
|
||||
extern AttributeId ATTRIB_WORDSIZE; ///< Marshaling attribute "wordsize"
|
||||
extern AttributeId ATTRIB_STORAGE; ///< Marshaling attribute "storage"
|
||||
|
||||
extern ElementId ELEM_DATA; ///< Marshaling element \<data>
|
||||
extern ElementId ELEM_INPUT; ///< Marshaling element \<input>
|
||||
|
|
918
Ghidra/Features/Decompiler/src/decompile/cpp/modelrules.cc
Normal file
918
Ghidra/Features/Decompiler/src/decompile/cpp/modelrules.cc
Normal file
|
@ -0,0 +1,918 @@
|
|||
/* ###
|
||||
* 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.
|
||||
*/
|
||||
#include "modelrules.hh"
|
||||
#include "funcdata.hh"
|
||||
|
||||
namespace ghidra {
|
||||
|
||||
ElementId ELEM_DATATYPE = ElementId("datatype",273);
|
||||
ElementId ELEM_CONSUME = ElementId("consume",274);
|
||||
ElementId ELEM_CONSUME_EXTRA = ElementId("consume_extra",275);
|
||||
ElementId ELEM_CONVERT_TO_PTR = ElementId("convert_to_ptr",276);
|
||||
ElementId ELEM_GOTO_STACK = ElementId("goto_stack",277);
|
||||
ElementId ELEM_JOIN = ElementId("join",278);
|
||||
ElementId ELEM_DATATYPE_AT = ElementId("datatype_at",279);
|
||||
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);
|
||||
|
||||
/// \brief Extract an ordered list of primitive data-types making up the given data-type
|
||||
///
|
||||
/// 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 filler data-type is provided, it is used to fill \e holes in structures. If
|
||||
/// a maximum number of extracted primitives is exceeded, or if no filler is provided and a hole
|
||||
/// is encountered, or if a non-primitive non-composite data-type is encountered, \b 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 filler is the data-type to use as filler (or null)
|
||||
/// \param res will hold the list of primitives
|
||||
/// \return \b true if all primitives were extracted
|
||||
bool DatatypeFilter::extractPrimitives(Datatype *dt,int4 max,Datatype *filler,vector<Datatype *> &res)
|
||||
|
||||
{
|
||||
switch(dt->getMetatype()) {
|
||||
case TYPE_UNKNOWN:
|
||||
case TYPE_INT:
|
||||
case TYPE_UINT:
|
||||
case TYPE_BOOL:
|
||||
case TYPE_CODE:
|
||||
case TYPE_FLOAT:
|
||||
case TYPE_PTR:
|
||||
case TYPE_PTRREL:
|
||||
if (res.size() >= max)
|
||||
return false;
|
||||
res.push_back(dt);
|
||||
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,filler,res))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case TYPE_STRUCT:
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
TypeStruct *structPtr = (TypeStruct *)dt;
|
||||
int4 curOff = 0;
|
||||
vector<TypeField>::const_iterator enditer = structPtr->endField();
|
||||
for(vector<TypeField>::const_iterator iter=structPtr->beginField();iter!=enditer;++iter) {
|
||||
int4 nextOff = (*iter).offset;
|
||||
if (nextOff > curOff) {
|
||||
if (filler == (Datatype *)0)
|
||||
return false;
|
||||
while(curOff < nextOff) {
|
||||
if (res.size() >= max)
|
||||
return false;
|
||||
res.push_back(filler);
|
||||
curOff += filler->getSize();
|
||||
}
|
||||
}
|
||||
if (!extractPrimitives((*iter).type,max,filler,res))
|
||||
return false;
|
||||
curOff += (*iter).type->getSize();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// \param decoder is the given stream decoder
|
||||
/// \return the new data-type filter instance
|
||||
DatatypeFilter *DatatypeFilter::decodeFilter(Decoder &decoder)
|
||||
|
||||
{
|
||||
DatatypeFilter *filter;
|
||||
uint4 elemId = decoder.openElement(ELEM_DATATYPE);
|
||||
string nm = decoder.readString(ATTRIB_NAME);
|
||||
if (nm == "any") {
|
||||
filter = new SizeRestrictedFilter();
|
||||
}
|
||||
else if (nm == "homogeneous-float-aggregate") {
|
||||
filter = new HomogeneousAggregate(TYPE_FLOAT,4,0,0);
|
||||
}
|
||||
else {
|
||||
// If no other name matches, assume this is a metatype
|
||||
type_metatype meta = string2metatype(nm);
|
||||
filter = new MetaTypeFilter(meta);
|
||||
}
|
||||
filter->decode(decoder);
|
||||
decoder.closeElement(elemId);
|
||||
return filter;
|
||||
}
|
||||
|
||||
SizeRestrictedFilter::SizeRestrictedFilter(int4 min,int4 max)
|
||||
|
||||
{
|
||||
minSize = min;
|
||||
maxSize = max;
|
||||
if (maxSize == 0 && minSize >= 0) {
|
||||
// If no ATTRIB_MAXSIZE is given, assume there is no upper bound on size
|
||||
maxSize = 0x7fffffff;
|
||||
}
|
||||
}
|
||||
|
||||
/// If \b maxSize is not zero, the data-type is checked to see if its size in bytes
|
||||
/// falls between \b minSize and \b maxSize inclusive.
|
||||
/// \param dt is the data-type to test
|
||||
/// \return \b true if the data-type meets the size restrictions
|
||||
bool SizeRestrictedFilter::filterOnSize(Datatype *dt) const
|
||||
|
||||
{
|
||||
if (maxSize == 0) return true; // maxSize of 0 means no size filtering is performed
|
||||
return (dt->getSize() >= minSize && dt->getSize() <= maxSize);
|
||||
}
|
||||
|
||||
void SizeRestrictedFilter::decode(Decoder &decoder)
|
||||
|
||||
{
|
||||
for(;;) {
|
||||
uint4 attribId = decoder.getNextAttributeId();
|
||||
if (attribId == 0) break;
|
||||
if (attribId == ATTRIB_MINSIZE)
|
||||
minSize = decoder.readUnsignedInteger();
|
||||
else if (attribId == ATTRIB_MAXSIZE)
|
||||
maxSize = decoder.readUnsignedInteger();
|
||||
}
|
||||
if (maxSize == 0 && minSize >= 0) {
|
||||
// If no ATTRIB_MAXSIZE is given, assume there is no upper bound on size
|
||||
maxSize = 0x7fffffff;
|
||||
}
|
||||
}
|
||||
|
||||
MetaTypeFilter::MetaTypeFilter(type_metatype meta)
|
||||
|
||||
{
|
||||
metaType = meta;
|
||||
}
|
||||
|
||||
MetaTypeFilter::MetaTypeFilter(type_metatype meta,int4 min,int4 max)
|
||||
: SizeRestrictedFilter(min,max)
|
||||
{
|
||||
metaType = meta;
|
||||
}
|
||||
|
||||
bool MetaTypeFilter::filter(Datatype *dt) const
|
||||
|
||||
{
|
||||
if (dt->getMetatype() != metaType) return false;
|
||||
return filterOnSize(dt);
|
||||
}
|
||||
|
||||
HomogeneousAggregate::HomogeneousAggregate(type_metatype meta)
|
||||
|
||||
{
|
||||
metaType = meta;
|
||||
maxPrimitives = 2;
|
||||
}
|
||||
|
||||
HomogeneousAggregate::HomogeneousAggregate(type_metatype meta,int4 maxPrim,int4 min,int4 max)
|
||||
: SizeRestrictedFilter(min,max)
|
||||
{
|
||||
metaType = meta;
|
||||
maxPrimitives = maxPrim;
|
||||
}
|
||||
|
||||
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, (Datatype *)0, res))
|
||||
return false;
|
||||
Datatype *base = res[0];
|
||||
if (base->getMetatype() != metaType)
|
||||
return false;
|
||||
for(int4 i=1;i<res.size();++i) {
|
||||
if (res[i] != base)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// If the next element is a qualifier filter, decode it from the stream and return it.
|
||||
/// Otherwise return null
|
||||
/// \param decoder is the given stream decoder
|
||||
/// \return the new qualifier instance or null
|
||||
QualifierFilter *QualifierFilter::decodeFilter(Decoder &decoder)
|
||||
|
||||
{
|
||||
QualifierFilter *filter;
|
||||
uint4 elemId = decoder.peekElement();
|
||||
if (elemId == ELEM_VARARGS)
|
||||
filter = new VarargsFilter();
|
||||
else if (elemId == ELEM_POSITION)
|
||||
filter = new PositionMatchFilter(-1);
|
||||
else if (elemId == ELEM_DATATYPE_AT)
|
||||
filter = new DatatypeMatchFilter();
|
||||
else
|
||||
return (QualifierFilter *)0;
|
||||
filter->decode(decoder);
|
||||
return filter;
|
||||
}
|
||||
|
||||
/// The AndFilter assumes ownership of all the filters in the array and the original vector is cleared
|
||||
/// \param filters is the list of filters pulled into \b this filter
|
||||
AndFilter::AndFilter(vector<QualifierFilter *> filters)
|
||||
|
||||
{
|
||||
subQualifiers.swap(filters);
|
||||
}
|
||||
|
||||
AndFilter::~AndFilter(void)
|
||||
|
||||
{
|
||||
for(int4 i=0;i<subQualifiers.size();++i)
|
||||
delete subQualifiers[i];
|
||||
}
|
||||
|
||||
QualifierFilter *AndFilter::clone(void) const
|
||||
|
||||
{
|
||||
vector<QualifierFilter *> newFilters;
|
||||
for(int4 i=0;i<subQualifiers.size();++i)
|
||||
newFilters.push_back(subQualifiers[i]->clone());
|
||||
return new AndFilter(newFilters);
|
||||
}
|
||||
|
||||
bool AndFilter::filter(const PrototypePieces &proto,int4 pos) const
|
||||
|
||||
{
|
||||
for(int4 i=0;i<subQualifiers.size();++i) {
|
||||
if (!subQualifiers[i]->filter(proto,pos))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VarargsFilter::filter(const PrototypePieces &proto,int4 pos) const
|
||||
|
||||
{
|
||||
if (proto.firstVarArgSlot < 0) return false;
|
||||
return (pos >= proto.firstVarArgSlot);
|
||||
}
|
||||
|
||||
void VarargsFilter::decode(Decoder &decoder)
|
||||
|
||||
{
|
||||
uint4 elemId = decoder.openElement(ELEM_VARARGS);
|
||||
decoder.closeElement(elemId);
|
||||
}
|
||||
|
||||
bool PositionMatchFilter::filter(const PrototypePieces &proto,int4 pos) const
|
||||
|
||||
{
|
||||
return (pos == position);
|
||||
}
|
||||
|
||||
void PositionMatchFilter::decode(Decoder &decoder)
|
||||
|
||||
{
|
||||
uint4 elemId = decoder.openElement(ELEM_POSITION);
|
||||
position = decoder.readSignedInteger(ATTRIB_INDEX);
|
||||
decoder.closeElement(elemId);
|
||||
}
|
||||
|
||||
DatatypeMatchFilter::~DatatypeMatchFilter(void)
|
||||
|
||||
{
|
||||
if (typeFilter != (DatatypeFilter *)0)
|
||||
delete typeFilter;
|
||||
}
|
||||
|
||||
QualifierFilter *DatatypeMatchFilter::clone(void) const
|
||||
|
||||
{
|
||||
DatatypeMatchFilter *res = new DatatypeMatchFilter();
|
||||
res->position = position;
|
||||
res->typeFilter = typeFilter->clone();
|
||||
return res;
|
||||
}
|
||||
|
||||
bool DatatypeMatchFilter::filter(const PrototypePieces &proto,int4 pos) const
|
||||
|
||||
{
|
||||
// The position of the current parameter being assigned, pos, is NOT used
|
||||
Datatype *dt;
|
||||
if (position < 0)
|
||||
dt = proto.outtype;
|
||||
else {
|
||||
if (position >= proto.intypes.size())
|
||||
return false;
|
||||
dt = proto.intypes[position];
|
||||
}
|
||||
return typeFilter->filter(dt);
|
||||
}
|
||||
|
||||
void DatatypeMatchFilter::decode(Decoder &decoder)
|
||||
|
||||
{
|
||||
uint4 elemId = decoder.openElement(ELEM_DATATYPE_AT);
|
||||
position = decoder.readSignedInteger(ATTRIB_INDEX);
|
||||
typeFilter = DatatypeFilter::decodeFilter(decoder);
|
||||
decoder.closeElement(elemId);
|
||||
}
|
||||
|
||||
/// \brief Read the next model rule action element from the stream
|
||||
///
|
||||
/// Allocate the action object corresponding to the element and configure it.
|
||||
/// If the next element is not an action, throw an exception.
|
||||
/// \param parser is the stream decoder
|
||||
/// \param res is the resource set for the new action
|
||||
/// \return the new action
|
||||
AssignAction *AssignAction::decodeAction(Decoder &decoder,const ParamListStandard *res)
|
||||
|
||||
{
|
||||
AssignAction *action;
|
||||
uint4 elemId = decoder.peekElement();
|
||||
if (elemId == ELEM_GOTO_STACK)
|
||||
action = new GotoStack(res,0);
|
||||
else if (elemId == ELEM_JOIN) {
|
||||
action = new MultiSlotAssign(res);
|
||||
}
|
||||
else if (elemId == ELEM_CONSUME) {
|
||||
action = new ConsumeAs(TYPECLASS_GENERAL,res);
|
||||
}
|
||||
else if (elemId == ELEM_CONVERT_TO_PTR) {
|
||||
action = new ConvertToPointer(res);
|
||||
}
|
||||
else if (elemId == ELEM_HIDDEN_RETURN) {
|
||||
action = new HiddenReturnAssign(res,false);
|
||||
}
|
||||
else if (elemId == ELEM_JOIN_PER_PRIMITIVE) {
|
||||
bool consumeMostSig = false;
|
||||
AddrSpace *spc = res->getSpacebase();
|
||||
if (spc != (AddrSpace *)0 && spc->isBigEndian()) {
|
||||
consumeMostSig = true;
|
||||
}
|
||||
action = new MultiMemberAssign(TYPECLASS_GENERAL,false,consumeMostSig,res);
|
||||
}
|
||||
else
|
||||
throw DecoderError("Expecting model rule action");
|
||||
action->decode(decoder);
|
||||
return action;
|
||||
}
|
||||
|
||||
/// \brief Read the next model rule sideeffect element from the stream
|
||||
///
|
||||
/// Allocate the sideeffect object corresponding to the element and configure it.
|
||||
/// If the next element is not a sideeffect, throw an exception.
|
||||
/// \param parser is the stream decoder
|
||||
/// \param res is the resource set for the new sideeffect
|
||||
/// \return the new sideeffect
|
||||
AssignAction *AssignAction::decodeSideeffect(Decoder &decoder,const ParamListStandard *res)
|
||||
|
||||
{
|
||||
AssignAction *action;
|
||||
uint4 elemId = decoder.peekElement();
|
||||
|
||||
if (elemId == ELEM_CONSUME_EXTRA) {
|
||||
action = new ConsumeExtra(res);
|
||||
}
|
||||
else
|
||||
throw DecoderError("Expecting model rule sideeffect");
|
||||
action->decode(decoder);
|
||||
return action;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
if (stackEntry == (const ParamEntry *)0)
|
||||
throw LowlevelError("Cannot find matching <pentry> for action: gotostack");
|
||||
}
|
||||
|
||||
/// \param res is the new resource set to associate with \b this action
|
||||
/// \param val is a dummy value
|
||||
GotoStack::GotoStack(const ParamListStandard *res,int4 val)
|
||||
: AssignAction(res)
|
||||
{
|
||||
stackEntry = (const ParamEntry *)0;
|
||||
}
|
||||
|
||||
GotoStack::GotoStack(const ParamListStandard *res)
|
||||
: AssignAction(res)
|
||||
{
|
||||
stackEntry = (const ParamEntry *)0;
|
||||
initializeEntry();
|
||||
}
|
||||
|
||||
uint4 GotoStack::assignAddress(Datatype *dt,const PrototypePieces &proto,int4 pos,TypeFactory &tlst,
|
||||
vector<int4> &status,ParameterPieces &res) const
|
||||
{
|
||||
int4 grp = stackEntry->getGroup();
|
||||
res.type = dt;
|
||||
res.addr = stackEntry->getAddrBySlot(status[grp],dt->getSize(),dt->getAlignment());
|
||||
res.flags = 0;
|
||||
return success;
|
||||
}
|
||||
|
||||
void GotoStack::decode(Decoder &decoder)
|
||||
|
||||
{
|
||||
uint4 elemId = decoder.openElement(ELEM_GOTO_STACK);
|
||||
decoder.closeElement(elemId);
|
||||
initializeEntry();
|
||||
}
|
||||
|
||||
ConvertToPointer::ConvertToPointer(const ParamListStandard *res)
|
||||
: AssignAction(res)
|
||||
{
|
||||
space = res->getSpacebase();
|
||||
}
|
||||
|
||||
uint4 ConvertToPointer::assignAddress(Datatype *dt,const PrototypePieces &proto,int4 pos,TypeFactory &tlist,
|
||||
vector<int4> &status,ParameterPieces &res) const
|
||||
{
|
||||
AddrSpace *spc = space;
|
||||
if (spc == (AddrSpace*)0)
|
||||
spc = tlist.getArch()->getDefaultDataSpace();
|
||||
int4 pointersize = spc->getAddrSize();
|
||||
int4 wordsize = spc->getWordSize();
|
||||
// Convert the data-type to a pointer
|
||||
Datatype *pointertp = tlist.getTypePointer(pointersize,dt,wordsize);
|
||||
// (Recursively) assign storage
|
||||
uint4 responseCode = resource->assignAddress(pointertp, proto, pos, tlist, status, res);
|
||||
res.flags = ParameterPieces::indirectstorage;
|
||||
return responseCode;
|
||||
}
|
||||
|
||||
void ConvertToPointer::decode(Decoder &decoder)
|
||||
|
||||
{
|
||||
uint4 elemId = decoder.openElement(ELEM_CONVERT_TO_PTR);
|
||||
decoder.closeElement(elemId);
|
||||
}
|
||||
|
||||
/// Find the first ParamEntry matching the \b resourceType, and the ParamEntry
|
||||
/// corresponding to the \e stack if \b consumeFromStack is set.
|
||||
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())
|
||||
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");
|
||||
}
|
||||
|
||||
/// Set default configuration
|
||||
/// \param res is the new resource set to associate with \b this action
|
||||
MultiSlotAssign::MultiSlotAssign(const ParamListStandard *res)
|
||||
: AssignAction(res)
|
||||
{
|
||||
resourceType = TYPECLASS_GENERAL; // Join general purpose registers
|
||||
uint4 listType = res->getType();
|
||||
// Consume from stack on input parameters by default
|
||||
consumeFromStack = (listType != ParamList::p_register_out && listType != ParamList::p_standard_out);
|
||||
consumeMostSig = false;
|
||||
enforceAlignment = false;
|
||||
justifyRight = false;
|
||||
AddrSpace *spc = res->getSpacebase();
|
||||
if (spc != (AddrSpace *)0 && spc->isBigEndian()) {
|
||||
consumeMostSig = true;
|
||||
justifyRight = true;
|
||||
}
|
||||
stackEntry = (const ParamEntry *)0;
|
||||
}
|
||||
|
||||
MultiSlotAssign::MultiSlotAssign(type_class store,bool stack,bool mostSig,bool align,bool justRight,const ParamListStandard *res)
|
||||
: AssignAction(res)
|
||||
{
|
||||
resourceType = store;
|
||||
consumeFromStack = stack;
|
||||
consumeMostSig = mostSig;
|
||||
enforceAlignment = align;
|
||||
justifyRight = justRight;
|
||||
stackEntry = (const ParamEntry *)0;
|
||||
initializeEntries();
|
||||
}
|
||||
|
||||
uint4 MultiSlotAssign::assignAddress(Datatype *dt,const PrototypePieces &proto,int4 pos,TypeFactory &tlist,
|
||||
vector<int4> &status,ParameterPieces &res) const
|
||||
{
|
||||
vector<int4> tmpStatus = status;
|
||||
vector<VarnodeData> pieces;
|
||||
int4 sizeLeft = dt->getSize();
|
||||
list<ParamEntry>::const_iterator iter = firstIter;
|
||||
list<ParamEntry>::const_iterator endIter = resource->getEntry().end();
|
||||
if (enforceAlignment) {
|
||||
int4 resourcesConsumed = 0;
|
||||
while(iter != endIter) {
|
||||
const ParamEntry &entry( *iter );
|
||||
if (!entry.isExclusion())
|
||||
break; // Reached end of resource list
|
||||
if (entry.getType() == resourceType && entry.getAllGroups().size() == 1) { // Single register
|
||||
if (tmpStatus[entry.getGroup()] == 0) { // Not consumed
|
||||
int4 align = dt->getAlignment();
|
||||
int4 regSize = entry.getSize();
|
||||
if (align <= regSize || (resourcesConsumed % align) == 0)
|
||||
break;
|
||||
tmpStatus[entry.getGroup()] = -1; // Consume unaligned register
|
||||
}
|
||||
resourcesConsumed += entry.getSize();
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
while(sizeLeft > 0 && iter != endIter) {
|
||||
const ParamEntry &entry( *iter );
|
||||
++iter;
|
||||
if (!entry.isExclusion())
|
||||
break; // Reached end of resource list
|
||||
if (entry.getType() != resourceType || entry.getAllGroups().size() != 1)
|
||||
continue; // Not a single register from desired resource list
|
||||
if (tmpStatus[entry.getGroup()] != 0)
|
||||
continue; // Already consumed
|
||||
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 to use stack to get enough bytes
|
||||
if (!consumeFromStack)
|
||||
return fail;
|
||||
int4 grp = stackEntry->getGroup();
|
||||
Address addr = stackEntry->getAddrBySlot(tmpStatus[grp],sizeLeft,1); // Consume all the space we need
|
||||
pieces.push_back(VarnodeData());
|
||||
pieces.back().space = addr.getSpace();
|
||||
pieces.back().offset = addr.getOffset();
|
||||
pieces.back().size = sizeLeft;
|
||||
}
|
||||
else if (sizeLeft < 0) { // Have odd data-type size
|
||||
if (resourceType == TYPECLASS_FLOAT && pieces.size() == 1) {
|
||||
AddrSpaceManager *manager = tlist.getArch();
|
||||
VarnodeData &tmp( pieces.front() );
|
||||
Address addr = manager->constructFloatExtensionAddress(tmp.getAddr(),tmp.size,dt->getSize());
|
||||
tmp.space = addr.getSpace();
|
||||
tmp.offset = addr.getOffset();
|
||||
tmp.size = dt->getSize();
|
||||
}
|
||||
else 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;
|
||||
}
|
||||
|
||||
void MultiSlotAssign::decode(Decoder &decoder)
|
||||
|
||||
{
|
||||
uint4 elemId = decoder.openElement(ELEM_JOIN);
|
||||
for(;;) {
|
||||
uint4 attribId = decoder.getNextAttributeId();
|
||||
if (attribId == 0) break;
|
||||
if (attribId == ATTRIB_REVERSEJUSTIFY) {
|
||||
if (decoder.readBool())
|
||||
justifyRight = !justifyRight;
|
||||
}
|
||||
else if (attribId == ATTRIB_STORAGE) {
|
||||
resourceType = string2typeclass(decoder.readString());
|
||||
}
|
||||
else if (attribId == ATTRIB_ALIGN) {
|
||||
enforceAlignment = decoder.readBool();
|
||||
}
|
||||
}
|
||||
decoder.closeElement(elemId);
|
||||
initializeEntries(); // Need new firstIter
|
||||
}
|
||||
|
||||
MultiMemberAssign::MultiMemberAssign(type_class store,bool stack,bool mostSig,const ParamListStandard *res)
|
||||
: AssignAction(res)
|
||||
{
|
||||
resourceType = store;
|
||||
consumeFromStack = stack;
|
||||
consumeMostSig = mostSig;
|
||||
}
|
||||
|
||||
uint4 MultiMemberAssign::assignAddress(Datatype *dt,const PrototypePieces &proto,int4 pos,TypeFactory &tlist,
|
||||
vector<int4> &status,ParameterPieces &res) const
|
||||
{
|
||||
vector<int4> tmpStatus = status;
|
||||
vector<VarnodeData> pieces;
|
||||
vector<Datatype *> primitives;
|
||||
if (!DatatypeFilter::extractPrimitives(dt,16,(Datatype *)0,primitives))
|
||||
return fail;
|
||||
ParameterPieces param;
|
||||
for(int4 i=0;i<primitives.size();++i) {
|
||||
Datatype *curType = primitives[i];
|
||||
if (resource->assignAddressFallback(resourceType, curType, !consumeFromStack, tmpStatus,param) == fail)
|
||||
return fail;
|
||||
pieces.push_back(VarnodeData());
|
||||
pieces.back().space = param.addr.getSpace();
|
||||
pieces.back().offset = param.addr.getOffset();
|
||||
pieces.back().size = curType->getSize();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void MultiMemberAssign::decode(Decoder &decoder)
|
||||
|
||||
{
|
||||
uint4 elemId = decoder.openElement(ELEM_JOIN_PER_PRIMITIVE);
|
||||
for(;;) {
|
||||
uint4 attribId = decoder.getNextAttributeId();
|
||||
if (attribId == 0) break;
|
||||
if (attribId == ATTRIB_STORAGE) {
|
||||
resourceType = string2typeclass(decoder.readString());
|
||||
}
|
||||
}
|
||||
decoder.closeElement(elemId);
|
||||
}
|
||||
|
||||
ConsumeAs::ConsumeAs(type_class store,const ParamListStandard *res)
|
||||
: AssignAction(res)
|
||||
{
|
||||
resourceType = store;
|
||||
}
|
||||
|
||||
uint4 ConsumeAs::assignAddress(Datatype *dt,const PrototypePieces &proto,int4 pos,TypeFactory &tlist,
|
||||
vector<int4> &status,ParameterPieces &res) const
|
||||
{
|
||||
return resource->assignAddressFallback(resourceType, dt, true, status, res);
|
||||
}
|
||||
|
||||
void ConsumeAs::decode(Decoder &decoder)
|
||||
|
||||
{
|
||||
uint4 elemId = decoder.openElement(ELEM_CONSUME);
|
||||
resourceType = string2typeclass(decoder.readString(ATTRIB_STORAGE));
|
||||
decoder.closeElement(elemId);
|
||||
}
|
||||
|
||||
HiddenReturnAssign::HiddenReturnAssign(const ParamListStandard *res,bool voidLock)
|
||||
: AssignAction(res)
|
||||
{
|
||||
retCode = voidLock ? hiddenret_specialreg_void : hiddenret_specialreg;
|
||||
}
|
||||
|
||||
uint4 HiddenReturnAssign::assignAddress(Datatype *dt,const PrototypePieces &proto,int4 pos,TypeFactory &tlist,
|
||||
vector<int4> &status,ParameterPieces &res) const
|
||||
{
|
||||
return retCode; // Signal to assignMap to use TYPECLASS_HIDDENRET
|
||||
}
|
||||
|
||||
void HiddenReturnAssign::decode(Decoder &decoder)
|
||||
|
||||
{
|
||||
uint4 elemId = decoder.openElement(ELEM_HIDDEN_RETURN);
|
||||
uint4 attribId = decoder.getNextAttributeId();
|
||||
if (attribId == ATTRIB_VOIDLOCK)
|
||||
retCode = hiddenret_specialreg_void;
|
||||
else
|
||||
retCode = hiddenret_specialreg;
|
||||
decoder.closeElement(elemId);
|
||||
}
|
||||
|
||||
/// Find the first ParamEntry matching the \b resourceType.
|
||||
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())
|
||||
throw LowlevelError("Could not find matching resources for action: consumeextra");
|
||||
}
|
||||
|
||||
ConsumeExtra::ConsumeExtra(const ParamListStandard *res)
|
||||
: AssignAction(res)
|
||||
{
|
||||
resourceType = TYPECLASS_GENERAL;
|
||||
matchSize = true;
|
||||
}
|
||||
|
||||
ConsumeExtra::ConsumeExtra(type_class store,bool match,const ParamListStandard *res)
|
||||
: AssignAction(res)
|
||||
{
|
||||
resourceType = store;
|
||||
matchSize = match;
|
||||
initializeEntries();
|
||||
}
|
||||
|
||||
uint4 ConsumeExtra::assignAddress(Datatype *dt,const PrototypePieces &proto,int4 pos,TypeFactory &tlist,
|
||||
vector<int4> &status,ParameterPieces &res) const
|
||||
{
|
||||
list<ParamEntry>::const_iterator iter = firstIter;
|
||||
list<ParamEntry>::const_iterator endIter = resource->getEntry().end();
|
||||
int4 sizeLeft = dt->getSize();
|
||||
while(sizeLeft > 0 && iter != endIter) {
|
||||
const ParamEntry &entry(*iter);
|
||||
++iter;
|
||||
if (!entry.isExclusion())
|
||||
break; // Reached end of resource list
|
||||
if (entry.getType() != resourceType || entry.getAllGroups().size() != 1)
|
||||
continue; // Not a single register in desired list
|
||||
if (status[entry.getGroup()] != 0)
|
||||
continue; // Already consumed
|
||||
status[entry.getGroup()] = -1; // Consume the slot/register
|
||||
sizeLeft -= entry.getSize();
|
||||
if (!matchSize)
|
||||
break; // Only consume a single register
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
void ConsumeExtra::decode(Decoder &decoder)
|
||||
|
||||
{
|
||||
uint4 elemId = decoder.openElement(ELEM_CONSUME_EXTRA);
|
||||
resourceType = string2typeclass(decoder.readString(ATTRIB_STORAGE));
|
||||
decoder.closeElement(elemId);
|
||||
initializeEntries();
|
||||
}
|
||||
|
||||
ModelRule::ModelRule(const ModelRule &op2,const ParamListStandard *res)
|
||||
|
||||
{
|
||||
if (op2.filter != (DatatypeFilter *)0)
|
||||
filter = op2.filter->clone();
|
||||
else
|
||||
filter = (DatatypeFilter *)0;
|
||||
if (op2.qualifier != (QualifierFilter *)0)
|
||||
qualifier = op2.qualifier->clone();
|
||||
else
|
||||
qualifier = (QualifierFilter *)0;
|
||||
if (op2.assign != (AssignAction *)0)
|
||||
assign = op2.assign->clone(res);
|
||||
else
|
||||
assign = (AssignAction *)0;
|
||||
for(int4 i=0;i<op2.sideeffects.size();++i)
|
||||
sideeffects.push_back(op2.sideeffects[i]->clone(res));
|
||||
}
|
||||
|
||||
/// The provided components are cloned into the new object.
|
||||
/// \param typeFilter is the data-type filter the rule applies before performing the action
|
||||
/// \param action is the action that will be applied
|
||||
/// \param res is the resource list to which \b this rule will be applied
|
||||
ModelRule::ModelRule(const DatatypeFilter &typeFilter,const AssignAction &action,const ParamListStandard *res)
|
||||
|
||||
{
|
||||
filter = typeFilter.clone();
|
||||
qualifier = (QualifierFilter *)0;
|
||||
assign = action.clone(res);
|
||||
}
|
||||
|
||||
ModelRule::~ModelRule(void)
|
||||
|
||||
{
|
||||
if (filter != (DatatypeFilter *)0)
|
||||
delete filter;
|
||||
if (qualifier != (QualifierFilter *)0)
|
||||
delete qualifier;
|
||||
if (assign != (AssignAction *)0)
|
||||
delete assign;
|
||||
for(int4 i=0;i<sideeffects.size();++i)
|
||||
delete sideeffects[i];
|
||||
}
|
||||
|
||||
/// \brief Assign an address and other details for a specific parameter or for return storage in context
|
||||
///
|
||||
/// The Address is only assigned if the data-type filter and the optional qualifier filter
|
||||
/// pass, otherwise a \b fail response is returned.
|
||||
/// If the filters pass, the Address is assigned based on the AssignAction specific to
|
||||
/// \b this rule, and the action's response code is returned.
|
||||
/// \param dt is the data-type of the parameter or return value
|
||||
/// \param proto is the high-level description of the function prototype
|
||||
/// \param pos is the position of the parameter (pos>=0) or return storage (pos=-1)
|
||||
/// \param tlist is a data-type factory for (possibly) transforming the data-type
|
||||
/// \param status is the resource consumption array
|
||||
/// \param res will hold the resulting description of the parameter
|
||||
/// \return the response code
|
||||
uint4 ModelRule::assignAddress(Datatype *dt,const PrototypePieces &proto,int4 pos,TypeFactory &tlist,
|
||||
vector<int4> &status,ParameterPieces &res) const
|
||||
{
|
||||
if (!filter->filter(dt)) {
|
||||
return AssignAction::fail;
|
||||
}
|
||||
if (qualifier != (QualifierFilter *)0 && !qualifier->filter(proto,pos)) {
|
||||
return AssignAction::fail;
|
||||
}
|
||||
uint4 response = assign->assignAddress(dt,proto,pos,tlist,status,res);
|
||||
if (response != AssignAction::fail) {
|
||||
for(int4 i=0;i<sideeffects.size();++i) {
|
||||
sideeffects[i]->assignAddress(dt,proto,pos,tlist,status,res);
|
||||
}
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
/// \param decoder is the stream decoder
|
||||
/// \param res is the parameter resource list owning \b this rule
|
||||
void ModelRule::decode(Decoder &decoder,const ParamListStandard *res)
|
||||
|
||||
{
|
||||
vector<QualifierFilter *> qualifiers;
|
||||
uint4 elemId = decoder.openElement(ELEM_RULE);
|
||||
filter = DatatypeFilter::decodeFilter(decoder);
|
||||
for(;;) {
|
||||
QualifierFilter *qual = QualifierFilter::decodeFilter(decoder);
|
||||
if (qual == (QualifierFilter *)0)
|
||||
break;
|
||||
qualifiers.push_back(qual);
|
||||
}
|
||||
if (qualifiers.size() == 0)
|
||||
qualifier = (QualifierFilter *)0;
|
||||
else if (qualifiers.size() == 1) {
|
||||
qualifier = qualifiers[0];
|
||||
qualifiers.clear();
|
||||
}
|
||||
else {
|
||||
qualifier = new AndFilter(qualifiers);
|
||||
}
|
||||
assign = AssignAction::decodeAction(decoder, res);
|
||||
while(decoder.peekElement() != 0) {
|
||||
sideeffects.push_back(AssignAction::decodeSideeffect(decoder,res));
|
||||
}
|
||||
|
||||
decoder.closeElement(elemId);
|
||||
}
|
||||
|
||||
} // End namespace ghidra
|
393
Ghidra/Features/Decompiler/src/decompile/cpp/modelrules.hh
Normal file
393
Ghidra/Features/Decompiler/src/decompile/cpp/modelrules.hh
Normal file
|
@ -0,0 +1,393 @@
|
|||
/* ###
|
||||
* 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.
|
||||
*/
|
||||
/// \file modelrules.hh
|
||||
/// \brief Definitions for rules governing mapping of data-type to address for prototype models
|
||||
|
||||
#ifndef __MODELRULES_HH__
|
||||
#define __MODELRULES_HH__
|
||||
|
||||
#include "op.hh"
|
||||
|
||||
namespace ghidra {
|
||||
|
||||
class ParameterPieces;
|
||||
class ParamListStandard;
|
||||
class ParamEntry;
|
||||
|
||||
extern ElementId ELEM_DATATYPE; ///< Marshaling element \<datatype>
|
||||
extern ElementId ELEM_CONSUME; ///< Marshaling element \<consume>
|
||||
extern ElementId ELEM_CONSUME_EXTRA; ///< Marshaling element \<consume_extra>
|
||||
extern ElementId ELEM_CONVERT_TO_PTR; ///< Marshaling element \<convert_to_ptr>
|
||||
extern ElementId ELEM_GOTO_STACK; ///< Marshaling element \<goto_stack>
|
||||
extern ElementId ELEM_JOIN; ///< Marshaling element \<join>
|
||||
extern ElementId ELEM_DATATYPE_AT; ///< Marshaling element \<datatype_at>
|
||||
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>
|
||||
|
||||
/// \brief A filter selecting a specific class of data-type
|
||||
///
|
||||
/// An instance is configured via the decode() method, then a test of whether
|
||||
/// a data-type belongs to its class can be performed by calling the filter() method.
|
||||
class DatatypeFilter {
|
||||
public:
|
||||
virtual ~DatatypeFilter(void) {} ///< Destructor
|
||||
|
||||
/// \brief Make a copy of \b this filter
|
||||
///
|
||||
/// \return the newly allocated copy
|
||||
virtual DatatypeFilter *clone(void) const=0;
|
||||
|
||||
/// \brief Test whether the given data-type belongs to \b this filter's data-type class
|
||||
///
|
||||
/// \param dt is the given data-type to test
|
||||
/// \return \b true if the data-type is in the class, \b false otherwise
|
||||
virtual bool filter(Datatype *dt) const=0;
|
||||
|
||||
/// \brief Configure details of the data-type class being filtered from the given stream
|
||||
///
|
||||
/// \param decoder is the given stream decoder
|
||||
virtual void decode(Decoder &decoder)=0;
|
||||
|
||||
static bool extractPrimitives(Datatype *dt,int4 max,Datatype *filler,vector<Datatype *> &res);
|
||||
static DatatypeFilter *decodeFilter(Decoder &decoder); ///< Instantiate a filter from the given stream
|
||||
};
|
||||
|
||||
/// \brief A common base class for data-type filters that tests for a size range
|
||||
///
|
||||
/// Any filter that inherits from \b this, can use ATTRIB_MINSIZE and ATTRIB_MAXSIZE
|
||||
/// to place bounds on the possible sizes of data-types. The bounds are enforced
|
||||
/// by calling filterOnSize() within the inheriting classes filter() method.
|
||||
class SizeRestrictedFilter : public DatatypeFilter {
|
||||
protected:
|
||||
int4 minSize; ///< Minimum size of the data-type in bytes
|
||||
int4 maxSize; ///< Maximum size of the data-type in bytes
|
||||
public:
|
||||
SizeRestrictedFilter(void) { minSize=0; maxSize=0; } ///< Constructor for use with decode()
|
||||
SizeRestrictedFilter(int4 min,int4 max); ///< Constructor
|
||||
bool filterOnSize(Datatype *dt) const; ///< Enforce any size bounds on a given data-type
|
||||
virtual DatatypeFilter *clone(void) const { return new SizeRestrictedFilter(minSize,maxSize); }
|
||||
virtual bool filter(Datatype *dt) const { return filterOnSize(dt); }
|
||||
virtual void decode(Decoder &decoder);
|
||||
};
|
||||
|
||||
/// \brief Filter on a single meta data-type
|
||||
///
|
||||
/// Filters on TYPE_STRUCT or TYPE_FLOAT etc. Additional filtering on size of the data-type can be configured.
|
||||
class MetaTypeFilter : public SizeRestrictedFilter {
|
||||
protected:
|
||||
type_metatype metaType; ///< The meta-type this filter lets through
|
||||
public:
|
||||
MetaTypeFilter(type_metatype meta); ///< Constructor for use with decode()
|
||||
MetaTypeFilter(type_metatype meta,int4 min,int4 max); ///< Constructor
|
||||
virtual DatatypeFilter *clone(void) const { return new MetaTypeFilter(metaType,minSize,maxSize); }
|
||||
virtual bool filter(Datatype *dt) const;
|
||||
};
|
||||
|
||||
/// \brief Filter on a homogeneous aggregate data-type
|
||||
///
|
||||
/// All primitive data-types must be the same.
|
||||
class HomogeneousAggregate : public SizeRestrictedFilter {
|
||||
type_metatype metaType; ///< The expected meta-type
|
||||
int4 maxPrimitives; ///< Maximum number of primitives in the aggregate
|
||||
public:
|
||||
HomogeneousAggregate(type_metatype meta); ///< Constructor for use with decode()
|
||||
HomogeneousAggregate(type_metatype meta,int4 maxPrim,int4 min,int4 max); ///< Constructor
|
||||
virtual DatatypeFilter *clone(void) const { return new HomogeneousAggregate(metaType,maxPrimitives, minSize,maxSize); }
|
||||
virtual bool filter(Datatype *dt) const;
|
||||
};
|
||||
|
||||
/// \brief A filter on some aspect of a specific function prototype
|
||||
///
|
||||
/// An instance is configured via the decode() method, then a test of whether
|
||||
/// a function prototype meets its criteria can be performed by calling its filter() method.
|
||||
class QualifierFilter {
|
||||
public:
|
||||
virtual ~QualifierFilter(void) {} ///< Destructor
|
||||
|
||||
/// \brief Make a copy of \b this qualifier
|
||||
///
|
||||
/// \return the newly allocated copy
|
||||
virtual QualifierFilter *clone(void) const=0;
|
||||
|
||||
/// \brief Test whether the given function prototype meets \b this filter's criteria
|
||||
///
|
||||
/// \param proto is the high-level description of the function prototype to test
|
||||
/// \param pos is the position of a specific output (pos=-1) or input (pos >=0) in context
|
||||
/// \return \b true if the prototype meets the criteria, \b false otherwise
|
||||
virtual bool filter(const PrototypePieces &proto,int4 pos) const=0;
|
||||
|
||||
/// \brief Configure details of the criteria being filtered from the given stream
|
||||
///
|
||||
/// \param decoder is the given stream decoder
|
||||
virtual void decode(Decoder &decoder) {}
|
||||
static QualifierFilter *decodeFilter(Decoder &decoder); ///< Try to instantiate a qualifier filter
|
||||
};
|
||||
|
||||
/// \brief Logically AND multiple QualifierFilters together into a single filter
|
||||
///
|
||||
/// An instances contains some number of other arbitrary filters. In order for \b this filter to
|
||||
/// pass, all these contained filters must pass.
|
||||
class AndFilter : public QualifierFilter {
|
||||
vector<QualifierFilter *> subQualifiers; ///< Filters being logically ANDed together
|
||||
public:
|
||||
AndFilter(vector<QualifierFilter *> filters); ///< Construct from array of filters
|
||||
virtual ~AndFilter(void);
|
||||
virtual QualifierFilter *clone(void) const;
|
||||
virtual bool filter(const PrototypePieces &proto,int4 pos) const;
|
||||
virtual void decode(Decoder &decoder) {}
|
||||
};
|
||||
|
||||
/// \brief A filter that selects function parameters that are considered optional
|
||||
///
|
||||
/// If the underlying function prototype is considered to take variable arguments, the first
|
||||
/// \e n parameters (as determined by PrototypePieces.firstVarArgSlot) are considered non-optional.
|
||||
/// If additional data-types are provided beyond the initial \e n, these are considered optional.
|
||||
/// This filter returns \b true for these optional parameters
|
||||
class VarargsFilter : public QualifierFilter {
|
||||
public:
|
||||
virtual QualifierFilter *clone(void) const { return new VarargsFilter(); }
|
||||
virtual bool filter(const PrototypePieces &proto,int4 pos) const;
|
||||
virtual void decode(Decoder &decoder);
|
||||
};
|
||||
|
||||
/// \brief Filter that selects for a particular parameter position
|
||||
///
|
||||
/// This matches if the position of the current parameter being assigned, within the data-type
|
||||
/// list, matches the \b position attribute of \b this filter.
|
||||
class PositionMatchFilter : public QualifierFilter {
|
||||
int4 position; ///< Parameter position being filtered for
|
||||
public:
|
||||
PositionMatchFilter(int4 pos) { position = pos; } ///< Constructor
|
||||
virtual QualifierFilter *clone(void) const { return new PositionMatchFilter(position); }
|
||||
virtual bool filter(const PrototypePieces &proto,int4 pos) const;
|
||||
virtual void decode(Decoder &decoder);
|
||||
};
|
||||
|
||||
/// \brief Check if the function signature has a specific data-type in a specific position
|
||||
/// This filter does not match against the data-type in the current position
|
||||
/// being assigned, but against a parameter at a fixed position.
|
||||
class DatatypeMatchFilter : public QualifierFilter {
|
||||
int4 position; ///< The position of the data-type to check
|
||||
DatatypeFilter *typeFilter; ///< The data-type that must be at \b position
|
||||
public:
|
||||
DatatypeMatchFilter(void) { position = -1; typeFilter = (DatatypeFilter *)0; } ///< Constructor for use with decode
|
||||
virtual ~DatatypeMatchFilter(void);
|
||||
virtual QualifierFilter *clone(void) const;
|
||||
virtual bool filter(const PrototypePieces &proto,int4 pos) const;
|
||||
virtual void decode(Decoder &decoder);
|
||||
};
|
||||
/// \brief An action that assigns an Address to a function prototype parameter
|
||||
///
|
||||
/// A request for the address of either \e return storage or an input parameter is made
|
||||
/// through the assignAddress() method, which is given full information about the function prototype.
|
||||
/// Details about how the action performs is configured through the decode() method.
|
||||
class AssignAction {
|
||||
public:
|
||||
enum {
|
||||
success, ///< Data-type is fully assigned
|
||||
fail, ///< Action could not be applied (not enough resources)
|
||||
hiddenret_ptrparam, ///< Hidden return pointer as first input parameter
|
||||
hiddenret_specialreg, ///< Hidden return pointer in dedicated input register
|
||||
hiddenret_specialreg_void ///< Hidden return pointer, but no normal return
|
||||
};
|
||||
protected:
|
||||
const ParamListStandard *resource; ///< Resources to which this action applies
|
||||
public:
|
||||
AssignAction(const ParamListStandard *res) {resource = res; } ///< Constructor
|
||||
virtual ~AssignAction(void) {}
|
||||
|
||||
/// \brief Make a copy of \b this action
|
||||
///
|
||||
/// \param newResource is the new resource object that will own the clone
|
||||
/// \return the newly allocated copy
|
||||
virtual AssignAction *clone(const ParamListStandard *newResource) const=0;
|
||||
|
||||
/// \brief Assign an address and other meta-data for a specific parameter or for return storage in context
|
||||
///
|
||||
/// The Address is assigned based on the data-type of the parameter, available register
|
||||
/// resources, and other details of the function prototype. Consumed resources are marked.
|
||||
/// This method returns a response code:
|
||||
/// - success - indicating the Address was successfully assigned
|
||||
/// - fail - if the Address could not be assigned
|
||||
/// - hiddenret_ptrparam - if an additional \e hidden \e return \e parameter is required
|
||||
///
|
||||
/// \param dt is the data-type of the parameter or return value
|
||||
/// \param proto is the high-level description of the function prototype
|
||||
/// \param pos is the position of the parameter (pos>=0) or return storage (pos=-1)
|
||||
/// \param tlist is a data-type factory for (possibly) transforming the data-type
|
||||
/// \param status is the resource consumption array
|
||||
/// \param res will hold the resulting description of the parameter
|
||||
/// \return the response code
|
||||
virtual uint4 assignAddress(Datatype *dt,const PrototypePieces &proto,int4 pos,TypeFactory &tlist,
|
||||
vector<int4> &status,ParameterPieces &res) const=0;
|
||||
|
||||
/// \brief Configure any details of how \b this action should behave from the stream
|
||||
///
|
||||
/// \param decoder is the given stream decoder
|
||||
virtual void decode(Decoder &decoder)=0;
|
||||
static AssignAction *decodeAction(Decoder &decoder,const ParamListStandard *res);
|
||||
static AssignAction *decodeSideeffect(Decoder &decoder,const ParamListStandard *res);
|
||||
};
|
||||
|
||||
/// \brief Action assigning a parameter Address from the next available stack location
|
||||
class GotoStack : public AssignAction {
|
||||
const ParamEntry *stackEntry; ///< Parameter Entry corresponding to the stack
|
||||
void initializeEntry(void); ///< Find stack entry in resource list
|
||||
public:
|
||||
GotoStack(const ParamListStandard *res,int4 val); ///< Constructor for use with decode
|
||||
GotoStack(const ParamListStandard *res); ///< Constructor for use with decode()
|
||||
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 void decode(Decoder &decoder);
|
||||
};
|
||||
|
||||
/// \brief Action converting the parameter's data-type to a pointer, and assigning storage for the pointer
|
||||
///
|
||||
/// This assumes the data-type is stored elsewhere and only the pointer is passed as a parameter
|
||||
class ConvertToPointer : public AssignAction {
|
||||
AddrSpace *space; ///< Address space used for pointer size
|
||||
public:
|
||||
ConvertToPointer(const ParamListStandard *res); ///< Constructor for use with decode()
|
||||
virtual AssignAction *clone(const ParamListStandard *newResource) const { return new ConvertToPointer(newResource); }
|
||||
virtual uint4 assignAddress(Datatype *dt,const PrototypePieces &proto,int4 pos,TypeFactory &tlist,
|
||||
vector<int4> &status,ParameterPieces &res) const;
|
||||
virtual void decode(Decoder &decoder);
|
||||
};
|
||||
|
||||
/// \brief Consume multiple registers to pass a data-type
|
||||
///
|
||||
/// Available registers are consumed until the data-type is covered, and an appropriate
|
||||
/// \e join space address is assigned. Registers can be consumed from a specific resource list.
|
||||
/// Consumption can spill over onto the stack if desired.
|
||||
class MultiSlotAssign : public AssignAction {
|
||||
type_class resourceType; ///< Resource list from which to consume
|
||||
bool consumeFromStack; ///< True if resources should be consumed from the stack
|
||||
bool consumeMostSig; ///< True if resources are consumed starting with most significant bytes
|
||||
bool enforceAlignment; ///< True if register resources are discarded to match alignment
|
||||
bool justifyRight; ///< True if initial bytes are padding for odd data-type sizes
|
||||
const ParamEntry *stackEntry; ///< The stack resource
|
||||
list<ParamEntry>::const_iterator firstIter; ///< Iterator to first element in the resource list
|
||||
void initializeEntries(void); ///< Cache specific ParamEntry needed by the action
|
||||
public:
|
||||
MultiSlotAssign(const ParamListStandard *res); ///< Constructor for use with decode
|
||||
MultiSlotAssign(type_class store,bool stack,bool mostSig,bool align,bool justRight,const ParamListStandard *res);
|
||||
virtual AssignAction *clone(const ParamListStandard *newResource) const {
|
||||
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 void decode(Decoder &decoder);
|
||||
};
|
||||
|
||||
/// \brief Consume a register per primitive member of an aggregate data-type
|
||||
///
|
||||
/// The data-type is split up into its underlying primitive elements, and each one
|
||||
/// is assigned a register from the specific resource list. There must be no padding between
|
||||
/// elements. No packing of elements into a single register occurs.
|
||||
class MultiMemberAssign : public AssignAction {
|
||||
type_class resourceType; ///< Resource list from which to consume
|
||||
bool consumeFromStack; ///< True if resources should be consumed from the stack
|
||||
bool consumeMostSig; ///< True if resources are consumed starting with most significant bytes
|
||||
public:
|
||||
MultiMemberAssign(type_class store,bool stack,bool mostSig,const ParamListStandard *res);
|
||||
virtual AssignAction *clone(const ParamListStandard *newResource) const {
|
||||
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 void decode(Decoder &decoder);
|
||||
};
|
||||
|
||||
/// \brief Consume a parameter from a specific resource list
|
||||
///
|
||||
/// Normally the resource list is determined by the parameter data-type, but this
|
||||
/// action specifies an overriding resource list. Assignment will \e not fall through to the stack.
|
||||
class ConsumeAs : public AssignAction {
|
||||
type_class resourceType; ///< The resource list the parameter is consumed from
|
||||
public:
|
||||
ConsumeAs(type_class store,const ParamListStandard *res); ///< Constructor
|
||||
virtual AssignAction *clone(const ParamListStandard *newResource) const {
|
||||
return new ConsumeAs(resourceType,newResource); }
|
||||
virtual uint4 assignAddress(Datatype *dt,const PrototypePieces &proto,int4 pos,TypeFactory &tlist,
|
||||
vector<int4> &status,ParameterPieces &res) const;
|
||||
virtual void decode(Decoder &decoder);
|
||||
};
|
||||
|
||||
/// \brief Allocate the return value as special input register
|
||||
///
|
||||
/// The assignAddress() method signals with \b hiddenret_specialreg, indicating that the
|
||||
/// input register assignMap() method should use storage class TYPECLASS_HIDDENRET to assign
|
||||
/// an additional input register to hold a pointer to the return value. This is different than
|
||||
/// the default \e hiddenret action that assigns a location based TYPECLASS_PTR and generally
|
||||
/// consumes a general purpose input register.
|
||||
class HiddenReturnAssign : public AssignAction {
|
||||
uint4 retCode; ///< The specific signal to pass back
|
||||
public:
|
||||
HiddenReturnAssign(const ParamListStandard *res,bool voidLock); ///< Constructor
|
||||
virtual AssignAction *clone(const ParamListStandard *newResource) const {
|
||||
return new HiddenReturnAssign(newResource, retCode == hiddenret_specialreg_void); }
|
||||
virtual uint4 assignAddress(Datatype *dt,const PrototypePieces &proto,int4 pos,TypeFactory &tlist,
|
||||
vector<int4> &status,ParameterPieces &res) const;
|
||||
virtual void decode(Decoder &decoder);
|
||||
};
|
||||
|
||||
/// \brief Consume additional registers from an alternate resource list
|
||||
///
|
||||
/// This action is a side-effect and doesn't assign an address for the current parameter.
|
||||
/// The resource list, \b resourceType, is specified. If the side-effect is triggered,
|
||||
/// register resources from this list are consumed. If \b matchSize is true (the default),
|
||||
/// registers are consumed, until the number of bytes in the data-type is reached. Otherwise,
|
||||
/// only a single register is consumed. If all registers are already consumed, no action is taken.
|
||||
class ConsumeExtra : public AssignAction {
|
||||
type_class resourceType; ///< The other resource list to consume from
|
||||
list<ParamEntry>::const_iterator firstIter; ///< Iterator to first element in the resource list
|
||||
bool matchSize; ///< \b false, if side-effect only consumes a single register
|
||||
void initializeEntries(void); ///< Cache specific ParamEntry needed by the action
|
||||
public:
|
||||
ConsumeExtra(const ParamListStandard *res); ///< Constructor for use with decode
|
||||
ConsumeExtra(type_class store,bool match,const ParamListStandard *res); ///< Constructor
|
||||
virtual AssignAction *clone(const ParamListStandard *newResource) const {
|
||||
return new ConsumeExtra(resourceType,matchSize,newResource); }
|
||||
virtual uint4 assignAddress(Datatype *dt,const PrototypePieces &proto,int4 pos,TypeFactory &tlist,
|
||||
vector<int4> &status,ParameterPieces &res) const;
|
||||
virtual void decode(Decoder &decoder);
|
||||
};
|
||||
|
||||
/// \brief A rule controlling how parameters are assigned addresses
|
||||
///
|
||||
/// Rules are applied to a parameter in the context of a full function prototype.
|
||||
/// A rule applies only for a specific class of data-type associated with the parameter, as
|
||||
/// determined by its DatatypeFilter, and may have other criteria limiting when it applies
|
||||
/// (via QualifierFilter).
|
||||
class ModelRule {
|
||||
DatatypeFilter *filter; ///< Which data-types \b this rule applies to
|
||||
QualifierFilter *qualifier; ///< Additional qualifiers for when the rule should apply (if non-null)
|
||||
AssignAction *assign; ///< How the Address should be assigned
|
||||
vector<AssignAction *> sideeffects; ///< Extra actions that happen on success
|
||||
public:
|
||||
ModelRule(void) {
|
||||
filter = (DatatypeFilter *)0; qualifier = (QualifierFilter *)0; assign = (AssignAction *)0; } ///< Constructor for use with decode
|
||||
ModelRule(const ModelRule &op2,const ParamListStandard *res); ///< Copy constructor
|
||||
ModelRule(const DatatypeFilter &typeFilter,const AssignAction &action,const ParamListStandard *res); ///< Construct from components
|
||||
~ModelRule(void); ///< Destructor
|
||||
uint4 assignAddress(Datatype *dt,const PrototypePieces &proto,int4 pos,TypeFactory &tlist,
|
||||
vector<int4> &status,ParameterPieces &res) const;
|
||||
void decode(Decoder &decoder,const ParamListStandard *res); ///< Decode \b this rule from stream
|
||||
};
|
||||
|
||||
} // End namespace ghidra
|
||||
#endif
|
|
@ -578,9 +578,9 @@ void JoinSpace::encodeAttributes(Encoder &encoder,uintb offset,int4 size) const
|
|||
encodeAttributes(encoder,offset); // Ignore size
|
||||
}
|
||||
|
||||
/// Parse a join address the current element. Pieces of the join are encoded as a sequence
|
||||
/// of attributes. The Translate::findAddJoin method is used to construct a logical
|
||||
/// address within the join space.
|
||||
/// Parse the current element as a join address. Pieces of the join are encoded as a sequence
|
||||
/// of ATTRIB_PIECE attributes. "piece1" corresponds to the most significant piece. The
|
||||
/// Translate::findAddJoin method is used to construct a logical address within the join space.
|
||||
/// \param decoder is the stream decoder
|
||||
/// \param size is a reference to be filled in as the size encoded by the tag
|
||||
/// \return the offset of the final address encoded by the tag
|
||||
|
|
|
@ -629,7 +629,8 @@ AddrSpace *AddrSpaceManager::getNextSpaceInOrder(AddrSpace *spc) const
|
|||
}
|
||||
|
||||
/// Given a list of memory locations, the \e pieces, either find a pre-existing JoinRecord or
|
||||
/// create a JoinRecord that represents the logical joining of the pieces.
|
||||
/// create a JoinRecord that represents the logical joining of the pieces. The pieces must
|
||||
/// be in order from most significant to least significant.
|
||||
/// \param pieces if the list memory locations to be joined
|
||||
/// \param logicalsize of a \e single \e piece join, or zero
|
||||
/// \return a pointer to the JoinRecord
|
||||
|
|
|
@ -196,7 +196,7 @@ public:
|
|||
/// from \e most \e significant to \e least \e significant.
|
||||
class JoinRecord {
|
||||
friend class AddrSpaceManager;
|
||||
vector<VarnodeData> pieces; ///< All the physical pieces of the symbol
|
||||
vector<VarnodeData> pieces; ///< All the physical pieces of the symbol, most significant to least
|
||||
VarnodeData unified; ///< Special entry representing entire symbol in one chunk
|
||||
public:
|
||||
int4 numPieces(void) const { return pieces.size(); } ///< Get number of pieces in this record
|
||||
|
|
|
@ -349,6 +349,72 @@ type_metatype string2metatype(const string &metastring)
|
|||
throw LowlevelError("Unknown metatype: "+metastring);
|
||||
}
|
||||
|
||||
/// Given a description of a data-type \e class, return the \b type_class.
|
||||
/// \param classstring is the description of the class
|
||||
/// \return the encoded type_class
|
||||
type_class string2typeclass(const string &classstring)
|
||||
|
||||
{
|
||||
switch(classstring[0]) {
|
||||
case 'c':
|
||||
if (classstring == "class1")
|
||||
return TYPECLASS_CLASS1;
|
||||
else if (classstring == "class2")
|
||||
return TYPECLASS_CLASS2;
|
||||
else if (classstring == "class3")
|
||||
return TYPECLASS_CLASS3;
|
||||
else if (classstring == "class4")
|
||||
return TYPECLASS_CLASS4;
|
||||
break;
|
||||
case 'g':
|
||||
if (classstring == "general")
|
||||
return TYPECLASS_GENERAL;
|
||||
break;
|
||||
case 'h':
|
||||
if (classstring == "hiddenret")
|
||||
return TYPECLASS_HIDDENRET;
|
||||
break;
|
||||
case 'f':
|
||||
if (classstring == "float")
|
||||
return TYPECLASS_FLOAT;
|
||||
break;
|
||||
case 'p':
|
||||
if (classstring == "ptr" || classstring == "pointer")
|
||||
return TYPECLASS_PTR;
|
||||
break;
|
||||
case 'v':
|
||||
if (classstring == "vector")
|
||||
return TYPECLASS_VECTOR;
|
||||
break;
|
||||
case 'u':
|
||||
if (classstring == "unknown")
|
||||
return TYPECLASS_GENERAL;
|
||||
break;
|
||||
}
|
||||
throw LowlevelError("Unknown data-type class: " + classstring);
|
||||
}
|
||||
|
||||
/// Assign the basic storage class based on a metatype:
|
||||
/// - TYPE_FLOAT -> TYPECLASS_FLOAT
|
||||
/// - TYPE_PTR -> TYPECLASS_PTR
|
||||
///
|
||||
/// Everything else returns the general purpose TYPECLASS_GENERAL
|
||||
/// \param meta is the metatype
|
||||
/// \return the storage class
|
||||
type_class metatype2typeclass(type_metatype meta)
|
||||
|
||||
{
|
||||
switch(meta) {
|
||||
case TYPE_FLOAT:
|
||||
return TYPECLASS_FLOAT;
|
||||
case TYPE_PTR:
|
||||
return TYPECLASS_PTR;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return TYPECLASS_GENERAL;
|
||||
}
|
||||
|
||||
/// Encode a formal description of the data-type as a \<type> element.
|
||||
/// For composite data-types, the description goes down one level, describing
|
||||
/// the component types only by reference.
|
||||
|
@ -2383,31 +2449,19 @@ Datatype *TypePointerRel::getPtrToFromParent(Datatype *base,int4 off,TypeFactory
|
|||
|
||||
/// Turn on the data-type's function prototype
|
||||
/// \param tfact is the factory that owns \b this
|
||||
/// \param model is the prototype model
|
||||
/// \param outtype is the return type of the prototype
|
||||
/// \param intypes is the list of input parameters
|
||||
/// \param dotdotdot is true if the prototype takes variable arguments
|
||||
/// \param sig is the list of names and data-types making up the prototype
|
||||
/// \param voidtype is the reference "void" data-type
|
||||
void TypeCode::setPrototype(TypeFactory *tfact,ProtoModel *model,
|
||||
Datatype *outtype,const vector<Datatype *> &intypes,
|
||||
bool dotdotdot,Datatype *voidtype)
|
||||
void TypeCode::setPrototype(TypeFactory *tfact,const PrototypePieces &sig,Datatype *voidtype)
|
||||
|
||||
{
|
||||
factory = tfact;
|
||||
flags |= variable_length;
|
||||
if (proto != (FuncProto *)0)
|
||||
delete proto;
|
||||
proto = new FuncProto();
|
||||
proto->setInternal(model,voidtype);
|
||||
vector<Datatype *> typelist;
|
||||
vector<string> blanknames(intypes.size()+1);
|
||||
if (outtype == (Datatype *)0)
|
||||
typelist.push_back(voidtype);
|
||||
else
|
||||
typelist.push_back(outtype);
|
||||
for(int4 i=0;i<intypes.size();++i)
|
||||
typelist.push_back(intypes[i]);
|
||||
proto->setInternal(sig.model,voidtype);
|
||||
|
||||
proto->updateAllTypes(blanknames,typelist,dotdotdot);
|
||||
proto->updateAllTypes(sig);
|
||||
proto->setInputLock(true);
|
||||
proto->setOutputLock(true);
|
||||
}
|
||||
|
@ -3670,17 +3724,12 @@ TypeSpacebase *TypeFactory::getTypeSpacebase(AddrSpace *id,const Address &addr)
|
|||
}
|
||||
|
||||
/// Creates a TypeCode object and associates a specific function prototype with it.
|
||||
/// \param model is the prototype model associated with the function
|
||||
/// \param outtype is the return type of the function
|
||||
/// \param intypes is the array of input parameters of the function
|
||||
/// \param dotdotdot is true if the function takes variable arguments
|
||||
/// \param proto is the list of names, data-types, and other attributes of the prototype
|
||||
/// \return the TypeCode object
|
||||
TypeCode *TypeFactory::getTypeCode(ProtoModel *model,Datatype *outtype,
|
||||
const vector<Datatype *> &intypes,
|
||||
bool dotdotdot)
|
||||
TypeCode *TypeFactory::getTypeCode(const PrototypePieces &proto)
|
||||
{
|
||||
TypeCode tc; // getFuncdata type with no name
|
||||
tc.setPrototype(this,model,outtype,intypes,dotdotdot,getTypeVoid());
|
||||
tc.setPrototype(this,proto,getTypeVoid());
|
||||
tc.markComplete();
|
||||
return (TypeCode *) findAdd(tc);
|
||||
}
|
||||
|
@ -4311,11 +4360,15 @@ void TypeFactory::decodeAlignmentMap(Decoder &decoder)
|
|||
void TypeFactory::setDefaultAlignmentMap(void)
|
||||
|
||||
{
|
||||
alignMap.resize(5,0);
|
||||
alignMap.resize(9,0);
|
||||
alignMap[1] = 1;
|
||||
alignMap[2] = 2;
|
||||
alignMap[3] = 2;
|
||||
alignMap[4] = 4;
|
||||
alignMap[5] = 4;
|
||||
alignMap[6] = 4;
|
||||
alignMap[7] = 4;
|
||||
alignMap[8] = 8;
|
||||
}
|
||||
|
||||
/// Recover default enumeration properties (size and meta-type) from
|
||||
|
|
|
@ -122,12 +122,32 @@ enum sub_metatype {
|
|||
SUB_UNION = 1, ///< Compare as TYPE_UNION
|
||||
SUB_PARTIALUNION = 0 ///< Compare as a TYPE_PARTIALUNION
|
||||
};
|
||||
|
||||
/// Data-type classes for the purpose of assigning storage
|
||||
enum type_class {
|
||||
TYPECLASS_GENERAL = 0, ///< General purpose
|
||||
TYPECLASS_FLOAT = 1, ///< Floating-point data-types
|
||||
TYPECLASS_PTR = 2, ///< Pointer data-types
|
||||
TYPECLASS_HIDDENRET = 3, ///< Class for hidden return values
|
||||
TYPECLASS_VECTOR = 4, ///< Vector data-types
|
||||
TYPECLASS_CLASS1 = 100, ///< Architecture specific class 1
|
||||
TYPECLASS_CLASS2 = 101, ///< Architecture specific class 2
|
||||
TYPECLASS_CLASS3 = 102, ///< Architecture specific class 3
|
||||
TYPECLASS_CLASS4 = 103 ///< Architecture specific class 4
|
||||
};
|
||||
|
||||
/// Convert type \b meta-type to name
|
||||
extern void metatype2string(type_metatype metatype,string &res);
|
||||
|
||||
/// Convert string to type \b meta-type
|
||||
extern type_metatype string2metatype(const string &metastring);
|
||||
|
||||
/// Convert a string to a data-type class
|
||||
extern type_class string2typeclass(const string &classstring);
|
||||
|
||||
/// Convert a data-type metatype to a data-type class
|
||||
extern type_class metatype2typeclass(type_metatype meta);
|
||||
|
||||
class Architecture; // Forward declarations
|
||||
class PcodeOp;
|
||||
class Scope;
|
||||
|
@ -616,7 +636,7 @@ public:
|
|||
};
|
||||
|
||||
class FuncProto; // Forward declaration
|
||||
class ProtoModel;
|
||||
class PrototypePieces;
|
||||
|
||||
/// \brief Datatype object representing executable code.
|
||||
///
|
||||
|
@ -626,9 +646,7 @@ protected:
|
|||
friend class TypeFactory;
|
||||
FuncProto *proto; ///< If non-null, this describes the prototype of the underlying function
|
||||
TypeFactory *factory; ///< Factory owning \b this
|
||||
void setPrototype(TypeFactory *tfact,ProtoModel *model,
|
||||
Datatype *outtype,const vector<Datatype *> &intypes,
|
||||
bool dotdotdot,Datatype *voidtype); ///< Establish a function pointer
|
||||
void setPrototype(TypeFactory *tfact,const PrototypePieces &sig,Datatype *voidtype); ///< Establish a function pointer
|
||||
void setPrototype(TypeFactory *typegrp,const FuncProto *fp); ///< Set a particular function prototype on \b this
|
||||
void decodeStub(Decoder &decoder); ///< Restore stub of data-type without the full prototype
|
||||
void decodePrototype(Decoder &decoder,bool isConstructor,bool isDestructor,TypeFactory &typegrp); ///< Restore any prototype description
|
||||
|
@ -752,9 +770,7 @@ public:
|
|||
TypePartialUnion *getTypePartialUnion(TypeUnion *contain,int4 off,int4 sz); ///< Create a partial union
|
||||
TypeEnum *getTypeEnum(const string &n); ///< Create an (empty) enumeration
|
||||
TypeSpacebase *getTypeSpacebase(AddrSpace *id,const Address &addr); ///< Create a "spacebase" type
|
||||
TypeCode *getTypeCode(ProtoModel *model,Datatype *outtype,
|
||||
const vector<Datatype *> &intypes,
|
||||
bool dotdotdot); ///< Create a "function" datatype
|
||||
TypeCode *getTypeCode(const PrototypePieces &proto); ///< Create a "function" datatype
|
||||
Datatype *getTypedef(Datatype *ct,const string &name,uint8 id,uint4 format); ///< Create a new \e typedef data-type
|
||||
TypePointerRel *getTypePointerRel(TypePointer *parentPtr,Datatype *ptrTo,int4 off); ///< Get pointer offset relative to a container
|
||||
TypePointerRel *getTypePointerRel(int4 sz,Datatype *parent,Datatype *ptrTo,int4 ws,int4 off,const string &nm);
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
<decompilertest>
|
||||
<binaryimage arch="AARCH64:LE:64:v8A:default">
|
||||
<!--
|
||||
Example of a special input register holding pointer to return value
|
||||
-->
|
||||
<bytechunk space="ram" offset="0x455c14" readonly="true">
|
||||
ff4300d1e10308aae00f00b9
|
||||
400180d2200000f9800c80d2200400f9
|
||||
e00f80b9200800f91f2003d5ff430091
|
||||
c0035fd6
|
||||
</bytechunk>
|
||||
<bytechunk space="ram" offset="0x455c50" readonly="true">
|
||||
fd7bbda9fd030091e8630091eeffff97
|
||||
e01740f9fd7bc3a8c0035fd6
|
||||
</bytechunk>
|
||||
<symbol space="ram" offset="0x455c14" name="returnbig"/>
|
||||
<symbol space="ram" offset="0x455c50" name="read_returnbig"/>
|
||||
</binaryimage>
|
||||
<script>
|
||||
<com>parse line struct mystruct { int8 a; int8 b; int8 c; };</com>
|
||||
<com>parse line extern mystruct returnbig(int4 num);</com>
|
||||
<com>lo fu returnbig</com>
|
||||
<com>decompile</com>
|
||||
<com>print C</com>
|
||||
<com>lo fu read_returnbig</com>
|
||||
<com>decompile</com>
|
||||
<com>print C</com>
|
||||
<com>quit</com>
|
||||
</script>
|
||||
<stringmatch name="Return Value Input Register #1" min="1" max="1">rethidden->a = 10;</stringmatch>
|
||||
<stringmatch name="Return Value Input Register #2" min="1" max="1">rethidden->b = 100;</stringmatch>
|
||||
<stringmatch name="Return Value Input Register #3" min="1" max="1">rethidden->c = \(int8\)num;</stringmatch>
|
||||
<stringmatch name="Return Value Input Register #4" min="1" max="1">return;</stringmatch>
|
||||
<stringmatch name="Return Value Input Register #5" min="1" max="1">mystruct mStack_18;</stringmatch>
|
||||
<stringmatch name="Return Value Input Register #6" min="1" max="1">returnbig\(&mStack_18,param_1\);</stringmatch>
|
||||
<stringmatch name="Return Value Input Register #7" min="1" max="1">return mStack_18\.c;</stringmatch>
|
||||
</decompilertest>
|
|
@ -215,9 +215,8 @@ TEST(funcproto_register) {
|
|||
istringstream s("void func(int4 a,int4 b);");
|
||||
PrototypePieces pieces;
|
||||
parse_protopieces(pieces,s,glb);
|
||||
pieces.intypes.insert(pieces.intypes.begin(),pieces.outtype);
|
||||
vector<ParameterPieces> res;
|
||||
model->assignParameterStorage(pieces.intypes, res, false);
|
||||
model->assignParameterStorage(pieces, res, false);
|
||||
ASSERT_EQUALS(res.size(),3);
|
||||
ASSERT(res[0].addr.isInvalid()); // Placeholder for void return value
|
||||
ASSERT(register_equal(res[1],"r12"));
|
||||
|
@ -229,9 +228,8 @@ TEST(funcproto_smallregister) {
|
|||
istringstream s("int4 func(char a,int4 b,int2 c,int4 d);");
|
||||
PrototypePieces pieces;
|
||||
parse_protopieces(pieces,s,glb);
|
||||
pieces.intypes.insert(pieces.intypes.begin(),pieces.outtype);
|
||||
vector<ParameterPieces> res;
|
||||
model->assignParameterStorage(pieces.intypes, res, false);
|
||||
model->assignParameterStorage(pieces, res, false);
|
||||
ASSERT_EQUALS(res.size(),5);
|
||||
ASSERT(register_equal(res[0],"r12")); // output register
|
||||
ASSERT_EQUALS(res[0].type->getName(),"int4");
|
||||
|
@ -250,9 +248,8 @@ TEST(funcproto_stackalign) {
|
|||
istringstream s("int4 func(int4 a,int4 b,int4 c,int4 d,int4 e,int2 f,int1 *g);");
|
||||
PrototypePieces pieces;
|
||||
parse_protopieces(pieces,s,glb);
|
||||
pieces.intypes.insert(pieces.intypes.begin(),pieces.outtype);
|
||||
vector<ParameterPieces> res;
|
||||
model->assignParameterStorage(pieces.intypes, res, false);
|
||||
model->assignParameterStorage(pieces, res, false);
|
||||
ASSERT_EQUALS(res.size(),8);
|
||||
ASSERT(register_equal(res[0],"r12")); // output register
|
||||
ASSERT_EQUALS(res[0].type->getName(),"int4");
|
||||
|
@ -279,9 +276,8 @@ TEST(funcproto_pointeroverflow) {
|
|||
istringstream s("int2 func(int4 a,int8 b,int4 c);");
|
||||
PrototypePieces pieces;
|
||||
parse_protopieces(pieces,s,glb);
|
||||
pieces.intypes.insert(pieces.intypes.begin(),pieces.outtype);
|
||||
vector<ParameterPieces> res;
|
||||
model->assignParameterStorage(pieces.intypes, res, false);
|
||||
model->assignParameterStorage(pieces, res, false);
|
||||
ASSERT_EQUALS(res.size(),4);
|
||||
ASSERT(register_equal(res[0],"r12")); // output register
|
||||
ASSERT_EQUALS(res[0].type->getName(),"int2");
|
||||
|
@ -300,9 +296,8 @@ TEST(funcproto_stackoverflow) {
|
|||
istringstream s("char func(int4 a,int8 b,int4 c);");
|
||||
PrototypePieces pieces;
|
||||
parse_protopieces(pieces,s,glb);
|
||||
pieces.intypes.insert(pieces.intypes.begin(),pieces.outtype);
|
||||
vector<ParameterPieces> res;
|
||||
model->assignParameterStorage(pieces.intypes, res, false);
|
||||
model->assignParameterStorage(pieces, res, false);
|
||||
ASSERT_EQUALS(res.size(),4);
|
||||
ASSERT(register_equal(res[0],"r12")); // output register
|
||||
ASSERT_EQUALS(res[0].type->getName(),"char");
|
||||
|
@ -320,9 +315,8 @@ TEST(funcproto_floatreg) {
|
|||
istringstream s("void func(int4 a,float4 b,float4 c,int4 d,float4 d);");
|
||||
PrototypePieces pieces;
|
||||
parse_protopieces(pieces,s,glb);
|
||||
pieces.intypes.insert(pieces.intypes.begin(),pieces.outtype);
|
||||
vector<ParameterPieces> res;
|
||||
model->assignParameterStorage(pieces.intypes, res, false);
|
||||
model->assignParameterStorage(pieces, res, false);
|
||||
ASSERT_EQUALS(res.size(),6);
|
||||
ASSERT(res[0].addr.isInvalid());
|
||||
ASSERT(register_equal(res[1],"r10"));
|
||||
|
@ -340,9 +334,8 @@ TEST(funcproto_floattogeneric) {
|
|||
istringstream s("float4 func(int4 a,float4 b,float4 c,float4 d,float4 e,float4 f);");
|
||||
PrototypePieces pieces;
|
||||
parse_protopieces(pieces,s,glb);
|
||||
pieces.intypes.insert(pieces.intypes.begin(),pieces.outtype);
|
||||
vector<ParameterPieces> res;
|
||||
model->assignParameterStorage(pieces.intypes, res, false);
|
||||
model->assignParameterStorage(pieces, res, false);
|
||||
ASSERT_EQUALS(res.size(),7);
|
||||
ASSERT(register_equal(res[0],"r12"));
|
||||
ASSERT_EQUALS(res[0].type->getName(),"float4");
|
||||
|
@ -364,9 +357,8 @@ TEST(funcproto_grouped) {
|
|||
istringstream s("float4 func(int4 a,float4 b,float4 c,int4 d,float4 e);");
|
||||
PrototypePieces pieces;
|
||||
parse_protopieces(pieces,s,glb);
|
||||
pieces.intypes.insert(pieces.intypes.begin(),pieces.outtype);
|
||||
vector<ParameterPieces> res;
|
||||
model->assignParameterStorage(pieces.intypes, res, false);
|
||||
model->assignParameterStorage(pieces, res, false);
|
||||
ASSERT_EQUALS(res.size(),6);
|
||||
ASSERT(register_equal(res[0],"r12"));
|
||||
ASSERT_EQUALS(res[0].type->getName(),"float4");
|
||||
|
@ -389,9 +381,8 @@ TEST(funcproto_join) {
|
|||
istringstream s("int2 func(int8 a,int4 b,int4 c);");
|
||||
PrototypePieces pieces;
|
||||
parse_protopieces(pieces,s,glb);
|
||||
pieces.intypes.insert(pieces.intypes.begin(),pieces.outtype);
|
||||
vector<ParameterPieces> res;
|
||||
model->assignParameterStorage(pieces.intypes, res, false);
|
||||
model->assignParameterStorage(pieces, res, false);
|
||||
ASSERT_EQUALS(res.size(),4);
|
||||
ASSERT(register_equal(res[0],"r12"));
|
||||
ASSERT_EQUALS(res[0].type->getName(),"int2");
|
||||
|
@ -409,9 +400,8 @@ TEST(funcproto_nojoin) {
|
|||
istringstream s("int4 func(int4 a,int8 b,int4 c);");
|
||||
PrototypePieces pieces;
|
||||
parse_protopieces(pieces,s,glb);
|
||||
pieces.intypes.insert(pieces.intypes.begin(),pieces.outtype);
|
||||
vector<ParameterPieces> res;
|
||||
model->assignParameterStorage(pieces.intypes, res, false);
|
||||
model->assignParameterStorage(pieces, res, false);
|
||||
ASSERT_EQUALS(res.size(),4);
|
||||
ASSERT(register_equal(res[0],"r12"));
|
||||
ASSERT_EQUALS(res[0].type->getName(),"int4");
|
||||
|
@ -429,9 +419,8 @@ TEST(funcproto_hiddenreturn) {
|
|||
istringstream s("int8 func(int4 a,int4 b);");
|
||||
PrototypePieces pieces;
|
||||
parse_protopieces(pieces,s,glb);
|
||||
pieces.intypes.insert(pieces.intypes.begin(),pieces.outtype);
|
||||
vector<ParameterPieces> res;
|
||||
model->assignParameterStorage(pieces.intypes, res, false);
|
||||
model->assignParameterStorage(pieces, res, false);
|
||||
ASSERT_EQUALS(res.size(),4);
|
||||
ASSERT(register_equal(res[0],"r12")); // Pointer to actual return value
|
||||
ASSERT_EQUALS(res[0].type->getMetatype(),TYPE_PTR);
|
||||
|
@ -451,9 +440,8 @@ TEST(funcproto_mixedmeta) {
|
|||
istringstream s("int4 func(char *a,int4 b,float4 c,int4 *d);");
|
||||
PrototypePieces pieces;
|
||||
parse_protopieces(pieces,s,glb);
|
||||
pieces.intypes.insert(pieces.intypes.begin(),pieces.outtype);
|
||||
vector<ParameterPieces> res;
|
||||
model->assignParameterStorage(pieces.intypes, res, false);
|
||||
model->assignParameterStorage(pieces, res, false);
|
||||
ASSERT_EQUALS(res.size(),5);
|
||||
ASSERT(register_equal(res[0],"r12"));
|
||||
ASSERT(register_equal(res[1],"r1"));
|
||||
|
|
|
@ -0,0 +1,267 @@
|
|||
/* ###
|
||||
* 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.
|
||||
*/
|
||||
#include "architecture.hh"
|
||||
#include "grammar.hh"
|
||||
#include "test.hh"
|
||||
|
||||
namespace ghidra {
|
||||
|
||||
class ParamStoreEnvironment {
|
||||
map<string,Architecture *> loadedArches;
|
||||
Architecture *buildArch(const string &arch);
|
||||
public:
|
||||
~ParamStoreEnvironment(void);
|
||||
ProtoModel *getModel(const string &arch,const string &model);
|
||||
void parseType(ProtoModel *model,const string &definition);
|
||||
bool test(ProtoModel *model,const string &signature,const string &stores);
|
||||
static void parseJoin(ProtoModel *model,const string &join,VarnodeData &res);
|
||||
static void parseStore(ProtoModel *model,const string &name,VarnodeData &res);
|
||||
static void parseStores(ProtoModel *model,vector<VarnodeData> &res,const string &names);
|
||||
static bool comparePiece(const VarnodeData &vData,ParameterPieces &piece);
|
||||
};
|
||||
|
||||
static ParamStoreEnvironment theEnviron;
|
||||
|
||||
Architecture *ParamStoreEnvironment::buildArch(const string &arch)
|
||||
|
||||
{
|
||||
map<string,Architecture *>::const_iterator iter = loadedArches.find(arch);
|
||||
if (iter != loadedArches.end()) {
|
||||
return (*iter).second;
|
||||
}
|
||||
ArchitectureCapability *xmlCapability = ArchitectureCapability::getCapability("xml");
|
||||
istringstream s(
|
||||
"<binaryimage arch=\"" + arch + "\"></binaryimage>"
|
||||
);
|
||||
DocumentStorage store;
|
||||
Document *doc = store.parseDocument(s);
|
||||
store.registerTag(doc->getRoot());
|
||||
|
||||
Architecture *g = xmlCapability->buildArchitecture("", "", &cout);
|
||||
g->init(store);
|
||||
loadedArches[arch] = g;
|
||||
return g;
|
||||
}
|
||||
|
||||
ParamStoreEnvironment::~ParamStoreEnvironment(void)
|
||||
|
||||
{
|
||||
for(map<string,Architecture *>::const_iterator iter=loadedArches.begin();iter!=loadedArches.end();++iter) {
|
||||
delete (*iter).second;
|
||||
}
|
||||
}
|
||||
|
||||
ProtoModel *ParamStoreEnvironment::getModel(const string &arch,const string &model)
|
||||
|
||||
{
|
||||
Architecture *glb = buildArch(arch);
|
||||
return glb->protoModels[model];
|
||||
}
|
||||
|
||||
void ParamStoreEnvironment::parseType(ProtoModel *model,const string &definition)
|
||||
|
||||
{
|
||||
istringstream s(definition);
|
||||
parse_C(model->getArch(),s);
|
||||
}
|
||||
|
||||
bool ParamStoreEnvironment::test(ProtoModel *model,const string &signature,const string &stores)
|
||||
|
||||
{
|
||||
istringstream s(signature);
|
||||
PrototypePieces pieces;
|
||||
parse_protopieces(pieces,s,model->getArch());
|
||||
vector<ParameterPieces> res;
|
||||
model->assignParameterStorage(pieces, res, false);
|
||||
vector<VarnodeData> storeData;
|
||||
parseStores(model,storeData,stores);
|
||||
if (storeData.size() != res.size())
|
||||
return false;
|
||||
for(int4 i=0;i<res.size();++i) {
|
||||
if (!comparePiece(storeData[i],res[i]))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ParamStoreEnvironment::parseJoin(ProtoModel *model,const string &join,VarnodeData &res)
|
||||
|
||||
{
|
||||
vector<VarnodeData> pieces;
|
||||
size_t pos = join.find(' ',0) + 1;
|
||||
for(;;) {
|
||||
size_t nextpos = join.find(' ',pos);
|
||||
string el;
|
||||
if (nextpos == string::npos)
|
||||
el = join.substr(pos);
|
||||
else
|
||||
el = join.substr(pos,nextpos - pos);
|
||||
pieces.push_back(VarnodeData());
|
||||
parseStore(model,el,pieces.back());
|
||||
if (nextpos == string::npos)
|
||||
break;
|
||||
pos = nextpos + 1;
|
||||
}
|
||||
int4 size = (pieces.size() == 1) ? 4 : 0; // Assume a single piece join is a 4-byte float in a bigger register
|
||||
JoinRecord *joinRec = model->getArch()->findAddJoin(pieces, size);
|
||||
res = joinRec->getUnified();
|
||||
}
|
||||
|
||||
void ParamStoreEnvironment::parseStore(ProtoModel *model,const string &name,VarnodeData &res)
|
||||
|
||||
{
|
||||
if (name == "void") {
|
||||
res.space = (AddrSpace *)0;
|
||||
res.offset = 0;
|
||||
res.size = 0;
|
||||
return;
|
||||
}
|
||||
if (name.compare(0,5,"stack",5) == 0) {
|
||||
size_t pos = name.find(':');
|
||||
istringstream s(name.substr(5,pos));
|
||||
res.space = model->getArch()->getStackSpace();
|
||||
s >> hex >> res.offset;
|
||||
res.size = 1;
|
||||
if (pos != string::npos) {
|
||||
istringstream t(name.substr(pos+1));
|
||||
t >> dec >> res.size;
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if (name.compare(0,4,"join",4) == 0) {
|
||||
parseJoin(model,name,res);
|
||||
return;
|
||||
}
|
||||
string regname;
|
||||
size_t pos = name.find(':');
|
||||
int4 sz = 0;
|
||||
if (pos != string::npos) {
|
||||
istringstream s(name.substr(pos + 1));
|
||||
s >> dec >> sz;
|
||||
regname = name.substr(0,pos);
|
||||
}
|
||||
else
|
||||
regname = name;
|
||||
res = model->getArch()->translate->getRegister(regname);
|
||||
if (sz != 0) {
|
||||
if (res.space->isBigEndian())
|
||||
res.offset += (res.size - sz);
|
||||
res.size = sz;
|
||||
}
|
||||
}
|
||||
|
||||
void ParamStoreEnvironment::parseStores(ProtoModel *model,vector<VarnodeData> &res,const string &names)
|
||||
|
||||
{
|
||||
size_t pos = 0;
|
||||
for(;;) {
|
||||
size_t nextpos = names.find(',',pos);
|
||||
string el;
|
||||
if (nextpos == string::npos)
|
||||
el = names.substr(pos);
|
||||
else
|
||||
el = names.substr(pos,nextpos - pos);
|
||||
res.push_back(VarnodeData());
|
||||
parseStore(model,el,res.back());
|
||||
if (nextpos == string::npos)
|
||||
break;
|
||||
pos = nextpos + 1;
|
||||
}
|
||||
}
|
||||
|
||||
bool ParamStoreEnvironment::comparePiece(const VarnodeData &vData,ParameterPieces &piece)
|
||||
|
||||
{
|
||||
if (vData.space == (AddrSpace *)0) {
|
||||
return (piece.type->getMetatype() == TYPE_VOID);
|
||||
}
|
||||
if (vData.space != piece.addr.getSpace())
|
||||
return false;
|
||||
if (vData.offset != piece.addr.getOffset())
|
||||
return false;
|
||||
if (vData.size != piece.type->getSize())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
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"));
|
||||
}
|
||||
|
||||
TEST(paramstore_aarch64_cdecl) {
|
||||
ProtoModel *model = theEnviron.getModel("AARCH64:LE:64:v8A:default","__cdecl");
|
||||
ASSERT(theEnviron.test(model, "void func(int2 a,int4 b,int1 c);", "void,w0:2,w1,w2:1"));
|
||||
ASSERT(theEnviron.test(model, "void func(int4, int4);", "void,w0,w1"));
|
||||
ASSERT(theEnviron.test(model, "void func(int8,int8);", "void,x0,x1"));
|
||||
ASSERT(theEnviron.test(model, "void func(float4,float4);", "void,s0,s1"));
|
||||
ASSERT(theEnviron.test(model, "void func(float8,float8);", "void,d0,d1"));
|
||||
ASSERT(theEnviron.test(model, "void func(int4,float4,int4,float4);", "void,w0,s0,w1,s1"));
|
||||
ASSERT(theEnviron.test(model, "void func(float4,int4,float4,int4);", "void,s0,w0,s1,w1"));
|
||||
ASSERT(theEnviron.test(model, "void func(int4,float8,float8,int4);", "void,w0,d0,d1,w1"));
|
||||
ASSERT(theEnviron.test(model, "void func(float8,int8,int8,float8);", "void,d0,x0,x1,d1"));
|
||||
ASSERT(theEnviron.test(model, "void func(float16);", "void,q0"));
|
||||
ASSERT(theEnviron.test(model, "void func(float4,float16);", "void,s0,q1"));
|
||||
ASSERT(theEnviron.test(model, "void func(int4,int4,int4,int4,int4,int4,int4,int4,int4,int4);",
|
||||
"void,w0,w1,w2,w3,w4,w5,w6,w7,stack0:4,stack8:4"));
|
||||
ASSERT(theEnviron.test(model, "void func(float4,float4,float4,float4,float4,float4,float4,float4,float4,float4);",
|
||||
"void,s0,s1,s2,s3,s4,s5,s6,s7,stack0:4,stack8:4"));
|
||||
ASSERT(theEnviron.test(model, "void func(float4,float4,float4,float4,float4,float4,float4,float4,float16);",
|
||||
"void,s0,s1,s2,s3,s4,s5,s6,s7,stack0:16"));
|
||||
ASSERT(theEnviron.test(model, "void func(float4,float4,float4,float4,float4,float4,float4,float4,float4,float16);",
|
||||
"void,s0,s1,s2,s3,s4,s5,s6,s7,stack0:4,stack10:16"));
|
||||
ASSERT(theEnviron.test(model, "void func(int4,int4,int4,int4,int4,int4,int4,int4,int4,float4);",
|
||||
"void,w0,w1,w2,w3,w4,w5,w6,w7,stack0:4,s0"));
|
||||
ASSERT(theEnviron.test(model, "void func(float4,float4,float4,float4,float4,float4,float4,float4,float4,int4);",
|
||||
"void,s0,s1,s2,s3,s4,s5,s6,s7,stack0:4,w0"));
|
||||
|
||||
theEnviron.parseType(model,"struct intpair { int4 a; int4 b;};");
|
||||
ASSERT(theEnviron.test(model, "void func(intpair);", "void,x0"));
|
||||
|
||||
theEnviron.parseType(model,"struct longpair { int8 a; int8 b; };");
|
||||
ASSERT(theEnviron.test(model, "void func(longpair);", "void,join x1 x0"));
|
||||
|
||||
theEnviron.parseType(model,"struct longquad { int8 a; int8 b; int8 c; int8 d; };");
|
||||
ASSERT(theEnviron.test(model, "void func(longquad);", "void,x0"));
|
||||
|
||||
theEnviron.parseType(model,"struct floatdouble { float4 a; float8 b; };");
|
||||
ASSERT(theEnviron.test(model, "void func(floatdouble);", "void,join x1 x0"));
|
||||
|
||||
theEnviron.parseType(model,"struct intfloat { int4 a; float4 b; };");
|
||||
ASSERT(theEnviron.test(model, "void func(intfloat);", "void,x0"));
|
||||
|
||||
theEnviron.parseType(model,"struct longdoublestruct { int8 a; float8 b; };");
|
||||
ASSERT(theEnviron.test(model, "void func(longdoublestruct);", "void,join x1 x0"));
|
||||
|
||||
ASSERT(theEnviron.test(model, "int4 func(void);", "w0"));
|
||||
ASSERT(theEnviron.test(model, "float4 func(void);", "s0"));
|
||||
ASSERT(theEnviron.test(model, "float8 func(void);", "d0"));
|
||||
|
||||
ASSERT(theEnviron.test(model, "intpair func(void);", "x0"));
|
||||
ASSERT(theEnviron.test(model, "longpair func(void);", "join x1 x0"));
|
||||
ASSERT(theEnviron.test(model, "longquad func(void);", "void,x8"));
|
||||
|
||||
theEnviron.parseType(model,"struct floatpair { float4 a; float4 b; };");
|
||||
ASSERT(theEnviron.test(model, "void func(floatpair);", "void,join s1 s0"));
|
||||
|
||||
theEnviron.parseType(model,"struct floatpairpair { floatpair a; floatpair b; };");
|
||||
ASSERT(theEnviron.test(model, "void func(floatpairpair);", "void,join s3 s2 s1 s0"));
|
||||
|
||||
theEnviron.parseType(model,"struct doublequad { float8 a; float8 b; float8 c; float8 d; };");
|
||||
ASSERT(theEnviron.test(model, "void func(doublequad);", "void,join d3 d2 d1 d0"));
|
||||
}
|
||||
|
||||
}
|
|
@ -290,7 +290,8 @@
|
|||
<optional> <attribute name="maxsize"/> </optional>
|
||||
<optional> <attribute name="minsize"/> </optional>
|
||||
<optional> <attribute name="align"/> </optional>
|
||||
<optional> <attribute name="metatype"/> </optional>
|
||||
<optional> <attribute name="storage"/> </optional>
|
||||
<optional> <attribute name="metatype"/> </optional> <!-- deprecated -->
|
||||
<optional> <attribute name="extension"/> </optional>
|
||||
<!--
|
||||
THIS IS THE WEAK ADDR TYPE. Attribute size is not required.
|
||||
|
@ -341,6 +342,81 @@
|
|||
</choice>
|
||||
</define>
|
||||
|
||||
<define name="model_datatype_type">
|
||||
<attribute name="name"/>
|
||||
<optional><attribute name="minsize"/></optional>
|
||||
<optional><attribute name="maxsize"/></optional>
|
||||
</define>
|
||||
|
||||
<define name="modelrule_type">
|
||||
<element name="datatype">
|
||||
<ref name="model_datatype_type"/>
|
||||
</element>
|
||||
<interleave>
|
||||
<optional>
|
||||
<element name="varargs">
|
||||
<empty/>
|
||||
</element>
|
||||
</optional>
|
||||
<optional>
|
||||
<element name="position">
|
||||
<attribute name="index"/>
|
||||
</element>
|
||||
</optional>
|
||||
<zeroOrMore>
|
||||
<element name="datatype_at">
|
||||
<attribute name="index"/>
|
||||
<element name="datatype">
|
||||
<ref name="model_datatype_type"/>
|
||||
</element>
|
||||
</element>
|
||||
</zeroOrMore>
|
||||
</interleave>
|
||||
<choice>
|
||||
<element name="consume">
|
||||
<attribute name="storage"/>
|
||||
</element>
|
||||
<element name="convert_to_ptr">
|
||||
<empty/>
|
||||
</element>
|
||||
<element name="goto_stack">
|
||||
<empty/>
|
||||
</element>
|
||||
<element name="join">
|
||||
<optional>
|
||||
<attribute name="reversejustify">
|
||||
<ref name="boolean_type"/>
|
||||
</attribute>
|
||||
</optional>
|
||||
<optional>
|
||||
<attribute name="align">
|
||||
<ref name="boolean_type"/>
|
||||
</attribute>
|
||||
</optional>
|
||||
<optional>
|
||||
<attribute name="storage"/>
|
||||
</optional>
|
||||
</element>
|
||||
<element name="hidden_return">
|
||||
<optional>
|
||||
<attribute name="voidlock">
|
||||
<ref name="boolean_type"/>
|
||||
</attribute>
|
||||
</optional>
|
||||
</element>
|
||||
<element name="join_per_primitive">
|
||||
<optional>
|
||||
<attribute name="storage"/>
|
||||
</optional>
|
||||
</element>
|
||||
</choice>
|
||||
<zeroOrMore>
|
||||
<element name="consume_extra">
|
||||
<attribute name="storage"/>
|
||||
</element>
|
||||
</zeroOrMore>
|
||||
</define>
|
||||
|
||||
<define name="prototype_type">
|
||||
<attribute name="extrapop"/>
|
||||
<attribute name="stackshift"/>
|
||||
|
@ -377,6 +453,11 @@
|
|||
</element>
|
||||
</zeroOrMore>
|
||||
</interleave>
|
||||
<zeroOrMore>
|
||||
<element name="rule">
|
||||
<ref name="modelrule_type"/>
|
||||
</element>
|
||||
</zeroOrMore>
|
||||
</element>
|
||||
|
||||
<element name="output">
|
||||
|
@ -386,6 +467,11 @@
|
|||
<ref name="pentry_type"/>
|
||||
</element>
|
||||
</zeroOrMore>
|
||||
<zeroOrMore>
|
||||
<element name="rule">
|
||||
<ref name="modelrule_type"/>
|
||||
</element>
|
||||
</zeroOrMore>
|
||||
</element>
|
||||
|
||||
<!--
|
||||
|
|
|
@ -22,7 +22,6 @@ import java.io.IOException;
|
|||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import ghidra.app.plugin.processors.sleigh.VarnodeData;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.data.*;
|
||||
|
@ -44,12 +43,8 @@ public class ParamEntry {
|
|||
private static final int IS_GROUPED = 512; // The entry is grouped with other entries
|
||||
private static final int OVERLAPPING = 0x100; // This overlaps an earlier entry
|
||||
|
||||
public static final int TYPE_UNKNOWN = 8; // Default type restriction
|
||||
public static final int TYPE_PTR = 2; // pointer types
|
||||
public static final int TYPE_FLOAT = 3; // floating point types
|
||||
|
||||
private int flags;
|
||||
private int type; // Restriction on DataType this entry must match
|
||||
private StorageClass type; // Restriction on DataType this entry must match
|
||||
private int[] groupSet; // Group(s) this entry belongs to
|
||||
private AddressSpace spaceid; // Space of this range
|
||||
private long addressbase; // Start of the range
|
||||
|
@ -88,7 +83,7 @@ public class ParamEntry {
|
|||
return addressbase;
|
||||
}
|
||||
|
||||
public int getType() {
|
||||
public StorageClass getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
|
@ -112,10 +107,6 @@ public class ParamEntry {
|
|||
return ((flags & IS_BIG_ENDIAN) != 0);
|
||||
}
|
||||
|
||||
public boolean isFloatExtended() {
|
||||
return ((flags & SMALLSIZE_FLOAT) != 0);
|
||||
}
|
||||
|
||||
private boolean isLeftJustified() {
|
||||
return (((flags & IS_BIG_ENDIAN) == 0) || ((flags & FORCE_LEFT_JUSTIFY) != 0));
|
||||
}
|
||||
|
@ -131,7 +122,7 @@ public class ParamEntry {
|
|||
* @param sz is the given size
|
||||
* @return the collected array of Varnodes or null
|
||||
*/
|
||||
public Varnode[] getJoinPieces(int sz) {
|
||||
private Varnode[] getJoinPieces(int sz) {
|
||||
int num = 0;
|
||||
int first, replace;
|
||||
Varnode vn = null;
|
||||
|
@ -337,18 +328,19 @@ public class ParamEntry {
|
|||
}
|
||||
|
||||
/**
|
||||
* Return the storage address assigned when allocating something of size -sz- assuming -slotnum- slots
|
||||
* have already been assigned. Set res.space to null if the -sz- is too small or if
|
||||
* Assign the storage address when allocating something of size -sz- assuming -slotnum- slots
|
||||
* have already been assigned. Set the address to null if the -sz- is too small or if
|
||||
* there are not enough slots left
|
||||
* @param slotnum number of slots already assigned
|
||||
* @param sz number of bytes to being assigned
|
||||
* @param typeAlign required byte alignment for the parameter
|
||||
* @param res the final storage address
|
||||
* @param res will hold the final storage address
|
||||
* @return slotnum plus the number of slots used
|
||||
*/
|
||||
public int getAddrBySlot(int slotnum, int sz, int typeAlign, VarnodeData res) {
|
||||
res.space = null; // Start with an invalid result
|
||||
public int getAddrBySlot(int slotnum, int sz, int typeAlign, ParameterPieces res) {
|
||||
int spaceused;
|
||||
long offset;
|
||||
res.address = null; // Start with an invalid result
|
||||
if (sz < minsize) {
|
||||
return slotnum;
|
||||
}
|
||||
|
@ -359,10 +351,12 @@ public class ParamEntry {
|
|||
if (sz > size) {
|
||||
return slotnum; // Check on maximum size
|
||||
}
|
||||
res.space = spaceid;
|
||||
res.offset = addressbase; // Get base address of the slot
|
||||
offset = addressbase; // Get base address of the slot
|
||||
spaceused = size;
|
||||
if ((flags & SMALLSIZE_FLOAT) != 0) {
|
||||
if ((flags & SMALLSIZE_FLOAT) != 0 && sz != size) {
|
||||
res.address = spaceid.getAddress(offset);
|
||||
res.joinPieces = new Varnode[1];
|
||||
res.joinPieces[0] = new Varnode(res.address, size);
|
||||
return slotnum;
|
||||
}
|
||||
}
|
||||
|
@ -390,13 +384,17 @@ public class ParamEntry {
|
|||
else {
|
||||
index = slotnum;
|
||||
}
|
||||
res.space = spaceid;
|
||||
res.offset = addressbase + index * alignment;
|
||||
offset = addressbase + index * alignment;
|
||||
slotnum += slotsused; // Inform caller of number of slots used
|
||||
}
|
||||
if (!isLeftJustified()) {
|
||||
res.offset += (spaceused - sz);
|
||||
offset += (spaceused - sz);
|
||||
}
|
||||
res.address = spaceid.getAddress(offset);
|
||||
if (res.address.getAddressSpace().getType() == AddressSpace.TYPE_JOIN) {
|
||||
res.joinPieces = getJoinPieces(sz);
|
||||
}
|
||||
|
||||
return slotnum;
|
||||
}
|
||||
|
||||
|
@ -496,9 +494,8 @@ public class ParamEntry {
|
|||
if (alignment != 0) {
|
||||
encoder.writeSignedInteger(ATTRIB_ALIGN, alignment);
|
||||
}
|
||||
if (type == TYPE_FLOAT || type == TYPE_PTR) {
|
||||
String tok = (type == TYPE_FLOAT) ? "float" : "ptr";
|
||||
encoder.writeString(ATTRIB_METATYPE, tok);
|
||||
if (type != StorageClass.GENERAL) {
|
||||
encoder.writeString(ATTRIB_STORAGE, type.toString());
|
||||
}
|
||||
String extString = null;
|
||||
if ((flags & SMALLSIZE_SEXT) != 0) {
|
||||
|
@ -531,41 +528,32 @@ public class ParamEntry {
|
|||
public void restoreXml(XmlPullParser parser, CompilerSpec cspec, List<ParamEntry> curList,
|
||||
boolean grouped) throws XmlParseException {
|
||||
flags = 0;
|
||||
type = TYPE_UNKNOWN;
|
||||
type = StorageClass.GENERAL;
|
||||
size = minsize = -1; // Must be filled in
|
||||
alignment = 0; // default
|
||||
numslots = 1;
|
||||
|
||||
XmlElement el = parser.start("pentry");
|
||||
XmlElement el = parser.start(ELEM_PENTRY.name());
|
||||
Iterator<Entry<String, String>> iter = el.getAttributes().entrySet().iterator();
|
||||
while (iter.hasNext()) {
|
||||
Entry<String, String> entry = iter.next();
|
||||
String name = entry.getKey();
|
||||
if (name.equals("minsize")) {
|
||||
if (name.equals(ATTRIB_MINSIZE.name())) {
|
||||
minsize = SpecXmlUtils.decodeInt(entry.getValue());
|
||||
}
|
||||
else if (name.equals("size")) { // old style
|
||||
else if (name.equals(ATTRIB_SIZE.name())) { // old style
|
||||
alignment = SpecXmlUtils.decodeInt(entry.getValue());
|
||||
}
|
||||
else if (name.equals("align")) {
|
||||
else if (name.equals(ATTRIB_ALIGN.name())) {
|
||||
alignment = SpecXmlUtils.decodeInt(entry.getValue());
|
||||
}
|
||||
else if (name.equals("maxsize")) {
|
||||
else if (name.equals(ATTRIB_MAXSIZE.name())) {
|
||||
size = SpecXmlUtils.decodeInt(entry.getValue());
|
||||
}
|
||||
else if (name.equals("metatype")) { // Not implemented at the moment
|
||||
String meta = entry.getValue();
|
||||
// TODO: Currently only supporting "float", "ptr", and "unknown" metatypes
|
||||
if ((meta != null)) {
|
||||
if (meta.equals("float")) {
|
||||
type = TYPE_FLOAT;
|
||||
}
|
||||
else if (meta.equals("ptr")) {
|
||||
type = TYPE_PTR;
|
||||
}
|
||||
}
|
||||
else if (name.equals(ATTRIB_STORAGE.name()) || name.equals(ATTRIB_METATYPE.name())) {
|
||||
type = StorageClass.getClass(entry.getValue());
|
||||
}
|
||||
else if (name.equals("extension")) {
|
||||
else if (name.equals(ATTRIB_EXTENSION.name())) {
|
||||
flags &= ~(SMALLSIZE_ZEXT | SMALLSIZE_SEXT | SMALLSIZE_INTTYPE | SMALLSIZE_FLOAT);
|
||||
String value = entry.getValue();
|
||||
if (value.equals("sign")) {
|
||||
|
@ -702,18 +690,17 @@ public class ParamEntry {
|
|||
return (int) (offset2 - offset1);
|
||||
}
|
||||
|
||||
public static int getMetatype(DataType tp) {
|
||||
// TODO: A complete metatype implementation
|
||||
public static StorageClass getBasicTypeClass(DataType tp) {
|
||||
if (tp instanceof TypeDef) {
|
||||
tp = ((TypeDef) tp).getBaseDataType();
|
||||
}
|
||||
if (tp instanceof AbstractFloatDataType) {
|
||||
return TYPE_FLOAT;
|
||||
return StorageClass.FLOAT;
|
||||
}
|
||||
if (tp instanceof Pointer) {
|
||||
return TYPE_PTR;
|
||||
return StorageClass.PTR;
|
||||
}
|
||||
return TYPE_UNKNOWN;
|
||||
return StorageClass.GENERAL;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -728,7 +715,7 @@ public class ParamEntry {
|
|||
return;
|
||||
}
|
||||
if (entry1.type != entry2.type) {
|
||||
if (entry1.type == TYPE_UNKNOWN) {
|
||||
if (entry1.type == StorageClass.GENERAL) {
|
||||
throw new XmlParseException(
|
||||
"<pentry> tags with a specific type must come before the general type");
|
||||
}
|
||||
|
|
|
@ -19,7 +19,8 @@ import java.io.IOException;
|
|||
import java.util.ArrayList;
|
||||
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.data.DataTypeManager;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.listing.VariableStorage;
|
||||
import ghidra.program.model.pcode.Encoder;
|
||||
|
@ -37,14 +38,14 @@ public interface ParamList {
|
|||
}
|
||||
|
||||
/**
|
||||
* Given a list of datatypes, calculate the storage locations used for passing those datatypes
|
||||
* @param prog is the active program
|
||||
* Given a list of datatypes, calculate the storage locations used for passing those data-types
|
||||
* @param proto is the list of datatypes
|
||||
* @param res is the vector for holding the VariableStorage corresponding to datatypes
|
||||
* @param dtManage is the data-type manager
|
||||
* @param res is the vector for holding the storage locations and other parameter properties
|
||||
* @param addAutoParams if true add/process auto-parameters
|
||||
*/
|
||||
public void assignMap(Program prog, DataType[] proto, ArrayList<VariableStorage> res,
|
||||
boolean addAutoParams);
|
||||
public void assignMap(PrototypePieces proto, DataTypeManager dtManage,
|
||||
ArrayList<ParameterPieces> res, boolean addAutoParams);
|
||||
|
||||
public void encode(Encoder encoder, boolean isInput) throws IOException;
|
||||
|
||||
|
@ -80,6 +81,21 @@ public interface ParamList {
|
|||
*/
|
||||
public boolean possibleParamWithSlot(Address loc, int size, WithSlotRec res);
|
||||
|
||||
/**
|
||||
* Get the address space associated with any stack based parameters in this list.
|
||||
*
|
||||
* @return the stack address space, if this models parameters passed on the stack, null otherwise
|
||||
*/
|
||||
public AddressSpace getSpacebase();
|
||||
|
||||
/**
|
||||
* Return true if the this pointer occurs before an indirect return pointer
|
||||
*
|
||||
* The automatic parameters: this parameter and the hidden return value pointer both
|
||||
* tend to be allocated from the initial general purpose registers reserved for parameter passing.
|
||||
* This method returns true if the this parameter is allocated first.
|
||||
* @return false if the hidden return value pointer is allocated first
|
||||
*/
|
||||
public boolean isThisBeforeRetPointer();
|
||||
|
||||
/**
|
||||
|
|
|
@ -17,9 +17,8 @@ package ghidra.program.model.lang;
|
|||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.listing.VariableStorage;
|
||||
import ghidra.program.model.data.DataTypeManager;
|
||||
import ghidra.program.model.data.VoidDataType;
|
||||
|
||||
/**
|
||||
* A list of resources describing possible storage locations for a function's return value,
|
||||
|
@ -28,17 +27,22 @@ import ghidra.program.model.listing.VariableStorage;
|
|||
* The assignment strategy for this class is to take the first storage location in the list
|
||||
* that fits for the given function signature's return data-type.
|
||||
*/
|
||||
public class ParamListRegisterOut extends ParamListStandard {
|
||||
public class ParamListRegisterOut extends ParamListStandardOut {
|
||||
|
||||
@Override
|
||||
public void assignMap(Program prog, DataType[] proto, ArrayList<VariableStorage> res,
|
||||
boolean addAutoParams) {
|
||||
public void assignMap(PrototypePieces proto, DataTypeManager dtManager,
|
||||
ArrayList<ParameterPieces> res, boolean addAutoParams) {
|
||||
int[] status = new int[numgroup];
|
||||
for (int i = 0; i < numgroup; ++i) {
|
||||
status[i] = 0;
|
||||
}
|
||||
VariableStorage store = assignAddress(prog, proto[0], status, false, false);
|
||||
ParameterPieces store = new ParameterPieces();
|
||||
res.add(store);
|
||||
if (VoidDataType.isVoidDataType(proto.outtype)) {
|
||||
store.type = proto.outtype;
|
||||
return; // Don't assign storage for VOID
|
||||
}
|
||||
assignAddress(proto.outtype, proto, -1, dtManager, status, store);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -21,13 +21,14 @@ import static ghidra.program.model.pcode.ElementId.*;
|
|||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import ghidra.app.plugin.processors.sleigh.VarnodeData;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.data.DataTypeManager;
|
||||
import ghidra.program.model.lang.protorules.*;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.listing.VariableStorage;
|
||||
import ghidra.program.model.pcode.Encoder;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
import ghidra.util.SystemUtilities;
|
||||
import ghidra.util.exception.InvalidInputException;
|
||||
import ghidra.util.xml.SpecXmlUtils;
|
||||
|
@ -41,11 +42,11 @@ public class ParamListStandard implements ParamList {
|
|||
|
||||
protected int numgroup; // Number of "groups" in this parameter convention
|
||||
// protected int maxdelay;
|
||||
protected int pointermax; // If non-zero, maximum size of a datatype before converting to a pointer
|
||||
protected boolean thisbeforeret; // Do hidden return pointers usurp the storage of the this pointer
|
||||
protected boolean splitMetatype; // Are metatyped entries in separate resource sections
|
||||
// protected int[] resourceStart; // The starting group for each resource section
|
||||
protected ParamEntry[] entry;
|
||||
protected ModelRule[] modelRules; // Rules to apply when assigning addresses
|
||||
protected AddressSpace spacebase; // Space containing relative offset parameters
|
||||
|
||||
/**
|
||||
|
@ -67,40 +68,37 @@ public class ParamListStandard implements ParamList {
|
|||
}
|
||||
|
||||
/**
|
||||
* Assign next available memory chunk to type
|
||||
* @param program is the Program
|
||||
* @param tp type being assigned storage
|
||||
* @param status status from previous assignments
|
||||
* @param ishiddenret is true if the parameter is a hidden return value
|
||||
* @param isindirect is true if parameter is really a pointer to the real parameter value
|
||||
* @return Address of assigned memory chunk
|
||||
* Assign storage for given parameter class, using the fallback assignment algorithm
|
||||
*
|
||||
* Given a resource list, a data-type, and the status of previously allocated slots,
|
||||
* select the storage location for the parameter. The status array is
|
||||
* indexed by group: a positive value indicates how many slots have been allocated
|
||||
* from that group, and a -1 indicates the group/resource is fully consumed.
|
||||
* If an Address can be assigned to the parameter, it and other details are passed back in the
|
||||
* ParameterPieces object and the SUCCESS code is returned. Otherwise, the FAIL code is returned.
|
||||
* @param resource is the resource list to allocate from
|
||||
* @param tp is the data-type of the parameter
|
||||
* @param matchExact is false if TYPECLASS_GENERAL is considered a match for any storage class
|
||||
* @param status is an array marking how many slots have already been consumed in a group
|
||||
* @param param will hold the address and other details of the assigned parameter
|
||||
* @return either SUCCESS or FAIL
|
||||
*/
|
||||
protected VariableStorage assignAddress(Program program, DataType tp, int[] status,
|
||||
boolean ishiddenret, boolean isindirect) {
|
||||
if (tp == null) {
|
||||
tp = DataType.DEFAULT;
|
||||
}
|
||||
if (VoidDataType.isVoidDataType(tp)) {
|
||||
return VariableStorage.VOID_STORAGE;
|
||||
}
|
||||
int sz = tp.getLength();
|
||||
if (sz == 0) {
|
||||
return VariableStorage.UNASSIGNED_STORAGE;
|
||||
}
|
||||
public int assignAddressFallback(StorageClass resource, DataType tp, boolean matchExact,
|
||||
int[] status, ParameterPieces param) {
|
||||
for (ParamEntry element : entry) {
|
||||
int grp = element.getGroup();
|
||||
if (status[grp] < 0) {
|
||||
continue;
|
||||
}
|
||||
if ((element.getType() != ParamEntry.TYPE_UNKNOWN) &&
|
||||
(ParamEntry.getMetatype(tp) != element.getType())) {
|
||||
continue; // Wrong type
|
||||
if (resource != element.getType()) {
|
||||
if (matchExact || element.getType() != StorageClass.GENERAL) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
VarnodeData res = new VarnodeData();
|
||||
status[grp] =
|
||||
element.getAddrBySlot(status[grp], tp.getAlignedLength(), tp.getAlignment(), res);
|
||||
if (res.space == null) {
|
||||
element.getAddrBySlot(status[grp], tp.getAlignedLength(), tp.getAlignment(), param);
|
||||
if (param.address == null) {
|
||||
continue; // -tp- does not fit in this entry
|
||||
}
|
||||
if (element.isExclusion()) {
|
||||
|
@ -108,72 +106,79 @@ public class ParamListStandard implements ParamList {
|
|||
// For an exclusion entry
|
||||
status[group] = -1; // some number of groups are taken up
|
||||
}
|
||||
if (element.isFloatExtended()) {
|
||||
sz = element.getSize(); // Still use the entire container size, when assigning storage
|
||||
}
|
||||
}
|
||||
VariableStorage store;
|
||||
try {
|
||||
if (res.space.getType() == AddressSpace.TYPE_JOIN) {
|
||||
Varnode[] pieces = element.getJoinPieces(sz);
|
||||
if (pieces != null) {
|
||||
store = new DynamicVariableStorage(program, false, pieces);
|
||||
}
|
||||
else {
|
||||
store = DynamicVariableStorage.getUnassignedDynamicStorage(false);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Address addr = res.space.getAddress(res.offset);
|
||||
if (ishiddenret) {
|
||||
store = new DynamicVariableStorage(program,
|
||||
AutoParameterType.RETURN_STORAGE_PTR, addr, sz);
|
||||
}
|
||||
else if (isindirect) {
|
||||
store = new DynamicVariableStorage(program, true, addr, sz);
|
||||
}
|
||||
else {
|
||||
store = new DynamicVariableStorage(program, false, addr, sz);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (InvalidInputException e) {
|
||||
break;
|
||||
}
|
||||
return store;
|
||||
param.type = tp;
|
||||
return AssignAction.SUCCESS;
|
||||
}
|
||||
if (ishiddenret) {
|
||||
return DynamicVariableStorage
|
||||
.getUnassignedDynamicStorage(AutoParameterType.RETURN_STORAGE_PTR);
|
||||
param.address = null;
|
||||
return AssignAction.FAIL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill in the Address and other details for the given parameter
|
||||
*
|
||||
* Attempt to apply a ModelRule first. If these do not succeed, use the fallback assignment algorithm.
|
||||
* @param dt is the data-type assigned to the parameter
|
||||
* @param proto is the description of the function prototype
|
||||
* @param pos is the position of the parameter to assign (pos=-1 for output, pos >=0 for input)
|
||||
* @param dtManager is the data-type manager for (possibly) transforming the parameter's data-type
|
||||
* @param status is the consumed resource status array
|
||||
* @param res is parameter description to be filled in
|
||||
* @return the response code
|
||||
*/
|
||||
public int assignAddress(DataType dt, PrototypePieces proto, int pos, DataTypeManager dtManager,
|
||||
int[] status, ParameterPieces res)
|
||||
|
||||
{
|
||||
for (ModelRule modelRule : modelRules) {
|
||||
int responseCode = modelRule.assignAddress(dt, proto, pos, dtManager, status, res);
|
||||
if (responseCode != AssignAction.FAIL) {
|
||||
return responseCode;
|
||||
}
|
||||
}
|
||||
return DynamicVariableStorage.getUnassignedDynamicStorage(isindirect);
|
||||
StorageClass store = ParamEntry.getBasicTypeClass(dt);
|
||||
return assignAddressFallback(store, dt, false, status, res);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the number of ParamEntry objets in this list
|
||||
*/
|
||||
public int getNumParamEntry() {
|
||||
return entry.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Within this list, get the ParamEntry at the given index
|
||||
* @param index is the given index
|
||||
* @return the selected ParamEntry
|
||||
*/
|
||||
public ParamEntry getEntry(int index) {
|
||||
return entry[index];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void assignMap(Program prog, DataType[] proto, ArrayList<VariableStorage> res,
|
||||
boolean addAutoParams) {
|
||||
public void assignMap(PrototypePieces proto, DataTypeManager dtManager,
|
||||
ArrayList<ParameterPieces> res, boolean addAutoParams) {
|
||||
int[] status = new int[numgroup];
|
||||
for (int i = 0; i < numgroup; ++i) {
|
||||
status[i] = 0;
|
||||
}
|
||||
|
||||
if (addAutoParams && res.size() == 2) { // Check for hidden parameters defined by the output list
|
||||
DataTypeManager dtm = prog.getDataTypeManager();
|
||||
Pointer pointer = dtm.getPointer(proto[0]);
|
||||
VariableStorage store = assignAddress(prog, pointer, status, true, false);
|
||||
res.set(1, store);
|
||||
}
|
||||
for (int i = 1; i < proto.length; ++i) {
|
||||
VariableStorage store;
|
||||
if ((pointermax != 0) && (proto[i] != null) && (proto[i].getLength() > pointermax)) { // DataType is too big
|
||||
// Assume datatype is stored elsewhere and only the pointer is passed
|
||||
DataTypeManager dtm = prog.getDataTypeManager();
|
||||
Pointer pointer = dtm.getPointer(proto[i]);
|
||||
store = assignAddress(prog, pointer, status, false, true);
|
||||
ParameterPieces last = res.get(res.size() - 1);
|
||||
StorageClass store;
|
||||
if (last.hiddenReturnPtr) {
|
||||
store = StorageClass.HIDDENRET;
|
||||
}
|
||||
else {
|
||||
store = assignAddress(prog, proto[i], status, false, false);
|
||||
store = ParamEntry.getBasicTypeClass(last.type);
|
||||
}
|
||||
assignAddressFallback(store, last.type, false, status, last);
|
||||
last.hiddenReturnPtr = true;
|
||||
}
|
||||
for (int i = 0; i < proto.intypes.size(); ++i) {
|
||||
ParameterPieces store = new ParameterPieces();
|
||||
assignAddress(proto.intypes.get(i), proto, i, dtManager, status, store);
|
||||
res.add(store);
|
||||
}
|
||||
}
|
||||
|
@ -208,9 +213,6 @@ public class ParamListStandard implements ParamList {
|
|||
@Override
|
||||
public void encode(Encoder encoder, boolean isInput) throws IOException {
|
||||
encoder.openElement(isInput ? ELEM_INPUT : ELEM_OUTPUT);
|
||||
if (pointermax != 0) {
|
||||
encoder.writeSignedInteger(ATTRIB_POINTERMAX, pointermax);
|
||||
}
|
||||
if (thisbeforeret) {
|
||||
encoder.writeBool(ATTRIB_THISBEFORERETPOINTER, true);
|
||||
}
|
||||
|
@ -236,25 +238,28 @@ public class ParamListStandard implements ParamList {
|
|||
if (curgroup >= 0) {
|
||||
encoder.closeElement(ELEM_GROUP);
|
||||
}
|
||||
for (ModelRule modelRule : modelRules) {
|
||||
modelRule.encode(encoder);
|
||||
}
|
||||
encoder.closeElement(isInput ? ELEM_INPUT : ELEM_OUTPUT);
|
||||
}
|
||||
|
||||
private void parsePentry(XmlPullParser parser, CompilerSpec cspec, ArrayList<ParamEntry> pe,
|
||||
int groupid, boolean splitFloat, boolean grouped) throws XmlParseException {
|
||||
int lastMeta = -1; // Smaller than any real metatype
|
||||
StorageClass lastClass = StorageClass.CLASS4;
|
||||
if (!pe.isEmpty()) {
|
||||
ParamEntry lastEntry = pe.get(pe.size() - 1);
|
||||
lastMeta = lastEntry.isGrouped() ? ParamEntry.TYPE_UNKNOWN : lastEntry.getType();
|
||||
lastClass = lastEntry.isGrouped() ? StorageClass.GENERAL : lastEntry.getType();
|
||||
}
|
||||
ParamEntry pentry = new ParamEntry(groupid);
|
||||
pe.add(pentry);
|
||||
pentry.restoreXml(parser, cspec, pe, grouped);
|
||||
if (splitFloat) {
|
||||
int currentMeta = grouped ? ParamEntry.TYPE_UNKNOWN : pentry.getType();
|
||||
if (lastMeta != currentMeta) {
|
||||
if (lastMeta > currentMeta) {
|
||||
StorageClass currentClass = grouped ? StorageClass.GENERAL : pentry.getType();
|
||||
if (lastClass != currentClass) {
|
||||
if (lastClass.getValue() < currentClass.getValue()) {
|
||||
throw new XmlParseException(
|
||||
"parameter list entries must be ordered by metatype");
|
||||
"parameter list entries must be ordered by storage class");
|
||||
}
|
||||
// int[] newResourceStart = new int[resourceStart.length + 1];
|
||||
// System.arraycopy(resourceStart, 0, newResourceStart, 0, resourceStart.length);
|
||||
|
@ -301,7 +306,7 @@ public class ParamListStandard implements ParamList {
|
|||
ArrayList<ParamEntry> pe = new ArrayList<>();
|
||||
numgroup = 0;
|
||||
spacebase = null;
|
||||
pointermax = 0;
|
||||
int pointermax = 0;
|
||||
thisbeforeret = false;
|
||||
splitMetatype = true;
|
||||
XmlElement mainel = parser.start();
|
||||
|
@ -329,14 +334,47 @@ public class ParamListStandard implements ParamList {
|
|||
else if (el.getName().equals("group")) {
|
||||
parseGroup(parser, cspec, pe, numgroup, splitMetatype);
|
||||
}
|
||||
else if (el.getName().equals("rule")) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
parser.end(mainel);
|
||||
entry = new ParamEntry[pe.size()];
|
||||
pe.toArray(entry);
|
||||
|
||||
ArrayList<ModelRule> rules = new ArrayList<>();
|
||||
for (;;) {
|
||||
XmlElement subId = parser.peek();
|
||||
if (!subId.isStart()) {
|
||||
break;
|
||||
}
|
||||
if (subId.getName().equals("rule")) {
|
||||
ModelRule rule = new ModelRule();
|
||||
rule.restoreXml(parser, this);
|
||||
rules.add(rule);
|
||||
}
|
||||
else {
|
||||
throw new XmlParseException(
|
||||
"<pentry> and <group> elements must come before any <modelrule>");
|
||||
}
|
||||
}
|
||||
|
||||
parser.end(mainel);
|
||||
// int[] newResourceStart = new int[resourceStart.length + 1];
|
||||
// System.arraycopy(resourceStart, 0, newResourceStart, 0, resourceStart.length);
|
||||
// newResourceStart[resourceStart.length] = numgroup;
|
||||
// resourceStart = newResourceStart;
|
||||
if (pointermax > 0) { // Add a ModelRule at the end that converts too big data-types to pointers
|
||||
SizeRestrictedFilter typeFilter = new SizeRestrictedFilter(pointermax + 1, 0);
|
||||
ConvertToPointer action = new ConvertToPointer(this);
|
||||
try {
|
||||
rules.add(new ModelRule(typeFilter, action, this));
|
||||
}
|
||||
catch (InvalidInputException e) {
|
||||
throw new XmlParseException(e.getMessage());
|
||||
}
|
||||
}
|
||||
modelRules = new ModelRule[rules.size()];
|
||||
rules.toArray(modelRules);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -389,6 +427,11 @@ public class ParamListStandard implements ParamList {
|
|||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressSpace getSpacebase() {
|
||||
return spacebase;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEquivalent(ParamList obj) {
|
||||
if (this.getClass() != obj.getClass()) {
|
||||
|
@ -403,7 +446,15 @@ public class ParamListStandard implements ParamList {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
if (numgroup != op2.numgroup || pointermax != op2.pointermax) {
|
||||
if (modelRules.length != op2.modelRules.length) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < modelRules.length; ++i) {
|
||||
if (!modelRules[i].isEquivalent(op2.modelRules[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (numgroup != op2.numgroup) {
|
||||
return false;
|
||||
}
|
||||
if (!SystemUtilities.isEqual(spacebase, op2.spacebase)) {
|
||||
|
|
|
@ -17,12 +17,8 @@ package ghidra.program.model.lang;
|
|||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.listing.VariableStorage;
|
||||
import ghidra.util.exception.InvalidInputException;
|
||||
import ghidra.xml.XmlParseException;
|
||||
import ghidra.xml.XmlPullParser;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.lang.protorules.AssignAction;
|
||||
|
||||
/**
|
||||
* A list of resources describing possible storage locations for a function's return value,
|
||||
|
@ -36,50 +32,43 @@ import ghidra.xml.XmlPullParser;
|
|||
*
|
||||
* The resource list is checked to ensure entries are distinguishable.
|
||||
*/
|
||||
public class ParamListStandardOut extends ParamListRegisterOut {
|
||||
public class ParamListStandardOut extends ParamListStandard {
|
||||
|
||||
@Override
|
||||
public void assignMap(Program prog, DataType[] proto, ArrayList<VariableStorage> res,
|
||||
boolean addAutoParams) {
|
||||
public void assignMap(PrototypePieces proto, DataTypeManager dtManager,
|
||||
ArrayList<ParameterPieces> res, boolean addAutoParams) {
|
||||
|
||||
int[] status = new int[numgroup];
|
||||
for (int i = 0; i < numgroup; ++i) {
|
||||
status[i] = 0;
|
||||
}
|
||||
|
||||
VariableStorage store = assignAddress(prog, proto[0], status, false, false);
|
||||
if (!store.isUnassignedStorage()) {
|
||||
res.add(store);
|
||||
return;
|
||||
ParameterPieces store = new ParameterPieces();
|
||||
res.add(store);
|
||||
if (VoidDataType.isVoidDataType(proto.outtype)) {
|
||||
store.type = proto.outtype;
|
||||
return; // Don't assign storage for VOID
|
||||
}
|
||||
// If the storage is not assigned (because the datatype is too big) create a hidden input parameter
|
||||
DataType pointer = prog.getDataTypeManager().getPointer(proto[0]);
|
||||
store = assignAddress(prog, pointer, status, false, false);
|
||||
try {
|
||||
if (store.isValid()) {
|
||||
store = new DynamicVariableStorage(prog, true, store.getVarnodes());
|
||||
res.add(store);
|
||||
// Signal to input assignment that there is a hidden return using additional unassigned storage param
|
||||
int responseCode = assignAddress(proto.outtype, proto, -1, dtManager, status, store);
|
||||
if (responseCode != AssignAction.SUCCESS) {
|
||||
// If the storage is not assigned (because the datatype is too big) create a hidden input parameter
|
||||
int sz = (spacebase == null) ? -1 : spacebase.getPointerSize();
|
||||
DataType pointerType = dtManager.getPointer(proto.outtype, sz);
|
||||
if (responseCode == AssignAction.HIDDENRET_SPECIALREG_VOID) {
|
||||
store.type = VoidDataType.dataType;
|
||||
}
|
||||
else {
|
||||
assignAddressFallback(StorageClass.PTR, pointerType, false, status, store);
|
||||
store.type = pointerType;
|
||||
store.isIndirect = true; // Signal that there is a hidden return
|
||||
}
|
||||
if (addAutoParams) {
|
||||
res.add(VariableStorage.UNASSIGNED_STORAGE); // will get replaced during input storage assignments
|
||||
}
|
||||
}
|
||||
catch (InvalidInputException e) {
|
||||
store = VariableStorage.UNASSIGNED_STORAGE;
|
||||
res.add(store);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreXml(XmlPullParser parser, CompilerSpec cspec) throws XmlParseException {
|
||||
super.restoreXml(parser, cspec);
|
||||
|
||||
// ParamEntry tags in the output list are considered a group. Check that entries are distinguishable.
|
||||
for (int i = 1; i < entry.length; ++i) {
|
||||
for (int j = 0; j < i; ++j) {
|
||||
ParamEntry.orderWithinGroup(entry[j], entry[i]);
|
||||
ParameterPieces hiddenRet = new ParameterPieces();
|
||||
hiddenRet.type = pointerType;
|
||||
// Encode whether or not hidden return should be drawn from TYPECLASS_HIDDENRET
|
||||
hiddenRet.hiddenReturnPtr = (responseCode == AssignAction.HIDDENRET_SPECIALREG) ||
|
||||
(responseCode == AssignAction.HIDDENRET_SPECIALREG_VOID);
|
||||
res.add(hiddenRet); // will get replaced during input storage assignments
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
/* ###
|
||||
* 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;
|
||||
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.data.VoidDataType;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
import ghidra.util.exception.InvalidInputException;
|
||||
|
||||
/**
|
||||
* Basic elements of a parameter: address, data-type, properties
|
||||
*/
|
||||
public class ParameterPieces {
|
||||
public Address address; // Storage address of the parameter
|
||||
public DataType type; // The data-type of the parameter
|
||||
public Varnode[] joinPieces; // If non-null, multiple pieces stitched together for single logical value
|
||||
public boolean isThisPointer = false; // True if the "this" pointer
|
||||
public boolean hiddenReturnPtr = false; // True if input pointer to return storage
|
||||
public boolean isIndirect = false; // True if parameter is indirect pointer to actual parameter
|
||||
// public boolean nameLock;
|
||||
// public boolean typeLock;
|
||||
// public boolean sizeLock;
|
||||
|
||||
/**
|
||||
* Swap data-type markup between this and another parameter
|
||||
*
|
||||
* Swap any data-type and flags, but leave the storage address intact.
|
||||
* This assumes the two parameters are the same size.
|
||||
* @param op is the other parameter to swap with this.
|
||||
*/
|
||||
public void swapMarkup(ParameterPieces op) {
|
||||
boolean tmpHidden = hiddenReturnPtr;
|
||||
boolean tmpIndirect = isIndirect;
|
||||
boolean tmpThis = isThisPointer;
|
||||
DataType tmpType = type;
|
||||
Varnode[] tmpJoin = joinPieces;
|
||||
hiddenReturnPtr = op.hiddenReturnPtr;
|
||||
isIndirect = op.isIndirect;
|
||||
isThisPointer = op.isThisPointer;
|
||||
type = op.type;
|
||||
joinPieces = op.joinPieces;
|
||||
op.hiddenReturnPtr = tmpHidden;
|
||||
op.isIndirect = tmpIndirect;
|
||||
op.isThisPointer = tmpThis;
|
||||
op.type = tmpType;
|
||||
op.joinPieces = tmpJoin;
|
||||
}
|
||||
|
||||
public VariableStorage getVariableStorage(Program program) {
|
||||
if (type == null) {
|
||||
type = DataType.DEFAULT;
|
||||
}
|
||||
if (VoidDataType.isVoidDataType(type)) {
|
||||
return VariableStorage.VOID_STORAGE;
|
||||
}
|
||||
int sz = type.getLength();
|
||||
if (sz == 0) {
|
||||
return VariableStorage.UNASSIGNED_STORAGE;
|
||||
}
|
||||
if (isThisPointer) {
|
||||
try {
|
||||
if (address != null) {
|
||||
return new DynamicVariableStorage(program, AutoParameterType.THIS, address, sz);
|
||||
}
|
||||
}
|
||||
catch (InvalidInputException e) {
|
||||
// Fall thru to getUnaassignedDynamicStorage
|
||||
}
|
||||
return DynamicVariableStorage.getUnassignedDynamicStorage(AutoParameterType.THIS);
|
||||
}
|
||||
if ((address == null || address == Address.NO_ADDRESS) && joinPieces == null) {
|
||||
return DynamicVariableStorage.getUnassignedDynamicStorage(isIndirect);
|
||||
}
|
||||
VariableStorage store;
|
||||
try {
|
||||
if (joinPieces != null) {
|
||||
store = new DynamicVariableStorage(program, false, joinPieces);
|
||||
}
|
||||
else {
|
||||
if (hiddenReturnPtr) {
|
||||
store = new DynamicVariableStorage(program,
|
||||
AutoParameterType.RETURN_STORAGE_PTR, address, sz);
|
||||
}
|
||||
else if (isIndirect) {
|
||||
store = new DynamicVariableStorage(program, true, address, sz);
|
||||
}
|
||||
else {
|
||||
store = new DynamicVariableStorage(program, false, address, sz);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (InvalidInputException e) {
|
||||
store = DynamicVariableStorage.getUnassignedDynamicStorage(isIndirect);
|
||||
}
|
||||
return store;
|
||||
}
|
||||
}
|
|
@ -27,7 +27,6 @@ import ghidra.program.model.data.*;
|
|||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.pcode.*;
|
||||
import ghidra.util.SystemUtilities;
|
||||
import ghidra.util.exception.InvalidInputException;
|
||||
import ghidra.util.xml.SpecXmlUtils;
|
||||
import ghidra.xml.*;
|
||||
|
||||
|
@ -222,24 +221,23 @@ public class PrototypeModel {
|
|||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* Get the preferred return location given the specified dataType.
|
||||
* In truth, there is no one location. The routines that use this method tend
|
||||
* to want the default storage location for integer or pointer return values.
|
||||
* If the return value is passed back through a hidden input pointer,
|
||||
* i.e. {@link AutoParameterType#RETURN_STORAGE_PTR}, this routine will not pass back
|
||||
* the storage location of the pointer, but will typically pass
|
||||
* back the location of the normal return register which holds a copy of the pointer.
|
||||
* @param dataType first parameter dataType or null for an undefined type.
|
||||
* @param program is the Program
|
||||
* @return return location or {@link VariableStorage#UNASSIGNED_STORAGE} if
|
||||
* unable to determine suitable location
|
||||
*/
|
||||
@Deprecated
|
||||
public VariableStorage getReturnLocation(DataType dataType, Program program) {
|
||||
DataType clone = dataType.clone(program.getDataTypeManager());
|
||||
DataType[] arr = new DataType[1];
|
||||
arr[0] = clone;
|
||||
ArrayList<VariableStorage> res = new ArrayList<>();
|
||||
outputParams.assignMap(program, arr, res, false);
|
||||
PrototypePieces proto = new PrototypePieces(this, clone);
|
||||
ArrayList<ParameterPieces> res = new ArrayList<>();
|
||||
outputParams.assignMap(proto, program.getDataTypeManager(), res, false);
|
||||
if (res.size() > 0) {
|
||||
return res.get(0);
|
||||
return res.get(0).getVariableStorage(program);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -301,12 +299,51 @@ public class PrototypeModel {
|
|||
}
|
||||
|
||||
/**
|
||||
* Compute the variable storage for a given function and set of return/parameter datatypes
|
||||
* defined by an array of data types.
|
||||
* Calculate input and output storage locations given a function prototype
|
||||
*
|
||||
* The data-types of the function prototype are passed in. Based on this model, a
|
||||
* location is selected for each (input and output) parameter and passed back to the
|
||||
* caller. The passed back storage locations are ordered with the output storage
|
||||
* as the first entry, followed by the input storage locations. The model has the option
|
||||
* of inserting a hidden return value pointer in the input storage locations.
|
||||
*
|
||||
* If the model cannot assign storage, the ParameterPieces will have a null Address.
|
||||
* @param proto is the function prototype parameter data-types
|
||||
* @param dtManager is the manager used to create indirect data-types
|
||||
* @param res will hold the storage addresses for each parameter
|
||||
* @param addAutoParams is true if auto parameters (like the this pointer) should be processed
|
||||
*/
|
||||
public void assignParameterStorage(PrototypePieces proto, DataTypeManager dtManager,
|
||||
ArrayList<ParameterPieces> res, boolean addAutoParams) {
|
||||
outputParams.assignMap(proto, dtManager, res, addAutoParams);
|
||||
inputParams.assignMap(proto, dtManager, res, addAutoParams);
|
||||
|
||||
if (hasThis && addAutoParams && res.size() > 1) {
|
||||
int thisIndex = 1;
|
||||
if (res.get(1).hiddenReturnPtr && res.size() > 2) {
|
||||
if (inputParams.isThisBeforeRetPointer()) {
|
||||
// pointer has been bumped by auto-return-storage
|
||||
res.get(1).swapMarkup(res.get(2)); // must swap storage and position for slots 1 and 2
|
||||
}
|
||||
else {
|
||||
thisIndex = 2;
|
||||
}
|
||||
}
|
||||
res.get(thisIndex).isThisPointer = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the variable storage for a given array of return/parameter datatypes. The first array element
|
||||
* is the return datatype, which is followed by any input parameter datatypes in order.
|
||||
* If addAutoParams is true, pointer datatypes will automatically be inserted for "this" or "hidden return"
|
||||
* input parameters, if needed. In this case, the dataTypes array should not include explicit entries for
|
||||
* these parameters. If addAutoParams is false, the dataTypes array is assumed to already contain explicit
|
||||
* entries for any of these parameters.
|
||||
* @param program is the Program
|
||||
* @param dataTypes return/parameter datatypes (first element is always the return datatype,
|
||||
* i.e., minimum array length is 1)
|
||||
* @param addAutoParams TODO
|
||||
* @param addAutoParams true if auto-parameter storage locations can be generated
|
||||
* @return dynamic storage locations orders by ordinal where first element corresponds to
|
||||
* return storage. The returned array may also include additional auto-parameter storage
|
||||
* locations.
|
||||
|
@ -314,61 +351,21 @@ public class PrototypeModel {
|
|||
public VariableStorage[] getStorageLocations(Program program, DataType[] dataTypes,
|
||||
boolean addAutoParams) {
|
||||
|
||||
boolean injectAutoThisParam = false;
|
||||
DataType injectedThis = null;
|
||||
if (addAutoParams && hasThis) {
|
||||
// explicit support for auto 'this' parameter
|
||||
// must inject pointer arg to obtain storage assignment
|
||||
injectAutoThisParam = true;
|
||||
DataType[] ammendedTypes = new DataType[dataTypes.length + 1];
|
||||
ammendedTypes[0] = dataTypes[0];
|
||||
ammendedTypes[1] = new PointerDataType(program.getDataTypeManager());
|
||||
if (dataTypes.length > 1) {
|
||||
System.arraycopy(dataTypes, 1, ammendedTypes, 2, dataTypes.length - 1);
|
||||
}
|
||||
dataTypes = ammendedTypes;
|
||||
injectedThis = new PointerDataType(program.getDataTypeManager());
|
||||
}
|
||||
PrototypePieces proto = new PrototypePieces(this, dataTypes, injectedThis);
|
||||
|
||||
ArrayList<VariableStorage> res = new ArrayList<>();
|
||||
outputParams.assignMap(program, dataTypes, res, addAutoParams);
|
||||
inputParams.assignMap(program, dataTypes, res, addAutoParams);
|
||||
ArrayList<ParameterPieces> res = new ArrayList<>();
|
||||
assignParameterStorage(proto, program.getDataTypeManager(), res, addAutoParams);
|
||||
VariableStorage[] finalres = new VariableStorage[res.size()];
|
||||
res.toArray(finalres);
|
||||
|
||||
if (injectAutoThisParam) {
|
||||
|
||||
Varnode[] thisVarnodes = finalres[1].getVarnodes();
|
||||
|
||||
int thisIndex = 1;
|
||||
try {
|
||||
if (finalres[1].isAutoStorage()) {
|
||||
if (inputParams.isThisBeforeRetPointer()) {
|
||||
// pointer has been bumped by auto-return-storage
|
||||
// must swap storage and position for slots 1 and 2
|
||||
finalres[2] = new DynamicVariableStorage(program,
|
||||
finalres[1].getAutoParameterType(), finalres[2].getVarnodes());
|
||||
}
|
||||
else {
|
||||
thisIndex = 2;
|
||||
thisVarnodes = finalres[2].getVarnodes();
|
||||
}
|
||||
}
|
||||
|
||||
if (thisVarnodes.length != 0) {
|
||||
finalres[thisIndex] =
|
||||
new DynamicVariableStorage(program, AutoParameterType.THIS, thisVarnodes);
|
||||
}
|
||||
else {
|
||||
finalres[thisIndex] =
|
||||
DynamicVariableStorage.getUnassignedDynamicStorage(AutoParameterType.THIS);
|
||||
}
|
||||
}
|
||||
catch (InvalidInputException e) {
|
||||
finalres[thisIndex] =
|
||||
DynamicVariableStorage.getUnassignedDynamicStorage(AutoParameterType.THIS);
|
||||
}
|
||||
|
||||
for (int i = 0; i < finalres.length; ++i) {
|
||||
finalres[i] = res.get(i).getVariableStorage(program);
|
||||
}
|
||||
|
||||
return finalres;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
/* ###
|
||||
* 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;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import ghidra.program.model.data.DataType;
|
||||
|
||||
/**
|
||||
* Raw components of a function prototype (obtained from parsing source code)
|
||||
*/
|
||||
public class PrototypePieces {
|
||||
public PrototypeModel model; // (Optional) model on which prototype is based
|
||||
// public String name; // Identifier (function name) associated with prototype
|
||||
public DataType outtype; // Return data-type
|
||||
public ArrayList<DataType> intypes; // Input data-types
|
||||
// public ArrayList<String> innames; // Identifiers for input types
|
||||
public int firstVarArgSlot; // First position of a variable argument, or -1 if not vararg
|
||||
|
||||
/**
|
||||
* Populate pieces from old-style array of DataTypes
|
||||
* @param model is the prototype model
|
||||
* @param oldList is the list of output and input data-types
|
||||
* @param injectedThis if non-null is the data-type of the this pointer to be injected
|
||||
*/
|
||||
public PrototypePieces(PrototypeModel model, DataType[] oldList, DataType injectedThis) {
|
||||
this.model = model;
|
||||
outtype = oldList[0];
|
||||
intypes = new ArrayList<>();
|
||||
firstVarArgSlot = -1;
|
||||
if (injectedThis != null) {
|
||||
intypes.add(injectedThis);
|
||||
}
|
||||
for (int i = 1; i < oldList.length; ++i) {
|
||||
intypes.add(oldList[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create prototype with output data-type and empty/unspecified input data-types
|
||||
* @param model is the prototype model
|
||||
* @param outType is the output data-type
|
||||
*/
|
||||
public PrototypePieces(PrototypeModel model, DataType outType) {
|
||||
this.model = model;
|
||||
outtype = outType;
|
||||
intypes = new ArrayList<>();
|
||||
firstVarArgSlot = -1;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
/* ###
|
||||
* 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;
|
||||
|
||||
import ghidra.xml.XmlParseException;
|
||||
|
||||
/**
|
||||
* Data-type class for the purpose of assigning storage
|
||||
*/
|
||||
public enum StorageClass {
|
||||
GENERAL(0, "general"), // General purpose
|
||||
FLOAT(1, "float"), // Floating-point data-types
|
||||
PTR(2, "ptr"), // Pointer data-types
|
||||
HIDDENRET(3, "hiddenret"), // Class for hidden return values
|
||||
VECTOR(4, "vector"), // Vector data-types
|
||||
CLASS1(100, "class1"), // Architecture specific class 1
|
||||
CLASS2(101, "class2"), // Architecture specific class 2
|
||||
CLASS3(102, "class3"), // Architecture specific class 3
|
||||
CLASS4(103, "class4"); // Architecture specific class 4
|
||||
|
||||
private int value; // Value for comparing storage classes
|
||||
private String name; // Name for marshaling
|
||||
|
||||
private StorageClass(int val, String nm) {
|
||||
value = val;
|
||||
name = nm;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public static StorageClass getClass(String val) throws XmlParseException {
|
||||
switch (val) {
|
||||
case "general":
|
||||
return GENERAL;
|
||||
case "float":
|
||||
return FLOAT;
|
||||
case "ptr":
|
||||
return PTR;
|
||||
case "hiddenret":
|
||||
return HIDDENRET;
|
||||
case "vector":
|
||||
return VECTOR;
|
||||
case "class1":
|
||||
return CLASS1;
|
||||
case "class2":
|
||||
return CLASS2;
|
||||
case "class3":
|
||||
return CLASS3;
|
||||
case "class4":
|
||||
return CLASS4;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
throw new XmlParseException("Unknown type class: " + val);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
/* ###
|
||||
* 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.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import ghidra.program.model.lang.PrototypePieces;
|
||||
import ghidra.program.model.pcode.Encoder;
|
||||
import ghidra.xml.XmlParseException;
|
||||
import ghidra.xml.XmlPullParser;
|
||||
|
||||
/**
|
||||
* Logically AND multiple QualifierFilters together into a single filter.
|
||||
* An instances contains some number of other arbitrary filters. In order for this filter to
|
||||
* pass, all these contained filters must pass.
|
||||
*/
|
||||
public class AndFilter implements QualifierFilter {
|
||||
|
||||
private QualifierFilter[] subQualifiers; // Filters being logically ANDed together
|
||||
|
||||
/**
|
||||
* The AndFilter assumes ownership of all the filters in the ArrayList
|
||||
* @param qualifierList is the list of filters pulled into this filter
|
||||
*/
|
||||
public AndFilter(ArrayList<QualifierFilter> qualifierList) {
|
||||
subQualifiers = new QualifierFilter[qualifierList.size()];
|
||||
qualifierList.toArray(subQualifiers);
|
||||
}
|
||||
|
||||
public AndFilter(AndFilter op) {
|
||||
subQualifiers = new QualifierFilter[op.subQualifiers.length];
|
||||
for (int i = 0; i < subQualifiers.length; ++i) {
|
||||
subQualifiers[i] = op.subQualifiers[i].clone();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public QualifierFilter clone() {
|
||||
return new AndFilter(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEquivalent(QualifierFilter op) {
|
||||
if (op.getClass() != this.getClass()) {
|
||||
return false;
|
||||
}
|
||||
AndFilter otherFilter = (AndFilter) op;
|
||||
if (subQualifiers.length != otherFilter.subQualifiers.length) {
|
||||
return false;
|
||||
}
|
||||
// Preserve strict order
|
||||
for (int i = 0; i < subQualifiers.length; ++i) {
|
||||
if (!subQualifiers[i].isEquivalent(otherFilter.subQualifiers[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean filter(PrototypePieces proto, int pos) {
|
||||
for (int i = 0; i < subQualifiers.length; ++i) {
|
||||
if (!subQualifiers[i].filter(proto, pos)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(Encoder encoder) throws IOException {
|
||||
for (int i = 0; i < subQualifiers.length; ++i) {
|
||||
subQualifiers[i].encode(encoder);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreXml(XmlPullParser parser) throws XmlParseException {
|
||||
// This method is not called
|
||||
}
|
||||
}
|
|
@ -0,0 +1,160 @@
|
|||
/* ###
|
||||
* 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.ElementId.*;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.data.DataTypeManager;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.pcode.Encoder;
|
||||
import ghidra.util.exception.InvalidInputException;
|
||||
import ghidra.xml.*;
|
||||
|
||||
/**
|
||||
* An action that assigns an Address to a function prototype parameter
|
||||
*
|
||||
* A request for the address of either return storage or an input parameter is made
|
||||
* through the assignAddress() method, which is given full information about the function prototype.
|
||||
* Details about how the action performs is configured through the restoreXml() method.
|
||||
*/
|
||||
public abstract class AssignAction {
|
||||
public static final int SUCCESS = 0; // Data-type is fully assigned
|
||||
public static final int FAIL = 1; // Action could not be applied (not enough resources)
|
||||
public static final int HIDDENRET_PTRPARAM = 2; // Hidden return pointer as first input parameter
|
||||
public static final int HIDDENRET_SPECIALREG = 3; // Hidden return pointer in special register
|
||||
public static final int HIDDENRET_SPECIALREG_VOID = 4; // Hidden return pointer, but no normal return
|
||||
|
||||
protected ParamListStandard resource; // Resources to which this action applies
|
||||
|
||||
public AssignAction(ParamListStandard res) {
|
||||
resource = res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a copy of this action
|
||||
* @param newResource is the new resource object that will own the clone
|
||||
* @return the newly allocated copy
|
||||
* @throws InvalidInputException if required configuration is not present in new resource object
|
||||
*/
|
||||
public abstract AssignAction clone(ParamListStandard newResource) throws InvalidInputException;
|
||||
|
||||
/**
|
||||
* Test if the given action is configured and performs identically to this
|
||||
* @param op is the given action
|
||||
* @return true if the two actions are equivalent
|
||||
*/
|
||||
public abstract boolean isEquivalent(AssignAction op);
|
||||
|
||||
/**
|
||||
* Assign an address and other meta-data for a specific parameter or for return storage in context
|
||||
* The Address is assigned based on the data-type of the parameter, available register
|
||||
* resources, and other details of the function prototype. Consumed resources are marked.
|
||||
* This method returns a response code:
|
||||
* - SUCCESS - indicating the Address was successfully assigned
|
||||
* - FAIL - if the Address could not be assigned
|
||||
* - HIDDENRET_PTRPARAM - if an additional hidden return parameter is required
|
||||
* @param dt is the data-type of the parameter or return value
|
||||
* @param proto is the high-level description of the function prototype
|
||||
* @param pos is the position of the parameter (pos>=0) or return storage (pos=-1)
|
||||
* @param dtManager is a data-type manager for (possibly) transforming the data-type
|
||||
* @param status is the resource consumption array
|
||||
* @param res will hold the resulting description of the parameter
|
||||
* @return the response code
|
||||
*/
|
||||
public abstract int assignAddress(DataType dt, PrototypePieces proto, int pos,
|
||||
DataTypeManager dtManager, int[] status, ParameterPieces res);
|
||||
|
||||
/**
|
||||
* Save this action and its configuration to a stream
|
||||
* @param encoder is the stream encoder
|
||||
* @throws IOException for problems writing to the stream
|
||||
*/
|
||||
public abstract void encode(Encoder encoder) throws IOException;
|
||||
|
||||
/**
|
||||
* Configure any details of how this action should behave from the stream
|
||||
* @param parser is the given stream decoder
|
||||
* @throws XmlParseException is there are problems decoding the stream
|
||||
*/
|
||||
public abstract void restoreXml(XmlPullParser parser) throws XmlParseException;
|
||||
|
||||
/**
|
||||
* Read the next action element from the stream and return the new configured
|
||||
* AssignAction object. If the next element is not an action, throw an exception.
|
||||
* @param parser is the stream parser
|
||||
* @param res is the resource set for the new action
|
||||
* @return the new action
|
||||
* @throws XmlParseException for problems parsing the stream
|
||||
*/
|
||||
static public AssignAction restoreActionXml(XmlPullParser parser, ParamListStandard res)
|
||||
throws XmlParseException {
|
||||
AssignAction action;
|
||||
XmlElement elemId = parser.peek();
|
||||
String nm = elemId.getName();
|
||||
if (nm.equals(ELEM_GOTO_STACK.name())) {
|
||||
action = new GotoStack(res, 0);
|
||||
}
|
||||
else if (nm.equals(ELEM_JOIN.name())) {
|
||||
action = new MultiSlotAssign(res);
|
||||
}
|
||||
else if (nm.equals(ELEM_CONSUME.name())) {
|
||||
action = new ConsumeAs(StorageClass.GENERAL, res);
|
||||
}
|
||||
else if (nm.equals(ELEM_CONVERT_TO_PTR.name())) {
|
||||
action = new ConvertToPointer(res);
|
||||
}
|
||||
else if (nm.equals(ELEM_HIDDEN_RETURN.name())) {
|
||||
action = new HiddenReturnAssign(res, false);
|
||||
}
|
||||
else if (nm.equals(ELEM_JOIN_PER_PRIMITIVE.name())) {
|
||||
boolean consumeMostSig = res.getEntry(0).isBigEndian();
|
||||
action = new MultiMemberAssign(StorageClass.GENERAL, false, consumeMostSig, res);
|
||||
}
|
||||
else {
|
||||
throw new XmlParseException("Unknown model rule action: " + nm);
|
||||
}
|
||||
action.restoreXml(parser);
|
||||
return action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the next sideeffect element from the stream and return the new configured
|
||||
* AssignAction object. If the next element is not a sideeffect, throw an exception.
|
||||
* @param parser is the stream parser
|
||||
* @param res is the resource set for the new sideeffect
|
||||
* @return the new sideeffect
|
||||
* @throws XmlParseException for problems parsing the stream
|
||||
*/
|
||||
static public AssignAction restoreSideeffectXml(XmlPullParser parser, ParamListStandard res)
|
||||
throws XmlParseException {
|
||||
AssignAction action;
|
||||
XmlElement elemId = parser.peek();
|
||||
String nm = elemId.getName();
|
||||
|
||||
if (nm.equals(ELEM_CONSUME_EXTRA.name())) {
|
||||
action = new ConsumeExtra(res);
|
||||
}
|
||||
else {
|
||||
throw new XmlParseException("Unknown model rule sideeffect: " + nm);
|
||||
}
|
||||
action.restoreXml(parser);
|
||||
return action;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
/* ###
|
||||
* 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 ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.data.DataTypeManager;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.pcode.Encoder;
|
||||
import ghidra.xml.*;
|
||||
|
||||
/**
|
||||
* Consume a parameter from a specific resource list
|
||||
*
|
||||
* Normally the resource list is determined by the parameter data-type, but this
|
||||
* action specifies an overriding resource list.
|
||||
*/
|
||||
public class ConsumeAs extends AssignAction {
|
||||
|
||||
private StorageClass resourceType; // The resource list the parameter is consumed from
|
||||
|
||||
public ConsumeAs(StorageClass store, ParamListStandard res) {
|
||||
super(res);
|
||||
resourceType = store;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AssignAction clone(ParamListStandard newResource) {
|
||||
return new ConsumeAs(resourceType, newResource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEquivalent(AssignAction op) {
|
||||
if (this.getClass() != op.getClass()) {
|
||||
return false;
|
||||
}
|
||||
ConsumeAs otherAction = (ConsumeAs) op;
|
||||
if (resourceType != otherAction.resourceType) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int assignAddress(DataType dt, PrototypePieces proto, int pos, DataTypeManager dtManager,
|
||||
int[] status, ParameterPieces res) {
|
||||
return resource.assignAddressFallback(resourceType, dt, true, status, res);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(Encoder encoder) throws IOException {
|
||||
encoder.openElement(ELEM_CONSUME);
|
||||
encoder.writeString(ATTRIB_STORAGE, resourceType.toString());
|
||||
encoder.closeElement(ELEM_CONSUME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreXml(XmlPullParser parser) throws XmlParseException {
|
||||
XmlElement elem = parser.start(ELEM_CONSUME.name());
|
||||
resourceType = StorageClass.getClass(elem.getAttribute(ATTRIB_STORAGE.name()));
|
||||
parser.end(elem);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,145 @@
|
|||
/* ###
|
||||
* 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 ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.data.DataTypeManager;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.pcode.Encoder;
|
||||
import ghidra.util.exception.InvalidInputException;
|
||||
import ghidra.xml.*;
|
||||
|
||||
/**
|
||||
* Consume additional registers from an alternate resource list
|
||||
*
|
||||
* This action is a side-effect and doesn't assign an address for the current parameter.
|
||||
* The resource list, resourceType, is specified. If the side-effect is triggered,
|
||||
* register resources from this list are consumed. If matchSize is true (the default),
|
||||
* registers are consumed, until the number of bytes in the data-type is reached. Otherwise,
|
||||
* only a single register is consumed. If all registers are already consumed, no action is taken.
|
||||
*/
|
||||
public class ConsumeExtra extends AssignAction {
|
||||
|
||||
private StorageClass resourceType; // The other resource list to consume from
|
||||
private int firstIter; // Iterator to first element in the resource list
|
||||
private boolean matchSize; // false, if side-effect only consumes a single register
|
||||
|
||||
/**
|
||||
* Cache specific ParamEntry needed by the action.
|
||||
* Find the first ParamEntry matching the resourceType.
|
||||
* @throws InvalidInputException if it cannot find the configured ParamEntry objects
|
||||
*/
|
||||
private void initializeEntries() throws InvalidInputException {
|
||||
firstIter = -1;
|
||||
for (int i = 0; i < resource.getNumParamEntry(); ++i) {
|
||||
ParamEntry entry = resource.getEntry(i);
|
||||
if (entry.isExclusion() && entry.getType() == resourceType &&
|
||||
entry.getAllGroups().length == 1) {
|
||||
firstIter = i; // First matching resource size
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (firstIter == -1) {
|
||||
throw new InvalidInputException(
|
||||
"Could not find matching resources for action: consumeextra");
|
||||
}
|
||||
}
|
||||
|
||||
protected ConsumeExtra(ParamListStandard res) {
|
||||
super(res);
|
||||
resourceType = StorageClass.GENERAL;
|
||||
matchSize = true;
|
||||
}
|
||||
|
||||
public ConsumeExtra(StorageClass store, boolean match, ParamListStandard res)
|
||||
throws InvalidInputException {
|
||||
super(res);
|
||||
resourceType = store;
|
||||
matchSize = match;
|
||||
initializeEntries();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AssignAction clone(ParamListStandard newResource) throws InvalidInputException {
|
||||
return new ConsumeExtra(resourceType, matchSize, newResource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEquivalent(AssignAction op) {
|
||||
if (this.getClass() != op.getClass()) {
|
||||
return false;
|
||||
}
|
||||
ConsumeExtra otherAction = (ConsumeExtra) op;
|
||||
if (firstIter != otherAction.firstIter || matchSize != otherAction.matchSize ||
|
||||
resourceType != otherAction.resourceType) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int assignAddress(DataType dt, PrototypePieces proto, int pos, DataTypeManager dtManager,
|
||||
int[] status, ParameterPieces res) {
|
||||
int iter = firstIter;
|
||||
int endIter = resource.getNumParamEntry();
|
||||
int sizeLeft = dt.getLength();
|
||||
while (sizeLeft > 0 && iter != endIter) {
|
||||
ParamEntry entry = resource.getEntry(iter);
|
||||
++iter;
|
||||
if (!entry.isExclusion()) {
|
||||
break; // Reached end of resource list
|
||||
}
|
||||
if (entry.getType() != resourceType || entry.getAllGroups().length != 1) {
|
||||
continue; // Not a single register in desired list
|
||||
}
|
||||
if (status[entry.getGroup()] != 0) {
|
||||
continue; // Already consumed
|
||||
}
|
||||
status[entry.getGroup()] = -1; // Consume the slot/register
|
||||
sizeLeft -= entry.getSize();
|
||||
if (!matchSize) {
|
||||
break; // Only consume a single register
|
||||
}
|
||||
}
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(Encoder encoder) throws IOException {
|
||||
encoder.openElement(ELEM_CONSUME_EXTRA);
|
||||
encoder.writeString(ATTRIB_STORAGE, resourceType.toString());
|
||||
encoder.closeElement(ELEM_CONSUME_EXTRA);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreXml(XmlPullParser parser) throws XmlParseException {
|
||||
XmlElement elem = parser.start(ELEM_CONSUME_EXTRA.name());
|
||||
resourceType = StorageClass.getClass(elem.getAttribute(ATTRIB_STORAGE.name()));
|
||||
parser.end(elem);
|
||||
try {
|
||||
initializeEntries();
|
||||
}
|
||||
catch (InvalidInputException e) {
|
||||
throw new XmlParseException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
/* ###
|
||||
* 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.ElementId.*;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.data.DataTypeManager;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.pcode.Encoder;
|
||||
import ghidra.util.exception.InvalidInputException;
|
||||
import ghidra.xml.*;
|
||||
|
||||
/**
|
||||
* Action converting the parameter's data-type to a pointer, and assigning storage for the pointer.
|
||||
* This assumes the data-type is stored elsewhere and only the pointer is passed as a parameter.
|
||||
*/
|
||||
public class ConvertToPointer extends AssignAction {
|
||||
|
||||
private AddressSpace space; // Address space used for pointer size
|
||||
|
||||
public ConvertToPointer(ParamListStandard res) {
|
||||
super(res);
|
||||
space = res.getSpacebase();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AssignAction clone(ParamListStandard newResource) throws InvalidInputException {
|
||||
return new ConvertToPointer(newResource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEquivalent(AssignAction op) {
|
||||
if (this.getClass() != op.getClass()) {
|
||||
return false;
|
||||
}
|
||||
ConvertToPointer otherAction = (ConvertToPointer) op;
|
||||
if (space == null && otherAction.space == null) {
|
||||
return true;
|
||||
}
|
||||
if (space == null || otherAction.space == null) {
|
||||
return false;
|
||||
}
|
||||
if (!space.equals(otherAction.space)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int assignAddress(DataType dt, PrototypePieces proto, int pos, DataTypeManager dtManager,
|
||||
int[] status, ParameterPieces res) {
|
||||
|
||||
int pointersize = (space != null) ? space.getPointerSize() : -1;
|
||||
// Convert the data-type to a pointer
|
||||
DataType pointertp = dtManager.getPointer(dt, pointersize);
|
||||
// (Recursively) assign storage
|
||||
int responseCode = resource.assignAddress(pointertp, proto, pos, dtManager, status, res);
|
||||
res.isIndirect = true;
|
||||
return responseCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(Encoder encoder) throws IOException {
|
||||
encoder.openElement(ELEM_CONVERT_TO_PTR);
|
||||
encoder.closeElement(ELEM_CONVERT_TO_PTR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreXml(XmlPullParser parser) throws XmlParseException {
|
||||
XmlElement elem = parser.start(ELEM_CONVERT_TO_PTR.name());
|
||||
parser.end(elem);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,165 @@
|
|||
/* ###
|
||||
* 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 java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.pcode.Encoder;
|
||||
import ghidra.program.model.pcode.PcodeDataTypeManager;
|
||||
import ghidra.xml.*;
|
||||
|
||||
/**
|
||||
* A filter selecting a specific class of data-type.
|
||||
* A test of whether data-type belongs to its class can be performed by calling
|
||||
* the filter() method.
|
||||
*/
|
||||
public interface DatatypeFilter {
|
||||
|
||||
/**
|
||||
* Make a copy of this filter
|
||||
* @return the new copy
|
||||
*/
|
||||
public DatatypeFilter clone();
|
||||
|
||||
/**
|
||||
* Test if the given filter is configured and performs identically to this
|
||||
* @param op is the given filter
|
||||
* @return true if the two filters are equivalent
|
||||
*/
|
||||
public boolean isEquivalent(DatatypeFilter op);
|
||||
|
||||
/**
|
||||
* Test whether the given data-type belongs to this filter's data-type class
|
||||
* @param dt is the given data-type to test
|
||||
* @return true if the data-type is in the class, false otherwise
|
||||
*/
|
||||
public boolean filter(DataType dt);
|
||||
|
||||
/**
|
||||
* Encode this filter and its configuration to a stream
|
||||
* @param encoder is the stream encoder
|
||||
* @throws IOException for problems writing to the stream
|
||||
*/
|
||||
public void encode(Encoder encoder) throws IOException;
|
||||
|
||||
/**
|
||||
* Configure details of the data-type class being filtered from the given stream
|
||||
* @param parser is the given stream decoder
|
||||
* @throws XmlParseException if there are problems with the stream
|
||||
*/
|
||||
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 filler data-type is provided, it is used to fill holes in structures. If
|
||||
* a maximum number of extracted primitives is exceeded, or if no filler is provided and a hole
|
||||
* is encountered, 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 filler is the data-type to use as filler (or null)
|
||||
* @param res will hold the list of primitives
|
||||
* @return true if all primitives were extracted
|
||||
*/
|
||||
public static boolean extractPrimitives(DataType dt, int max, DataType filler,
|
||||
ArrayList<DataType> res) {
|
||||
int metaType = PcodeDataTypeManager.getMetatype(dt);
|
||||
switch (metaType) {
|
||||
case PcodeDataTypeManager.TYPE_UNKNOWN:
|
||||
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, filler, res)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case PcodeDataTypeManager.TYPE_STRUCT:
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
Structure structPtr = (Structure) dt;
|
||||
int curOff = 0;
|
||||
DataTypeComponent[] components = structPtr.getDefinedComponents();
|
||||
for (DataTypeComponent component : components) {
|
||||
int nextOff = component.getOffset();
|
||||
if (nextOff > curOff) {
|
||||
if (filler == null) {
|
||||
return false;
|
||||
}
|
||||
while (curOff < nextOff) {
|
||||
if (res.size() >= max) {
|
||||
return false;
|
||||
}
|
||||
res.add(filler);
|
||||
curOff += filler.getLength();
|
||||
}
|
||||
}
|
||||
if (!extractPrimitives(component.getDataType(), max, filler, res)) {
|
||||
return false;
|
||||
}
|
||||
curOff += component.getDataType().getLength();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate a filter from the given stream.
|
||||
* @param parser is the given stream decoder
|
||||
* @return the new data-type filter instance
|
||||
* @throws XmlParseException for problems reading the stream
|
||||
*/
|
||||
public static DatatypeFilter restoreFilterXml(XmlPullParser parser) throws XmlParseException {
|
||||
DatatypeFilter filter;
|
||||
XmlElement elemId = parser.peek();
|
||||
String nm = elemId.getAttribute(ATTRIB_NAME.name());
|
||||
if (nm.equals(SizeRestrictedFilter.NAME)) {
|
||||
filter = new SizeRestrictedFilter();
|
||||
}
|
||||
else if (nm.equals(HomogeneousAggregate.NAME_FLOAT4)) {
|
||||
filter = new HomogeneousAggregate(HomogeneousAggregate.NAME_FLOAT4,
|
||||
PcodeDataTypeManager.TYPE_FLOAT, 4, 0, 0);
|
||||
}
|
||||
else {
|
||||
// If no other name matches, assume this is a decompiler metatype
|
||||
int meta = PcodeDataTypeManager.getMetatype(nm);
|
||||
filter = new MetaTypeFilter(meta);
|
||||
}
|
||||
filter.restoreXml(parser);
|
||||
return filter;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
/* ###
|
||||
* 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 ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.lang.PrototypePieces;
|
||||
import ghidra.program.model.pcode.Encoder;
|
||||
import ghidra.util.xml.SpecXmlUtils;
|
||||
import ghidra.xml.*;
|
||||
|
||||
/**
|
||||
* Check if the function signature has a specific data-type in a specific position.
|
||||
* This filter does not match against the data-type in the current position
|
||||
* being assigned, but against a parameter at a fixed position.
|
||||
*/
|
||||
public class DatatypeMatchFilter implements QualifierFilter {
|
||||
|
||||
private int position; // The position of the data-type to check
|
||||
private DatatypeFilter typeFilter; // The data-type that must be at position
|
||||
|
||||
public DatatypeMatchFilter() {
|
||||
position = -1;
|
||||
typeFilter = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QualifierFilter clone() {
|
||||
DatatypeMatchFilter res = new DatatypeMatchFilter();
|
||||
res.position = position;
|
||||
res.typeFilter = typeFilter.clone();
|
||||
return res;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEquivalent(QualifierFilter op) {
|
||||
if (this.getClass() != op.getClass()) {
|
||||
return false;
|
||||
}
|
||||
DatatypeMatchFilter otherFilter = (DatatypeMatchFilter) op;
|
||||
if (position != otherFilter.position) {
|
||||
return false;
|
||||
}
|
||||
return typeFilter.isEquivalent(otherFilter.typeFilter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean filter(PrototypePieces proto, int pos) {
|
||||
// The position of the current parameter being assigned, pos, is NOT used.
|
||||
DataType dt;
|
||||
if (position < 0) {
|
||||
dt = proto.outtype;
|
||||
}
|
||||
else {
|
||||
if (position >= proto.intypes.size()) {
|
||||
return false;
|
||||
}
|
||||
dt = proto.intypes.get(position);
|
||||
}
|
||||
return typeFilter.filter(dt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(Encoder encoder) throws IOException {
|
||||
encoder.openElement(ELEM_DATATYPE_AT);
|
||||
encoder.writeSignedInteger(ATTRIB_INDEX, position);
|
||||
typeFilter.encode(encoder);
|
||||
encoder.closeElement(ELEM_DATATYPE_AT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreXml(XmlPullParser parser) throws XmlParseException {
|
||||
XmlElement elem = parser.start(ELEM_DATATYPE_AT.name());
|
||||
position = SpecXmlUtils.decodeInt(elem.getAttribute(ATTRIB_INDEX.name()));
|
||||
typeFilter = DatatypeFilter.restoreFilterXml(parser);
|
||||
parser.end(elem);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
/* ###
|
||||
* 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.ElementId.*;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.data.DataTypeManager;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.pcode.Encoder;
|
||||
import ghidra.util.exception.InvalidInputException;
|
||||
import ghidra.xml.*;
|
||||
|
||||
/**
|
||||
* Action assigning a parameter Address from the next available stack location
|
||||
*/
|
||||
public class GotoStack extends AssignAction {
|
||||
|
||||
private ParamEntry stackEntry; // Parameter Entry corresponding to the stack
|
||||
|
||||
private void initializeEntry() throws InvalidInputException {
|
||||
for (int i = 0; i < resource.getNumParamEntry(); ++i) {
|
||||
ParamEntry entry = resource.getEntry(i);
|
||||
if (!entry.isExclusion() && entry.getSpace().isStackSpace()) {
|
||||
stackEntry = entry;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (stackEntry == null) {
|
||||
throw new InvalidInputException("Cannot find matching <pentry> for action: gotostack");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for use with restoreXml
|
||||
* @param res is the new resource list to associate with the action
|
||||
* @param val is a dummy argument
|
||||
*/
|
||||
protected GotoStack(ParamListStandard res, int val) {
|
||||
super(res);
|
||||
stackEntry = null;
|
||||
}
|
||||
|
||||
public GotoStack(ParamListStandard res) throws InvalidInputException {
|
||||
super(res);
|
||||
stackEntry = null;
|
||||
initializeEntry();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AssignAction clone(ParamListStandard newResource) throws InvalidInputException {
|
||||
return new GotoStack(newResource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEquivalent(AssignAction op) {
|
||||
if (this.getClass() != op.getClass()) {
|
||||
return false;
|
||||
}
|
||||
GotoStack otherAction = (GotoStack) op;
|
||||
return stackEntry.isEquivalent(otherAction.stackEntry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int assignAddress(DataType dt, PrototypePieces proto, int pos, DataTypeManager dtManager,
|
||||
int[] status, ParameterPieces res) {
|
||||
int grp = stackEntry.getGroup();
|
||||
res.type = dt;
|
||||
status[grp] = stackEntry.getAddrBySlot(status[grp], dt.getLength(), dt.getAlignment(), res);
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(Encoder encoder) throws IOException {
|
||||
encoder.openElement(ELEM_GOTO_STACK);
|
||||
encoder.closeElement(ELEM_GOTO_STACK);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreXml(XmlPullParser parser) throws XmlParseException {
|
||||
XmlElement elem = parser.start(ELEM_GOTO_STACK.name());
|
||||
parser.end(elem);
|
||||
try {
|
||||
initializeEntry();
|
||||
}
|
||||
catch (InvalidInputException e) {
|
||||
throw new XmlParseException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
/* ###
|
||||
* 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 ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.data.DataTypeManager;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.pcode.Encoder;
|
||||
import ghidra.util.exception.InvalidInputException;
|
||||
import ghidra.util.xml.SpecXmlUtils;
|
||||
import ghidra.xml.*;
|
||||
|
||||
/**
|
||||
* Allocate the return value as special input register
|
||||
*
|
||||
* The assignAddress() method signals with hiddenret_specialreg, indicating that the
|
||||
* input register assignMap() method should use storage class TYPECLASS_HIDDENRET to assign
|
||||
* an additional input register to hold a pointer to the return value. This is different than
|
||||
* the default hiddenret action that assigns a location based TYPECLASS_PTR and generally
|
||||
* consumes a general purpose input register.
|
||||
*/
|
||||
public class HiddenReturnAssign extends AssignAction {
|
||||
|
||||
private int retCode; // The specific signal to pass back
|
||||
|
||||
public HiddenReturnAssign(ParamListStandard res, boolean voidLock) {
|
||||
super(res);
|
||||
retCode = voidLock ? HIDDENRET_SPECIALREG_VOID : HIDDENRET_SPECIALREG;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AssignAction clone(ParamListStandard newResource) throws InvalidInputException {
|
||||
return new HiddenReturnAssign(newResource, retCode == HIDDENRET_SPECIALREG_VOID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEquivalent(AssignAction op) {
|
||||
if (this.getClass() != op.getClass()) {
|
||||
return false;
|
||||
}
|
||||
HiddenReturnAssign otherOp = (HiddenReturnAssign) op;
|
||||
return (retCode == otherOp.retCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int assignAddress(DataType dt, PrototypePieces proto, int pos, DataTypeManager dtManager,
|
||||
int[] status, ParameterPieces res) {
|
||||
return retCode; // Signal to assignMap to use TYPECLASS_HIDDENRET
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(Encoder encoder) throws IOException {
|
||||
encoder.openElement(ELEM_HIDDEN_RETURN);
|
||||
if (retCode == HIDDENRET_SPECIALREG_VOID) {
|
||||
encoder.writeBool(ATTRIB_VOIDLOCK, true);
|
||||
}
|
||||
encoder.closeElement(ELEM_HIDDEN_RETURN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreXml(XmlPullParser parser) throws XmlParseException {
|
||||
retCode = HIDDENRET_SPECIALREG;
|
||||
XmlElement elem = parser.start(ELEM_HIDDEN_RETURN.name());
|
||||
String voidLockString = elem.getAttribute(ATTRIB_VOIDLOCK.name());
|
||||
if (SpecXmlUtils.decodeBoolean(voidLockString)) {
|
||||
retCode = HIDDENRET_SPECIALREG_VOID;
|
||||
}
|
||||
parser.end(elem);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
/* ###
|
||||
* 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 ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.pcode.Encoder;
|
||||
import ghidra.program.model.pcode.PcodeDataTypeManager;
|
||||
import ghidra.xml.*;
|
||||
|
||||
/**
|
||||
* Filter on a homogeneous aggregate data-type
|
||||
* All primitive data-types must be the same.
|
||||
*/
|
||||
public class HomogeneousAggregate extends SizeRestrictedFilter {
|
||||
|
||||
public static final String NAME_FLOAT4 = "homogeneous-float-aggregate";
|
||||
public static final int MAX_PRIMITIVES = 4; // Maximum number of primitives in aggregate data-type
|
||||
private String name;
|
||||
private int metaType; // The expected meta-type
|
||||
private int maxPrimitives; // Maximum number of primitives in the aggregate
|
||||
|
||||
/**
|
||||
* Constructor for use with decode()
|
||||
* @param nm is the name attribute associated with the tag
|
||||
* @param meta is the expected element meta-type
|
||||
*/
|
||||
public HomogeneousAggregate(String nm, int meta) {
|
||||
name = nm;
|
||||
metaType = meta;
|
||||
maxPrimitives = 2;
|
||||
}
|
||||
|
||||
public HomogeneousAggregate(String nm, int meta, int maxPrim, int min, int max) {
|
||||
super(min, max);
|
||||
name = nm;
|
||||
metaType = meta;
|
||||
maxPrimitives = maxPrim;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DatatypeFilter clone() {
|
||||
return new HomogeneousAggregate(name, metaType, maxPrimitives, minSize, maxSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean filter(DataType dt) {
|
||||
int meta = PcodeDataTypeManager.getMetatype(dt);
|
||||
if (meta != PcodeDataTypeManager.TYPE_ARRAY && meta != PcodeDataTypeManager.TYPE_STRUCT) {
|
||||
return false;
|
||||
}
|
||||
ArrayList<DataType> res = new ArrayList<>();
|
||||
if (!DatatypeFilter.extractPrimitives(dt, MAX_PRIMITIVES, null, res)) {
|
||||
return false;
|
||||
}
|
||||
DataType base = res.get(0);
|
||||
int baseMeta = PcodeDataTypeManager.getMetatype(base);
|
||||
if (baseMeta != metaType) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 1; i < res.size(); ++i) {
|
||||
if (res.get(i) != base) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(Encoder encoder) throws IOException {
|
||||
encoder.openElement(ELEM_DATATYPE);
|
||||
encoder.writeString(ATTRIB_NAME, name);
|
||||
encodeAttributes(encoder);
|
||||
encoder.closeElement(ELEM_DATATYPE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreXml(XmlPullParser parser) throws XmlParseException {
|
||||
XmlElement elem = parser.start(ELEM_DATATYPE.name());
|
||||
restoreAttributesXml(elem);
|
||||
parser.end(elem);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
/* ###
|
||||
* 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 ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.pcode.Encoder;
|
||||
import ghidra.program.model.pcode.PcodeDataTypeManager;
|
||||
import ghidra.xml.*;
|
||||
|
||||
/**
|
||||
* Filter on a single meta data-type. Filters on TYPE_STRUCT or TYPE_FLOAT etc.
|
||||
* Additional filtering on size of the data-type can be configured.
|
||||
*/
|
||||
public class MetaTypeFilter extends SizeRestrictedFilter {
|
||||
|
||||
protected int metaType; // The meta-type this filter lets through
|
||||
|
||||
/**
|
||||
* Constructor for use with decode().
|
||||
* @param meta is the data-type metatype to filter on
|
||||
*/
|
||||
public MetaTypeFilter(int meta) {
|
||||
metaType = meta;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param meta is the data-type metatype to filter on
|
||||
* @param min is the minimum size in bytes
|
||||
* @param max is the maximum size in bytes
|
||||
*/
|
||||
public MetaTypeFilter(int meta, int min, int max) {
|
||||
super(min, max);
|
||||
metaType = meta;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEquivalent(DatatypeFilter op) {
|
||||
if (!super.isEquivalent(op)) {
|
||||
return false;
|
||||
}
|
||||
if (this.getClass() != op.getClass()) {
|
||||
return false;
|
||||
}
|
||||
MetaTypeFilter otherFilter = (MetaTypeFilter) op;
|
||||
if (metaType != otherFilter.metaType) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DatatypeFilter clone() {
|
||||
return new MetaTypeFilter(metaType, minSize, maxSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean filter(DataType dt) {
|
||||
if (PcodeDataTypeManager.getMetatype(dt) != metaType) {
|
||||
return false;
|
||||
}
|
||||
return filterOnSize(dt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(Encoder encoder) throws IOException {
|
||||
encoder.openElement(ELEM_DATATYPE);
|
||||
String meta = PcodeDataTypeManager.getMetatypeString(metaType);
|
||||
encoder.writeString(ATTRIB_NAME, meta);
|
||||
encodeAttributes(encoder);
|
||||
encoder.closeElement(ELEM_DATATYPE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreXml(XmlPullParser parser) throws XmlParseException {
|
||||
XmlElement elem = parser.start(ELEM_DATATYPE.name());
|
||||
metaType = PcodeDataTypeManager.getMetatype(elem.getAttribute(ATTRIB_NAME.name()));
|
||||
restoreAttributesXml(elem);
|
||||
parser.end(elem);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,239 @@
|
|||
/* ###
|
||||
* 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.ElementId.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.data.DataTypeManager;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.pcode.Encoder;
|
||||
import ghidra.util.exception.InvalidInputException;
|
||||
import ghidra.xml.*;
|
||||
|
||||
/**
|
||||
* A rule controlling how parameters are assigned addresses
|
||||
*
|
||||
* Rules are applied to a parameter in the context of a full function prototype.
|
||||
* A rule applies only for a specific class of data-type associated with the parameter, as
|
||||
* determined by its DatatypeFilter, and may have other criteria limiting when it applies
|
||||
* (via QualifierFilter).
|
||||
*/
|
||||
public class ModelRule {
|
||||
private DatatypeFilter filter; // Which data-types this rule applies to
|
||||
private QualifierFilter qualifier; // Additional qualifiers for when the rule should apply (if non-null)
|
||||
private AssignAction assign; // How the Address should be assigned
|
||||
private AssignAction[] sideeffects; // Extra actions that happen on success
|
||||
|
||||
public ModelRule() {
|
||||
filter = null;
|
||||
qualifier = null;
|
||||
assign = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy constructor
|
||||
* @param op2 is the ModelRule to copy from
|
||||
* @param res is the new resource set to associate with the copy
|
||||
* @throws InvalidInputException if necessary resources are not present in the resource set
|
||||
*/
|
||||
public ModelRule(ModelRule op2, ParamListStandard res) throws InvalidInputException {
|
||||
if (op2.filter != null) {
|
||||
filter = op2.filter.clone();
|
||||
}
|
||||
else {
|
||||
filter = null;
|
||||
}
|
||||
if (op2.qualifier != null) {
|
||||
qualifier = op2.qualifier.clone();
|
||||
}
|
||||
else {
|
||||
qualifier = null;
|
||||
}
|
||||
if (op2.assign != null) {
|
||||
assign = op2.assign.clone(res);
|
||||
}
|
||||
else {
|
||||
assign = null;
|
||||
}
|
||||
sideeffects = new AssignAction[op2.sideeffects.length];
|
||||
for (int i = 0; i < op2.sideeffects.length; ++i) {
|
||||
sideeffects[i] = op2.sideeffects[i].clone(res);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct from components
|
||||
*
|
||||
* The provided components are cloned into the new object.
|
||||
* @param typeFilter is the data-type filter the rule applies before performing the action
|
||||
* @param action is the action that will be applied
|
||||
* @param res is the resource list to which this rule will be applied
|
||||
* @throws InvalidInputException if necessary resources are missing from the list
|
||||
*/
|
||||
public ModelRule(DatatypeFilter typeFilter, AssignAction action, ParamListStandard res)
|
||||
throws InvalidInputException
|
||||
|
||||
{
|
||||
filter = typeFilter.clone();
|
||||
qualifier = null;
|
||||
assign = action.clone(res);
|
||||
sideeffects = new AssignAction[0];
|
||||
}
|
||||
|
||||
public boolean isEquivalent(ModelRule op) {
|
||||
if (assign == null && op.assign == null) {
|
||||
// Nothing to compare
|
||||
}
|
||||
else if (assign != null && op.assign != null) {
|
||||
if (!assign.isEquivalent(op.assign)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
if (filter == null && op.filter == null) {
|
||||
// Nothing to compare
|
||||
}
|
||||
else if (filter != null && op.filter != null) {
|
||||
if (!filter.isEquivalent(op.filter)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
if (qualifier == null && op.qualifier == null) {
|
||||
// Nothing to compare
|
||||
}
|
||||
else if (qualifier != null && op.qualifier != null) {
|
||||
if (!qualifier.isEquivalent(op.qualifier)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
if (sideeffects.length != op.sideeffects.length) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < sideeffects.length; ++i) {
|
||||
if (!sideeffects[i].isEquivalent(op.sideeffects[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign an address and other details for a specific parameter or for return storage in context
|
||||
*
|
||||
* The Address is only assigned if the data-type filter and the optional qualifier filter
|
||||
* pass, otherwise a FAIL response is returned.
|
||||
* If the filters pass, the Address is assigned based on the AssignAction specific to
|
||||
* this rule, and the action's response code is returned.
|
||||
* @param dt is the data-type of the parameter or return value
|
||||
* @param proto is the high-level description of the function prototype
|
||||
* @param pos is the position of the parameter (pos>=0) or return storage (pos=-1)
|
||||
* @param dtManager is a data-type manager for (possibly) transforming the data-type
|
||||
* @param status is the resource consumption array
|
||||
* @param res will hold the resulting description of the parameter
|
||||
* @return the response code
|
||||
*/
|
||||
public int assignAddress(DataType dt, PrototypePieces proto, int pos, DataTypeManager dtManager,
|
||||
int[] status, ParameterPieces res) {
|
||||
if (!filter.filter(dt)) {
|
||||
return AssignAction.FAIL;
|
||||
}
|
||||
if (qualifier != null && !qualifier.filter(proto, pos)) {
|
||||
return AssignAction.FAIL;
|
||||
}
|
||||
int response = assign.assignAddress(dt, proto, pos, dtManager, status, res);
|
||||
if (response != AssignAction.FAIL) {
|
||||
for (int i = 0; i < sideeffects.length; ++i) {
|
||||
sideeffects[i].assignAddress(dt, proto, pos, dtManager, status, res);
|
||||
}
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode this rule to a stream
|
||||
* @param encoder is the stream encode
|
||||
* @throws IOException for problems with the stream
|
||||
*/
|
||||
public void encode(Encoder encoder) throws IOException {
|
||||
encoder.openElement(ELEM_RULE);
|
||||
filter.encode(encoder);
|
||||
if (qualifier != null) {
|
||||
qualifier.encode(encoder);
|
||||
}
|
||||
assign.encode(encoder);
|
||||
for (int i = 0; i < sideeffects.length; ++i) {
|
||||
sideeffects[i].encode(encoder);
|
||||
}
|
||||
encoder.closeElement(ELEM_RULE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode this rule from stream
|
||||
*
|
||||
* @param parser is the stream decoder
|
||||
* @param res is the parameter resource list owning this rule
|
||||
* @throws XmlParseException if there are problems decoding are missing resources
|
||||
*/
|
||||
public void restoreXml(XmlPullParser parser, ParamListStandard res) throws XmlParseException
|
||||
|
||||
{
|
||||
XmlElement elemId = parser.start(ELEM_RULE.name());
|
||||
filter = DatatypeFilter.restoreFilterXml(parser);
|
||||
ArrayList<QualifierFilter> qualifierList = new ArrayList<>();
|
||||
for (;;) {
|
||||
QualifierFilter tmpFilter = QualifierFilter.restoreFilterXml(parser);
|
||||
if (tmpFilter == null) {
|
||||
break;
|
||||
}
|
||||
qualifierList.add(tmpFilter);
|
||||
}
|
||||
if (qualifierList.size() == 0) {
|
||||
qualifier = null;
|
||||
}
|
||||
else if (qualifierList.size() == 1) {
|
||||
qualifier = qualifierList.get(0);
|
||||
qualifierList.clear();
|
||||
}
|
||||
else {
|
||||
qualifier = new AndFilter(qualifierList);
|
||||
}
|
||||
assign = AssignAction.restoreActionXml(parser, res);
|
||||
ArrayList<AssignAction> sideList = new ArrayList<>();
|
||||
for (;;) {
|
||||
XmlElement subEl = parser.peek();
|
||||
if (!subEl.isStart()) {
|
||||
break;
|
||||
}
|
||||
sideList.add(AssignAction.restoreSideeffectXml(parser, res));
|
||||
}
|
||||
sideeffects = new AssignAction[sideList.size()];
|
||||
sideList.toArray(sideeffects);
|
||||
parser.end(elemId);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
/* ###
|
||||
* 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 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.pcode.Encoder;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
import ghidra.util.exception.InvalidInputException;
|
||||
import ghidra.xml.*;
|
||||
|
||||
/**
|
||||
* Consume a register per primitive member of an aggregate data-type
|
||||
*
|
||||
* The data-type is split up into its underlying primitive elements, and each one
|
||||
* is assigned a register from the specific resource list. There must be no padding between
|
||||
* elements. No packing of elements into a single register occurs.
|
||||
*/
|
||||
public class MultiMemberAssign extends AssignAction {
|
||||
|
||||
private StorageClass resourceType; // Resource list from which to consume
|
||||
private boolean consumeFromStack; // True if resources should be consumed from the stack
|
||||
private boolean consumeMostSig; // True if resources are consumed starting with most significant bytes
|
||||
|
||||
public MultiMemberAssign(StorageClass store, boolean stack, boolean mostSig,
|
||||
ParamListStandard res) {
|
||||
super(res);
|
||||
resourceType = store;
|
||||
consumeFromStack = stack;
|
||||
consumeMostSig = mostSig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AssignAction clone(ParamListStandard newResource) throws InvalidInputException {
|
||||
return new MultiMemberAssign(resourceType, consumeFromStack, consumeMostSig, newResource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEquivalent(AssignAction op) {
|
||||
if (this.getClass() != op.getClass()) {
|
||||
return false;
|
||||
}
|
||||
MultiMemberAssign otherOp = (MultiMemberAssign) op;
|
||||
if (resourceType != otherOp.resourceType) {
|
||||
return false;
|
||||
}
|
||||
if (consumeFromStack != otherOp.consumeFromStack) {
|
||||
return false;
|
||||
}
|
||||
return consumeMostSig == otherOp.consumeMostSig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int assignAddress(DataType dt, PrototypePieces proto, int pos, DataTypeManager dtManager,
|
||||
int[] status, ParameterPieces res) {
|
||||
int[] tmpStatus = status.clone();
|
||||
ArrayList<Varnode> pieces = new ArrayList<>();
|
||||
ParameterPieces param = new ParameterPieces();
|
||||
ArrayList<DataType> primitives = new ArrayList<>();
|
||||
if (!DatatypeFilter.extractPrimitives(dt, 16, null, primitives)) {
|
||||
return FAIL;
|
||||
}
|
||||
for (int i = 0; i < primitives.size(); ++i) {
|
||||
DataType curType = primitives.get(i);
|
||||
if (resource.assignAddressFallback(resourceType, curType, !consumeFromStack, tmpStatus,
|
||||
param) == FAIL) {
|
||||
return FAIL;
|
||||
}
|
||||
Varnode vn = new Varnode(param.address, curType.getLength());
|
||||
pieces.add(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; // Placeholder for join space address
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(Encoder encoder) throws IOException {
|
||||
encoder.openElement(ELEM_JOIN_PER_PRIMITIVE);
|
||||
if (resourceType != StorageClass.GENERAL) {
|
||||
encoder.writeString(ATTRIB_STORAGE, resourceType.toString());
|
||||
}
|
||||
encoder.closeElement(ELEM_JOIN_PER_PRIMITIVE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreXml(XmlPullParser parser) throws XmlParseException {
|
||||
XmlElement elem = parser.start(ELEM_JOIN_PER_PRIMITIVE.name());
|
||||
String attribString = elem.getAttribute(ATTRIB_STORAGE.name());
|
||||
if (attribString != null) {
|
||||
resourceType = StorageClass.getClass(attribString);
|
||||
}
|
||||
parser.end(elem);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,289 @@
|
|||
/* ###
|
||||
* 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.Iterator;
|
||||
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.pcode.Encoder;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
import ghidra.util.exception.InvalidInputException;
|
||||
import ghidra.util.xml.SpecXmlUtils;
|
||||
import ghidra.xml.*;
|
||||
|
||||
/**
|
||||
* Consume multiple registers to pass a data-type
|
||||
*
|
||||
* Available registers are consumed until the data-type is covered, and an appropriate
|
||||
* join space address is assigned. Registers can be consumed from a specific resource list.
|
||||
* Consumption can spill over onto the stack if desired.
|
||||
*/
|
||||
public class MultiSlotAssign extends AssignAction {
|
||||
private StorageClass resourceType; // Resource list from which to consume
|
||||
private boolean consumeFromStack; // True if resources should be consumed from the stack
|
||||
private boolean consumeMostSig; // True if resources are consumed starting with most significant bytes
|
||||
private boolean enforceAlignment; // True if register resources are discarded to match alignment
|
||||
private boolean justifyRight; // True if initial bytes are padding for odd data-type sizes
|
||||
private ParamEntry stackEntry; // The stack resource
|
||||
private int firstIter; // Iterator to first element in the resource list
|
||||
|
||||
/**
|
||||
* Cache specific ParamEntry needed by the action
|
||||
*
|
||||
* Find the first ParamEntry matching the resourceType, and the ParamEntry
|
||||
* corresponding to the stack if consumeFromStack is set.
|
||||
* @throws InvalidInputException if the required elements are not available in the resource list
|
||||
*/
|
||||
private void initializeEntries() throws InvalidInputException {
|
||||
firstIter = -1;
|
||||
for (int i = 0; i < resource.getNumParamEntry(); ++i) {
|
||||
ParamEntry entry = resource.getEntry(i);
|
||||
if (firstIter == -1 && entry.isExclusion() && entry.getType() == resourceType &&
|
||||
entry.getAllGroups().length == 1) {
|
||||
firstIter = i; // First matching resource size
|
||||
}
|
||||
if (!entry.isExclusion() && entry.getSpace().isStackSpace()) {
|
||||
stackEntry = entry;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (firstIter == -1) {
|
||||
throw new InvalidInputException("Could not find matching resources for action: join");
|
||||
}
|
||||
if (consumeFromStack && stackEntry == null) {
|
||||
throw new InvalidInputException("Cannot find matching <pentry> for action: join");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for use with restoreXml
|
||||
* @param res is the new resource set to associate with this action
|
||||
*/
|
||||
protected MultiSlotAssign(ParamListStandard res) {
|
||||
super(res);
|
||||
resourceType = StorageClass.GENERAL; // Join general purpose registers
|
||||
consumeFromStack = !(res instanceof ParamListStandardOut); // Spill into stack by default
|
||||
consumeMostSig = false;
|
||||
enforceAlignment = false;
|
||||
justifyRight = false;
|
||||
if (res.getEntry(0).isBigEndian()) {
|
||||
consumeMostSig = true;
|
||||
justifyRight = true;
|
||||
}
|
||||
stackEntry = null;
|
||||
}
|
||||
|
||||
public MultiSlotAssign(StorageClass store, boolean stack, boolean mostSig, boolean align,
|
||||
boolean justRight, ParamListStandard res) throws InvalidInputException {
|
||||
super(res);
|
||||
resourceType = store;
|
||||
consumeFromStack = stack;
|
||||
consumeMostSig = mostSig;
|
||||
enforceAlignment = align;
|
||||
justifyRight = justRight;
|
||||
stackEntry = null;
|
||||
initializeEntries();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AssignAction clone(ParamListStandard newResource) throws InvalidInputException {
|
||||
return new MultiSlotAssign(resourceType, consumeFromStack, consumeMostSig, enforceAlignment,
|
||||
justifyRight, newResource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEquivalent(AssignAction op) {
|
||||
if (this.getClass() != op.getClass()) {
|
||||
return false;
|
||||
}
|
||||
MultiSlotAssign otherAction = (MultiSlotAssign) op;
|
||||
if (consumeFromStack != otherAction.consumeFromStack ||
|
||||
consumeMostSig != otherAction.consumeMostSig ||
|
||||
enforceAlignment != otherAction.enforceAlignment) {
|
||||
return false;
|
||||
}
|
||||
if (firstIter != otherAction.firstIter || justifyRight != otherAction.justifyRight) {
|
||||
return false;
|
||||
}
|
||||
if (resourceType != otherAction.resourceType) {
|
||||
return false;
|
||||
}
|
||||
if (stackEntry == null && otherAction.stackEntry == null) {
|
||||
// Nothing to compare
|
||||
}
|
||||
else if (stackEntry != null && otherAction.stackEntry != null) {
|
||||
if (!stackEntry.isEquivalent(otherAction.stackEntry)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int assignAddress(DataType dt, PrototypePieces proto, int pos, DataTypeManager dtManager,
|
||||
int[] status, ParameterPieces res) {
|
||||
int[] tmpStatus = status.clone();
|
||||
ArrayList<Varnode> pieces = new ArrayList<>();
|
||||
ParameterPieces param = new ParameterPieces();
|
||||
int sizeLeft = dt.getLength();
|
||||
int iter = firstIter;
|
||||
int endIter = resource.getNumParamEntry();
|
||||
if (enforceAlignment) {
|
||||
int resourcesConsumed = 0;
|
||||
while (iter != endIter) {
|
||||
ParamEntry entry = resource.getEntry(iter);
|
||||
if (!entry.isExclusion()) {
|
||||
break;
|
||||
} // Reached end of resource list
|
||||
if (entry.getType() == resourceType && entry.getAllGroups().length == 1) { // Single register
|
||||
if (tmpStatus[entry.getGroup()] == 0) { // Not consumed
|
||||
int align = dt.getAlignment();
|
||||
int regSize = entry.getSize();
|
||||
if (align <= regSize || (resourcesConsumed % align) == 0) {
|
||||
break;
|
||||
}
|
||||
tmpStatus[entry.getGroup()] = -1; // Consume unaligned register
|
||||
}
|
||||
resourcesConsumed += entry.getSize();
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
while (sizeLeft > 0 && iter != endIter) {
|
||||
ParamEntry entry = resource.getEntry(iter);
|
||||
++iter;
|
||||
if (!entry.isExclusion()) {
|
||||
break;
|
||||
} // Reached end of resource list
|
||||
if (entry.getType() != resourceType || entry.getAllGroups().length != 1) {
|
||||
continue;
|
||||
} // Not a single register from desired resource list
|
||||
if (tmpStatus[entry.getGroup()] != 0) {
|
||||
continue;
|
||||
} // Already consumed
|
||||
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;
|
||||
}
|
||||
boolean onePieceJoin = false;
|
||||
if (sizeLeft > 0) { // Have to use stack to get enough bytes
|
||||
if (!consumeFromStack) {
|
||||
return FAIL;
|
||||
}
|
||||
int grp = stackEntry.getGroup();
|
||||
tmpStatus[grp] = stackEntry.getAddrBySlot(tmpStatus[grp], sizeLeft, 1, param); // Consume all the space we need
|
||||
Varnode vn = new Varnode(param.address, sizeLeft);
|
||||
pieces.add(vn);
|
||||
}
|
||||
else if (sizeLeft < 0) { // Have odd data-type size
|
||||
if (resourceType == StorageClass.FLOAT && pieces.size() == 1) {
|
||||
// Floating-point register holding extended lower precision value
|
||||
onePieceJoin = true; // Treat as "join" of full size register
|
||||
}
|
||||
else if (justifyRight) {
|
||||
// Initial bytes 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 && !onePieceJoin) {
|
||||
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; // Placeholder for join space address
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(Encoder encoder) throws IOException {
|
||||
encoder.openElement(ELEM_JOIN);
|
||||
if (resource.getEntry(0).isBigEndian() != justifyRight) {
|
||||
encoder.writeBool(ATTRIB_REVERSEJUSTIFY, true);
|
||||
}
|
||||
if (resourceType != StorageClass.GENERAL) {
|
||||
encoder.writeString(ATTRIB_STORAGE, resourceType.toString());
|
||||
}
|
||||
encoder.writeBool(ATTRIB_ALIGN, enforceAlignment);
|
||||
encoder.closeElement(ELEM_JOIN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreXml(XmlPullParser parser) throws XmlParseException {
|
||||
XmlElement elem = parser.start(ELEM_JOIN.name());
|
||||
Iterator<Entry<String, String>> iter = elem.getAttributes().entrySet().iterator();
|
||||
while (iter.hasNext()) {
|
||||
Entry<String, String> attrib = iter.next();
|
||||
String name = attrib.getKey();
|
||||
if (name.equals(ATTRIB_REVERSEJUSTIFY.name())) {
|
||||
if (SpecXmlUtils.decodeBoolean(attrib.getValue())) {
|
||||
justifyRight = !justifyRight;
|
||||
}
|
||||
}
|
||||
else if (name.equals(ATTRIB_STORAGE.name())) {
|
||||
resourceType = StorageClass.getClass(attrib.getValue());
|
||||
}
|
||||
else if (name.equals(ATTRIB_ALIGN.name())) {
|
||||
enforceAlignment = SpecXmlUtils.decodeBoolean(attrib.getValue());
|
||||
}
|
||||
}
|
||||
parser.end(elem);
|
||||
try {
|
||||
initializeEntries();
|
||||
}
|
||||
catch (InvalidInputException e) {
|
||||
throw new XmlParseException(e.getMessage());
|
||||
} // Need new firstIter
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
/* ###
|
||||
* 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 ghidra.program.model.lang.PrototypePieces;
|
||||
import ghidra.program.model.pcode.Encoder;
|
||||
import ghidra.util.xml.SpecXmlUtils;
|
||||
import ghidra.xml.*;
|
||||
|
||||
/**
|
||||
* Filter that selects for a particular parameter position.
|
||||
* This matches if the position of the current parameter being assigned, within the data-type
|
||||
* list, matches the position attribute of this filter.
|
||||
*/
|
||||
public class PositionMatchFilter implements QualifierFilter {
|
||||
|
||||
private int position; // Parameter position being filtered for
|
||||
|
||||
public PositionMatchFilter(int pos) {
|
||||
position = pos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QualifierFilter clone() {
|
||||
return new PositionMatchFilter(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEquivalent(QualifierFilter op) {
|
||||
if (this.getClass() != op.getClass()) {
|
||||
return false;
|
||||
}
|
||||
PositionMatchFilter otherFilter = (PositionMatchFilter) op;
|
||||
if (position != otherFilter.position) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean filter(PrototypePieces proto, int pos) {
|
||||
return (pos == position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(Encoder encoder) throws IOException {
|
||||
encoder.openElement(ELEM_POSITION);
|
||||
encoder.writeSignedInteger(ATTRIB_INDEX, position);
|
||||
encoder.closeElement(ELEM_POSITION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreXml(XmlPullParser parser) throws XmlParseException {
|
||||
XmlElement elem = parser.start(ELEM_POSITION.name());
|
||||
position = SpecXmlUtils.decodeInt(elem.getAttribute(ATTRIB_INDEX.name()));
|
||||
parser.end(elem);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
/* ###
|
||||
* 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.ElementId.*;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import ghidra.program.model.lang.PrototypePieces;
|
||||
import ghidra.program.model.pcode.Encoder;
|
||||
import ghidra.xml.*;
|
||||
|
||||
/**
|
||||
* A filter on some aspect of a specific function prototype.
|
||||
* An instance is configured via the restoreXml() method, then a test of whether
|
||||
* a function prototype meets its criteria can be performed by calling its filter() method.
|
||||
*/
|
||||
public interface QualifierFilter {
|
||||
/**
|
||||
* Make a copy of this qualifier
|
||||
* @return the copy
|
||||
*/
|
||||
public QualifierFilter clone();
|
||||
|
||||
/**
|
||||
* Test if the given filter is configured and performs identically to this
|
||||
* @param op is the given filter
|
||||
* @return true if the two filters are equivalent
|
||||
*/
|
||||
public boolean isEquivalent(QualifierFilter op);
|
||||
|
||||
/**
|
||||
* Test whether the given function prototype meets this filter's criteria
|
||||
* @param proto is the high-level description of the function prototype to test
|
||||
* @param pos is the position of a specific output (pos=-1) or input (pos >=0) in context
|
||||
* @return true if the prototype meets the criteria, false otherwise
|
||||
*/
|
||||
public boolean filter(PrototypePieces proto, int pos);
|
||||
|
||||
/**
|
||||
* Save this filter and its configuration to a stream
|
||||
* @param encoder is the stream encoder
|
||||
* @throws IOException for problems writing to the stream
|
||||
*/
|
||||
public void encode(Encoder encoder) throws IOException;
|
||||
|
||||
/**
|
||||
* Configure details of the criteria being filtered from the given stream
|
||||
* @param parser is the given stream decoder
|
||||
* @throws XmlParseException if there are problems with the stream
|
||||
*/
|
||||
public void restoreXml(XmlPullParser parser) throws XmlParseException;
|
||||
|
||||
/**
|
||||
* Instantiate a qualifier from the stream. If the next element is not a qualifier,
|
||||
* return null.
|
||||
* @param parser is the given stream decoder
|
||||
* @return the new qualifier instance or null
|
||||
* @throws XmlParseException for problems decoding the stream
|
||||
*/
|
||||
public static QualifierFilter restoreFilterXml(XmlPullParser parser) throws XmlParseException {
|
||||
QualifierFilter filter;
|
||||
XmlElement elemId = parser.peek();
|
||||
String nm = elemId.getName();
|
||||
if (nm.equals(ELEM_VARARGS.name())) {
|
||||
filter = new VarargsFilter();
|
||||
}
|
||||
else if (nm.equals(ELEM_POSITION.name())) {
|
||||
filter = new PositionMatchFilter(-1);
|
||||
}
|
||||
else if (nm.equals(ELEM_DATATYPE_AT.name())) {
|
||||
filter = new DatatypeMatchFilter();
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
filter.restoreXml(parser);
|
||||
return filter;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
/* ###
|
||||
* 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.Iterator;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.pcode.Encoder;
|
||||
import ghidra.util.xml.SpecXmlUtils;
|
||||
import ghidra.xml.*;
|
||||
|
||||
/**
|
||||
* A common base class for data-type filters that tests for a size range.
|
||||
* Any filter that inherits from this, can use ATTRIB_MINSIZE and ATTRIB_MAXSIZE
|
||||
* to place bounds on the possible sizes of data-types. The bounds are enforced
|
||||
* by calling filterOnSize() within the inheriting classes filter() method.
|
||||
*/
|
||||
public class SizeRestrictedFilter implements DatatypeFilter {
|
||||
|
||||
public static final String NAME = "any";
|
||||
|
||||
protected int minSize; // Minimum size of the data-type in bytes
|
||||
protected int maxSize; // Maximum size of the data-type in bytes
|
||||
|
||||
public SizeRestrictedFilter() {
|
||||
minSize = 0;
|
||||
maxSize = 0;
|
||||
}
|
||||
|
||||
public SizeRestrictedFilter(int min, int max) {
|
||||
minSize = min;
|
||||
maxSize = max;
|
||||
if (maxSize == 0 && minSize >= 0) {
|
||||
// If no ATTRIB_MAXSIZE is given, assume there is no upper bound on size
|
||||
maxSize = 0x7fffffff;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enforce any size bounds on a given data-type.
|
||||
* If \b maxSize is not zero, the data-type is checked to see if its size in bytes
|
||||
* falls between \b minSize and \b maxSize inclusive.
|
||||
* @param dt is the data-type to test
|
||||
* @return true if the data-type meets the size restrictions
|
||||
*/
|
||||
public boolean filterOnSize(DataType dt) {
|
||||
if (maxSize == 0) {
|
||||
return true; // maxSize of 0 means no size filtering is performed
|
||||
}
|
||||
return (dt.getLength() >= minSize && dt.getLength() <= maxSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DatatypeFilter clone() {
|
||||
return new SizeRestrictedFilter(minSize, maxSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEquivalent(DatatypeFilter op) {
|
||||
if (this.getClass() != op.getClass()) {
|
||||
return false;
|
||||
}
|
||||
SizeRestrictedFilter otherFilter = (SizeRestrictedFilter) op;
|
||||
if (maxSize != otherFilter.maxSize || minSize != otherFilter.minSize) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean filter(DataType dt) {
|
||||
return filterOnSize(dt);
|
||||
}
|
||||
|
||||
protected void encodeAttributes(Encoder encoder) throws IOException {
|
||||
encoder.writeUnsignedInteger(ATTRIB_MINSIZE, minSize);
|
||||
encoder.writeUnsignedInteger(ATTRIB_MAXSIZE, maxSize);
|
||||
}
|
||||
|
||||
protected void restoreAttributesXml(XmlElement el) {
|
||||
Iterator<Entry<String, String>> iter = el.getAttributes().entrySet().iterator();
|
||||
while (iter.hasNext()) {
|
||||
Entry<String, String> attrib = iter.next();
|
||||
String nm = attrib.getKey();
|
||||
if (nm.equals(ATTRIB_MINSIZE.name())) {
|
||||
minSize = SpecXmlUtils.decodeInt(attrib.getValue());
|
||||
}
|
||||
else if (nm.equals(ATTRIB_MAXSIZE.name())) {
|
||||
maxSize = SpecXmlUtils.decodeInt(attrib.getValue());
|
||||
}
|
||||
}
|
||||
if (maxSize == 0 && minSize >= 0) {
|
||||
// If no ATTRIB_MAXSIZE is given, assume there is no upper bound on size
|
||||
maxSize = 0x7fffffff;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(Encoder encoder) throws IOException {
|
||||
encoder.openElement(ELEM_DATATYPE);
|
||||
encoder.writeString(ATTRIB_NAME, NAME);
|
||||
encodeAttributes(encoder);
|
||||
encoder.closeElement(ELEM_DATATYPE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreXml(XmlPullParser parser) throws XmlParseException {
|
||||
XmlElement elem = parser.start(ELEM_DATATYPE.name());
|
||||
restoreAttributesXml(elem);
|
||||
parser.end(elem);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
/* ###
|
||||
* 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.ElementId.*;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import ghidra.program.model.lang.PrototypePieces;
|
||||
import ghidra.program.model.pcode.Encoder;
|
||||
import ghidra.xml.*;
|
||||
|
||||
/**
|
||||
* A filter that selects function parameters that are considered optional.
|
||||
* If the underlying function prototype is considered to take variable arguments, the first
|
||||
* n parameters (as determined by PrototypePieces.firstVarArgSlot) are considered non-optional.
|
||||
* If additional data-types are provided beyond the initial n, these are considered optional.
|
||||
* This filter returns true for these optional parameters
|
||||
*/
|
||||
public class VarargsFilter implements QualifierFilter {
|
||||
@Override
|
||||
public QualifierFilter clone() {
|
||||
return new VarargsFilter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEquivalent(QualifierFilter op) {
|
||||
return (this.getClass() == op.getClass());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean filter(PrototypePieces proto, int pos) {
|
||||
if (proto.firstVarArgSlot < 0) {
|
||||
return false;
|
||||
}
|
||||
return (pos >= proto.firstVarArgSlot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(Encoder encoder) throws IOException {
|
||||
encoder.openElement(ELEM_VARARGS);
|
||||
encoder.closeElement(ELEM_VARARGS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreXml(XmlPullParser parser) throws XmlParseException {
|
||||
XmlElement elem = parser.start(ELEM_VARARGS.name());
|
||||
parser.end(elem);
|
||||
}
|
||||
}
|
|
@ -236,5 +236,7 @@ public record AttributeId(String name, int id) {
|
|||
// public static final AttributeId ATTRIB_VERSION = new AttributeId("version", 144);
|
||||
|
||||
// public static final AttributeId ATTRIB_ADDRESS = new AttributeId("address", 148);
|
||||
public static final AttributeId ATTRIB_UNKNOWN = new AttributeId("XMLunknown", 149);
|
||||
public static final AttributeId ATTRIB_STORAGE = new AttributeId("storage", 149);
|
||||
|
||||
public static final AttributeId ATTRIB_UNKNOWN = new AttributeId("XMLunknown", 150);
|
||||
}
|
||||
|
|
|
@ -424,5 +424,20 @@ public record ElementId(String name, int id) {
|
|||
public static final ElementId ELEM_SPLITDATATYPE = new ElementId("splitdatatype", 270);
|
||||
public static final ElementId ELEM_JUMPTABLEMAX = new ElementId("jumptablemax", 271);
|
||||
public static final ElementId ELEM_NANIGNORE = new ElementId("nanignore", 272);
|
||||
public static final ElementId ELEM_UNKNOWN = new ElementId("XMLunknown", 273);
|
||||
|
||||
// modelrules
|
||||
public static final ElementId ELEM_DATATYPE = new ElementId("datatype", 273);
|
||||
public static final ElementId ELEM_CONSUME = new ElementId("consume", 274);
|
||||
public static final ElementId ELEM_CONSUME_EXTRA = new ElementId("consume_extra", 275);
|
||||
public static final ElementId ELEM_CONVERT_TO_PTR = new ElementId("convert_to_ptr", 276);
|
||||
public static final ElementId ELEM_GOTO_STACK = new ElementId("goto_stack", 277);
|
||||
public static final ElementId ELEM_JOIN = new ElementId("join", 278);
|
||||
public static final ElementId ELEM_DATATYPE_AT = new ElementId("datatype_at", 279);
|
||||
public static final ElementId ELEM_POSITION = new ElementId("position", 280);
|
||||
public static final ElementId ELEM_VARARGS = new ElementId("varargs", 281);
|
||||
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_UNKNOWN = new ElementId("XMLunknown", 284);
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ import ghidra.program.model.lang.DecompilerLanguage;
|
|||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.symbol.NameTransformer;
|
||||
import ghidra.util.UniversalID;
|
||||
import ghidra.xml.XmlParseException;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -51,6 +52,20 @@ public class PcodeDataTypeManager {
|
|||
private static final long DEFAULT_DECOMPILER_ID = 0xC000000000000000L; // ID for "undefined" (decompiler side)
|
||||
private static final long CODE_DECOMPILER_ID = 0xE000000000000001L; // ID for internal "code" data-type
|
||||
|
||||
public static final int TYPE_VOID = 14; // Standard "void" type, absence of type
|
||||
public static final int TYPE_UNKNOWN = 12; // An unknown low-level type. Treated as an unsigned integer.
|
||||
public static final int TYPE_INT = 11; // Signed integer. Signed is considered less specific than unsigned in C
|
||||
public static final int TYPE_UINT = 10; // Unsigned integer
|
||||
public static final int TYPE_BOOL = 9; // Boolean
|
||||
public static final int TYPE_CODE = 8; // Data is actual executable code
|
||||
public static final int TYPE_FLOAT = 7; // Floating-point
|
||||
|
||||
public static final int TYPE_PTR = 6; // Pointer data-type
|
||||
public static final int TYPE_PTRREL = 5; // Pointer relative to another data-type (specialization of TYPE_PTR)
|
||||
public static final int TYPE_ARRAY = 4; // Array data-type, made up of a sequence of "element" datatype
|
||||
public static final int TYPE_STRUCT = 3; // Structure data-type, made up of component datatypes
|
||||
public static final int TYPE_UNION = 2; // An overlapping union of multiple datatypes
|
||||
|
||||
/**
|
||||
* A mapping between a DataType and its (name,id) on the decompiler side
|
||||
*/
|
||||
|
@ -1238,4 +1253,147 @@ public class PcodeDataTypeManager {
|
|||
}
|
||||
encoder.closeElement(ELEM_CORETYPES);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the decompiler meta-type associated with a data-type.
|
||||
* @param tp is the data-type
|
||||
* @return the meta-type
|
||||
*/
|
||||
public static int getMetatype(DataType tp) {
|
||||
if (tp instanceof TypeDef) {
|
||||
tp = ((TypeDef) tp).getBaseDataType();
|
||||
}
|
||||
if (tp instanceof Undefined) {
|
||||
return TYPE_UNKNOWN;
|
||||
}
|
||||
if (tp instanceof AbstractFloatDataType) {
|
||||
return TYPE_FLOAT;
|
||||
}
|
||||
if (tp instanceof Pointer) {
|
||||
return TYPE_PTR;
|
||||
}
|
||||
if (tp instanceof BooleanDataType) {
|
||||
return TYPE_BOOL;
|
||||
}
|
||||
if (tp instanceof AbstractSignedIntegerDataType) {
|
||||
return TYPE_INT;
|
||||
}
|
||||
if (tp instanceof AbstractUnsignedIntegerDataType) {
|
||||
return TYPE_UINT;
|
||||
}
|
||||
if (tp instanceof Structure) {
|
||||
return TYPE_STRUCT;
|
||||
}
|
||||
if (tp instanceof Union) {
|
||||
return TYPE_UNION;
|
||||
}
|
||||
if (tp instanceof Array) {
|
||||
return TYPE_ARRAY;
|
||||
}
|
||||
return TYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an XML marshaling string to a metatype code
|
||||
* @param metaString is the string
|
||||
* @return the metatype code
|
||||
* @throws XmlParseException if the string does not represent a valid metatype
|
||||
*/
|
||||
public static int getMetatype(String metaString) throws XmlParseException {
|
||||
switch (metaString.charAt(0)) {
|
||||
case 'p':
|
||||
if (metaString.equals("ptr")) {
|
||||
return TYPE_PTR;
|
||||
}
|
||||
else if (metaString.equals("ptrrel")) {
|
||||
return TYPE_PTRREL;
|
||||
}
|
||||
break;
|
||||
case 'a':
|
||||
if (metaString.equals("array")) {
|
||||
return TYPE_ARRAY;
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
if (metaString.equals("struct")) {
|
||||
return TYPE_STRUCT;
|
||||
}
|
||||
break;
|
||||
case 'u':
|
||||
if (metaString.equals("unknown")) {
|
||||
return TYPE_UNKNOWN;
|
||||
}
|
||||
else if (metaString.equals("uint")) {
|
||||
return TYPE_UINT;
|
||||
}
|
||||
else if (metaString.equals("union")) {
|
||||
return TYPE_UNION;
|
||||
}
|
||||
break;
|
||||
case 'i':
|
||||
if (metaString.equals("int")) {
|
||||
return TYPE_INT;
|
||||
}
|
||||
break;
|
||||
case 'f':
|
||||
if (metaString.equals("float")) {
|
||||
return TYPE_FLOAT;
|
||||
}
|
||||
break;
|
||||
case 'b':
|
||||
if (metaString.equals("bool")) {
|
||||
return TYPE_BOOL;
|
||||
}
|
||||
break;
|
||||
case 'c':
|
||||
if (metaString.equals("code")) {
|
||||
return TYPE_CODE;
|
||||
}
|
||||
break;
|
||||
case 'v':
|
||||
if (metaString.equals("void")) {
|
||||
return TYPE_VOID;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
throw new XmlParseException("Unknown metatype: " + metaString);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a decompiler metatype code to a string for XML marshaling
|
||||
* @param meta is the metatype
|
||||
* @return the marshaling string
|
||||
* @throws IOException is the metatype is invalid
|
||||
*/
|
||||
public static String getMetatypeString(int meta) throws IOException {
|
||||
switch (meta) {
|
||||
case TYPE_VOID:
|
||||
return "void";
|
||||
case TYPE_UNKNOWN:
|
||||
return "unknown";
|
||||
case TYPE_INT:
|
||||
return "int";
|
||||
case TYPE_UINT:
|
||||
return "uint";
|
||||
case TYPE_BOOL:
|
||||
return "bool";
|
||||
case TYPE_CODE:
|
||||
return "code";
|
||||
case TYPE_FLOAT:
|
||||
return "float";
|
||||
case TYPE_PTR:
|
||||
return "ptr";
|
||||
case TYPE_PTRREL:
|
||||
return "ptrrel";
|
||||
case TYPE_ARRAY:
|
||||
return "array";
|
||||
case TYPE_STRUCT:
|
||||
return "struct";
|
||||
case TYPE_UNION:
|
||||
return "union";
|
||||
}
|
||||
throw new IOException("Unknown metatype");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
<entry size="2" alignment="2" />
|
||||
<entry size="4" alignment="4" />
|
||||
<entry size="8" alignment="8" />
|
||||
<entry size="16" alignment="16"/>
|
||||
</size_alignment_map>
|
||||
</data_organization>
|
||||
|
||||
|
@ -67,29 +68,32 @@
|
|||
<default_proto>
|
||||
<prototype name="__cdecl" extrapop="0" stackshift="0">
|
||||
<input>
|
||||
<pentry minsize="1" maxsize="8" metatype="float">
|
||||
<register name="d0"/>
|
||||
<pentry minsize="8" maxsize="8" storage="hiddenret">
|
||||
<register name="x8"/>
|
||||
</pentry>
|
||||
<pentry minsize="1" maxsize="8" metatype="float">
|
||||
<register name="d1"/>
|
||||
<pentry minsize="1" maxsize="16" storage="float">
|
||||
<register name="q0"/>
|
||||
</pentry>
|
||||
<pentry minsize="1" maxsize="8" metatype="float">
|
||||
<register name="d2"/>
|
||||
<pentry minsize="1" maxsize="16" storage="float">
|
||||
<register name="q1"/>
|
||||
</pentry>
|
||||
<pentry minsize="1" maxsize="8" metatype="float">
|
||||
<register name="d3"/>
|
||||
<pentry minsize="1" maxsize="16" storage="float">
|
||||
<register name="q2"/>
|
||||
</pentry>
|
||||
<pentry minsize="1" maxsize="8" metatype="float">
|
||||
<register name="d4"/>
|
||||
<pentry minsize="1" maxsize="16" storage="float">
|
||||
<register name="q3"/>
|
||||
</pentry>
|
||||
<pentry minsize="1" maxsize="8" metatype="float">
|
||||
<register name="d5"/>
|
||||
<pentry minsize="1" maxsize="16" storage="float">
|
||||
<register name="q4"/>
|
||||
</pentry>
|
||||
<pentry minsize="1" maxsize="8" metatype="float">
|
||||
<register name="d6"/>
|
||||
<pentry minsize="1" maxsize="16" storage="float">
|
||||
<register name="q5"/>
|
||||
</pentry>
|
||||
<pentry minsize="1" maxsize="8" metatype="float">
|
||||
<register name="d7"/>
|
||||
<pentry minsize="1" maxsize="16" storage="float">
|
||||
<register name="q6"/>
|
||||
</pentry>
|
||||
<pentry minsize="1" maxsize="16" storage="float">
|
||||
<register name="q7"/>
|
||||
</pentry>
|
||||
<pentry minsize="1" maxsize="8" extension="zero">
|
||||
<register name="x0"/>
|
||||
|
@ -115,13 +119,33 @@
|
|||
<pentry minsize="1" maxsize="8" extension="zero">
|
||||
<register name="x7"/>
|
||||
</pentry>
|
||||
<pentry minsize="1" maxsize="500" align="4">
|
||||
<pentry minsize="1" maxsize="500" align="8">
|
||||
<addr offset="0" space="stack"/>
|
||||
</pentry>
|
||||
<rule>
|
||||
<datatype name="homogeneous-float-aggregate"/>
|
||||
<join_per_primitive storage="float"/>
|
||||
</rule>
|
||||
<rule>
|
||||
<datatype name="struct" minsize="17"/>
|
||||
<convert_to_ptr/>
|
||||
</rule>
|
||||
<rule>
|
||||
<datatype name="float"/>
|
||||
<consume storage="float"/>
|
||||
</rule>
|
||||
<rule>
|
||||
<datatype name="float"/>
|
||||
<goto_stack/> <!-- Don't consume general purpose registers -->
|
||||
</rule>
|
||||
<rule>
|
||||
<datatype name="any"/>
|
||||
<join align="true"/> <!-- Chunk from general purpose registers -->
|
||||
</rule>
|
||||
</input>
|
||||
<output>
|
||||
<pentry minsize="1" maxsize="8" metatype="float">
|
||||
<register name="d0"/>
|
||||
<pentry minsize="1" maxsize="16" storage="float">
|
||||
<register name="q0"/>
|
||||
</pentry>
|
||||
<pentry minsize="1" maxsize="8" extension="zero">
|
||||
<register name="x0"/>
|
||||
|
@ -129,6 +153,10 @@
|
|||
<pentry minsize="9" maxsize="16" extension="zero">
|
||||
<addr space="join" piece1="x1" piece2="x0"/>
|
||||
</pentry>
|
||||
<rule>
|
||||
<datatype name="any" minsize="17"/>
|
||||
<hidden_return voidlock="true"/>
|
||||
</rule>
|
||||
</output>
|
||||
<unaffected>
|
||||
<register name="x19"/>
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
/* ###
|
||||
* 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;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class AArch64ProtoModelTest extends AbstractProtoModelTest {
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
buildArchitecture("AARCH64:LE:64:v8A:default");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStdCall() throws Exception {
|
||||
PrototypeModel model = cspec.getCallingConvention("__cdecl");
|
||||
|
||||
test(model, "void func(short a,int b,char c)", "void,w0:2,w1,w2:1");
|
||||
test(model, "void func(int, int)", "void,w0,w1");
|
||||
test(model, "void func(long,long)", "void,x0,x1");
|
||||
test(model, "void func(float,float)", "void,s0,s1");
|
||||
test(model, "void func(double,double)", "void,d0,d1");
|
||||
test(model, "void func(int,float,int,float)", "void,w0,s0,w1,s1");
|
||||
test(model, "void func(float,int,float,int)", "void,s0,w0,s1,w1");
|
||||
test(model, "void func(int,double,double,int)", "void,w0,d0,d1,w1");
|
||||
test(model, "void func(double,long,long,double)", "void,d0,x0,x1,d1");
|
||||
test(model, "void func(float16)", "void,q0");
|
||||
test(model, "void func(float,float16)", "void,s0,q1");
|
||||
test(model, "void func(int,int,int,int,int,int,int,int,int,int)",
|
||||
"void,w0,w1,w2,w3,w4,w5,w6,w7,stack0:4,stack8:4");
|
||||
test(model, "void func(float,float,float,float,float,float,float,float,float,float)",
|
||||
"void,s0,s1,s2,s3,s4,s5,s6,s7,stack0:4,stack8:4");
|
||||
test(model, "void func(float,float,float,float,float,float,float,float,float16)",
|
||||
"void,s0,s1,s2,s3,s4,s5,s6,s7,stack0:16");
|
||||
test(model, "void func(float,float,float,float,float,float,float,float,float,float16)",
|
||||
"void,s0,s1,s2,s3,s4,s5,s6,s7,stack0:4,stack10:16");
|
||||
test(model, "void func(int,int,int,int,int,int,int,int,int,float)",
|
||||
"void,w0,w1,w2,w3,w4,w5,w6,w7,stack0:4,s0");
|
||||
test(model, "void func(float,float,float,float,float,float,float,float,float,int)",
|
||||
"void,s0,s1,s2,s3,s4,s5,s6,s7,stack0:4,w0");
|
||||
|
||||
parseStructure("intpair", "int,int");
|
||||
test(model, "void func(intpair)", "void,x0");
|
||||
|
||||
parseStructure("longpair", "long,long");
|
||||
test(model, "void func(longpair)", "void,join x1 x0");
|
||||
|
||||
parseStructure("longquad", "long,long,long,long");
|
||||
test(model, "void func(longquad)", "void,x0");
|
||||
|
||||
parseStructure("floatdouble", "float,double");
|
||||
test(model, "void func(floatdouble)", "void,join x1 x0");
|
||||
|
||||
parseStructure("intfloat", "int,float");
|
||||
test(model, "void func(intfloat)", "void,x0");
|
||||
|
||||
parseStructure("longdoublestruct", "long,double");
|
||||
test(model, "void func(longdoublestruct)", "void,join x1 x0");
|
||||
|
||||
test(model, "int func()", "w0");
|
||||
test(model, "float func()", "s0");
|
||||
test(model, "double func()", "d0");
|
||||
|
||||
test(model, "intpair func()", "x0");
|
||||
test(model, "longpair func()", "join x1 x0");
|
||||
test(model, "longquad func()", "void,x8");
|
||||
|
||||
parseStructure("floatpair", "float,float");
|
||||
test(model, "void func(floatpair)", "void,join s1 s0");
|
||||
|
||||
parseStructure("floatpairpair", "floatpair,floatpair");
|
||||
test(model, "void func(floatpairpair)", "void,join s3 s2 s1 s0");
|
||||
|
||||
parseStructure("doublequad", "double,double,double,double");
|
||||
test(model, "void func(doublequad)", "void,join d3 d2 d1 d0");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,266 @@
|
|||
/* ###
|
||||
* 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;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.junit.Assert;
|
||||
|
||||
import generic.test.AbstractGenericTest;
|
||||
import ghidra.app.plugin.processors.sleigh.SleighLanguageProvider;
|
||||
import ghidra.app.util.cparser.C.ParseException;
|
||||
import ghidra.app.util.parser.FunctionSignatureParser;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.data.DataTypeParser;
|
||||
import ghidra.util.data.DataTypeParser.AllowedDataTypes;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
|
||||
public class AbstractProtoModelTest extends AbstractGenericTest {
|
||||
protected Language language;
|
||||
protected CompilerSpec cspec;
|
||||
protected DataTypeManager dtManager;
|
||||
protected FunctionSignatureParser parser;
|
||||
protected DataTypeParser dataTypeParser;
|
||||
|
||||
protected void buildParsers() {
|
||||
parser = new FunctionSignatureParser(dtManager, null);
|
||||
dataTypeParser =
|
||||
new DataTypeParser(dtManager, dtManager, null, AllowedDataTypes.FIXED_LENGTH);
|
||||
}
|
||||
|
||||
protected PrototypePieces parseSignature(PrototypeModel protoModel, String signatureText)
|
||||
throws CancelledException, ParseException {
|
||||
FunctionDefinitionDataType f = parser.parse(null, signatureText);
|
||||
PrototypePieces proto = new PrototypePieces(protoModel, null);
|
||||
proto.outtype = f.getReturnType();
|
||||
ParameterDefinition[] args = f.getArguments();
|
||||
for (int i = 0; i < args.length; ++i) {
|
||||
proto.intypes.add(args[i].getDataType());
|
||||
}
|
||||
return proto;
|
||||
}
|
||||
|
||||
protected void parseStructure(String name, String body)
|
||||
throws InvalidDataTypeException, CancelledException {
|
||||
String[] split = body.split(",");
|
||||
StructureDataType struct = new StructureDataType(name, 0, dtManager);
|
||||
struct.setPackingEnabled(true);
|
||||
for (int i = 0; i < split.length; ++i) {
|
||||
DataType element = dataTypeParser.parse(split[i]);
|
||||
struct.add(element);
|
||||
}
|
||||
int txID = dtManager.startTransaction("Add core types");
|
||||
try {
|
||||
dtManager.addDataType(struct, null);
|
||||
}
|
||||
finally {
|
||||
dtManager.endTransaction(txID, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected void buildDataTypeManager(String name) {
|
||||
dtManager = new StandAloneDataTypeManager(name, cspec.getDataOrganization());
|
||||
int txID = dtManager.startTransaction("Add core types");
|
||||
|
||||
try {
|
||||
dtManager.addDataType(new VoidDataType(), null);
|
||||
dtManager.addDataType(new IntegerDataType(), null);
|
||||
dtManager.addDataType(new ShortDataType(), null);
|
||||
dtManager.addDataType(new CharDataType(), null);
|
||||
dtManager.addDataType(new UnsignedIntegerDataType(), null);
|
||||
dtManager.addDataType(new LongDataType(), null);
|
||||
dtManager.addDataType(new FloatDataType(), null);
|
||||
dtManager.addDataType(new DoubleDataType(), null);
|
||||
dtManager.addDataType(new Float16DataType(), null);
|
||||
}
|
||||
finally {
|
||||
dtManager.endTransaction(txID, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain a specific CompilerSpec given a language id
|
||||
* @param langId is the 5 long colon separated language id
|
||||
*/
|
||||
protected void buildArchitecture(String langId) {
|
||||
int pos = langId.lastIndexOf(':');
|
||||
CompilerSpecID cspecId = new CompilerSpecID(langId.substring(pos + 1));
|
||||
LanguageID languageId = new LanguageID(langId.substring(0, pos));
|
||||
SleighLanguageProvider provider = SleighLanguageProvider.getSleighLanguageProvider();
|
||||
try {
|
||||
language = provider.getLanguage(languageId);
|
||||
}
|
||||
catch (Exception e) {
|
||||
String msg = "Language " + languageId + " failed to load: ";
|
||||
Msg.error(this, msg, e);
|
||||
return;
|
||||
}
|
||||
cspec = null;
|
||||
try {
|
||||
cspec = language.getCompilerSpecByID(cspecId);
|
||||
}
|
||||
catch (Exception e) {
|
||||
String msg = "Language " + languageId + " failed to load cspec: " + cspecId;
|
||||
Msg.error(this, msg, e);
|
||||
return;
|
||||
}
|
||||
buildDataTypeManager("base");
|
||||
buildParsers();
|
||||
}
|
||||
|
||||
private ArrayList<Varnode> parseStore(String name) {
|
||||
ArrayList<Varnode> res = new ArrayList<>();
|
||||
if (name.equals("void")) {
|
||||
res.add(null);
|
||||
return res;
|
||||
}
|
||||
if (name.startsWith("stack")) {
|
||||
int pos = name.indexOf(':');
|
||||
int size = 1;
|
||||
if (pos != -1) {
|
||||
String sizeString = name.substring(pos + 1);
|
||||
size = Integer.parseInt(sizeString);
|
||||
}
|
||||
else {
|
||||
pos = name.length();
|
||||
}
|
||||
String offsetString = name.substring(5, pos);
|
||||
long offset = Long.parseLong(offsetString, 16);
|
||||
AddressSpace spc = cspec.getAddressSpace("stack");
|
||||
Varnode vn = new Varnode(spc.getAddress(offset), size);
|
||||
res.add(vn);
|
||||
return res;
|
||||
}
|
||||
else if (name.startsWith("join")) {
|
||||
parseJoin(name, res);
|
||||
return res;
|
||||
}
|
||||
String regname;
|
||||
int pos = name.indexOf(':');
|
||||
int sz = 0;
|
||||
if (pos != -1) {
|
||||
String sizeString = name.substring(pos + 1);
|
||||
sz = Integer.parseInt(sizeString);
|
||||
regname = name.substring(0, pos);
|
||||
}
|
||||
else {
|
||||
regname = name;
|
||||
}
|
||||
Register register = language.getRegister(regname);
|
||||
Address addr = register.getAddress();
|
||||
if (sz != 0) {
|
||||
if (language.isBigEndian()) {
|
||||
addr = addr.add(register.getBitLength() / 8 - sz);
|
||||
}
|
||||
}
|
||||
else {
|
||||
sz = register.getBitLength() / 8;
|
||||
}
|
||||
Varnode vn = new Varnode(addr, sz);
|
||||
res.add(vn);
|
||||
return res;
|
||||
}
|
||||
|
||||
private void parseJoin(String join, ArrayList<Varnode> res) {
|
||||
join = join.substring(5);
|
||||
String[] split = join.split(" ");
|
||||
for (String piece : split) {
|
||||
ArrayList<Varnode> onePiece = parseStore(piece);
|
||||
res.add(onePiece.get(0));
|
||||
}
|
||||
}
|
||||
|
||||
protected void parseStores(ArrayList<ArrayList<Varnode>> res, String names) {
|
||||
String[] split = names.split(",");
|
||||
for (String el : split) {
|
||||
ArrayList<Varnode> vnList = parseStore(el);
|
||||
res.add(vnList);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean compareVarnodes(Varnode vn1, Varnode vn2) {
|
||||
if (!vn1.getAddress().equals(vn2.getAddress())) {
|
||||
return false;
|
||||
}
|
||||
return (vn1.getSize() == vn2.getSize());
|
||||
}
|
||||
|
||||
protected boolean comparePiece(ArrayList<Varnode> vData, ParameterPieces piece)
|
||||
|
||||
{
|
||||
if (vData.size() == 1 && vData.get(0) == null) {
|
||||
if (piece.address == null) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (piece.joinPieces != null) {
|
||||
if (vData.size() != piece.joinPieces.length) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < vData.size(); ++i) {
|
||||
if (!compareVarnodes(vData.get(i), piece.joinPieces[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (vData.size() != 1) {
|
||||
return false;
|
||||
}
|
||||
Varnode vn = vData.get(0);
|
||||
if (!vn.getAddress().equals(piece.address)) {
|
||||
return false;
|
||||
}
|
||||
return (vn.getSize() == piece.type.getLength());
|
||||
}
|
||||
|
||||
protected void test(PrototypeModel model, String signature, String stores)
|
||||
throws CancelledException, ParseException {
|
||||
PrototypePieces pieces = parseSignature(model, signature);
|
||||
ArrayList<ParameterPieces> res = new ArrayList<>();
|
||||
model.assignParameterStorage(pieces, dtManager, res, true);
|
||||
ArrayList<ArrayList<Varnode>> storeData = new ArrayList<>();
|
||||
parseStores(storeData, stores);
|
||||
Assert.assertEquals(storeData.size(), res.size());
|
||||
for (int i = 0; i < res.size(); ++i) {
|
||||
boolean compare = comparePiece(storeData.get(i), res.get(i));
|
||||
String message = null;
|
||||
if (!compare) {
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
buffer.append(language.getLanguageID()).append(':');
|
||||
buffer.append(cspec.getCompilerSpecID());
|
||||
buffer.append(' ').append(model.getName()).append(' ');
|
||||
if (i == 0) {
|
||||
buffer.append("Output does not match for ");
|
||||
}
|
||||
else {
|
||||
buffer.append("Parameter ").append(i - 1).append(" does not match for: ");
|
||||
}
|
||||
buffer.append(signature);
|
||||
message = buffer.toString();
|
||||
}
|
||||
Assert.assertTrue(message, compare);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/* ###
|
||||
* 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;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.junit.*;
|
||||
|
||||
public class X64PrototypeModelTest extends AbstractProtoModelTest {
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
buildArchitecture("x86:LE:64:default:gcc");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStdCall() throws Exception {
|
||||
PrototypeModel model = cspec.getCallingConvention("__stdcall");
|
||||
test(model, "void func(int a,int b)", "void,EDI,ESI");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThisCall() throws Exception {
|
||||
PrototypeModel model = cspec.getCallingConvention("__thiscall");
|
||||
parseStructure("bigstruct", "int,int,int,int,int");
|
||||
PrototypePieces pieces = parseSignature(model, "bigstruct func(int *,int)");
|
||||
ArrayList<ParameterPieces> res = new ArrayList<>();
|
||||
model.assignParameterStorage(pieces, dtManager, res, true);
|
||||
Assert.assertEquals(res.size(), 4);
|
||||
Assert.assertTrue(res.get(1).hiddenReturnPtr);
|
||||
Assert.assertTrue(res.get(2).isThisPointer);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/* ###
|
||||
* 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;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.junit.*;
|
||||
|
||||
public class X64WinPrototypeModelTest extends AbstractProtoModelTest {
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
buildArchitecture("x86:LE:64:default:windows");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThisCall() throws Exception {
|
||||
PrototypeModel model = cspec.getCallingConvention("__thiscall");
|
||||
parseStructure("bigstruct", "int,int,int,int,int");
|
||||
PrototypePieces pieces = parseSignature(model, "bigstruct func(int *,int)");
|
||||
ArrayList<ParameterPieces> res = new ArrayList<>();
|
||||
model.assignParameterStorage(pieces, dtManager, res, true);
|
||||
Assert.assertEquals(res.size(), 4);
|
||||
Assert.assertTrue(res.get(2).hiddenReturnPtr);
|
||||
Assert.assertTrue(res.get(1).isThisPointer);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user