Merge remote-tracking branch 'origin/GP-3842_PointerToArray'

(Closes #5591)
This commit is contained in:
Ryan Kurtz 2023-09-21 07:29:27 -04:00
commit e47d57fb21
8 changed files with 187 additions and 44 deletions

View File

@ -53,6 +53,7 @@ src/decompile/datatests/pointercmp.xml||GHIDRA||||END|
src/decompile/datatests/pointerrel.xml||GHIDRA||||END|
src/decompile/datatests/pointersub.xml||GHIDRA||||END|
src/decompile/datatests/promotecompare.xml||GHIDRA||||END|
src/decompile/datatests/ptrtoarray.xml||GHIDRA||||END|
src/decompile/datatests/readvolatile.xml||GHIDRA||||END|
src/decompile/datatests/retstruct.xml||GHIDRA||||END|
src/decompile/datatests/sbyte.xml||GHIDRA||||END|

View File

@ -2325,34 +2325,43 @@ void ActionSetCasts::checkPointerIssues(PcodeOp *op,Varnode *vn,Funcdata &data)
}
}
/// \brief Test if the given cast conflict can be resolved by passing to the first structure field
/// \brief Test if the given cast conflict can be resolved by passing to the first structure/array field
///
/// Test if the given Varnode data-type is a pointer to a structure and if interpreting
/// Test if the current data-type is a pointer to a structure and if interpreting
/// the data-type as a pointer to the structure's first field will get it to match the
/// desired data-type.
/// \param vn is the given Varnode
/// \param op is the PcodeOp reading the Varnode
/// \param ct is the desired data-type
/// required data-type.
/// \param reqtype is the required data-type
/// \param curtype is the current data-type
/// \param castStrategy is used to determine if the data-types are compatible
/// \return \b true if a pointer to the first field makes sense
bool ActionSetCasts::testStructOffset0(Varnode *vn,PcodeOp *op,Datatype *ct,CastStrategy *castStrategy)
bool ActionSetCasts::testStructOffset0(Datatype *reqtype,Datatype *curtype,CastStrategy *castStrategy)
{
if (ct->getMetatype() != TYPE_PTR) return false;
Datatype *highType = vn->getHighTypeReadFacing(op);
if (highType->getMetatype() != TYPE_PTR) return false;
Datatype *highPtrTo = ((TypePointer *)highType)->getPtrTo();
if (highPtrTo->getMetatype() != TYPE_STRUCT) return false;
TypeStruct *highStruct = (TypeStruct *)highPtrTo;
if (highStruct->numDepend() == 0) return false;
vector<TypeField>::const_iterator iter = highStruct->beginField();
if ((*iter).offset != 0) return false;
Datatype *reqtype = ((TypePointer *)ct)->getPtrTo();
Datatype *curtype = (*iter).type;
if (reqtype->getMetatype() == TYPE_ARRAY)
reqtype = ((TypeArray *)reqtype)->getBase();
if (curtype->getMetatype() == TYPE_ARRAY)
curtype = ((TypeArray *)curtype)->getBase();
if (curtype->getMetatype() != TYPE_PTR) return false;
Datatype *highPtrTo = ((TypePointer *)curtype)->getPtrTo();
if (highPtrTo->getMetatype() == TYPE_STRUCT) {
TypeStruct *highStruct = (TypeStruct *)highPtrTo;
if (highStruct->numDepend() == 0) return false;
vector<TypeField>::const_iterator iter = highStruct->beginField();
if ((*iter).offset != 0) return false;
reqtype = ((TypePointer *)reqtype)->getPtrTo();
curtype = (*iter).type;
if (reqtype->getMetatype() == TYPE_ARRAY)
reqtype = ((TypeArray *)reqtype)->getBase();
if (curtype->getMetatype() == TYPE_ARRAY)
curtype = ((TypeArray *)curtype)->getBase();
}
else if (highPtrTo->getMetatype() == TYPE_ARRAY) {
TypeArray *highArray = (TypeArray *)highPtrTo;
reqtype = ((TypePointer *)reqtype)->getPtrTo();
curtype = highArray->getBase();
}
else {
return false;
}
if (reqtype->getMetatype() == TYPE_VOID) {
return false; // Don't induce PTRSUB for "void *"
}
return (castStrategy->castStandard(reqtype, curtype, true, true) == (Datatype *)0);
}
@ -2519,22 +2528,31 @@ int4 ActionSetCasts::castOutput(PcodeOp *op,Funcdata &data,CastStrategy *castStr
}
}
}
OpCode opc = CPUI_CAST;
if (!force) {
outct = outHighResolve; // Type of result
ct = castStrategy->castStandard(outct,tokenct,false,true);
if (ct == (Datatype *)0) return 0;
if (outct->getMetatype() == TYPE_PTR && testStructOffset0(outct, tokenct, castStrategy)) {
opc = CPUI_PTRSUB;
}
else {
ct = castStrategy->castStandard(outct,tokenct,false,true);
if (ct == (Datatype *)0) return 0;
}
}
// Generate the cast op
vn = data.newUnique(outvn->getSize());
vn->updateType(tokenct,false,false);
vn->setImplied();
newop = data.newOp(1,op->getAddr());
newop = data.newOp((opc != CPUI_CAST) ? 2 : 1,op->getAddr());
#ifdef CPUI_STATISTICS
data.getArch()->stats->countCast();
#endif
data.opSetOpcode(newop,CPUI_CAST);
data.opSetOpcode(newop,opc);
data.opSetOutput(newop,outvn);
data.opSetInput(newop,vn,0);
if (opc != CPUI_CAST) {
data.opSetInput(newop,data.newConstant(4, 0),1);
}
data.opSetOutput(op,vn);
data.opInsertAfter(newop,op); // Cast comes AFTER this operation
if (tokenct->needsResolution())
@ -2612,7 +2630,7 @@ int4 ActionSetCasts::castInput(PcodeOp *op,int4 slot,Funcdata &data,CastStrategy
if (vn->getType() == ct)
return 1;
}
else if (testStructOffset0(vn, op, ct, castStrategy)) {
else if (ct->getMetatype() == TYPE_PTR && testStructOffset0(ct, vn->getHighTypeReadFacing(op), castStrategy)) {
// Insert a PTRSUB(vn,#0) instead of a CAST
newop = insertPtrsubZero(op, slot, ct, data);
if (vn->getHigh()->getType()->needsResolution())

View File

@ -319,7 +319,7 @@ public:
/// immediately.
class ActionSetCasts : public Action {
static void checkPointerIssues(PcodeOp *op,Varnode *vn,Funcdata &data);
static bool testStructOffset0(Varnode *vn,PcodeOp *op,Datatype *ct,CastStrategy *castStrategy);
static bool testStructOffset0(Datatype *reqtype,Datatype *curtype,CastStrategy *castStrategy);
static bool tryResolutionAdjustment(PcodeOp *op,int4 slot,Funcdata &data);
static bool isOpIdentical(Datatype *ct1,Datatype *ct2);
static int4 resolveUnion(PcodeOp *op,int4 slot,Funcdata &data);

View File

@ -368,6 +368,55 @@ bool PrintC::checkArrayDeref(const Varnode *vn) const
return true;
}
/// Check that the output data-type is a pointer to an array and then that
/// the second data-type is a pointer to the element type (of the array).
/// If this holds and the input variable represents a symbol with an \e array data-type,
/// return \b true.
/// \return \b true if the CAST can be rendered as '&'
bool PrintC::checkAddressOfCast(const PcodeOp *op) const
{
Datatype *dt0 = op->getOut()->getHighTypeDefFacing();
const Varnode *vnin = op->getIn(0);
Datatype *dt1 = vnin->getHighTypeReadFacing(op);
if (dt0->getMetatype() != TYPE_PTR || dt1->getMetatype() != TYPE_PTR)
return false;
const Datatype *base0 = ((const TypePointer *)dt0)->getPtrTo();
const Datatype *base1 = ((const TypePointer *)dt1)->getPtrTo();
if (base0->getMetatype() != TYPE_ARRAY)
return false;
int4 arraySize = base0->getSize();
base0 = ((const TypeArray *)base0)->getBase();
while(base0->getTypedef() != (Datatype *)0)
base0 = base0->getTypedef();
while(base1->getTypedef() != (Datatype *)0)
base1 = base1->getTypedef();
if (base0 != base1)
return false;
Datatype *symbolType = (Datatype *)0;
if (vnin->getSymbolEntry() != (SymbolEntry *)0 && vnin->getHigh()->getSymbolOffset() == -1) {
symbolType = vnin->getSymbolEntry()->getSymbol()->getType();
}
else if (vnin->isWritten()) {
const PcodeOp *ptrsub = vnin->getDef();
if (ptrsub->code() == CPUI_PTRSUB) {
Datatype *rootType = ptrsub->getIn(0)->getHighTypeReadFacing(ptrsub);
if (rootType->getMetatype() == TYPE_PTR) {
rootType = ((TypePointer *)rootType)->getPtrTo();
int8 off = ptrsub->getIn(1)->getOffset();
symbolType = rootType->getSubType(off, &off);
if (off != 0)
return false;
}
}
}
if (symbolType == (Datatype *)0)
return false;
if (symbolType->getMetatype() != TYPE_ARRAY || symbolType->getSize() != arraySize)
return false;
return true;
}
/// This is used for expression that require functional syntax, where the name of the
/// function is the name of the operator. The inputs to the p-code op form the roots
/// of the comma separated list of \e parameters within the syntax.
@ -399,9 +448,17 @@ void PrintC::opFunc(const PcodeOp *op)
void PrintC::opTypeCast(const PcodeOp *op)
{
Datatype *dt = op->getOut()->getHighTypeDefFacing();
if (dt->isPointerToArray()) {
if (checkAddressOfCast(op)) {
pushOp(&addressof,op);
pushVn(op->getIn(0),op,mods);
return;
}
}
if (!option_nocasts) {
pushOp(&typecast,op);
pushType(op->getOut()->getHighTypeDefFacing());
pushType(dt);
}
pushVn(op->getIn(0),op,mods);
}
@ -798,13 +855,6 @@ void PrintC::opPtradd(const PcodeOp *op)
{
bool printval = isSet(print_load_value|print_store_value);
uint4 m = mods & ~(print_load_value|print_store_value);
if (!printval) {
TypePointer *tp = (TypePointer *)op->getIn(0)->getHighTypeReadFacing(op);
if (tp->getMetatype() == TYPE_PTR) {
if (tp->getPtrTo()->getMetatype() == TYPE_ARRAY)
printval = true;
}
}
if (printval) // Use array notation if we need value
pushOp(&subscript,op);
else // just a '+'
@ -820,8 +870,15 @@ static bool isValueFlexible(const Varnode *vn)
{
if ((vn->isImplied())&&(vn->isWritten())) {
const PcodeOp *def = vn->getDef();
if (def->code() == CPUI_PTRSUB) return true;
if (def->code() == CPUI_PTRADD) return true;
OpCode opc = def->code();
if (opc == CPUI_COPY) {
const Varnode *invn = def->getIn(0);
if (!invn->isImplied() || !invn->isWritten())
return false;
opc = invn->getDef()->code();
}
if (opc == CPUI_PTRSUB) return true;
if (opc == CPUI_PTRADD) return true;
}
return false;
}
@ -1020,12 +1077,11 @@ void PrintC::opPtrsub(const PcodeOp *op)
// and this PTRSUB(*,0) represents changing
// to treating it as a pointer to its element type
if (!valueon) {
if (flex) { // EMIT ( )
// (*&struct->arrayfield)[i]
// becomes struct->arrayfield[i]
// Even though there is no valueon, the PTRSUB still acts as a dereference
if (flex) { // EMIT ( )
if (ptrel != (TypePointerRel *)0)
pushTypePointerRel(op);
pushVn(in0,op,m);
pushVn(in0,op,m | print_load_value); // Absorb dereference into in0's defining op
}
else { // EMIT *( )
pushOp(&dereference,op);
@ -1035,11 +1091,12 @@ void PrintC::opPtrsub(const PcodeOp *op)
}
}
else {
// We need to show two dereferences here: one for the valueon and one for the PTRSUB
if (flex) { // EMIT ( )[0]
pushOp(&subscript,op);
if (ptrel != (TypePointerRel *)0)
pushTypePointerRel(op);
pushVn(in0,op,m);
pushVn(in0,op,m | print_load_value); // Absorb one dereference into in0's defining op
push_integer(0,4,false,syntax,(Varnode *)0,op);
}
else { // EMIT (* )[0]

View File

@ -172,6 +172,7 @@ protected:
virtual bool doEmitWideCharPrefix(void) const;
bool checkArrayDeref(const Varnode *vn) const; ///< Determine whether a LOAD/STORE expression requires pointer '*' syntax
bool checkAddressOfCast(const PcodeOp *op) const; ///< Check if CAST can be printed as an '&'
void emitStructDefinition(const TypeStruct *ct); ///< Emit the definition of a \e structure data-type
void emitEnumDefinition(const TypeEnum *ct); ///< Emit the definition of an \e enumeration data-type
void emitPrototypeOutput(const FuncProto *proto,const Funcdata *fd); ///< Emit the output data-type of a function prototype

View File

@ -899,6 +899,9 @@ void TypePointer::calcSubmeta(void)
else if (ptrtoMeta == TYPE_UNION) {
submeta = SUB_PTR_STRUCT;
}
else if (ptrtoMeta == TYPE_ARRAY) {
flags |= pointer_to_array;
}
if (ptrto->needsResolution() && ptrtoMeta != TYPE_PTR)
flags |= needs_resolution; // Inherit needs_resolution, but only if not a pointer
}

View File

@ -156,7 +156,8 @@ protected:
type_incomplete = 0x400, ///< Set if \b this (recursive) data-type has not been fully defined yet
needs_resolution = 0x800, ///< Datatype (union, pointer to union) needs resolution before propagation
force_format = 0x7000, ///< 3-bits encoding display format, 0=none, 1=hex, 2=dec, 3=oct, 4=bin, 5=char
truncate_bigendian = 0x8000 ///< Pointer can be truncated and is big endian
truncate_bigendian = 0x8000, ///< Pointer can be truncated and is big endian
pointer_to_array = 0x10000 ///< Data-type is a pointer to an array
};
friend class TypeFactory;
friend struct DatatypeCompare;
@ -193,6 +194,7 @@ public:
bool isVariableLength(void) const { return ((flags&variable_length)!=0); } ///< Is \b this a variable length structure
bool hasSameVariableBase(const Datatype *ct) const; ///< Are these the same variable length data-type
bool isOpaqueString(void) const { return ((flags&opaque_string)!=0); } ///< Is \b this an opaquely encoded string
bool isPointerToArray(void) const { return ((flags&pointer_to_array)!=0); } ///< Is \b this a pointer to an array
bool isPointerRel(void) const { return ((flags & is_ptrrel)!=0); } ///< Is \b this a TypePointerRel
bool isFormalPointerRel(void) const { return (flags & (is_ptrrel | has_stripped))==is_ptrrel; } ///< Is \b this a non-ephemeral TypePointerRel
bool hasStripped(void) const { return (flags & has_stripped)!=0; } ///< Return \b true if \b this has a stripped form

View File

@ -0,0 +1,61 @@
<decompilertest>
<!--
Functions manipulating explicit pointer-to-array data-types.
-->
<binaryimage arch="x86:LE:64:default:gcc">
<bytechunk space="ram" offset="0x10123e" readonly="true">
f30f
1efa554889e54883ec1048897df88975
f4488b45f84883c0404889c7e846ffff
ff488b45f84883e8804889c7e87dffff
ff837df40a7508488145f80002000048
8b45f84889c7e81cffffff488b45f848
89c7e857ffffff488b45f8c9c3
</bytechunk>
<bytechunk space="ram" offset="0x10129d" readonly="true">
f30f1e
fa554889e54883ec70897d9c64488b04
2528000000488945f831c0488d45b048
8905522d0000488d45a44889c7e8b7fe
ffff488d45a84883c0044889c7e8b6fe
ffff488d3d372d0000e89bfeffff9048
8b45f86448330425280000007405e86d
fdffffc9c3
</bytechunk>
<symbol space="ram" offset="0x10123e" name="ptrToArray"/>
<symbol space="ram" offset="0x10129d" name="passPtrToArray"/>
</binaryimage>
<script>
<com>map addr r0x104018 int4 (*paiGlob)[16]</com>
<com>map addr r0x104020 float4 globarray[1]</com>
<com>map fun r0x101189 floatarray</com>
<com>map fun r0x101198 intarray</com>
<com>map fun r0x1011a7 display</com>
<com>map fun r0x1011ee displayLow</com>
<com>parse line extern void floatarray(float4 (*a)[1]);</com>
<com>parse line extern void intarray(int4 (*a)[1]);</com>
<com>parse line extern void display(int4 (*ptr)[16]);</com>
<com>parse line extern void displayLow(int4 *ptr);</com>
<com>parse line struct mystruct { int4 a; int4 b[1]; };</com>
<com>lo fu ptrToArray</com>
<com>dec</com>
<com>print C</com>
<com>lo fu passPtrToArray</com>
<com>map addr s0xffffffffffffff9c float4 a[1]</com>
<com>map addr s0xffffffffffffffa0 mystruct myval</com>
<com>map addr s0xffffffffffffffa8 int4 c[16]</com>
<com>dec</com>
<com>print C</com>
<com>quit</com>
</script>
<stringmatch name="Pointer to array #1" min="1" max="1">display\(param_1 \+ 1\);</stringmatch>
<stringmatch name="Pointer to array #2" min="1" max="1">displayLow\(param_1\[2\]\);</stringmatch>
<stringmatch name="Pointer to array #3" min="1" max="1">paiStack_10 = param_1 \+ 8;</stringmatch>
<stringmatch name="Pointer to array #4" min="1" max="1">display\(paiStack_10\);</stringmatch>
<stringmatch name="Pointer to array #5" min="1" max="1">displayLow\(\*paiStack_10\);</stringmatch>
<stringmatch name="Pointer to array #6" min="1" max="1">return paiStack_10;</stringmatch>
<stringmatch name="Pointer to array #7" min="1" max="1">paiGlob = &amp;c;</stringmatch>
<stringmatch name="Pointer to array #8" min="1" max="1">floatarray\(&amp;a\);</stringmatch>
<stringmatch name="Pointer to array #9" min="1" max="1">intarray\(&amp;myval\.b\);</stringmatch>
<stringmatch name="Pointer to array #10" min="1" max="1">floatarray\(&amp;globarray\);</stringmatch>
</decompilertest>