diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/cast.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/cast.cc index da27695088..2f3590aee7 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/cast.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/cast.cc @@ -302,6 +302,8 @@ Datatype *CastStrategyC::castStandard(Datatype *reqtype,Datatype *curtype, { // Generic casting rules that apply for most ops if (curtype == reqtype) return (Datatype *)0; // Types are equal, no cast required + if (curtype->getMetatype()==TYPE_VOID) + return reqtype; // If coming from "void" (as a dereferenced pointer) we need a cast Datatype *reqbase = reqtype; Datatype *curbase = curtype; bool isptr = false; @@ -325,8 +327,9 @@ Datatype *CastStrategyC::castStandard(Datatype *reqtype,Datatype *curtype, while(curbase->getTypedef() != (Datatype *)0) curbase = curbase->getTypedef(); if (curbase == reqbase) return (Datatype *)0; // Different typedefs could point to the same type - if ((reqbase->getMetatype()==TYPE_VOID)||(curtype->getMetatype()==TYPE_VOID)) - return (Datatype *)0; // Don't cast from or to VOID + if (reqbase->getMetatype()==TYPE_VOID || curbase->getMetatype()==TYPE_VOID) { + return (Datatype *)0; // Don't cast to or from a void pointer + } if (reqbase->getSize() != curbase->getSize()) { if (reqbase->isVariableLength() && isptr && reqbase->hasSameVariableBase(curbase)) { return (Datatype *)0; // Don't need a cast diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.cc index b85a75b477..d87eb2f923 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.cc @@ -1917,19 +1917,19 @@ bool ParamTrial::operator<(const ParamTrial &b) const /// \param a trial /// \param b trial /// \return \b true if \b a should be ordered before \b b -bool ParamTrial::fixedPositionCompare(const ParamTrial &a, const ParamTrial &b) +bool ParamTrial::fixedPositionCompare(const ParamTrial &a,const ParamTrial &b) { - if (a.fixedPosition == -1 && b.fixedPosition == -1){ - return a < b; - } - if (a.fixedPosition == -1){ - return false; - } - if (b.fixedPosition == -1){ - return true; - } - return a.fixedPosition < b.fixedPosition; + if (a.fixedPosition == -1 && b.fixedPosition == -1) { + return a < b; + } + if (a.fixedPosition == -1) { + return false; + } + if (b.fixedPosition == -1) { + return true; + } + return a.fixedPosition < b.fixedPosition; } /// \param recoversub selects whether a sub-function or the active function is being tested @@ -1943,6 +1943,7 @@ ParamActive::ParamActive(bool recoversub) isfullychecked = false; needsfinalcheck = false; recoversubcall = recoversub; + joinReverse = false; } void ParamActive::clear(void) @@ -1953,6 +1954,7 @@ void ParamActive::clear(void) stackplaceholder = -1; numpasses = 0; isfullychecked = false; + joinReverse = false; } /// A ParamTrial object is created and a slot is assigned. @@ -5693,9 +5695,9 @@ void FuncCallSpecs::buildInputFromTrials(Funcdata &data) newparam.push_back(op->getIn(0)); // Preserve the fspec parameter if (isDotdotdot() && isInputLocked()){ - //if varargs, move the fixed args to the beginning of the list in order - //preserve relative order of variable args - activeinput.sortFixedPosition(); + // if varargs, move the fixed args to the beginning of the list in order to + // preserve relative order of variable args + activeinput.sortFixedPosition(); } for(int4 i=0;i &tria data.opSetOutput(op,finaloutvn); // Move varnode to its new position as output of call } else if (activeoutput.getNumTrials()==2) { - Varnode *hivn = finalvn[1]; // orderOutputPieces puts hi last - Varnode *lovn = finalvn[0]; + Varnode *hivn,*lovn; + if (activeoutput.isJoinReverse()) { + hivn = finalvn[0]; + lovn = finalvn[1]; + } + else { + hivn = finalvn[1]; + lovn = finalvn[0]; + } if (data.isDoublePrecisOn()) { lovn->setPrecisLo(); // Mark that these varnodes are part of a larger precision whole hivn->setPrecisHi(); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.hh index df41307e4a..6d1615b449 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.hh @@ -291,6 +291,7 @@ class ParamActive { bool isfullychecked; ///< True if all trials are fully examined (and no new trials are expected) bool needsfinalcheck; ///< Should a final pass be made on trials (to take into account control-flow changes) bool recoversubcall; ///< True if \b this is being used to recover prototypes of a sub-function call + bool joinReverse; ///< True if varnodes should be joined in reverse order public: ParamActive(bool recoversub); ///< Construct an empty container void clear(void); ///< Reset to an empty container @@ -301,6 +302,8 @@ public: int4 whichTrial(const Address &addr,int4 sz) const; ///< Get the trial overlapping with the given memory range bool needsFinalCheck(void) const { return needsfinalcheck; } ///< Is a final check required void markNeedsFinalCheck(void) { needsfinalcheck = true; } ///< Mark that a final check is required + bool isJoinReverse(void) const { return joinReverse; } ///< Do Varnodes need to be joined in reverse order + void setJoinReverse(void) { joinReverse = true; } ///< Mark that varnodes need to be joined in reverse order bool isRecoverSubcall(void) const { return recoversubcall; } ///< Are these trials for a call to a sub-function bool isFullyChecked(void) const { return isfullychecked; } ///< Are all trials checked with no new trials expected void markFullyChecked(void) { isfullychecked = true; } ///< Mark that all trials are checked @@ -311,6 +314,7 @@ public: void setMaxPass(int4 val) { maxpass = val; } ///< Set the maximum number of passes void finishPass(void) { numpasses += 1; } ///< Mark that an analysis pass has completed void sortTrials(void) { sort(trial.begin(),trial.end()); } ///< Sort the trials in formal parameter order + void sortFixedPosition(void) {sort(trial.begin(),trial.end(),ParamTrial::fixedPositionCompare);} ///< sort the trials by fixed position then < void deleteUnusedTrials(void); ///< Remove trials that were found not to be parameters void splitTrial(int4 i,int4 sz); ///< Split the given trial in two void joinTrial(int4 slot,const Address &addr,int4 sz); ///< Join adjacent parameter trials @@ -330,8 +334,6 @@ public: /// \param addr is the new range's starting address /// \param sz is the new range's size in bytes void shrink(int4 i,const Address &addr,int4 sz) { trial[i].setAddress(addr,sz); } - - void sortFixedPosition(void) {sort(trial.begin(),trial.end(),ParamTrial::fixedPositionCompare);} ///< sort the trials by fixed position then < }; /// \brief A special space for encoding FuncCallSpecs @@ -616,6 +618,7 @@ public: ParamListStandard(const ParamListStandard &op2); ///< Copy constructor virtual ~ParamListStandard(void); const list &getEntry(void) const { return entry; } ///< Get the list of parameter entries + bool isBigEndian(void) const { return entry.front().getSpace()->isBigEndian(); } ///< Return \b true if resources are big endian void extractTiles(vector &tiles,type_class type) const; ///< Get registers of given storage class const ParamEntry *getStackEntry(void) const; ///< Get the stack entry uint4 assignAddressFallback(type_class resource,Datatype *tp,bool matchExact,vector &status, diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/modelrules.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/modelrules.cc index 3bfd039f1d..2335bc012d 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/modelrules.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/modelrules.cc @@ -609,12 +609,7 @@ AssignAction *AssignAction::decodeAction(Decoder &decoder,const ParamListStandar action = new HiddenReturnAssign(res,hiddenret_specialreg); } else if (elemId == ELEM_JOIN_PER_PRIMITIVE) { - bool consumeMostSig = false; - AddrSpace *spc = res->getSpacebase(); - if (spc != (AddrSpace *)0 && spc->isBigEndian()) { - consumeMostSig = true; - } - action = new MultiMemberAssign(TYPECLASS_GENERAL,false,consumeMostSig,res); + action = new MultiMemberAssign(TYPECLASS_GENERAL,false,res->isBigEndian(),res); } else if (elemId == ELEM_JOIN_DUAL_CLASS) { action = new MultiSlotDualAssign(res); @@ -676,6 +671,28 @@ AssignAction *AssignAction::decodeSideeffect(Decoder &decoder,const ParamListSta return action; } +/// \brief Truncate a tiling by a given number of bytes +/// +/// The extra bytes are considered padding and removed from one end of the tiling. +/// The bytes removed depend on the endianness and how the data is justified within the tiling. +/// \param pieces is the tiling of 2 or more Varnodes +/// \param offset is the given number of bytes to truncate +/// \param isBigEndian is true for big endian architectures +/// \param consumeMostSig is true if the first tile in the list covers the most significant bytes +/// \param justifyRight is true if the data is right justified within the tiling +void AssignAction::justifyPieces(vector &pieces,int4 offset,bool isBigEndian, + bool consumeMostSig,bool justifyRight) +{ + bool addOffset = isBigEndian ^ consumeMostSig ^ justifyRight; + int pos = justifyRight ? 0 : pieces.size() - 1; + + VarnodeData &vndata(pieces[pos]); + if (addOffset) { + vndata.offset += offset; + } + vndata.size -= offset; +} + void GotoStack::initializeEntry(void) { @@ -784,6 +801,7 @@ MultiSlotAssign::MultiSlotAssign(const ParamListStandard *res) : AssignAction(res) { resourceType = TYPECLASS_GENERAL; // Join general purpose registers + isBigEndian = res->isBigEndian(); fillinOutputActive = true; uint4 listType = res->getType(); // Consume from stack on input parameters by default @@ -791,8 +809,7 @@ MultiSlotAssign::MultiSlotAssign(const ParamListStandard *res) consumeMostSig = false; enforceAlignment = false; justifyRight = false; - AddrSpace *spc = res->getSpacebase(); - if (spc != (AddrSpace *)0 && spc->isBigEndian()) { + if (isBigEndian) { consumeMostSig = true; justifyRight = true; } @@ -803,6 +820,7 @@ MultiSlotAssign::MultiSlotAssign(type_class store,bool stack,bool mostSig,bool a : AssignAction(res) { resourceType = store; + isBigEndian = res->isBigEndian(); fillinOutputActive = true; consumeFromStack = stack; consumeMostSig = mostSig; @@ -870,12 +888,8 @@ uint4 MultiSlotAssign::assignAddress(Datatype *dt,const PrototypePieces &proto,i tmp.offset = addr.getOffset(); tmp.size = dt->getSize(); } - else if (justifyRight) { - pieces.front().offset += -sizeLeft; // Initial bytes of first entry are padding - pieces.front().size += sizeLeft; - } else { - pieces.back().size += sizeLeft; + justifyPieces(pieces, -sizeLeft, isBigEndian, consumeMostSig, justifyRight); } } status = tmpStatus; // Commit resource usage for all the pieces @@ -931,7 +945,10 @@ bool MultiSlotAssign::fillinOutputMap(ParamActive *active) const } } } - return (count > 0); + if (count==0) return false; + if (consumeMostSig) + active->setJoinReverse(); + return true; } void MultiSlotAssign::decode(Decoder &decoder) @@ -1023,7 +1040,10 @@ bool MultiMemberAssign::fillinOutputMap(ParamActive *active) const return false; // Entry must be justified count += 1; } - return (count > 0); + if (count==0) return false; + if (consumeMostSig) + active->setJoinReverse(); + return true; } void MultiMemberAssign::decode(Decoder &decoder) @@ -1119,14 +1139,14 @@ int4 MultiSlotDualAssign::getTileClass(const PrimitiveExtractor &primitives,int4 MultiSlotDualAssign::MultiSlotDualAssign(const ParamListStandard *res) : AssignAction(res) { + isBigEndian = res->isBigEndian(); fillinOutputActive = true; baseType = TYPECLASS_GENERAL; // Tile from general purpose registers altType = TYPECLASS_FLOAT; // Use specialized registers for floating-point components consumeFromStack = false; consumeMostSig = false; justifyRight = false; - AddrSpace *spc = res->getSpacebase(); - if (spc != (AddrSpace *)0 && spc->isBigEndian()) { + if (isBigEndian) { consumeMostSig = true; justifyRight = true; } @@ -1139,6 +1159,7 @@ MultiSlotDualAssign::MultiSlotDualAssign(type_class baseStore,type_class altStor bool mostSig,bool justRight,bool fillAlt,const ParamListStandard *res) : AssignAction(res) { + isBigEndian = res->isBigEndian(); fillinOutputActive = true; baseType = baseStore; altType = altStore; @@ -1209,13 +1230,7 @@ uint4 MultiSlotDualAssign::assignAddress(Datatype *dt,const PrototypePieces &pro pieces.back().size = sizeLeft; } if (sizeLeft < 0) { // Have odd data-type size - if (justifyRight) { - pieces.front().offset += -sizeLeft; // Initial bytes of first entry are padding - pieces.front().size += sizeLeft; - } - else { - pieces.back().size += sizeLeft; - } + justifyPieces(pieces, -sizeLeft, isBigEndian, consumeMostSig, justifyRight); } status = tmpStatus; // Commit resource usage for all the pieces res.flags = 0; @@ -1276,8 +1291,10 @@ bool MultiSlotDualAssign::fillinOutputMap(ParamActive *active) const } } } - return (count > 0); - + if (count==0) return false; + if (consumeMostSig) + active->setJoinReverse(); + return true; } void MultiSlotDualAssign::decode(Decoder &decoder) diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/modelrules.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/modelrules.hh index 5d8e40a689..572a133be4 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/modelrules.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/modelrules.hh @@ -319,6 +319,7 @@ public: static AssignAction *decodeAction(Decoder &decoder,const ParamListStandard *res); static AssignAction *decodePrecondition(Decoder &decoder, const ParamListStandard *res); static AssignAction *decodeSideeffect(Decoder &decoder,const ParamListStandard *res); + static void justifyPieces(vector &pieces,int4 offset,bool isBigEndian,bool consumeMostSig,bool justifyRight); }; /// \brief Action assigning a parameter Address from the next available stack location @@ -355,6 +356,7 @@ public: /// Consumption can spill over onto the stack if desired. class MultiSlotAssign : public AssignAction { type_class resourceType; ///< Resource list from which to consume + bool isBigEndian; ///< True for big endian architectures bool consumeFromStack; ///< True if resources should be consumed from the stack bool consumeMostSig; ///< True if resources are consumed starting with most significant bytes bool enforceAlignment; ///< True if register resources are discarded to match alignment @@ -399,10 +401,11 @@ public: class MultiSlotDualAssign : public AssignAction { type_class baseType; ///< Resource list from which to consume general tiles type_class altType; ///< Resource list from which to consume alternate tiles - bool consumeFromStack; ///< True if resources should be consumed from the stack + bool isBigEndian; ///< True for big endian architectures + bool consumeFromStack; ///< True if resources should be consumed from the stack bool consumeMostSig; ///< True if resources are consumed starting with most significant bytes bool justifyRight; ///< True if initial bytes are padding for odd data-type sizes - bool fillAlternate; ///< True if a single primitive needs to fill an alternate tile + bool fillAlternate; ///< True if a single primitive needs to fill an alternate tile int4 tileSize; ///< Number of bytes in a tile vector baseTiles; ///< General registers to be joined vector altTiles; ///< Alternate registers to be joined diff --git a/Ghidra/Features/Decompiler/src/decompile/unittests/testparamstore.cc b/Ghidra/Features/Decompiler/src/decompile/unittests/testparamstore.cc index 2621e35ea7..1c1ef1f4b7 100644 --- a/Ghidra/Features/Decompiler/src/decompile/unittests/testparamstore.cc +++ b/Ghidra/Features/Decompiler/src/decompile/unittests/testparamstore.cc @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -258,6 +258,52 @@ TEST(paramstore_x64) { ASSERT(theEnviron.test(model, "doubleintintint func(void);", "RAX,RDI")); } +TEST(paramstore_ppc64be_stdcall) { + ProtoModel *model = theEnviron.getModel("PowerPC:BE:64:default:default","__stdcall"); + ASSERT(theEnviron.test(model,"void func(int4 a,float4 b,float8 c);","void,r3:4,join f1,f2")); + ASSERT(theEnviron.test(model,"void func(float8 a,int8 b,float8 c);","void,f1,r4,f2")); + theEnviron.parseType(model,"struct sparm { int4 a; float8 dd; };"); + + string proto= "void func(int4 c,float8 ff,int4 d,float16 ld,sparm s,float8 gg,sparm t,int4 e,float8 hh);"; + string res="void,r3:4,f1,r5:4,join f2 f3,join r8 r9,f4,stack70:16,stack84:4,f5"; + ASSERT(theEnviron.test(model,proto,res)); +} + +TEST(paramstore_mips32be_stdcall) { + ProtoModel *model = theEnviron.getModel("MIPS:BE:32:default:default","__stdcall"); + ASSERT(theEnviron.test(model,"void func(int2 a,int4 b,char c);","void,a0:2,a1,a2:1")); + ASSERT(theEnviron.test(model,"void func(float8 a,float8 b);","void,f12_13,f14_15")); + ASSERT(theEnviron.test(model,"void func(float4 a,float4 b);","void,f12,f14")); + ASSERT(theEnviron.test(model,"void func(float4 a,float8 b);","void,f12,f14_15")); + ASSERT(theEnviron.test(model,"void func(float8 a,float4 b);","void,f12_13,f14")); + ASSERT(theEnviron.test(model,"void func(int4 a,int4 b,int4 c,int4 d);","void,a0,a1,a2,a3")); + ASSERT(theEnviron.test(model,"void func(float8 a,int4 b,float8 c);","void,f12_13,a2,stack10:8")); + ASSERT(theEnviron.test(model,"void func(float8 a,int4 b,int4 c);","void,f12_13,a2,a3")); + ASSERT(theEnviron.test(model,"void func(float4 a,int4 b,int4 c);","void,f12,a1,a2")); + ASSERT(theEnviron.test(model,"void func(int4 a,int4 b,int4 c,float8 d);","void,a0,a1,a2,stack10:8")); + ASSERT(theEnviron.test(model,"void func(int4 a,int4 b,int4 c,float4 d);","void,a0,a1,a2,a3")); + ASSERT(theEnviron.test(model,"void func(int4 a,int4 b,float8 c);","void,a0,a1,join a2 a3")); + ASSERT(theEnviron.test(model,"void func(int4 a,float8 b);","void,a0,join a2 a3")); + ASSERT(theEnviron.test(model,"void func(float4 a,float4 b,float4 c,float4 d);","void,f12,f14,a2,a3")); + ASSERT(theEnviron.test(model,"void func(float4 a,int4 b,float4 c,int4 d);","void,f12,a1,a2,a3")); + ASSERT(theEnviron.test(model,"void func(float8 a,float4 b,float4 c);","void,f12_13,f14,a3")); + ASSERT(theEnviron.test(model,"void func(float4 a,float4 b,float8 c);","void,f12,f14,join a2 a3")); + ASSERT(theEnviron.test(model,"void func(int4 a,float4 b,int4 c,float4 d);","void,a0,a1,a2,a3")); + ASSERT(theEnviron.test(model,"void func(int4 a,float4 b,int4 c,int4 d);","void,a0,a1,a2,a3")); + ASSERT(theEnviron.test(model,"void func(int4 a,int4 b,float4 c,int4 d);","void,a0,a1,a2,a3")); + ASSERT(theEnviron.test(model,"int4 func(void);","v0")); + ASSERT(theEnviron.test(model, "float4 func(void);", "f0")); + ASSERT(theEnviron.test(model, "float8 func(void);", "f0_1")); + theEnviron.parseType(model,"struct onefieldstruct { int4 a; };"); + theEnviron.parseType(model,"struct twofieldstruct { int4 a; int4 b; };"); + ASSERT(theEnviron.test(model, "onefieldstruct func(int4 a);", "v0,a0,a1")); + ASSERT(theEnviron.test(model, "twofieldstruct func(int4 a);", "v0,a0,a1")); + ASSERT(theEnviron.test(model, "void func(twofieldstruct a);", "void,join a0 a1")); + + theEnviron.parseType(model,"struct intdouble { int4 a; float8 b; };"); + ASSERT(theEnviron.test(model, "void func(intdouble a);", "void,join a0 a1 a2 a3")); +} + TEST(paramstore_aarch64_cdecl) { ProtoModel *model = theEnviron.getModel("AARCH64:LE:64:v8A:default","__cdecl"); ASSERT(theEnviron.test(model, "void func(int2 a,int4 b,int1 c);", "void,w0:2,w1,w2:1")); diff --git a/Ghidra/Features/Decompiler/src/decompile/unittests/testtypes.cc b/Ghidra/Features/Decompiler/src/decompile/unittests/testtypes.cc index 542d39267e..eb41872bab 100644 --- a/Ghidra/Features/Decompiler/src/decompile/unittests/testtypes.cc +++ b/Ghidra/Features/Decompiler/src/decompile/unittests/testtypes.cc @@ -138,7 +138,7 @@ TEST(cast_pointer) { TypeTestEnvironment::build(); ASSERT(castPrinted(CPUI_COPY,parse("uint4 *"),parse("int4 *"))); ASSERT(!castPrinted(CPUI_COPY,parse("void *"),parse("float4 *"))); - ASSERT(castPrinted(CPUI_COPY,parse("int2 *"),parse("void *"))); + ASSERT(!castPrinted(CPUI_COPY,parse("int2 *"),parse("void *"))); Datatype *typedefInt = types->getBase(4,TYPE_INT,"myint4"); Datatype *typedefPtr = types->getTypePointer(8,typedefInt,1); ASSERT(!castPrinted(CPUI_COPY,typedefPtr,parse("int4 *"))); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/ParamListStandard.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/ParamListStandard.java index e90a3c842b..274cde0bd8 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/ParamListStandard.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/ParamListStandard.java @@ -167,6 +167,13 @@ public class ParamListStandard implements ParamList { return entry[index]; } + /** + * @return true if resources are from a big endian address space + */ + public boolean isBigEndian() { + return entry[0].isBigEndian(); + } + @Override public void assignMap(PrototypePieces proto, DataTypeManager dtManager, ArrayList res, boolean addAutoParams) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/protorules/AssignAction.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/protorules/AssignAction.java index 2f73bc8143..629f96192f 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/protorules/AssignAction.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/protorules/AssignAction.java @@ -18,11 +18,14 @@ package ghidra.program.model.lang.protorules; import static ghidra.program.model.pcode.ElementId.*; import java.io.IOException; +import java.util.ArrayList; +import ghidra.program.model.address.Address; import ghidra.program.model.data.DataType; import ghidra.program.model.data.DataTypeManager; import ghidra.program.model.lang.*; import ghidra.program.model.pcode.Encoder; +import ghidra.program.model.pcode.Varnode; import ghidra.util.exception.InvalidInputException; import ghidra.xml.*; @@ -124,8 +127,7 @@ public abstract class AssignAction { action = new HiddenReturnAssign(res, HIDDENRET_SPECIALREG); } else if (nm.equals(ELEM_JOIN_PER_PRIMITIVE.name())) { - boolean consumeMostSig = res.getEntry(0).isBigEndian(); - action = new MultiMemberAssign(StorageClass.GENERAL, false, consumeMostSig, res); + action = new MultiMemberAssign(StorageClass.GENERAL, false, res.isBigEndian(), res); } else if (nm.equals(ELEM_JOIN_DUAL_CLASS.name())) { action = new MultiSlotDualAssign(res); @@ -191,4 +193,20 @@ public abstract class AssignAction { action.restoreXml(parser); return action; } + + public static void justifyPieces(ArrayList pieces, int offset, boolean isBigEndian, + boolean consumeMostSig, + boolean justifyRight) { + boolean addOffset = isBigEndian ^ consumeMostSig ^ justifyRight; + int pos = justifyRight ? 0 : pieces.size() - 1; + + Varnode vn = pieces.get(pos); + Address addr = vn.getAddress(); + if (addOffset) { + addr = addr.add(offset); + } + int sz = vn.getSize() - offset; + vn = new Varnode(addr, sz); + pieces.set(pos, vn); + } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/protorules/MultiSlotAssign.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/protorules/MultiSlotAssign.java index 8633e06048..74f7cf5266 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/protorules/MultiSlotAssign.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/protorules/MultiSlotAssign.java @@ -22,7 +22,6 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Map.Entry; -import ghidra.program.model.address.Address; import ghidra.program.model.data.DataType; import ghidra.program.model.data.DataTypeManager; import ghidra.program.model.lang.*; @@ -41,6 +40,7 @@ import ghidra.xml.*; */ public class MultiSlotAssign extends AssignAction { private StorageClass resourceType; // Resource list from which to consume + private boolean isBigEndian; // True for big endian architectures private boolean consumeFromStack; // True if resources should be consumed from the stack private boolean consumeMostSig; // True if resources are consumed starting with most significant bytes private boolean enforceAlignment; // True if register resources are discarded to match alignment @@ -74,6 +74,7 @@ public class MultiSlotAssign extends AssignAction { */ protected MultiSlotAssign(ParamListStandard res) { super(res); + isBigEndian = res.isBigEndian(); resourceType = StorageClass.GENERAL; // Join general purpose registers consumeFromStack = !(res instanceof ParamListStandardOut); // Spill into stack by default consumeMostSig = false; @@ -81,7 +82,7 @@ public class MultiSlotAssign extends AssignAction { justifyRight = false; adjacentEntries = true; allowBackfill = false; - if (res.getEntry(0).isBigEndian()) { + if (isBigEndian) { consumeMostSig = true; justifyRight = true; } @@ -92,6 +93,7 @@ public class MultiSlotAssign extends AssignAction { boolean justRight, boolean backfill, ParamListStandard res) throws InvalidInputException { super(res); + isBigEndian = res.isBigEndian(); resourceType = store; consumeFromStack = stack; consumeMostSig = mostSig; @@ -240,20 +242,8 @@ public class MultiSlotAssign extends AssignAction { // Floating-point register holding extended lower precision value onePieceJoin = true; // Treat as "join" of full size register } - else if (justifyRight) { - // Initial bytes are padding - Varnode vn = pieces.get(0); - Address addr = vn.getAddress().add(-sizeLeft); - int sz = vn.getSize() + sizeLeft; - vn = new Varnode(addr, sz); - pieces.set(0, vn); - } else { - int end = pieces.size() - 1; - Varnode vn = pieces.get(end); - int sz = vn.getSize() + sizeLeft; - vn = new Varnode(vn.getAddress(), sz); - pieces.set(end, vn); + justifyPieces(pieces, -sizeLeft, isBigEndian, consumeMostSig, justifyRight); } } System.arraycopy(tmpStatus, 0, status, 0, tmpStatus.length); // Commit resource usage for all the pieces @@ -265,10 +255,10 @@ public class MultiSlotAssign extends AssignAction { @Override public void encode(Encoder encoder) throws IOException { encoder.openElement(ELEM_JOIN); - if (resource.getEntry(0).isBigEndian() != justifyRight) { + if (resource.isBigEndian() != justifyRight) { encoder.writeBool(ATTRIB_REVERSEJUSTIFY, true); } - if (resource.getEntry(0).isBigEndian() != consumeMostSig) { + if (resource.isBigEndian() != consumeMostSig) { encoder.writeBool(ATTRIB_REVERSESIGNIF, true); } if (resourceType != StorageClass.GENERAL) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/protorules/MultiSlotDualAssign.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/protorules/MultiSlotDualAssign.java index 3a9add487c..83b4fff43e 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/protorules/MultiSlotDualAssign.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/protorules/MultiSlotDualAssign.java @@ -22,7 +22,6 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Map.Entry; -import ghidra.program.model.address.Address; import ghidra.program.model.data.DataType; import ghidra.program.model.data.DataTypeManager; import ghidra.program.model.lang.*; @@ -42,6 +41,7 @@ import ghidra.xml.*; public class MultiSlotDualAssign extends AssignAction { private StorageClass baseType; // Resource list from which to consume general tiles private StorageClass altType; // Resource list from which to consume alternate tiles + private boolean isBigEndian; // True for big endian architectures private boolean consumeFromStack; // True if resources can be consumed from the stack private boolean consumeMostSig; // True if resources are consumed starting with most significant bytes private boolean justifyRight; // True if initial bytes are padding for odd data-type sizes @@ -151,12 +151,13 @@ public class MultiSlotDualAssign extends AssignAction { */ protected MultiSlotDualAssign(ParamListStandard res) { super(res); + isBigEndian = res.isBigEndian(); baseType = StorageClass.GENERAL; // Tile from general purpose registers altType = StorageClass.FLOAT; // Use specialized registers for floating-point components consumeFromStack = false; consumeMostSig = false; justifyRight = false; - if (res.getEntry(0).isBigEndian()) { + if (isBigEndian) { consumeMostSig = true; justifyRight = true; } @@ -180,6 +181,7 @@ public class MultiSlotDualAssign extends AssignAction { boolean mostSig, boolean justRight, boolean fillAlt, ParamListStandard res) throws InvalidInputException { super(res); + isBigEndian = res.isBigEndian(); baseType = baseStore; altType = altStore; consumeFromStack = stack; @@ -294,21 +296,7 @@ public class MultiSlotDualAssign extends AssignAction { pieces.add(vn); } if (sizeLeft < 0) { // Have odd data-type size - if (justifyRight) { - // Initial bytes of first entry are padding - Varnode vn = pieces.get(0); - Address addr = vn.getAddress().add(-sizeLeft); - int sz = vn.getSize() + sizeLeft; - vn = new Varnode(addr, sz); - pieces.set(0, vn); - } - else { - int end = pieces.size() - 1; - Varnode vn = pieces.get(end); - int sz = vn.getSize() + sizeLeft; - vn = new Varnode(vn.getAddress(), sz); - pieces.set(end, vn); - } + justifyPieces(pieces, -sizeLeft, isBigEndian, consumeMostSig, justifyRight); } System.arraycopy(tmpStatus, 0, status, 0, tmpStatus.length); // Commit resource usage for all the pieces res.type = dt; @@ -319,10 +307,10 @@ public class MultiSlotDualAssign extends AssignAction { @Override public void encode(Encoder encoder) throws IOException { encoder.openElement(ELEM_JOIN_DUAL_CLASS); - if (resource.getEntry(0).isBigEndian() != justifyRight) { + if (resource.isBigEndian() != justifyRight) { encoder.writeBool(ATTRIB_REVERSEJUSTIFY, true); } - if (resource.getEntry(0).isBigEndian() != consumeMostSig) { + if (resource.isBigEndian() != consumeMostSig) { encoder.writeBool(ATTRIB_REVERSESIGNIF, true); } if (baseType != StorageClass.GENERAL) { diff --git a/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/program/model/lang/MipsPrototypeModelTest.java b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/program/model/lang/MipsPrototypeModelTest.java new file mode 100644 index 0000000000..9f775f9b87 --- /dev/null +++ b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/program/model/lang/MipsPrototypeModelTest.java @@ -0,0 +1,65 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.program.model.lang; + +import org.junit.Before; +import org.junit.Test; + +public class MipsPrototypeModelTest extends AbstractProtoModelTest { + + @Before + public void setUp() throws Exception { + buildArchitecture("MIPS:BE:32:default:default"); + } + + @Test + public void tesStdCall() throws Exception { + PrototypeModel model = cspec.getCallingConvention("__stdcall"); + + test(model, "void func(short a,int b,char c)", "void,a0:2,a1,a2:1"); + test(model, "void func(double a,double b)", "void,f12_13,f14_15"); + test(model, "void func(float a,float b)", "void,f12,f14"); + test(model, "void func(float a,double b)", "void,f12,f14_15"); + test(model, "void func(double a,float b)", "void,f12_13,f14"); + test(model, "void func(int a,int b,int c,int d)", "void,a0,a1,a2,a3"); + test(model, "void func(double a,int b,double c)", "void,f12_13,a2,stack10:8"); + test(model, "void func(double a,int b,int c)", "void,f12_13,a2,a3"); + test(model, "void func(float a,int b,int c)", "void,f12,a1,a2"); + test(model, "void func(int a,int b,int c,double d)", "void,a0,a1,a2,stack10:8"); + test(model, "void func(int a,int b,int c,float d)", "void,a0,a1,a2,a3"); + test(model, "void func(int a,int b,double c)", "void,a0,a1,join a2 a3"); + test(model, "void func(int a,double b)", "void,a0,join a2 a3"); + test(model, "void func(float a,float b,float c,float d)", "void,f12,f14,a2,a3"); + test(model, "void func(float a,int b,float c,int d)", "void,f12,a1,a2,a3"); + test(model, "void func(double a,float b,float c)", "void,f12_13,f14,a3"); + test(model, "void func(float a,float b,double c)", "void,f12,f14,join a2 a3"); + test(model, "void func(int a,float b,int c,float d)", "void,a0,a1,a2,a3"); + test(model, "void func(int a,float b,int c,int d)", "void,a0,a1,a2,a3"); + test(model, "void func(int a,int b,float c,int d)", "void,a0,a1,a2,a3"); + + test(model, "int func(void)", "v0"); + test(model, "float func(void)", "f0"); + test(model, "double func(void)", "f0_1"); + parseStructure("onefieldstruct", "int"); + parseStructure("twofieldstruct", "int,int"); + test(model, "onefieldstruct func(int a)", "v0,a0,a1"); + test(model, "twofieldstruct func(int a)", "v0,a0,a1"); + test(model, "void func(twofieldstruct a)", "void,join a0 a1"); + + parseStructure("intdouble", "int,double"); + test(model, "void func(intdouble a)", "void,join a0 a1 a2 a3"); + } +} diff --git a/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/program/model/lang/PowerPCPrototypeModelTest.java b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/program/model/lang/PowerPCPrototypeModelTest.java new file mode 100644 index 0000000000..3c258da790 --- /dev/null +++ b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/program/model/lang/PowerPCPrototypeModelTest.java @@ -0,0 +1,42 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.program.model.lang; + +import org.junit.Before; +import org.junit.Test; + +public class PowerPCPrototypeModelTest extends AbstractProtoModelTest { + + @Before + public void setUp() throws Exception { + buildArchitecture("PowerPC:BE:64:default:default"); + } + + @Test + public void tesStdCall() throws Exception { + PrototypeModel model = cspec.getCallingConvention("__stdcall"); + test(model, "void func(int a,float b,double c)", "void,r3:4,join f1,f2"); + test(model, "void func(double a,long b,double c)", "void,f1,r4,f2"); + + parseStructure("sparm", "int,double"); + + String proto = + "void func(int c,double ff,int d,float16 ld,sparm s,double gg,sparm t,int e,double hh)"; + String res = "void,r3:4,f1,r5:4,join f2 f3,join r8 r9,f4,stack70:16,stack84:4,f5"; + test(model, proto, res); + } + +}