Program specific, user-defined, cspec extensions

Documentation for spec extensions

Handle extensions with parse errors
Export button for spec extensions
Pop-up dialog for parse errors in user-defined specification extensions
GP-653 corrected some minor issues and established new ProgramDB version
make incremental initialization constructor for AddressSized private
Make AddressSized fields private
More adjustments to AddressSized
Review fixes for BasicCompilerSpec
Take restoreXml out of DataOrganization interface
Remove restoreXml from BitFieldPacking interface
More review fixes
Prevent callotherfixup extension with non-existent target
Suggested export name
More documentation for SpecExtension
Support for undo/redo with spec extensions
Documentation for ConstructTpl
Split out ProgramCompilerSpec and other changes for review
Changes after next round of reviews
This commit is contained in:
caheckman 2021-02-02 13:07:54 -05:00 committed by ghidra1
parent 27fbe7278d
commit a5d4ca3cab
108 changed files with 7997 additions and 1997 deletions

View file

@ -101,8 +101,8 @@ public class CallFixupAnalyzer extends AbstractAnalyzer {
if (mustFix) { if (mustFix) {
PcodeInjectLibrary snippetLibrary = PcodeInjectLibrary snippetLibrary =
program.getCompilerSpec().getPcodeInjectLibrary(); program.getCompilerSpec().getPcodeInjectLibrary();
InjectPayload callFixup = snippetLibrary.getPayload(InjectPayload.CALLFIXUP_TYPE, InjectPayload callFixup =
callFixupApplied, program, null); snippetLibrary.getPayload(InjectPayload.CALLFIXUP_TYPE, callFixupApplied);
boolean isfallthru = true; boolean isfallthru = true;
if (callFixup != null) { if (callFixup != null) {
isfallthru = callFixup.isFallThru(); isfallthru = callFixup.isFallThru();
@ -405,8 +405,8 @@ public class CallFixupAnalyzer extends AbstractAnalyzer {
} }
} }
program.getBookmarkManager().removeBookmarks(repairedCallLocations, BookmarkType.ERROR, program.getBookmarkManager()
monitor); .removeBookmarks(repairedCallLocations, BookmarkType.ERROR, monitor);
if (!clearInstSet.isEmpty()) { if (!clearInstSet.isEmpty()) {
// entries including data flow referenced from instructions will be repaired // entries including data flow referenced from instructions will be repaired
@ -449,7 +449,7 @@ public class CallFixupAnalyzer extends AbstractAnalyzer {
String[] callFixupNames = snippetLibrary.getCallFixupNames(); String[] callFixupNames = snippetLibrary.getCallFixupNames();
for (String fixupName : callFixupNames) { for (String fixupName : callFixupNames) {
InjectPayload payload = InjectPayload payload =
snippetLibrary.getPayload(InjectPayload.CALLFIXUP_TYPE, fixupName, program, null); snippetLibrary.getPayload(InjectPayload.CALLFIXUP_TYPE, fixupName);
List<String> callFixupTargets = ((InjectPayloadCallfixup) payload).getTargets(); List<String> callFixupTargets = ((InjectPayloadCallfixup) payload).getTargets();
for (String name : callFixupTargets) { for (String name : callFixupTargets) {
cachedTargetFixupMap.put(name, fixupName); cachedTargetFixupMap.put(name, fixupName);

View file

@ -359,6 +359,9 @@ public class FunctionEditorDialog extends DialogComponentProvider implements Mod
} }
private JComponent createCallFixupComboPanel() { private JComponent createCallFixupComboPanel() {
JPanel panel = new JPanel();
callFixupComboBox = new GComboBox<>(); callFixupComboBox = new GComboBox<>();
String[] callFixupNames = model.getCallFixupNames(); String[] callFixupNames = model.getCallFixupNames();
@ -377,7 +380,8 @@ public class FunctionEditorDialog extends DialogComponentProvider implements Mod
callFixupComboBox.setEnabled(false); callFixupComboBox.setEnabled(false);
} }
return callFixupComboBox; panel.add(callFixupComboBox);
return panel;
} }
private Component buildTable() { private Component buildTable() {

View file

@ -405,8 +405,9 @@ public class SymbolicPropogator {
canceled = false; canceled = false;
// only stop flowing on unknown bad calls when the stack depth could be unknown // only stop flowing on unknown bad calls when the stack depth could be unknown
boolean callCouldCauseBadStackDepth = boolean callCouldCauseBadStackDepth = program.getCompilerSpec()
program.getCompilerSpec().getDefaultCallingConvention().getExtrapop() == PrototypeModel.UNKNOWN_EXTRAPOP; .getDefaultCallingConvention()
.getExtrapop() == PrototypeModel.UNKNOWN_EXTRAPOP;
while (!contextStack.isEmpty()) { while (!contextStack.isEmpty()) {
monitor.checkCanceled(); monitor.checkCanceled();
@ -834,8 +835,8 @@ public class SymbolicPropogator {
try { try {
val1 = vContext.getValue(in[0], evaluator); val1 = vContext.getValue(in[0], evaluator);
lval1 = vContext.getConstant(val1, evaluator); lval1 = vContext.getConstant(val1, evaluator);
vt = vContext.getVarnode( vt = vContext.getVarnode(minInstrAddress.getAddressSpace().getSpaceID(),
minInstrAddress.getAddressSpace().getSpaceID(), lval1, 0); lval1, 0);
makeReference(vContext, instruction, ptype, -1, vt, makeReference(vContext, instruction, ptype, -1, vt,
instruction.getFlowType(), monitor); instruction.getFlowType(), monitor);
} }
@ -873,8 +874,8 @@ public class SymbolicPropogator {
if (val1.isConstant()) { if (val1.isConstant()) {
// indirect target - assume single code space (same as instruction) // indirect target - assume single code space (same as instruction)
target = instruction.getAddress().getNewTruncatedAddress( target = instruction.getAddress()
val1.getOffset(), true); .getNewTruncatedAddress(val1.getOffset(), true);
} }
else if (val1.isAddress()) { else if (val1.isAddress()) {
// TODO: could this also occur if a memory location was copied ?? // TODO: could this also occur if a memory location was copied ??
@ -957,8 +958,8 @@ public class SymbolicPropogator {
case PcodeOp.CALLOTHER: case PcodeOp.CALLOTHER:
// HACK ALERT! // HACK ALERT!
// if this is a segment op, emulate the segmenting for now. // if this is a segment op, emulate the segmenting for now.
String opName = this.program.getLanguage().getUserDefinedOpName( String opName = this.program.getLanguage()
(int) in[0].getOffset()); .getUserDefinedOpName((int) in[0].getOffset());
if (opName.equals("segment") && in.length > 2) { if (opName.equals("segment") && in.length > 2) {
checkSegmented(out, in[1], in[2], mustClearAll); checkSegmented(out, in[1], in[2], mustClearAll);
} }
@ -1074,8 +1075,8 @@ public class SymbolicPropogator {
} }
else if (!evaluator.followFalseConditionalBranches()) { else if (!evaluator.followFalseConditionalBranches()) {
// pcode addresses are raw addresses, make sure address is in same instruction space // pcode addresses are raw addresses, make sure address is in same instruction space
nextAddr = minInstrAddress.getAddressSpace().getOverlayAddress( nextAddr = minInstrAddress.getAddressSpace()
in[0].getAddress()); .getOverlayAddress(in[0].getAddress());
pcodeIndex = ops.length; // break out of the processing pcodeIndex = ops.length; // break out of the processing
} }
} }
@ -1202,8 +1203,8 @@ public class SymbolicPropogator {
case PcodeOp.INT_RIGHT: case PcodeOp.INT_RIGHT:
val1 = vContext.getValue(in[0], false, evaluator); val1 = vContext.getValue(in[0], false, evaluator);
val2 = vContext.getValue(in[1], false, evaluator); val2 = vContext.getValue(in[1], false, evaluator);
lresult = vContext.getConstant(val1, lresult = vContext.getConstant(val1, evaluator) >> vContext
evaluator) >> vContext.getConstant(val2, evaluator); .getConstant(val2, evaluator);
result = vContext.createConstantVarnode(lresult, val1.getSize()); result = vContext.createConstantVarnode(lresult, val1.getSize());
vContext.putValue(out, result, mustClearAll); vContext.putValue(out, result, mustClearAll);
break; break;
@ -1211,8 +1212,8 @@ public class SymbolicPropogator {
case PcodeOp.INT_SRIGHT: case PcodeOp.INT_SRIGHT:
val1 = vContext.getValue(in[0], true, evaluator); val1 = vContext.getValue(in[0], true, evaluator);
val2 = vContext.getValue(in[1], false, evaluator); val2 = vContext.getValue(in[1], false, evaluator);
lresult = vContext.getConstant(val1, lresult = vContext.getConstant(val1, evaluator) >>> vContext
evaluator) >>> vContext.getConstant(val2, evaluator); .getConstant(val2, evaluator);
result = vContext.createConstantVarnode(lresult, val1.getSize()); result = vContext.createConstantVarnode(lresult, val1.getSize());
vContext.putValue(out, result, mustClearAll); vContext.putValue(out, result, mustClearAll);
break; break;
@ -1315,8 +1316,8 @@ public class SymbolicPropogator {
val2 = vContext.getValue(in[1], true, evaluator); val2 = vContext.getValue(in[1], true, evaluator);
lval1 = vContext.getConstant(val1, evaluator); lval1 = vContext.getConstant(val1, evaluator);
lval2 = vContext.getConstant(val2, evaluator); lval2 = vContext.getConstant(val2, evaluator);
lresult = (vContext.getConstant(val1, lresult = (vContext.getConstant(val1, evaluator) < vContext
evaluator) < vContext.getConstant(val2, evaluator)) ? 1 : 0; .getConstant(val2, evaluator)) ? 1 : 0;
result = vContext.createConstantVarnode(lresult, val1.getSize()); result = vContext.createConstantVarnode(lresult, val1.getSize());
vContext.putValue(out, result, mustClearAll); vContext.putValue(out, result, mustClearAll);
break; break;
@ -1334,8 +1335,8 @@ public class SymbolicPropogator {
case PcodeOp.INT_SLESSEQUAL: case PcodeOp.INT_SLESSEQUAL:
val1 = vContext.getValue(in[0], true, evaluator); val1 = vContext.getValue(in[0], true, evaluator);
val2 = vContext.getValue(in[1], true, evaluator); val2 = vContext.getValue(in[1], true, evaluator);
lresult = (vContext.getConstant(val1, lresult = (vContext.getConstant(val1, evaluator) <= vContext
evaluator) <= vContext.getConstant(val2, evaluator)) ? 1 : 0; .getConstant(val2, evaluator)) ? 1 : 0;
result = vContext.createConstantVarnode(lresult, val1.getSize()); result = vContext.createConstantVarnode(lresult, val1.getSize());
vContext.putValue(out, result, mustClearAll); vContext.putValue(out, result, mustClearAll);
break; break;
@ -1344,8 +1345,8 @@ public class SymbolicPropogator {
val1 = vContext.getValue(in[0], false, evaluator); val1 = vContext.getValue(in[0], false, evaluator);
val2 = vContext.getValue(in[1], false, evaluator); val2 = vContext.getValue(in[1], false, evaluator);
lresult = (vContext.getConstant(val1, lresult = (vContext.getConstant(val1, evaluator) == vContext
evaluator) == vContext.getConstant(val2, evaluator)) ? 1 : 0; .getConstant(val2, evaluator)) ? 1 : 0;
result = vContext.createConstantVarnode(lresult, val1.getSize()); result = vContext.createConstantVarnode(lresult, val1.getSize());
vContext.putValue(out, result, mustClearAll); vContext.putValue(out, result, mustClearAll);
break; break;
@ -1353,8 +1354,8 @@ public class SymbolicPropogator {
case PcodeOp.INT_NOTEQUAL: case PcodeOp.INT_NOTEQUAL:
val1 = vContext.getValue(in[0], false, evaluator); val1 = vContext.getValue(in[0], false, evaluator);
val2 = vContext.getValue(in[1], false, evaluator); val2 = vContext.getValue(in[1], false, evaluator);
lresult = (vContext.getConstant(val1, lresult = (vContext.getConstant(val1, evaluator) != vContext
evaluator) != vContext.getConstant(val2, evaluator)) ? 1 : 0; .getConstant(val2, evaluator)) ? 1 : 0;
result = vContext.createConstantVarnode(lresult, val1.getSize()); result = vContext.createConstantVarnode(lresult, val1.getSize());
vContext.putValue(out, result, mustClearAll); vContext.putValue(out, result, mustClearAll);
break; break;
@ -1571,7 +1572,7 @@ public class SymbolicPropogator {
PcodeInjectLibrary snippetLibrary = prog.getCompilerSpec().getPcodeInjectLibrary(); PcodeInjectLibrary snippetLibrary = prog.getCompilerSpec().getPcodeInjectLibrary();
InjectPayload payload = InjectPayload payload =
snippetLibrary.getPayload(InjectPayload.CALLFIXUP_TYPE, callFixupName, prog, null); snippetLibrary.getPayload(InjectPayload.CALLFIXUP_TYPE, callFixupName);
if (payload == null) { if (payload == null) {
return null; return null;
} }
@ -1598,7 +1599,7 @@ public class SymbolicPropogator {
PcodeInjectLibrary snippetLibrary = prog.getCompilerSpec().getPcodeInjectLibrary(); PcodeInjectLibrary snippetLibrary = prog.getCompilerSpec().getPcodeInjectLibrary();
InjectPayload payload = InjectPayload payload =
snippetLibrary.getPayload(InjectPayload.CALLMECHANISM_TYPE, injectionName, prog, null); snippetLibrary.getPayload(InjectPayload.CALLMECHANISM_TYPE, injectionName);
if (payload == null) { if (payload == null) {
return null; return null;
} }
@ -1843,14 +1844,14 @@ public class SymbolicPropogator {
// } else // } else
if (!vContext.isStackSymbolicSpace(refLocation) && evaluator != null) { if (!vContext.isStackSymbolicSpace(refLocation) && evaluator != null) {
Address constant = program.getAddressFactory().getAddress( Address constant = program.getAddressFactory()
(int) targetSpaceID.getOffset(), offset); .getAddress((int) targetSpaceID.getOffset(), offset);
Address newTarget = evaluator.evaluateConstant(vContext, instruction, Address newTarget = evaluator.evaluateConstant(vContext, instruction,
pcodeType, constant, 0, reftype); pcodeType, constant, 0, reftype);
if (newTarget != null) { if (newTarget != null) {
makeReference(vContext, instruction, Reference.MNEMONIC, makeReference(vContext, instruction, Reference.MNEMONIC,
newTarget.getAddressSpace().getSpaceID(), newTarget.getOffset(), newTarget.getAddressSpace().getSpaceID(), newTarget.getOffset(), 0,
0, reftype, pcodeType, false, monitor); reftype, pcodeType, false, monitor);
return; return;
} }
} }

View file

@ -120,7 +120,7 @@ task buildDecompilerHelpHtml(type: Exec) {
rm -f $installHelpPoint/topics/DecompilePlugin/*.html rm -f $installHelpPoint/topics/DecompilePlugin/*.html
echo '** Building html files **' echo '** Building html files **'
xsltproc --output $buildDir/decomp_noscaling.xml --stringparam profile.condition "noscaling" /usr/share/sgml/docbook/xsl-stylesheets/profiling/profile.xsl decompileplugin.xml 2>&1 xsltproc --output $buildDir/decomp_noscaling.xml --stringparam profile.condition "noscaling" commonprofile.xsl decompileplugin.xml 2>&1
xsltproc --stringparam base.dir ${installHelpPoint}/topics/DecompilePlugin/ --stringparam root.filename Decompiler decompileplugin_html.xsl $buildDir/decomp_noscaling.xml 2>&1 xsltproc --stringparam base.dir ${installHelpPoint}/topics/DecompilePlugin/ --stringparam root.filename Decompiler decompileplugin_html.xsl $buildDir/decomp_noscaling.xml 2>&1
rm ${installHelpPoint}/topics/DecompilePlugin/Decompiler.html rm ${installHelpPoint}/topics/DecompilePlugin/Decompiler.html
sed -i -e '/Frontpage.css/ { p; s/Frontpage.css/languages.css/; }' ${installHelpPoint}/topics/DecompilePlugin/*.html 2>&1 sed -i -e '/Frontpage.css/ { p; s/Frontpage.css/languages.css/; }' ${installHelpPoint}/topics/DecompilePlugin/*.html 2>&1
@ -175,19 +175,19 @@ task buildDecompilerHelpPdf(type: Exec) {
echo '** Checking if required executables are installed. **' echo '** Checking if required executables are installed. **'
which fop 2>&1 which fop 2>&1
which xsltproc 2>&1 which xsltproc 2>&1
rm -f decompileplugin.fo decompileplugin.pdf decompileplugin_withscaling.xml 2>&1 rm -f $buildDir/decompileplugin.fo $buildDir/decompileplugin.pdf $buildDir/decompileplugin_withscaling.xml 2>&1
rm -rf ./images 2>&1 rm -rf $buildDir/images 2>&1
mkdir -p ./images 2>&1 mkdir -p $buildDir/images 2>&1
cp $installHelpPoint/topics/DecompilePlugin/images/*.png ./images 2>&1 cp $installHelpPoint/topics/DecompilePlugin/images/*.png $buildDir/images 2>&1
cp $installHelpPoint/topics/DecompilePlugin/images/*.gif ./images 2>&1 cp $installHelpPoint/topics/DecompilePlugin/images/*.gif $buildDir/images 2>&1
cp $installHelpPoint/shared/*.png ./images 2>&1 cp $installHelpPoint/shared/*.png $buildDir/images 2>&1
echo '** Building decompileplugin.fo **' echo '** Building decompileplugin.fo **'
xsltproc --output ./decompileplugin_withscaling.xml --stringparam profile.condition "withscaling" /usr/share/sgml/docbook/xsl-stylesheets/profiling/profile.xsl decompileplugin.xml 2>&1 xsltproc --output $buildDir/decompileplugin_withscaling.xml --stringparam profile.condition "withscaling" commonprofile.xsl decompileplugin.xml 2>&1
xsltproc --output ./decompileplugin.fo decompileplugin_pdf.xsl decompileplugin_withscaling.xml 2>&1 xsltproc --output $buildDir/decompileplugin.fo decompileplugin_pdf.xsl $buildDir/decompileplugin_withscaling.xml 2>&1
echo '** Building decompileplugin.pdf **' echo '** Building decompileplugin.pdf **'
fop decompileplugin.fo decompileplugin.pdf 2>&1 fop $buildDir/decompileplugin.fo $buildDir/decompileplugin.pdf 2>&1
echo '** Done. **' echo '** Done. **'
""" """

View file

@ -30,6 +30,7 @@ src/decompile/datatests/sbyte.xml||GHIDRA||||END|
src/decompile/datatests/threedim.xml||GHIDRA||||END| src/decompile/datatests/threedim.xml||GHIDRA||||END|
src/decompile/datatests/twodim.xml||GHIDRA||||END| src/decompile/datatests/twodim.xml||GHIDRA||||END|
src/decompile/datatests/wayoffarray.xml||GHIDRA||||END| src/decompile/datatests/wayoffarray.xml||GHIDRA||||END|
src/main/doc/commonprofile.xsl||GHIDRA||||END|
src/main/doc/cspec.xml||GHIDRA||||END| src/main/doc/cspec.xml||GHIDRA||||END|
src/main/doc/cspec_html.xsl||GHIDRA||||END| src/main/doc/cspec_html.xsl||GHIDRA||||END|
src/main/doc/decompileplugin.xml||GHIDRA||||END| src/main/doc/decompileplugin.xml||GHIDRA||||END|

View file

@ -15,18 +15,17 @@
*/ */
import ghidra.app.script.GhidraScript; import ghidra.app.script.GhidraScript;
import ghidra.framework.options.Options; import ghidra.framework.options.Options;
import ghidra.program.model.lang.BasicCompilerSpec; import ghidra.program.database.ProgramCompilerSpec;
public class TurnOnLanguage extends GhidraScript { public class TurnOnLanguage extends GhidraScript {
@Override @Override
protected void run() throws Exception { protected void run() throws Exception {
Options decompilerPropertyList = currentProgram.getOptions(BasicCompilerSpec.DECOMPILER_PROPERTY_LIST_NAME); Options decompilerPropertyList =
decompilerPropertyList.registerOption( currentProgram.getOptions(ProgramCompilerSpec.DECOMPILER_PROPERTY_LIST_NAME);
BasicCompilerSpec.DECOMPILER_OUTPUT_LANGUAGE, decompilerPropertyList.registerOption(ProgramCompilerSpec.DECOMPILER_OUTPUT_LANGUAGE,
BasicCompilerSpec.DECOMPILER_OUTPUT_DEF, ProgramCompilerSpec.DECOMPILER_OUTPUT_DEF, null,
null, ProgramCompilerSpec.DECOMPILER_OUTPUT_DESC);
BasicCompilerSpec.DECOMPILER_OUTPUT_DESC);
} }
} }

View file

@ -1218,6 +1218,26 @@ void Architecture::parseCompilerConfig(DocumentStorage &store)
else if (elname == "inferptrbounds") else if (elname == "inferptrbounds")
parseInferPtrBounds(*iter); parseInferPtrBounds(*iter);
} }
el = store.getTag("specextensions"); // Look for any user-defined configuration document
if (el != (const Element *)0) {
const List &userlist(el->getChildren());
for(iter=userlist.begin();iter!=userlist.end();++iter) {
const string &elname( (*iter)->getName() );
if (elname == "prototype")
parseProto(*iter);
else if (elname == "callfixup") {
pcodeinjectlib->restoreXmlInject(archid+" : compiler spec", (*iter)->getAttributeValue("name"),
InjectPayload::CALLFIXUP_TYPE, *iter);
}
else if (elname == "callotherfixup") {
userops.parseCallOtherFixup(*iter,this);
}
else if (elname == "global")
globaltags.push_back(*iter);
}
}
// <global> tags instantiate the base symbol table // <global> tags instantiate the base symbol table
// They need to know about all spaces, so it must come // They need to know about all spaces, so it must come
// after parsing of <stackpointer> and <spacebase> // after parsing of <stackpointer> and <spacebase>

View file

@ -314,7 +314,7 @@ public:
enum { enum {
unaffected = 1, ///< The sub-function does not change the value at all unaffected = 1, ///< The sub-function does not change the value at all
killedbycall = 2, ///< The memory is changed and is completely unrelated to its original value killedbycall = 2, ///< The memory is changed and is completely unrelated to its original value
return_address = 3, ///< The memory is being used to pass back a return value from the sub-function return_address = 3, ///< The memory is being used to store the return address
unknown_effect = 4 ///< An unknown effect (indicates the absence of an EffectRecord) unknown_effect = 4 ///< An unknown effect (indicates the absence of an EffectRecord)
}; };
private: private:

View file

@ -293,10 +293,10 @@ void ArchitectureGhidra::buildSpecFile(DocumentStorage &store)
doc = store.parseDocument(corestream); doc = store.parseDocument(corestream);
store.registerTag(doc->getRoot()); store.registerTag(doc->getRoot());
pspecxml = ""; // Strings aren't used again free memory pspecxml.clear(); // Strings aren't used again free memory
cspecxml = ""; cspecxml.clear();
tspecxml = ""; tspecxml.clear();
corespecxml = ""; corespecxml.clear();
} }
void ArchitectureGhidra::postSpecFile(void) void ArchitectureGhidra::postSpecFile(void)

View file

@ -82,7 +82,8 @@ class ArchitectureGhidra : public Architecture {
virtual void postSpecFile(void); virtual void postSpecFile(void);
virtual void resolveArchitecture(void); virtual void resolveArchitecture(void);
public: public:
ArchitectureGhidra(const string &pspec,const string &cspec,const string &tspec,const string &corespec,istream &i,ostream &o); ArchitectureGhidra(const string &pspec,const string &cspec,const string &tspec,const string &corespec,
istream &i,ostream &o);
const string &getWarnings(void) const { return warnings; } ///< Get warnings produced by the last decompilation const string &getWarnings(void) const { return warnings; } ///< Get warnings produced by the last decompilation
void clearWarnings(void) { warnings.clear(); } ///< Clear warnings void clearWarnings(void) { warnings.clear(); } ///< Clear warnings
Document *getRegister(const string &regname); ///< Retrieve a register description given a name Document *getRegister(const string &regname); ///< Retrieve a register description given a name

View file

@ -174,6 +174,10 @@ void RegisterProgram::rawAction(void)
} }
} }
ghidra = new ArchitectureGhidra(pspec,cspec,tspec,corespec,sin,sout); ghidra = new ArchitectureGhidra(pspec,cspec,tspec,corespec,sin,sout);
pspec.clear();
cspec.clear();
tspec.clear();
corespec.clear();
DocumentStorage store; // temp storage of initialization xml docs DocumentStorage store; // temp storage of initialization xml docs
ghidra->init(store); ghidra->init(store);

View file

@ -293,6 +293,23 @@ int4 PcodeInjectLibrarySleigh::registerDynamicInject(InjectPayload *payload)
return id; return id;
} }
/// \brief Force a payload to be dynamic for debug purposes
///
/// Debug information may include inject information for payloads that aren't dynamic.
/// We substitute a dynamic payload so that analysis uses the debug info to inject, rather
/// than the hard-coded payload information.
/// \param injectid is the id of the payload to treat dynamic
/// \return the new dynamic payload object
InjectPayloadDynamic *PcodeInjectLibrarySleigh::forceDebugDynamic(int4 injectid)
{
InjectPayload *oldPayload = injection[injectid];
InjectPayloadDynamic *newPayload = new InjectPayloadDynamic(glb,oldPayload->getName(),oldPayload->getType());
delete oldPayload;
injection[injectid] = newPayload;
return newPayload;
}
void PcodeInjectLibrarySleigh::parseInject(InjectPayload *payload) void PcodeInjectLibrarySleigh::parseInject(InjectPayload *payload)
{ {
@ -399,9 +416,10 @@ void PcodeInjectLibrarySleigh::restoreDebug(const Element *el)
s.unsetf(ios::dec | ios::hex | ios::oct); s.unsetf(ios::dec | ios::hex | ios::oct);
s >> type; s >> type;
int4 id = getPayloadId(type,name); int4 id = getPayloadId(type,name);
InjectPayloadDynamic *payload = (InjectPayloadDynamic *)getPayload(id); InjectPayloadDynamic *payload = dynamic_cast<InjectPayloadDynamic *>(getPayload(id));
if (payload->getSource() != "dynamic") if (payload == (InjectPayloadDynamic *)0) {
throw LowlevelError("Mismatch with debug inject XML"); payload = forceDebugDynamic(id);
}
payload->restoreEntry(subel); payload->restoreEntry(subel);
} }
} }

View file

@ -91,6 +91,7 @@ class PcodeInjectLibrarySleigh : public PcodeInjectLibrary {
vector<OpBehavior *> inst; vector<OpBehavior *> inst;
InjectContextSleigh contextCache; InjectContextSleigh contextCache;
int4 registerDynamicInject(InjectPayload *payload); int4 registerDynamicInject(InjectPayload *payload);
InjectPayloadDynamic *forceDebugDynamic(int4 injectid);
void parseInject(InjectPayload *payload); void parseInject(InjectPayload *payload);
protected: protected:
virtual int4 allocateInject(const string &sourceName,const string &name,int4 type); virtual int4 allocateInject(const string &sourceName,const string &name,int4 type);

View file

@ -216,10 +216,10 @@ void SegmentOp::restoreXml(const Element *el)
throw LowlevelError("Bad segment pattern tag: "+subel->getName()); throw LowlevelError("Bad segment pattern tag: "+subel->getName());
} }
if (injectId < 0) if (injectId < 0)
throw LowlevelError("Missing <execute> child in <segmentop> tag"); throw LowlevelError("Missing <pcode> child in <segmentop> tag");
InjectPayload *payload = glb->pcodeinjectlib->getPayload(injectId); InjectPayload *payload = glb->pcodeinjectlib->getPayload(injectId);
if (payload->sizeOutput() != 1) if (payload->sizeOutput() != 1)
throw LowlevelError("<execute> child of <segmentop> tag must declare one <output>"); throw LowlevelError("<pcode> child of <segmentop> tag must declare one <output>");
if (payload->sizeInput() == 1) { if (payload->sizeInput() == 1) {
innerinsize = payload->getInput(0).getSize(); innerinsize = payload->getInput(0).getSize();
} }
@ -228,7 +228,7 @@ void SegmentOp::restoreXml(const Element *el)
innerinsize = payload->getInput(1).getSize(); innerinsize = payload->getInput(1).getSize();
} }
else else
throw LowlevelError("<execute> child of <segmentop> tag must declare one or two <input> tags"); throw LowlevelError("<pcode> child of <segmentop> tag must declare one or two <input> tags");
} }
/// \param g is the Architecture owning this set of jump assist scripts /// \param g is the Architecture owning this set of jump assist scripts

View file

@ -127,6 +127,12 @@ void XmlArchitecture::restoreXml(DocumentStorage &store)
++iter; ++iter;
} }
} }
if (iter != list.end()) {
if ((*iter)->getName() == "specextensions") {
store.registerTag(*iter);
++iter;
}
}
if (iter!=list.end()) { if (iter!=list.end()) {
if ((*iter)->getName() == "coretypes") { if ((*iter)->getName() == "coretypes") {
store.registerTag(*iter); store.registerTag(*iter);

View file

@ -0,0 +1,6 @@
<?xml version='1.0'?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:import href="http://docbook.sourceforge.net/release/xsl/current/profiling/profile.xsl"/>
</xsl:stylesheet>

View file

@ -2,7 +2,7 @@
<xsl:stylesheet <xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:import href="/usr/share/sgml/docbook/xsl-stylesheets/html/chunk.xsl"/> <xsl:import href="http://docbook.sourceforge.net/release/xsl/current/html/chunk.xsl"/>
<xsl:param name="generate.toc"> <xsl:param name="generate.toc">
article/appendix nop article/appendix nop

View file

@ -834,6 +834,33 @@
</para> </para>
</sect3> </sect3>
<sect3 id="ConceptCallother">
<title>User-defined P-code Operations - CALLOTHER</title>
<para>
P-code allows for additional, processor specific, operations referred to
as <emphasis>user-defined</emphasis> or CALLOTHER operations.
These may be defined as part of a Ghidra's specification for the processor and
are typically used as placeholders for what is otherwise unmodeled processor behavior.
Each CALLOTHER must have a unique name, and as a p-code operation, it still takes
varnode inputs and may produce a varnode output. But the exact affect of the operation is
not specified.
</para>
<para>
The decompiler treats a CALLOTHER operation as a black box. It will keep track of data
flowing into and out of the operation but won't simplify or transform it. In decompiler
output, a CALLOTHER is usually displayed using its unique name, with functional syntax
showing its inputs and output.
</para>
<para>
Ghidra or a user can provide the behavior details for a named CALLOTHER operation. The
details are provided as a sequence of p-code operations, referred to as a
<emphasis role="bold">Callother-Fixup</emphasis>, which is substituted for the
CALLOTHER operation during decompilation, or by other Analyzers that use p-code.
Callother-Fixups are applied by Ghidra for specific processor or compiler variants,
and a user can choose to apply them to an individual Program. (See <xref linkend="ExtensionOptions"/>)
</para>
</sect3>
<sect3 id="ConceptInternalFunctions"> <sect3 id="ConceptInternalFunctions">
<title>Internal Decompiler Functions</title> <title>Internal Decompiler Functions</title>
<para> <para>
@ -1162,10 +1189,13 @@
use of multiple models. Subsequently, each distinct model has a name like <code>__stdcall</code> or use of multiple models. Subsequently, each distinct model has a name like <code>__stdcall</code> or
<code>__thiscall</code>. The decompiler makes use of the prototype model, as assigned to the function by the user or <code>__thiscall</code>. The decompiler makes use of the prototype model, as assigned to the function by the user or
discovered in some other way, when performing its analysis of parameters. discovered in some other way, when performing its analysis of parameters.
It is possible for users to extend the set of prototype models available to a Program,
see <xref linkend="ExtensionOptions"/>.
</para> </para>
<para> <para>
A prototype model is typically used as a whole and is assigned by name to individual functions. But some of A prototype model is typically used as a whole and is assigned by name to individual functions. But some of
the sub-concepts of the model may be relevant to reverse engineers. the sub-concepts of the model may be relevant to reverse engineers. Concepts that a prototype
model encapsulates include:
</para> </para>
<sect3 id="ConceptPrototypeStorage"> <sect3 id="ConceptPrototypeStorage">
<title>Incoming and Outgoing Storage Locations</title> <title>Incoming and Outgoing Storage Locations</title>
@ -1211,6 +1241,138 @@
</sect2> </sect2>
</section> </section>
<section id="ConceptSpecification">
<title>SLEIGH Specification Files</title>
<para>
SLEIGH is Ghidra's specification language for describing processor instructions.
Specification files are read in for a Program, and once configured, Ghidra's SLEIGH engine can:
<informalexample>
<itemizedlist mark='bullet' spacing='compact'>
<listitem>
Disassemble machine instructions from the underlying bytes and
</listitem>
<listitem>
Produce the raw p-code consumed by the decompiler and other analyzers.
</listitem>
</itemizedlist>
</informalexample>
</para>
<para>
Specification files are selected based on the <emphasis>Language Id</emphasis>
assigned to the Program at the time it is imported into Ghidra.
(See <link xlink:href="help/topics/ImporterPlugin/importer.htm">Import Program</link>)
<informalexample>
<itemizedlist mark='none' spacing='compact'>
<listitem><code>x86:LE:32:default:windows</code></listitem>
<listitem><code>AARCH64:LE:64:default:v8A:default</code></listitem>
<listitem><code>MIPS:BE:32:micro:default</code></listitem>
</itemizedlist>
</informalexample>
A <emphasis role="bold">Language Id</emphasis> is a label with these 5 formal fields, separated
by a ':' character:
<informalexample>
<itemizedlist spacing='compact'>
<listitem>Processor family</listitem>
<listitem>Endianess</listitem>
<listitem>Size of the address bus</listitem>
<listitem>Process variant</listitem>
<listitem>Compiler producing the Program</listitem>
</itemizedlist>
</informalexample>
A field with the value 'default' indicates either the preferred processor variant or the preferred compiler.
</para>
<para>
Within the Ghidra installation, specification files are stored based on the overarching
processor family, such as 'MIPS' or 'x86'. For a specific family, files are located under
<informalexample>
<code>&lt;Root&gt;/Ghidra/Processors/&lt;Family&gt;/data/languages</code>
</informalexample>
where <code>&lt;Root&gt;</code> represents the root directory of the Ghidra installation and
<code>&lt;Family&gt;</code> is the processor family.
</para>
<para>
There are several types of specification files that are distinguishable by their suffix.
These include:
<informalexample>
<variablelist>
<?dbfo list-presentation="blocks"?>
<varlistentry>
<term><emphasis role="bold">SLEIGH files</emphasis> - *.slaspec or *.sinc</term>
<listitem>
<para>
These are the human readable SLEIGH language files. A single specification is
rooted in one of the <code>*.slaspec</code> files, which may recursively include
one or more <code>*.sinc</code> files. The format of these files is described
in the document "SLEIGH: A Language for Rapid Processor Specification".
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><emphasis role="bold">Compiled SLEIGH files</emphasis> - *.sla</term>
<listitem>
<para>
This is a compiled form of a single SLEIGH specification. It is produced
automatically by Ghidra from the corresponding <code>*.slaspec</code>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><emphasis role="bold">Compiler specification files</emphasis> - *.cspec</term>
<listitem>
<para>
These files contain configuration for a specific compiler. Analysis of Programs whose
executable content was produced using this compiler benefits from this information.
The file is an XML document with tags describing details of data organization and
other conventions used by the compiler. In particular, the compiler specification
contains tags:
</para>
<para>
<itemizedlist mark='none' spacing='compact'>
<listitem>&lt;prototype&gt; - describing a specific calling convention</listitem>
<listitem>&lt;callfixup&gt; - describing a Call-fixup</listitem>
<listitem>&lt;callotherfixup&gt; - describing a Callother-fixup</listitem>
</itemizedlist>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><emphasis role="bold">Processor specification files</emphasis> - *.pspec</term>
<listitem>
<para>
These files contain configuration information that is specific to a particular
processor variant.
</para>
</listitem>
</varlistentry>
</variablelist>
</informalexample>
</para>
<sect2 id="ConceptModifySpec">
<title>Modifying Specification Files</title>
<para>
Changing any of the specification files described here is not recommended.
To make additions to either the <emphasis>compiler specification</emphasis>
or the <emphasis>processor specification</emphasis> files, see
<xref linkend="ExtensionOptions"/>, which describes a safe and portable way
to add specific elements.
</para>
<warning>
Making modifications to specification files within a Ghidra installation is possible,
but any analysis results obtained will likely not be portable to other installations.
In particular, saving a Program from a modified Ghidra and then reopening it using
an unmodified installation may corrupt the Program database.
</warning>
<para>
When Ghidra starts, it checks for changes to <code>*.slaspec</code>
and <code>*.sinc</code> files and will rebuild the corresponding
<code>*.sla</code> file automatically. Also, specification files are read again when
Ghidra restarts. So analysts can and do make changes to these files.
However they need to be prepared to view any results as temporary and
should backup their installation and specific Programs being analyzed.
</para>
</sect2>
</section>
</chapter> </chapter>
<chapter id="DecompilerAnnotations"> <chapter id="DecompilerAnnotations">
@ -2085,7 +2247,8 @@
The calling convention used by the function can be specified as part of the function prototype. The convention The calling convention used by the function can be specified as part of the function prototype. The convention
is specified by name, referring to the formal <xref linkend="ConceptPrototypeModel"/> that describes how storage is specified by name, referring to the formal <xref linkend="ConceptPrototypeModel"/> that describes how storage
locations are selected for individual parameters along with other information about how the compiler treats locations are selected for individual parameters along with other information about how the compiler treats
the function. the function. Available models are determined by the processor and compiler, but can be extended by the user.
See <xref linkend="ExtensionOptions"/>.
</para> </para>
<para> <para>
In the absence of parameter and return value annotations, the decompiler will use the prototype model as In the absence of parameter and return value annotations, the decompiler will use the prototype model as
@ -2152,7 +2315,8 @@
</para> </para>
<para> <para>
Call-fixups are specified by name. The name and associated p-code chunk are typically defined in the Call-fixups are specified by name. The name and associated p-code chunk are typically defined in the
<emphasis>compiler specification</emphasis> for the Program. <emphasis>compiler specification</emphasis> for the Program. Users can extend the available set
of call-fixups. See <xref linkend="ExtensionOptions"/>.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
@ -2453,12 +2617,15 @@
</informalexample> </informalexample>
</para> </para>
<para> <para>
Another source of options can be accessed by selecting the Code Browser menu Options that are specific to the particular Program being analyzed are accessed by
selecting the Code Browser menu
<informalexample> <informalexample>
<emphasis role="bold">Edit -> Options for &lt;Program&gt;</emphasis> <emphasis role="bold">Edit -> Options for &lt;Program&gt;</emphasis>
</informalexample> </informalexample>
and the picking the <emphasis>Decompiler</emphasis> tab. These <xref linkend="ProgramOptions"/> Picking the <emphasis>Decompiler</emphasis> tab shows <xref linkend="ProgramOptions"/>
are specific to the particular Program being analyzed. that only affect the decompiler. Picking the <xref linkend="ExtensionOptions"/> tab
shows a table of the available prototype models, call-fixups, and callother-fixups. These
affect more than just the decompiler but are also documented here.
</para> </para>
</simplesect> </simplesect>
<section id="GeneralOptions"> <section id="GeneralOptions">
@ -2961,7 +3128,8 @@
<section id="ProgramOptions"> <section id="ProgramOptions">
<title>Program Options</title> <title>Program Options</title>
<para> <para>
Changes to these options affect only the current Program being analyzed. Changes to these options affect only the decompiler and only for
the current Program being analyzed.
</para> </para>
<para> <para>
<informalexample> <informalexample>
@ -2982,6 +3150,280 @@
</informalexample> </informalexample>
</para> </para>
</section> </section>
<section id="ExtensionOptions">
<title>Specification Extensions</title>
<para>
This tab displays elements from the Program's <emphasis>compiler specification</emphasis> and
<emphasis>processor specification</emphasis> and allows the user to add or remove
<emphasis role="bold">extensions</emphasis>, including prototype models, call-fixups, and
callother-fixups.
</para>
<para>
Every program has a <emphasis>core</emphasis> set of specification elements,
loaded from the <xref linkend="ConceptSpecification"/>, that cannot
be modified or removed. Extensions, however, can be added to this core specification. Any extension
imported from this dialog is directly associated with the active Program and is stored permanently
with it.
</para>
<para>
Users can change or reimport an extension, if new information points to a better definition.
Users have full control over an extension, and unlike a core element, can tailor it specifically
to the Program.
</para>
<para>
This options tab presents a table of all specification elements.
Each element, whether core or an extension, is displayed on a separate row with three columns:
<informalexample>
<itemizedlist mark='none' spacing='compact'>
<listitem><emphasis role="bold">Extension Type</emphasis> - indicating the type of element</listitem>
<listitem><emphasis role="bold">Name</emphasis> - showing the formal name of the element</listitem>
<listitem><emphasis role="bold">Status</emphasis> - indicating whether the element is core or an extension</listitem>
</itemizedlist>
</informalexample>
The core elements of the specification have a blank Status column, and any extension
is labeled either as "extension" or "override".
</para>
<sect2 id="ExtendTypes">
<title>Extension Types</title>
<para>
Each of the element types described here represents an XML tag of the same name, which, if
present in the table, must either be in the <emphasis>compiler specification</emphasis> file,
the <emphasis>processor specification</emphasis> file, or provided to Ghidra as an
import document.
<informalexample>
<variablelist>
<?dbfo list-presentation="blocks"?>
<varlistentry>
<term><emphasis role="bold">prototype</emphasis></term>
<listitem>
<para>
This element is a <xref linkend="ConceptPrototypeModel"/> that holds a specific named set
of parameter passing details. It
can be applied to individual functions by name, typically via the "Calling Convention" menu
in the <link xlink:href="help/topics/FunctionPlugin/Variables.htm#Edit_Function">Function Editor Dialog</link>.
See the documentation on <xref linkend="AnnotePrototype"/> for how they affect decompilation.
</para>
<para>
The XML tag, <code>&lt;prototype&gt;</code> always has a <emphasis role="bold">name</emphasis> attribute
that defines the formal name of the prototype model, which must be unique across all models.
<programlisting>
<![CDATA[<prototype name="__stdcall" extrapop="unknown" stackshift="4">
<input>
<pentry minsize="1" maxsize="500" align="4">
<addr offset="4" space="stack"/>
</pentry>
</input>
<output>
...]]>
</programlisting>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><emphasis role="bold">callfixup</emphasis></term>
<listitem>
<para>
This element is a Call-fixup, which can be used to substitute a specific p-code
sequence for CALL instructions during decompilation, as described in
<xref linkend="AnnotePrototype"/>.
</para>
<para>
The <code>&lt;callfixup&gt;</code> tag has a <emphasis role="bold">name</emphasis>
attribute listing the formal name, which must be unique across all call-fixups.
<programlisting>
<![CDATA[<callfixup name="EH_prolog3">
<pcode>
<body><![CDATA<
EBP = ESP + 4;
tmp = * EBP;
ESP = ESP - tmp;
ESP = ESP - 24;
]]>]]&gt;<![CDATA[</body>
</pcode>
</callfixup>]]>
</programlisting>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><emphasis role="bold">callotherfixup</emphasis></term>
<listitem>
<para>
This element is a Callother-fixup, which can be used to substitute a specific p-code
sequence for CALLOTHER p-code operations. A CALLOTHER
is a black-box, or unspecified p-code operation, see <xref linkend="ConceptCallother"/>.
</para>
<para>
The <code>&lt;callotherfixup&gt;</code> tag has a
<emphasis role="bold">targetop</emphasis> attribute which lists the
name of the particular CALLOTHER operation it substitutes for.
<programlisting>
<![CDATA[<callotherfixup targetop="dynamicPush">
<pcode>
<input name="amount"/>
<body><![CDATA[
RSP = RSP + amount;
]]>]]&gt;<![CDATA[</body>
</pcode>
</callotherfixup>]]>
</programlisting>
</para>
</listitem>
</varlistentry>
</variablelist>
</informalexample>
</para>
</sect2>
<sect2 id="ExtendStatus">
<title>Status</title>
<para>
The Status column labels an element as either a core specification
or an extension; it also gives an indication of whether the element
is about to be installed or removed.
</para>
<para>
With no changes pending, the column will show one of the three main values:
<informalexample>
<variablelist>
<?dbfo list-presentation="blocks"?>
<varlistentry>
<term><emphasis>&lt;blank&gt;</emphasis></term>
<listitem>
<para>
A blank Status column indicates that the element is a core part of the
specification, originating from one of the <emphasis>specification files</emphasis>.
These elements cannot be changed or removed.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><emphasis role="bold">extension</emphasis></term>
<listitem>
<para>
Indicates that the element is a program specific extension that has been
added to the specification.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><emphasis role="bold">override</emphasis></term>
<listitem>
<para>
Indicates that the element, which must be a <emphasis role="bold">callotherfixup</emphasis>,
is an extension that overrides a core element with the same target. The extension
effectively replaces the p-code injection of the core element with a user supplied one.
If this type of extension is later removed, the core element becomes active again.
</para>
</listitem>
</varlistentry>
</variablelist>
</informalexample>
</para>
<para>
If the user has either imported additional extensions or selected an extension for removal but
has not yet clicked the <emphasis>Apply</emphasis> button in the Options dialog, the Status column
may show one of the following values, indicating a pending change.
<informalexample>
<variablelist>
<?dbfo list-presentation="blocks"?>
<varlistentry>
<term><emphasis role="bold">install</emphasis></term>
<listitem>
<para>
Indicates a new extension that will be installed.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><emphasis role="bold">remove</emphasis></term>
<listitem>
<para>
Indicates an extension that is about to be removed.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><emphasis role="bold">replace</emphasis></term>
<listitem>
<para>
Indicates a new extension that will replace a current
extension with the same name.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><emphasis role="bold">override pending</emphasis></term>
<listitem>
<para>
Indicates a new extension that will override a core element when
it is installed.
</para>
</listitem>
</varlistentry>
</variablelist>
</informalexample>
</para>
</sect2>
<sect2 id="ExtendImport">
<title>Importing a New Extension</title>
<para>
The <emphasis>Import</emphasis> button at the bottom of the
"Specification Extensions" pane allows the user to import one of the
three element types, <emphasis role="bold">prototype</emphasis>,
<emphasis role="bold">callfixup</emphasis>, or <emphasis role="bold">callotherfixup</emphasis>,
into the program as a new extension.
The user must supply a properly formed XML document, as a file, that fully describes the new
extension. Clicking the <emphasis>Import</emphasis> button brings up a File Chooser dialog,
from which the user must select their prepared XML file. Once <emphasis>Ok</emphasis> is
clicked, the file is read in and validated. If there are any problems with the validation, or if
the new extension's name collides with a core element, the import does not succeed and
an error message will be displayed. Otherwise, the import is accepted, and the table is updated
to indicate the pending change.
</para>
<para>
The final change to the program, installing the new extension, will not happen until the
<emphasis>Apply</emphasis> button, at the bottom of the Options dialog, is clicked.
</para>
<para>
The XML file describing the extension <emphasis>must</emphasis> have one of the tags,
<code>&lt;prototype&gt;</code>, <code>&lt;callfixup&gt;</code>, or <code>&lt;callotherfixup&gt;</code>,
as its single root element. Users can find numerous examples within the compiler
and processor specification files that come as part of Ghidra's installation.
See <xref linkend="ConceptSpecification"/>.
</para>
<para>
In the case of <emphasis role="bold">prototype</emphasis> and <emphasis role="bold">callfixup</emphasis>
elements, extensions cannot replace existing core elements, so the new extension <emphasis>must not</emphasis>
have a name that matches an existing core element. If a new <emphasis role="bold">callotherfixup</emphasis>
extension has a targetop that matches a core element, the extension is automatically treated as an override.
</para>
<para>
Existing extensions can be replaced simply by importing a new extension with the same name or targetop.
</para>
</sect2>
<sect2 id="ExtendRemove">
<title>Removing an Extension</title>
<para>
The <emphasis>Remove</emphasis> button at the bottom of the "Specification Extensions" pane allows
the user to remove a previously installed extension. A row from the table is selected first, which
must have a Status of <emphasis role="bold">extension</emphasis> or <emphasis role="bold">override</emphasis>.
Core elements of the specification cannot be removed.
Clicking the <emphasis>Remove</emphasis> button brings up a confirmation dialog, and if
<emphasis>Ok</emphasis> is clicked, the selected extension is marked for removal. The Status of the row
changes to <emphasis role="bold">remove</emphasis>, reflecting this.
</para>
<para>
The final change to the program, removing the extension, will not happen until the
<emphasis>Apply</emphasis> button, at the bottom of the Options dialog, is clicked.
</para>
<para>
If a <emphasis role="bold">prototype</emphasis> or <emphasis role="bold">callfixup</emphasis> is removed,
all functions are checked to see if they have the matching calling convention or call-fixup set.
A function with matching calling convention is changed to have the <emphasis>default</emphasis> convention, which is always a core element.
A function with matching call-fixup is changed to have no call-fixup.
</para>
</sect2>
</section>
</chapter> </chapter>

View file

@ -2,7 +2,7 @@
<xsl:stylesheet <xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:import href="/usr/share/sgml/docbook/xsl-stylesheets/html/chunk.xsl"/> <xsl:import href="http://docbook.sourceforge.net/release/xsl/current/html/chunk.xsl"/>
<xsl:include href="decompileplugin_common.xsl" /> <xsl:include href="decompileplugin_common.xsl" />

View file

@ -2,7 +2,7 @@
<xsl:stylesheet <xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:import href="/usr/share/sgml/docbook/xsl-stylesheets/fo/docbook.xsl"/> <xsl:import href="http://docbook.sourceforge.net/release/xsl/current/fo/docbook.xsl"/>
<xsl:include href="decompileplugin_common.xsl" /> <xsl:include href="decompileplugin_common.xsl" />
@ -16,6 +16,6 @@
<xsl:param name="admon.textlabel" select="0"/> <!-- Don't display title for important/note tags --> <xsl:param name="admon.textlabel" select="0"/> <!-- Don't display title for important/note tags -->
<xsl:param name="admon.graphics.path" select="'./images/'"/> <xsl:param name="admon.graphics.path" select="'../../../build/images'"/>
</xsl:stylesheet> </xsl:stylesheet>

View file

@ -2,7 +2,7 @@
<xsl:stylesheet <xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:import href="/usr/share/sgml/docbook/xsl-stylesheets/html/docbook.xsl"/> <xsl:import href="http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl"/>
<xsl:param name="generate.toc"> <xsl:param name="generate.toc">
article/appendix nop article/appendix nop

View file

@ -2,7 +2,7 @@
<xsl:stylesheet <xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:import href="/usr/share/sgml/docbook/xsl-stylesheets/html/chunk.xsl"/> <xsl:import href="http://docbook.sourceforge.net/release/xsl/current/html/chunk.xsl"/>
<xsl:include href="pcoderef_common.xsl" /> <xsl:include href="pcoderef_common.xsl" />

View file

@ -2,7 +2,7 @@
<xsl:stylesheet <xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:import href="/usr/share/sgml/docbook/xsl-stylesheets/fo/docbook.xsl"/> <xsl:import href="http://docbook.sourceforge.net/release/xsl/current/fo/docbook.xsl"/>
<xsl:template match="table" mode="label.markup"/> <xsl:template match="table" mode="label.markup"/>

View file

@ -2,7 +2,7 @@
<xsl:stylesheet <xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:import href="/usr/share/sgml/docbook/xsl-stylesheets/html/chunk.xsl"/> <xsl:import href="http://docbook.sourceforge.net/release/xsl/current/html/chunk.xsl"/>
<xsl:include href="sleigh_common.xsl" /> <xsl:include href="sleigh_common.xsl" />

View file

@ -2,7 +2,7 @@
<xsl:stylesheet <xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:import href="/usr/share/sgml/docbook/xsl-stylesheets/fo/docbook.xsl"/> <xsl:import href="http://docbook.sourceforge.net/release/xsl/current/fo/docbook.xsl"/>
<xsl:include href="sleigh_common.xsl" /> <xsl:include href="sleigh_common.xsl" />

View file

@ -62,6 +62,7 @@
target="help/topics/DecompilePlugin/DecompilerConcepts.html"> target="help/topics/DecompilePlugin/DecompilerConcepts.html">
<tocdef id="ConceptPcode" sortgroup="a" text="P-code" target="help/topics/DecompilePlugin/DecompilerConcepts.html#ConceptPcode"/> <tocdef id="ConceptPcode" sortgroup="a" text="P-code" target="help/topics/DecompilePlugin/DecompilerConcepts.html#ConceptPcode"/>
<tocdef id="ConceptHighFunction" sortgroup="b" text="The HighFunction" target="help/topics/DecompilePlugin/DecompilerConcepts.html#ConceptHighFunction"/> <tocdef id="ConceptHighFunction" sortgroup="b" text="The HighFunction" target="help/topics/DecompilePlugin/DecompilerConcepts.html#ConceptHighFunction"/>
<tocdef id="ConceptSpecification" sortgroup="c" text="SLEIGH Specification Files" target="help/topics/DecompilePlugin/DecompilerConcepts.html#ConceptSpecification"/>
</tocdef> </tocdef>
<tocdef id="Program Annotations Affecting the Decompiler" <tocdef id="Program Annotations Affecting the Decompiler"
sortgroup="b" sortgroup="b"
@ -83,6 +84,7 @@
<tocdef id="AnalysisOptions" sortgroup="b" text="Analysis Options" target="help/topics/DecompilePlugin/DecompilerOptions.html#AnalysisOptions"/> <tocdef id="AnalysisOptions" sortgroup="b" text="Analysis Options" target="help/topics/DecompilePlugin/DecompilerOptions.html#AnalysisOptions"/>
<tocdef id="DisplayOptions" sortgroup="c" text="Display Options" target="help/topics/DecompilePlugin/DecompilerOptions.html#DisplayOptions"/> <tocdef id="DisplayOptions" sortgroup="c" text="Display Options" target="help/topics/DecompilePlugin/DecompilerOptions.html#DisplayOptions"/>
<tocdef id="ProgramOptions" sortgroup="d" text="Program Options" target="help/topics/DecompilePlugin/DecompilerOptions.html#ProgramOptions"/> <tocdef id="ProgramOptions" sortgroup="d" text="Program Options" target="help/topics/DecompilePlugin/DecompilerOptions.html#ProgramOptions"/>
<tocdef id="ExtensionOptions" sortgroup="e" text="Specification Extensions" target="help/topics/DecompilePlugin/DecompilerOptions.html#ExtensionOptions"/>
</tocdef> </tocdef>
<tocdef id="Decompiler Window" <tocdef id="Decompiler Window"
sortgroup="d" sortgroup="d"
@ -91,9 +93,10 @@
<tocdef id="DecompilerDisplay" sortgroup="a" text="Display" target="help/topics/DecompilePlugin/DecompilerWindow.html#DecompilerDisplay"/> <tocdef id="DecompilerDisplay" sortgroup="a" text="Display" target="help/topics/DecompilePlugin/DecompilerWindow.html#DecompilerDisplay"/>
<tocdef id="MainWindow" sortgroup="b" text="Main Window" target="help/topics/DecompilePlugin/DecompilerWindow.html#MainWindow"/> <tocdef id="MainWindow" sortgroup="b" text="Main Window" target="help/topics/DecompilePlugin/DecompilerWindow.html#MainWindow"/>
<tocdef id="Snapshot" sortgroup="c" text="Snapshot Windows" target="help/topics/DecompilePlugin/DecompilerWindow.html#Snapshot"/> <tocdef id="Snapshot" sortgroup="c" text="Snapshot Windows" target="help/topics/DecompilePlugin/DecompilerWindow.html#Snapshot"/>
<tocdef id="ToolBar" sortgroup="d" text="Tool Bar" target="help/topics/DecompilePlugin/DecompilerWindow.html#ToolBar"/> <tocdef id="UndefinedFunction" sortgroup="d" text="Undefined Functions" target="help/topics/DecompilePlugin/DecompilerWindow.html#UndefinedFunction"/>
<tocdef id="MouseActions" sortgroup="e" text="Mouse Actions" target="help/topics/DecompilePlugin/DecompilerWindow.html#MouseActions"/> <tocdef id="ToolBar" sortgroup="e" text="Tool Bar" target="help/topics/DecompilePlugin/DecompilerWindow.html#ToolBar"/>
<tocdef id="MenuActions" sortgroup="f" text="Pop-up Menu Actions" target="help/topics/DecompilePlugin/DecompilerWindow.html#MenuActions"/> <tocdef id="MouseActions" sortgroup="f" text="Mouse Actions" target="help/topics/DecompilePlugin/DecompilerWindow.html#MouseActions"/>
<tocdef id="MenuActions" sortgroup="g" text="Pop-up Menu Actions" target="help/topics/DecompilePlugin/DecompilerWindow.html#MenuActions"/>
</tocdef> </tocdef>
</tocdef> </tocdef>
</tocref> </tocref>

View file

@ -4,7 +4,7 @@
<title>Program Annotations Affecting the Decompiler</title> <title>Program Annotations Affecting the Decompiler</title>
<link rel="stylesheet" type="text/css" href="../../shared/Frontpage.css"> <link rel="stylesheet" type="text/css" href="../../shared/Frontpage.css">
<link rel="stylesheet" type="text/css" href="../../shared/languages.css"> <link rel="stylesheet" type="text/css" href="../../shared/languages.css">
<meta name="generator" content="DocBook XSL Stylesheets V1.78.1"> <meta name="generator" content="DocBook XSL Stylesheets V1.79.1">
<link rel="home" href="Decompiler.html" title="Decompiler"> <link rel="home" href="Decompiler.html" title="Decompiler">
<link rel="up" href="Decompiler.html" title="Decompiler"> <link rel="up" href="Decompiler.html" title="Decompiler">
<link rel="prev" href="DecompilerConcepts.html" title="Decompiler Concepts"> <link rel="prev" href="DecompilerConcepts.html" title="Decompiler Concepts">
@ -983,7 +983,8 @@
The calling convention used by the function can be specified as part of the function prototype. The convention The calling convention used by the function can be specified as part of the function prototype. The convention
is specified by name, referring to the formal <a class="xref" href="DecompilerConcepts.html#ConceptPrototypeModel" title="Prototype Model">&#8220;Prototype Model&#8221;</a> that describes how storage is specified by name, referring to the formal <a class="xref" href="DecompilerConcepts.html#ConceptPrototypeModel" title="Prototype Model">&#8220;Prototype Model&#8221;</a> that describes how storage
locations are selected for individual parameters along with other information about how the compiler treats locations are selected for individual parameters along with other information about how the compiler treats
the function. the function. Available models are determined by the processor and compiler, but can be extended by the user.
See <a class="xref" href="DecompilerOptions.html#ExtensionOptions" title="Specification Extensions">&#8220;Specification Extensions&#8221;</a>.
</p> </p>
<p> <p>
In the absence of parameter and return value annotations, the decompiler will use the prototype model as In the absence of parameter and return value annotations, the decompiler will use the prototype model as
@ -1042,7 +1043,8 @@
</p> </p>
<p> <p>
Call-fixups are specified by name. The name and associated p-code chunk are typically defined in the Call-fixups are specified by name. The name and associated p-code chunk are typically defined in the
<span class="emphasis"><em>compiler specification</em></span> for the Program. <span class="emphasis"><em>compiler specification</em></span> for the Program. Users can extend the available set
of call-fixups. See <a class="xref" href="DecompilerOptions.html#ExtensionOptions" title="Specification Extensions">&#8220;Specification Extensions&#8221;</a>.
</p> </p>
</dd> </dd>
</dl></div> </dl></div>

View file

@ -4,7 +4,7 @@
<title>Decompiler Concepts</title> <title>Decompiler Concepts</title>
<link rel="stylesheet" type="text/css" href="../../shared/Frontpage.css"> <link rel="stylesheet" type="text/css" href="../../shared/Frontpage.css">
<link rel="stylesheet" type="text/css" href="../../shared/languages.css"> <link rel="stylesheet" type="text/css" href="../../shared/languages.css">
<meta name="generator" content="DocBook XSL Stylesheets V1.78.1"> <meta name="generator" content="DocBook XSL Stylesheets V1.79.1">
<link rel="home" href="Decompiler.html" title="Decompiler"> <link rel="home" href="Decompiler.html" title="Decompiler">
<link rel="up" href="Decompiler.html" title="Decompiler"> <link rel="up" href="Decompiler.html" title="Decompiler">
<link rel="prev" href="DecompilerIntro.html" title="Decompiler"> <link rel="prev" href="DecompilerIntro.html" title="Decompiler">
@ -183,7 +183,7 @@
<div class="informalexample"> <div class="informalexample">
<div class="table"> <div class="table">
<a name="ops.htmltable"></a><p class="title"><b>Table . P-code Operations</b></p> <a name="ops.htmltable"></a><p class="title"><b>Table . P-code Operations</b></p>
<div class="table-contents"><table width="90%" frame="box" rules="all"> <div class="table-contents"><table width="90%" frame="box" rules="all" id="ops.htmltable">
<col width="40%"> <col width="40%">
<col width="60%"> <col width="60%">
@ -283,7 +283,7 @@
</p> </p>
<div class="informalexample"> <div class="informalexample">
<div class="table"> <div class="table">
<a name="ref.htmltable"></a><table width="90%" frame="box" rules="rows"> <a name="ref.htmltable"></a><table width="90%" frame="box" rules="rows" id="ref.htmltable">
<col width="25%"> <col width="25%">
<col width="25%"> <col width="25%">
<col width="50%"> <col width="50%">
@ -725,6 +725,35 @@
<div class="sect3"> <div class="sect3">
<div class="titlepage"><div><div><h4 class="title"> <div class="titlepage"><div><div><h4 class="title">
<a name="ConceptCallother"></a>User-defined P-code Operations - CALLOTHER</h4></div></div></div>
<p>
P-code allows for additional, processor specific, operations referred to
as <span class="emphasis"><em>user-defined</em></span> or CALLOTHER operations.
These may be defined as part of a Ghidra's specification for the processor and
are typically used as placeholders for what is otherwise unmodeled processor behavior.
Each CALLOTHER must have a unique name, and as a p-code operation, it still takes
varnode inputs and may produce a varnode output. But the exact affect of the operation is
not specified.
</p>
<p>
The decompiler treats a CALLOTHER operation as a black box. It will keep track of data
flowing into and out of the operation but won't simplify or transform it. In decompiler
output, a CALLOTHER is usually displayed using its unique name, with functional syntax
showing its inputs and output.
</p>
<p>
Ghidra or a user can provide the behavior details for a named CALLOTHER operation. The
details are provided as a sequence of p-code operations, referred to as a
<span class="bold"><strong>Callother-Fixup</strong></span>, which is substituted for the
CALLOTHER operation during decompilation, or by other Analyzers that use p-code.
Callother-Fixups are applied by Ghidra for specific processor or compiler variants,
and a user can choose to apply them to an individual Program. (See <a class="xref" href="DecompilerOptions.html#ExtensionOptions" title="Specification Extensions">&#8220;Specification Extensions&#8221;</a>)
</p>
</div>
<div class="sect3">
<div class="titlepage"><div><div><h4 class="title">
<a name="ConceptInternalFunctions"></a>Internal Decompiler Functions</h4></div></div></div> <a name="ConceptInternalFunctions"></a>Internal Decompiler Functions</h4></div></div></div>
<p> <p>
@ -1052,10 +1081,13 @@
use of multiple models. Subsequently, each distinct model has a name like <code class="code">__stdcall</code> or use of multiple models. Subsequently, each distinct model has a name like <code class="code">__stdcall</code> or
<code class="code">__thiscall</code>. The decompiler makes use of the prototype model, as assigned to the function by the user or <code class="code">__thiscall</code>. The decompiler makes use of the prototype model, as assigned to the function by the user or
discovered in some other way, when performing its analysis of parameters. discovered in some other way, when performing its analysis of parameters.
It is possible for users to extend the set of prototype models available to a Program,
see <a class="xref" href="DecompilerOptions.html#ExtensionOptions" title="Specification Extensions">&#8220;Specification Extensions&#8221;</a>.
</p> </p>
<p> <p>
A prototype model is typically used as a whole and is assigned by name to individual functions. But some of A prototype model is typically used as a whole and is assigned by name to individual functions. But some of
the sub-concepts of the model may be relevant to reverse engineers. the sub-concepts of the model may be relevant to reverse engineers. Concepts that a prototype
model encapsulates include:
</p> </p>
<div class="sect3"> <div class="sect3">
<div class="titlepage"><div><div><h4 class="title"> <div class="titlepage"><div><div><h4 class="title">
@ -1107,5 +1139,150 @@
</div> </div>
</div> </div>
<div class="section">
<div class="titlepage"><div><div><h2 class="title" style="clear: both">
<a name="ConceptSpecification"></a>SLEIGH Specification Files</h2></div></div></div>
<p>
SLEIGH is Ghidra's specification language for describing processor instructions.
Specification files are read in for a Program, and once configured, Ghidra's SLEIGH engine can:
</p>
<div class="informalexample">
<div class="itemizedlist"><ul class="itemizedlist compact" style="list-style-type: bullet; ">
<li class="listitem" style="list-style-type: disc">
Disassemble machine instructions from the underlying bytes and
</li>
<li class="listitem" style="list-style-type: disc">
Produce the raw p-code consumed by the decompiler and other analyzers.
</li>
</ul></div>
</div>
<p>
</p>
<p>
Specification files are selected based on the <span class="emphasis"><em>Language Id</em></span>
assigned to the Program at the time it is imported into Ghidra.
(See <a class="ulink" href="help/topics/ImporterPlugin/importer.htm" target="_top">Import Program</a>)
</p>
<div class="informalexample">
<div class="itemizedlist"><ul class="itemizedlist compact" style="list-style-type: none; ">
<li class="listitem" style="list-style-type: none"><code class="code">x86:LE:32:default:windows</code></li>
<li class="listitem" style="list-style-type: none"><code class="code">AARCH64:LE:64:default:v8A:default</code></li>
<li class="listitem" style="list-style-type: none"><code class="code">MIPS:BE:32:micro:default</code></li>
</ul></div>
</div>
<p>
A <span class="bold"><strong>Language Id</strong></span> is a label with these 5 formal fields, separated
by a ':' character:
</p>
<div class="informalexample">
<div class="itemizedlist"><ul class="itemizedlist compact" style="list-style-type: disc; ">
<li class="listitem">Processor family</li>
<li class="listitem">Endianess</li>
<li class="listitem">Size of the address bus</li>
<li class="listitem">Process variant</li>
<li class="listitem">Compiler producing the Program</li>
</ul></div>
</div>
<p>
A field with the value 'default' indicates either the preferred processor variant or the preferred compiler.
</p>
<p>
Within the Ghidra installation, specification files are stored based on the overarching
processor family, such as 'MIPS' or 'x86'. For a specific family, files are located under
</p>
<div class="informalexample">
<code class="code">&lt;Root&gt;/Ghidra/Processors/&lt;Family&gt;/data/languages</code>
</div>
<p>
where <code class="code">&lt;Root&gt;</code> represents the root directory of the Ghidra installation and
<code class="code">&lt;Family&gt;</code> is the processor family.
</p>
<p>
There are several types of specification files that are distinguishable by their suffix.
These include:
</p>
<div class="informalexample">
<div class="variablelist"><dl class="variablelist">
<dt><span class="term"><span class="bold"><strong>SLEIGH files</strong></span> - *.slaspec or *.sinc</span></dt>
<dd>
<p>
These are the human readable SLEIGH language files. A single specification is
rooted in one of the <code class="code">*.slaspec</code> files, which may recursively include
one or more <code class="code">*.sinc</code> files. The format of these files is described
in the document "SLEIGH: A Language for Rapid Processor Specification".
</p>
</dd>
<dt><span class="term"><span class="bold"><strong>Compiled SLEIGH files</strong></span> - *.sla</span></dt>
<dd>
<p>
This is a compiled form of a single SLEIGH specification. It is produced
automatically by Ghidra from the corresponding <code class="code">*.slaspec</code>.
</p>
</dd>
<dt><span class="term"><span class="bold"><strong>Compiler specification files</strong></span> - *.cspec</span></dt>
<dd>
<p>
These files contain configuration for a specific compiler. Analysis of Programs whose
executable content was produced using this compiler benefits from this information.
The file is an XML document with tags describing details of data organization and
other conventions used by the compiler. In particular, the compiler specification
contains tags:
</p>
<p>
</p>
<div class="itemizedlist"><ul class="itemizedlist compact" style="list-style-type: none; ">
<li class="listitem" style="list-style-type: none">&lt;prototype&gt; - describing a specific calling convention</li>
<li class="listitem" style="list-style-type: none">&lt;callfixup&gt; - describing a Call-fixup</li>
<li class="listitem" style="list-style-type: none">&lt;callotherfixup&gt; - describing a Callother-fixup</li>
</ul></div>
<p>
</p>
</dd>
<dt><span class="term"><span class="bold"><strong>Processor specification files</strong></span> - *.pspec</span></dt>
<dd>
<p>
These files contain configuration information that is specific to a particular
processor variant.
</p>
</dd>
</dl></div>
</div>
<p>
</p>
<div class="sect2">
<div class="titlepage"><div><div><h3 class="title">
<a name="ConceptModifySpec"></a>Modifying Specification Files</h3></div></div></div>
<p>
Changing any of the specification files described here is not recommended.
To make additions to either the <span class="emphasis"><em>compiler specification</em></span>
or the <span class="emphasis"><em>processor specification</em></span> files, see
<a class="xref" href="DecompilerOptions.html#ExtensionOptions" title="Specification Extensions">&#8220;Specification Extensions&#8221;</a>, which describes a safe and portable way
to add specific elements.
</p>
<div class="warning" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Warning">
<tr>
<td rowspan="2" align="center" valign="top" width="25"><img alt="[Warning]" src="../../shared/warning.png"></td>
<th align="left"></th>
</tr>
<tr><td align="left" valign="top">
Making modifications to specification files within a Ghidra installation is possible,
but any analysis results obtained will likely not be portable to other installations.
In particular, saving a Program from a modified Ghidra and then reopening it using
an unmodified installation may corrupt the Program database.
</td></tr>
</table></div>
<p>
When Ghidra starts, it checks for changes to <code class="code">*.slaspec</code>
and <code class="code">*.sinc</code> files and will rebuild the corresponding
<code class="code">*.sla</code> file automatically. Also, specification files are read again when
Ghidra restarts. So analysts can and do make changes to these files.
However they need to be prepared to view any results as temporary and
should backup their installation and specific Programs being analyzed.
</p>
</div>
</div>
</div></body> </div></body>
</html> </html>

View file

@ -4,7 +4,7 @@
<title>Decompiler</title> <title>Decompiler</title>
<link rel="stylesheet" type="text/css" href="../../shared/Frontpage.css"> <link rel="stylesheet" type="text/css" href="../../shared/Frontpage.css">
<link rel="stylesheet" type="text/css" href="../../shared/languages.css"> <link rel="stylesheet" type="text/css" href="../../shared/languages.css">
<meta name="generator" content="DocBook XSL Stylesheets V1.78.1"> <meta name="generator" content="DocBook XSL Stylesheets V1.79.1">
<link rel="home" href="Decompiler.html" title="Decompiler"> <link rel="home" href="Decompiler.html" title="Decompiler">
<link rel="up" href="Decompiler.html" title="Decompiler"> <link rel="up" href="Decompiler.html" title="Decompiler">
<link rel="prev" href="Decompiler.html" title="Decompiler"> <link rel="prev" href="Decompiler.html" title="Decompiler">

View file

@ -4,7 +4,7 @@
<title>Decompiler Options</title> <title>Decompiler Options</title>
<link rel="stylesheet" type="text/css" href="../../shared/Frontpage.css"> <link rel="stylesheet" type="text/css" href="../../shared/Frontpage.css">
<link rel="stylesheet" type="text/css" href="../../shared/languages.css"> <link rel="stylesheet" type="text/css" href="../../shared/languages.css">
<meta name="generator" content="DocBook XSL Stylesheets V1.78.1"> <meta name="generator" content="DocBook XSL Stylesheets V1.79.1">
<link rel="home" href="Decompiler.html" title="Decompiler"> <link rel="home" href="Decompiler.html" title="Decompiler">
<link rel="up" href="Decompiler.html" title="Decompiler"> <link rel="up" href="Decompiler.html" title="Decompiler">
<link rel="prev" href="DecompilerAnnotations.html" title="Program Annotations Affecting the Decompiler"> <link rel="prev" href="DecompilerAnnotations.html" title="Program Annotations Affecting the Decompiler">
@ -42,14 +42,17 @@
<p> <p>
</p> </p>
<p> <p>
Another source of options can be accessed by selecting the Code Browser menu Options that are specific to the particular Program being analyzed are accessed by
selecting the Code Browser menu
</p> </p>
<div class="informalexample"> <div class="informalexample">
<span class="bold"><strong>Edit -&gt; Options for &lt;Program&gt;</strong></span> <span class="bold"><strong>Edit -&gt; Options for &lt;Program&gt;</strong></span>
</div> </div>
<p> <p>
and the picking the <span class="emphasis"><em>Decompiler</em></span> tab. These <a class="xref" href="DecompilerOptions.html#ProgramOptions" title="Program Options">&#8220;Program Options&#8221;</a> Picking the <span class="emphasis"><em>Decompiler</em></span> tab shows <a class="xref" href="DecompilerOptions.html#ProgramOptions" title="Program Options">&#8220;Program Options&#8221;</a>
are specific to the particular Program being analyzed. that only affect the decompiler. Picking the <a class="xref" href="DecompilerOptions.html#ExtensionOptions" title="Specification Extensions">&#8220;Specification Extensions&#8221;</a> tab
shows a table of the available prototype models, call-fixups, and callother-fixups. These
affect more than just the decompiler but are also documented here.
</p> </p>
</div> </div>
<div class="section"> <div class="section">
@ -573,7 +576,8 @@
<a name="ProgramOptions"></a>Program Options</h2></div></div></div> <a name="ProgramOptions"></a>Program Options</h2></div></div></div>
<p> <p>
Changes to these options affect only the current Program being analyzed. Changes to these options affect only the decompiler and only for
the current Program being analyzed.
</p> </p>
<p> <p>
</p> </p>
@ -595,6 +599,284 @@
<p> <p>
</p> </p>
</div> </div>
<div class="section">
<div class="titlepage"><div><div><h2 class="title" style="clear: both">
<a name="ExtensionOptions"></a>Specification Extensions</h2></div></div></div>
<p>
This tab displays elements from the Program's <span class="emphasis"><em>compiler specification</em></span> and
<span class="emphasis"><em>processor specification</em></span> and allows the user to add or remove
<span class="bold"><strong>extensions</strong></span>, including prototype models, call-fixups, and
callother-fixups.
</p>
<p>
Every program has a <span class="emphasis"><em>core</em></span> set of specification elements,
loaded from the <a class="xref" href="DecompilerConcepts.html#ConceptSpecification" title="SLEIGH Specification Files">&#8220;SLEIGH Specification Files&#8221;</a>, that cannot
be modified or removed. Extensions, however, can be added to this core specification. Any extension
imported from this dialog is directly associated with the active Program and is stored permanently
with it.
</p>
<p>
Users can change or reimport an extension, if new information points to a better definition.
Users have full control over an extension, and unlike a core element, can tailor it specifically
to the Program.
</p>
<p>
This options tab presents a table of all specification elements.
Each element, whether core or an extension, is displayed on a separate row with three columns:
</p>
<div class="informalexample">
<div class="itemizedlist"><ul class="itemizedlist compact" style="list-style-type: none; ">
<li class="listitem" style="list-style-type: none">
<span class="bold"><strong>Extension Type</strong></span> - indicating the type of element</li>
<li class="listitem" style="list-style-type: none">
<span class="bold"><strong>Name</strong></span> - showing the formal name of the element</li>
<li class="listitem" style="list-style-type: none">
<span class="bold"><strong>Status</strong></span> - indicating whether the element is core or an extension</li>
</ul></div>
</div>
<p>
The core elements of the specification have a blank Status column, and any extension
is labeled either as "extension" or "override".
</p>
<div class="sect2">
<div class="titlepage"><div><div><h3 class="title">
<a name="ExtendTypes"></a>Extension Types</h3></div></div></div>
<p>
Each of the element types described here represents an XML tag of the same name, which, if
present in the table, must either be in the <span class="emphasis"><em>compiler specification</em></span> file,
the <span class="emphasis"><em>processor specification</em></span> file, or provided to Ghidra as an
import document.
</p>
<div class="informalexample">
<div class="variablelist"><dl class="variablelist">
<dt><span class="term"><span class="bold"><strong>prototype</strong></span></span></dt>
<dd>
<p>
This element is a <a class="xref" href="DecompilerConcepts.html#ConceptPrototypeModel" title="Prototype Model">&#8220;Prototype Model&#8221;</a> that holds a specific named set
of parameter passing details. It
can be applied to individual functions by name, typically via the "Calling Convention" menu
in the <a class="ulink" href="help/topics/FunctionPlugin/Variables.htm#Edit_Function" target="_top">Function Editor Dialog</a>.
See the documentation on <a class="xref" href="DecompilerAnnotations.html#AnnotePrototype" title="Function Prototypes">&#8220;Function Prototypes&#8221;</a> for how they affect decompilation.
</p>
<p>
The XML tag, <code class="code">&lt;prototype&gt;</code> always has a <span class="bold"><strong>name</strong></span> attribute
that defines the formal name of the prototype model, which must be unique across all models.
</p>
<pre class="programlisting">
&lt;prototype name="__stdcall" extrapop="unknown" stackshift="4"&gt;
&lt;input&gt;
&lt;pentry minsize="1" maxsize="500" align="4"&gt;
&lt;addr offset="4" space="stack"/&gt;
&lt;/pentry&gt;
&lt;/input&gt;
&lt;output&gt;
...
</pre>
<p>
</p>
</dd>
<dt><span class="term"><span class="bold"><strong>callfixup</strong></span></span></dt>
<dd>
<p>
This element is a Call-fixup, which can be used to substitute a specific p-code
sequence for CALL instructions during decompilation, as described in
<a class="xref" href="DecompilerAnnotations.html#AnnotePrototype" title="Function Prototypes">&#8220;Function Prototypes&#8221;</a>.
</p>
<p>
The <code class="code">&lt;callfixup&gt;</code> tag has a <span class="bold"><strong>name</strong></span>
attribute listing the formal name, which must be unique across all call-fixups.
</p>
<pre class="programlisting">
&lt;callfixup name="EH_prolog3"&gt;
&lt;pcode&gt;
&lt;body&gt;&lt;![CDATA&lt;
EBP = ESP + 4;
tmp = * EBP;
ESP = ESP - tmp;
ESP = ESP - 24;
]]&gt;&lt;/body&gt;
&lt;/pcode&gt;
&lt;/callfixup&gt;
</pre>
<p>
</p>
</dd>
<dt><span class="term"><span class="bold"><strong>callotherfixup</strong></span></span></dt>
<dd>
<p>
This element is a Callother-fixup, which can be used to substitute a specific p-code
sequence for CALLOTHER p-code operations. A CALLOTHER
is a black-box, or unspecified p-code operation, see <a class="xref" href="DecompilerConcepts.html#ConceptCallother" title="User-defined P-code Operations - CALLOTHER">&#8220;User-defined P-code Operations - CALLOTHER&#8221;</a>.
</p>
<p>
The <code class="code">&lt;callotherfixup&gt;</code> tag has a
<span class="bold"><strong>targetop</strong></span> attribute which lists the
name of the particular CALLOTHER operation it substitutes for.
</p>
<pre class="programlisting">
&lt;callotherfixup targetop="dynamicPush"&gt;
&lt;pcode&gt;
&lt;input name="amount"/&gt;
&lt;body&gt;&lt;![CDATA[
RSP = RSP + amount;
]]&gt;&lt;/body&gt;
&lt;/pcode&gt;
&lt;/callotherfixup&gt;
</pre>
<p>
</p>
</dd>
</dl></div>
</div>
<p>
</p>
</div>
<div class="sect2">
<div class="titlepage"><div><div><h3 class="title">
<a name="ExtendStatus"></a>Status</h3></div></div></div>
<p>
The Status column labels an element as either a core specification
or an extension; it also gives an indication of whether the element
is about to be installed or removed.
</p>
<p>
With no changes pending, the column will show one of the three main values:
</p>
<div class="informalexample">
<div class="variablelist"><dl class="variablelist">
<dt><span class="term"><span class="emphasis"><em>&lt;blank&gt;</em></span></span></dt>
<dd>
<p>
A blank Status column indicates that the element is a core part of the
specification, originating from one of the <span class="emphasis"><em>specification files</em></span>.
These elements cannot be changed or removed.
</p>
</dd>
<dt><span class="term"><span class="bold"><strong>extension</strong></span></span></dt>
<dd>
<p>
Indicates that the element is a program specific extension that has been
added to the specification.
</p>
</dd>
<dt><span class="term"><span class="bold"><strong>override</strong></span></span></dt>
<dd>
<p>
Indicates that the element, which must be a <span class="bold"><strong>callotherfixup</strong></span>,
is an extension that overrides a core element with the same target. The extension
effectively replaces the p-code injection of the core element with a user supplied one.
If this type of extension is later removed, the core element becomes active again.
</p>
</dd>
</dl></div>
</div>
<p>
</p>
<p>
If the user has either imported additional extensions or selected an extensions for removal but
has not yet clicked the <span class="emphasis"><em>Apply</em></span> button in the Options dialog, the Status column
may show one of the following values, indicating a pending change.
</p>
<div class="informalexample">
<div class="variablelist"><dl class="variablelist">
<dt><span class="term"><span class="bold"><strong>install</strong></span></span></dt>
<dd>
<p>
Indicates a new extension that will be installed.
</p>
</dd>
<dt><span class="term"><span class="bold"><strong>remove</strong></span></span></dt>
<dd>
<p>
Indicates an extension that is about to be removed.
</p>
</dd>
<dt><span class="term"><span class="bold"><strong>replace</strong></span></span></dt>
<dd>
<p>
Indicates a new extension that will replace a current
extension with the same name.
</p>
</dd>
<dt><span class="term"><span class="bold"><strong>override pending</strong></span></span></dt>
<dd>
<p>
Indicates a new extension that will override a core element when
it is installed.
</p>
</dd>
</dl></div>
</div>
<p>
</p>
</div>
<div class="sect2">
<div class="titlepage"><div><div><h3 class="title">
<a name="ExtendImport"></a>Importing a New Extension</h3></div></div></div>
<p>
The <span class="emphasis"><em>Import</em></span> button at the bottom of the
"Specification Extensions" pane allows the user to import one of the
three element types, <span class="bold"><strong>prototype</strong></span>,
<span class="bold"><strong>callfixup</strong></span>, or <span class="bold"><strong>callotherfixup</strong></span>,
into the program as a new extension.
The user must supply a properly formed XML document, as a file, that fully describes the new
extension. Clicking the <span class="emphasis"><em>Import</em></span> button brings up a File Chooser dialog,
from which the user must select their prepared XML file. Once <span class="emphasis"><em>Ok</em></span> is
clicked, the file is read in and validated. If there are any problems with the validation, or if
the new extension's name collides with a core element, the import does not succeed and
an error message will be displayed. Otherwise, the import is accepted, and the table is updated
to indicate the pending change.
</p>
<p>
The final change to the program, installing the new extension, will not happen until the
<span class="emphasis"><em>Apply</em></span> button, at the bottom of the Options dialog, is clicked.
</p>
<p>
The XML file describing the extension <span class="emphasis"><em>must</em></span> have one of the tags,
<code class="code">&lt;prototype&gt;</code>, <code class="code">&lt;callfixup&gt;</code>, or <code class="code">&lt;callotherfixup&gt;</code>,
as its single root element. Users can find numerous examples within the compiler
and processor specification files that come as part of Ghidra's installation.
See <a class="xref" href="DecompilerConcepts.html#ConceptSpecification" title="SLEIGH Specification Files">&#8220;SLEIGH Specification Files&#8221;</a>.
</p>
<p>
In the case of <span class="bold"><strong>prototype</strong></span> and <span class="bold"><strong>callfixup</strong></span>
elements, extensions cannot replace existing core elements, so the new extension <span class="emphasis"><em>must not</em></span>
have a name that matches an existing core element. If a new <span class="bold"><strong>callotherfixup</strong></span>
extension has a targetop that matches a core element, the extension is automatically treated as an override.
</p>
<p>
Existing extensions can be replaced simply by importing a new extension with the same name or targetop.
</p>
</div>
<div class="sect2">
<div class="titlepage"><div><div><h3 class="title">
<a name="ExtendRemove"></a>Removing an Extension</h3></div></div></div>
<p>
The <span class="emphasis"><em>Remove</em></span> button at the bottom of the "Specification Extensions" pane allows
the user to remove a previously installed extension. A row from the table is selected first, which
must have a Status of <span class="bold"><strong>extension</strong></span> or <span class="bold"><strong>override</strong></span>.
Core elements of the specification cannot be removed.
Clicking the <span class="emphasis"><em>Remove</em></span> button brings up a confirmation dialog, and if
<span class="emphasis"><em>Ok</em></span> is clicked, the selected extension is marked for removal. The Status of the row
changes to <span class="bold"><strong>remove</strong></span>, reflecting this.
</p>
<p>
The final change to the program, removing the extension, will not happen until the
<span class="emphasis"><em>Apply</em></span> button, at the bottom of the Options dialog, is clicked.
</p>
<p>
If a <span class="bold"><strong>prototype</strong></span> or <span class="bold"><strong>callfixup</strong></span> is removed,
all functions are checked to see if they have the matching calling convention or call-fixup set.
A function with matching calling convention is changed to have the <span class="emphasis"><em>default</em></span> convention, which is always a core element.
A function with matching call-fixup is changed to have no call-fixup.
</p>
</div>
</div>
</div></body> </div></body>
</html> </html>

View file

@ -4,7 +4,7 @@
<title>Decompiler Window</title> <title>Decompiler Window</title>
<link rel="stylesheet" type="text/css" href="../../shared/Frontpage.css"> <link rel="stylesheet" type="text/css" href="../../shared/Frontpage.css">
<link rel="stylesheet" type="text/css" href="../../shared/languages.css"> <link rel="stylesheet" type="text/css" href="../../shared/languages.css">
<meta name="generator" content="DocBook XSL Stylesheets V1.78.1"> <meta name="generator" content="DocBook XSL Stylesheets V1.79.1">
<link rel="home" href="Decompiler.html" title="Decompiler"> <link rel="home" href="Decompiler.html" title="Decompiler">
<link rel="up" href="Decompiler.html" title="Decompiler"> <link rel="up" href="Decompiler.html" title="Decompiler">
<link rel="prev" href="DecompilerOptions.html" title="Decompiler Options"> <link rel="prev" href="DecompilerOptions.html" title="Decompiler Options">

View file

@ -222,7 +222,7 @@ public class DecompInterface {
(SleighLanguageDescription) pcodelanguage.getLanguageDescription(); (SleighLanguageDescription) pcodelanguage.getLanguageDescription();
ResourceFile pspecfile = sleighdescription.getSpecFile(); ResourceFile pspecfile = sleighdescription.getSpecFile();
String pspecxml = fileToString(pspecfile); String pspecxml = fileToString(pspecfile);
String cspecxml = compilerSpec.getCompilerSpecString(); String cspecxml = compilerSpec.getXMLString();
decompCallback.setNativeMessage(null); decompCallback.setNativeMessage(null);
decompProcess.registerProgram(decompCallback, pspecxml, cspecxml, tspec, coretypes); decompProcess.registerProgram(decompCallback, pspecxml, cspecxml, tspec, coretypes);
@ -232,8 +232,9 @@ public class DecompInterface {
} }
if (xmlOptions != null) { if (xmlOptions != null) {
decompProcess.setMaxResultSize(xmlOptions.getMaxPayloadMBytes()); decompProcess.setMaxResultSize(xmlOptions.getMaxPayloadMBytes());
if (!decompProcess.sendCommand1Param("setOptions", if (!decompProcess.sendCommand1Param("setOptions", xmlOptions.getXML(this))
xmlOptions.getXML(this)).toString().equals("t")) { .toString()
.equals("t")) {
throw new IOException("Did not accept decompiler options"); throw new IOException("Did not accept decompiler options");
} }
} }
@ -241,14 +242,16 @@ public class DecompInterface {
throw new IOException("Decompile action not specified"); throw new IOException("Decompile action not specified");
} }
if (!actionname.equals("decompile")) { if (!actionname.equals("decompile")) {
if (!decompProcess.sendCommand2Params("setAction", actionname, "").toString().equals( if (!decompProcess.sendCommand2Params("setAction", actionname, "")
"t")) { .toString()
.equals("t")) {
throw new IOException("Could not set decompile action"); throw new IOException("Could not set decompile action");
} }
} }
if (!printSyntaxTree) { if (!printSyntaxTree) {
if (!decompProcess.sendCommand2Params("setAction", "", "notree").toString().equals( if (!decompProcess.sendCommand2Params("setAction", "", "notree")
"t")) { .toString()
.equals("t")) {
throw new IOException("Could not turn off syntax tree"); throw new IOException("Could not turn off syntax tree");
} }
} }
@ -258,14 +261,16 @@ public class DecompInterface {
} }
} }
if (sendParamMeasures) { if (sendParamMeasures) {
if (!decompProcess.sendCommand2Params("setAction", "", if (!decompProcess.sendCommand2Params("setAction", "", "parammeasures")
"parammeasures").toString().equals("t")) { .toString()
.equals("t")) {
throw new IOException("Could not turn on sending of parameter measures"); throw new IOException("Could not turn on sending of parameter measures");
} }
} }
if (jumpLoad) { if (jumpLoad) {
if (!decompProcess.sendCommand2Params("setAction", "", "jumpload").toString().equals( if (!decompProcess.sendCommand2Params("setAction", "", "jumpload")
"t")) { .toString()
.equals("t")) {
throw new IOException("Could not turn on jumptable loads"); throw new IOException("Could not turn on jumptable loads");
} }
} }
@ -409,8 +414,9 @@ public class DecompInterface {
} }
try { try {
verifyProcess(); verifyProcess();
return decompProcess.sendCommand2Params("setAction", actionstring, return decompProcess.sendCommand2Params("setAction", actionstring, "")
"").toString().equals("t"); .toString()
.equals("t");
} }
catch (IOException e) { catch (IOException e) {
// don't care // don't care
@ -446,8 +452,9 @@ public class DecompInterface {
String printstring = val ? "tree" : "notree"; String printstring = val ? "tree" : "notree";
try { try {
verifyProcess(); verifyProcess();
return decompProcess.sendCommand2Params("setAction", "", printstring).toString().equals( return decompProcess.sendCommand2Params("setAction", "", printstring)
"t"); .toString()
.equals("t");
} }
catch (IOException e) { catch (IOException e) {
// don't care // don't care
@ -484,8 +491,9 @@ public class DecompInterface {
String printstring = val ? "c" : "noc"; String printstring = val ? "c" : "noc";
try { try {
verifyProcess(); verifyProcess();
return decompProcess.sendCommand2Params("setAction", "", printstring).toString().equals( return decompProcess.sendCommand2Params("setAction", "", printstring)
"t"); .toString()
.equals("t");
} }
catch (IOException e) { catch (IOException e) {
// don't care // don't care
@ -521,8 +529,9 @@ public class DecompInterface {
String printstring = val ? "parammeasures" : "noparammeasures"; String printstring = val ? "parammeasures" : "noparammeasures";
try { try {
verifyProcess(); verifyProcess();
return decompProcess.sendCommand2Params("setAction", "", printstring).toString().equals( return decompProcess.sendCommand2Params("setAction", "", printstring)
"t"); .toString()
.equals("t");
} }
catch (IOException e) { catch (IOException e) {
// don't care // don't care
@ -551,8 +560,9 @@ public class DecompInterface {
String jumpstring = val ? "jumpload" : "nojumpload"; String jumpstring = val ? "jumpload" : "nojumpload";
try { try {
verifyProcess(); verifyProcess();
return decompProcess.sendCommand2Params("setAction", "", jumpstring).toString().equals( return decompProcess.sendCommand2Params("setAction", "", jumpstring)
"t"); .toString()
.equals("t");
} }
catch (IOException e) { catch (IOException e) {
// don't care // don't care
@ -588,8 +598,9 @@ public class DecompInterface {
try { try {
verifyProcess(); verifyProcess();
decompProcess.setMaxResultSize(xmlOptions.getMaxPayloadMBytes()); decompProcess.setMaxResultSize(xmlOptions.getMaxPayloadMBytes());
return decompProcess.sendCommand1Param("setOptions", return decompProcess.sendCommand1Param("setOptions", xmloptions.getXML(this))
xmloptions.getXML(this)).toString().equals("t"); .toString()
.equals("t");
} }
catch (IOException e) { catch (IOException e) {
// don't care // don't care
@ -707,9 +718,10 @@ public class DecompInterface {
debug.setFunction(func); debug.setFunction(func);
} }
decompCallback.setFunction(func, funcEntry, debug); decompCallback.setFunction(func, funcEntry, debug);
String addrstring = Varnode.buildXMLAddress(funcEntry); StringBuilder addrBuf = new StringBuilder();
AddressXML.buildXML(addrBuf, funcEntry);
verifyProcess(); verifyProcess();
res = decompProcess.sendCommand1ParamTimeout("decompileAt", addrstring.toString(), res = decompProcess.sendCommand1ParamTimeout("decompileAt", addrBuf.toString(),
timeoutSecs); timeoutSecs);
decompileMessage = decompCallback.getNativeMessage(); decompileMessage = decompCallback.getNativeMessage();
} }

View file

@ -55,6 +55,7 @@ import ghidra.util.xml.XmlUtilities;
public class DecompileCallback { public class DecompileCallback {
public final static int MAX_SYMBOL_COUNT = 16; public final static int MAX_SYMBOL_COUNT = 16;
/** /**
* Data returned for a query about strings * Data returned for a query about strings
*/ */
@ -70,6 +71,7 @@ public class DecompileCallback {
private Function cachedFunction; private Function cachedFunction;
private AddressSet undefinedBody; private AddressSet undefinedBody;
private Address funcEntry; private Address funcEntry;
private AddressSpace overlaySpace; // non-null if function being decompiled is in an overlay
private int default_extrapop; private int default_extrapop;
private Language pcodelanguage; private Language pcodelanguage;
private CompilerSpec pcodecompilerspec; private CompilerSpec pcodecompilerspec;
@ -125,6 +127,8 @@ public class DecompileCallback {
undefinedBody = new AddressSet(func.getBody()); undefinedBody = new AddressSet(func.getBody());
} }
funcEntry = entry; funcEntry = entry;
AddressSpace spc = funcEntry.getAddressSpace();
overlaySpace = spc.isOverlaySpace() ? spc : null;
debug = dbg; debug = dbg;
if (debug != null) { if (debug != null) {
debug.setPcodeDataTypeManager(dtmanage); debug.setPcodeDataTypeManager(dtmanage);
@ -153,19 +157,6 @@ public class DecompileCallback {
nativeMessage = msg; nativeMessage = msg;
} }
public synchronized int readXMLSize(String addrxml) {
int attrstart = addrxml.indexOf("size=\"");
if (attrstart >= 4) {
attrstart += 6;
int attrend = addrxml.indexOf('\"', attrstart);
if (attrend > attrstart) {
int size = SpecXmlUtils.decodeInt(addrxml.substring(attrstart, attrend));
return size;
}
}
return 0;
}
public synchronized ArrayList<String> readXMLNameList(String xml) throws PcodeXMLException { public synchronized ArrayList<String> readXMLNameList(String xml) throws PcodeXMLException {
try { try {
NameListHandler nmHandler = new NameListHandler(); NameListHandler nmHandler = new NameListHandler();
@ -182,9 +173,11 @@ public class DecompileCallback {
public byte[] getBytes(String addrxml) { public byte[] getBytes(String addrxml) {
try { try {
int size = readXMLSize(addrxml); Address addr = AddressXML.readXML(addrxml, addrfactory);
Address addr; int size = AddressXML.readXMLSize(addrxml);
addr = Varnode.readXMLAddress(addrxml, addrfactory, funcEntry.getAddressSpace()); if (overlaySpace != null) {
addr = overlaySpace.getOverlayAddress(addr);
}
if (addr == Address.NO_ADDRESS) { if (addr == Address.NO_ADDRESS) {
throw new PcodeXMLException("Address does not physically map"); throw new PcodeXMLException("Address does not physically map");
} }
@ -230,7 +223,10 @@ public class DecompileCallback {
Address addr; Address addr;
int flags; int flags;
try { try {
addr = Varnode.readXMLAddress(addrstring, addrfactory, funcEntry.getAddressSpace()); addr = AddressXML.readXML(addrstring, addrfactory);
if (overlaySpace != null) {
addr = overlaySpace.getOverlayAddress(addr);
}
} }
catch (PcodeXMLException e) { catch (PcodeXMLException e) {
Msg.error(this, "Decompiling " + funcEntry + ": " + e.getMessage()); Msg.error(this, "Decompiling " + funcEntry + ": " + e.getMessage());
@ -270,7 +266,10 @@ public class DecompileCallback {
public PackedBytes getPcodePacked(String addrstring) { public PackedBytes getPcodePacked(String addrstring) {
Address addr = null; Address addr = null;
try { try {
addr = Varnode.readXMLAddress(addrstring, addrfactory, funcEntry.getAddressSpace()); addr = AddressXML.readXML(addrstring, addrfactory);
if (overlaySpace != null) {
addr = overlaySpace.getOverlayAddress(addr);
}
} }
catch (PcodeXMLException e) { catch (PcodeXMLException e) {
Msg.error(this, "Decompiling " + funcEntry + ": " + e.getMessage()); Msg.error(this, "Decompiling " + funcEntry + ": " + e.getMessage());
@ -293,7 +292,8 @@ public class DecompileCallback {
} }
} }
PackedBytes pcode = instr.getPrototype().getPcodePacked(instr.getInstructionContext(), PackedBytes pcode = instr.getPrototype()
.getPcodePacked(instr.getInstructionContext(),
new InstructionPcodeOverride(instr), uniqueFactory); new InstructionPcodeOverride(instr), uniqueFactory);
return pcode; return pcode;
@ -346,7 +346,7 @@ public class DecompileCallback {
public String getPcodeInject(String nm, String context, int type) { public String getPcodeInject(String nm, String context, int type) {
PcodeInjectLibrary snippetLibrary = pcodecompilerspec.getPcodeInjectLibrary(); PcodeInjectLibrary snippetLibrary = pcodecompilerspec.getPcodeInjectLibrary();
InjectPayload payload = snippetLibrary.getPayload(type, nm, program, context); InjectPayload payload = snippetLibrary.getPayload(type, nm);
if (payload == null) { if (payload == null) {
Msg.warn(this, "Decompiling " + funcEntry + ", no pcode inject with name: " + nm); Msg.warn(this, "Decompiling " + funcEntry + ", no pcode inject with name: " + nm);
return null; // No fixup associated with this name return null; // No fixup associated with this name
@ -381,8 +381,8 @@ public class DecompileCallback {
con.nextAddr = con.baseAddr.add(fallThruOffset); con.nextAddr = con.baseAddr.add(fallThruOffset);
con.refAddr = null; con.refAddr = null;
for (Reference ref : program.getReferenceManager().getReferencesFrom( for (Reference ref : program.getReferenceManager()
con.baseAddr)) { .getReferencesFrom(con.baseAddr)) {
if (ref.isPrimary() && ref.getReferenceType().isCall()) { if (ref.isPrimary() && ref.getReferenceType().isCall()) {
con.refAddr = ref.getToAddress(); con.refAddr = ref.getToAddress();
break; break;
@ -487,7 +487,10 @@ public class DecompileCallback {
public String getSymbol(String addrstring) { // Return first symbol name at this address public String getSymbol(String addrstring) { // Return first symbol name at this address
Address addr; Address addr;
try { try {
addr = Varnode.readXMLAddress(addrstring, addrfactory, funcEntry.getAddressSpace()); addr = AddressXML.readXML(addrstring, addrfactory);
if (overlaySpace != null) {
addr = overlaySpace.getOverlayAddress(addr);
}
} }
catch (PcodeXMLException e) { catch (PcodeXMLException e) {
Msg.error(this, "Decompiling " + funcEntry + ": " + e.getMessage()); Msg.error(this, "Decompiling " + funcEntry + ": " + e.getMessage());
@ -627,8 +630,8 @@ public class DecompileCallback {
buf.append("<comment"); buf.append("<comment");
SpecXmlUtils.encodeStringAttribute(buf, "type", "header"); SpecXmlUtils.encodeStringAttribute(buf, "type", "header");
buf.append(">\n"); buf.append(">\n");
buf.append(Varnode.buildXMLAddress(addr)); AddressXML.buildXML(buf, addr);
buf.append(Varnode.buildXMLAddress(addr)); AddressXML.buildXML(buf, addr);
buf.append("\n<text>"); buf.append("\n<text>");
SpecXmlUtils.xmlEscape(buf, text); SpecXmlUtils.xmlEscape(buf, text);
buf.append("</text>\n"); buf.append("</text>\n");
@ -680,8 +683,8 @@ public class DecompileCallback {
buf.append("<comment"); buf.append("<comment");
SpecXmlUtils.encodeStringAttribute(buf, "type", typename); SpecXmlUtils.encodeStringAttribute(buf, "type", typename);
buf.append(">\n"); buf.append(">\n");
buf.append(Varnode.buildXMLAddress(addr)); AddressXML.buildXML(buf, addr);
buf.append(Varnode.buildXMLAddress(commaddr)); AddressXML.buildXML(buf, commaddr);
buf.append("\n<text>"); buf.append("\n<text>");
SpecXmlUtils.xmlEscape(buf, text); SpecXmlUtils.xmlEscape(buf, text);
buf.append("</text>\n"); buf.append("</text>\n");
@ -701,7 +704,10 @@ public class DecompileCallback {
public String getMappedSymbolsXML(String addrstring) { // Return XML describing data or functions at addr public String getMappedSymbolsXML(String addrstring) { // Return XML describing data or functions at addr
Address addr; Address addr;
try { try {
addr = Varnode.readXMLAddress(addrstring, addrfactory, funcEntry.getAddressSpace()); addr = AddressXML.readXML(addrstring, addrfactory);
if (overlaySpace != null) {
addr = overlaySpace.getOverlayAddress(addr);
}
if (addr == Address.NO_ADDRESS) { if (addr == Address.NO_ADDRESS) {
// Unknown spaces may result from "spacebase" registers defined in cspec // Unknown spaces may result from "spacebase" registers defined in cspec
return null; return null;
@ -743,7 +749,10 @@ public class DecompileCallback {
public String getExternalRefXML(String addrstring) { // Return any external reference at addr public String getExternalRefXML(String addrstring) { // Return any external reference at addr
Address addr; Address addr;
try { try {
addr = Varnode.readXMLAddress(addrstring, addrfactory, funcEntry.getAddressSpace()); addr = AddressXML.readXML(addrstring, addrfactory);
if (overlaySpace != null) {
addr = overlaySpace.getOverlayAddress(addr);
}
} }
catch (PcodeXMLException e) { catch (PcodeXMLException e) {
Msg.error(this, "Decompiling " + funcEntry + ": " + e.getMessage()); Msg.error(this, "Decompiling " + funcEntry + ": " + e.getMessage());
@ -792,6 +801,7 @@ public class DecompileCallback {
Namespace namespc = funcSymbol.getNamespace(); Namespace namespc = funcSymbol.getNamespace();
if (debug != null) { if (debug != null) {
debug.getFNTypes(hfunc); debug.getFNTypes(hfunc);
debug.addPossiblePrototypeExtension(func);
} }
return buildResult(funcSymbol, namespc); return buildResult(funcSymbol, namespc);
} }
@ -829,8 +839,8 @@ public class DecompileCallback {
public String getRegisterName(String addrstring) { public String getRegisterName(String addrstring) {
try { try {
Address addr = Varnode.readXMLAddress(addrstring, addrfactory, null); Address addr = AddressXML.readXML(addrstring, addrfactory);
int size = readXMLSize(addrstring); int size = AddressXML.readXMLSize(addrstring);
Register reg = pcodelanguage.getRegister(addr, size); Register reg = pcodelanguage.getRegister(addr, size);
if (reg == null) { if (reg == null) {
return null; return null;
@ -847,7 +857,10 @@ public class DecompileCallback {
public String getTrackedRegisters(String addrstring) { public String getTrackedRegisters(String addrstring) {
Address addr; Address addr;
try { try {
addr = Varnode.readXMLAddress(addrstring, addrfactory, funcEntry.getAddressSpace()); addr = AddressXML.readXML(addrstring, addrfactory);
if (overlaySpace != null) {
addr = overlaySpace.getOverlayAddress(addr);
}
} }
catch (PcodeXMLException e) { catch (PcodeXMLException e) {
Msg.error(this, "Decompiling " + funcEntry + ": " + e.getMessage()); Msg.error(this, "Decompiling " + funcEntry + ": " + e.getMessage());
@ -858,7 +871,7 @@ public class DecompileCallback {
StringBuilder stringBuf = new StringBuilder(); StringBuilder stringBuf = new StringBuilder();
stringBuf.append("<tracked_pointset"); stringBuf.append("<tracked_pointset");
Varnode.appendSpaceOffset(stringBuf, addr); AddressXML.appendAttributes(stringBuf, addr);
stringBuf.append(">\n"); stringBuf.append(">\n");
for (Register reg : context.getRegisters()) { for (Register reg : context.getRegisters()) {
if (reg.isProcessorContext()) { if (reg.isProcessorContext()) {
@ -1020,8 +1033,8 @@ public class DecompileCallback {
if (entry.getAddressSpace().equals(addr.getAddressSpace())) { if (entry.getAddressSpace().equals(addr.getAddressSpace())) {
long diff = addr.getOffset() - entry.getOffset(); long diff = addr.getOffset() - entry.getOffset();
if ((diff >= 0) && (diff < 8)) { if ((diff >= 0) && (diff < 8)) {
HighFunction hfunc = new HighFunction(func, pcodelanguage, pcodecompilerspec, HighFunction hfunc =
dtmanage); new HighFunction(func, pcodelanguage, pcodecompilerspec, dtmanage);
int extrapop = getExtraPopOverride(func, addr); int extrapop = getExtraPopOverride(func, addr);
hfunc.grabFromFunction(extrapop, includeDefaultNames, hfunc.grabFromFunction(extrapop, includeDefaultNames,
@ -1031,6 +1044,7 @@ public class DecompileCallback {
Namespace namespc = functionSymbol.getNamespace(); Namespace namespc = functionSymbol.getNamespace();
if (debug != null) { if (debug != null) {
debug.getFNTypes(hfunc); debug.getFNTypes(hfunc);
debug.addPossiblePrototypeExtension(func);
} }
return buildResult(functionSymbol, namespc); return buildResult(functionSymbol, namespc);
} }
@ -1304,8 +1318,11 @@ public class DecompileCallback {
Address addr; Address addr;
int maxChars; int maxChars;
try { try {
maxChars = readXMLSize(addrString); addr = AddressXML.readXML(addrString, addrfactory);
addr = Varnode.readXMLAddress(addrString, addrfactory, funcEntry.getAddressSpace()); maxChars = AddressXML.readXMLSize(addrString);
if (overlaySpace != null) {
addr = overlaySpace.getOverlayAddress(addr);
}
if (addr == Address.NO_ADDRESS) { if (addr == Address.NO_ADDRESS) {
throw new PcodeXMLException("Address does not physically map"); throw new PcodeXMLException("Address does not physically map");
} }

View file

@ -47,6 +47,7 @@ public class DecompileDebug {
private Function func; // The function being decompiled private Function func; // The function being decompiled
private Program program; // The program private Program program; // The program
private File debugFile; // The file to dump the XML document to private File debugFile; // The file to dump the XML document to
private Map<String, Object> specExtensions; // Local extensions to the compiler spec
private ArrayList<Namespace> dbscope; // Symbol query: scope private ArrayList<Namespace> dbscope; // Symbol query: scope
private ArrayList<String> database; // description of the symbol private ArrayList<String> database; // description of the symbol
private ArrayList<DataType> dtypes; // Data-types queried private ArrayList<DataType> dtypes; // Data-types queried
@ -108,16 +109,17 @@ public class DecompileDebug {
public DecompileDebug(File debugf) { public DecompileDebug(File debugf) {
func = null; func = null;
debugFile = debugf; debugFile = debugf;
dbscope = new ArrayList<Namespace>(); specExtensions = new TreeMap<>();
database = new ArrayList<String>(); dbscope = new ArrayList<>();
dtypes = new ArrayList<DataType>(); database = new ArrayList<>();
context = new ArrayList<String>(); dtypes = new ArrayList<>();
cpool = new ArrayList<String>(); context = new ArrayList<>();
byteset = new TreeSet<ByteChunk>(); cpool = new ArrayList<>();
contextchange = new TreeSet<Address>(); byteset = new TreeSet<>();
stringmap = new TreeMap<Address, StringData>(); contextchange = new TreeSet<>();
flowoverride = new ArrayList<String>(); stringmap = new TreeMap<>();
inject = new ArrayList<String>(); flowoverride = new ArrayList<>();
inject = new ArrayList<>();
contextRegister = null; contextRegister = null;
comments = null; comments = null;
globalnamespace = null; globalnamespace = null;
@ -158,6 +160,7 @@ public class DecompileDebug {
buf.append(">\n"); buf.append(">\n");
debugStream.write(buf.toString().getBytes()); debugStream.write(buf.toString().getBytes());
dumpImage(debugStream, pcodelanguage); dumpImage(debugStream, pcodelanguage);
dumpExtensions(debugStream);
dumpCoretypes(debugStream); dumpCoretypes(debugStream);
debugStream.write("<save_state>\n".getBytes()); debugStream.write("<save_state>\n".getBytes());
// dumpTypes(debugStream); // dumpTypes(debugStream);
@ -178,7 +181,6 @@ public class DecompileDebug {
debugStream.close(); debugStream.close();
} }
catch (Exception e) { catch (Exception e) {
// TODO Auto-generated catch block
Msg.error(this, "Unexpected Exception: " + e.getMessage(), e); Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
} }
} }
@ -224,24 +226,24 @@ public class DecompileDebug {
lastreadonly = readval; lastreadonly = readval;
} }
if (tagstarted && if (tagstarted && ((chunk.min != 0) || (lastspace != space) ||
((chunk.min != 0) || (lastspace != space) || (lastoffset != chunk.addr.getOffset()))) { (lastoffset != chunk.addr.getOffset()))) {
buf.append("\n</bytechunk>\n"); buf.append("\n</bytechunk>\n");
tagstarted = false; tagstarted = false;
} }
if (!tagstarted) { if (!tagstarted) {
buf.append("<bytechunk"); buf.append("<bytechunk");
SpecXmlUtils.encodeStringAttribute(buf, "space", space.getPhysicalSpace().getName()); SpecXmlUtils.encodeStringAttribute(buf, "space",
SpecXmlUtils.encodeUnsignedIntegerAttribute(buf, "offset", chunk.addr.getOffset() + space.getPhysicalSpace().getName());
chunk.min); SpecXmlUtils.encodeUnsignedIntegerAttribute(buf, "offset",
chunk.addr.getOffset() + chunk.min);
if (lastreadonly) { if (lastreadonly) {
SpecXmlUtils.encodeBooleanAttribute(buf, "readonly", lastreadonly); SpecXmlUtils.encodeBooleanAttribute(buf, "readonly", lastreadonly);
} }
buf.append(">\n"); buf.append(">\n");
tagstarted = true; tagstarted = true;
} }
for (int i = 0; i < chunk.min; ++i) for (int i = 0; i < chunk.min; ++i) {
{
buf.append(" "); // pad the hex display to 16 bytes buf.append(" "); // pad the hex display to 16 bytes
} }
for (int i = chunk.min; i < chunk.max; ++i) { for (int i = chunk.min; i < chunk.max; ++i) {
@ -290,9 +292,9 @@ public class DecompileDebug {
StringBuilder buf = new StringBuilder(); StringBuilder buf = new StringBuilder();
buf.append("<stringmanage>\n"); buf.append("<stringmanage>\n");
for (Map.Entry<Address, StringData> entry : stringmap.entrySet()) { for (Map.Entry<Address, StringData> entry : stringmap.entrySet()) {
buf.append("<string>\n<addr"); buf.append("<string>\n");
Varnode.appendSpaceOffset(buf, entry.getKey()); AddressXML.buildXML(buf, entry.getKey());
buf.append("/>\n<bytes"); buf.append("\n<bytes");
SpecXmlUtils.encodeBooleanAttribute(buf, "trunc", entry.getValue().isTruncated); SpecXmlUtils.encodeBooleanAttribute(buf, "trunc", entry.getValue().isTruncated);
buf.append(">\n "); buf.append(">\n ");
int count = 0; int count = 0;
@ -339,12 +341,15 @@ public class DecompileDebug {
new DataTypeDependencyOrderer(program.getDataTypeManager(), dtypes); new DataTypeDependencyOrderer(program.getDataTypeManager(), dtypes);
//First output all structures as zero size so to avoid any cyclic dependencies. //First output all structures as zero size so to avoid any cyclic dependencies.
for (DataType dataType : TypeOrderer.getStructList()) { for (DataType dataType : TypeOrderer.getStructList()) {
debugStream.write((dtmanage.buildStructTypeZeroSizeOveride(dataType) + "\n").toString().getBytes()); debugStream.write(
(dtmanage.buildStructTypeZeroSizeOveride(dataType) + "\n").toString().getBytes());
} }
//Next, use the dependency stack to output types. //Next, use the dependency stack to output types.
for (DataType dataType : TypeOrderer.getDependencyList()) { for (DataType dataType : TypeOrderer.getDependencyList()) {
if (!(dataType instanceof BuiltIn)) { if (!(dataType instanceof BuiltIn)) {
debugStream.write((dtmanage.buildType(dataType, dataType.getLength()) + "\n").toString().getBytes()); debugStream.write(
(dtmanage.buildType(dataType, dataType.getLength()) + "\n").toString()
.getBytes());
} }
} }
debugStream.write("</typegrp>\n".getBytes()); debugStream.write("</typegrp>\n".getBytes());
@ -365,7 +370,7 @@ public class DecompileDebug {
if (!(lang instanceof SleighLanguage)) { if (!(lang instanceof SleighLanguage)) {
return null; return null;
} }
ArrayList<ContextSymbol> res = new ArrayList<ContextSymbol>(); ArrayList<ContextSymbol> res = new ArrayList<>();
ghidra.app.plugin.processors.sleigh.symbol.Symbol[] list = ghidra.app.plugin.processors.sleigh.symbol.Symbol[] list =
((SleighLanguage) lang).getSymbolTable().getSymbolList(); ((SleighLanguage) lang).getSymbolTable().getSymbolList();
for (Symbol element : list) { for (Symbol element : list) {
@ -426,8 +431,7 @@ public class DecompileDebug {
break; break;
} }
} }
if (i == buf.length) if (i == buf.length) {
{
continue; // If all data is identical, then changepoint is not necessary continue; // If all data is identical, then changepoint is not necessary
} }
} }
@ -439,7 +443,7 @@ public class DecompileDebug {
} }
stringBuf.append("<context_pointset"); stringBuf.append("<context_pointset");
Varnode.appendSpaceOffset(stringBuf, addr); AddressXML.appendAttributes(stringBuf, addr);
stringBuf.append(">\n"); stringBuf.append(">\n");
for (ContextSymbol sym : ctxsymbols) { for (ContextSymbol sym : ctxsymbols) {
int sbit = sym.getInternalLow(); int sbit = sym.getInternalLow();
@ -466,7 +470,7 @@ public class DecompileDebug {
return; return;
} }
debugStream.write("<constantpool>\n".getBytes()); debugStream.write("<constantpool>\n".getBytes());
for(String rec : cpool) { for (String rec : cpool) {
debugStream.write(rec.getBytes()); debugStream.write(rec.getBytes());
} }
debugStream.write("</constantpool>\n".getBytes()); debugStream.write("</constantpool>\n".getBytes());
@ -508,11 +512,11 @@ public class DecompileDebug {
} }
private ArrayList<Namespace> orderNamespaces() { private ArrayList<Namespace> orderNamespaces() {
TreeMap<Long, Namespace> namespaceMap = new TreeMap<Long, Namespace>(); TreeMap<Long, Namespace> namespaceMap = new TreeMap<>();
for (Namespace namespace : dbscope) { for (Namespace namespace : dbscope) {
namespaceMap.put(namespace.getID(), namespace); namespaceMap.put(namespace.getID(), namespace);
} }
ArrayList<Namespace> res = new ArrayList<Namespace>(); ArrayList<Namespace> res = new ArrayList<>();
while (!namespaceMap.isEmpty()) { while (!namespaceMap.isEmpty()) {
Entry<Long, Namespace> entry = namespaceMap.firstEntry(); Entry<Long, Namespace> entry = namespaceMap.firstEntry();
Long curKey = entry.getKey(); Long curKey = entry.getKey();
@ -592,6 +596,31 @@ public class DecompileDebug {
debugStream.write("</db>\n".getBytes()); debugStream.write("</db>\n".getBytes());
} }
private void dumpExtensions(OutputStream debugStream) throws IOException {
if (specExtensions.isEmpty()) {
return;
}
PcodeInjectLibrary library = program.getCompilerSpec().getPcodeInjectLibrary();
debugStream.write("<specextensions>\n".getBytes());
for (Object obj : specExtensions.values()) {
if (obj instanceof PrototypeModel) {
PrototypeModel model = (PrototypeModel) obj;
StringBuilder buffer = new StringBuilder();
model.saveXml(buffer, library);
String modelString = buffer.toString();
debugStream.write(modelString.getBytes());
}
else if (obj instanceof InjectPayload) {
InjectPayload payload = (InjectPayload) obj;
StringBuilder buffer = new StringBuilder();
payload.saveXml(buffer);
String payloadString = buffer.toString();
debugStream.write(payloadString.getBytes());
}
}
debugStream.write("</specextensions>\n".getBytes());
}
private void dumpCoretypes(OutputStream debugStream) throws IOException { private void dumpCoretypes(OutputStream debugStream) throws IOException {
debugStream.write(dtmanage.buildCoreTypes().getBytes()); debugStream.write(dtmanage.buildCoreTypes().getBytes());
} }
@ -666,11 +695,9 @@ public class DecompileDebug {
buf.append(" <labelsym"); buf.append(" <labelsym");
SpecXmlUtils.xmlEscapeAttribute(buf, "name", name); SpecXmlUtils.xmlEscapeAttribute(buf, "name", name);
SpecXmlUtils.encodeUnsignedIntegerAttribute(buf, "id", id); SpecXmlUtils.encodeUnsignedIntegerAttribute(buf, "id", id);
buf.append("/>\n"); buf.append("/>\n ");
buf.append(" <addr"); AddressXML.buildXML(buf, addr);
Varnode.appendSpaceOffset(buf, addr); buf.append("\n <rangelist/>\n");
buf.append("/>\n");
buf.append(" <rangelist/>\n");
buf.append("</mapsym>\n"); buf.append("</mapsym>\n");
getMapped(namespace, buf.toString()); getMapped(namespace, buf.toString());
} }
@ -711,7 +738,7 @@ public class DecompileDebug {
context.add(doc); context.add(doc);
} }
public void getCPoolRef(String rec,long[] refs) { public void getCPoolRef(String rec, long[] refs) {
StringBuilder buf = new StringBuilder(); StringBuilder buf = new StringBuilder();
buf.append("<ref"); buf.append("<ref");
SpecXmlUtils.encodeUnsignedIntegerAttribute(buf, "a", refs[0]); SpecXmlUtils.encodeUnsignedIntegerAttribute(buf, "a", refs[0]);
@ -733,7 +760,7 @@ public class DecompileDebug {
getMapped(spc, buffer.toString()); getMapped(spc, buffer.toString());
} }
public void addFlowOverride(Address addr,FlowOverride fo) { public void addFlowOverride(Address addr, FlowOverride fo) {
StringBuilder buf = new StringBuilder(); StringBuilder buf = new StringBuilder();
buf.append("<flow type=\""); buf.append("<flow type=\"");
if (fo == FlowOverride.BRANCH) { if (fo == FlowOverride.BRANCH) {
@ -751,25 +778,44 @@ public class DecompileDebug {
else { else {
buf.append("none"); buf.append("none");
} }
buf.append("\"><addr"); buf.append("\">");
Varnode.appendSpaceOffset(buf,func.getEntryPoint()); AddressXML.buildXML(buf, func.getEntryPoint());
buf.append("/><addr"); AddressXML.buildXML(buf, addr);
Varnode.appendSpaceOffset(buf, addr); buf.append("</flow>\n");
buf.append("/></flow>\n");
flowoverride.add(buf.toString()); flowoverride.add(buf.toString());
} }
public void addInject(Address addr,String name,int injectType,String payload) { public void addInject(Address addr, String name, int injectType, String payload) {
StringBuilder buf = new StringBuilder(); StringBuilder buf = new StringBuilder();
buf.append("<inject name=\""); buf.append("<inject name=\"");
buf.append(name); buf.append(name);
buf.append('"'); buf.append('"');
SpecXmlUtils.encodeSignedIntegerAttribute(buf, "type", injectType); SpecXmlUtils.encodeSignedIntegerAttribute(buf, "type", injectType);
buf.append(">\n <addr"); buf.append(">\n ");
Varnode.appendSpaceOffset(buf, addr); AddressXML.buildXML(buf, addr);
buf.append("/>\n <payload><![CDATA[\n"); buf.append("\n <payload><![CDATA[\n");
buf.append(payload); buf.append(payload);
buf.append("\n]]></payload>\n</inject>\n"); buf.append("\n]]></payload>\n</inject>\n");
inject.add(buf.toString()); inject.add(buf.toString());
PcodeInjectLibrary library = program.getCompilerSpec().getPcodeInjectLibrary();
if (library.hasProgramPayload(name, injectType)) {
InjectPayload programPayload = library.getPayload(injectType, name);
String title =
(injectType == InjectPayload.CALLFIXUP_TYPE) ? "callfixup_" : "callotherfixup_";
title = title + name;
specExtensions.put(title, programPayload);
}
}
public void addPossiblePrototypeExtension(Function testFunc) {
PrototypeModel model = testFunc.getCallingConvention();
if (model == null) {
return;
}
if (model.isProgramExtension()) {
String title = "prototype_" + model.getName();
specExtensions.put(title, model);
}
} }
} }

View file

@ -27,7 +27,9 @@ import ghidra.framework.options.Options;
import ghidra.framework.options.ToolOptions; import ghidra.framework.options.ToolOptions;
import ghidra.framework.plugintool.Plugin; import ghidra.framework.plugintool.Plugin;
import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.PluginTool;
import ghidra.program.database.ProgramCompilerSpec;
import ghidra.program.model.lang.*; import ghidra.program.model.lang.*;
import ghidra.program.model.lang.CompilerSpec.EvaluationModelType;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.util.HelpLocation; import ghidra.util.HelpLocation;
import ghidra.util.SystemUtilities; import ghidra.util.SystemUtilities;
@ -404,7 +406,7 @@ public class DecompileOptions {
codeViewerBackgroundColor = CODE_VIEWER_BACKGROUND_COLOR; codeViewerBackgroundColor = CODE_VIEWER_BACKGROUND_COLOR;
defaultFont = DEFAULT_FONT; defaultFont = DEFAULT_FONT;
displayLineNumbers = LINE_NUMBER_DEF; displayLineNumbers = LINE_NUMBER_DEF;
displayLanguage = BasicCompilerSpec.DECOMPILER_OUTPUT_DEF; displayLanguage = ProgramCompilerSpec.DECOMPILER_OUTPUT_DEF;
protoEvalModel = "default"; protoEvalModel = "default";
decompileTimeoutSeconds = SUGGESTED_DECOMPILE_TIMEOUT_SECS; decompileTimeoutSeconds = SUGGESTED_DECOMPILE_TIMEOUT_SECS;
payloadLimitMBytes = SUGGESTED_MAX_PAYLOAD_BYTES; payloadLimitMBytes = SUGGESTED_MAX_PAYLOAD_BYTES;
@ -503,21 +505,21 @@ public class DecompileOptions {
*/ */
public void grabFromProgram(Program program) { public void grabFromProgram(Program program) {
// Default values, even if there is no program // Default values, even if there is no program
displayLanguage = BasicCompilerSpec.DECOMPILER_OUTPUT_DEF; displayLanguage = ProgramCompilerSpec.DECOMPILER_OUTPUT_DEF;
protoEvalModel = "default"; protoEvalModel = "default";
if (program == null) { if (program == null) {
return; return;
} }
CompilerSpec cspec = program.getCompilerSpec(); CompilerSpec cspec = program.getCompilerSpec();
PrototypeModel model = (PrototypeModel) cspec.getPrototypeEvaluationModel(program); PrototypeModel model = cspec.getPrototypeEvaluationModel(EvaluationModelType.EVAL_CURRENT);
if (model != null) { if (model != null) {
String modelname = model.getName(); String modelname = model.getName();
if (modelname != null) { if (modelname != null) {
protoEvalModel = modelname; protoEvalModel = modelname;
} }
} }
displayLanguage = cspec.getDecompilerOutputLanguage(program); displayLanguage = cspec.getDecompilerOutputLanguage();
} }
public String getProtoEvalModel() { public String getProtoEvalModel() {

View file

@ -13,10 +13,6 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
/*
* Created on Jun 3, 2005
*
*/
package ghidra.app.decompiler; package ghidra.app.decompiler;
import java.io.*; import java.io.*;

View file

@ -29,6 +29,7 @@ import ghidra.framework.model.DomainFile;
import ghidra.framework.options.SaveState; import ghidra.framework.options.SaveState;
import ghidra.framework.plugintool.*; import ghidra.framework.plugintool.*;
import ghidra.framework.plugintool.util.PluginStatus; import ghidra.framework.plugintool.util.PluginStatus;
import ghidra.program.database.SpecExtension;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.listing.*; import ghidra.program.model.listing.*;
import ghidra.program.util.ProgramLocation; import ghidra.program.util.ProgramLocation;
@ -231,6 +232,9 @@ public class DecompilePlugin extends Plugin {
if (event instanceof ProgramActivatedPluginEvent) { if (event instanceof ProgramActivatedPluginEvent) {
currentProgram = ((ProgramActivatedPluginEvent) event).getActiveProgram(); currentProgram = ((ProgramActivatedPluginEvent) event).getActiveProgram();
connectedProvider.doSetProgram(currentProgram); connectedProvider.doSetProgram(currentProgram);
if (currentProgram != null) {
SpecExtension.registerOptions(currentProgram);
}
} }
else if (event instanceof ProgramLocationPluginEvent) { else if (event instanceof ProgramLocationPluginEvent) {
ProgramLocation location = ((ProgramLocationPluginEvent) event).getLocation(); ProgramLocation location = ((ProgramLocationPluginEvent) event).getLocation();

View file

@ -36,11 +36,11 @@ import ghidra.app.plugin.core.decompile.actions.*;
import ghidra.app.services.*; import ghidra.app.services.*;
import ghidra.app.util.HelpTopics; import ghidra.app.util.HelpTopics;
import ghidra.app.util.HighlightProvider; import ghidra.app.util.HighlightProvider;
import ghidra.framework.model.DomainObjectChangedEvent; import ghidra.framework.model.*;
import ghidra.framework.model.DomainObjectListener;
import ghidra.framework.options.*; import ghidra.framework.options.*;
import ghidra.framework.plugintool.NavigatableComponentProviderAdapter; import ghidra.framework.plugintool.NavigatableComponentProviderAdapter;
import ghidra.framework.plugintool.util.ServiceListener; import ghidra.framework.plugintool.util.ServiceListener;
import ghidra.program.database.SpecExtension;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.listing.Function; import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
@ -307,17 +307,33 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
@Override @Override
public void domainObjectChanged(DomainObjectChangedEvent ev) { public void domainObjectChanged(DomainObjectChangedEvent ev) {
if (!isVisible()) { // Check for events that signal that a decompiler process' data is stale
return; // and if so force a new process to be spawned
}
if (ev.containsEvent(ChangeManager.DOCR_MEMORY_BLOCK_ADDED) || if (ev.containsEvent(ChangeManager.DOCR_MEMORY_BLOCK_ADDED) ||
ev.containsEvent(ChangeManager.DOCR_MEMORY_BLOCK_REMOVED)) { ev.containsEvent(ChangeManager.DOCR_MEMORY_BLOCK_REMOVED) ||
ev.containsEvent(DomainObject.DO_OBJECT_RESTORED)) {
controller.resetDecompiler(); controller.resetDecompiler();
} }
else if (ev.containsEvent(DomainObject.DO_PROPERTY_CHANGED)) {
Iterator<DomainObjectChangeRecord> iter = ev.iterator();
while (iter.hasNext()) {
DomainObjectChangeRecord record = iter.next();
if (record.getEventType() == DomainObject.DO_PROPERTY_CHANGED) {
if (record.getOldValue() instanceof String) {
String value = (String) record.getOldValue();
if (value.startsWith(SpecExtension.SPEC_EXTENSION)) {
controller.resetDecompiler();
break;
}
}
}
}
}
// Trigger a redecompile an any program change if the window is active
if (isVisible()) {
redecompileUpdater.update(); redecompileUpdater.update();
}
} }
private void doRefresh() { private void doRefresh() {

View file

@ -26,6 +26,7 @@ import ghidra.app.decompiler.parallel.*;
import ghidra.app.plugin.core.analysis.validator.PostAnalysisValidator; import ghidra.app.plugin.core.analysis.validator.PostAnalysisValidator;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.lang.CompilerSpec; import ghidra.program.model.lang.CompilerSpec;
import ghidra.program.model.lang.CompilerSpec.EvaluationModelType;
import ghidra.program.model.lang.PrototypeModel; import ghidra.program.model.lang.PrototypeModel;
import ghidra.program.model.listing.*; import ghidra.program.model.listing.*;
import ghidra.util.Msg; import ghidra.util.Msg;
@ -46,7 +47,7 @@ public class DecompilerValidator extends PostAnalysisValidator {
List<Function> functions = filterFunctions(program, iter, monitor); List<Function> functions = filterFunctions(program, iter, monitor);
DecompilerCallback<String> callback = DecompilerCallback<String> callback =
new DecompilerCallback<String>(program, new DecompilerValidatorConfigurer()) { new DecompilerCallback<>(program, new DecompilerValidatorConfigurer()) {
@Override @Override
public String process(DecompileResults results, TaskMonitor m) throws Exception { public String process(DecompileResults results, TaskMonitor m) throws Exception {
@ -147,7 +148,8 @@ public class DecompilerValidator extends PostAnalysisValidator {
private DecompileOptions getDecompilerOptions() { private DecompileOptions getDecompilerOptions() {
try { try {
CompilerSpec spec = program.getCompilerSpec(); CompilerSpec spec = program.getCompilerSpec();
PrototypeModel model = (PrototypeModel) spec.getPrototypeEvaluationModel(program); PrototypeModel model =
spec.getPrototypeEvaluationModel(EvaluationModelType.EVAL_CURRENT);
options.setProtoEvalModel(model.getName()); options.setProtoEvalModel(model.getName());
} }
catch (Exception e) { catch (Exception e) {

View file

@ -29,6 +29,7 @@ import docking.widgets.fieldpanel.support.FieldLocation;
import ghidra.app.decompiler.ClangToken; import ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.component.ClangTextField; import ghidra.app.decompiler.component.ClangTextField;
import ghidra.app.decompiler.component.DecompilerPanel; import ghidra.app.decompiler.component.DecompilerPanel;
import ghidra.program.model.pcode.HighFunction;
import ghidra.test.AbstractProgramBasedTest; import ghidra.test.AbstractProgramBasedTest;
public abstract class AbstractDecompilerTest extends AbstractProgramBasedTest { public abstract class AbstractDecompilerTest extends AbstractProgramBasedTest {
@ -171,4 +172,34 @@ public abstract class AbstractDecompilerTest extends AbstractProgramBasedTest {
assertEquals(tokenText, text); assertEquals(tokenText, text);
} }
} }
protected ClangTextField getLineStarting(String val) {
DecompilerPanel panel = provider.getDecompilerPanel();
List<Field> fields = panel.getFields();
for (Field field : fields) {
ClangTextField textField = (ClangTextField) field;
String text = textField.getText().trim();
if (text.startsWith(val)) {
return textField;
}
}
return null;
}
protected ClangTextField getLineContaining(String val) {
DecompilerPanel panel = provider.getDecompilerPanel();
List<Field> fields = panel.getFields();
for (Field field : fields) {
ClangTextField textField = (ClangTextField) field;
String text = textField.getText();
if (text.contains(val)) {
return textField;
}
}
return null;
}
protected HighFunction getHighFunction() {
return provider.getController().getHighFunction();
}
} }

View file

@ -17,11 +17,8 @@ package ghidra.app.plugin.core.decompile;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import java.util.List;
import org.junit.Test; import org.junit.Test;
import docking.widgets.fieldpanel.field.Field;
import docking.widgets.fieldpanel.support.FieldLocation; import docking.widgets.fieldpanel.support.FieldLocation;
import ghidra.app.cmd.equate.SetEquateCmd; import ghidra.app.cmd.equate.SetEquateCmd;
import ghidra.app.cmd.function.CreateFunctionCmd; import ghidra.app.cmd.function.CreateFunctionCmd;
@ -30,7 +27,6 @@ import ghidra.app.cmd.label.RenameLabelCmd;
import ghidra.app.decompiler.ClangToken; import ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.ClangVariableToken; import ghidra.app.decompiler.ClangVariableToken;
import ghidra.app.decompiler.component.ClangTextField; import ghidra.app.decompiler.component.ClangTextField;
import ghidra.app.decompiler.component.DecompilerPanel;
import ghidra.app.plugin.core.decompile.actions.IsolateVariableTask; import ghidra.app.plugin.core.decompile.actions.IsolateVariableTask;
import ghidra.app.plugin.core.decompile.actions.RenameVariableTask; import ghidra.app.plugin.core.decompile.actions.RenameVariableTask;
import ghidra.framework.options.Options; import ghidra.framework.options.Options;
@ -49,36 +45,6 @@ public class HighSymbolTest extends AbstractDecompilerTest {
return "Winmine__XP.exe.gzf"; return "Winmine__XP.exe.gzf";
} }
protected ClangTextField getLineStarting(String val) {
DecompilerPanel panel = provider.getDecompilerPanel();
List<Field> fields = panel.getFields();
for (Field field : fields) {
ClangTextField textField = (ClangTextField) field;
String text = textField.getText().trim();
if (text.startsWith(val)) {
return textField;
}
}
return null;
}
protected ClangTextField getLineContaining(String val) {
DecompilerPanel panel = provider.getDecompilerPanel();
List<Field> fields = panel.getFields();
for (Field field : fields) {
ClangTextField textField = (ClangTextField) field;
String text = textField.getText();
if (text.contains(val)) {
return textField;
}
}
return null;
}
protected HighFunction getHighFunction() {
return provider.getController().getHighFunction();
}
private void renameGlobalVariable(HighSymbol highSymbol, ClangToken tokenAtCursor, private void renameGlobalVariable(HighSymbol highSymbol, ClangToken tokenAtCursor,
String newName) { String newName) {
Address addr = highSymbol.getStorage().getMinAddress(); Address addr = highSymbol.getStorage().getMinAddress();
@ -114,6 +80,7 @@ public class HighSymbolTest extends AbstractDecompilerTest {
options.setBoolean("Stack", false); options.setBoolean("Stack", false);
}); });
} }
private void renameVariable(HighSymbol highSymbol, ClangToken tokenAtCursor, String newName) { private void renameVariable(HighSymbol highSymbol, ClangToken tokenAtCursor, String newName) {
RenameVariableTask rename = RenameVariableTask rename =
new RenameVariableTask(provider.getTool(), highSymbol.getProgram(), new RenameVariableTask(provider.getTool(), highSymbol.getProgram(),

View file

@ -0,0 +1,259 @@
/* ###
* 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 org.xml.sax.SAXException;
import ghidra.app.decompiler.component.ClangTextField;
import ghidra.app.plugin.processors.sleigh.SleighException;
import ghidra.framework.options.Options;
import ghidra.framework.store.LockException;
import ghidra.program.database.ProgramCompilerSpec;
import ghidra.program.database.SpecExtension;
import ghidra.program.model.address.Address;
import ghidra.program.model.lang.*;
import ghidra.program.model.lang.CompilerSpec.EvaluationModelType;
import ghidra.program.model.listing.Function;
import ghidra.program.model.symbol.SourceType;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.InvalidInputException;
import ghidra.util.task.TaskMonitor;
import ghidra.xml.XmlParseException;
public class SpecExtensionTest extends AbstractDecompilerTest {
@Override
protected String getProgramName() {
return "Winmine__XP.exe.gzf";
}
@Test
public void test_BadCallotherTarget() {
String myfixup = "<callotherfixup targetop=\"unknownop\">\n" + " <pcode>\n" +
" <input name=\"fcx\"/>\n" + " <body><![CDATA[\n" + " EAX = fcx + 2;\n" +
" ]]></body>\n" + " </pcode>\n" + "</callotherfixup>\n";
String errMessage = null;
try {
SpecExtension specExtension = new SpecExtension(program);
specExtension.addReplaceCompilerSpecExtension(myfixup, TaskMonitor.DUMMY);
}
catch (SleighException | XmlParseException | SAXException | LockException ex) {
errMessage = ex.getMessage();
}
assertTrue(errMessage.contains("CALLOTHER_FIXUP target does not exist"));
}
@Test
public void test_BadExtension() {
// Document with a p-code compile error
String myfixup = "<callfixup name=\"mynewthing\">\n" + " <target name=\"targ1\"/>\n" +
" <pcode>\n" + " <body><![CDATA[\n" + " *ESP = 1000:4;\n" +
" ESP = blahhh - 4;\n" + " ]]></body>\n" + " </pcode>\n" + "</callfixup>\n";
String errMessage = null;
SpecExtension specExtension = new SpecExtension(program);
try {
specExtension.addReplaceCompilerSpecExtension(myfixup, TaskMonitor.DUMMY);
}
catch (SleighException | XmlParseException | SAXException | LockException ex) {
errMessage = ex.getMessage();
}
assertTrue(errMessage.contains("halting compilation"));
// Document with an XML parsing problem
myfixup = "<callfixup name=\"mynewthing\"> </badendtag>";
errMessage = null;
String subError = null;
try {
specExtension.testExtensionDocument(myfixup);
}
catch (Exception e) {
errMessage = e.getMessage();
subError = e.getCause().getMessage();
}
assertTrue(errMessage.contains("Invalid compiler specification"));
assertTrue(subError.contains("must be terminated by the matching"));
// Document that does not validate against the grammar
myfixup = "<callfixup> <pcode> <body><![CDATA[ESP = 1000;\n]]></body></pcode></callfixup>";
errMessage = null;
try {
specExtension.testExtensionDocument(myfixup);
}
catch (Exception e) {
errMessage = e.getMessage();
}
assertTrue(errMessage.contains("Could not find attribute: name"));
}
@Test
public void test_ExtensionNameCollision() {
// Legal document that would overwrite a core callfixup
String myfixup =
"<callfixup name=\"alloca_probe\"><pcode><body>ESP = ESP - 4;</body></pcode></callfixup>";
String errMessage = null;
SpecExtension specExtension = new SpecExtension(program);
try {
specExtension.addReplaceCompilerSpecExtension(myfixup, TaskMonitor.DUMMY);
}
catch (SleighException | XmlParseException | SAXException | LockException ex) {
errMessage = ex.getMessage();
}
assertTrue(errMessage.contains("Extension cannot replace"));
}
@Test
public void test_PrototypeExtension() {
decompile("100272e");
ClangTextField line = getLineContaining("FUN_010026a7(pHVar1);");
assertNotNull(line);
CompilerSpec cspec = program.getCompilerSpec();
PrototypeModel defaultModel = cspec.getDefaultCallingConvention();
StringBuilder buffer = new StringBuilder();
defaultModel.saveXml(buffer, cspec.getPcodeInjectLibrary());
String defaultString = buffer.toString();
// Replace the output register EAX with ECX
defaultString = defaultString.replace("<addr space=\"register\" offset=\"0x0\"/>",
"<addr space=\"register\" offset=\"4\"/>");
// Change the name
defaultString = defaultString.replace("name=\"__stdcall\"", "name=\"myproto\"");
SpecExtension specExtension = new SpecExtension(program);
int id1 = program.startTransaction("Test prototype install");
try {
specExtension.addReplaceCompilerSpecExtension(defaultString, TaskMonitor.DUMMY);
}
catch (LockException | SleighException | SAXException | XmlParseException ex) {
throw new AssertionError("Unexpected exception: " + ex.getMessage());
}
program.endTransaction(id1, true);
PrototypeModel myproto = cspec.getCallingConvention("myproto");
assertNotNull(myproto);
int id = program.startTransaction("test extension install");
Address addr = program.getAddressFactory().getDefaultAddressSpace().getAddress(0x100112c);
Function func = program.getFunctionManager().getReferencedFunction(addr);
boolean changeWorks = true;
try {
func.setCallingConvention("myproto");
}
catch (InvalidInputException e) {
changeWorks = false;
}
program.endTransaction(id, true);
assertTrue(changeWorks);
decompile("100272e");
// Look for the affect of ECX being the output register
line = getLineContaining("FUN_010026a7(extraout_EAX);");
assertNotNull(line);
int id3 = program.startTransaction("Change eval model");
Options options = program.getOptions(ProgramCompilerSpec.DECOMPILER_PROPERTY_LIST_NAME);
options.setString(ProgramCompilerSpec.EVALUATION_MODEL_PROPERTY_NAME, "myproto");
program.endTransaction(id3, true);
PrototypeModel evalModel =
program.getCompilerSpec().getPrototypeEvaluationModel(EvaluationModelType.EVAL_CURRENT);
ParamList.WithSlotRec res = new ParamList.WithSlotRec();
Address ecxAddr = program.getAddressFactory().getRegisterSpace().getAddress(4);
boolean outExists = evalModel.possibleOutputParamWithSlot(ecxAddr, 4, res);
assertTrue(outExists);
int id2 = program.startTransaction("test extension removal");
try {
specExtension.removeCompilerSpecExtension("prototype_myproto", TaskMonitor.DUMMY);
}
catch (LockException | CancelledException ex) {
throw new AssertionError("Unexpected exception: " + ex.getMessage());
}
program.endTransaction(id2, true);
myproto = cspec.getCallingConvention("myproto");
assertNull(myproto);
assertFalse(func.getCallingConventionName().equals("myproto"));
evalModel =
program.getCompilerSpec().getPrototypeEvaluationModel(EvaluationModelType.EVAL_CURRENT);
assertEquals(evalModel.getName(), "__stdcall");
}
@Test
public void test_CallFixupExtension() {
String myfixup = "<callfixup name=\"mynewthing\">\n" + " <target name=\"targ1\"/>\n" +
" <pcode>\n" + " <body><![CDATA[\n" + " *ESP = 1000:4;\n" +
" ESP = ESP - 4;\n" + " *:4 ESP = inst_next;\n" + " ]]></body>\n" +
" </pcode>\n" + "</callfixup>\n";
SpecExtension specExtension = new SpecExtension(program);
int id1 = program.startTransaction("test extension install");
try {
specExtension.addReplaceCompilerSpecExtension(myfixup, TaskMonitor.DUMMY);
}
catch (LockException | SleighException | SAXException | XmlParseException ex) {
throw new AssertionError("Unexpected exception: " + ex.getMessage());
}
program.endTransaction(id1, true);
PcodeInjectLibrary library = program.getCompilerSpec().getPcodeInjectLibrary();
InjectPayloadSleigh[] programPayloads = library.getProgramPayloads();
assertEquals(programPayloads.length, 1);
InjectPayload payload = programPayloads[0];
assertTrue(programPayloads[0] instanceof InjectPayloadCallfixup);
InjectPayloadCallfixup callfixup = (InjectPayloadCallfixup) payload;
List<String> targets = callfixup.getTargets();
assertEquals(targets.size(), 1);
assertEquals(targets.get(0), "targ1");
assertEquals(payload.getName(), "mynewthing");
assertTrue(payload.isFallThru());
assertFalse(payload.isIncidentalCopy());
int id = program.startTransaction("test extensions");
Address firstAddr =
program.getAddressFactory().getDefaultAddressSpace().getAddress(0x1002607);
Function func1 = program.getFunctionManager().getFunctionAt(firstAddr);
func1.setCallFixup("mynewthing");
Address secondAddr =
program.getAddressFactory().getDefaultAddressSpace().getAddress(0x10038d7);
Function func = program.getFunctionManager().getFunctionAt(secondAddr);
func.setSignatureSource(SourceType.DEFAULT);
program.endTransaction(id, true);
decompile("100263c");
ClangTextField line = getLineContaining("injection: mynewthing");
assertNotNull(line);
// injection causes remaining call to look like it takes 1000 as a parameter
line = getLineStarting("FUN_010038d7(1000);");
assertNotNull(line);
// Remove the fixup extension
int id2 = program.startTransaction("test extension removal");
try {
specExtension.removeCompilerSpecExtension("callfixup_mynewthing", TaskMonitor.DUMMY);
}
catch (LockException | CancelledException ex) {
throw new AssertionError("Unexpected exception: " + ex.getMessage());
}
program.endTransaction(id2, true);
programPayloads = library.getProgramPayloads();
assertNull(programPayloads);
decompile("100263c");
line = getLineStarting("FUN_01002607();");
assertNotNull(line);
line = getLineStarting("FUN_010038d7();");
assertNotNull(line);
}
}

View file

@ -28,10 +28,12 @@ import ghidra.app.util.importer.MessageLog;
import ghidra.file.analyzers.FileFormatAnalyzer; import ghidra.file.analyzers.FileFormatAnalyzer;
import ghidra.file.formats.android.dex.format.*; import ghidra.file.formats.android.dex.format.*;
import ghidra.file.formats.android.dex.util.DexUtil; import ghidra.file.formats.android.dex.util.DexUtil;
import ghidra.program.database.ProgramCompilerSpec;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSetView; import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.data.*; import ghidra.program.model.data.*;
import ghidra.program.model.lang.*; import ghidra.program.model.lang.Language;
import ghidra.program.model.lang.Register;
import ghidra.program.model.listing.*; import ghidra.program.model.listing.*;
import ghidra.program.model.listing.Function.FunctionUpdateType; import ghidra.program.model.listing.Function.FunctionUpdateType;
import ghidra.program.model.mem.Memory; import ghidra.program.model.mem.Memory;
@ -65,7 +67,7 @@ public class DexHeaderFormatAnalyzer extends FileFormatAnalyzer {
createInitialFragments(program, header, monitor); createInitialFragments(program, header, monitor);
BasicCompilerSpec.enableJavaLanguageDecompilation(program); ProgramCompilerSpec.enableJavaLanguageDecompilation(program);
createNamespaces(program, header, monitor, log); createNamespaces(program, header, monitor, log);
processMap(program, header, monitor, log); processMap(program, header, monitor, log);
processStrings(program, header, monitor, log); processStrings(program, header, monitor, log);
@ -172,11 +174,9 @@ public class DexHeaderFormatAnalyzer extends FileFormatAnalyzer {
return; return;
} }
for (int i = 0; i < methods.size(); ++i) { for (EncodedMethod encodedMethod : methods) {
monitor.checkCanceled(); monitor.checkCanceled();
EncodedMethod encodedMethod = methods.get(i);
MethodIDItem methodID = header.getMethods().get(encodedMethod.getMethodIndex()); MethodIDItem methodID = header.getMethods().get(encodedMethod.getMethodIndex());
String methodName = DexUtil.convertToString(header, methodID.getNameIndex()); String methodName = DexUtil.convertToString(header, methodID.getNameIndex());
@ -213,8 +213,8 @@ public class DexHeaderFormatAnalyzer extends FileFormatAnalyzer {
Namespace classNameSpace, MessageLog log) { Namespace classNameSpace, MessageLog log) {
program.getSymbolTable().addExternalEntryPoint(methodAddress); program.getSymbolTable().addExternalEntryPoint(methodAddress);
try { try {
return program.getSymbolTable().createLabel(methodAddress, methodName, classNameSpace, return program.getSymbolTable()
SourceType.ANALYSIS); .createLabel(methodAddress, methodName, classNameSpace, SourceType.ANALYSIS);
} }
catch (InvalidInputException e) { catch (InvalidInputException e) {
log.appendException(e); log.appendException(e);
@ -240,8 +240,8 @@ public class DexHeaderFormatAnalyzer extends FileFormatAnalyzer {
commentBuilder.append("Method Debug Info Offset: 0x" + commentBuilder.append("Method Debug Info Offset: 0x" +
Integer.toHexString(codeItem.getDebugInfoOffset()) + "\n"); Integer.toHexString(codeItem.getDebugInfoOffset()) + "\n");
} }
commentBuilder.append( commentBuilder
"Method ID Offset: 0x" + Long.toHexString(methodID.getFileOffset()) + "\n"); .append("Method ID Offset: 0x" + Long.toHexString(methodID.getFileOffset()) + "\n");
setPlateComment(program, methodAddress, commentBuilder.toString()); setPlateComment(program, methodAddress, commentBuilder.toString());
} }
@ -522,11 +522,9 @@ public class DexHeaderFormatAnalyzer extends FileFormatAnalyzer {
private void processEncodedMethods(Program program, DexHeader header, ClassDefItem item, private void processEncodedMethods(Program program, DexHeader header, ClassDefItem item,
List<EncodedMethod> methods, TaskMonitor monitor) throws Exception { List<EncodedMethod> methods, TaskMonitor monitor) throws Exception {
for (int i = 0; i < methods.size(); ++i) { for (EncodedMethod method : methods) {
monitor.checkCanceled(); monitor.checkCanceled();
EncodedMethod method = methods.get(i);
MethodIDItem methodID = header.getMethods().get(method.getMethodIndex()); MethodIDItem methodID = header.getMethods().get(method.getMethodIndex());
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
@ -662,7 +660,8 @@ public class DexHeaderFormatAnalyzer extends FileFormatAnalyzer {
methodAddress.add(setItemDataType.getLength())); methodAddress.add(setItemDataType.getLength()));
processAnnotationSetItem(program, setItem, monitor, log); processAnnotationSetItem(program, setItem, monitor, log);
} }
for (ParameterAnnotation parameter : annotationsDirectoryItem.getParameterAnnotations()) { for (ParameterAnnotation parameter : annotationsDirectoryItem
.getParameterAnnotations()) {
monitor.checkCanceled(); monitor.checkCanceled();
Address parameterAddress = toAddr(program, parameter.getAnnotationsOffset()); Address parameterAddress = toAddr(program, parameter.getAnnotationsOffset());
AnnotationSetReferenceList annotationSetReferenceList = AnnotationSetReferenceList annotationSetReferenceList =
@ -767,9 +766,9 @@ public class DexHeaderFormatAnalyzer extends FileFormatAnalyzer {
classNameSpace, log); classNameSpace, log);
if (methodSymbol != null) { if (methodSymbol != null) {
String externalName = methodSymbol.getName(true); String externalName = methodSymbol.getName(true);
program.getReferenceManager().addExternalReference(methodIndexAddress, program.getReferenceManager()
"EXTERNAL.dex", externalName, null, SourceType.ANALYSIS, 0, .addExternalReference(methodIndexAddress, "EXTERNAL.dex",
RefType.DATA); externalName, null, SourceType.ANALYSIS, 0, RefType.DATA);
} }
} }
} }

View file

@ -481,7 +481,8 @@ public abstract class DomainObjectAdapterDB extends DomainObjectAdapter
*/ */
protected boolean propertyChanged(String propertyName, Object oldValue, Object newValue) { protected boolean propertyChanged(String propertyName, Object oldValue, Object newValue) {
setChanged(true); setChanged(true);
fireEvent(new DomainObjectChangeRecord(DomainObject.DO_PROPERTY_CHANGED, name, name)); fireEvent(
new DomainObjectChangeRecord(DomainObject.DO_PROPERTY_CHANGED, propertyName, newValue));
return true; return true;
} }

View file

@ -157,6 +157,8 @@ class OptionsDB extends AbstractOptions {
public synchronized void removeOption(String propertyName) { public synchronized void removeOption(String propertyName) {
super.removeOption(propertyName); super.removeOption(propertyName);
removePropertyFromDB(propertyName); removePropertyFromDB(propertyName);
// NOTE: AbstractOptions does not provide removal notification
notifyOptionChanged(propertyName, null, null);
} }
private void removePropertyFromDB(String propertyName) { private void removePropertyFromDB(String propertyName) {
@ -331,7 +333,7 @@ class OptionsDB extends AbstractOptions {
@Override @Override
protected boolean notifyOptionChanged(String optionName, Object oldValue, Object newValue) { protected boolean notifyOptionChanged(String optionName, Object oldValue, Object newValue) {
return domainObj.propertyChanged(name, oldValue, newValue); return domainObj.propertyChanged(optionName, oldValue, newValue);
} }
} }

View file

@ -122,7 +122,8 @@ public class SleighLanguage implements Language {
throws SAXException, IOException, UnknownInstructionException { throws SAXException, IOException, UnknownInstructionException {
this.defaultSymbols = new ArrayList<>(); this.defaultSymbols = new ArrayList<>();
this.compilerSpecDescriptions = new LinkedHashMap<>(); this.compilerSpecDescriptions = new LinkedHashMap<>();
for (CompilerSpecDescription compilerSpecDescription : langDescription.getCompatibleCompilerSpecDescriptions()) { for (CompilerSpecDescription compilerSpecDescription : langDescription
.getCompatibleCompilerSpecDescriptions()) {
this.compilerSpecDescriptions.put(compilerSpecDescription.getCompilerSpecID(), this.compilerSpecDescriptions.put(compilerSpecDescription.getCompilerSpecID(),
(SleighCompilerSpecDescription) compilerSpecDescription); (SleighCompilerSpecDescription) compilerSpecDescription);
} }
@ -478,8 +479,9 @@ public class SleighLanguage implements Language {
String[] args; String[] args;
if (sleighArgsFile != null && sleighArgsFile.isFile()) { if (sleighArgsFile != null && sleighArgsFile.isFile()) {
String baseDir = Application.getInstallationDirectory().getAbsolutePath().replace( String baseDir = Application.getInstallationDirectory()
File.separatorChar, '/'); .getAbsolutePath()
.replace(File.separatorChar, '/');
if (!baseDir.endsWith("/")) { if (!baseDir.endsWith("/")) {
baseDir += "/"; baseDir += "/";
} }
@ -626,63 +628,14 @@ public class SleighLanguage implements Language {
return new Pair<>(addrspace.getAddress(first), addrspace.getAddress(last)); return new Pair<>(addrspace.getAddress(first), addrspace.getAddress(last));
} }
private void parseJumpAssist(XmlElement el, XmlPullParser parser) { private void read(XmlPullParser parser) throws XmlParseException {
String name = el.getAttribute("name");
String source = "pspec: " + getLanguageID().getIdAsString();
while (parser.peek().isStart()) {
String subName;
XmlElement subel = parser.peek();
if (subel.getName().charAt(0) == 'c') {
subName = name + "_index2case";
}
else if (subel.getName().charAt(0) == 'a') {
subName = name + "_index2addr";
}
else if (subel.getName().charAt(0) == 's') {
subName = name + "_calcsize";
}
else {
subName = name + "_defaultaddr";
}
InjectPayloadSleigh payload =
new InjectPayloadSleigh(subName, InjectPayload.EXECUTABLEPCODE_TYPE, source);
payload.restoreXml(parser);
addAdditionInject(payload);
}
}
public InjectPayloadSleigh parseSegmentOp(XmlElement el, XmlPullParser parser) {
String name = el.getAttribute("userop");
if (name == null) {
name = "segment";
}
name = name + "_pcode";
String source = "pspec: " + getLanguageID().getIdAsString();
InjectPayloadSleigh payload = null;
if (parser.peek().isStart()) {
if (parser.peek().getName().equals("pcode")) {
payload = new InjectPayloadSleigh(name, InjectPayload.EXECUTABLEPCODE_TYPE, source);
payload.restoreXml(parser);
}
}
while (parser.peek().isStart()) {
parser.discardSubTree();
}
if (payload == null) {
throw new SleighException("Missing <pcode> child for <segmentop> tag");
}
return payload;
}
private void read(XmlPullParser parser) {
Set<String> registerDataSet = new HashSet<>(); Set<String> registerDataSet = new HashSet<>();
XmlElement element = parser.start("processor_spec"); XmlElement el = parser.start("processor_spec");
while (!parser.peek().isEnd()) { while (parser.peek().isStart()) {
element = parser.start("properties", "segmented_address", "segmentop", "programcounter", String elName = parser.peek().getName();
"data_space", "inferptrbounds", "context_data", "volatile", "jumpassist", if (elName.equals("properties")) {
"incidentalcopy", "register_data", "default_symbols", "default_memory_blocks"); XmlElement subel = parser.start();
if (element.getName().equals("properties")) {
while (!parser.peek().isEnd()) { while (!parser.peek().isEnd()) {
XmlElement next = parser.start("property"); XmlElement next = parser.start("property");
String key = next.getAttribute("key"); String key = next.getAttribute("key");
@ -690,13 +643,17 @@ public class SleighLanguage implements Language {
properties.put(key, value); properties.put(key, value);
parser.end(next); parser.end(next);
} }
parser.end(subel);
} }
else if (element.getName().equals("programcounter")) { else if (elName.equals("programcounter")) {
setProgramCounter(element.getAttribute("register")); XmlElement subel = parser.start();
setProgramCounter(subel.getAttribute("register"));
parser.end(subel);
} }
else if (element.getName().equals("data_space")) { else if (elName.equals("data_space")) {
setDefaultDataSpace(element.getAttribute("space")); XmlElement subel = parser.start();
String overrideString = element.getAttribute("ptr_wordsize"); setDefaultDataSpace(subel.getAttribute("space"));
String overrideString = subel.getAttribute("ptr_wordsize");
if (overrideString != null) { if (overrideString != null) {
int val = SpecXmlUtils.decodeInt(overrideString); int val = SpecXmlUtils.decodeInt(overrideString);
if (val <= 0 || val >= 32) { if (val <= 0 || val >= 32) {
@ -704,8 +661,10 @@ public class SleighLanguage implements Language {
} }
defaultPointerWordSize = val; defaultPointerWordSize = val;
} }
parser.end(subel);
} }
else if (element.getName().equals("context_data")) { else if (elName.equals("context_data")) {
XmlElement subel = parser.start();
while (!parser.peek().isEnd()) { while (!parser.peek().isEnd()) {
XmlElement next = parser.start(); XmlElement next = parser.start();
boolean isContext = next.getName().equals("context_set"); boolean isContext = next.getName().equals("context_set");
@ -744,8 +703,10 @@ public class SleighLanguage implements Language {
// skip the end tag // skip the end tag
parser.end(next); parser.end(next);
} }
parser.end(subel);
} }
else if (element.getName().equals("volatile")) { else if (elName.equals("volatile")) {
XmlElement subel = parser.start();
while (!parser.peek().getName().equals("volatile")) { while (!parser.peek().getName().equals("volatile")) {
XmlElement next = parser.start(); XmlElement next = parser.start();
if (next.getName().equals("register")) { if (next.getName().equals("register")) {
@ -759,11 +720,21 @@ public class SleighLanguage implements Language {
// skip the end tag // skip the end tag
parser.end(next); parser.end(next);
} }
parser.end(subel);
} }
else if (element.getName().equals("jumpassist")) { else if (elName.equals("jumpassist")) {
parseJumpAssist(element, parser); XmlElement subel = parser.start();
String source = "pspec: " + getLanguageID().getIdAsString();
String name = subel.getAttribute("name");
while (parser.peek().isStart()) {
InjectPayloadSleigh payload = new InjectPayloadJumpAssist(name, source);
payload.restoreXml(parser, this);
addAdditionInject(payload);
} }
else if (element.getName().equals("register_data")) { parser.end(subel);
}
else if (elName.equals("register_data")) {
XmlElement subel = parser.start();
while (parser.peek().getName().equals("register")) { while (parser.peek().getName().equals("register")) {
XmlElement reg = parser.start(); XmlElement reg = parser.start();
String registerName = reg.getAttribute("name"); String registerName = reg.getAttribute("name");
@ -810,8 +781,10 @@ public class SleighLanguage implements Language {
// skip the end tag // skip the end tag
parser.end(reg); parser.end(reg);
} }
parser.end(subel);
} }
else if (element.getName().equals("default_symbols")) { else if (elName.equals("default_symbols")) {
XmlElement subel = parser.start();
while (parser.peek().getName().equals("symbol")) { while (parser.peek().getName().equals("symbol")) {
XmlElement symbol = parser.start(); XmlElement symbol = parser.start();
String labelName = symbol.getAttribute("name"); String labelName = symbol.getAttribute("name");
@ -832,8 +805,10 @@ public class SleighLanguage implements Language {
// skip the end tag // skip the end tag
parser.end(symbol); parser.end(symbol);
} }
parser.end(subel);
} }
else if (element.getName().equals("default_memory_blocks")) { else if (elName.equals("default_memory_blocks")) {
XmlElement subel = parser.start();
List<MemoryBlockDefinition> list = new ArrayList<>(); List<MemoryBlockDefinition> list = new ArrayList<>();
while (parser.peek().getName().equals("memory_block")) { while (parser.peek().getName().equals("memory_block")) {
XmlElement mblock = parser.start(); XmlElement mblock = parser.start();
@ -841,27 +816,39 @@ public class SleighLanguage implements Language {
// skip the end tag // skip the end tag
parser.end(mblock); parser.end(mblock);
} }
parser.end(subel);
defaultMemoryBlocks = new MemoryBlockDefinition[list.size()]; defaultMemoryBlocks = new MemoryBlockDefinition[list.size()];
list.toArray(defaultMemoryBlocks); list.toArray(defaultMemoryBlocks);
} }
else if (element.getName().equals("incidentalcopy")) { else if (elName.equals("incidentalcopy")) {
XmlElement subel = parser.start();
while (parser.peek().isStart()) { while (parser.peek().isStart()) {
parser.discardSubTree(); parser.discardSubTree();
} }
parser.end(subel);
} }
else if (element.getName().equals("inferptrbounds")) { else if (elName.equals("inferptrbounds")) {
XmlElement subel = parser.start();
while (parser.peek().isStart()) { while (parser.peek().isStart()) {
parser.discardSubTree(); parser.discardSubTree();
} }
parser.end(subel);
} }
else if (element.getName().equals("segmentop")) { else if (elName.equals("segmentop")) {
InjectPayloadSleigh payload = parseSegmentOp(element, parser); String source = "pspec: " + getLanguageID().getIdAsString();
InjectPayloadSleigh payload = new InjectPayloadSegment(source);
payload.restoreXml(parser, this);
addAdditionInject(payload); addAdditionInject(payload);
} }
// get rid of the end tag of whatever we started with at the top of the while else if (elName.equals("segmented_address")) {
parser.end(element); XmlElement subel = parser.start();
parser.end(subel);
} }
parser.dispose(); else {
throw new XmlParseException("Unknown pspec tag: " + elName);
}
}
parser.end(el);
} }
private void readRemainingSpecification() throws SAXException, IOException { private void readRemainingSpecification() throws SAXException, IOException {
@ -870,6 +857,9 @@ public class SleighLanguage implements Language {
try { try {
read(parser); read(parser);
} }
catch (XmlParseException e) {
Msg.error(this, e.getMessage());
}
finally { finally {
parser.dispose(); parser.dispose();
} }
@ -912,9 +902,8 @@ public class SleighLanguage implements Language {
if (isBigEndian ^ description.getEndian().isBigEndian()) { if (isBigEndian ^ description.getEndian().isBigEndian()) {
if (description.getInstructionEndian().isBigEndian() == description.getEndian() if (description.getInstructionEndian().isBigEndian() == description.getEndian()
.isBigEndian()) { .isBigEndian()) {
throw new SleighException( throw new SleighException(".ldefs says " + getLanguageID() + " is " +
".ldefs says " + getLanguageID() + " is " + description.getEndian() + description.getEndian() + " but .sla says " + el.getAttribute("bigendian"));
" but .sla says " + el.getAttribute("bigendian"));
} }
} }
uniqueBase = SpecXmlUtils.decodeLong(el.getAttribute("uniqbase")); uniqueBase = SpecXmlUtils.decodeLong(el.getAttribute("uniqbase"));
@ -1164,7 +1153,9 @@ public class SleighLanguage implements Language {
@Override @Override
public CompilerSpec getDefaultCompilerSpec() { public CompilerSpec getDefaultCompilerSpec() {
SleighCompilerSpecDescription compilerSpecDescription = SleighCompilerSpecDescription compilerSpecDescription =
(SleighCompilerSpecDescription) description.getCompatibleCompilerSpecDescriptions().iterator().next(); (SleighCompilerSpecDescription) description.getCompatibleCompilerSpecDescriptions()
.iterator()
.next();
try { try {
return getCompilerSpecByID(compilerSpecDescription.getCompilerSpecID()); return getCompilerSpecByID(compilerSpecDescription.getCompilerSpecID());
} }
@ -1328,8 +1319,8 @@ public class SleighLanguage implements Language {
if (matcher.find()) { if (matcher.find()) {
if (SystemUtilities.isInDevelopmentMode()) { if (SystemUtilities.isInDevelopmentMode()) {
// Search across repositories in development mode // Search across repositories in development mode
currentManual = Application.findDataFileInAnyModule( currentManual = Application
"manuals/" + matcher.group(1).trim()); .findDataFileInAnyModule("manuals/" + matcher.group(1).trim());
} }
if (currentManual == null) { if (currentManual == null) {
currentManual = currentManual =
@ -1598,7 +1589,8 @@ public class SleighLanguage implements Language {
} }
else { else {
parallelHelper = parallelHelper =
(ParallelInstructionLanguageHelper) helperClass.getDeclaredConstructor().newInstance(); (ParallelInstructionLanguageHelper) helperClass.getDeclaredConstructor()
.newInstance();
} }
} }
catch (Exception e) { catch (Exception e) {

View file

@ -16,6 +16,7 @@
package ghidra.app.plugin.processors.sleigh; package ghidra.app.plugin.processors.sleigh;
import java.io.InputStream; import java.io.InputStream;
import java.io.StringReader;
import org.iso_relax.verifier.*; import org.iso_relax.verifier.*;
import org.xml.sax.*; import org.xml.sax.*;
@ -26,10 +27,32 @@ import ghidra.util.Msg;
import utilities.util.FileResolutionResult; import utilities.util.FileResolutionResult;
import utilities.util.FileUtilities; import utilities.util.FileUtilities;
/**
* Validate SLEIGH related XML configuration files: .cspec .pspec and .ldefs
*
* A ResourceFile containing an XML document can be verified with one of the
* static methods:
* - validateCspecFile
* - validateLdefsFile
* - validatePspecFile
*
* Alternately the class can be instantiated, which will allocate a single verifier
* that can be run on multiple files.
*/
public class SleighLanguageValidator { public class SleighLanguageValidator {
private static final ResourceFile ldefsRelaxSchemaFile; private static final ResourceFile ldefsRelaxSchemaFile;
private static final ResourceFile pspecRelaxSchemaFile; private static final ResourceFile pspecRelaxSchemaFile;
private static final ResourceFile cspecRelaxSchemaFile; private static final ResourceFile cspecRelaxSchemaFile;
public static final int CSPEC_TYPE = 1;
public static final int PSPEC_TYPE = 2;
public static final int LDEFS_TYPE = 3;
public static final int CSPECTAG_TYPE = 4;
private static final String LANGUAGE_TYPESTRING = "language definitions";
private static final String COMPILER_TYPESTRING = "compiler specification";
private static final String PROCESSOR_TYPESTRING = "processor specification";
private int verifierType;
private Verifier verifier;
static { static {
ResourceFile file = null; ResourceFile file = null;
@ -69,16 +92,107 @@ public class SleighLanguageValidator {
cspecRelaxSchemaFile = file; cspecRelaxSchemaFile = file;
} }
public SleighLanguageValidator(int type) {
verifierType = type;
ResourceFile schemaFile = null;
switch (type) {
case CSPEC_TYPE:
case CSPECTAG_TYPE:
schemaFile = cspecRelaxSchemaFile;
break;
case PSPEC_TYPE:
schemaFile = pspecRelaxSchemaFile;
break;
case LDEFS_TYPE:
schemaFile = ldefsRelaxSchemaFile;
break;
default:
throw new SleighException("Bad verifier type");
}
verifier = null;
try {
verifier = getVerifier(schemaFile);
}
catch (Exception e) {
throw new SleighException("Error creating verifier", e);
}
}
private String getTypeString() {
if (verifierType == PSPEC_TYPE) {
return PROCESSOR_TYPESTRING;
}
if (verifierType == LDEFS_TYPE) {
return LANGUAGE_TYPESTRING;
}
return COMPILER_TYPESTRING;
}
/**
* Verify the given file against this validator.
* @param specFile is the file
* @throws SleighException with an explanation if the file does not validate
*/
public void verify(ResourceFile specFile) throws SleighException {
FileResolutionResult result = FileUtilities.existsAndIsCaseDependent(specFile);
if (!result.isOk()) {
throw new SleighException(
specFile + " is not properly case dependent: " + result.getMessage());
}
try {
InputStream in = specFile.getInputStream();
verifier.setErrorHandler(new VerifierErrorHandler(specFile));
verifier.verify(new InputSource(in));
in.close();
}
catch (Exception e) {
throw new SleighException(
"Invalid " + getTypeString() + " file: " + specFile.getAbsolutePath(), e);
}
}
/**
* Verify an XML document as a string against this validator.
* Currently this only supports verifierType == CSPECTAG_TYPE.
* @param title is a description of the document
* @param document is the XML document body
* @throws SleighException with an explanation if the document does not validate
*/
public void verify(String title, String document) throws SleighException {
if (verifierType != CSPECTAG_TYPE) {
throw new SleighException("Only cspec tag verification is supported");
}
StringBuilder buffer = new StringBuilder();
buffer.append("<compiler_spec>\n");
buffer.append("<default_proto>\n");
buffer.append("<prototype name=\"a\" extrapop=\"0\" stackshift=\"0\">\n");
buffer.append("<input/><output/>\n");
buffer.append("</prototype>\n");
buffer.append("</default_proto>\n");
buffer.append(document);
buffer.append("</compiler_spec>\n");
ErrorHandler errorHandler = new VerifierErrorHandler(title, 6);
StringReader reader = new StringReader(buffer.toString());
verifier.setErrorHandler(errorHandler);
try {
verifier.verify(new InputSource(reader));
}
catch (Exception e) {
throw new SleighException("Invalid " + getTypeString() + ": " + title, e);
}
}
public static void validateLdefsFile(ResourceFile ldefsFile) throws SleighException { public static void validateLdefsFile(ResourceFile ldefsFile) throws SleighException {
validateSleighFile(ldefsRelaxSchemaFile, ldefsFile, "language definitions"); validateSleighFile(ldefsRelaxSchemaFile, ldefsFile, LANGUAGE_TYPESTRING);
} }
public static void validatePspecFile(ResourceFile pspecFile) throws SleighException { public static void validatePspecFile(ResourceFile pspecFile) throws SleighException {
validateSleighFile(pspecRelaxSchemaFile, pspecFile, "processor specification"); validateSleighFile(pspecRelaxSchemaFile, pspecFile, PROCESSOR_TYPESTRING);
} }
public static void validateCspecFile(ResourceFile cspecFile) throws SleighException { public static void validateCspecFile(ResourceFile cspecFile) throws SleighException {
validateSleighFile(cspecRelaxSchemaFile, cspecFile, "compiler specification"); validateSleighFile(cspecRelaxSchemaFile, cspecFile, COMPILER_TYPESTRING);
} }
private static void validateSleighFile(ResourceFile relaxSchemaFile, private static void validateSleighFile(ResourceFile relaxSchemaFile,
@ -86,8 +200,8 @@ public class SleighLanguageValidator {
FileResolutionResult result = FileUtilities.existsAndIsCaseDependent(fileToValidate); FileResolutionResult result = FileUtilities.existsAndIsCaseDependent(fileToValidate);
if (!result.isOk()) { if (!result.isOk()) {
throw new SleighException(fileToValidate + " is not properly case dependent: " + throw new SleighException(
result.getMessage()); fileToValidate + " is not properly case dependent: " + result.getMessage());
} }
Verifier verifier = null; Verifier verifier = null;
@ -99,7 +213,7 @@ public class SleighLanguageValidator {
} }
try { try {
InputStream in = fileToValidate.getInputStream(); InputStream in = fileToValidate.getInputStream();
verifier.setErrorHandler(new MyErrorHandler(fileToValidate)); verifier.setErrorHandler(new VerifierErrorHandler(fileToValidate));
verifier.verify(new InputSource(in)); verifier.verify(new InputSource(in));
in.close(); in.close();
} }
@ -116,11 +230,18 @@ public class SleighLanguageValidator {
return verifier; return verifier;
} }
private static class MyErrorHandler implements ErrorHandler { private static class VerifierErrorHandler implements ErrorHandler {
final ResourceFile file; final String documentTitle;
int lineNumberBase;
public MyErrorHandler(ResourceFile file) { public VerifierErrorHandler(ResourceFile file) {
this.file = file; documentTitle = file.toString();
lineNumberBase = 0;
}
public VerifierErrorHandler(String title, int base) {
documentTitle = title;
lineNumberBase = base;
} }
@Override @Override
@ -130,10 +251,10 @@ public class SleighLanguageValidator {
@Override @Override
public void error(SAXParseException e) throws SAXException { public void error(SAXParseException e) throws SAXException {
Msg.error( int lineno = e.getLineNumber() - lineNumberBase;
SleighLanguageValidator.class, Msg.error(SleighLanguageValidator.class,
"Error validating " + file + " at " + e.getLineNumber() + ":" + "Error validating " + documentTitle + " at " + lineno + ":" + e.getColumnNumber(),
e.getColumnNumber(), e); e);
throw e; throw e;
} }

View file

@ -0,0 +1,76 @@
/* ###
* 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.processors.sleigh;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JComponent;
import ghidra.framework.options.*;
import ghidra.program.database.ProgramDB;
import ghidra.util.exception.InvalidInputException;
import ghidra.util.task.TaskMonitor;
public class SpecExtensionEditor implements OptionsEditor, PropertyChangeListener {
private ProgramDB program;
private PropertyChangeListener listener;
private SpecExtensionPanel panel;
public SpecExtensionEditor(ProgramDB program) {
this.program = program;
}
@Override
public void apply() throws InvalidInputException {
panel.apply(TaskMonitor.DUMMY);
}
@Override
public void cancel() {
panel.cancel();
}
@Override
public void reload() {
// doesn't respond to reload
}
@Override
public void setOptionsPropertyChangeListener(PropertyChangeListener listener) {
this.listener = listener;
}
@Override
public JComponent getEditorComponent(Options options, EditorStateFactory editorStateFactory) {
panel = new SpecExtensionPanel(program, this);
return panel;
}
@Override
public void dispose() {
// stub
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (listener != null) {
listener.propertyChange(evt);
}
}
}

View file

@ -0,0 +1,709 @@
/* ###
* 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.processors.sleigh;
import java.awt.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.*;
import org.xml.sax.SAXException;
import docking.widgets.OptionDialog;
import docking.widgets.filechooser.GhidraFileChooser;
import docking.widgets.table.*;
import ghidra.framework.preferences.Preferences;
import ghidra.framework.store.LockException;
import ghidra.program.database.ProgramDB;
import ghidra.program.database.SpecExtension;
import ghidra.program.database.SpecExtension.DocInfo;
import ghidra.program.model.lang.*;
import ghidra.util.Msg;
import ghidra.util.Swing;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.filechooser.GhidraFileChooserModel;
import ghidra.util.filechooser.GhidraFileFilter;
import ghidra.util.task.*;
import ghidra.xml.XmlParseException;
public class SpecExtensionPanel extends JPanel {
private ProgramDB program;
private PropertyChangeListener listener;
private boolean unappliedChanges;
private SpecExtension specExtension;
private List<CompilerElement> tableElements;
private ExtensionTableModel tableModel;
private GTable extensionTable;
private JButton exportButton;
private JButton removeButton;
private ListSelectionModel selectionModel;
private final static int EXTENSION_TYPE_COLUMN = 0;
private final static int NAME_COLUMN = 1;
private final static int STATUS_COLUMN = 2;
/**
* Status of a particular compiler specification element
*/
public enum Status {
// The order is used to sort the table
CORE("core"), // A core element (cannot be deleted)
EXTENSION("extension"), // An extension thats already present (and won't be changed)
EXTENSION_ERROR("extension(parse error)"), // An extension (already present) that didn't parse
EXTENSION_INSTALL("install"), // A pending extension to be installed
EXTENSION_REPLACE("replace"), // A pending extension replacing existing
EXTENSION_REMOVE("remove"), // An extension to be removed
EXTENSION_OVERRIDE("override"), // An extension overriding a core module
EXTENSION_OVERPENDING("override pending"); // A pending extension which overrides
private String formalName;
private Status(String nm) {
formalName = nm;
}
}
private static final String LAST_EXPORT_DIRECTORY = "LastSpecificationExportDirectory";
public static final String PREFERENCES_FILE_EXTENSION = ".xml";
private static final GhidraFileFilter FILE_FILTER = new GhidraFileFilter() {
@Override
public boolean accept(File pathname, GhidraFileChooserModel model) {
return (pathname.isDirectory()) ||
(pathname.getName().endsWith(PREFERENCES_FILE_EXTENSION));
}
@Override
public String getDescription() {
return "Specification XML Files";
}
};
/**
* A row in the table of compiler spec elements
*/
private static class CompilerElement implements Comparable<CompilerElement> {
String name;
String optionName;
SpecExtension.Type type;
Status status;
String xmlString;
public CompilerElement(String nm, SpecExtension.Type tp, Status st) {
name = nm;
type = tp;
optionName = type.getOptionName(name);
status = st;
xmlString = null;
}
/**
* Return true if the element is already installed (not pending)
* @return true for an existing extension
*/
public boolean isExisting() {
return (status == Status.CORE || status == Status.EXTENSION ||
status == Status.EXTENSION_ERROR || status == Status.EXTENSION_OVERRIDE);
}
@Override
public int compareTo(CompilerElement o) {
if (type != o.type) {
return type.ordinal() - o.type.ordinal();
}
if (status != o.status) {
return status.ordinal() - o.status.ordinal();
}
return name.compareTo(o.name);
}
}
/**
* Selection listener class for the table model.
*/
private class TableSelectionListener implements ListSelectionListener {
@Override
public void valueChanged(ListSelectionEvent e) {
if (e.getValueIsAdjusting()) {
return;
}
CompilerElement compilerElement = getSelectedCompilerElement();
if (compilerElement == null) {
removeButton.setEnabled(false);
exportButton.setEnabled(false);
return;
}
boolean rowExisting = compilerElement.isExisting();
removeButton.setEnabled(rowExisting && compilerElement.status != Status.CORE);
exportButton.setEnabled(rowExisting);
}
}
private class ExtensionTableModel extends AbstractGTableModel<CompilerElement> {
private final String[] columnNames = { "Extension Type", "Name", "Status" };
@Override
public String getColumnName(int column) {
return columnNames[column];
}
@Override
public int getColumnCount() {
return columnNames.length;
}
@Override
public String getName() {
return "Compiler Specification Elements";
}
@Override
public List<CompilerElement> getModelData() {
return tableElements;
}
@Override
public Object getColumnValueForRow(CompilerElement t, int columnIndex) {
switch (columnIndex) {
case EXTENSION_TYPE_COLUMN:
return t.type.getTagName();
case NAME_COLUMN:
return t.name;
case STATUS_COLUMN:
if (t.status == Status.CORE) {
return "";
}
return t.status.formalName;
}
return "Unknown column!";
}
}
private class CompilerElementTable extends GTable {
private ElementRenderer renderer;
CompilerElementTable(TableModel model) {
super(model);
renderer = new ElementRenderer();
}
@Override
public TableCellRenderer getCellRenderer(int row, int col) {
return renderer;
}
}
private class ElementRenderer extends GTableCellRenderer {
@Override
public Component getTableCellRendererComponent(GTableCellRenderingData data) {
super.getTableCellRendererComponent(data);
if (data.isSelected()) {
return this;
}
int row = data.getRowViewIndex();
CompilerElement compilerElement = tableModel.getRowObject(row);
if (compilerElement.status == Status.EXTENSION_ERROR) {
setBackground(Color.pink);
}
return this;
}
}
/**
* Task for applying any accumulated changes in the list of CompilerElements for this Panel to the Program.
*/
public class ChangeExtensionTask extends Task {
public ChangeExtensionTask() {
super("Committing extension changes", true, true, true);
}
@Override
public void run(TaskMonitor monitor) {
try {
for (CompilerElement element : tableElements) {
switch (element.status) {
case CORE:
case EXTENSION:
case EXTENSION_ERROR:
case EXTENSION_OVERRIDE:
break; // Unchanged
case EXTENSION_REMOVE:
specExtension.removeCompilerSpecExtension(element.optionName, monitor);
break;
case EXTENSION_INSTALL:
case EXTENSION_REPLACE:
case EXTENSION_OVERPENDING:
specExtension.addReplaceCompilerSpecExtension(element.xmlString,
monitor);
break;
}
}
}
catch (LockException ex) {
Msg.showError(this, null, "Missing Exclusive Access",
"Do not have exclusive acces");
}
catch (XmlParseException | SAXException ex) {
Msg.showError(this, null, "Failed Committing Extension Changes", ex.getMessage());
}
catch (CancelledException ex) {
// User cancelled
}
}
}
private void populateElementTable() {
tableElements = new ArrayList<>();
CompilerSpec compilerSpec = program.getCompilerSpec();
PrototypeModel[] models = compilerSpec.getAllModels();
for (PrototypeModel model : models) {
SpecExtension.Type type = SpecExtension.Type.PROTOTYPE_MODEL;
Status status = Status.CORE;
if (model.isProgramExtension()) {
status = model.isErrorPlaceholder() ? Status.EXTENSION_ERROR : Status.EXTENSION;
}
if (model instanceof PrototypeModelMerged) {
type = SpecExtension.Type.MERGE_MODEL;
}
CompilerElement compEl = new CompilerElement(model.getName(), type, status);
tableElements.add(compEl);
}
PcodeInjectLibrary injectLibrary = compilerSpec.getPcodeInjectLibrary();
String[] callFixupNames = injectLibrary.getCallFixupNames();
for (String fixupName : callFixupNames) {
SpecExtension.Type type = SpecExtension.Type.CALL_FIXUP;
Status status = Status.CORE;
if (injectLibrary.hasProgramPayload(fixupName, InjectPayload.CALLFIXUP_TYPE)) {
status = Status.EXTENSION;
if (injectLibrary.getPayload(InjectPayload.CALLFIXUP_TYPE, fixupName)
.isErrorPlaceholder()) {
status = Status.EXTENSION_ERROR;
}
}
CompilerElement compEl = new CompilerElement(fixupName, type, status);
tableElements.add(compEl);
}
String[] callOtherNames = injectLibrary.getCallotherFixupNames();
for (String fixupName : callOtherNames) {
SpecExtension.Type type = SpecExtension.Type.CALLOTHER_FIXUP;
Status status = Status.CORE;
if (injectLibrary.hasProgramPayload(fixupName, InjectPayload.CALLOTHERFIXUP_TYPE)) {
status = Status.EXTENSION;
if (injectLibrary.isOverride(fixupName, InjectPayload.CALLOTHERFIXUP_TYPE)) {
status = Status.EXTENSION_OVERRIDE;
}
if (injectLibrary.getPayload(InjectPayload.CALLFIXUP_TYPE, fixupName)
.isErrorPlaceholder()) {
status = Status.EXTENSION_ERROR;
}
}
CompilerElement compEl = new CompilerElement(fixupName, type, status);
tableElements.add(compEl);
}
tableElements.sort(null);
}
private void addListeners() {
selectionModel = extensionTable.getSelectionModel();
selectionModel.addListSelectionListener(new TableSelectionListener());
}
SpecExtensionPanel(ProgramDB program, PropertyChangeListener listener) {
this.program = program;
this.listener = listener;
unappliedChanges = false;
specExtension = new SpecExtension(program);
createPanel();
populateElementTable();
addListeners();
}
public void apply(TaskMonitor monitor) {
ChangeExtensionTask task = new ChangeExtensionTask();
new TaskLauncher(task, this);
populateElementTable();
changesMade(false);
tableModel.fireTableDataChanged();
}
/**
* Cancel any pending changes and reload the current table
*/
public void cancel() {
populateElementTable();
tableModel.fireTableDataChanged();
}
/**
* Size the columns.
*/
private void adjustTableColumns() {
extensionTable.doLayout();
TableColumn column =
extensionTable.getColumn(extensionTable.getColumnName(EXTENSION_TYPE_COLUMN));
column.setPreferredWidth(100);
column = extensionTable.getColumn(extensionTable.getColumnName(NAME_COLUMN));
column.setPreferredWidth(250);
column = extensionTable.getColumn(extensionTable.getColumnName(STATUS_COLUMN));
column.setPreferredWidth(150);
}
private void createPanel() {
setLayout(new BorderLayout(10, 10));
tableModel = new ExtensionTableModel();
extensionTable = new CompilerElementTable(tableModel);
JScrollPane sp = new JScrollPane(extensionTable);
extensionTable.setPreferredScrollableViewportSize(new Dimension(400, 100));
extensionTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
adjustTableColumns();
JPanel centerPanel = new JPanel(new BorderLayout());
JPanel lowerPanel = createButtonPanel();
centerPanel.add(sp, BorderLayout.CENTER);
add(centerPanel, BorderLayout.CENTER);
add(lowerPanel, BorderLayout.SOUTH);
}
private static File getStartingDir() {
String lastDirectoryPath = Preferences.getProperty(LAST_EXPORT_DIRECTORY);
if (lastDirectoryPath != null) {
return new File(lastDirectoryPath);
}
return new File(System.getProperty("user.home"));
}
private static File getFileFromUser(String suggestedName) {
KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
Component activeComponent = kfm.getActiveWindow();
GhidraFileChooser fileChooser = new GhidraFileChooser(activeComponent);
fileChooser.setTitle("Please Select A File");
fileChooser.setFileFilter(FILE_FILTER);
fileChooser.setApproveButtonText("OK");
File startDir = getStartingDir();
if (suggestedName != null) {
fileChooser.setSelectedFile(new File(startDir, suggestedName));
}
else {
fileChooser.setCurrentDirectory(startDir);
}
File selectedFile = fileChooser.getSelectedFile();
// make sure the file has the correct extension
if ((selectedFile != null) &&
!selectedFile.getName().endsWith(PREFERENCES_FILE_EXTENSION)) {
selectedFile = new File(selectedFile.getAbsolutePath() + PREFERENCES_FILE_EXTENSION);
}
// save off the last location to which the user navigated so we can
// return them to that spot if they use the dialog again.
Preferences.setProperty(LAST_EXPORT_DIRECTORY,
fileChooser.getCurrentDirectory().getAbsolutePath());
return selectedFile;
}
private static String fileToString(File file) throws IOException {
FileReader inputReader = new FileReader(file);
BufferedReader reader = new BufferedReader(inputReader);
try {
StringBuffer buffer = new StringBuffer();
String line = null;
while ((line = reader.readLine()) != null) {
buffer.append(line);
buffer.append('\n');
}
return buffer.toString();
}
finally {
reader.close();
}
}
private int findMatch(SpecExtension.Type type, String name) {
for (int i = 0; i < tableElements.size(); ++i) {
CompilerElement el = tableElements.get(i);
if (el.name.equals(name) && el.type == type) {
return i;
}
}
return -1;
}
// signals that there are unapplied changes
private void changesMade(boolean changes) {
listener.propertyChange(
new PropertyChangeEvent(this, "apply.enabled", unappliedChanges, changes));
unappliedChanges = changes;
}
/**
* Present a file chooser, then
* - Load the file as a String
* - Test the validity of the file as an XML document describing an extension
* - Create a new CompilerElement representing the extension OR
* - Mark an existing CompilerElement as being overwritten with the new document
*/
private void importExtension() {
if (!program.hasExclusiveAccess()) {
Msg.showError(this, this, "Import Failure",
"Must have an exclusive checkout to import a new extension");
return;
}
File file = getFileFromUser(null);
if (file == null) {
return;
}
String document;
DocInfo docInfo = null;
Exception errMessage = null;
try {
document = fileToString(file).trim();
docInfo = specExtension.testExtensionDocument(document);
int pos = findMatch(docInfo.getType(), docInfo.getFormalName());
Status status = Status.EXTENSION_INSTALL;
if (pos >= 0) {
CompilerElement previousEl = tableElements.get(pos);
switch (previousEl.status) {
case CORE:
if (!docInfo.isOverride()) {
throw new DuplicateNameException(
"Cannot override core extension: " + previousEl.name);
}
status = Status.EXTENSION_OVERPENDING;
break;
case EXTENSION:
case EXTENSION_ERROR:
case EXTENSION_REMOVE:
case EXTENSION_REPLACE:
status = Status.EXTENSION_REPLACE;
break;
case EXTENSION_OVERRIDE:
case EXTENSION_OVERPENDING:
status = Status.EXTENSION_OVERPENDING;
break;
case EXTENSION_INSTALL:
break;
}
}
CompilerElement newEl =
new CompilerElement(docInfo.getFormalName(), docInfo.getType(), status);
newEl.xmlString = document;
if (pos >= 0) {
tableElements.set(pos, newEl);
}
else {
tableElements.add(newEl);
}
tableElements.sort(null);
changesMade(true);
tableModel.fireTableDataChanged();
}
catch (Exception e) {
errMessage = e;
}
if (errMessage != null) {
Msg.showError(this, this, "Import Failure", errMessage.getMessage(), errMessage);
return;
}
}
private String getXmlString(CompilerElement element) {
CompilerSpec compilerSpec = program.getCompilerSpec();
PcodeInjectLibrary injectLibrary = compilerSpec.getPcodeInjectLibrary();
InjectPayload payload;
PrototypeModel model;
String resultString = null;
if (element.status == Status.CORE) {
StringBuilder buffer = new StringBuilder();
switch (element.type) {
case CALL_FIXUP:
payload = injectLibrary.getPayload(InjectPayload.CALLFIXUP_TYPE, element.name);
if (payload != null) {
payload.saveXml(buffer);
}
break;
case CALLOTHER_FIXUP:
payload =
injectLibrary.getPayload(InjectPayload.CALLOTHERFIXUP_TYPE, element.name);
if (payload != null) {
payload.saveXml(buffer);
}
break;
case PROTOTYPE_MODEL:
case MERGE_MODEL:
model = compilerSpec.getCallingConvention(element.name);
if (model != null) {
model.saveXml(buffer, injectLibrary);
}
break;
}
resultString = buffer.toString();
if (resultString.length() == 0) {
resultString = null;
}
}
else {
resultString =
SpecExtension.getCompilerSpecExtension(program, element.type, element.name);
}
return resultString;
}
private void exportExtension() {
CompilerElement compilerElement = getSelectedCompilerElement();
if (compilerElement == null) {
return;
}
if (!compilerElement.isExisting()) {
return; // Only export existing elements
}
String suggestedName = compilerElement.name + PREFERENCES_FILE_EXTENSION;
File outputFile = getFileFromUser(suggestedName);
if (outputFile == null) {
return;
}
if (outputFile.exists()) {
int userChoice = OptionDialog.showYesNoDialog(this, "File exists.",
"Overwrite " + outputFile.getName() + " ?");
if (userChoice != OptionDialog.OPTION_ONE) {
return;
}
}
String exportString = getXmlString(compilerElement);
String errMessage = null;
if (exportString == null) {
errMessage = "Unable to build document for " + compilerElement.name;
}
else {
FileWriter writer = null;
try {
writer = new FileWriter(outputFile);
writer.write(exportString);
writer.close();
}
catch (IOException ex) {
errMessage = "Failed to write to file: " + ex.getMessage();
}
}
if (errMessage != null) {
Msg.showError(this, this, "Export Failure", errMessage);
}
}
/**
* Present the user with a confirmation dialog. If confirmed, mark
* the selected element for removal.
*/
private void removeExtension() {
if (!program.hasExclusiveAccess()) {
Msg.showError(this, this, "Remove Failure",
"Must have an exclusive checkout to remove an extension");
return;
}
CompilerElement compilerElement = getSelectedCompilerElement();
if (compilerElement == null) {
return;
}
if (compilerElement.status == Status.EXTENSION ||
compilerElement.status == Status.EXTENSION_ERROR) {
int userChoice = OptionDialog.showYesNoDialog(this, "Remove Extension?",
"Mark the extension " + compilerElement.name + " for removal?");
if (userChoice != OptionDialog.OPTION_ONE) {
return;
}
}
else if (compilerElement.status == Status.EXTENSION_OVERRIDE) {
int userChoice = OptionDialog.showYesNoDialog(this, "Remove Override?",
"Mark the override " + compilerElement.name + " for removal?");
if (userChoice != OptionDialog.OPTION_ONE) {
return;
}
}
else {
return;
}
compilerElement.status = Status.EXTENSION_REMOVE;
extensionTable.clearSelection();
changesMade(true);
tableModel.fireTableDataChanged();
}
private CompilerElement getSelectedCompilerElement() {
if (selectionModel.isSelectionEmpty()) {
return null;
}
int selectedRow = extensionTable.getSelectedRow();
return tableElements.get(selectedRow);
}
private JPanel createButtonPanel() {
JButton importButton = new JButton("Import...");
importButton.setToolTipText("Load extension from an XML file");
importButton.addActionListener(event -> {
// give Swing a chance to repaint
Swing.runLater(() -> {
extensionTable.clearSelection();
importExtension();
});
});
exportButton = new JButton("Export...");
exportButton.setToolTipText("Export extensions to an XML file");
exportButton.addActionListener(event -> {
// give Swing a chance to repaint
Swing.runLater(() -> {
exportExtension();
});
});
removeButton = new JButton("Remove");
removeButton.setToolTipText("Remove an existing extension");
removeButton.addActionListener(event -> {
// give Swing a chance to repaint
Swing.runLater(() -> {
removeExtension();
});
});
JPanel containerPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
containerPanel.add(importButton);
containerPanel.add(exportButton);
containerPanel.add(removeButton);
return containerPanel;
}
}

View file

@ -1,6 +1,5 @@
/* ### /* ###
* IP: GHIDRA * IP: GHIDRA
* REVIEWED: YES
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -20,47 +19,90 @@
*/ */
package ghidra.app.plugin.processors.sleigh.template; package ghidra.app.plugin.processors.sleigh.template;
import java.util.ArrayList;
import ghidra.program.model.address.AddressFactory; import ghidra.program.model.address.AddressFactory;
import ghidra.program.model.lang.UnknownInstructionException; import ghidra.program.model.lang.UnknownInstructionException;
import ghidra.util.xml.SpecXmlUtils; import ghidra.util.xml.SpecXmlUtils;
import ghidra.xml.XmlElement; import ghidra.xml.XmlElement;
import ghidra.xml.XmlPullParser; import ghidra.xml.XmlPullParser;
import java.util.ArrayList;
/** /**
* A constructor template, representing the semantic action of a SLEIGH constructor, without
* its final context. The constructor template is made up of a list of p-code op templates,
* which are in turn made up of varnode templates.
* This is one step removed from the final array of PcodeOp objects, but:
* - Constants may still need to incorporate context dependent address resolution and relative offsets.
* - Certain p-code operations may still need expansion to include a dynamic LOAD or STORE operation.
* - The list may hold "build" directives for sub-constructor templates.
* - The list may still hold "label" information for the final resolution of relative jump offsets.
* *
* * The final PcodeOps are produced by handing this to the build() method of PcodeEmit which has
* Placeholder for what resolves to a list of PcodeOps and * the InstructionContext necessary for final resolution.
* a FixedHandle. It represents the semantic action of a constructor
* and its return value for a particular InstructionContext
*/ */
public class ConstructTpl { public class ConstructTpl {
private int numlabels=0; private int numlabels = 0; // Number of relative-offset labels in this template
private OpTpl[] vec; // The semantic action of constructor private OpTpl[] vec; // The semantic action of constructor
private HandleTpl result; // The final semantic value private HandleTpl result; // The final semantic value
/**
* Constructor for use with restoreXML
*/
public ConstructTpl() { public ConstructTpl() {
} }
public int getNumLabels() { return numlabels; } /**
public OpTpl[] getOpVec() { return vec; } * Manually build a constructor template. This is useful for building constructor templates
public HandleTpl getResult() { return result; } * outside of the normal SLEIGH pipeline, as for an internally created InjectPayload.
* @param opvec is the list of p-code op templates making up the constructor
*/
public ConstructTpl(OpTpl[] opvec) {
vec = opvec;
result = null;
}
public int restoreXml(XmlPullParser parser,AddressFactory factory) throws UnknownInstructionException { /**
* @return the number of labels needing resolution in this template
*/
public int getNumLabels() {
return numlabels;
}
/**
* @return the list of p-code op templates making up this constructor template
*/
public OpTpl[] getOpVec() {
return vec;
}
/**
* @return the (possibly dynamic) location of the final semantic value produced by this constructor
*/
public HandleTpl getResult() {
return result;
}
/**
* Restore this template from a \<construct_tpl> tag in an XML stream.
* @param parser is the XML stream
* @param factory is for manufacturing Address objects
* @return the constructor section id described by the tag
* @throws UnknownInstructionException if the p-code templates contain unknown op-codes
*/
public int restoreXml(XmlPullParser parser, AddressFactory factory)
throws UnknownInstructionException {
int sectionid = -1; int sectionid = -1;
XmlElement el = parser.start("construct_tpl"); XmlElement el = parser.start("construct_tpl");
// String delaystr = el.getAttribute("delay");
// if (delaystr != null)
// delayslot = SpecXmlUtils.decodeInt(delaystr);
String nmlabelstr = el.getAttribute("labels"); String nmlabelstr = el.getAttribute("labels");
if (nmlabelstr != null) if (nmlabelstr != null) {
numlabels = SpecXmlUtils.decodeInt(nmlabelstr); numlabels = SpecXmlUtils.decodeInt(nmlabelstr);
}
String sectionidstr = el.getAttribute("section"); String sectionidstr = el.getAttribute("section");
if (sectionidstr != null) if (sectionidstr != null) {
sectionid = SpecXmlUtils.decodeInt(sectionidstr); sectionid = SpecXmlUtils.decodeInt(sectionidstr);
}
XmlElement handel = parser.peek(); XmlElement handel = parser.peek();
if (handel.getName().equals("null")) { if (handel.getName().equals("null")) {
result = null; result = null;
@ -68,12 +110,12 @@ public class ConstructTpl {
} }
else { else {
result = new HandleTpl(); result = new HandleTpl();
result.restoreXml(parser,factory); result.restoreXml(parser, factory);
} }
ArrayList<Object> oplist = new ArrayList<Object>(); ArrayList<Object> oplist = new ArrayList<>();
while(!parser.peek().isEnd()) { while (!parser.peek().isEnd()) {
OpTpl op = new OpTpl(); OpTpl op = new OpTpl();
op.restoreXml(parser,factory); op.restoreXml(parser, factory);
oplist.add(op); oplist.add(op);
} }
vec = new OpTpl[oplist.size()]; vec = new OpTpl[oplist.size()];

View file

@ -0,0 +1,374 @@
/* ###
* 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.program.database;
import java.beans.PropertyEditor;
import java.util.*;
import docking.options.editor.StringWithChoicesEditor;
import generic.stl.Pair;
import ghidra.framework.options.OptionType;
import ghidra.framework.options.Options;
import ghidra.program.model.lang.*;
import ghidra.program.model.listing.Program;
import ghidra.util.*;
/**
* A Program specific version of the CompilerSpec.
*
* Every Program owns a specific ProgramCompilerSpec. It is based on a CompilerSpec
* returned by the Language assigned to the Program, but it may include extensions.
* Extensions are currently either a new form of:
* - PrototypeModel or
* - InjectPayload
*
* Extensions can be installed or removed from a ProgramDB via the Options mechanism (See SpecExtension)
* using SpecExtension.addReplaceCompilerSpecExtension() or SpecExtension.removeCompilerSpecExtension().
*
* ProgramCompilerSpec allows the static evaluation models, described by the underlying BasicCompilerSpec
* and returned by getPrototypeEvaluationModel(), to be overridden by Program specific options.
*
* getDecompilerOutputLanguage() queries the Program specific language the decompiler should use as output.
*
* installExtensions() is the main entry point for integrating the Program Options
* with the Language's base CompilerSpec and producing a complete in-memory CompilerSpec for the Program.
*
*/
public class ProgramCompilerSpec extends BasicCompilerSpec {
public static final String DECOMPILER_PROPERTY_LIST_NAME = "Decompiler";
public static final String DECOMPILER_OUTPUT_LANGUAGE = "Output Language";
public final static DecompilerLanguage DECOMPILER_OUTPUT_DEF = DecompilerLanguage.C_LANGUAGE;
public final static String DECOMPILER_OUTPUT_DESC =
"Select the source language output by the decompiler.";
public static final String EVALUATION_MODEL_PROPERTY_NAME = "Prototype Evaluation";
private Program program; // Program owning this compiler spec
private Map<String, PrototypeModel> usermodels = null;
private int versionCounter = 0; // Version number among all cspec variants for the same Program
/**
* Construct the CompilerSpec for a Program based on a Language CompilerSpec
* @param program is the Program
* @param langSpec is the CompilerSpec from Language to base this on
*/
public ProgramCompilerSpec(Program program, CompilerSpec langSpec) {
super((BasicCompilerSpec) langSpec);
this.program = program;
if (langSpec instanceof ProgramCompilerSpec) {
throw new IllegalArgumentException(
"Cannot instantiate ProgramCompilerSpec from another ProgramCompilerSpec");
}
}
/**
* Adds and enables an option to have the decompiler display java.
* @param program to be enabled
*/
public static void enableJavaLanguageDecompilation(Program program) {
Options decompilerPropertyList = program.getOptions(DECOMPILER_PROPERTY_LIST_NAME);
decompilerPropertyList.registerOption(DECOMPILER_OUTPUT_LANGUAGE, DECOMPILER_OUTPUT_DEF,
null, DECOMPILER_OUTPUT_DESC);
decompilerPropertyList.setEnum(DECOMPILER_OUTPUT_LANGUAGE,
DecompilerLanguage.JAVA_LANGUAGE);
}
@Override
public DecompilerLanguage getDecompilerOutputLanguage() {
Options options = program.getOptions(DECOMPILER_PROPERTY_LIST_NAME);
if (options.contains(DECOMPILER_OUTPUT_LANGUAGE)) {
return options.getEnum(DECOMPILER_OUTPUT_LANGUAGE, DECOMPILER_OUTPUT_DEF);
}
return DECOMPILER_OUTPUT_DEF;
}
/**
* Install a new set of user-defined (extension) prototype models.
* All the models from the compiler spec are preserved. Any old user-defined
* models are removed or replaced.
* @param extensions is the list of new user-defined models
*/
private void installPrototypeExtensions(List<PrototypeModel> extensions) {
if (usermodels == null) {
if (extensions.isEmpty()) {
return; // No change to prototypes
}
usermodels = new TreeMap<>();
}
ArrayList<PrototypeModel> finalList = new ArrayList<>();
TreeSet<String> currentNames = new TreeSet<>();
for (PrototypeModel model : allmodels) {
currentNames.add(model.getName());
if (usermodels.containsKey(model.getName())) {
continue;
}
finalList.add(model); // Add original non-userdef models
}
for (PrototypeModel model : extensions) {
if (currentNames.contains(model.getName())) {
if (!usermodels.containsKey(model.getName())) {
Msg.warn(this,
"Cannot override prototype model " + model.getName() + " with extension");
continue;
}
}
markPrototypeAsExtension(model);
finalList.add(model);
usermodels.put(model.getName(), model);
}
String defaultName = null;
String evalName = null;
String evalCalledName = null;
if (defaultModel != null) {
defaultName = defaultModel.getName();
}
if (evalCurrentModel != null) {
evalName = evalCurrentModel.getName();
}
if (evalCalledModel != null) {
evalCalledName = evalCalledModel.getName();
}
modelXrefs(finalList, defaultName, evalName, evalCalledName);
if (usermodels.isEmpty()) {
usermodels = null;
}
}
/**
* Add a new PrototypeModel to the list of extensions with errors
* @param errList is the list of errors
* @param model is the PrototypeModel with errors
* @return the updated list
*/
private static ArrayList<String> addPrototypeError(ArrayList<String> errList,
PrototypeModel model) {
if (model.isErrorPlaceholder()) {
if (errList == null) {
errList = new ArrayList<>();
}
else if (errList.size() > 4) { // Only accumulate up to 5
errList.add("...");
return errList;
}
String message = "prototype: " + model.getName();
errList.add(message);
}
return errList;
}
/**
* Add a new InjectPayload to the list of extensions with errors
* @param errList is the list of errors
* @param payload is the InjectPayload with errors
* @return the updated list
*/
private static ArrayList<String> addPayloadError(ArrayList<String> errList,
InjectPayload payload) {
if (payload.isErrorPlaceholder()) {
if (errList == null) {
errList = new ArrayList<>();
}
else if (errList.size() > 4) { // Only accumulate up to 5
errList.add("...");
return errList;
}
String message;
if (payload instanceof InjectPayloadCallfixup) {
message = "callfixup: " + payload.getName();
}
else {
message = "callotherfixup: " + payload.getName();
}
errList.add(message);
}
return errList;
}
/**
* Update the choices presented for evaluation model program option.
*/
private void updateModelChoices() {
Options decompilerPropertyList = program.getOptions(DECOMPILER_PROPERTY_LIST_NAME);
PropertyEditor editor =
decompilerPropertyList.getRegisteredPropertyEditor(EVALUATION_MODEL_PROPERTY_NAME);
if (editor == null) {
return;
}
if (!(editor instanceof StringWithChoicesEditor)) {
return;
}
String[] evalChoices = establishEvaluationModelChoices(evalCurrentModel);
StringWithChoicesEditor choiceEditor = (StringWithChoicesEditor) editor;
choiceEditor.setChoices(evalChoices);
}
/**
* Report any extensions that have parse errors
* @param errorList is the list of extensions (or null)
*/
private void reportExtensionErrors(ArrayList<String> errorList) {
if (errorList == null) {
return;
}
StringBuilder buffer = new StringBuilder();
buffer.append("<HTML>User-defined extensions failed to parse: ");
buffer.append("<ul>");
for (String line : errorList) {
buffer.append("<li>").append(line).append("</li>");
}
buffer.append("</ul>");
buffer.append("See Program Options - Specification Extensions</HTML>");
Msg.showError(BasicCompilerSpec.class, null, "Specification Extension Errors",
buffer.toString());
}
/**
* Update this object with any program specific compiler specification extensions.
*/
protected void installExtensions() {
int storedVersion = SpecExtension.getVersionCounter(program);
if (storedVersion == versionCounter) {
return; // We currently match stored version, nothing to update
}
versionCounter = storedVersion; // Update ourselves to stored version
List<Pair<String, String>> pairList = SpecExtension.getCompilerSpecExtensions(program);
if (pairList.isEmpty() && usermodels == null && pcodeInject.getProgramPayloads() == null) {
return; // No change
}
if (usermodels != null) {
removeProgramMechanismPayloads(usermodels.values());
}
ArrayList<PrototypeModel> modelExtensions = new ArrayList<>();
ArrayList<InjectPayloadSleigh> injectExtensions = new ArrayList<>();
ArrayList<String> errorList = null;
for (Pair<String, String> pair : pairList) {
try {
Object obj = SpecExtension.parseExtension(pair.first, pair.second, this, true);
if (obj instanceof PrototypeModel) {
PrototypeModel prototypeModel = (PrototypeModel) obj;
modelExtensions.add(prototypeModel);
errorList = addPrototypeError(errorList, prototypeModel);
}
else if (obj instanceof InjectPayloadSleigh) {
InjectPayloadSleigh payload = (InjectPayloadSleigh) obj;
injectExtensions.add(payload);
errorList = addPayloadError(errorList, payload);
}
}
catch (Exception e) {
Msg.error(this,
"Bad compiler spec extension: " + pair.first + " - " + e.getMessage());
}
}
installPrototypeExtensions(modelExtensions);
registerProgramInject(injectExtensions);
updateModelChoices();
reportExtensionErrors(errorList);
}
/**
* Build up the choice strings for all the evaluation methods
*/
private String[] establishEvaluationModelChoices(PrototypeModel defaultEval) {
String[] evalChoices = new String[allmodels.length];
// Make sure the default evaluation model occurs at the top of the list
int defaultnum = -1;
for (int i = 0; i < allmodels.length; ++i) {
PrototypeModel curModel = allmodels[i];
evalChoices[i] = curModel.getName();
if (curModel == defaultEval) {
defaultnum = i;
}
}
if (defaultnum > 0) {
String tmp = evalChoices[defaultnum];
for (int i = defaultnum; i > 0; --i) {
// Push everybody down to make room for default at top
evalChoices[i] = evalChoices[i - 1];
}
evalChoices[0] = tmp;
}
return evalChoices;
}
@Override
public PrototypeModel getPrototypeEvaluationModel(EvaluationModelType modelType) {
Options options = program.getOptions(DECOMPILER_PROPERTY_LIST_NAME);
switch (modelType) {
case EVAL_CURRENT:
String name =
options.getString(EVALUATION_MODEL_PROPERTY_NAME, evalCurrentModel.getName());
for (PrototypeModel model : allmodels) { // Could be a merge model
if (model.getName().equals(name)) {
return model;
}
}
break;
case EVAL_CALLED:
return evalCalledModel; // TODO: Currently no option
}
return null;
}
/**
* Register program-specific compiler-spec options
*/
protected void registerProgramOptions() {
// NOTE: Any changes to the option name/path must be handled carefully since
// old property values will remain in the program. There is currently no support
// for upgrading/moving old property values.
String[] evalChoices = establishEvaluationModelChoices(evalCurrentModel);
Options decompilerPropertyList = program.getOptions(DECOMPILER_PROPERTY_LIST_NAME);
decompilerPropertyList
.setOptionsHelpLocation(new HelpLocation("DecompilePlugin", "ProgramOptions"));
decompilerPropertyList.registerOption(EVALUATION_MODEL_PROPERTY_NAME,
OptionType.STRING_TYPE, evalChoices[0],
new HelpLocation("DecompilePlugin", "OptionProtoEval"),
"Select the default function prototype/evaluation model to be used during Decompiler analysis",
new StringWithChoicesEditor(evalChoices));
// TODO: registration of DECOMPILER_OUTPUT_LANGUAGE option should be tied to Processor
// and not presence of stored option.
if (decompilerPropertyList.contains(DECOMPILER_OUTPUT_LANGUAGE)) {
decompilerPropertyList.registerOption(DECOMPILER_OUTPUT_LANGUAGE, DECOMPILER_OUTPUT_DEF,
null, DECOMPILER_OUTPUT_DESC);
}
Options analysisPropertyList =
program.getOptions(Program.ANALYSIS_PROPERTIES + ".Decompiler Parameter ID");
analysisPropertyList.createAlias(EVALUATION_MODEL_PROPERTY_NAME, decompilerPropertyList,
EVALUATION_MODEL_PROPERTY_NAME);
}
@Override
public boolean equals(Object obj) {
if (!super.equals(obj)) {
return false;
}
ProgramCompilerSpec op2 = (ProgramCompilerSpec) obj;
if (!SystemUtilities.isEqual(usermodels, op2.usermodels)) {
return false;
}
return true;
}
}

View file

@ -98,8 +98,9 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
* created tables. * created tables.
* 18-Feb-2021 - version 23 Added support for Big Reflist for tracking FROM references. * 18-Feb-2021 - version 23 Added support for Big Reflist for tracking FROM references.
* Primarily used for large numbers of Entry Point references. * Primarily used for large numbers of Entry Point references.
* 31-Mar-2021 - version 24 Added support for CompilerSpec extensions
*/ */
static final int DB_VERSION = 23; static final int DB_VERSION = 24;
/** /**
* UPGRADE_REQUIRED_BFORE_VERSION should be changed to DB_VERSION anytime the * UPGRADE_REQUIRED_BFORE_VERSION should be changed to DB_VERSION anytime the
@ -189,7 +190,7 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
private ProgramUserDataDB programUserData; private ProgramUserDataDB programUserData;
private Table table; private Table table;
private Language language; private Language language;
private CompilerSpec compilerSpec; private ProgramCompilerSpec compilerSpec;
private LanguageID languageID; private LanguageID languageID;
private CompilerSpecID compilerSpecID; private CompilerSpecID compilerSpecID;
@ -219,7 +220,7 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
super(new DBHandle(), name, 500, 1000, consumer); super(new DBHandle(), name, 500, 1000, consumer);
this.language = language; this.language = language;
this.compilerSpec = compilerSpec; this.compilerSpec = new ProgramCompilerSpec(this, compilerSpec);
languageID = language.getLanguageID(); languageID = language.getLanguageID();
compilerSpecID = compilerSpec.getCompilerSpecID(); compilerSpecID = compilerSpec.getCompilerSpecID();
@ -244,7 +245,7 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
programUserData = new ProgramUserDataDB(this); programUserData = new ProgramUserDataDB(this);
endTransaction(id, true); endTransaction(id, true);
clearUndo(false); clearUndo(false);
compilerSpec.registerProgramOptions(this); this.compilerSpec.registerProgramOptions();
getCodeManager().activateContextLocking(); getCodeManager().activateContextLocking();
success = true; success = true;
} }
@ -353,7 +354,9 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
recordChanges = true; recordChanges = true;
endTransaction(id, true); endTransaction(id, true);
clearUndo(false); clearUndo(false);
compilerSpec.registerProgramOptions(this); SpecExtension.checkFormatVersion(this);
compilerSpec.installExtensions();
compilerSpec.registerProgramOptions();
getCodeManager().activateContextLocking(); getCodeManager().activateContextLocking();
success = true; success = true;
} }
@ -374,12 +377,13 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
* @throws CompilerSpecNotFoundException if the compiler spec cannot be found * @throws CompilerSpecNotFoundException if the compiler spec cannot be found
*/ */
private void initCompilerSpec() throws CompilerSpecNotFoundException { private void initCompilerSpec() throws CompilerSpecNotFoundException {
CompilerSpec langSpec;
try { try {
if (languageUpgradeTranslator != null) { if (languageUpgradeTranslator != null) {
compilerSpec = languageUpgradeTranslator.getOldCompilerSpec(compilerSpecID); langSpec = languageUpgradeTranslator.getOldCompilerSpec(compilerSpecID);
} }
else { else {
compilerSpec = language.getCompilerSpecByID(compilerSpecID); langSpec = language.getCompilerSpecByID(compilerSpecID);
} }
} }
catch (CompilerSpecNotFoundException e) { catch (CompilerSpecNotFoundException e) {
@ -387,12 +391,13 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
"Compiler Spec " + compilerSpecID + " for Language " + "Compiler Spec " + compilerSpecID + " for Language " +
language.getLanguageDescription().getDescription() + language.getLanguageDescription().getDescription() +
" Not Found, using default: " + e); " Not Found, using default: " + e);
compilerSpec = language.getDefaultCompilerSpec(); langSpec = language.getDefaultCompilerSpec();
if (compilerSpec == null) { if (compilerSpec == null) {
throw e; throw e;
} }
compilerSpecID = compilerSpec.getCompilerSpecID(); compilerSpecID = compilerSpec.getCompilerSpecID();
} }
compilerSpec = new ProgramCompilerSpec(this, langSpec);
} }
/** /**
@ -412,8 +417,7 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
Language newLanguage = language; Language newLanguage = language;
Language oldLanguage = OldLanguageFactory.getOldLanguageFactory() Language oldLanguage = OldLanguageFactory.getOldLanguageFactory()
.getOldLanguage( .getOldLanguage(languageID, languageVersion);
languageID, languageVersion);
if (oldLanguage == null) { if (oldLanguage == null) {
// Assume minor version behavior - old language does not exist for current major version // Assume minor version behavior - old language does not exist for current major version
Msg.error(this, "Old language specification not found: " + languageID + Msg.error(this, "Old language specification not found: " + languageID +
@ -422,10 +426,8 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
} }
// Ensure that we can upgrade the language // Ensure that we can upgrade the language
languageUpgradeTranslator = languageUpgradeTranslator = LanguageTranslatorFactory.getLanguageTranslatorFactory()
LanguageTranslatorFactory.getLanguageTranslatorFactory() .getLanguageTranslator(oldLanguage, newLanguage);
.getLanguageTranslator(
oldLanguage, newLanguage);
if (languageUpgradeTranslator == null) { if (languageUpgradeTranslator == null) {
// TODO: This is a bad situation!! Most language revisions should be supportable, if not we have no choice but to throw // TODO: This is a bad situation!! Most language revisions should be supportable, if not we have no choice but to throw
@ -467,10 +469,8 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
private VersionException checkForLanguageChange(LanguageNotFoundException e, int openMode) private VersionException checkForLanguageChange(LanguageNotFoundException e, int openMode)
throws LanguageNotFoundException { throws LanguageNotFoundException {
languageUpgradeTranslator = languageUpgradeTranslator = LanguageTranslatorFactory.getLanguageTranslatorFactory()
LanguageTranslatorFactory.getLanguageTranslatorFactory() .getLanguageTranslator(languageID, languageVersion);
.getLanguageTranslator(
languageID, languageVersion);
if (languageUpgradeTranslator == null) { if (languageUpgradeTranslator == null) {
throw e; throw e;
} }
@ -1201,8 +1201,7 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
* @throws MemoryConflictException if image base override is active * @throws MemoryConflictException if image base override is active
*/ */
public AddressSpace addOverlaySpace(String blockName, AddressSpace originalSpace, public AddressSpace addOverlaySpace(String blockName, AddressSpace originalSpace,
long minOffset, long maxOffset) long minOffset, long maxOffset) throws LockException, MemoryConflictException {
throws LockException, MemoryConflictException {
checkExclusiveAccess(); checkExclusiveAccess();
if (imageBaseOverride) { if (imageBaseOverride) {
@ -1838,6 +1837,7 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
for (int i = 0; i < NUM_MANAGERS; i++) { for (int i = 0; i < NUM_MANAGERS; i++) {
managers[i].invalidateCache(all); managers[i].invalidateCache(all);
} }
compilerSpec.installExtensions(); // Reload any extensions
} }
catch (IOException e) { catch (IOException e) {
dbError(e); dbError(e);
@ -2002,8 +2002,7 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
} }
LanguageTranslator languageTranslator = LanguageTranslator languageTranslator =
LanguageTranslatorFactory.getLanguageTranslatorFactory() LanguageTranslatorFactory.getLanguageTranslatorFactory()
.getLanguageTranslator(language, .getLanguageTranslator(language, newLanguage);
newLanguage);
if (languageTranslator == null) { if (languageTranslator == null) {
throw new IncompatibleLanguageException("Language translation not supported"); throw new IncompatibleLanguageException("Language translation not supported");
} }
@ -2054,7 +2053,8 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
} }
if (newCompilerSpecID != null) { if (newCompilerSpecID != null) {
compilerSpec = language.getCompilerSpecByID(newCompilerSpecID); compilerSpec = new ProgramCompilerSpec(this,
language.getCompilerSpecByID(newCompilerSpecID));
} }
compilerSpecID = compilerSpec.getCompilerSpecID(); compilerSpecID = compilerSpec.getCompilerSpecID();
languageVersion = language.getVersion(); languageVersion = language.getVersion();
@ -2459,4 +2459,18 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
contextMgr.flushProcessorContextWriteCache(); contextMgr.flushProcessorContextWriteCache();
super.flushWriteCache(); super.flushWriteCache();
} }
/**
* Install updated compiler spec extension options.
* See {@link SpecExtension}.
*/
protected void installExtensions() {
lock.acquire();
try {
compilerSpec.installExtensions();
}
finally {
lock.release();
}
}
} }

View file

@ -0,0 +1,785 @@
/* ###
* 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.program.database;
import java.util.ArrayList;
import java.util.List;
import org.xml.sax.*;
import generic.stl.Pair;
import ghidra.app.plugin.processors.sleigh.*;
import ghidra.framework.options.Options;
import ghidra.framework.store.LockException;
import ghidra.program.model.lang.*;
import ghidra.program.model.lang.CompilerSpec.EvaluationModelType;
import ghidra.program.model.listing.*;
import ghidra.util.*;
import ghidra.util.exception.*;
import ghidra.util.task.TaskMonitor;
import ghidra.xml.*;
/**
* Utility class for installing/removing "specification extensions" to a Program.
* A specification extension is a program specific version of either a:
* - Prototype Model
* - Call Fixup or
* - Callother Fixup
* Normally these objects are provided by the language specific configuration files (.cspec or .pspec),
* but this class allows additional objects to be added that are specific to the program.
*
* Internally, each spec extension is stored as an XML document as a formal Program Option. Each type of
* extension is described by a specific XML tag and is parsed as it would be in a .cspec or .pspec file.
* The XML tags are:
* - \<callfixup> - describing a Call Fixup
* - \<callotherfixup> - describing a Callother Fixup
* - \<prototype> - describing a typical Prototype Model
* - \<resolveprototype> - describing a Prototype Model merged from other models
*
* Each type of object has a unique name or target, which must be specified as part of the XML tag,
* which is referred to in this class as the extension's "formal name". In the \<callotherfixup> tag,
* the formal name is given by the "targetop" attribute; for all the other tags, the formal name is
* given by the "name" attribute".
*
* The parent option for all extensions is given by the static field SPEC_EXTENSION. Under the parent
* option, each extension is stored as a string with an option name, constructed by
* concatenating the extension's formal name with a prefix corresponding to the extension's XML tag name.
*
* testExtensionDocument() is used independently to extensively test whether a document
* describes a valid extension.
*
* Extensions are installed on a program via addReplaceCompilerSpecExtension().
* Extensions are removed from a program via removeCompilerSpecExtension().
*/
public class SpecExtension {
public final static String SPEC_EXTENSION = "Specification Extensions";
public final static String FORMAT_VERSION_OPTIONNAME = "FormatVersion";
public final static String VERSION_COUNTER_OPTIONNAME = "VersionCounter";
public final static int FORMAT_VERSION = 1; // Current version of specification XML format
private ProgramDB program;
private SleighLanguageValidator cspecValidator = null;
/**
* The possible types of spec extensions.
*/
public enum Type {
// The order is used to sort tables of extensions
PROTOTYPE_MODEL("prototype"),
MERGE_MODEL("resolve"),
CALL_FIXUP("callfixup"),
CALLOTHER_FIXUP("callotherfixup");
private String tagName;
private Type(String nm) {
tagName = nm;
}
/**
* Get the XML tag name associated with the specific extension type.
* @return the tag name
*/
public String getTagName() {
return tagName;
}
/**
* For a given extension's formal name, generate the option name used to store the extension.
* The option name is the tag name concatenated with the formal name, separated by '_'
* @param formalName is the formal name of the extension
* @return the option name
*/
public String getOptionName(String formalName) {
return tagName + '_' + formalName;
}
}
/**
* Helper class for collecting information about an extension XML document
* and constructing its option name for storage
*/
public static class DocInfo {
private Type type; // Type of extension
private String formalName; // Formal name extracted from the document
private String optionName; // Option name used to store document
private boolean override; // true if the extension overrides a core document
private static String generateOptionNameFromDocument(Type type, String document)
throws SleighException {
int startPos, endPos;
String tagAttribute;
switch (type) {
case PROTOTYPE_MODEL:
case MERGE_MODEL:
case CALL_FIXUP:
tagAttribute = "name=\"";
break;
case CALLOTHER_FIXUP:
tagAttribute = "targetop=\"";
break;
default:
throw new SleighException("Unsupported extension type");
}
startPos = document.indexOf(tagAttribute, 0);
if (startPos < 0) {
throw new SleighException("Could not find attribute: " + tagAttribute);
}
startPos += tagAttribute.length();
endPos = document.indexOf('\"', startPos);
if (endPos < 0) {
throw new SleighException("Bad XML document");
}
String formalName = document.substring(startPos, endPos);
if (!isValidFormalName(formalName)) {
throw new SleighException("Name of extension uses invalid characters");
}
return type.getOptionName(formalName);
}
/**
* Construct by directly pulling information from the XML document
* @param document is the entire XML document as a String
*/
public DocInfo(String document) {
type = getExtensionType(document, true);
optionName = generateOptionNameFromDocument(type, document);
formalName = SpecExtension.getFormalName(optionName);
override = false;
}
/**
* @return the Type of the extension
*/
public final Type getType() {
return type;
}
/**
* @return the formal name of the extension
*/
public final String getFormalName() {
return formalName;
}
/**
* @return the option name associated with the extension
*/
public final String getOptionName() {
return optionName;
}
/**
* @return true if the extension overrides a core object
*/
public final boolean isOverride() {
return override;
}
}
/**
* Construct an extension manager attached to a specific program.
* Multiple add/remove/test actions can be performed. Validator state is cached between calls.
* @param program is the specific Program
*/
public SpecExtension(Program program) {
if (!(program instanceof ProgramDB)) {
throw new IllegalArgumentException("only normal program supported");
}
this.program = (ProgramDB) program;
}
/**
* Get the extension type either from the XML tag name or the option name
* @param nm is the XML tag or option name
* @param isXML is true for an XML tag, false for an option name
* @return the extension type
* @throws SleighException if no type matches the name
*/
public static Type getExtensionType(String nm, boolean isXML) throws SleighException {
int pos = 0;
if (isXML) {
while (pos + 1 < nm.length() && (nm.charAt(pos) != '<' || nm.charAt(pos + 1) == '?') ||
nm.charAt(pos + 1) == '!') {
pos += 1;
}
pos += 1;
}
if (nm.length() <= pos) {
throw new SleighException("Unrecognized extension");
}
switch (nm.charAt(pos)) {
case 'c':
if (nm.startsWith(Type.CALL_FIXUP.getTagName(), pos)) {
return Type.CALL_FIXUP;
}
else if (nm.startsWith(Type.CALLOTHER_FIXUP.getTagName(), pos)) {
return Type.CALLOTHER_FIXUP;
}
break;
case 'p':
if (nm.startsWith(Type.PROTOTYPE_MODEL.getTagName(), pos)) {
return Type.PROTOTYPE_MODEL;
}
break;
case 'r':
if (nm.startsWith(Type.MERGE_MODEL.getTagName(), pos)) {
return Type.MERGE_MODEL;
}
break;
}
throw new SleighException("Unrecognized extension");
}
/**
* Check if the given option name corresponds to an extension
* @param nm is the given option name
* @return true if the name labels a spec extension
*/
private static boolean isCompilerProperty(String nm) {
try {
getExtensionType(nm, false);
return true;
}
catch (SleighException ex) {
return false;
}
}
/**
* Get version of CompilerSpec extensions stored with the Program
* @param program is the given Program
* @return the version number
*/
public static int getVersionCounter(Program program) {
Options options = program.getOptions(SPEC_EXTENSION);
return options.getInt(VERSION_COUNTER_OPTIONNAME, 0);
}
/**
* Get all compiler spec extensions for the program. The extensions are XML documents
* strings, with an associated "option name" string.
* Return a list of (optionname,document) pairs, which may be empty
* @param program is the Program to get extensions for
* @return the list of (optionname,document) pairs
*/
public static List<Pair<String, String>> getCompilerSpecExtensions(Program program) {
Options options = program.getOptions(SPEC_EXTENSION);
List<String> optionNames = options.getOptionNames();
ArrayList<Pair<String, String>> pairList = new ArrayList<>();
for (String optionName : optionNames) {
if (isCompilerProperty(optionName)) {
String value = options.getString(optionName, null);
if (value != null) {
pairList.add(new Pair<>(optionName, value));
}
}
}
return pairList;
}
/**
* Get the raw string making up an extension, given its type and name
* @param program is the program to extract the extension from
* @param type is the type of extension
* @param name is the formal name of the extension
* @return the extension string or null
*/
public static String getCompilerSpecExtension(Program program, Type type, String name) {
String optionName = type.getOptionName(name);
Options options = program.getOptions(SPEC_EXTENSION);
return options.getString(optionName, null);
}
/**
* Check the format version for spec extensions for a given program.
* If the program reports a version that does not match the current
* number attached to the running tool (FORMAT_VERSION), a VersionException is thrown
* @param program is the given Program
* @throws VersionException the reported version does not match the tool
*/
public static void checkFormatVersion(Program program) throws VersionException {
Options options = program.getOptions(SPEC_EXTENSION);
int formatVersion = options.getInt(FORMAT_VERSION_OPTIONNAME, 0);
if (formatVersion > FORMAT_VERSION) {
throw new VersionException("Program contains spec extensions with newer/unknown format",
VersionException.NEWER_VERSION, false);
}
}
/**
* Register the options system allowing spec extensions with the given Program
* @param program is the given Program
*/
public static void registerOptions(Program program) {
if (!(program instanceof ProgramDB)) {
Msg.error(SpecExtension.class, "Can only add extensions on a normal program");
return;
}
if (!SystemUtilities.isInHeadlessMode()) {
Options options = program.getOptions(SPEC_EXTENSION);
options.setOptionsHelpLocation(new HelpLocation("DecompilePlugin", "ExtensionOptions"));
options.registerOptionsEditor(new SpecExtensionEditor((ProgramDB) program));
}
}
/**
* Get the formal name of an extension from its option name.
* @param optionName is the option name
* @return the formal name
*/
public static String getFormalName(String optionName) {
return optionName.substring(optionName.indexOf('_') + 1);
}
/**
* Determine if the desired formal name is a valid identifier
* @param formalName is the formal name to check
* @return true if the name is valid
*/
public static boolean isValidFormalName(String formalName) {
if (formalName.length() == 0) {
return false;
}
for (int i = 0; i < formalName.length(); ++i) {
char c = formalName.charAt(i);
if (!Character.isLetterOrDigit(c) && c != '_' && c != '.' && c != '-') {
return false;
}
}
return true;
}
/**
* Generate an XML error handler suitable for parsing a specification document.
* - Warnings are logged.
* - Errors cause a SAXParseException
*
* @param docTitle is the title of the document
* @return the error handler object
*/
private static ErrorHandler getErrorHandler(String docTitle) {
ErrorHandler errHandler = new ErrorHandler() {
@Override
public void error(SAXParseException exception) throws SAXException {
throw exception;
}
@Override
public void fatalError(SAXParseException exception) throws SAXException {
throw exception;
}
@Override
public void warning(SAXParseException exception) throws SAXException {
Msg.warn(this, "Warning parsing '" + docTitle + "'", exception);
}
};
return errHandler;
}
/**
* Parse an XML string and build the corresponding compiler spec extension object.
* Currently this can either be a
* - PrototypeModel or
* - InjectPayload
*
* For InjectPayloadCallfixup or InjectPayloadCallother, the p-code \<body> tag
* is also parsed, and the caller can control whether any parse errors
* cause an exception or whether a dummy payload is provided instead.
* @param optionName is the option name the extension is attached to
* @param extension is the XML document as a String
* @param cspec is the compiler spec the new extension is for
* @param provideDummy if true, provide a dummy payload if necessary
* @return the extension object
* @throws SAXException is there are XML format errors
* @throws XmlParseException if the XML document is badly formed
* @throws SleighException if internal p-code does not parse
*/
public static Object parseExtension(String optionName, String extension, CompilerSpec cspec,
boolean provideDummy) throws SAXException, XmlParseException, SleighException {
ErrorHandler errHandler = getErrorHandler("extensions");
XmlPullParser parser =
XmlPullParserFactory.create(extension, optionName, errHandler, false);
String elName = parser.peek().getName();
if (elName.endsWith("prototype")) {
PrototypeModel model;
if (parser.peek().getName().equals("resolveprototype")) {
PrototypeModelMerged mergemodel = new PrototypeModelMerged();
ArrayList<PrototypeModel> curModels =
new ArrayList<>(cspec.getCallingConventions().length);
for (PrototypeModel curModel : cspec.getCallingConventions()) {
curModels.add(curModel);
}
try {
mergemodel.restoreXml(parser, curModels);
model = mergemodel;
}
catch (XmlParseException ex) {
if (!provideDummy) {
throw ex;
}
// XML failed to parse, associate default model as a placeholder
model = new PrototypeModelError(getFormalName(optionName),
cspec.getDefaultCallingConvention());
}
}
else {
model = new PrototypeModel();
try {
model.restoreXml(parser, cspec);
}
catch (XmlParseException ex) {
if (!provideDummy) {
throw ex;
}
// XML failed to parse, associate default model as a placeholder
model = new PrototypeModelError(getFormalName(optionName),
cspec.getDefaultCallingConvention());
}
}
return model;
}
else if (elName.equals("callfixup")) {
String nm = parser.peek().getAttribute("name");
PcodeInjectLibrary injectLibrary = cspec.getPcodeInjectLibrary();
InjectPayload payload =
injectLibrary.allocateInject(optionName, nm, InjectPayload.CALLFIXUP_TYPE);
if (!(payload instanceof InjectPayloadSleigh)) {
throw new XmlParseException("Cannot use attached name: " + nm);
}
try {
payload.restoreXml(parser, (SleighLanguage) cspec.getLanguage());
injectLibrary.parseInject(payload); // Try to parse the pcode body
}
catch (XmlParseException ex) {
if (!provideDummy) {
throw ex;
}
// The XML parse itself failed, provide a generic placeholder
payload = new InjectPayloadCallfixupError(cspec.getLanguage().getAddressFactory(),
getFormalName(optionName));
}
catch (SleighException ex) {
if (!provideDummy) {
throw ex;
}
// The pcode body failed to parse, payload metadata, but provide dummy p-code
payload = new InjectPayloadCallfixupError(cspec.getLanguage().getAddressFactory(),
(InjectPayloadCallfixup) payload);
}
return payload;
}
else if (elName.equals("callotherfixup")) {
String nm = parser.peek().getAttribute("name");
PcodeInjectLibrary injectLibrary = cspec.getPcodeInjectLibrary();
InjectPayload payload =
injectLibrary.allocateInject(optionName, nm, InjectPayload.CALLOTHERFIXUP_TYPE);
if (!(payload instanceof InjectPayloadSleigh)) {
throw new XmlParseException("Cannot use attached name: " + nm);
}
try {
payload.restoreXml(parser, (SleighLanguage) cspec.getLanguage());
injectLibrary.parseInject(payload);
}
catch (XmlParseException ex) {
if (!provideDummy) {
throw ex;
}
// The XML parse itself failed, provide a generic placeholder
payload = new InjectPayloadCallotherError(cspec.getLanguage().getAddressFactory(),
getFormalName(optionName));
}
catch (SleighException ex) {
// The p-code parse failed, keep the metadata, but provide dummy p-code
payload = new InjectPayloadCallotherError(cspec.getLanguage().getAddressFactory(),
(InjectPayloadCallother) payload);
}
return payload;
}
throw new XmlParseException("Unknown compiler spec extension: " + elName);
}
/**
* Check that the proposed callfixup extension does not collide with built-in fixups
* @param doc is info about the proposed extension
* @throws SleighException is there is a collision
*/
private void checkCallFixup(DocInfo doc) throws SleighException {
CompilerSpec cspec = program.getCompilerSpec();
PcodeInjectLibrary injectLibrary = cspec.getPcodeInjectLibrary();
InjectPayload payload =
injectLibrary.getPayload(InjectPayload.CALLFIXUP_TYPE, doc.formalName);
if (payload == null) {
return;
}
if (injectLibrary.hasProgramPayload(doc.formalName, InjectPayload.CALLFIXUP_TYPE)) {
return;
}
throw new SleighException("Extension cannot replace callfixup: " + doc.formalName);
}
/**
* Check that the proposed callotherfixup extension targets a user-defined op
* that exists. Check if the extension would override a built-in fixup.
* @param doc is info on the proposed extension
* @throws SleighException if the targeted op does not exist
*/
private void checkCallotherFixup(DocInfo doc) throws SleighException {
CompilerSpec cspec = program.getCompilerSpec();
PcodeInjectLibrary injectLibrary = cspec.getPcodeInjectLibrary();
if (!injectLibrary.hasUserDefinedOp(doc.formalName)) {
throw new SleighException("CALLOTHER_FIXUP target does not exist: " + doc.formalName);
}
InjectPayload payload =
injectLibrary.getPayload(InjectPayload.CALLOTHERFIXUP_TYPE, doc.formalName);
if (payload == null) {
return;
}
if (injectLibrary.hasProgramPayload(doc.formalName, InjectPayload.CALLOTHERFIXUP_TYPE)) {
return;
}
// A callother payload is allowed to override an existing core payload
// So this check never fails, but we mark that the override is occurring
doc.override = true;
}
/**
* Check that the proposed prototype extension does not collide with a
* built-in prototype.
* @param doc is info on the proposed prototype
* @throws SleighException if there is a collision
*/
private void checkPrototype(DocInfo doc) throws SleighException {
CompilerSpec cspec = program.getCompilerSpec();
PrototypeModel[] allModels = cspec.getAllModels();
for (PrototypeModel model : allModels) {
if (model.getName().equals(doc.formalName)) {
if (!model.isProgramExtension()) {
throw new SleighException(
"Extension cannot replace prototype: " + doc.formalName);
}
}
}
}
/**
* Check the given document information against existing objects already in the compiler spec.
* Any problem (like name collisions) causes an exception to get thrown.
* Checks may populate additional document information
* @param doc is the document information: name, type
* @throws SleighException if there is a problem
*/
private void checkExtension(DocInfo doc) throws SleighException {
switch (doc.type) {
case CALL_FIXUP:
checkCallFixup(doc);
break;
case CALLOTHER_FIXUP:
checkCallotherFixup(doc);
break;
case MERGE_MODEL:
checkPrototype(doc);
break;
case PROTOTYPE_MODEL:
checkPrototype(doc);
break;
}
}
/**
* Test if the given XML document describes a suitable spec extension.
* The document must fully parse and validate and must not conflict with the existing spec;
* otherwise an exception is thrown. If all tests pass, an object describing basic properties
* of the document is returned.
* @param document is the given XML document
* @return info about the document
* @throws SleighException if validity checks fail
* @throws XmlParseException if the XML is badly formed
* @throws SAXException if there are parse errors
*/
public DocInfo testExtensionDocument(String document)
throws SleighException, SAXException, XmlParseException {
DocInfo res = new DocInfo(document);
if (cspecValidator == null) {
cspecValidator = new SleighLanguageValidator(SleighLanguageValidator.CSPECTAG_TYPE);
}
cspecValidator.verify(res.optionName, document);
checkExtension(res);
parseExtension(res.optionName, document, program.getCompilerSpec(), false);
return res;
}
/**
* Clean up references to a callfixup that is going to be removed
* @param fixupName is the name of the fixup
* @param monitor is a task monitor
* @throws CancelledException if the task is cancelled
*/
private void removeCallFixup(String fixupName, TaskMonitor monitor) throws CancelledException {
FunctionManager manager = program.getFunctionManager();
monitor.setMessage("Searching for references to " + fixupName);
monitor.setMaximum(manager.getFunctionCount());
FunctionIterator iter = manager.getFunctions(true);
for (int i = 0; i < 2; ++i) {
while (iter.hasNext()) {
monitor.checkCanceled();
monitor.incrementProgress(1);
Function function = iter.next();
String currentFixup = function.getCallFixup();
if (currentFixup != null && currentFixup.equals(fixupName)) {
function.setCallFixup(null);
}
}
if (i == 1) {
break;
}
iter = manager.getExternalFunctions();
}
}
/**
* Clean up any references to a callotherfixup that is going to be removed
* @param fixupName is the name of the callother fixup
* @param monitor is a task monitor
*/
private void removeCallotherFixup(String fixupName, TaskMonitor monitor) {
// Nothing to clean up currently
}
/**
* If the indicated evaluation model matches the given name,
* clear the evaluation model to the default
* @param modelType is the indicated evaluation model
* @param modelName is the given name needing to be cleared
*/
private void clearPrototypeEvaluationModel(EvaluationModelType modelType, String modelName) {
CompilerSpec compilerSpec = program.getCompilerSpec();
PrototypeModel evalModel = compilerSpec.getPrototypeEvaluationModel(modelType);
if (!evalModel.getName().equals(modelName)) {
return;
}
String newName = compilerSpec.getDefaultCallingConvention().getName();
Options options = program.getOptions(ProgramCompilerSpec.DECOMPILER_PROPERTY_LIST_NAME);
options.setString(ProgramCompilerSpec.EVALUATION_MODEL_PROPERTY_NAME, newName);
}
/**
* Clean up references to a prototype extension that is about to be removed.
* Functions that use this prototype are changed to have an "unknown" prototype.
* @param modelName is the name of the prototype being removed
* @param monitor is a task monitor
* @throws CancelledException if the task is cancelled
*/
private void removePrototype(String modelName, TaskMonitor monitor) throws CancelledException {
FunctionManager manager = program.getFunctionManager();
monitor.setMessage("Searching for references to " + modelName);
monitor.setMaximum(manager.getFunctionCount());
FunctionIterator iter = manager.getFunctions(true);
for (int i = 0; i < 2; i += 1) {
while (iter.hasNext()) {
monitor.checkCanceled();
monitor.incrementProgress(1);
Function function = iter.next();
PrototypeModel currentModel = function.getCallingConvention();
if (currentModel != null && currentModel.getName().equals(modelName)) {
try {
function.setCallingConvention("unknown");
}
catch (InvalidInputException e) {
// shouldn't reach here
}
}
}
if (i == 1) {
break;
}
iter = manager.getExternalFunctions();
}
// Clear any evaluation model that matches the prototype being removed
clearPrototypeEvaluationModel(EvaluationModelType.EVAL_CURRENT, modelName);
}
/**
* Install or replace a spec extension to the program. The extension is presented as
* an XML document, from which a name is extracted. If an extension previously existed
* with the same name, it is overwritten. Otherwise the document is treated as a new
* extension. Testing is performed before installation:
* - Document is parsed as XML and is verified against spec grammars
* - Internal p-code tags from InjectPayloads are compiled
* - Name collisions are checked for
* @param document is the XML document describing the extension
* @param monitor is a task monitor
* @throws LockException if the caller does not exclusive access to the program
* @throws XmlParseException for a badly formed extension document
* @throws SAXException for parse errors in the extension document
* @throws SleighException for a document that fails verification
*/
public void addReplaceCompilerSpecExtension(String document, TaskMonitor monitor)
throws LockException, SleighException, SAXException, XmlParseException {
program.checkExclusiveAccess();
monitor.setMessage("Testing validity of new document");
DocInfo info = testExtensionDocument(document);
monitor.setMessage("Installing " + info.getFormalName());
Options specOptions = program.getOptions(SpecExtension.SPEC_EXTENSION);
int progVersion = specOptions.getInt(SpecExtension.VERSION_COUNTER_OPTIONNAME, 0);
progVersion = (progVersion + 1) % 0x40000000; // Change the version number associated with the CompilerSpec
specOptions.setString(info.getOptionName(), document);
specOptions.setInt(SpecExtension.VERSION_COUNTER_OPTIONNAME, progVersion);
specOptions.setInt(SpecExtension.FORMAT_VERSION_OPTIONNAME, SpecExtension.FORMAT_VERSION);
program.installExtensions();
}
/**
* Remove the indicated spec extension from the program.
* Depending on the type, references to the extension are removed or altered
* first, to facilitate final removal of the extension.
* All changes are made in a single transaction that can be cancelled.
* @param optionName is the option name where the extension is stored
* @param monitor is a provided monitor that can trigger cancellation
* @throws LockException if the caller does not have exclusive access to the program
* @throws CancelledException if the caller cancels the operation via the task monitor
*/
public void removeCompilerSpecExtension(String optionName, TaskMonitor monitor)
throws LockException, CancelledException {
program.checkExclusiveAccess();
Type type = SpecExtension.getExtensionType(optionName, false);
Options specOptions = program.getOptions(SpecExtension.SPEC_EXTENSION);
if (!specOptions.contains(optionName)) {
throw new SleighException("Extension does not exist: " + optionName);
}
int progVersion = specOptions.getInt(SpecExtension.VERSION_COUNTER_OPTIONNAME, 0);
progVersion = (progVersion + 1) % 0x40000000; // Change version number associated with the CompilerSpec
String extName = SpecExtension.getFormalName(optionName);
switch (type) {
case CALL_FIXUP:
removeCallFixup(extName, monitor);
break;
case CALLOTHER_FIXUP:
removeCallotherFixup(extName, monitor);
break;
case MERGE_MODEL:
removePrototype(extName, monitor);
break;
case PROTOTYPE_MODEL:
removePrototype(extName, monitor);
break;
}
specOptions.removeOption(optionName);
specOptions.setInt(SpecExtension.VERSION_COUNTER_OPTIONNAME, progVersion);
program.installExtensions();
}
}

View file

@ -295,8 +295,7 @@ public class FunctionDB extends DatabaseObject implements Function {
try { try {
checkIsValid(); checkIsValid();
return manager.getCodeManager() return manager.getCodeManager()
.getComment(CodeUnit.REPEATABLE_COMMENT, .getComment(CodeUnit.REPEATABLE_COMMENT, getEntryPoint());
getEntryPoint());
} }
finally { finally {
manager.lock.release(); manager.lock.release();
@ -314,8 +313,7 @@ public class FunctionDB extends DatabaseObject implements Function {
try { try {
checkDeleted(); checkDeleted();
manager.getCodeManager() manager.getCodeManager()
.setComment(getEntryPoint(), CodeUnit.REPEATABLE_COMMENT, .setComment(getEntryPoint(), CodeUnit.REPEATABLE_COMMENT, comment);
comment);
} }
finally { finally {
manager.lock.release(); manager.lock.release();
@ -809,8 +807,8 @@ public class FunctionDB extends DatabaseObject implements Function {
if (baseType instanceof TypeDef) { if (baseType instanceof TypeDef) {
baseType = ((TypeDef) baseType).getBaseDataType(); baseType = ((TypeDef) baseType).getBaseDataType();
} }
returnParam.setDynamicStorage( returnParam
(baseType instanceof VoidDataType) ? VariableStorage.VOID_STORAGE .setDynamicStorage((baseType instanceof VoidDataType) ? VariableStorage.VOID_STORAGE
: VariableStorage.UNASSIGNED_STORAGE); : VariableStorage.UNASSIGNED_STORAGE);
PrototypeModel callingConvention = getCallingConvention(); PrototypeModel callingConvention = getCallingConvention();
@ -898,8 +896,8 @@ public class FunctionDB extends DatabaseObject implements Function {
} }
} }
program.getBookmarkManager() program.getBookmarkManager()
.setBookmark(getEntryPoint(), BookmarkType.ERROR, .setBookmark(getEntryPoint(), BookmarkType.ERROR, "Bad Variables Removed",
"Bad Variables Removed", "Removed " + badSymbols.size() + " bad variables"); "Removed " + badSymbols.size() + " bad variables");
for (Symbol s : badSymbols) { for (Symbol s : badSymbols) {
s.delete(); s.delete();
} }
@ -2711,8 +2709,7 @@ public class FunctionDB extends DatabaseObject implements Function {
else { else {
if (program.getCompilerSpec() if (program.getCompilerSpec()
.getPcodeInjectLibrary() .getPcodeInjectLibrary()
.getPayload( .getPayload(InjectPayload.CALLFIXUP_TYPE, name) == null) {
InjectPayload.CALLFIXUP_TYPE, name, null, null) == null) {
Msg.warn(this, "Undefined CallFixup set at " + entryPoint + ": " + name); Msg.warn(this, "Undefined CallFixup set at " + entryPoint + ": " + name);
} }
callFixupMap.add(entryPoint, name); callFixupMap.add(entryPoint, name);
@ -2829,8 +2826,7 @@ public class FunctionDB extends DatabaseObject implements Function {
tagManager.applyFunctionTag(getID(), tag.getId()); tagManager.applyFunctionTag(getID(), tag.getId());
Address addr = getEntryPoint(); Address addr = getEntryPoint();
program.setChanged(ChangeManager.DOCR_TAG_ADDED_TO_FUNCTION, addr, addr, tag, program.setChanged(ChangeManager.DOCR_TAG_ADDED_TO_FUNCTION, addr, addr, tag, tag);
tag);
} }
// Add to local cache // Add to local cache

View file

@ -211,7 +211,7 @@ public class FunctionManagerDB implements FunctionManager {
@Override @Override
public List<String> getCallingConventionNames() { public List<String> getCallingConventionNames() {
CompilerSpec compilerSpec = program.getCompilerSpec(); CompilerSpec compilerSpec = program.getCompilerSpec();
PrototypeModel[] namedCallingConventions = compilerSpec.getNamedCallingConventions(); PrototypeModel[] namedCallingConventions = compilerSpec.getCallingConventions();
List<String> names = new ArrayList<>(namedCallingConventions.length + 2); List<String> names = new ArrayList<>(namedCallingConventions.length + 2);
names.add(Function.UNKNOWN_CALLING_CONVENTION_STRING); names.add(Function.UNKNOWN_CALLING_CONVENTION_STRING);
names.add(Function.DEFAULT_CALLING_CONVENTION_STRING); names.add(Function.DEFAULT_CALLING_CONVENTION_STRING);
@ -1006,8 +1006,7 @@ public class FunctionManagerDB implements FunctionManager {
} }
else { else {
it = program.getSymbolTable() it = program.getSymbolTable()
.getSymbols(program.getMemory(), SymbolType.FUNCTION, .getSymbols(program.getMemory(), SymbolType.FUNCTION, forward);
forward);
} }
} }

View file

@ -922,7 +922,8 @@ public class Disassembler implements DisassemblerConflictHandler {
disassemblerContext.flowToAddress(addr); disassemblerContext.flowToAddress(addr);
MemBuffer instrMemBuffer = new WrappedMemBuffer(blockMemBuffer, DISASSEMBLE_MEMORY_CACHE_SIZE, MemBuffer instrMemBuffer =
new WrappedMemBuffer(blockMemBuffer, DISASSEMBLE_MEMORY_CACHE_SIZE,
(int) addr.subtract(blockMemBuffer.getAddress())); (int) addr.subtract(blockMemBuffer.getAddress()));
adjustPreParseContext(instrMemBuffer); adjustPreParseContext(instrMemBuffer);
@ -971,8 +972,8 @@ public class Disassembler implements DisassemblerConflictHandler {
// delay slots assumed to always fall-through - queue next addr // delay slots assumed to always fall-through - queue next addr
disassemblerContext.copyToFutureFlowState(addr); disassemblerContext.copyToFutureFlowState(addr);
if (disassemblerQueue != null) { if (disassemblerQueue != null) {
disassemblerQueue.queueDelaySlotFallthrough( disassemblerQueue
existingBlockStartInstr); .queueDelaySlotFallthrough(existingBlockStartInstr);
} }
return; return;
} }
@ -1356,8 +1357,8 @@ public class Disassembler implements DisassemblerConflictHandler {
return false; return false;
} }
PcodeInjectLibrary pcodeInjectLibrary = program.getCompilerSpec().getPcodeInjectLibrary(); PcodeInjectLibrary pcodeInjectLibrary = program.getCompilerSpec().getPcodeInjectLibrary();
InjectPayload callFixup = pcodeInjectLibrary.getPayload(InjectPayload.CALLFIXUP_TYPE, InjectPayload callFixup =
callFixupStr, program, null); pcodeInjectLibrary.getPayload(InjectPayload.CALLFIXUP_TYPE, callFixupStr);
if (callFixup == null) { if (callFixup == null) {
return false; return false;
} }

View file

@ -42,5 +42,4 @@ public interface BitFieldPacking {
* a zero-length bit-field * a zero-length bit-field
*/ */
int getZeroLengthBoundary(); int getZeroLengthBoundary();
} }

View file

@ -15,6 +15,10 @@
*/ */
package ghidra.program.model.data; package ghidra.program.model.data;
import ghidra.util.xml.SpecXmlUtils;
import ghidra.xml.XmlElement;
import ghidra.xml.XmlPullParser;
public class BitFieldPackingImpl implements BitFieldPacking { public class BitFieldPackingImpl implements BitFieldPacking {
private boolean useMSConvention = false; private boolean useMSConvention = false;
@ -65,4 +69,73 @@ public class BitFieldPackingImpl implements BitFieldPacking {
this.zeroLengthBoundary = zeroLengthBoundary; this.zeroLengthBoundary = zeroLengthBoundary;
} }
/**
* Write configuration to a stream as an XML \<bitfield_packing> tag
* @param buffer is the stream to write to
*/
public void saveXml(StringBuilder buffer) {
if (!useMSConvention && typeAlignmentEnabled && zeroLengthBoundary == 0) {
return; // All defaults
}
buffer.append("<bitfield_packing>\n");
if (useMSConvention) {
buffer.append("<use_MS_convention value=\"yes\"/>\n");
}
if (!typeAlignmentEnabled) {
buffer.append("<type_alignment_enabled value=\"no\"/>\n");
}
if (zeroLengthBoundary != 0) {
buffer.append("<zero_length_boundary");
SpecXmlUtils.encodeSignedIntegerAttribute(buffer, "value", zeroLengthBoundary);
buffer.append("/>\n");
}
buffer.append("</bitfield_packing>\n");
}
/**
* Restore settings from a \<bitfield_packing> tag in an XML stream.
* The XML is designed to override existing settings from the default constructor
* @param parser is the XML stream
*/
protected void restoreXml(XmlPullParser parser) {
parser.start();
while (parser.peek().isStart()) {
XmlElement subel = parser.start();
String name = subel.getName();
String value = subel.getAttribute("value");
if (name.equals("use_MS_convention")) {
useMSConvention = SpecXmlUtils.decodeBoolean(value);
}
else if (name.equals("type_alignment_enabled")) {
typeAlignmentEnabled = SpecXmlUtils.decodeBoolean(value);
}
else if (name.equals("zero_length_boundary")) {
zeroLengthBoundary = SpecXmlUtils.decodeInt(value);
}
parser.end(subel);
}
parser.end();
}
@Override
public boolean equals(Object obj) {
BitFieldPackingImpl op2 = (BitFieldPackingImpl) obj;
if (typeAlignmentEnabled != op2.typeAlignmentEnabled) {
return false;
}
if (useMSConvention != op2.useMSConvention) {
return false;
}
if (zeroLengthBoundary != op2.zeroLengthBoundary) {
return false;
}
return true;
}
@Override
public int hashCode() {
return (typeAlignmentEnabled ? 1 : 13) + (useMSConvention ? 5 : 27) + zeroLengthBoundary;
}
} }

View file

@ -20,6 +20,9 @@ import java.util.Arrays;
import ghidra.program.model.lang.Language; import ghidra.program.model.lang.Language;
import ghidra.util.datastruct.IntIntHashtable; import ghidra.util.datastruct.IntIntHashtable;
import ghidra.util.exception.NoValueException; import ghidra.util.exception.NoValueException;
import ghidra.util.xml.SpecXmlUtils;
import ghidra.xml.XmlElement;
import ghidra.xml.XmlPullParser;
/** /**
* DataOrganization provides a single place for determining size and alignment information * DataOrganization provides a single place for determining size and alignment information
@ -48,7 +51,7 @@ public class DataOrganizationImpl implements DataOrganization {
private boolean bigEndian = false; private boolean bigEndian = false;
private boolean isSignedChar = true; private boolean isSignedChar = true;
private BitFieldPacking bitFieldPacking = new BitFieldPackingImpl(); private BitFieldPackingImpl bitFieldPacking = new BitFieldPackingImpl();
/* /*
* Map for determining the alignment of a data type based upon its size. * Map for determining the alignment of a data type based upon its size.
@ -163,7 +166,7 @@ public class DataOrganizationImpl implements DataOrganization {
/** /**
* Set data endianess * Set data endianess
* @param bigEndian * @param bigEndian is true to set big endian
*/ */
public void setBigEndian(boolean bigEndian) { public void setBigEndian(boolean bigEndian) {
this.bigEndian = bigEndian; this.bigEndian = bigEndian;
@ -401,7 +404,7 @@ public class DataOrganizationImpl implements DataOrganization {
* Set the bitfield packing information associated with this data organization. * Set the bitfield packing information associated with this data organization.
* @param bitFieldPacking bitfield packing information * @param bitFieldPacking bitfield packing information
*/ */
public void setBitFieldPacking(BitFieldPacking bitFieldPacking) { public void setBitFieldPacking(BitFieldPackingImpl bitFieldPacking) {
this.bitFieldPacking = bitFieldPacking; this.bitFieldPacking = bitFieldPacking;
} }
@ -647,4 +650,287 @@ public class DataOrganizationImpl implements DataOrganization {
return (value2 != 0) ? getGreatestCommonDenominator(value2, value1 % value2) : value1; return (value2 != 0) ? getGreatestCommonDenominator(value2, value1 % value2) : value1;
} }
public void saveXml(StringBuilder buffer) {
buffer.append("<data_organization>\n");
if (absoluteMaxAlignment != NO_MAXIMUM_ALIGNMENT) {
buffer.append("<absolute_max_alignment");
SpecXmlUtils.encodeSignedIntegerAttribute(buffer, "value", absoluteMaxAlignment);
buffer.append("/>\n");
}
if (machineAlignment != 8) {
buffer.append("<machine_alignment");
SpecXmlUtils.encodeSignedIntegerAttribute(buffer, "value", machineAlignment);
buffer.append("/>\n");
}
if (defaultAlignment != 1) {
buffer.append("<default_alignment");
SpecXmlUtils.encodeSignedIntegerAttribute(buffer, "value", defaultAlignment);
buffer.append("/>\n");
}
if (defaultPointerAlignment != 4) {
buffer.append("<default_pointer_alignment");
SpecXmlUtils.encodeSignedIntegerAttribute(buffer, "value", defaultPointerAlignment);
buffer.append("/>\n");
}
if (pointerSize != 0) {
buffer.append("<pointer_size");
SpecXmlUtils.encodeSignedIntegerAttribute(buffer, "value", pointerSize);
buffer.append("/>\n");
}
if (pointerShift != 0) {
buffer.append("<pointer_shift");
SpecXmlUtils.encodeSignedIntegerAttribute(buffer, "value", pointerShift);
buffer.append("/>\n");
}
if (!isSignedChar) {
buffer.append("<char_type signed=\"no\"/>\n");
}
if (charSize != 1) {
buffer.append("<char_size");
SpecXmlUtils.encodeSignedIntegerAttribute(buffer, "value", charSize);
buffer.append("/>\n");
}
if (wideCharSize != 2) {
buffer.append("<wchar_size");
SpecXmlUtils.encodeSignedIntegerAttribute(buffer, "value", wideCharSize);
buffer.append("/>\n");
}
if (shortSize != 2) {
buffer.append("<short_size");
SpecXmlUtils.encodeSignedIntegerAttribute(buffer, "value", shortSize);
buffer.append("/>\n");
}
if (integerSize != 4) {
buffer.append("<integer_size");
SpecXmlUtils.encodeSignedIntegerAttribute(buffer, "value", integerSize);
buffer.append("/>\n");
}
if (longSize != 4) {
buffer.append("<long_size");
SpecXmlUtils.encodeSignedIntegerAttribute(buffer, "value", longSize);
buffer.append("/>\n");
}
if (longLongSize != 8) {
buffer.append("<long_long_size");
SpecXmlUtils.encodeSignedIntegerAttribute(buffer, "value", longLongSize);
buffer.append("/>\n");
}
if (floatSize != 4) {
buffer.append("<float_size");
SpecXmlUtils.encodeSignedIntegerAttribute(buffer, "value", floatSize);
buffer.append("/>\n");
}
if (doubleSize != 8) {
buffer.append("<double_size");
SpecXmlUtils.encodeSignedIntegerAttribute(buffer, "value", doubleSize);
buffer.append("/>\n");
}
if (longDoubleSize != 8) {
buffer.append("<long_double_size");
SpecXmlUtils.encodeSignedIntegerAttribute(buffer, "value", longDoubleSize);
buffer.append("/>\n");
}
if (sizeAlignmentMap.size() != 0) {
int[] keys = sizeAlignmentMap.getKeys();
buffer.append("<size_alignment_map>\n");
for (int key : keys) {
buffer.append("<entry");
int value;
try {
value = sizeAlignmentMap.get(key);
}
catch (NoValueException e) {
value = 0;
}
SpecXmlUtils.encodeSignedIntegerAttribute(buffer, "size", key);
SpecXmlUtils.encodeSignedIntegerAttribute(buffer, "alignment", value);
buffer.append("/>\n");
}
buffer.append("</size_alignment_map>\n");
}
bitFieldPacking.saveXml(buffer);
buffer.append("</data_organization>\n");
}
/**
* Restore settings from an XML stream. This expects to see a \<data_organization> tag.
* The XML is designed to override existing default settings. So this object needs to
* be pre-populated with defaults, typically via getDefaultOrganization().
* @param parser is the XML stream
*/
public void restoreXml(XmlPullParser parser) {
parser.start();
while (parser.peek().isStart()) {
String name = parser.peek().getName();
if (name.equals("char_type")) {
XmlElement subel = parser.start();
String boolStr = subel.getAttribute("signed");
isSignedChar = SpecXmlUtils.decodeBoolean(boolStr);
parser.end(subel);
continue;
}
else if (name.equals("bitfield_packing")) {
bitFieldPacking.restoreXml(parser);
continue;
}
else if (name.equals("size_alignment_map")) {
XmlElement subel = parser.start();
while (parser.peek().isStart()) {
XmlElement subsubel = parser.start();
int size = SpecXmlUtils.decodeInt(subsubel.getAttribute("size"));
int alignment = SpecXmlUtils.decodeInt(subsubel.getAttribute("alignment"));
sizeAlignmentMap.put(size, alignment);
parser.end(subsubel);
}
parser.end(subel);
continue;
}
XmlElement subel = parser.start();
String value = subel.getAttribute("value");
if (name.equals("absolute_max_alignment")) {
absoluteMaxAlignment = SpecXmlUtils.decodeInt(value);
}
else if (name.equals("machine_alignment")) {
machineAlignment = SpecXmlUtils.decodeInt(value);
}
else if (name.equals("default_alignment")) {
defaultAlignment = SpecXmlUtils.decodeInt(value);
}
else if (name.equals("default_pointer_alignment")) {
defaultPointerAlignment = SpecXmlUtils.decodeInt(value);
}
else if (name.equals("pointer_size")) {
pointerSize = SpecXmlUtils.decodeInt(value);
}
else if (name.equals("pointer_shift")) {
pointerShift = SpecXmlUtils.decodeInt(value);
}
else if (name.equals("char_size")) {
charSize = SpecXmlUtils.decodeInt(value);
}
else if (name.equals("wchar_size")) {
wideCharSize = SpecXmlUtils.decodeInt(value);
}
else if (name.equals("short_size")) {
shortSize = SpecXmlUtils.decodeInt(value);
}
else if (name.equals("integer_size")) {
integerSize = SpecXmlUtils.decodeInt(value);
}
else if (name.equals("long_size")) {
longSize = SpecXmlUtils.decodeInt(value);
}
else if (name.equals("long_long_size")) {
longLongSize = SpecXmlUtils.decodeInt(value);
}
else if (name.equals("float_size")) {
floatSize = SpecXmlUtils.decodeInt(value);
}
else if (name.equals("double_size")) {
doubleSize = SpecXmlUtils.decodeInt(value);
}
else if (name.equals("long_double_size")) {
longDoubleSize = SpecXmlUtils.decodeInt(value);
}
parser.end(subel);
}
parser.end();
}
@Override
public boolean equals(Object obj) {
DataOrganizationImpl op2 = (DataOrganizationImpl) obj;
if (absoluteMaxAlignment != op2.absoluteMaxAlignment) {
return false;
}
if (bigEndian != op2.bigEndian) {
return false;
}
if (!bitFieldPacking.equals(op2.bitFieldPacking)) {
return false;
}
if (charSize != op2.charSize || wideCharSize != op2.wideCharSize) {
return false;
}
if (defaultAlignment != op2.defaultAlignment) {
return false;
}
if (defaultPointerAlignment != op2.defaultPointerAlignment) {
return false;
}
if (doubleSize != op2.doubleSize || floatSize != op2.floatSize) {
return false;
}
if (integerSize != op2.integerSize || longLongSize != op2.longLongSize) {
return false;
}
if (shortSize != op2.shortSize) {
return false;
}
if (longSize != op2.longSize || longDoubleSize != op2.longDoubleSize) {
return false;
}
if (isSignedChar != op2.isSignedChar) {
return false;
}
if (machineAlignment != op2.machineAlignment) {
return false;
}
if (pointerSize != op2.pointerSize || pointerShift != op2.pointerShift) {
return false;
}
int[] keys = sizeAlignmentMap.getKeys();
int[] op2keys = op2.sizeAlignmentMap.getKeys();
if (keys.length != op2keys.length) {
return false;
}
try {
for (int i = 0; i < keys.length; ++i) {
if (sizeAlignmentMap.get(keys[i]) != op2.sizeAlignmentMap.get(op2keys[i])) {
return false;
}
}
}
catch (NoValueException ex) {
return false;
}
return true;
}
@Override
public int hashCode() {
int hash = bitFieldPacking.hashCode();
hash = 79 * hash + absoluteMaxAlignment;
hash = 79 * hash + (bigEndian ? 27 : 13);
hash = 79 * hash + charSize;
hash = 79 * hash + defaultAlignment;
hash = 79 * hash + defaultPointerAlignment;
hash = 79 * hash + doubleSize;
hash = 79 * hash + floatSize;
hash = 79 * hash + integerSize;
hash = 79 * hash + (isSignedChar ? 1 : 3);
hash = 79 * hash + longDoubleSize;
hash = 79 * hash + longLongSize;
hash = 79 * hash + longSize;
hash = 79 * hash + machineAlignment;
hash = 79 * hash + pointerShift;
hash = 79 * hash + pointerSize;
hash = 79 * hash + shortSize;
hash = 79 * hash + wideCharSize;
int[] keys = sizeAlignmentMap.getKeys();
try {
for (int key : keys) {
hash = 79 * hash + sizeAlignmentMap.get(key);
}
}
catch (NoValueException ex) {
hash = 0;
}
return hash;
}
} }

View file

@ -21,10 +21,20 @@ import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace; import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.data.DataOrganization; import ghidra.program.model.data.DataOrganization;
import ghidra.program.model.data.GenericCallingConvention; import ghidra.program.model.data.GenericCallingConvention;
import ghidra.program.model.listing.*; import ghidra.program.model.listing.DefaultProgramContext;
import ghidra.program.model.listing.Parameter;
/** /**
* Interface for classes that hold compiler option information * Interface for requesting specific information about the compiler used to
* build a Program being analyzed. Major elements that can be queried include:
* - AddressSpaces from the Language plus compiler specific ones like "stack"
* - DataOrganization describing size and alignment of primitive data-types: int, long, pointers, etc.
* - PrototypeModels describing calling conventions used by the compiler: __stdcall, __thiscall, etc.
* - InjectPayloads or p-code that can used for
* - Call-fixups, substituting p-code for compiler bookkeeping functions during analysis.
* - Callother-fixups, substituting p-code for user-defined p-code operations.
* - Memory ranges that the compiler treats as global
* - Context and register values known to the compiler over specific memory ranges
*/ */
public interface CompilerSpec { public interface CompilerSpec {
@ -35,6 +45,16 @@ public interface CompilerSpec {
public final static String CALLING_CONVENTION_fastcall = "__fastcall"; public final static String CALLING_CONVENTION_fastcall = "__fastcall";
public final static String CALLING_CONVENTION_vectorcall = "__vectorcall"; public final static String CALLING_CONVENTION_vectorcall = "__vectorcall";
/**
* Labels for PrototypeModels that are used by default for various analysis/evaluation
* use-cases, when the true model isn't known. The CompilerSpec maintains a specific
* default PrototypeModel to be used for each use-case label.
*/
public enum EvaluationModelType {
EVAL_CURRENT, // A PrototypeModel used to evaluate the "current" function
EVAL_CALLED // A PrototypeModel used to evaluate a "called" function
}
/** /**
* Get the Language this compiler spec is based on. Note that * Get the Language this compiler spec is based on. Note that
* compiler specs may be reused across multiple languages in the * compiler specs may be reused across multiple languages in the
@ -45,12 +65,11 @@ public interface CompilerSpec {
public Language getLanguage(); public Language getLanguage();
/** /**
* Returns a brief description of the compiler spec * @return a brief description of the compiler spec
*/ */
public CompilerSpecDescription getCompilerSpecDescription(); public CompilerSpecDescription getCompilerSpecDescription();
/** /**
* Returns the id string associated with this compiler spec;
* @return the id string associated with this compiler spec; * @return the id string associated with this compiler spec;
*/ */
public CompilerSpecID getCompilerSpecID(); public CompilerSpecID getCompilerSpecID();
@ -72,7 +91,7 @@ public interface CompilerSpec {
/** /**
* Get an address space by name. This can be value added over the normal AddressFactory.getAddressSpace * Get an address space by name. This can be value added over the normal AddressFactory.getAddressSpace
* routine because the compiler spec can refer to special internal spaces like the stack space * routine because the compiler spec can refer to special internal spaces like the stack space
* @param spaceName * @param spaceName is the name of the address space
* @return the corresponding AddressSpace object * @return the corresponding AddressSpace object
*/ */
public AddressSpace getAddressSpace(String spaceName); public AddressSpace getAddressSpace(String spaceName);
@ -90,7 +109,7 @@ public interface CompilerSpec {
public AddressSpace getStackBaseSpace(); public AddressSpace getStackBaseSpace();
/** /**
* Returns true if stack grows with negative offsets * @return true if the stack grows with negative offsets
*/ */
public boolean stackGrowsNegative(); public boolean stackGrowsNegative();
@ -102,7 +121,7 @@ public interface CompilerSpec {
public void applyContextSettings(DefaultProgramContext ctx); public void applyContextSettings(DefaultProgramContext ctx);
/** /**
* Returns an array of the prototype models. Each prototype model specifies a calling convention. * @return an array of the prototype models. Each prototype model specifies a calling convention.
*/ */
public PrototypeModel[] getCallingConventions(); public PrototypeModel[] getCallingConventions();
@ -114,9 +133,9 @@ public interface CompilerSpec {
public PrototypeModel getCallingConvention(String name); public PrototypeModel getCallingConvention(String name);
/** /**
* Returns an array of the named prototype models. Each prototype model specifies a calling convention. * @return all possible PrototypeModels, including calling conventions and merge models
*/ */
public PrototypeModel[] getNamedCallingConventions(); public PrototypeModel[] getAllModels();
/** /**
* Returns the prototype model that is the default calling convention or else null. * Returns the prototype model that is the default calling convention or else null.
@ -125,8 +144,26 @@ public interface CompilerSpec {
public PrototypeModel getDefaultCallingConvention(); public PrototypeModel getDefaultCallingConvention();
/** /**
* Returns true if specified address location has been designated global * Get the language that the decompiler produces
* @param addr address location * @return an enum specifying the language
*/
public DecompilerLanguage getDecompilerOutputLanguage();
/**
* Get the evaluation model matching the given type.
* If analysis needs to apply a PrototypeModel to a function but a specific model
* is not known, then this method can be used to select a putative PrototypeModel
* based on the analysis use-case:
* - EVAL_CURRENT indicates the model to use for the "current function" being analyzed
* - EVAL_CALLED indicates the model to use for a function called by the current function
* @param modelType is the type of evaluation model
* @return prototype evaluation model
*/
public PrototypeModel getPrototypeEvaluationModel(EvaluationModelType modelType);
/**
* @param addr is the (start of the) storage location
* @return true if the specified storage location has been designated "global" in scope
*/ */
public boolean isGlobal(Address addr); public boolean isGlobal(Address addr);
@ -135,28 +172,8 @@ public interface CompilerSpec {
public PcodeInjectLibrary getPcodeInjectLibrary(); public PcodeInjectLibrary getPcodeInjectLibrary();
/** /**
* Register program-specific compiler-spec options * Get the PrototypeModel corresponding to the given generic calling convention
* @param program * @param genericCallingConvention is the given generic calling convention
*/
public void registerProgramOptions(Program program);
/**
* Get the program-specific prototype evaluation model.
* @param program
* @return prototype evaluation model
*/
public Object getPrototypeEvaluationModel(Program program);
/**
* Get the language that the decompiler produces
* @param program
* @return an enum specifying the language
*/
public DecompilerLanguage getDecompilerOutputLanguage(Program program);
/**
* Get the PrototypeModel based on the genericCallingConvention
* @param genericCallingConvention
* @return the matching model or the defaultModel if nothing matches * @return the matching model or the defaultModel if nothing matches
*/ */
public PrototypeModel matchConvention(GenericCallingConvention genericCallingConvention); public PrototypeModel matchConvention(GenericCallingConvention genericCallingConvention);
@ -164,22 +181,23 @@ public interface CompilerSpec {
/** /**
* Find the best guess at a calling convention model from this compiler spec * Find the best guess at a calling convention model from this compiler spec
* given an ordered list of (potential) parameters. * given an ordered list of (potential) parameters.
* @param params is the ordered list of parameters
* @return prototype model corresponding to the specified function signature * @return prototype model corresponding to the specified function signature
*/ */
public PrototypeModel findBestCallingConvention(Parameter[] params); public PrototypeModel findBestCallingConvention(Parameter[] params);
/** /**
* Returns whether this lanugage has a property defined. * Returns whether this language has a property defined.
* @param key the property key * @param key the property key
* @return if the property is defined * @return if the property is defined
*/ */
public boolean hasProperty(String key); public boolean hasProperty(String key);
/** /**
* Return true if function prototypes respect the C-language datatype conversion conventions. * Return true if function prototypes respect the C-language data-type conversion conventions.
* This amounts to converting array datatypes to pointer-to-element datatypes. * This amounts to converting array data-types to pointer-to-element data-types.
* In C, arrays are passed by reference (structures are still passed by value) * In C, arrays are passed by reference (structures are still passed by value)
* @return * @return if the prototype does C-language data-type conversions
*/ */
public boolean doesCDataTypeConversions(); public boolean doesCDataTypeConversions();

View file

@ -1,6 +1,5 @@
/* ### /* ###
* IP: GHIDRA * IP: GHIDRA
* REVIEWED: YES
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,9 +15,15 @@
*/ */
package ghidra.program.model.lang; package ghidra.program.model.lang;
import ghidra.program.model.address.Address;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.Iterator;
import java.util.List;
import ghidra.app.plugin.processors.sleigh.SleighException;
import ghidra.program.model.address.Address;
import ghidra.program.model.pcode.AddressXML;
import ghidra.util.xml.SpecXmlUtils;
import ghidra.xml.*;
/** /**
* Class for context configuration information as * Class for context configuration information as
@ -37,6 +42,35 @@ public class ContextSetting {
this.endAddr = endAddr; this.endAddr = endAddr;
} }
/**
* Construct from an XML \<set> tag. The tag is a child of either \<context_set> or \<tracked_set>
* which provides details of the memory range affected.
* @param el is the XML tag
* @param cspec is used to lookup register names present in the tag
* @param isContextReg is true for a \<context_set> parent, false for a \<tracked_set> parent
* @param first is the first Address in the affected memory range
* @param last is the last Address in the affected memory range
*/
private ContextSetting(XmlElement el, CompilerSpec cspec, boolean isContextReg, Address first,
Address last) throws SleighException {
startAddr = first;
endAddr = last;
String name = el.getAttribute("name");
value = getBigInteger(el.getAttribute("val"), 0);
register = cspec.getLanguage().getRegister(name);
if (register == null) {
throw new SleighException("Unknown register: " + name);
}
if (isContextReg) {
if (!register.isProcessorContext()) {
throw new SleighException("Register " + name + " is not a context register");
}
}
else if (register.isProcessorContext()) {
throw new SleighException("Unexpected context register " + name);
}
}
public Register getRegister() { public Register getRegister() {
return register; return register;
} }
@ -52,4 +86,128 @@ public class ContextSetting {
public Address getEndAddress() { public Address getEndAddress() {
return endAddr; return endAddr;
} }
private BigInteger getBigInteger(String valStr, long defaultValue) {
int radix = 10;
if (valStr.startsWith("0x") || valStr.startsWith("0X")) {
valStr = valStr.substring(2);
radix = 16;
}
try {
return new BigInteger(valStr, radix);
}
catch (Exception e) {
return BigInteger.valueOf(defaultValue);
}
}
public void saveXml(StringBuilder buffer) {
buffer.append("<set");
SpecXmlUtils.encodeStringAttribute(buffer, "name", register.getName());
SpecXmlUtils.encodeStringAttribute(buffer, "val", value.toString());
buffer.append("/>\n");
}
@Override
public boolean equals(Object obj) {
ContextSetting op2 = (ContextSetting) obj;
if (!startAddr.equals(op2.startAddr)) {
return false;
}
if (!endAddr.equals(op2.endAddr)) {
return false;
}
if (!register.equals(op2.register)) {
return false;
}
if (!value.equals(op2.value)) {
return false;
}
return true;
}
@Override
public int hashCode() {
int hash = startAddr.hashCode();
hash = 79 * hash + endAddr.hashCode();
hash = 79 * hash + register.hashCode();
hash = 79 * hash + value.hashCode();
return hash;
}
public static void parseContextSet(List<ContextSetting> resList, XmlPullParser parser,
CompilerSpec cspec) throws XmlParseException {
XmlElement el = parser.start();
boolean isContextReg;
if (el.getName().equals("context_set")) {
isContextReg = true;
}
else if (el.getName().equals("tracked_set")) {
isContextReg = false;
}
else {
throw new XmlParseException("Unknown context setting tag: " + el.getName());
}
AddressXML range = AddressXML.restoreRangeXml(el, cspec);
Address firstAddr = range.getFirstAddress();
Address lastAddr = range.getLastAddress();
while (parser.peek().isStart()) {
XmlElement subel = parser.start();
ContextSetting ctxSetting =
new ContextSetting(subel, cspec, isContextReg, firstAddr, lastAddr);
parser.end(subel);
resList.add(ctxSetting);
}
parser.end(el);
}
public static void parseContextData(List<ContextSetting> resList, XmlPullParser parser,
CompilerSpec cspec) throws XmlParseException {
parser.start();
while (parser.peek().isStart()) {
parseContextSet(resList, parser, cspec);
}
parser.end();
}
public static void buildContextDataXml(StringBuilder buffer, List<ContextSetting> ctxList) {
if (ctxList.isEmpty()) {
return;
}
buffer.append("<context_data>\n");
Iterator<ContextSetting> iter = ctxList.iterator();
ContextSetting startContext = iter.next();
boolean isContextReg = startContext.register.isProcessorContext();
Address firstAddr = startContext.startAddr;
Address lastAddr = startContext.endAddr;
while (iter.hasNext()) {
buffer.append(isContextReg ? "<context_set" : "<tracked_set");
AddressXML.appendAttributes(buffer, firstAddr, lastAddr);
buffer.append(">\n");
startContext.saveXml(buffer);
while (iter.hasNext()) {
startContext = iter.next();
boolean nextIsContext = startContext.register.isProcessorContext();
boolean shouldBreak = false;
if (isContextReg != nextIsContext) {
isContextReg = nextIsContext;
shouldBreak = true;
}
if (!firstAddr.equals(startContext.startAddr)) {
firstAddr = startContext.startAddr;
shouldBreak = true;
}
if (!lastAddr.equals(startContext.endAddr)) {
lastAddr = startContext.endAddr;
shouldBreak = true;
}
if (shouldBreak) {
break;
}
startContext.saveXml(buffer);
}
buffer.append(isContextReg ? "</context_set>\n" : "</tracked_set>\n");
}
buffer.append("</context_data>\n");
}
} }

View file

@ -15,24 +15,21 @@
*/ */
package ghidra.program.model.lang; package ghidra.program.model.lang;
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressFactory;
import ghidra.program.model.pcode.PcodeXMLException;
import ghidra.program.model.pcode.Varnode;
import ghidra.util.xml.SpecXmlUtils;
import java.io.IOException; import java.io.IOException;
import java.io.StringReader; import java.io.StringReader;
import java.util.ArrayList; import java.util.ArrayList;
import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParser;
import org.xml.sax.Attributes; import org.xml.sax.*;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler; import org.xml.sax.helpers.DefaultHandler;
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressFactory;
import ghidra.program.model.pcode.*;
import ghidra.util.xml.SpecXmlUtils;
public class InjectContext { public class InjectContext {
private class Handler extends DefaultHandler { private class Handler extends DefaultHandler {
private AddressFactory addrFactory; private AddressFactory addrFactory;
@ -48,18 +45,19 @@ public class InjectContext {
@Override @Override
public void startElement(String uri, String localName, String rawName, Attributes attr) public void startElement(String uri, String localName, String rawName, Attributes attr)
throws SAXException { throws SAXException {
if (rawName.equals("context")) if (rawName.equals("context")) {
state = 1; state = 1;
}
else if (rawName.equals("input")) { else if (rawName.equals("input")) {
inputlist = new ArrayList<Varnode>(); inputlist = new ArrayList<>();
state = 3; state = 3;
} }
else if (rawName.equals("output")) { else if (rawName.equals("output")) {
output = new ArrayList<Varnode>(); output = new ArrayList<>();
state = 4; state = 4;
} }
else if (rawName.equals("addr")) { else if (rawName.equals("addr")) {
curaddr = Varnode.readXMLAddress(rawName, attr, addrFactory); curaddr = AddressXML.readXML(rawName, attr, addrFactory);
if (state == 1) { if (state == 1) {
baseAddr = curaddr; baseAddr = curaddr;
state = 2; state = 2;
@ -69,20 +67,22 @@ public class InjectContext {
} }
else if (state == 3) { else if (state == 3) {
int size = SpecXmlUtils.decodeInt(attr.getValue("size")); int size = SpecXmlUtils.decodeInt(attr.getValue("size"));
Varnode vn = new Varnode(curaddr,size); Varnode vn = new Varnode(curaddr, size);
inputlist.add(vn); inputlist.add(vn);
} }
else if (state == 4) { else if (state == 4) {
int size = SpecXmlUtils.decodeInt(attr.getValue("size")); int size = SpecXmlUtils.decodeInt(attr.getValue("size"));
Varnode vn = new Varnode(curaddr,size); Varnode vn = new Varnode(curaddr, size);
output.add(vn); output.add(vn);
} }
} }
else else {
throw new SAXException("Unrecognized inject tag: "+rawName); throw new SAXException("Unrecognized inject tag: " + rawName);
}
} }
} }
public SleighLanguage language; public SleighLanguage language;
public Address baseAddr; // Base address of op (call,userop) causing the inject public Address baseAddr; // Base address of op (call,userop) causing the inject
public Address nextAddr; // Address of next instruction following the injecting instruction public Address nextAddr; // Address of next instruction following the injecting instruction
@ -94,14 +94,17 @@ public class InjectContext {
public InjectContext() { public InjectContext() {
} }
public void restoreXml(SAXParser parser,String xml,AddressFactory addrFactory) throws PcodeXMLException { public void restoreXml(SAXParser parser, String xml, AddressFactory addrFactory)
throws PcodeXMLException {
Handler handler = new Handler(addrFactory); Handler handler = new Handler(addrFactory);
try { try {
parser.parse(new InputSource(new StringReader(xml)), handler); parser.parse(new InputSource(new StringReader(xml)), handler);
} catch (SAXException e) { }
throw new PcodeXMLException("Problem parsing inject context: "+e.getMessage()); catch (SAXException e) {
} catch (IOException e) { throw new PcodeXMLException("Problem parsing inject context: " + e.getMessage());
throw new PcodeXMLException("Problem parsing inject context: "+e.getMessage()); }
catch (IOException e) {
throw new PcodeXMLException("Problem parsing inject context: " + e.getMessage());
} }
} }

View file

@ -16,8 +16,11 @@
package ghidra.program.model.lang; package ghidra.program.model.lang;
import ghidra.app.plugin.processors.sleigh.PcodeEmit; import ghidra.app.plugin.processors.sleigh.PcodeEmit;
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.program.model.pcode.PcodeOp; import ghidra.program.model.pcode.PcodeOp;
import ghidra.xml.XmlParseException;
import ghidra.xml.XmlPullParser;
/** /**
* <code>InjectPayload</code> encapsulates a semantic (p-code) override which can be injected * <code>InjectPayload</code> encapsulates a semantic (p-code) override which can be injected
@ -37,7 +40,7 @@ public interface InjectPayload {
private int index; private int index;
private int size; private int size;
public InjectParameter(String nm,int sz) { public InjectParameter(String nm, int sz) {
name = nm; name = nm;
index = 0; index = 0;
size = sz; size = sz;
@ -58,6 +61,26 @@ public interface InjectPayload {
void setIndex(int i) { void setIndex(int i) {
index = i; index = i;
} }
@Override
public boolean equals(Object obj) {
InjectParameter op2 = (InjectParameter) obj;
if (index != op2.index || size != op2.size) {
return false;
}
if (!name.equals(op2.name)) {
return false;
}
return true;
}
@Override
public int hashCode() {
int hash = name.hashCode();
hash = 79 * hash + index;
hash = 79 * hash + size;
return hash;
}
} }
/** /**
@ -90,12 +113,19 @@ public interface InjectPayload {
*/ */
public InjectParameter[] getOutput(); public InjectParameter[] getOutput();
/**
* If parsing a payload (from XML) fails, a placeholder payload may be substituted and
* this method returns true for the substitute. In all other cases, this returns false.
* @return true if this is a placeholder for a payload with parse errors.
*/
public boolean isErrorPlaceholder();
/** /**
* Given a context, send the p-code payload to the emitter * Given a context, send the p-code payload to the emitter
* @param context is the context for injection * @param context is the context for injection
* @param emit is the object accumulating the final p-code * @param emit is the object accumulating the final p-code
*/ */
public void inject(InjectContext context,PcodeEmit emit); public void inject(InjectContext context, PcodeEmit emit);
/** /**
* A convenience function wrapping the inject method, to produce the final set * A convenience function wrapping the inject method, to produce the final set
@ -110,4 +140,24 @@ public interface InjectPayload {
* @return true if the injected p-code falls thru * @return true if the injected p-code falls thru
*/ */
public boolean isFallThru(); public boolean isFallThru();
/**
* @return true if this inject's COPY operations should be treated as incidental
*/
public boolean isIncidentalCopy();
/**
* Write out configuration parameters as a \<pcode> XML tag
* @param buffer is the stream to write to
*/
public void saveXml(StringBuilder buffer);
/**
* Restore the payload from an XML stream. The root expected document is
* the \<pcode> tag, which may be wrapped with another tag by the derived class.
* @param parser is the XML stream
* @param language is used to resolve registers and address spaces
* @throws XmlParseException for badly formed XML
*/
public void restoreXml(XmlPullParser parser, SleighLanguage language) throws XmlParseException;
} }

View file

@ -18,18 +18,39 @@ package ghidra.program.model.lang;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import ghidra.app.plugin.processors.sleigh.SleighException; import ghidra.app.plugin.processors.sleigh.SleighLanguage;
import ghidra.xml.XmlElement; import ghidra.app.plugin.processors.sleigh.template.ConstructTpl;
import ghidra.xml.XmlPullParser; import ghidra.util.xml.SpecXmlUtils;
import ghidra.xml.*;
public class InjectPayloadCallfixup extends InjectPayloadSleigh { public class InjectPayloadCallfixup extends InjectPayloadSleigh {
private List<String> targetSymbolNames; protected List<String> targetSymbolNames;
/**
* Constructor for a partial clone of a payload whose p-code failed to parse.
* @param pcode is the p-code to substitute
* @param failedPayload is the failed callfixup
*/
protected InjectPayloadCallfixup(ConstructTpl pcode, InjectPayloadCallfixup failedPayload) {
super(pcode, failedPayload);
targetSymbolNames = failedPayload.targetSymbolNames;
}
/**
* Construct a dummy payload
* @param pcode is the dummy p-code sequence to use
* @param nm is the name of the payload
*/
protected InjectPayloadCallfixup(ConstructTpl pcode, String nm) {
super(pcode, CALLFIXUP_TYPE, nm);
targetSymbolNames = new ArrayList<>();
}
public InjectPayloadCallfixup(String sourceName) { public InjectPayloadCallfixup(String sourceName) {
super(sourceName); super(sourceName);
type = CALLFIXUP_TYPE; type = CALLFIXUP_TYPE;
targetSymbolNames = new ArrayList<String>(); targetSymbolNames = new ArrayList<>();
} }
public List<String> getTargets() { public List<String> getTargets() {
@ -37,22 +58,21 @@ public class InjectPayloadCallfixup extends InjectPayloadSleigh {
} }
@Override @Override
public InjectPayloadSleigh clone() { public void saveXml(StringBuilder buffer) {
InjectPayloadSleigh res = new InjectPayloadCallfixup(source); buffer.append("<callfixup");
res.copy(this); SpecXmlUtils.encodeStringAttribute(buffer, "name", name);
return res; buffer.append(">\n");
for (String nm : targetSymbolNames) {
buffer.append("<target");
SpecXmlUtils.encodeStringAttribute(buffer, "name", nm);
buffer.append("/>\n");
}
super.saveXml(buffer);
buffer.append("</callfixup>\n");
} }
@Override @Override
protected void copy(InjectPayloadSleigh op2) { public void restoreXml(XmlPullParser parser, SleighLanguage language) throws XmlParseException {
super.copy(op2);
InjectPayloadCallfixup fixup = (InjectPayloadCallfixup) op2;
for (String target : fixup.targetSymbolNames)
targetSymbolNames.add(target);
}
@Override
public void restoreXml(XmlPullParser parser) {
XmlElement fixupEl = parser.start("callfixup"); XmlElement fixupEl = parser.start("callfixup");
name = fixupEl.getAttribute("name"); name = fixupEl.getAttribute("name");
boolean pcodeSubtag = false; boolean pcodeSubtag = false;
@ -62,21 +82,41 @@ public class InjectPayloadCallfixup extends InjectPayloadSleigh {
XmlElement subel = parser.start(); XmlElement subel = parser.start();
String targetName = subel.getAttribute("name"); String targetName = subel.getAttribute("name");
if (targetName == null) { if (targetName == null) {
throw new SleighException("Invalid callfixup target, missing target name"); throw new XmlParseException("Invalid callfixup target, missing target name");
} }
targetSymbolNames.add(targetName); targetSymbolNames.add(targetName);
parser.end(subel); parser.end(subel);
} }
else if (elname.equals("pcode")) { else if (elname.equals("pcode")) {
super.restoreXml(parser); super.restoreXml(parser, language);
pcodeSubtag = true; pcodeSubtag = true;
} }
else { else {
throw new SleighException("Unknown callfixup tag: " + elname); throw new XmlParseException("Unknown callfixup tag: " + elname);
} }
} }
if (!pcodeSubtag) if (!pcodeSubtag) {
throw new SleighException("<callfixup> missing <pcode> subtag: " + name); throw new XmlParseException("<callfixup> missing <pcode> subtag: " + name);
}
parser.end(fixupEl); parser.end(fixupEl);
} }
@Override
public boolean equals(Object obj) {
InjectPayloadCallfixup op2 = (InjectPayloadCallfixup) obj;
if (!targetSymbolNames.equals(op2.targetSymbolNames)) {
return false;
}
return super.equals(obj);
}
@Override
public int hashCode() {
int hash = 13;
for (String target : targetSymbolNames) {
hash = 79 * hash + target.hashCode();
}
hash = 79 * hash + super.hashCode();
return hash;
}
} }

View file

@ -0,0 +1,39 @@
/* ###
* 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.program.model.lang;
import ghidra.program.model.address.AddressFactory;
/**
* A substitute for a callfixup that did not successfully parse.
*/
public class InjectPayloadCallfixupError extends InjectPayloadCallfixup {
public InjectPayloadCallfixupError(AddressFactory addrFactory,
InjectPayloadCallfixup failedPayload) {
// Make a partial clone
super(InjectPayloadSleigh.getDummyPcode(addrFactory), failedPayload);
}
public InjectPayloadCallfixupError(AddressFactory addrFactory, String nm) {
super(InjectPayloadSleigh.getDummyPcode(addrFactory), nm);
}
@Override
public boolean isErrorPlaceholder() {
return true;
}
}

View file

@ -15,31 +15,53 @@
*/ */
package ghidra.program.model.lang; package ghidra.program.model.lang;
import ghidra.app.plugin.processors.sleigh.SleighException; import ghidra.app.plugin.processors.sleigh.SleighLanguage;
import ghidra.xml.XmlElement; import ghidra.app.plugin.processors.sleigh.template.ConstructTpl;
import ghidra.xml.XmlPullParser; import ghidra.util.xml.SpecXmlUtils;
import ghidra.xml.*;
public class InjectPayloadCallother extends InjectPayloadSleigh { public class InjectPayloadCallother extends InjectPayloadSleigh {
/**
* Constructor for a partial clone of a payload whose p-code failed to parse.
* @param pcode is the p-code to substitute
* @param failedPayload is the failed callfixup
*/
protected InjectPayloadCallother(ConstructTpl pcode, InjectPayloadCallother failedPayload) {
super(pcode, failedPayload);
}
/**
* Constructor for a dummy payload
* @param pcode is the dummy p-code to use
* @param nm is the name of the payload
*/
protected InjectPayloadCallother(ConstructTpl pcode, String nm) {
super(pcode, CALLOTHERFIXUP_TYPE, nm);
}
public InjectPayloadCallother(String sourceName) { public InjectPayloadCallother(String sourceName) {
super(sourceName); super(sourceName);
type = CALLOTHERFIXUP_TYPE; type = CALLOTHERFIXUP_TYPE;
} }
@Override @Override
public InjectPayloadSleigh clone() { public void saveXml(StringBuilder buffer) {
InjectPayloadSleigh res = new InjectPayloadCallother(source); buffer.append("<callotherfixup");
res.copy(this); SpecXmlUtils.encodeStringAttribute(buffer, "targetop", name);
return res; buffer.append(">\n");
super.saveXml(buffer);
buffer.append("</callotherfixup>\n");
} }
@Override @Override
public void restoreXml(XmlPullParser parser) { public void restoreXml(XmlPullParser parser, SleighLanguage language) throws XmlParseException {
XmlElement fixupEl = parser.start("callotherfixup"); XmlElement fixupEl = parser.start("callotherfixup");
name = fixupEl.getAttribute("targetop"); name = fixupEl.getAttribute("targetop");
if (!parser.peek().isStart() || !parser.peek().getName().equals("pcode")) if (!parser.peek().isStart() || !parser.peek().getName().equals("pcode")) {
throw new SleighException("<callotherfixup> does not contain a <pcode> tag"); throw new XmlParseException("<callotherfixup> does not contain a <pcode> tag");
super.restoreXml(parser); }
super.restoreXml(parser, language);
parser.end(fixupEl); parser.end(fixupEl);
} }
} }

View file

@ -0,0 +1,43 @@
/* ###
* 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.program.model.lang;
import ghidra.program.model.address.AddressFactory;
/**
* A substitute for a callother fixup that did not fully parse
*/
public class InjectPayloadCallotherError extends InjectPayloadCallother {
/**
* Constructor for use if the p-code template did not parse
* @param addrFactory is the address factory to use constructing dummy p-code
* @param failedPayload is the object with the failed template
*/
public InjectPayloadCallotherError(AddressFactory addrFactory,
InjectPayloadCallother failedPayload) {
super(InjectPayloadSleigh.getDummyPcode(addrFactory), failedPayload);
}
public InjectPayloadCallotherError(AddressFactory addrFactory, String nm) {
super(InjectPayloadSleigh.getDummyPcode(addrFactory), nm);
}
@Override
public boolean isErrorPlaceholder() {
return true;
}
}

View file

@ -0,0 +1,48 @@
/* ###
* 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.program.model.lang;
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
import ghidra.xml.*;
public class InjectPayloadJumpAssist extends InjectPayloadSleigh {
private String baseName;
public InjectPayloadJumpAssist(String bName, String sourceName) {
super(sourceName);
baseName = bName;
type = EXECUTABLEPCODE_TYPE;
}
@Override
public void restoreXml(XmlPullParser parser, SleighLanguage language) throws XmlParseException {
XmlElement subel = parser.peek();
if (subel.getName().charAt(0) == 'c') {
name = baseName + "_index2case";
}
else if (subel.getName().charAt(0) == 'a') {
name = baseName + "_index2addr";
}
else if (subel.getName().charAt(0) == 's') {
name = baseName + "_calcsize";
}
else {
name = baseName + "_defaultaddr";
}
super.restoreXml(parser, language);
}
}

View file

@ -0,0 +1,137 @@
/* ###
* 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.program.model.lang;
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.pcode.AddressXML;
import ghidra.util.SystemUtilities;
import ghidra.util.xml.SpecXmlUtils;
import ghidra.xml.*;
public class InjectPayloadSegment extends InjectPayloadSleigh {
private AddressSpace space;
private boolean supportsFarPointer;
private AddressSpace constResolveSpace;
private long constResolveOffset;
private int constResolveSize;
public InjectPayloadSegment(String source) {
super(source);
type = EXECUTABLEPCODE_TYPE;
space = null;
supportsFarPointer = false;
constResolveSpace = null;
constResolveOffset = 0;
constResolveSize = 0;
}
@Override
public void saveXml(StringBuilder buffer) {
buffer.append("<segmentop");
int pos = name.indexOf('_');
String subName = pos > 0 ? name.substring(0, pos) : name;
if (!subName.equals("segment")) {
SpecXmlUtils.encodeStringAttribute(buffer, "userop", subName);
}
SpecXmlUtils.encodeStringAttribute(buffer, "space", space.getName());
if (supportsFarPointer) {
SpecXmlUtils.encodeBooleanAttribute(buffer, "farpointer", supportsFarPointer);
}
buffer.append(">\n");
super.saveXml(buffer);
if (constResolveSpace != null) {
buffer.append("<constresolve>\n");
buffer.append("<varnode");
SpecXmlUtils.encodeStringAttribute(buffer, "space", constResolveSpace.getName());
SpecXmlUtils.encodeUnsignedIntegerAttribute(buffer, "offset", constResolveOffset);
SpecXmlUtils.encodeSignedIntegerAttribute(buffer, "size", constResolveSize);
buffer.append("/>\n");
buffer.append("</constresolve>\n");
}
buffer.append("</segmentop>\n");
}
@Override
public void restoreXml(XmlPullParser parser, SleighLanguage language) throws XmlParseException {
XmlElement el = parser.start();
name = el.getAttribute("userop");
if (name == null) {
name = "segment";
}
name = name + "_pcode";
String spaceString = el.getAttribute("space");
space = language.getAddressFactory().getAddressSpace(spaceString);
if (space == null) {
throw new XmlParseException("Unknown address space: " + spaceString);
}
supportsFarPointer = SpecXmlUtils.decodeBoolean(el.getAttribute("farpointer"));
if (parser.peek().isStart()) {
if (parser.peek().getName().equals("pcode")) {
super.restoreXml(parser, language);
}
else {
throw new XmlParseException("Missing <pcode> child for <segmentop> tag");
}
}
if (parser.peek().isStart()) {
XmlElement subel = parser.start("constresolve");
XmlElement subsubel = parser.start();
AddressXML addrSize = AddressXML.restoreXml(subsubel, language);
addrSize.getFirstAddress(); // Fail fast. Throws AddressOutOfBoundsException if offset is invalid
constResolveSpace = addrSize.getAddressSpace();
constResolveOffset = addrSize.getOffset();
constResolveSize = (int) addrSize.getSize();
parser.end(subsubel);
parser.end(subel);
}
parser.end(el);
}
@Override
public boolean equals(Object obj) {
InjectPayloadSegment op2 = (InjectPayloadSegment) obj;
if (constResolveOffset != op2.constResolveOffset) {
return false;
}
if (constResolveSize != op2.constResolveSize) {
return false;
}
if (!SystemUtilities.isEqual(constResolveSpace, op2.constResolveSpace)) {
return false;
}
if (!space.equals(op2.space)) {
return false;
}
if (supportsFarPointer != op2.supportsFarPointer) {
return false;
}
return true;
}
@Override
public int hashCode() {
int hash = space.hashCode();
if (constResolveSpace != null) {
hash = 79 * hash + constResolveSpace.hashCode();
}
hash = 79 * hash + Long.hashCode(constResolveOffset);
hash = 79 * hash + constResolveSize;
hash = 79 * hash + (supportsFarPointer ? 1 : 13);
return hash;
}
}

View file

@ -16,18 +16,19 @@
package ghidra.program.model.lang; package ghidra.program.model.lang;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
import ghidra.app.plugin.processors.sleigh.*; import ghidra.app.plugin.processors.sleigh.*;
import ghidra.app.plugin.processors.sleigh.template.ConstructTpl; import ghidra.app.plugin.processors.sleigh.template.*;
import ghidra.app.plugin.processors.sleigh.template.OpTpl;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressFactory;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.MemoryAccessException; import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.pcode.PcodeOp; import ghidra.program.model.pcode.PcodeOp;
import ghidra.program.model.pcode.Varnode; import ghidra.program.model.pcode.Varnode;
import ghidra.util.SystemUtilities;
import ghidra.util.xml.SpecXmlUtils; import ghidra.util.xml.SpecXmlUtils;
import ghidra.xml.XmlElement; import ghidra.xml.*;
import ghidra.xml.XmlPullParser;
/** /**
* <code>InjectPayloadSleigh</code> defines an InjectPayload of p-code which is defined via * <code>InjectPayloadSleigh</code> defines an InjectPayload of p-code which is defined via
@ -38,66 +39,78 @@ public class InjectPayloadSleigh implements InjectPayload {
private ConstructTpl pcodeTemplate; private ConstructTpl pcodeTemplate;
private int paramShift; private int paramShift;
private boolean isfallthru; // Precomputed fallthru of inject private boolean isfallthru; // Precomputed fallthru of inject
private boolean incidentalCopy; // Treat COPY operations as incidental
private InjectParameter[] inputlist; private InjectParameter[] inputlist;
private InjectParameter[] output; private InjectParameter[] output;
private int subType; // 0=uponentry 1=uponreturn
protected String name; // Formal name of this inject protected String name; // Formal name of this inject
protected int type; // type of this payload CALLFIXUP_TYPE, CALLOTHERFIXUP_TYPE, etc. protected int type; // type of this payload CALLFIXUP_TYPE, CALLOTHERFIXUP_TYPE, etc.
protected String source; // Source of this payload protected String source; // Source of this payload
private String parseString; // String to be parsed for pcode private String parseString; // String to be parsed for pcode
/**
* Constructor for partial clone of another payload whose p-code failed to parse
* @param pcode is substitute p-code to replace the failed parse
* @param failedPayload is the failed payload
*/
protected InjectPayloadSleigh(ConstructTpl pcode, InjectPayloadSleigh failedPayload) {
pcodeTemplate = pcode;
paramShift = failedPayload.paramShift;
incidentalCopy = failedPayload.incidentalCopy;
inputlist = failedPayload.inputlist;
output = failedPayload.output;
subType = failedPayload.subType;
name = failedPayload.name;
type = failedPayload.type;
source = failedPayload.source + "_FAILED";
parseString = null;
isfallthru = computeFallThru();
}
/**
* Constructor for a dummy payload, given just a name
* @param pcode is the dummy p-code sequence
* @param tp is the type of injection
* @param nm is the name of the injection
*/
protected InjectPayloadSleigh(ConstructTpl pcode, int tp, String nm) {
pcodeTemplate = pcode;
paramShift = 0;
incidentalCopy = false;
inputlist = new InjectParameter[0];
output = new InjectParameter[0];
subType = -1;
name = nm;
type = tp;
source = "FAILED";
parseString = null;
isfallthru = computeFallThru();
}
/** /**
* Constructor for use where restoreXml is overridden and provides name and type * Constructor for use where restoreXml is overridden and provides name and type
* @param sourceName * @param sourceName is string describing the source of this payload
*/ */
protected InjectPayloadSleigh(String sourceName) { protected InjectPayloadSleigh(String sourceName) {
name = null; name = null;
type = -1; type = -1;
subType = -1;
incidentalCopy = false;
inputlist = null; inputlist = null;
output = null; output = null;
source = sourceName; source = sourceName;
} }
public InjectPayloadSleigh clone() {
InjectPayloadSleigh res = new InjectPayloadSleigh(source);
res.copy(this);
return res;
}
protected void copy(InjectPayloadSleigh op2) {
inputlist = null;
output = null;
paramShift = op2.paramShift;
isfallthru = op2.isfallthru;
name = op2.name;
type = op2.type;
source = op2.source;
parseString = op2.parseString;
if (op2.inputlist != null) {
inputlist = new InjectParameter[op2.inputlist.length];
for (int i = 0; i < inputlist.length; ++i) {
inputlist[i] =
new InjectParameter(op2.inputlist[i].getName(), op2.inputlist[i].getSize());
inputlist[i].setIndex(op2.inputlist[i].getIndex());
}
}
if (op2.output != null) {
output = new InjectParameter[op2.output.length];
for (int i = 0; i < output.length; ++i) {
output[i] = new InjectParameter(op2.output[i].getName(), op2.output[i].getSize());
output[i].setIndex(op2.output[i].getIndex());
}
}
}
/** /**
* Provide basic form, restoreXml fills in the rest * Provide basic form, restoreXml fills in the rest
* @param nm must provide formal name * @param nm must provide formal name
* @param tp must provide type * @param tp must provide type
* @param sourceName * @param sourceName is a description of the source of this payload
*/ */
public InjectPayloadSleigh(String nm, int tp, String sourceName) { public InjectPayloadSleigh(String nm, int tp, String sourceName) {
name = nm; name = nm;
type = tp; type = tp;
subType = -1;
inputlist = null; inputlist = null;
output = null; output = null;
source = sourceName; source = sourceName;
@ -123,12 +136,12 @@ public class InjectPayloadSleigh implements InjectPayload {
return paramShift; return paramShift;
} }
protected void setInputParameters(ArrayList<InjectParameter> in) { protected void setInputParameters(List<InjectParameter> in) {
inputlist = new InjectParameter[in.size()]; inputlist = new InjectParameter[in.size()];
in.toArray(inputlist); in.toArray(inputlist);
} }
protected void setOutputParameters(ArrayList<InjectParameter> out) { protected void setOutputParameters(List<InjectParameter> out) {
output = new InjectParameter[out.size()]; output = new InjectParameter[out.size()];
out.toArray(output); out.toArray(output);
} }
@ -144,12 +157,17 @@ public class InjectPayloadSleigh implements InjectPayload {
} }
@Override @Override
public void inject(InjectContext context,PcodeEmit emit) { public boolean isErrorPlaceholder() {
return false;
}
@Override
public void inject(InjectContext context, PcodeEmit emit) {
ParserWalker walker = emit.getWalker(); ParserWalker walker = emit.getWalker();
try { try {
walker.snippetState(); walker.snippetState();
setupParameters(context, walker); setupParameters(context, walker);
emit.build(pcodeTemplate,-1); emit.build(pcodeTemplate, -1);
} }
catch (UnknownInstructionException e) { // Should not be happening in a CallFixup catch (UnknownInstructionException e) { // Should not be happening in a CallFixup
e.printStackTrace(); e.printStackTrace();
@ -168,7 +186,7 @@ public class InjectPayloadSleigh implements InjectPayload {
new SleighParserContext(con.baseAddr, con.nextAddr, con.refAddr, con.callAddr); new SleighParserContext(con.baseAddr, con.nextAddr, con.refAddr, con.callAddr);
ParserWalker walker = new ParserWalker(protoContext); ParserWalker walker = new ParserWalker(protoContext);
PcodeEmitObjects emit = new PcodeEmitObjects(walker); PcodeEmitObjects emit = new PcodeEmitObjects(walker);
inject(con,emit); inject(con, emit);
return emit.getPcodeOp(); return emit.getPcodeOp();
} }
@ -177,6 +195,11 @@ public class InjectPayloadSleigh implements InjectPayload {
return isfallthru; return isfallthru;
} }
@Override
public boolean isIncidentalCopy() {
return incidentalCopy;
}
private boolean computeFallThru() { private boolean computeFallThru() {
OpTpl[] opVec = pcodeTemplate.getOpVec(); OpTpl[] opVec = pcodeTemplate.getOpVec();
if (opVec.length <= 0) { if (opVec.length <= 0) {
@ -207,18 +230,65 @@ public class InjectPayloadSleigh implements InjectPayload {
} }
} }
public void restoreXml(XmlPullParser parser) { @Override
ArrayList<InjectParameter> inlist = new ArrayList<InjectParameter>(); public void saveXml(StringBuilder buffer) {
ArrayList<InjectParameter> outlist = new ArrayList<InjectParameter>(); buffer.append("<pcode");
XmlElement el = parser.start(); if (type == CALLMECHANISM_TYPE && subType >= 0) {
SpecXmlUtils.encodeStringAttribute(buffer, "inject",
(subType == 0) ? "uponentry" : "uponreturn");
}
if (paramShift != 0) {
SpecXmlUtils.encodeSignedIntegerAttribute(buffer, "paramshift", paramShift);
}
if (pcodeTemplate == null) {
SpecXmlUtils.encodeBooleanAttribute(buffer, "dynamic", true);
}
if (incidentalCopy) {
SpecXmlUtils.encodeBooleanAttribute(buffer, "incidentalcopy", incidentalCopy);
}
buffer.append(">\n");
for (InjectParameter param : inputlist) {
buffer.append("<input");
SpecXmlUtils.encodeStringAttribute(buffer, "name", param.getName());
SpecXmlUtils.encodeSignedIntegerAttribute(buffer, "size", param.getSize());
buffer.append("/>\n");
}
for (InjectParameter param : output) {
buffer.append("<output");
SpecXmlUtils.encodeStringAttribute(buffer, "name", param.getName());
SpecXmlUtils.encodeSignedIntegerAttribute(buffer, "size", param.getSize());
buffer.append("/>\n");
}
if (pcodeTemplate != null) {
// Decompiler will not read the <body> tag
buffer.append("<body> local tmp:1 = 0; </body>\n");
}
buffer.append("</pcode>\n");
}
@Override
public void restoreXml(XmlPullParser parser, SleighLanguage language) throws XmlParseException {
ArrayList<InjectParameter> inlist = new ArrayList<>();
ArrayList<InjectParameter> outlist = new ArrayList<>();
XmlElement el = parser.start(); // The <pcode> tag
String injectstr = el.getAttribute("inject");
if (injectstr != null) {
if (injectstr.equals("uponentry")) {
subType = 0;
}
else if (injectstr.equals("uponreturn")) {
subType = 1;
}
else {
throw new XmlParseException("Unknown \"inject\" attribute value: " + injectstr);
}
}
String pshiftstr = el.getAttribute("paramshift"); String pshiftstr = el.getAttribute("paramshift");
paramShift = SpecXmlUtils.decodeInt(pshiftstr); paramShift = SpecXmlUtils.decodeInt(pshiftstr);
boolean isDynamic = false; boolean isDynamic = SpecXmlUtils.decodeBoolean(el.getAttribute("dynamic"));
String dynstr = el.getAttribute("dynamic"); incidentalCopy = SpecXmlUtils.decodeBoolean(el.getAttribute("incidentalcopy"));
if (dynstr != null)
isDynamic = SpecXmlUtils.decodeBoolean(dynstr);
XmlElement subel = parser.peek(); XmlElement subel = parser.peek();
while(subel.isStart()) { while (subel.isStart()) {
subel = parser.start(); subel = parser.start();
if (subel.getName().equals("body")) { if (subel.getName().equals("body")) {
parseString = parser.end(subel).getText(); parseString = parser.end(subel).getText();
@ -227,21 +297,25 @@ public class InjectPayloadSleigh implements InjectPayload {
String paramName = subel.getAttribute("name"); String paramName = subel.getAttribute("name");
int size = SpecXmlUtils.decodeInt(subel.getAttribute("size")); int size = SpecXmlUtils.decodeInt(subel.getAttribute("size"));
InjectParameter param = new InjectParameter(paramName, size); InjectParameter param = new InjectParameter(paramName, size);
if (subel.getName().equals("input")) if (subel.getName().equals("input")) {
inlist.add(param); inlist.add(param);
else }
else {
outlist.add(param); outlist.add(param);
}
parser.end(subel); parser.end(subel);
subel = parser.peek(); subel = parser.peek();
} }
parser.end(el); parser.end(el);
if (parseString != null) { if (parseString != null) {
parseString = parseString.trim(); parseString = parseString.trim();
if (parseString.length()==0) if (parseString.length() == 0) {
parseString = null; parseString = null;
} }
if (parseString == null && (!isDynamic)) }
throw new SleighException("Missing pcode <body> in injection: "+source); if (parseString == null && (!isDynamic)) {
throw new XmlParseException("Missing pcode <body> in injection: " + source);
}
setInputParameters(inlist); setInputParameters(inlist);
setOutputParameters(outlist); setOutputParameters(outlist);
@ -254,8 +328,7 @@ public class InjectPayloadSleigh implements InjectPayload {
return res; return res;
} }
//changed to public for PcodeInjectLibraryJava protected void setTemplate(ConstructTpl ctl) {
public void setTemplate(ConstructTpl ctl) {
pcodeTemplate = ctl; pcodeTemplate = ctl;
isfallthru = computeFallThru(); isfallthru = computeFallThru();
} }
@ -264,22 +337,29 @@ public class InjectPayloadSleigh implements InjectPayload {
* Verify that the storage locations passed -con- match the restrictions for this payload * Verify that the storage locations passed -con- match the restrictions for this payload
* @param con is InjectContext containing parameter storage locations * @param con is InjectContext containing parameter storage locations
*/ */
private void checkParameterRestrictions(InjectContext con,Address addr) { private void checkParameterRestrictions(InjectContext con, Address addr) {
int insize = (con.inputlist == null) ? 0 : con.inputlist.size(); int insize = (con.inputlist == null) ? 0 : con.inputlist.size();
if (inputlist.length != insize) if (inputlist.length != insize) {
throw new SleighException("Input parameters do not match injection specification: "+source); throw new SleighException(
for(int i=0;i<inputlist.length;++i) { "Input parameters do not match injection specification: " + source);
}
for (int i = 0; i < inputlist.length; ++i) {
int sz = inputlist[i].getSize(); int sz = inputlist[i].getSize();
if (sz != 0 && sz != con.inputlist.get(i).getSize()) if (sz != 0 && sz != con.inputlist.get(i).getSize()) {
throw new SleighException("Input parameter size does not match injection specification: "+source); throw new SleighException(
"Input parameter size does not match injection specification: " + source);
}
} }
int outsize = (con.output == null) ? 0 : con.output.size(); int outsize = (con.output == null) ? 0 : con.output.size();
if (output.length != outsize) if (output.length != outsize) {
throw new SleighException("Output does not match injection specification: "+source); throw new SleighException("Output does not match injection specification: " + source);
for(int i=0;i<output.length;++i) { }
for (int i = 0; i < output.length; ++i) {
int sz = output[i].getSize(); int sz = output[i].getSize();
if (sz != 0 && sz != con.output.get(i).getSize()) if (sz != 0 && sz != con.output.get(i).getSize()) {
throw new SleighException("Output size does not match injection specification: "+source); throw new SleighException(
"Output size does not match injection specification: " + source);
}
} }
} }
@ -287,11 +367,12 @@ public class InjectPayloadSleigh implements InjectPayload {
* Set-up operands in the parser state so that they pick up storage locations from InjectContext * Set-up operands in the parser state so that they pick up storage locations from InjectContext
* @param con is the InjectContext containing storage locations * @param con is the InjectContext containing storage locations
* @param walker is the sleigh parser state object * @param walker is the sleigh parser state object
* @throws UnknownInstructionException * @throws UnknownInstructionException if there are too many parameters for the parser
*/ */
private void setupParameters(InjectContext con,ParserWalker walker) throws UnknownInstructionException { private void setupParameters(InjectContext con, ParserWalker walker)
checkParameterRestrictions(con,walker.getAddr()); throws UnknownInstructionException {
for(int i=0;i<inputlist.length;++i) { checkParameterRestrictions(con, walker.getAddr());
for (int i = 0; i < inputlist.length; ++i) {
walker.allocateOperand(); walker.allocateOperand();
Varnode vn = con.inputlist.get(i); Varnode vn = con.inputlist.get(i);
FixedHandle hand = walker.getParentHandle(); FixedHandle hand = walker.getParentHandle();
@ -301,7 +382,7 @@ public class InjectPayloadSleigh implements InjectPayload {
hand.offset_space = null; hand.offset_space = null;
walker.popOperand(); walker.popOperand();
} }
for(int i=0;i<output.length;++i) { for (int i = 0; i < output.length; ++i) {
walker.allocateOperand(); walker.allocateOperand();
Varnode vn = con.output.get(i); Varnode vn = con.output.get(i);
FixedHandle hand = walker.getParentHandle(); FixedHandle hand = walker.getParentHandle();
@ -312,4 +393,75 @@ public class InjectPayloadSleigh implements InjectPayload {
walker.popOperand(); walker.popOperand();
} }
} }
@Override
public boolean equals(Object obj) {
InjectPayloadSleigh op2 = (InjectPayloadSleigh) obj;
if (!name.equals(op2.name)) {
return false;
}
if (!SystemUtilities.isArrayEqual(inputlist, op2.inputlist)) {
return false;
}
if (!SystemUtilities.isArrayEqual(output, op2.output)) {
return false;
}
if (incidentalCopy != op2.incidentalCopy) {
return false;
}
// Cannot compare isfallthru as it is a product of the p-code templates
// if (isfallthru != op2.isfallthru) {
// return false;
// }
if (paramShift != op2.paramShift) {
return false;
}
if (type != op2.type || subType != op2.subType) {
return false;
}
// We are NOT checking parseString and pcodeTemplate
return true;
}
@Override
public int hashCode() {
int hash = (incidentalCopy ? 1 : 13);
hash = 79 * hash + name.hashCode();
hash = 79 * hash + type;
hash = 79 * hash + subType;
hash = 79 * hash + paramShift;
for (InjectParameter param : inputlist) {
hash = 79 * hash + param.hashCode();
}
for (InjectParameter param : output) {
hash = 79 * hash + param.hashCode();
}
return hash;
}
/**
* Build a dummy p-code sequence to use in place of a normal parsed payload.
* A ConstructTpl is built out of Varnode and PcodeOp templates that can
* be assigned directly to the pcodeTemplate field of the payload.
* The sequence itself is non-empty, consisting of a single operation:
* tmp = tmp + 0;
* @param addrFactory is used to construct temp and constant Varnodes
* @return the final dummy template
*/
public static ConstructTpl getDummyPcode(AddressFactory addrFactory) {
ConstTpl uniqueSpace = new ConstTpl(addrFactory.getUniqueSpace());
ConstTpl constSpace = new ConstTpl(addrFactory.getConstantSpace());
ConstTpl tmpOffset = new ConstTpl(ConstTpl.REAL, 0x100);
ConstTpl constZero = new ConstTpl(ConstTpl.REAL, 0);
ConstTpl size = new ConstTpl(ConstTpl.REAL, 4);
VarnodeTpl temp = new VarnodeTpl(uniqueSpace, tmpOffset, size);
VarnodeTpl zero = new VarnodeTpl(constSpace, constZero, size);
VarnodeTpl[] inputs = new VarnodeTpl[2];
inputs[0] = temp;
inputs[1] = zero;
OpTpl[] ops = new OpTpl[1];
ops[0] = new OpTpl(PcodeOp.INT_ADD, temp, inputs);
ConstructTpl pcode = new ConstructTpl(ops);
return pcode;
}
} }

View file

@ -15,7 +15,6 @@
*/ */
package ghidra.program.model.lang; package ghidra.program.model.lang;
import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
import java.util.Map.Entry; import java.util.Map.Entry;
@ -23,7 +22,9 @@ import ghidra.app.plugin.processors.sleigh.VarnodeData;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace; import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.data.*; import ghidra.program.model.data.*;
import ghidra.program.model.pcode.AddressXML;
import ghidra.program.model.pcode.Varnode; import ghidra.program.model.pcode.Varnode;
import ghidra.util.SystemUtilities;
import ghidra.util.xml.SpecXmlUtils; import ghidra.util.xml.SpecXmlUtils;
import ghidra.xml.*; import ghidra.xml.*;
@ -85,23 +86,23 @@ public class ParamEntry {
} }
public boolean isExclusion() { public boolean isExclusion() {
return (alignment==0); return (alignment == 0);
} }
public boolean isReverseStack() { public boolean isReverseStack() {
return ((flags & REVERSE_STACK)!=0); return ((flags & REVERSE_STACK) != 0);
} }
public boolean isBigEndian() { public boolean isBigEndian() {
return ((flags & IS_BIG_ENDIAN)!=0); return ((flags & IS_BIG_ENDIAN) != 0);
} }
public boolean isFloatExtended() { public boolean isFloatExtended() {
return ((flags & SMALLSIZE_FLOAT)!=0); return ((flags & SMALLSIZE_FLOAT) != 0);
} }
private boolean isLeftJustified() { private boolean isLeftJustified() {
return (((flags & IS_BIG_ENDIAN)==0) || ((flags & FORCE_LEFT_JUSTIFY)!=0)); return (((flags & IS_BIG_ENDIAN) == 0) || ((flags & FORCE_LEFT_JUSTIFY) != 0));
} }
public AddressSpace getSpace() { public AddressSpace getSpace() {
@ -113,18 +114,18 @@ public class ParamEntry {
} }
public boolean contains(ParamEntry op2) { public boolean contains(ParamEntry op2) {
if ((type != TYPE_UNKNOWN)&&(op2.type != type)) { if ((type != TYPE_UNKNOWN) && (op2.type != type)) {
return false; return false;
} }
if (spaceid != op2.spaceid) { if (spaceid != op2.spaceid) {
return false; return false;
} }
if (unsignedCompare(op2.addressbase,addressbase)) { if (unsignedCompare(op2.addressbase, addressbase)) {
return false; return false;
} }
long op2end = op2.addressbase + op2.size -1; long op2end = op2.addressbase + op2.size - 1;
long end = addressbase+size-1; long end = addressbase + size - 1;
if (unsignedCompare(end,op2end)) { if (unsignedCompare(end, op2end)) {
return false; return false;
} }
if (alignment != op2.alignment) { if (alignment != op2.alignment) {
@ -133,14 +134,15 @@ public class ParamEntry {
return true; return true;
} }
public int justifiedContain(Address addr,int sz) { public int justifiedContain(Address addr, int sz) {
if (joinrec != null) { if (joinrec != null) {
int res = 0; int res = 0;
for(int i=joinrec.length-1;i>=0;--i) { // Move from least significant to most for (int i = joinrec.length - 1; i >= 0; --i) { // Move from least significant to most
Varnode vdata = joinrec[i]; Varnode vdata = joinrec[i];
int cur = justifiedContainAddress(vdata.getAddress().getAddressSpace(),vdata.getOffset(),vdata.getSize(), int cur = justifiedContainAddress(vdata.getAddress().getAddressSpace(),
addr.getAddressSpace(),addr.getOffset(),sz,false,((flags & IS_BIG_ENDIAN)!=0)); vdata.getOffset(), vdata.getSize(), addr.getAddressSpace(), addr.getOffset(),
if (cur<0) { sz, false, ((flags & IS_BIG_ENDIAN) != 0));
if (cur < 0) {
res += vdata.getSize(); // We skipped this many less significant bytes res += vdata.getSize(); // We skipped this many less significant bytes
} }
else { else {
@ -150,9 +152,9 @@ public class ParamEntry {
return -1; // Not contained at all return -1; // Not contained at all
} }
if (alignment == 0) { // Ordinary endian containment if (alignment == 0) { // Ordinary endian containment
return justifiedContainAddress(spaceid,addressbase,size, return justifiedContainAddress(spaceid, addressbase, size, addr.getAddressSpace(),
addr.getAddressSpace(),addr.getOffset(),sz, addr.getOffset(), sz, ((flags & FORCE_LEFT_JUSTIFY) != 0),
((flags & FORCE_LEFT_JUSTIFY)!=0),((flags & IS_BIG_ENDIAN)!=0)); ((flags & IS_BIG_ENDIAN) != 0));
} }
if (spaceid != addr.getAddressSpace()) { if (spaceid != addr.getAddressSpace()) {
return -1; return -1;
@ -165,17 +167,17 @@ public class ParamEntry {
if (unsignedCompare(endaddr, startaddr)) { if (unsignedCompare(endaddr, startaddr)) {
return -1; // Don't allow wrap around return -1; // Don't allow wrap around
} }
if (unsignedCompare(addressbase + size-1,endaddr)) { if (unsignedCompare(addressbase + size - 1, endaddr)) {
return -1; return -1;
} }
startaddr -= addressbase; startaddr -= addressbase;
endaddr -= addressbase; endaddr -= addressbase;
if (!isLeftJustified()) { // For right justified (big endian), endaddr must be aligned if (!isLeftJustified()) { // For right justified (big endian), endaddr must be aligned
int res = (int)((endaddr+1) % alignment); int res = (int) ((endaddr + 1) % alignment);
if (res==0) { if (res == 0) {
return 0; return 0;
} }
return (alignment-res); return (alignment - res);
} }
return (int) (startaddr % alignment); return (int) (startaddr % alignment);
} }
@ -187,20 +189,20 @@ public class ParamEntry {
* @param skip is the number of bytes to skip * @param skip is the number of bytes to skip
* @return the slot index * @return the slot index
*/ */
public int getSlot(Address addr,int skip) { public int getSlot(Address addr, int skip) {
int res = group; int res = group;
if (alignment != 0) { if (alignment != 0) {
long diff = addr.getOffset() + skip - addressbase; long diff = addr.getOffset() + skip - addressbase;
int baseslot = (int)diff / alignment; int baseslot = (int) diff / alignment;
if (isReverseStack()) { if (isReverseStack()) {
res += (numslots-1) - baseslot; res += (numslots - 1) - baseslot;
} }
else { else {
res += baseslot; res += baseslot;
} }
} }
else if (skip != 0) { else if (skip != 0) {
res += (groupsize -1); res += (groupsize - 1);
} }
return res; return res;
} }
@ -214,7 +216,7 @@ public class ParamEntry {
* @param res the final storage address * @param res the final storage address
* @return slotnum plus the number of slots used * @return slotnum plus the number of slots used
*/ */
public int getAddrBySlot(int slotnum,int sz,VarnodeData res) { public int getAddrBySlot(int slotnum, int sz, VarnodeData res) {
res.space = null; // Start with an invalid result res.space = null; // Start with an invalid result
int spaceused; int spaceused;
if (sz < minsize) { if (sz < minsize) {
@ -230,13 +232,13 @@ public class ParamEntry {
res.space = spaceid; res.space = spaceid;
res.offset = addressbase; // Get base address of the slot res.offset = addressbase; // Get base address of the slot
spaceused = size; spaceused = size;
if ((flags & SMALLSIZE_FLOAT)!=0) { if ((flags & SMALLSIZE_FLOAT) != 0) {
return slotnum; return slotnum;
} }
} }
else { else {
int slotsused = sz / alignment; // How many slots does a -sz- byte object need int slotsused = sz / alignment; // How many slots does a -sz- byte object need
if ( (sz %alignment) != 0) { if ((sz % alignment) != 0) {
slotsused += 1; slotsused += 1;
} }
if (slotnum + slotsused > numslots) { if (slotnum + slotsused > numslots) {
@ -262,85 +264,50 @@ public class ParamEntry {
return slotnum; return slotnum;
} }
/** public void saveXml(StringBuilder buffer) {
* Create a join record from an XML tag. Pieces of the join are encoded as a sequence of tag attributes buffer.append("<pentry");
* @param el SpecXmlUtils.encodeSignedIntegerAttribute(buffer, "minsize", minsize);
* @throws XmlParseException SpecXmlUtils.encodeSignedIntegerAttribute(buffer, "maxsize", size);
*/ if (alignment != 0) {
private void readJoinXML(XmlElement el,CompilerSpec cspec) throws XmlParseException { SpecXmlUtils.encodeSignedIntegerAttribute(buffer, "align", alignment);
ArrayList<Varnode> pieces = new ArrayList<Varnode>();
int sizesum = 0;
int pos = 0;
for(;;) {
String attrName = "piece" + Integer.toString(pos+1);
String attrVal = el.getAttribute(attrName);
if (attrVal == null) {
break;
} }
int offpos = attrVal.indexOf(':'); if (type == TYPE_FLOAT || type == TYPE_PTR) {
Varnode newvn; String tok = (type == TYPE_FLOAT) ? "float" : "ptr";
if (offpos == -1) { SpecXmlUtils.encodeStringAttribute(buffer, "metatype", tok);
Register register = cspec.getLanguage().getRegister(attrVal);
if (register == null) {
throw new XmlParseException("Unknown pentry register: " + attrVal);
} }
newvn = new Varnode(register.getAddress(),register.getBitLength()/8); if (groupsize != 1) {
SpecXmlUtils.encodeSignedIntegerAttribute(buffer, "groupsize", groupsize);
}
String extString = null;
if ((flags & SMALLSIZE_SEXT) != 0) {
extString = "sign";
}
else if ((flags & SMALLSIZE_ZEXT) != 0) {
extString = "zero";
}
else if ((flags & SMALLSIZE_INTTYPE) != 0) {
extString = "inttype";
}
else if ((flags & SMALLSIZE_FLOAT) != 0) {
extString = "float";
}
if (extString != null) {
SpecXmlUtils.encodeStringAttribute(buffer, "extension", extString);
}
buffer.append(">\n");
AddressXML addressSize;
if (joinrec == null) {
// Treat as unsized address with no size
addressSize = new AddressXML(spaceid, addressbase, 0);
} }
else { else {
int szpos = attrVal.indexOf(':', offpos+1); addressSize = new AddressXML(spaceid, addressbase, size, joinrec);
if (szpos == -1) {
throw new XmlParseException("join address piece attribute is malformed");
} }
String spcname = attrVal.substring(0, offpos); addressSize.saveXml(buffer);
AddressSpace spc = cspec.getAddressSpace(spcname); buffer.append("</pentry>");
long offset = SpecXmlUtils.decodeLong(attrVal.substring(offpos+1,szpos));
long sz = SpecXmlUtils.decodeLong(attrVal.substring(szpos+1));
newvn = new Varnode(spc.getAddress(offset),(int)sz);
}
pieces.add(newvn);
sizesum += newvn.getSize();
pos += 1;
}
addressbase = 0; // This should be the offset assigned by the join space
size = sizesum;
joinrec = new Varnode[ pieces.size() ];
pieces.toArray(joinrec);
} }
private void readXMLAddress(XmlPullParser parser, CompilerSpec cspec, int size) public void restoreXml(XmlPullParser parser, CompilerSpec cspec) throws XmlParseException {
throws XmlParseException {
XmlElement subel = parser.start();
if (subel.getName().equals("register")) {
String regName = subel.getAttribute("name");
if (regName == null) {
throw new XmlParseException("Missing pentry register name");
}
Register register = cspec.getLanguage().getRegister(regName);
if (register == null) {
throw new XmlParseException("Unknown pentry register: " + regName);
}
int regSize = register.getMinimumByteSize();
if (size > regSize) {
throw new XmlParseException(
"Invalid pentry size specified for " + regSize + "-byte register: " + regName);
}
spaceid = register.getAddressSpace();
addressbase = register.getOffset();
}
else {
spaceid = cspec.getAddressSpace(subel.getAttribute("space"));
if (spaceid.getType() == AddressSpace.TYPE_JOIN) {
readJoinXML(subel,cspec);
}
else {
addressbase = SpecXmlUtils.decodeLong(subel.getAttribute("offset"));
}
}
parser.end(subel);
}
public void restoreXml(XmlPullParser parser,CompilerSpec cspec,boolean normalstack) throws XmlParseException {
flags = 0; flags = 0;
type = TYPE_UNKNOWN; type = TYPE_UNKNOWN;
size = minsize = -1; // Must be filled in size = minsize = -1; // Must be filled in
@ -350,7 +317,7 @@ public class ParamEntry {
XmlElement el = parser.start("pentry"); XmlElement el = parser.start("pentry");
Iterator<Entry<String, String>> iter = el.getAttributes().entrySet().iterator(); Iterator<Entry<String, String>> iter = el.getAttributes().entrySet().iterator();
while(iter.hasNext()) { while (iter.hasNext()) {
Entry<String, String> entry = iter.next(); Entry<String, String> entry = iter.next();
String name = entry.getKey(); String name = entry.getKey();
if (name.equals("minsize")) { if (name.equals("minsize")) {
@ -384,7 +351,7 @@ public class ParamEntry {
groupsize = SpecXmlUtils.decodeInt(entry.getValue()); groupsize = SpecXmlUtils.decodeInt(entry.getValue());
} }
else if (name.equals("extension")) { else if (name.equals("extension")) {
flags &= ~(SMALLSIZE_ZEXT | SMALLSIZE_SEXT | SMALLSIZE_INTTYPE); flags &= ~(SMALLSIZE_ZEXT | SMALLSIZE_SEXT | SMALLSIZE_INTTYPE | SMALLSIZE_FLOAT);
String value = entry.getValue(); String value = entry.getValue();
if (value.equals("sign")) { if (value.equals("sign")) {
flags |= SMALLSIZE_SEXT; flags |= SMALLSIZE_SEXT;
@ -399,11 +366,11 @@ public class ParamEntry {
flags |= SMALLSIZE_FLOAT; flags |= SMALLSIZE_FLOAT;
} }
else if (!value.equals("none")) { else if (!value.equals("none")) {
throw new XmlParseException("Bad extension attribute: "+value); throw new XmlParseException("Bad extension attribute: " + value);
} }
} }
else { else {
throw new XmlParseException("Unknown paramentry attribute: "+name); throw new XmlParseException("Unknown paramentry attribute: " + name);
} }
} }
if (minsize < 1 || size < minsize) { if (minsize < 1 || size < minsize) {
@ -414,7 +381,16 @@ public class ParamEntry {
alignment = 0; alignment = 0;
} }
readXMLAddress(parser, cspec, size); XmlElement subel = parser.start();
AddressXML addressSized = AddressXML.restoreXml(subel, cspec);
parser.end(subel);
if (addressSized.getSize() != 0 && size > addressSized.getSize()) {
throw new XmlParseException("<pentry> maxsize is bigger than memory range");
}
addressSized.getFirstAddress(); // Fail fast. Throws AddressOutOfBounds exception if offset is invalid
spaceid = addressSized.getAddressSpace();
addressbase = addressSized.getOffset();
joinrec = addressSized.getJoinRecord();
boolean isbigendian = cspec.getLanguage().isBigEndian(); boolean isbigendian = cspec.getLanguage().isBigEndian();
if (isbigendian) { if (isbigendian) {
@ -428,11 +404,12 @@ public class ParamEntry {
if (spaceid.isStackSpace() && (!cspec.isStackRightJustified()) && isbigendian) { if (spaceid.isStackSpace() && (!cspec.isStackRightJustified()) && isbigendian) {
flags |= FORCE_LEFT_JUSTIFY; flags |= FORCE_LEFT_JUSTIFY;
} }
if (!normalstack) { if (!cspec.stackGrowsNegative()) {
flags |= REVERSE_STACK; flags |= REVERSE_STACK;
if (alignment != 0) { if (alignment != 0) {
if ((size % alignment) != 0) { if ((size % alignment) != 0) {
throw new XmlParseException("For positive stack growth, <pentry> size must match alignment"); throw new XmlParseException(
"For positive stack growth, <pentry> size must match alignment");
} }
} }
} }
@ -440,14 +417,58 @@ public class ParamEntry {
parser.end(el); parser.end(el);
} }
@Override
public boolean equals(Object obj) {
ParamEntry op2 = (ParamEntry) obj;
if (!spaceid.equals(op2.spaceid) || addressbase != op2.addressbase) {
return false;
}
if (size != op2.size || minsize != op2.minsize || alignment != op2.alignment) {
return false;
}
if (type != op2.type || flags != op2.flags) {
return false;
}
if (numslots != op2.numslots) {
return false;
}
if (group != op2.group || groupsize != op2.groupsize) {
return false;
}
if (!SystemUtilities.isArrayEqual(joinrec, op2.joinrec)) {
return false;
}
return true;
}
@Override
public int hashCode() {
int hash = spaceid.hashCode();
hash = 79 * hash + Long.hashCode(addressbase);
hash = 79 * hash + alignment;
hash = 79 * hash + flags;
hash = 79 * hash + group;
hash = 79 * hash + groupsize;
hash = 79 * hash + minsize;
hash = 79 * hash + numslots;
hash = 79 * hash + size;
hash = 79 * hash + type;
if (joinrec != null) {
for (Varnode vn : joinrec) {
hash = 79 * hash + vn.hashCode();
}
}
return hash;
}
/** /**
* Unsigned less-than operation * Unsigned less-than operation
* @param a * @param a is the first operand
* @param b * @param b is the second operand
* @return return true is a is less than b, where a and b are interpreted as unsigned integers * @return return true is a is less than b, where a and b are interpreted as unsigned integers
*/ */
public static boolean unsignedCompare(long a,long b) { public static boolean unsignedCompare(long a, long b) {
return (a+0x8000000000000000L < b + 0x8000000000000000L); return (a + 0x8000000000000000L < b + 0x8000000000000000L);
} }
/** /**
@ -459,33 +480,35 @@ public class ParamEntry {
* @param offset1 the first offset * @param offset1 the first offset
* @param sz1 size of first space * @param sz1 size of first space
* @param spc2 the second address space * @param spc2 the second address space
* @param offset2 is the second offset
* @param sz2 size of second space * @param sz2 size of second space
* @param forceleft is true if containment is forced to be on the left even for big endian * @param forceleft is true if containment is forced to be on the left even for big endian
* @param isBigEndian true if big endian * @param isBigEndian true if big endian
* @return the endian aware offset or -1 * @return the endian aware offset or -1
*/ */
public static int justifiedContainAddress(AddressSpace spc1,long offset1,int sz1,AddressSpace spc2,long offset2,int sz2,boolean forceleft,boolean isBigEndian) { public static int justifiedContainAddress(AddressSpace spc1, long offset1, int sz1,
AddressSpace spc2, long offset2, int sz2, boolean forceleft, boolean isBigEndian) {
if (spc1 != spc2) { if (spc1 != spc2) {
return -1; return -1;
} }
if (unsignedCompare(offset2,offset1)) { if (unsignedCompare(offset2, offset1)) {
return -1; return -1;
} }
long off1 = offset1 + (sz1 - 1); long off1 = offset1 + (sz1 - 1);
long off2 = offset2 + (sz2 - 1); long off2 = offset2 + (sz2 - 1);
if (unsignedCompare(off1,off2)) { if (unsignedCompare(off1, off2)) {
return -1; return -1;
} }
if (isBigEndian && (!forceleft)) { if (isBigEndian && (!forceleft)) {
return (int)(off1 - off2); return (int) (off1 - off2);
} }
return (int)(offset2 - offset1); return (int) (offset2 - offset1);
} }
public static int getMetatype(DataType tp) { public static int getMetatype(DataType tp) {
// TODO: A complete metatype implementation // TODO: A complete metatype implementation
if (tp instanceof TypeDef) { if (tp instanceof TypeDef) {
tp = ((TypeDef)tp).getBaseDataType(); tp = ((TypeDef) tp).getBaseDataType();
} }
if (tp instanceof AbstractFloatDataType) { if (tp instanceof AbstractFloatDataType) {
return TYPE_FLOAT; return TYPE_FLOAT;

View file

@ -42,13 +42,17 @@ public interface ParamList {
* @param res is the vector for holding the VariableStorage corresponding to datatypes * @param res is the vector for holding the VariableStorage corresponding to datatypes
* @param addAutoParams if true add/process auto-parameters * @param addAutoParams if true add/process auto-parameters
*/ */
public void assignMap(Program prog,DataType[] proto,boolean isinput,ArrayList<VariableStorage> res, boolean addAutoParams); public void assignMap(Program prog, DataType[] proto, boolean isinput,
ArrayList<VariableStorage> res, boolean addAutoParams);
public void restoreXml(XmlPullParser parser,CompilerSpec cspec,boolean normalstack) throws XmlParseException; public void saveXml(StringBuilder buffer, boolean isInput);
public void restoreXml(XmlPullParser parser, CompilerSpec cspec) throws XmlParseException;
/** /**
* Get a list of all parameter storage locations consisting of a single register * Get a list of all parameter storage locations consisting of a single register
* @return * @param prog is the controlling program
* @return an array of VariableStorage
*/ */
public VariableStorage[] getPotentialRegisterStorage(Program prog); public VariableStorage[] getPotentialRegisterStorage(Program prog);
@ -62,7 +66,7 @@ public interface ParamList {
* Find the boundary offset that separates parameters on the stack from other local variables * Find the boundary offset that separates parameters on the stack from other local variables
* This is usually the address of the first stack parameter, but if the stack grows positive, this is * This is usually the address of the first stack parameter, but if the stack grows positive, this is
* the first address AFTER the parameters on the stack * the first address AFTER the parameters on the stack
* @return * @return the boundary offset
*/ */
public Long getStackParameterOffset(); public Long getStackParameterOffset();
@ -73,7 +77,7 @@ public interface ParamList {
* @param res holds the resulting slot and slotsize * @param res holds the resulting slot and slotsize
* @return true if the range is a possible parameter * @return true if the range is a possible parameter
*/ */
public boolean possibleParamWithSlot(Address loc,int size,WithSlotRec res); public boolean possibleParamWithSlot(Address loc, int size, WithSlotRec res);
public boolean isThisBeforeRetPointer(); public boolean isThisBeforeRetPointer();
} }

View file

@ -23,6 +23,7 @@ import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.data.*; import ghidra.program.model.data.*;
import ghidra.program.model.listing.*; import ghidra.program.model.listing.*;
import ghidra.program.model.pcode.Varnode; import ghidra.program.model.pcode.Varnode;
import ghidra.util.SystemUtilities;
import ghidra.util.exception.InvalidInputException; import ghidra.util.exception.InvalidInputException;
import ghidra.util.xml.SpecXmlUtils; import ghidra.util.xml.SpecXmlUtils;
import ghidra.xml.*; import ghidra.xml.*;
@ -48,52 +49,66 @@ public class ParamListStandard implements ParamList {
*/ */
private int findEntry(Address loc, int size) { private int findEntry(Address loc, int size) {
for (int i = 0; i < entry.length; ++i) { for (int i = 0; i < entry.length; ++i) {
if (entry[i].getMinSize() > size) if (entry[i].getMinSize() > size) {
continue; continue;
if (entry[i].justifiedContain(loc, size) == 0) // Make sure the range is properly justified in entry }
if (entry[i].justifiedContain(loc, size) == 0) {
return i; return i;
} }
}
return -1; return -1;
} }
/** /**
* Assign next available memory chunk to type * Assign next available memory chunk to type
* @param program is the Program
* @param tp type being assigned storage * @param tp type being assigned storage
* @param status status from previous assignments * @param status status from previous assignments
* @param ishiddenret is true if the parameter is a hidden return value
* @param isindirect is true if parameter is really a pointer to the real parameter value
* @return Address of assigned memory chunk * @return Address of assigned memory chunk
*/ */
protected VariableStorage assignAddress(Program program, DataType tp, int[] status, protected VariableStorage assignAddress(Program program, DataType tp, int[] status,
boolean ishiddenret, boolean isindirect) { boolean ishiddenret, boolean isindirect) {
if (tp == null) if (tp == null) {
tp = DataType.DEFAULT; tp = DataType.DEFAULT;
}
DataType baseType = tp; DataType baseType = tp;
if (baseType instanceof TypeDef) if (baseType instanceof TypeDef) {
baseType = ((TypeDef) baseType).getBaseDataType(); baseType = ((TypeDef) baseType).getBaseDataType();
if (baseType instanceof VoidDataType) }
if (baseType instanceof VoidDataType) {
return VariableStorage.VOID_STORAGE; return VariableStorage.VOID_STORAGE;
}
int sz = tp.getLength(); int sz = tp.getLength();
if (sz == 0) if (sz == 0) {
return VariableStorage.UNASSIGNED_STORAGE; return VariableStorage.UNASSIGNED_STORAGE;
}
for (ParamEntry element : entry) { for (ParamEntry element : entry) {
int grp = element.getGroup(); int grp = element.getGroup();
if (status[grp] < 0) if (status[grp] < 0) {
continue; continue;
}
if ((element.getType() != ParamEntry.TYPE_UNKNOWN) && if ((element.getType() != ParamEntry.TYPE_UNKNOWN) &&
(ParamEntry.getMetatype(tp) != element.getType())) (ParamEntry.getMetatype(tp) != element.getType())) {
continue; // Wrong type continue; // Wrong type
}
VarnodeData res = new VarnodeData(); VarnodeData res = new VarnodeData();
status[grp] = element.getAddrBySlot(status[grp], tp.getLength(), res); status[grp] = element.getAddrBySlot(status[grp], tp.getLength(), res);
if (res.space == null) if (res.space == null) {
continue; // -tp- does not fit in this entry continue; // -tp- does not fit in this entry
}
if (element.isExclusion()) { if (element.isExclusion()) {
int maxgrp = grp + element.getGroupSize(); int maxgrp = grp + element.getGroupSize();
for (int j = grp; j < maxgrp; ++j) for (int j = grp; j < maxgrp; ++j) {
// For an exclusion entry // For an exclusion entry
status[j] = -1; // some number of groups are taken up status[j] = -1; // some number of groups are taken up
if (element.isFloatExtended()) // If this is a small float datatype in a bigger container }
if (element.isFloatExtended()) {
sz = element.getSize(); // Still use the entire container size, when assigning storage sz = element.getSize(); // Still use the entire container size, when assigning storage
} }
}
VariableStorage store; VariableStorage store;
try { try {
if (res.space.getType() == AddressSpace.TYPE_JOIN) { if (res.space.getType() == AddressSpace.TYPE_JOIN) {
@ -120,8 +135,8 @@ public class ParamListStandard implements ParamList {
return store; return store;
} }
if (ishiddenret) { if (ishiddenret) {
return DynamicVariableStorage.getUnassignedDynamicStorage( return DynamicVariableStorage
AutoParameterType.RETURN_STORAGE_PTR); .getUnassignedDynamicStorage(AutoParameterType.RETURN_STORAGE_PTR);
} }
return DynamicVariableStorage.getUnassignedDynamicStorage(isindirect); return DynamicVariableStorage.getUnassignedDynamicStorage(isindirect);
} }
@ -130,8 +145,9 @@ public class ParamListStandard implements ParamList {
public void assignMap(Program prog, DataType[] proto, boolean isinput, public void assignMap(Program prog, DataType[] proto, boolean isinput,
ArrayList<VariableStorage> res, boolean addAutoParams) { ArrayList<VariableStorage> res, boolean addAutoParams) {
int[] status = new int[numgroup]; int[] status = new int[numgroup];
for (int i = 0; i < numgroup; ++i) for (int i = 0; i < numgroup; ++i) {
status[i] = 0; status[i] = 0;
}
if (isinput) { if (isinput) {
if (addAutoParams && res.size() == 2) { // Check for hidden parameters defined by the output list if (addAutoParams && res.size() == 2) { // Check for hidden parameters defined by the output list
@ -149,8 +165,9 @@ public class ParamListStandard implements ParamList {
Pointer pointer = dtm.getPointer(proto[i]); Pointer pointer = dtm.getPointer(proto[i]);
store = assignAddress(prog, pointer, status, false, true); store = assignAddress(prog, pointer, status, false, true);
} }
else else {
store = assignAddress(prog, proto[i], status, false, false); store = assignAddress(prog, proto[i], status, false, false);
}
res.add(store); res.add(store);
} }
} }
@ -165,8 +182,9 @@ public class ParamListStandard implements ParamList {
ArrayList<VariableStorage> res = new ArrayList<>(); ArrayList<VariableStorage> res = new ArrayList<>();
for (ParamEntry element : entry) { for (ParamEntry element : entry) {
ParamEntry pe = element; ParamEntry pe = element;
if (!pe.isExclusion()) if (!pe.isExclusion()) {
continue; continue;
}
if (pe.getSpace().isRegisterSpace()) { if (pe.getSpace().isRegisterSpace()) {
VariableStorage var = null; VariableStorage var = null;
try { try {
@ -176,18 +194,35 @@ public class ParamListStandard implements ParamList {
catch (InvalidInputException e) { catch (InvalidInputException e) {
// Skip this particular storage location // Skip this particular storage location
} }
if (var != null) if (var != null) {
res.add(var); res.add(var);
} }
} }
}
VariableStorage[] arres = new VariableStorage[res.size()]; VariableStorage[] arres = new VariableStorage[res.size()];
res.toArray(arres); res.toArray(arres);
return arres; return arres;
} }
@Override @Override
public void restoreXml(XmlPullParser parser, CompilerSpec cspec, boolean normalstack) public void saveXml(StringBuilder buffer, boolean isInput) {
throws XmlParseException { buffer.append(isInput ? "<input" : "<output");
if (pointermax != 0) {
SpecXmlUtils.encodeSignedIntegerAttribute(buffer, "pointermax", pointermax);
}
if (thisbeforeret) {
SpecXmlUtils.encodeStringAttribute(buffer, "thisbeforeretpointer", "yes");
}
buffer.append(">\n");
for (ParamEntry el : entry) {
el.saveXml(buffer);
buffer.append('\n');
}
buffer.append(isInput ? "</input>" : "</output>");
}
@Override
public void restoreXml(XmlPullParser parser, CompilerSpec cspec) throws XmlParseException {
ArrayList<ParamEntry> pe = new ArrayList<>(); ArrayList<ParamEntry> pe = new ArrayList<>();
int lastgroup = -1; int lastgroup = -1;
numgroup = 0; numgroup = 0;
@ -200,30 +235,37 @@ public class ParamListStandard implements ParamList {
pointermax = SpecXmlUtils.decodeInt(attribute); pointermax = SpecXmlUtils.decodeInt(attribute);
} }
attribute = mainel.getAttribute("thisbeforeretpointer"); attribute = mainel.getAttribute("thisbeforeretpointer");
if (attribute != null) if (attribute != null) {
thisbeforeret = SpecXmlUtils.decodeBoolean(attribute); thisbeforeret = SpecXmlUtils.decodeBoolean(attribute);
}
boolean seennonfloat = false; // Have we seen any integer slots yet boolean seennonfloat = false; // Have we seen any integer slots yet
for (;;) { for (;;) {
XmlElement el = parser.peek(); XmlElement el = parser.peek();
if (!el.isStart()) if (!el.isStart()) {
break; break;
}
ParamEntry pentry = new ParamEntry(numgroup); ParamEntry pentry = new ParamEntry(numgroup);
pentry.restoreXml(parser, cspec, normalstack); pentry.restoreXml(parser, cspec);
pe.add(pentry); pe.add(pentry);
if (pentry.getType() == ParamEntry.TYPE_FLOAT) { if (pentry.getType() == ParamEntry.TYPE_FLOAT) {
if (seennonfloat) if (seennonfloat) {
throw new XmlParseException( throw new XmlParseException(
"parameter list floating-point entries must come first"); "parameter list floating-point entries must come first");
} }
else }
else {
seennonfloat = true; seennonfloat = true;
if (pentry.getSpace().isStackSpace()) }
if (pentry.getSpace().isStackSpace()) {
spacebase = pentry.getSpace(); spacebase = pentry.getSpace();
}
int maxgroup = pentry.getGroup() + pentry.getGroupSize(); int maxgroup = pentry.getGroup() + pentry.getGroupSize();
if (maxgroup > numgroup) if (maxgroup > numgroup) {
numgroup = maxgroup; numgroup = maxgroup;
if (pentry.getGroup() < lastgroup) }
if (pentry.getGroup() < lastgroup) {
throw new XmlParseException("pentrys must come in group order"); throw new XmlParseException("pentrys must come in group order");
}
lastgroup = pentry.getGroup(); lastgroup = pentry.getGroup();
} }
parser.end(mainel); parser.end(mainel);
@ -245,13 +287,16 @@ public class ParamListStandard implements ParamList {
public Long getStackParameterOffset() { public Long getStackParameterOffset() {
for (ParamEntry element : entry) { for (ParamEntry element : entry) {
ParamEntry pentry = element; ParamEntry pentry = element;
if (pentry.isExclusion()) if (pentry.isExclusion()) {
continue; continue;
if (!pentry.getSpace().isStackSpace()) }
if (!pentry.getSpace().isStackSpace()) {
continue; continue;
}
long res = pentry.getAddressBase(); long res = pentry.getAddressBase();
if (pentry.isReverseStack()) if (pentry.isReverseStack()) {
res += pentry.getSize(); res += pentry.getSize();
}
res = pentry.getSpace().truncateOffset(res); res = pentry.getSpace().truncateOffset(res);
return res; return res;
} }
@ -260,20 +305,56 @@ public class ParamListStandard implements ParamList {
@Override @Override
public boolean possibleParamWithSlot(Address loc, int size, WithSlotRec res) { public boolean possibleParamWithSlot(Address loc, int size, WithSlotRec res) {
if (loc == null) if (loc == null) {
return false; return false;
}
int num = findEntry(loc, size); int num = findEntry(loc, size);
if (num == -1) if (num == -1) {
return false; return false;
}
ParamEntry curentry = entry[num]; ParamEntry curentry = entry[num];
res.slot = curentry.getSlot(loc, 0); res.slot = curentry.getSlot(loc, 0);
if (curentry.isExclusion()) if (curentry.isExclusion()) {
res.slotsize = curentry.getGroupSize(); res.slotsize = curentry.getGroupSize();
else }
else {
res.slotsize = ((size - 1) / curentry.getAlign()) + 1; res.slotsize = ((size - 1) / curentry.getAlign()) + 1;
}
return true; return true;
} }
@Override
public boolean equals(Object obj) {
ParamListStandard op2 = (ParamListStandard) obj;
if (!SystemUtilities.isArrayEqual(entry, op2.entry)) {
return false;
}
if (numgroup != op2.numgroup || pointermax != op2.pointermax) {
return false;
}
if (!SystemUtilities.isEqual(spacebase, op2.spacebase)) {
return false;
}
if (thisbeforeret != op2.thisbeforeret) {
return false;
}
return true;
}
@Override
public int hashCode() {
int hash = numgroup;
hash = 79 * hash + pointermax;
hash = 79 * hash + (thisbeforeret ? 27 : 19);
for (ParamEntry param : entry) {
hash = 79 * hash + param.hashCode();
}
if (spacebase == null) {
hash = 79 * hash + spacebase.hashCode();
}
return hash;
}
@Override @Override
public boolean isThisBeforeRetPointer() { public boolean isThisBeforeRetPointer() {
return thisbeforeret; return thisbeforeret;

View file

@ -17,6 +17,7 @@ package ghidra.program.model.lang;
import java.io.IOException; import java.io.IOException;
import java.util.*; import java.util.*;
import java.util.Map.Entry;
import org.jdom.JDOMException; import org.jdom.JDOMException;
import org.xml.sax.*; import org.xml.sax.*;
@ -30,30 +31,107 @@ import ghidra.program.model.lang.InjectPayload.InjectParameter;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.sleigh.grammar.Location; import ghidra.sleigh.grammar.Location;
import ghidra.util.Msg; import ghidra.util.Msg;
import ghidra.xml.XmlPullParser; import ghidra.util.SystemUtilities;
import ghidra.xml.XmlPullParserFactory; import ghidra.xml.*;
public class PcodeInjectLibrary { public class PcodeInjectLibrary {
private SleighLanguage language; protected SleighLanguage language;
private long uniqueBase; // Current base address for new temporary registers protected long uniqueBase; // Current base address for new temporary registers
private Map<String, InjectPayload> callFixupMap; // Map of names to registered callfixups private Map<String, InjectPayload> callFixupMap; // Map of names to registered callfixups
private Map<String, InjectPayload> callOtherFixupMap; // Map of registered callotherfixups names to injection id private Map<String, InjectPayload> callOtherFixupMap; // Map of registered callotherfixups names to injection id
private InjectPayload[] callOtherOverride; // List of overridden callotherfixups
private Map<String, InjectPayload> callMechFixupMap; // Map of registered injectUponEntry/Return ids private Map<String, InjectPayload> callMechFixupMap; // Map of registered injectUponEntry/Return ids
private Map<String, InjectPayload> exePcodeMap; // Map of registered p-code scripts private Map<String, InjectPayload> exePcodeMap; // Map of registered p-code scripts
private InjectPayloadSleigh[] programPayload; // List of Program specific payloads
public PcodeInjectLibrary(SleighLanguage l) { public PcodeInjectLibrary(SleighLanguage l) {
language = l; language = l;
uniqueBase = language.getUniqueBase(); uniqueBase = language.getUniqueBase();
callFixupMap = new TreeMap<String, InjectPayload>(); callFixupMap = new TreeMap<>();
callOtherFixupMap = new TreeMap<String, InjectPayload>(); callOtherFixupMap = new TreeMap<>();
callMechFixupMap = new TreeMap<String, InjectPayload>(); callOtherOverride = null;
exePcodeMap = new TreeMap<String, InjectPayload>(); callMechFixupMap = new TreeMap<>();
exePcodeMap = new TreeMap<>();
programPayload = null;
} }
public InjectPayload getPayload(int type, String name, Program program, String context) { /**
if (name == null) * Clone a library so that a Program can extend the library without
* modifying the base library from Language. InjectPayloads can be considered
* immutable and don't need to be cloned.
* @param op2 is the library to clone
*/
public PcodeInjectLibrary(PcodeInjectLibrary op2) {
language = op2.language;
uniqueBase = op2.uniqueBase;
callFixupMap = new TreeMap<>(op2.callFixupMap);
callOtherFixupMap = new TreeMap<>(op2.callOtherFixupMap);
callOtherOverride = op2.callOtherOverride;
callMechFixupMap = new TreeMap<>(op2.callMechFixupMap);
exePcodeMap = new TreeMap<>(op2.exePcodeMap);
programPayload = op2.programPayload;
}
/**
* @return A clone of this library
*/
@Override
public PcodeInjectLibrary clone() {
return new PcodeInjectLibrary(this);
}
/**
* @return an array of all the program specific payloads (or null)
*/
public InjectPayloadSleigh[] getProgramPayloads() {
return programPayload;
}
/**
* Determine if the given payload name and type exists and is an extension
* of the program.
* @param nm is the payload name
* @param type is the payload type
* @return true if the program extension exists
*/
public boolean hasProgramPayload(String nm, int type) {
if (programPayload == null) {
return false;
}
for (InjectPayload payload : programPayload) {
if (payload.getType() != type) {
continue;
}
if (payload.getName().equals(nm)) {
return true;
}
}
return false;
}
/**
* Check if a specific payload has been overridden by a user extension
* @param nm is the name of the payload
* @param type is the type of payload
* @return true if the payload is overridden
*/
public boolean isOverride(String nm, int type) {
if (callOtherOverride == null || type != InjectPayload.CALLOTHERFIXUP_TYPE) {
return false;
}
for (InjectPayload payload : callOtherOverride) {
if (payload.getName().equals(nm)) {
return true;
}
}
return false;
}
public InjectPayload getPayload(int type, String name) {
if (name == null) {
return null; return null;
}
if (type == InjectPayload.CALLFIXUP_TYPE) { if (type == InjectPayload.CALLFIXUP_TYPE) {
return callFixupMap.get(name); return callFixupMap.get(name);
} }
@ -69,30 +147,41 @@ public class PcodeInjectLibrary {
return null; return null;
} }
private void parseInject(InjectPayload payload) throws SleighException { /**
* Convert the XML string representation of the given payload to a ConstructTpl
* The payload should be unattached (not already installed in the library)
* @param payload is the given payload whose XML should be converted
* @throws SleighException if there is any parsing issue
*/
public void parseInject(InjectPayload payload) throws SleighException {
String sourceName = payload.getSource(); String sourceName = payload.getSource();
if (sourceName == null) if (sourceName == null) {
sourceName = "unknown"; sourceName = "unknown";
if (!(payload instanceof InjectPayloadSleigh)) }
if (!(payload instanceof InjectPayloadSleigh)) {
return; return;
}
InjectPayloadSleigh payloadSleigh = (InjectPayloadSleigh) payload; InjectPayloadSleigh payloadSleigh = (InjectPayloadSleigh) payload;
String translateSpec = String pcodeText = payloadSleigh.releaseParseString();
language.buildTranslatorTag(language.getAddressFactory(), uniqueBase, if (pcodeText == null) {
return; // Dynamic p-code generation, or already parsed
}
String translateSpec = language.buildTranslatorTag(language.getAddressFactory(), uniqueBase,
language.getSymbolTable()); language.getSymbolTable());
String pcodeText = payloadSleigh.releaseParseString();
if (pcodeText == null)
return; // Dynamic p-code generation
try { try {
PcodeParser parser = new PcodeParser(translateSpec); PcodeParser parser = new PcodeParser(translateSpec);
Location loc = new Location(sourceName, 1); Location loc = new Location(sourceName, 1);
InjectParameter[] input = payload.getInput(); InjectParameter[] input = payload.getInput();
for (InjectParameter element : input) for (InjectParameter element : input) {
parser.addOperand(loc, element.getName(), element.getIndex()); parser.addOperand(loc, element.getName(), element.getIndex());
}
InjectParameter[] output = payload.getOutput(); InjectParameter[] output = payload.getOutput();
for (InjectParameter element : output) for (InjectParameter element : output) {
parser.addOperand(loc, element.getName(), element.getIndex()); parser.addOperand(loc, element.getName(), element.getIndex());
}
String constructTplXml = String constructTplXml =
PcodeParser.stringifyTemplate(parser.compilePcode(pcodeText, sourceName, 1)); PcodeParser.stringifyTemplate(parser.compilePcode(pcodeText, sourceName, 1));
if (constructTplXml == null) { if (constructTplXml == null) {
@ -132,8 +221,8 @@ public class PcodeInjectLibrary {
throw new SleighException("compiled pcode contains invalid opcode " + sourceName, e); throw new SleighException("compiled pcode contains invalid opcode " + sourceName, e);
} }
catch (JDOMException e) { catch (JDOMException e) {
throw new SleighException("pcode compile failed due to invalid translator tag " + throw new SleighException(
sourceName, e); "pcode compile failed due to invalid translator tag " + sourceName, e);
} }
catch (SAXException e) { catch (SAXException e) {
throw new SleighException("pcode compiler returned invalid xml " + sourceName, e); throw new SleighException("pcode compiler returned invalid xml " + sourceName, e);
@ -165,6 +254,9 @@ public class PcodeInjectLibrary {
} }
} }
/**
* @return a list of names for all installed call-fixups
*/
public String[] getCallFixupNames() { public String[] getCallFixupNames() {
Set<String> keySet = callFixupMap.keySet(); Set<String> keySet = callFixupMap.keySet();
String[] names = new String[keySet.size()]; String[] names = new String[keySet.size()];
@ -172,13 +264,46 @@ public class PcodeInjectLibrary {
return names; return names;
} }
/**
* @return a list of names for all installed callother-fixups
*/
public String[] getCallotherFixupNames() {
ArrayList<String> list = new ArrayList<>();
for (Entry<String, InjectPayload> entry : callOtherFixupMap.entrySet()) {
if (entry.getValue() != null) {
list.add(entry.getKey());
}
}
String[] res = new String[list.size()];
list.toArray(res);
return res;
}
public InjectContext buildInjectContext() { public InjectContext buildInjectContext() {
InjectContext res = new InjectContext(); InjectContext res = new InjectContext();
res.language = language; res.language = language;
return res; return res;
} }
/**
* Determine if the language has a given user-defined op.
* In which case, a CALLOTHER_FIXUP can be installed for it.
* @param name is the putative name of the user-defined op
* @return true if the user-defined op exists
*/
public boolean hasUserDefinedOp(String name) {
if (callOtherFixupMap.size() == 0) {
int max = language.getNumberOfUserDefinedOpNames();
for (int i = 0; i < max; ++i) {
String opname = language.getUserDefinedOpName(i);
callOtherFixupMap.put(opname, null); // Initialize with null pcodeinjection
}
}
return callOtherFixupMap.containsKey(name);
}
protected void registerInject(InjectPayload payload) { protected void registerInject(InjectPayload payload) {
parseInject(payload);
switch (payload.getType()) { switch (payload.getType()) {
case InjectPayload.CALLFIXUP_TYPE: case InjectPayload.CALLFIXUP_TYPE:
if (callFixupMap.containsKey(payload.getName())) { if (callFixupMap.containsKey(payload.getName())) {
@ -188,19 +313,14 @@ public class PcodeInjectLibrary {
callFixupMap.put(payload.getName(), payload); callFixupMap.put(payload.getName(), payload);
break; break;
case InjectPayload.CALLOTHERFIXUP_TYPE: case InjectPayload.CALLOTHERFIXUP_TYPE:
if (callOtherFixupMap.size() == 0) { if (!hasUserDefinedOp(payload.getName())) {
int max = language.getNumberOfUserDefinedOpNames();
for (int i = 0; i < max; ++i) {
String opname = language.getUserDefinedOpName(i);
callOtherFixupMap.put(opname, null); // Initialize with null pcodeinjection
}
}
if (!callOtherFixupMap.containsKey(payload.getName()))
throw new SleighException( throw new SleighException(
"Unknown callother name in <callotherfixup>: " + payload.getName()); "Unknown callother name in <callotherfixup>: " + payload.getName());
if (callOtherFixupMap.get(payload.getName()) != null) }
if (callOtherFixupMap.get(payload.getName()) != null) {
throw new SleighException( throw new SleighException(
"Duplicate <callotherfixup> tag: " + payload.getName()); "Duplicate <callotherfixup> tag: " + payload.getName());
}
callOtherFixupMap.put(payload.getName(), payload); callOtherFixupMap.put(payload.getName(), payload);
break; break;
case InjectPayload.CALLMECHANISM_TYPE: case InjectPayload.CALLMECHANISM_TYPE:
@ -220,25 +340,154 @@ public class PcodeInjectLibrary {
default: default:
throw new SleighException("Unknown p-code inject type"); throw new SleighException("Unknown p-code inject type");
} }
parseInject(payload);
} }
protected InjectPayloadSleigh allocateInject(String sourceName, String name, int tp) { /**
if (tp == InjectPayload.CALLFIXUP_TYPE) * Remove a specific call mechanism payload.
* @param nm is the name of the payload
* @return true if a payload was successfully removed
*/
protected boolean removeMechanismPayload(String nm) {
InjectPayload payload = callMechFixupMap.remove(nm);
return (payload != null);
}
protected void uninstallProgramPayloads() {
if (programPayload != null) {
for (InjectPayloadSleigh payload : programPayload) {
if (payload.type == InjectPayload.CALLFIXUP_TYPE) {
callFixupMap.remove(payload.name);
}
else if (payload.type == InjectPayload.CALLOTHERFIXUP_TYPE) {
callOtherFixupMap.put(payload.name, null);
}
}
programPayload = null;
if (callOtherOverride != null) {
// Undo callother overrides, reinstalling the overridden payloads
for (InjectPayload payload : callOtherOverride) {
callOtherFixupMap.put(payload.getName(), payload);
}
callOtherOverride = null;
}
}
}
/**
* Look for user callother payloads that override an existing core fixup.
* Move these out of the map into the override list. Don't install user payload yet.
* @param userPayloads is the list of user payloads
*/
private void setupOverrides(List<InjectPayloadSleigh> userPayloads) {
int count = 0;
for (InjectPayloadSleigh payload : userPayloads) {
if (payload.getType() == InjectPayload.CALLOTHERFIXUP_TYPE) {
InjectPayload origPayload = callOtherFixupMap.get(payload.name);
if (origPayload != null) {
count += 1;
}
}
}
if (count == 0) {
return;
}
callOtherOverride = new InjectPayload[count];
count = 0;
for (InjectPayloadSleigh payload : userPayloads) {
if (payload.getType() == InjectPayload.CALLOTHERFIXUP_TYPE) {
InjectPayload origPayload = callOtherFixupMap.get(payload.name);
if (origPayload != null) {
callOtherFixupMap.put(payload.name, null);
callOtherOverride[count] = origPayload;
count += 1;
}
}
}
}
protected void registerProgramInject(List<InjectPayloadSleigh> userPayloads) {
uninstallProgramPayloads();
if (userPayloads.isEmpty()) {
return; // Leave programPayload null if there are no program payloads
}
setupOverrides(userPayloads);
programPayload = new InjectPayloadSleigh[userPayloads.size()];
int count = 0;
for (InjectPayloadSleigh payload : userPayloads) {
try {
registerInject(payload);
programPayload[count] = payload;
count += 1;
}
catch (SleighException ex) {
Msg.warn(this,
"Error installing fixup extension: " + payload.name + ": " + ex.getMessage());
}
}
if (count != programPayload.length) {
InjectPayloadSleigh[] finalPayloads = new InjectPayloadSleigh[count];
System.arraycopy(programPayload, 0, finalPayloads, 0, count);
programPayload = finalPayloads;
}
}
/**
* The main InjectPayload factory interface. This can be overloaded by derived libraries
* to produce custom dynamic payloads.
* @param sourceName is a description of the source of the payload
* @param name is the formal name of the payload
* @param tp is the type of payload: CALLFIXUP_TYPE, CALLOTHERFIXUP_TYPE, etc.
* @return the newly minted InjectPayload
*/
public InjectPayload allocateInject(String sourceName, String name, int tp) {
if (tp == InjectPayload.CALLFIXUP_TYPE) {
return new InjectPayloadCallfixup(sourceName); return new InjectPayloadCallfixup(sourceName);
else if (tp == InjectPayload.CALLOTHERFIXUP_TYPE) }
else if (tp == InjectPayload.CALLOTHERFIXUP_TYPE) {
return new InjectPayloadCallother(sourceName); return new InjectPayloadCallother(sourceName);
}
return new InjectPayloadSleigh(name, tp, sourceName); return new InjectPayloadSleigh(name, tp, sourceName);
} }
public InjectPayload restoreXmlInject(String source, String name, int tp, /**
XmlPullParser parser) { * Save the parts of the inject library that come from the compiler spec
InjectPayloadSleigh payload = allocateInject(source, name, tp); * to the output stream as XML tags
payload.restoreXml(parser); * @param buffer is the output stream
*/
public void saveCompilerSpecXml(StringBuilder buffer) {
for (InjectPayload injectPayload : callFixupMap.values()) {
if (injectPayload instanceof InjectPayloadSleigh) {
((InjectPayloadSleigh) injectPayload).saveXml(buffer);
}
}
for (InjectPayload injectPayload : callOtherFixupMap.values()) {
if (injectPayload instanceof InjectPayloadSleigh) {
((InjectPayloadSleigh) injectPayload).saveXml(buffer);
}
}
for (InjectPayload injectPayload : exePcodeMap.values()) {
if (injectPayload instanceof InjectPayloadSegment) {
if (injectPayload.getSource().startsWith("cspec")) {
((InjectPayloadSleigh) injectPayload).saveXml(buffer);
}
}
}
}
public InjectPayload restoreXmlInject(String source, String name, int tp, XmlPullParser parser)
throws XmlParseException {
InjectPayload payload = allocateInject(source, name, tp);
payload.restoreXml(parser, language);
registerInject(payload); registerInject(payload);
return payload; return payload;
} }
/**
* Get the constant pool associated with the given Program
* @param program is the given Program
* @return the ConstantPool associated with the Program
* @throws IOException for issues constructing the object
*/
public ConstantPool getConstantPool(Program program) throws IOException { public ConstantPool getConstantPool(Program program) throws IOException {
return null; return null;
} }
@ -247,4 +496,60 @@ public class PcodeInjectLibrary {
protected long getUniqueBase() { protected long getUniqueBase() {
return uniqueBase; return uniqueBase;
} }
@Override
public boolean equals(Object obj) {
PcodeInjectLibrary op2 = (PcodeInjectLibrary) obj;
// Cannot compare uniqueBase as one side may not have parsed p-code
// if (uniqueBase != op2.uniqueBase) {
// return false;
// }
if (!callFixupMap.equals(op2.callFixupMap)) {
return false;
}
if (!callMechFixupMap.equals(op2.callMechFixupMap)) {
return false;
}
if (!callOtherFixupMap.equals(op2.callOtherFixupMap)) {
return false;
}
if (!SystemUtilities.isArrayEqual(callOtherOverride, op2.callOtherOverride)) {
return false;
}
if (!exePcodeMap.equals(op2.exePcodeMap)) {
return false;
}
if (!SystemUtilities.isArrayEqual(programPayload, op2.programPayload)) {
return false;
}
return true;
}
@Override
public int hashCode() {
int hash = 111;
for (InjectPayload payload : callFixupMap.values()) {
hash = 79 * hash + payload.hashCode();
}
for (InjectPayload payload : callMechFixupMap.values()) {
hash = 79 * hash + payload.hashCode();
}
for (InjectPayload payload : callOtherFixupMap.values()) {
hash = 79 * hash + payload.hashCode();
}
for (InjectPayload payload : exePcodeMap.values()) {
hash = 79 * hash + payload.hashCode();
}
if (programPayload != null) {
for (InjectPayloadSleigh payload : programPayload) {
hash = 79 * hash + payload.hashCode();
}
}
if (callOtherOverride != null) {
for (InjectPayload payload : callOtherOverride) {
hash = 79 * hash + payload.hashCode();
}
}
return hash;
}
} }

View file

@ -17,10 +17,12 @@ package ghidra.program.model.lang;
import java.util.ArrayList; import java.util.ArrayList;
import ghidra.program.model.address.Address; import ghidra.program.model.address.*;
import ghidra.program.model.data.*; import ghidra.program.model.data.*;
import ghidra.program.model.listing.*; import ghidra.program.model.listing.*;
import ghidra.program.model.pcode.AddressXML;
import ghidra.program.model.pcode.Varnode; import ghidra.program.model.pcode.Varnode;
import ghidra.util.SystemUtilities;
import ghidra.util.exception.InvalidInputException; import ghidra.util.exception.InvalidInputException;
import ghidra.util.xml.SpecXmlUtils; import ghidra.util.xml.SpecXmlUtils;
import ghidra.xml.*; import ghidra.xml.*;
@ -37,48 +39,122 @@ public class PrototypeModel {
public static final int UNKNOWN_EXTRAPOP = 0x8000; public static final int UNKNOWN_EXTRAPOP = 0x8000;
protected String name; // Name of model protected String name; // Name of model
protected boolean isExtension; // True if this model is a Program specific extension
private int extrapop; // change in stackpointer private int extrapop; // change in stackpointer
// across function calls // across function calls
private int stackshift; // change in stackpointer private int stackshift; // change in stackpointer
// due to call mechanism // due to call mechanism
private ParamList inputParams; // (possible) parameter locations private ParamList inputParams; // (possible) parameter locations
private ParamList outputParams; private ParamList outputParams;
private Varnode[] unaffected; // Memory ranges unaffected by calls
private Varnode[] killedbycall; // Memory ranges definitely affected by calls
private Varnode[] returnaddress; // Memory used to store the return address
private Varnode[] likelytrash; // Memory likely to be meaningless on input
private AddressSet localRange; // Range on the stack considered for local storage
private AddressSet paramRange; // Range on the stack considered for parameter storage
private InputListType inputListType = InputListType.STANDARD; private InputListType inputListType = InputListType.STANDARD;
private GenericCallingConvention genericCallingConvention; private GenericCallingConvention genericCallingConvention;
private boolean hasThis; // Convention has a this (auto-parameter) private boolean hasThis; // Convention has a this (auto-parameter)
private boolean isConstruct; // Convention is used for object construction private boolean isConstruct; // Convention is used for object construction
private boolean hasUponEntry; // Does this have an uponentry injection
private boolean hasUponReturn; // Does this have an uponreturn injection
public PrototypeModel(String name, PrototypeModel model) { public PrototypeModel(String name, PrototypeModel model) {
this.name = name; this.name = name;
isExtension = false;
extrapop = model.extrapop; extrapop = model.extrapop;
stackshift = model.stackshift; stackshift = model.stackshift;
inputListType = model.inputListType; inputListType = model.inputListType;
inputParams = model.inputParams; inputParams = model.inputParams;
outputParams = model.outputParams; outputParams = model.outputParams;
unaffected = model.unaffected;
killedbycall = model.killedbycall;
returnaddress = model.returnaddress;
likelytrash = model.likelytrash;
localRange = new AddressSet(model.localRange);
paramRange = new AddressSet(model.paramRange);
hasThis = model.hasThis || name.equals(CompilerSpec.CALLING_CONVENTION_thiscall); hasThis = model.hasThis || name.equals(CompilerSpec.CALLING_CONVENTION_thiscall);
isConstruct = model.isConstruct; isConstruct = model.isConstruct;
genericCallingConvention = GenericCallingConvention.getGenericCallingConvention(name); genericCallingConvention = GenericCallingConvention.getGenericCallingConvention(name);
hasUponEntry = model.hasUponEntry;
hasUponReturn = model.hasUponReturn;
} }
public PrototypeModel() { public PrototypeModel() {
name = null; name = null;
isExtension = false;
extrapop = PrototypeModel.UNKNOWN_EXTRAPOP; extrapop = PrototypeModel.UNKNOWN_EXTRAPOP;
stackshift = -1; stackshift = -1;
inputParams = null; inputParams = null;
outputParams = null; outputParams = null;
unaffected = null;
killedbycall = null;
returnaddress = null;
likelytrash = null;
localRange = null;
paramRange = null;
genericCallingConvention = GenericCallingConvention.unknown; genericCallingConvention = GenericCallingConvention.unknown;
hasThis = false; hasThis = false;
isConstruct = false; isConstruct = false;
hasUponEntry = false;
hasUponReturn = false;
} }
public GenericCallingConvention getGenericCallingConvention() { public GenericCallingConvention getGenericCallingConvention() {
return genericCallingConvention; return genericCallingConvention;
} }
/**
* @return list of registers unaffected by called functions
*/
public Varnode[] getUnaffectedList() {
if (unaffected == null) {
unaffected = new Varnode[0];
}
return unaffected;
}
/**
* @return list of registers definitely affected by called functions
*/
public Varnode[] getKilledByCallList() {
if (killedbycall == null) {
killedbycall = new Varnode[0];
}
return killedbycall;
}
/**
* @return list of registers whose input value is likely meaningless
*/
public Varnode[] getLikelyTrash() {
if (likelytrash == null) {
likelytrash = new Varnode[0];
}
return likelytrash;
}
/**
* @return list of registers/memory used to store the return address
*/
public Varnode[] getReturnAddress() {
if (returnaddress == null) {
returnaddress = new Varnode[0];
}
return returnaddress;
}
public boolean isMerged() { public boolean isMerged() {
return false; return false;
} }
/**
* @return true if this model is a Program specific extension to the CompilerSpec
*/
public boolean isProgramExtension() {
return isExtension;
}
public String getName() { public String getName() {
return name; return name;
} }
@ -103,14 +179,17 @@ public class PrototypeModel {
return inputListType; return inputListType;
} }
public boolean hasInjection() {
return hasUponEntry || hasUponReturn;
}
/** /**
* @deprecated * @deprecated
* Get the preferred return location given the specified dataType. * Get the preferred return location given the specified dataType.
* In truth, there is no one location. The routines that use this method tend * In truth, there is no one location. The routines that use this method tend
* to want the default storage location for integer or pointer return values. * to want the default storage location for integer or pointer return values.
* @param dataType first parameter dataType or null for a default * @param dataType first parameter dataType or null for an undefined type.
* undefined type. * @param program is the Program
* @param program
* @return return location or {@link VariableStorage#UNASSIGNED_STORAGE} if * @return return location or {@link VariableStorage#UNASSIGNED_STORAGE} if
* unable to determine suitable location * unable to determine suitable location
*/ */
@ -119,7 +198,7 @@ public class PrototypeModel {
DataType clone = dataType.clone(program.getDataTypeManager()); DataType clone = dataType.clone(program.getDataTypeManager());
DataType[] arr = new DataType[1]; DataType[] arr = new DataType[1];
arr[0] = clone; arr[0] = clone;
ArrayList<VariableStorage> res = new ArrayList<VariableStorage>(); ArrayList<VariableStorage> res = new ArrayList<>();
outputParams.assignMap(program, arr, false, res, false); outputParams.assignMap(program, arr, false, res, false);
if (res.size() > 0) { if (res.size() > 0) {
return res.get(0); return res.get(0);
@ -135,43 +214,48 @@ public class PrototypeModel {
* be appended. (may be null) * be appended. (may be null)
* @param dataType dataType associated with next parameter location or null * @param dataType dataType associated with next parameter location or null
* for a default undefined type. * for a default undefined type.
* @param program * @param program is the Program
* @return next parameter location or {@link VariableStorage#UNASSIGNED_STORAGE} if * @return next parameter location or {@link VariableStorage#UNASSIGNED_STORAGE} if
* unable to determine suitable location * unable to determine suitable location
*/ */
public VariableStorage getNextArgLocation(Parameter[] params, DataType dataType, Program program) { public VariableStorage getNextArgLocation(Parameter[] params, DataType dataType,
Program program) {
return getArgLocation(params != null ? params.length : 0, params, dataType, program); return getArgLocation(params != null ? params.length : 0, params, dataType, program);
} }
/** /**
* Get the preferred parameter location for a specified parameter specified by argIndex * Get the preferred parameter location for a specified index,
* which will be added/inserted within the set of existing function params. * which will be added/inserted within the set of existing function params.
* If existing parameters use custom storage, this method should not be used. * If existing parameters use custom storage, this method should not be used.
* @param argIndex is the index
* @param params existing set parameters to which the parameter specified by * @param params existing set parameters to which the parameter specified by
* argIndex will be added/inserted be appended (may be null). * argIndex will be added/inserted be appended (may be null).
* @param dataType dataType associated with next parameter location or null * @param dataType dataType associated with next parameter location or null
* for a default undefined type. * for a default undefined type.
* @param program * @param program is the Program
* @return parameter location or {@link VariableStorage#UNASSIGNED_STORAGE} if * @return parameter location or {@link VariableStorage#UNASSIGNED_STORAGE} if
* unable to determine suitable location * unable to determine suitable location
*/ */
public VariableStorage getArgLocation(int argIndex, Parameter[] params, DataType dataType, public VariableStorage getArgLocation(int argIndex, Parameter[] params, DataType dataType,
Program program) { Program program) {
if (dataType != null) if (dataType != null) {
dataType = dataType.clone(program.getDataTypeManager()); dataType = dataType.clone(program.getDataTypeManager());
// Identify next arg index based upon number of storage varnodes // Identify next arg index based upon number of storage varnodes
// already assigned to parameters - this may not work well if // already assigned to parameters - this may not work well if
// customized storage has been used // customized storage has been used
}
DataType arr[] = new DataType[argIndex + 2]; DataType arr[] = new DataType[argIndex + 2];
arr[0] = DataType.VOID; // Assume the return type is void arr[0] = DataType.VOID; // Assume the return type is void
for (int i = 0; i < argIndex; ++i) { for (int i = 0; i < argIndex; ++i) {
if (params != null && i < params.length) if (params != null && i < params.length) {
arr[i + 1] = params[i].getDataType(); // Copy in current types if we have them arr[i + 1] = params[i].getDataType(); // Copy in current types if we have them
else }
else {
arr[i + 1] = DataType.DEFAULT; // Otherwise assume default (integer) type arr[i + 1] = DataType.DEFAULT; // Otherwise assume default (integer) type
} }
}
arr[argIndex + 1] = dataType; arr[argIndex + 1] = dataType;
VariableStorage res[] = getStorageLocations(program, arr, false); VariableStorage res[] = getStorageLocations(program, arr, false);
@ -181,7 +265,7 @@ public class PrototypeModel {
/** /**
* Compute the variable storage for a given function and set of return/parameter datatypes * Compute the variable storage for a given function and set of return/parameter datatypes
* defined by an array of data types. * defined by an array of data types.
* @param program * @param program is the Program
* @param dataTypes return/parameter datatypes (first element is always the return datatype, * @param dataTypes return/parameter datatypes (first element is always the return datatype,
* i.e., minimum array length is 1) * i.e., minimum array length is 1)
* @param addAutoParams TODO * @param addAutoParams TODO
@ -206,7 +290,7 @@ public class PrototypeModel {
dataTypes = ammendedTypes; dataTypes = ammendedTypes;
} }
ArrayList<VariableStorage> res = new ArrayList<VariableStorage>(); ArrayList<VariableStorage> res = new ArrayList<>();
outputParams.assignMap(program, dataTypes, false, res, addAutoParams); outputParams.assignMap(program, dataTypes, false, res, addAutoParams);
inputParams.assignMap(program, dataTypes, true, res, addAutoParams); inputParams.assignMap(program, dataTypes, true, res, addAutoParams);
VariableStorage[] finalres = new VariableStorage[res.size()]; VariableStorage[] finalres = new VariableStorage[res.size()];
@ -222,9 +306,8 @@ public class PrototypeModel {
if (inputParams.isThisBeforeRetPointer()) { if (inputParams.isThisBeforeRetPointer()) {
// pointer has been bumped by auto-return-storage // pointer has been bumped by auto-return-storage
// must swap storage and position for slots 1 and 2 // must swap storage and position for slots 1 and 2
finalres[2] = finalres[2] = new DynamicVariableStorage(program,
new DynamicVariableStorage(program, finalres[1].getAutoParameterType(), finalres[1].getAutoParameterType(), finalres[2].getVarnodes());
finalres[2].getVarnodes());
} }
else { else {
thisIndex = 2; thisIndex = 2;
@ -242,7 +325,8 @@ public class PrototypeModel {
} }
} }
catch (InvalidInputException e) { catch (InvalidInputException e) {
finalres[thisIndex] = DynamicVariableStorage.getUnassignedDynamicStorage(AutoParameterType.THIS); finalres[thisIndex] =
DynamicVariableStorage.getUnassignedDynamicStorage(AutoParameterType.THIS);
} }
} }
@ -250,6 +334,15 @@ public class PrototypeModel {
return finalres; return finalres;
} }
/**
* If a PrototypeModel fails to parse (from XML) a substitute model may be provided, in which
* case this method returns true. In all other cases this method returns false;
* @return true if this object is a substitute for a model that didn't parse
*/
public boolean isErrorPlaceholder() {
return false;
}
private void buildParamList(String strategy) throws XmlParseException { private void buildParamList(String strategy) throws XmlParseException {
if (strategy == null || strategy.equals("standard")) { if (strategy == null || strategy.equals("standard")) {
inputParams = new ParamListStandard(); inputParams = new ParamListStandard();
@ -261,20 +354,178 @@ public class PrototypeModel {
outputParams = new ParamListStandard(); outputParams = new ParamListStandard();
inputListType = InputListType.REGISTER; inputListType = InputListType.REGISTER;
} }
else else {
throw new XmlParseException("Unknown assign strategy: " + strategy); throw new XmlParseException("Unknown assign strategy: " + strategy);
} }
}
public void restoreXml(XmlPullParser parser, CompilerSpec cspec, boolean normalstack) public void saveXml(StringBuilder buffer, PcodeInjectLibrary injectLibrary) {
buffer.append("<prototype");
SpecXmlUtils.encodeStringAttribute(buffer, "name", name);
if (extrapop != PrototypeModel.UNKNOWN_EXTRAPOP) {
SpecXmlUtils.encodeSignedIntegerAttribute(buffer, "extrapop", extrapop);
}
else {
SpecXmlUtils.encodeStringAttribute(buffer, "extrapop", "unknown");
}
SpecXmlUtils.encodeSignedIntegerAttribute(buffer, "stackshift", stackshift);
GenericCallingConvention nameType = GenericCallingConvention.guessFromName(name);
if (nameType != genericCallingConvention) {
SpecXmlUtils.encodeStringAttribute(buffer, "type",
genericCallingConvention.getDeclarationName());
}
if (hasThis) {
SpecXmlUtils.encodeStringAttribute(buffer, "hasthis", "yes");
}
if (isConstruct) {
SpecXmlUtils.encodeStringAttribute(buffer, "constructor", "yes");
}
if (inputListType != InputListType.STANDARD) {
SpecXmlUtils.encodeStringAttribute(buffer, "strategy", "register");
}
buffer.append(">\n");
inputParams.saveXml(buffer, true);
buffer.append('\n');
outputParams.saveXml(buffer, false);
buffer.append('\n');
if (hasUponEntry || hasUponReturn) {
InjectPayload payload =
injectLibrary.getPayload(InjectPayload.CALLMECHANISM_TYPE, getInjectName());
payload.saveXml(buffer);
}
if (unaffected != null) {
buffer.append("<unaffected>\n");
writeVarnodes(buffer, unaffected);
buffer.append("</unaffected>\n");
}
if (killedbycall != null) {
buffer.append("<killedbycall>\n");
writeVarnodes(buffer, killedbycall);
buffer.append("</killedbycall>\n");
}
if (likelytrash != null) {
buffer.append("<likelytrash>\n");
writeVarnodes(buffer, likelytrash);
buffer.append("</likelytrash>\n");
}
if (returnaddress != null) {
buffer.append("<returnaddress>\n");
writeVarnodes(buffer, returnaddress);
buffer.append("</returnaddress>\n");
}
if (localRange != null && !localRange.isEmpty()) {
buffer.append("<localrange>\n");
writeAddressSet(buffer, localRange);
buffer.append("</localrange>\n");
}
if (paramRange != null && !paramRange.isEmpty()) {
buffer.append("<paramrange>\n");
writeAddressSet(buffer, paramRange);
buffer.append("</paramrange>\n");
}
buffer.append("</prototype>\n");
}
private void writeVarnodes(StringBuilder buffer, Varnode[] varnodes) {
for (Varnode vn : varnodes) {
buffer.append("<varnode");
AddressXML.appendAttributes(buffer, vn.getAddress(), vn.getSize());
buffer.append("/>\n");
}
}
private Varnode[] readVarnodes(XmlPullParser parser, CompilerSpec cspec)
throws XmlParseException { throws XmlParseException {
parser.start();
ArrayList<Varnode> varnodeList = new ArrayList<>();
while (parser.peek().isStart()) {
XmlElement el = parser.start();
AddressXML ourAddress = AddressXML.restoreXml(el, cspec);
if (ourAddress.getJoinRecord() != null) {
throw new XmlParseException(
"No \"join\" in <unaffected>, <killedbycall>, or <likelytrash>");
}
varnodeList.add(ourAddress.getVarnode());
parser.end(el);
}
parser.end();
Varnode[] res = new Varnode[varnodeList.size()];
varnodeList.toArray(res);
return res;
}
private void writeAddressSet(StringBuilder buffer, AddressSet addressSet) {
AddressRangeIterator iter = addressSet.getAddressRanges();
while (iter.hasNext()) {
AddressRange addrRange = iter.next();
AddressSpace space = addrRange.getAddressSpace();
long first = addrRange.getMinAddress().getOffset();
long last = addrRange.getMaxAddress().getOffset();
if (space.hasSignedOffset()) {
long mask;
if (space.getSize() < 64) {
mask = 1;
mask <<= space.getSize();
}
else {
mask = 0;
}
mask -= 1;
if (first < 0 && last >= 0) { // Range crosses 0
first &= mask;
// Split out the piece coming before 0
buffer.append("<range");
SpecXmlUtils.encodeStringAttribute(buffer, "space", space.getName());
SpecXmlUtils.encodeUnsignedIntegerAttribute(buffer, "first", first);
SpecXmlUtils.encodeUnsignedIntegerAttribute(buffer, "last", mask);
buffer.append("/>\n");
// Reset first,last to be the piece coming after 0
first = 0;
}
first &= mask;
last &= mask;
}
buffer.append("<range");
SpecXmlUtils.encodeStringAttribute(buffer, "space", space.getName());
SpecXmlUtils.encodeUnsignedIntegerAttribute(buffer, "first", first);
SpecXmlUtils.encodeUnsignedIntegerAttribute(buffer, "last", last);
buffer.append("/>\n");
}
}
private AddressSet readAddressSet(XmlPullParser parser, CompilerSpec cspec)
throws XmlParseException {
AddressSet addressSet = new AddressSet();
parser.start();
while (parser.peek().isStart()) {
XmlElement el = parser.start();
AddressXML range = AddressXML.restoreRangeXml(el, cspec);
parser.end(el);
Address firstAddr = range.getFirstAddress();
Address lastAddr = range.getLastAddress();
addressSet.add(firstAddr, lastAddr);
}
parser.end();
return addressSet;
}
protected String getInjectName() {
if (hasUponEntry) {
return name + "@@inject_uponentry";
}
return name + "@@inject_uponreturn";
}
public void restoreXml(XmlPullParser parser, CompilerSpec cspec) throws XmlParseException {
inputParams = null; inputParams = null;
outputParams = null; outputParams = null;
XmlElement protoElement = parser.start(); XmlElement protoElement = parser.start();
name = protoElement.getAttribute("name"); name = protoElement.getAttribute("name");
extrapop = PrototypeModel.UNKNOWN_EXTRAPOP; extrapop = PrototypeModel.UNKNOWN_EXTRAPOP;
String extpopStr = protoElement.getAttribute("extrapop"); String extpopStr = protoElement.getAttribute("extrapop");
if (!extpopStr.equals("unknown")) if (!extpopStr.equals("unknown")) {
extrapop = SpecXmlUtils.decodeInt(extpopStr); extrapop = SpecXmlUtils.decodeInt(extpopStr);
}
stackshift = SpecXmlUtils.decodeInt(protoElement.getAttribute("stackshift")); stackshift = SpecXmlUtils.decodeInt(protoElement.getAttribute("stackshift"));
String type = protoElement.getAttribute("type"); String type = protoElement.getAttribute("type");
if (type != null) { if (type != null) {
@ -286,33 +537,57 @@ public class PrototypeModel {
hasThis = false; hasThis = false;
isConstruct = false; isConstruct = false;
String thisString = protoElement.getAttribute("hasthis"); String thisString = protoElement.getAttribute("hasthis");
if (thisString != null) if (thisString != null) {
hasThis = SpecXmlUtils.decodeBoolean(thisString); hasThis = SpecXmlUtils.decodeBoolean(thisString);
else }
else {
hasThis = name.equals(CompilerSpec.CALLING_CONVENTION_thiscall); hasThis = name.equals(CompilerSpec.CALLING_CONVENTION_thiscall);
}
String constructString = protoElement.getAttribute("constructor"); String constructString = protoElement.getAttribute("constructor");
if (constructString != null) if (constructString != null) {
isConstruct = SpecXmlUtils.decodeBoolean(constructString); isConstruct = SpecXmlUtils.decodeBoolean(constructString);
}
buildParamList(protoElement.getAttribute("strategy")); buildParamList(protoElement.getAttribute("strategy"));
while (parser.peek().isStart()) { while (parser.peek().isStart()) {
XmlElement subel = parser.peek(); XmlElement subel = parser.peek();
if (subel.getName().equals("input")) { String elName = subel.getName();
inputParams.restoreXml(parser, cspec, normalstack); if (elName.equals("input")) {
inputParams.restoreXml(parser, cspec);
} }
else if (subel.getName().equals("output")) { else if (elName.equals("output")) {
outputParams.restoreXml(parser, cspec, normalstack); outputParams.restoreXml(parser, cspec);
} }
else if (subel.getName().equals("pcode")) { else if (elName.equals("pcode")) {
XmlElement el = parser.peek(); XmlElement el = parser.peek();
String nm;
String source = "Compiler spec=" + cspec.getCompilerSpecID().getIdAsString(); String source = "Compiler spec=" + cspec.getCompilerSpecID().getIdAsString();
if (el.getAttribute("inject").equals("uponentry")) if (el.getAttribute("inject").equals("uponentry")) {
nm = name + "@@inject_uponentry"; hasUponEntry = true;
else }
nm = name + "@@inject_uponreturn"; else {
cspec.getPcodeInjectLibrary().restoreXmlInject(source, nm, hasUponReturn = true;
InjectPayload.CALLMECHANISM_TYPE, parser); }
cspec.getPcodeInjectLibrary()
.restoreXmlInject(source, getInjectName(), InjectPayload.CALLMECHANISM_TYPE,
parser);
}
else if (elName.equals("unaffected")) {
unaffected = readVarnodes(parser, cspec);
}
else if (elName.equals("killedbycall")) {
killedbycall = readVarnodes(parser, cspec);
}
else if (elName.equals("returnaddress")) {
returnaddress = readVarnodes(parser, cspec);
}
else if (elName.equals("likelytrash")) {
likelytrash = readVarnodes(parser, cspec);
}
else if (elName.equals("localrange")) {
localRange = readAddressSet(parser, cspec);
}
else if (elName.equals("paramrange")) {
paramRange = readAddressSet(parser, cspec);
} }
else { else {
subel = parser.start(); subel = parser.start();
@ -342,6 +617,59 @@ public class PrototypeModel {
return inputParams.getPotentialRegisterStorage(prog); return inputParams.getPotentialRegisterStorage(prog);
} }
@Override
public boolean equals(Object obj) {
PrototypeModel op2 = (PrototypeModel) obj;
if (!name.equals(op2.name)) {
return false;
}
if (extrapop != op2.extrapop || stackshift != op2.stackshift) {
return false;
}
if (genericCallingConvention != op2.genericCallingConvention) {
return false;
}
if (hasThis != op2.hasThis || isConstruct != op2.isConstruct) {
return false;
}
if (hasUponEntry != op2.hasUponEntry || hasUponReturn != op2.hasUponReturn) {
return false;
}
if (inputListType != op2.inputListType) {
return false;
}
if (!inputParams.equals(op2.inputParams)) {
return false;
}
if (!outputParams.equals(op2.outputParams)) {
return false;
}
if (!SystemUtilities.isArrayEqual(unaffected, op2.unaffected)) {
return false;
}
if (!SystemUtilities.isArrayEqual(killedbycall, op2.killedbycall)) {
return false;
}
if (!SystemUtilities.isArrayEqual(likelytrash, op2.likelytrash)) {
return false;
}
if (!SystemUtilities.isEqual(localRange, op2.localRange)) {
return false;
}
if (!SystemUtilities.isEqual(paramRange, op2.paramRange)) {
return false;
}
if (!SystemUtilities.isArrayEqual(returnaddress, op2.returnaddress)) {
return false;
}
return true;
}
@Override
public int hashCode() {
return name.hashCode();
}
@Override @Override
public String toString() { public String toString() {
return getName(); return getName();

View file

@ -0,0 +1,31 @@
/* ###
* 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.program.model.lang;
/**
* A PrototypeModel cloned from another, but marked as an error placeholder
*/
public class PrototypeModelError extends PrototypeModel {
public PrototypeModelError(String name, PrototypeModel copyModel) {
super(name, copyModel);
}
@Override
public boolean isErrorPlaceholder() {
return true;
}
}

View file

@ -20,6 +20,8 @@ import java.util.*;
import ghidra.app.plugin.processors.sleigh.SleighException; import ghidra.app.plugin.processors.sleigh.SleighException;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Parameter; import ghidra.program.model.listing.Parameter;
import ghidra.util.SystemUtilities;
import ghidra.util.xml.SpecXmlUtils;
import ghidra.xml.*; import ghidra.xml.*;
/** /**
@ -39,6 +41,7 @@ public class PrototypeModelMerged extends PrototypeModel {
/* (non-Javadoc) /* (non-Javadoc)
* @see ghidra.program.model.lang.PrototypeModel#isMerged() * @see ghidra.program.model.lang.PrototypeModel#isMerged()
*/ */
@Override
public boolean isMerged() { public boolean isMerged() {
return true; return true;
} }
@ -51,35 +54,50 @@ public class PrototypeModelMerged extends PrototypeModel {
return modellist[i]; return modellist[i];
} }
public void restoreXml(XmlPullParser parser,List<PrototypeModel> modelList,boolean normalstack) throws XmlParseException { @Override
ArrayList<PrototypeModel> mylist = new ArrayList<PrototypeModel>(); public void saveXml(StringBuilder buffer, PcodeInjectLibrary injectLibrary) {
buffer.append("<resolveprototype");
SpecXmlUtils.encodeStringAttribute(buffer, "name", name);
buffer.append(">\n");
for (PrototypeModel model : modellist) {
buffer.append("<model");
SpecXmlUtils.encodeStringAttribute(buffer, "name", model.name);
buffer.append("/>\n");
}
buffer.append("</resolveprototype>\n");
}
public void restoreXml(XmlPullParser parser, List<PrototypeModel> modelList)
throws XmlParseException {
ArrayList<PrototypeModel> mylist = new ArrayList<>();
XmlElement el = parser.start(); XmlElement el = parser.start();
name = el.getAttribute("name"); name = el.getAttribute("name");
while(parser.peek().isStart()) { while (parser.peek().isStart()) {
XmlElement subel = parser.start(); XmlElement subel = parser.start();
String modelName = subel.getAttribute("name"); String modelName = subel.getAttribute("name");
PrototypeModel foundModel = null; PrototypeModel foundModel = null;
for(PrototypeModel model : modelList) { for (PrototypeModel model : modelList) {
if (model.name.equals(modelName)) { if (model.name.equals(modelName)) {
foundModel = model; foundModel = model;
break; break;
} }
} }
if (foundModel == null) if (foundModel == null) {
throw new XmlParseException("Missing prototype model: "+modelName); throw new XmlParseException("Missing prototype model: " + modelName);
}
mylist.add(foundModel); mylist.add(foundModel);
parser.end(subel); parser.end(subel);
} }
parser.end(el); parser.end(el);
modellist = new PrototypeModel[ mylist.size() ]; modellist = new PrototypeModel[mylist.size()];
mylist.toArray(modellist); mylist.toArray(modellist);
} }
public PrototypeModel selectModel(Parameter[] params) throws SleighException { public PrototypeModel selectModel(Parameter[] params) throws SleighException {
int bestscore = 500; int bestscore = 500;
int bestindex = -1; int bestindex = -1;
for(int i=0;i<modellist.length;++i) { for (int i = 0; i < modellist.length; ++i) {
ScoreProtoModel scoremodel = new ScoreProtoModel(true,modellist[i],params.length); ScoreProtoModel scoremodel = new ScoreProtoModel(true, modellist[i], params.length);
for (Parameter p : params) { for (Parameter p : params) {
scoremodel.addParameter(p.getMinAddress(), p.getLength()); scoremodel.addParameter(p.getMinAddress(), p.getLength());
} }
@ -88,24 +106,27 @@ public class PrototypeModelMerged extends PrototypeModel {
if (score < bestscore) { if (score < bestscore) {
bestscore = score; bestscore = score;
bestindex = i; bestindex = i;
if (bestscore == 0) if (bestscore == 0) {
break; // Can't get any lower break; // Can't get any lower
} }
} }
if (bestindex >= 0) }
if (bestindex >= 0) {
return modellist[bestindex]; return modellist[bestindex];
}
throw new SleighException("No model matches : missing default"); throw new SleighException("No model matches : missing default");
} }
private static class PEntry implements Comparable<PEntry> { private static class PEntry implements Comparable<PEntry> {
public int origIndex; // Original index of parameter // public int origIndex; // Original index of parameter
public int slot; // slot within the list public int slot; // slot within the list
public int size; // number of slots occupied public int size; // number of slots occupied
@Override @Override
public int compareTo(PEntry o) { public int compareTo(PEntry o) {
if (slot != o.slot) if (slot != o.slot) {
return (slot < o.slot) ? -1 : 1; return (slot < o.slot) ? -1 : 1;
}
return 0; return 0;
} }
} }
@ -117,10 +138,10 @@ public class PrototypeModelMerged extends PrototypeModel {
private int finalscore; private int finalscore;
private int mismatch; private int mismatch;
public ScoreProtoModel(boolean isinput,PrototypeModel mod,int numparam) { public ScoreProtoModel(boolean isinput, PrototypeModel mod, int numparam) {
isinputscore = isinput; isinputscore = isinput;
model = mod; model = mod;
entry = new ArrayList<PEntry>(numparam); entry = new ArrayList<>(numparam);
finalscore = -1; finalscore = -1;
mismatch = 0; mismatch = 0;
} }
@ -129,30 +150,29 @@ public class PrototypeModelMerged extends PrototypeModel {
return finalscore; return finalscore;
} }
public int getNumMismatch() { public void addParameter(Address addr, int sz) {
return mismatch; // int orig = entry.size();
}
public void addParameter(Address addr,int sz) {
int orig = entry.size();
ParamList.WithSlotRec rec = new ParamList.WithSlotRec(); ParamList.WithSlotRec rec = new ParamList.WithSlotRec();
boolean isparam; boolean isparam;
if (isinputscore) if (isinputscore) {
isparam = model.possibleInputParamWithSlot(addr, sz, rec); isparam = model.possibleInputParamWithSlot(addr, sz, rec);
else }
else {
isparam = model.possibleOutputParamWithSlot(addr, sz, rec); isparam = model.possibleOutputParamWithSlot(addr, sz, rec);
}
if (isparam) { if (isparam) {
PEntry pe = new PEntry(); PEntry pe = new PEntry();
pe.origIndex = orig; // pe.origIndex = orig;
pe.slot = rec.slot; pe.slot = rec.slot;
pe.size = rec.slotsize; pe.size = rec.slotsize;
entry.add(pe); entry.add(pe);
} }
else else {
mismatch += 1; mismatch += 1;
} }
}
public void doScore() { public void doScore() {
Collections.sort(entry); // Sort our entries via slot Collections.sort(entry); // Sort our entries via slot
@ -167,23 +187,25 @@ public class PrototypeModelMerged extends PrototypeModel {
int penaltyfinal = 3; int penaltyfinal = 3;
int mismatchpenalty = 20; int mismatchpenalty = 20;
for(int i=0;i<entry.size();++i) { for (PEntry p : entry) {
PEntry p = entry.get(i);
if (p.slot > nextfree) { // We have some kind of hole in our slot coverage if (p.slot > nextfree) { // We have some kind of hole in our slot coverage
while(nextfree < p.slot) { while (nextfree < p.slot) {
if (nextfree < 4) if (nextfree < 4) {
basescore += penalty[nextfree]; basescore += penalty[nextfree];
else }
else {
basescore += penaltyfinal; basescore += penaltyfinal;
}
nextfree += 1; nextfree += 1;
} }
nextfree += p.size; nextfree += p.size;
} }
else if (nextfree > p.slot) { // Some kind of slot duplication else if (nextfree > p.slot) { // Some kind of slot duplication
basescore += mismatchpenalty; basescore += mismatchpenalty;
if (p.slot + p.size > nextfree) if (p.slot + p.size > nextfree) {
nextfree = p.slot + p.size; nextfree = p.slot + p.size;
} }
}
else { else {
nextfree = p.slot + p.size; nextfree = p.slot + p.size;
} }
@ -191,4 +213,22 @@ public class PrototypeModelMerged extends PrototypeModel {
finalscore = basescore + mismatchpenalty * mismatch; finalscore = basescore + mismatchpenalty * mismatch;
} }
} }
@Override
public boolean equals(Object obj) {
PrototypeModelMerged op2 = (PrototypeModelMerged) obj;
if (!name.equals(op2.name)) {
return false;
}
return SystemUtilities.isArrayEqual(modellist, op2.modellist);
}
@Override
public int hashCode() {
int hash = name.hashCode();
for (PrototypeModel model : modellist) {
hash = 79 * hash + model.hashCode();
}
return hash;
}
} }

View file

@ -125,8 +125,9 @@ public class InstructionPcodeOverride implements PcodeOverride {
if (fixupName == null) { if (fixupName == null) {
return null; return null;
} }
InjectPayload fixup = program.getCompilerSpec().getPcodeInjectLibrary().getPayload( InjectPayload fixup = program.getCompilerSpec()
InjectPayload.CALLFIXUP_TYPE, fixupName, program, null); .getPcodeInjectLibrary()
.getPayload(InjectPayload.CALLFIXUP_TYPE, fixupName);
if (fixup == null) { if (fixup == null) {
Msg.warn(this, "Undefined call-fixup at " + callDestAddr + ": " + fixupName); Msg.warn(this, "Undefined call-fixup at " + callDestAddr + ": " + fixupName);
} }

View file

@ -0,0 +1,664 @@
/* ###
* 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.program.model.pcode;
import java.util.ArrayList;
import org.xml.sax.Attributes;
import ghidra.program.model.address.*;
import ghidra.program.model.lang.*;
import ghidra.util.xml.SpecXmlUtils;
import ghidra.xml.XmlElement;
import ghidra.xml.XmlParseException;
/**
* Utility class for the myriad ways of marshaling/unmarshaling an address and an optional size,
* to/from XML for the various configuration files.
*
* An object of the class itself is the most general form, where the specified address
* - MAY have an associated size given in bytes
* - MAY be in the JOIN address space, with physical pieces making up the logical value explicitly provided.
*
* The static buildXML methods write out an \<addr> tag given component elements without allocating an object.
* The static readXML methods read XML tags (presented in different forms) and returns an Address object.
* The static appendAttributes methods write out attributes of an address to an arbitrary XML tag.
* The static restoreXML methods read an \<addr> tag and produce a general AddressXML object.
*/
public class AddressXML {
private AddressSpace space; // Address space containing the memory range
private long offset; // Starting offset of the range
private long size; // Number of bytes in the size
private Varnode[] joinRecord; // If non-null, separate address ranges being bonded in the "join" space
/**
* Internal constructor for incremental initialization
*/
private AddressXML() {
space = null;
joinRecord = null;
}
/**
* Construct an Address range as a space/offset/size
* @param spc is the address space containing the range
* @param off is the starting byte offset of the range
* @param sz is the size of the range in bytes
*/
public AddressXML(AddressSpace spc, long off, int sz) {
space = spc;
offset = off;
size = sz;
joinRecord = null;
}
/**
* Construct a logical memory range, representing multiple ranges pieced together.
* The logical range is assigned an address in the JOIN address space.
* The physical pieces making up the logical range are passed in as a sequence of
* Varnodes representing, in order, the most significant through the least significant
* portions of the value.
* @param spc is the JOIN address space (must have a type of AddressSpace.TYPE_JOIN)
* @param off is the offset of the logical value within the JOIN space
* @param sz is the number of bytes in the logical value
* @param pieces is the array of 1 or more physical pieces
*/
public AddressXML(AddressSpace spc, long off, int sz, Varnode[] pieces) {
if (spc.getType() != AddressSpace.TYPE_JOIN) {
throw new IllegalArgumentException(
"JOIN address space required to represent an Address with pieces");
}
space = spc;
offset = off;
size = sz;
joinRecord = pieces;
}
private void readJoinXML(XmlElement el, CompilerSpec cspec) throws XmlParseException {
ArrayList<Varnode> pieces = new ArrayList<>();
int sizesum = 0;
int pos = 0;
for (;;) {
String attrName = "piece" + Integer.toString(pos + 1);
String attrVal = el.getAttribute(attrName);
if (attrVal == null) {
break;
}
int offpos = attrVal.indexOf(':');
Varnode newvn;
if (offpos == -1) {
Register register = cspec.getLanguage().getRegister(attrVal);
if (register == null) {
throw new XmlParseException("Unknown pentry register: " + attrVal);
}
newvn = new Varnode(register.getAddress(), register.getBitLength() / 8);
}
else {
int szpos = attrVal.indexOf(':', offpos + 1);
if (szpos == -1) {
throw new XmlParseException("join address piece attribute is malformed");
}
String spcname = attrVal.substring(0, offpos);
AddressSpace spc = cspec.getAddressSpace(spcname);
long off = SpecXmlUtils.decodeLong(attrVal.substring(offpos + 1, szpos));
long sz = SpecXmlUtils.decodeLong(attrVal.substring(szpos + 1));
newvn = new Varnode(spc.getAddress(off), (int) sz);
}
pieces.add(newvn);
sizesum += newvn.getSize();
pos += 1;
}
offset = 0; // This should be the offset assigned by the join space
size = sizesum; // Size is sum unless explicit attribute overwrites this with logical size
joinRecord = new Varnode[pieces.size()];
pieces.toArray(joinRecord);
}
/**
* Write this sized address as an \<addr> XML tag.
* @param buffer is the buffer to write to
*/
public void saveXml(StringBuilder buffer) {
if (joinRecord != null) {
long logicalSize = size;
long sizeSum = 0;
for (Varnode vn : joinRecord) {
sizeSum += vn.getSize();
}
if (sizeSum == size) {
logicalSize = 0;
}
buildXML(buffer, joinRecord, logicalSize);
return;
}
buffer.append("<addr");
if (space != null) {
SpecXmlUtils.encodeStringAttribute(buffer, "space", space.getName());
SpecXmlUtils.encodeUnsignedIntegerAttribute(buffer, "offset", offset);
if (size != 0) {
SpecXmlUtils.encodeSignedIntegerAttribute(buffer, "size", size);
}
}
buffer.append("/>");
}
/**
* Restore an Address (as an AddressSpace and an offset) and an optional size from XML tag.
* The tag can have any name, but it must either have:
* - A "name" attribute, indicating a register name OR
* - A "space" and "offset" attribute, indicating the address space and offset
*
* If a register name is given, size is obtained from the register. If an offset is
* given, the size can optionally be specified using a "size" attribute.
* If not explicitly described, the size is set to zero.
*
* This method supports the "join" address space attached to the compiler specification
* @param el is the XML tag
* @param cspec is the compiler spec for looking up registers
* @return an AddressXML object containing the recovered space,offset,size
* @throws XmlParseException for problems parsing
*/
public static AddressXML restoreXml(XmlElement el, CompilerSpec cspec)
throws XmlParseException {
AddressXML result;
if (el.getName().equals("register")) {
String regName = el.getAttribute("name");
if (regName == null) {
throw new XmlParseException("Missing pentry register name");
}
Register register = cspec.getLanguage().getRegister(regName);
if (register == null) {
throw new XmlParseException("Unknown pentry register: " + regName);
}
result = new AddressXML(register.getAddressSpace(), register.getOffset(),
register.getMinimumByteSize());
}
else {
result = new AddressXML();
result.size = 0;
String spaceName = el.getAttribute("space");
result.space = cspec.getAddressSpace(spaceName);
if (result.space == null) {
throw new XmlParseException("Unknown address space: " + spaceName);
}
if (result.space.getType() == AddressSpace.TYPE_JOIN) {
result.readJoinXML(el, cspec);
}
else {
result.offset = SpecXmlUtils.decodeLong(el.getAttribute("offset"));
}
String sizeString = el.getAttribute("size");
if (sizeString != null) {
result.size = SpecXmlUtils.decodeInt(sizeString);
}
}
return result;
}
/**
* Restore an Address (as an AddressSpace and an offset) and an optional size from XML tag.
* The tag can have any name, but it must either have:
* - A "name" attribute, indicating a register name OR
* - A "space" and "offset" attribute, indicating the address space and offset
*
* If a register name is given, size is obtained from the register. If an offset is
* given, the size can optionally be specified using a "size" attribute.
* If not explicitly described, the size is set to zero.
* @param el is the XML tag
* @param language is the processor language for looking up registers and address spaces
* @return an AddressXML object containing the recovered space,offset,size
* @throws XmlParseException for problems parsing
*/
public static AddressXML restoreXml(XmlElement el, Language language) throws XmlParseException {
AddressXML result;
if (el.getName().equals("register")) {
String regName = el.getAttribute("name");
if (regName == null) {
throw new XmlParseException("Missing register name");
}
Register register = language.getRegister(regName);
if (register == null) {
throw new XmlParseException("Unknown register: " + regName);
}
result = new AddressXML(register.getAddressSpace(), register.getOffset(),
register.getMinimumByteSize());
}
else {
result = new AddressXML();
result.size = 0;
String spaceName = el.getAttribute("space");
result.space = language.getAddressFactory().getAddressSpace(spaceName);
if (result.space == null) {
throw new XmlParseException("Unknown address space: " + spaceName);
}
result.offset = SpecXmlUtils.decodeLong(el.getAttribute("offset"));
String sizeString = el.getAttribute("size");
if (sizeString != null) {
result.size = SpecXmlUtils.decodeInt(sizeString);
}
}
return result;
}
/**
* A memory range is read from attributes of an XML tag. The tag must either have:
* - "name" attribute - indicating a register
* - "space" attribute - with optional "first" and "last" attributes
*
* With the "space" attribute, "first" defaults to 0 and "last" defaults to the last offset in the space.
* @param el is the XML element
* @param cspec is a compiler spec to resolve address spaces and registers
* @return an AddressXML object representing the range
* @throws XmlParseException if the XML is badly formed
*/
public static AddressXML restoreRangeXml(XmlElement el, CompilerSpec cspec)
throws XmlParseException {
AddressXML result = new AddressXML();
result.offset = 0;
long last = -1;
boolean seenLast = false;
String attrvalue = el.getAttribute("space");
if (attrvalue != null) {
result.space = cspec.getAddressSpace(attrvalue);
if (result.space == null) {
throw new XmlParseException("Undefined space: " + attrvalue);
}
}
attrvalue = el.getAttribute("first");
if (attrvalue != null) {
result.offset = SpecXmlUtils.decodeLong(attrvalue);
}
attrvalue = el.getAttribute("last");
if (attrvalue != null) {
last = SpecXmlUtils.decodeLong(attrvalue);
seenLast = true;
}
attrvalue = el.getAttribute("name");
if (attrvalue != null) {
Register register = cspec.getLanguage().getRegister(attrvalue);
result.space = register.getAddressSpace();
result.offset = register.getOffset();
last = (result.offset - 1) + register.getMinimumByteSize();
seenLast = true;
}
if (result.space == null) {
throw new XmlParseException("No address space indicated in range tag");
}
if (!seenLast) {
last = result.space.getMaxAddress().getOffset();
}
result.size = (last - result.offset) + 1;
return result;
}
/**
* @return the space associated of this address
*/
public final AddressSpace getAddressSpace() {
return space;
}
/**
* @return the byte offset of this address
*/
public final long getOffset() {
return offset;
}
/**
* @return the size in bytes associated with this address
*/
public final long getSize() {
return size;
}
/**
* Get the array of physical pieces making up this logical address range, if
* the range is in the JOIN address space. Otherwise return null.
* @return the physical pieces or null
*/
public final Varnode[] getJoinRecord() {
return joinRecord;
}
/**
* Build a raw Varnode from the Address and size
* @return the new Varnode
*/
public Varnode getVarnode() {
Address addr = space.getAddress(offset);
return new Varnode(addr, (int) size);
}
/**
* @return the first address in the range
*/
public Address getFirstAddress() {
return space.getAddress(offset);
}
/**
* @return the last address in the range
*/
public Address getLastAddress() {
return space.getAddress(offset + size - 1);
}
/**
* Parse String containing an XML tag representing an Address.
* The format options are simple enough that we don't try to invoke
* an actual XML parser but just walk the string. This recognizes
* - \<addr>
* - \<spaceid> or
* - any tag with a "space" and "offset" attribute
*
* @param addrstring is the string containing the XML tag
* @param addrfactory is the factory that can produce addresses
* @return the created Address or Address.NO_ADDRESS in some special cases
* @throws PcodeXMLException for a badly formed Address
*/
public static Address readXML(String addrstring, AddressFactory addrfactory)
throws PcodeXMLException {
int tagstart = addrstring.indexOf('<');
if (tagstart >= 0) {
tagstart += 1;
if (addrstring.startsWith("spaceid", tagstart)) {
tagstart += 8;
int attrstart = addrstring.indexOf("name=\"", tagstart);
if (attrstart >= 0) {
attrstart += 6;
int nameend = addrstring.indexOf('\"', attrstart);
if (nameend >= 0) {
AddressSpace spc =
addrfactory.getAddressSpace(addrstring.substring(attrstart, nameend));
int spaceid = spc.getSpaceID();
spc = addrfactory.getConstantSpace();
return spc.getAddress(spaceid);
}
}
}
// There are several tag forms where we essentially want to just look for 'space' and 'offset' attributes
// don't explicitly check the tag name
int spacestart = addrstring.indexOf("space=\"");
if (spacestart >= 4) {
spacestart += 7;
int spaceend = addrstring.indexOf('"', spacestart);
if (spaceend >= spacestart) {
String spcname = addrstring.substring(spacestart, spaceend);
int offstart = addrstring.indexOf("offset=\"");
if (offstart >= 4) {
offstart += 8;
int offend = addrstring.indexOf('"', offstart);
if (offend >= offstart) {
String offstr = addrstring.substring(offstart, offend);
AddressSpace spc = addrfactory.getAddressSpace(spcname);
// Unknown spaces may result from "spacebase" registers defined in cspec
if (spc == null) {
return Address.NO_ADDRESS;
}
long offset = SpecXmlUtils.decodeLong(offstr);
return spc.getAddress(offset);
}
}
}
}
}
throw new PcodeXMLException("Badly formed address: " + addrstring);
}
/**
* Read the (first) size attribute from an XML tag string as an integer
* @param addrxml is the XML string
* @return the decoded integer or zero if the attribute doesn't exist
*/
public static int readXMLSize(String addrxml) {
int attrstart = addrxml.indexOf("size=\"");
if (attrstart >= 4) {
attrstart += 6;
int attrend = addrxml.indexOf('\"', attrstart);
if (attrend > attrstart) {
int size = SpecXmlUtils.decodeInt(addrxml.substring(attrstart, attrend));
return size;
}
}
return 0;
}
/**
* Create an address from an XML parse tree node. This recognizes XML tags
* - \<addr>
* - \<spaceid>
* - \<iop> or
* - any tag with "space" and "offset" attributes
*
* An empty \<addr> tag, with no attributes, results in Address.NO_ADDRESS being returned.
* @param el is the parse tree element
* @param addrFactory address factory used to create valid addresses
* @return Address created from XML info
*/
public static Address readXML(XmlElement el, AddressFactory addrFactory) {
String localName = el.getName();
if (localName.equals("spaceid")) {
AddressSpace spc = addrFactory.getAddressSpace(el.getAttribute("name"));
int spaceid = spc.getSpaceID();
spc = addrFactory.getConstantSpace();
return spc.getAddress(spaceid);
}
else if (localName.equals("iop")) {
int ref = SpecXmlUtils.decodeInt(el.getAttribute("value"));
AddressSpace spc = addrFactory.getConstantSpace();
return spc.getAddress(ref);
}
String space = el.getAttribute("space");
if (space == null) {
return Address.NO_ADDRESS;
}
long offset = SpecXmlUtils.decodeLong(el.getAttribute("offset"));
AddressSpace spc = addrFactory.getAddressSpace(space);
if (spc == null) {
return null;
}
return spc.getAddress(offset);
}
/**
* Read an Address given an XML tag name and its attributes. This recognizes XML tags
* - \<addr>
* - \<spaceid>
* - \<iop>
* - any tag with "space" or "offset" attributes
*
* An empty \<addr> tag, with no attributes, results in Address.NO_ADDRESS being returned.
* @param localName is the name of the tag
* @param attr is the collection of attributes for the tag
* @param addrFactory is an Address factory
* @return the scanned address
*/
public static Address readXML(String localName, Attributes attr, AddressFactory addrFactory) {
if (localName.equals("spaceid")) {
AddressSpace spc = addrFactory.getAddressSpace(attr.getValue("name"));
int spaceid = spc.getSpaceID();
spc = addrFactory.getConstantSpace();
return spc.getAddress(spaceid);
}
else if (localName.equals("iop")) {
int ref = SpecXmlUtils.decodeInt(attr.getValue("value"));
AddressSpace spc = addrFactory.getConstantSpace();
return spc.getAddress(ref);
}
String space = attr.getValue("space");
if (space == null) {
return Address.NO_ADDRESS;
}
long offset = SpecXmlUtils.decodeLong(attr.getValue("offset"));
AddressSpace spc = addrFactory.getAddressSpace(space);
if (spc == null) {
return Address.NO_ADDRESS;
}
return spc.getAddress(offset);
}
/**
* Append "space" and "offset" attributes describing the given Address to the XML stream.
* This assumes the XML tag name has already been emitted.
* @param buf is the XML stream
* @param addr is the given Address
*/
public static void appendAttributes(StringBuilder buf, Address addr) {
AddressSpace space = addr.getAddressSpace();
if (space.isOverlaySpace()) {
if (space.getType() != AddressSpace.TYPE_OTHER) {
space = space.getPhysicalSpace();
addr = space.getAddress(addr.getOffset());
}
}
SpecXmlUtils.encodeStringAttribute(buf, "space", space.getName());
SpecXmlUtils.encodeUnsignedIntegerAttribute(buf, "offset", addr.getUnsignedOffset());
}
/**
* Append "space" "offset" and "size" attributes describing the given memory range to the XML stream.
* This assumes the XML tag name has already been emitted.
* @param buf is the XML stream
* @param addr is the starting Address of the memory range
* @param size is the size of the memory range
*/
public static void appendAttributes(StringBuilder buf, Address addr, int size) {
AddressSpace space = addr.getAddressSpace();
if (space.isOverlaySpace()) {
if (space.getType() != AddressSpace.TYPE_OTHER) {
space = space.getPhysicalSpace();
addr = space.getAddress(addr.getOffset());
}
}
SpecXmlUtils.encodeStringAttribute(buf, "space", space.getName());
SpecXmlUtils.encodeUnsignedIntegerAttribute(buf, "offset", addr.getUnsignedOffset());
SpecXmlUtils.encodeSignedIntegerAttribute(buf, "size", size);
}
/**
* Append a memory range, as "space", "first", and "last" attributes, to the XML stream.
* This assumes the XML tag name has already been emitted.
* @param buffer is the XML stream
* @param startAddr is the first address in the range
* @param endAddr is the last address in the range
*/
public static void appendAttributes(StringBuilder buffer, Address startAddr, Address endAddr) {
AddressSpace space = startAddr.getAddressSpace();
long offset = startAddr.getOffset();
long size = endAddr.getOffset() - offset + 1;
if (space != endAddr.getAddressSpace()) {
throw new IllegalArgumentException(
"Range boundaries are not in the same address space");
}
if (size < 0) {
throw new IllegalArgumentException("Start of range comes after end of range");
}
long last = offset + size - 1;
boolean useFirst = (offset != 0);
boolean useLast = (last != -1);
SpecXmlUtils.encodeStringAttribute(buffer, "space", space.getName());
if (useFirst) {
SpecXmlUtils.encodeUnsignedIntegerAttribute(buffer, "first", offset);
}
if (useLast) {
SpecXmlUtils.encodeUnsignedIntegerAttribute(buffer, "last", last);
}
}
/**
* Write out the given Address as an \<addr> tag to the XML stream
*
* @param buf is the XML stream
* @param addr -- Address to convert to XML
*/
public static void buildXML(StringBuilder buf, Address addr) {
if ((addr == null) || (addr == Address.NO_ADDRESS)) {
buf.append("<addr/>");
return;
}
buf.append("<addr");
AddressXML.appendAttributes(buf, addr);
buf.append("/>");
}
/**
* Write out the given Address and a size as an \<addr> tag to the XML stream
*
* @param buf is the XML stream
* @param addr is the given Address
* @param size is the given size
*/
public static void buildXML(StringBuilder buf, Address addr, int size) {
buf.append("<addr");
AddressXML.appendAttributes(buf, addr, size);
buf.append("/>");
}
private static void buildVarnodePiece(StringBuilder buf, Address addr, int size) {
AddressSpace space = addr.getAddressSpace();
if (space.isOverlaySpace()) {
space = space.getPhysicalSpace();
addr = space.getAddress(addr.getOffset());
}
buf.append(space.getName());
buf.append(":0x");
long off = addr.getUnsignedOffset();
buf.append(Long.toHexString(off));
buf.append(':');
buf.append(Integer.toString(size));
}
/**
* Write out a sequence of Varnodes as a single \<addr> tag to an XML stream.
* If there is more than one Varnode, or if the logical size is non-zero,
* the \<addr> tag will specify the address space as "join" and will have
* additional "piece" attributes.
*
* @param buf is the XML stream
* @param varnodes is the sequence of storage varnodes
* @param logicalsize is the logical size value of the varnode
*/
public static void buildXML(StringBuilder buf, Varnode[] varnodes, long logicalsize) {
if (varnodes == null) {
buf.append("<addr/>");
return;
}
if ((varnodes.length == 1) && (logicalsize == 0)) {
AddressXML.buildXML(buf, varnodes[0].getAddress(), varnodes[0].getSize());
return;
}
buf.append("<addr space=\"join\"");
int piece = 0;
for (Varnode vn : varnodes) {
buf.append(" piece");
buf.append(Integer.toString(++piece));
buf.append("=\"");
buildVarnodePiece(buf, vn.getAddress(), vn.getSize());
buf.append("\"");
}
if (logicalsize != 0) {
buf.append(" logicalsize=\"").append(logicalsize).append('\"');
}
buf.append("/>");
}
}

View file

@ -190,8 +190,9 @@ public class FunctionPrototype {
} }
// if the callfixup has no fallthru, set the noreturn property too // if the callfixup has no fallthru, set the noreturn property too
Program program = f.getProgram(); Program program = f.getProgram();
InjectPayload callFixup = program.getCompilerSpec().getPcodeInjectLibrary().getPayload( InjectPayload callFixup = program.getCompilerSpec()
InjectPayload.CALLFIXUP_TYPE, fixupname, program, null); .getPcodeInjectLibrary()
.getPayload(InjectPayload.CALLFIXUP_TYPE, fixupname);
if (callFixup == null) { if (callFixup == null) {
return false; return false;
} }
@ -369,8 +370,8 @@ public class FunctionPrototype {
if (sz != returnstorage.size()) { // If the sizes do no match if (sz != returnstorage.size()) { // If the sizes do no match
logicalsize = sz; // force the logical size on the varnode logicalsize = sz; // force the logical size on the varnode
} }
String addrstring = Varnode.buildXMLAddress(returnstorage.getVarnodes(), logicalsize); AddressXML.buildXML(res, returnstorage.getVarnodes(), logicalsize);
res.append(addrstring).append("\n "); res.append("\n ");
} }
else { else {
// Decompiler will use model for storage // Decompiler will use model for storage

View file

@ -61,7 +61,7 @@ public class HighExternalSymbol extends HighSymbol {
SpecXmlUtils.xmlEscapeAttribute(buf, "name", name + "_exref"); SpecXmlUtils.xmlEscapeAttribute(buf, "name", name + "_exref");
} }
buf.append(">\n"); buf.append(">\n");
buf.append(Varnode.buildXMLAddress(resolveAddress)); AddressXML.buildXML(buf, resolveAddress);
buf.append("</externrefsymbol>\n"); buf.append("</externrefsymbol>\n");
} }
} }

View file

@ -183,7 +183,7 @@ public class HighFunction extends PcodeSyntaxTree {
JumpTable jumpTab = JumpTable.readOverride((Namespace) obj, symtab); JumpTable jumpTab = JumpTable.readOverride((Namespace) obj, symtab);
if (jumpTab != null) { if (jumpTab != null) {
if (jumpTables == null) { if (jumpTables == null) {
jumpTables = new ArrayList<JumpTable>(); jumpTables = new ArrayList<>();
} }
jumpTables.add(jumpTab); jumpTables.add(jumpTab);
} }
@ -194,7 +194,7 @@ public class HighFunction extends PcodeSyntaxTree {
DataTypeSymbol protover = HighFunctionDBUtil.readOverride(sym); DataTypeSymbol protover = HighFunctionDBUtil.readOverride(sym);
if (protover != null) { if (protover != null) {
if (protoOverrides == null) { if (protoOverrides == null) {
protoOverrides = new ArrayList<DataTypeSymbol>(); protoOverrides = new ArrayList<>();
} }
protoOverrides.add(protover); protoOverrides.add(protover);
} }
@ -259,14 +259,13 @@ public class HighFunction extends PcodeSyntaxTree {
XmlElement start = parser.start("function"); XmlElement start = parser.start("function");
String name = start.getAttribute("name"); String name = start.getAttribute("name");
if (!func.getName().equals(name)) { if (!func.getName().equals(name)) {
throw new PcodeXMLException( throw new PcodeXMLException("Function name mismatch: " + func.getName() + " + " + name);
"Function name mismatch: " + func.getName() + " + " + name);
} }
while (!parser.peek().isEnd()) { while (!parser.peek().isEnd()) {
XmlElement subel = parser.peek(); XmlElement subel = parser.peek();
if (subel.getName().equals("addr")) { if (subel.getName().equals("addr")) {
subel = parser.start("addr"); subel = parser.start("addr");
Address addr = Varnode.readXMLAddress(subel, getAddressFactory()); Address addr = AddressXML.readXML(subel, getAddressFactory());
parser.end(subel); parser.end(subel);
addr = func.getEntryPoint().getAddressSpace().getOverlayAddress(addr); addr = func.getEntryPoint().getAddressSpace().getOverlayAddress(addr);
if (!func.getEntryPoint().equals(addr)) { if (!func.getEntryPoint().equals(addr)) {
@ -318,7 +317,7 @@ public class HighFunction extends PcodeSyntaxTree {
table.restoreXml(parser, getAddressFactory()); table.restoreXml(parser, getAddressFactory());
if (!table.isEmpty()) { if (!table.isEmpty()) {
if (jumpTables == null) { if (jumpTables == null) {
jumpTables = new ArrayList<JumpTable>(); jumpTables = new ArrayList<>();
} }
jumpTables.add(table); jumpTables.add(table);
} }
@ -352,8 +351,8 @@ public class HighFunction extends PcodeSyntaxTree {
*/ */
public HighVariable splitOutMergeGroup(HighVariable high, Varnode vn) throws PcodeException { public HighVariable splitOutMergeGroup(HighVariable high, Varnode vn) throws PcodeException {
try { try {
ArrayList<Varnode> newinst = new ArrayList<Varnode>(); ArrayList<Varnode> newinst = new ArrayList<>();
ArrayList<Varnode> oldinst = new ArrayList<Varnode>(); ArrayList<Varnode> oldinst = new ArrayList<>();
short ourgroup = vn.getMergeGroup(); short ourgroup = vn.getMergeGroup();
Varnode[] curinst = high.getInstances(); Varnode[] curinst = high.getInstances();
for (Varnode curvn : curinst) { for (Varnode curvn : curinst) {
@ -457,10 +456,10 @@ public class HighFunction extends PcodeSyntaxTree {
} }
resBuf.append(">\n"); resBuf.append(">\n");
if (entryPoint == null) { if (entryPoint == null) {
resBuf.append(Varnode.buildXMLAddress(func.getEntryPoint())); AddressXML.buildXML(resBuf, func.getEntryPoint());
} }
else { else {
resBuf.append(Varnode.buildXMLAddress(entryPoint)); // Address is forced on XML AddressXML.buildXML(resBuf, entryPoint); // Address is forced on XML
} }
localSymbols.buildLocalDbXML(resBuf, namespace); localSymbols.buildLocalDbXML(resBuf, namespace);
proto.buildPrototypeXML(resBuf, getDataTypeManager()); proto.buildPrototypeXML(resBuf, getDataTypeManager());
@ -482,9 +481,7 @@ public class HighFunction extends PcodeSyntaxTree {
FunctionPrototype fproto = new FunctionPrototype( FunctionPrototype fproto = new FunctionPrototype(
(FunctionSignature) sym.getDataType(), compilerSpec, false); (FunctionSignature) sym.getDataType(), compilerSpec, false);
resBuf.append("<protooverride>\n"); resBuf.append("<protooverride>\n");
resBuf.append("<addr"); AddressXML.buildXML(resBuf, addr);
Varnode.appendSpaceOffset(resBuf, addr);
resBuf.append("/>\n");
fproto.buildPrototypeXML(resBuf, dtmanage); fproto.buildPrototypeXML(resBuf, dtmanage);
resBuf.append("</protooverride>\n"); resBuf.append("</protooverride>\n");
} }
@ -560,8 +557,8 @@ public class HighFunction extends PcodeSyntaxTree {
public static boolean clearNamespace(SymbolTable symtab, Namespace space) public static boolean clearNamespace(SymbolTable symtab, Namespace space)
throws InvalidInputException { throws InvalidInputException {
SymbolIterator iter = symtab.getSymbols(space); SymbolIterator iter = symtab.getSymbols(space);
ArrayList<Address> addrlist = new ArrayList<Address>(); ArrayList<Address> addrlist = new ArrayList<>();
ArrayList<String> namelist = new ArrayList<String>(); ArrayList<String> namelist = new ArrayList<>();
while (iter.hasNext()) { while (iter.hasNext()) {
Symbol sym = iter.next(); Symbol sym = iter.next();
if (!(sym instanceof CodeSymbol)) { if (!(sym instanceof CodeSymbol)) {
@ -638,7 +635,7 @@ public class HighFunction extends PcodeSyntaxTree {
static public void createNamespaceTag(StringBuilder buf, Namespace namespace) { static public void createNamespaceTag(StringBuilder buf, Namespace namespace) {
buf.append("<parent>\n"); buf.append("<parent>\n");
if (namespace != null) { if (namespace != null) {
ArrayList<Namespace> arr = new ArrayList<Namespace>(); ArrayList<Namespace> arr = new ArrayList<>();
Namespace curspc = namespace; Namespace curspc = namespace;
while (curspc != null) { while (curspc != null) {
arr.add(0, curspc); arr.add(0, curspc);

View file

@ -59,7 +59,7 @@ public class HighFunctionShellSymbol extends HighSymbol {
SpecXmlUtils.xmlEscapeAttribute(buf, "name", name); SpecXmlUtils.xmlEscapeAttribute(buf, "name", name);
SpecXmlUtils.encodeSignedIntegerAttribute(buf, "size", 1); SpecXmlUtils.encodeSignedIntegerAttribute(buf, "size", 1);
buf.append(">\n"); buf.append(">\n");
buf.append(Varnode.buildXMLAddress(getStorage().getMinAddress())); AddressXML.buildXML(buf, getStorage().getMinAddress());
buf.append("</function>\n"); buf.append("</function>\n");
} }
} }

View file

@ -46,8 +46,8 @@ public class HighParamID extends PcodeSyntaxTree {
private Address functionaddress; private Address functionaddress;
private String modelname; // Name of prototype model private String modelname; // Name of prototype model
private Integer protoextrapop; private Integer protoextrapop;
private List<ParamMeasure> inputlist = new ArrayList<ParamMeasure>(); private List<ParamMeasure> inputlist = new ArrayList<>();
private List<ParamMeasure> outputlist = new ArrayList<ParamMeasure>(); private List<ParamMeasure> outputlist = new ArrayList<>();
/** /**
* @param function function associated with the higher level function abstraction. * @param function function associated with the higher level function abstraction.
@ -107,7 +107,8 @@ public class HighParamID extends PcodeSyntaxTree {
} }
/** /**
* @return the specific of input for functionparams * @param i is the specific index to return
* @return the specific input for functionparams
*/ */
public ParamMeasure getInput(int i) { public ParamMeasure getInput(int i) {
return inputlist.get(i); return inputlist.get(i);
@ -121,6 +122,7 @@ public class HighParamID extends PcodeSyntaxTree {
} }
/** /**
* @param i is the index of the specific output
* @return the specific of output for functionparams * @return the specific of output for functionparams
*/ */
public ParamMeasure getOutput(int i) { public ParamMeasure getOutput(int i) {
@ -135,14 +137,14 @@ public class HighParamID extends PcodeSyntaxTree {
XmlElement start = parser.start("parammeasures"); XmlElement start = parser.start("parammeasures");
functionname = start.getAttribute("name"); functionname = start.getAttribute("name");
if (!func.getName().equals(functionname)) { if (!func.getName().equals(functionname)) {
throw new PcodeXMLException("Function name mismatch: " + func.getName() + throw new PcodeXMLException(
" + " + functionname); "Function name mismatch: " + func.getName() + " + " + functionname);
} }
while (!parser.peek().isEnd()) { while (!parser.peek().isEnd()) {
XmlElement subel = parser.peek(); XmlElement subel = parser.peek();
if (subel.getName().equals("addr")) { if (subel.getName().equals("addr")) {
subel = parser.start("addr"); subel = parser.start("addr");
functionaddress = Varnode.readXMLAddress(subel, getAddressFactory()); functionaddress = AddressXML.readXML(subel, getAddressFactory());
parser.end(subel); parser.end(subel);
functionaddress = functionaddress =
func.getEntryPoint().getAddressSpace().getOverlayAddress(functionaddress); func.getEntryPoint().getAddressSpace().getOverlayAddress(functionaddress);
@ -177,8 +179,10 @@ public class HighParamID extends PcodeSyntaxTree {
/** /**
* Read in the inputs or outputs list for this function from an XML rep * Read in the inputs or outputs list for this function from an XML rep
* @param el * @param parser is the XML parser
* @throws PcodeXMLException * @param pmlist is populated with the resulting list
* @param tag is the name of the tag
* @throws PcodeXMLException for improperly formed XML
*/ */
private void parseParamMeasureXML(XmlPullParser parser, List<ParamMeasure> pmlist, String tag) private void parseParamMeasureXML(XmlPullParser parser, List<ParamMeasure> pmlist, String tag)
throws PcodeXMLException { throws PcodeXMLException {
@ -191,7 +195,8 @@ public class HighParamID extends PcodeSyntaxTree {
parser.end(el); parser.end(el);
} }
public static ErrorHandler getErrorHandler(final Object errOriginator, final String targetName) { public static ErrorHandler getErrorHandler(final Object errOriginator,
final String targetName) {
return new ErrorHandler() { return new ErrorHandler() {
@Override @Override
public void error(SAXParseException exception) throws SAXException { public void error(SAXParseException exception) throws SAXException {
@ -216,9 +221,10 @@ public class HighParamID extends PcodeSyntaxTree {
* TODO: this probably doesn't belong here. * TODO: this probably doesn't belong here.
* *
* @param xml string to parse * @param xml string to parse
* @param handler is the error handler
* @return an XML tree element * @return an XML tree element
* *
* @throws PcodeXMLException * @throws PcodeXMLException for improper XML
*/ */
static public XmlPullParser stringTree(String xml, ErrorHandler handler) static public XmlPullParser stringTree(String xml, ErrorHandler handler)
throws PcodeXMLException { throws PcodeXMLException {
@ -235,6 +241,7 @@ public class HighParamID extends PcodeSyntaxTree {
/** /**
* Update any parameters for this Function from parameters defined in this map. * Update any parameters for this Function from parameters defined in this map.
* *
* @param storeDataTypes is true if data-types are getting stored
* @param srctype function signature source * @param srctype function signature source
*/ */
public void storeReturnToDatabase(boolean storeDataTypes, SourceType srctype) { public void storeReturnToDatabase(boolean storeDataTypes, SourceType srctype) {
@ -275,12 +282,13 @@ public class HighParamID extends PcodeSyntaxTree {
* Update any parameters for this Function from parameters defined in this map. * Update any parameters for this Function from parameters defined in this map.
* Originally from LocalSymbolMap, but being modified. * Originally from LocalSymbolMap, but being modified.
* *
* @param storeDataTypes is true if data-types are being stored
* @param srctype function signature source * @param srctype function signature source
*/ */
public void storeParametersToDatabase(boolean storeDataTypes, SourceType srctype) { public void storeParametersToDatabase(boolean storeDataTypes, SourceType srctype) {
PcodeDataTypeManager dtManage = getDataTypeManager(); PcodeDataTypeManager dtManage = getDataTypeManager();
try { try {
List<Variable> params = new ArrayList<Variable>(); List<Variable> params = new ArrayList<>();
for (ParamMeasure pm : inputlist) { for (ParamMeasure pm : inputlist) {
Varnode vn = pm.getVarnode(); Varnode vn = pm.getVarnode();
DataType dataType; DataType dataType;
@ -292,9 +300,7 @@ public class HighParamID extends PcodeSyntaxTree {
else { else {
dataType = dtManage.findUndefined(vn.getSize()); dataType = dtManage.findUndefined(vn.getSize());
} }
Variable v = Variable v = new ParameterImpl(null, dataType, buildStorage(vn), func.getProgram());
new ParameterImpl(null, dataType, buildStorage(vn),
func.getProgram());
//Msg.debug(this, "function(" + func.getName() + ")--param: " + v.toString() + //Msg.debug(this, "function(" + func.getName() + ")--param: " + v.toString() +
// " -- type: " + dataType.getName()); // " -- type: " + dataType.getName());
params.add(v); params.add(v);
@ -302,7 +308,7 @@ public class HighParamID extends PcodeSyntaxTree {
func.updateFunction(modelname, null, params, func.updateFunction(modelname, null, params,
FunctionUpdateType.DYNAMIC_STORAGE_ALL_PARAMS, true, srctype); FunctionUpdateType.DYNAMIC_STORAGE_ALL_PARAMS, true, srctype);
if ( !paramStorageMatches(func, params)) { if (!paramStorageMatches(func, params)) {
// try again if dynamic storage assignment does not match decompiler's // try again if dynamic storage assignment does not match decompiler's
// force into custom storage mode // force into custom storage mode
func.updateFunction(modelname, null, params, FunctionUpdateType.CUSTOM_STORAGE, func.updateFunction(modelname, null, params, FunctionUpdateType.CUSTOM_STORAGE,

View file

@ -38,8 +38,7 @@ public class JumpTable {
/** /**
* Translate address into preferred memory space (JumpTable.preferredSpace) * Translate address into preferred memory space (JumpTable.preferredSpace)
* @param addr * @param addr is the given Address
* @param preferredSpace
* @return preferred address or original addr * @return preferred address or original addr
*/ */
private Address translateOverlayAddress(Address addr) { private Address translateOverlayAddress(Address addr) {
@ -84,7 +83,7 @@ public class JumpTable {
size = SpecXmlUtils.decodeInt(el.getAttribute("size")); size = SpecXmlUtils.decodeInt(el.getAttribute("size"));
num = SpecXmlUtils.decodeInt(el.getAttribute("num")); num = SpecXmlUtils.decodeInt(el.getAttribute("num"));
XmlElement subel = parser.start("addr"); XmlElement subel = parser.start("addr");
addr = translateOverlayAddress(Varnode.readXMLAddress(subel, addrFactory)); addr = translateOverlayAddress(AddressXML.readXML(subel, addrFactory));
parser.end(subel); parser.end(subel);
parser.end(el); parser.end(el);
} }
@ -106,7 +105,7 @@ public class JumpTable {
buf.append("<basicoverride>\n"); buf.append("<basicoverride>\n");
for (Address element : destlist) { for (Address element : destlist) {
buf.append("<dest"); buf.append("<dest");
Varnode.appendSpaceOffset(buf, element); AddressXML.appendAttributes(buf, element);
buf.append("/>\n"); buf.append("/>\n");
} }
// We could add <normaddr> and <normhash> tags to specify switch variable // We could add <normaddr> and <normhash> tags to specify switch variable
@ -150,41 +149,42 @@ public class JumpTable {
} }
public boolean isEmpty() { public boolean isEmpty() {
if (addressTable == null) if (addressTable == null) {
return true; return true;
if (addressTable.length == 0) }
if (addressTable.length == 0) {
return true; return true;
}
return false; return false;
} }
/** /**
* Create a JumpTable object by parsing the XML elements * Create a JumpTable object by parsing the XML elements
* @param parser * @param parser is the XML parser
* @param addrFactory * @param addrFactory is used to look-up address spaces
* @throws PcodeXMLException * @throws PcodeXMLException for improperly formed XML
*/ */
public void restoreXml(XmlPullParser parser, AddressFactory addrFactory) public void restoreXml(XmlPullParser parser, AddressFactory addrFactory)
throws PcodeXMLException { throws PcodeXMLException {
XmlElement el = parser.start("jumptable"); XmlElement el = parser.start("jumptable");
try { try {
ArrayList<Address> aTable = new ArrayList<Address>(); ArrayList<Address> aTable = new ArrayList<>();
ArrayList<Integer> lTable = new ArrayList<Integer>(); ArrayList<Integer> lTable = new ArrayList<>();
ArrayList<LoadTable> ldTable = new ArrayList<LoadTable>(); ArrayList<LoadTable> ldTable = new ArrayList<>();
if (!parser.peek().isStart()) { // Empty jumptable if (!parser.peek().isStart()) { // Empty jumptable
return; return;
} }
XmlElement addrel = parser.start("addr"); XmlElement addrel = parser.start("addr");
Address switchAddr = Address switchAddr = translateOverlayAddress(AddressXML.readXML(addrel, addrFactory));
translateOverlayAddress(Varnode.readXMLAddress(addrel, addrFactory));
parser.end(addrel); parser.end(addrel);
while (parser.peek().isStart()) { while (parser.peek().isStart()) {
if (parser.peek().getName().equals("dest")) { if (parser.peek().getName().equals("dest")) {
XmlElement subel = parser.start("dest"); XmlElement subel = parser.start("dest");
Address caseAddr = Address caseAddr =
translateOverlayAddress(Varnode.readXMLAddress(subel, addrFactory)); translateOverlayAddress(AddressXML.readXML(subel, addrFactory));
aTable.add(caseAddr); aTable.add(caseAddr);
String slabel = subel.getAttribute("label"); String slabel = subel.getAttribute("label");
if (slabel != null) { if (slabel != null) {
@ -198,9 +198,10 @@ public class JumpTable {
loadtable.restoreXml(parser, addrFactory); loadtable.restoreXml(parser, addrFactory);
ldTable.add(loadtable); ldTable.add(loadtable);
} }
else else {
parser.discardSubTree(); parser.discardSubTree();
} }
}
opAddress = switchAddr; opAddress = switchAddr;
addressTable = new Address[aTable.size()]; addressTable = new Address[aTable.size()];
@ -217,18 +218,18 @@ public class JumpTable {
public void buildXml(StringBuilder buf) { public void buildXml(StringBuilder buf) {
buf.append("<jumptable>\n"); buf.append("<jumptable>\n");
buf.append("<addr"); AddressXML.buildXML(buf, opAddress);
Varnode.appendSpaceOffset(buf, opAddress); buf.append('\n');
buf.append("/>\n");
if (addressTable != null) { if (addressTable != null) {
for (Address element : addressTable) { for (Address element : addressTable) {
buf.append("<dest"); buf.append("<dest");
Varnode.appendSpaceOffset(buf, element); AddressXML.appendAttributes(buf, element);
buf.append("/>\n"); buf.append("/>\n");
} }
} }
if (override != null) if (override != null) {
override.buildXml(buf); override.buildXml(buf);
}
buf.append("</jumptable>\n"); buf.append("</jumptable>\n");
} }
@ -249,24 +250,29 @@ public class JumpTable {
} }
public void writeOverride(Function func) throws InvalidInputException { public void writeOverride(Function func) throws InvalidInputException {
if (override == null) if (override == null) {
throw new InvalidInputException("Jumptable is not an override"); throw new InvalidInputException("Jumptable is not an override");
}
Address[] destlist = override.getDestinations(); Address[] destlist = override.getDestinations();
if (destlist.length == 0) if (destlist.length == 0) {
throw new InvalidInputException("Jumptable has no destinations"); throw new InvalidInputException("Jumptable has no destinations");
if (!func.getBody().contains(opAddress)) }
if (!func.getBody().contains(opAddress)) {
throw new InvalidInputException("Switch is not in function body"); throw new InvalidInputException("Switch is not in function body");
}
Program program = func.getProgram(); Program program = func.getProgram();
SymbolTable symtab = program.getSymbolTable(); SymbolTable symtab = program.getSymbolTable();
Namespace space = HighFunction.findCreateOverrideSpace(func); Namespace space = HighFunction.findCreateOverrideSpace(func);
if (space == null) if (space == null) {
throw new InvalidInputException("Could not create \"override\" namespace"); throw new InvalidInputException("Could not create \"override\" namespace");
}
space = HighFunction.findCreateNamespace(symtab, space, "jmp_" + opAddress.toString()); space = HighFunction.findCreateNamespace(symtab, space, "jmp_" + opAddress.toString());
if (!HighFunction.clearNamespace(symtab, space)) if (!HighFunction.clearNamespace(symtab, space)) {
throw new InvalidInputException( throw new InvalidInputException(
"Jumptable override namespace contains non-label symbols."); "Jumptable override namespace contains non-label symbols.");
}
HighFunction.createLabelSymbol(symtab, opAddress, "switch", space, SourceType.USER_DEFINED, HighFunction.createLabelSymbol(symtab, opAddress, "switch", space, SourceType.USER_DEFINED,
false); false);
@ -279,20 +285,24 @@ public class JumpTable {
public static JumpTable readOverride(Namespace space, SymbolTable symtab) { public static JumpTable readOverride(Namespace space, SymbolTable symtab) {
Address branchind = null; Address branchind = null;
ArrayList<Address> destlist = new ArrayList<Address>(); ArrayList<Address> destlist = new ArrayList<>();
SymbolIterator iter = symtab.getSymbols(space); SymbolIterator iter = symtab.getSymbols(space);
while (iter.hasNext()) { while (iter.hasNext()) {
Symbol sym = iter.next(); Symbol sym = iter.next();
if (!(sym instanceof CodeSymbol)) if (!(sym instanceof CodeSymbol)) {
continue; continue;
}
Address addr = sym.getAddress(); Address addr = sym.getAddress();
if (sym.getName().equals("switch")) if (sym.getName().equals("switch")) {
branchind = addr; branchind = addr;
else if (sym.getName().startsWith("case")) }
else if (sym.getName().startsWith("case")) {
destlist.add(addr); destlist.add(addr);
} }
if ((branchind != null) && (destlist.size() > 0)) }
if ((branchind != null) && (destlist.size() > 0)) {
return new JumpTable(branchind, destlist, true); return new JumpTable(branchind, destlist, true);
}
return null; return null;
} }

View file

@ -65,7 +65,7 @@ public class MappedEntry extends SymbolEntry {
"Invalid symbol 0-sized data-type: " + symbol.type.getName()); "Invalid symbol 0-sized data-type: " + symbol.type.getName());
} }
try { try {
Address varAddr = Varnode.readXMLAddress(addrel, addrFactory); Address varAddr = AddressXML.readXML(addrel, addrFactory);
AddressSpace spc = varAddr.getAddressSpace(); AddressSpace spc = varAddr.getAddressSpace();
if ((spc == null) || (spc.getType() != AddressSpace.TYPE_VARIABLE)) { if ((spc == null) || (spc.getType() != AddressSpace.TYPE_VARIABLE)) {
storage = new VariableStorage(program, varAddr, sz); storage = new VariableStorage(program, varAddr, sz);
@ -89,8 +89,7 @@ public class MappedEntry extends SymbolEntry {
if (typeLength != storage.size() && symbol.type instanceof AbstractFloatDataType) { if (typeLength != storage.size() && symbol.type instanceof AbstractFloatDataType) {
logicalsize = typeLength; // Force a logicalsize logicalsize = typeLength; // Force a logicalsize
} }
String addrRes = Varnode.buildXMLAddress(storage.getVarnodes(), logicalsize); AddressXML.buildXML(buf, storage.getVarnodes(), logicalsize);
buf.append(addrRes);
buildRangelistXML(buf); buildRangelistXML(buf);
} }

View file

@ -100,7 +100,7 @@ public class PcodeDataTypeManager {
progDataTypes = prog.getDataTypeManager(); progDataTypes = prog.getDataTypeManager();
dataOrganization = progDataTypes.getDataOrganization(); dataOrganization = progDataTypes.getDataOrganization();
voidInputIsVarargs = true; // By default, do not lock-in void parameter lists voidInputIsVarargs = true; // By default, do not lock-in void parameter lists
displayLanguage = prog.getCompilerSpec().getDecompilerOutputLanguage(prog); displayLanguage = prog.getCompilerSpec().getDecompilerOutputLanguage();
if (displayLanguage != DecompilerLanguage.C_LANGUAGE) { if (displayLanguage != DecompilerLanguage.C_LANGUAGE) {
voidInputIsVarargs = false; voidInputIsVarargs = false;
} }

View file

@ -17,8 +17,6 @@ package ghidra.program.model.pcode;
import java.util.Iterator; import java.util.Iterator;
import org.xml.sax.Attributes;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.lang.Language; import ghidra.program.model.lang.Language;
import ghidra.program.model.lang.Register; import ghidra.program.model.lang.Register;
@ -123,22 +121,22 @@ public class Varnode {
/** /**
* Determine if this varnode contains the specified address * Determine if this varnode contains the specified address
* @param address the address for which to check * @param addr the address for which to check
* @return true if this varnode contains the specified address * @return true if this varnode contains the specified address
*/ */
public boolean contains(Address address) { public boolean contains(Address addr) {
if (spaceID != address.getAddressSpace().getSpaceID()) { if (spaceID != addr.getAddressSpace().getSpaceID()) {
return false; return false;
} }
if (isConstant() || isUnique() || isHash()) { if (isConstant() || isUnique() || isHash()) {
// this is not really a valid use case // this is not really a valid use case
return offset == address.getOffset(); return offset == addr.getOffset();
} }
long endOffset = offset; long endOffset = offset;
if (size > 0) { if (size > 0) {
endOffset = offset + size - 1; endOffset = offset + size - 1;
} }
long addrOffset = address.getOffset(); long addrOffset = addr.getOffset();
if (offset > endOffset) { // handle long-wrap condition if (offset > endOffset) { // handle long-wrap condition
return offset <= addrOffset; return offset <= addrOffset;
} }
@ -320,110 +318,16 @@ public class Varnode {
* @param buf is the builder to which to append XML * @param buf is the builder to which to append XML
*/ */
public void buildXML(StringBuilder buf) { public void buildXML(StringBuilder buf) {
buildXMLAddress(buf, address, size); AddressXML.buildXML(buf, address, size);
} }
/** /**
* Build an XML document representation of a varnode with the given address and size. * Build a Varnode from an XML stream
*
* @param resBuf is the builder to which to append the XML
* @param addr location varnode is defined at
* @param size size of the varnode.
*/
public static void buildXMLAddress(StringBuilder resBuf, Address addr, int size) {
resBuf.append("<addr");
appendSpaceOffset(resBuf, addr);
SpecXmlUtils.encodeSignedIntegerAttribute(resBuf, "size", size);
resBuf.append("/>");
}
/**
* Convert an address into an XML document.
*
* @param addr -- Address to convert to XML
* @return XML string
*/
public static String buildXMLAddress(Address addr) {
if ((addr == null) || (addr == Address.NO_ADDRESS)) {
return "<addr/>";
}
StringBuilder resBuf = new StringBuilder();
resBuf.append("<addr");
appendSpaceOffset(resBuf, addr);
resBuf.append("/>");
return resBuf.toString();
}
/**
* Convert a varnode array into an XML document.
*
* @param varnodes sequence of storage varnodes
* @param logicalsize the logical size value of the varnode
* @return XML string
*/
public static String buildXMLAddress(Varnode[] varnodes, int logicalsize) {
if (varnodes == null) {
return "<addr/>";
}
if ((varnodes.length == 1) && (logicalsize == 0)) {
StringBuilder buf = new StringBuilder();
buildXMLAddress(buf, varnodes[0].address, varnodes[0].size);
return buf.toString();
}
StringBuilder resBuf = new StringBuilder();
resBuf.append("<addr space=\"join\"");
int piece = 0;
for (Varnode vn : varnodes) {
resBuf.append(" piece");
resBuf.append(Integer.toString(++piece));
resBuf.append("=\"");
buildVarnodePiece(resBuf, vn.address, vn.size);
resBuf.append("\"");
}
if (logicalsize != 0) {
resBuf.append(" logicalsize=\"").append(logicalsize).append('\"');
}
resBuf.append("/>");
return resBuf.toString();
}
private static void buildVarnodePiece(StringBuilder buf, Address addr, int size) {
AddressSpace space = addr.getAddressSpace();
if (space.isOverlaySpace()) {
space = space.getPhysicalSpace();
addr = space.getAddress(addr.getOffset());
}
buf.append(space.getName());
buf.append(":0x");
long off = addr.getUnsignedOffset();
buf.append(Long.toHexString(off));
buf.append(':');
buf.append(Integer.toString(size));
}
public static void appendSpaceOffset(StringBuilder buf, Address addr) {
AddressSpace space = addr.getAddressSpace();
if (space.isOverlaySpace()) {
if (space.getType() != AddressSpace.TYPE_OTHER) {
space = space.getPhysicalSpace();
addr = space.getAddress(addr.getOffset());
}
}
SpecXmlUtils.encodeStringAttribute(buf, "space", space.getName());
SpecXmlUtils.encodeUnsignedIntegerAttribute(buf, "offset", addr.getUnsignedOffset());
}
/**
* Build a varnode from a SAX parse tree node
* *
* @param parser the parser * @param parser the parser
* @param factory pcode factory used to create valid pcode * @param factory pcode factory used to create valid pcode
*
* @return new varnode element based on info in the XML. * @return new varnode element based on info in the XML.
* * @throws PcodeXMLException if XML is improperly formed
* @throws PcodeXMLException
*/ */
public static Varnode readXML(XmlPullParser parser, PcodeFactory factory) public static Varnode readXML(XmlPullParser parser, PcodeFactory factory)
throws PcodeXMLException { throws PcodeXMLException {
@ -442,7 +346,7 @@ public class Varnode {
return vn; return vn;
} }
} }
Address addr = readXMLAddress(el, factory.getAddressFactory()); Address addr = AddressXML.readXML(el, factory.getAddressFactory());
if (addr == null) { if (addr == null) {
return null; return null;
} }
@ -525,7 +429,7 @@ public class Varnode {
/** /**
* Convert this varnode to an alternate String representation based on a specified language. * Convert this varnode to an alternate String representation based on a specified language.
* @param language * @param language is the specified Language
* @return string representation * @return string representation
*/ */
public String toString(Language language) { public String toString(Language language) {
@ -576,125 +480,4 @@ public class Varnode {
result = prime * result + spaceID; result = prime * result + spaceID;
return result; return result;
} }
/**
* Create an address from a SAX parse tree node.
*
* @param el SAX parse tree element
* @param addrFactory address factory used to create valid addresses
* @return Address created from XML info
*/
public static Address readXMLAddress(XmlElement el, AddressFactory addrFactory) {
String localName = el.getName();
if (localName.equals("spaceid")) {
AddressSpace spc = addrFactory.getAddressSpace(el.getAttribute("name"));
int spaceid = spc.getSpaceID();
spc = addrFactory.getConstantSpace();
return spc.getAddress(spaceid);
}
else if (localName.equals("iop")) {
int ref = SpecXmlUtils.decodeInt(el.getAttribute("value"));
AddressSpace spc = addrFactory.getConstantSpace();
return spc.getAddress(ref);
}
String space = el.getAttribute("space");
if (space == null) {
return Address.NO_ADDRESS;
}
long offset = SpecXmlUtils.decodeLong(el.getAttribute("offset"));
AddressSpace spc = addrFactory.getAddressSpace(space);
if (spc == null) {
return null;
}
return spc.getAddress(offset);
}
public static Address readXMLAddress(String localName, Attributes attr,
AddressFactory addrFactory) {
if (localName.equals("spaceid")) {
AddressSpace spc = addrFactory.getAddressSpace(attr.getValue("name"));
int spaceid = spc.getSpaceID();
spc = addrFactory.getConstantSpace();
return spc.getAddress(spaceid);
}
else if (localName.equals("iop")) {
int ref = SpecXmlUtils.decodeInt(attr.getValue("value"));
AddressSpace spc = addrFactory.getConstantSpace();
return spc.getAddress(ref);
}
String space = attr.getValue("space");
if (space == null) {
return Address.NO_ADDRESS;
}
long offset = SpecXmlUtils.decodeLong(attr.getValue("offset"));
AddressSpace spc = addrFactory.getAddressSpace(space);
if (spc == null) {
return Address.NO_ADDRESS;
}
return spc.getAddress(offset);
}
/**
* Parse an XML containing an address. The format options are simple enough that we don't try to invoke
* an actual XML parser but just walk the string
* @param addrstring is the string containing the XML tag
* @param addrfactory is the factory that can produce addresses
* @param refSpace can be null but is otherwise the reference AddressSpace from which the request is sent.
* @return the created Address or Address.NO_ADDRESS in some special cases
* @throws PcodeXMLException
*/
public static Address readXMLAddress(String addrstring, AddressFactory addrfactory,
AddressSpace refSpace) throws PcodeXMLException {
int tagstart = addrstring.indexOf('<');
if (tagstart >= 0) {
tagstart += 1;
if (addrstring.startsWith("spaceid", tagstart)) {
tagstart += 8;
int attrstart = addrstring.indexOf("name=\"", tagstart);
if (attrstart >= 0) {
attrstart += 6;
int nameend = addrstring.indexOf('\"', attrstart);
if (nameend >= 0) {
AddressSpace spc =
addrfactory.getAddressSpace(addrstring.substring(attrstart, nameend));
int spaceid = spc.getSpaceID();
spc = addrfactory.getConstantSpace();
return spc.getAddress(spaceid);
}
}
}
// There are several tag forms where we essentially want to just look for 'space' and 'offset' attributes
// don't explicitly check the tag name
int spacestart = addrstring.indexOf("space=\"");
if (spacestart >= 4) {
spacestart += 7;
int spaceend = addrstring.indexOf('"', spacestart);
if (spaceend >= spacestart) {
String spcname = addrstring.substring(spacestart, spaceend);
int offstart = addrstring.indexOf("offset=\"");
if (offstart >= 4) {
offstart += 8;
int offend = addrstring.indexOf('"', offstart);
if (offend >= offstart) {
String offstr = addrstring.substring(offstart, offend);
AddressSpace spc = addrfactory.getAddressSpace(spcname);
// Unknown spaces may result from "spacebase" registers defined in cspec
if (spc == null) {
return Address.NO_ADDRESS;
}
long offset = SpecXmlUtils.decodeLong(offstr);
Address addr = spc.getAddress(offset);
if (refSpace != null && refSpace.isOverlaySpace()) {
return refSpace.getOverlayAddress(addr);
}
return addr;
}
}
}
}
}
throw new PcodeXMLException("Badly formed address: " + addrstring);
}
} }

View file

@ -215,8 +215,8 @@ public abstract class LanguageTranslatorAdapter implements LanguageTranslator {
if (!oldSpaces.isEmpty()) { if (!oldSpaces.isEmpty()) {
// spaceMap = null; // spaceMap = null;
throw new IncompatibleLanguageException("Failed to map one or more address spaces: " + throw new IncompatibleLanguageException(
oldSpaces); "Failed to map one or more address spaces: " + oldSpaces);
} }
} }
@ -333,7 +333,8 @@ public abstract class LanguageTranslatorAdapter implements LanguageTranslator {
} }
protected boolean isSameRegisterConstruction(Register oldReg, Register newReg) { protected boolean isSameRegisterConstruction(Register oldReg, Register newReg) {
if (oldReg.getLeastSignificatBitInBaseRegister() != newReg.getLeastSignificatBitInBaseRegister() || if (oldReg.getLeastSignificatBitInBaseRegister() != newReg
.getLeastSignificatBitInBaseRegister() ||
oldReg.getBitLength() != newReg.getBitLength()) { oldReg.getBitLength() != newReg.getBitLength()) {
return false; return false;
} }
@ -522,11 +523,9 @@ class TemporaryCompilerSpec implements CompilerSpec {
throws CompilerSpecNotFoundException { throws CompilerSpecNotFoundException {
this.translator = translator; this.translator = translator;
this.oldCompilerSpecID = oldCompilerSpecID; this.oldCompilerSpecID = oldCompilerSpecID;
newCompilerSpec = newCompilerSpec = translator.getNewLanguage()
translator.getNewLanguage().getCompilerSpecByID( .getCompilerSpecByID(translator.getNewCompilerSpecID(oldCompilerSpecID));
translator.getNewCompilerSpecID(oldCompilerSpecID)); description = new BasicCompilerSpecDescription(oldCompilerSpecID,
description =
new BasicCompilerSpecDescription(oldCompilerSpecID,
newCompilerSpec.getCompilerSpecDescription().getCompilerSpecName()); newCompilerSpec.getCompilerSpecDescription().getCompilerSpecName());
} }
@ -549,6 +548,11 @@ class TemporaryCompilerSpec implements CompilerSpec {
return null; return null;
} }
@Override
public PrototypeModel[] getAllModels() {
return new PrototypeModel[0];
}
@Override @Override
public CompilerSpecDescription getCompilerSpecDescription() { public CompilerSpecDescription getCompilerSpecDescription() {
return description; return description;
@ -565,13 +569,18 @@ class TemporaryCompilerSpec implements CompilerSpec {
} }
@Override @Override
public Language getLanguage() { public DecompilerLanguage getDecompilerOutputLanguage() {
return translator.getOldLanguage(); return DecompilerLanguage.C_LANGUAGE;
} }
@Override @Override
public PrototypeModel[] getNamedCallingConventions() { public PrototypeModel getPrototypeEvaluationModel(EvaluationModelType modelType) {
return new PrototypeModel[0]; return newCompilerSpec.getPrototypeEvaluationModel(modelType);
}
@Override
public Language getLanguage() {
return translator.getOldLanguage();
} }
@Override @Override
@ -614,16 +623,6 @@ class TemporaryCompilerSpec implements CompilerSpec {
return newCompilerSpec.getDataOrganization(); return newCompilerSpec.getDataOrganization();
} }
@Override
public Object getPrototypeEvaluationModel(Program program) {
throw new UnsupportedOperationException(
"Language for upgrade use only (getPrototypeEvaluationModel)");
}
@Override
public void registerProgramOptions(Program program) {
}
@Override @Override
public PrototypeModel matchConvention(GenericCallingConvention genericCallingConvention) { public PrototypeModel matchConvention(GenericCallingConvention genericCallingConvention) {
throw new UnsupportedOperationException("Language for upgrade use only (matchConvention)"); throw new UnsupportedOperationException("Language for upgrade use only (matchConvention)");
@ -680,9 +679,4 @@ class TemporaryCompilerSpec implements CompilerSpec {
public PcodeInjectLibrary getPcodeInjectLibrary() { public PcodeInjectLibrary getPcodeInjectLibrary() {
return newCompilerSpec.getPcodeInjectLibrary(); return newCompilerSpec.getPcodeInjectLibrary();
} }
@Override
public DecompilerLanguage getDecompilerOutputLanguage(Program program) {
return newCompilerSpec.getDecompilerOutputLanguage(program);
}
} }

View file

@ -18,6 +18,7 @@ package ghidra.dalvik.dex.inject;
import java.io.IOException; import java.io.IOException;
import ghidra.app.plugin.processors.sleigh.PcodeEmit; import ghidra.app.plugin.processors.sleigh.PcodeEmit;
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
import ghidra.file.formats.android.dex.analyzer.DexAnalysisState; import ghidra.file.formats.android.dex.analyzer.DexAnalysisState;
import ghidra.file.formats.android.dex.format.*; import ghidra.file.formats.android.dex.format.*;
import ghidra.file.formats.android.dex.util.DexUtil; import ghidra.file.formats.android.dex.util.DexUtil;
@ -30,6 +31,8 @@ import ghidra.program.model.listing.Program;
import ghidra.program.model.pcode.PcodeOp; import ghidra.program.model.pcode.PcodeOp;
import ghidra.program.model.pcode.Varnode; import ghidra.program.model.pcode.Varnode;
import ghidra.util.Msg; import ghidra.util.Msg;
import ghidra.util.xml.SpecXmlUtils;
import ghidra.xml.*;
/** /**
* The "uponentry" injection for a DEX method. We simulate DEX's register stack by copying values from * The "uponentry" injection for a DEX method. We simulate DEX's register stack by copying values from
@ -39,17 +42,21 @@ import ghidra.util.Msg;
public class InjectPayloadDexParameters implements InjectPayload { public class InjectPayloadDexParameters implements InjectPayload {
public final static int INPUT_REGISTER_START = 0x100; public final static int INPUT_REGISTER_START = 0x100;
public final static int REGISTER_START = 0x1000; public final static int REGISTER_START = 0x1000;
private String name;
private String sourceName;
private InjectParameter[] noParams; private InjectParameter[] noParams;
private boolean analysisStateRecoverable; private boolean analysisStateRecoverable;
public InjectPayloadDexParameters() { public InjectPayloadDexParameters(String nm, String srcName) {
name = nm;
sourceName = srcName;
noParams = new InjectParameter[0]; noParams = new InjectParameter[0];
analysisStateRecoverable = true; analysisStateRecoverable = true;
} }
@Override @Override
public String getName() { public String getName() {
return "dexparameters"; return name;
} }
@Override @Override
@ -57,10 +64,9 @@ public class InjectPayloadDexParameters implements InjectPayload {
return CALLMECHANISM_TYPE; return CALLMECHANISM_TYPE;
} }
@Override @Override
public String getSource() { public String getSource() {
return "dexparameters"; return sourceName;
} }
@Override @Override
@ -78,6 +84,11 @@ public class InjectPayloadDexParameters implements InjectPayload {
return noParams; return noParams;
} }
@Override
public boolean isErrorPlaceholder() {
return false;
}
@Override @Override
public void inject(InjectContext context, PcodeEmit emit) { public void inject(InjectContext context, PcodeEmit emit) {
// not used // not used
@ -101,23 +112,27 @@ public class InjectPayloadDexParameters implements InjectPayload {
PcodeOp[] resOps; PcodeOp[] resOps;
Function func = program.getFunctionManager().getFunctionContaining(con.baseAddr); Function func = program.getFunctionManager().getFunctionContaining(con.baseAddr);
EncodedMethod encodedMethod = null; EncodedMethod encodedMethod = null;
if (func != null) if (func != null) {
encodedMethod = analysisState.getEncodedMethod(func.getEntryPoint()); encodedMethod = analysisState.getEncodedMethod(func.getEntryPoint());
if (encodedMethod == null) }
if (encodedMethod == null) {
return new PcodeOp[0]; return new PcodeOp[0];
}
int paramCount = 0; int paramCount = 0;
if (!encodedMethod.isStatic()) if (!encodedMethod.isStatic()) {
paramCount += 1; // A this pointer at least paramCount += 1; // A this pointer at least
}
CodeItem codeItem = encodedMethod.getCodeItem(); CodeItem codeItem = encodedMethod.getCodeItem();
int registerIndex = codeItem.getRegistersSize() - codeItem.getIncomingSize(); int registerIndex = codeItem.getRegistersSize() - codeItem.getIncomingSize();
MethodIDItem methodIDItem = header.getMethods().get( encodedMethod.getMethodIndex() ); MethodIDItem methodIDItem = header.getMethods().get(encodedMethod.getMethodIndex());
int prototypeIndex = methodIDItem.getProtoIndex() & 0xffff; int prototypeIndex = methodIDItem.getProtoIndex() & 0xffff;
PrototypesIDItem prototype = header.getPrototypes().get(prototypeIndex); PrototypesIDItem prototype = header.getPrototypes().get(prototypeIndex);
TypeList parameters = prototype.getParameters(); TypeList parameters = prototype.getParameters();
if (parameters != null) if (parameters != null) {
paramCount += parameters.getItems().size(); paramCount += parameters.getItems().size();
}
AddressSpace registerSpace = program.getAddressFactory().getAddressSpace("register"); AddressSpace registerSpace = program.getAddressFactory().getAddressSpace("register");
resOps = new PcodeOp[ paramCount ]; resOps = new PcodeOp[paramCount];
long fromOffset = INPUT_REGISTER_START; // Base of designated input registers long fromOffset = INPUT_REGISTER_START; // Base of designated input registers
long toOffset = REGISTER_START + 4 * registerIndex; // Base of registers in method's frame long toOffset = REGISTER_START + 4 * registerIndex; // Base of registers in method's frame
int i = 0; int i = 0;
@ -126,16 +141,16 @@ public class InjectPayloadDexParameters implements InjectPayload {
Address toAddr = registerSpace.getAddress(toOffset); Address toAddr = registerSpace.getAddress(toOffset);
fromOffset += 4; fromOffset += 4;
toOffset += 4; toOffset += 4;
PcodeOp op = new PcodeOp(con.baseAddr,i,PcodeOp.COPY); PcodeOp op = new PcodeOp(con.baseAddr, i, PcodeOp.COPY);
op.setInput(new Varnode(fromAddr,4), 0); op.setInput(new Varnode(fromAddr, 4), 0);
op.setOutput(new Varnode(toAddr,4)); op.setOutput(new Varnode(toAddr, 4));
resOps[i] = op; resOps[i] = op;
i += 1; i += 1;
} }
if (parameters != null) { if (parameters != null) {
for (TypeItem parameterTypeItem : parameters.getItems()) { for (TypeItem parameterTypeItem : parameters.getItems()) {
String parameterTypeString = DexUtil.convertTypeIndexToString( String parameterTypeString =
header, parameterTypeItem.getType()); DexUtil.convertTypeIndexToString(header, parameterTypeItem.getType());
int size; int size;
char firstChar = parameterTypeString.charAt(0); char firstChar = parameterTypeString.charAt(0);
Address fromAddr = registerSpace.getAddress(fromOffset); Address fromAddr = registerSpace.getAddress(fromOffset);
@ -156,4 +171,42 @@ public class InjectPayloadDexParameters implements InjectPayload {
public boolean isFallThru() { public boolean isFallThru() {
return true; return true;
} }
@Override
public boolean isIncidentalCopy() {
return false;
}
@Override
public void saveXml(StringBuilder buffer) {
// Provide a minimal tag so decompiler can call-back
buffer.append("<pcode");
SpecXmlUtils.encodeStringAttribute(buffer, "inject", "uponentry");
SpecXmlUtils.encodeBooleanAttribute(buffer, "dynamic", true);
buffer.append("/>\n");
}
@Override
public void restoreXml(XmlPullParser parser, SleighLanguage language) throws XmlParseException {
XmlElement el = parser.start();
String injectString = el.getAttribute("inject");
if (injectString == null || !injectString.equals("uponentry")) {
throw new XmlParseException("Expecting inject=\"uponentry\" attribute");
}
boolean isDynamic = SpecXmlUtils.decodeBoolean(el.getAttribute("dynamic"));
if (!isDynamic) {
throw new XmlParseException("Expecting dynamic attribute");
}
parser.end(el);
}
@Override
public boolean equals(Object obj) {
return (obj instanceof InjectPayloadDexParameters); // All instances are equal
}
@Override
public int hashCode() {
return 123474219; // All instances are equal
}
} }

View file

@ -15,11 +15,10 @@
*/ */
package ghidra.dalvik.dex.inject; package ghidra.dalvik.dex.inject;
import ghidra.app.plugin.processors.sleigh.PcodeEmit;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace; import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.lang.InjectContext; import ghidra.program.model.lang.InjectContext;
import ghidra.program.model.lang.InjectPayload; import ghidra.program.model.lang.InjectPayloadCallother;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.program.model.pcode.PcodeOp; import ghidra.program.model.pcode.PcodeOp;
import ghidra.program.model.pcode.Varnode; import ghidra.program.model.pcode.Varnode;
@ -31,44 +30,10 @@ import ghidra.program.model.pcode.Varnode;
* The registers are moved to the specially designated input registers iv0, iv1, iv2, ... * The registers are moved to the specially designated input registers iv0, iv1, iv2, ...
* *
*/ */
public class InjectPayloadDexRange implements InjectPayload { public class InjectPayloadDexRange extends InjectPayloadCallother {
public InjectPayloadDexRange() { public InjectPayloadDexRange() {
} super("dexrange");
@Override
public String getName() {
return "dexrange";
}
@Override
public int getType() {
return CALLOTHERFIXUP_TYPE;
}
@Override
public String getSource() {
return "dexrange";
}
@Override
public int getParamShift() {
return 0;
}
@Override
public InjectParameter[] getInput() {
return null; // Not used
}
@Override
public InjectParameter[] getOutput() {
return null; // Not used
}
@Override
public void inject(InjectContext context, PcodeEmit emit) {
// not used
} }
@Override @Override
@ -96,10 +61,4 @@ public class InjectPayloadDexRange implements InjectPayload {
} }
return resOps; return resOps;
} }
@Override
public boolean isFallThru() {
return true;
}
} }

View file

@ -23,35 +23,32 @@ import ghidra.program.model.listing.Program;
public class PcodeInjectLibraryDex extends PcodeInjectLibrary { public class PcodeInjectLibraryDex extends PcodeInjectLibrary {
private InjectPayloadDexParameters paramPayload = null;
private InjectPayloadDexRange rangePayload = null;
public PcodeInjectLibraryDex(SleighLanguage l) { public PcodeInjectLibraryDex(SleighLanguage l) {
super(l); super(l);
} }
@Override public PcodeInjectLibraryDex(PcodeInjectLibraryDex op2) {
public InjectPayload getPayload(int type, String name, Program program, super(op2);
String context) {
if (type == InjectPayload.CALLMECHANISM_TYPE) {
if (paramPayload == null) {
paramPayload = new InjectPayloadDexParameters();
}
return paramPayload;
}
else if (type == InjectPayload.CALLOTHERFIXUP_TYPE && name.equals("moveRangeToIV")) {
if (rangePayload == null) {
rangePayload = new InjectPayloadDexRange();
}
return rangePayload;
} }
return super.getPayload(type, name, program, context); @Override
public PcodeInjectLibrary clone() {
return new PcodeInjectLibraryDex(this);
}
@Override
public InjectPayload allocateInject(String sourceName, String name, int tp) {
if (tp == InjectPayload.CALLMECHANISM_TYPE) {
return new InjectPayloadDexParameters(name, sourceName);
}
else if (tp == InjectPayload.CALLOTHERFIXUP_TYPE && name.equals("moveRangeToIV")) {
return new InjectPayloadDexRange();
}
return super.allocateInject(sourceName, name, tp);
} }
@Override @Override
public ConstantPool getConstantPool(Program program) throws IOException { public ConstantPool getConstantPool(Program program) throws IOException {
return new ConstantPoolDex(program); return new ConstantPoolDex(program);
} }
} }

View file

@ -27,11 +27,6 @@ public class InjectGetField extends InjectPayloadJava {
super(sourceName, language, uniqBase); super(sourceName, language, uniqBase);
} }
@Override
public String getName() {
return PcodeInjectLibraryJava.GETFIELD;
}
@Override @Override
public PcodeOp[] getPcode(Program program, InjectContext con) { public PcodeOp[] getPcode(Program program, InjectContext con) {
AbstractConstantPoolInfoJava[] constantPool = getConstantPool(program); AbstractConstantPoolInfoJava[] constantPool = getConstantPool(program);

View file

@ -27,11 +27,6 @@ public class InjectGetStatic extends InjectPayloadJava {
super(sourceName, language, uniqBase); super(sourceName, language, uniqBase);
} }
@Override
public String getName() {
return PcodeInjectLibraryJava.GETSTATIC;
}
@Override @Override
public PcodeOp[] getPcode(Program program, InjectContext con) { public PcodeOp[] getPcode(Program program, InjectContext con) {
AbstractConstantPoolInfoJava[] constantPool = getConstantPool(program); AbstractConstantPoolInfoJava[] constantPool = getConstantPool(program);

View file

@ -27,11 +27,6 @@ public class InjectInvokeDynamic extends InjectPayloadJava {
super(sourceName, language, uniqBase); super(sourceName, language, uniqBase);
} }
@Override
public String getName() {
return PcodeInjectLibraryJava.INVOKE_DYNAMIC;
}
@Override @Override
public PcodeOp[] getPcode(Program program, InjectContext con) { public PcodeOp[] getPcode(Program program, InjectContext con) {
AbstractConstantPoolInfoJava[] constantPool = getConstantPool(program); AbstractConstantPoolInfoJava[] constantPool = getConstantPool(program);

View file

@ -27,11 +27,6 @@ public class InjectInvokeInterface extends InjectPayloadJava {
super(sourceName, language, uniqBase); super(sourceName, language, uniqBase);
} }
@Override
public String getName() {
return PcodeInjectLibraryJava.INVOKE_INTERFACE;
}
@Override @Override
public PcodeOp[] getPcode(Program program, InjectContext con) { public PcodeOp[] getPcode(Program program, InjectContext con) {
AbstractConstantPoolInfoJava[] constantPool = getConstantPool(program); AbstractConstantPoolInfoJava[] constantPool = getConstantPool(program);

View file

@ -27,11 +27,6 @@ public class InjectInvokeSpecial extends InjectPayloadJava {
super(sourceName, language, uniqBase); super(sourceName, language, uniqBase);
} }
@Override
public String getName() {
return PcodeInjectLibraryJava.INVOKE_SPECIAL;
}
@Override @Override
public PcodeOp[] getPcode(Program program, InjectContext con) { public PcodeOp[] getPcode(Program program, InjectContext con) {
AbstractConstantPoolInfoJava[] constantPool = getConstantPool(program); AbstractConstantPoolInfoJava[] constantPool = getConstantPool(program);

View file

@ -27,11 +27,6 @@ public class InjectInvokeStatic extends InjectPayloadJava {
super(sourceName, language, uniqBase); super(sourceName, language, uniqBase);
} }
@Override
public String getName() {
return PcodeInjectLibraryJava.INVOKE_STATIC;
}
@Override @Override
public PcodeOp[] getPcode(Program program, InjectContext con) { public PcodeOp[] getPcode(Program program, InjectContext con) {
AbstractConstantPoolInfoJava[] constantPool = getConstantPool(program); AbstractConstantPoolInfoJava[] constantPool = getConstantPool(program);

View file

@ -27,11 +27,6 @@ public class InjectInvokeVirtual extends InjectPayloadJava {
super(sourceName, language, uniqBase); super(sourceName, language, uniqBase);
} }
@Override
public String getName() {
return PcodeInjectLibraryJava.INVOKE_VIRTUAL;
}
@Override @Override
public PcodeOp[] getPcode(Program program, InjectContext con) { public PcodeOp[] getPcode(Program program, InjectContext con) {
AbstractConstantPoolInfoJava[] constantPool = getConstantPool(program); AbstractConstantPoolInfoJava[] constantPool = getConstantPool(program);

Some files were not shown because too many files have changed in this diff Show more