From 072d8fa08f6269984829277044417633233ff1d6 Mon Sep 17 00:00:00 2001 From: caheckman <48068198+caheckman@users.noreply.github.com> Date: Tue, 20 Sep 2022 13:17:02 -0400 Subject: [PATCH] GP-2578 Display volatile reads/writes as simple assignments --- .../src/decompile/cpp/coreaction.cc | 2 + .../Decompiler/src/decompile/cpp/database.hh | 1 + .../src/decompile/cpp/funcdata_varnode.cc | 10 ++- .../Decompiler/src/decompile/cpp/op.hh | 5 +- .../src/decompile/cpp/prettyprint.hh | 3 +- .../Decompiler/src/decompile/cpp/printc.cc | 68 ++++++++++--------- .../Decompiler/src/decompile/cpp/userop.cc | 66 +++++++++++++----- .../Decompiler/src/decompile/cpp/userop.hh | 27 ++++++-- .../Decompiler/src/decompile/cpp/varnode.cc | 2 + .../src/decompile/datatests/deadvolatile.xml | 3 +- .../src/decompile/datatests/readvolatile.xml | 4 +- .../src/main/doc/decompileplugin.xml | 1 + .../ghidra/app/decompiler/ClangToken.java | 3 +- .../app/decompiler/DecompileOptions.java | 15 ++++ .../component/ClangLayoutController.java | 1 + .../component/DecompilerHoverProvider.java | 3 + .../data/languages/processor_spec.rxg | 4 ++ .../program/model/pcode/GlobalSymbolMap.java | 40 ++++++++++- .../program/model/pcode/HighFunction.java | 9 +++ .../program/model/pcode/PcodeFactory.java | 8 ++- .../program/model/pcode/PcodeSyntaxTree.java | 6 +- .../ghidra/program/model/pcode/Varnode.java | 5 ++ 22 files changed, 221 insertions(+), 65 deletions(-) diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc index 8d0678a836..e6c42eff2e 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc @@ -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(); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/database.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/database.hh index f2ea83a151..8da3a41fa1 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/database.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/database.hh @@ -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 diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_varnode.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_varnode.cc index 6ec15c4863..b72f263ba5 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_varnode.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_varnode.cc @@ -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 diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/op.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/op.hh index b8b960c55c..d93dcd3c18 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/op.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/op.hh @@ -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 diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/prettyprint.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/prettyprint.hh index e27261ffd7..cc81dad23d 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/prettyprint.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/prettyprint.hh @@ -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 diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/printc.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/printc.cc index 75a21fb886..5c90024a2d 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/printc.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/printc.cc @@ -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;inumInput()-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; diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/userop.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/userop.cc index 6aa3cbaf8a..e603539416 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/userop.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/userop.cc @@ -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 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; } } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/userop.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/userop.hh index 8bc62223d8..a283255cb2 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/userop.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/userop.hh @@ -43,15 +43,22 @@ extern ElementId ELEM_SEGMENTOP; ///< Marshaling element \ /// 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 diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/varnode.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/varnode.cc index 59dce2075b..4675e9ee8d 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/varnode.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/varnode.cc @@ -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); } diff --git a/Ghidra/Features/Decompiler/src/decompile/datatests/deadvolatile.xml b/Ghidra/Features/Decompiler/src/decompile/datatests/deadvolatile.xml index 43e7bbacd6..10492be24d 100644 --- a/Ghidra/Features/Decompiler/src/decompile/datatests/deadvolatile.xml +++ b/Ghidra/Features/Decompiler/src/decompile/datatests/deadvolatile.xml @@ -26,5 +26,6 @@ print C quit -read_volatile.*0x1000 +xVar1 = Ram00001000; +xVar1 diff --git a/Ghidra/Features/Decompiler/src/decompile/datatests/readvolatile.xml b/Ghidra/Features/Decompiler/src/decompile/datatests/readvolatile.xml index 9187ec7e6f..93c815dd19 100644 --- a/Ghidra/Features/Decompiler/src/decompile/datatests/readvolatile.xml +++ b/Ghidra/Features/Decompiler/src/decompile/datatests/readvolatile.xml @@ -19,6 +19,6 @@ print C quit -read_volatile.*NVRAM.*30 -write_volatile.*NVRAM +Var1 = NVRAM\[30\]; +NVRAM\[.0\] = 0; diff --git a/Ghidra/Features/Decompiler/src/main/doc/decompileplugin.xml b/Ghidra/Features/Decompiler/src/main/doc/decompileplugin.xml index 10157c8c1c..bf9eaeaaca 100644 --- a/Ghidra/Features/Decompiler/src/main/doc/decompileplugin.xml +++ b/Ghidra/Features/Decompiler/src/main/doc/decompileplugin.xml @@ -3016,6 +3016,7 @@ Parameters - names of function input variables Types - names of data-types in variable declarations and casts Variables - names of local variables + Special - volatile variables and other special symbols diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangToken.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangToken.java index 50926e23dd..23609af976 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangToken.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangToken.java @@ -48,7 +48,8 @@ public class ClangToken implements ClangNode { public final static int GLOBAL_COLOR = 7; public final static int DEFAULT_COLOR = 8; public final static int ERROR_COLOR = 9; - public final static int MAX_COLOR = 10; + public final static int SPECIAL_COLOR = 10; + public final static int MAX_COLOR = 11; private ClangNode parent; private ClangLine lineparent; diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileOptions.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileOptions.java index 59a687c19f..fb5d59265f 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileOptions.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileOptions.java @@ -334,6 +334,9 @@ public class DecompileOptions { private final static String HIGHLIGHT_GLOBAL_MSG = "Display.Color for Globals"; private final static Color HIGHLIGHT_GLOBAL_DEF = Color.decode("0x009999"); private Color globalColor; + private final static String HIGHLIGHT_SPECIAL_MSG = "Display.Color for Special"; + private final static Color HIGHLIGHT_SPECIAL_DEF = Color.decode("0xCC0033"); + private Color specialColor; private final static String HIGHLIGHT_DEFAULT_MSG = "Display.Color Default"; private final static Color HIGHLIGHT_DEFAULT_DEF = Color.BLACK; private Color defaultColor; @@ -407,6 +410,7 @@ public class DecompileOptions { typeColor = HIGHLIGHT_TYPE_DEF; parameterColor = HIGHLIGHT_PARAMETER_DEF; globalColor = HIGHLIGHT_GLOBAL_DEF; + specialColor = HIGHLIGHT_SPECIAL_DEF; defaultColor = HIGHLIGHT_DEFAULT_DEF; codeViewerBackgroundColor = CODE_VIEWER_BACKGROUND_COLOR; defaultFont = DEFAULT_FONT; @@ -470,6 +474,7 @@ public class DecompileOptions { constantColor = opt.getColor(HIGHLIGHT_CONST_MSG, HIGHLIGHT_CONST_DEF); parameterColor = opt.getColor(HIGHLIGHT_PARAMETER_MSG, HIGHLIGHT_PARAMETER_DEF); globalColor = opt.getColor(HIGHLIGHT_GLOBAL_MSG, HIGHLIGHT_GLOBAL_DEF); + specialColor = opt.getColor(HIGHLIGHT_SPECIAL_MSG, HIGHLIGHT_SPECIAL_DEF); defaultColor = opt.getColor(HIGHLIGHT_DEFAULT_MSG, HIGHLIGHT_DEFAULT_DEF); codeViewerBackgroundColor = opt.getColor(CODE_VIEWER_BACKGROUND_COLOR_MSG, CODE_VIEWER_BACKGROUND_COLOR); @@ -638,6 +643,9 @@ public class DecompileOptions { opt.registerOption(HIGHLIGHT_GLOBAL_MSG, HIGHLIGHT_GLOBAL_DEF, new HelpLocation(HelpTopics.DECOMPILER, "DisplayTokenColor"), "Color used for highlighting global variables."); + opt.registerOption(HIGHLIGHT_SPECIAL_MSG, HIGHLIGHT_SPECIAL_DEF, + new HelpLocation(HelpTopics.DECOMPILER, "DisplayTokenColor"), + "Color used for volatile or other exceptional variables."); opt.registerOption(HIGHLIGHT_DEFAULT_MSG, HIGHLIGHT_DEFAULT_DEF, new HelpLocation(HelpTopics.DECOMPILER, "DisplayColorDefault"), "The color used when a specific color is not specified."); @@ -857,6 +865,13 @@ public class DecompileOptions { return globalColor; } + /** + * @return color associated with volatile variables or other special tokens + */ + public Color getSpecialColor() { + return specialColor; + } + /** * @return color for generic syntax or other unspecified tokens */ diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/ClangLayoutController.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/ClangLayoutController.java index 861c9bb6e1..bd9de25b7e 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/ClangLayoutController.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/ClangLayoutController.java @@ -240,6 +240,7 @@ public class ClangLayoutController implements LayoutModel, LayoutModelListener { syntax_color[ClangToken.GLOBAL_COLOR] = options.getGlobalColor(); syntax_color[ClangToken.DEFAULT_COLOR] = options.getDefaultColor(); syntax_color[ClangToken.ERROR_COLOR] = options.getErrorColor(); + syntax_color[ClangToken.SPECIAL_COLOR] = options.getSpecialColor(); // setting the metrics here will indirectly trigger the new font to be used deeper in // the bowels of the FieldPanel (you can get the font from the metrics) diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/DecompilerHoverProvider.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/DecompilerHoverProvider.java index 0d96086cd2..45a7d40816 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/DecompilerHoverProvider.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/DecompilerHoverProvider.java @@ -77,6 +77,9 @@ public class DecompilerHoverProvider extends AbstractHoverProvider { if (highVar instanceof HighGlobal) { reference = highVar.getRepresentative().getAddress(); } + else if (highVar == null && vn.getAddress().isLoadedMemoryAddress()) { + reference = vn.getAddress(); + } } return new ProgramLocation(program, token.getMinAddress(), reference); diff --git a/Ghidra/Framework/SoftwareModeling/data/languages/processor_spec.rxg b/Ghidra/Framework/SoftwareModeling/data/languages/processor_spec.rxg index 588eaf47b4..cc7d032433 100644 --- a/Ghidra/Framework/SoftwareModeling/data/languages/processor_spec.rxg +++ b/Ghidra/Framework/SoftwareModeling/data/languages/processor_spec.rxg @@ -72,6 +72,10 @@ + + + + diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/GlobalSymbolMap.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/GlobalSymbolMap.java index a7530d0683..aa3db5cd41 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/GlobalSymbolMap.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/GlobalSymbolMap.java @@ -51,8 +51,8 @@ public class GlobalSymbolMap { program = f.getFunction().getProgram(); func = f; symbolTable = program.getSymbolTable(); - addrMappedSymbols = new HashMap(); - symbolMap = new HashMap(); + addrMappedSymbols = new HashMap<>(); + symbolMap = new HashMap<>(); uniqueSymbolId = 0; } @@ -111,6 +111,42 @@ public class GlobalSymbolMap { return highSym; } + /** + * Some Varnode annotations refer to global symbols. Check if there is symbol at the + * Varnode address and, if there is, create a corresponding HighSymbol + * @param vn is the annotation Varnode + */ + public void populateAnnotation(Varnode vn) { + Address addr = vn.getAddress(); + if (!addr.isLoadedMemoryAddress() || addrMappedSymbols.containsKey(addr)) { + return; + } + Symbol symbol = symbolTable.getPrimarySymbol(addr); + if (symbol == null) { + return; + } + HighSymbol highSym; + if (symbol instanceof CodeSymbol) { + Data dataAt = program.getListing().getDataAt(addr); + DataType dataType = DataType.DEFAULT; + int sz = 1; + if (dataAt != null) { + dataType = dataAt.getDataType(); + sz = dataAt.getLength(); + } + + highSym = new HighCodeSymbol((CodeSymbol) symbol, dataType, sz, func); + } + else if (symbol instanceof FunctionSymbol) { + highSym = new HighFunctionShellSymbol(symbol.getID(), symbol.getName(), + symbol.getAddress(), func.getDataTypeManager()); + } + else { + return; + } + insertSymbol(highSym, symbol.getAddress()); + } + /** * Create a HighSymbol corresponding to an underlying Data object. The name of the symbol is * generated dynamically. A symbol is always returned unless the address is invalid, diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunction.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunction.java index cbec3a1adf..057f533ec1 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunction.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunction.java @@ -471,6 +471,15 @@ public class HighFunction extends PcodeSyntaxTree { encoder.closeElement(ELEM_FUNCTION); } + @Override + public void setVolatile(Varnode vn, boolean val) { + if (val) { + // Volatile varnodes are modeled as annotations with no HighVariable + // Give the volatile a chance to populate a global symbol + globalSymbols.populateAnnotation(vn); + } + } + public static Namespace findOverrideSpace(Function func) { SymbolTable symtab = func.getProgram().getSymbolTable(); return findNamespace(symtab, func, "override"); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeFactory.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeFactory.java index 3a30767796..4cb941f880 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeFactory.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeFactory.java @@ -144,6 +144,13 @@ public interface PcodeFactory { */ public void setUnaffected(Varnode vn, boolean val); + /** + * Mark (or unmark) the given Varnode with the "volatile" property + * @param vn is the given Varnode + * @param val is true if the Varnode should be marked volatile + */ + public void setVolatile(Varnode vn, boolean val); + /** * Associate a specific merge group with the given Varnode * @param vn is the given Varnode @@ -167,5 +174,4 @@ public interface PcodeFactory { * @return the new PcodeOp */ public PcodeOp newOp(SequenceNumber sq, int opc, ArrayList inputs, Varnode output); - } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeSyntaxTree.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeSyntaxTree.java index 0d58e2ee7a..6a362e5ca4 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeSyntaxTree.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeSyntaxTree.java @@ -331,6 +331,11 @@ public class PcodeSyntaxTree implements PcodeFactory { vnast.setUnaffected(val); } + @Override + public void setVolatile(Varnode vn, boolean val) { + // Currently we don't set anything directly on the Varnode + } + @Override public void setMergeGroup(Varnode vn, short val) { VarnodeAST vnast = (VarnodeAST) vn; @@ -524,5 +529,4 @@ public class PcodeSyntaxTree implements PcodeFactory { } decoder.closeElement(el); } - } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/Varnode.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/Varnode.java index 680ef28c86..01987285d7 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/Varnode.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/Varnode.java @@ -438,6 +438,11 @@ public class Varnode { vn = factory.setInput(vn, true); } } + else if (attribId == ATTRIB_VOLATILE.id()) { + if (decoder.readBool()) { + factory.setVolatile(vn, true); + } + } } decoder.closeElement(el); return vn;