GP-5256 ExtraStack, sizes attribute, merge join sequence

This commit is contained in:
caheckman 2025-01-10 22:49:01 +00:00
parent acbda8b076
commit 00f9789116
40 changed files with 1015 additions and 411 deletions

View file

@ -604,20 +604,21 @@ ParamListStandard::~ParamListStandard(void)
}
}
/// The entry must have a unique group.
/// If no matching entry is found, the \b end iterator is returned.
/// \param tiles will contain the set of matching entries
/// \param type is the storage class
/// \return the first matching iterator
list<ParamEntry>::const_iterator ParamListStandard::getFirstIter(type_class type) const
void ParamListStandard::extractTiles(vector<const ParamEntry *> &tiles,type_class type) const
{
list<ParamEntry>::const_iterator iter;
for(iter=entry.begin();iter!=entry.end();++iter) {
const ParamEntry &curEntry( *iter );
if (curEntry.getType() == type && curEntry.getAllGroups().size() == 1)
return iter;
if (!curEntry.isExclusion())
continue;
if (curEntry.getType() != type || curEntry.getAllGroups().size() != 1)
continue;
tiles.push_back(&curEntry);
}
return iter;
}
/// If the stack entry is not present, null is returned
@ -2157,6 +2158,29 @@ void ParameterPieces::swapMarkup(ParameterPieces &op)
op.type = tmpType;
}
/// \brief Generate a parameter address given the list of Varnodes making up the parameter
///
/// \param pieces is the given list of Varnodes
/// \param mostToLeast is \b true if the list is ordered \e most significant to \e least
/// \param glb is the Architecture
void ParameterPieces::assignAddressFromPieces(vector<VarnodeData> &pieces,bool mostToLeast,Architecture *glb)
{
if (!mostToLeast && pieces.size() > 1) {
vector<VarnodeData> reverse;
for(int4 i=pieces.size()-1;i>=0;--i)
reverse.push_back(pieces[i]);
pieces.swap(reverse);
}
JoinRecord::mergeSequence(pieces,glb->translate);
if (pieces.size() == 1) {
addr = pieces[0].getAddr();
return;
}
JoinRecord *joinRecord = glb->findAddJoin(pieces, 0);
addr = joinRecord->getUnified().getAddr();
}
/// 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

View file

@ -367,6 +367,7 @@ struct ParameterPieces {
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
void assignAddressFromPieces(vector<VarnodeData> &pieces,bool mostToLeast,Architecture *glb);
};
/// \brief Raw components of a function prototype (obtained from parsing source code)
@ -614,7 +615,7 @@ public:
ParamListStandard(const ParamListStandard &op2); ///< Copy constructor
virtual ~ParamListStandard(void);
const list<ParamEntry> &getEntry(void) const { return entry; } ///< Get the list of parameter entries
list<ParamEntry>::const_iterator getFirstIter(type_class type) const; ///< Get iterator to first entry in a storage class
void extractTiles(vector<const ParamEntry *> &tiles,type_class type) const; ///< Get registers of given storage class
const ParamEntry *getStackEntry(void) const; ///< Get the stack entry
uint4 assignAddressFallback(type_class resource,Datatype *tp,bool matchExact,vector<int4> &status,
ParameterPieces &param) const;

View file

@ -627,7 +627,7 @@ class CloneBlockOps {
PcodeOp *origOp; ///< Original op that was cloned
ClonePair(PcodeOp *c,PcodeOp *o) { cloneOp = c; origOp = o; } ///< Constructor
};
Funcdata &data;
Funcdata &data; ///< Function containing ops to clone
vector<ClonePair> cloneList; ///< List of cloned ops
map<PcodeOp *,PcodeOp *> origToClone; ///< Map from original p-code op to its clone
PcodeOp *buildOpClone(PcodeOp *op); ///< Produce a skeleton copy of the given PcodeOp

View file

@ -971,10 +971,10 @@ PcodeOp *CloneBlockOps::buildOpClone(PcodeOp *op)
return dup;
}
/// Make a basic clone of the Varnode and its flags. The clone is created
/// as an output of a previously cloned PcodeOp.
/// \param op is the given op whose output should be cloned
/// \param newop is the cloned version
/// Make a basic clone of a Varnode and its flags. The clone is created
/// as an output Varnode of a previously cloned PcodeOp.
/// \param origOp is the given op whose output should be cloned
/// \param cloneOp is the cloned version
void CloneBlockOps::buildVarnodeOutput(PcodeOp *origOp,PcodeOp *cloneOp)
{

View file

@ -555,6 +555,7 @@ Varnode *Funcdata::opStackLoad(AddrSpace *spc,uintb off,uint4 sz,PcodeOp *op,Var
///
/// \param vn is the given Varnode
/// \param op is the point at which to insert the BOOL_NEGATE op
/// \param insertafter is \b true if the BOOL_NEGATE is inserted after, otherwise its inserted before
/// \return the result Varnode
Varnode *Funcdata::opBoolNegate(Varnode *vn,PcodeOp *op,bool insertafter)
@ -1273,7 +1274,6 @@ int4 Funcdata::opFlipInPlaceTest(PcodeOp *op,vector<PcodeOp *> &fliplist)
///
/// The precomputed list of PcodeOps have their op-codes modified to
/// facilitate the flip.
/// \param data is the function being modified
/// \param fliplist is the list of PcodeOps to modify
void Funcdata::opFlipInPlaceExecute(vector<PcodeOp *> &fliplist)

View file

@ -4,9 +4,9 @@
* 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.
@ -86,6 +86,25 @@ string GhidraTranslate::getRegisterName(AddrSpace *base,uintb off,int4 size) con
return res;
}
string GhidraTranslate::getExactRegisterName(AddrSpace *base,uintb off,int4 size) const
{
if (base->getType() != IPTR_PROCESSOR) return "";
VarnodeData vndata;
vndata.space = base;
vndata.offset = off;
vndata.size = size;
map<VarnodeData,string>::const_iterator iter = addr2nm.find(vndata);
if (iter != addr2nm.end())
return (*iter).second;
string res = glb->getRegisterName(vndata);
if (res.size()!=0) { // Cause this register to be cached if not already
if (getRegister(res).size == size)
return res;
}
return "";
}
void GhidraTranslate::getUserOpNames(vector<string> &res) const
{

View file

@ -4,9 +4,9 @@
* 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.
@ -36,7 +36,7 @@ namespace ghidra {
class GhidraTranslate : public Translate {
ArchitectureGhidra *glb; ///< The Ghidra Architecture and connection to the client
mutable map<string,VarnodeData> nm2addr; ///< Mapping from register name to Varnode
mutable map<VarnodeData,string> addr2nm; ///< Mapping rom Varnode to register name
mutable map<VarnodeData,string> addr2nm; ///< Mapping from Varnode to register name
const VarnodeData &cacheRegister(const string &nm,const VarnodeData &data) const;
void decode(Decoder &decoder); ///< Initialize \b this Translate from a stream
public:
@ -45,6 +45,7 @@ public:
virtual void initialize(DocumentStorage &store);
virtual const VarnodeData &getRegister(const string &nm) const;
virtual string getRegisterName(AddrSpace *base,uintb off,int4 size) const;
virtual string getExactRegisterName(AddrSpace *base,uintb off,int4 size) const;
virtual void getAllRegisters(map<VarnodeData,string> &reglist) const {
throw LowlevelError("Cannot currently get all registers through this interface"); }
virtual void getUserOpNames(vector<string> &res) const;

View file

@ -1254,7 +1254,7 @@ AttributeId ATTRIB_WORDSIZE = AttributeId("wordsize",26);
AttributeId ATTRIB_STORAGE = AttributeId("storage",149);
AttributeId ATTRIB_STACKSPILL = AttributeId("stackspill",150);
AttributeId ATTRIB_UNKNOWN = AttributeId("XMLunknown",151); // Number serves as next open index
AttributeId ATTRIB_UNKNOWN = AttributeId("XMLunknown",152); // Number serves as next open index
ElementId ELEM_DATA = ElementId("data",1);
ElementId ELEM_INPUT = ElementId("input",2);
@ -1267,6 +1267,6 @@ ElementId ELEM_VAL = ElementId("val",8);
ElementId ELEM_VALUE = ElementId("value",9);
ElementId ELEM_VOID = ElementId("void",10);
ElementId ELEM_UNKNOWN = ElementId("XMLunknown",287); // Number serves as next open index
ElementId ELEM_UNKNOWN = ElementId("XMLunknown",288); // Number serves as next open index
} // End namespace ghidra

View file

@ -18,6 +18,8 @@
namespace ghidra {
AttributeId ATTRIB_SIZES = AttributeId("sizes",151);
ElementId ELEM_DATATYPE = ElementId("datatype",273);
ElementId ELEM_CONSUME = ElementId("consume",274);
ElementId ELEM_CONSUME_EXTRA = ElementId("consume_extra",275);
@ -30,6 +32,7 @@ ElementId ELEM_VARARGS = ElementId("varargs",281);
ElementId ELEM_HIDDEN_RETURN = ElementId("hidden_return",282);
ElementId ELEM_JOIN_PER_PRIMITIVE = ElementId("join_per_primitive",283);
ElementId ELEM_JOIN_DUAL_CLASS = ElementId("join_dual_class",285);
ElementId ELEM_EXTRA_STACK = ElementId("extra_stack",287);
/// \brief Check that a big Primitive properly overlaps smaller Primitives
///
@ -261,6 +264,35 @@ DatatypeFilter *DatatypeFilter::decodeFilter(Decoder &decoder)
return filter;
}
/// Parse the given string as a comma or space separated list of decimal integers,
/// populating the \b sizes set.
/// \param str is the given string to parse
void SizeRestrictedFilter::initFromSizeList(const string &str)
{
istringstream s(str);
int4 val;
for(;;) {
val = -1;
s >> ws;
if (s.eof()) break;
if (s.peek() == ',') {
char punc;
s >> punc >> ws;
}
s >> val;
if (val <= 0)
throw DecoderError("Bad filter size");
sizes.insert(val);
}
if (!sizes.empty()) {
minSize = *sizes.begin();
set<int4>::const_iterator iter = sizes.end();
--iter;
maxSize = *iter;
}
}
SizeRestrictedFilter::SizeRestrictedFilter(int4 min,int4 max)
{
@ -272,14 +304,26 @@ SizeRestrictedFilter::SizeRestrictedFilter(int4 min,int4 max)
}
}
SizeRestrictedFilter::SizeRestrictedFilter(const SizeRestrictedFilter &op2)
{
minSize = op2.minSize;
maxSize = op2.maxSize;
sizes = op2.sizes;
}
/// 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.
/// falls between \b minSize and \b maxSize inclusive. If enumerated sizes are present,
/// also check that the particular size is in the enumerated set.
/// \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
if (!sizes.empty()) {
return (sizes.find(dt->getSize()) != sizes.end());
}
return (dt->getSize() >= minSize && dt->getSize() <= maxSize);
}
@ -289,10 +333,22 @@ void SizeRestrictedFilter::decode(Decoder &decoder)
for(;;) {
uint4 attribId = decoder.getNextAttributeId();
if (attribId == 0) break;
if (attribId == ATTRIB_MINSIZE)
if (attribId == ATTRIB_MINSIZE) {
if (!sizes.empty())
throw DecoderError("Mixing \"sizes\" with \"minsize\" and \"maxsize\"");
minSize = decoder.readUnsignedInteger();
else if (attribId == ATTRIB_MAXSIZE)
}
else if (attribId == ATTRIB_MAXSIZE) {
if (!sizes.empty())
throw DecoderError("Mixing \"sizes\" with \"minsize\" and \"maxsize\"");
maxSize = decoder.readUnsignedInteger();
}
else if (attribId == ATTRIB_SIZES) {
if (minSize != 0 || maxSize != 0)
throw DecoderError("Mixing \"sizes\" with \"minsize\" and \"maxsize\"");
string sizeList = decoder.readString();
initFromSizeList(sizeList);
}
}
if (maxSize == 0 && minSize >= 0) {
// If no ATTRIB_MAXSIZE is given, assume there is no upper bound on size
@ -312,6 +368,12 @@ MetaTypeFilter::MetaTypeFilter(type_metatype meta,int4 min,int4 max)
metaType = meta;
}
MetaTypeFilter::MetaTypeFilter(const MetaTypeFilter &op2)
: SizeRestrictedFilter(op2)
{
metaType = op2.metaType;
}
bool MetaTypeFilter::filter(Datatype *dt) const
{
@ -333,6 +395,13 @@ HomogeneousAggregate::HomogeneousAggregate(type_metatype meta,int4 maxPrim,int4
maxPrimitives = maxPrim;
}
HomogeneousAggregate::HomogeneousAggregate(const HomogeneousAggregate &op2)
: SizeRestrictedFilter(op2)
{
metaType = op2.metaType;
maxPrimitives = op2.maxPrimitives;
}
bool HomogeneousAggregate::filter(Datatype *dt) const
{
@ -550,6 +619,9 @@ AssignAction *AssignAction::decodeSideeffect(Decoder &decoder,const ParamListSta
if (elemId == ELEM_CONSUME_EXTRA) {
action = new ConsumeExtra(res);
}
else if (elemId == ELEM_EXTRA_STACK) {
action = new ExtraStack(res,0);
}
else
throw DecoderError("Expecting model rule sideeffect");
action->decode(decoder);
@ -561,7 +633,7 @@ void GotoStack::initializeEntry(void)
{
stackEntry = resource->getStackEntry();
if (stackEntry == (const ParamEntry *)0)
throw LowlevelError("Cannot find matching <pentry> for action: gotostack");
throw LowlevelError("Cannot find matching <pentry> for action: goto_stack");
}
/// \param res is the new resource set to associate with \b this action
@ -650,9 +722,9 @@ void ConvertToPointer::decode(Decoder &decoder)
void MultiSlotAssign::initializeEntries(void)
{
firstIter = resource->getFirstIter(resourceType);
resource->extractTiles(tiles,resourceType);
stackEntry = resource->getStackEntry();
if (firstIter == resource->getEntry().end())
if (tiles.size() == 0)
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");
@ -699,38 +771,29 @@ uint4 MultiSlotAssign::assignAddress(Datatype *dt,const PrototypePieces &proto,i
vector<VarnodeData> pieces;
int4 sizeLeft = dt->getSize();
int4 align = dt->getAlignment();
list<ParamEntry>::const_iterator iter = firstIter;
list<ParamEntry>::const_iterator endIter = resource->getEntry().end();
int4 iter = 0;
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 regSize = entry.getSize();
if (align <= regSize || (resourcesConsumed % align) == 0)
break;
tmpStatus[entry.getGroup()] = -1; // Consume unaligned register
}
resourcesConsumed += entry.getSize();
while(iter != tiles.size()) {
const ParamEntry *entry = tiles[iter];
if (tmpStatus[entry->getGroup()] == 0) { // Not consumed
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 );
while(sizeLeft > 0 && iter != tiles.size()) {
const ParamEntry *entry = tiles[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)
if (tmpStatus[entry->getGroup()] != 0)
continue; // Already consumed
int4 trialSize = entry.getSize();
Address addr = entry.getAddrBySlot(tmpStatus[entry.getGroup()], trialSize,align);
tmpStatus[entry.getGroup()] = -1; // Consume the register
int4 trialSize = entry->getSize();
Address addr = entry->getAddrBySlot(tmpStatus[entry->getGroup()], trialSize,align);
tmpStatus[entry->getGroup()] = -1; // Consume the register
pieces.push_back(VarnodeData());
pieces.back().space = addr.getSpace();
pieces.back().offset = addr.getOffset();
@ -770,18 +833,7 @@ uint4 MultiSlotAssign::assignAddress(Datatype *dt,const PrototypePieces &proto,i
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();
res.assignAddressFromPieces(pieces, consumeMostSig, tlist.getArch());
return success;
}
@ -891,18 +943,7 @@ uint4 MultiMemberAssign::assignAddress(Datatype *dt,const PrototypePieces &proto
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();
res.assignAddressFromPieces(pieces, consumeMostSig, tlist.getArch());
return success;
}
@ -951,37 +992,30 @@ void MultiMemberAssign::decode(Decoder &decoder)
void MultiSlotDualAssign::initializeEntries(void)
{
baseIter = resource->getFirstIter(baseType);
altIter = resource->getFirstIter(altType);
list<ParamEntry>::const_iterator enditer = resource->getEntry().end();
if (baseIter == enditer || altIter == enditer)
resource->extractTiles(baseTiles,baseType);
resource->extractTiles(altTiles,altType);
if (baseTiles.size() == 0 || altTiles.size() == 0)
throw LowlevelError("Could not find matching resources for action: join_dual_class");
tileSize = (*baseIter).getSize();
if (tileSize != (*altIter).getSize())
tileSize = baseTiles[0]->getSize();
if (tileSize != altTiles[0]->getSize())
throw LowlevelError("Storage class register sizes do not match for action: join_dual_class");
}
/// \brief Get the first unused ParamEntry that matches the given storage class
/// \brief Get the index of the first unused ParamEntry in the given list
///
/// \param iter points to the starting entry to search
/// \param storage is the given storage class to match
/// \param iter is the index of the starting entry to search
/// \param tiles is the given list to search
/// \param status is the usage information for the entries
/// \return the iterator to the unused ParamEntry
list<ParamEntry>::const_iterator MultiSlotDualAssign::getFirstUnused(list<ParamEntry>::const_iterator iter,
type_class storage,vector<int4> &status) const
/// \return the index of the unused ParamEntry
int4 MultiSlotDualAssign::getFirstUnused(int4 iter,const vector<const ParamEntry *> &tiles,vector<int4> &status) const
{
list<ParamEntry>::const_iterator endIter = resource->getEntry().end();
for(;iter != endIter; ++iter) {
const ParamEntry &entry( *iter );
if (!entry.isExclusion())
break; // Reached end of resource list
if (entry.getType() != storage || entry.getAllGroups().size() != 1)
continue; // Not a single register from desired resource
if (status[entry.getGroup()] != 0)
for(;iter != tiles.size(); ++iter) {
const ParamEntry *entry = tiles[iter];
if (status[entry->getGroup()] != 0)
continue; // Already consumed
return iter;
}
return endIter;
return tiles.size();
}
/// \brief Get the storage class to use for the specific section of the data-type
@ -1057,26 +1091,28 @@ uint4 MultiSlotDualAssign::assignAddress(Datatype *dt,const PrototypePieces &pro
vector<VarnodeData> pieces;
int4 typeSize = dt->getSize();
int4 sizeLeft = typeSize;
list<ParamEntry>::const_iterator iterBase = baseIter;
list<ParamEntry>::const_iterator iterAlt = altIter;
list<ParamEntry>::const_iterator endIter = resource->getEntry().end();
int4 iterBase = 0;
int4 iterAlt = 0;
while(sizeLeft > 0) {
list<ParamEntry>::const_iterator iter;
const ParamEntry *entry;
int4 iterType = getTileClass(primitives, typeSize-sizeLeft, primitiveIndex);
if (iterType < 0)
return fail;
if (iterType == 0) {
iter = iterBase = getFirstUnused(iterBase, baseType, tmpStatus);
iterBase = getFirstUnused(iterBase, baseTiles, tmpStatus);
if (iterBase == baseTiles.size())
return fail; // Out of general purpose registers
entry = baseTiles[iterBase];
}
else {
iter = iterAlt = getFirstUnused(iterAlt, altType, tmpStatus);
iterAlt = getFirstUnused(iterAlt, altTiles, tmpStatus);
if (iterAlt == altTiles.size())
return fail; // Out of alternate registers
entry = altTiles[iterAlt];
}
if (iter == endIter)
return fail; // Out of the particular resource
const ParamEntry &entry( *iter );
int4 trialSize = entry.getSize();
Address addr = entry.getAddrBySlot(tmpStatus[entry.getGroup()], trialSize,1);
tmpStatus[entry.getGroup()] = -1; // Consume the register
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();
@ -1095,18 +1131,7 @@ uint4 MultiSlotDualAssign::assignAddress(Datatype *dt,const PrototypePieces &pro
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();
res.assignAddressFromPieces(pieces, consumeMostSig, tlist.getArch());
return success;
}
@ -1270,9 +1295,9 @@ void HiddenReturnAssign::decode(Decoder &decoder)
void ConsumeExtra::initializeEntries(void)
{
firstIter = resource->getFirstIter(resourceType);
if (firstIter == resource->getEntry().end())
throw LowlevelError("Could not find matching resources for action: consumeextra");
resource->extractTiles(tiles,resourceType);
if (tiles.size() == 0)
throw LowlevelError("Could not find matching resources for action: consume_extra");
}
ConsumeExtra::ConsumeExtra(const ParamListStandard *res)
@ -1293,20 +1318,15 @@ ConsumeExtra::ConsumeExtra(type_class store,bool match,const ParamListStandard *
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 iter = 0;
int4 sizeLeft = dt->getSize();
while(sizeLeft > 0 && iter != endIter) {
const ParamEntry &entry(*iter);
while(sizeLeft > 0 && iter != tiles.size()) {
const ParamEntry *entry = tiles[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)
if (status[entry->getGroup()] != 0)
continue; // Already consumed
status[entry.getGroup()] = -1; // Consume the slot/register
sizeLeft -= entry.getSize();
status[entry->getGroup()] = -1; // Consume the slot/register
sizeLeft -= entry->getSize();
if (!matchSize)
break; // Only consume a single register
}
@ -1322,6 +1342,49 @@ void ConsumeExtra::decode(Decoder &decoder)
initializeEntries();
}
void ExtraStack::initializeEntry(void)
{
stackEntry = resource->getStackEntry();
if (stackEntry == (const ParamEntry *)0)
throw LowlevelError("Cannot find matching <pentry> for action: extra_stack");
}
/// \param res is the new resource set to associate with \b this action
/// \param val is a dummy value
ExtraStack::ExtraStack(const ParamListStandard *res,int4 val)
: AssignAction(res)
{
stackEntry = (const ParamEntry *)0;
}
ExtraStack::ExtraStack(const ParamListStandard *res)
: AssignAction(res)
{
stackEntry = (const ParamEntry *)0;
initializeEntry();
}
uint4 ExtraStack::assignAddress(Datatype *dt,const PrototypePieces &proto,int4 pos,TypeFactory &tlst,
vector<int4> &status,ParameterPieces &res) const
{
if (res.addr.getSpace() == stackEntry->getSpace())
return success; // Parameter was already assigned to the stack
int4 grp = stackEntry->getGroup();
// We assign the stack address (but ignore the actual address) updating the status for the stack,
// which consumes the stack resources.
stackEntry->getAddrBySlot(status[grp],dt->getSize(),dt->getAlignment());
return success;
}
void ExtraStack::decode(Decoder &decoder)
{
uint4 elemId = decoder.openElement(ELEM_EXTRA_STACK);
decoder.closeElement(elemId);
initializeEntry();
}
ModelRule::ModelRule(const ModelRule &op2,const ParamListStandard *res)
{

View file

@ -28,6 +28,8 @@ class ParamListStandard;
class ParamEntry;
class ParamActive;
extern AttributeId ATTRIB_SIZES; ///< Marshaling attribute "sizes"
extern ElementId ELEM_DATATYPE; ///< Marshaling element \<datatype>
extern ElementId ELEM_CONSUME; ///< Marshaling element \<consume>
extern ElementId ELEM_CONSUME_EXTRA; ///< Marshaling element \<consume_extra>
@ -40,6 +42,7 @@ extern ElementId ELEM_VARARGS; ///< Marshaling element \<varargs>
extern ElementId ELEM_HIDDEN_RETURN; ///< Marshaling element \<hidden_return>
extern ElementId ELEM_JOIN_PER_PRIMITIVE; ///< Marshaling element \<join_per_primitive>
extern ElementId ELEM_JOIN_DUAL_CLASS; ///< Marshaling element \<join_dual_class>
extern ElementId ELEM_EXTRA_STACK; ///< Marshaling element \<extra_stack>
/// \brief Class for extracting primitive elements of a data-type
///
@ -105,20 +108,23 @@ public:
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
/// \brief A base class for data-type filters that tests for either a range or an enumerated list of sizes
///
/// Any filter that inherits from \b this, can use ATTRIB_MINSIZE and ATTRIB_MAXSIZE
/// Any filter that inherits from \b this, can use ATTRIB_MINSIZE, ATTRIB_MAXSIZE, or ATTRIB_SIZES
/// 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
set<int4> sizes; ///< An enumerated list of sizes (if not empty)
void initFromSizeList(const string &str); ///< Initialize filter from enumerated list of sizes
public:
SizeRestrictedFilter(void) { minSize=0; maxSize=0; } ///< Constructor for use with decode()
SizeRestrictedFilter(int4 min,int4 max); ///< Constructor
SizeRestrictedFilter(const SizeRestrictedFilter &op2); ///< Copy 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 DatatypeFilter *clone(void) const { return new SizeRestrictedFilter(*this); }
virtual bool filter(Datatype *dt) const { return filterOnSize(dt); }
virtual void decode(Decoder &decoder);
};
@ -132,7 +138,8 @@ protected:
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); }
MetaTypeFilter(const MetaTypeFilter &op2); ///< Copy constructor
virtual DatatypeFilter *clone(void) const { return new MetaTypeFilter(*this); }
virtual bool filter(Datatype *dt) const;
};
@ -145,7 +152,8 @@ class HomogeneousAggregate : public SizeRestrictedFilter {
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); }
HomogeneousAggregate(const HomogeneousAggregate &op2); ///< Copy constructor
virtual DatatypeFilter *clone(void) const { return new HomogeneousAggregate(*this); }
virtual bool filter(Datatype *dt) const;
};
@ -310,7 +318,7 @@ class GotoStack : public AssignAction {
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()
GotoStack(const ParamListStandard *res); ///< Constructor
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;
@ -342,8 +350,8 @@ class MultiSlotAssign : public AssignAction {
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
vector<const ParamEntry *> tiles; ///< List of registers that can be joined
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
@ -385,11 +393,10 @@ class MultiSlotDualAssign : public AssignAction {
bool consumeMostSig; ///< True if resources are consumed starting with most significant bytes
bool justifyRight; ///< True if initial bytes are padding for odd data-type sizes
int4 tileSize; ///< Number of bytes in a tile
list<ParamEntry>::const_iterator baseIter; ///< Iterator to first element in the base resource list
list<ParamEntry>::const_iterator altIter; ///< Iterator to first element in alternate resource list
vector<const ParamEntry *> baseTiles; ///< General registers to be joined
vector<const ParamEntry *> altTiles; ///< Alternate registers to be joined
void initializeEntries(void); ///< Cache specific ParamEntry needed by the action
list<ParamEntry>::const_iterator getFirstUnused(list<ParamEntry>::const_iterator iter,type_class storage,
vector<int4> &status) const;
int4 getFirstUnused(int4 iter,const vector<const ParamEntry *> &tiles,vector<int4> &status) const;
int4 getTileClass(const PrimitiveExtractor &primitives,int4 off,int4 &index) const;
public:
MultiSlotDualAssign(const ParamListStandard *res); ///< Constructor for use with decode
@ -451,8 +458,8 @@ public:
/// 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
vector<const ParamEntry *> tiles; ///< List of registers that can be consumed
void initializeEntries(void); ///< Cache specific ParamEntry needed by the action
public:
ConsumeExtra(const ParamListStandard *res); ///< Constructor for use with decode
@ -464,6 +471,25 @@ public:
virtual void decode(Decoder &decoder);
};
/// \brief Consume stack resources as a side-effect
///
/// This action is a side-effect and doesn't assign an address for the current parameter.
/// If the current parameter has been assigned a address that is not on the stack, this action consumes
/// stack resources as if the parameter were allocated to the stack. If the current parameter was
/// already assigned a stack address, no additional action is taken.
class ExtraStack : public AssignAction {
const ParamEntry *stackEntry; ///< Parameter Entry corresponding to the stack
void initializeEntry(void); ///< Find stack entry in resource list
public:
ExtraStack(const ParamListStandard *res,int4 val); ///< Constructor for use with decode
ExtraStack(const ParamListStandard *res); ///< Constructor
virtual AssignAction *clone(const ParamListStandard *newResource) const {
return new ExtraStack(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.

View file

@ -4,9 +4,9 @@
* 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.
@ -66,6 +66,25 @@ bool VarnodeData::contains(const VarnodeData &op2) const
return true;
}
/// If \b this and \b lo form a contiguous range of bytes, where \b this makes up the most significant
/// bytes and \b lo makes up the least significant bytes, return \b true.
/// \param lo is the given VarnodeData to compare with
/// \return \b true if the two byte ranges are contiguous and in order
bool VarnodeData::isContiguous(const VarnodeData &lo) const
{
if (space != lo.space) return false;
if (space->isBigEndian()) {
uintb nextoff = space->wrapOffset(offset+size);
if (nextoff == lo.offset) return true;
}
else {
uintb nextoff = space->wrapOffset(lo.offset+lo.size);
if (nextoff == offset) return true;
}
return false;
}
/// This assumes the \<op> element is already open.
/// Decode info suitable for call to PcodeEmit::dump. The output pointer is changed to null if there
/// is no output for this op, otherwise the existing pointer is used to store the output.

View file

@ -4,9 +4,9 @@
* 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.
@ -54,6 +54,9 @@ struct VarnodeData {
/// Does \b this container another given VarnodeData
bool contains(const VarnodeData &op2) const;
/// Is \b this contiguous (as the most significant piece) with the given VarnodeData
bool isContiguous(const VarnodeData &lo) const;
};
/// VarnodeData can be sorted in terms of the space its in

View file

@ -2797,6 +2797,16 @@ void RuleBooleanDedup::getOpList(vector<uint4> &oplist) const
oplist.push_back(CPUI_BOOL_OR);
}
/// \brief Determine if the two given boolean Varnodes always contain matching values
///
/// The boolean values can either always be equal or can always be complements of each other, and
/// \b true will be returned. In this case \b isFlip passes back \b false if the values are always equal
/// or passes back \b true if the values are complements. The method returns \b false if the values are
/// uncorrelated.
/// \param leftVn is the first given boolean Varnode to compare
/// \param rightVn is the second given boolean Varnode to compare
/// \param isFlip will pass back the type of correlation
/// \return \b true if the Varnodes are correlated and \b false otherwise
bool RuleBooleanDedup::isMatch(Varnode *leftVn,Varnode *rightVn,bool &isFlip)
{
@ -9245,12 +9255,16 @@ bool RuleConditionalMove::gatherExpression(Varnode *vn,vector<PcodeOp *> &ops,Fl
return true;
}
/// Reproduce the bolean expression resulting in the given Varnode.
/// \brief Construct the expression after the merge
///
/// Reproduce the boolean expression resulting in the given Varnode.
/// Either reuse the existing Varnode or reconstruct it,
/// making sure the expression does not depend on data in the branch.
/// \param vn is the given boolean Varnode at the base of the expression
/// \param ops is the set of PcodeOps in the expression
/// \param insertop is point at which any reconstruction should be inserted
/// \param data is the function being analyzed
/// \return the Varnode representing the boolean expression
/// \return the Varnode representing the (possible reproduced) boolean expression
Varnode *RuleConditionalMove::constructBool(Varnode *vn,PcodeOp *insertop,vector<PcodeOp *> &ops,Funcdata &data)
{

View file

@ -1418,7 +1418,8 @@ public:
class RuleConditionalMove : public Rule {
static Varnode *checkBoolean(Varnode *vn); ///< Check for boolean expression
static bool gatherExpression(Varnode *vn,vector<PcodeOp *> &ops,FlowBlock *root,FlowBlock *branch);
static Varnode *constructBool(Varnode *vn,PcodeOp *insertop,vector<PcodeOp *> &ops,Funcdata &data); ///< Construct the expression after the merge
static Varnode *constructBool(Varnode *vn,PcodeOp *insertop,vector<PcodeOp *> &ops,Funcdata &data);
/// \brief Sort PcodeOps based only on order within a basic block
static bool compareOp(PcodeOp *op0,PcodeOp *op1) { return op0->getSeqNum().getOrder() < op1->getSeqNum().getOrder(); }
public:
RuleConditionalMove(const string &g) : Rule( g, 0, "conditionalmove") {} ///< Constructor

View file

@ -4,9 +4,9 @@
* 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.
@ -65,6 +65,7 @@ public:
virtual void initialize(DocumentStorage &store) {}
virtual const VarnodeData &getRegister(const string &nm) const { throw LowlevelError("Cannot add register to DummyTranslate"); }
virtual string getRegisterName(AddrSpace *base,uintb off,int4 size) const { return ""; }
virtual string getExactRegisterName(AddrSpace *base,uintb off,int4 size) const { return ""; }
virtual void getAllRegisters(map<VarnodeData,string> &reglist) const {}
virtual void getUserOpNames(vector<string> &res) const {}
virtual int4 instructionLength(const Address &baseaddr) const { return -1; }

View file

@ -4,9 +4,9 @@
* 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.
@ -167,6 +167,18 @@ string SleighBase::getRegisterName(AddrSpace *base,uintb off,int4 size) const
return "";
}
string SleighBase::getExactRegisterName(AddrSpace *base,uintb off,int4 size) const
{
VarnodeData sym;
sym.space = base;
sym.offset = off;
sym.size = size;
map<VarnodeData,string>::const_iterator iter = varnode_xref.find(sym);
if (iter == varnode_xref.end()) return "";
return (*iter).second;
}
void SleighBase::getAllRegisters(map<VarnodeData,string> &reglist) const
{

View file

@ -4,9 +4,9 @@
* 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.
@ -79,6 +79,7 @@ public:
virtual ~SleighBase(void) {} ///< Destructor
virtual const VarnodeData &getRegister(const string &nm) const;
virtual string getRegisterName(AddrSpace *base,uintb off,int4 size) const;
virtual string getExactRegisterName(AddrSpace *base,uintb off,int4 size) const;
virtual void getAllRegisters(map<VarnodeData,string> &reglist) const;
virtual void getUserOpNames(vector<string> &res) const;

View file

@ -4,9 +4,9 @@
* 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.
@ -188,6 +188,49 @@ bool JoinRecord::operator<(const JoinRecord &op2) const
}
}
/// Assuming the given list of VarnodeData go from most significant to least significant,
/// merge any contiguous elements in the list. Varnodes that are not in the \e stack address space
/// are only merged if the resulting byte range has a formal register name.
/// \param seq is the given list of VarnodeData
/// \param trans is the language to use for register names
void JoinRecord::mergeSequence(vector<VarnodeData> &seq,const Translate *trans)
{
int4 i=1;
while(i<seq.size()) {
VarnodeData &hi(seq[i-1]);
VarnodeData &lo(seq[i]);
if (hi.isContiguous(lo))
break;
i += 1;
}
if (i >= seq.size()) return;
vector<VarnodeData> res;
i = 1;
res.push_back(seq.front());
bool lastIsInformal = false;
while(i<seq.size()) {
VarnodeData &hi(res.back());
VarnodeData &lo(seq[i]);
if (hi.isContiguous(lo)) {
hi.offset = hi.space->isBigEndian() ? hi.offset : lo.offset;
hi.size += lo.size;
if (hi.space->getType() != IPTR_SPACEBASE) {
lastIsInformal = trans->getExactRegisterName(hi.space, hi.offset, hi.size).size() == 0;
}
}
else {
if (lastIsInformal)
break;
res.push_back(lo);
}
i += 1;
}
if (lastIsInformal) // If the merge contains an informal register
return; // throw it out and keep the original sequence
seq = res;
}
/// Initialize manager containing no address spaces. All the cached space slots are set to null
AddrSpaceManager::AddrSpaceManager(void)

View file

@ -4,9 +4,9 @@
* 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.
@ -204,6 +204,7 @@ public:
const VarnodeData &getUnified(void) const { return unified; } ///< Get the Varnode whole
Address getEquivalentAddress(uintb offset,int4 &pos) const; ///< Given offset in \e join space, get equivalent address of piece
bool operator<(const JoinRecord &op2) const; ///< Compare records lexigraphically by pieces
static void mergeSequence(vector<VarnodeData> &seq,const Translate *trans); ///< Merge any contiguous ranges in a sequence
};
/// \brief Comparator for JoinRecord objects
@ -368,18 +369,26 @@ public:
/// \return the VarnodeData for the register
virtual const VarnodeData &getRegister(const string &nm) const=0;
/// \brief Get the name of a register given its location
/// \brief Get the name of the smallest containing register given a location and size
///
/// Generic references to locations in a \e register space can
/// be translated into the associated register \e name. If the
/// location doesn't match a register \e exactly, an empty string
/// is returned.
/// Generic references to locations in a \e register space are translated into the
/// register \e name. If a containing register isn't found, an empty string is returned.
/// \param base is the address space containing the location
/// \param off is the offset of the location
/// \param size is the size of the location
/// \return the name of the register, or an empty string
virtual string getRegisterName(AddrSpace *base,uintb off,int4 size) const=0;
/// \brief Get the name of a register with an exact location and size
///
/// If a register exists with the given location and size, return the name of the register.
/// Otherwise return the empty string.
/// \param base is the address space containing the location
/// \param off is the offset of the location
/// \param size is the size of the location
/// \return the name of the register, or an empty string
virtual string getExactRegisterName(AddrSpace *base,uintb off,int4 size) const=0;
/// \brief Get a list of all register names and the corresponding location
///
/// Most processors have a list of named registers and possibly other memory locations