Decompiler window integer conversions

This commit is contained in:
caheckman 2021-06-23 15:18:34 -04:00
parent 11149b9ef2
commit 1391e83ce9
35 changed files with 2430 additions and 281 deletions

View file

@ -16,6 +16,7 @@
package ghidra.trace.database.program;
import java.util.Collection;
import java.util.List;
import com.google.common.collect.Range;
@ -85,6 +86,11 @@ public class DBTraceProgramViewEquate implements Equate {
return refs.toArray(new EquateReference[refs.size()]);
}
@Override
public List<EquateReference> getReferences(Address refAddr) {
return equate.getReferences(refAddr);
}
@Override
public void removeReference(Address refAddr, int opndPosition) {
TraceEquateReference ref = equate.getReference(program.snap, null, refAddr, opndPosition);

View file

@ -16,6 +16,7 @@
package ghidra.trace.database.symbol;
import java.util.Collection;
import java.util.List;
import com.google.common.collect.Range;
@ -23,6 +24,7 @@ import db.DBRecord;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.Enum;
import ghidra.program.model.pcode.Varnode;
import ghidra.program.model.symbol.EquateReference;
import ghidra.trace.model.symbol.TraceEquate;
import ghidra.trace.model.symbol.TraceEquateReference;
import ghidra.trace.model.thread.TraceThread;
@ -114,6 +116,10 @@ public class DBTraceEquate extends DBAnnotatedObject implements TraceEquate {
return null;
}
public List<EquateReference> getReferences(Address refAddr) {
return null;
}
@Override
public TraceEquateReference getReference(long snap, TraceThread thread, Address address,
int operandIndex) {

View file

@ -479,6 +479,17 @@ public class SetEquateDialog extends DialogComponentProvider {
overwriteExistingEquates.setEnabled(!applyToCurrent.isSelected());
}
/**
* For using the dialog outside of the EquatePlugin, the "Apply to Current" radio button
* can be selected and the other buttons disabled.
*/
public void disableHasSelection() {
applyToAll.setEnabled(false);
applyToSelection.setEnabled(false);
applyToSelection.setSelected(false);
overwriteExistingEquates.setEnabled(false);
}
/**
* Sets the dialogs status display to the given message.
*/

View file

@ -9,6 +9,7 @@ src/decompile/.project||GHIDRA||||END|
src/decompile/cpp/.gitignore||GHIDRA||||END|
src/decompile/cpp/Doxyfile||GHIDRA|||Most of this file is autogenerated by doxygen which falls under the GPL - output from GPL products are NOT GPL! - mjbell4|END|
src/decompile/cpp/Makefile||GHIDRA||||END|
src/decompile/datatests/convert.xml||GHIDRA||||END|
src/decompile/datatests/deadvolatile.xml||GHIDRA||||END|
src/decompile/datatests/floatprint.xml||GHIDRA||||END|
src/decompile/datatests/forloop1.xml||GHIDRA||||END|

View file

@ -639,6 +639,21 @@ void FunctionSymbol::restoreXml(const Element *el)
}
}
/// Create a symbol either to associate a name with a constant or to force a display conversion
///
/// \param sc is the scope owning the new symbol
/// \param nm is the name of the equate (an empty string can be used for a convert)
/// \param format is the desired display conversion (0 for no conversion)
/// \param val is the constant value whose display is being altered
EquateSymbol::EquateSymbol(Scope *sc,const string &nm,uint4 format,uintb val)
: Symbol(sc, nm, (Datatype *)0)
{
value = val;
category = 1;
type = sc->getArch()->types->getBase(1,TYPE_UNKNOWN);
dispflags |= format;
}
/// An EquateSymbol should survive certain kinds of transforms during decompilation,
/// such as negation, twos-complementing, adding or subtracting 1.
/// Return \b true if the given value looks like a transform of this type relative
@ -688,7 +703,6 @@ void EquateSymbol::restoreXml(const Element *el)
TypeFactory *types = scope->getArch()->types;
type = types->getBase(1,TYPE_UNKNOWN);
checkSizeTypeLock();
}
/// Label symbols don't really have a data-type, so we just put
@ -1661,6 +1675,26 @@ Symbol *Scope::addDynamicSymbol(const string &nm,Datatype *ct,const Address &cad
return sym;
}
/// \brief Create a symbol that forces a constant display conversion
///
/// \param format is the type of conversion (Symbol::force_hex, Symbol::force_dec, etc.)
/// \param value is the constant value being converted
/// \param addr is the address of the p-code op reading the constant
/// \param hash is the dynamic hash identifying the constant
/// \return the new EquateSymbol
Symbol *Scope::addConvertSymbol(uint4 format,uintb value,Address &addr,uint8 hash)
{
Symbol *sym;
sym = new EquateSymbol(owner,"",format,value);
RangeList rnglist;
if (!addr.isInvalid())
rnglist.insertRange(addr.getSpace(),addr.getOffset(),addr.getOffset());
addDynamicMapInternal(sym,Varnode::mapped,hash,0,1,rnglist);
return sym;
}
/// Create default name given information in the Symbol and possibly a representative Varnode.
/// This method extracts the crucial properties and then uses the buildVariableName method to
/// construct the actual name.

View file

@ -270,6 +270,7 @@ public:
class EquateSymbol : public Symbol {
uintb value; ///< Value of the constant being equated
public:
EquateSymbol(Scope *sc,const string &nm,uint4 format,uintb val); ///< Constructor
EquateSymbol(Scope *sc) : Symbol(sc) { value = 0; category = 1; } ///< Constructor for use with restoreXml
uintb getValue(void) const { return value; } ///< Get the constant value
bool isValueClose(uintb op2Value,int4 size) const; ///< Is the given value similar to \b this equate
@ -712,6 +713,7 @@ public:
ExternRefSymbol *addExternalRef(const Address &addr,const Address &refaddr,const string &nm);
LabSymbol *addCodeLabel(const Address &addr,const string &nm);
Symbol *addDynamicSymbol(const string &nm,Datatype *ct,const Address &caddr,uint8 hash);
Symbol *addConvertSymbol(uint4 format,uintb value,Address &addr,uint8 hash);
string buildDefaultName(Symbol *sym,int4 &base,Varnode *vn) const; ///< Create a default name for the given Symbol
bool isReadOnly(const Address &addr,int4 size,const Address &usepoint) const;
void printBounds(ostream &s) const { rangetree.printBounds(s); } ///< Print a description of \b this Scope's \e owned memory ranges

View file

@ -1207,6 +1207,10 @@ bool Funcdata::attemptDynamicMappingLate(SymbolEntry *entry,DynamicHash &dhash)
return false;
if (vn->getSymbolEntry() == entry) return false; // Already applied it
Symbol *sym = entry->getSymbol();
if (sym->getCategory() == 1) { // Equate symbol does not depend on size
vn->setSymbolEntry(entry);
return true;
}
if (vn->getSize() != entry->getSize()) {
ostringstream s;
s << "Unable to use symbol ";

View file

@ -57,6 +57,7 @@ void IfaceDecompCapability::registerCommands(IfaceStatus *status)
status->registerCom(new IfcMapfunction(),"map","function");
status->registerCom(new IfcMapexternalref(),"map","externalref");
status->registerCom(new IfcMaplabel(),"map","label");
status->registerCom(new IfcMapconvert(),"map","convert");
status->registerCom(new IfcPrintdisasm(),"disassemble");
status->registerCom(new IfcDecompile(),"decompile");
status->registerCom(new IfcDump(),"dump");
@ -665,6 +666,45 @@ void IfcMaplabel::execute(istream &s)
scope->setAttribute(sym,Varnode::namelock|Varnode::typelock);
}
/// \class IfcMapconvert
/// \brief Create an convert directive: `map convert <format> <value> <address> <hash>`
///
/// Creates a \e convert directive that causes a targeted constant value to be displayed
/// with the specified integer format. The constant is specified by \e value, and the
/// \e address of the p-code op using the constant plus a dynamic \e hash is also given.
void IfcMapconvert::execute(istream &s)
{
if (dcp->fd == (Funcdata *)0)
throw IfaceExecutionError("No function loaded");
string name;
uintb value;
uint8 hash;
int4 size;
uint4 format = 0;
s >> name; // Parse the format token
if (name == "hex")
format = Symbol::force_hex;
else if (name == "dec")
format = Symbol::force_dec;
else if (name == "bin")
format = Symbol::force_bin;
else if (name == "oct")
format = Symbol::force_oct;
else if (name == "char")
format = Symbol::force_char;
else
throw IfaceParseError("Bad convert format");
s >> ws >> hex >> value;
Address addr = parse_machaddr(s,size,*dcp->conf->types); // Read pc address of hash
s >> hex >> hash; // Parse the hash value
dcp->fd->getScopeLocal()->addConvertSymbol(format, value, addr, hash);
}
/// \class IfcPrintdisasm
/// \brief Print disassembly of a memory range: `disassemble [<address1> <address2>]`
///

View file

@ -178,6 +178,11 @@ public:
virtual void execute(istream &s);
};
class IfcMapconvert : public IfaceDecompCommand {
public:
virtual void execute(istream &s);
};
class IfcPrintdisasm : public IfaceDecompCommand {
public:
virtual void execute(istream &s);

View file

@ -1099,7 +1099,7 @@ void PrintC::push_integer(uintb val,int4 sz,bool sign,
displayFormat = sym->getDisplayFormat();
}
}
if (sign) { // Print the constant as signed
if (sign && displayFormat != Symbol::force_char) { // Print the constant as signed
uintb mask = calc_mask(sz);
uintb flip = val^mask;
print_negsign = (flip < val);
@ -1136,22 +1136,15 @@ void PrintC::push_integer(uintb val,int4 sz,bool sign,
else if (displayFormat == Symbol::force_oct)
t << oct << '0' << val;
else if (displayFormat == Symbol::force_char) {
int4 internalSize = 4;
if (val < 256)
internalSize = 1;
else if (val < 65536)
internalSize = 2;
if ((internalSize==1)&&((val<7)||(val>0x7e)||((val>13)&&(val<0x20)))) { // not a good character constant
t << dec << val; // Just emit as decimal
}
else {
if (doEmitWideCharPrefix() && internalSize > 1)
if (doEmitWideCharPrefix() && sz > 1)
t << 'L'; // Print symbol indicating wide character
t << '\''; // char is surrounded with single quotes
if (sz == 1 && val >= 0x80)
printCharHexEscape(t,(int4)val);
else
printUnicode(t,(int4)val);
t << '\'';
}
}
else { // Must be Symbol::force_bin
t << "0b";
formatBinary(t, val);
@ -1270,15 +1263,8 @@ void PrintC::printUnicode(ostream &s,int4 onechar) const
s << "\\\'";
return;
}
// Generic unicode escape
if (onechar < 256) {
s << "\\x" << setfill('0') << setw(2) << hex << onechar;
}
else if (onechar < 65536) {
s << "\\x" << setfill('0') << setw(4) << hex << onechar;
}
else
s << "\\x" << setfill('0') << setw(8) << hex << onechar;
// Generic escape code
printCharHexEscape(s, onechar);
return;
}
StringManager::writeUtf8(s, onechar); // emit normally
@ -1321,6 +1307,22 @@ bool PrintC::doEmitWideCharPrefix(void) const
return true;
}
/// Print the given value using the standard character hexadecimal escape sequence.
/// \param s is the stream to write to
/// \param val is the given value
void PrintC::printCharHexEscape(ostream &s,int4 val)
{
if (val < 256) {
s << "\\x" << setfill('0') << setw(2) << hex << val;
}
else if (val < 65536) {
s << "\\x" << setfill('0') << setw(4) << hex << val;
}
else
s << "\\x" << setfill('0') << setw(8) << hex << val;
}
/// \brief Print a quoted (unicode) string at the given address.
///
/// Data for the string is obtained directly from the LoadImage. The bytes are checked
@ -1397,28 +1399,50 @@ void PrintC::resetDefaultsPrintC(void)
/// \param ct is data-type attached to the value
/// \param vn is the Varnode holding the value
/// \param op is the PcodeOp using the value
void PrintC::pushCharConstant(uintb val,const TypeChar *ct,const Varnode *vn,const PcodeOp *op)
void PrintC::pushCharConstant(uintb val,const Datatype *ct,const Varnode *vn,const PcodeOp *op)
{
ostringstream t;
uint4 displayFormat = 0;
bool isSigned = (ct->getMetatype() == TYPE_INT);
if ((vn != (const Varnode *)0)&&(!vn->isAnnotation())) {
Symbol *sym = vn->getHigh()->getSymbol();
if (sym != (Symbol *)0) {
if (sym->isNameLocked() && (sym->getCategory() == 1)) {
if (pushEquate(val,vn->getSize(),(EquateSymbol *)sym,vn,op))
return;
}
displayFormat = sym->getDisplayFormat();
if (displayFormat == Symbol::force_bin || displayFormat == Symbol::force_dec || displayFormat == Symbol::force_oct) {
push_integer(val, ct->getSize(), isSigned, vn, op);
return;
}
}
}
if ((ct->getSize()==1)&&(val >= 0x80)) {
// For byte characters, the encoding is assumed to be ASCII, UTF-8, or some other
// code-page that extends ASCII. At 0x80 and above, we cannot treat the value as a
// unicode code-point. Its either part of a multi-byte UTF-8 encoding or an unknown
// code-page value. In either case, we print it as an integer.
push_integer(val,1,true,vn,op);
// code-page value. In either case, we print as an integer or an escape sequence.
if (displayFormat != Symbol::force_hex && displayFormat != Symbol::force_char) {
push_integer(val, 1, isSigned, vn, op);
return;
}
else {
displayFormat = Symbol::force_hex; // Fallthru but force a hex representation
}
ostringstream t;
// From here we assume, the constant value is a direct unicode code-point.
// The value could be an illegal code-point (surrogates or beyond the max code-point),
// but this will just be emitted as an escape sequence.
if (doEmitWideCharPrefix() && ct->getSize() > 1)
t << 'L'; // Print symbol indicating wide character
t << '\''; // char is surrounded with single quotes
if (displayFormat == Symbol::force_hex) {
printCharHexEscape(t,(int4)val);
}
else
printUnicode(t,(int4)val);
t << '\'';
pushAtom(Atom(t.str(),vartoken,EmitXml::const_color,op,vn));
}
}
/// \brief Push an enumerated value to the RPN stack

View file

@ -131,7 +131,7 @@ protected:
virtual void pushTypeEnd(const Datatype *ct); ///< Push the tail ends of a data-type declaration onto the RPN stack
void pushBoolConstant(uintb val,const TypeBase *ct,const Varnode *vn,
const PcodeOp *op);
void pushCharConstant(uintb val,const TypeChar *ct,const Varnode *vn,
void pushCharConstant(uintb val,const Datatype *ct,const Varnode *vn,
const PcodeOp *op);
void pushEnumConstant(uintb val,const TypeEnum *ct,const Varnode *vn,
const PcodeOp *op);
@ -162,6 +162,7 @@ protected:
void opFunc(const PcodeOp *op); ///< Push a \e functional expression based on the given p-code op to the RPN stack
void opTypeCast(const PcodeOp *op); ///< Push the given p-code op using type-cast syntax to the RPN stack
void opHiddenFunc(const PcodeOp *op); ///< Push the given p-code op as a hidden token
static void printCharHexEscape(ostream &s,int4 val); ///< Print value as an escaped hex sequence
bool printCharacterConstant(ostream &s,const Address &addr,Datatype *charType) const;
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

View file

@ -431,7 +431,7 @@ bool PrintLanguage::unicodeNeedsEscape(int4 codepoint)
if (codepoint > 0xa0) { // Printable codepoints A1-FF
return false;
}
return true;
return true; // Delete + C1 Control characters
}
if (codepoint >= 0x2fa20) { // Up to last currently defined language
return true;
@ -483,6 +483,8 @@ bool PrintLanguage::unicodeNeedsEscape(int4 codepoint)
return true; // zero width non-breaking space
}
if (codepoint >= 0xfff0 && codepoint <= 0xffff) {
if ((codepoint == 0xfffc || codepoint == 0xfffd))
return false;
return true; // interlinear specials
}
return false;
@ -772,11 +774,11 @@ void PrintLanguage::formatBinary(ostream &s,uintb val)
s << '0';
return;
}
else if (pos < 7)
else if (pos <= 7)
pos = 7;
else if (pos < 15)
else if (pos <= 15)
pos = 15;
else if (pos < 31)
else if (pos <= 31)
pos = 31;
else
pos = 63;

View file

@ -2464,6 +2464,7 @@ int4 RuleZextEliminate::applyOp(PcodeOp *op,Funcdata &data)
val = vn2->getOffset();
if ((val>>(8*smallsize))==0) { // Is zero extension unnecessary
newvn = data.newConstant(smallsize,val);
newvn->copySymbolIfValid(vn2);
data.opSetInput(op,zext->getIn(0),zextslot);
data.opSetInput(op,newvn,otherslot);
return 1;
@ -3696,7 +3697,9 @@ int4 RuleXorCollapse::applyOp(PcodeOp *op,Funcdata &data)
}
coeff2 = xorvn->getOffset();
if (coeff2 == 0) return 0;
data.opSetInput(op,data.newConstant(op->getIn(1)->getSize(),coeff1^coeff2),1);
Varnode *constvn = data.newConstant(op->getIn(1)->getSize(),coeff1^coeff2);
constvn->copySymbolIfValid(xorvn);
data.opSetInput(op,constvn,1);
data.opSetInput(op,xorop->getIn(0),0);
return 1;
}
@ -3757,6 +3760,10 @@ int4 RuleAddMultCollapse::applyOp(PcodeOp *op,Funcdata &data)
uintb val = op->getOpcode()->evaluateBinary(c[0]->getSize(),c[0]->getSize(),c[0]->getOffset(),c[1]->getOffset());
newvn = data.newConstant(c[0]->getSize(),val);
if (c[0]->getSymbolEntry() != (SymbolEntry *)0)
newvn->copySymbolIfValid(c[0]);
else if (c[1]->getSymbolEntry() != (SymbolEntry *)0)
newvn->copySymbolIfValid(c[1]);
PcodeOp *newop = data.newOp(2,op->getAddr());
data.opSetOpcode(newop,CPUI_INT_ADD);
Varnode *newout = data.newUniqueOut(c[0]->getSize(),newop);
@ -3774,6 +3781,10 @@ int4 RuleAddMultCollapse::applyOp(PcodeOp *op,Funcdata &data)
uintb val = op->getOpcode()->evaluateBinary(c[0]->getSize(),c[0]->getSize(),c[0]->getOffset(),c[1]->getOffset());
newvn = data.newConstant(c[0]->getSize(),val);
if (c[0]->getSymbolEntry() != (SymbolEntry *)0)
newvn->copySymbolIfValid(c[0]);
else if (c[1]->getSymbolEntry() != (SymbolEntry *)0)
newvn->copySymbolIfValid(c[1]);
data.opSetInput(op,newvn,1); // Replace c[0] with c[0]+c[1] or c[0]*c[1]
data.opSetInput(op,sub2,0); // Replace sub with sub2
return 1;

View file

@ -83,7 +83,7 @@ SubvariableFlow::ReplaceVarnode *SubvariableFlow::setReplacement(Varnode *vn,uin
if (sextval != cval)
return (ReplaceVarnode *)0;
}
return addConstant((ReplaceOp *)0,mask,0,vn->getOffset());
return addConstant((ReplaceOp *)0,mask,0,vn);
}
if (vn->isFree())
@ -628,7 +628,7 @@ bool SubvariableFlow::traceBackward(ReplaceVarnode *rvn)
sa = doesAndClear(op,rvn->mask);
if (sa != -1) {
rop = createOp(CPUI_COPY,1,rvn);
addConstant(rop,rvn->mask,0,op->getIn(sa)->getOffset());
addConstant(rop,rvn->mask,0,op->getIn(sa));
}
else {
rop = createOp(CPUI_INT_AND,2,rvn);
@ -640,7 +640,7 @@ bool SubvariableFlow::traceBackward(ReplaceVarnode *rvn)
sa = doesOrSet(op,rvn->mask);
if (sa != -1) {
rop = createOp(CPUI_COPY,1,rvn);
addConstant(rop,rvn->mask,0,op->getIn(sa)->getOffset());
addConstant(rop,rvn->mask,0,op->getIn(sa));
}
else {
rop = createOp(CPUI_INT_OR,2,rvn);
@ -676,7 +676,7 @@ bool SubvariableFlow::traceBackward(ReplaceVarnode *rvn)
newmask = rvn->mask >> sa; // What mask looks like before shift
if (newmask == 0) { // Subvariable filled with shifted zero
rop = createOp(CPUI_COPY,1,rvn);
addConstant(rop,rvn->mask,0,(uintb)0);
addNewConstant(rop,0,(uintb)0);
return true;
}
if ((newmask<<sa) != rvn->mask)
@ -690,7 +690,7 @@ bool SubvariableFlow::traceBackward(ReplaceVarnode *rvn)
newmask = (rvn->mask << sa) & calc_mask(op->getIn(0)->getSize());
if (newmask == 0) { // Subvariable filled with shifted zero
rop = createOp(CPUI_COPY,1,rvn);
addConstant(rop,rvn->mask,0,(uintb)0);
addNewConstant(rop,0,(uintb)0);
return true;
}
if ((newmask>>sa) != rvn->mask)
@ -772,7 +772,7 @@ bool SubvariableFlow::traceBackward(ReplaceVarnode *rvn)
if ((rvn->mask&1)==1) break; // Not normal variable flow
// Variable is filled with zero
rop = createOp(CPUI_COPY,1,rvn);
addConstant(rop,rvn->mask,0,(uintb)0);
addNewConstant(rop,0,(uintb)0);
return true;
default:
break; // Everything else we abort
@ -825,7 +825,7 @@ bool SubvariableFlow::traceForwardSext(ReplaceVarnode *rvn)
if (!op->getIn(1)->isConstant()) return false; // Right now we only deal with constant shifts
rop = createOpDown(CPUI_INT_SRIGHT,2,op,rvn,0);
if (!createLink(rop,rvn->mask,-1,outvn)) return false; // Keep the same mask size
addConstant(rop,calc_mask(op->getIn(1)->getSize()),1,op->getIn(1)->getOffset()); // Preserve the shift amount
addConstant(rop,calc_mask(op->getIn(1)->getSize()),1,op->getIn(1)); // Preserve the shift amount
hcount += 1;
break;
case CPUI_SUBPIECE:
@ -916,7 +916,7 @@ bool SubvariableFlow::traceBackwardSext(ReplaceVarnode *rvn)
rop = createOp(CPUI_INT_SRIGHT,2,rvn);
if (!createLink(rop,rvn->mask,0,op->getIn(0))) return false; // Keep the same mask
if (rop->input.size()==1)
addConstant(rop,calc_mask(op->getIn(1)->getSize()),1,op->getIn(1)->getOffset()); // Preserve the shift amount
addConstant(rop,calc_mask(op->getIn(1)->getSize()),1,op->getIn(1)); // Preserve the shift amount
return true;
case CPUI_CALL:
case CPUI_CALLIND:
@ -993,23 +993,40 @@ bool SubvariableFlow::createCompareBridge(PcodeOp *op,ReplaceVarnode *inrvn,int4
/// \brief Add a constant variable node to the logical subgraph
///
/// Unlike other subgraph variable nodes, this one does not maintain a mirror with the original containing Varnode.
/// \param rop is the logical operation taking the constant as input
/// \param mask is the set of bits holding the logical value (within a bigger value)
/// \param slot is the input slot to the operation
/// \param val is the bigger constant value holding the logical value
/// \param constvn is the original constant
SubvariableFlow::ReplaceVarnode *SubvariableFlow::addConstant(ReplaceOp *rop,uintb mask,
uint4 slot,uintb val)
{ // Add a constant to the replacement tree
uint4 slot,Varnode *constvn)
{
newvarlist.emplace_back();
ReplaceVarnode *res = &newvarlist.back();
res->vn = (Varnode *)0;
res->vn = constvn;
res->replacement = (Varnode *)0;
res->mask = mask;
// Calculate the actual constant value
int4 sa = leastsigbit_set(mask);
res->val = (mask & val) >> sa;
res->val = (mask & constvn->getOffset()) >> sa;
res->def = (ReplaceOp *)0;
if (rop != (ReplaceOp *)0) {
while(rop->input.size() <= slot)
rop->input.push_back((ReplaceVarnode *)0);
rop->input[slot] = res;
}
return res;
}
SubvariableFlow::ReplaceVarnode *SubvariableFlow::addNewConstant(ReplaceOp *rop,uint4 slot,uintb val)
{
newvarlist.emplace_back();
ReplaceVarnode *res = &newvarlist.back();
res->vn = (Varnode *)0;
res->replacement = (Varnode *)0;
res->mask = 0;
res->val = val;
res->def = (ReplaceOp *)0;
if (rop != (ReplaceOp *)0) {
while(rop->input.size() <= slot)
@ -1214,13 +1231,17 @@ Varnode *SubvariableFlow::getReplaceVarnode(ReplaceVarnode *rvn)
{
if (rvn->replacement != (Varnode *)0)
return rvn->replacement;
// Only a constant if BOTH replacement and vn fields are null
if (rvn->vn == (Varnode *)0) {
if (rvn->def==(ReplaceOp *)0) // A constant
if (rvn->def==(ReplaceOp *)0) // A constant that did not come from an original Varnode
return fd->newConstant(flowsize,rvn->val);
rvn->replacement = fd->newUnique(flowsize);
return rvn->replacement;
}
if (rvn->vn->isConstant()) {
Varnode *newVn = fd->newConstant(flowsize,rvn->val);
newVn->copySymbolIfValid(rvn->vn);
return newVn;
}
bool isinput = rvn->vn->isInput();
if (useSameAddress(rvn)) {

View file

@ -111,7 +111,8 @@ class SubvariableFlow {
void addBooleanPatch(PcodeOp *pullop,ReplaceVarnode *rvn,int4 slot);
void addSuggestedPatch(ReplaceVarnode *rvn,PcodeOp *pushop,int4 sa);
void addComparePatch(ReplaceVarnode *in1,ReplaceVarnode *in2,PcodeOp *op);
ReplaceVarnode *addConstant(ReplaceOp *rop,uintb mask,uint4 slot,uintb val);
ReplaceVarnode *addConstant(ReplaceOp *rop,uintb mask,uint4 slot,Varnode *constvn);
ReplaceVarnode *addNewConstant(ReplaceOp *rop,uint4 slot,uintb val);
void createNewOut(ReplaceOp *rop,uintb mask);
void replaceInput(ReplaceVarnode *rvn);
bool useSameAddress(ReplaceVarnode *rvn);

View file

@ -0,0 +1,66 @@
<decompilertest>
<binaryimage arch="x86:LE:64:default:gcc">
<!--
A contrived function with many different signed and unsigned constants to convert.
-->
<bytechunk space="ram" offset="0x1006b9" readonly="true">
554889e5bf0001
0000e883ffffffbf00feffffe879ffff
ffbf00030000e893ffffffbf00ffffff
e889ffffffbfe8030000e85bffffffbf
48f4ffffe851ffffffbf70170000e86b
ffffffbf00ea56fae861ffffffbfff01
0000e833ffffffbf25ffffffe829ffff
ffbf6d010000e843ffffffbffeffffff
e839ffffffbf88000000e80bffffffbf
3433ffffe801ffffffbfeeee0000e81b
ffffffbfdff7ffffe811ffffffbf6100
0000e8e3feffff905dc3
</bytechunk>
<symbol space="ram" offset="0x10064a" name="recv_signed"/>
<symbol space="ram" offset="0x10066e" name="recv_unsigned"/>
<symbol space="ram" offset="0x1006b9" name="convert_list"/>
</binaryimage>
<script>
<com>parse line extern void recv_signed(int4 val);</com>
<com>parse line extern void recv_unsigned(uint4 val);</com>
<com>lo fu convert_list</com>
<com>map convert dec 100 r0x1006c2 e1721eecc7</com>
<com>map convert dec fffffe00 r0x1006cc e17658909c</com>
<com>map convert dec 300 r0x1006d6 e1eab3665d</com>
<com>map convert dec ffffff00 r0x1006e0 e105173e00</com>
<com>map convert hex 3e8 r0x1006ea e104265e92</com>
<com>map convert hex fffff448 r0x1006f4 e168cf57b5</com>
<com>map convert hex 1770 r0x1006fe e15ef160b7</com>
<com>map convert hex fa56ea00 r0x100708 e1ce753c62</com>
<com>map convert oct 1ff r0x100712 e1edfc793e</com>
<com>map convert oct ffffff25 r0x10071c e142632204</com>
<com>map convert oct 16d r0x100726 e1e8d4339a</com>
<com>map convert oct fffffffe r0x100730 e107fa4a8f</com>
<com>map convert bin 88 r0x10073a e1955bc0dd </com>
<com>map convert bin ffff3334 r0x100744 e1b8401a2a</com>
<com>map convert bin eeee r0x10074e e19d2168d1</com>
<com>map convert bin fffff7df r0x100758 e10e9cbb42</com>
<com>map convert char 61 r0x10075d 20c20f516a</com>
<com>decompile</com>
<com>print C</com>
<com>quit</com>
</script>
<stringmatch name="Convert #1" min="1" max="1">recv_signed\(256\)</stringmatch>
<stringmatch name="Convert #2" min="1" max="1">recv_signed\(-512\)</stringmatch>
<stringmatch name="Convert #3" min="1" max="1">recv_unsigned\(768\)</stringmatch>
<stringmatch name="Convert #4" min="1" max="1">recv_unsigned\(4294967040\)</stringmatch>
<stringmatch name="Convert #5" min="1" max="1">recv_signed\(0x3e8\)</stringmatch>
<stringmatch name="Convert #6" min="1" max="1">recv_signed\(-0xbb8\)</stringmatch>
<stringmatch name="Convert #7" min="1" max="1">recv_unsigned\(0x1770\)</stringmatch>
<stringmatch name="Convert #8" min="1" max="1">recv_unsigned\(0xfa56ea00\)</stringmatch>
<stringmatch name="Convert #9" min="1" max="1">recv_signed\(0777\)</stringmatch>
<stringmatch name="Convert #10" min="1" max="1">recv_signed\(-0333\)</stringmatch>
<stringmatch name="Convert #11" min="1" max="1">recv_unsigned\(0555\)</stringmatch>
<stringmatch name="Convert #12" min="1" max="1">recv_unsigned\(037777777776\)</stringmatch>
<stringmatch name="Convert #13" min="1" max="1">recv_signed\(0b10001000\)</stringmatch>
<stringmatch name="Convert #14" min="1" max="1">recv_signed\(-0b1100110011001100\)</stringmatch>
<stringmatch name="Convert #15" min="1" max="1">recv_unsigned\(0b1110111011101110\)</stringmatch>
<stringmatch name="Convert #16" min="1" max="1">recv_unsigned\(0b11111111111111111111011111011111\)</stringmatch>
<stringmatch name="Convert #17" min="1" max="1">recv_signed\('a'\)</stringmatch>
</decompilertest>

View file

@ -2456,12 +2456,14 @@
<section id="AnnoteConstants">
<title>Constant Annotations</title>
<para>
Ghidra provides some ways to control how specific constants shown in disassembly are formatted or displayed.
These annotations are attached to constants as operands of specific machine instructions. To the extent
possible, the decompiler applies these annotations to the matching constant in the decompiler output.
The constant may be transformed from its value in the original machine instruction during the decompiler's
analysis. The decompiler will follow the constant through simple transformations, but if the transformed
is too far from the original value, the annotation will not be applied. The transforms followed are:
Ghidra provides numerous actions to control how specific constants are formatted or displayed.
An annotation can be applied directly to a constant in the Decompiler Window, which always affects
decompiler output. Or, an annotation can be applied to the constant operand of a specific machine
instruction displayed in the Listing Window. In this case, to the extent possible, the decompiler
attempts to track the operand and apply the annotation to the matching constant in the decompiler output.
However, the constant may be transformed from its value in the original machine instruction during the decompiler's
analysis. The decompiler will follow the constant through simple transformations, but if the constant strays
too far from its original value, the annotation will not be applied. The transforms followed are:
<informalexample>
<itemizedlist mark='bullet'>
<listitem>Signed or zero extension</listitem>
@ -2475,39 +2477,53 @@
<title>Equates</title>
<para>
Ghidra can create an association between a name and a constant, called an <emphasis role="bold">equate</emphasis>.
The constant must be a constant operand of a specific machine instruction, and the equate
is applied directly to the operand from the Listing Window using the
<link xlink:href="help/topics/EquatePlugin/Equates.htm#Set_Equate">Set Equate</link> menu.
Once applied, the equate's name is displayed instead of the numeric representation of the constant.
Equates across the entire Program can be viewed from the
An equate is a descriptive string that is intended to replace the numeric form of the constant, and equates
across the entire Program can be viewed from the
<link xlink:href="help/topics/EquatePlugin/Equates.htm#Equate_Table">Equate Table</link>.
</para>
<para>
When analyzing a function, the decompiler attempts to follow any constant in the function with an attached
equate to the matching constant in the final output. If successful, the equate's name is printed instead of
the numeric form of the matching constant. If the constant was transformed from its original value, the
matching constant is printed as an expression, where the transforming operations are applied to the equate
symbol (representing the original constant).
An equate can be applied to a machine instruction with a constant
operand by using the <link xlink:href="help/topics/EquatePlugin/Equates.htm#Set_Equate">Set Equate</link>
menu from the Listing Window. If the decompiler successfully follows the operand to a matching constant,
the equate's name is displayed as part of the decompiler's output as well as in the Listing Window.
A transformed operand is displayed as an expression, where the transforming operations are applied to
the equate symbol (representing the original constant).
</para>
<para>
Alternately an equate can be applied directly to a constant from the Decompiler Window using its
<xref linkend="ActionSetEquate"/> menu. The constant may or may not have a corresponding instruction
operand but will be displayed in decompiler output using the descriptive string.
</para>
<para>
</para>
</sect2>
<sect2 id="ConstantConversions">
<title>Format Conversions</title>
<para>
Ghidra can apply a <emphasis role="bold">format conversion</emphasis> to override how an integer operand
is displayed in a specific machine instruction. Conversions are generally applied from the Listing Window using the
<link xlink:href="help/topics/EquatePlugin/Equates.htm#Convert">Convert</link> menu option. When analyzing a
function containing a machine instruction that has a format conversion applied, the decompiler will attempt
to trace the constant to a matching constant in the final output. If successful, the format conversion is also
applied to the matching constant.
Ghidra can apply a <emphasis role="bold">format conversion</emphasis> to integer constants that are displayed
in decompiler output.
</para>
<para>
A conversion can be applied to the machine instruction containing the constant
as an operand using the <link xlink:href="help/topics/EquatePlugin/Equates.htm#Convert">Convert</link> menu option
from the Listing Window. If the decompiler successfully traces the operand to a matching constant,
the format conversion is applied in the decompiler output as well as in the Listing Window.
</para>
<para>
Alternately, a conversion can be applied directly to an integer constant in the
Decompiler Window using its <xref linkend="ActionConvert"/> menu option. The constant may or may not
have a corresponding instruction operand but is displayed in decompiler output using the conversion.
</para>
<para>
Conversions applied by the decompiler are currently limited to:
<informalexample>
<itemizedlist mark='bullet'>
<listitem>Binary - 0b10100111</listitem>
<listitem>Decimal- 167</listitem>
<listitem>Hexadecimal - 0xa7</listitem>
<listitem>Octal - 0247</listitem>
<listitem>Binary - 0b01100001</listitem>
<listitem>Decimal- 97</listitem>
<listitem>Hexadecimal - 0x61</listitem>
<listitem>Octal - 0141</listitem>
<listitem>Char - 'a'</listitem>
</itemizedlist>
</informalexample>
An appropriate header matching the format is prepended to the representation string, either "0b", "0x" or just
@ -4020,6 +4036,63 @@
</para>
</sect2>
<sect2 id="ActionConvert">
<title>Convert</title>
<para>
Change the displayed encoding for the integer or character constant under the cursor.
</para>
<para>
Various encodings are possible.
<informalexample>
<table xml:id="convert.htmltable" width="50%" frame="none" rules="none">
<col width="30%"/>
<col width="70%"/>
<tbody>
<tr>
<td><emphasis role="bold">Binary:</emphasis></td>
<td>0b01100001</td>
</tr>
<tr>
<td><emphasis role="bold">Decimal:</emphasis></td>
<td>97</td>
</tr>
<tr>
<td><emphasis role="bold">Hexadecimal:</emphasis></td>
<td>0x61</td>
</tr>
<tr>
<td><emphasis role="bold">Octal:</emphasis></td>
<td>0141</td>
</tr>
<tr>
<td><emphasis role="bold">Char:</emphasis></td>
<td>'a'</td>
</tr>
</tbody>
</table>
</informalexample>
</para>
<para>
This command primarily targets a constant token in the Decompiler window, but
if there is a <emphasis>scalar</emphasis> operand in an instruction that corresponds
with the selected constant, the same conversion is also applied to the scalar in the Listing
window. This is equivalent to selecting the
<link xlink:href="help/topics/EquatePlugin/Equates.htm#Convert">Convert</link> command from the
Listing. There may not be a scalar operand directly corresponding to the selected constant, in
which case the conversion will be applied only in the Decompiler window.
</para>
<para>
The data-type of the selected token is not changed by the action, and it retains its
meaning in the decompiler output. This slightly limits the possible conversions as signed
values cannot be forced into an unsigned representation, and vice versa.
</para>
<para>
The constant's encoding can be changed by selecting a different <emphasis role="bold">Convert</emphasis>
command, or it can be returned to its <emphasis>default</emphasis> encoding by selecting
the <xref linkend="ActionRemoveEquate"/> command.
</para>
</sect2>
<sect2 id="ActionCopy">
<title>Copy/Copy Special ...</title>
<para>
@ -4322,6 +4395,21 @@
</sect3>
</sect2>
<sect2 id="ActionRemoveEquate">
<title>Remove Convert/Equate</title>
<para>
Remove the display <emphasis>conversion</emphasis> or <emphasis>equate</emphasis> from
the constant under the cursor.
</para>
<para>
The selected constant must have had either a <xref linkend="ActionConvert"/> or a
<xref linkend="ActionSetEquate"/> command applied to it. After applying this command,
the conversion is no longer applied, and the selected constant will be displayed
using the decompiler's default strategy, which depends on the data-type of the constant and
other display settings (See <xref linkend="DisplayIntegerFormat"/>).
</para>
</sect2>
<sect2 id="ActionRemoveOverride">
<title>Remove Signature Override</title>
<para>
@ -4563,6 +4651,38 @@
</para>
</sect2>
<sect2 id="ActionSetEquate">
<title>Set Equate ...</title>
<para>
Change the display of the integer or character constant under the cursor to an <emphasis>equate</emphasis>
string.
</para>
<para>
An equate is a descriptive string associated with a specific constant value, which is managed globally for
a Program by the Equate plug-in (See <link xlink:href="help/topics/EquatePlugin/Equates.htm">Equates</link>).
</para>
<para>
The <emphasis role="bold">Set Equate</emphasis> command, brings up a dialog listing other equates
that have previously been associated with the selected constant. The user can choose an equate from this list
or define a new equate for the constant by typing in the text field at the top of the dialog. Alternately,
the text field can be used as a filter to narrow down possibilities prior to selecting from the list. Clicking
OK, completes the action, and the selected equate is substituted for its constant.
</para>
<para>
This command primarily targets a constant token in the Decompiler window, but
if there is a <emphasis>scalar</emphasis> operand in an instruction that corresponds
with the selected constant, the same equate is also applied to the scalar in the Listing
window. This is equivalent to selecting the
<link xlink:href="help/topics/EquatePlugin/Equates.htm#Set_Equate">Set Equate</link> command from the
Listing. There may not be a scalar operand directly corresponding to the selected constant, in
which case the equate will be applied only in the Decompiler window.
</para>
<para>
Once an equate is applied, the constant can be returned to its <emphasis>default</emphasis> display
by selecting the <xref linkend="ActionRemoveEquate"/> command.
</para>
</sect2>
<sect2 id="ActionIsolate">
<title>Split Out As New Variable</title>
<para>

View file

@ -1214,12 +1214,14 @@
<a name="AnnoteConstants"></a>Constant Annotations</h2></div></div></div>
<p>
Ghidra provides some ways to control how specific constants shown in disassembly are formatted or displayed.
These annotations are attached to constants as operands of specific machine instructions. To the extent
possible, the decompiler applies these annotations to the matching constant in the decompiler output.
The constant may be transformed from its value in the original machine instruction during the decompiler's
analysis. The decompiler will follow the constant through simple transformations, but if the transformed
is too far from the original value, the annotation will not be applied. The transforms followed are:
Ghidra provides numerous actions to control how specific constants are formatted or displayed.
An annotation can be applied directly to a constant in the Decompiler Window, which always affects
decompiler output. Or, an annotation can be applied to the constant operand of a specific machine
instruction displayed in the Listing Window. In this case, to the extent possible, the decompiler
attempts to track the operand and apply the annotation to the matching constant in the decompiler output.
However, the constant may be transformed from its value in the original machine instruction during the decompiler's
analysis. The decompiler will follow the constant through simple transformations, but if the constant strays
too far from its original value, the annotation will not be applied. The transforms followed are:
</p>
<div class="informalexample">
<div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: bullet; ">
@ -1237,19 +1239,25 @@
<p>
Ghidra can create an association between a name and a constant, called an <span class="bold"><strong>equate</strong></span>.
The constant must be a constant operand of a specific machine instruction, and the equate
is applied directly to the operand from the Listing Window using the
<a class="ulink" href="help/topics/EquatePlugin/Equates.htm#Set_Equate" target="_top">Set Equate</a> menu.
Once applied, the equate's name is displayed instead of the numeric representation of the constant.
Equates across the entire Program can be viewed from the
An equate is a descriptive string that is intended to replace the numeric form of the constant, and equates
across the entire Program can be viewed from the
<a class="ulink" href="help/topics/EquatePlugin/Equates.htm#Equate_Table" target="_top">Equate Table</a>.
</p>
<p>
When analyzing a function, the decompiler attempts to follow any constant in the function with an attached
equate to the matching constant in the final output. If successful, the equate's name is printed instead of
the numeric form of the matching constant. If the constant was transformed from its original value, the
matching constant is printed as an expression, where the transforming operations are applied to the equate
symbol (representing the original constant).
An equate can be applied to a machine instruction with a constant
operand by using the <a class="ulink" href="help/topics/EquatePlugin/Equates.htm#Set_Equate" target="_top">Set Equate</a>
menu from the Listing Window. If the decompiler successfully follows the operand to a matching constant,
the equate's name is displayed as part of the decompiler's output as well as in the Listing Window.
A transformed operand is displayed as an expression, where the transforming operations are applied to
the equate symbol (representing the original constant).
</p>
<p>
Alternately an equate can be applied directly to a constant from the Decompiler Window using its
<a class="xref" href="DecompilerWindow.html#ActionSetEquate" title="Set Equate ...">&#8220;Set Equate ...&#8221;</a> menu. The constant may or may not have a corresponding instruction
operand but will be displayed in decompiler output using the descriptive string.
</p>
<p>
</p>
</div>
<div class="sect2">
@ -1257,22 +1265,30 @@
<a name="ConstantConversions"></a>Format Conversions</h3></div></div></div>
<p>
Ghidra can apply a <span class="bold"><strong>format conversion</strong></span> to override how an integer operand
is displayed in a specific machine instruction. Conversions are generally applied from the Listing Window using the
<a class="ulink" href="help/topics/EquatePlugin/Equates.htm#Convert" target="_top">Convert</a> menu option. When analyzing a
function containing a machine instruction that has a format conversion applied, the decompiler will attempt
to trace the constant to a matching constant in the final output. If successful, the format conversion is also
applied to the matching constant.
Ghidra can apply a <span class="bold"><strong>format conversion</strong></span> to integer constants that are displayed
in decompiler output.
</p>
<p>
A conversion can be applied to the machine instruction containing the constant
as an operand using the <a class="ulink" href="help/topics/EquatePlugin/Equates.htm#Convert" target="_top">Convert</a> menu option
from the Listing Window. If the decompiler successfully traces the operand to a matching constant,
the format conversion is applied in the decompiler output as well as in the Listing Window.
</p>
<p>
Alternately, a conversion can be applied directly to an integer constant in the
Decompiler Window using its <a class="xref" href="DecompilerWindow.html#ActionConvert" title="Convert">&#8220;Convert&#8221;</a> menu option. The constant may or may not
have a corresponding instruction operand but is displayed in decompiler output using the conversion.
</p>
<p>
Conversions applied by the decompiler are currently limited to:
</p>
<div class="informalexample">
<div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: bullet; ">
<li class="listitem" style="list-style-type: disc">Binary - 0b10100111</li>
<li class="listitem" style="list-style-type: disc">Decimal- 167</li>
<li class="listitem" style="list-style-type: disc">Hexadecimal - 0xa7</li>
<li class="listitem" style="list-style-type: disc">Octal - 0247</li>
<li class="listitem" style="list-style-type: disc">Binary - 0b01100001</li>
<li class="listitem" style="list-style-type: disc">Decimal- 97</li>
<li class="listitem" style="list-style-type: disc">Hexadecimal - 0x61</li>
<li class="listitem" style="list-style-type: disc">Octal - 0141</li>
<li class="listitem" style="list-style-type: disc">Char - 'a'</li>
</ul></div>
</div>
<p>

View file

@ -620,6 +620,69 @@
</p>
</div>
<div class="sect2">
<div class="titlepage"><div><div><h3 class="title">
<a name="ActionConvert"></a>Convert</h3></div></div></div>
<p>
Change the displayed encoding for the integer or character constant under the cursor.
</p>
<p>
Various encodings are possible.
</p>
<div class="informalexample">
<div class="table">
<a name="convert.htmltable"></a><table width="50%" frame="none" rules="none" id="convert.htmltable">
<col width="30%">
<col width="70%">
<tbody>
<tr>
<td><span class="bold"><strong>Binary:</strong></span></td>
<td>0b01100001</td>
</tr>
<tr>
<td><span class="bold"><strong>Decimal:</strong></span></td>
<td>97</td>
</tr>
<tr>
<td><span class="bold"><strong>Hexadecimal:</strong></span></td>
<td>0x61</td>
</tr>
<tr>
<td><span class="bold"><strong>Octal:</strong></span></td>
<td>0141</td>
</tr>
<tr>
<td><span class="bold"><strong>Char:</strong></span></td>
<td>'a'</td>
</tr>
</tbody>
</table>
</div>
</div>
<p>
</p>
<p>
This command primarily targets a constant token in the Decompiler window, but
if there is a <span class="emphasis"><em>scalar</em></span> operand in an instruction that corresponds
with the selected constant, the same conversion is also applied to the scalar in the Listing
window. This is equivalent to selecting the
<a class="ulink" href="help/topics/EquatePlugin/Equates.htm#Convert" target="_top">Convert</a> command from the
Listing. There may not be a scalar operand directly corresponding to the selected constant, in
which case the conversion will be applied only in the Decompiler window.
</p>
<p>
The data-type of the selected token is not changed by the action, and it retains its
meaning in the decompiler output. This slightly limits the possible conversions as signed
values cannot be forced into an unsigned representation, and vice versa.
</p>
<p>
The constant's encoding can be changed by selecting a different <span class="bold"><strong>Convert</strong></span>
command, or it can be returned to its <span class="emphasis"><em>default</em></span> encoding by selecting
the <a class="xref" href="DecompilerWindow.html#ActionRemoveEquate" title="Remove Convert/Equate">&#8220;Remove Convert/Equate&#8221;</a> command.
</p>
</div>
<div class="sect2">
<div class="titlepage"><div><div><h3 class="title">
<a name="ActionCopy"></a>Copy/Copy Special ...</h3></div></div></div>
@ -921,6 +984,23 @@
</div>
</div>
<div class="sect2">
<div class="titlepage"><div><div><h3 class="title">
<a name="ActionRemoveEquate"></a>Remove Convert/Equate</h3></div></div></div>
<p>
Remove the display <span class="emphasis"><em>conversion</em></span> or <span class="emphasis"><em>equate</em></span> from
the constant under the cursor.
</p>
<p>
The selected constant must have had either a <a class="xref" href="DecompilerWindow.html#ActionConvert" title="Convert">&#8220;Convert&#8221;</a> or a
<a class="xref" href="DecompilerWindow.html#ActionSetEquate" title="Set Equate ...">&#8220;Set Equate ...&#8221;</a> command applied to it. After applying this command,
the conversion is no longer applied, and the selected constant will be displayed
using the decompiler's default strategy, which depends on the data-type of the constant and
other display settings (See <a class="xref" href="DecompilerOptions.html#DisplayIntegerFormat"><span class="bold"><strong>Integer format</strong></span></a>).
</p>
</div>
<div class="sect2">
<div class="titlepage"><div><div><h3 class="title">
<a name="ActionRemoveOverride"></a>Remove Signature Override</h3></div></div></div>
@ -1180,6 +1260,40 @@
</p>
</div>
<div class="sect2">
<div class="titlepage"><div><div><h3 class="title">
<a name="ActionSetEquate"></a>Set Equate ...</h3></div></div></div>
<p>
Change the display of the integer or character constant under the cursor to an <span class="emphasis"><em>equate</em></span>
string.
</p>
<p>
An equate is a descriptive string associated with a specific constant value, which is managed globally for
a Program by the Equate plug-in (See <a class="ulink" href="help/topics/EquatePlugin/Equates.htm" target="_top">Equates</a>).
</p>
<p>
The <span class="bold"><strong>Set Equate</strong></span> command, brings up a dialog listing other equates
that have previously been associated with the selected constant. The user can choose an equate from this list
or define a new equate for the constant by typing in the text field at the top of the dialog. Alternately,
the text field can be used as a filter to narrow down possibilities prior to selecting from the list. Clicking
OK, completes the action, and the selected equate is substituted for its constant.
</p>
<p>
This command primarily targets a constant token in the Decompiler window, but
if there is a <span class="emphasis"><em>scalar</em></span> operand in an instruction that corresponds
with the selected constant, the same equate is also applied to the scalar in the Listing
window. This is equivalent to selecting the
<a class="ulink" href="help/topics/EquatePlugin/Equates.htm#Set_Equate" target="_top">Set Equate</a> command from the
Listing. There may not be a scalar operand directly corresponding to the selected constant, in
which case the equate will be applied only in the Decompiler window.
</p>
<p>
Once an equate is applied, the constant can be returned to its <span class="emphasis"><em>default</em></span> display
by selecting the <a class="xref" href="DecompilerWindow.html#ActionRemoveEquate" title="Remove Convert/Equate">&#8220;Remove Convert/Equate&#8221;</a> command.
</p>
</div>
<div class="sect2">
<div class="titlepage"><div><div><h3 class="title">
<a name="ActionIsolate"></a>Split Out As New Variable</h3></div></div></div>

View file

@ -63,43 +63,7 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
static final ImageIcon C_SOURCE_ICON =
ResourceManager.loadImage("images/decompileFunction.gif");
private DockingAction defUseHighlightAction;
private DockingAction forwardSliceAction;
private DockingAction forwardSliceToOpsAction;
private DockingAction backwardSliceAction;
private DockingAction backwardSliceToOpsAction;
private DockingAction lockProtoAction;
private DockingAction lockLocalAction;
private DockingAction renameLocalAction;
private DockingAction renameGlobalAction;
private DockingAction renameFieldAction;
private DockingAction retypeLocalAction;
private DockingAction retypeGlobalAction;
private DockingAction retypeReturnAction;
private DockingAction retypeFieldAction;
private DockingAction isolateVarAction;
private DockingAction specifyCProtoAction;
private DockingAction overrideSigAction;
private DockingAction deleteSigAction;
private DockingAction debugFunctionAction;
private DockingAction convertAction;
private DockingAction graphASTControlFlowAction;
private DockingAction findAction;
private DockingAction propertiesAction;
private DockingAction editDataTypeAction;
private DockingAction decompilerCreateStructureAction;
private DockingAction listingCreateStructureAction;
private DockingAction renameFunctionAction;
private DockingAction findReferencesAction;
private CloneDecompilerAction cloneDecompilerAction;
private SelectAllAction selectAllAction;
private SetSecondaryHighlightAction setSecondaryHighlightAction;
private SetSecondaryHighlightColorChooserAction setSecondaryHighlightColorChooserAction;
private RemoveAllSecondaryHighlightsAction removeAllSecondadryHighlightsAction;
private RemoveSecondaryHighlightAction removeSecondaryHighlightAction;
private final DecompilePlugin plugin;
private ClipboardService clipboardService;
@ -753,7 +717,8 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
private void createActions(boolean isConnected) {
String owner = plugin.getName();
selectAllAction = new SelectAllAction(owner, controller.getDecompilerPanel());
SelectAllAction selectAllAction =
new SelectAllAction(owner, controller.getDecompilerPanel());
DockingAction refreshAction = new DockingAction("Refresh", owner) {
@Override
@ -796,16 +761,16 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
String functionGroup = "1 - Function Group";
int subGroupPosition = 0;
specifyCProtoAction = new SpecifyCPrototypeAction();
SpecifyCPrototypeAction specifyCProtoAction = new SpecifyCPrototypeAction();
setGroupInfo(specifyCProtoAction, functionGroup, subGroupPosition++);
overrideSigAction = new OverridePrototypeAction();
OverridePrototypeAction overrideSigAction = new OverridePrototypeAction();
setGroupInfo(overrideSigAction, functionGroup, subGroupPosition++);
deleteSigAction = new DeletePrototypeOverrideAction();
DeletePrototypeOverrideAction deleteSigAction = new DeletePrototypeOverrideAction();
setGroupInfo(deleteSigAction, functionGroup, subGroupPosition++);
renameFunctionAction = new RenameFunctionAction();
RenameFunctionAction renameFunctionAction = new RenameFunctionAction();
setGroupInfo(renameFunctionAction, functionGroup, subGroupPosition++);
//
@ -814,41 +779,42 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
String variableGroup = "2 - Variable Group";
subGroupPosition = 0; // reset for the next group
renameLocalAction = new RenameLocalAction();
RenameLocalAction renameLocalAction = new RenameLocalAction();
setGroupInfo(renameLocalAction, variableGroup, subGroupPosition++);
renameGlobalAction = new RenameGlobalAction();
RenameGlobalAction renameGlobalAction = new RenameGlobalAction();
setGroupInfo(renameGlobalAction, variableGroup, subGroupPosition++);
renameFieldAction = new RenameFieldAction();
RenameFieldAction renameFieldAction = new RenameFieldAction();
setGroupInfo(renameFieldAction, variableGroup, subGroupPosition++);
retypeLocalAction = new RetypeLocalAction();
RetypeLocalAction retypeLocalAction = new RetypeLocalAction();
setGroupInfo(retypeLocalAction, variableGroup, subGroupPosition++);
retypeGlobalAction = new RetypeGlobalAction();
RetypeGlobalAction retypeGlobalAction = new RetypeGlobalAction();
setGroupInfo(retypeGlobalAction, variableGroup, subGroupPosition++);
retypeReturnAction = new RetypeReturnAction();
RetypeReturnAction retypeReturnAction = new RetypeReturnAction();
setGroupInfo(retypeReturnAction, variableGroup, subGroupPosition++);
retypeFieldAction = new RetypeFieldAction();
RetypeFieldAction retypeFieldAction = new RetypeFieldAction();
setGroupInfo(retypeFieldAction, variableGroup, subGroupPosition++);
isolateVarAction = new IsolateVariableAction();
IsolateVariableAction isolateVarAction = new IsolateVariableAction();
setGroupInfo(isolateVarAction, variableGroup, subGroupPosition++);
decompilerCreateStructureAction =
DecompilerStructureVariableAction decompilerCreateStructureAction =
new DecompilerStructureVariableAction(owner, tool, controller);
setGroupInfo(decompilerCreateStructureAction, variableGroup, subGroupPosition++);
editDataTypeAction = new EditDataTypeAction();
EditDataTypeAction editDataTypeAction = new EditDataTypeAction();
setGroupInfo(editDataTypeAction, variableGroup, subGroupPosition++);
//
// Listing action for Creating Structure on a Variable
//
listingCreateStructureAction = new ListingStructureVariableAction(owner, tool, controller);
ListingStructureVariableAction listingCreateStructureAction =
new ListingStructureVariableAction(owner, tool, controller);
//
// Commit
@ -856,10 +822,10 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
String commitGroup = "3 - Commit Group";
subGroupPosition = 0; // reset for the next group
lockProtoAction = new CommitParamsAction();
CommitParamsAction lockProtoAction = new CommitParamsAction();
setGroupInfo(lockProtoAction, commitGroup, subGroupPosition++);
lockLocalAction = new CommitLocalsAction();
CommitLocalsAction lockLocalAction = new CommitLocalsAction();
setGroupInfo(lockLocalAction, commitGroup, subGroupPosition++);
subGroupPosition = 0; // reset for the next group
@ -869,34 +835,61 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
//
String highlightGroup = "4a - Highlight Group";
tool.setMenuGroup(new String[] { "Highlight" }, highlightGroup);
defUseHighlightAction = new HighlightDefinedUseAction();
HighlightDefinedUseAction defUseHighlightAction = new HighlightDefinedUseAction();
setGroupInfo(defUseHighlightAction, highlightGroup, subGroupPosition++);
forwardSliceAction = new ForwardSliceAction();
ForwardSliceAction forwardSliceAction = new ForwardSliceAction();
setGroupInfo(forwardSliceAction, highlightGroup, subGroupPosition++);
backwardSliceAction = new BackwardsSliceAction();
BackwardsSliceAction backwardSliceAction = new BackwardsSliceAction();
setGroupInfo(backwardSliceAction, highlightGroup, subGroupPosition++);
forwardSliceToOpsAction = new ForwardSliceToPCodeOpsAction();
ForwardSliceToPCodeOpsAction forwardSliceToOpsAction = new ForwardSliceToPCodeOpsAction();
setGroupInfo(forwardSliceToOpsAction, highlightGroup, subGroupPosition++);
backwardSliceToOpsAction = new BackwardsSliceToPCodeOpsAction();
BackwardsSliceToPCodeOpsAction backwardSliceToOpsAction =
new BackwardsSliceToPCodeOpsAction();
setGroupInfo(backwardSliceToOpsAction, highlightGroup, subGroupPosition++);
tool.setMenuGroup(new String[] { "Secondary Highlight" }, highlightGroup);
setSecondaryHighlightAction = new SetSecondaryHighlightAction();
SetSecondaryHighlightAction setSecondaryHighlightAction = new SetSecondaryHighlightAction();
setGroupInfo(setSecondaryHighlightAction, highlightGroup, subGroupPosition++);
setSecondaryHighlightColorChooserAction = new SetSecondaryHighlightColorChooserAction();
SetSecondaryHighlightColorChooserAction setSecondaryHighlightColorChooserAction =
new SetSecondaryHighlightColorChooserAction();
setGroupInfo(setSecondaryHighlightColorChooserAction, highlightGroup, subGroupPosition++);
removeSecondaryHighlightAction = new RemoveSecondaryHighlightAction();
RemoveSecondaryHighlightAction removeSecondaryHighlightAction =
new RemoveSecondaryHighlightAction();
setGroupInfo(removeSecondaryHighlightAction, highlightGroup, subGroupPosition++);
removeAllSecondadryHighlightsAction = new RemoveAllSecondaryHighlightsAction();
RemoveAllSecondaryHighlightsAction removeAllSecondadryHighlightsAction =
new RemoveAllSecondaryHighlightsAction();
setGroupInfo(removeAllSecondadryHighlightsAction, highlightGroup, subGroupPosition++);
String convertGroup = "7 - Convert Group";
subGroupPosition = 0;
RemoveEquateAction removeEquateAction = new RemoveEquateAction();
setGroupInfo(removeEquateAction, convertGroup, subGroupPosition++);
SetEquateAction setEquateAction = new SetEquateAction(plugin);
setGroupInfo(setEquateAction, convertGroup, subGroupPosition++);
ConvertBinaryAction convertBinaryAction = new ConvertBinaryAction(plugin);
setGroupInfo(convertBinaryAction, convertGroup, subGroupPosition++);
ConvertDecAction convertDecAction = new ConvertDecAction(plugin);
setGroupInfo(convertDecAction, convertGroup, subGroupPosition++);
ConvertHexAction convertHexAction = new ConvertHexAction(plugin);
setGroupInfo(convertHexAction, convertGroup, subGroupPosition++);
ConvertOctAction convertOctAction = new ConvertOctAction(plugin);
setGroupInfo(convertOctAction, convertGroup, subGroupPosition++);
ConvertCharAction convertCharAction = new ConvertCharAction(plugin);
setGroupInfo(convertCharAction, convertGroup, subGroupPosition++);
//
// Comments
//
@ -910,7 +903,7 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
String searchGroup = "comment2 - Search Group";
subGroupPosition = 0; // reset for the next group
findAction = new FindAction();
FindAction findAction = new FindAction();
setGroupInfo(findAction, searchGroup, subGroupPosition++);
//
@ -920,7 +913,8 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
// note: set the menu group so that the 'References' group is with the 'Find' action
String referencesParentGroup = searchGroup;
findReferencesAction = new FindReferencesToDataTypeAction(owner, tool, controller);
FindReferencesToDataTypeAction findReferencesAction =
new FindReferencesToDataTypeAction(owner, tool, controller);
setGroupInfo(findReferencesAction, searchGroup, subGroupPosition++);
findReferencesAction.getPopupMenuData().setParentMenuGroup(referencesParentGroup);
@ -942,15 +936,15 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
String optionsGroup = "comment6 - Options Group";
subGroupPosition = 0; // reset for the next group
propertiesAction = new EditPropertiesAction(owner, tool);
EditPropertiesAction propertiesAction = new EditPropertiesAction(owner, tool);
setGroupInfo(propertiesAction, optionsGroup, subGroupPosition++);
//
// These actions are not in the popup menu
//
debugFunctionAction = new DebugDecompilerAction(controller);
convertAction = new ExportToCAction();
cloneDecompilerAction = new CloneDecompilerAction();
DebugDecompilerAction debugFunctionAction = new DebugDecompilerAction(controller);
ExportToCAction convertAction = new ExportToCAction();
CloneDecompilerAction cloneDecompilerAction = new CloneDecompilerAction();
addLocalAction(refreshAction);
addLocalAction(selectAllAction);
@ -968,6 +962,13 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
addLocalAction(setSecondaryHighlightColorChooserAction);
addLocalAction(removeSecondaryHighlightAction);
addLocalAction(removeAllSecondadryHighlightsAction);
addLocalAction(convertBinaryAction);
addLocalAction(convertDecAction);
addLocalAction(convertHexAction);
addLocalAction(convertOctAction);
addLocalAction(convertCharAction);
addLocalAction(setEquateAction);
addLocalAction(removeEquateAction);
addLocalAction(retypeLocalAction);
addLocalAction(retypeGlobalAction);
addLocalAction(retypeReturnAction);

View file

@ -0,0 +1,82 @@
/* ###
* 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 docking.action.MenuData;
import ghidra.app.plugin.core.decompile.DecompilePlugin;
import ghidra.app.util.HelpTopics;
import ghidra.program.model.listing.Program;
import ghidra.program.model.pcode.EquateSymbol;
import ghidra.program.model.scalar.Scalar;
import ghidra.util.HelpLocation;
import ghidra.util.StringUtilities;
/**
* Convert a selected constant in the decompiler to a binary representation.
*/
public class ConvertBinaryAction extends ConvertConstantAction {
public ConvertBinaryAction(DecompilePlugin plugin) {
super(plugin, "Convert To Binary", EquateSymbol.FORMAT_BIN);
setHelpLocation(new HelpLocation(HelpTopics.DECOMPILER, "ActionConvert"));
setPopupMenuData(new MenuData(new String[] { "Binary" }, "Decompile"));
}
@Override
public String getMenuPrefix() {
return "Binary: ";
}
@Override
public String getMenuDisplay(long value, int size, boolean isSigned) {
Scalar scalar = new Scalar(size * 8, value);
long v;
String prefix = "0b";
if (isSigned) {
v = scalar.getSignedValue();
if (v < 0) {
v = -v;
prefix = "-0b";
}
}
else {
v = scalar.getUnsignedValue();
}
String bitString = Long.toBinaryString(v);
int bitlen = bitString.length();
if (bitlen <= 8) {
bitlen = 8;
}
else if (bitlen <= 16) {
bitlen = 16;
}
else if (bitlen <= 32) {
bitlen = 32;
}
else {
bitlen = 64;
}
return prefix + StringUtilities.pad(bitString, '0', bitlen);
}
@Override
public String getEquateName(long value, int size, boolean isSigned, Program program) {
String valueStr = Long.toBinaryString(value);
valueStr = StringUtilities.pad(valueStr, '0', size * 8);
return valueStr + "b";
}
}

View file

@ -0,0 +1,152 @@
/* ###
* 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 docking.action.MenuData;
import ghidra.app.plugin.core.decompile.DecompilePlugin;
import ghidra.app.util.HelpTopics;
import ghidra.program.model.data.ByteDataType;
import ghidra.program.model.data.StringDataInstance;
import ghidra.program.model.listing.Program;
import ghidra.program.model.pcode.EquateSymbol;
import ghidra.util.BigEndianDataConverter;
import ghidra.util.HelpLocation;
/**
* Convert a selected constant in the decompiler window to a character representation.
*/
public class ConvertCharAction extends ConvertConstantAction {
public ConvertCharAction(DecompilePlugin plugin) {
super(plugin, "Convert To Char", EquateSymbol.FORMAT_CHAR);
setHelpLocation(new HelpLocation(HelpTopics.DECOMPILER, "ActionConvert"));
setPopupMenuData(new MenuData(new String[] { "Char" }, "Decompile"));
}
@Override
public String getMenuPrefix() {
return "Char: ";
}
private static void generateHexEscape(StringBuilder buffer, int value) {
String res = Integer.toHexString(value);
int pad = res.length();
if (pad > 4) {
pad = 8;
}
else if (pad > 2) {
pad = 4;
}
else {
pad = 2;
}
pad = pad - res.length();
buffer.append("'\\x");
for (int i = 0; i < pad; ++i) {
buffer.append('0');
}
buffer.append(res.toLowerCase());
buffer.append('\'');
}
@Override
public String getEquateName(long value, int size, boolean isSigned, Program program) {
byte[] bytes = new byte[size];
BigEndianDataConverter.INSTANCE.putValue(value, size, bytes, 0);
return StringDataInstance.getCharRepresentation(ByteDataType.dataType, bytes, null);
}
/**
* Return true for any unicode codepoint that needs to be represented with an escape sequence
* @param codepoint is the code point value
* @return true if the codepoint needs to be escaped
*/
private static boolean codePointNeedsEscape(int codepoint) {
int characterClass = Character.getType(codepoint);
switch (characterClass) {
case Character.SPACE_SEPARATOR:
if (codepoint == 0x20) {
return false; // Only the ASCII space is not escaped
}
return true;
case Character.COMBINING_SPACING_MARK:
case Character.CONTROL:
case Character.ENCLOSING_MARK:
case Character.FORMAT:
case Character.LINE_SEPARATOR:
case Character.NON_SPACING_MARK:
case Character.PARAGRAPH_SEPARATOR:
case Character.PRIVATE_USE:
case Character.SURROGATE:
case Character.UNASSIGNED:
return true;
}
return false;
}
@Override
public String getMenuDisplay(long value, int size, boolean isSigned) {
StringBuilder buffer = new StringBuilder();
if (size > 1) {
buffer.append('L');
}
if ((size == 1 && value >= 0x7f) || codePointNeedsEscape((int) value)) {
switch ((int) value) {
case 0:
buffer.append("'\\0'");
break;
case 7:
buffer.append("'\\a'");
break;
case 8:
buffer.append("'\\b'");
break;
case 9:
buffer.append("'\\t'");
break;
case 10:
buffer.append("'\\n'");
break;
case 11:
buffer.append("'\\v'");
break;
case 12:
buffer.append("'\\f'");
break;
case 13:
buffer.append("'\\r'");
break;
case '"':
buffer.append("\\\"");
break;
case 92:
buffer.append("\\\\");
break;
case '\'':
buffer.append("\\'");
break;
default:
// Generic unicode escape
generateHexEscape(buffer, (int) value);
break;
}
}
else {
buffer.append('\'').append((char) value).append('\'');
}
return buffer.toString();
}
}

View file

@ -0,0 +1,370 @@
/* ###
* 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.awt.Font;
import java.awt.FontMetrics;
import java.util.List;
import javax.swing.JMenuItem;
import ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.ClangVariableToken;
import ghidra.app.plugin.core.decompile.DecompilePlugin;
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.Enum;
import ghidra.program.model.listing.Instruction;
import ghidra.program.model.listing.Program;
import ghidra.program.model.pcode.*;
import ghidra.program.model.scalar.Scalar;
import ghidra.program.model.symbol.*;
import ghidra.util.Msg;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
/**
* Abstract pop-up menu convert action for the decompiler. If triggered, it lays down
* a new EquateReference that forces the selected constant to be displayed using
* the desired integer format.
*/
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;
protected DecompilePlugin plugin;
private FontMetrics metrics = null;
private int convertType; // The EquateSymbol conversion type performed by the action
/**
* A helper class describing a (matching) scalar operand
*/
private static class ScalarMatch {
Address refAddr; // Address of instruction
Scalar scalar;
int opIndex;
public ScalarMatch(Address addr, Scalar value, int index) {
refAddr = addr;
scalar = value;
opIndex = index;
}
}
public ConvertConstantAction(DecompilePlugin plugin, String name, int convertType) {
super(name);
this.plugin = plugin;
this.convertType = convertType;
}
private int stringWidth(String s) {
if (metrics == null) {
JMenuItem item = new JMenuItem();
Font font = item.getFont();
metrics = plugin.getTool().getActiveWindow().getFontMetrics(font);
}
int w = metrics.stringWidth(s);
if (w == 0) {
// use default computation if metrics report 0
return 10 * s.length();
}
return w;
}
protected String getStandardLengthString(String baseString) {
int baseWidth = stringWidth(baseString);
int spaceWidth = stringWidth(" ");
int paddingSize = (140 - baseWidth) / spaceWidth;
if (paddingSize <= 0) {
return baseString;
}
StringBuilder buf = new StringBuilder(baseString);
for (int i = 0; i < paddingSize; i++) {
buf.append(" ");
}
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
* @param monitor is the TaskMonitor
* @return a description of the scalar match, or null if there is no match
* @throws CancelledException if the user cancels
*/
private ScalarMatch findScalarMatch(Program program, Address startAddress, Varnode constVn,
TaskMonitor monitor) throws CancelledException {
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 = model.getFirstCodeBlockContaining(startAddress, 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()) {
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);
}
ConvertConstantTask task = null;
String equateName = getEquateName(convertVn.getOffset(), convertVn.getSize(),
convertIsSigned, context.getProgram());
if (equateName == null) { // A null is a user cancel
return null;
}
Program program = context.getProgram();
Address convertAddr;
long convertHash;
if (convertSymbol != null) {
convertAddr = convertSymbol.getPCAddress();
convertHash = 0;
int convertIndex = -1;
boolean foundEquate = false;
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;
}
task = new ConvertConstantTask(context, equateName, convertAddr, convertVn, convertHash,
convertIndex);
}
else {
PcodeOp op = convertVn.getLoneDescend();
convertAddr = op.getSeqnum().getTarget();
DynamicHash dynamicHash = new DynamicHash(convertVn, 0);
convertHash = dynamicHash.getHash();
task = new ConvertConstantTask(context, equateName, convertAddr, convertVn, convertHash,
-1);
try {
ScalarMatch scalarMatch = findScalarMatch(context.getProgram(), convertAddr,
convertVn, TaskMonitor.DUMMY);
if (scalarMatch != null) {
long value = scalarMatch.scalar.getUnsignedValue();
int size = scalarMatch.scalar.bitLength() / 8;
if (size == 0) {
size = 1;
}
String altName = getEquateName(value, size, convertIsSigned, null);
if (altName == null) {
altName = equateName;
}
// 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 == convertVn.getOffset()) {
task.setAlternate(altName, scalarMatch.refAddr, scalarMatch.opIndex, value);
}
}
}
catch (CancelledException e) {
// scalar match is not added to task
}
}
return task;
}
@Override
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
ConvertConstantTask task = establishTask(context, false);
if (task == null) {
return false;
}
String convDisplay = getMenuDisplay(task.getValue(), task.getSize(), task.isSigned());
if (convDisplay.equals(context.getTokenAtCursor().getText())) {
return false;
}
String menuString = getStandardLengthString(getMenuPrefix()) + convDisplay;
getPopupMenuData().setMenuItemName(menuString);
return true;
}
@Override
protected void decompilerActionPerformed(DecompilerActionContext context) {
ConvertConstantTask task = establishTask(context, true);
if (task == null) {
return;
}
task.runTask();
}
/**
* The menu option for this kind of action is intended to look like:
* {@literal Hexadecimal: 0x2408}
* This method establishes the first part of this string, up to the colon.
* @return the menu prefix
*/
public abstract String getMenuPrefix();
/**
* The menu option for this kind of action is intended to look like:
* {@literal Hexadecimal: 0x2408}
* This method constructs the final part of this string, after the colon by
* formatting the actual value that is to be converted.
* @param value is the actual value
* @param size is the number of bytes used for the constant Varnode
* @param isSigned is true if the constant represents a signed data-type
* @return the formatted String
*/
public abstract String getMenuDisplay(long value, int size, boolean isSigned);
/**
* Construct the name of the Equate, either absolutely for a conversion or
* by preventing the user with a dialog to select a name.
* @param value is the value 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
* @return the equate name
*/
public abstract String getEquateName(long value, int size, boolean isSigned, Program program);
}

View file

@ -0,0 +1,319 @@
/* ###
* 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 Address convertAddress; // The primary address of the Equate
private String convertName; // The primary name to use in the Equate table
private Varnode convertVn; // The Varnode holding the constant value being equated
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) {
convertVn = vn;
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 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, long hash, int index) {
this.context = context;
program = context.getProgram();
convertName = name;
convertAddress = addr;
convertVn = vn;
convertHash = hash;
convertIndex = index;
}
/**
* 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 convertVn.getOffset();
}
/**
* @return the size of constant (Varnode) being equated
*/
public int getSize() {
return convertVn.getSize();
}
/**
* @return true if the constant value is treated as a signed integer
*/
public boolean isSigned() {
return convertSigned;
}
/**
* Remove any preexisting 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 preexisting 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() != convertVn.getOffset()) {
String msg = "Equate named " + convertName + " already exists with value of " +
equate.getValue() + ".";
throw new DuplicateNameException(msg);
}
if (equate == null) {
equate = equateTable.createEquate(convertName, convertVn.getOffset());
}
// 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().doWheNotBusy(this);
}
else {
applyPrimaryEquate();
}
}
}

View file

@ -0,0 +1,54 @@
/* ###
* 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 docking.action.MenuData;
import ghidra.app.plugin.core.decompile.DecompilePlugin;
import ghidra.app.util.HelpTopics;
import ghidra.program.model.listing.Program;
import ghidra.program.model.pcode.EquateSymbol;
import ghidra.program.model.scalar.Scalar;
import ghidra.util.HelpLocation;
/**
* Convert a selected constant in the decompiler to a decimal representation.
*/
public class ConvertDecAction extends ConvertConstantAction {
public ConvertDecAction(DecompilePlugin plugin) {
super(plugin, "Convert To Decimal", EquateSymbol.FORMAT_DEC);
setHelpLocation(new HelpLocation(HelpTopics.DECOMPILER, "ActionConvert"));
setPopupMenuData(new MenuData(new String[] { "Decimal" }, "Decompile"));
}
@Override
public String getMenuPrefix() {
return "Decimal: ";
}
@Override
public String getMenuDisplay(long value, int size, boolean isSigned) {
return getEquateName(value, size, isSigned, null);
}
@Override
public String getEquateName(long value, int size, boolean isSigned, Program program) {
Scalar scalar = new Scalar(size * 8, value);
if (isSigned) {
return Long.toString(scalar.getSignedValue());
}
return Long.toString(scalar.getUnsignedValue());
}
}

View file

@ -0,0 +1,74 @@
/* ###
* 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 docking.action.MenuData;
import ghidra.app.plugin.core.decompile.DecompilePlugin;
import ghidra.app.util.HelpTopics;
import ghidra.program.model.listing.Program;
import ghidra.program.model.pcode.EquateSymbol;
import ghidra.program.model.scalar.Scalar;
import ghidra.util.HelpLocation;
/**
* Convert a selected constant in the decompiler to a hexadecimal representation.
*/
public class ConvertHexAction extends ConvertConstantAction {
public ConvertHexAction(DecompilePlugin plugin) {
super(plugin, "Convert To Hexadecimal", EquateSymbol.FORMAT_HEX);
setHelpLocation(new HelpLocation(HelpTopics.DECOMPILER, "ActionConvert"));
setPopupMenuData(new MenuData(new String[] { "Hexadecimal" }, "Decompile"));
}
@Override
public String getMenuPrefix() {
return "Hexadecimal: ";
}
@Override
public String getMenuDisplay(long value, int size, boolean isSigned) {
Scalar scalar = new Scalar(size * 8, value);
if (isSigned) {
long v = scalar.getSignedValue();
String valueStr = Long.toString(v, 16);
if (v < 0) {
// use of substring removes '-' prefix for negative value
return "-0x" + valueStr.substring(1);
}
return "0x" + valueStr;
}
String valueStr = Long.toHexString(scalar.getUnsignedValue());
return "0x" + valueStr;
}
@Override
public String getEquateName(long value, int size, boolean isSigned, Program program) {
Scalar scalar = new Scalar(size * 8, value);
if (isSigned) {
long v = scalar.getSignedValue();
String valueStr = Long.toString(v, 16).toUpperCase();
if (v < 0) {
// use of substring removes '-' prefix for negative value
return "-0x" + valueStr.substring(1);
}
return "0x" + valueStr;
}
String valueStr = Long.toHexString(scalar.getUnsignedValue()).toUpperCase();
// Instructions rely on equate which uses 0x prefix (consistent with default scalar formatting)
return "0x" + valueStr;
}
}

View file

@ -0,0 +1,63 @@
/* ###
* 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 docking.action.MenuData;
import ghidra.app.plugin.core.decompile.DecompilePlugin;
import ghidra.app.util.HelpTopics;
import ghidra.program.model.listing.Program;
import ghidra.program.model.pcode.EquateSymbol;
import ghidra.program.model.scalar.Scalar;
import ghidra.util.HelpLocation;
/**
* Convert a selected constant in the decompiler to an octal representation.
*/
public class ConvertOctAction extends ConvertConstantAction {
public ConvertOctAction(DecompilePlugin plugin) {
super(plugin, "Convert To Octal", EquateSymbol.FORMAT_OCT);
setHelpLocation(new HelpLocation(HelpTopics.DECOMPILER, "ActionConvert"));
setPopupMenuData(new MenuData(new String[] { "Octal" }, "Decompile"));
}
@Override
public String getMenuPrefix() {
return "Octal: ";
}
@Override
public String getMenuDisplay(long value, int size, boolean isSigned) {
Scalar scalar = new Scalar(size * 8, value);
if (isSigned) {
long v = scalar.getSignedValue();
String valueStr = Long.toString(v, 8);
if (v < 0) {
// use of substring removes '-' prefix for negative value
return "-0" + valueStr.substring(1);
}
return "0" + valueStr;
}
String valueStr = Long.toOctalString(scalar.getUnsignedValue());
return "0" + valueStr;
}
@Override
public String getEquateName(long value, int size, boolean isSigned, Program program) {
Scalar scalar = new Scalar(size * 8, value);
return Long.toOctalString(scalar.getUnsignedValue()) + "o";
}
}

View file

@ -0,0 +1,104 @@
/* ###
* 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 docking.action.MenuData;
import ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.ClangVariableToken;
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
import ghidra.app.util.HelpTopics;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program;
import ghidra.program.model.pcode.*;
import ghidra.program.model.symbol.Equate;
import ghidra.program.model.symbol.EquateTable;
import ghidra.util.HelpLocation;
public class RemoveEquateAction extends AbstractDecompilerAction {
public RemoveEquateAction() {
super("Remove Equate");
setHelpLocation(new HelpLocation(HelpTopics.DECOMPILER, "ActionRemoveEquate"));
setPopupMenuData(new MenuData(new String[] { "Remove Convert/Equate" }, "Decompile"));
}
@Override
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
ClangToken tokenAtCursor = context.getTokenAtCursor();
if (!(tokenAtCursor instanceof ClangVariableToken)) {
return false;
}
Varnode convertVn = tokenAtCursor.getVarnode();
if (convertVn == null || !convertVn.isConstant()) {
return false;
}
HighSymbol symbol = convertVn.getHigh().getSymbol();
return (symbol instanceof EquateSymbol);
}
private void removeReference(Program program, Equate equate, Address refAddr,
long convertHash) {
int transaction = program.startTransaction("Remove Equate Reference");
boolean commit = false;
try {
if (equate.getReferenceCount() <= 1) {
program.getEquateTable().removeEquate(equate.getName());
}
else {
equate.removeReference(convertHash, refAddr);
}
commit = true;
}
finally {
program.endTransaction(transaction, commit);
}
}
@Override
protected void decompilerActionPerformed(DecompilerActionContext context) {
ClangToken tokenAtCursor = context.getTokenAtCursor();
if (!(tokenAtCursor instanceof ClangVariableToken)) {
return;
}
Varnode convertVn = tokenAtCursor.getVarnode();
if (convertVn == null || !convertVn.isConstant()) {
return;
}
HighSymbol convertSymbol = convertVn.getHigh().getSymbol();
if (convertSymbol instanceof EquateSymbol) {
Address convertAddr = convertSymbol.getPCAddress();
SymbolEntry entry = convertSymbol.getFirstWholeMap();
if (!(entry instanceof DynamicEntry)) {
return;
}
long convertHash = ((DynamicEntry) entry).getHash();
Program program = context.getProgram();
EquateTable equateTable = program.getEquateTable();
List<Equate> equates = equateTable.getEquates(convertAddr);
for (Equate equate : equates) {
if (equate.getValue() != convertVn.getOffset()) {
continue;
}
removeReference(program, equate, convertAddr, convertHash);
break;
}
}
}
}

View file

@ -0,0 +1,68 @@
/* ###
* 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 docking.action.MenuData;
import ghidra.app.plugin.core.decompile.DecompilePlugin;
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
import ghidra.app.util.HelpTopics;
import ghidra.app.util.bean.SetEquateDialog;
import ghidra.program.model.listing.Program;
import ghidra.program.model.pcode.EquateSymbol;
import ghidra.program.model.scalar.Scalar;
import ghidra.util.HelpLocation;
public class SetEquateAction extends ConvertConstantAction {
public SetEquateAction(DecompilePlugin plugin) {
super(plugin, "Set Equate", EquateSymbol.FORMAT_DEFAULT);
setHelpLocation(new HelpLocation(HelpTopics.DECOMPILER, "ActionSetEquate"));
setPopupMenuData(new MenuData(new String[] { "Set Equate..." }, "Decompile"));
}
@Override
public String getMenuPrefix() {
return null; // Menu isn't tailored for this action
}
@Override
public String getMenuDisplay(long value, int size, boolean isSigned) {
return null; // Menu isn't tailored for this action
}
@Override
public String getEquateName(long value, int size, boolean isSigned, Program program) {
if (program == null) {
return null;
}
Scalar scalar = new Scalar(size * 8, value, isSigned);
SetEquateDialog dialog = new SetEquateDialog(plugin.getTool(), program, scalar);
dialog.disableHasSelection();
int res = dialog.showSetDialog();
String name = null;
if (res != SetEquateDialog.CANCELED) {
name = dialog.getEquateName();
}
dialog.dispose();
return name;
}
@Override
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
ConvertConstantTask task = establishTask(context, false);
return (task != null);
}
}

View file

@ -0,0 +1,282 @@
/* ###
* 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;
import static org.junit.Assert.*;
import java.util.List;
import org.junit.Test;
import ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.ClangVariableToken;
import ghidra.app.decompiler.component.ClangTextField;
import ghidra.app.plugin.core.decompile.actions.*;
import ghidra.program.model.listing.*;
import ghidra.program.model.pcode.*;
import ghidra.program.model.symbol.*;
public class EquateTest extends AbstractDecompilerTest {
private static class EquateNameForce extends SetEquateAction {
private String nameForce; // Simulate name chosen by the equate dialog
public EquateNameForce(DecompilePlugin plugin, String nm) {
super(plugin);
nameForce = nm;
}
@Override
public String getEquateName(long value, int size, boolean isSigned, Program program) {
if (program == null) {
return null;
}
return nameForce;
}
}
@Override
protected String getProgramName() {
return "Winmine__XP.exe.gzf";
}
private DecompilerActionContext getContext() {
Function function = getHighFunction().getFunction();
return new DecompilerActionContext(getDecompilerProvider(), function.getEntryPoint(),
false);
}
/**
* Simulate the action of "converting" the current token to the given format
* @param convertType is the given format
*/
private void convertToken(int convertType) {
checkInitialToken();
ConvertConstantAction action;
switch (convertType) {
case EquateSymbol.FORMAT_DEC:
action = new ConvertDecAction(decompiler);
break;
case EquateSymbol.FORMAT_BIN:
action = new ConvertBinaryAction(decompiler);
break;
case EquateSymbol.FORMAT_OCT:
action = new ConvertOctAction(decompiler);
break;
case EquateSymbol.FORMAT_CHAR:
action = new ConvertCharAction(decompiler);
break;
case EquateSymbol.FORMAT_HEX:
action = new ConvertHexAction(decompiler);
break;
default:
action = null;
}
modifyProgram(p -> {
action.actionPerformed(getContext());
});
waitForDecompiler();
}
/**
* Simulate setting an equate on the current token with the given name
* @param nm is the given name
*/
private void convertToken(String nm) {
checkInitialToken();
EquateNameForce action = new EquateNameForce(decompiler, nm);
modifyProgram(p -> {
action.actionPerformed(getContext());
});
waitForDecompiler();
}
private void checkInitialToken() {
ClangToken token = getToken();
assertTrue(token instanceof ClangVariableToken);
HighVariable variable = token.getHighVariable();
assertTrue(variable instanceof HighConstant);
}
private void verifyMatch(String eqName, String text, long address, boolean seeInListing) {
ClangToken token = getToken();
assertTrue(token instanceof ClangVariableToken);
assertEquals(token.getText(), text);
HighSymbol symbol = token.getHighVariable().getSymbol();
assertTrue(symbol instanceof EquateSymbol);
EquateSymbol eqSym = (EquateSymbol) symbol;
SymbolEntry entry = eqSym.getFirstWholeMap();
assertTrue(entry instanceof DynamicEntry);
DynamicEntry dynEntry = (DynamicEntry) entry;
assertEquals(dynEntry.getPCAdress().getOffset(), address);
EquateTable equateTable = program.getEquateTable();
assertNotNull(equateTable);
Equate equate = equateTable.getEquate(eqName);
assertNotNull(equate);
List<EquateReference> references = equate.getReferences(eqSym.getPCAddress());
assertEquals(references.size(), 1);
EquateReference ref = references.get(0);
assertEquals(equate.getValue(), eqSym.getValue());
boolean foundHash = false;
if (ref.getDynamicHashValue() == 0) {
Instruction instr = program.getListing().getInstructionAt(ref.getAddress());
long values[] = DynamicHash.calcConstantHash(instr, equate.getValue());
for (long value : values) {
if (value == dynEntry.getHash()) {
foundHash = true;
break;
}
}
}
else {
foundHash = (dynEntry.getHash() == ref.getDynamicHashValue());
}
assertTrue(foundHash);
assertEquals(ref.getOpIndex() >= 0, seeInListing);
}
@Test
public void testEquate_basicConversion() {
decompile("10016fa");
ClangTextField line = getLineContaining("0x53");
setDecompilerLocation(line.getLineNumber(), 17);
convertToken(EquateSymbol.FORMAT_DEC);
line = getLineContaining("83");
setDecompilerLocation(line.getLineNumber(), 15);
verifyMatch("83", "83", 0x1001700, true);
}
@Test
public void testEquate_offByOne() {
decompile("10016fa");
ClangTextField line = getLineContaining("< 3)");
setDecompilerLocation(line.getLineNumber(), line.getText().indexOf('3'));
convertToken(EquateSymbol.FORMAT_BIN);
line = getLineContaining("0b00000011"); // Binary format of "3"
setDecompilerLocation(line.getLineNumber(), line.getText().indexOf("0b"));
verifyMatch("00000000000000000000000000000010b", "0b00000011", 0x1001732, true);
}
@Test
public void testEquate_decompilerInvented() {
decompile("10016fa");
ClangTextField line = getLineContaining("0x111)");
setDecompilerLocation(line.getLineNumber(), line.getText().indexOf("0x111"));
convertToken(EquateSymbol.FORMAT_OCT);
line = getLineContaining("0421");
setDecompilerLocation(line.getLineNumber(), line.getText().indexOf("0421"));
verifyMatch("421o", "0421", 0x100171f, false);
}
@Test
public void testEquate_oneOffNearby() {
decompile("1002bc2");
ClangTextField line = getLineContaining("9,0,0,1");
setDecompilerLocation(line.getLineNumber(), line.getText().indexOf("0,0"));
convertToken("MYZERO");
line = getLineContaining("9,MYZERO"); // Make sure equate comes right after '9'
setDecompilerLocation(line.getLineNumber(), line.getText().indexOf("MYZERO"));
// The operand of the nearby "PUSH 0x1" instruction gets selected as a possible
// candidate for the equate, but it doesn't propagate to the desired zero.
verifyMatch("MYZERO", "MYZERO", 0x1002c8a, false);
}
@Test
public void testEquate_namedMinus() {
decompile("1002825");
ClangTextField line = getLineContaining("0x38");
setDecompilerLocation(line.getLineNumber(), line.getText().indexOf("0x38"));
convertToken("MYMINUS");
line = getLineContaining("+ MYMINUS");
setDecompilerLocation(line.getLineNumber(), line.getText().indexOf("MYMINUS"));
// Make sure the named equate applies to the negative number in the decompiler window
// NOT the positive variant in the listing
verifyMatch("MYMINUS", "MYMINUS", 0x1002862, false);
}
@Test
public void testEquate_unnamedMinus() {
decompile("1002825");
ClangTextField line = getLineContaining("0x2b");
setDecompilerLocation(line.getLineNumber(), line.getText().indexOf("0x2b"));
convertToken(EquateSymbol.FORMAT_DEC);
line = getLineContaining("43");
setDecompilerLocation(line.getLineNumber(), line.getText().indexOf("43"));
// Conversion should be attached to the positive formatting, but should affect both listing and decompiler
verifyMatch("43", "-43", 0x1002882, true);
}
@Test
public void testEquate_escapechar() {
decompile("1002785");
ClangTextField line = getLineContaining("/ 10)");
setDecompilerLocation(line.getLineNumber(), line.getText().indexOf("10)"));
convertToken(EquateSymbol.FORMAT_CHAR);
line = getLineContaining("\\n");
setDecompilerLocation(line.getLineNumber(), line.getText().indexOf("\\n"));
verifyMatch("'\\n'", "L'\\n'", 0x10027d3, true);
}
@Test
public void testEquate_convertChar() {
decompile("1003d76");
ClangTextField line = getLineContaining("'.'");
setDecompilerLocation(line.getLineNumber(), line.getText().indexOf("'.'"));
convertToken(EquateSymbol.FORMAT_HEX);
line = getLineContaining("x2e");
setDecompilerLocation(line.getLineNumber(), line.getText().indexOf("x2e"));
verifyMatch("0x2E", "'\\x2e'", 0x1003db9, true);
}
@Test
public void testEquate_actionNoShow() {
decompile("1001915");
ClangTextField line = getLineContaining("GetSystemMetrics(0x4e)");
setDecompilerLocation(line.getLineNumber(), line.getText().indexOf("4e"));
ConvertHexAction action = new ConvertHexAction(decompiler);
assertFalse(action.isEnabledForContext(getContext()));
}
@Test
public void testEquate_charNonAscii() {
decompile("1002eab");
ClangTextField line = getLineContaining("0xe0");
setDecompilerLocation(line.getLineNumber(), line.getText().indexOf("0xe0"));
convertToken(EquateSymbol.FORMAT_CHAR);
line = getLineContaining("xe0");
setDecompilerLocation(line.getLineNumber(), line.getText().indexOf("xe0"));
verifyMatch("E0h", "'\\xe0'", 0x1002ec3, true);
}
}

View file

@ -16,6 +16,8 @@
package ghidra.program.database.symbol;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import db.DBRecord;
import ghidra.program.database.DBObjectCache;
@ -73,16 +75,19 @@ public class EquateDB extends DatabaseObject implements Equate {
try {
Instruction instr = equateMgr.getProgram().getCodeManager().getInstructionAt(refAddr);
long dynamicHash;
if (instr == null)
if (instr == null) {
dynamicHash = 0;
}
else {
long value = record.getLongValue(EquateDBAdapter.VALUE_COL);
long hashArray[] = DynamicHash.calcConstantHash(instr, value);
if (hashArray.length != 1)
if (hashArray.length != 1) {
dynamicHash = 0;
else
}
else {
dynamicHash = hashArray[0];
}
}
equateMgr.addReference(key, refAddr, (short) opIndex, dynamicHash);
}
catch (IOException e) {
@ -112,9 +117,11 @@ public class EquateDB extends DatabaseObject implements Equate {
}
long value = record.getLongValue(EquateDBAdapter.VALUE_COL);
long checkHash[] = DynamicHash.calcConstantHash(instr, value);
for (long element : checkHash)
if (element == dynamicHash)
for (long element : checkHash) {
if (element == dynamicHash) {
return findScalarOpIndex(instr);
}
}
return -1;
}
@ -201,6 +208,27 @@ public class EquateDB extends DatabaseObject implements Equate {
return new EquateReference[0];
}
/**
* @see ghidra.program.model.symbol.Equate#getReferences(Address)
*/
@Override
public List<EquateReference> getReferences(Address refAddr) {
Lock lock = equateMgr.getLock();
lock.acquire();
try {
if (checkIsValid()) {
return equateMgr.getReferences(key, refAddr);
}
}
catch (IOException e) {
equateMgr.getProgram().dbError(e);
}
finally {
lock.release();
}
return new ArrayList<>();
}
/**
* @see ghidra.program.model.symbol.Equate#getValue()
*/

View file

@ -24,8 +24,7 @@ import ghidra.program.database.*;
import ghidra.program.database.map.AddressKeyAddressIterator;
import ghidra.program.database.map.AddressMap;
import ghidra.program.model.address.*;
import ghidra.program.model.symbol.Equate;
import ghidra.program.model.symbol.EquateTable;
import ghidra.program.model.symbol.*;
import ghidra.program.util.ChangeManager;
import ghidra.program.util.EquateInfo;
import ghidra.util.Lock;
@ -328,11 +327,10 @@ public class EquateManager implements EquateTable, ErrorHandler, ManagerDB {
list.add(ref);
}
}
for (int i = 0; i < list.size(); i++) {
for (EquateRefDB ref : list) {
if (monitor.isCancelled()) {
throw new CancelledException();
}
EquateRefDB ref = list.get(i);
EquateDB equateDB = getEquateDB(ref.getEquateID());
removeRef(equateDB, ref);
@ -454,6 +452,22 @@ public class EquateManager implements EquateTable, ErrorHandler, ManagerDB {
return getReferences(equateID).length;
}
List<EquateReference> getReferences(long equateID, Address reference) throws IOException {
List<EquateReference> refs = new ArrayList<>();
long refAddr = addrMap.getKey(reference, false);
if (refAddr == AddressMap.INVALID_ADDRESS_KEY) {
return refs;
}
Field[] keys = refAdapter.getRecordKeysForAddr(refAddr);
for (Field key : keys) {
EquateRefDB ref = getEquateRefDB(key.getLongValue());
if (ref.getEquateID() == equateID) {
refs.add(ref);
}
}
return refs;
}
void removeReference(EquateDB equateDB, Address refAddr, short opIndex) throws IOException {
Field[] keys = refAdapter.getRecordKeysForEquateID(equateDB.getKey());

View file

@ -57,7 +57,9 @@ public class EquateSymbol extends HighSymbol {
addMapEntry(entry);
}
public long getValue() { return value; }
public long getValue() {
return value;
}
public int getConvert() {
return convert;
@ -122,7 +124,17 @@ public class EquateSymbol extends HighSymbol {
buf.append("</equatesymbol>\n");
}
public static int convertName(String nm,long val) {
/**
* Determine what format a given equate name is in.
* Integer format conversions are stored using an Equate object, where the name of the equate
* is the actual conversion String. So the only way to tell what kind of conversion is being performed
* is by examining the name of the equate. The format code of the conversion is returned, or if
* the name is not a conversion, FORMAT_DEFAULT is returned indicating a normal String equate.
* @param nm is the name of the equate
* @param val is the value being equated
* @return the format code for the conversion or FORMAT_DEFAULT if not a conversion
*/
public static int convertName(String nm, long val) {
int pos = 0;
char firstChar = nm.charAt(pos++);
if (firstChar == '-') {
@ -133,32 +145,53 @@ public class EquateSymbol extends HighSymbol {
return FORMAT_DEFAULT; // Bad equate name, just print number normally
}
}
if (firstChar == '\'') {
switch (firstChar) {
case '\'':
case '"':
return FORMAT_CHAR;
case '0':
if (nm.length() >= (pos + 1) && nm.charAt(pos) == 'x') {
return FORMAT_HEX;
}
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
break;
case 'A':
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
if (nm.length() >= 3 && nm.charAt(2) == 'h') {
char secondChar = nm.charAt(1);
if (secondChar >= '0' && secondChar <= '9') {
return FORMAT_CHAR;
}
if (firstChar == '"') { // Multi-character conversion
return FORMAT_DEC; // not currently supported, just format in decimal
if (secondChar >= 'A' && secondChar <= 'F') {
return FORMAT_CHAR;
}
if (firstChar < '0' || firstChar > '9') {
return -1; // Don't treat as a conversion
}
char lastChar = nm.charAt(nm.length() - 1);
if (lastChar == 'b') {
return FORMAT_DEFAULT;
default:
return FORMAT_DEFAULT; // Don't treat as a conversion
}
switch (nm.charAt(nm.length() - 1)) {
case 'b':
return FORMAT_BIN;
}
else if (lastChar == 'o') {
case 'o':
return FORMAT_OCT;
case '\'':
case '"':
case 'h': // The 'h' encoding is used for "unrepresentable" characters
return FORMAT_CHAR;
}
int format = FORMAT_DEC;
if (firstChar == '0') {
format = FORMAT_DEC;
if (nm.length() >= (pos + 1)) {
char c = nm.charAt(pos);
if (c == 'x') {
format = FORMAT_HEX;
}
}
}
return format;
return FORMAT_DEC;
}
}

View file

@ -50,8 +50,8 @@ public class LocalSymbolMap {
public LocalSymbolMap(HighFunction highFunc, String spcname) {
func = highFunc;
spacename = spcname;
addrMappedSymbols = new HashMap<MappedVarKey, HighSymbol>();
symbolMap = new HashMap<Long, HighSymbol>();
addrMappedSymbols = new HashMap<>();
symbolMap = new HashMap<>();
paramSymbols = new HighSymbol[0];
uniqueSymbolId = 0;
}
@ -79,7 +79,7 @@ public class LocalSymbolMap {
* @return the new name to symbol map
*/
public Map<String, HighSymbol> getNameToSymbolMap() {
Map<String, HighSymbol> newMap = new TreeMap<String, HighSymbol>();
Map<String, HighSymbol> newMap = new TreeMap<>();
for (HighSymbol highSymbol : symbolMap.values()) {
newMap.put(highSymbol.getName(), highSymbol);
}
@ -171,7 +171,7 @@ public class LocalSymbolMap {
// An indication of names like "name", "name@1", "name@2"
if (name.charAt(name.length() - 1) == '1') {
if (mergeNames == null) {
mergeNames = new ArrayList<String>();
mergeNames = new ArrayList<>();
}
mergeNames.add(name);
}
@ -207,7 +207,7 @@ public class LocalSymbolMap {
Address pcaddr = dbFunction.getEntryPoint();
pcaddr = pcaddr.subtractWrap(1);
List<HighSymbol> paramList = new ArrayList<HighSymbol>();
List<HighSymbol> paramList = new ArrayList<>();
for (int i = 0; i < p.length; ++i) {
Parameter var = p[i];
if (!var.isValid()) {
@ -220,7 +220,7 @@ public class LocalSymbolMap {
// An indication of names like "name", "name@1", "name@2"
if (name.charAt(name.length() - 1) == '1') {
if (mergeNames == null) {
mergeNames = new ArrayList<String>();
mergeNames = new ArrayList<>();
}
mergeNames.add(name);
}
@ -297,8 +297,7 @@ public class LocalSymbolMap {
parser.end(el);
}
private static final Comparator<HighSymbol> PARAM_SYMBOL_SLOT_COMPARATOR =
new Comparator<HighSymbol>() {
private static final Comparator<HighSymbol> PARAM_SYMBOL_SLOT_COMPARATOR = new Comparator<>() {
@Override
public int compare(HighSymbol sym1, HighSymbol sym2) {
return sym1.getCategoryIndex() - sym2.getCategoryIndex();
@ -312,7 +311,7 @@ public class LocalSymbolMap {
*/
public void parseSymbolList(XmlPullParser parser) throws PcodeXMLException {
XmlElement el = parser.start("symbollist");
ArrayList<HighSymbol> parms = new ArrayList<HighSymbol>();
ArrayList<HighSymbol> parms = new ArrayList<>();
while (parser.peek().isStart()) {
HighSymbol sym = parseSymbolXML(parser);
if (sym.isParameter()) {
@ -483,7 +482,7 @@ public class LocalSymbolMap {
uniqueId = getNextId();
}
int conv = EquateSymbol.convertName(nm, val);
if (conv < 0) {
if (conv == EquateSymbol.FORMAT_DEFAULT) {
eqSymbol = new EquateSymbol(uniqueId, nm, val, func, addr, hash);
eqSymbol.setNameLock(true);
}
@ -507,27 +506,39 @@ public class LocalSymbolMap {
while (equateAddresses.hasNext()) {
Address defAddr = equateAddresses.next();
for (Equate eq : equateTable.getEquates(defAddr)) {
Instruction instr = listing.getInstructionAt(defAddr);
if (instr == null) {
continue;
}
long hash[] = DynamicHash.calcConstantHash(instr, eq.getValue());
if (hash.length == 0) {
continue;
}
Arrays.sort(hash); // Sort in preparation for deduping
List<EquateReference> references = eq.getReferences(defAddr);
String displayName = eq.getDisplayName();
long eqValue = eq.getValue();
EquateSymbol eqSymbol;
for (EquateReference ref : references) {
long hashVal = ref.getDynamicHashValue();
if (hashVal == 0) {
// Multiple varnodes of the same constant
Instruction instr = listing.getInstructionAt(defAddr);
if (instr == null) {
continue;
}
long hash[] = DynamicHash.calcConstantHash(instr, eqValue);
if (hash.length == 0) {
continue;
}
Arrays.sort(hash); // Sort in preparation for deduping
for (int i = 0; i < hash.length; ++i) {
if (i != 0 && hash[i - 1] == hash[i]) {
continue; // Found a duplicate, skip it
}
// Emit each varnode copy as a separate EquateSymbol
eqSymbol = newEquateSymbol(0, displayName, eqValue, hash[i], defAddr);
symbolMap.put(eqSymbol.getId(), eqSymbol);
}
}
else {
eqSymbol = newEquateSymbol(0, displayName, eqValue, hashVal, defAddr);
symbolMap.put(eqSymbol.getId(), eqSymbol);
}
}
}
}
}

View file

@ -15,10 +15,13 @@
*/
package ghidra.program.model.symbol;
import java.util.List;
import ghidra.program.model.address.Address;
import ghidra.util.UniversalID;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.exception.InvalidInputException;
/**
* An Equate associates a string with a scalar value in the program,
* and contains a list of addresses and operand positions that refer
@ -67,7 +70,7 @@ public interface Equate {
* @param refAddr the address where the equate is used.
* @param opndPosition the operand index where the equate is used.
*/
public void addReference(Address refAddr,int opndPosition);
public void addReference(Address refAddr, int opndPosition);
/**
* Add a reference (at the given dynamic hash position) to this equate. If a reference already
@ -87,8 +90,7 @@ public interface Equate {
* @throws InvalidInputException if newName contains blank characters,
* is zero length, or is null
*/
void renameEquate(String newName) throws DuplicateNameException,
InvalidInputException;
void renameEquate(String newName) throws DuplicateNameException, InvalidInputException;
/**
* Get the references for this equate.
@ -96,6 +98,13 @@ public interface Equate {
*/
public EquateReference[] getReferences();
/**
* Get references for this equate attached to a specific address
* @param refAddr is the address
* @return the list of EquateReferences
*/
public List<EquateReference> getReferences(Address refAddr);
/**
* Remove the reference at the given operand position.
* @param refAddr the address that was using this equate