diff --git a/Ghidra/Features/Decompiler/certification.manifest b/Ghidra/Features/Decompiler/certification.manifest index ccf3b465a8..2799dc5bd4 100644 --- a/Ghidra/Features/Decompiler/certification.manifest +++ b/Ghidra/Features/Decompiler/certification.manifest @@ -30,6 +30,8 @@ src/decompile/datatests/noforloop_alias.xml||GHIDRA||||END| src/decompile/datatests/noforloop_globcall.xml||GHIDRA||||END| src/decompile/datatests/noforloop_iterused.xml||GHIDRA||||END| src/decompile/datatests/offsetarray.xml||GHIDRA||||END| +src/decompile/datatests/pointercmp.xml||GHIDRA||||END| +src/decompile/datatests/pointerrel.xml||GHIDRA||||END| src/decompile/datatests/promotecompare.xml||GHIDRA||||END| src/decompile/datatests/readvolatile.xml||GHIDRA||||END| src/decompile/datatests/sbyte.xml||GHIDRA||||END| diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc index 04b69e90b8..ae0b1c3a3c 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc @@ -2231,10 +2231,12 @@ int4 ActionSetCasts::castOutput(PcodeOp *op,Funcdata &data,CastStrategy *castStr Datatype *outct,*ct,*tokenct; Varnode *vn,*outvn; PcodeOp *newop; + HighVariable *outHigh; bool force=false; tokenct = op->getOpcode()->getOutputToken(op,castStrategy); outvn = op->getOut(); + outHigh = outvn->getHigh(); if (outvn->isImplied()) { // implied varnode must have parse type if (outvn->isTypeLock()) { @@ -2242,13 +2244,13 @@ int4 ActionSetCasts::castOutput(PcodeOp *op,Funcdata &data,CastStrategy *castStr // The Varnode input to a CPUI_RETURN is marked as implied but // casting should act as if it were explicit if (outOp == (PcodeOp *)0 || outOp->code() != CPUI_RETURN) { - force = !isOpIdentical(outvn->getType(), tokenct); + force = !isOpIdentical(outHigh->getType(), tokenct); } } - else if (outvn->getType()->getMetatype() != TYPE_PTR) // If implied varnode has an atomic (non-pointer) type + else if (outHigh->getType()->getMetatype() != TYPE_PTR) // If implied varnode has an atomic (non-pointer) type outvn->updateType(tokenct,false,false); // Ignore it in favor of the token type else if (tokenct->getMetatype() == TYPE_PTR) { // If the token is a pointer AND implied varnode is pointer - outct = ((TypePointer *)outvn->getType())->getPtrTo(); + outct = ((TypePointer *)outHigh->getType())->getPtrTo(); type_metatype meta = outct->getMetatype(); // Preserve implied pointer if it points to a composite if ((meta!=TYPE_ARRAY)&&(meta!=TYPE_STRUCT)) @@ -2256,7 +2258,7 @@ int4 ActionSetCasts::castOutput(PcodeOp *op,Funcdata &data,CastStrategy *castStr } } if (!force) { - outct = outvn->getHigh()->getType(); // Type of result + outct = outHigh->getType(); // Type of result ct = castStrategy->castStandard(outct,tokenct,false,true); if (ct == (Datatype *)0) return 0; } @@ -4237,7 +4239,10 @@ void ActionInferTypes::buildLocaltypes(Funcdata &data) vn = *iter; if (vn->isAnnotation()) continue; if ((!vn->isWritten())&&(vn->hasNoDescend())) continue; - ct = vn->getLocalType(); + bool needsBlock = false; + ct = vn->getLocalType(needsBlock); + if (needsBlock) + vn->setStopUpPropagation(); #ifdef TYPEPROP_DEBUG propagationDebug(data.getArch(),vn,ct,(PcodeOp *)0,0,(Varnode *)0); #endif @@ -4271,11 +4276,12 @@ bool ActionInferTypes::writeBack(Funcdata &data) /// Determine if the given data-type edge looks like a pointer /// propagating through an "add a constant" operation. We assume the input /// to the edge has a pointer data-type. This routine returns one the commands: -/// - 0 indicates this is "add a constant" and the constant is passed back -/// - 1 indicating the pointer does not propagate through -/// - 2 the input data-type propagates through untransformed +/// - 0 indicates this is "add a constant" adding a zero (PTRSUB or PTRADD) +/// - 1 indicates this is "add a constant" and the constant is passed back +/// - 2 indicating the pointer does not propagate through +/// - 3 the input data-type propagates through untransformed /// -/// \param off passes back the constant offset if the command is '0' +/// \param off passes back the constant offset if the command is '0' or '1' /// \param op is the PcodeOp propagating the data-type /// \param slot is the input edge being propagated /// \param sz is the size of the data-type being pointed to @@ -4284,21 +4290,21 @@ int4 ActionInferTypes::propagateAddPointer(uintb &off,PcodeOp *op,int4 slot,int4 { if (op->code() == CPUI_PTRADD) { - if (slot != 0) return 1; + if (slot != 0) return 2; Varnode *constvn = op->getIn(1); uintb mult = op->getIn(2)->getOffset(); if (constvn->isConstant()) { off = (constvn->getOffset() * mult) & calc_mask(constvn->getSize()) ; - return 0; + return (off == 0) ? 0 : 1; } if (sz != 0 && (mult % sz) != 0) - return 1; - return 2; + return 2; + return 3; } if (op->code() == CPUI_PTRSUB) { - if (slot != 0) return 1; + if (slot != 0) return 2; off = op->getIn(1)->getOffset(); - return 0; + return (off == 0) ? 0 : 1; } if (op->code() == CPUI_INT_ADD) { Varnode *othervn = op->getIn(1-slot); @@ -4311,23 +4317,23 @@ int4 ActionInferTypes::propagateAddPointer(uintb &off,PcodeOp *op,int4 slot,int4 if (constvn->isConstant()) { uintb mult = constvn->getOffset(); if (mult == calc_mask(constvn->getSize())) // If multiplying by -1 - return 1; // Assume this is a pointer difference and don't propagate + return 2; // Assume this is a pointer difference and don't propagate if (sz != 0 && (mult % sz) !=0) - return 1; + return 2; } - return 2; + return 3; } } if (sz == 1) - return 2; - return 1; + return 3; + return 2; } if (othervn->getTempType()->getMetatype() == TYPE_PTR) // Check if othervn marked as ptr - return 1; + return 2; off = othervn->getOffset(); - return 0; + return (off == 0) ? 0 : 1; } - return 1; + return 2; } /// \brief Propagate a pointer data-type through an ADD operation. @@ -4347,27 +4353,38 @@ Datatype *ActionInferTypes::propagateAddIn2Out(TypeFactory *typegrp,PcodeOp *op, TypePointer *pointer = (TypePointer *)op->getIn(inslot)->getTempType(); // We know this is a pointer type uintb uoffset; int4 command = propagateAddPointer(uoffset,op,inslot,pointer->getPtrTo()->getSize()); - if (command == 1) return op->getOut()->getTempType(); // Doesn't look like a good pointer add - if (command != 2) { + if (command == 2) return op->getOut()->getTempType(); // Doesn't look like a good pointer add + TypePointer *parent = (TypePointer *)0; + uintb parentOff; + if (command != 3) { uoffset = AddrSpace::addressToByte(uoffset,pointer->getWordSize()); bool allowWrap = (op->code() != CPUI_PTRSUB); do { - pointer = pointer->downChain(uoffset,allowWrap,*typegrp); + pointer = pointer->downChain(uoffset,parent,parentOff,allowWrap,*typegrp); if (pointer == (TypePointer *)0) - return op->getOut()->getTempType(); + break; } while(uoffset != 0); } - Datatype *rettype = pointer; - if (rettype == (Datatype *)0) - rettype = op->getOut()->getTempType(); - if (op->getIn(inslot)->isSpacebase()) { - if (rettype->getMetatype() == TYPE_PTR) { - TypePointer *ptype = (TypePointer *)rettype; - if (ptype->getPtrTo()->getMetatype() == TYPE_SPACEBASE) - rettype = typegrp->getTypePointer(ptype->getSize(),typegrp->getBase(1,TYPE_UNKNOWN),ptype->getWordSize()); - } + if (parent != (TypePointer *)0) { + // If the innermost containing object is a TYPE_STRUCT or TYPE_ARRAY + // preserve info about this container + Datatype *pt; + if (pointer == (TypePointer *)0) + pt = typegrp->getBase(1,TYPE_UNKNOWN); // Offset does not point at a proper sub-type + else + pt = pointer->getPtrTo(); // The sub-type being directly pointed at + pointer = typegrp->getTypePointerRel(parent, pt, parentOff); } - return rettype; + if (pointer == (TypePointer *)0) { + if (command == 0) + return op->getIn(inslot)->getTempType(); + return op->getOut()->getTempType(); + } + if (op->getIn(inslot)->isSpacebase()) { + if (pointer->getPtrTo()->getMetatype() == TYPE_SPACEBASE) + pointer = typegrp->getTypePointer(pointer->getSize(),typegrp->getBase(1,TYPE_UNKNOWN),pointer->getWordSize()); + } + return pointer; } /// \brief Determine if propagation should happen along the given edge @@ -4468,7 +4485,13 @@ bool ActionInferTypes::propagateTypeEdge(TypeFactory *typegrp,PcodeOp *op,int4 i Varnode *invn,*outvn; Datatype *newtype; - outvn = (outslot==-1) ? op->getOut() : op->getIn(outslot); + if (outslot < 0) + outvn = op->getOut(); + else { + outvn = op->getIn(outslot); + if (outvn->stopsUpPropagation()) + return false; + } if (outvn->isAnnotation()) return false; if (outvn->isTypeLock()) return false; // Can't propagate through typelock invn = (inslot==-1) ? op->getOut() : op->getIn(inslot); @@ -4481,13 +4504,31 @@ bool ActionInferTypes::propagateTypeEdge(TypeFactory *typegrp,PcodeOp *op,int4 i return false; } switch(op->code()) { - case CPUI_INDIRECT: - case CPUI_COPY: - case CPUI_MULTIEQUAL: case CPUI_INT_LESS: case CPUI_INT_LESSEQUAL: case CPUI_INT_EQUAL: case CPUI_INT_NOTEQUAL: + if (invn->isSpacebase()) { + AddrSpace *spc = typegrp->getArch()->getDefaultDataSpace(); + newtype = typegrp->getTypePointer(alttype->getSize(),typegrp->getBase(1,TYPE_UNKNOWN),spc->getWordSize()); + } + else if (alttype->isPointerRel() && !outvn->isConstant()) { + TypePointerRel *relPtr = (TypePointerRel *)alttype; + if (relPtr->getParent()->getMetatype() == TYPE_STRUCT && relPtr->getPointerOffset() >= 0) { + // If we know the pointer is in the middle of a structure, don't propagate across comparison operators + // as the two sides of the operator are likely to be different types , and the other side can also + // get data-type information from the structure pointer + newtype = typegrp->getTypePointer(relPtr->getSize(),typegrp->getBase(1,TYPE_UNKNOWN),relPtr->getWordSize()); + } + else + newtype = alttype; + } + else + newtype = alttype; + break; + case CPUI_INDIRECT: + case CPUI_COPY: + case CPUI_MULTIEQUAL: case CPUI_INT_AND: case CPUI_INT_OR: case CPUI_INT_XOR: @@ -4805,6 +4846,7 @@ PcodeOp *ActionInferTypes::canonicalReturnOp(Funcdata &data) void ActionInferTypes::propagateAcrossReturns(Funcdata &data) { + if (data.getFuncProto().isOutputLocked()) return; PcodeOp *op = canonicalReturnOp(data); if (op == (PcodeOp *)0) return; TypeFactory *typegrp = data.getArch()->types; @@ -4826,7 +4868,7 @@ void ActionInferTypes::propagateAcrossReturns(Funcdata &data) if (vn->getTempType() == ct) continue; // Already propagated vn->setTempType(ct); #ifdef TYPEPROP_DEBUG - propagationDebug(typegrp->getArch(),vn,ct,retop,1,(Varnode *)0); + propagationDebug(typegrp->getArch(),vn,ct,op,1,(Varnode *)0); #endif propagateOneType(typegrp, vn); } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/database.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/database.cc index 8472b84a4d..c54843332d 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/database.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/database.cc @@ -2147,6 +2147,7 @@ void ScopeInternal::setAttribute(Symbol *sym,uint4 attr) attr &= (Varnode::typelock | Varnode::namelock | Varnode::readonly | Varnode::incidental_copy | Varnode::nolocalalias | Varnode::volatil | Varnode::indirectstorage | Varnode::hiddenretparm); sym->flags |= attr; + sym->checkSizeTypeLock(); } void ScopeInternal::clearAttribute(Symbol *sym,uint4 attr) @@ -2155,6 +2156,7 @@ void ScopeInternal::clearAttribute(Symbol *sym,uint4 attr) attr &= (Varnode::typelock | Varnode::namelock | Varnode::readonly | Varnode::incidental_copy | Varnode::nolocalalias | Varnode::volatil | Varnode::indirectstorage | Varnode::hiddenretparm); sym->flags &= ~attr; + sym->checkSizeTypeLock(); } void ScopeInternal::setDisplayFormat(Symbol *sym,uint4 attr) diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/ifacedecomp.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/ifacedecomp.cc index 3f7a2bf653..e6e4c745ea 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/ifacedecomp.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/ifacedecomp.cc @@ -127,6 +127,7 @@ void IfaceDecompCapability::registerCommands(IfaceStatus *status) status->registerCom(new IfcCallOtherFixup(),"fixup","callother"); status->registerCom(new IfcVolatile(),"volatile"); status->registerCom(new IfcReadonly(),"readonly"); + status->registerCom(new IfcPointerSetting(),"pointer","setting"); status->registerCom(new IfcPreferSplit(),"prefersplit"); status->registerCom(new IfcStructureBlocks(),"structure","blocks"); status->registerCom(new IfcAnalyzeRange(), "analyze","range"); @@ -2889,6 +2890,48 @@ void IfcReadonly::execute(istream &s) *status->optr << "Successfully marked range as readonly" << endl; } +/// \class IfcPointerSetting +/// \brief Create a pointer with additional settings: `pointer setting offset ` +/// +/// The new data-type is named and must be pointer. It must have a setting +/// - \b offset which creates a shifted pointer +void IfcPointerSetting::execute(istream &s) + +{ + if (dcp->conf == (Architecture *)0) + throw IfaceExecutionError("No load image present"); + string typeName; + string baseType; + string setting; + + s >> ws; + if (s.eof()) + throw IfaceParseError("Missing name"); + s >> typeName >> ws; + if (s.eof()) + throw IfaceParseError("Missing base-type"); + s >> baseType >> ws; + if (s.eof()) + throw IfaceParseError("Missing setting"); + s >> setting >> ws; + if (setting == "offset") { + int4 off = -1; + s.unsetf(ios::dec | ios::hex | ios::oct); // Let user specify base + s >> off; + if (off <= 0) + throw IfaceParseError("Missing offset"); + Datatype *bt = dcp->conf->types->findByName(baseType); + if (bt == (Datatype *)0 || bt->getMetatype() != TYPE_STRUCT) + throw IfaceParseError("Base-type must be a structure"); + Datatype *ptrto = TypePointerRel::getPtrTo(bt, off, *dcp->conf->types); + AddrSpace *spc = dcp->conf->getDefaultDataSpace(); + dcp->conf->types->getTypePointerRel(spc->getAddrSize(), bt, ptrto, spc->getWordSize(), off,typeName); + } + else + throw IfaceParseError("Unknown pointer setting: "+setting); + *status->optr << "Successfully created pointer: " << typeName << endl; +} + /// \class IfcPreferSplit /// \brief Mark a storage location to be split: `prefersplit ` /// diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/ifacedecomp.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/ifacedecomp.hh index 9d71cfcc26..a5ea99a824 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/ifacedecomp.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/ifacedecomp.hh @@ -554,6 +554,11 @@ public: virtual void execute(istream &s); }; +class IfcPointerSetting : public IfaceDecompCommand { +public: + virtual void execute(istream &s); +}; + class IfcPreferSplit : public IfaceDecompCommand { public: virtual void execute(istream &s); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/op.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/op.hh index 6c87300ea8..3aee0f533b 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/op.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/op.hh @@ -105,7 +105,8 @@ public: modified = 4, ///< This op has been modified by the current action 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 + is_cpool_transformed = 0x20, ///< Have we checked for cpool transforms + stop_propagation = 40 ///< Stop propagation into output from descendants }; private: TypeOp *opcode; ///< Pointer to class providing behavioral details of the operation @@ -202,6 +203,9 @@ public: /// \brief Return \b true if we have already examined this cpool bool isCpoolTransformed(void) const { return ((addlflags&PcodeOp::is_cpool_transformed)!=0); } bool isCollapsible(void) const; ///< Return \b true if this can be collapsed to a COPY of a constant + bool stopsPropagation(void) const { return ((addlflags&stop_propagation)!=0); } ///< Is propagation from below stopped + void setStopPropagation(void) { addlflags |= stop_propagation; } ///< Stop propagation from below + void clearStopPropagation(void) { addlflags &= ~stop_propagation; } ///< Allow propagation from below /// \brief Return \b true if this LOADs or STOREs from a dynamic \e spacebase pointer bool usesSpacebasePtr(void) const { return ((flags&PcodeOp::spacebase_ptr)!=0); } uintm getCseHash(void) const; ///< Return hash indicating possibility of common subexpression elimination diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/printc.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/printc.cc index f6eb149dc9..2b9851dd64 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/printc.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/printc.cc @@ -74,6 +74,8 @@ OpToken PrintC::ptr_expr = { "*", 1, 62, false, OpToken::unary_prefix, 0, 0, (Op OpToken PrintC::array_expr = { "[]", 2, 66, false, OpToken::postsurround, 1, 0, (OpToken *)0 }; OpToken PrintC::enum_cat = { "|", 2, 26, true, OpToken::binary, 0, 0, (OpToken *)0 }; +const string PrintC::typePointerRelToken = "ADJ"; + // Constructing this registers the capability PrintCCapability PrintCCapability::printCCapability; @@ -780,6 +782,7 @@ void PrintC::opPtrsub(const PcodeOp *op) { TypePointer *ptype; + TypePointerRel *ptrel; Datatype *ct; const Varnode *in0; bool valueon,flex,arrayvalue; @@ -791,13 +794,24 @@ void PrintC::opPtrsub(const PcodeOp *op) clear(); throw LowlevelError("PTRSUB off of non-pointer type"); } - ct = ptype->getPtrTo(); + if (ptype->isFormalPointerRel()) { + ptrel = (TypePointerRel *)ptype; + ct = ptrel->getParent(); + } + else { + ptrel = (TypePointerRel *)0; + ct = ptype->getPtrTo(); + } m = mods & ~(print_load_value|print_store_value); // Current state of mods valueon = (mods & (print_load_value|print_store_value)) != 0; flex = isValueFlexible(in0); if (ct->getMetatype() == TYPE_STRUCT) { uintb suboff = op->getIn(1)->getOffset(); // How far into container + if (ptrel != (TypePointerRel *)0) { + suboff += ptrel->getPointerOffset(); + suboff &= calc_mask(ptype->getSize()); + } suboff = AddrSpace::addressToByte(suboff,ptype->getWordSize()); string fieldname; Datatype *fieldtype; @@ -832,12 +846,16 @@ void PrintC::opPtrsub(const PcodeOp *op) if (flex) { // EMIT &( ).name pushOp(&addressof,op); pushOp(&object_member,op); + if (ptrel != (TypePointerRel *)0) + pushTypePointerRel(op); pushVnImplied(in0,op,m | print_load_value); pushAtom(Atom(fieldname,fieldtoken,EmitXml::no_color,ct,fieldoffset)); } else { // EMIT &( )->name pushOp(&addressof,op); pushOp(&pointer_member,op); + if (ptrel != (TypePointerRel *)0) + pushTypePointerRel(op); pushVnImplied(in0,op,m); pushAtom(Atom(fieldname,fieldtoken,EmitXml::no_color,ct,fieldoffset)); } @@ -847,11 +865,15 @@ void PrintC::opPtrsub(const PcodeOp *op) pushOp(&subscript,op); if (flex) { // EMIT ( ).name pushOp(&object_member,op); + if (ptrel != (TypePointerRel *)0) + pushTypePointerRel(op); pushVnImplied(in0,op,m | print_load_value); pushAtom(Atom(fieldname,fieldtoken,EmitXml::no_color,ct,fieldoffset)); } else { // EMIT ( )->name pushOp(&pointer_member,op); + if (ptrel != (TypePointerRel *)0) + pushTypePointerRel(op); pushVnImplied(in0,op,m); pushAtom(Atom(fieldname,fieldtoken,EmitXml::no_color,ct,fieldoffset)); } @@ -912,22 +934,30 @@ void PrintC::opPtrsub(const PcodeOp *op) if (flex) { // EMIT ( ) // (*&struct->arrayfield)[i] // becomes struct->arrayfield[i] + if (ptrel != (TypePointerRel *)0) + pushTypePointerRel(op); pushVnImplied(in0,op,m); } else { // EMIT *( ) pushOp(&dereference,op); + if (ptrel != (TypePointerRel *)0) + pushTypePointerRel(op); pushVnImplied(in0,op,m); } } else { if (flex) { // EMIT ( )[0] pushOp(&subscript,op); + if (ptrel != (TypePointerRel *)0) + pushTypePointerRel(op); pushVnImplied(in0,op,m); push_integer(0,4,false,(Varnode *)0,op); } else { // EMIT (* )[0] pushOp(&subscript,op); pushOp(&dereference,op); + if (ptrel != (TypePointerRel *)0) + pushTypePointerRel(op); pushVnImplied(in0,op,m); push_integer(0,4,false,(Varnode *)0,op); } @@ -1561,6 +1591,7 @@ void PrintC::pushConstant(uintb val,const Datatype *ct, clear(); throw LowlevelError("Cannot have a constant of type void"); case TYPE_PTR: + case TYPE_PTRSTRUCT: if (option_NULL&&(val==0)) { // A null pointer pushAtom(Atom(nullToken,vartoken,EmitXml::var_color,op,vn)); return; @@ -1582,6 +1613,7 @@ void PrintC::pushConstant(uintb val,const Datatype *ct, case TYPE_CODE: case TYPE_ARRAY: case TYPE_STRUCT: + case TYPE_PARTIALSTRUCT: break; } // Default printing diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/printc.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/printc.hh index decff3114d..8b0efd3a05 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/printc.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/printc.hh @@ -113,6 +113,7 @@ protected: static OpToken ptr_expr; ///< Pointer adornment for a type declaration static OpToken array_expr; ///< Array adornment for a type declaration static OpToken enum_cat; ///< The \e concatenation operator for enumerated values + static const string typePointerRelToken; ///< The token to print indicating PTRSUB relative to a TypePointerRel bool option_NULL; ///< Set to \b true if we should emit NULL keyword bool option_inplace_ops; ///< Set to \b true if we should use '+=' '&=' etc. bool option_convention; ///< Set to \b true if we should print calling convention @@ -196,6 +197,7 @@ protected: virtual void emitFunctionDeclaration(const Funcdata *fd); virtual void emitTypeDefinition(const Datatype *ct); virtual bool checkPrintNegation(const Varnode *vn); + void pushTypePointerRel(const PcodeOp *op); public: PrintC(Architecture *g,const string &nm="c-language"); ///< Constructor void setNULLPrinting(bool val) { option_NULL = val; } ///< Toggle the printing of a 'NULL' token @@ -313,4 +315,16 @@ public: virtual void callback(EmitXml *emit); }; +/// \brief Push a token indicating a PTRSUB (a -> operator) is acting at an offset from the original pointer +/// +/// When a variable has TypePointerRel as its data-type, PTRSUB acts relative to the \e parent +/// data-type. We print a specific token to indicate this relative shift is happening. +/// \param op is is the PTRSUB op +inline void PrintC::pushTypePointerRel(const PcodeOp *op) + +{ + pushOp(&function_call,op); + pushAtom(Atom(typePointerRelToken,optoken,EmitXml::funcname_color,op)); +} + #endif diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc index fe8cd97891..1ab814d28c 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc @@ -5619,6 +5619,10 @@ void AddTreeState::clear(void) { multsum = 0; nonmultsum = 0; + if (pRelType != (const TypePointerRel *)0) { + nonmultsum = ((TypePointerRel *)ct)->getPointerOffset(); + nonmultsum &= ptrmask; + } multiple.clear(); coeff.clear(); nonmult.clear(); @@ -5630,6 +5634,29 @@ void AddTreeState::clear(void) distributeOp = (PcodeOp *)0; } +/// For some forms of pointer (TypePointerRel), the pointer can be interpreted as having two versions +/// of the data-type being pointed to. This method initializes analysis for the second version, assuming +/// analysis of the first version has failed. +/// \return \b true if there is a second version that can still be analyzed +bool AddTreeState::initAlternateForm(void) + +{ + if (pRelType == (const TypePointerRel *)0) + return false; + + pRelType = (const TypePointerRel *)0; + baseType = ct->getPtrTo(); + if (baseType->isVariableLength()) + size = 0; // Open-ended size being pointed to, there will be no "multiples" component + else + size = AddrSpace::byteToAddressInt(baseType->getSize(),ct->getWordSize()); + int4 unitsize = AddrSpace::addressToByteInt(1,ct->getWordSize()); + isDegenerate = (baseType->getSize() <= unitsize && baseType->getSize() > 0); + preventDistribution = false; + clear(); + return true; +} + AddTreeState::AddTreeState(Funcdata &d,PcodeOp *op,int4 slot) : data(d) { @@ -5639,12 +5666,19 @@ AddTreeState::AddTreeState(Funcdata &d,PcodeOp *op,int4 slot) ptrsize = ptr->getSize(); ptrmask = calc_mask(ptrsize); baseType = ct->getPtrTo(); + multsum = 0; // Sums start out as zero + nonmultsum = 0; + pRelType = (const TypePointerRel *)0; + if (ct->isFormalPointerRel()) { + pRelType = (const TypePointerRel *)ct; + baseType = pRelType->getParent(); + nonmultsum = pRelType->getPointerOffset(); + nonmultsum &= ptrmask; + } if (baseType->isVariableLength()) size = 0; // Open-ended size being pointed to, there will be no "multiples" component else size = AddrSpace::byteToAddressInt(baseType->getSize(),ct->getWordSize()); - multsum = 0; // Sums start out as zero - nonmultsum = 0; correct = 0; offset = 0; valid = true; // Valid until proven otherwise @@ -5652,6 +5686,8 @@ AddTreeState::AddTreeState(Funcdata &d,PcodeOp *op,int4 slot) isDistributeUsed = false; isSubtype = false; distributeOp = (PcodeOp *)0; + int4 unitsize = AddrSpace::addressToByteInt(1,ct->getWordSize()); + isDegenerate = (baseType->getSize() <= unitsize && baseType->getSize() > 0); } /// Even if the current base data-type is not an array, the pointer expression may incorporate @@ -5815,11 +5851,13 @@ bool AddTreeState::checkTerm(Varnode *vn,uintb treeCoeff) isDistributeUsed = true; } nonmultsum += val; + nonmultsum &= ptrmask; return true; } if (treeCoeff != 1) isDistributeUsed = true; multsum += val; // Add multiples of size into multsum + multsum &= ptrmask; return false; } if (vn->isWritten()) { @@ -5861,6 +5899,12 @@ bool AddTreeState::spanAddTree(PcodeOp *op,uintb treeCoeff) two_is_non = checkTerm(op->getIn(1),treeCoeff); if (!valid) return false; + if (pRelType != (const TypePointerRel *)0) { + if (multsum != 0 || nonmultsum >= size || !multiple.empty()) { + valid = false; + return false; + } + } if (one_is_non&&two_is_non) return true; if (one_is_non) nonmult.push_back(op->getIn(0)); @@ -5874,8 +5918,6 @@ bool AddTreeState::spanAddTree(PcodeOp *op,uintb treeCoeff) void AddTreeState::calcSubtype(void) { - nonmultsum &= ptrmask; // Make sure we are modulo ptr's space - multsum &= ptrmask; if (size == 0 || nonmultsum < size) offset = nonmultsum; else { @@ -6018,10 +6060,34 @@ Varnode *AddTreeState::buildExtra(void) return resNode; } +/// The base data-type being pointed to is unit sized (or smaller). Everything is a multiple, so an ADD +/// is always converted into a PTRADD. +/// \return \b true if the degenerate transform was applied +bool AddTreeState::buildDegenerate(void) + +{ + if (baseType->getSize() < ct->getWordSize()) + // If the size is really less than scale, there is + // probably some sort of padding going on + return false; // Don't transform at all + if (baseOp->getOut()->getType()->getMetatype() != TYPE_PTR) // Make sure pointer propagates thru INT_ADD + return false; + vector newparams; + int4 slot = baseOp->getSlot(ptr); + newparams.push_back( ptr ); + newparams.push_back( baseOp->getIn(1-slot) ); + newparams.push_back( data.newConstant(ct->getSize(),1)); + data.opSetAllInput(baseOp,newparams); + data.opSetOpcode(baseOp,CPUI_PTRADD); + return true; +} + /// \return \b true if a transform was applied bool AddTreeState::apply(void) { + if (isDegenerate) + return buildDegenerate(); spanAddTree(baseOp,1); if (!valid) return false; // Were there any show stoppers if (distributeOp != (PcodeOp *)0 && !isDistributeUsed) { @@ -6060,13 +6126,18 @@ bool AddTreeState::apply(void) return true; } -/// The original ADD tree has been successfully spit into \e multiple and +/// The original ADD tree has been successfully split into \e multiple and /// \e non-multiple pieces. Rewrite the tree as a pointer expression, putting /// any \e multiple pieces into a PTRADD operation, creating a PTRSUB if a sub /// data-type offset has been calculated, and preserving and remaining terms. void AddTreeState::buildTree(void) { + if (pRelType != (const TypePointerRel *)0) { + int4 ptrOff = ((TypePointerRel *)ct)->getPointerOffset(); + offset -= ptrOff; + offset &= ptrmask; + } Varnode *multNode = buildMultiples(); Varnode *extraNode = buildExtra(); PcodeOp *newop = (PcodeOp *)0; @@ -6082,6 +6153,8 @@ void AddTreeState::buildTree(void) // Create PTRSUB portion of operation if (isSubtype) { newop = data.newOpBefore(baseOp,CPUI_PTRSUB,multNode,data.newConstant(ptrsize,offset)); + if (size != 0) + newop->setStopPropagation(); multNode = newop->getOut(); } @@ -6216,29 +6289,12 @@ int4 RulePtrArith::applyOp(PcodeOp *op,Funcdata &data) if (evaluatePointerExpression(op, slot) != 2) return 0; if (!verifyPreferredPointer(op, slot)) return 0; - const TypePointer *tp = (const TypePointer *) ct; - ct = tp->getPtrTo(); // Type being pointed to - int4 unitsize = AddrSpace::addressToByteInt(1,tp->getWordSize()); - if (ct->getSize() == unitsize) { // Degenerate case - if (op->getOut()->getType()->getMetatype() != TYPE_PTR) // Make sure pointer propagates thru INT_ADD - return 0; - vector newparams; - newparams.push_back( op->getIn(slot) ); - newparams.push_back( op->getIn(1-slot) ); - newparams.push_back( data.newConstant(tp->getSize(),1)); - data.opSetAllInput(op,newparams); - data.opSetOpcode(op,CPUI_PTRADD); - return 1; - } - if ((ct->getSize() < unitsize)&&(ct->getSize()>0)) - // If the size is really less than scale, there is - // probably some sort of padding going on - return 0; - AddTreeState state(data,op,slot); - if (!state.apply()) - return 0; - return 1; + if (state.apply()) return 1; + if (state.initAlternateForm()) { + if (state.apply()) return 1; + } + return 0; } /// \class RuleStructOffset0 @@ -6259,10 +6315,7 @@ void RuleStructOffset0::getOpList(vector &oplist) const int4 RuleStructOffset0::applyOp(PcodeOp *op,Funcdata &data) { - Datatype *ct,*sub_ct; - PcodeOp *newop; int4 movesize; // Number of bytes being moved by load or store - uintb offset; if (!data.isTypeRecoveryOn()) return 0; if (op->code()==CPUI_LOAD) { @@ -6274,25 +6327,37 @@ int4 RuleStructOffset0::applyOp(PcodeOp *op,Funcdata &data) else return 0; - ct = op->getIn(1)->getType(); + Datatype *ct = op->getIn(1)->getType(); if (ct->getMetatype() != TYPE_PTR) return 0; - ct = ((TypePointer *)ct)->getPtrTo(); - if (ct->getMetatype() == TYPE_STRUCT) { - if (ct->getSize() < movesize) + Datatype *baseType = ((TypePointer *)ct)->getPtrTo(); + uintb offset = 0; + if (ct->isFormalPointerRel()) { + TypePointerRel *ptRel = (TypePointerRel *)ct; + baseType = ptRel->getParent(); + if (baseType->getMetatype() != TYPE_STRUCT) + return 0; + int4 iOff = ptRel->getPointerOffset(); + iOff = AddrSpace::addressToByteInt(iOff, ptRel->getWordSize()); + if (iOff >= baseType->getSize()) + return 0; + offset = iOff; + } + if (baseType->getMetatype() == TYPE_STRUCT) { + if (baseType->getSize() < movesize) return 0; // Moving something bigger than entire structure - sub_ct = ct->getSubType(0,&offset); // Get field at offset 0 - if (sub_ct==(Datatype *)0) return 0; - if (sub_ct->getSize() < movesize) return 0; // Subtype is too small to handle LOAD/STORE -// if (ct->getSize() == movesize) { + Datatype *subType = baseType->getSubType(offset,&offset); // Get field at pointer's offset + if (subType==(Datatype *)0) return 0; + if (subType->getSize() < movesize) return 0; // Subtype is too small to handle LOAD/STORE +// if (baseType->getSize() == movesize) { // If we reach here, move is same size as the structure, which is the same size as // the first element. // } } - else if (ct->getMetatype() == TYPE_ARRAY) { - if (ct->getSize() < movesize) + else if (baseType->getMetatype() == TYPE_ARRAY) { + if (baseType->getSize() < movesize) return 0; // Moving something bigger than entire array - if (ct->getSize() == movesize) { // Moving something the size of entire array - if (((TypeArray *)ct)->numElements() != 1) + if (baseType->getSize() == movesize) { // Moving something the size of entire array + if (((TypeArray *)baseType)->numElements() != 1) return 0; // If we reach here, moving something size of single element. Assume this is normal access. } @@ -6300,7 +6365,8 @@ int4 RuleStructOffset0::applyOp(PcodeOp *op,Funcdata &data) else return 0; - newop = data.newOpBefore(op,CPUI_PTRSUB,op->getIn(1),data.newConstant(op->getIn(1)->getSize(),0)); + PcodeOp *newop = data.newOpBefore(op,CPUI_PTRSUB,op->getIn(1),data.newConstant(op->getIn(1)->getSize(),0)); + newop->setStopPropagation(); data.opSetInput(op,newop->getOut(),1); return 1; } @@ -6499,6 +6565,7 @@ int4 RulePtrsubUndo::applyOp(PcodeOp *op,Funcdata &data) return 0; data.opSetOpcode(op,CPUI_INT_ADD); + op->clearStopPropagation(); return 1; } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.hh index e6f0fb9da2..d62e62df07 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.hh @@ -45,6 +45,7 @@ class AddTreeState { Varnode *ptr; ///< The pointer varnode const TypePointer *ct; ///< The pointer data-type const Datatype *baseType; ///< The base data-type being pointed at + const TypePointerRel *pRelType; ///< A copy of \b ct, if it is a relative pointer int4 ptrsize; ///< Size of the pointer int4 size; ///< Size of data-type being pointed to (in address units) or 0 for open ended pointer uintb ptrmask; ///< Mask for modulo calculations in ptr space @@ -60,6 +61,7 @@ class AddTreeState { bool isDistributeUsed; ///< Are terms produced by distributing used bool isSubtype; ///< Is there a sub-type (using CPUI_PTRSUB) bool valid; ///< Set to \b true if the whole expression can be transformed + bool isDegenerate; ///< Set to \b true if pointer to unitsize or smaller uint4 findArrayHint(void) const; ///< Look for evidence of an array in a sub-component bool hasMatchingSubType(uintb off,uint4 arrayHint,uintb *newoff) const; bool checkMultTerm(Varnode *vn,PcodeOp *op, uintb treeCoeff); ///< Accumulate details of INT_MULT term and continue traversal if appropriate @@ -68,11 +70,13 @@ class AddTreeState { void calcSubtype(void); ///< Calculate final sub-type offset Varnode *buildMultiples(void); ///< Build part of tree that is multiple of base size Varnode *buildExtra(void); ///< Build part of tree not accounted for by multiples or \e offset + bool buildDegenerate(void); ///< Transform ADD into degenerate PTRADD void buildTree(void); ///< Build the transformed ADD tree void clear(void); ///< Reset for a new ADD tree traversal public: AddTreeState(Funcdata &d,PcodeOp *op,int4 slot); ///< Construct given root of ADD tree and pointer bool apply(void); ///< Attempt to transform the pointer expression + bool initAlternateForm(void); ///< Prepare analysis if there is an alternate form of the base pointer }; class RuleEarlyRemoval : public Rule { diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc index 126c473963..598528a373 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc @@ -16,8 +16,8 @@ #include "type.hh" #include "funcdata.hh" -sub_metatype Datatype::base2sub[11] = { - SUB_STRUCT, SUB_ARRAY, SUB_PTR, SUB_FLOAT, SUB_CODE, SUB_BOOL, +sub_metatype Datatype::base2sub[13] = { + SUB_STRUCT, SUB_PARTIALSTRUCT, SUB_ARRAY, SUB_PTRREL, SUB_PTR, SUB_FLOAT, SUB_CODE, SUB_BOOL, SUB_UINT_PLAIN, SUB_INT_PLAIN, SUB_UNKNOWN, SUB_SPACEBASE, SUB_VOID }; @@ -176,9 +176,15 @@ void metatype2string(type_metatype metatype,string &res) case TYPE_PTR: res = "ptr"; break; + case TYPE_PTRSTRUCT: + res = "ptrstruct"; + break; case TYPE_ARRAY: res = "array"; break; + case TYPE_PARTIALSTRUCT: + res = "part"; + break; case TYPE_STRUCT: res = "struct"; break; @@ -218,6 +224,10 @@ type_metatype string2metatype(const string &metastring) case 'p': if (metastring=="ptr") return TYPE_PTR; + else if (metastring=="part") + return TYPE_PARTIALSTRUCT; + else if (metastring=="ptrstruct") + return TYPE_PTRSTRUCT; break; case 'a': if (metastring=="array") @@ -269,14 +279,15 @@ void Datatype::saveXml(ostream &s) const { s << ""; } /// Write out basic data-type properties (name,size,id) as XML attributes. /// This routine presumes the initial tag is already written to the stream. +/// \param meta is the metatype to put in the XML /// \param s is the stream to write to -void Datatype::saveXmlBasic(ostream &s) const +void Datatype::saveXmlBasic(type_metatype meta,ostream &s) const { a_v(s,"name",name); @@ -290,7 +301,7 @@ void Datatype::saveXmlBasic(ostream &s) const } a_v_i(s,"size",size); string metastring; - metatype2string(metatype,metastring); + metatype2string(meta,metastring); a_v(s,"metatype",metastring); if ((flags & coretype)!=0) a_v_b(s,"core",true); @@ -339,31 +350,22 @@ void Datatype::saveXmlTypedef(ostream &s) const /// A CPUI_PTRSUB must act on a pointer data-type where the given offset addresses a component. /// Perform this check. -/// \param offset is the given offset +/// \param off is the given offset /// \return \b true if \b this is a suitable PTRSUB data-type -bool Datatype::isPtrsubMatching(uintb offset) const +bool Datatype::isPtrsubMatching(uintb off) const { - if (metatype != TYPE_PTR) - return false; + return false; +} - Datatype *basetype = ((TypePointer *)this)->getPtrTo(); - uint4 wordsize = ((TypePointer *)this)->getWordSize(); - if (basetype->metatype==TYPE_SPACEBASE) { - uintb newoff = AddrSpace::addressToByte(offset,wordsize); - basetype->getSubType(newoff,&newoff); - if (newoff != 0) - return false; - } - else { - int4 sz = offset; - int4 typesize = basetype->getSize(); - if ((basetype->metatype != TYPE_ARRAY)&&(basetype->metatype != TYPE_STRUCT)) - return false; // Not a pointer to a structured type - else if ((typesize <= AddrSpace::addressToByteInt(sz,wordsize))&&(typesize!=0)) - return false; - } - return true; +/// Some data-types are ephemeral, and, in the final decompiler output, get replaced with a formal version +/// that is a stripped down version of the original. This method returns this stripped down version, if it +/// exists, or null otherwise. A non-null return should correspond with hasStripped returning \b true. +/// \return the stripped version or null +Datatype *Datatype::getStripped(void) const + +{ + return (Datatype *)0; } /// Restore the basic properties (name,size,id) of a data-type from an XML element @@ -469,7 +471,7 @@ void TypeChar::saveXml(ostream &s) const return; } s << ""; } @@ -511,7 +513,7 @@ void TypeUnicode::saveXml(ostream &s) const return; } s << ""; } @@ -569,7 +571,7 @@ void TypePointer::saveXml(ostream &s) const return; } s << "'; @@ -588,21 +590,37 @@ void TypePointer::restoreXml(const Element *el,TypeFactory &typegrp) s >> wordsize; } ptrto = typegrp.restoreXmlType( *el->getChildren().begin() ); + calcSubmeta(); if (name.size() == 0) // Inherit only coretype only if no name flags = ptrto->getInheritable(); } +/// Pointers to structures may require a specific \b submeta +void TypePointer::calcSubmeta(void) + +{ + if (ptrto->getMetatype() == TYPE_STRUCT) { + if (ptrto->numDepend() > 1 || ptrto->isIncompleteStruct()) + submeta = SUB_PTR_STRUCT; + } +} + /// \brief Find a sub-type pointer given an offset into \b this /// /// Add a constant offset to \b this pointer. /// If there is a valid component at that offset, return a pointer /// to the data-type of the component or NULL otherwise. /// This routine only goes down one level at most. Pass back the -/// renormalized offset relative to the new data-type +/// renormalized offset relative to the new data-type. If \b this is +/// a pointer to (into) a container, the data-type of the container is passed back, +/// with the offset into the container. /// \param off is a reference to the offset to add +/// \param par is used to pass back the container +/// \param parOff is used to pass back the offset into the container /// \param allowArrayWrap is \b true if the pointer should be treated as a pointer to an array +/// \param typegrp is the factory producing the (possibly new) data-type /// \return a pointer datatype for the component or NULL -TypePointer *TypePointer::downChain(uintb &off,bool allowArrayWrap,TypeFactory &typegrp) +TypePointer *TypePointer::downChain(uintb &off,TypePointer *&par,uintb &parOff,bool allowArrayWrap,TypeFactory &typegrp) { int4 ptrtoSize = ptrto->getSize(); @@ -621,16 +639,41 @@ TypePointer *TypePointer::downChain(uintb &off,bool allowArrayWrap,TypeFactory & } } - // If we know we have exactly one of an array, strip the array to get pointer to element - bool doStrip = (ptrto->getMetatype() != TYPE_ARRAY); + type_metatype meta = ptrto->getMetatype(); + bool isArray = (meta == TYPE_ARRAY); + if (isArray || meta == TYPE_STRUCT) { + par = this; + parOff = off; + } + Datatype *pt = ptrto->getSubType(off,&off); if (pt == (Datatype *)0) return (TypePointer *)0; - if (doStrip) + if (!isArray) return typegrp.getTypePointerStripArray(size, pt, wordsize); return typegrp.getTypePointer(size,pt,wordsize); } +bool TypePointer::isPtrsubMatching(uintb off) const + +{ + if (ptrto->getMetatype()==TYPE_SPACEBASE) { + uintb newoff = AddrSpace::addressToByte(off,wordsize); + ptrto->getSubType(newoff,&newoff); + if (newoff != 0) + return false; + } + else { + int4 sz = off; + int4 typesize = ptrto->getSize(); + if ((ptrto->getMetatype() != TYPE_ARRAY)&&(ptrto->getMetatype() != TYPE_STRUCT)) + return false; // Not a pointer to a structured type + else if ((typesize <= AddrSpace::addressToByteInt(sz,wordsize))&&(typesize!=0)) + return false; + } + return true; +} + void TypeArray::printRaw(ostream &s) const { @@ -696,7 +739,7 @@ void TypeArray::saveXml(ostream &s) const return; } s << "'; arrayof->saveXmlRef(s); @@ -870,7 +913,7 @@ void TypeEnum::saveXml(ostream &s) const return; } s << "\n"; map::const_iterator iter; @@ -1142,7 +1185,7 @@ void TypeStruct::saveXml(ostream &s) const return; } s << "\n"; vector::const_iterator iter; for(iter=field.begin();iter!=field.end();++iter) { @@ -1181,6 +1224,125 @@ void TypeStruct::restoreXml(const Element *el,TypeFactory &typegrp) } if (maxoffset > size) throw LowlevelError("Size too small for fields of structure "+name); + if (size == 0) // We can restore an incomplete structure, indicated by 0 size + flags |= struct_incomplete; + else + flags &= ~(uint4)struct_incomplete; // Otherwise the structure is complete +} + +void TypePointerRel::restoreXml(const Element *el,TypeFactory &typegrp) + +{ + flags = is_ptrrel; + restoreXmlBasic(el); + for(int4 i=0;igetNumAttributes();++i) + if (el->getAttributeName(i) == "wordsize") { + istringstream s(el->getAttributeValue(i)); + s.unsetf(ios::dec | ios::hex | ios::oct); + s >> wordsize; + } + const List &list(el->getChildren()); + List::const_iterator iter; + iter = list.begin(); + parent = typegrp.restoreXmlType( *iter ); + ++iter; + istringstream s1((*iter)->getContent()); + s1.unsetf(ios::dec | ios::hex | ios::oct); + s1 >> offset; + if (offset == 0) + throw new LowlevelError("For metatype=\"ptrstruct\", tag must not be zero"); + ptrto = getPtrTo(parent, offset, typegrp); + submeta = (ptrto->getMetatype()==TYPE_UNKNOWN) ? SUB_PTRREL_UNK: SUB_PTRREL; + if (name.size() == 0) // If the data-type is not named + cacheStrippedType(typegrp); // it is considered ephemeral +} + +void TypePointerRel::printRaw(ostream &s) const + +{ + ptrto->printRaw(s); + s << " *+"; + s << dec << offset; + s << '[' ; + parent->printRaw(s); + s << ']'; +} + +int4 TypePointerRel::compareDependency(const Datatype &op) const + +{ + int4 res = Datatype::compareDependency(op); // Note: we go to Datatype, not TypePointer + if (res != 0) return res; + // Both must be pointers + const TypePointerRel *tp = (const TypePointerRel*)&op; + if (offset != tp->offset) return (offset < tp->offset) ? -1 : 1; + if (parent != tp->parent) return (parent < tp->parent) ? -1 : 1; + + if (wordsize != tp->wordsize) return (wordsize < tp->wordsize) ? -1 : 1; + if (ptrto == tp->ptrto) return 0; + return (ptrto < tp->ptrto) ? -1 : 1; // Compare the absolute pointers +} + +void TypePointerRel::saveXml(ostream &s) const + +{ + s << "\n"; + parent->saveXmlRef(s); + s << "" << dec << offset << "\n"; + s << ""; +} + +TypePointer *TypePointerRel::downChain(uintb &off,TypePointer *&par,uintb &parOff,bool allowArrayWrap, + TypeFactory &typegrp) +{ + if (off < ptrto->getSize() && stripped != (TypePointer *)0) { + return TypePointer::downChain(off,par,parOff,allowArrayWrap,typegrp); + } + uintb relOff = (off + offset) & calc_mask(size); // Convert off to be relative to the parent container + if (relOff >= parent->getSize()) + return (TypePointer *)0; // Don't let pointer shift beyond original container + + TypePointer *origPointer = typegrp.getTypePointer(size, parent, wordsize); + off = relOff; + return origPointer->downChain(off,par,parOff,allowArrayWrap,typegrp); +} + +bool TypePointerRel::isPtrsubMatching(uintb off) const + +{ + if (stripped != (TypePointer *)0) + return TypePointer::isPtrsubMatching(off); + int4 iOff = AddrSpace::addressToByteInt((int4)off,wordsize); + iOff += offset; + return (iOff >= 0 && iOff <= parent->getSize()); +} + +/// \brief Given a containing data-type and offset, find the "pointed to" data-type suitable for a TypePointerRel +/// +/// The biggest contained data-type that starts at the exact offset is returned. If the offset is negative +/// or the is no data-type starting exactly there, an \b xunknown1 data-type is returned. +/// \param base is the given container data-type +/// \param off is the offset relative to the start of the container +/// \param typegrp is the factory owning the data-types +/// \return the "pointed to" data-type +Datatype *TypePointerRel::getPtrTo(Datatype *base,int4 off,TypeFactory &typegrp) + +{ + if (off > 0) { + uintb curoff = off; + do { + base = base->getSubType(curoff,&curoff); + } while(curoff != 0 && base != (Datatype *)0); + if (base == (Datatype *)0) + base = typegrp.getBase(1, TYPE_UNKNOWN); + } + else + base = typegrp.getBase(1, TYPE_UNKNOWN); + return base; } /// Turn on the data-type's function prototype @@ -1374,7 +1536,7 @@ void TypeCode::saveXml(ostream &s) const return; } s << "\n"; if (proto != (FuncProto *)0) proto->saveXml(s); @@ -1552,7 +1714,7 @@ void TypeSpacebase::saveXml(ostream &s) const return; } s << "getName()); s << '>'; localframe.saveXml(s); @@ -1865,7 +2027,8 @@ Datatype *TypeFactory::setName(Datatype *ct,const string &n) } /// Make sure all the offsets are fully established then set fields of the structure -/// If -fixedsize- is greater than 0, force the final structure to have that size +/// If \b fixedsize is greater than 0, force the final structure to have that size. +/// This method should only be used on an incomplete structure. It will mark the structure as complete. /// \param fd is the list of fields to set /// \param ot is the TypeStruct object to modify /// \param fixedsize is 0 or the forced size of the structure @@ -1876,6 +2039,8 @@ bool TypeFactory::setFields(vector &fd,TypeStruct *ot,int4 fixedsize, { int4 offset,cursize,curalign; + if (!ot->isIncompleteStruct()) + throw LowlevelError("Can only set fields on an incomplete structure"); offset = 0; vector::iterator iter; @@ -1916,7 +2081,8 @@ bool TypeFactory::setFields(vector &fd,TypeStruct *ot,int4 fixedsize, tree.erase(ot); ot->setFields(fd); - ot->flags |= (flags & (Datatype::opaque_string | Datatype::variable_length)); + ot->flags &= ~(uint4)Datatype::struct_incomplete; + ot->flags |= (flags & (Datatype::opaque_string | Datatype::variable_length | Datatype::struct_incomplete)); if (fixedsize > 0) { // If the caller is trying to force a size if (fixedsize > ot->size) // If the forced size is bigger than the size required for fields ot->size = fixedsize; // Force the bigger size @@ -2172,6 +2338,8 @@ Datatype *TypeFactory::getTypedef(Datatype *ct,const string &name,uint8 id) TypePointer *TypeFactory::getTypePointerStripArray(int4 s,Datatype *pt,uint4 ws) { + if (pt->hasStripped()) + pt = pt->getStripped(); if (pt->getMetatype() == TYPE_ARRAY) pt = ((TypeArray *)pt)->getBase(); // Strip the first ARRAY type TypePointer tmp(s,pt,ws); @@ -2186,6 +2354,8 @@ TypePointer *TypeFactory::getTypePointerStripArray(int4 s,Datatype *pt,uint4 ws) TypePointer *TypeFactory::getTypePointer(int4 s,Datatype *pt,uint4 ws) { + if (pt->hasStripped()) + pt = pt->getStripped(); TypePointer tmp(s,pt,ws); return (TypePointer *) findAdd(tmp); } @@ -2200,6 +2370,8 @@ TypePointer *TypeFactory::getTypePointer(int4 s,Datatype *pt,uint4 ws) TypePointer *TypeFactory::getTypePointer(int4 s,Datatype *pt,uint4 ws,const string &n) { + if (pt->hasStripped()) + pt = pt->getStripped(); TypePointer tmp(s,pt,ws); tmp.name = n; tmp.id = Datatype::hashName(n); @@ -2219,7 +2391,7 @@ TypePointer *TypeFactory::getTypePointerNoDepth(int4 s,Datatype *pt,uint4 ws) type_metatype meta = basetype->getMetatype(); // Make sure that at least we return a pointer to something the size of -pt- if (meta == TYPE_PTR) - return (TypePointer *)pt; + pt = getBase(pt->getSize(),TYPE_UNKNOWN); // Pass back unknown * else if (meta == TYPE_UNKNOWN) { if (basetype->getSize() == pt->getSize()) // If -pt- is pointer to UNKNOWN of the size of a pointer return (TypePointer *)pt; // Just return pt, don't add another pointer @@ -2239,7 +2411,7 @@ TypeArray *TypeFactory::getTypeArray(int4 as,Datatype *ao) return (TypeArray *) findAdd(tmp); } -/// The created structure will have no fields. They must be added later. +/// The created structure will be incomplete and have no fields. They must be added later. /// \param n is the name of the structure /// \return the TypeStruct object TypeStruct *TypeFactory::getTypeStruct(const string &n) @@ -2290,6 +2462,35 @@ TypeCode *TypeFactory::getTypeCode(ProtoModel *model,Datatype *outtype, return (TypeCode *) findAdd(tc); } +/// Find/create a pointer data-type that points at a known offset relative to a containing data-type. +/// The resulting data-type is unnamed and ephemeral. +/// \param parentPtr is a model pointer data-type, pointing to the containing data-type +/// \param ptrTo is the data-type being pointed directly to +/// \param off is the offset of the pointed-to data-type relative to the \e container +/// \return the new/matching pointer +TypePointerRel *TypeFactory::getTypePointerRel(TypePointer *parentPtr,Datatype *ptrTo,int4 off) + +{ + TypePointerRel tp(parentPtr->size,ptrTo,parentPtr->wordsize,parentPtr->ptrto,off); + tp.cacheStrippedType(*this); // Mark as ephemeral + TypePointerRel *res = (TypePointerRel *) findAdd(tp); + return res; +} + +/// \brief Build a named pointer offset into a larger container +/// +/// The resulting data-type is named and not ephemeral and will display as a formal data-type +/// in decompiler output. +TypePointerRel *TypeFactory::getTypePointerRel(int4 sz,Datatype *parent,Datatype *ptrTo,int4 ws,int4 off,const string &nm) + +{ + TypePointerRel tp(sz,ptrTo,ws,parent,off); + tp.name = nm; + tp.id = Datatype::hashName(nm); + TypePointerRel *res = (TypePointerRel *)findAdd(tp); + return res; +} + /// The indicated Datatype object is removed from this container. /// Indirect references (via TypeArray TypeStruct etc.) are not affected /// \param ct is the data-type to destroy @@ -2542,19 +2743,17 @@ Datatype *TypeFactory::restoreXmlTypeNoRef(const Element *el,bool forcecore) if (isVarLength) newid = Datatype::hashSize(newid, structsize); ct = findByIdLocal(structname,newid); - bool stubfirst = false; if (ct == (Datatype *)0) { ts.id = newid; ts.size = structsize; // Include size if we have it, so arrays can be defined without knowing struct fields ct = findAdd(ts); // Create stub to allow recursive definitions - stubfirst = true; } else if (ct->getMetatype() != TYPE_STRUCT) throw LowlevelError("Trying to redefine type: "+structname); ts.restoreXml(el,*this); if (forcecore) ts.flags |= Datatype::coretype; - if ((ct->getSize() != 0)&&(!stubfirst)) { // Structure of this name was already present + if (!ct->isIncompleteStruct()) { // Structure of this name was already present if (0!=ct->compareDependency(ts)) throw LowlevelError("Redefinition of structure: "+structname); } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/type.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/type.hh index b6aee62aca..4b08806028 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/type.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/type.hh @@ -31,38 +31,43 @@ extern void print_data(ostream &s,uint1 *buffer,int4 size,const Address &baseadd /// the number, the more \b specific the type, in calculations involving the generality /// of a type. enum type_metatype { - TYPE_VOID = 10, ///< Standard "void" type, absence of type - TYPE_SPACEBASE = 9, ///< Placeholder for symbol/type look-up calculations - TYPE_UNKNOWN = 8, ///< An unknown low-level type. Treated as an unsigned integer. - TYPE_INT = 7, ///< Signed integer. Signed is considered less specific than unsigned in C - TYPE_UINT = 6, ///< Unsigned integer - TYPE_BOOL = 5, ///< Boolean - TYPE_CODE = 4, ///< Data is actual executable code - TYPE_FLOAT = 3, ///< Floating-point + TYPE_VOID = 12, ///< Standard "void" type, absence of type + TYPE_SPACEBASE = 11, ///< Placeholder for symbol/type look-up calculations + TYPE_UNKNOWN = 10, ///< An unknown low-level type. Treated as an unsigned integer. + TYPE_INT = 9, ///< Signed integer. Signed is considered less specific than unsigned in C + TYPE_UINT = 8, ///< Unsigned integer + TYPE_BOOL = 7, ///< Boolean + TYPE_CODE = 6, ///< Data is actual executable code + TYPE_FLOAT = 5, ///< Floating-point - TYPE_PTR = 2, ///< Pointer data-type - TYPE_ARRAY = 1, ///< Array data-type, made up of a sequence of "element" datatype + TYPE_PTR = 4, ///< Pointer data-type + TYPE_PTRSTRUCT = 3, ///< Pointer into a structure data-type (specialization of TYPE_PTR) + TYPE_ARRAY = 2, ///< Array data-type, made up of a sequence of "element" datatype + TYPE_PARTIALSTRUCT = 1, ///< Part of a structure, stored separately from the whole TYPE_STRUCT = 0 ///< Structure data-type, made up of component datatypes }; enum sub_metatype { - SUB_VOID = 16, ///< Compare as a TYPE_VOID - SUB_SPACEBASE = 15, ///< Compare as a TYPE_SPACEBASE - SUB_UNKNOWN = 14, ///< Compare as a TYPE_UNKNOWN - SUB_INT_CHAR = 13, ///< Signed 1-byte character, sub-type of TYPE_INT - SUB_UINT_CHAR = 12, ///< Unsigned 1-byte character, sub-type of TYPE_UINT - SUB_INT_PLAIN = 11, ///< Compare as a plain TYPE_INT - SUB_UINT_PLAIN = 10, ///< Compare as a plain TYPE_UINT - SUB_INT_ENUM = 9, ///< Signed enum, sub-type of TYPE_INT - SUB_UINT_ENUM = 8, ///< Unsigned enum, sub-type of TYPE_UINT - SUB_INT_UNICODE = 7, ///< Signed wide character, sub-type of TYPE_INT - SUB_UINT_UNICODE = 6, ///< Unsigned wide character, sub-type of TYPE_UINT - SUB_BOOL = 5, ///< Compare as TYPE_BOOL - SUB_CODE = 4, ///< Compare as TYPE_CODE - SUB_FLOAT = 3, ///< Compare as TYPE_FLOAT - - SUB_PTR = 2, ///< Compare as TYPE_PTR - SUB_ARRAY = 1, ///< Compare as TYPE_ARRAY + SUB_VOID = 20, ///< Compare as a TYPE_VOID + SUB_SPACEBASE = 19, ///< Compare as a TYPE_SPACEBASE + SUB_UNKNOWN = 18, ///< Compare as a TYPE_UNKNOWN + SUB_INT_CHAR = 17, ///< Signed 1-byte character, sub-type of TYPE_INT + SUB_UINT_CHAR = 16, ///< Unsigned 1-byte character, sub-type of TYPE_UINT + SUB_INT_PLAIN = 15, ///< Compare as a plain TYPE_INT + SUB_UINT_PLAIN = 14, ///< Compare as a plain TYPE_UINT + SUB_INT_ENUM = 13, ///< Signed enum, sub-type of TYPE_INT + SUB_UINT_ENUM = 12, ///< Unsigned enum, sub-type of TYPE_UINT + SUB_INT_UNICODE = 11, ///< Signed wide character, sub-type of TYPE_INT + SUB_UINT_UNICODE = 10, ///< Unsigned wide character, sub-type of TYPE_UINT + SUB_BOOL = 9, ///< Compare as TYPE_BOOL + SUB_CODE = 8, ///< Compare as TYPE_CODE + SUB_FLOAT = 7, ///< Compare as TYPE_FLOAT + SUB_PTRREL_UNK = 6, ///< Pointer to unknown field of struct, sub-type of TYPE_PTR + SUB_PTR = 5, ///< Compare as TYPE_PTR + SUB_PTRREL = 4, ///< Pointer relative to another data-type, sub-type of TYPE_PTR + SUB_PTR_STRUCT = 3, ///< Pointer into struct, sub-type of TYPE_PTR + SUB_ARRAY = 2, ///< Compare as TYPE_ARRAY + SUB_PARTIALSTRUCT = 1, ///< Compare as TYPE_PARTIALSTRUCT SUB_STRUCT = 0 ///< Compare as TYPE_STRUCT }; /// Convert type \b meta-type to name @@ -81,7 +86,7 @@ struct DatatypeCompare; /// Used for symbols, function prototypes, type propagation etc. class Datatype { protected: - static sub_metatype base2sub[11]; + static sub_metatype base2sub[13]; /// Boolean properties of datatypes enum { coretype = 1, ///< This is a basic type which will never be redefined @@ -96,7 +101,10 @@ protected: utf16 = 16, ///< 16-bit wide chars in unicode UTF16 utf32 = 32, ///< 32-bit wide chars in unicode UTF32 opaque_string = 64, ///< Structure that should be treated as a string - variable_length = 128 ///< May be other structures with same name different lengths + variable_length = 128, ///< May be other structures with same name different lengths + has_stripped = 0x100, ///< Datatype has a stripped form for formal declarations + is_ptrrel = 0x200, ///< Datatype is a TypePointerRel + struct_incomplete = 0x400, ///< Set if \b this (recursive) structure has not been fully defined yet }; friend class TypeFactory; friend struct DatatypeCompare; @@ -108,7 +116,7 @@ protected: uint8 id; ///< A unique id for the type (or 0 if an id is not assigned) Datatype *typedefImm; ///< The immediate data-type being typedefed by \e this void restoreXmlBasic(const Element *el); ///< Recover basic data-type properties - void saveXmlBasic(ostream &s) const; ///< Save basic data-type properties + void saveXmlBasic(type_metatype meta,ostream &s) const; ///< Save basic data-type properties void saveXmlTypedef(ostream &s) const; ///< Write \b this as a \e typedef tag to stream virtual void restoreXml(const Element *el,TypeFactory &typegrp); ///< Restore data-type from XML virtual Datatype *clone(void) const=0; ///< Clone the data-type @@ -133,6 +141,10 @@ public: bool isVariableLength(void) const { return ((flags&variable_length)!=0); } ///< Is \b this a variable length structure bool hasSameVariableBase(const Datatype *ct) const; ///< Are these the same variable length data-type bool isOpaqueString(void) const { return ((flags&opaque_string)!=0); } ///< Is \b this an opaquely encoded string + bool isPointerRel(void) const { return ((flags & is_ptrrel)!=0); } ///< Is \b this a TypePointerRel + bool isFormalPointerRel(void) const { return (flags & (is_ptrrel | has_stripped))==is_ptrrel; } ///< Is \b this a non-ephemeral TypePointerRel + bool hasStripped(void) const { return (flags & has_stripped)!=0; } ///< Return \b true if \b this has a stripped form + bool isIncompleteStruct(void) const { return (flags & struct_incomplete)!=0; } ///< Is \b this an incompletely defined struct uint4 getInheritable(void) const { return (flags & coretype); } ///< Get properties pointers inherit type_metatype getMetatype(void) const { return metatype; } ///< Get the type \b meta-type uint8 getId(void) const { return id; } ///< Get the type id @@ -149,10 +161,11 @@ public: virtual int4 compare(const Datatype &op,int4 level) const; ///< Order types for propagation virtual int4 compareDependency(const Datatype &op) const; ///< Compare for storage in tree structure virtual void saveXml(ostream &s) const; ///< Serialize the data-type to XML + virtual bool isPtrsubMatching(uintb off) const; ///< Is this data-type suitable as input to a CPUI_PTRSUB op + virtual Datatype *getStripped(void) const; ///< Get a stripped version of \b this for formal use in formal declarations int4 typeOrder(const Datatype &op) const { if (this==&op) return 0; return compare(op,10); } ///< Order this with -op- datatype int4 typeOrderBool(const Datatype &op) const; ///< Order \b this with -op-, treating \e bool data-type as special void saveXmlRef(ostream &s) const; ///< Write an XML reference of \b this to stream - bool isPtrsubMatching(uintb offset) const; ///< Is this data-type suitable as input to a CPUI_PTRSUB op }; /// \brief Specifies subfields of a structure or what a pointer points to @@ -258,13 +271,15 @@ protected: Datatype *ptrto; ///< Type being pointed to uint4 wordsize; ///< What size unit does the pointer address virtual void restoreXml(const Element *el,TypeFactory &typegrp); + void calcSubmeta(void); ///< Calculate specific submeta for \b this pointer /// Internal constructor for use with restoreXml TypePointer(void) : Datatype(0,TYPE_PTR) { ptrto = (Datatype *)0; wordsize=1; } public: /// Construct from another TypePointer TypePointer(const TypePointer &op) : Datatype(op) { ptrto = op.ptrto; wordsize=op.wordsize; } /// Construct from a size, pointed-to type, and wordsize - TypePointer(int4 s,Datatype *pt,uint4 ws) : Datatype(s,TYPE_PTR) { ptrto = pt; flags = ptrto->getInheritable(); wordsize=ws; } + TypePointer(int4 s,Datatype *pt,uint4 ws) : Datatype(s,TYPE_PTR) { + ptrto = pt; flags = ptrto->getInheritable(); wordsize=ws; calcSubmeta(); } Datatype *getPtrTo(void) const { return ptrto; } ///< Get the pointed-to Datatype uint4 getWordSize(void) const { return wordsize; } ///< Get the wordsize of the pointer virtual void printRaw(ostream &s) const; @@ -275,7 +290,8 @@ public: virtual int4 compareDependency(const Datatype &op) const; // For tree structure virtual Datatype *clone(void) const { return new TypePointer(*this); } virtual void saveXml(ostream &s) const; - virtual TypePointer *downChain(uintb &off,bool allowArrayWrap,TypeFactory &typegrp); + virtual TypePointer *downChain(uintb &off,TypePointer *&par,uintb &parOff,bool allowArrayWrap,TypeFactory &typegrp); + virtual bool isPtrsubMatching(uintb off) const; }; /// \brief Datatype object representing an array of elements @@ -347,7 +363,7 @@ protected: virtual void restoreXml(const Element *el,TypeFactory &typegrp); public: TypeStruct(const TypeStruct &op); ///< Construct from another TypeStruct - TypeStruct(const string &n) : Datatype(0,TYPE_STRUCT,n) {} ///< Construct empty TypeStruct from a name + TypeStruct(const string &n) : Datatype(0,TYPE_STRUCT,n) { flags |= struct_incomplete; } ///< Construct incomplete/empty TypeStruct from a name vector::const_iterator beginField(void) const { return field.begin(); } ///< Beginning of fields vector::const_iterator endField(void) const { return field.end(); } ///< End of fields const TypeField *getField(int4 off,int4 sz,int4 *newoff) const; ///< Get field based on offset @@ -362,6 +378,45 @@ public: virtual void saveXml(ostream &s) const; }; +/// \brief A pointer data-type that knows it is offset relative to another data-type +/// +/// The other data, the \b container, is typically a TypeStruct or TypeArray. Even though \b this pointer +/// does not point directly to the start of the container, it is possible to access the container through \b this, +/// as the distance (the \b offset) to the start of the container is explicitly known. +class TypePointerRel : public TypePointer { +protected: + friend class TypeFactory; + TypePointer *stripped; ///< Same data-type with container info stripped + Datatype *parent; ///< Parent structure or array which \b this is pointing into + int4 offset; ///< Byte offset within the parent where \b this points to + void cacheStrippedType(TypeFactory &typegrp); + virtual void restoreXml(const Element *el,TypeFactory &typegrp); + /// Internal constructor for restoreXml + TypePointerRel(void) : TypePointer() { offset = 0; parent = (Datatype *)0; stripped = (TypePointer *)0; submeta = SUB_PTRREL; } +public: + /// Construct from another TypePointerRel + TypePointerRel(const TypePointerRel &op) : TypePointer((const TypePointer &)op) { + offset = op.offset; parent = op.parent; stripped = op.stripped; } + /// Construct given a size, pointed-to type, parent, and offset + TypePointerRel(int4 sz,Datatype *pt,uint4 ws,Datatype *par,int4 off) : TypePointer(sz,pt,ws) { + parent = par; offset = off; stripped = (TypePointer *)0; flags |= is_ptrrel; + submeta = pt->getMetatype()==TYPE_UNKNOWN ? SUB_PTRREL_UNK : SUB_PTRREL; } + Datatype *getParent(void) const { return parent; } ///< Get the parent data-type to which \b this pointer is offset + + /// \brief Get offset of \b this pointer relative to start of the containing data-type + /// + /// \return the offset value in \e address \e units + int4 getPointerOffset(void) const { return AddrSpace::byteToAddressInt(offset, wordsize); } + virtual void printRaw(ostream &s) const; + virtual int4 compareDependency(const Datatype &op) const; + virtual Datatype *clone(void) const { return new TypePointerRel(*this); } + virtual void saveXml(ostream &s) const; + virtual TypePointer *downChain(uintb &off,TypePointer *&par,uintb &parOff,bool allowArrayWrap,TypeFactory &typegrp); + virtual bool isPtrsubMatching(uintb off) const; + virtual Datatype *getStripped(void) const { return stripped; } ///< Get the plain form of the pointer + static Datatype *getPtrTo(Datatype *base,int4 off,TypeFactory &typegrp); +}; + class FuncProto; // Forward declaration class ProtoModel; @@ -484,6 +539,8 @@ public: const vector &intypes, bool dotdotdot); ///< Create a "function" datatype Datatype *getTypedef(Datatype *ct,const string &name,uint8 id); ///< Create a new \e typedef data-type + TypePointerRel *getTypePointerRel(TypePointer *parentPtr,Datatype *ptrTo,int4 off); ///< Get pointer offset relative to a container + TypePointerRel *getTypePointerRel(int4 sz,Datatype *parent,Datatype *ptrTo,int4 ws,int4 off,const string &nm); void destroyType(Datatype *ct); ///< Remove a data-type from \b this Datatype *concretize(Datatype *ct); ///< Convert given data-type to concrete form void dependentOrder(vector &deporder) const; ///< Place all data-types in dependency order @@ -512,4 +569,18 @@ inline int4 Datatype::typeOrderBool(const Datatype &op) const return compare(op,10); } +/// \brief Set up the base pointer data-type \b this is modeling +/// +/// This base data-type is used for formal variable declarations in source code output. +/// Calling this method marks the TypePointerRel as an ephemeral data-type. The TypePointerRel +/// is not considered a formal data-type and is replaced with the base pointer data-type, after propagation, +/// in the final decompiler output. +/// \param typegrp is the factory from which to fetch the base pointer +inline void TypePointerRel::cacheStrippedType(TypeFactory &typegrp) + +{ + stripped = typegrp.getTypePointer(size,ptrto,wordsize); + flags |= has_stripped; +} + #endif diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/typeop.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/typeop.cc index 104b5d2338..8e3eb2ec21 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/typeop.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/typeop.cc @@ -1572,7 +1572,7 @@ Datatype *TypeOpPiece::getOutputToken(const PcodeOp *op,CastStrategy *castStrate { const Varnode *vn = op->getOut(); - Datatype *dt = vn->getType(); + Datatype *dt = vn->getHigh()->getType(); type_metatype meta = dt->getMetatype(); if ((meta == TYPE_INT)||(meta == TYPE_UINT)) // PIECE casts to uint or int, based on output return dt; @@ -1599,7 +1599,7 @@ Datatype *TypeOpSubpiece::getOutputToken(const PcodeOp *op,CastStrategy *castStr { const Varnode *vn = op->getOut(); - Datatype *dt = vn->getType(); // SUBPIECE prints as cast to whatever its output is + Datatype *dt = vn->getHigh()->getType(); // SUBPIECE prints as cast to whatever its output is if (dt->getMetatype() != TYPE_UNKNOWN) return dt; return tlst->getBase(vn->getSize(),TYPE_INT); // If output is unknown, treat as cast to int @@ -1711,7 +1711,9 @@ Datatype *TypeOpPtrsub::getOutputToken(const PcodeOp *op,CastStrategy *castStrat TypePointer *ptype = (TypePointer *)op->getIn(0)->getHigh()->getType(); if (ptype->getMetatype() == TYPE_PTR) { uintb offset = AddrSpace::addressToByte(op->getIn(1)->getOffset(),ptype->getWordSize()); - Datatype *rettype = ptype->downChain(offset,false,*tlst); + uintb unusedOffset; + TypePointer *unusedParent; + Datatype *rettype = ptype->downChain(offset,unusedParent,unusedOffset,false,*tlst); if ((offset==0)&&(rettype != (Datatype *)0)) return rettype; rettype = tlst->getBase(1, TYPE_UNKNOWN); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/variable.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/variable.cc index f49bc2fee5..d4f25b44cc 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/variable.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/variable.cc @@ -153,6 +153,9 @@ void HighVariable::updateType(void) const vn = getTypeRepresentative(); type = vn->getType(); + if (type->hasStripped()) + type = type->getStripped(); + // Update lock flags flags &= ~Varnode::typelock; if (vn->isTypeLock()) diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/varnode.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/varnode.cc index 9192305070..6fa44dfe48 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/varnode.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/varnode.cc @@ -723,7 +723,7 @@ int4 Varnode::isConstantExtended(uintb &val) const /// to determine if the Varnode is getting used as an \b int, \b float, or \b pointer, etc. /// Throw an exception if no Datatype can be found at all. /// \return the determined Datatype -Datatype *Varnode::getLocalType(void) const +Datatype *Varnode::getLocalType(bool &blockup) const { Datatype *ct; @@ -733,8 +733,13 @@ Datatype *Varnode::getLocalType(void) const return type; // Not a partial lock, return the locked type ct = (Datatype *)0; - if (def != (PcodeOp *)0) + if (def != (PcodeOp *)0) { ct = def->outputTypeLocal(); + if (def->stopsPropagation()) { + blockup = true; + return ct; + } + } list::const_iterator iter; PcodeOp *op; diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/varnode.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/varnode.hh index b3afd26660..c4443c76d7 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/varnode.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/varnode.hh @@ -116,8 +116,9 @@ public: unsignedprint = 0x40, ///< Constant that must be explicitly printed as unsigned stack_store = 0x80, ///< Created by an explicit STORE locked_input = 0x100, ///< Input that exists even if its unused - spacebase_placeholder = 0x200 ///< This varnode is inserted artificially to track a register + spacebase_placeholder = 0x200, ///< This varnode is inserted artificially to track a register ///< value at a specific point in the code + stop_uppropagation = 0x400 ///< Data-types do not propagate from an output into \b this }; private: mutable uint4 flags; ///< The collection of boolean attributes for this Varnode @@ -243,6 +244,7 @@ public: bool isActiveHeritage(void) const { return ((addlflags&Varnode::activeheritage)!=0); } ///< Is \b this currently being traced by the Heritage algorithm? bool isStackStore(void) const { return ((addlflags&Varnode::stack_store)!=0); } ///< Was this originally produced by an explicit STORE bool isLockedInput(void) const { return ((addlflags&Varnode::locked_input)!=0); } ///< Is always an input, even if unused + bool stopsUpPropagation(void) const { return ((addlflags&Varnode::stop_uppropagation)!=0); } ///< Is data-type propagation stopped /// Is \b this just a special placeholder representing INDIRECT creation? bool isIndirectZero(void) const { return ((flags&(Varnode::indirect_creation|Varnode::constant))==(Varnode::indirect_creation|Varnode::constant)); } @@ -301,12 +303,14 @@ public: void setAutoLiveHold(void) { flags |= Varnode::autolive_hold; } ///< Place temporary hold on dead code removal void clearAutoLiveHold(void) { flags &= ~Varnode::autolive_hold; } ///< Clear temporary hold on dead code removal void setUnsignedPrint(void) { addlflags |= Varnode::unsignedprint; } ///< Force \b this to be printed as unsigned + void setStopUpPropagation(void) { addlflags |= Varnode::stop_uppropagation; } ///< Stop up-propagation thru \b this + void clearStopUpPropagation(void) { addlflags &= ~Varnode::stop_uppropagation; } ///< Stop up-propagation thru \b this bool updateType(Datatype *ct,bool lock,bool override); ///< (Possibly) set the Datatype given various restrictions void setStackStore(void) { addlflags |= Varnode::stack_store; } ///< Mark as produced by explicit CPUI_STORE void setLockedInput(void) { addlflags |= Varnode::locked_input; } ///< Mark as existing input, even if unused void copySymbol(const Varnode *vn); ///< Copy symbol info from \b vn void copySymbolIfValid(const Varnode *vn); ///< Copy symbol info from \b vn if constant value matches - Datatype *getLocalType(void) const; ///< Calculate type of Varnode based on local information + Datatype *getLocalType(bool &blockup) const; ///< Calculate type of Varnode based on local information bool copyShadow(const Varnode *op2) const; ///< Are \b this and \b op2 copied from the same source? void saveXml(ostream &s) const; ///< Save a description of \b this as an XML tag static bool comparePointers(const Varnode *a,const Varnode *b) { return (*a < *b); } ///< Compare Varnodes as pointers diff --git a/Ghidra/Features/Decompiler/src/decompile/datatests/pointercmp.xml b/Ghidra/Features/Decompiler/src/decompile/datatests/pointercmp.xml new file mode 100644 index 0000000000..9d29b7ab29 --- /dev/null +++ b/Ghidra/Features/Decompiler/src/decompile/datatests/pointercmp.xml @@ -0,0 +1,23 @@ + + + + 554889e54889 +7de8488b45e84883c008488945f8eb0c +488b45f8c60061488345f801488b45e8 +4883c018483945f872e6905dc3 + + + + +for \(pchar = ptr->arr; pchar < \&ptr->fval; pchar = pchar \+ 1\) +char \*pchar; +\*pchar = 'a'; + diff --git a/Ghidra/Features/Decompiler/src/decompile/datatests/pointerrel.xml b/Ghidra/Features/Decompiler/src/decompile/datatests/pointerrel.xml new file mode 100644 index 0000000000..6f46703392 --- /dev/null +++ b/Ghidra/Features/Decompiler/src/decompile/datatests/pointerrel.xml @@ -0,0 +1,50 @@ + + + + + 554889e54889 +7dd88975d4c745ec00000000f30f1005 +a0010000f30f1145f0488b45d84883c0 +08488945f8c745f400000000eb39488b +45f88b000145ec488b45f84883c0048b +000145ec488b45f84883e804f30f1000 +f30f104df0f30f58c1f30f1145f04883 +45f8508345f4018b45f43b45d47cbff3 +0f1045f00f2e053d01000076048345ec +058b45ec5dc3 + + + 000020410000a841 + + + + +ADJ +iSum = iSum \+ ADJ\(ptrrel\)->c \+ ADJ\(ptrrel\)->d; +fSum = ADJ\(ptrrel\)->b \+ fSum; +ptrrel = &ptr->c; +myptroff ptrrel; +ptrrel = ptrrel \+ 0x14; +if \(21.0 < fSum\) +for \(i = 0; i < count; i = i \+ 1\) +