mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 01:39:21 +02:00
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:
parent
eb7dbaa04f
commit
f6495e4146
13 changed files with 280 additions and 89 deletions
|
@ -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
|
||||
|
|
|
@ -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<activeinput.getNumTrials();++i) {
|
||||
|
@ -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
|
||||
}
|
||||
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();
|
||||
|
|
|
@ -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<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
|
||||
const ParamEntry *getStackEntry(void) const; ///< Get the stack entry
|
||||
uint4 assignAddressFallback(type_class resource,Datatype *tp,bool matchExact,vector<int4> &status,
|
||||
|
|
|
@ -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<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)
|
||||
|
||||
{
|
||||
|
@ -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)
|
||||
|
|
|
@ -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<VarnodeData> &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<const ParamEntry *> baseTiles; ///< General registers to be joined
|
||||
vector<const ParamEntry *> altTiles; ///< Alternate registers to be joined
|
||||
|
|
|
@ -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"));
|
||||
|
|
|
@ -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 *")));
|
||||
|
|
|
@ -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<ParameterPieces> res, boolean addAutoParams) {
|
||||
|
|
|
@ -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<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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue