mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 02:09:44 +02:00
ActionCollectLanedAccess
This commit is contained in:
parent
e7c75b663d
commit
dddcf4c715
5 changed files with 202 additions and 60 deletions
|
@ -495,6 +495,127 @@ int4 ActionStackPtrFlow::apply(Funcdata &data)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/// Mark every PcodeOp that is reachable from the given Varnode and create a LanedAccess record.
|
||||
/// \param data is the function to trace through
|
||||
/// \param base is the description of the laned register being traced from
|
||||
/// \param vn is the Varnode to trace
|
||||
/// \param pos is the significance position of the Varnode within the laned register
|
||||
void ActionCollectLanedAccess::traceVarnode(Funcdata &data,const LanedRegister *base,Varnode *vn,int4 pos)
|
||||
|
||||
{
|
||||
list<PcodeOp *>::const_iterator iter = vn->beginDescend();
|
||||
int4 step = 0;
|
||||
while(step < 2) {
|
||||
PcodeOp *op;
|
||||
if (step == 0) {
|
||||
op = *iter;
|
||||
++iter;
|
||||
if (iter == vn->endDescend())
|
||||
step = 1;
|
||||
}
|
||||
else {
|
||||
step = 2;
|
||||
if (!vn->isWritten()) continue;
|
||||
op = vn->getDef();
|
||||
}
|
||||
if (op->isMark()) continue;
|
||||
// Make sure op is associated with a lane access records describing its biggest Varnode
|
||||
switch(op->code()) {
|
||||
case CPUI_LOAD:
|
||||
case CPUI_PIECE:
|
||||
case CPUI_INT_ZEXT:
|
||||
case CPUI_INT_SEXT:
|
||||
if (vn != op->getOut()) continue; // Biggest Varnode is the output
|
||||
break;
|
||||
case CPUI_SUBPIECE:
|
||||
if (vn != op->getIn(0)) continue; // Biggest Varnode is first input
|
||||
break;
|
||||
case CPUI_STORE:
|
||||
if (vn != op->getIn(2)) continue;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
op->setMark();
|
||||
data.opMarkLanedAccess(base, op, vn->getSize(), pos);
|
||||
}
|
||||
}
|
||||
|
||||
/// \brief Search for Varnodes that match the given laned vector register
|
||||
///
|
||||
/// All varnodes (bigger than 8 bytes) that are contained in the vector register are processed,
|
||||
/// looking for a working lane scheme. Data-flow from these Varnodes is traces and LanedAccess
|
||||
/// records are created for each PcodeOp flowed through.
|
||||
/// \param data is the function being traced
|
||||
/// \param lanedRegister is the given register and acceptable lane schemes
|
||||
/// \param iter is an iterator to the first Varnode that matches the vector register
|
||||
void ActionCollectLanedAccess::processLane(Funcdata &data,const LanedRegister &lanedRegister,
|
||||
VarnodeLocSet::const_iterator iter)
|
||||
{
|
||||
int4 fullSize = lanedRegister.getStorage().size;
|
||||
Address startAddress(lanedRegister.getStorage().getAddr());
|
||||
Address lastAddress(startAddress + (fullSize-1));
|
||||
VarnodeLocSet::const_iterator enditer = data.endLoc();
|
||||
while(iter != enditer) {
|
||||
Varnode *vn = *iter;
|
||||
++iter;
|
||||
if (lastAddress < vn->getAddr())
|
||||
break;
|
||||
if (vn->getSize() <= 8) // Varnode not big enough to be vector register
|
||||
continue;
|
||||
int4 diff = (int4)(vn->getOffset() - startAddress.getOffset());
|
||||
if (diff + vn->getSize() > fullSize) // Must be contained by full register
|
||||
continue;
|
||||
if (vn->getSpace()->isBigEndian())
|
||||
diff = fullSize - (diff + vn->getSize());
|
||||
traceVarnode(data, &lanedRegister, vn, diff);
|
||||
}
|
||||
}
|
||||
|
||||
/// Give each PcodeOp in the laned access list a chance to propagate to new PcodeOps,
|
||||
/// which will have new records added to the list.
|
||||
/// \param data is the function being traced
|
||||
void ActionCollectLanedAccess::propagate(Funcdata &data)
|
||||
|
||||
{
|
||||
list<LanedAccess>::const_iterator iter = data.beginLaneAccess();
|
||||
while(iter != data.endLaneAccess()) {
|
||||
const LanedAccess lanedAccess( *iter );
|
||||
PcodeOp *op = lanedAccess.getOp();
|
||||
int4 sz = lanedAccess.getSize();
|
||||
for(int4 i=0;i<op->numInput();++i) {
|
||||
Varnode *vn = op->getIn(i);
|
||||
if (vn->getSize() != sz) continue; // Size must match exactly
|
||||
if (vn->isConstant() || vn->isAnnotation()) continue;
|
||||
traceVarnode(data, lanedAccess.getBase(), vn, lanedAccess.getBytePos());
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
|
||||
int4 ActionCollectLanedAccess::apply(Funcdata &data)
|
||||
|
||||
{
|
||||
if (data.getArch()->lanerecords.empty())
|
||||
return 0;
|
||||
const LanedRegister &lastRecord( data.getArch()->lanerecords.back() );
|
||||
Address lastAddress( lastRecord.getStorage().getAddr() + lastRecord.getStorage().size - 1);
|
||||
list<LanedRegister>::const_iterator iter;
|
||||
for(iter=data.getArch()->lanerecords.begin();iter!=data.getArch()->lanerecords.end();++iter) {
|
||||
const LanedRegister &lanedRegister( *iter );
|
||||
VarnodeLocSet::const_iterator viter = data.beginLoc(lanedRegister.getStorage().getAddr());
|
||||
if (viter == data.endLoc()) break;
|
||||
Varnode *vn = *viter;
|
||||
if (lastAddress < vn->getAddr()) break;
|
||||
processLane(data,lanedRegister,viter);
|
||||
}
|
||||
propagate(data);
|
||||
list<LanedAccess>::const_iterator aiter;
|
||||
for(aiter=data.beginLaneAccess();aiter!=data.endLaneAccess();++aiter)
|
||||
(*aiter).getOp()->clearMark();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// \brief Try to divide a single Varnode into lanes
|
||||
///
|
||||
/// Look for a CPUI_SUBPIECE op that takes the given Varnode as the input or a
|
||||
|
@ -505,8 +626,10 @@ int4 ActionStackPtrFlow::apply(Funcdata &data)
|
|||
/// \param data is the function being transformed
|
||||
/// \param vn is the given single Varnode
|
||||
/// \param lanedRegister is acceptable set of lane sizes for the Varnode
|
||||
/// \param bytePos is the significance position of the Varnode within the laned register
|
||||
/// \param allowDowncast is \b true if we allow lane systems with SUBPIECE terminators
|
||||
bool ActionLaneDivide::processVarnode(Funcdata &data,Varnode *vn,const LanedRegister &lanedRegister,bool allowDowncast)
|
||||
bool ActionLaneDivide::processVarnode(Funcdata &data,Varnode *vn,const LanedRegister &lanedRegister,int4 bytePos,
|
||||
bool allowDowncast)
|
||||
|
||||
{
|
||||
list<PcodeOp *>::const_iterator iter = vn->beginDescend();
|
||||
|
@ -536,10 +659,6 @@ bool ActionLaneDivide::processVarnode(Funcdata &data,Varnode *vn,const LanedRegi
|
|||
if (checkedLanes.allowedLane(curSize)) continue;
|
||||
checkedLanes.addSize(curSize); // Only check this scheme once
|
||||
LaneDescription description(lanedRegister.getStorage().size,curSize); // Lane scheme dictated by curSize
|
||||
int4 bytePos = (int4)(lanedRegister.getStorage().offset - vn->getOffset());
|
||||
if (lanedRegister.getStorage().space->isBigEndian()) {
|
||||
bytePos = lanedRegister.getStorage().size - (bytePos + vn->getSize()); // Convert to significance order
|
||||
}
|
||||
if (!description.subset(bytePos,vn->getSize())) // Try to restrict lane scheme to actual Varnode
|
||||
continue;
|
||||
LaneDivide laneDivide(&data,vn,description,allowDowncast);
|
||||
|
@ -553,63 +672,34 @@ bool ActionLaneDivide::processVarnode(Funcdata &data,Varnode *vn,const LanedRegi
|
|||
return false;
|
||||
}
|
||||
|
||||
/// \brief Search for and attempt to split Varnodes that match the given laned vector register
|
||||
///
|
||||
/// All varnodes (bigger than 8 bytes) that are contained in the vector register are processed,
|
||||
/// looking for a working lane scheme, and splitting based on that scheme. Return \b false if there
|
||||
/// is at least one eligible Varnode that could not be split.
|
||||
/// \param data is the function being modified
|
||||
/// \param lanedRegister is the given register and acceptable lane schemes
|
||||
/// \param allowDowncast is \b true if SUBPIECE terminators are allowed in the schemes
|
||||
/// \param iter is an iterator to the first Varnode that matches the vector register
|
||||
/// \return \b true if all varnodes were successfully split
|
||||
bool ActionLaneDivide::processLane(Funcdata &data,const LanedRegister &lanedRegister,bool allowDowncast,
|
||||
VarnodeLocSet::const_iterator iter)
|
||||
|
||||
{
|
||||
int4 fullSize = lanedRegister.getStorage().size;
|
||||
Address startAddress(lanedRegister.getStorage().getAddr());
|
||||
Address lastAddress(startAddress + (fullSize-1));
|
||||
VarnodeLocSet::const_iterator enditer = data.endLoc();
|
||||
bool res = true;
|
||||
while(iter != enditer) {
|
||||
Varnode *vn = *iter;
|
||||
++iter;
|
||||
if (lastAddress < vn->getAddr())
|
||||
break;
|
||||
if (vn->getSize() <= 8) // Varnode not big enough to be vector register
|
||||
continue;
|
||||
int4 diff = (int4)(vn->getOffset() - startAddress.getOffset());
|
||||
if (diff + vn->getSize() > fullSize) // Must be contained by full register
|
||||
continue;
|
||||
if (processVarnode(data,vn,lanedRegister,allowDowncast)) {
|
||||
// If changes were made, iterator may no longer be valid, generate a new one
|
||||
iter = data.beginLoc(startAddress);
|
||||
res = true; // We may have eliminated a previous failure
|
||||
}
|
||||
else
|
||||
res = false; // Note the failure
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
int4 ActionLaneDivide::apply(Funcdata &data)
|
||||
|
||||
{
|
||||
if (data.getArch()->lanerecords.empty())
|
||||
return 0;
|
||||
const LanedRegister &lastRecord( data.getArch()->lanerecords.back() );
|
||||
Address lastAddress( lastRecord.getStorage().getAddr() + lastRecord.getStorage().size - 1);
|
||||
list<LanedRegister>::const_iterator iter;
|
||||
for(iter=data.getArch()->lanerecords.begin();iter!=data.getArch()->lanerecords.end();++iter) {
|
||||
const LanedRegister &lanedRegister( *iter );
|
||||
VarnodeLocSet::const_iterator viter = data.beginLoc(lanedRegister.getStorage().getAddr());
|
||||
if (viter == data.endLoc()) break;
|
||||
Varnode *vn = *viter;
|
||||
if (lastAddress < vn->getAddr()) break;
|
||||
if (!processLane(data,lanedRegister,false,viter)) // Try without SUBPIECE terminators
|
||||
processLane(data,lanedRegister,true,viter); // If we fail, try with SUBPIECE terminators
|
||||
list<LanedAccess>::const_iterator iter;
|
||||
bool allowDowncast = false;
|
||||
for(int4 i=0;i<2;++i) {
|
||||
for(iter=data.beginLaneAccess();iter!=data.endLaneAccess();++iter) {
|
||||
const LanedAccess &lanedAccess(*iter);
|
||||
PcodeOp *op = lanedAccess.getOp();
|
||||
if (op->isDead()) continue;
|
||||
Varnode *vn = op->getOut();
|
||||
if (vn != (Varnode *)0 && vn->getSize() == lanedAccess.getSize())
|
||||
processVarnode(data, vn, *lanedAccess.getBase(), lanedAccess.getBytePos(), allowDowncast);
|
||||
else {
|
||||
for(int4 j=0;j<op->numInput();++j) {
|
||||
vn = op->getIn(j);
|
||||
if (vn->getSize() == lanedAccess.getSize()) {
|
||||
if (!vn->isConstant() && !vn->isAnnotation() && !vn->isWritten()) {
|
||||
processVarnode(data, vn, *lanedAccess.getBase(), lanedAccess.getBytePos(), allowDowncast);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
allowDowncast = true;
|
||||
}
|
||||
data.clearLanedAccessList();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -4624,6 +4714,7 @@ void universal_action(Architecture *conf)
|
|||
// actmainloop->addAction( new ActionParamShiftStop("paramshift") );
|
||||
actmainloop->addAction( new ActionRestrictLocal("localrecovery") ); // Do before dead code removed
|
||||
actmainloop->addAction( new ActionDeadCode("deadcode") );
|
||||
actmainloop->addAction( new ActionCollectLanedAccess("base") );
|
||||
actmainloop->addAction( new ActionDynamicMapping("dynamic") ); // Must come before restructurevarnode and infertypes
|
||||
actmainloop->addAction( new ActionRestructureVarnode("localrecovery") );
|
||||
actmainloop->addAction( new ActionSpacebase("base") ); // Must come before infertypes and nonzeromask
|
||||
|
|
|
@ -97,6 +97,26 @@ public:
|
|||
virtual int4 apply(Funcdata &data);
|
||||
};
|
||||
|
||||
/// \brief Find PcodeOps that are accessing values from laned registers
|
||||
///
|
||||
/// This looks for Varnodes that match any of the LanedRegister records associated with
|
||||
/// the architecture. A record is created in the function's LanedAccess list for every
|
||||
/// PcodeOp that accesses one of these Varnodes. These are later examined by ActionLaneDivide.
|
||||
/// The LanedRegister is the key for finding the Varnode, but the \e big property can propagate
|
||||
/// through data-flow to other Varnodes that are not necessarily using the same storage (i.e. uniques).
|
||||
class ActionCollectLanedAccess : public Action {
|
||||
void traceVarnode(Funcdata &data,const LanedRegister *base,Varnode *vn,int4 pos); ///< Trace a big Varnode instance to other big Varnodes
|
||||
void processLane(Funcdata &data,const LanedRegister &lanedRegister,VarnodeLocSet::const_iterator iter);
|
||||
void propagate(Funcdata &data); ///< Discover other PcodeOps that use laned values
|
||||
public:
|
||||
ActionCollectLanedAccess(const string &g) : Action(rule_onceperfunc,"collectlanedaccess",g) {} ///< Constructor
|
||||
virtual Action *clone(const ActionGroupList &grouplist) const {
|
||||
if (!grouplist.contains(getGroup())) return (Action *)0;
|
||||
return new ActionCollectLanedAccess(getGroup());
|
||||
}
|
||||
virtual int4 apply(Funcdata &data);
|
||||
};
|
||||
|
||||
/// \brief Find Varnodes with a vectorized lane scheme and attempt to split the lanes
|
||||
///
|
||||
/// The Architecture lists (vector) registers that may be used to perform parallelized operations
|
||||
|
@ -104,8 +124,7 @@ public:
|
|||
/// if a particular lane scheme makes sense in terms of the function's data-flow, and then
|
||||
/// rewrites the data-flow so that the lanes become explicit Varnodes.
|
||||
class ActionLaneDivide : public Action {
|
||||
bool processVarnode(Funcdata &data,Varnode *vn,const LanedRegister &lanedRegister,bool allowDowncast);
|
||||
bool processLane(Funcdata &data,const LanedRegister &lanedRegister,bool allowDowncast,VarnodeLocSet::const_iterator iter);
|
||||
bool processVarnode(Funcdata &data,Varnode *vn,const LanedRegister &lanedRegister,int4 bytePos,bool allowDowncast);
|
||||
public:
|
||||
ActionLaneDivide(const string &g) : Action(rule_onceperfunc,"lanedivide",g) {} ///< Constructor
|
||||
virtual Action *clone(const ActionGroupList &grouplist) const {
|
||||
|
|
|
@ -80,6 +80,7 @@ class Funcdata {
|
|||
Merge covermerge; ///< Variable range intersection algorithms
|
||||
ParamActive *activeoutput; ///< Data for assessing which parameters are passed to \b this function
|
||||
Override localoverride; ///< Overrides of data-flow, prototypes, etc. that are local to \b this function
|
||||
list<LanedAccess> lanedList; ///< List of ops that are accessing potentially laned registers
|
||||
|
||||
// Low level Varnode functions
|
||||
void setVarnodeProperties(Varnode *vn) const; ///< Look-up boolean properties and data-type information
|
||||
|
@ -428,6 +429,10 @@ public:
|
|||
Varnode *opStackLoad(AddrSpace *spc,uintb off,uint4 sz,PcodeOp *op,Varnode *stackptr,bool insertafter);
|
||||
PcodeOp *opStackStore(AddrSpace *spc,uintb off,PcodeOp *op,bool insertafter);
|
||||
void opUndoPtradd(PcodeOp *op,bool finalize); ///< Convert a CPUI_PTRADD back into a CPUI_INT_ADD
|
||||
void opMarkLanedAccess(const LanedRegister *base,PcodeOp *op,int4 sz,int4 pos); ///< Mark op as using laned register
|
||||
list<LanedAccess>::const_iterator beginLaneAccess(void) const { return lanedList.begin(); } ///< Beginning iterator over laned accesses
|
||||
list<LanedAccess>::const_iterator endLaneAccess(void) const { return lanedList.end(); } ///< Ending iterator over laned accesses
|
||||
void clearLanedAccessList(void) { lanedList.clear(); } ///< Clear records from the laned access list
|
||||
|
||||
/// \brief Start of PcodeOp objects with the given op-code
|
||||
list<PcodeOp *>::const_iterator beginOp(OpCode opc) const { return obank.begin(opc); }
|
||||
|
|
|
@ -557,6 +557,19 @@ void Funcdata::opUndoPtradd(PcodeOp *op,bool finalize)
|
|||
opInsertBefore(multOp,op);
|
||||
}
|
||||
|
||||
/// Store a record indicating a potential use of a large laned register. Generally, the output
|
||||
/// of the op is the use, except in the case of SUBPIECE or STORE, where getIn(0) and getIn(2)
|
||||
/// respectively are the Varnodes being used.
|
||||
/// \param base is the description of the laned register that is triggering this record
|
||||
/// \param op is the PcodeOp using the large Varnode
|
||||
/// \param sz is the size of the large Varnode in bytes
|
||||
/// \param pos is the significance position of the Varnode, relative to the LanedRegister description
|
||||
void Funcdata::opMarkLanedAccess(const LanedRegister *base,PcodeOp *op,int4 sz,int4 pos)
|
||||
|
||||
{
|
||||
lanedList.push_back(LanedAccess(base,op,sz,pos));
|
||||
}
|
||||
|
||||
/// Make a clone of the given PcodeOp, copying control-flow properties as well. The data-type
|
||||
/// is \e not cloned.
|
||||
/// \param op is the PcodeOp to clone
|
||||
|
|
|
@ -98,6 +98,20 @@ public:
|
|||
bool operator<(const LanedRegister &op2) const { return (storage < op2.storage); } ///< Compare based on VarnodeData
|
||||
};
|
||||
|
||||
/// \brief Record describing the access of a large Varnode that can be traced to a LanedRegister
|
||||
class LanedAccess {
|
||||
const LanedRegister *base; ///< Base register dictating the lane scheme
|
||||
PcodeOp *op; ///< Operation using the big register
|
||||
int4 size; ///< Size of the register in bytes
|
||||
int4 bytePos; ///< Significance position relative to the laned register
|
||||
public:
|
||||
LanedAccess(const LanedRegister *b,PcodeOp *o,int4 sz,int4 pos) { base=b; op=o; size=sz; bytePos=pos; } ///< Constructor
|
||||
const LanedRegister *getBase(void) const { return base; } ///< Get the base LanedRegister being traced
|
||||
PcodeOp *getOp(void) const { return op; } ///< Get the PcodeOp using the large Varnode
|
||||
int4 getSize(void) const { return size; } ///< Get the size of the Varnode being accessed
|
||||
int4 getBytePos(void) const { return bytePos; } ///< Get the significance position relative to the laned register
|
||||
};
|
||||
|
||||
/// \brief Description of logical lanes within a \b big Varnode
|
||||
///
|
||||
/// A \b lane is a byte offset and size within a Varnode. Lanes within a
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue