GP-5922 Integer formatting for switch cases

This commit is contained in:
caheckman
2026-02-10 19:33:46 +00:00
parent 069b90f507
commit bf1f4288aa
29 changed files with 1005 additions and 782 deletions
@@ -140,7 +140,7 @@ public class SwitchOverride extends GhidraScript {
} }
// Allocate an override jumptable // Allocate an override jumptable
JumpTable jumpTab = new JumpTable(branchind, destlist, true); JumpTable jumpTab = new JumpTable(branchind, destlist, true, 0);
jumpTab.writeOverride(function); jumpTab.writeOverride(function);
// fixup the body now that there are jump references // fixup the body now that there are jump references
@@ -790,6 +790,7 @@ public:
uint4 getGotoType(int4 i) const { return caseblocks[i].gototype; } ///< Get the edge type for the i-th \e case block uint4 getGotoType(int4 i) const { return caseblocks[i].gototype; } ///< Get the edge type for the i-th \e case block
bool isExit(int4 i) const { return caseblocks[i].isexit; } ///< Does the i-th \e case block exit the switch? bool isExit(int4 i) const { return caseblocks[i].isexit; } ///< Does the i-th \e case block exit the switch?
const Datatype *getSwitchType(void) const; ///< Get the data-type of the switch variable const Datatype *getSwitchType(void) const; ///< Get the data-type of the switch variable
uint4 getDisplayFormat(void) const { return jump->getDisplayFormat(); } ///< Get any integer display format for cases
virtual block_type getType(void) const { return t_switch; } virtual block_type getType(void) const { return t_switch; }
virtual void markUnstructured(void); virtual void markUnstructured(void);
virtual void scopeBreak(int4 curexit,int4 curloopexit); virtual void scopeBreak(int4 curexit,int4 curloopexit);
@@ -1059,7 +1059,7 @@ void FlowInfo::xrefInlinedBranch(PcodeOp *op)
setupCallindSpecs(op,(FuncCallSpecs *)0); setupCallindSpecs(op,(FuncCallSpecs *)0);
else if (op->code() == CPUI_BRANCHIND) { else if (op->code() == CPUI_BRANCHIND) {
JumpTable *jt = data.linkJumpTable(op); JumpTable *jt = data.linkJumpTable(op);
if (jt == (JumpTable *)0) if (jt == (JumpTable *)0 || jt->numEntries() == 0)
tablelist.push_back(op); // Didn't recover a jumptable tablelist.push_back(op); // Didn't recover a jumptable
} }
} }
@@ -615,7 +615,7 @@ void Funcdata::decodeJumpTable(Decoder &decoder)
{ {
uint4 elemId = decoder.openElement(ELEM_JUMPTABLELIST); uint4 elemId = decoder.openElement(ELEM_JUMPTABLELIST);
while(decoder.peekElement() != 0) { while(decoder.peekElement() != 0) {
JumpTable *jt = new JumpTable(glb); JumpTable *jt = new JumpTable();
jt->decode(decoder); jt->decode(decoder);
jumpvec.push_back(jt); jumpvec.push_back(jt);
} }
@@ -470,7 +470,7 @@ JumpTable *Funcdata::installJumpTable(const Address &addr)
if (jt->getOpAddress() == addr) if (jt->getOpAddress() == addr)
throw LowlevelError("Trying to install over existing jumptable"); throw LowlevelError("Trying to install over existing jumptable");
} }
JumpTable *newjt = new JumpTable(glb,addr); JumpTable *newjt = new JumpTable(addr);
jumpvec.push_back(newjt); jumpvec.push_back(newjt);
return newjt; return newjt;
} }
@@ -645,10 +645,10 @@ JumpTable *Funcdata::recoverJumpTable(Funcdata &partial,PcodeOp *op,FlowInfo *fl
jt = linkJumpTable(op); // Search for pre-existing jumptable jt = linkJumpTable(op); // Search for pre-existing jumptable
if (jt != (JumpTable *)0) { if (jt != (JumpTable *)0) {
if (!jt->isOverride()) { if (!jt->isOverride()) {
if (!jt->isPartial()) if (!jt->isPartial() && jt->numEntries() != 0)
return jt; // Previously calculated jumptable (NOT an override and NOT incomplete) return jt; // Previously calculated jumptable (NOT an override and NOT incomplete)
} }
mode = stageJumpTable(partial,jt,op,flow); // Recover based on override information mode = stageJumpTable(partial,jt,op,flow); // Recover empty jumptable or based on override information
if (mode != JumpTable::success) if (mode != JumpTable::success)
return (JumpTable *)0; return (JumpTable *)0;
jt->setIndirectOp(op); // Relink table back to original op jt->setIndirectOp(op); // Relink table back to original op
@@ -660,7 +660,7 @@ JumpTable *Funcdata::recoverJumpTable(Funcdata &partial,PcodeOp *op,FlowInfo *fl
mode = earlyJumpTableFail(op); mode = earlyJumpTableFail(op);
if (mode != JumpTable::success) if (mode != JumpTable::success)
return (JumpTable *)0; return (JumpTable *)0;
JumpTable trialjt(glb); JumpTable trialjt;
mode = stageJumpTable(partial,&trialjt,op,flow); mode = stageJumpTable(partial,&trialjt,op,flow);
if (mode != JumpTable::success) if (mode != JumpTable::success)
return (JumpTable *)0; return (JumpTable *)0;
@@ -2271,9 +2271,10 @@ void JumpTable::clearSavedModel(void)
void JumpTable::recoverModel(Funcdata *fd) void JumpTable::recoverModel(Funcdata *fd)
{ {
uint4 maxTableSize = fd->getArch()->max_jumptable_size;
if (jmodel != (JumpModel *)0) { if (jmodel != (JumpModel *)0) {
if (jmodel->isOverride()) { // If preexisting model is override if (jmodel->isOverride()) { // If preexisting model is override
jmodel->recoverModel(fd,indirect,0,glb->max_jumptable_size); jmodel->recoverModel(fd,indirect,0,maxTableSize);
return; return;
} }
delete jmodel; // Otherwise this is an old attempt we should remove delete jmodel; // Otherwise this is an old attempt we should remove
@@ -2284,18 +2285,18 @@ void JumpTable::recoverModel(Funcdata *fd)
if (op->code() == CPUI_CALLOTHER) { if (op->code() == CPUI_CALLOTHER) {
JumpAssisted *jassisted = new JumpAssisted(this); JumpAssisted *jassisted = new JumpAssisted(this);
jmodel = jassisted; jmodel = jassisted;
if (jmodel->recoverModel(fd,indirect,addresstable.size(),glb->max_jumptable_size)) if (jmodel->recoverModel(fd,indirect,addresstable.size(),maxTableSize))
return; return;
} }
} }
JumpBasic *jbasic = new JumpBasic(this); JumpBasic *jbasic = new JumpBasic(this);
jmodel = jbasic; jmodel = jbasic;
if (jmodel->recoverModel(fd,indirect,addresstable.size(),glb->max_jumptable_size)) if (jmodel->recoverModel(fd,indirect,addresstable.size(),maxTableSize))
return; return;
jmodel = new JumpBasic2(this); jmodel = new JumpBasic2(this);
((JumpBasic2 *)jmodel)->initializeStart(jbasic->getPathMeld()); ((JumpBasic2 *)jmodel)->initializeStart(jbasic->getPathMeld());
delete jbasic; delete jbasic;
if (jmodel->recoverModel(fd,indirect,addresstable.size(),glb->max_jumptable_size)) if (jmodel->recoverModel(fd,indirect,addresstable.size(),maxTableSize))
return; return;
delete jmodel; delete jmodel;
jmodel = (JumpModel *)0; jmodel = (JumpModel *)0;
@@ -2392,12 +2393,10 @@ bool JumpTable::isReachable(PcodeOp *op)
return true; return true;
} }
/// \param g is the Architecture the table exists within
/// \param ad is the Address of the BRANCHIND \b this models /// \param ad is the Address of the BRANCHIND \b this models
JumpTable::JumpTable(Architecture *g,Address ad) JumpTable::JumpTable(Address ad)
: opaddress(ad) : opaddress(ad)
{ {
glb = g;
jmodel = (JumpModel *)0; jmodel = (JumpModel *)0;
origmodel = (JumpModel *)0; origmodel = (JumpModel *)0;
indirect = (PcodeOp *)0; indirect = (PcodeOp *)0;
@@ -2407,6 +2406,7 @@ JumpTable::JumpTable(Architecture *g,Address ad)
maxaddsub = 1; maxaddsub = 1;
maxleftright = 1; maxleftright = 1;
maxext = 1; maxext = 1;
displayFormat = 0;
partialTable = false; partialTable = false;
collectloads = false; collectloads = false;
defaultIsFolded = false; defaultIsFolded = false;
@@ -2418,7 +2418,6 @@ JumpTable::JumpTable(Architecture *g,Address ad)
JumpTable::JumpTable(const JumpTable *op2) JumpTable::JumpTable(const JumpTable *op2)
{ {
glb = op2->glb;
jmodel = (JumpModel *)0; jmodel = (JumpModel *)0;
origmodel = (JumpModel *)0; origmodel = (JumpModel *)0;
indirect = (PcodeOp *)0; indirect = (PcodeOp *)0;
@@ -2428,6 +2427,7 @@ JumpTable::JumpTable(const JumpTable *op2)
maxaddsub = op2->maxaddsub; maxaddsub = op2->maxaddsub;
maxleftright = op2->maxleftright; maxleftright = op2->maxleftright;
maxext = op2->maxext; maxext = op2->maxext;
displayFormat = op2->displayFormat;
partialTable = op2->partialTable; partialTable = op2->partialTable;
collectloads = op2->collectloads; collectloads = op2->collectloads;
defaultIsFolded = false; defaultIsFolded = false;
@@ -2743,7 +2743,7 @@ void JumpTable::recoverLabels(Funcdata *fd)
} }
else { else {
jmodel = new JumpModelTrivial(this); jmodel = new JumpModelTrivial(this);
jmodel->recoverModel(fd,indirect,addresstable.size(),glb->max_jumptable_size); jmodel->recoverModel(fd,indirect,addresstable.size(),fd->getArch()->max_jumptable_size);
jmodel->buildAddresses(fd,indirect,addresstable,(vector<LoadTable> *)0,(vector<int4> *)0); jmodel->buildAddresses(fd,indirect,addresstable,(vector<LoadTable> *)0,(vector<int4> *)0);
trivialSwitchOver(); trivialSwitchOver();
jmodel->buildLabels(fd,addresstable,label,origmodel); jmodel->buildLabels(fd,addresstable,label,origmodel);
@@ -2785,6 +2785,8 @@ void JumpTable::encode(Encoder &encoder) const
throw LowlevelError("Trying to save unrecovered jumptable"); throw LowlevelError("Trying to save unrecovered jumptable");
encoder.openElement(ELEM_JUMPTABLE); encoder.openElement(ELEM_JUMPTABLE);
if (displayFormat != 0)
encoder.writeUnsignedInteger(ATTRIB_FORMAT, displayFormat);
opaddress.encode(encoder); opaddress.encode(encoder);
for(int4 i=0;i<addresstable.size();++i) { for(int4 i=0;i<addresstable.size();++i) {
encoder.openElement(ELEM_DEST); encoder.openElement(ELEM_DEST);
@@ -2814,6 +2816,8 @@ void JumpTable::decode(Decoder &decoder)
{ {
uint4 elemId = decoder.openElement(ELEM_JUMPTABLE); uint4 elemId = decoder.openElement(ELEM_JUMPTABLE);
if (decoder.getNextAttributeId() == ATTRIB_FORMAT)
displayFormat = decoder.readUnsignedInteger();
opaddress = Address::decode( decoder ); opaddress = Address::decode( decoder );
bool missedlabel = false; bool missedlabel = false;
for(;;) { for(;;) {
@@ -557,7 +557,6 @@ private:
bool operator<(const IndexPair &op2) const; ///< Compare by position then by index bool operator<(const IndexPair &op2) const; ///< Compare by position then by index
static bool compareByPosition(const IndexPair &op1,const IndexPair &op2); ///< Compare just by position static bool compareByPosition(const IndexPair &op1,const IndexPair &op2); ///< Compare just by position
}; };
Architecture *glb; ///< Architecture under which this jump-table operates
JumpModel *jmodel; ///< Current model of how the jump table is implemented in code JumpModel *jmodel; ///< Current model of how the jump table is implemented in code
JumpModel *origmodel; ///< Initial jump table model, which may be incomplete JumpModel *origmodel; ///< Initial jump table model, which may be incomplete
vector<Address> addresstable; ///< Raw addresses in the jump-table vector<Address> addresstable; ///< Raw addresses in the jump-table
@@ -572,6 +571,7 @@ private:
uint4 maxaddsub; ///< Maximum ADDs or SUBs to normalize uint4 maxaddsub; ///< Maximum ADDs or SUBs to normalize
uint4 maxleftright; ///< Maximum shifts to normalize uint4 maxleftright; ///< Maximum shifts to normalize
uint4 maxext; ///< Maximum extensions to normalize uint4 maxext; ///< Maximum extensions to normalize
uint4 displayFormat; ///< Display format for integer \e case values
bool partialTable; ///< Set to \b true if \b this table is incomplete and needs additional recovery steps bool partialTable; ///< Set to \b true if \b this table is incomplete and needs additional recovery steps
bool collectloads; ///< Set to \b true if information about in-memory model data is/should be collected bool collectloads; ///< Set to \b true if information about in-memory model data is/should be collected
bool defaultIsFolded; ///< The \e default block is the target of a folded CBRANCH (and cannot have a label) bool defaultIsFolded; ///< The \e default block is the target of a folded CBRANCH (and cannot have a label)
@@ -584,7 +584,7 @@ private:
int4 block2Position(const FlowBlock *bl) const; ///< Convert a basic-block to an out-edge index from the switch. int4 block2Position(const FlowBlock *bl) const; ///< Convert a basic-block to an out-edge index from the switch.
static bool isReachable(PcodeOp *op); ///< Check if the given PcodeOp still seems reachable in its function static bool isReachable(PcodeOp *op); ///< Check if the given PcodeOp still seems reachable in its function
public: public:
JumpTable(Architecture *g,Address ad=Address()); ///< Constructor JumpTable(Address ad=Address()); ///< Constructor
JumpTable(const JumpTable *op2); ///< Copy constructor JumpTable(const JumpTable *op2); ///< Copy constructor
~JumpTable(void); ///< Destructor ~JumpTable(void); ///< Destructor
bool isRecovered(void) const { return !addresstable.empty(); } ///< Return \b true if a model has been recovered bool isRecovered(void) const { return !addresstable.empty(); } ///< Return \b true if a model has been recovered
@@ -600,6 +600,8 @@ public:
void setIndirectOp(PcodeOp *ind) { opaddress = ind->getAddr(); indirect = ind; } ///< Set the BRANCHIND PcodeOp void setIndirectOp(PcodeOp *ind) { opaddress = ind->getAddr(); indirect = ind; } ///< Set the BRANCHIND PcodeOp
void setNormMax(uint4 maddsub,uint4 mleftright,uint4 mext) { void setNormMax(uint4 maddsub,uint4 mleftright,uint4 mext) {
maxaddsub = maddsub; maxleftright = mleftright; maxext = mext; } ///< Set the switch variable normalization model restrictions maxaddsub = maddsub; maxleftright = mleftright; maxext = mext; } ///< Set the switch variable normalization model restrictions
uint4 getDisplayFormat(void) const { return displayFormat; } ///< Get the display format for integer cases
void setDisplayFormat(uint4 format) { displayFormat = format; } ///< Set the display format to use for integer case values
void setOverride(const vector<Address> &addrtable,const Address &naddr,uintb h,uintb sv); void setOverride(const vector<Address> &addrtable,const Address &naddr,uintb h,uintb sv);
int4 numIndicesByBlock(const FlowBlock *bl) const; int4 numIndicesByBlock(const FlowBlock *bl) const;
int4 getIndexByBlock(const FlowBlock *bl,int4 i) const; int4 getIndexByBlock(const FlowBlock *bl,int4 i) const;
@@ -325,7 +325,7 @@ void PrintC::pushTypeEnd(const Datatype *ct)
const TypeArray *ctarray = (const TypeArray *)ct; const TypeArray *ctarray = (const TypeArray *)ct;
ct = ctarray->getBase(); ct = ctarray->getBase();
push_integer(ctarray->numElements(),4,false,syntax, push_integer(ctarray->numElements(),4,false,syntax,
(const Varnode *)0,(const PcodeOp *)0); (const Varnode *)0,(const PcodeOp *)0,0);
} }
else if (ct->getMetatype()==TYPE_CODE) { else if (ct->getMetatype()==TYPE_CODE) {
const TypeCode *ctcode = (const TypeCode *)ct; const TypeCode *ctcode = (const TypeCode *)ct;
@@ -1051,7 +1051,7 @@ void PrintC::opPtrsub(const PcodeOp *op)
pushAtom(Atom(fieldname,fieldtoken,EmitMarkup::no_color,ct,fieldid,op)); pushAtom(Atom(fieldname,fieldtoken,EmitMarkup::no_color,ct,fieldid,op));
} }
if (arrayvalue) if (arrayvalue)
push_integer(0,4,false,syntax,(Varnode *)0,op); push_integer(0,4,false,syntax,(Varnode *)0,op,0);
} }
} }
else if (ct->getMetatype() == TYPE_SPACEBASE) { else if (ct->getMetatype() == TYPE_SPACEBASE) {
@@ -1093,7 +1093,7 @@ void PrintC::opPtrsub(const PcodeOp *op)
} }
} }
if (arrayvalue) if (arrayvalue)
push_integer(0,4,false,syntax,(Varnode *)0,op); push_integer(0,4,false,syntax,(Varnode *)0,op,0);
} }
else if (ct->getMetatype() == TYPE_ARRAY) { else if (ct->getMetatype() == TYPE_ARRAY) {
if (in1const != 0) { if (in1const != 0) {
@@ -1124,7 +1124,7 @@ void PrintC::opPtrsub(const PcodeOp *op)
if (ptrel != (TypePointerRel *)0) if (ptrel != (TypePointerRel *)0)
pushTypePointerRel(op); pushTypePointerRel(op);
pushVn(in0,op,m | print_load_value); // Absorb one dereference into in0's defining op pushVn(in0,op,m | print_load_value); // Absorb one dereference into in0's defining op
push_integer(0,4,false,syntax,(Varnode *)0,op); push_integer(0,4,false,syntax,(Varnode *)0,op,0);
} }
else { // EMIT (* )[0] else { // EMIT (* )[0]
pushOp(&subscript,op); pushOp(&subscript,op);
@@ -1132,7 +1132,7 @@ void PrintC::opPtrsub(const PcodeOp *op)
if (ptrel != (TypePointerRel *)0) if (ptrel != (TypePointerRel *)0)
pushTypePointerRel(op); pushTypePointerRel(op);
pushVn(in0,op,m); pushVn(in0,op,m);
push_integer(0,4,false,syntax,(Varnode *)0,op); push_integer(0,4,false,syntax,(Varnode *)0,op,0);
} }
} }
} }
@@ -1285,13 +1285,13 @@ void PrintC::opExtractOp(const PcodeOp *op)
/// \param tag is the type of token to associate with the integer /// \param tag is the type of token to associate with the integer
/// \param vn is the Varnode holding the value /// \param vn is the Varnode holding the value
/// \param op is the PcodeOp using the value /// \param op is the PcodeOp using the value
/// \param displayFormat is the default display format (which can be 0 and may be overridden)
void PrintC::push_integer(uintb val,int4 sz,bool sign,tagtype tag, void PrintC::push_integer(uintb val,int4 sz,bool sign,tagtype tag,
const Varnode *vn,const PcodeOp *op) const Varnode *vn,const PcodeOp *op,uint4 displayFormat)
{ {
bool print_negsign; bool print_negsign;
bool force_unsigned_token; bool force_unsigned_token;
bool force_sized_token; bool force_sized_token;
uint4 displayFormat = 0;
force_unsigned_token = false; force_unsigned_token = false;
force_sized_token = false; force_sized_token = false;
@@ -1307,8 +1307,6 @@ void PrintC::push_integer(uintb val,int4 sz,bool sign,tagtype tag,
} }
force_unsigned_token = vn->isUnsignedPrint(); force_unsigned_token = vn->isUnsignedPrint();
force_sized_token = vn->isLongPrint(); force_sized_token = vn->isLongPrint();
if (displayFormat == 0) // The symbol's formatting overrides any formatting on the data-type
displayFormat = high->getType()->getDisplayFormat();
} }
if (sign && displayFormat != Symbol::force_char) { // Print the constant as signed if (sign && displayFormat != Symbol::force_char) { // Print the constant as signed
uintb mask = calc_mask(sz); uintb mask = calc_mask(sz);
@@ -1324,7 +1322,7 @@ void PrintC::push_integer(uintb val,int4 sz,bool sign,tagtype tag,
// Figure whether to print as hex or decimal // Figure whether to print as hex or decimal
if (displayFormat != 0) { if (displayFormat != 0) {
// Format is forced by the Symbol // Format is forced by the Symbol or data-type
} }
else if ((mods & force_hex)!=0) { else if ((mods & force_hex)!=0) {
displayFormat = Symbol::force_hex; displayFormat = Symbol::force_hex;
@@ -1623,7 +1621,7 @@ void PrintC::pushCharConstant(uintb val,const Datatype *ct,tagtype tag,const Var
} }
if (displayFormat != 0 && displayFormat != Symbol::force_char) { if (displayFormat != 0 && displayFormat != Symbol::force_char) {
if (!castStrategy->caresAboutCharRepresentation(vn, op)) { if (!castStrategy->caresAboutCharRepresentation(vn, op)) {
push_integer(val, ct->getSize(), isSigned, tag, vn, op); push_integer(val, ct->getSize(), isSigned, tag, vn, op, displayFormat);
return; return;
} }
} }
@@ -1633,7 +1631,7 @@ void PrintC::pushCharConstant(uintb val,const Datatype *ct,tagtype tag,const Var
// unicode code-point. Its either part of a multi-byte UTF-8 encoding or an unknown // unicode code-point. Its either part of a multi-byte UTF-8 encoding or an unknown
// code-page value. In either case, we print as an integer or an escape sequence. // code-page value. In either case, we print as an integer or an escape sequence.
if (displayFormat != Symbol::force_hex && displayFormat != Symbol::force_char) { if (displayFormat != Symbol::force_hex && displayFormat != Symbol::force_char) {
push_integer(val, 1, isSigned, tag, vn, op); push_integer(val, 1, isSigned, tag, vn, op, displayFormat);
return; return;
} }
displayFormat = Symbol::force_hex; // Fallthru but force a hex representation displayFormat = Symbol::force_hex; // Fallthru but force a hex representation
@@ -1679,10 +1677,10 @@ void PrintC::pushEnumConstant(uintb val,const TypeEnum *ct,tagtype tag,
for(int4 i=0;i<rep.matchname.size();++i) for(int4 i=0;i<rep.matchname.size();++i)
pushAtom(Atom(rep.matchname[i],tag,EmitMarkup::const_color,op,vn,val)); pushAtom(Atom(rep.matchname[i],tag,EmitMarkup::const_color,op,vn,val));
if (rep.shiftAmount != 0) if (rep.shiftAmount != 0)
push_integer(rep.shiftAmount,4,false,tag,vn,op); push_integer(rep.shiftAmount,4,false,tag,vn,op,0);
} }
else { else {
push_integer(val,ct->getSize(),false,tag,vn,op); push_integer(val,ct->getSize(),false,tag,vn,op,ct->getDisplayFormat());
} }
} }
@@ -1742,8 +1740,7 @@ bool PrintC::pushPtrCodeConstant(uintb val,const TypePointer *ct,
} }
void PrintC::pushConstant(uintb val,const Datatype *ct,tagtype tag, void PrintC::pushConstant(uintb val,const Datatype *ct,tagtype tag,
const Varnode *vn, const Varnode *vn,const PcodeOp *op,uint4 displayFormat)
const PcodeOp *op)
{ {
Datatype *subtype; Datatype *subtype;
switch(ct->getMetatype()) { switch(ct->getMetatype()) {
@@ -1753,7 +1750,7 @@ void PrintC::pushConstant(uintb val,const Datatype *ct,tagtype tag,
else if (ct->isEnumType()) else if (ct->isEnumType())
pushEnumConstant(val,(TypeEnum *)ct,tag,vn,op); pushEnumConstant(val,(TypeEnum *)ct,tag,vn,op);
else else
push_integer(val,ct->getSize(),false,tag,vn,op); push_integer(val,ct->getSize(),false,tag,vn,op,displayFormat);
return; return;
case TYPE_INT: case TYPE_INT:
if (ct->isCharPrint()) if (ct->isCharPrint())
@@ -1761,10 +1758,10 @@ void PrintC::pushConstant(uintb val,const Datatype *ct,tagtype tag,
else if (ct->isEnumType()) else if (ct->isEnumType())
pushEnumConstant(val,(TypeEnum *)ct,tag,vn,op); pushEnumConstant(val,(TypeEnum *)ct,tag,vn,op);
else else
push_integer(val,ct->getSize(),true,tag,vn,op); push_integer(val,ct->getSize(),true,tag,vn,op,displayFormat);
return; return;
case TYPE_UNKNOWN: case TYPE_UNKNOWN:
push_integer(val,ct->getSize(),false,tag,vn,op); push_integer(val,ct->getSize(),false,tag,vn,op,displayFormat);
return; return;
case TYPE_BOOL: case TYPE_BOOL:
pushBoolConstant(val,(const TypeBase *)ct,tag,vn,op); pushBoolConstant(val,(const TypeBase *)ct,tag,vn,op);
@@ -1811,7 +1808,7 @@ void PrintC::pushConstant(uintb val,const Datatype *ct,tagtype tag,
pushMod(); pushMod();
if (!isSet(force_dec)) if (!isSet(force_dec))
setMod(force_hex); setMod(force_hex);
push_integer(val,ct->getSize(),false,tag,vn,op); push_integer(val,ct->getSize(),false,tag,vn,op,displayFormat);
popMod(); popMod();
} }
@@ -1845,14 +1842,14 @@ bool PrintC::pushEquate(uintb val,int4 sz,const EquateSymbol *sym,const Varnode
if (modval == val) { if (modval == val) {
pushOp(&binary_plus,(const PcodeOp *)0); pushOp(&binary_plus,(const PcodeOp *)0);
pushSymbol(sym,vn,op); pushSymbol(sym,vn,op);
push_integer(1, sz, false, syntax, (const Varnode *)0, (const PcodeOp *)0); push_integer(1, sz, false, syntax, (const Varnode *)0, (const PcodeOp *)0, 0);
return true; return true;
} }
modval = (baseval - 1) & mask; modval = (baseval - 1) & mask;
if (modval == val) { if (modval == val) {
pushOp(&binary_minus,(const PcodeOp *)0); pushOp(&binary_minus,(const PcodeOp *)0);
pushSymbol(sym,vn,op); pushSymbol(sym,vn,op);
push_integer(1, sz, false, syntax, (const Varnode *)0, (const PcodeOp *)0); push_integer(1, sz, false, syntax, (const Varnode *)0, (const PcodeOp *)0, 0);
return true; return true;
} }
return false; return false;
@@ -2053,7 +2050,7 @@ void PrintC::pushPartialSymbol(const Symbol *sym,int4 off,int4 sz,
PartialSymbolEntry &entry (stack[i]); PartialSymbolEntry &entry (stack[i]);
if (entry.field == (const TypeField *)0) { if (entry.field == (const TypeField *)0) {
if (entry.size <= 0) if (entry.size <= 0)
push_integer(entry.offset, entry.size, (entry.offset < 0), syntax, (Varnode *)0, op); push_integer(entry.offset, entry.size, (entry.offset < 0), syntax, (Varnode *)0, op, 0);
else { else {
string field = unnamedField(entry.offset,entry.size); string field = unnamedField(entry.offset,entry.size);
pushAtom(Atom(field,syntax,entry.hilite,op)); pushAtom(Atom(field,syntax,entry.hilite,op));
@@ -2172,7 +2169,7 @@ void PrintC::emitEnumDefinition(const TypeEnum *ct)
emit->spaces(1); emit->spaces(1);
emit->print(EQUALSIGN,EmitMarkup::no_color); emit->print(EQUALSIGN,EmitMarkup::no_color);
emit->spaces(1); emit->spaces(1);
push_integer((*iter).first,ct->getSize(),sign,syntax,(Varnode *)0,(PcodeOp *)0); push_integer((*iter).first,ct->getSize(),sign,syntax,(Varnode *)0,(PcodeOp *)0,0);
recurse(); recurse();
emit->print(SEMICOLON); emit->print(SEMICOLON);
++iter; ++iter;
@@ -3145,12 +3142,15 @@ void PrintC::emitSwitchCase(int4 casenum,const BlockSwitch *switchbl)
} }
else { else {
num = switchbl->getNumLabels(casenum); num = switchbl->getNumLabels(casenum);
uint4 displayFormat = switchbl->getDisplayFormat();
if (displayFormat == 0)
displayFormat = ct->getDisplayFormat();
for(i=0;i<num;++i) { for(i=0;i<num;++i) {
val = switchbl->getLabel(casenum,i); val = switchbl->getLabel(casenum,i);
emit->tagLine(); emit->tagLine();
emit->print(KEYWORD_CASE,EmitMarkup::keyword_color); emit->print(KEYWORD_CASE,EmitMarkup::keyword_color);
emit->spaces(1); emit->spaces(1);
pushConstant(val,ct,casetoken,(Varnode *)0,op); pushConstant(val,ct,casetoken,(Varnode *)0,op,displayFormat);
recurse(); recurse();
emit->print(COLON); emit->print(COLON);
} }
@@ -203,7 +203,7 @@ protected:
int4 getHiddenThisSlot(const PcodeOp *op,FuncProto *fc); ///< Get position of "this" pointer needing to be hidden int4 getHiddenThisSlot(const PcodeOp *op,FuncProto *fc); ///< Get position of "this" pointer needing to be hidden
void resetDefaultsPrintC(void); ///< Set default values for options specific to PrintC void resetDefaultsPrintC(void); ///< Set default values for options specific to PrintC
virtual void pushConstant(uintb val,const Datatype *ct,tagtype tag, virtual void pushConstant(uintb val,const Datatype *ct,tagtype tag,
const Varnode *vn,const PcodeOp *op); const Varnode *vn,const PcodeOp *op,uint4 displayFormat);
virtual bool pushEquate(uintb val,int4 sz,const EquateSymbol *sym, virtual bool pushEquate(uintb val,int4 sz,const EquateSymbol *sym,
const Varnode *vn,const PcodeOp *op); const Varnode *vn,const PcodeOp *op);
virtual void pushAnnotation(const Varnode *vn,const PcodeOp *op); virtual void pushAnnotation(const Varnode *vn,const PcodeOp *op);
@@ -216,8 +216,7 @@ protected:
const Varnode *vn,const PcodeOp *op); const Varnode *vn,const PcodeOp *op);
virtual void pushImpliedField(const Varnode *vn,const PcodeOp *op); virtual void pushImpliedField(const Varnode *vn,const PcodeOp *op);
virtual void push_integer(uintb val,int4 sz,bool sign,tagtype tag, virtual void push_integer(uintb val,int4 sz,bool sign,tagtype tag,
const Varnode *vn, const Varnode *vn,const PcodeOp *op,uint4 displayFormat);
const PcodeOp *op);
virtual void push_float(uintb val,int4 sz,tagtype tag,const Varnode *vn, virtual void push_float(uintb val,int4 sz,tagtype tag,const Varnode *vn,
const PcodeOp *op); const PcodeOp *op);
virtual void printUnicode(ostream &s,int4 onechar) const; virtual void printUnicode(ostream &s,int4 onechar) const;
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -234,7 +234,7 @@ void PrintJava::opLoad(const PcodeOp *op)
pushOp(&subscript,op); pushOp(&subscript,op);
pushVn(op->getIn(1),op,m); pushVn(op->getIn(1),op,m);
if (printArrayRef) if (printArrayRef)
push_integer(0,4,false,syntax,(Varnode *)0,op); push_integer(0,4,false,syntax,(Varnode *)0,op,0);
} }
void PrintJava::opStore(const PcodeOp *op) void PrintJava::opStore(const PcodeOp *op)
@@ -245,7 +245,7 @@ void PrintJava::opStore(const PcodeOp *op)
if (needZeroArray(op->getIn(1))) { if (needZeroArray(op->getIn(1))) {
pushOp(&subscript,op); pushOp(&subscript,op);
pushVn(op->getIn(1),op,m); pushVn(op->getIn(1),op,m);
push_integer(0,4,false,syntax,(Varnode *)0,op); push_integer(0,4,false,syntax,(Varnode *)0,op,0);
pushVn(op->getIn(2),op,mods); pushVn(op->getIn(2),op,mods);
} }
else { else {
@@ -223,7 +223,8 @@ void PrintLanguage::pushVnExplicit(const Varnode *vn,const PcodeOp *op)
return; return;
} }
if (vn->isConstant()) { if (vn->isConstant()) {
pushConstant(vn->getOffset(),vn->getHighTypeReadFacing(op),vartoken,vn,op); Datatype *ct = vn->getHighTypeReadFacing(op);
pushConstant(vn->getOffset(),ct,vartoken,vn,op,ct->getDisplayFormat());
return; return;
} }
pushSymbolDetail(vn,op,true); pushSymbolDetail(vn,op,true);
@@ -324,10 +324,11 @@ protected:
/// \param val is the value of the constant /// \param val is the value of the constant
/// \param ct is the data-type of the constant /// \param ct is the data-type of the constant
/// \param tag is the type of token associated with the constant /// \param tag is the type of token associated with the constant
/// \param vn is the Varnode holding the constant (optional) /// \param vn is the Varnode holding the constant (may be null)
/// \param op is the PcodeOp using the constant (optional) /// \param op is the PcodeOp using the constant (may be null)
/// \param displayFormat is the default display format to use (may be 0)
virtual void pushConstant(uintb val,const Datatype *ct,tagtype tag, virtual void pushConstant(uintb val,const Datatype *ct,tagtype tag,
const Varnode *vn,const PcodeOp *op)=0; const Varnode *vn,const PcodeOp *op,uint4 displayFormat)=0;
/// \brief Push a constant marked up by and EquateSymbol onto the RPN stack /// \brief Push a constant marked up by and EquateSymbol onto the RPN stack
/// ///
@@ -335,8 +336,8 @@ protected:
/// \param val is the value of the constant /// \param val is the value of the constant
/// \param sz is the number of bytes to use for the encoding /// \param sz is the number of bytes to use for the encoding
/// \param sym is the EquateSymbol that marks up the constant /// \param sym is the EquateSymbol that marks up the constant
/// \param vn is the Varnode holding the constant (optional) /// \param vn is the Varnode holding the constant (may be null)
/// \param op is the PcodeOp using the constant (optional) /// \param op is the PcodeOp using the constant (may be null)
virtual bool pushEquate(uintb val,int4 sz,const EquateSymbol *sym,const Varnode *vn,const PcodeOp *op)=0; virtual bool pushEquate(uintb val,int4 sz,const EquateSymbol *sym,const Varnode *vn,const PcodeOp *op)=0;
/// \brief Push an address which is not in the normal data-flow. /// \brief Push an address which is not in the normal data-flow.
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -36,6 +36,13 @@ public class ClangCaseToken extends ClangToken {
value = 0; value = 0;
} }
/**
* @return the constant value associated with this label
*/
public long getValue() {
return value;
}
@Override @Override
public boolean isVariableRef() { public boolean isVariableRef() {
return true; return true;
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -41,11 +41,10 @@ public class ConvertBinaryAction extends ConvertConstantAction {
} }
@Override @Override
public String getMenuDisplay(long value, int size, boolean isSigned, Program program) { public String getMenuDisplay(Scalar scalar, Program program) {
Scalar scalar = new Scalar(size * 8, value);
long v; long v;
String prefix = "0b"; String prefix = "0b";
if (isSigned) { if (scalar.isSigned()) {
v = scalar.getSignedValue(); v = scalar.getSignedValue();
if (v < 0) { if (v < 0) {
v = -v; v = -v;
@@ -74,9 +73,9 @@ public class ConvertBinaryAction extends ConvertConstantAction {
} }
@Override @Override
public String getEquateName(long value, int size, boolean isSigned, Program program) { public String getEquateName(Scalar scalar, Program program) {
String valueStr = Long.toBinaryString(value); String valueStr = Long.toBinaryString(scalar.getValue());
valueStr = StringUtilities.pad(valueStr, '0', size * 8); valueStr = StringUtilities.pad(valueStr, '0', scalar.bitLength());
return valueStr + "b"; return valueStr + "b";
} }
} }
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -22,6 +22,7 @@ import ghidra.program.model.data.ByteDataType;
import ghidra.program.model.data.StringDataInstance; import ghidra.program.model.data.StringDataInstance;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.program.model.pcode.EquateSymbol; import ghidra.program.model.pcode.EquateSymbol;
import ghidra.program.model.scalar.Scalar;
import ghidra.util.BigEndianDataConverter; import ghidra.util.BigEndianDataConverter;
import ghidra.util.HelpLocation; import ghidra.util.HelpLocation;
@@ -63,9 +64,9 @@ public class ConvertCharAction extends ConvertConstantAction {
} }
@Override @Override
public String getEquateName(long value, int size, boolean isSigned, Program program) { public String getEquateName(Scalar scalar, Program program) {
byte[] bytes = new byte[size]; byte[] bytes = new byte[scalar.bitLength() / 8];
BigEndianDataConverter.INSTANCE.putValue(value, size, bytes, 0); BigEndianDataConverter.INSTANCE.putValue(scalar.getValue(), bytes.length, bytes, 0);
return StringDataInstance.getCharRepresentation(ByteDataType.dataType, bytes, null); return StringDataInstance.getCharRepresentation(ByteDataType.dataType, bytes, null);
} }
@@ -98,13 +99,14 @@ public class ConvertCharAction extends ConvertConstantAction {
} }
@Override @Override
public String getMenuDisplay(long value, int size, boolean isSigned, Program program) { public String getMenuDisplay(Scalar scalar, Program program) {
StringBuilder buffer = new StringBuilder(); StringBuilder buffer = new StringBuilder();
if (size > 1) { if (scalar.bitLength() > 8) {
buffer.append('L'); buffer.append('L');
} }
if ((size == 1 && value >= 0x7f) || codePointNeedsEscape((int) value)) { if ((scalar.bitLength() == 8 && scalar.getUnsignedValue() >= 0x7f) ||
switch ((int) value) { codePointNeedsEscape((int) scalar.getUnsignedValue())) {
switch ((int) scalar.getValue()) {
case 0: case 0:
buffer.append("'\\0'"); buffer.append("'\\0'");
break; break;
@@ -140,12 +142,12 @@ public class ConvertCharAction extends ConvertConstantAction {
break; break;
default: default:
// Generic unicode escape // Generic unicode escape
generateHexEscape(buffer, (int) value); generateHexEscape(buffer, (int) scalar.getUnsignedValue());
break; break;
} }
} }
else { else {
buffer.append('\'').append((char) value).append('\''); buffer.append('\'').append((char) scalar.getUnsignedValue()).append('\'');
} }
return buffer.toString(); return buffer.toString();
} }
@@ -17,27 +17,21 @@ package ghidra.app.plugin.core.decompile.actions;
import java.awt.Font; import java.awt.Font;
import java.awt.FontMetrics; import java.awt.FontMetrics;
import java.util.List;
import javax.swing.JMenuItem; import javax.swing.JMenuItem;
import ghidra.app.decompiler.ClangCaseToken;
import ghidra.app.decompiler.ClangToken; import ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.ClangVariableToken;
import ghidra.app.plugin.core.decompile.DecompilePlugin; import ghidra.app.plugin.core.decompile.DecompilePlugin;
import ghidra.app.plugin.core.decompile.DecompilerActionContext; import ghidra.app.plugin.core.decompile.DecompilerActionContext;
import ghidra.program.model.address.Address;
import ghidra.program.model.block.CodeBlock;
import ghidra.program.model.block.SimpleBlockModel;
import ghidra.program.model.data.*; import ghidra.program.model.data.*;
import ghidra.program.model.data.Enum; import ghidra.program.model.data.Enum;
import ghidra.program.model.listing.Instruction; import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.program.model.pcode.*; import ghidra.program.model.pcode.*;
import ghidra.program.model.scalar.Scalar; import ghidra.program.model.scalar.Scalar;
import ghidra.program.model.symbol.*;
import ghidra.util.Msg; import ghidra.util.Msg;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.InvalidInputException;
import ghidra.util.task.TaskMonitor;
/** /**
* Abstract pop-up menu convert action for the decompiler. If triggered, it lays down * Abstract pop-up menu convert action for the decompiler. If triggered, it lays down
@@ -46,29 +40,45 @@ import ghidra.util.task.TaskMonitor;
*/ */
public abstract class ConvertConstantAction extends AbstractDecompilerAction { public abstract class ConvertConstantAction extends AbstractDecompilerAction {
/**
* Max instructions to search through, when looking for a scalar match in the listing
* that corresponds with the selected constant in the decompiler window.
*/
private final static int MAX_INSTRUCTION_WINDOW = 20;
private static final int MAX_SCALAR_SIZE = 8;
protected DecompilePlugin plugin; protected DecompilePlugin plugin;
private FontMetrics metrics = null; private FontMetrics metrics = null;
private int convertType; // The EquateSymbol conversion type performed by the action protected int convertType; // The conversion type performed by the action
/** /**
* A helper class describing a (matching) scalar operand * Helper class for identifying integer values that are "near" a given value.
* "Near" can mean off by 1, negated, or inverted.
*/ */
private static class ScalarMatch { public static class NearMatchValues {
Address refAddr; // Address of instruction private long[] values;
Scalar scalar; private long mask;
int opIndex;
public ScalarMatch(Address addr, Scalar value, int index) { public NearMatchValues(long value, int size) {
refAddr = addr; mask = -1;
scalar = value; if (size < 8) {
opIndex = index; mask = mask >>> (8 - size) * 8;
}
values = new long[4];
values[0] = value & mask;
values[1] = (value - 1) & mask;
values[2] = (value + 1) & mask;
values[3] = (-value) & mask;
}
public NearMatchValues(Scalar scalar) {
this(scalar.getValue(), scalar.bitLength() / 8);
}
/**
* @param value is the value to match
* @return true if the value matches
*/
public boolean isMatch(long value) {
value = value & mask;
for (long match : values) {
if (match == value)
return true;
}
return false;
} }
} }
@@ -106,238 +116,17 @@ public abstract class ConvertConstantAction extends AbstractDecompilerAction {
return buf.toString(); return buf.toString();
} }
/**
* Find a scalar in the instruction matching one of the given values.
* Return an object describing the match or null if there is no match.
* @param instr is the instruction
* @param values is an array of the given values
* @return the Scalar and
*/
private ScalarMatch findScalarInInstruction(Instruction instr, long values[]) {
int numOperands = instr.getNumOperands();
ScalarMatch scalarMatch = null;
for (int i = 0; i < numOperands; i++) {
for (Object obj : instr.getOpObjects(i)) {
if (obj instanceof Scalar) {
Scalar scalar = (Scalar) obj;
for (long value : values) {
if (scalar.getUnsignedValue() != value) {
continue;
}
if (scalarMatch != null) {
scalarMatch.opIndex = -1; // non-unique scalar operand value - can't identify operand
return scalarMatch;
}
scalarMatch = new ScalarMatch(instr.getAddress(), scalar, i);
}
}
}
}
return scalarMatch;
}
/**
* Find a scalar (instruction operand) that matches the given constant Varnode.
* We walk backward from the starting address inspecting operands until a match is found.
* The search is terminated if either a match is found, the beginning of the basic block
* is reached, or if 20 instructions are traversed. The scalar can be a "near" match, meaning
* off by 1 or the negated value.
* @param program is the Program
* @param startAddress is the starting address to search backward from
* @param constVn is the given constant Varnode
* @return a description of the scalar match, or null if there is no match
*/
private ScalarMatch findScalarMatch(Program program, Address startAddress, Varnode constVn) {
long value = constVn.getOffset();
long mask = -1;
if (constVn.getSize() < 8) {
mask = mask >>> (8 - constVn.getSize()) * 8;
}
long values[] = new long[4];
values[0] = value;
values[1] = (value - 1) & mask;
values[2] = (value + 1) & mask;
values[3] = (-value) & mask;
int count = 0;
ScalarMatch scalarMatch = null;
Instruction curInst = program.getListing().getInstructionAt(startAddress);
if (curInst == null) {
return null;
}
SimpleBlockModel model = new SimpleBlockModel(program);
CodeBlock basicBlock = null;
try {
basicBlock = model.getFirstCodeBlockContaining(startAddress, TaskMonitor.DUMMY);
}
catch (CancelledException e) {
// can't happen; dummy monitor
}
if (basicBlock == null) {
return null;
}
while (count < MAX_INSTRUCTION_WINDOW) {
count += 1;
ScalarMatch newMatch = findScalarInInstruction(curInst, values);
if (newMatch != null) {
if (scalarMatch != null) {
return null; // Matches at more than one address
}
if (newMatch.opIndex < 0) {
return null; // Matches at more than one operand
}
scalarMatch = newMatch;
}
curInst = curInst.getPrevious();
if (curInst == null) {
break;
}
if (!basicBlock.contains(curInst.getAddress())) {
break;
}
}
return scalarMatch;
}
/**
* Given the context, set up the task object that will execute the conversion.
* If setupFinal toggle is false, only enough of the task is set up to complete
* the isEnabled test for the action. Otherwise the whole task is set up, ready for runTask().
* If the context is not suitable for a conversion, null is returned.
* @param context is the given context for the action
* @param setupFinal is true if a full task setup is needed
* @return the task object or null
*/
protected ConvertConstantTask establishTask(DecompilerActionContext context,
boolean setupFinal) {
ClangToken tokenAtCursor = context.getTokenAtCursor();
if (!(tokenAtCursor instanceof ClangVariableToken)) {
return null;
}
Varnode convertVn = tokenAtCursor.getVarnode();
if (convertVn == null || !convertVn.isConstant() || convertVn.getSize() > MAX_SCALAR_SIZE) {
return null;
}
HighSymbol symbol = convertVn.getHigh().getSymbol();
EquateSymbol convertSymbol = null;
if (symbol != null) {
if (symbol instanceof EquateSymbol) {
convertSymbol = (EquateSymbol) symbol;
int type = convertSymbol.getConvert();
if (type == convertType || type == EquateSymbol.FORMAT_DEFAULT) {
return null;
}
}
else {
return null; // Something already attached to constant
}
}
DataType convertDataType = convertVn.getHigh().getDataType();
boolean convertIsSigned = false;
if (convertDataType instanceof AbstractIntegerDataType) {
if (convertDataType instanceof BooleanDataType) {
return null;
}
convertIsSigned = ((AbstractIntegerDataType) convertDataType).isSigned();
}
else if (convertDataType instanceof Enum) {
return null;
}
if (!setupFinal) {
return new ConvertConstantTask(convertVn, convertIsSigned);
}
if (convertSymbol != null) {
return convertExistingSymbol(context, convertSymbol, convertVn, convertIsSigned);
}
PcodeOp op = convertVn.getLoneDescend();
Address convertAddr = op.getSeqnum().getTarget();
DynamicHash dynamicHash = new DynamicHash(convertVn, 0);
long convertHash = dynamicHash.getHash();
Program program = context.getProgram();
ScalarMatch scalarMatch = findScalarMatch(program, convertAddr, convertVn);
if (scalarMatch == null) {
String equateName = getEquateName(convertVn.getOffset(), convertVn.getSize(),
convertIsSigned, program);
if (equateName == null) {
return null; // A null is a user cancel
}
return new ConvertConstantTask(context, equateName, convertAddr, convertVn,
convertIsSigned, convertHash, -1);
}
long value = scalarMatch.scalar.getUnsignedValue();
int size = scalarMatch.scalar.bitLength() / 8;
if (size == 0) {
size = 1;
}
value = ConvertConstantTask.signExtendValue(convertIsSigned, value, size);
String equateName = getEquateName(value, size, convertIsSigned, program);
if (equateName == null) {
return null; // user cancelled
}
ConvertConstantTask task =
new ConvertConstantTask(context, equateName, convertAddr, convertVn,
convertIsSigned, convertHash, -1);
// Don't create a named equate if the varnode and the instruction operand differ
// as the name was selected specifically for the varnode
if (convertType != EquateSymbol.FORMAT_DEFAULT || value == task.getValue()) {
task.setAlternate(equateName, scalarMatch.refAddr, scalarMatch.opIndex, value);
}
return task;
}
private ConvertConstantTask convertExistingSymbol(DecompilerActionContext context,
EquateSymbol convertSymbol, Varnode convertVn, boolean convertIsSigned) {
Address convertAddr = convertSymbol.getPCAddress();
long convertHash = 0;
int convertIndex = -1;
boolean foundEquate = false;
Program program = context.getProgram();
EquateTable equateTable = program.getEquateTable();
List<Equate> equates = equateTable.getEquates(convertAddr);
for (Equate equate : equates) {
if (equate.getValue() != convertVn.getOffset()) {
continue;
}
for (EquateReference equateRef : equate.getReferences(convertAddr)) {
convertHash = equateRef.getDynamicHashValue();
convertIndex = equateRef.getOpIndex();
foundEquate = true;
break;
}
break;
}
if (!foundEquate) {
Msg.error(this, "Symbol does not have matching entry in equate table");
return null;
}
String equateName = getEquateName(convertVn.getOffset(), convertVn.getSize(),
convertIsSigned, context.getProgram());
if (equateName == null) { // A null is a user cancel
return null;
}
return new ConvertConstantTask(context, equateName, convertAddr, convertVn,
convertIsSigned, convertHash, convertIndex);
}
@Override @Override
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) { protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
ConvertConstantTask task = establishTask(context, false); Scalar scalar;
if (task == null) { scalar = getCaseConstant(context, convertType);
if (scalar == null) {
scalar = ConvertConstantEquateTask.getConvertibleConstant(context, convertType);
}
if (scalar == null) {
return false; return false;
} }
String convDisplay = String convDisplay = getMenuDisplay(scalar, context.getProgram());
getMenuDisplay(task.getValue(), task.getSize(), task.isSigned(), context.getProgram());
if (convDisplay == null) { if (convDisplay == null) {
return false; return false;
} }
@@ -352,13 +141,72 @@ public abstract class ConvertConstantAction extends AbstractDecompilerAction {
@Override @Override
protected void decompilerActionPerformed(DecompilerActionContext context) { protected void decompilerActionPerformed(DecompilerActionContext context) {
ConvertConstantTask task = establishTask(context, true); if (context.getTokenAtCursor() instanceof ClangCaseToken) {
writeSwitchFormat(context);
return;
}
ConvertConstantEquateTask task = ConvertConstantEquateTask.establishTask(context, this);
if (task == null) { if (task == null) {
return; return;
} }
task.runTask(); task.runTask();
} }
/**
* If the mouse context is a constant from a switch case that is suitable for conversion
* return a description of the constant. Otherwise return null.
* @param context is the mouse context
* @param convertType is the type of conversion being selected (FORMAT_DEC FORMAT_HEX etc.)
* @return the constant description or null
*/
static protected Scalar getCaseConstant(DecompilerActionContext context,
int convertType) {
ClangToken tokenAtCursor = context.getTokenAtCursor();
if (!(tokenAtCursor instanceof ClangCaseToken)) {
return null;
}
if (convertType == EquateSymbol.FORMAT_DEFAULT ||
convertType == EquateSymbol.FORMAT_DOUBLE || convertType == EquateSymbol.FORMAT_FLOAT) {
return null;
}
ClangCaseToken caseToken = (ClangCaseToken) tokenAtCursor;
HighVariable high = caseToken.getHighVariable();
if (high == null) {
return null;
}
DataType convertDataType = high.getDataType();
boolean convertIsSigned = false;
if (convertDataType instanceof AbstractIntegerDataType) {
if (convertDataType instanceof BooleanDataType) {
return null;
}
convertIsSigned = ((AbstractIntegerDataType) convertDataType).isSigned();
}
else if (convertDataType instanceof Enum) {
return null;
}
return new Scalar(high.getSize() * 8, caseToken.getValue(), convertIsSigned);
}
private void writeSwitchFormat(DecompilerActionContext context) {
ClangCaseToken caseToken = (ClangCaseToken) context.getTokenAtCursor();
PcodeOp switchOp = caseToken.getSwitchOp();
Function func = context.getFunction();
Program program = context.getProgram();
int transaction = program.startTransaction("Convert case constants");
boolean commit = false;
try {
JumpTable.writeFormat(func, switchOp.getSeqnum().getTarget(), convertType);
commit = true;
}
catch (InvalidInputException ex) {
Msg.error(this, ex);
}
finally {
program.endTransaction(transaction, commit);
}
}
/** /**
* The menu option for this kind of action is intended to look like: * The menu option for this kind of action is intended to look like:
* {@literal Hexadecimal: 0x2408} * {@literal Hexadecimal: 0x2408}
@@ -372,22 +220,18 @@ public abstract class ConvertConstantAction extends AbstractDecompilerAction {
* {@literal Hexadecimal: 0x2408} * {@literal Hexadecimal: 0x2408}
* This method constructs the final part of this string, after the colon by * This method constructs the final part of this string, after the colon by
* formatting the actual value that is to be converted. * formatting the actual value that is to be converted.
* @param value is the actual value * @param scalar is the constant being converted
* @param size is the number of bytes used for the constant Varnode
* @param isSigned is true if the constant represents a signed data-type
* @param program the program * @param program the program
* @return the formatted String * @return the formatted String
*/ */
public abstract String getMenuDisplay(long value, int size, boolean isSigned, Program program); public abstract String getMenuDisplay(Scalar scalar, Program program);
/** /**
* Construct the name of the Equate, either absolutely for a conversion or * Construct the name of the Equate, either absolutely for a conversion or
* by preventing the user with a dialog to select a name. * by preventing the user with a dialog to select a name.
* @param value is the value being converted * @param scalar is the constant being converted
* @param size is the number of bytes used for the constant Varnode
* @param isSigned is true if the constant represents a signed data-type
* @param program is the current Program * @param program is the current Program
* @return the equate name * @return the equate name
*/ */
public abstract String getEquateName(long value, int size, boolean isSigned, Program program); public abstract String getEquateName(Scalar scalar, Program program);
} }
@@ -1,342 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.decompile.actions;
import java.util.List;
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program;
import ghidra.program.model.pcode.*;
import ghidra.program.model.symbol.*;
import ghidra.util.Msg;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.exception.InvalidInputException;
import utility.function.Callback;
/**
* Create an equate in the table for the specific Address and hash value.
* The equate is not assumed to be attached to a particular instruction operand and
* uses the dynamic hash value to identify the particular constant (within p-code) to label.
*
* If altAddress is non-null and the other alt* fields are filled in, the task attempts
* to set the equation on the altAddress first to get the representation of the p-code
* constant at convertAddress to change. After the decompilation finishes, the representation
* is checked, and if it did not change, the alt* equate is removed and an equate is created
* directly for the convertAddress;
*/
public class ConvertConstantTask implements Callback {
private DecompilerActionContext context;
private Program program;
private long equateValue; // Primary value of the equate
private int equateSize; // The number of bytes in the Varnode constant being equated
private Address convertAddress; // The primary address of the Equate
private String convertName; // The primary name to use in the Equate table
private long convertHash; // A dynamic hash locating the constant Varnode in data-flow
private int convertIndex; // The scalar index associated with the primary Equate (or -1)
private boolean convertSigned;
private Address altAddress = null; // Alternate location of constant
private int altIndex; // Index of alternate scalar
private String altName = null; // Alternate equate name
private long altValue; // Alternate value
public ConvertConstantTask(Varnode vn, boolean isSigned) {
equateValue = signExtendValue(isSigned, vn.getOffset(), vn.getSize());
equateSize = vn.getSize();
convertSigned = isSigned;
}
/**
* Construct a primary Equate task
* @param context is the action context for the task
* @param name is the primary Equate name
* @param addr is the primary address of the Equate
* @param vn is the constant Varnode being equated
* @param isSigned is true if the equate value is considered signed
* @param hash is the dynamic hash
* @param index is the operand index if the Equate is known to label an instruction operand
*/
public ConvertConstantTask(DecompilerActionContext context, String name, Address addr,
Varnode vn, boolean isSigned, long hash, int index) {
this.context = context;
program = context.getProgram();
convertName = name;
convertAddress = addr;
equateValue = signExtendValue(isSigned, vn.getOffset(), vn.getSize());
equateSize = vn.getSize();
convertSigned = isSigned;
convertHash = hash;
convertIndex = index;
}
/**
* Negative equates must be sign extended to 64-bits to be properly stored in the table.
* Compute the proper 64-bit value of a constant given its signedness and the number
* of bytes used to store the constant.
* @param isSigned is true if the equate is considered signed
* @param value is the (unsigned) form of the constant
* @param size is the number of bytes used to store the constant
* @return the 64-bit extended value
*/
public static long signExtendValue(boolean isSigned, long value, int size) {
if (isSigned && size < 8) {
int sa = (8 /* sizeof(long) */ - size) * 8 /* bits per byte */;
value <<= sa;
value >>= sa;
}
return value;
}
/**
* Establish an alternate Equate to try before falling back on the primary Equate
* @param name is the alternate name of the Equate
* @param addr is the alternate address
* @param index is the operand index
* @param value is the alternate constant value to equate
*/
public void setAlternate(String name, Address addr, int index, long value) {
altName = name;
altAddress = addr;
altValue = value;
altIndex = index;
}
/**
* @return the primary value being equated
*/
public long getValue() {
return equateValue;
}
/**
* @return the size of constant (Varnode) being equated
*/
public int getSize() {
return equateSize;
}
/**
* @return true if the constant value is treated as a signed integer
*/
public boolean isSigned() {
return convertSigned;
}
/**
* Remove any pre-existing equate reference with the same address and hash as the
* primate equate.
*/
private void removePrimaryReference() {
EquateTable equateTable = program.getEquateTable();
List<Equate> equates = equateTable.getEquates(convertAddress);
for (Equate equate : equates) {
List<EquateReference> references = equate.getReferences(convertAddress);
for (EquateReference ref : references) {
if (ref.getDynamicHashValue() == convertHash) {
if (equate.getReferenceCount() <= 1) {
equateTable.removeEquate(equate.getName());
}
else {
equate.removeReference(convertHash, convertAddress);
}
return;
}
}
}
}
/**
* Remove and pre-existing equate reference with the same address and hash as the
* alternate equate.
*/
private void removeAlternateReference() {
EquateTable equateTable = program.getEquateTable();
List<Equate> equates = equateTable.getEquates(altAddress);
for (Equate equate : equates) {
List<EquateReference> references = equate.getReferences(altAddress);
for (EquateReference ref : references) {
if (ref.getOpIndex() == altIndex) {
if (equate.getReferenceCount() <= 1) {
equateTable.removeEquate(equate.getName());
}
else {
equate.removeReference(altAddress, altIndex);
}
return;
}
}
}
}
/**
* Add equate based on the alternate constant information: altAddress, altName, altIndex
* @throws DuplicateNameException if there is already an equate with same name but different value
* @throws InvalidInputException if the equate name is illegal
*/
private void addPrimaryEquate() throws DuplicateNameException, InvalidInputException {
EquateTable equateTable = program.getEquateTable();
Equate equate = equateTable.getEquate(convertName);
if (equate != null && equate.getValue() != equateValue) {
String msg = "Equate named " + convertName + " already exists with value of " +
equate.getValue() + ".";
throw new DuplicateNameException(msg);
}
if (equate == null) {
equate = equateTable.createEquate(convertName, equateValue);
}
// Add reference to existing equate
if (convertHash != 0) {
equate.addReference(convertHash, convertAddress);
}
else {
equate.addReference(convertAddress, convertIndex);
}
}
/**
* Add equate based on the direct constant information: convertAddress, convertName, convertHash
* @throws DuplicateNameException if there is already an equate with same name but different value
* @throws InvalidInputException if the equate name is illegal
*/
private void addAlternateEquate() throws InvalidInputException, DuplicateNameException {
EquateTable equateTable = program.getEquateTable();
Equate equate = equateTable.getEquate(altName);
if (equate != null && equate.getValue() != altValue) {
String msg = "Equate named " + altName + " already exists with value of " +
equate.getValue() + ".";
throw new DuplicateNameException(msg);
}
if (equate == null) {
equate = equateTable.createEquate(altName, altValue);
}
equate.addReference(altAddress, altIndex);
}
/**
* Create a reference to primary equate, removing any previous reference.
* If an alternate equate is given, remove any existing reference to it as well.
*/
private void applyPrimaryEquate() {
int transaction = program.startTransaction("Convert constant");
boolean commit = false;
try {
if (altAddress != null) {
removeAlternateReference();
}
removePrimaryReference();
addPrimaryEquate();
commit = true;
}
catch (DuplicateNameException e) {
Msg.showError(this, null, "Convert Failed", e.getMessage());
}
catch (InvalidInputException e) {
Msg.showError(this, null, "Convert Failed", e.getMessage());
}
finally {
program.endTransaction(transaction, commit);
}
}
/**
* Create a reference to the alternate equate.
*/
private void applyAlternateEquate() {
int transaction = program.startTransaction("Convert constant");
boolean commit = false;
try {
addAlternateEquate();
commit = true;
}
catch (DuplicateNameException e) {
Msg.showError(this, null, "Convert Failed", e.getMessage());
}
catch (InvalidInputException e) {
Msg.showError(this, null, "Convert Failed", e.getMessage());
}
finally {
program.endTransaction(transaction, commit);
}
}
/**
* Look for the EquateSymbol pointing to the altAddress, attached to the constant
* @return true if we find the EquateSymbol, false otherwise
*/
private boolean isAlternatePlaced() {
HighFunction highFunction = context.getHighFunction(); // Get the updated HighFunction
if (highFunction == null) {
return false;
}
// Varnode itself should be unchanged
Varnode vn = DynamicHash.findVarnode(highFunction, convertAddress, convertHash);
if (vn == null) {
return false;
}
HighSymbol symbol = vn.getHigh().getSymbol(); // But now it should have an equate on it
if (!(symbol instanceof EquateSymbol)) {
return false;
}
EquateSymbol eqSymbol = (EquateSymbol) symbol;
if (!eqSymbol.getPCAddress().equals(altAddress)) {
return false;
}
return true;
}
/**
* Callback executed after the alternative equate is placed and the DecompilerProvider has updated its window.
* We check to see if the equate reached the desired constant in the decompiler.
* If not, we remove the alternate equate and place a direct equate
*/
@Override
public void call() {
if (isAlternatePlaced()) {
return;
}
applyPrimaryEquate();
}
/**
* Run the convert task. If the task is given an alternate equate, this is placed, otherwise
* the primary equate is placed. If an alternate is placed, a thread is scheduled to check if
* the alternate equate reached the constant Varnode. If not the alternate equate reference is
* removed, and the task falls back and places the primary equate.
*/
public void runTask() {
if (altAddress != null) {
applyAlternateEquate();
try {
Thread.sleep(50); // Let the decompiler get going
}
catch (InterruptedException e) {
return;
}
context.getComponentProvider().doWhenNotBusy(this);
}
else {
applyPrimaryEquate();
}
}
}
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -39,14 +39,13 @@ public class ConvertDecAction extends ConvertConstantAction {
} }
@Override @Override
public String getMenuDisplay(long value, int size, boolean isSigned, Program program) { public String getMenuDisplay(Scalar scalar, Program program) {
return getEquateName(value, size, isSigned, program); return getEquateName(scalar, program);
} }
@Override @Override
public String getEquateName(long value, int size, boolean isSigned, Program program) { public String getEquateName(Scalar scalar, Program program) {
Scalar scalar = new Scalar(size * 8, value); if (scalar.isSigned()) {
if (isSigned) {
return Long.toString(scalar.getSignedValue()); return Long.toString(scalar.getSignedValue());
} }
return Long.toString(scalar.getUnsignedValue()); return Long.toString(scalar.getUnsignedValue());
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -41,19 +41,18 @@ public class ConvertDoubleAction extends ConvertConstantAction {
} }
@Override @Override
public String getMenuDisplay(long value, int size, boolean isSigned, Program program) { public String getMenuDisplay(Scalar scalar, Program program) {
return getText(value, size, isSigned, program); return getText(scalar, program);
} }
@Override @Override
public String getEquateName(long value, int size, boolean isSigned, Program program) { public String getEquateName(Scalar scalar, Program program) {
return getText(value, size, isSigned, program); return getText(scalar, program);
} }
private String getText(long value, int size, boolean isSigned, Program program) { private String getText(Scalar scalar, Program program) {
DataOrganization organization = program.getDataTypeManager().getDataOrganization(); DataOrganization organization = program.getDataTypeManager().getDataOrganization();
int doubleSize = organization.getDoubleSize(); int doubleSize = organization.getDoubleSize();
Scalar scalar = new Scalar(size * 8, value);
BigDecimal bd = value(doubleSize, scalar); BigDecimal bd = value(doubleSize, scalar);
if (bd != null) { if (bd != null) {
return bd.toString(); return bd.toString();
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -41,19 +41,18 @@ public class ConvertFloatAction extends ConvertConstantAction {
} }
@Override @Override
public String getMenuDisplay(long value, int size, boolean isSigned, Program program) { public String getMenuDisplay(Scalar scalar, Program program) {
return getText(value, size, isSigned, program); return getText(scalar, program);
} }
@Override @Override
public String getEquateName(long value, int size, boolean isSigned, Program program) { public String getEquateName(Scalar scalar, Program program) {
return getText(value, size, isSigned, program); return getText(scalar, program);
} }
private String getText(long value, int size, boolean isSigned, Program program) { private String getText(Scalar scalar, Program program) {
DataOrganization organization = program.getDataTypeManager().getDataOrganization(); DataOrganization organization = program.getDataTypeManager().getDataOrganization();
int floatSize = organization.getFloatSize(); int floatSize = organization.getFloatSize();
Scalar scalar = new Scalar(size * 8, value);
BigDecimal bd = value(floatSize, scalar); BigDecimal bd = value(floatSize, scalar);
if (bd != null) { if (bd != null) {
return bd.toString(); return bd.toString();
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -40,9 +40,8 @@ public class ConvertHexAction extends ConvertConstantAction {
} }
@Override @Override
public String getMenuDisplay(long value, int size, boolean isSigned, Program program) { public String getMenuDisplay(Scalar scalar, Program program) {
Scalar scalar = new Scalar(size * 8, value); if (scalar.isSigned()) {
if (isSigned) {
long v = scalar.getSignedValue(); long v = scalar.getSignedValue();
String valueStr = Long.toString(v, 16); String valueStr = Long.toString(v, 16);
if (v < 0) { if (v < 0) {
@@ -56,9 +55,8 @@ public class ConvertHexAction extends ConvertConstantAction {
} }
@Override @Override
public String getEquateName(long value, int size, boolean isSigned, Program program) { public String getEquateName(Scalar scalar, Program program) {
Scalar scalar = new Scalar(size * 8, value); if (scalar.isSigned()) {
if (isSigned) {
long v = scalar.getSignedValue(); long v = scalar.getSignedValue();
String valueStr = Long.toString(v, 16).toUpperCase(); String valueStr = Long.toString(v, 16).toUpperCase();
if (v < 0) { if (v < 0) {
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -40,9 +40,8 @@ public class ConvertOctAction extends ConvertConstantAction {
} }
@Override @Override
public String getMenuDisplay(long value, int size, boolean isSigned, Program program) { public String getMenuDisplay(Scalar scalar, Program program) {
Scalar scalar = new Scalar(size * 8, value); if (scalar.isSigned()) {
if (isSigned) {
long v = scalar.getSignedValue(); long v = scalar.getSignedValue();
String valueStr = Long.toString(v, 8); String valueStr = Long.toString(v, 8);
if (v < 0) { if (v < 0) {
@@ -56,8 +55,7 @@ public class ConvertOctAction extends ConvertConstantAction {
} }
@Override @Override
public String getEquateName(long value, int size, boolean isSigned, Program program) { public String getEquateName(Scalar scalar, Program program) {
Scalar scalar = new Scalar(size * 8, value);
return Long.toOctalString(scalar.getUnsignedValue()) + "o"; return Long.toOctalString(scalar.getUnsignedValue()) + "o";
} }
} }
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -18,9 +18,9 @@ package ghidra.app.plugin.core.decompile.actions;
import java.util.List; import java.util.List;
import docking.action.MenuData; import docking.action.MenuData;
import ghidra.app.decompiler.ClangToken; import ghidra.app.decompiler.*;
import ghidra.app.decompiler.ClangVariableToken;
import ghidra.app.plugin.core.decompile.DecompilerActionContext; import ghidra.app.plugin.core.decompile.DecompilerActionContext;
import ghidra.app.plugin.core.decompile.actions.ConvertConstantAction.NearMatchValues;
import ghidra.app.util.HelpTopics; import ghidra.app.util.HelpTopics;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
@@ -28,7 +28,12 @@ import ghidra.program.model.pcode.*;
import ghidra.program.model.symbol.Equate; import ghidra.program.model.symbol.Equate;
import ghidra.program.model.symbol.EquateTable; import ghidra.program.model.symbol.EquateTable;
import ghidra.util.HelpLocation; import ghidra.util.HelpLocation;
import ghidra.util.Msg;
import ghidra.util.exception.InvalidInputException;
/**
* Remove a selected conversion or other equate set on a constant
*/
public class RemoveEquateAction extends AbstractDecompilerAction { public class RemoveEquateAction extends AbstractDecompilerAction {
public RemoveEquateAction() { public RemoveEquateAction() {
@@ -40,6 +45,17 @@ public class RemoveEquateAction extends AbstractDecompilerAction {
@Override @Override
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) { protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
ClangToken tokenAtCursor = context.getTokenAtCursor(); ClangToken tokenAtCursor = context.getTokenAtCursor();
if (tokenAtCursor instanceof ClangCaseToken) {
// Check for a conversion applied to case labels on a specific jumptable/switch
PcodeOp switchOp = ((ClangCaseToken) tokenAtCursor).getSwitchOp();
if (switchOp != null) {
int format = JumpTable.getFormatOverride(context.getFunction(),
switchOp.getSeqnum().getTarget());
return (EquateSymbol.FORMAT_DEFAULT != format);
}
return false;
}
// Check for any Equate on a constant token
if (!(tokenAtCursor instanceof ClangVariableToken)) { if (!(tokenAtCursor instanceof ClangVariableToken)) {
return false; return false;
} }
@@ -51,6 +67,13 @@ public class RemoveEquateAction extends AbstractDecompilerAction {
return (symbol instanceof EquateSymbol); return (symbol instanceof EquateSymbol);
} }
/**
* Remove a specific Equate reference on a constant
* @param program is the Program
* @param equate is the Equate being referenced
* @param refAddr is the address of the reference
* @param convertHash is a dynamic hash of the Varnode (or -1 if the equate is on an instruction)
*/
private void removeReference(Program program, Equate equate, Address refAddr, private void removeReference(Program program, Equate equate, Address refAddr,
long convertHash) { long convertHash) {
int transaction = program.startTransaction("Remove Equate Reference"); int transaction = program.startTransaction("Remove Equate Reference");
@@ -70,9 +93,35 @@ public class RemoveEquateAction extends AbstractDecompilerAction {
} }
/**
* Remove the integer format override for a specific JumpTable
* @param context is the selected context
* @param opAddress is the address of the JumpTable branch site
*/
private void removeCaseOverride(DecompilerActionContext context, Address opAddress) {
Program program = context.getProgram();
int transaction = program.startTransaction("Remove Case Label Override");
boolean commit = false;
try {
JumpTable.writeFormat(context.getFunction(), opAddress, transaction);
commit = true;
}
catch (InvalidInputException e) {
Msg.error(this, e);
}
finally {
program.endTransaction(transaction, commit);
}
}
@Override @Override
protected void decompilerActionPerformed(DecompilerActionContext context) { protected void decompilerActionPerformed(DecompilerActionContext context) {
ClangToken tokenAtCursor = context.getTokenAtCursor(); ClangToken tokenAtCursor = context.getTokenAtCursor();
if (tokenAtCursor instanceof ClangCaseToken) {
PcodeOp switchOp = ((ClangCaseToken) tokenAtCursor).getSwitchOp();
removeCaseOverride(context, switchOp.getSeqnum().getTarget());
return;
}
if (!(tokenAtCursor instanceof ClangVariableToken)) { if (!(tokenAtCursor instanceof ClangVariableToken)) {
return; return;
} }
@@ -91,12 +140,13 @@ public class RemoveEquateAction extends AbstractDecompilerAction {
Program program = context.getProgram(); Program program = context.getProgram();
EquateTable equateTable = program.getEquateTable(); EquateTable equateTable = program.getEquateTable();
List<Equate> equates = equateTable.getEquates(convertAddr); List<Equate> equates = equateTable.getEquates(convertAddr);
NearMatchValues values =
new NearMatchValues(convertVn.getOffset(), convertVn.getSize());
for (Equate equate : equates) { for (Equate equate : equates) {
if (equate.getValue() != convertVn.getOffset()) { if (values.isMatch(equate.getValue())) {
continue; removeReference(program, equate, convertAddr, convertHash);
break;
} }
removeReference(program, equate, convertAddr, convertHash);
break;
} }
} }
} }
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -43,16 +43,15 @@ public class SetEquateAction extends ConvertConstantAction {
} }
@Override @Override
public String getMenuDisplay(long value, int size, boolean isSigned, Program program) { public String getMenuDisplay(Scalar scalar, Program program) {
return null; // Menu isn't tailored for this action return null; // Menu isn't tailored for this action
} }
@Override @Override
public String getEquateName(long value, int size, boolean isSigned, Program program) { public String getEquateName(Scalar scalar, Program program) {
if (program == null) { if (program == null) {
return null; return null;
} }
Scalar scalar = new Scalar(size * 8, value, isSigned);
SetEquateDialog dialog = new SetEquateDialog(plugin.getTool(), program, scalar); SetEquateDialog dialog = new SetEquateDialog(plugin.getTool(), program, scalar);
dialog.disableHasSelection(); dialog.disableHasSelection();
int res = dialog.showSetDialog(); int res = dialog.showSetDialog();
@@ -66,7 +65,7 @@ public class SetEquateAction extends ConvertConstantAction {
@Override @Override
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) { protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
ConvertConstantTask task = establishTask(context, false); Scalar scalar = ConvertConstantEquateTask.getConvertibleConstant(context, convertType);
return (task != null); return (scalar != null);
} }
} }
@@ -27,6 +27,7 @@ import ghidra.app.decompiler.component.ClangTextField;
import ghidra.app.plugin.core.decompile.actions.*; import ghidra.app.plugin.core.decompile.actions.*;
import ghidra.program.model.listing.*; import ghidra.program.model.listing.*;
import ghidra.program.model.pcode.*; import ghidra.program.model.pcode.*;
import ghidra.program.model.scalar.Scalar;
import ghidra.program.model.symbol.*; import ghidra.program.model.symbol.*;
public class DecompilerEquateTest extends AbstractDecompilerTest { public class DecompilerEquateTest extends AbstractDecompilerTest {
@@ -41,7 +42,7 @@ public class DecompilerEquateTest extends AbstractDecompilerTest {
} }
@Override @Override
public String getEquateName(long value, int size, boolean isSigned, Program program) { public String getEquateName(Scalar scalar, Program program) {
if (program == null) { if (program == null) {
return null; return null;
} }
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -115,34 +115,65 @@ public class EquateSymbol extends HighSymbol {
decoder.closeElement(symel); decoder.closeElement(symel);
} }
/**
* @param convert is an integer conversion type
* @return the name of the corresponding conversion
*/
public static String getIntegerFormatString(int convert) {
if (convert == FORMAT_HEX) {
return "hex";
}
else if (convert == FORMAT_DEC) {
return "dec";
}
else if (convert == FORMAT_OCT) {
return "oct";
}
else if (convert == FORMAT_BIN) {
return "bin";
}
else if (convert == FORMAT_CHAR) {
return "char";
}
else if (convert == FORMAT_FLOAT) {
return "float";
}
else if (convert == FORMAT_DOUBLE) {
return "double";
}
return "_";
}
/**
* @param format is the name of a conversion format
* @return return the matching conversion type or FORMAT_DEFAULT if there is no matching conversion
*/
public static int getFormatStringValue(String format) {
switch (format) {
case "hex":
return FORMAT_HEX;
case "dec":
return FORMAT_DEC;
case "oct":
return FORMAT_OCT;
case "bin":
return FORMAT_BIN;
case "char":
return FORMAT_CHAR;
case "float":
return FORMAT_FLOAT;
case "double":
return FORMAT_DOUBLE;
}
return FORMAT_DEFAULT;
}
@Override @Override
public void encode(Encoder encoder) throws IOException { public void encode(Encoder encoder) throws IOException {
encoder.openElement(ELEM_EQUATESYMBOL); encoder.openElement(ELEM_EQUATESYMBOL);
encodeHeader(encoder); encodeHeader(encoder);
if (convert != 0) { if (convert != 0) {
String formString = "hex"; String formString = getIntegerFormatString(convert);
if (convert == FORMAT_HEX) {
// Most common case
}
else if (convert == FORMAT_DEC) {
formString = "dec";
}
else if (convert == FORMAT_OCT) {
formString = "oct";
}
else if (convert == FORMAT_BIN) {
formString = "bin";
}
else if (convert == FORMAT_CHAR) {
formString = "char";
}
else if (convert == FORMAT_FLOAT) {
formString = "float";
}
else if (convert == FORMAT_DOUBLE) {
formString = "double";
}
encoder.writeString(ATTRIB_FORMAT, formString); encoder.writeString(ATTRIB_FORMAT, formString);
} }
encoder.openElement(ELEM_VALUE); encoder.openElement(ELEM_VALUE);
@@ -302,7 +302,7 @@ public class HighFunction extends PcodeSyntaxTree {
private void decodeJumpTableList(Decoder decoder) throws DecoderException { private void decodeJumpTableList(Decoder decoder) throws DecoderException {
int el = decoder.openElement(ELEM_JUMPTABLELIST); int el = decoder.openElement(ELEM_JUMPTABLELIST);
while (decoder.peekElement() != 0) { while (decoder.peekElement() != 0) {
JumpTable table = new JumpTable(entryPoint.getAddressSpace()); JumpTable table = new JumpTable();
table.decode(decoder); table.decode(decoder);
if (!table.isEmpty()) { if (!table.isEmpty()) {
if (jumpTables == null) { if (jumpTables == null) {
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -23,7 +23,6 @@ import java.util.ArrayList;
import ghidra.program.database.symbol.CodeSymbol; import ghidra.program.database.symbol.CodeSymbol;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.listing.Function; import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.program.model.symbol.*; import ghidra.program.model.symbol.*;
@@ -101,7 +100,6 @@ public class JumpTable {
} }
} }
private AddressSpace preferredSpace;
private Address opAddress; private Address opAddress;
// Address corresponds to label entries. If DEFAULT_VALUE, then entry is the default guard case, not a jump target. // Address corresponds to label entries. If DEFAULT_VALUE, then entry is the default guard case, not a jump target.
@@ -109,21 +107,22 @@ public class JumpTable {
private Integer labelTable[]; private Integer labelTable[];
private LoadTable loadTable[]; private LoadTable loadTable[];
private BasicOverride override; private BasicOverride override;
private int displayFormat; // Default format for displaying integer case values
public JumpTable(AddressSpace preferredSpace) { public JumpTable() {
this.preferredSpace = preferredSpace;
opAddress = null; opAddress = null;
addressTable = null; addressTable = null;
labelTable = null; labelTable = null;
loadTable = null; loadTable = null;
override = null; override = null;
displayFormat = 0;
} }
public JumpTable(Address addr, ArrayList<Address> destlist, boolean override) { public JumpTable(Address addr, ArrayList<Address> destlist, boolean override, int format) {
opAddress = addr; opAddress = addr;
preferredSpace = opAddress.getAddressSpace();
labelTable = null; labelTable = null;
loadTable = null; loadTable = null;
displayFormat = format;
if (override) { if (override) {
addressTable = null; addressTable = null;
this.override = new BasicOverride(destlist); this.override = new BasicOverride(destlist);
@@ -152,6 +151,9 @@ public class JumpTable {
*/ */
public void decode(Decoder decoder) throws DecoderException { public void decode(Decoder decoder) throws DecoderException {
int el = decoder.openElement(ELEM_JUMPTABLE); int el = decoder.openElement(ELEM_JUMPTABLE);
if (decoder.getNextAttributeId() == ATTRIB_FORMAT.id()) {
displayFormat = (int) decoder.readUnsignedInteger();
}
if (decoder.peekElement() == 0) { // Empty jumptable if (decoder.peekElement() == 0) { // Empty jumptable
decoder.closeElement(el); decoder.closeElement(el);
return; return;
@@ -206,6 +208,9 @@ public class JumpTable {
public void encode(Encoder encoder) throws IOException { public void encode(Encoder encoder) throws IOException {
encoder.openElement(ELEM_JUMPTABLE); encoder.openElement(ELEM_JUMPTABLE);
if (displayFormat != 0) {
encoder.writeUnsignedInteger(ATTRIB_FORMAT, displayFormat);
}
AddressXML.encode(encoder, opAddress); AddressXML.encode(encoder, opAddress);
if (addressTable != null) { if (addressTable != null) {
for (Address element : addressTable) { for (Address element : addressTable) {
@@ -236,6 +241,82 @@ public class JumpTable {
return loadTable.clone(); return loadTable.clone();
} }
/**
* Find or create a namespace for a specific jumptable
* @param func is the function with the jumptable
* @param opAddress is the address of the call point using the table
* @param symtab is the symbol table
* @return the jumptable namespace
* @throws InvalidInputException for any problems creating the namespace
*/
private static Namespace getSwitchNamespace(Function func, Address opAddress,
SymbolTable symtab)
throws InvalidInputException {
if (!func.getBody().contains(opAddress)) {
throw new InvalidInputException("Switch is not in function body");
}
Namespace space = HighFunction.findCreateOverrideSpace(func);
if (space == null) {
throw new InvalidInputException("Could not create \"override\" namespace");
}
return HighFunction.findCreateNamespace(symtab, space, "jmp_" + opAddress.toString());
}
/**
* Get any case format override for this jumptable.
* @param func is the function with the jumptable
* @param opAddress is the address of the call point using the table
* @return the overriding format if it exists or EquateSymbol.FORMAT_DEFAULT otherwise
*/
public static int getFormatOverride(Function func, Address opAddress) {
Namespace space = HighFunction.findOverrideSpace(func);
if (space == null)
return EquateSymbol.FORMAT_DEFAULT;
SymbolTable symtab = func.getProgram().getSymbolTable();
space = HighFunction.findNamespace(symtab, space, "jmp_" + opAddress.toString());
if (space == null)
return EquateSymbol.FORMAT_DEFAULT;
SymbolIterator iter = symtab.getSymbols(space);
while (iter.hasNext()) {
Symbol sym = iter.next();
if (sym.getName().startsWith("format")) {
String format = sym.getName().substring(7);
return EquateSymbol.getFormatStringValue(format);
}
}
return EquateSymbol.FORMAT_DEFAULT;
}
/**
* Write/update the current display format for a JumpTable.
* The format is stored as a symbol with a name like "format_dec" or "format_char"
* Any previous existing symbol is removed.
* @param func is the function containing the switch
* @param opAddress is the address of the CALLIND
* @param displayFormat is the new desired format
* @throws InvalidInputException for problems writing to the database
*/
public static void writeFormat(Function func, Address opAddress, int displayFormat)
throws InvalidInputException {
Program program = func.getProgram();
SymbolTable symtab = program.getSymbolTable();
Namespace space = getSwitchNamespace(func, opAddress, symtab);
SymbolIterator iter = symtab.getSymbols(space);
while (iter.hasNext()) {
Symbol sym = iter.next();
if (sym.getName().startsWith("format")) {
HighFunction.deleteSymbol(symtab, sym.getAddress(), sym.getName(), space);
break;
}
}
if (displayFormat != EquateSymbol.FORMAT_DEFAULT) {
String nm = "format_" + EquateSymbol.getIntegerFormatString(displayFormat);
HighFunction.createLabelSymbol(symtab, opAddress, nm, space, SourceType.USER_DEFINED,
false);
}
}
public void writeOverride(Function func) throws InvalidInputException { public void writeOverride(Function func) throws InvalidInputException {
if (override == null) { if (override == null) {
throw new InvalidInputException("Jumptable is not an override"); throw new InvalidInputException("Jumptable is not an override");
@@ -244,23 +325,14 @@ public class JumpTable {
if (destlist.length == 0) { if (destlist.length == 0) {
throw new InvalidInputException("Jumptable has no destinations"); throw new InvalidInputException("Jumptable has no destinations");
} }
if (!func.getBody().contains(opAddress)) {
throw new InvalidInputException("Switch is not in function body");
}
Program program = func.getProgram(); Program program = func.getProgram();
SymbolTable symtab = program.getSymbolTable(); SymbolTable symtab = program.getSymbolTable();
Namespace space = HighFunction.findCreateOverrideSpace(func); Namespace space = getSwitchNamespace(func, opAddress, symtab);
if (space == null) {
throw new InvalidInputException("Could not create \"override\" namespace");
}
space = HighFunction.findCreateNamespace(symtab, space, "jmp_" + opAddress.toString());
if (!HighFunction.clearNamespace(symtab, space)) { if (!HighFunction.clearNamespace(symtab, space)) {
throw new InvalidInputException( throw new InvalidInputException(
"Jumptable override namespace contains non-label symbols."); "Jumptable override namespace contains non-label symbols.");
} }
HighFunction.createLabelSymbol(symtab, opAddress, "switch", space, SourceType.USER_DEFINED, HighFunction.createLabelSymbol(symtab, opAddress, "switch", space, SourceType.USER_DEFINED,
false); false);
for (int i = 0; i < destlist.length; ++i) { for (int i = 0; i < destlist.length; ++i) {
@@ -268,12 +340,18 @@ public class JumpTable {
HighFunction.createLabelSymbol(symtab, destlist[i], nm, space, SourceType.USER_DEFINED, HighFunction.createLabelSymbol(symtab, destlist[i], nm, space, SourceType.USER_DEFINED,
false); false);
} }
if (displayFormat != EquateSymbol.FORMAT_DEFAULT) {
String nm = "format_" + EquateSymbol.getIntegerFormatString(displayFormat);
HighFunction.createLabelSymbol(symtab, opAddress, nm, space, SourceType.USER_DEFINED,
false);
}
} }
public static JumpTable readOverride(Namespace space, SymbolTable symtab) { public static JumpTable readOverride(Namespace space, SymbolTable symtab) {
Address branchind = null; Address branchind = null;
ArrayList<Address> destlist = new ArrayList<>(); ArrayList<Address> destlist = new ArrayList<>();
SymbolIterator iter = symtab.getSymbols(space); SymbolIterator iter = symtab.getSymbols(space);
int displayFormat = 0;
while (iter.hasNext()) { while (iter.hasNext()) {
Symbol sym = iter.next(); Symbol sym = iter.next();
if (!(sym instanceof CodeSymbol)) { if (!(sym instanceof CodeSymbol)) {
@@ -286,9 +364,14 @@ public class JumpTable {
else if (sym.getName().startsWith("case")) { else if (sym.getName().startsWith("case")) {
destlist.add(addr); destlist.add(addr);
} }
else if (sym.getName().startsWith("format")) {
branchind = addr;
displayFormat = EquateSymbol.getFormatStringValue(sym.getName().substring(7));
}
} }
if ((branchind != null) && (destlist.size() > 0)) { if ((branchind != null) && (destlist.size() > 0 || displayFormat != 0)) {
return new JumpTable(branchind, destlist, true); boolean override = destlist.size() > 0;
return new JumpTable(branchind, destlist, override, displayFormat);
} }
return null; return null;
} }