GP-2578 Display volatile reads/writes as simple assignments

This commit is contained in:
caheckman 2022-09-20 13:17:02 -04:00
parent 7e24c986ad
commit 072d8fa08f
22 changed files with 221 additions and 65 deletions

View file

@ -3831,6 +3831,8 @@ int4 ActionDeadCode::apply(Funcdata &data)
}
if (!op->isAssignment())
continue;
if (op->holdOutput())
pushConsumed(~((uintb)0),op->getOut(),worklist);
}
else if (!op->isAssignment()) {
OpCode opc = op->code();

View file

@ -225,6 +225,7 @@ public:
bool isTypeLocked(void) const { return ((flags&Varnode::typelock)!=0); } ///< Is the Symbol type-locked
bool isNameLocked(void) const { return ((flags&Varnode::namelock)!=0); } ///< Is the Symbol name-locked
bool isSizeTypeLocked(void) const { return ((dispflags & size_typelock)!=0); } ///< Is the Symbol size type-locked
bool isVolatile(void) const { return ((flags & Varnode::volatil)!=0); } ///< Is the Symbol volatile
bool isThisPointer(void) const { return ((dispflags & is_this_ptr)!=0); } ///< Is \b this the "this" pointer
bool isIndirectStorage(void) const { return ((flags&Varnode::indirectstorage)!=0); } ///< Is storage really a pointer to the true Symbol
bool isHiddenReturn(void) const { return ((flags&Varnode::hiddenretparm)!=0); } ///< Is this a reference to the function return value

View file

@ -608,7 +608,9 @@ bool Funcdata::replaceVolatile(Varnode *vn)
// Create a userop of type specified by vw_op
opSetInput(newop,newConstant(4,vw_op->getIndex()),0);
// The first parameter is the offset of volatile memory location
opSetInput(newop,newCodeRef(vn->getAddr()),1);
Varnode *annoteVn = newCodeRef(vn->getAddr());
annoteVn->setFlags(Varnode::volatil);
opSetInput(newop,annoteVn,1);
// Replace the volatile variable with a temp
Varnode *tmp = newUnique(vn->getSize());
opSetOutput(defop,tmp);
@ -629,9 +631,13 @@ bool Funcdata::replaceVolatile(Varnode *vn)
// Create a userop of type specified by vr_op
opSetInput(newop,newConstant(4,vr_op->getIndex()),0);
// The first parameter is the offset of the volatile memory loc
opSetInput(newop,newCodeRef(vn->getAddr()),1);
Varnode *annoteVn = newCodeRef(vn->getAddr());
annoteVn->setFlags(Varnode::volatil);
opSetInput(newop,annoteVn,1);
opSetInput(readop,tmp,readop->getSlot(vn));
opInsertBefore(newop,readop); // Insert before read
if (vr_op->getDisplay() != 0) // Unless the display is functional,
newop->setHoldOutput(); // read value may not be used. Keep it around anyway.
}
if (vn->isTypeLock()) // If the original varnode had a type locked on it
newop->setAdditionalFlag(PcodeOp::special_prop); // Mark this op as doing special propagation

View file

@ -110,7 +110,8 @@ public:
warning = 8, ///< Warning has been generated for this op
incidental_copy = 0x10, ///< Treat this as \e incidental for parameter recovery algorithms
is_cpool_transformed = 0x20, ///< Have we checked for cpool transforms
stop_type_propagation = 0x40 ///< Stop data-type propagation into output from descendants
stop_type_propagation = 0x40, ///< Stop data-type propagation into output from descendants
hold_output = 0x80 ///< Output varnode (of call) should not be removed if it is unread
};
private:
TypeOp *opcode; ///< Pointer to class providing behavioral details of the operation
@ -209,6 +210,8 @@ public:
bool stopsTypePropagation(void) const { return ((addlflags&stop_type_propagation)!=0); } ///< Is data-type propagation from below stopped
void setStopTypePropagation(void) { addlflags |= stop_type_propagation; } ///< Stop data-type propagation from below
void clearStopTypePropagation(void) { addlflags &= ~stop_type_propagation; } ///< Allow data-type propagation from below
bool holdOutput(void) const { return ((addlflags&hold_output)!=0); } ///< If \b true, do not remove output as dead code
void setHoldOutput(void) { addlflags |= hold_output; } ///< Prevent output from being removed as dead code
bool stopsCopyPropagation(void) const { return ((flags&no_copy_propagation)!=0); } ///< Does \b this allow COPY propagation
void setStopCopyPropagation(void) { flags |= no_copy_propagation; } ///< Stop COPY propagation through inputs
/// \brief Return \b true if this LOADs or STOREs from a dynamic \e spacebase pointer

View file

@ -115,7 +115,8 @@ public:
param_color = 6, ///< Function parameters
global_color = 7, ///< Global variable identifiers
no_color = 8, ///< Un-highlighted
error_color = 9 ///< Indicates a warning or error state
error_color = 9, ///< Indicates a warning or error state
special_color = 10 ///< A token with special/highlighted meaning
};
virtual ~Emit(void) {} ///< Destructor

View file

@ -614,19 +614,31 @@ void PrintC::opCallind(const PcodeOp *op)
void PrintC::opCallother(const PcodeOp *op)
{
string nm = op->getOpcode()->getOperatorName(op);
pushOp(&function_call,op);
pushAtom(Atom(nm,optoken,EmitMarkup::funcname_color,op));
if (op->numInput() > 1) {
for(int4 i=1;i<op->numInput()-1;++i)
pushOp(&comma,op);
// implied vn's pushed on in reverse order for efficiency
// see PrintLanguage::pushVnImplied
for(int4 i=op->numInput()-1;i>=1;--i)
pushVn(op->getIn(i),op,mods);
UserPcodeOp *userop = glb->userops.getOp(op->getIn(0)->getOffset());
uint4 display = userop->getDisplay();
if (display == UserPcodeOp::annotation_assignment) {
pushOp(&assignment,op);
pushVn(op->getIn(2),op,mods);
pushVn(op->getIn(1),op,mods);
}
else if (display == UserPcodeOp::no_operator) {
pushVn(op->getIn(1),op,mods);
}
else { // Emit using functional syntax
string nm = op->getOpcode()->getOperatorName(op);
pushOp(&function_call,op);
pushAtom(Atom(nm,optoken,EmitMarkup::funcname_color,op));
if (op->numInput() > 1) {
for(int4 i = 1;i < op->numInput() - 1;++i)
pushOp(&comma,op);
// implied vn's pushed on in reverse order for efficiency
// see PrintLanguage::pushVnImplied
for(int4 i = op->numInput() - 1;i >= 1;--i)
pushVn(op->getIn(i),op,mods);
}
else
pushAtom(Atom(EMPTY_STRING,blanktoken,EmitMarkup::no_color)); // Push empty token for void
}
else // Push empty token for void
pushAtom(Atom(EMPTY_STRING,blanktoken,EmitMarkup::no_color));
}
void PrintC::opConstructor(const PcodeOp *op,bool withNew)
@ -1755,21 +1767,8 @@ void PrintC::pushAnnotation(const Varnode *vn,const PcodeOp *op)
const Scope *symScope = op->getParent()->getFuncdata()->getScopeLocal();
int4 size = 0;
if (op->code() == CPUI_CALLOTHER) {
// This construction is for volatile CALLOTHERs where the input annotation is the original address
// of the volatile access
int4 userind = (int4)op->getIn(0)->getOffset();
VolatileWriteOp *vw_op = glb->userops.getVolatileWrite();
VolatileReadOp *vr_op = glb->userops.getVolatileRead();
if (userind == vw_op->getIndex()) { // Annotation from a volatile write
size = op->getIn(2)->getSize(); // Get size from the 3rd parameter of write function
}
else if (userind == vr_op->getIndex()) {
const Varnode *outvn = op->getOut();
if (outvn != (const Varnode *)0)
size = op->getOut()->getSize(); // Get size from output of read function
else
size = 1;
}
size = glb->userops.getOp(userind)->extractAnnotationSize(vn, op);
}
SymbolEntry *entry;
if (size != 0)
@ -1793,11 +1792,16 @@ void PrintC::pushAnnotation(const Varnode *vn,const PcodeOp *op)
else {
string regname = glb->translate->getRegisterName(vn->getSpace(),vn->getOffset(),size);
if (regname.empty()) {
Datatype *ct = glb->types->getBase(size,TYPE_UINT);
pushConstant(AddrSpace::byteToAddress(vn->getOffset(),vn->getSpace()->getWordSize()),ct,vn,op);
AddrSpace *spc = vn->getSpace();
string spacename = spc->getName();
spacename[0] = toupper( spacename[0] ); // Capitalize space
ostringstream s;
s << spacename;
s << hex << setfill('0') << setw(2*spc->getAddrSize());
s << AddrSpace::byteToAddress( vn->getOffset(), spc->getWordSize() );
regname = s.str();
}
else
pushAtom(Atom(regname,vartoken,EmitMarkup::var_color,op,vn));
pushAtom(Atom(regname,vartoken,EmitMarkup::special_color,op,vn));
}
}
@ -1805,7 +1809,9 @@ void PrintC::pushSymbol(const Symbol *sym,const Varnode *vn,const PcodeOp *op)
{
EmitMarkup::syntax_highlight tokenColor;
if (sym->getScope()->isGlobal())
if (sym->isVolatile())
tokenColor = EmitMarkup::special_color;
else if (sym->getScope()->isGlobal())
tokenColor = EmitMarkup::global_color;
else if (sym->getCategory() == Symbol::function_parameter)
tokenColor = EmitMarkup::param_color;

View file

@ -25,6 +25,12 @@ ElementId ELEM_CONSTRESOLVE = ElementId("constresolve",127);
ElementId ELEM_JUMPASSIST = ElementId("jumpassist",128);
ElementId ELEM_SEGMENTOP = ElementId("segmentop",129);
int4 UserPcodeOp::extractAnnotationSize(const Varnode *vn,const PcodeOp *op)
{
throw LowlevelError("Unexpected annotation input for CALLOTHER " + name);
}
void InjectedUserOp::decode(Decoder &decoder)
{
@ -68,6 +74,15 @@ string VolatileReadOp::getOperatorName(const PcodeOp *op) const
return appendSize(name,op->getOut()->getSize());
}
int4 VolatileReadOp::extractAnnotationSize(const Varnode *vn,const PcodeOp *op)
{
const Varnode *outvn = op->getOut();
if (outvn != (const Varnode *)0)
return op->getOut()->getSize(); // Get size from output of read function
return 1;
}
string VolatileWriteOp::getOperatorName(const PcodeOp *op) const
{
@ -75,6 +90,12 @@ string VolatileWriteOp::getOperatorName(const PcodeOp *op) const
return appendSize(name,op->getIn(2)->getSize());
}
int4 VolatileWriteOp::extractAnnotationSize(const Varnode *vn,const PcodeOp *op)
{
return op->getIn(2)->getSize(); // Get size from the 3rd parameter of write function
}
/// \param g is the owning Architecture for this instance of the segment operation
/// \param nm is the low-level name of the segment operation
/// \param ind is the constant id identifying the specific CALLOTHER variant
@ -291,11 +312,11 @@ void UserOpManage::setDefaults(Architecture *glb)
{
if (vol_read == (VolatileReadOp *)0) {
VolatileReadOp *volread = new VolatileReadOp(glb,"read_volatile",useroplist.size());
VolatileReadOp *volread = new VolatileReadOp(glb,"read_volatile",useroplist.size(), false);
registerOp(volread);
}
if (vol_write == (VolatileWriteOp *)0) {
VolatileWriteOp *volwrite = new VolatileWriteOp(glb,"write_volatile",useroplist.size());
VolatileWriteOp *volwrite = new VolatileWriteOp(glb,"write_volatile",useroplist.size(), false);
registerOp(volwrite);
}
}
@ -392,28 +413,39 @@ void UserOpManage::decodeSegmentOp(Decoder &decoder,Architecture *glb)
void UserOpManage::decodeVolatile(Decoder &decoder,Architecture *glb)
{
string readOpName;
string writeOpName;
bool functionalDisplay = false;
for(;;) {
uint4 attribId = decoder.getNextAttributeId();
if (attribId == 0) break;
if (attribId==ATTRIB_INPUTOP) {
VolatileReadOp *vr_op = new VolatileReadOp(glb,decoder.readString(),useroplist.size());
try {
registerOp(vr_op);
} catch(LowlevelError &err) {
delete vr_op;
throw err;
}
readOpName = decoder.readString();
}
else if (attribId==ATTRIB_OUTPUTOP) {
// Read in the volatile output tag
VolatileWriteOp *vw_op = new VolatileWriteOp(glb,decoder.readString(),useroplist.size());
try {
registerOp(vw_op);
} catch(LowlevelError &err) {
delete vw_op;
throw err;
}
writeOpName = decoder.readString();
}
else if (attribId == ATTRIB_FORMAT) {
string format = decoder.readString();
if (format == "functional")
functionalDisplay = true;
}
}
if (readOpName.size() == 0 || writeOpName.size() == 0)
throw LowlevelError("Missing inputop/outputop attributes in <volatile> element");
VolatileReadOp *vr_op = new VolatileReadOp(glb,readOpName,useroplist.size(),functionalDisplay);
try {
registerOp(vr_op);
} catch(LowlevelError &err) {
delete vr_op;
throw err;
}
VolatileWriteOp *vw_op = new VolatileWriteOp(glb,writeOpName,useroplist.size(),functionalDisplay);
try {
registerOp(vw_op);
} catch(LowlevelError &err) {
delete vw_op;
throw err;
}
}

View file

@ -43,15 +43,22 @@ extern ElementId ELEM_SEGMENTOP; ///< Marshaling element \<segmentop>
/// or program. At this base level, the only commonality is a formal \b name of the operator and
/// its CALLOTHER index. A facility for reading in implementation details is provided via decode().
class UserPcodeOp {
public:
enum userop_flags {
annotation_assignment = 1, ///< Displayed as assignment, `in1 = in2`, where the first parameter is an annotation
no_operator = 2 ///< Don't emit special token, just emit the first input parameter as expression
};
protected:
string name; ///< Low-level name of p-code operator
int4 useropindex; ///< Index passed in the CALLOTHER op
Architecture *glb; ///< Architecture owning the user defined op
uint4 flags; ///< Boolean attributes of the CALLOTHER
public:
UserPcodeOp(Architecture *g,const string &nm,int4 ind) {
name = nm; useropindex = ind; glb = g; } ///< Construct from name and index
name = nm; useropindex = ind; glb = g; flags = 0; } ///< Construct from name and index
const string &getName(void) const { return name; } ///< Get the low-level name of the p-code op
int4 getIndex(void) const { return useropindex; } ///< Get the constant id of the op
uint4 getDisplay(void) const { return (flags & (annotation_assignment | no_operator)); } ///< Get display type (0=functional)
virtual ~UserPcodeOp(void) {} ///< Destructor
/// \brief Get the symbol representing this operation in decompiled code
@ -63,6 +70,14 @@ public:
virtual string getOperatorName(const PcodeOp *op) const {
return name; }
/// \brief Assign a size to an annotation input to \b this userop
///
/// Assuming an annotation refers to a special symbol accessed by \b this operation, retrieve the
/// size (in bytes) of the symbol, which isn't ordinarily stored as part of the annotation.
/// \param vn is the annotation Varnode
/// \param op is the specific PcodeOp instance of \b this userop
virtual int4 extractAnnotationSize(const Varnode *vn,const PcodeOp *op);
/// \brief Restore the detailed description from a stream element
///
/// The details of how a user defined operation behaves are parsed from the element.
@ -120,9 +135,10 @@ public:
/// is the actual value read from memory.
class VolatileReadOp : public VolatileOp {
public:
VolatileReadOp(Architecture *g,const string &nm,int4 ind)
: VolatileOp(g,nm,ind) {} ///< Constructor
VolatileReadOp(Architecture *g,const string &nm,int4 ind,bool functional)
: VolatileOp(g,nm,ind) { flags = functional ? 0 : no_operator; } ///< Constructor
virtual string getOperatorName(const PcodeOp *op) const;
virtual int4 extractAnnotationSize(const Varnode *vn,const PcodeOp *op);
};
/// \brief An operation that writes to volatile memory
@ -133,9 +149,10 @@ public:
/// - The Varnode value being written to the memory
class VolatileWriteOp : public VolatileOp {
public:
VolatileWriteOp(Architecture *g,const string &nm,int4 ind)
: VolatileOp(g,nm,ind) {} ///< Constructor
VolatileWriteOp(Architecture *g,const string &nm,int4 ind,bool functional)
: VolatileOp(g,nm,ind) { flags = functional ? 0 : annotation_assignment; } ///< Constructor
virtual string getOperatorName(const PcodeOp *op) const;
virtual int4 extractAnnotationSize(const Varnode *vn,const PcodeOp *op);
};
/// \brief A user defined p-code op that has a dynamically defined procedure

View file

@ -910,6 +910,8 @@ void Varnode::encode(Encoder &encoder) const
encoder.writeBool(ATTRIB_UNAFF, true);
if (isInput())
encoder.writeBool(ATTRIB_INPUT, true);
if (isVolatile())
encoder.writeBool(ATTRIB_VOLATILE, true);
encoder.closeElement(ELEM_ADDR);
}