GP-5851 Fix tiling truncations for odd data-type sizes in

MultiSlotAssign. Fix for big endian multi-slot return value.
This commit is contained in:
caheckman 2025-07-15 23:09:23 +00:00
parent eb7dbaa04f
commit f6495e4146
13 changed files with 280 additions and 89 deletions

View file

@ -302,6 +302,8 @@ Datatype *CastStrategyC::castStandard(Datatype *reqtype,Datatype *curtype,
{ // Generic casting rules that apply for most ops { // Generic casting rules that apply for most ops
if (curtype == reqtype) return (Datatype *)0; // Types are equal, no cast required 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 *reqbase = reqtype;
Datatype *curbase = curtype; Datatype *curbase = curtype;
bool isptr = false; bool isptr = false;
@ -325,8 +327,9 @@ Datatype *CastStrategyC::castStandard(Datatype *reqtype,Datatype *curtype,
while(curbase->getTypedef() != (Datatype *)0) while(curbase->getTypedef() != (Datatype *)0)
curbase = curbase->getTypedef(); curbase = curbase->getTypedef();
if (curbase == reqbase) return (Datatype *)0; // Different typedefs could point to the same type if (curbase == reqbase) return (Datatype *)0; // Different typedefs could point to the same type
if ((reqbase->getMetatype()==TYPE_VOID)||(curtype->getMetatype()==TYPE_VOID)) if (reqbase->getMetatype()==TYPE_VOID || curbase->getMetatype()==TYPE_VOID) {
return (Datatype *)0; // Don't cast from or to VOID return (Datatype *)0; // Don't cast to or from a void pointer
}
if (reqbase->getSize() != curbase->getSize()) { if (reqbase->getSize() != curbase->getSize()) {
if (reqbase->isVariableLength() && isptr && reqbase->hasSameVariableBase(curbase)) { if (reqbase->isVariableLength() && isptr && reqbase->hasSameVariableBase(curbase)) {
return (Datatype *)0; // Don't need a cast return (Datatype *)0; // Don't need a cast

View file

@ -1943,6 +1943,7 @@ ParamActive::ParamActive(bool recoversub)
isfullychecked = false; isfullychecked = false;
needsfinalcheck = false; needsfinalcheck = false;
recoversubcall = recoversub; recoversubcall = recoversub;
joinReverse = false;
} }
void ParamActive::clear(void) void ParamActive::clear(void)
@ -1953,6 +1954,7 @@ void ParamActive::clear(void)
stackplaceholder = -1; stackplaceholder = -1;
numpasses = 0; numpasses = 0;
isfullychecked = false; isfullychecked = false;
joinReverse = false;
} }
/// A ParamTrial object is created and a slot is assigned. /// A ParamTrial object is created and a slot is assigned.
@ -5693,7 +5695,7 @@ void FuncCallSpecs::buildInputFromTrials(Funcdata &data)
newparam.push_back(op->getIn(0)); // Preserve the fspec parameter newparam.push_back(op->getIn(0)); // Preserve the fspec parameter
if (isDotdotdot() && isInputLocked()){ if (isDotdotdot() && isInputLocked()){
//if varargs, move the fixed args to the beginning of the list in order // if varargs, move the fixed args to the beginning of the list in order to
// preserve relative order of variable args // preserve relative order of variable args
activeinput.sortFixedPosition(); activeinput.sortFixedPosition();
} }
@ -5802,8 +5804,15 @@ void FuncCallSpecs::buildOutputFromTrials(Funcdata &data,vector<Varnode *> &tria
data.opSetOutput(op,finaloutvn); // Move varnode to its new position as output of call data.opSetOutput(op,finaloutvn); // Move varnode to its new position as output of call
} }
else if (activeoutput.getNumTrials()==2) { else if (activeoutput.getNumTrials()==2) {
Varnode *hivn = finalvn[1]; // orderOutputPieces puts hi last Varnode *hivn,*lovn;
Varnode *lovn = finalvn[0]; if (activeoutput.isJoinReverse()) {
hivn = finalvn[0];
lovn = finalvn[1];
}
else {
hivn = finalvn[1];
lovn = finalvn[0];
}
if (data.isDoublePrecisOn()) { if (data.isDoublePrecisOn()) {
lovn->setPrecisLo(); // Mark that these varnodes are part of a larger precision whole lovn->setPrecisLo(); // Mark that these varnodes are part of a larger precision whole
hivn->setPrecisHi(); hivn->setPrecisHi();

View file

@ -291,6 +291,7 @@ class ParamActive {
bool isfullychecked; ///< True if all trials are fully examined (and no new trials are expected) 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 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 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: public:
ParamActive(bool recoversub); ///< Construct an empty container ParamActive(bool recoversub); ///< Construct an empty container
void clear(void); ///< Reset to 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 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 bool needsFinalCheck(void) const { return needsfinalcheck; } ///< Is a final check required
void markNeedsFinalCheck(void) { needsfinalcheck = true; } ///< Mark that a final check is 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 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 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 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 setMaxPass(int4 val) { maxpass = val; } ///< Set the maximum number of passes
void finishPass(void) { numpasses += 1; } ///< Mark that an analysis pass has completed 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 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 deleteUnusedTrials(void); ///< Remove trials that were found not to be parameters
void splitTrial(int4 i,int4 sz); ///< Split the given trial in two 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 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 addr is the new range's starting address
/// \param sz is the new range's size in bytes /// \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 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 /// \brief A special space for encoding FuncCallSpecs
@ -616,6 +618,7 @@ public:
ParamListStandard(const ParamListStandard &op2); ///< Copy constructor ParamListStandard(const ParamListStandard &op2); ///< Copy constructor
virtual ~ParamListStandard(void); virtual ~ParamListStandard(void);
const list<ParamEntry> &getEntry(void) const { return entry; } ///< Get the list of parameter entries const list<ParamEntry> &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<const ParamEntry *> &tiles,type_class type) const; ///< Get registers of given storage class void extractTiles(vector<const ParamEntry *> &tiles,type_class type) const; ///< Get registers of given storage class
const ParamEntry *getStackEntry(void) const; ///< Get the stack entry const ParamEntry *getStackEntry(void) const; ///< Get the stack entry
uint4 assignAddressFallback(type_class resource,Datatype *tp,bool matchExact,vector<int4> &status, uint4 assignAddressFallback(type_class resource,Datatype *tp,bool matchExact,vector<int4> &status,

View file

@ -609,12 +609,7 @@ AssignAction *AssignAction::decodeAction(Decoder &decoder,const ParamListStandar
action = new HiddenReturnAssign(res,hiddenret_specialreg); action = new HiddenReturnAssign(res,hiddenret_specialreg);
} }
else if (elemId == ELEM_JOIN_PER_PRIMITIVE) { else if (elemId == ELEM_JOIN_PER_PRIMITIVE) {
bool consumeMostSig = false; action = new MultiMemberAssign(TYPECLASS_GENERAL,false,res->isBigEndian(),res);
AddrSpace *spc = res->getSpacebase();
if (spc != (AddrSpace *)0 && spc->isBigEndian()) {
consumeMostSig = true;
}
action = new MultiMemberAssign(TYPECLASS_GENERAL,false,consumeMostSig,res);
} }
else if (elemId == ELEM_JOIN_DUAL_CLASS) { else if (elemId == ELEM_JOIN_DUAL_CLASS) {
action = new MultiSlotDualAssign(res); action = new MultiSlotDualAssign(res);
@ -676,6 +671,28 @@ AssignAction *AssignAction::decodeSideeffect(Decoder &decoder,const ParamListSta
return action; 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<VarnodeData> &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) void GotoStack::initializeEntry(void)
{ {
@ -784,6 +801,7 @@ MultiSlotAssign::MultiSlotAssign(const ParamListStandard *res)
: AssignAction(res) : AssignAction(res)
{ {
resourceType = TYPECLASS_GENERAL; // Join general purpose registers resourceType = TYPECLASS_GENERAL; // Join general purpose registers
isBigEndian = res->isBigEndian();
fillinOutputActive = true; fillinOutputActive = true;
uint4 listType = res->getType(); uint4 listType = res->getType();
// Consume from stack on input parameters by default // Consume from stack on input parameters by default
@ -791,8 +809,7 @@ MultiSlotAssign::MultiSlotAssign(const ParamListStandard *res)
consumeMostSig = false; consumeMostSig = false;
enforceAlignment = false; enforceAlignment = false;
justifyRight = false; justifyRight = false;
AddrSpace *spc = res->getSpacebase(); if (isBigEndian) {
if (spc != (AddrSpace *)0 && spc->isBigEndian()) {
consumeMostSig = true; consumeMostSig = true;
justifyRight = true; justifyRight = true;
} }
@ -803,6 +820,7 @@ MultiSlotAssign::MultiSlotAssign(type_class store,bool stack,bool mostSig,bool a
: AssignAction(res) : AssignAction(res)
{ {
resourceType = store; resourceType = store;
isBigEndian = res->isBigEndian();
fillinOutputActive = true; fillinOutputActive = true;
consumeFromStack = stack; consumeFromStack = stack;
consumeMostSig = mostSig; consumeMostSig = mostSig;
@ -870,12 +888,8 @@ uint4 MultiSlotAssign::assignAddress(Datatype *dt,const PrototypePieces &proto,i
tmp.offset = addr.getOffset(); tmp.offset = addr.getOffset();
tmp.size = dt->getSize(); tmp.size = dt->getSize();
} }
else if (justifyRight) {
pieces.front().offset += -sizeLeft; // Initial bytes of first entry are padding
pieces.front().size += sizeLeft;
}
else { else {
pieces.back().size += sizeLeft; justifyPieces(pieces, -sizeLeft, isBigEndian, consumeMostSig, justifyRight);
} }
} }
status = tmpStatus; // Commit resource usage for all the pieces 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) void MultiSlotAssign::decode(Decoder &decoder)
@ -1023,7 +1040,10 @@ bool MultiMemberAssign::fillinOutputMap(ParamActive *active) const
return false; // Entry must be justified return false; // Entry must be justified
count += 1; count += 1;
} }
return (count > 0); if (count==0) return false;
if (consumeMostSig)
active->setJoinReverse();
return true;
} }
void MultiMemberAssign::decode(Decoder &decoder) void MultiMemberAssign::decode(Decoder &decoder)
@ -1119,14 +1139,14 @@ int4 MultiSlotDualAssign::getTileClass(const PrimitiveExtractor &primitives,int4
MultiSlotDualAssign::MultiSlotDualAssign(const ParamListStandard *res) MultiSlotDualAssign::MultiSlotDualAssign(const ParamListStandard *res)
: AssignAction(res) : AssignAction(res)
{ {
isBigEndian = res->isBigEndian();
fillinOutputActive = true; fillinOutputActive = true;
baseType = TYPECLASS_GENERAL; // Tile from general purpose registers baseType = TYPECLASS_GENERAL; // Tile from general purpose registers
altType = TYPECLASS_FLOAT; // Use specialized registers for floating-point components altType = TYPECLASS_FLOAT; // Use specialized registers for floating-point components
consumeFromStack = false; consumeFromStack = false;
consumeMostSig = false; consumeMostSig = false;
justifyRight = false; justifyRight = false;
AddrSpace *spc = res->getSpacebase(); if (isBigEndian) {
if (spc != (AddrSpace *)0 && spc->isBigEndian()) {
consumeMostSig = true; consumeMostSig = true;
justifyRight = true; justifyRight = true;
} }
@ -1139,6 +1159,7 @@ MultiSlotDualAssign::MultiSlotDualAssign(type_class baseStore,type_class altStor
bool mostSig,bool justRight,bool fillAlt,const ParamListStandard *res) bool mostSig,bool justRight,bool fillAlt,const ParamListStandard *res)
: AssignAction(res) : AssignAction(res)
{ {
isBigEndian = res->isBigEndian();
fillinOutputActive = true; fillinOutputActive = true;
baseType = baseStore; baseType = baseStore;
altType = altStore; altType = altStore;
@ -1209,13 +1230,7 @@ uint4 MultiSlotDualAssign::assignAddress(Datatype *dt,const PrototypePieces &pro
pieces.back().size = sizeLeft; pieces.back().size = sizeLeft;
} }
if (sizeLeft < 0) { // Have odd data-type size if (sizeLeft < 0) { // Have odd data-type size
if (justifyRight) { justifyPieces(pieces, -sizeLeft, isBigEndian, consumeMostSig, justifyRight);
pieces.front().offset += -sizeLeft; // Initial bytes of first entry are padding
pieces.front().size += sizeLeft;
}
else {
pieces.back().size += sizeLeft;
}
} }
status = tmpStatus; // Commit resource usage for all the pieces status = tmpStatus; // Commit resource usage for all the pieces
res.flags = 0; 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) void MultiSlotDualAssign::decode(Decoder &decoder)

View file

@ -319,6 +319,7 @@ public:
static AssignAction *decodeAction(Decoder &decoder,const ParamListStandard *res); static AssignAction *decodeAction(Decoder &decoder,const ParamListStandard *res);
static AssignAction *decodePrecondition(Decoder &decoder, const ParamListStandard *res); static AssignAction *decodePrecondition(Decoder &decoder, const ParamListStandard *res);
static AssignAction *decodeSideeffect(Decoder &decoder,const ParamListStandard *res); static AssignAction *decodeSideeffect(Decoder &decoder,const ParamListStandard *res);
static void justifyPieces(vector<VarnodeData> &pieces,int4 offset,bool isBigEndian,bool consumeMostSig,bool justifyRight);
}; };
/// \brief Action assigning a parameter Address from the next available stack location /// \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. /// Consumption can spill over onto the stack if desired.
class MultiSlotAssign : public AssignAction { class MultiSlotAssign : public AssignAction {
type_class resourceType; ///< Resource list from which to consume 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 consumeFromStack; ///< True if resources should be consumed from the stack
bool consumeMostSig; ///< True if resources are consumed starting with most significant bytes bool consumeMostSig; ///< True if resources are consumed starting with most significant bytes
bool enforceAlignment; ///< True if register resources are discarded to match alignment bool enforceAlignment; ///< True if register resources are discarded to match alignment
@ -399,6 +401,7 @@ public:
class MultiSlotDualAssign : public AssignAction { class MultiSlotDualAssign : public AssignAction {
type_class baseType; ///< Resource list from which to consume general tiles type_class baseType; ///< Resource list from which to consume general tiles
type_class altType; ///< Resource list from which to consume alternate tiles type_class altType; ///< Resource list from which to consume alternate tiles
bool isBigEndian; ///< True for big endian architectures
bool consumeFromStack; ///< True if resources should be consumed from the stack bool consumeFromStack; ///< True if resources should be consumed from the stack
bool consumeMostSig; ///< True if resources are consumed starting with most significant bytes 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 justifyRight; ///< True if initial bytes are padding for odd data-type sizes

View file

@ -258,6 +258,52 @@ TEST(paramstore_x64) {
ASSERT(theEnviron.test(model, "doubleintintint func(void);", "RAX,RDI")); 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) { TEST(paramstore_aarch64_cdecl) {
ProtoModel *model = theEnviron.getModel("AARCH64:LE:64:v8A:default","__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")); ASSERT(theEnviron.test(model, "void func(int2 a,int4 b,int1 c);", "void,w0:2,w1,w2:1"));

View file

@ -138,7 +138,7 @@ TEST(cast_pointer) {
TypeTestEnvironment::build(); TypeTestEnvironment::build();
ASSERT(castPrinted(CPUI_COPY,parse("uint4 *"),parse("int4 *"))); ASSERT(castPrinted(CPUI_COPY,parse("uint4 *"),parse("int4 *")));
ASSERT(!castPrinted(CPUI_COPY,parse("void *"),parse("float4 *"))); 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 *typedefInt = types->getBase(4,TYPE_INT,"myint4");
Datatype *typedefPtr = types->getTypePointer(8,typedefInt,1); Datatype *typedefPtr = types->getTypePointer(8,typedefInt,1);
ASSERT(!castPrinted(CPUI_COPY,typedefPtr,parse("int4 *"))); ASSERT(!castPrinted(CPUI_COPY,typedefPtr,parse("int4 *")));

View file

@ -167,6 +167,13 @@ public class ParamListStandard implements ParamList {
return entry[index]; return entry[index];
} }
/**
* @return true if resources are from a big endian address space
*/
public boolean isBigEndian() {
return entry[0].isBigEndian();
}
@Override @Override
public void assignMap(PrototypePieces proto, DataTypeManager dtManager, public void assignMap(PrototypePieces proto, DataTypeManager dtManager,
ArrayList<ParameterPieces> res, boolean addAutoParams) { ArrayList<ParameterPieces> res, boolean addAutoParams) {

View file

@ -18,11 +18,14 @@ package ghidra.program.model.lang.protorules;
import static ghidra.program.model.pcode.ElementId.*; import static ghidra.program.model.pcode.ElementId.*;
import java.io.IOException; 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.DataType;
import ghidra.program.model.data.DataTypeManager; import ghidra.program.model.data.DataTypeManager;
import ghidra.program.model.lang.*; import ghidra.program.model.lang.*;
import ghidra.program.model.pcode.Encoder; import ghidra.program.model.pcode.Encoder;
import ghidra.program.model.pcode.Varnode;
import ghidra.util.exception.InvalidInputException; import ghidra.util.exception.InvalidInputException;
import ghidra.xml.*; import ghidra.xml.*;
@ -124,8 +127,7 @@ public abstract class AssignAction {
action = new HiddenReturnAssign(res, HIDDENRET_SPECIALREG); action = new HiddenReturnAssign(res, HIDDENRET_SPECIALREG);
} }
else if (nm.equals(ELEM_JOIN_PER_PRIMITIVE.name())) { else if (nm.equals(ELEM_JOIN_PER_PRIMITIVE.name())) {
boolean consumeMostSig = res.getEntry(0).isBigEndian(); action = new MultiMemberAssign(StorageClass.GENERAL, false, res.isBigEndian(), res);
action = new MultiMemberAssign(StorageClass.GENERAL, false, consumeMostSig, res);
} }
else if (nm.equals(ELEM_JOIN_DUAL_CLASS.name())) { else if (nm.equals(ELEM_JOIN_DUAL_CLASS.name())) {
action = new MultiSlotDualAssign(res); action = new MultiSlotDualAssign(res);
@ -191,4 +193,20 @@ public abstract class AssignAction {
action.restoreXml(parser); action.restoreXml(parser);
return action; return action;
} }
public static void justifyPieces(ArrayList<Varnode> 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);
}
} }

View file

@ -22,7 +22,6 @@ import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Map.Entry; import java.util.Map.Entry;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.DataType; import ghidra.program.model.data.DataType;
import ghidra.program.model.data.DataTypeManager; import ghidra.program.model.data.DataTypeManager;
import ghidra.program.model.lang.*; import ghidra.program.model.lang.*;
@ -41,6 +40,7 @@ import ghidra.xml.*;
*/ */
public class MultiSlotAssign extends AssignAction { public class MultiSlotAssign extends AssignAction {
private StorageClass resourceType; // Resource list from which to consume 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 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 consumeMostSig; // True if resources are consumed starting with most significant bytes
private boolean enforceAlignment; // True if register resources are discarded to match alignment 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) { protected MultiSlotAssign(ParamListStandard res) {
super(res); super(res);
isBigEndian = res.isBigEndian();
resourceType = StorageClass.GENERAL; // Join general purpose registers resourceType = StorageClass.GENERAL; // Join general purpose registers
consumeFromStack = !(res instanceof ParamListStandardOut); // Spill into stack by default consumeFromStack = !(res instanceof ParamListStandardOut); // Spill into stack by default
consumeMostSig = false; consumeMostSig = false;
@ -81,7 +82,7 @@ public class MultiSlotAssign extends AssignAction {
justifyRight = false; justifyRight = false;
adjacentEntries = true; adjacentEntries = true;
allowBackfill = false; allowBackfill = false;
if (res.getEntry(0).isBigEndian()) { if (isBigEndian) {
consumeMostSig = true; consumeMostSig = true;
justifyRight = true; justifyRight = true;
} }
@ -92,6 +93,7 @@ public class MultiSlotAssign extends AssignAction {
boolean justRight, boolean backfill, ParamListStandard res) boolean justRight, boolean backfill, ParamListStandard res)
throws InvalidInputException { throws InvalidInputException {
super(res); super(res);
isBigEndian = res.isBigEndian();
resourceType = store; resourceType = store;
consumeFromStack = stack; consumeFromStack = stack;
consumeMostSig = mostSig; consumeMostSig = mostSig;
@ -240,20 +242,8 @@ public class MultiSlotAssign extends AssignAction {
// Floating-point register holding extended lower precision value // Floating-point register holding extended lower precision value
onePieceJoin = true; // Treat as "join" of full size register 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 { else {
int end = pieces.size() - 1; justifyPieces(pieces, -sizeLeft, isBigEndian, consumeMostSig, justifyRight);
Varnode vn = pieces.get(end);
int sz = vn.getSize() + sizeLeft;
vn = new Varnode(vn.getAddress(), sz);
pieces.set(end, vn);
} }
} }
System.arraycopy(tmpStatus, 0, status, 0, tmpStatus.length); // Commit resource usage for all the pieces 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 @Override
public void encode(Encoder encoder) throws IOException { public void encode(Encoder encoder) throws IOException {
encoder.openElement(ELEM_JOIN); encoder.openElement(ELEM_JOIN);
if (resource.getEntry(0).isBigEndian() != justifyRight) { if (resource.isBigEndian() != justifyRight) {
encoder.writeBool(ATTRIB_REVERSEJUSTIFY, true); encoder.writeBool(ATTRIB_REVERSEJUSTIFY, true);
} }
if (resource.getEntry(0).isBigEndian() != consumeMostSig) { if (resource.isBigEndian() != consumeMostSig) {
encoder.writeBool(ATTRIB_REVERSESIGNIF, true); encoder.writeBool(ATTRIB_REVERSESIGNIF, true);
} }
if (resourceType != StorageClass.GENERAL) { if (resourceType != StorageClass.GENERAL) {

View file

@ -22,7 +22,6 @@ import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Map.Entry; import java.util.Map.Entry;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.DataType; import ghidra.program.model.data.DataType;
import ghidra.program.model.data.DataTypeManager; import ghidra.program.model.data.DataTypeManager;
import ghidra.program.model.lang.*; import ghidra.program.model.lang.*;
@ -42,6 +41,7 @@ import ghidra.xml.*;
public class MultiSlotDualAssign extends AssignAction { public class MultiSlotDualAssign extends AssignAction {
private StorageClass baseType; // Resource list from which to consume general tiles private StorageClass baseType; // Resource list from which to consume general tiles
private StorageClass altType; // Resource list from which to consume alternate 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 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 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 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) { protected MultiSlotDualAssign(ParamListStandard res) {
super(res); super(res);
isBigEndian = res.isBigEndian();
baseType = StorageClass.GENERAL; // Tile from general purpose registers baseType = StorageClass.GENERAL; // Tile from general purpose registers
altType = StorageClass.FLOAT; // Use specialized registers for floating-point components altType = StorageClass.FLOAT; // Use specialized registers for floating-point components
consumeFromStack = false; consumeFromStack = false;
consumeMostSig = false; consumeMostSig = false;
justifyRight = false; justifyRight = false;
if (res.getEntry(0).isBigEndian()) { if (isBigEndian) {
consumeMostSig = true; consumeMostSig = true;
justifyRight = true; justifyRight = true;
} }
@ -180,6 +181,7 @@ public class MultiSlotDualAssign extends AssignAction {
boolean mostSig, boolean justRight, boolean fillAlt, ParamListStandard res) boolean mostSig, boolean justRight, boolean fillAlt, ParamListStandard res)
throws InvalidInputException { throws InvalidInputException {
super(res); super(res);
isBigEndian = res.isBigEndian();
baseType = baseStore; baseType = baseStore;
altType = altStore; altType = altStore;
consumeFromStack = stack; consumeFromStack = stack;
@ -294,21 +296,7 @@ public class MultiSlotDualAssign extends AssignAction {
pieces.add(vn); pieces.add(vn);
} }
if (sizeLeft < 0) { // Have odd data-type size if (sizeLeft < 0) { // Have odd data-type size
if (justifyRight) { justifyPieces(pieces, -sizeLeft, isBigEndian, consumeMostSig, 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);
}
} }
System.arraycopy(tmpStatus, 0, status, 0, tmpStatus.length); // Commit resource usage for all the pieces System.arraycopy(tmpStatus, 0, status, 0, tmpStatus.length); // Commit resource usage for all the pieces
res.type = dt; res.type = dt;
@ -319,10 +307,10 @@ public class MultiSlotDualAssign extends AssignAction {
@Override @Override
public void encode(Encoder encoder) throws IOException { public void encode(Encoder encoder) throws IOException {
encoder.openElement(ELEM_JOIN_DUAL_CLASS); encoder.openElement(ELEM_JOIN_DUAL_CLASS);
if (resource.getEntry(0).isBigEndian() != justifyRight) { if (resource.isBigEndian() != justifyRight) {
encoder.writeBool(ATTRIB_REVERSEJUSTIFY, true); encoder.writeBool(ATTRIB_REVERSEJUSTIFY, true);
} }
if (resource.getEntry(0).isBigEndian() != consumeMostSig) { if (resource.isBigEndian() != consumeMostSig) {
encoder.writeBool(ATTRIB_REVERSESIGNIF, true); encoder.writeBool(ATTRIB_REVERSESIGNIF, true);
} }
if (baseType != StorageClass.GENERAL) { if (baseType != StorageClass.GENERAL) {

View file

@ -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");
}
}

View file

@ -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);
}
}