diff --git a/Ghidra/Features/Decompiler/certification.manifest b/Ghidra/Features/Decompiler/certification.manifest index b9bf25c13e..9aab00555b 100644 --- a/Ghidra/Features/Decompiler/certification.manifest +++ b/Ghidra/Features/Decompiler/certification.manifest @@ -53,6 +53,7 @@ src/decompile/datatests/pointercmp.xml||GHIDRA||||END| src/decompile/datatests/pointerrel.xml||GHIDRA||||END| src/decompile/datatests/pointersub.xml||GHIDRA||||END| src/decompile/datatests/promotecompare.xml||GHIDRA||||END| +src/decompile/datatests/ptrtoarray.xml||GHIDRA||||END| src/decompile/datatests/readvolatile.xml||GHIDRA||||END| src/decompile/datatests/retstruct.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 928c4a0739..208ca7fc7d 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc @@ -2325,34 +2325,43 @@ void ActionSetCasts::checkPointerIssues(PcodeOp *op,Varnode *vn,Funcdata &data) } } -/// \brief Test if the given cast conflict can be resolved by passing to the first structure field +/// \brief Test if the given cast conflict can be resolved by passing to the first structure/array field /// -/// Test if the given Varnode data-type is a pointer to a structure and if interpreting +/// Test if the current data-type is a pointer to a structure and if interpreting /// the data-type as a pointer to the structure's first field will get it to match the -/// desired data-type. -/// \param vn is the given Varnode -/// \param op is the PcodeOp reading the Varnode -/// \param ct is the desired data-type +/// required data-type. +/// \param reqtype is the required data-type +/// \param curtype is the current data-type /// \param castStrategy is used to determine if the data-types are compatible /// \return \b true if a pointer to the first field makes sense -bool ActionSetCasts::testStructOffset0(Varnode *vn,PcodeOp *op,Datatype *ct,CastStrategy *castStrategy) +bool ActionSetCasts::testStructOffset0(Datatype *reqtype,Datatype *curtype,CastStrategy *castStrategy) { - if (ct->getMetatype() != TYPE_PTR) return false; - Datatype *highType = vn->getHighTypeReadFacing(op); - if (highType->getMetatype() != TYPE_PTR) return false; - Datatype *highPtrTo = ((TypePointer *)highType)->getPtrTo(); - if (highPtrTo->getMetatype() != TYPE_STRUCT) return false; - TypeStruct *highStruct = (TypeStruct *)highPtrTo; - if (highStruct->numDepend() == 0) return false; - vector::const_iterator iter = highStruct->beginField(); - if ((*iter).offset != 0) return false; - Datatype *reqtype = ((TypePointer *)ct)->getPtrTo(); - Datatype *curtype = (*iter).type; - if (reqtype->getMetatype() == TYPE_ARRAY) - reqtype = ((TypeArray *)reqtype)->getBase(); - if (curtype->getMetatype() == TYPE_ARRAY) - curtype = ((TypeArray *)curtype)->getBase(); + if (curtype->getMetatype() != TYPE_PTR) return false; + Datatype *highPtrTo = ((TypePointer *)curtype)->getPtrTo(); + if (highPtrTo->getMetatype() == TYPE_STRUCT) { + TypeStruct *highStruct = (TypeStruct *)highPtrTo; + if (highStruct->numDepend() == 0) return false; + vector::const_iterator iter = highStruct->beginField(); + if ((*iter).offset != 0) return false; + reqtype = ((TypePointer *)reqtype)->getPtrTo(); + curtype = (*iter).type; + if (reqtype->getMetatype() == TYPE_ARRAY) + reqtype = ((TypeArray *)reqtype)->getBase(); + if (curtype->getMetatype() == TYPE_ARRAY) + curtype = ((TypeArray *)curtype)->getBase(); + } + else if (highPtrTo->getMetatype() == TYPE_ARRAY) { + TypeArray *highArray = (TypeArray *)highPtrTo; + reqtype = ((TypePointer *)reqtype)->getPtrTo(); + curtype = highArray->getBase(); + } + else { + return false; + } + if (reqtype->getMetatype() == TYPE_VOID) { + return false; // Don't induce PTRSUB for "void *" + } return (castStrategy->castStandard(reqtype, curtype, true, true) == (Datatype *)0); } @@ -2519,22 +2528,31 @@ int4 ActionSetCasts::castOutput(PcodeOp *op,Funcdata &data,CastStrategy *castStr } } } + OpCode opc = CPUI_CAST; if (!force) { outct = outHighResolve; // Type of result - ct = castStrategy->castStandard(outct,tokenct,false,true); - if (ct == (Datatype *)0) return 0; + if (outct->getMetatype() == TYPE_PTR && testStructOffset0(outct, tokenct, castStrategy)) { + opc = CPUI_PTRSUB; + } + else { + ct = castStrategy->castStandard(outct,tokenct,false,true); + if (ct == (Datatype *)0) return 0; + } } // Generate the cast op vn = data.newUnique(outvn->getSize()); vn->updateType(tokenct,false,false); vn->setImplied(); - newop = data.newOp(1,op->getAddr()); + newop = data.newOp((opc != CPUI_CAST) ? 2 : 1,op->getAddr()); #ifdef CPUI_STATISTICS data.getArch()->stats->countCast(); #endif - data.opSetOpcode(newop,CPUI_CAST); + data.opSetOpcode(newop,opc); data.opSetOutput(newop,outvn); data.opSetInput(newop,vn,0); + if (opc != CPUI_CAST) { + data.opSetInput(newop,data.newConstant(4, 0),1); + } data.opSetOutput(op,vn); data.opInsertAfter(newop,op); // Cast comes AFTER this operation if (tokenct->needsResolution()) @@ -2612,7 +2630,7 @@ int4 ActionSetCasts::castInput(PcodeOp *op,int4 slot,Funcdata &data,CastStrategy if (vn->getType() == ct) return 1; } - else if (testStructOffset0(vn, op, ct, castStrategy)) { + else if (ct->getMetatype() == TYPE_PTR && testStructOffset0(ct, vn->getHighTypeReadFacing(op), castStrategy)) { // Insert a PTRSUB(vn,#0) instead of a CAST newop = insertPtrsubZero(op, slot, ct, data); if (vn->getHigh()->getType()->needsResolution()) diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.hh index ab1c42ff1f..eefa3a8176 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.hh @@ -319,7 +319,7 @@ public: /// immediately. class ActionSetCasts : public Action { static void checkPointerIssues(PcodeOp *op,Varnode *vn,Funcdata &data); - static bool testStructOffset0(Varnode *vn,PcodeOp *op,Datatype *ct,CastStrategy *castStrategy); + static bool testStructOffset0(Datatype *reqtype,Datatype *curtype,CastStrategy *castStrategy); static bool tryResolutionAdjustment(PcodeOp *op,int4 slot,Funcdata &data); static bool isOpIdentical(Datatype *ct1,Datatype *ct2); static int4 resolveUnion(PcodeOp *op,int4 slot,Funcdata &data); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/printc.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/printc.cc index 2db668b566..dc81ae4233 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/printc.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/printc.cc @@ -368,6 +368,55 @@ bool PrintC::checkArrayDeref(const Varnode *vn) const return true; } +/// Check that the output data-type is a pointer to an array and then that +/// the second data-type is a pointer to the element type (of the array). +/// If this holds and the input variable represents a symbol with an \e array data-type, +/// return \b true. +/// \return \b true if the CAST can be rendered as '&' +bool PrintC::checkAddressOfCast(const PcodeOp *op) const + +{ + Datatype *dt0 = op->getOut()->getHighTypeDefFacing(); + const Varnode *vnin = op->getIn(0); + Datatype *dt1 = vnin->getHighTypeReadFacing(op); + if (dt0->getMetatype() != TYPE_PTR || dt1->getMetatype() != TYPE_PTR) + return false; + const Datatype *base0 = ((const TypePointer *)dt0)->getPtrTo(); + const Datatype *base1 = ((const TypePointer *)dt1)->getPtrTo(); + if (base0->getMetatype() != TYPE_ARRAY) + return false; + int4 arraySize = base0->getSize(); + base0 = ((const TypeArray *)base0)->getBase(); + while(base0->getTypedef() != (Datatype *)0) + base0 = base0->getTypedef(); + while(base1->getTypedef() != (Datatype *)0) + base1 = base1->getTypedef(); + if (base0 != base1) + return false; + Datatype *symbolType = (Datatype *)0; + if (vnin->getSymbolEntry() != (SymbolEntry *)0 && vnin->getHigh()->getSymbolOffset() == -1) { + symbolType = vnin->getSymbolEntry()->getSymbol()->getType(); + } + else if (vnin->isWritten()) { + const PcodeOp *ptrsub = vnin->getDef(); + if (ptrsub->code() == CPUI_PTRSUB) { + Datatype *rootType = ptrsub->getIn(0)->getHighTypeReadFacing(ptrsub); + if (rootType->getMetatype() == TYPE_PTR) { + rootType = ((TypePointer *)rootType)->getPtrTo(); + int8 off = ptrsub->getIn(1)->getOffset(); + symbolType = rootType->getSubType(off, &off); + if (off != 0) + return false; + } + } + } + if (symbolType == (Datatype *)0) + return false; + if (symbolType->getMetatype() != TYPE_ARRAY || symbolType->getSize() != arraySize) + return false; + return true; +} + /// This is used for expression that require functional syntax, where the name of the /// function is the name of the operator. The inputs to the p-code op form the roots /// of the comma separated list of \e parameters within the syntax. @@ -399,9 +448,17 @@ void PrintC::opFunc(const PcodeOp *op) void PrintC::opTypeCast(const PcodeOp *op) { + Datatype *dt = op->getOut()->getHighTypeDefFacing(); + if (dt->isPointerToArray()) { + if (checkAddressOfCast(op)) { + pushOp(&addressof,op); + pushVn(op->getIn(0),op,mods); + return; + } + } if (!option_nocasts) { pushOp(&typecast,op); - pushType(op->getOut()->getHighTypeDefFacing()); + pushType(dt); } pushVn(op->getIn(0),op,mods); } @@ -798,13 +855,6 @@ void PrintC::opPtradd(const PcodeOp *op) { bool printval = isSet(print_load_value|print_store_value); uint4 m = mods & ~(print_load_value|print_store_value); - if (!printval) { - TypePointer *tp = (TypePointer *)op->getIn(0)->getHighTypeReadFacing(op); - if (tp->getMetatype() == TYPE_PTR) { - if (tp->getPtrTo()->getMetatype() == TYPE_ARRAY) - printval = true; - } - } if (printval) // Use array notation if we need value pushOp(&subscript,op); else // just a '+' @@ -820,8 +870,15 @@ static bool isValueFlexible(const Varnode *vn) { if ((vn->isImplied())&&(vn->isWritten())) { const PcodeOp *def = vn->getDef(); - if (def->code() == CPUI_PTRSUB) return true; - if (def->code() == CPUI_PTRADD) return true; + OpCode opc = def->code(); + if (opc == CPUI_COPY) { + const Varnode *invn = def->getIn(0); + if (!invn->isImplied() || !invn->isWritten()) + return false; + opc = invn->getDef()->code(); + } + if (opc == CPUI_PTRSUB) return true; + if (opc == CPUI_PTRADD) return true; } return false; } @@ -1020,12 +1077,11 @@ void PrintC::opPtrsub(const PcodeOp *op) // and this PTRSUB(*,0) represents changing // to treating it as a pointer to its element type if (!valueon) { - if (flex) { // EMIT ( ) - // (*&struct->arrayfield)[i] - // becomes struct->arrayfield[i] + // Even though there is no valueon, the PTRSUB still acts as a dereference + if (flex) { // EMIT ( ) if (ptrel != (TypePointerRel *)0) pushTypePointerRel(op); - pushVn(in0,op,m); + pushVn(in0,op,m | print_load_value); // Absorb dereference into in0's defining op } else { // EMIT *( ) pushOp(&dereference,op); @@ -1035,11 +1091,12 @@ void PrintC::opPtrsub(const PcodeOp *op) } } else { + // We need to show two dereferences here: one for the valueon and one for the PTRSUB if (flex) { // EMIT ( )[0] pushOp(&subscript,op); if (ptrel != (TypePointerRel *)0) pushTypePointerRel(op); - pushVn(in0,op,m); + pushVn(in0,op,m | print_load_value); // Absorb one dereference into in0's defining op push_integer(0,4,false,syntax,(Varnode *)0,op); } else { // EMIT (* )[0] diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/printc.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/printc.hh index 7e07905c71..91f1197711 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/printc.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/printc.hh @@ -172,6 +172,7 @@ protected: virtual bool doEmitWideCharPrefix(void) const; bool checkArrayDeref(const Varnode *vn) const; ///< Determine whether a LOAD/STORE expression requires pointer '*' syntax + bool checkAddressOfCast(const PcodeOp *op) const; ///< Check if CAST can be printed as an '&' void emitStructDefinition(const TypeStruct *ct); ///< Emit the definition of a \e structure data-type void emitEnumDefinition(const TypeEnum *ct); ///< Emit the definition of an \e enumeration data-type void emitPrototypeOutput(const FuncProto *proto,const Funcdata *fd); ///< Emit the output data-type of a function prototype diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc index 118bf74e9a..c57f4705e9 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc @@ -899,6 +899,9 @@ void TypePointer::calcSubmeta(void) else if (ptrtoMeta == TYPE_UNION) { submeta = SUB_PTR_STRUCT; } + else if (ptrtoMeta == TYPE_ARRAY) { + flags |= pointer_to_array; + } if (ptrto->needsResolution() && ptrtoMeta != TYPE_PTR) flags |= needs_resolution; // Inherit needs_resolution, but only if not a pointer } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/type.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/type.hh index 8868e39148..5064d4a9ec 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/type.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/type.hh @@ -156,7 +156,8 @@ protected: type_incomplete = 0x400, ///< Set if \b this (recursive) data-type has not been fully defined yet needs_resolution = 0x800, ///< Datatype (union, pointer to union) needs resolution before propagation force_format = 0x7000, ///< 3-bits encoding display format, 0=none, 1=hex, 2=dec, 3=oct, 4=bin, 5=char - truncate_bigendian = 0x8000 ///< Pointer can be truncated and is big endian + truncate_bigendian = 0x8000, ///< Pointer can be truncated and is big endian + pointer_to_array = 0x10000 ///< Data-type is a pointer to an array }; friend class TypeFactory; friend struct DatatypeCompare; @@ -193,6 +194,7 @@ 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 isPointerToArray(void) const { return ((flags&pointer_to_array)!=0); } ///< Is \b this a pointer to an array 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 diff --git a/Ghidra/Features/Decompiler/src/decompile/datatests/ptrtoarray.xml b/Ghidra/Features/Decompiler/src/decompile/datatests/ptrtoarray.xml new file mode 100644 index 0000000000..961e287c5a --- /dev/null +++ b/Ghidra/Features/Decompiler/src/decompile/datatests/ptrtoarray.xml @@ -0,0 +1,61 @@ + + + + + f30f +1efa554889e54883ec1048897df88975 +f4488b45f84883c0404889c7e846ffff +ff488b45f84883e8804889c7e87dffff +ff837df40a7508488145f80002000048 +8b45f84889c7e81cffffff488b45f848 +89c7e857ffffff488b45f8c9c3 + + + f30f1e +fa554889e54883ec70897d9c64488b04 +2528000000488945f831c0488d45b048 +8905522d0000488d45a44889c7e8b7fe +ffff488d45a84883c0044889c7e8b6fe +ffff488d3d372d0000e89bfeffff9048 +8b45f86448330425280000007405e86d +fdffffc9c3 + + + + + +display\(param_1 \+ 1\); +displayLow\(param_1\[2\]\); +paiStack_10 = param_1 \+ 8; +display\(paiStack_10\); +displayLow\(\*paiStack_10\); +return paiStack_10; +paiGlob = &c; +floatarray\(&a\); +intarray\(&myval\.b\); +floatarray\(&globarray\); +