mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 02:39:44 +02:00
Decompiler window integer conversions
This commit is contained in:
parent
11149b9ef2
commit
1391e83ce9
35 changed files with 2430 additions and 281 deletions
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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|
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ";
|
||||
|
|
|
@ -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>]`
|
||||
///
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
|
@ -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 ...">“Set Equate ...”</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">“Convert”</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>
|
||||
|
|
|
@ -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">“Remove Convert/Equate”</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">“Convert”</a> or a
|
||||
<a class="xref" href="DecompilerWindow.html#ActionSetEquate" title="Set Equate ...">“Set Equate ...”</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">“Remove Convert/Equate”</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>
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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";
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
*/
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue