mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-11-24 13:11:47 +00:00
GP-2470 Support for partial enums
This commit is contained in:
parent
d5f4d3b9bc
commit
55a026b3ba
@ -25,6 +25,7 @@ src/decompile/datatests/divopt.xml||GHIDRA||||END|
|
|||||||
src/decompile/datatests/doublemove.xml||GHIDRA||||END|
|
src/decompile/datatests/doublemove.xml||GHIDRA||||END|
|
||||||
src/decompile/datatests/dupptr.xml||GHIDRA||||END|
|
src/decompile/datatests/dupptr.xml||GHIDRA||||END|
|
||||||
src/decompile/datatests/elseif.xml||GHIDRA||||END|
|
src/decompile/datatests/elseif.xml||GHIDRA||||END|
|
||||||
|
src/decompile/datatests/enum.xml||GHIDRA||||END|
|
||||||
src/decompile/datatests/floatcast.xml||GHIDRA||||END|
|
src/decompile/datatests/floatcast.xml||GHIDRA||||END|
|
||||||
src/decompile/datatests/floatconv.xml||GHIDRA||||END|
|
src/decompile/datatests/floatconv.xml||GHIDRA||||END|
|
||||||
src/decompile/datatests/floatprint.xml||GHIDRA||||END|
|
src/decompile/datatests/floatprint.xml||GHIDRA||||END|
|
||||||
|
@ -5600,6 +5600,7 @@ void ActionDatabase::universalAction(Architecture *conf)
|
|||||||
actcleanup->addRule( new Rule2Comp2Sub("cleanup") );
|
actcleanup->addRule( new Rule2Comp2Sub("cleanup") );
|
||||||
actcleanup->addRule( new RuleSubRight("cleanup") );
|
actcleanup->addRule( new RuleSubRight("cleanup") );
|
||||||
actcleanup->addRule( new RuleFloatSignCleanup("cleanup") );
|
actcleanup->addRule( new RuleFloatSignCleanup("cleanup") );
|
||||||
|
actcleanup->addRule( new RuleExpandLoad("cleanup") );
|
||||||
actcleanup->addRule( new RulePtrsubCharConstant("cleanup") );
|
actcleanup->addRule( new RulePtrsubCharConstant("cleanup") );
|
||||||
actcleanup->addRule( new RuleExtensionPush("cleanup") );
|
actcleanup->addRule( new RuleExtensionPush("cleanup") );
|
||||||
actcleanup->addRule( new RulePieceStructure("cleanup") );
|
actcleanup->addRule( new RulePieceStructure("cleanup") );
|
||||||
|
@ -3112,7 +3112,7 @@ int grammarerror(const char *str)
|
|||||||
Datatype *parse_type(istream &s,string &name,Architecture *glb)
|
Datatype *parse_type(istream &s,string &name,Architecture *glb)
|
||||||
|
|
||||||
{
|
{
|
||||||
CParse parser(glb,1000);
|
CParse parser(glb,4096);
|
||||||
|
|
||||||
if (!parser.parseStream(s,CParse::doc_parameter_declaration))
|
if (!parser.parseStream(s,CParse::doc_parameter_declaration))
|
||||||
throw ParseError(parser.getError());
|
throw ParseError(parser.getError());
|
||||||
@ -3131,7 +3131,7 @@ Datatype *parse_type(istream &s,string &name,Architecture *glb)
|
|||||||
void parse_protopieces(PrototypePieces &pieces,
|
void parse_protopieces(PrototypePieces &pieces,
|
||||||
istream &s,Architecture *glb)
|
istream &s,Architecture *glb)
|
||||||
{
|
{
|
||||||
CParse parser(glb,1000);
|
CParse parser(glb,4096);
|
||||||
|
|
||||||
if (!parser.parseStream(s,CParse::doc_declaration))
|
if (!parser.parseStream(s,CParse::doc_declaration))
|
||||||
throw ParseError(parser.getError());
|
throw ParseError(parser.getError());
|
||||||
@ -3151,7 +3151,7 @@ void parse_protopieces(PrototypePieces &pieces,
|
|||||||
void parse_C(Architecture *glb,istream &s)
|
void parse_C(Architecture *glb,istream &s)
|
||||||
|
|
||||||
{ // Load type data straight into datastructures
|
{ // Load type data straight into datastructures
|
||||||
CParse parser(glb,1000);
|
CParse parser(glb,4096);
|
||||||
|
|
||||||
if (!parser.parseStream(s,CParse::doc_declaration))
|
if (!parser.parseStream(s,CParse::doc_declaration))
|
||||||
throw ParseError(parser.getError());
|
throw ParseError(parser.getError());
|
||||||
|
@ -1366,7 +1366,7 @@ int grammarerror(const char *str)
|
|||||||
Datatype *parse_type(istream &s,string &name,Architecture *glb)
|
Datatype *parse_type(istream &s,string &name,Architecture *glb)
|
||||||
|
|
||||||
{
|
{
|
||||||
CParse parser(glb,1000);
|
CParse parser(glb,4096);
|
||||||
|
|
||||||
if (!parser.parseStream(s,CParse::doc_parameter_declaration))
|
if (!parser.parseStream(s,CParse::doc_parameter_declaration))
|
||||||
throw ParseError(parser.getError());
|
throw ParseError(parser.getError());
|
||||||
@ -1385,7 +1385,7 @@ Datatype *parse_type(istream &s,string &name,Architecture *glb)
|
|||||||
void parse_protopieces(PrototypePieces &pieces,
|
void parse_protopieces(PrototypePieces &pieces,
|
||||||
istream &s,Architecture *glb)
|
istream &s,Architecture *glb)
|
||||||
{
|
{
|
||||||
CParse parser(glb,1000);
|
CParse parser(glb,4096);
|
||||||
|
|
||||||
if (!parser.parseStream(s,CParse::doc_declaration))
|
if (!parser.parseStream(s,CParse::doc_declaration))
|
||||||
throw ParseError(parser.getError());
|
throw ParseError(parser.getError());
|
||||||
@ -1405,7 +1405,7 @@ void parse_protopieces(PrototypePieces &pieces,
|
|||||||
void parse_C(Architecture *glb,istream &s)
|
void parse_C(Architecture *glb,istream &s)
|
||||||
|
|
||||||
{ // Load type data straight into datastructures
|
{ // Load type data straight into datastructures
|
||||||
CParse parser(glb,1000);
|
CParse parser(glb,4096);
|
||||||
|
|
||||||
if (!parser.parseStream(s,CParse::doc_declaration))
|
if (!parser.parseStream(s,CParse::doc_declaration))
|
||||||
throw ParseError(parser.getError());
|
throw ParseError(parser.getError());
|
||||||
|
@ -959,7 +959,7 @@ void PrintC::opPtrsub(const PcodeOp *op)
|
|||||||
if (ct->getMetatype() == TYPE_STRUCT || ct->getMetatype() == TYPE_UNION) {
|
if (ct->getMetatype() == TYPE_STRUCT || ct->getMetatype() == TYPE_UNION) {
|
||||||
int8 suboff = (int4)in1const; // How far into container
|
int8 suboff = (int4)in1const; // How far into container
|
||||||
if (ptrel != (TypePointerRel *)0) {
|
if (ptrel != (TypePointerRel *)0) {
|
||||||
suboff += ptrel->getPointerOffset();
|
suboff += ptrel->getAddressOffset();
|
||||||
suboff &= calc_mask(ptype->getSize());
|
suboff &= calc_mask(ptype->getSize());
|
||||||
if (suboff == 0) {
|
if (suboff == 0) {
|
||||||
// Special case where we do not print a field
|
// Special case where we do not print a field
|
||||||
@ -1666,22 +1666,23 @@ void PrintC::pushCharConstant(uintb val,const Datatype *ct,tagtype tag,const Var
|
|||||||
void PrintC::pushEnumConstant(uintb val,const TypeEnum *ct,tagtype tag,
|
void PrintC::pushEnumConstant(uintb val,const TypeEnum *ct,tagtype tag,
|
||||||
const Varnode *vn,const PcodeOp *op)
|
const Varnode *vn,const PcodeOp *op)
|
||||||
{
|
{
|
||||||
vector<string> valnames;
|
TypeEnum::Representation rep;
|
||||||
|
|
||||||
bool complement = ct->getMatches(val,valnames);
|
ct->getMatches(val,rep);
|
||||||
if (valnames.size() > 0) {
|
if (rep.matchname.size() > 0) {
|
||||||
if (complement)
|
if (rep.shiftAmount != 0)
|
||||||
|
pushOp(&shift_right,op);
|
||||||
|
if (rep.complement)
|
||||||
pushOp(&bitwise_not,op);
|
pushOp(&bitwise_not,op);
|
||||||
for(int4 i=valnames.size()-1;i>0;--i)
|
for(int4 i=rep.matchname.size()-1;i>0;--i)
|
||||||
pushOp(&enum_cat,op);
|
pushOp(&enum_cat,op);
|
||||||
for(int4 i=0;i<valnames.size();++i)
|
for(int4 i=0;i<rep.matchname.size();++i)
|
||||||
pushAtom(Atom(valnames[i],tag,EmitMarkup::const_color,op,vn,val));
|
pushAtom(Atom(rep.matchname[i],tag,EmitMarkup::const_color,op,vn,val));
|
||||||
|
if (rep.shiftAmount != 0)
|
||||||
|
push_integer(rep.shiftAmount,4,false,tag,vn,op);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
push_integer(val,ct->getSize(),false,tag,vn,op);
|
push_integer(val,ct->getSize(),false,tag,vn,op);
|
||||||
// ostringstream s;
|
|
||||||
// s << "BAD_ENUM(0x" << hex << val << ")";
|
|
||||||
// pushAtom(Atom(s.str(),vartoken,EmitMarkup::const_color,op,vn));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1793,8 +1794,11 @@ void PrintC::pushConstant(uintb val,const Datatype *ct,tagtype tag,
|
|||||||
case TYPE_SPACEBASE:
|
case TYPE_SPACEBASE:
|
||||||
case TYPE_CODE:
|
case TYPE_CODE:
|
||||||
case TYPE_ARRAY:
|
case TYPE_ARRAY:
|
||||||
|
case TYPE_ENUM_INT:
|
||||||
|
case TYPE_ENUM_UINT:
|
||||||
case TYPE_STRUCT:
|
case TYPE_STRUCT:
|
||||||
case TYPE_UNION:
|
case TYPE_UNION:
|
||||||
|
case TYPE_PARTIALENUM:
|
||||||
case TYPE_PARTIALSTRUCT:
|
case TYPE_PARTIALSTRUCT:
|
||||||
case TYPE_PARTIALUNION:
|
case TYPE_PARTIALUNION:
|
||||||
break;
|
break;
|
||||||
|
@ -15,7 +15,6 @@
|
|||||||
*/
|
*/
|
||||||
#include "ruleaction.hh"
|
#include "ruleaction.hh"
|
||||||
#include "coreaction.hh"
|
#include "coreaction.hh"
|
||||||
#include "subflow.hh"
|
|
||||||
#include "rangeutil.hh"
|
#include "rangeutil.hh"
|
||||||
#include "multiprecision.hh"
|
#include "multiprecision.hh"
|
||||||
|
|
||||||
@ -5679,7 +5678,7 @@ void AddTreeState::clear(void)
|
|||||||
nonmultsum = 0;
|
nonmultsum = 0;
|
||||||
biggestNonMultCoeff = 0;
|
biggestNonMultCoeff = 0;
|
||||||
if (pRelType != (const TypePointerRel *)0) {
|
if (pRelType != (const TypePointerRel *)0) {
|
||||||
nonmultsum = ((TypePointerRel *)ct)->getPointerOffset();
|
nonmultsum = ((TypePointerRel *)ct)->getAddressOffset();
|
||||||
nonmultsum &= ptrmask;
|
nonmultsum &= ptrmask;
|
||||||
}
|
}
|
||||||
multiple.clear();
|
multiple.clear();
|
||||||
@ -5733,7 +5732,7 @@ AddTreeState::AddTreeState(Funcdata &d,PcodeOp *op,int4 slot)
|
|||||||
if (ct->isFormalPointerRel()) {
|
if (ct->isFormalPointerRel()) {
|
||||||
pRelType = (const TypePointerRel *)ct;
|
pRelType = (const TypePointerRel *)ct;
|
||||||
baseType = pRelType->getParent();
|
baseType = pRelType->getParent();
|
||||||
nonmultsum = pRelType->getPointerOffset();
|
nonmultsum = pRelType->getAddressOffset();
|
||||||
nonmultsum &= ptrmask;
|
nonmultsum &= ptrmask;
|
||||||
}
|
}
|
||||||
if (baseType->isVariableLength())
|
if (baseType->isVariableLength())
|
||||||
@ -6012,7 +6011,7 @@ void AddTreeState::calcSubtype(void)
|
|||||||
extra = AddrSpace::byteToAddressInt(extra, ct->getWordSize()); // Convert back to address units
|
extra = AddrSpace::byteToAddressInt(extra, ct->getWordSize()); // Convert back to address units
|
||||||
offset = (offset - extra) & ptrmask;
|
offset = (offset - extra) & ptrmask;
|
||||||
correct = (correct - extra) & ptrmask;
|
correct = (correct - extra) & ptrmask;
|
||||||
if (pRelType != (TypePointerRel *)0 && offset == pRelType->getPointerOffset()) {
|
if (pRelType != (TypePointerRel *)0 && offset == pRelType->getAddressOffset()) {
|
||||||
// offset falls within basic ptrto
|
// offset falls within basic ptrto
|
||||||
if (!pRelType->evaluateThruParent(0)) { // If we are not representing offset 0 through parent
|
if (!pRelType->evaluateThruParent(0)) { // If we are not representing offset 0 through parent
|
||||||
valid = false; // Use basic (alternate) form
|
valid = false; // Use basic (alternate) form
|
||||||
@ -6031,7 +6030,7 @@ void AddTreeState::calcSubtype(void)
|
|||||||
valid = false; // There is substructure we don't know about
|
valid = false; // There is substructure we don't know about
|
||||||
}
|
}
|
||||||
if (pRelType != (const TypePointerRel *)0) {
|
if (pRelType != (const TypePointerRel *)0) {
|
||||||
int4 ptrOff = ((TypePointerRel *)ct)->getPointerOffset();
|
int4 ptrOff = ((TypePointerRel *)ct)->getAddressOffset();
|
||||||
offset = (offset - ptrOff) & ptrmask;
|
offset = (offset - ptrOff) & ptrmask;
|
||||||
correct = (correct - ptrOff) & ptrmask;
|
correct = (correct - ptrOff) & ptrmask;
|
||||||
}
|
}
|
||||||
@ -6398,8 +6397,7 @@ int4 RuleStructOffset0::applyOp(PcodeOp *op,Funcdata &data)
|
|||||||
baseType = ptRel->getParent();
|
baseType = ptRel->getParent();
|
||||||
if (baseType->getMetatype() != TYPE_STRUCT)
|
if (baseType->getMetatype() != TYPE_STRUCT)
|
||||||
return 0;
|
return 0;
|
||||||
int8 iOff = ptRel->getPointerOffset();
|
int8 iOff = ptRel->getByteOffset();
|
||||||
iOff = AddrSpace::addressToByteInt(iOff, ptRel->getWordSize());
|
|
||||||
if (iOff >= baseType->getSize())
|
if (iOff >= baseType->getSize())
|
||||||
return 0;
|
return 0;
|
||||||
offset = iOff;
|
offset = iOff;
|
||||||
@ -6864,7 +6862,6 @@ int4 RuleAddUnsigned::applyOp(PcodeOp *op,Funcdata &data)
|
|||||||
Datatype *dt = constvn->getTypeReadFacing(op);
|
Datatype *dt = constvn->getTypeReadFacing(op);
|
||||||
if (dt->getMetatype() != TYPE_UINT) return 0;
|
if (dt->getMetatype() != TYPE_UINT) return 0;
|
||||||
if (dt->isCharPrint()) return 0; // Only change integer forms
|
if (dt->isCharPrint()) return 0; // Only change integer forms
|
||||||
if (dt->isEnumType()) return 0;
|
|
||||||
uintb val = constvn->getOffset();
|
uintb val = constvn->getOffset();
|
||||||
uintb mask = calc_mask(constvn->getSize());
|
uintb mask = calc_mask(constvn->getSize());
|
||||||
int4 sa = constvn->getSize() * 6; // 1/4 less than full bitsize
|
int4 sa = constvn->getSize() * 6; // 1/4 less than full bitsize
|
||||||
@ -6877,8 +6874,14 @@ int4 RuleAddUnsigned::applyOp(PcodeOp *op,Funcdata &data)
|
|||||||
return 0; // Dont transform a named equate
|
return 0; // Dont transform a named equate
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
uintb negatedVal = (-val) & mask;
|
||||||
|
if (dt->isEnumType()) {
|
||||||
|
TypeEnum *enumType = (TypeEnum *)dt;
|
||||||
|
if (!enumType->hasNamedValue(negatedVal) && enumType->hasNamedValue((~val)&mask))
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
data.opSetOpcode(op,CPUI_INT_SUB);
|
data.opSetOpcode(op,CPUI_INT_SUB);
|
||||||
Varnode *cvn = data.newConstant(constvn->getSize(), (-val) & mask);
|
Varnode *cvn = data.newConstant(constvn->getSize(), negatedVal);
|
||||||
cvn->copySymbol(constvn);
|
cvn->copySymbol(constvn);
|
||||||
data.opSetInput(op,cvn,1);
|
data.opSetInput(op,cvn,1);
|
||||||
return 1;
|
return 1;
|
||||||
@ -7369,86 +7372,6 @@ int4 RulePieceStructure::applyOp(PcodeOp *op,Funcdata &data)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \class RuleSplitCopy
|
|
||||||
/// \brief Split COPY ops based on TypePartialStruct
|
|
||||||
///
|
|
||||||
/// If more than one logical component of a structure or array is copied at once,
|
|
||||||
/// rewrite the COPY operator as multiple COPYs.
|
|
||||||
void RuleSplitCopy::getOpList(vector<uint4> &oplist) const
|
|
||||||
|
|
||||||
{
|
|
||||||
oplist.push_back(CPUI_COPY);
|
|
||||||
}
|
|
||||||
|
|
||||||
int4 RuleSplitCopy::applyOp(PcodeOp *op,Funcdata &data)
|
|
||||||
|
|
||||||
{
|
|
||||||
Datatype *inType = op->getIn(0)->getTypeReadFacing(op);
|
|
||||||
Datatype *outType = op->getOut()->getTypeDefFacing();
|
|
||||||
type_metatype metain = inType->getMetatype();
|
|
||||||
type_metatype metaout = outType->getMetatype();
|
|
||||||
if (metain != TYPE_PARTIALSTRUCT && metaout != TYPE_PARTIALSTRUCT &&
|
|
||||||
metain != TYPE_ARRAY && metaout != TYPE_ARRAY &&
|
|
||||||
metain != TYPE_STRUCT && metaout != TYPE_STRUCT)
|
|
||||||
return false;
|
|
||||||
SplitDatatype splitter(data);
|
|
||||||
if (splitter.splitCopy(op, inType, outType))
|
|
||||||
return 1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \class RuleSplitLoad
|
|
||||||
/// \brief Split LOAD ops based on TypePartialStruct
|
|
||||||
///
|
|
||||||
/// If more than one logical component of a structure or array is loaded at once,
|
|
||||||
/// rewrite the LOAD operator as multiple LOADs.
|
|
||||||
void RuleSplitLoad::getOpList(vector<uint4> &oplist) const
|
|
||||||
|
|
||||||
{
|
|
||||||
oplist.push_back(CPUI_LOAD);
|
|
||||||
}
|
|
||||||
|
|
||||||
int4 RuleSplitLoad::applyOp(PcodeOp *op,Funcdata &data)
|
|
||||||
|
|
||||||
{
|
|
||||||
Datatype *inType = SplitDatatype::getValueDatatype(op, op->getOut()->getSize(), data.getArch()->types);
|
|
||||||
if (inType == (Datatype *)0)
|
|
||||||
return 0;
|
|
||||||
type_metatype metain = inType->getMetatype();
|
|
||||||
if (metain != TYPE_STRUCT && metain != TYPE_ARRAY && metain != TYPE_PARTIALSTRUCT)
|
|
||||||
return 0;
|
|
||||||
SplitDatatype splitter(data);
|
|
||||||
if (splitter.splitLoad(op, inType))
|
|
||||||
return 1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \class RuleSplitStore
|
|
||||||
/// \brief Split STORE ops based on TypePartialStruct
|
|
||||||
///
|
|
||||||
/// If more than one logical component of a structure or array is stored at once,
|
|
||||||
/// rewrite the STORE operator as multiple STOREs.
|
|
||||||
void RuleSplitStore::getOpList(vector<uint4> &oplist) const
|
|
||||||
|
|
||||||
{
|
|
||||||
oplist.push_back(CPUI_STORE);
|
|
||||||
}
|
|
||||||
|
|
||||||
int4 RuleSplitStore::applyOp(PcodeOp *op,Funcdata &data)
|
|
||||||
|
|
||||||
{
|
|
||||||
Datatype *outType = SplitDatatype::getValueDatatype(op, op->getIn(2)->getSize(), data.getArch()->types);
|
|
||||||
if (outType == (Datatype *)0)
|
|
||||||
return 0;
|
|
||||||
type_metatype metain = outType->getMetatype();
|
|
||||||
if (metain != TYPE_STRUCT && metain != TYPE_ARRAY && metain != TYPE_PARTIALSTRUCT)
|
|
||||||
return 0;
|
|
||||||
SplitDatatype splitter(data);
|
|
||||||
if (splitter.splitStore(op, outType))
|
|
||||||
return 1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \class RuleSubNormal
|
/// \class RuleSubNormal
|
||||||
/// \brief Pull-back SUBPIECE through INT_RIGHT and INT_SRIGHT
|
/// \brief Pull-back SUBPIECE through INT_RIGHT and INT_SRIGHT
|
||||||
///
|
///
|
||||||
@ -8779,138 +8702,6 @@ int4 RuleSegment::applyOp(PcodeOp *op,Funcdata &data)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \class RuleSubvarAnd
|
|
||||||
/// \brief Perform SubVariableFlow analysis triggered by INT_AND
|
|
||||||
void RuleSubvarAnd::getOpList(vector<uint4> &oplist) const
|
|
||||||
|
|
||||||
{
|
|
||||||
oplist.push_back(CPUI_INT_AND);
|
|
||||||
}
|
|
||||||
|
|
||||||
int4 RuleSubvarAnd::applyOp(PcodeOp *op,Funcdata &data)
|
|
||||||
|
|
||||||
{
|
|
||||||
if (!op->getIn(1)->isConstant()) return 0;
|
|
||||||
Varnode *vn = op->getIn(0);
|
|
||||||
Varnode *outvn = op->getOut();
|
|
||||||
// if (vn->getSize() != 1) return 0; // Only for bitsize variables
|
|
||||||
if (outvn->getConsume() != op->getIn(1)->getOffset()) return 0;
|
|
||||||
if ((outvn->getConsume() & 1)==0) return 0;
|
|
||||||
uintb cmask;
|
|
||||||
if (outvn->getConsume() == (uintb)1)
|
|
||||||
cmask = (uintb)1;
|
|
||||||
else {
|
|
||||||
cmask = calc_mask(vn->getSize());
|
|
||||||
cmask >>=8;
|
|
||||||
while(cmask != 0) {
|
|
||||||
if (cmask == outvn->getConsume()) break;
|
|
||||||
cmask >>=8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (cmask == 0) return 0;
|
|
||||||
// if (vn->getConsume() == 0) return 0;
|
|
||||||
// if ((vn->getConsume() & 0xff)==0xff) return 0;
|
|
||||||
// if (op->getIn(1)->getOffset() != (uintb)1) return 0;
|
|
||||||
if (op->getOut()->hasNoDescend()) return 0;
|
|
||||||
SubvariableFlow subflow(&data,vn,cmask,false,false,false);
|
|
||||||
if (!subflow.doTrace()) return 0;
|
|
||||||
subflow.doReplacement();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \class RuleSubvarSubpiece
|
|
||||||
/// \brief Perform SubVariableFlow analysis triggered by SUBPIECE
|
|
||||||
void RuleSubvarSubpiece::getOpList(vector<uint4> &oplist) const
|
|
||||||
|
|
||||||
{
|
|
||||||
oplist.push_back(CPUI_SUBPIECE);
|
|
||||||
}
|
|
||||||
|
|
||||||
int4 RuleSubvarSubpiece::applyOp(PcodeOp *op,Funcdata &data)
|
|
||||||
|
|
||||||
{
|
|
||||||
Varnode *vn = op->getIn(0);
|
|
||||||
Varnode *outvn = op->getOut();
|
|
||||||
int4 flowsize = outvn->getSize();
|
|
||||||
uintb mask = calc_mask( flowsize );
|
|
||||||
mask <<= 8*((int4)op->getIn(1)->getOffset());
|
|
||||||
bool aggressive = outvn->isPtrFlow();
|
|
||||||
if (!aggressive) {
|
|
||||||
if ((vn->getConsume() & mask) != vn->getConsume()) return 0;
|
|
||||||
if (op->getOut()->hasNoDescend()) return 0;
|
|
||||||
}
|
|
||||||
bool big = false;
|
|
||||||
if (flowsize >= 8 && vn->isInput()) {
|
|
||||||
// Vector register inputs getting truncated to what actually gets used
|
|
||||||
// happens occasionally. We let SubvariableFlow deal with this special case
|
|
||||||
// to avoid overlapping inputs
|
|
||||||
// TODO: ActionLaneDivide should be handling this
|
|
||||||
if (vn->loneDescend() == op)
|
|
||||||
big = true;
|
|
||||||
}
|
|
||||||
SubvariableFlow subflow(&data,vn,mask,aggressive,false,big);
|
|
||||||
if (!subflow.doTrace()) return 0;
|
|
||||||
subflow.doReplacement();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \class RuleSplitFlow
|
|
||||||
/// \brief Try to detect and split artificially joined Varnodes
|
|
||||||
///
|
|
||||||
/// Look for SUBPIECE coming from a PIECE that has come through INDIRECTs and/or MULTIEQUAL
|
|
||||||
/// Then: check if the input to SUBPIECE can be viewed as two independent pieces
|
|
||||||
/// If so: split the pieces into independent data-flows
|
|
||||||
void RuleSplitFlow::getOpList(vector<uint4> &oplist) const
|
|
||||||
|
|
||||||
{
|
|
||||||
oplist.push_back(CPUI_SUBPIECE);
|
|
||||||
}
|
|
||||||
|
|
||||||
int4 RuleSplitFlow::applyOp(PcodeOp *op,Funcdata &data)
|
|
||||||
|
|
||||||
{
|
|
||||||
int4 loSize = (int4)op->getIn(1)->getOffset();
|
|
||||||
if (loSize == 0) // Make sure SUBPIECE doesn't take least significant part
|
|
||||||
return 0;
|
|
||||||
Varnode *vn = op->getIn(0);
|
|
||||||
if (!vn->isWritten())
|
|
||||||
return 0;
|
|
||||||
if (vn->isPrecisLo() || vn->isPrecisHi())
|
|
||||||
return 0;
|
|
||||||
if (op->getOut()->getSize() + loSize != vn->getSize())
|
|
||||||
return 0; // Make sure SUBPIECE is taking most significant part
|
|
||||||
PcodeOp *concatOp = (PcodeOp *)0;
|
|
||||||
PcodeOp *multiOp = vn->getDef();
|
|
||||||
while(multiOp->code() == CPUI_INDIRECT) { // PIECE may come through INDIRECT
|
|
||||||
Varnode *tmpvn = multiOp->getIn(0);
|
|
||||||
if (!tmpvn->isWritten()) return 0;
|
|
||||||
multiOp = tmpvn->getDef();
|
|
||||||
}
|
|
||||||
if (multiOp->code() == CPUI_PIECE) {
|
|
||||||
if (vn->getDef() != multiOp)
|
|
||||||
concatOp = multiOp;
|
|
||||||
}
|
|
||||||
else if (multiOp->code() == CPUI_MULTIEQUAL) { // Otherwise PIECE comes through MULTIEQUAL
|
|
||||||
for(int4 i=0;i<multiOp->numInput();++i) {
|
|
||||||
Varnode *invn = multiOp->getIn(i);
|
|
||||||
if (!invn->isWritten()) continue;
|
|
||||||
PcodeOp *tmpOp = invn->getDef();
|
|
||||||
if (tmpOp->code() == CPUI_PIECE) {
|
|
||||||
concatOp = tmpOp;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (concatOp == (PcodeOp *)0) // Didn't find the concatenate
|
|
||||||
return 0;
|
|
||||||
if (concatOp->getIn(1)->getSize() != loSize)
|
|
||||||
return 0;
|
|
||||||
SplitFlow splitFlow(&data,vn,loSize);
|
|
||||||
if (!splitFlow.doTrace()) return 0;
|
|
||||||
splitFlow.apply();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \class RulePtrFlow
|
/// \class RulePtrFlow
|
||||||
/// \brief Mark Varnode and PcodeOp objects that are carrying or operating on pointers
|
/// \brief Mark Varnode and PcodeOp objects that are carrying or operating on pointers
|
||||||
///
|
///
|
||||||
@ -9111,178 +8902,6 @@ int4 RulePtrFlow::applyOp(PcodeOp *op,Funcdata &data)
|
|||||||
return madeChange;
|
return madeChange;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \class RuleSubvarCompZero
|
|
||||||
/// \brief Perform SubvariableFlow analysis triggered by testing of a single bit
|
|
||||||
///
|
|
||||||
/// Given a comparison (INT_EQUAL or INT_NOTEEQUAL_ to a constant,
|
|
||||||
/// check that input has only 1 bit that can possibly be non-zero
|
|
||||||
/// and that the constant is testing this. This then triggers
|
|
||||||
/// the full SubvariableFlow analysis.
|
|
||||||
void RuleSubvarCompZero::getOpList(vector<uint4> &oplist) const
|
|
||||||
|
|
||||||
{
|
|
||||||
oplist.push_back(CPUI_INT_NOTEQUAL);
|
|
||||||
oplist.push_back(CPUI_INT_EQUAL);
|
|
||||||
}
|
|
||||||
|
|
||||||
int4 RuleSubvarCompZero::applyOp(PcodeOp *op,Funcdata &data)
|
|
||||||
|
|
||||||
{
|
|
||||||
if (!op->getIn(1)->isConstant()) return 0;
|
|
||||||
Varnode *vn = op->getIn(0);
|
|
||||||
uintb mask = vn->getNZMask();
|
|
||||||
int4 bitnum = leastsigbit_set(mask);
|
|
||||||
if (bitnum == -1) return 0;
|
|
||||||
if ((mask >> bitnum) != 1) return 0; // Check if only one bit active
|
|
||||||
|
|
||||||
// Check if the active bit is getting tested
|
|
||||||
if ((op->getIn(1)->getOffset()!=mask)&&
|
|
||||||
(op->getIn(1)->getOffset()!=0))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (op->getOut()->hasNoDescend()) return 0;
|
|
||||||
// We do a basic check that the stream from which it looks like
|
|
||||||
// the bit is getting pulled is not fully consumed
|
|
||||||
if (vn->isWritten()) {
|
|
||||||
PcodeOp *andop = vn->getDef();
|
|
||||||
if (andop->numInput()==0) return 0;
|
|
||||||
Varnode *vn0 = andop->getIn(0);
|
|
||||||
switch(andop->code()) {
|
|
||||||
case CPUI_INT_AND:
|
|
||||||
case CPUI_INT_OR:
|
|
||||||
case CPUI_INT_RIGHT:
|
|
||||||
{
|
|
||||||
if (vn0->isConstant()) return 0;
|
|
||||||
uintb mask0 = vn0->getConsume() & vn0->getNZMask();
|
|
||||||
uintb wholemask = calc_mask(vn0->getSize()) & mask0;
|
|
||||||
// We really need a popcnt here
|
|
||||||
// We want: if the number of bits that are both consumed
|
|
||||||
// and not known to be zero are "big" then don't continue
|
|
||||||
// because it doesn't look like a few bits getting manipulated
|
|
||||||
// within a status register
|
|
||||||
if ((wholemask & 0xff)==0xff) return 0;
|
|
||||||
if ((wholemask & 0xff00)==0xff00) return 0;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SubvariableFlow subflow(&data,vn,mask,false,false,false);
|
|
||||||
if (!subflow.doTrace()) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
subflow.doReplacement();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \class RuleSubvarShift
|
|
||||||
/// \brief Perform SubvariableFlow analysis triggered by INT_RIGHT
|
|
||||||
///
|
|
||||||
/// If the INT_RIGHT input has only 1 bit that can possibly be non-zero
|
|
||||||
/// and it is getting shifted into the least significant bit position,
|
|
||||||
/// trigger the full SubvariableFlow analysis.
|
|
||||||
void RuleSubvarShift::getOpList(vector<uint4> &oplist) const
|
|
||||||
|
|
||||||
{
|
|
||||||
oplist.push_back(CPUI_INT_RIGHT);
|
|
||||||
}
|
|
||||||
|
|
||||||
int4 RuleSubvarShift::applyOp(PcodeOp *op,Funcdata &data)
|
|
||||||
|
|
||||||
{
|
|
||||||
Varnode *vn = op->getIn(0);
|
|
||||||
if (vn->getSize() != 1) return 0;
|
|
||||||
if (!op->getIn(1)->isConstant()) return 0;
|
|
||||||
int4 sa = (int4)op->getIn(1)->getOffset();
|
|
||||||
uintb mask = vn->getNZMask();
|
|
||||||
if ((mask >> sa) != (uintb)1) return 0; // Pulling out a single bit
|
|
||||||
mask = (mask >> sa) << sa;
|
|
||||||
if (op->getOut()->hasNoDescend()) return 0;
|
|
||||||
|
|
||||||
SubvariableFlow subflow(&data,vn,mask,false,false,false);
|
|
||||||
if (!subflow.doTrace()) return 0;
|
|
||||||
subflow.doReplacement();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \class RuleSubvarZext
|
|
||||||
/// \brief Perform SubvariableFlow analysis triggered by INT_ZEXT
|
|
||||||
void RuleSubvarZext::getOpList(vector<uint4> &oplist) const
|
|
||||||
|
|
||||||
{
|
|
||||||
oplist.push_back(CPUI_INT_ZEXT);
|
|
||||||
}
|
|
||||||
|
|
||||||
int4 RuleSubvarZext::applyOp(PcodeOp *op,Funcdata &data)
|
|
||||||
|
|
||||||
{
|
|
||||||
Varnode *vn = op->getOut();
|
|
||||||
Varnode *invn = op->getIn(0);
|
|
||||||
uintb mask = calc_mask(invn->getSize());
|
|
||||||
|
|
||||||
SubvariableFlow subflow(&data,vn,mask,invn->isPtrFlow(),false,false);
|
|
||||||
if (!subflow.doTrace()) return 0;
|
|
||||||
subflow.doReplacement();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \class RuleSubvarSext
|
|
||||||
/// \brief Perform SubvariableFlow analysis triggered by INT_SEXT
|
|
||||||
void RuleSubvarSext::getOpList(vector<uint4> &oplist) const
|
|
||||||
|
|
||||||
{
|
|
||||||
oplist.push_back(CPUI_INT_SEXT);
|
|
||||||
}
|
|
||||||
|
|
||||||
int4 RuleSubvarSext::applyOp(PcodeOp *op,Funcdata &data)
|
|
||||||
|
|
||||||
{
|
|
||||||
Varnode *vn = op->getOut();
|
|
||||||
Varnode *invn = op->getIn(0);
|
|
||||||
uintb mask = calc_mask(invn->getSize());
|
|
||||||
|
|
||||||
SubvariableFlow subflow(&data,vn,mask,isaggressive,true,false);
|
|
||||||
if (!subflow.doTrace()) return 0;
|
|
||||||
subflow.doReplacement();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RuleSubvarSext::reset(Funcdata &data)
|
|
||||||
|
|
||||||
{
|
|
||||||
isaggressive = data.getArch()->aggressive_ext_trim;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \class RuleSubfloatConvert
|
|
||||||
/// \brief Perform SubfloatFlow analysis triggered by FLOAT_FLOAT2FLOAT
|
|
||||||
void RuleSubfloatConvert::getOpList(vector<uint4> &oplist) const
|
|
||||||
|
|
||||||
{
|
|
||||||
oplist.push_back(CPUI_FLOAT_FLOAT2FLOAT);
|
|
||||||
}
|
|
||||||
|
|
||||||
int4 RuleSubfloatConvert::applyOp(PcodeOp *op,Funcdata &data)
|
|
||||||
|
|
||||||
{
|
|
||||||
Varnode *invn = op->getIn(0);
|
|
||||||
Varnode *outvn = op->getOut();
|
|
||||||
int4 insize = invn->getSize();
|
|
||||||
int4 outsize = outvn->getSize();
|
|
||||||
if (outsize > insize) {
|
|
||||||
SubfloatFlow subflow(&data,outvn,insize);
|
|
||||||
if (!subflow.doTrace()) return 0;
|
|
||||||
subflow.apply();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
SubfloatFlow subflow(&data,invn,outsize);
|
|
||||||
if (!subflow.doTrace()) return 0;
|
|
||||||
subflow.apply();
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \class RuleNegateNegate
|
/// \class RuleNegateNegate
|
||||||
/// \brief Simplify INT_NEGATE chains: `~~V => V`
|
/// \brief Simplify INT_NEGATE chains: `~~V => V`
|
||||||
void RuleNegateNegate::getOpList(vector<uint4> &oplist) const
|
void RuleNegateNegate::getOpList(vector<uint4> &oplist) const
|
||||||
@ -10926,4 +10545,145 @@ int4 RuleOrCompare::applyOp(PcodeOp *op,Funcdata &data)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// \brief Check that all uses of given Varnode are of the form `(V & C) == D`
|
||||||
|
///
|
||||||
|
/// \param vn is the given Varnode
|
||||||
|
/// \return \b true if all uses match the INT_AND form
|
||||||
|
bool RuleExpandLoad::checkAndComparison(Varnode *vn)
|
||||||
|
|
||||||
|
{
|
||||||
|
list<PcodeOp *>::const_iterator iter;
|
||||||
|
for(iter=vn->beginDescend();iter!=vn->endDescend();++iter) {
|
||||||
|
PcodeOp *op = *iter;
|
||||||
|
if (op->code() != CPUI_INT_AND) return false;
|
||||||
|
if (!op->getIn(1)->isConstant()) return false;
|
||||||
|
PcodeOp *compOp = op->getOut()->loneDescend();
|
||||||
|
if (compOp == (PcodeOp *)0) return false;
|
||||||
|
OpCode opc = compOp->code();
|
||||||
|
if (opc != CPUI_INT_EQUAL && opc != CPUI_INT_NOTEQUAL) return false;
|
||||||
|
if (!compOp->getIn(1)->isConstant()) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \brief Expand the constants in the previously scanned forms: `(V & C) == D`
|
||||||
|
///
|
||||||
|
/// The method checkAndComparison() must have returned \b true for \b oldVn. Change the size and
|
||||||
|
/// data-type of all the constants in these expressions.
|
||||||
|
/// \param data is the function containing the expressions
|
||||||
|
/// \param oldVn is incoming variable in all the expressions
|
||||||
|
/// \param newVn is the new bigger variable
|
||||||
|
/// \param dt is the data-type to associate with the constants
|
||||||
|
/// \param offset is the number of least significant (zero) bytes to add to each constant
|
||||||
|
void RuleExpandLoad::modifyAndComparison(Funcdata &data,Varnode *oldVn,Varnode *newVn,Datatype *dt,int4 offset)
|
||||||
|
|
||||||
|
{
|
||||||
|
offset = 8*offset; // Convert to shift amount
|
||||||
|
list<PcodeOp *>::const_iterator iter = oldVn->beginDescend();
|
||||||
|
while(iter != oldVn->endDescend()) {
|
||||||
|
PcodeOp *andOp = *iter;
|
||||||
|
++iter; // Advance iterator before modifying op
|
||||||
|
PcodeOp *compOp = andOp->getOut()->loneDescend();
|
||||||
|
uintb newOff = andOp->getIn(1)->getOffset();
|
||||||
|
newOff <<= offset;
|
||||||
|
Varnode *vn = data.newConstant(dt->getSize(), newOff);
|
||||||
|
vn->updateType(dt, false, false);
|
||||||
|
data.opSetInput(andOp, newVn, 0);
|
||||||
|
data.opSetInput(andOp, vn, 1);
|
||||||
|
newOff = compOp->getIn(1)->getOffset();
|
||||||
|
newOff <<= offset;
|
||||||
|
vn = data.newConstant(dt->getSize(), newOff);
|
||||||
|
vn->updateType(dt, false, false);
|
||||||
|
data.opSetInput(compOp,vn,1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \class RuleExpandLoad
|
||||||
|
/// \brief Convert LOAD size to match pointer data-type
|
||||||
|
///
|
||||||
|
/// Change LOAD output to a larger size if used for INT_AND comparisons or if a truncation is natural
|
||||||
|
void RuleExpandLoad::getOpList(vector<uint4> &oplist) const
|
||||||
|
|
||||||
|
{
|
||||||
|
oplist.push_back(CPUI_LOAD);
|
||||||
|
}
|
||||||
|
|
||||||
|
int4 RuleExpandLoad::applyOp(PcodeOp *op,Funcdata &data)
|
||||||
|
|
||||||
|
{
|
||||||
|
Varnode *outVn = op->getOut();
|
||||||
|
int4 outSize = outVn->getSize();
|
||||||
|
Varnode *rootPtr = op->getIn(1);
|
||||||
|
PcodeOp *addOp = (PcodeOp *)0;
|
||||||
|
int4 offset = 0;
|
||||||
|
Datatype *elType;
|
||||||
|
if (rootPtr->isWritten()) {
|
||||||
|
PcodeOp *defOp = rootPtr->getDef();
|
||||||
|
if (defOp->code() == CPUI_INT_ADD && defOp->getIn(1)->isConstant()) {
|
||||||
|
addOp = defOp;
|
||||||
|
rootPtr = defOp->getIn(0);
|
||||||
|
offset = defOp->getIn(1)->getOffset();
|
||||||
|
if (offset > 16) return 0; // INT_ADD offset must be small
|
||||||
|
if (defOp->getOut()->loneDescend() == (PcodeOp *)0) return 0; // INT_ADD must be used only once
|
||||||
|
elType = rootPtr->getTypeReadFacing(defOp);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
elType = rootPtr->getTypeReadFacing(op);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
elType = rootPtr->getTypeReadFacing(op);
|
||||||
|
if (elType->getMetatype() != TYPE_PTR) return 0;
|
||||||
|
elType = ((TypePointer *)elType)->getPtrTo();
|
||||||
|
if (elType->getSize() <= outSize) return 0; // Pointer data-type must be bigger than LOAD
|
||||||
|
if (elType->getSize() < outSize + offset) return 0;
|
||||||
|
|
||||||
|
type_metatype meta = elType->getMetatype();
|
||||||
|
if (meta == TYPE_UNKNOWN) return 0;
|
||||||
|
bool addForm = checkAndComparison(outVn);
|
||||||
|
AddrSpace *spc = op->getIn(0)->getSpaceFromConst();
|
||||||
|
int4 lsbCut = 0;
|
||||||
|
if (addForm) {
|
||||||
|
if (spc->isBigEndian()) {
|
||||||
|
lsbCut = elType->getSize() - outSize - offset;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
lsbCut = offset;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Check for natural integer truncation
|
||||||
|
if (meta != TYPE_INT && meta != TYPE_UINT) return 0;
|
||||||
|
type_metatype outMeta = outVn->getTypeDefFacing()->getMetatype();
|
||||||
|
if (outMeta != TYPE_INT && outMeta != TYPE_UINT && outMeta != TYPE_UNKNOWN && outMeta != TYPE_BOOL)
|
||||||
|
return false;
|
||||||
|
// Check that LOAD is grabbing least significant bytes
|
||||||
|
if (spc->isBigEndian()) {
|
||||||
|
if (outSize + offset != elType->getSize()) return 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (offset != 0) return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Modify the LOAD
|
||||||
|
Varnode *newOut = data.newUnique(elType->getSize(), elType);
|
||||||
|
data.opSetOutput(op, newOut);
|
||||||
|
if (addOp != (PcodeOp *)0) {
|
||||||
|
data.opSetInput(op, rootPtr, 1);
|
||||||
|
data.opDestroy(addOp);
|
||||||
|
}
|
||||||
|
if (addForm) {
|
||||||
|
if (meta != TYPE_INT && meta != TYPE_UINT)
|
||||||
|
elType = data.getArch()->types->getBase(elType->getSize(), TYPE_UINT);
|
||||||
|
modifyAndComparison(data, outVn, newOut, elType, lsbCut);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
PcodeOp *subOp = data.newOp(2,op->getAddr());
|
||||||
|
data.opSetOpcode(subOp, CPUI_SUBPIECE);
|
||||||
|
data.opSetInput(subOp,newOut,0); // Truncate new bigger LOAD output
|
||||||
|
data.opSetInput(subOp, data.newConstant(4, 0), 1);
|
||||||
|
data.opSetOutput(subOp, outVn); // Original LOAD output is now defined by SUBPIECE
|
||||||
|
data.opInsertAfter(subOp, op);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
} // End namespace ghidra
|
} // End namespace ghidra
|
||||||
|
@ -1181,39 +1181,6 @@ public:
|
|||||||
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
||||||
};
|
};
|
||||||
|
|
||||||
class RuleSplitCopy : public Rule {
|
|
||||||
public:
|
|
||||||
RuleSplitCopy(const string &g) : Rule( g, 0, "splitcopy") {} ///< Constructor
|
|
||||||
virtual Rule *clone(const ActionGroupList &grouplist) const {
|
|
||||||
if (!grouplist.contains(getGroup())) return (Rule *)0;
|
|
||||||
return new RuleSplitCopy(getGroup());
|
|
||||||
}
|
|
||||||
virtual void getOpList(vector<uint4> &oplist) const;
|
|
||||||
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
|
||||||
};
|
|
||||||
|
|
||||||
class RuleSplitLoad : public Rule {
|
|
||||||
public:
|
|
||||||
RuleSplitLoad(const string &g) : Rule( g, 0, "splitload") {} ///< Constructor
|
|
||||||
virtual Rule *clone(const ActionGroupList &grouplist) const {
|
|
||||||
if (!grouplist.contains(getGroup())) return (Rule *)0;
|
|
||||||
return new RuleSplitLoad(getGroup());
|
|
||||||
}
|
|
||||||
virtual void getOpList(vector<uint4> &oplist) const;
|
|
||||||
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
|
||||||
};
|
|
||||||
|
|
||||||
class RuleSplitStore : public Rule {
|
|
||||||
public:
|
|
||||||
RuleSplitStore(const string &g) : Rule( g, 0, "splitstore") {} ///< Constructor
|
|
||||||
virtual Rule *clone(const ActionGroupList &grouplist) const {
|
|
||||||
if (!grouplist.contains(getGroup())) return (Rule *)0;
|
|
||||||
return new RuleSplitStore(getGroup());
|
|
||||||
}
|
|
||||||
virtual void getOpList(vector<uint4> &oplist) const;
|
|
||||||
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
|
||||||
};
|
|
||||||
|
|
||||||
class RuleSubNormal : public Rule {
|
class RuleSubNormal : public Rule {
|
||||||
public:
|
public:
|
||||||
RuleSubNormal(const string &g) : Rule( g, 0, "subnormal") {} ///< Constructor
|
RuleSubNormal(const string &g) : Rule( g, 0, "subnormal") {} ///< Constructor
|
||||||
@ -1398,39 +1365,6 @@ public:
|
|||||||
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
||||||
};
|
};
|
||||||
|
|
||||||
class RuleSubvarAnd : public Rule {
|
|
||||||
public:
|
|
||||||
RuleSubvarAnd(const string &g) : Rule( g, 0, "subvar_and") {} ///< Constructor
|
|
||||||
virtual Rule *clone(const ActionGroupList &grouplist) const {
|
|
||||||
if (!grouplist.contains(getGroup())) return (Rule *)0;
|
|
||||||
return new RuleSubvarAnd(getGroup());
|
|
||||||
}
|
|
||||||
virtual void getOpList(vector<uint4> &oplist) const;
|
|
||||||
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
|
||||||
};
|
|
||||||
|
|
||||||
class RuleSubvarSubpiece : public Rule {
|
|
||||||
public:
|
|
||||||
RuleSubvarSubpiece(const string &g) : Rule( g, 0, "subvar_subpiece") {} ///< Constructor
|
|
||||||
virtual Rule *clone(const ActionGroupList &grouplist) const {
|
|
||||||
if (!grouplist.contains(getGroup())) return (Rule *)0;
|
|
||||||
return new RuleSubvarSubpiece(getGroup());
|
|
||||||
}
|
|
||||||
virtual void getOpList(vector<uint4> &oplist) const;
|
|
||||||
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
|
||||||
};
|
|
||||||
|
|
||||||
class RuleSplitFlow : public Rule {
|
|
||||||
public:
|
|
||||||
RuleSplitFlow(const string &g) : Rule( g, 0, "splitflow") {} ///< Constructor
|
|
||||||
virtual Rule *clone(const ActionGroupList &grouplist) const {
|
|
||||||
if (!grouplist.contains(getGroup())) return (Rule *)0;
|
|
||||||
return new RuleSplitFlow(getGroup());
|
|
||||||
}
|
|
||||||
virtual void getOpList(vector<uint4> &oplist) const;
|
|
||||||
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
|
||||||
};
|
|
||||||
|
|
||||||
class RulePtrFlow : public Rule {
|
class RulePtrFlow : public Rule {
|
||||||
Architecture *glb; ///< The address space manager
|
Architecture *glb; ///< The address space manager
|
||||||
bool hasTruncations; ///< \b true if this architecture needs truncated pointers
|
bool hasTruncations; ///< \b true if this architecture needs truncated pointers
|
||||||
@ -1448,63 +1382,6 @@ public:
|
|||||||
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
||||||
};
|
};
|
||||||
|
|
||||||
class RuleSubvarCompZero : public Rule {
|
|
||||||
public:
|
|
||||||
RuleSubvarCompZero(const string &g) : Rule( g, 0, "subvar_compzero") {} ///< Constructor
|
|
||||||
virtual Rule *clone(const ActionGroupList &grouplist) const {
|
|
||||||
if (!grouplist.contains(getGroup())) return (Rule *)0;
|
|
||||||
return new RuleSubvarCompZero(getGroup());
|
|
||||||
}
|
|
||||||
virtual void getOpList(vector<uint4> &oplist) const;
|
|
||||||
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
|
||||||
};
|
|
||||||
|
|
||||||
class RuleSubvarShift : public Rule {
|
|
||||||
public:
|
|
||||||
RuleSubvarShift(const string &g) : Rule( g, 0, "subvar_shift") {} ///< Constructor
|
|
||||||
virtual Rule *clone(const ActionGroupList &grouplist) const {
|
|
||||||
if (!grouplist.contains(getGroup())) return (Rule *)0;
|
|
||||||
return new RuleSubvarShift(getGroup());
|
|
||||||
}
|
|
||||||
virtual void getOpList(vector<uint4> &oplist) const;
|
|
||||||
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
|
||||||
};
|
|
||||||
|
|
||||||
class RuleSubvarZext : public Rule {
|
|
||||||
public:
|
|
||||||
RuleSubvarZext(const string &g) : Rule( g, 0, "subvar_zext") {} ///< Constructor
|
|
||||||
virtual Rule *clone(const ActionGroupList &grouplist) const {
|
|
||||||
if (!grouplist.contains(getGroup())) return (Rule *)0;
|
|
||||||
return new RuleSubvarZext(getGroup());
|
|
||||||
}
|
|
||||||
virtual void getOpList(vector<uint4> &oplist) const;
|
|
||||||
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
|
||||||
};
|
|
||||||
|
|
||||||
class RuleSubvarSext : public Rule {
|
|
||||||
int4 isaggressive; ///< Is it guaranteed the root is a sub-variable needing to be trimmed
|
|
||||||
public:
|
|
||||||
RuleSubvarSext(const string &g) : Rule( g, 0, "subvar_sext") { isaggressive = false; } ///< Constructor
|
|
||||||
virtual Rule *clone(const ActionGroupList &grouplist) const {
|
|
||||||
if (!grouplist.contains(getGroup())) return (Rule *)0;
|
|
||||||
return new RuleSubvarSext(getGroup());
|
|
||||||
}
|
|
||||||
virtual void getOpList(vector<uint4> &oplist) const;
|
|
||||||
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
|
||||||
virtual void reset(Funcdata &data);
|
|
||||||
};
|
|
||||||
|
|
||||||
class RuleSubfloatConvert : public Rule {
|
|
||||||
public:
|
|
||||||
RuleSubfloatConvert(const string &g) : Rule( g, 0, "subfloat_convert") {} ///< Constructor
|
|
||||||
virtual Rule *clone(const ActionGroupList &grouplist) const {
|
|
||||||
if (!grouplist.contains(getGroup())) return (Rule *)0;
|
|
||||||
return new RuleSubfloatConvert(getGroup());
|
|
||||||
}
|
|
||||||
virtual void getOpList(vector<uint4> &oplist) const;
|
|
||||||
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
|
||||||
};
|
|
||||||
|
|
||||||
class RuleNegateNegate : public Rule {
|
class RuleNegateNegate : public Rule {
|
||||||
public:
|
public:
|
||||||
RuleNegateNegate(const string &g) : Rule( g, 0, "negatenegate") {} ///< Constructor
|
RuleNegateNegate(const string &g) : Rule( g, 0, "negatenegate") {} ///< Constructor
|
||||||
@ -1695,5 +1572,18 @@ public:
|
|||||||
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class RuleExpandLoad : public Rule {
|
||||||
|
static bool checkAndComparison(Varnode *vn);
|
||||||
|
static void modifyAndComparison(Funcdata &data,Varnode *oldVn,Varnode *newVn,Datatype *dt,int4 offset);
|
||||||
|
public:
|
||||||
|
RuleExpandLoad(const string &g) : Rule( g, 0, "expandload") {} ///< Constructor
|
||||||
|
virtual Rule *clone(const ActionGroupList &grouplist) const {
|
||||||
|
if (!grouplist.contains(getGroup())) return (Rule *)0;
|
||||||
|
return new RuleExpandLoad(getGroup());
|
||||||
|
}
|
||||||
|
virtual void getOpList(vector<uint4> &oplist) const;
|
||||||
|
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
||||||
|
};
|
||||||
|
|
||||||
} // End namespace ghidra
|
} // End namespace ghidra
|
||||||
#endif
|
#endif
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
#include "subflow.hh"
|
#include "subflow.hh"
|
||||||
|
#include "funcdata.hh"
|
||||||
|
|
||||||
namespace ghidra {
|
namespace ghidra {
|
||||||
|
|
||||||
@ -1506,6 +1507,204 @@ void SubvariableFlow::doReplacement(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RuleSubvarAnd::getOpList(vector<uint4> &oplist) const
|
||||||
|
|
||||||
|
{
|
||||||
|
oplist.push_back(CPUI_INT_AND);
|
||||||
|
}
|
||||||
|
|
||||||
|
int4 RuleSubvarAnd::applyOp(PcodeOp *op,Funcdata &data)
|
||||||
|
|
||||||
|
{
|
||||||
|
if (!op->getIn(1)->isConstant()) return 0;
|
||||||
|
Varnode *vn = op->getIn(0);
|
||||||
|
Varnode *outvn = op->getOut();
|
||||||
|
// if (vn->getSize() != 1) return 0; // Only for bitsize variables
|
||||||
|
if (outvn->getConsume() != op->getIn(1)->getOffset()) return 0;
|
||||||
|
if ((outvn->getConsume() & 1)==0) return 0;
|
||||||
|
uintb cmask;
|
||||||
|
if (outvn->getConsume() == (uintb)1)
|
||||||
|
cmask = (uintb)1;
|
||||||
|
else {
|
||||||
|
cmask = calc_mask(vn->getSize());
|
||||||
|
cmask >>=8;
|
||||||
|
while(cmask != 0) {
|
||||||
|
if (cmask == outvn->getConsume()) break;
|
||||||
|
cmask >>=8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (cmask == 0) return 0;
|
||||||
|
// if (vn->getConsume() == 0) return 0;
|
||||||
|
// if ((vn->getConsume() & 0xff)==0xff) return 0;
|
||||||
|
// if (op->getIn(1)->getOffset() != (uintb)1) return 0;
|
||||||
|
if (op->getOut()->hasNoDescend()) return 0;
|
||||||
|
SubvariableFlow subflow(&data,vn,cmask,false,false,false);
|
||||||
|
if (!subflow.doTrace()) return 0;
|
||||||
|
subflow.doReplacement();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RuleSubvarSubpiece::getOpList(vector<uint4> &oplist) const
|
||||||
|
|
||||||
|
{
|
||||||
|
oplist.push_back(CPUI_SUBPIECE);
|
||||||
|
}
|
||||||
|
|
||||||
|
int4 RuleSubvarSubpiece::applyOp(PcodeOp *op,Funcdata &data)
|
||||||
|
|
||||||
|
{
|
||||||
|
Varnode *vn = op->getIn(0);
|
||||||
|
Varnode *outvn = op->getOut();
|
||||||
|
int4 flowsize = outvn->getSize();
|
||||||
|
uintb mask = calc_mask( flowsize );
|
||||||
|
mask <<= 8*((int4)op->getIn(1)->getOffset());
|
||||||
|
bool aggressive = outvn->isPtrFlow();
|
||||||
|
if (!aggressive) {
|
||||||
|
if ((vn->getConsume() & mask) != vn->getConsume()) return 0;
|
||||||
|
if (op->getOut()->hasNoDescend()) return 0;
|
||||||
|
}
|
||||||
|
bool big = false;
|
||||||
|
if (flowsize >= 8 && vn->isInput()) {
|
||||||
|
// Vector register inputs getting truncated to what actually gets used
|
||||||
|
// happens occasionally. We let SubvariableFlow deal with this special case
|
||||||
|
// to avoid overlapping inputs
|
||||||
|
// TODO: ActionLaneDivide should be handling this
|
||||||
|
if (vn->loneDescend() == op)
|
||||||
|
big = true;
|
||||||
|
}
|
||||||
|
SubvariableFlow subflow(&data,vn,mask,aggressive,false,big);
|
||||||
|
if (!subflow.doTrace()) return 0;
|
||||||
|
subflow.doReplacement();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RuleSubvarCompZero::getOpList(vector<uint4> &oplist) const
|
||||||
|
|
||||||
|
{
|
||||||
|
oplist.push_back(CPUI_INT_NOTEQUAL);
|
||||||
|
oplist.push_back(CPUI_INT_EQUAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
int4 RuleSubvarCompZero::applyOp(PcodeOp *op,Funcdata &data)
|
||||||
|
|
||||||
|
{
|
||||||
|
if (!op->getIn(1)->isConstant()) return 0;
|
||||||
|
Varnode *vn = op->getIn(0);
|
||||||
|
uintb mask = vn->getNZMask();
|
||||||
|
int4 bitnum = leastsigbit_set(mask);
|
||||||
|
if (bitnum == -1) return 0;
|
||||||
|
if ((mask >> bitnum) != 1) return 0; // Check if only one bit active
|
||||||
|
|
||||||
|
// Check if the active bit is getting tested
|
||||||
|
if ((op->getIn(1)->getOffset()!=mask)&&
|
||||||
|
(op->getIn(1)->getOffset()!=0))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (op->getOut()->hasNoDescend()) return 0;
|
||||||
|
// We do a basic check that the stream from which it looks like
|
||||||
|
// the bit is getting pulled is not fully consumed
|
||||||
|
if (vn->isWritten()) {
|
||||||
|
PcodeOp *andop = vn->getDef();
|
||||||
|
if (andop->numInput()==0) return 0;
|
||||||
|
Varnode *vn0 = andop->getIn(0);
|
||||||
|
switch(andop->code()) {
|
||||||
|
case CPUI_INT_AND:
|
||||||
|
case CPUI_INT_OR:
|
||||||
|
case CPUI_INT_RIGHT:
|
||||||
|
{
|
||||||
|
if (vn0->isConstant()) return 0;
|
||||||
|
uintb mask0 = vn0->getConsume() & vn0->getNZMask();
|
||||||
|
uintb wholemask = calc_mask(vn0->getSize()) & mask0;
|
||||||
|
// We really need a popcnt here
|
||||||
|
// We want: if the number of bits that are both consumed
|
||||||
|
// and not known to be zero are "big" then don't continue
|
||||||
|
// because it doesn't look like a few bits getting manipulated
|
||||||
|
// within a status register
|
||||||
|
if ((wholemask & 0xff)==0xff) return 0;
|
||||||
|
if ((wholemask & 0xff00)==0xff00) return 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SubvariableFlow subflow(&data,vn,mask,false,false,false);
|
||||||
|
if (!subflow.doTrace()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
subflow.doReplacement();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RuleSubvarShift::getOpList(vector<uint4> &oplist) const
|
||||||
|
|
||||||
|
{
|
||||||
|
oplist.push_back(CPUI_INT_RIGHT);
|
||||||
|
}
|
||||||
|
|
||||||
|
int4 RuleSubvarShift::applyOp(PcodeOp *op,Funcdata &data)
|
||||||
|
|
||||||
|
{
|
||||||
|
Varnode *vn = op->getIn(0);
|
||||||
|
if (vn->getSize() != 1) return 0;
|
||||||
|
if (!op->getIn(1)->isConstant()) return 0;
|
||||||
|
int4 sa = (int4)op->getIn(1)->getOffset();
|
||||||
|
uintb mask = vn->getNZMask();
|
||||||
|
if ((mask >> sa) != (uintb)1) return 0; // Pulling out a single bit
|
||||||
|
mask = (mask >> sa) << sa;
|
||||||
|
if (op->getOut()->hasNoDescend()) return 0;
|
||||||
|
|
||||||
|
SubvariableFlow subflow(&data,vn,mask,false,false,false);
|
||||||
|
if (!subflow.doTrace()) return 0;
|
||||||
|
subflow.doReplacement();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RuleSubvarZext::getOpList(vector<uint4> &oplist) const
|
||||||
|
|
||||||
|
{
|
||||||
|
oplist.push_back(CPUI_INT_ZEXT);
|
||||||
|
}
|
||||||
|
|
||||||
|
int4 RuleSubvarZext::applyOp(PcodeOp *op,Funcdata &data)
|
||||||
|
|
||||||
|
{
|
||||||
|
Varnode *vn = op->getOut();
|
||||||
|
Varnode *invn = op->getIn(0);
|
||||||
|
uintb mask = calc_mask(invn->getSize());
|
||||||
|
|
||||||
|
SubvariableFlow subflow(&data,vn,mask,invn->isPtrFlow(),false,false);
|
||||||
|
if (!subflow.doTrace()) return 0;
|
||||||
|
subflow.doReplacement();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RuleSubvarSext::getOpList(vector<uint4> &oplist) const
|
||||||
|
|
||||||
|
{
|
||||||
|
oplist.push_back(CPUI_INT_SEXT);
|
||||||
|
}
|
||||||
|
|
||||||
|
int4 RuleSubvarSext::applyOp(PcodeOp *op,Funcdata &data)
|
||||||
|
|
||||||
|
{
|
||||||
|
Varnode *vn = op->getOut();
|
||||||
|
Varnode *invn = op->getIn(0);
|
||||||
|
uintb mask = calc_mask(invn->getSize());
|
||||||
|
|
||||||
|
SubvariableFlow subflow(&data,vn,mask,isaggressive,true,false);
|
||||||
|
if (!subflow.doTrace()) return 0;
|
||||||
|
subflow.doReplacement();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RuleSubvarSext::reset(Funcdata &data)
|
||||||
|
|
||||||
|
{
|
||||||
|
isaggressive = data.getArch()->aggressive_ext_trim;
|
||||||
|
}
|
||||||
|
|
||||||
/// \brief Find or build the placeholder objects for a Varnode that needs to be split
|
/// \brief Find or build the placeholder objects for a Varnode that needs to be split
|
||||||
///
|
///
|
||||||
/// Mark the Varnode so it doesn't get revisited.
|
/// Mark the Varnode so it doesn't get revisited.
|
||||||
@ -1797,6 +1996,57 @@ bool SplitFlow::doTrace(void)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RuleSplitFlow::getOpList(vector<uint4> &oplist) const
|
||||||
|
|
||||||
|
{
|
||||||
|
oplist.push_back(CPUI_SUBPIECE);
|
||||||
|
}
|
||||||
|
|
||||||
|
int4 RuleSplitFlow::applyOp(PcodeOp *op,Funcdata &data)
|
||||||
|
|
||||||
|
{
|
||||||
|
int4 loSize = (int4)op->getIn(1)->getOffset();
|
||||||
|
if (loSize == 0) // Make sure SUBPIECE doesn't take least significant part
|
||||||
|
return 0;
|
||||||
|
Varnode *vn = op->getIn(0);
|
||||||
|
if (!vn->isWritten())
|
||||||
|
return 0;
|
||||||
|
if (vn->isPrecisLo() || vn->isPrecisHi())
|
||||||
|
return 0;
|
||||||
|
if (op->getOut()->getSize() + loSize != vn->getSize())
|
||||||
|
return 0; // Make sure SUBPIECE is taking most significant part
|
||||||
|
PcodeOp *concatOp = (PcodeOp *)0;
|
||||||
|
PcodeOp *multiOp = vn->getDef();
|
||||||
|
while(multiOp->code() == CPUI_INDIRECT) { // PIECE may come through INDIRECT
|
||||||
|
Varnode *tmpvn = multiOp->getIn(0);
|
||||||
|
if (!tmpvn->isWritten()) return 0;
|
||||||
|
multiOp = tmpvn->getDef();
|
||||||
|
}
|
||||||
|
if (multiOp->code() == CPUI_PIECE) {
|
||||||
|
if (vn->getDef() != multiOp)
|
||||||
|
concatOp = multiOp;
|
||||||
|
}
|
||||||
|
else if (multiOp->code() == CPUI_MULTIEQUAL) { // Otherwise PIECE comes through MULTIEQUAL
|
||||||
|
for(int4 i=0;i<multiOp->numInput();++i) {
|
||||||
|
Varnode *invn = multiOp->getIn(i);
|
||||||
|
if (!invn->isWritten()) continue;
|
||||||
|
PcodeOp *tmpOp = invn->getDef();
|
||||||
|
if (tmpOp->code() == CPUI_PIECE) {
|
||||||
|
concatOp = tmpOp;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (concatOp == (PcodeOp *)0) // Didn't find the concatenate
|
||||||
|
return 0;
|
||||||
|
if (concatOp->getIn(1)->getSize() != loSize)
|
||||||
|
return 0;
|
||||||
|
SplitFlow splitFlow(&data,vn,loSize);
|
||||||
|
if (!splitFlow.doTrace()) return 0;
|
||||||
|
splitFlow.apply();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
/// If \b pointer Varnode is written by a COPY, INT_ADD, PTRSUB, or PTRADD from another pointer to a
|
/// If \b pointer Varnode is written by a COPY, INT_ADD, PTRSUB, or PTRADD from another pointer to a
|
||||||
/// - structure
|
/// - structure
|
||||||
/// - array OR
|
/// - array OR
|
||||||
@ -2628,8 +2878,7 @@ Datatype *SplitDatatype::getValueDatatype(PcodeOp *loadStore,int4 size,TypeFacto
|
|||||||
if (ptrType->isPointerRel()) {
|
if (ptrType->isPointerRel()) {
|
||||||
TypePointerRel *ptrRel = (TypePointerRel *)ptrType;
|
TypePointerRel *ptrRel = (TypePointerRel *)ptrType;
|
||||||
resType = ptrRel->getParent();
|
resType = ptrRel->getParent();
|
||||||
baseOffset = ptrRel->getPointerOffset();
|
baseOffset = ptrRel->getByteOffset();
|
||||||
baseOffset = AddrSpace::addressToByteInt(baseOffset, ptrRel->getWordSize());
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
resType = ((TypePointer *)ptrType)->getPtrTo();
|
resType = ((TypePointer *)ptrType)->getPtrTo();
|
||||||
@ -2649,6 +2898,71 @@ Datatype *SplitDatatype::getValueDatatype(PcodeOp *loadStore,int4 size,TypeFacto
|
|||||||
return (Datatype *)0;
|
return (Datatype *)0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RuleSplitCopy::getOpList(vector<uint4> &oplist) const
|
||||||
|
|
||||||
|
{
|
||||||
|
oplist.push_back(CPUI_COPY);
|
||||||
|
}
|
||||||
|
|
||||||
|
int4 RuleSplitCopy::applyOp(PcodeOp *op,Funcdata &data)
|
||||||
|
|
||||||
|
{
|
||||||
|
Datatype *inType = op->getIn(0)->getTypeReadFacing(op);
|
||||||
|
Datatype *outType = op->getOut()->getTypeDefFacing();
|
||||||
|
type_metatype metain = inType->getMetatype();
|
||||||
|
type_metatype metaout = outType->getMetatype();
|
||||||
|
if (metain != TYPE_PARTIALSTRUCT && metaout != TYPE_PARTIALSTRUCT &&
|
||||||
|
metain != TYPE_ARRAY && metaout != TYPE_ARRAY &&
|
||||||
|
metain != TYPE_STRUCT && metaout != TYPE_STRUCT)
|
||||||
|
return false;
|
||||||
|
SplitDatatype splitter(data);
|
||||||
|
if (splitter.splitCopy(op, inType, outType))
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RuleSplitLoad::getOpList(vector<uint4> &oplist) const
|
||||||
|
|
||||||
|
{
|
||||||
|
oplist.push_back(CPUI_LOAD);
|
||||||
|
}
|
||||||
|
|
||||||
|
int4 RuleSplitLoad::applyOp(PcodeOp *op,Funcdata &data)
|
||||||
|
|
||||||
|
{
|
||||||
|
Datatype *inType = SplitDatatype::getValueDatatype(op, op->getOut()->getSize(), data.getArch()->types);
|
||||||
|
if (inType == (Datatype *)0)
|
||||||
|
return 0;
|
||||||
|
type_metatype metain = inType->getMetatype();
|
||||||
|
if (metain != TYPE_STRUCT && metain != TYPE_ARRAY && metain != TYPE_PARTIALSTRUCT)
|
||||||
|
return 0;
|
||||||
|
SplitDatatype splitter(data);
|
||||||
|
if (splitter.splitLoad(op, inType))
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RuleSplitStore::getOpList(vector<uint4> &oplist) const
|
||||||
|
|
||||||
|
{
|
||||||
|
oplist.push_back(CPUI_STORE);
|
||||||
|
}
|
||||||
|
|
||||||
|
int4 RuleSplitStore::applyOp(PcodeOp *op,Funcdata &data)
|
||||||
|
|
||||||
|
{
|
||||||
|
Datatype *outType = SplitDatatype::getValueDatatype(op, op->getIn(2)->getSize(), data.getArch()->types);
|
||||||
|
if (outType == (Datatype *)0)
|
||||||
|
return 0;
|
||||||
|
type_metatype metain = outType->getMetatype();
|
||||||
|
if (metain != TYPE_STRUCT && metain != TYPE_ARRAY && metain != TYPE_PARTIALSTRUCT)
|
||||||
|
return 0;
|
||||||
|
SplitDatatype splitter(data);
|
||||||
|
if (splitter.splitStore(op, outType))
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/// This method distinguishes between a floating-point variable with \e full precision, where all the
|
/// This method distinguishes between a floating-point variable with \e full precision, where all the
|
||||||
/// storage can vary (or is unknown), versus a value that is extended from a floating-point variable with
|
/// storage can vary (or is unknown), versus a value that is extended from a floating-point variable with
|
||||||
/// smaller storage. Within the data-flow above the given Varnode, we search for the maximum
|
/// smaller storage. Within the data-flow above the given Varnode, we search for the maximum
|
||||||
@ -3062,6 +3376,32 @@ bool SubfloatFlow::doTrace(void)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RuleSubfloatConvert::getOpList(vector<uint4> &oplist) const
|
||||||
|
|
||||||
|
{
|
||||||
|
oplist.push_back(CPUI_FLOAT_FLOAT2FLOAT);
|
||||||
|
}
|
||||||
|
|
||||||
|
int4 RuleSubfloatConvert::applyOp(PcodeOp *op,Funcdata &data)
|
||||||
|
|
||||||
|
{
|
||||||
|
Varnode *invn = op->getIn(0);
|
||||||
|
Varnode *outvn = op->getOut();
|
||||||
|
int4 insize = invn->getSize();
|
||||||
|
int4 outsize = outvn->getSize();
|
||||||
|
if (outsize > insize) {
|
||||||
|
SubfloatFlow subflow(&data,outvn,insize);
|
||||||
|
if (!subflow.doTrace()) return 0;
|
||||||
|
subflow.apply();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
SubfloatFlow subflow(&data,invn,outsize);
|
||||||
|
if (!subflow.doTrace()) return 0;
|
||||||
|
subflow.apply();
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
/// \brief Find or build the placeholder objects for a Varnode that needs to be split into lanes
|
/// \brief Find or build the placeholder objects for a Varnode that needs to be split into lanes
|
||||||
///
|
///
|
||||||
/// The Varnode is split based on the given subset of the lane description.
|
/// The Varnode is split based on the given subset of the lane description.
|
||||||
|
@ -18,7 +18,8 @@
|
|||||||
#ifndef __SUBFLOW_HH__
|
#ifndef __SUBFLOW_HH__
|
||||||
#define __SUBFLOW_HH__
|
#define __SUBFLOW_HH__
|
||||||
|
|
||||||
#include "funcdata.hh"
|
#include "ruleaction.hh"
|
||||||
|
#include "transform.hh"
|
||||||
|
|
||||||
namespace ghidra {
|
namespace ghidra {
|
||||||
|
|
||||||
@ -128,6 +129,89 @@ public:
|
|||||||
void doReplacement(void); ///< Perform the discovered transform, making logical values explicit
|
void doReplacement(void); ///< Perform the discovered transform, making logical values explicit
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// \brief Perform SubVariableFlow analysis triggered by INT_AND
|
||||||
|
class RuleSubvarAnd : public Rule {
|
||||||
|
public:
|
||||||
|
RuleSubvarAnd(const string &g) : Rule( g, 0, "subvar_and") {} ///< Constructor
|
||||||
|
virtual Rule *clone(const ActionGroupList &grouplist) const {
|
||||||
|
if (!grouplist.contains(getGroup())) return (Rule *)0;
|
||||||
|
return new RuleSubvarAnd(getGroup());
|
||||||
|
}
|
||||||
|
virtual void getOpList(vector<uint4> &oplist) const;
|
||||||
|
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
||||||
|
};
|
||||||
|
|
||||||
|
/// \brief Perform SubVariableFlow analysis triggered by SUBPIECE
|
||||||
|
class RuleSubvarSubpiece : public Rule {
|
||||||
|
public:
|
||||||
|
RuleSubvarSubpiece(const string &g) : Rule( g, 0, "subvar_subpiece") {} ///< Constructor
|
||||||
|
virtual Rule *clone(const ActionGroupList &grouplist) const {
|
||||||
|
if (!grouplist.contains(getGroup())) return (Rule *)0;
|
||||||
|
return new RuleSubvarSubpiece(getGroup());
|
||||||
|
}
|
||||||
|
virtual void getOpList(vector<uint4> &oplist) const;
|
||||||
|
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
||||||
|
};
|
||||||
|
|
||||||
|
/// \brief Perform SubvariableFlow analysis triggered by testing of a single bit
|
||||||
|
///
|
||||||
|
/// Given a comparison (INT_EQUAL or INT_NOTEEQUAL_ to a constant,
|
||||||
|
/// check that input has only 1 bit that can possibly be non-zero
|
||||||
|
/// and that the constant is testing this. This then triggers
|
||||||
|
/// the full SubvariableFlow analysis.
|
||||||
|
class RuleSubvarCompZero : public Rule {
|
||||||
|
public:
|
||||||
|
RuleSubvarCompZero(const string &g) : Rule( g, 0, "subvar_compzero") {} ///< Constructor
|
||||||
|
virtual Rule *clone(const ActionGroupList &grouplist) const {
|
||||||
|
if (!grouplist.contains(getGroup())) return (Rule *)0;
|
||||||
|
return new RuleSubvarCompZero(getGroup());
|
||||||
|
}
|
||||||
|
virtual void getOpList(vector<uint4> &oplist) const;
|
||||||
|
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
||||||
|
};
|
||||||
|
|
||||||
|
/// \brief Perform SubvariableFlow analysis triggered by INT_RIGHT
|
||||||
|
///
|
||||||
|
/// If the INT_RIGHT input has only 1 bit that can possibly be non-zero
|
||||||
|
/// and it is getting shifted into the least significant bit position,
|
||||||
|
/// trigger the full SubvariableFlow analysis.
|
||||||
|
class RuleSubvarShift : public Rule {
|
||||||
|
public:
|
||||||
|
RuleSubvarShift(const string &g) : Rule( g, 0, "subvar_shift") {} ///< Constructor
|
||||||
|
virtual Rule *clone(const ActionGroupList &grouplist) const {
|
||||||
|
if (!grouplist.contains(getGroup())) return (Rule *)0;
|
||||||
|
return new RuleSubvarShift(getGroup());
|
||||||
|
}
|
||||||
|
virtual void getOpList(vector<uint4> &oplist) const;
|
||||||
|
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
||||||
|
};
|
||||||
|
|
||||||
|
/// \brief Perform SubvariableFlow analysis triggered by INT_ZEXT
|
||||||
|
class RuleSubvarZext : public Rule {
|
||||||
|
public:
|
||||||
|
RuleSubvarZext(const string &g) : Rule( g, 0, "subvar_zext") {} ///< Constructor
|
||||||
|
virtual Rule *clone(const ActionGroupList &grouplist) const {
|
||||||
|
if (!grouplist.contains(getGroup())) return (Rule *)0;
|
||||||
|
return new RuleSubvarZext(getGroup());
|
||||||
|
}
|
||||||
|
virtual void getOpList(vector<uint4> &oplist) const;
|
||||||
|
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
||||||
|
};
|
||||||
|
|
||||||
|
/// \brief Perform SubvariableFlow analysis triggered by INT_SEXT
|
||||||
|
class RuleSubvarSext : public Rule {
|
||||||
|
int4 isaggressive; ///< Is it guaranteed the root is a sub-variable needing to be trimmed
|
||||||
|
public:
|
||||||
|
RuleSubvarSext(const string &g) : Rule( g, 0, "subvar_sext") { isaggressive = false; } ///< Constructor
|
||||||
|
virtual Rule *clone(const ActionGroupList &grouplist) const {
|
||||||
|
if (!grouplist.contains(getGroup())) return (Rule *)0;
|
||||||
|
return new RuleSubvarSext(getGroup());
|
||||||
|
}
|
||||||
|
virtual void getOpList(vector<uint4> &oplist) const;
|
||||||
|
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
||||||
|
virtual void reset(Funcdata &data);
|
||||||
|
};
|
||||||
|
|
||||||
/// \brief Class for splitting up Varnodes that hold 2 logical variables
|
/// \brief Class for splitting up Varnodes that hold 2 logical variables
|
||||||
///
|
///
|
||||||
/// Starting from a \e root Varnode provided to the constructor, \b this class looks for data-flow
|
/// Starting from a \e root Varnode provided to the constructor, \b this class looks for data-flow
|
||||||
@ -147,6 +231,22 @@ public:
|
|||||||
bool doTrace(void); ///< Trace split through data-flow, constructing transform
|
bool doTrace(void); ///< Trace split through data-flow, constructing transform
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// \brief Try to detect and split artificially joined Varnodes
|
||||||
|
///
|
||||||
|
/// Look for SUBPIECE coming from a PIECE that has come through INDIRECTs and/or MULTIEQUAL
|
||||||
|
/// Then: check if the input to SUBPIECE can be viewed as two independent pieces
|
||||||
|
/// If so: split the pieces into independent data-flows
|
||||||
|
class RuleSplitFlow : public Rule {
|
||||||
|
public:
|
||||||
|
RuleSplitFlow(const string &g) : Rule( g, 0, "splitflow") {} ///< Constructor
|
||||||
|
virtual Rule *clone(const ActionGroupList &grouplist) const {
|
||||||
|
if (!grouplist.contains(getGroup())) return (Rule *)0;
|
||||||
|
return new RuleSplitFlow(getGroup());
|
||||||
|
}
|
||||||
|
virtual void getOpList(vector<uint4> &oplist) const;
|
||||||
|
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
||||||
|
};
|
||||||
|
|
||||||
/// \brief Split a p-code COPY, LOAD, or STORE op based on underlying composite data-type
|
/// \brief Split a p-code COPY, LOAD, or STORE op based on underlying composite data-type
|
||||||
///
|
///
|
||||||
/// During the cleanup phase, if a COPY, LOAD, or STORE occurs on a partial structure or array
|
/// During the cleanup phase, if a COPY, LOAD, or STORE occurs on a partial structure or array
|
||||||
@ -208,6 +308,51 @@ public:
|
|||||||
static Datatype *getValueDatatype(PcodeOp *loadStore,int4 size,TypeFactory *tlst);
|
static Datatype *getValueDatatype(PcodeOp *loadStore,int4 size,TypeFactory *tlst);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// \brief Split COPY ops based on TypePartialStruct
|
||||||
|
///
|
||||||
|
/// If more than one logical component of a structure or array is copied at once,
|
||||||
|
/// rewrite the COPY operator as multiple COPYs.
|
||||||
|
class RuleSplitCopy : public Rule {
|
||||||
|
public:
|
||||||
|
RuleSplitCopy(const string &g) : Rule( g, 0, "splitcopy") {} ///< Constructor
|
||||||
|
virtual Rule *clone(const ActionGroupList &grouplist) const {
|
||||||
|
if (!grouplist.contains(getGroup())) return (Rule *)0;
|
||||||
|
return new RuleSplitCopy(getGroup());
|
||||||
|
}
|
||||||
|
virtual void getOpList(vector<uint4> &oplist) const;
|
||||||
|
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
||||||
|
};
|
||||||
|
|
||||||
|
/// \brief Split LOAD ops based on TypePartialStruct
|
||||||
|
///
|
||||||
|
/// If more than one logical component of a structure or array is loaded at once,
|
||||||
|
/// rewrite the LOAD operator as multiple LOADs.
|
||||||
|
class RuleSplitLoad : public Rule {
|
||||||
|
public:
|
||||||
|
RuleSplitLoad(const string &g) : Rule( g, 0, "splitload") {} ///< Constructor
|
||||||
|
virtual Rule *clone(const ActionGroupList &grouplist) const {
|
||||||
|
if (!grouplist.contains(getGroup())) return (Rule *)0;
|
||||||
|
return new RuleSplitLoad(getGroup());
|
||||||
|
}
|
||||||
|
virtual void getOpList(vector<uint4> &oplist) const;
|
||||||
|
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
||||||
|
};
|
||||||
|
|
||||||
|
/// \brief Split STORE ops based on TypePartialStruct
|
||||||
|
///
|
||||||
|
/// If more than one logical component of a structure or array is stored at once,
|
||||||
|
/// rewrite the STORE operator as multiple STOREs.
|
||||||
|
class RuleSplitStore : public Rule {
|
||||||
|
public:
|
||||||
|
RuleSplitStore(const string &g) : Rule( g, 0, "splitstore") {} ///< Constructor
|
||||||
|
virtual Rule *clone(const ActionGroupList &grouplist) const {
|
||||||
|
if (!grouplist.contains(getGroup())) return (Rule *)0;
|
||||||
|
return new RuleSplitStore(getGroup());
|
||||||
|
}
|
||||||
|
virtual void getOpList(vector<uint4> &oplist) const;
|
||||||
|
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
||||||
|
};
|
||||||
|
|
||||||
/// \brief Class for tracing changes of precision in floating point variables
|
/// \brief Class for tracing changes of precision in floating point variables
|
||||||
///
|
///
|
||||||
/// It follows the flow of a logical lower precision value stored in higher precision locations
|
/// It follows the flow of a logical lower precision value stored in higher precision locations
|
||||||
@ -242,6 +387,18 @@ public:
|
|||||||
bool doTrace(void); ///< Trace logical value as far as possible
|
bool doTrace(void); ///< Trace logical value as far as possible
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// \brief Perform SubfloatFlow analysis triggered by FLOAT_FLOAT2FLOAT
|
||||||
|
class RuleSubfloatConvert : public Rule {
|
||||||
|
public:
|
||||||
|
RuleSubfloatConvert(const string &g) : Rule( g, 0, "subfloat_convert") {} ///< Constructor
|
||||||
|
virtual Rule *clone(const ActionGroupList &grouplist) const {
|
||||||
|
if (!grouplist.contains(getGroup())) return (Rule *)0;
|
||||||
|
return new RuleSubfloatConvert(getGroup());
|
||||||
|
}
|
||||||
|
virtual void getOpList(vector<uint4> &oplist) const;
|
||||||
|
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
||||||
|
};
|
||||||
|
|
||||||
/// \brief Class for splitting data-flow on \e laned registers
|
/// \brief Class for splitting data-flow on \e laned registers
|
||||||
///
|
///
|
||||||
/// From a root Varnode and a description of its \e lanes, trace data-flow as far as
|
/// From a root Varnode and a description of its \e lanes, trace data-flow as far as
|
||||||
|
@ -113,6 +113,24 @@ void FunctionTestCollection::clear(void)
|
|||||||
console->reset();
|
console->reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Remove any carriage return character as well.
|
||||||
|
/// \param ref is the string to strip
|
||||||
|
/// \return the stripped string
|
||||||
|
string FunctionTestCollection::stripNewlines(const string &ref)
|
||||||
|
|
||||||
|
{
|
||||||
|
string res;
|
||||||
|
|
||||||
|
for(int4 i=0;i<ref.size();++i) {
|
||||||
|
char c = ref[i];
|
||||||
|
if (c == '\r') continue; // Remove carriage return
|
||||||
|
if (c == '\n')
|
||||||
|
c = ' '; // Convert newline to space
|
||||||
|
res.push_back(c);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
/// \param el is the root \<script> tag
|
/// \param el is the root \<script> tag
|
||||||
void FunctionTestCollection::restoreXmlCommands(const Element *el)
|
void FunctionTestCollection::restoreXmlCommands(const Element *el)
|
||||||
|
|
||||||
@ -122,7 +140,7 @@ void FunctionTestCollection::restoreXmlCommands(const Element *el)
|
|||||||
|
|
||||||
for(iter=list.begin();iter!=list.end();++iter) {
|
for(iter=list.begin();iter!=list.end();++iter) {
|
||||||
const Element *subel = *iter;
|
const Element *subel = *iter;
|
||||||
commands.push_back(subel->getContent());
|
commands.push_back(stripNewlines(subel->getContent()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,6 +77,7 @@ class FunctionTestCollection {
|
|||||||
mutable int4 numTestsApplied; ///< Count of tests that were executed
|
mutable int4 numTestsApplied; ///< Count of tests that were executed
|
||||||
mutable int4 numTestsSucceeded; ///< Count of tests that passed
|
mutable int4 numTestsSucceeded; ///< Count of tests that passed
|
||||||
void clear(void); ///< Clear any previous architecture and function
|
void clear(void); ///< Clear any previous architecture and function
|
||||||
|
static string stripNewlines(const string &ref); ///< Convert any \e newline character to a \e space
|
||||||
void restoreXmlCommands(const Element *el); ///< Reconstruct commands from an XML tag
|
void restoreXmlCommands(const Element *el); ///< Reconstruct commands from an XML tag
|
||||||
void buildProgram(DocumentStorage &store); ///< Build program (Architecture) from \<binaryimage> tag
|
void buildProgram(DocumentStorage &store); ///< Build program (Architecture) from \<binaryimage> tag
|
||||||
void startTests(void) const; ///< Initialize each FunctionTestProperty
|
void startTests(void) const; ///< Initialize each FunctionTestProperty
|
||||||
|
@ -20,16 +20,17 @@ namespace ghidra {
|
|||||||
|
|
||||||
/// The base propagation ordering associated with each meta-type.
|
/// The base propagation ordering associated with each meta-type.
|
||||||
/// The array elements correspond to the ordering of #type_metatype.
|
/// The array elements correspond to the ordering of #type_metatype.
|
||||||
sub_metatype Datatype::base2sub[15] = {
|
sub_metatype Datatype::base2sub[18] = {
|
||||||
SUB_PARTIALUNION, SUB_PARTIALSTRUCT, SUB_UNION, SUB_STRUCT, SUB_ARRAY, SUB_PTRREL, SUB_PTR, SUB_FLOAT, SUB_CODE,
|
SUB_PARTIALUNION, SUB_PARTIALSTRUCT, SUB_UINT_ENUM, SUB_UNION, SUB_STRUCT, SUB_INT_ENUM, SUB_UINT_ENUM,
|
||||||
SUB_BOOL, SUB_UINT_PLAIN, SUB_INT_PLAIN, SUB_UNKNOWN, SUB_SPACEBASE, SUB_VOID
|
SUB_ARRAY, SUB_PTRREL, SUB_PTR, SUB_FLOAT, SUB_CODE, SUB_BOOL, SUB_UINT_PLAIN, SUB_INT_PLAIN, SUB_UNKNOWN,
|
||||||
|
SUB_SPACEBASE, SUB_VOID
|
||||||
};
|
};
|
||||||
|
|
||||||
AttributeId ATTRIB_ALIGNMENT = AttributeId("alignment",47);
|
AttributeId ATTRIB_ALIGNMENT = AttributeId("alignment",47);
|
||||||
AttributeId ATTRIB_ARRAYSIZE = AttributeId("arraysize",48);
|
AttributeId ATTRIB_ARRAYSIZE = AttributeId("arraysize",48);
|
||||||
AttributeId ATTRIB_CHAR = AttributeId("char",49);
|
AttributeId ATTRIB_CHAR = AttributeId("char",49);
|
||||||
AttributeId ATTRIB_CORE = AttributeId("core",50);
|
AttributeId ATTRIB_CORE = AttributeId("core",50);
|
||||||
AttributeId ATTRIB_ENUM = AttributeId("enum",51);
|
//AttributeId ATTRIB_ENUM = AttributeId("enum",51); // deprecated
|
||||||
AttributeId ATTRIB_INCOMPLETE = AttributeId("incomplete",52);
|
AttributeId ATTRIB_INCOMPLETE = AttributeId("incomplete",52);
|
||||||
//AttributeId ATTRIB_ENUMSIZE = AttributeId("enumsize",53); // deprecated
|
//AttributeId ATTRIB_ENUMSIZE = AttributeId("enumsize",53); // deprecated
|
||||||
//AttributeId ATTRIB_INTSIZE = AttributeId("intsize",54); // deprecated
|
//AttributeId ATTRIB_INTSIZE = AttributeId("intsize",54); // deprecated
|
||||||
@ -250,12 +251,21 @@ void metatype2string(type_metatype metatype,string &res)
|
|||||||
case TYPE_ARRAY:
|
case TYPE_ARRAY:
|
||||||
res = "array";
|
res = "array";
|
||||||
break;
|
break;
|
||||||
|
case TYPE_PARTIALENUM:
|
||||||
|
res = "partenum";
|
||||||
|
break;
|
||||||
case TYPE_PARTIALSTRUCT:
|
case TYPE_PARTIALSTRUCT:
|
||||||
res = "partstruct";
|
res = "partstruct";
|
||||||
break;
|
break;
|
||||||
case TYPE_PARTIALUNION:
|
case TYPE_PARTIALUNION:
|
||||||
res = "partunion";
|
res = "partunion";
|
||||||
break;
|
break;
|
||||||
|
case TYPE_ENUM_INT:
|
||||||
|
res = "enum_int";
|
||||||
|
break;
|
||||||
|
case TYPE_ENUM_UINT:
|
||||||
|
res = "enum_uint";
|
||||||
|
break;
|
||||||
case TYPE_STRUCT:
|
case TYPE_STRUCT:
|
||||||
res = "struct";
|
res = "struct";
|
||||||
break;
|
break;
|
||||||
@ -309,6 +319,12 @@ type_metatype string2metatype(const string &metastring)
|
|||||||
if (metastring=="array")
|
if (metastring=="array")
|
||||||
return TYPE_ARRAY;
|
return TYPE_ARRAY;
|
||||||
break;
|
break;
|
||||||
|
case 'e':
|
||||||
|
if (metastring=="enum_int")
|
||||||
|
return TYPE_ENUM_INT;
|
||||||
|
else if (metastring == "enum_uint")
|
||||||
|
return TYPE_ENUM_UINT;
|
||||||
|
break;
|
||||||
case 's':
|
case 's':
|
||||||
if (metastring=="struct")
|
if (metastring=="struct")
|
||||||
return TYPE_STRUCT;
|
return TYPE_STRUCT;
|
||||||
@ -1083,6 +1099,12 @@ TypePointer *TypePointer::downChain(int8 &off,TypePointer *&par,int8 &parOff,boo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ptrto->isEnumType()) {
|
||||||
|
// Go "into" the enumeration
|
||||||
|
Datatype *tmp = typegrp.getBase(1, TYPE_UINT);
|
||||||
|
off = 0;
|
||||||
|
return typegrp.getTypePointer(size,tmp,wordsize);
|
||||||
|
}
|
||||||
type_metatype meta = ptrto->getMetatype();
|
type_metatype meta = ptrto->getMetatype();
|
||||||
bool isArray = (meta == TYPE_ARRAY);
|
bool isArray = (meta == TYPE_ARRAY);
|
||||||
if (isArray || meta == TYPE_STRUCT) {
|
if (isArray || meta == TYPE_STRUCT) {
|
||||||
@ -1325,81 +1347,22 @@ TypeEnum::TypeEnum(const TypeEnum &op) : TypeBase(op)
|
|||||||
|
|
||||||
{
|
{
|
||||||
namemap = op.namemap;
|
namemap = op.namemap;
|
||||||
masklist = op.masklist;
|
|
||||||
flags |= (op.flags&poweroftwo)|enumtype;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the map. Calculate the independent bit-fields within the named values of the enumeration
|
/// \param val is the given value to test
|
||||||
/// Two bits are in the same bit-field if there is a name in the map whose value
|
/// \return \b true if \b this enumeration has a name with the value
|
||||||
/// has those two bits set. Bit-fields must be a contiguous range of bits.
|
bool TypeEnum::hasNamedValue(uintb val) const
|
||||||
void TypeEnum::setNameMap(const map<uintb,string> &nmap)
|
|
||||||
|
|
||||||
{
|
{
|
||||||
map<uintb,string>::const_iterator iter;
|
return (namemap.find(val) != namemap.end());
|
||||||
uintb curmask,lastmask;
|
|
||||||
int4 maxbit;
|
|
||||||
int4 curmaxbit;
|
|
||||||
bool fieldisempty;
|
|
||||||
|
|
||||||
namemap = nmap;
|
|
||||||
masklist.clear();
|
|
||||||
|
|
||||||
flags &= ~((uint4)poweroftwo);
|
|
||||||
|
|
||||||
maxbit = 8 * size - 1;
|
|
||||||
|
|
||||||
curmaxbit = 0;
|
|
||||||
while(curmaxbit <= maxbit) {
|
|
||||||
curmask = 1;
|
|
||||||
curmask <<= curmaxbit;
|
|
||||||
lastmask = 0;
|
|
||||||
fieldisempty = true;
|
|
||||||
while(curmask != lastmask) { // Repeat until there is no change in the current mask
|
|
||||||
lastmask = curmask; // Note changes from last time through
|
|
||||||
|
|
||||||
for(iter=namemap.begin();iter!=namemap.end();++iter) { // For every named enumeration value
|
|
||||||
uintb val = (*iter).first;
|
|
||||||
if ((val & curmask) != 0) { // If the value shares ANY bits in common with the current mask
|
|
||||||
curmask |= val; // Absorb ALL defined bits of the value into the current mask
|
|
||||||
fieldisempty = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fill in any holes in the mask (bit field must consist of contiguous bits
|
|
||||||
int4 lsb = leastsigbit_set(curmask);
|
|
||||||
int4 msb = mostsigbit_set(curmask);
|
|
||||||
if (msb > curmaxbit)
|
|
||||||
curmaxbit = msb;
|
|
||||||
|
|
||||||
uintb mask1 = 1;
|
|
||||||
mask1 = (mask1 << lsb) - 1; // every bit below lsb is set to 1
|
|
||||||
uintb mask2 = 1;
|
|
||||||
mask2 <<= msb;
|
|
||||||
mask2 <<= 1;
|
|
||||||
mask2 -= 1; // every bit below or equal to msb is set to 1
|
|
||||||
curmask = mask1 ^ mask2;
|
|
||||||
}
|
|
||||||
if (fieldisempty) { // If no value hits this bit
|
|
||||||
if (!masklist.empty())
|
|
||||||
masklist.back() |= curmask; // Include the bit with the previous mask
|
|
||||||
else
|
|
||||||
masklist.push_back(curmask);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
masklist.push_back(curmask);
|
|
||||||
curmaxbit += 1;
|
|
||||||
}
|
|
||||||
if (masklist.size() > 1)
|
|
||||||
flags |= poweroftwo;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Given a specific value of the enumeration, calculate the named representation of that value.
|
/// Given a specific value of the enumeration, calculate the named representation of that value.
|
||||||
/// The representation is returned as a list of names that must logically ORed and possibly complemented.
|
/// The representation is returned as a list of names that must logically ORed and possibly complemented.
|
||||||
/// If no representation is possible, no names will be returned.
|
/// If no representation is possible, no names will be returned.
|
||||||
/// \param val is the value to find the representation for
|
/// \param val is the value to find the representation for
|
||||||
/// \param valnames will hold the returned list of names
|
/// \param rep will contain the individual names in the representation and other transforms
|
||||||
/// \return true if the representation needs to be complemented
|
void TypeEnum::getMatches(uintb val,Representation &rep) const
|
||||||
bool TypeEnum::getMatches(uintb val,vector<string> &valnames) const
|
|
||||||
|
|
||||||
{
|
{
|
||||||
map<uintb,string>::const_iterator iter;
|
map<uintb,string>::const_iterator iter;
|
||||||
@ -1407,33 +1370,47 @@ bool TypeEnum::getMatches(uintb val,vector<string> &valnames) const
|
|||||||
|
|
||||||
for(count=0;count<2;++count) {
|
for(count=0;count<2;++count) {
|
||||||
bool allmatch = true;
|
bool allmatch = true;
|
||||||
if (val == 0) { // Zero handled specially, it crosses all masks
|
if (val == 0) { // Zero handled specially
|
||||||
iter = namemap.find(val);
|
iter = namemap.find(val);
|
||||||
if (iter != namemap.end())
|
if (iter != namemap.end())
|
||||||
valnames.push_back( (*iter).second );
|
rep.matchname.push_back( (*iter).second );
|
||||||
else
|
else
|
||||||
allmatch = false;
|
allmatch = false;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
for(int4 i=0;i<masklist.size();++i) {
|
uintb bitsleft = val;
|
||||||
uintb maskedval = val & masklist[i];
|
uintb target = val;
|
||||||
if (maskedval == 0) // No component of -val- in this mask
|
while(target != 0) {
|
||||||
continue; // print nothing
|
// Find named value that matches the largest number of most significant bits in bitsleft
|
||||||
iter = namemap.find(maskedval);
|
iter = namemap.upper_bound(target);
|
||||||
if (iter != namemap.end())
|
if (iter == namemap.begin()) break; // All named values are greater than target
|
||||||
valnames.push_back( (*iter).second ); // Found name for this component
|
--iter; // Biggest named value less than or equal to target
|
||||||
else { // If no name for this component
|
uintb curval = (*iter).first;
|
||||||
allmatch = false; // Give up on representation
|
uintb diff = coveringmask(bitsleft ^ curval);
|
||||||
break; // Stop searching for other components
|
if (diff >= bitsleft) break; // Could not match most significant bit of bitsleft
|
||||||
|
if ((curval & diff) == 0) {
|
||||||
|
// Found a named value that matches at least most significant bit of bitsleft
|
||||||
|
rep.matchname.push_back( (*iter).second ); // Accept the name
|
||||||
|
bitsleft ^= curval; // Remove the bits from bitsleft
|
||||||
|
target = bitsleft; // Continue searching for named value that match the new bitsleft
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Not all the (one) bits of curval match into bitsleft, but we can restrict a further search.
|
||||||
|
// Bits above diff in curval are the maximum we can hope to match with one named value.
|
||||||
|
// Zero out bits below this and prepare to search at or below this value
|
||||||
|
target = curval & ~diff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
allmatch = (bitsleft == 0);
|
||||||
|
}
|
||||||
|
if (allmatch) { // If we have a complete representation
|
||||||
|
rep.complement = (count==1); // Set whether we represented original value or complement
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (allmatch) // If we have a complete representation
|
|
||||||
return (count==1); // Return whether we represented original value or complement
|
|
||||||
val = val ^ calc_mask(size); // Switch value we are trying to represent (to complement)
|
val = val ^ calc_mask(size); // Switch value we are trying to represent (to complement)
|
||||||
valnames.clear(); // Clear out old attempt
|
rep.matchname.clear(); // Clear out old attempt
|
||||||
}
|
}
|
||||||
return false; // If we reach here, no representation was possible, -valnames- is empty
|
// If we reach here, no representation was possible, -matchname- is empty
|
||||||
}
|
}
|
||||||
|
|
||||||
int4 TypeEnum::compare(const Datatype &op,int4 level) const
|
int4 TypeEnum::compare(const Datatype &op,int4 level) const
|
||||||
@ -1475,8 +1452,7 @@ void TypeEnum::encode(Encoder &encoder) const
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
encoder.openElement(ELEM_TYPE);
|
encoder.openElement(ELEM_TYPE);
|
||||||
encodeBasic(metatype,-1,encoder);
|
encodeBasic((metatype == TYPE_INT) ? TYPE_ENUM_INT : TYPE_ENUM_UINT,-1,encoder);
|
||||||
encoder.writeString(ATTRIB_ENUM, "true");
|
|
||||||
map<uintb,string>::const_iterator iter;
|
map<uintb,string>::const_iterator iter;
|
||||||
for(iter=namemap.begin();iter!=namemap.end();++iter) {
|
for(iter=namemap.begin();iter!=namemap.end();++iter) {
|
||||||
encoder.openElement(ELEM_VAL);
|
encoder.openElement(ELEM_VAL);
|
||||||
@ -1496,7 +1472,7 @@ string TypeEnum::decode(Decoder &decoder,TypeFactory &typegrp)
|
|||||||
{
|
{
|
||||||
// uint4 elemId = decoder.openElement();
|
// uint4 elemId = decoder.openElement();
|
||||||
decodeBasic(decoder);
|
decodeBasic(decoder);
|
||||||
submeta = (metatype == TYPE_INT) ? SUB_INT_ENUM : SUB_UINT_ENUM;
|
metatype = (metatype == TYPE_ENUM_INT) ? TYPE_INT : TYPE_UINT; // Use TYPE_INT or TYPE_UINT internally
|
||||||
map<uintb,string> nmap;
|
map<uintb,string> nmap;
|
||||||
string warning;
|
string warning;
|
||||||
|
|
||||||
@ -2265,6 +2241,81 @@ void TypeUnion::assignFieldOffsets(vector<TypeField> &list,int4 &newSize,int4 &n
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TypePartialEnum::TypePartialEnum(const TypePartialEnum &op)
|
||||||
|
: TypeEnum(op)
|
||||||
|
{
|
||||||
|
stripped = op.stripped;
|
||||||
|
parent = op.parent;
|
||||||
|
offset = op.offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
TypePartialEnum::TypePartialEnum(TypeEnum *par,int4 off,int4 sz,Datatype *strip)
|
||||||
|
: TypeEnum(sz, TYPE_PARTIALENUM)
|
||||||
|
{
|
||||||
|
flags |= has_stripped;
|
||||||
|
stripped = strip;
|
||||||
|
parent = par;
|
||||||
|
offset = off;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TypePartialEnum::printRaw(ostream &s) const
|
||||||
|
|
||||||
|
{
|
||||||
|
parent->printRaw(s);
|
||||||
|
s << "[off=" << dec << offset << ",sz=" << size << ']';
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TypePartialEnum::hasNamedValue(uintb val) const
|
||||||
|
|
||||||
|
{
|
||||||
|
val <<= 8*offset;
|
||||||
|
return parent->hasNamedValue(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TypePartialEnum::getMatches(uintb val,Representation &rep) const
|
||||||
|
|
||||||
|
{
|
||||||
|
val <<= 8*offset;
|
||||||
|
rep.shiftAmount = offset * 8;
|
||||||
|
parent->getMatches(val,rep);
|
||||||
|
}
|
||||||
|
|
||||||
|
int4 TypePartialEnum::compare(const Datatype &op,int4 level) const
|
||||||
|
|
||||||
|
{
|
||||||
|
int4 res = Datatype::compare(op,level);
|
||||||
|
if (res != 0) return res;
|
||||||
|
// Both must be partial
|
||||||
|
TypePartialEnum *tp = (TypePartialEnum *) &op;
|
||||||
|
if (offset != tp->offset) return (offset < tp->offset) ? -1 : 1;
|
||||||
|
level -= 1;
|
||||||
|
if (level < 0) {
|
||||||
|
if (id == op.getId()) return 0;
|
||||||
|
return (id < op.getId()) ? -1 : 1;
|
||||||
|
}
|
||||||
|
return parent->compare(*tp->parent,level); // Compare the underlying union
|
||||||
|
}
|
||||||
|
|
||||||
|
int4 TypePartialEnum::compareDependency(const Datatype &op) const
|
||||||
|
|
||||||
|
{
|
||||||
|
if (submeta != op.getSubMeta()) return (submeta < op.getSubMeta()) ? -1 : 1;
|
||||||
|
TypePartialEnum *tp = (TypePartialEnum *) &op; // Both must be partial
|
||||||
|
if (parent != tp->parent) return (parent < tp->parent) ? -1 : 1; // Compare absolute pointers
|
||||||
|
if (offset != tp->offset) return (offset < tp->offset) ? -1 : 1;
|
||||||
|
return (op.getSize()-size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TypePartialEnum::encode(Encoder &encoder) const
|
||||||
|
|
||||||
|
{
|
||||||
|
encoder.openElement(ELEM_TYPE);
|
||||||
|
encodeBasic(TYPE_PARTIALENUM,-1,encoder);
|
||||||
|
encoder.writeSignedInteger(ATTRIB_OFFSET, offset);
|
||||||
|
parent->encodeRef(encoder);
|
||||||
|
encoder.closeElement(ELEM_TYPE);
|
||||||
|
}
|
||||||
|
|
||||||
TypePartialStruct::TypePartialStruct(const TypePartialStruct &op)
|
TypePartialStruct::TypePartialStruct(const TypePartialStruct &op)
|
||||||
: Datatype(op)
|
: Datatype(op)
|
||||||
{
|
{
|
||||||
@ -3111,7 +3162,7 @@ void TypeFactory::setupSizes(void)
|
|||||||
setDefaultAlignmentMap();
|
setDefaultAlignmentMap();
|
||||||
if (enumsize == 0) {
|
if (enumsize == 0) {
|
||||||
enumsize = glb->getDefaultSize();
|
enumsize = glb->getDefaultSize();
|
||||||
enumtype = TYPE_UINT;
|
enumtype = TYPE_ENUM_UINT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3866,6 +3917,11 @@ TypeStruct *TypeFactory::getTypeStruct(const string &n)
|
|||||||
return (TypeStruct *) findAdd(tmp);
|
return (TypeStruct *) findAdd(tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a data-type representing storage of part of an \e array or \e structure.
|
||||||
|
/// \param contain is the parent \e array or \e structure data-type that we are taking a part of.
|
||||||
|
/// \param off is the offset (in bytes) within the parent that the partial data-type starts at
|
||||||
|
/// \param sz is the number of bytes in the partial data-type
|
||||||
|
/// \return the TypePartialStruct object
|
||||||
TypePartialStruct *TypeFactory::getTypePartialStruct(Datatype *contain,int4 off,int4 sz)
|
TypePartialStruct *TypeFactory::getTypePartialStruct(Datatype *contain,int4 off,int4 sz)
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -3887,6 +3943,11 @@ TypeUnion *TypeFactory::getTypeUnion(const string &n)
|
|||||||
return (TypeUnion *) findAdd(tmp);
|
return (TypeUnion *) findAdd(tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a data-type representing storage of part of a \e union data-type.
|
||||||
|
/// \param contain is the parent \e union data-type that we are taking a part of.
|
||||||
|
/// \param off is the offset (in bytes) within the parent that the partial data-type starts at
|
||||||
|
/// \param sz is the number of bytes in the partial data-type
|
||||||
|
/// \return the TypePartialUnion object
|
||||||
TypePartialUnion *TypeFactory::getTypePartialUnion(TypeUnion *contain,int4 off,int4 sz)
|
TypePartialUnion *TypeFactory::getTypePartialUnion(TypeUnion *contain,int4 off,int4 sz)
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -3907,6 +3968,19 @@ TypeEnum *TypeFactory::getTypeEnum(const string &n)
|
|||||||
return (TypeEnum *) findAdd(tmp);
|
return (TypeEnum *) findAdd(tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a data-type representing storage of part of an \e enumeration.
|
||||||
|
/// \param contain is the parent \e enumeration data-type that we are taking a part of.
|
||||||
|
/// \param off is the offset (in bytes) within the parent that the partial data-type starts at
|
||||||
|
/// \param sz is the number of bytes in the partial data-type
|
||||||
|
/// \return the TypePartialEnum object
|
||||||
|
TypePartialEnum *TypeFactory::getTypePartialEnum(TypeEnum *contain,int4 off,int4 sz)
|
||||||
|
|
||||||
|
{
|
||||||
|
Datatype *strip = getBase(sz, TYPE_UNKNOWN);
|
||||||
|
TypePartialEnum tpe(contain,off,sz,strip);
|
||||||
|
return (TypePartialEnum *) findAdd(tpe);
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates the special TypeSpacebase with an associated address space and scope
|
/// Creates the special TypeSpacebase with an associated address space and scope
|
||||||
/// \param id is the address space
|
/// \param id is the address space
|
||||||
/// \param addr specifies the function scope, or isInvalid() for global scope
|
/// \param addr specifies the function scope, or isInvalid() for global scope
|
||||||
@ -4032,6 +4106,8 @@ Datatype *TypeFactory::getExactPiece(Datatype *ct,int4 offset,int4 size)
|
|||||||
// If we reach here, lastType is bigger than size
|
// If we reach here, lastType is bigger than size
|
||||||
if (lastType->getMetatype() == TYPE_STRUCT || lastType->getMetatype() == TYPE_ARRAY)
|
if (lastType->getMetatype() == TYPE_STRUCT || lastType->getMetatype() == TYPE_ARRAY)
|
||||||
return getTypePartialStruct(lastType, lastOff, size);
|
return getTypePartialStruct(lastType, lastOff, size);
|
||||||
|
else if (lastType->isEnumType() && !lastType->hasStripped())
|
||||||
|
return getTypePartialEnum((TypeEnum *)lastType, lastOff, size);
|
||||||
}
|
}
|
||||||
return (Datatype *)0;
|
return (Datatype *)0;
|
||||||
}
|
}
|
||||||
@ -4238,7 +4314,7 @@ Datatype *TypeFactory::decodeTypedef(Decoder &decoder)
|
|||||||
Datatype *TypeFactory::decodeEnum(Decoder &decoder,bool forcecore)
|
Datatype *TypeFactory::decodeEnum(Decoder &decoder,bool forcecore)
|
||||||
|
|
||||||
{
|
{
|
||||||
TypeEnum te(1,TYPE_INT); // size and metatype are replaced
|
TypeEnum te(1,TYPE_ENUM_INT); // metatype and size are replaced
|
||||||
string warning = te.decode(decoder,*this);
|
string warning = te.decode(decoder,*this);
|
||||||
if (forcecore)
|
if (forcecore)
|
||||||
te.flags |= Datatype::coretype;
|
te.flags |= Datatype::coretype;
|
||||||
@ -4399,6 +4475,10 @@ Datatype *TypeFactory::decodeTypeNoRef(Decoder &decoder,bool forcecore)
|
|||||||
ct = findAdd(ta);
|
ct = findAdd(ta);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case TYPE_ENUM_INT:
|
||||||
|
case TYPE_ENUM_UINT:
|
||||||
|
ct = decodeEnum(decoder,forcecore);
|
||||||
|
break;
|
||||||
case TYPE_STRUCT:
|
case TYPE_STRUCT:
|
||||||
ct = decodeStruct(decoder,forcecore);
|
ct = decodeStruct(decoder,forcecore);
|
||||||
break;
|
break;
|
||||||
@ -4438,12 +4518,6 @@ Datatype *TypeFactory::decodeTypeNoRef(Decoder &decoder,bool forcecore)
|
|||||||
decoder.closeElement(elemId);
|
decoder.closeElement(elemId);
|
||||||
return ct;
|
return ct;
|
||||||
}
|
}
|
||||||
else if (attribId == ATTRIB_ENUM && decoder.readBool()) {
|
|
||||||
decoder.rewindAttributes();
|
|
||||||
ct = decodeEnum(decoder, forcecore);
|
|
||||||
decoder.closeElement(elemId);
|
|
||||||
return ct;
|
|
||||||
}
|
|
||||||
else if (attribId == ATTRIB_UTF && decoder.readBool()) {
|
else if (attribId == ATTRIB_UTF && decoder.readBool()) {
|
||||||
TypeUnicode tu;
|
TypeUnicode tu;
|
||||||
decoder.rewindAttributes();
|
decoder.rewindAttributes();
|
||||||
@ -4587,9 +4661,9 @@ void TypeFactory::parseEnumConfig(Decoder &decoder)
|
|||||||
uint4 elemId = decoder.openElement(ELEM_ENUM);
|
uint4 elemId = decoder.openElement(ELEM_ENUM);
|
||||||
enumsize = decoder.readSignedInteger(ATTRIB_SIZE);
|
enumsize = decoder.readSignedInteger(ATTRIB_SIZE);
|
||||||
if (decoder.readBool(ATTRIB_SIGNED))
|
if (decoder.readBool(ATTRIB_SIGNED))
|
||||||
enumtype = TYPE_INT;
|
enumtype = TYPE_ENUM_INT;
|
||||||
else
|
else
|
||||||
enumtype = TYPE_UINT;
|
enumtype = TYPE_ENUM_UINT;
|
||||||
decoder.closeElement(elemId);
|
decoder.closeElement(elemId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ extern AttributeId ATTRIB_ALIGNMENT; ///< Marshaling attribute "alignment"
|
|||||||
extern AttributeId ATTRIB_ARRAYSIZE; ///< Marshaling attribute "arraysize"
|
extern AttributeId ATTRIB_ARRAYSIZE; ///< Marshaling attribute "arraysize"
|
||||||
extern AttributeId ATTRIB_CHAR; ///< Marshaling attribute "char"
|
extern AttributeId ATTRIB_CHAR; ///< Marshaling attribute "char"
|
||||||
extern AttributeId ATTRIB_CORE; ///< Marshaling attribute "core"
|
extern AttributeId ATTRIB_CORE; ///< Marshaling attribute "core"
|
||||||
extern AttributeId ATTRIB_ENUM; ///< Marshaling attribute "enum"
|
//extern AttributeId ATTRIB_ENUM; ///< Marshaling attribute "enum" deprecated
|
||||||
extern AttributeId ATTRIB_INCOMPLETE; ///< Marshaling attribute "incomplete"
|
extern AttributeId ATTRIB_INCOMPLETE; ///< Marshaling attribute "incomplete"
|
||||||
//extern AttributeId ATTRIB_ENUMSIZE; ///< Marshaling attribute "enumsize" deprecated
|
//extern AttributeId ATTRIB_ENUMSIZE; ///< Marshaling attribute "enumsize" deprecated
|
||||||
//extern AttributeId ATTRIB_INTSIZE; ///< Marshaling attribute "intsize" deprecated
|
//extern AttributeId ATTRIB_INTSIZE; ///< Marshaling attribute "intsize" deprecated
|
||||||
@ -77,20 +77,23 @@ extern void print_data(ostream &s,uint1 *buffer,int4 size,const Address &baseadd
|
|||||||
/// The core meta-types supported by the decompiler. These are sizeless templates
|
/// The core meta-types supported by the decompiler. These are sizeless templates
|
||||||
/// for the elements making up the type algebra. Index is important for Datatype::base2sub array.
|
/// for the elements making up the type algebra. Index is important for Datatype::base2sub array.
|
||||||
enum type_metatype {
|
enum type_metatype {
|
||||||
TYPE_VOID = 14, ///< Standard "void" type, absence of type
|
TYPE_VOID = 17, ///< Standard "void" type, absence of type
|
||||||
TYPE_SPACEBASE = 13, ///< Placeholder for symbol/type look-up calculations
|
TYPE_SPACEBASE = 16, ///< Placeholder for symbol/type look-up calculations
|
||||||
TYPE_UNKNOWN = 12, ///< An unknown low-level type. Treated as an unsigned integer.
|
TYPE_UNKNOWN = 15, ///< An unknown low-level type. Treated as an unsigned integer.
|
||||||
TYPE_INT = 11, ///< Signed integer. Signed is considered less specific than unsigned in C
|
TYPE_INT = 14, ///< Signed integer. Signed is considered less specific than unsigned in C
|
||||||
TYPE_UINT = 10, ///< Unsigned integer
|
TYPE_UINT = 13, ///< Unsigned integer
|
||||||
TYPE_BOOL = 9, ///< Boolean
|
TYPE_BOOL = 12, ///< Boolean
|
||||||
TYPE_CODE = 8, ///< Data is actual executable code
|
TYPE_CODE = 11, ///< Data is actual executable code
|
||||||
TYPE_FLOAT = 7, ///< Floating-point
|
TYPE_FLOAT = 10, ///< Floating-point
|
||||||
|
|
||||||
TYPE_PTR = 6, ///< Pointer data-type
|
TYPE_PTR = 9, ///< Pointer data-type
|
||||||
TYPE_PTRREL = 5, ///< Pointer relative to another data-type (specialization of TYPE_PTR)
|
TYPE_PTRREL = 8, ///< Pointer relative to another data-type (specialization of TYPE_PTR)
|
||||||
TYPE_ARRAY = 4, ///< Array data-type, made up of a sequence of "element" datatype
|
TYPE_ARRAY = 7, ///< Array data-type, made up of a sequence of "element" datatype
|
||||||
TYPE_STRUCT = 3, ///< Structure data-type, made up of component datatypes
|
TYPE_ENUM_UINT = 6, ///< Unsigned enumeration data-type (specialization of TYPE_UINT)
|
||||||
TYPE_UNION = 2, ///< An overlapping union of multiple datatypes
|
TYPE_ENUM_INT = 5, ///< Signed enumeration data-type (specialization of TYPE_INT)
|
||||||
|
TYPE_STRUCT = 4, ///< Structure data-type, made up of component datatypes
|
||||||
|
TYPE_UNION = 3, ///< An overlapping union of multiple datatypes
|
||||||
|
TYPE_PARTIALENUM = 2, ///< Part of an enumerated value (specialization of TYPE_UINT)
|
||||||
TYPE_PARTIALSTRUCT = 1, ///< Part of a structure, stored separately from the whole
|
TYPE_PARTIALSTRUCT = 1, ///< Part of a structure, stored separately from the whole
|
||||||
TYPE_PARTIALUNION = 0 ///< Part of a union
|
TYPE_PARTIALUNION = 0 ///< Part of a union
|
||||||
};
|
};
|
||||||
@ -160,7 +163,7 @@ struct DatatypeCompare;
|
|||||||
/// Used for symbols, function prototypes, type propagation etc.
|
/// Used for symbols, function prototypes, type propagation etc.
|
||||||
class Datatype {
|
class Datatype {
|
||||||
protected:
|
protected:
|
||||||
static sub_metatype base2sub[15];
|
static sub_metatype base2sub[18];
|
||||||
/// Boolean properties of datatypes
|
/// Boolean properties of datatypes
|
||||||
enum {
|
enum {
|
||||||
coretype = 1, ///< This is a basic type which will never be redefined
|
coretype = 1, ///< This is a basic type which will never be redefined
|
||||||
@ -213,7 +216,6 @@ public:
|
|||||||
bool isCoreType(void) const { return ((flags&coretype)!=0); } ///< Is this a core data-type
|
bool isCoreType(void) const { return ((flags&coretype)!=0); } ///< Is this a core data-type
|
||||||
bool isCharPrint(void) const { return ((flags&(chartype|utf16|utf32|opaque_string))!=0); } ///< Does this print as a 'char'
|
bool isCharPrint(void) const { return ((flags&(chartype|utf16|utf32|opaque_string))!=0); } ///< Does this print as a 'char'
|
||||||
bool isEnumType(void) const { return ((flags&enumtype)!=0); } ///< Is this an enumerated type
|
bool isEnumType(void) const { return ((flags&enumtype)!=0); } ///< Is this an enumerated type
|
||||||
bool isPowerOfTwo(void) const { return ((flags&poweroftwo)!=0); } ///< Is this a flag-based enumeration
|
|
||||||
bool isASCII(void) const { return ((flags&chartype)!=0); } ///< Does this print as an ASCII 'char'
|
bool isASCII(void) const { return ((flags&chartype)!=0); } ///< Does this print as an ASCII 'char'
|
||||||
bool isUTF16(void) const { return ((flags&utf16)!=0); } ///< Does this print as UTF16 'wchar'
|
bool isUTF16(void) const { return ((flags&utf16)!=0); } ///< Does this print as UTF16 'wchar'
|
||||||
bool isUTF32(void) const { return ((flags&utf32)!=0); } ///< Does this print as UTF32 'wchar'
|
bool isUTF32(void) const { return ((flags&utf32)!=0); } ///< Does this print as UTF32 'wchar'
|
||||||
@ -466,24 +468,33 @@ public:
|
|||||||
/// This supports combinations of the enumeration values (using logical OR and bit-wise complement)
|
/// This supports combinations of the enumeration values (using logical OR and bit-wise complement)
|
||||||
/// by defining independent \b bit-fields.
|
/// by defining independent \b bit-fields.
|
||||||
class TypeEnum : public TypeBase {
|
class TypeEnum : public TypeBase {
|
||||||
|
public:
|
||||||
|
/// \brief Class describing how a particular enumeration value is constructed using tokens
|
||||||
|
class Representation {
|
||||||
|
public:
|
||||||
|
vector<string> matchname; ///< Name tokens that are ORed together
|
||||||
|
bool complement; ///< If \b true, bitwise complement value after ORing
|
||||||
|
int4 shiftAmount; ///< Number of bits to left-shift final value
|
||||||
|
Representation(void) { complement = false; shiftAmount = 0; } ///< Constructor
|
||||||
|
};
|
||||||
protected:
|
protected:
|
||||||
friend class TypeFactory;
|
friend class TypeFactory;
|
||||||
map<uintb,string> namemap; ///< Map from integer to name
|
map<uintb,string> namemap; ///< Map from integer to name
|
||||||
vector<uintb> masklist; ///< Masks for each bitfield within the enum
|
void setNameMap(const map<uintb,string> &nmap) { namemap = nmap; } ///< Establish the value -> name map
|
||||||
void setNameMap(const map<uintb,string> &nmap); ///< Establish the value -> name map
|
|
||||||
string decode(Decoder &decoder,TypeFactory &typegrp); ///< Restore \b this enum data-type from a stream
|
string decode(Decoder &decoder,TypeFactory &typegrp); ///< Restore \b this enum data-type from a stream
|
||||||
public:
|
public:
|
||||||
/// Construct from another TypeEnum
|
/// Construct from another TypeEnum
|
||||||
TypeEnum(const TypeEnum &op);
|
TypeEnum(const TypeEnum &op);
|
||||||
/// Construct from a size and meta-type (TYPE_INT or TYPE_UINT)
|
/// Construct from a size and meta-type (TYPE_INT or TYPE_UINT)
|
||||||
TypeEnum(int4 s,type_metatype m) : TypeBase(s,m) {
|
TypeEnum(int4 s,type_metatype m) : TypeBase(s,m) {
|
||||||
flags |= enumtype; submeta = (m==TYPE_INT) ? SUB_INT_ENUM : SUB_UINT_ENUM; }
|
flags |= enumtype; metatype = (m==TYPE_ENUM_INT) ? TYPE_INT : TYPE_UINT; }
|
||||||
/// Construct from a size, meta-type, and name
|
/// Construct from a size, meta-type, and name
|
||||||
TypeEnum(int4 s,type_metatype m,const string &nm) : TypeBase(s,m,nm) {
|
TypeEnum(int4 s,type_metatype m,const string &nm) : TypeBase(s,m,nm) {
|
||||||
flags |= enumtype; submeta = (m==TYPE_INT) ? SUB_INT_ENUM : SUB_UINT_ENUM; }
|
flags |= enumtype; metatype = (m==TYPE_ENUM_INT) ? TYPE_INT : TYPE_UINT; }
|
||||||
map<uintb,string>::const_iterator beginEnum(void) const { return namemap.begin(); } ///< Beginning of name map
|
map<uintb,string>::const_iterator beginEnum(void) const { return namemap.begin(); } ///< Beginning of name map
|
||||||
map<uintb,string>::const_iterator endEnum(void) const { return namemap.end(); } ///< End of name map
|
map<uintb,string>::const_iterator endEnum(void) const { return namemap.end(); } ///< End of name map
|
||||||
bool getMatches(uintb val,vector<string> &matchname) const; ///< Recover the named representation
|
virtual bool hasNamedValue(uintb val) const; ///< Does \b this have a (single) name for the given value
|
||||||
|
virtual void getMatches(uintb val,Representation &rep) const; ///< Recover the named representation
|
||||||
virtual int4 compare(const Datatype &op,int4 level) const;
|
virtual int4 compare(const Datatype &op,int4 level) const;
|
||||||
virtual int4 compareDependency(const Datatype &op) const;
|
virtual int4 compareDependency(const Datatype &op) const;
|
||||||
virtual Datatype *clone(void) const { return new TypeEnum(*this); }
|
virtual Datatype *clone(void) const { return new TypeEnum(*this); }
|
||||||
@ -553,6 +564,27 @@ public:
|
|||||||
static void assignFieldOffsets(vector<TypeField> &list,int4 &newSize,int4 &newAlign,TypeUnion *tu); ///< Assign field offsets
|
static void assignFieldOffsets(vector<TypeField> &list,int4 &newSize,int4 &newAlign,TypeUnion *tu); ///< Assign field offsets
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// \brief A data-type thats holds part of a TypeEnum and possible additional padding
|
||||||
|
class TypePartialEnum : public TypeEnum {
|
||||||
|
friend class TypeFactory;
|
||||||
|
Datatype *stripped; ///< The \e undefined data-type to use if a formal data-type is required.
|
||||||
|
TypeEnum *parent; ///< The enumeration data-type \b this is based on
|
||||||
|
int4 offset; ///< Byte offset with the parent enum where \b this starts
|
||||||
|
public:
|
||||||
|
TypePartialEnum(const TypePartialEnum &op); ///< Construct from another TypePartialEnum
|
||||||
|
TypePartialEnum(TypeEnum *par,int4 off,int4 sz,Datatype *strip); ///< Constructor
|
||||||
|
int4 getOffset(void) const { return offset; } ///< Get the byte offset into the containing data-type
|
||||||
|
Datatype *getParent(void) const { return parent; } ///< Get the enumeration containing \b this piece
|
||||||
|
virtual void printRaw(ostream &s) const;
|
||||||
|
virtual bool hasNamedValue(uintb val) const;
|
||||||
|
virtual void getMatches(uintb val,Representation &rep) const;
|
||||||
|
virtual int4 compare(const Datatype &op,int4 level) const;
|
||||||
|
virtual int4 compareDependency(const Datatype &op) const;
|
||||||
|
virtual Datatype *clone(void) const { return new TypePartialEnum(*this); }
|
||||||
|
virtual void encode(Encoder &encoder) const;
|
||||||
|
virtual Datatype *getStripped(void) const { return stripped; }
|
||||||
|
};
|
||||||
|
|
||||||
/// \brief A data-type that holds \e part of a TypeStruct or TypeArray
|
/// \brief A data-type that holds \e part of a TypeStruct or TypeArray
|
||||||
class TypePartialStruct : public Datatype {
|
class TypePartialStruct : public Datatype {
|
||||||
friend class TypeFactory;
|
friend class TypeFactory;
|
||||||
@ -634,7 +666,12 @@ public:
|
|||||||
/// \brief Get offset of \b this pointer relative to start of the containing data-type
|
/// \brief Get offset of \b this pointer relative to start of the containing data-type
|
||||||
///
|
///
|
||||||
/// \return the offset value in \e address \e units
|
/// \return the offset value in \e address \e units
|
||||||
int4 getPointerOffset(void) const { return AddrSpace::byteToAddressInt(offset, wordsize); }
|
int4 getAddressOffset(void) const { return AddrSpace::byteToAddressInt(offset, wordsize); }
|
||||||
|
|
||||||
|
/// \brief Get offset of \b this pointer relative to start of the containing data-type
|
||||||
|
///
|
||||||
|
/// \return the offset value in \e byte units
|
||||||
|
int4 getByteOffset(void) const { return offset; }
|
||||||
virtual void printRaw(ostream &s) const;
|
virtual void printRaw(ostream &s) const;
|
||||||
virtual int4 compare(const Datatype &op,int4 level) const;
|
virtual int4 compare(const Datatype &op,int4 level) const;
|
||||||
virtual int4 compareDependency(const Datatype &op) const;
|
virtual int4 compareDependency(const Datatype &op) const;
|
||||||
@ -803,6 +840,7 @@ public:
|
|||||||
TypeUnion *getTypeUnion(const string &n); ///< Create an (empty) union
|
TypeUnion *getTypeUnion(const string &n); ///< Create an (empty) union
|
||||||
TypePartialUnion *getTypePartialUnion(TypeUnion *contain,int4 off,int4 sz); ///< Create a partial union
|
TypePartialUnion *getTypePartialUnion(TypeUnion *contain,int4 off,int4 sz); ///< Create a partial union
|
||||||
TypeEnum *getTypeEnum(const string &n); ///< Create an (empty) enumeration
|
TypeEnum *getTypeEnum(const string &n); ///< Create an (empty) enumeration
|
||||||
|
TypePartialEnum *getTypePartialEnum(TypeEnum *contain,int4 off,int4 sz); ///< Create a partial enumeration
|
||||||
TypeSpacebase *getTypeSpacebase(AddrSpace *id,const Address &addr); ///< Create a "spacebase" type
|
TypeSpacebase *getTypeSpacebase(AddrSpace *id,const Address &addr); ///< Create a "spacebase" type
|
||||||
TypeCode *getTypeCode(const PrototypePieces &proto); ///< Create a "function" datatype
|
TypeCode *getTypeCode(const PrototypePieces &proto); ///< Create a "function" datatype
|
||||||
Datatype *getTypedef(Datatype *ct,const string &name,uint8 id,uint4 format); ///< Create a new \e typedef data-type
|
Datatype *getTypedef(Datatype *ct,const string &name,uint8 id,uint4 format); ///< Create a new \e typedef data-type
|
||||||
|
@ -175,10 +175,11 @@ OpCode TypeOp::floatSignManipulation(PcodeOp *op)
|
|||||||
return CPUI_MAX;
|
return CPUI_MAX;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \brief Propagate a dereferenced data-type up to its pointer data-type
|
/// \brief Propagate a dereferenced data-type up to its pointer data-type through a LOAD or STORE
|
||||||
///
|
///
|
||||||
/// Don't create more than a depth of 1, i.e. ptr->ptr
|
/// Don't create more than a depth of 1, i.e. ptr->ptr
|
||||||
/// \param pt is the pointed-to data-type
|
/// \param t is the TypeFactory containing the data-types
|
||||||
|
/// \param dt is the pointed-to data-type
|
||||||
/// \param sz is the size of the pointer
|
/// \param sz is the size of the pointer
|
||||||
/// \param wordsz is the wordsize associated with the pointer
|
/// \param wordsz is the wordsize associated with the pointer
|
||||||
/// \return the TypePointer object
|
/// \return the TypePointer object
|
||||||
@ -196,6 +197,36 @@ Datatype *TypeOp::propagateToPointer(TypeFactory *t,Datatype *dt,int4 sz,int4 wo
|
|||||||
return t->getTypePointer(sz,dt,wordsz);
|
return t->getTypePointer(sz,dt,wordsz);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// \brief Propagate a pointer data-type down to its element data-type through a LOAD or STORE
|
||||||
|
///
|
||||||
|
/// \param t is the TypeFactory containing the data-types
|
||||||
|
/// \param dt is the pointer data-type
|
||||||
|
/// \param sz is the size of the dereferenced pointer
|
||||||
|
/// \return the dereferenced data-type
|
||||||
|
Datatype *TypeOp::propagateFromPointer(TypeFactory *t,Datatype *dt,int4 sz)
|
||||||
|
|
||||||
|
{
|
||||||
|
if (dt->getMetatype() != TYPE_PTR)
|
||||||
|
return (Datatype *)0;
|
||||||
|
Datatype *ptrto = ((TypePointer *)dt)->getPtrTo();
|
||||||
|
if (ptrto->isVariableLength())
|
||||||
|
return (Datatype *)0;
|
||||||
|
if (ptrto->getSize() == sz)
|
||||||
|
return ptrto;
|
||||||
|
// If we reach here, there is a size mismatch between the pointer data-type and the dereferenced value.
|
||||||
|
// We only propagate (partial) enumerations in this case.
|
||||||
|
if (dt->isPointerRel()) {
|
||||||
|
TypePointerRel *ptrrel = (TypePointerRel *)dt;
|
||||||
|
Datatype *res = t->getExactPiece(ptrrel->getParent(), ptrrel->getByteOffset(), sz);
|
||||||
|
if (res != (Datatype *)0 && res->isEnumType())
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
else if (ptrto->isEnumType() && !ptrto->hasStripped()) {
|
||||||
|
return t->getTypePartialEnum((TypeEnum *)ptrto, 0, sz);
|
||||||
|
}
|
||||||
|
return (Datatype *)0;
|
||||||
|
}
|
||||||
|
|
||||||
/// \param t is the TypeFactory used to construct data-types
|
/// \param t is the TypeFactory used to construct data-types
|
||||||
/// \param opc is the op-code value the new object will represent
|
/// \param opc is the op-code value the new object will represent
|
||||||
/// \param n is the display name that will represent the op-code
|
/// \param n is the display name that will represent the op-code
|
||||||
@ -463,13 +494,8 @@ Datatype *TypeOpLoad::propagateType(Datatype *alttype,PcodeOp *op,Varnode *invn,
|
|||||||
AddrSpace *spc = op->getIn(0)->getSpaceFromConst();
|
AddrSpace *spc = op->getIn(0)->getSpaceFromConst();
|
||||||
newtype = propagateToPointer(tlst,alttype,outvn->getSize(),spc->getWordSize());
|
newtype = propagateToPointer(tlst,alttype,outvn->getSize(),spc->getWordSize());
|
||||||
}
|
}
|
||||||
else if (alttype->getMetatype()==TYPE_PTR) {
|
|
||||||
newtype = ((TypePointer *)alttype)->getPtrTo();
|
|
||||||
if (newtype->getSize() != outvn->getSize() || newtype->isVariableLength()) // Size must be appropriate
|
|
||||||
newtype = (Datatype *)0;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
newtype = (Datatype *)0; // Don't propagate anything
|
newtype = propagateFromPointer(tlst, alttype, outvn->getSize());
|
||||||
return newtype;
|
return newtype;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -538,13 +564,8 @@ Datatype *TypeOpStore::propagateType(Datatype *alttype,PcodeOp *op,Varnode *invn
|
|||||||
AddrSpace *spc = op->getIn(0)->getSpaceFromConst();
|
AddrSpace *spc = op->getIn(0)->getSpaceFromConst();
|
||||||
newtype = propagateToPointer(tlst,alttype,outvn->getSize(),spc->getWordSize());
|
newtype = propagateToPointer(tlst,alttype,outvn->getSize(),spc->getWordSize());
|
||||||
}
|
}
|
||||||
else if (alttype->getMetatype()==TYPE_PTR) {
|
|
||||||
newtype = ((TypePointer *)alttype)->getPtrTo();
|
|
||||||
if (newtype->getSize() != outvn->getSize() || newtype->isVariableLength())
|
|
||||||
newtype = (Datatype *)0;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
newtype = (Datatype *)0; // Don't propagate anything
|
newtype = propagateFromPointer(tlst, alttype, outvn->getSize());
|
||||||
return newtype;
|
return newtype;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -933,7 +954,7 @@ Datatype *TypeOpEqual::propagateAcrossCompare(Datatype *alttype,TypeFactory *typ
|
|||||||
}
|
}
|
||||||
else if (alttype->isPointerRel() && !outvn->isConstant()) {
|
else if (alttype->isPointerRel() && !outvn->isConstant()) {
|
||||||
TypePointerRel *relPtr = (TypePointerRel *)alttype;
|
TypePointerRel *relPtr = (TypePointerRel *)alttype;
|
||||||
if (relPtr->getParent()->getMetatype() == TYPE_STRUCT && relPtr->getPointerOffset() >= 0) {
|
if (relPtr->getParent()->getMetatype() == TYPE_STRUCT && relPtr->getByteOffset() >= 0) {
|
||||||
// If we know the pointer is in the middle of a structure, don't propagate across comparison operators
|
// If we know the pointer is in the middle of a structure, don't propagate across comparison operators
|
||||||
// as the two sides of the operator are likely to be different types , and the other side can also
|
// as the two sides of the operator are likely to be different types , and the other side can also
|
||||||
// get data-type information from the structure pointer
|
// get data-type information from the structure pointer
|
||||||
@ -1383,7 +1404,7 @@ Datatype *TypeOpIntXor::getOutputToken(const PcodeOp *op,CastStrategy *castStrat
|
|||||||
Datatype *TypeOpIntXor::propagateType(Datatype *alttype,PcodeOp *op,Varnode *invn,Varnode *outvn,
|
Datatype *TypeOpIntXor::propagateType(Datatype *alttype,PcodeOp *op,Varnode *invn,Varnode *outvn,
|
||||||
int4 inslot,int4 outslot)
|
int4 inslot,int4 outslot)
|
||||||
{
|
{
|
||||||
if (!alttype->isPowerOfTwo()) {
|
if (!alttype->isEnumType()) {
|
||||||
if (alttype->getMetatype() != TYPE_FLOAT)
|
if (alttype->getMetatype() != TYPE_FLOAT)
|
||||||
return (Datatype *)0;
|
return (Datatype *)0;
|
||||||
if (floatSignManipulation(op) == CPUI_MAX)
|
if (floatSignManipulation(op) == CPUI_MAX)
|
||||||
@ -1416,7 +1437,7 @@ Datatype *TypeOpIntAnd::getOutputToken(const PcodeOp *op,CastStrategy *castStrat
|
|||||||
Datatype *TypeOpIntAnd::propagateType(Datatype *alttype,PcodeOp *op,Varnode *invn,Varnode *outvn,
|
Datatype *TypeOpIntAnd::propagateType(Datatype *alttype,PcodeOp *op,Varnode *invn,Varnode *outvn,
|
||||||
int4 inslot,int4 outslot)
|
int4 inslot,int4 outslot)
|
||||||
{
|
{
|
||||||
if (!alttype->isPowerOfTwo()) {
|
if (!alttype->isEnumType()) {
|
||||||
if (alttype->getMetatype() != TYPE_FLOAT)
|
if (alttype->getMetatype() != TYPE_FLOAT)
|
||||||
return (Datatype *)0;
|
return (Datatype *)0;
|
||||||
if (floatSignManipulation(op) == CPUI_MAX)
|
if (floatSignManipulation(op) == CPUI_MAX)
|
||||||
@ -1449,7 +1470,7 @@ Datatype *TypeOpIntOr::getOutputToken(const PcodeOp *op,CastStrategy *castStrate
|
|||||||
Datatype *TypeOpIntOr::propagateType(Datatype *alttype,PcodeOp *op,Varnode *invn,Varnode *outvn,
|
Datatype *TypeOpIntOr::propagateType(Datatype *alttype,PcodeOp *op,Varnode *invn,Varnode *outvn,
|
||||||
int4 inslot,int4 outslot)
|
int4 inslot,int4 outslot)
|
||||||
{
|
{
|
||||||
if (!alttype->isPowerOfTwo()) return (Datatype *)0; // Only propagate flag enums
|
if (!alttype->isEnumType()) return (Datatype *)0; // Only propagate enums
|
||||||
Datatype *newtype;
|
Datatype *newtype;
|
||||||
if (invn->isSpacebase()) {
|
if (invn->isSpacebase()) {
|
||||||
AddrSpace *spc = tlst->getArch()->getDefaultDataSpace();
|
AddrSpace *spc = tlst->getArch()->getDefaultDataSpace();
|
||||||
|
@ -181,6 +181,7 @@ public:
|
|||||||
/// \brief Return the floating-point operation associated with the \e sign bit manipulation by the given PcodeOp
|
/// \brief Return the floating-point operation associated with the \e sign bit manipulation by the given PcodeOp
|
||||||
static OpCode floatSignManipulation(PcodeOp *op);
|
static OpCode floatSignManipulation(PcodeOp *op);
|
||||||
static Datatype *propagateToPointer(TypeFactory *t,Datatype *dt,int4 sz,int4 wordsz);
|
static Datatype *propagateToPointer(TypeFactory *t,Datatype *dt,int4 sz,int4 wordsz);
|
||||||
|
static Datatype *propagateFromPointer(TypeFactory *t,Datatype *dt,int4 sz);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Major classes of operations
|
// Major classes of operations
|
||||||
|
@ -297,6 +297,27 @@ void HighVariable::transferPiece(HighVariable *tv2)
|
|||||||
tv2->highflags &= ~(uint4)(intersectdirty | extendcoverdirty);
|
tv2->highflags &= ~(uint4)(intersectdirty | extendcoverdirty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Except in specific circumstances, convert \b type into its stripped form.
|
||||||
|
void HighVariable::stripType(void) const
|
||||||
|
|
||||||
|
{
|
||||||
|
if (!type->hasStripped())
|
||||||
|
return;
|
||||||
|
if (type->getMetatype() == TYPE_PARTIALUNION) {
|
||||||
|
if (symbol != (Symbol *)0 && symboloffset != -1) {
|
||||||
|
type_metatype meta = symbol->getType()->getMetatype();
|
||||||
|
if (meta != TYPE_STRUCT && meta != TYPE_UNION) // If partial union does not have a bigger backing symbol
|
||||||
|
type = type->getStripped(); // strip the partial union
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (type->isEnumType()) {
|
||||||
|
if (inst.size() != 1 || !inst[0]->isConstant()) // Only preserve partial enum on a constant
|
||||||
|
type = type->getStripped();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
type = type->getStripped();
|
||||||
|
}
|
||||||
|
|
||||||
/// Only update if the cover is marked as \e dirty.
|
/// Only update if the cover is marked as \e dirty.
|
||||||
/// Merge the covers of all Varnode instances.
|
/// Merge the covers of all Varnode instances.
|
||||||
void HighVariable::updateInternalCover(void) const
|
void HighVariable::updateInternalCover(void) const
|
||||||
@ -386,17 +407,7 @@ void HighVariable::updateType(void) const
|
|||||||
vn = getTypeRepresentative();
|
vn = getTypeRepresentative();
|
||||||
|
|
||||||
type = vn->getType();
|
type = vn->getType();
|
||||||
if (type->hasStripped()) {
|
stripType();
|
||||||
if (type->getMetatype() == TYPE_PARTIALUNION) {
|
|
||||||
if (symbol != (Symbol *)0 && symboloffset != -1) {
|
|
||||||
type_metatype meta = symbol->getType()->getMetatype();
|
|
||||||
if (meta != TYPE_STRUCT && meta != TYPE_UNION) // If partial union does not have a bigger backing symbol
|
|
||||||
type = type->getStripped(); // strip the partial union
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
type = type->getStripped();
|
|
||||||
}
|
|
||||||
// Update lock flags
|
// Update lock flags
|
||||||
flags &= ~Varnode::typelock;
|
flags &= ~Varnode::typelock;
|
||||||
if (vn->isTypeLock())
|
if (vn->isTypeLock())
|
||||||
@ -549,17 +560,7 @@ void HighVariable::finalizeDatatype(TypeFactory *typeFactory)
|
|||||||
if (tp == (Datatype *)0 || tp->getMetatype() == TYPE_UNKNOWN)
|
if (tp == (Datatype *)0 || tp->getMetatype() == TYPE_UNKNOWN)
|
||||||
return;
|
return;
|
||||||
type = tp;
|
type = tp;
|
||||||
if (type->hasStripped()) {
|
stripType();
|
||||||
if (type->getMetatype() == TYPE_PARTIALUNION) {
|
|
||||||
if (symboloffset != -1) {
|
|
||||||
type_metatype meta = symbol->getType()->getMetatype();
|
|
||||||
if (meta != TYPE_STRUCT && meta != TYPE_UNION) // If partial union does not have a bigger backing symbol
|
|
||||||
type = type->getStripped(); // strip the partial union
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
type = type->getStripped();
|
|
||||||
}
|
|
||||||
highflags |= type_finalized;
|
highflags |= type_finalized;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,6 +167,7 @@ private:
|
|||||||
void symbolDirty(void) const { highflags |= symboldirty; } ///< Mark the symbol as \e dirty
|
void symbolDirty(void) const { highflags |= symboldirty; } ///< Mark the symbol as \e dirty
|
||||||
void setUnmerged(void) const { highflags |= unmerged; } ///< Mark \b this as having merge problems
|
void setUnmerged(void) const { highflags |= unmerged; } ///< Mark \b this as having merge problems
|
||||||
bool isCoverDirty(void) const; ///< Is the cover returned by getCover() up-to-date
|
bool isCoverDirty(void) const; ///< Is the cover returned by getCover() up-to-date
|
||||||
|
void stripType(void) const; ///< Take the stripped form of the current data-type.
|
||||||
public:
|
public:
|
||||||
HighVariable(Varnode *vn); ///< Construct a HighVariable with a single member Varnode
|
HighVariable(Varnode *vn); ///< Construct a HighVariable with a single member Varnode
|
||||||
~HighVariable(void); ///< Destructor
|
~HighVariable(void); ///< Destructor
|
||||||
|
108
Ghidra/Features/Decompiler/src/decompile/datatests/enum.xml
Normal file
108
Ghidra/Features/Decompiler/src/decompile/datatests/enum.xml
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
<decompilertest>
|
||||||
|
<binaryimage arch="x86:LE:64:default:gcc">
|
||||||
|
<!--
|
||||||
|
Functions that read enum values and compare with constant values that should be printed by name.
|
||||||
|
-->
|
||||||
|
<bytechunk space="ram" offset="0x100000" readonly="true">
|
||||||
|
4883ec184889e7e8f40f0000f6442408
|
||||||
|
4175054883c418c3bf00000000e8e60f
|
||||||
|
0000ebef4883ec184889e7e8d00f0000
|
||||||
|
f744240c0003000075054883c418c3bf
|
||||||
|
00000000e8bf0f0000ebef
|
||||||
|
</bytechunk>
|
||||||
|
<bytechunk space="ram" offset="0x100065" readonly="true">
|
||||||
|
f647082c7501c34883ec08
|
||||||
|
bf02000000e88e0f00004883c408c3f7
|
||||||
|
470c571300007501c34883ec08bf0300
|
||||||
|
0000e8710f00004883c408c3
|
||||||
|
</bytechunk>
|
||||||
|
<bytechunk space="ram" offset="0x1000d3" readonly="true">
|
||||||
|
48817f08000810007401c34883
|
||||||
|
ec08bf06000000e81c0f00004883c408
|
||||||
|
c3
|
||||||
|
</bytechunk>
|
||||||
|
<symbol space="ram" offset="0x100000" name="stackenumlow"/>
|
||||||
|
<symbol space="ram" offset="0x100024" name="stackenumhigh"/>
|
||||||
|
<symbol space="ram" offset="0x100065" name="ptrenumlow"/>
|
||||||
|
<symbol space="ram" offset="0x10007f" name="ptrenumhigh"/>
|
||||||
|
<symbol space="ram" offset="0x1000d3" name="ptrenumequal"/>
|
||||||
|
<symbol space="ram" offset="0x101000" name="setStruct"/>
|
||||||
|
</binaryimage>
|
||||||
|
<script>
|
||||||
|
<com>parse line enum flags {
|
||||||
|
FLAG_1=1,
|
||||||
|
FLAG_2 = 2,
|
||||||
|
FLAG_4 = 4,
|
||||||
|
FLAG_8 = 8,
|
||||||
|
FLAG_10 = 0x10,
|
||||||
|
FLAG_20 = 0x20,
|
||||||
|
FLAG_40 = 0x40,
|
||||||
|
FLAG_80 = 0x80,
|
||||||
|
FLAG_100 = 0x100,
|
||||||
|
FLAG_200 = 0x200,
|
||||||
|
FLAG_400 = 0x400,
|
||||||
|
FLAG_800 = 0x800,
|
||||||
|
FLAG_1000 = 0x1000,
|
||||||
|
FLAG_2000 = 0x2000,
|
||||||
|
FLAG_4000 = 0x4000,
|
||||||
|
FLAG_8000 = 0x8000,
|
||||||
|
FLAG_10000 = 0x10000,
|
||||||
|
FLAG_20000 = 0x20000,
|
||||||
|
FLAG_40000 = 0x40000,
|
||||||
|
FLAG_80000 = 0x80000,
|
||||||
|
FLAG_100000 = 0x100000,
|
||||||
|
FLAG_200000 = 0x200000,
|
||||||
|
FLAG_400000 = 0x400000,
|
||||||
|
FLAG_800000 = 0x800000,
|
||||||
|
HIGH_1 = 0x100000000,
|
||||||
|
HIGH_2 = 0x200000000,
|
||||||
|
HIGH_4 = 0x400000000,
|
||||||
|
HIGH_8 = 0x800000000,
|
||||||
|
HIGH_10 = 0x1000000000,
|
||||||
|
HIGH_20 = 0x2000000000,
|
||||||
|
HIGH_40 = 0x4000000000,
|
||||||
|
HIGH_80 = 0x8000000000,
|
||||||
|
HIGH_100 = 0x10000000000,
|
||||||
|
HIGH_200 = 0x20000000000,
|
||||||
|
HIGH_400 = 0x40000000000,
|
||||||
|
HIGH_800 = 0x80000000000,
|
||||||
|
HIGH_1000 = 0x100000000000,
|
||||||
|
HIGH_2000 = 0x200000000000,
|
||||||
|
HIGH_4000 = 0x400000000000,
|
||||||
|
HIGH_8000 = 0x800000000000,
|
||||||
|
HIGH_10000 = 0x1000000000000,
|
||||||
|
HIGH_20000 = 0x2000000000000,
|
||||||
|
HIGH_40000 = 0x4000000000000,
|
||||||
|
HIGH_80000 = 0x8000000000000,
|
||||||
|
HIGH_100000 = 0x10000000000000,
|
||||||
|
HIGH_200000 = 0x20000000000000,
|
||||||
|
HIGH_400000 = 0x40000000000000,
|
||||||
|
HIGH_800000 = 0x80000000000000 };</com>
|
||||||
|
<com>parse line struct enumstruct { int4 a; int4 b; flags flagfield; };</com>
|
||||||
|
<com>parse line extern void setStruct(enumstruct *ptr);</com>
|
||||||
|
<com>parse line extern void ptrenumlow(enumstruct *ptr);</com>
|
||||||
|
<com>parse line extern void ptrenumhigh(enumstruct *ptrhigh);</com>
|
||||||
|
<com>parse line extern void ptrenumequal(enumstruct *ptrequal);</com>
|
||||||
|
<com>lo fu stackenumlow</com>
|
||||||
|
<com>decompile</com>
|
||||||
|
<com>print C</com>
|
||||||
|
<com>lo fu stackenumhigh</com>
|
||||||
|
<com>decompile</com>
|
||||||
|
<com>print C</com>
|
||||||
|
<com>lo fu ptrenumlow</com>
|
||||||
|
<com>decompile</com>
|
||||||
|
<com>print C</com>
|
||||||
|
<com>lo fu ptrenumhigh</com>
|
||||||
|
<com>decompile</com>
|
||||||
|
<com>print C</com>
|
||||||
|
<com>lo fu ptrenumequal</com>
|
||||||
|
<com>decompile</com>
|
||||||
|
<com>print C</com>
|
||||||
|
<com>quit</com>
|
||||||
|
</script>
|
||||||
|
<stringmatch name="Enum Reading #1" min="1" max="1">Stack_18\.flagfield & \(FLAG_40\|FLAG_1\)\) .= 0\)</stringmatch>
|
||||||
|
<stringmatch name="Enum Reading #2" min="1" max="1">Stack_18\.flagfield\._4_4_ & \(HIGH_200\|HIGH_100\) >> 0x20\) .= 0\)</stringmatch>
|
||||||
|
<stringmatch name="Enum Reading #3" min="1" max="1">ptr->flagfield & \(FLAG_20\|FLAG_8\|FLAG_4\)\) .= 0\)</stringmatch>
|
||||||
|
<stringmatch name="Enum Reading #4" min="1" max="1">if \(\(ptrhigh->flagfield & \(HIGH_1000\|HIGH_200\|HIGH_100\|HIGH_40\|HIGH_10\|HIGH_4\|HIGH_2\|HIGH_1\)\) .= 0</stringmatch>
|
||||||
|
<stringmatch name="Enum Reading #5" min="1" max="1">if \(ptrequal->flagfield .= \(FLAG_100000\|FLAG_800\)\)</stringmatch>
|
||||||
|
</decompilertest>
|
@ -212,4 +212,52 @@ TEST(cast_integertoken) {
|
|||||||
ASSERT(!longPrinted(CPUI_INT_RIGHT,parse("uint8"),0x100000000));
|
ASSERT(!longPrinted(CPUI_INT_RIGHT,parse("uint8"),0x100000000));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(enum_matching) {
|
||||||
|
TypeTestEnvironment::build();
|
||||||
|
TypeEnum *enum3 = (TypeEnum *)parse("enum enum3 { ZERO=0, ONE=1, TWO=2, FOUR=4, EIGHT=8 }");
|
||||||
|
TypeEnum::Representation rep;
|
||||||
|
enum3->getMatches(5, rep);
|
||||||
|
ASSERT(rep.matchname.size() == 2);
|
||||||
|
ASSERT(rep.matchname[0] == "FOUR");
|
||||||
|
ASSERT(rep.matchname[1] == "ONE");
|
||||||
|
ASSERT(!rep.complement);
|
||||||
|
rep.matchname.clear();
|
||||||
|
enum3->getMatches(0xfffffffffffffff7,rep);
|
||||||
|
ASSERT(rep.matchname.size() == 1);
|
||||||
|
ASSERT(rep.matchname[0] == "EIGHT");
|
||||||
|
ASSERT(rep.complement);
|
||||||
|
rep.matchname.clear();
|
||||||
|
rep.complement = false;
|
||||||
|
enum3->getMatches(0,rep);
|
||||||
|
ASSERT(rep.matchname.size() == 1);
|
||||||
|
ASSERT(rep.matchname[0] == "ZERO");
|
||||||
|
ASSERT(!rep.complement);
|
||||||
|
rep.matchname.clear();
|
||||||
|
enum3->getMatches(0x10, rep);
|
||||||
|
ASSERT(rep.matchname.size() == 0);
|
||||||
|
ASSERT(!rep.complement);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(enum_matching2) {
|
||||||
|
TypeTestEnvironment::build();
|
||||||
|
TypeEnum *enum4 = (TypeEnum *)parse("enum enum4 { ZERO=0, ONE=1, TWO=2, FOUR=4, SIX=6, EIGHT=8, ELEVEN=11 }");
|
||||||
|
TypeEnum::Representation rep;
|
||||||
|
enum4->getMatches(12,rep);
|
||||||
|
ASSERT(rep.matchname.size()==2);
|
||||||
|
ASSERT(rep.matchname[0] == "EIGHT");
|
||||||
|
ASSERT(rep.matchname[1] == "FOUR");
|
||||||
|
ASSERT(!rep.complement);
|
||||||
|
rep.matchname.clear();
|
||||||
|
enum4->getMatches(7,rep);
|
||||||
|
ASSERT(rep.matchname.size()==2);
|
||||||
|
ASSERT(rep.matchname[0] == "SIX");
|
||||||
|
ASSERT(rep.matchname[1] == "ONE");
|
||||||
|
ASSERT(!rep.complement);
|
||||||
|
rep.matchname.clear();
|
||||||
|
enum4->getMatches(11,rep);
|
||||||
|
ASSERT(rep.matchname.size()==1);
|
||||||
|
ASSERT(rep.matchname[0] == "ELEVEN");
|
||||||
|
ASSERT(!rep.complement);
|
||||||
|
}
|
||||||
|
|
||||||
} // End namespace ghidra
|
} // End namespace ghidra
|
||||||
|
@ -114,7 +114,7 @@ public record AttributeId(String name, int id) {
|
|||||||
public static final AttributeId ATTRIB_ARRAYSIZE = new AttributeId("arraysize", 48);
|
public static final AttributeId ATTRIB_ARRAYSIZE = new AttributeId("arraysize", 48);
|
||||||
public static final AttributeId ATTRIB_CHAR = new AttributeId("char", 49);
|
public static final AttributeId ATTRIB_CHAR = new AttributeId("char", 49);
|
||||||
public static final AttributeId ATTRIB_CORE = new AttributeId("core", 50);
|
public static final AttributeId ATTRIB_CORE = new AttributeId("core", 50);
|
||||||
public static final AttributeId ATTRIB_ENUM = new AttributeId("enum", 51);
|
// public static final AttributeId ATTRIB_ENUM = new AttributeId("enum", 51); // deprecated
|
||||||
public static final AttributeId ATTRIB_INCOMPLETE = new AttributeId("incomplete", 52);
|
public static final AttributeId ATTRIB_INCOMPLETE = new AttributeId("incomplete", 52);
|
||||||
// public static final AttributeId ATTRIB_ENUMSIZE = new AttributeId("enumsize", 53); // deprecated
|
// public static final AttributeId ATTRIB_ENUMSIZE = new AttributeId("enumsize", 53); // deprecated
|
||||||
// public static final AttributeId ATTRIB_INTSIZE = new AttributeId("intsize", 54); // deprecated
|
// public static final AttributeId ATTRIB_INTSIZE = new AttributeId("intsize", 54); // deprecated
|
||||||
|
@ -278,6 +278,13 @@ public class PcodeDataTypeManager {
|
|||||||
decoder.closeElement(el);
|
decoder.closeElement(el);
|
||||||
return new PartialUnion(progDataTypes, dt, offset, size);
|
return new PartialUnion(progDataTypes, dt, offset, size);
|
||||||
}
|
}
|
||||||
|
else if (meta.equals("partenum")) {
|
||||||
|
int size = (int) decoder.readSignedInteger(ATTRIB_SIZE);
|
||||||
|
// int offset = (int) decoder.readSignedInteger(ATTRIB_OFFSET);
|
||||||
|
// DataType dt = decodeDataType(decoder);
|
||||||
|
decoder.closeElementSkipping(el);
|
||||||
|
return AbstractIntegerDataType.getUnsignedDataType(size, progDataTypes);
|
||||||
|
}
|
||||||
else { // We typically reach here if the decompiler invents a new type
|
else { // We typically reach here if the decompiler invents a new type
|
||||||
// probably an unknown with a non-standard size
|
// probably an unknown with a non-standard size
|
||||||
int size = (int) decoder.readSignedInteger(ATTRIB_SIZE);
|
int size = (int) decoder.readSignedInteger(ATTRIB_SIZE);
|
||||||
@ -541,11 +548,10 @@ public class PcodeDataTypeManager {
|
|||||||
private void encodeEnum(Encoder encoder, Enum type, int size) throws IOException {
|
private void encodeEnum(Encoder encoder, Enum type, int size) throws IOException {
|
||||||
encoder.openElement(ELEM_TYPE);
|
encoder.openElement(ELEM_TYPE);
|
||||||
encodeNameIdAttributes(encoder, type);
|
encodeNameIdAttributes(encoder, type);
|
||||||
String metatype = type.isSigned() ? "int" : "uint";
|
String metatype = type.isSigned() ? "enum_int" : "enum_uint";
|
||||||
String[] names = type.getNames();
|
String[] names = type.getNames();
|
||||||
encoder.writeString(ATTRIB_METATYPE, metatype);
|
encoder.writeString(ATTRIB_METATYPE, metatype);
|
||||||
encoder.writeSignedInteger(ATTRIB_SIZE, type.getLength());
|
encoder.writeSignedInteger(ATTRIB_SIZE, type.getLength());
|
||||||
encoder.writeBool(ATTRIB_ENUM, true);
|
|
||||||
for (String name : names) {
|
for (String name : names) {
|
||||||
encoder.openElement(ELEM_VAL);
|
encoder.openElement(ELEM_VAL);
|
||||||
encoder.writeString(ATTRIB_NAME, name);
|
encoder.writeString(ATTRIB_NAME, name);
|
||||||
|
Loading…
Reference in New Issue
Block a user