diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/disassembler/CallFixupAnalyzer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/disassembler/CallFixupAnalyzer.java
index 36931f31b0..60f8c68024 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/disassembler/CallFixupAnalyzer.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/disassembler/CallFixupAnalyzer.java
@@ -101,8 +101,8 @@ public class CallFixupAnalyzer extends AbstractAnalyzer {
if (mustFix) {
PcodeInjectLibrary snippetLibrary =
program.getCompilerSpec().getPcodeInjectLibrary();
- InjectPayload callFixup = snippetLibrary.getPayload(InjectPayload.CALLFIXUP_TYPE,
- callFixupApplied, program, null);
+ InjectPayload callFixup =
+ snippetLibrary.getPayload(InjectPayload.CALLFIXUP_TYPE, callFixupApplied);
boolean isfallthru = true;
if (callFixup != null) {
isfallthru = callFixup.isFallThru();
@@ -405,8 +405,8 @@ public class CallFixupAnalyzer extends AbstractAnalyzer {
}
}
- program.getBookmarkManager().removeBookmarks(repairedCallLocations, BookmarkType.ERROR,
- monitor);
+ program.getBookmarkManager()
+ .removeBookmarks(repairedCallLocations, BookmarkType.ERROR, monitor);
if (!clearInstSet.isEmpty()) {
// entries including data flow referenced from instructions will be repaired
@@ -449,7 +449,7 @@ public class CallFixupAnalyzer extends AbstractAnalyzer {
String[] callFixupNames = snippetLibrary.getCallFixupNames();
for (String fixupName : callFixupNames) {
InjectPayload payload =
- snippetLibrary.getPayload(InjectPayload.CALLFIXUP_TYPE, fixupName, program, null);
+ snippetLibrary.getPayload(InjectPayload.CALLFIXUP_TYPE, fixupName);
List callFixupTargets = ((InjectPayloadCallfixup) payload).getTargets();
for (String name : callFixupTargets) {
cachedTargetFixupMap.put(name, fixupName);
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/editor/FunctionEditorDialog.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/editor/FunctionEditorDialog.java
index da0ecfab24..a8d85b1a9e 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/editor/FunctionEditorDialog.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/editor/FunctionEditorDialog.java
@@ -359,6 +359,9 @@ public class FunctionEditorDialog extends DialogComponentProvider implements Mod
}
private JComponent createCallFixupComboPanel() {
+
+ JPanel panel = new JPanel();
+
callFixupComboBox = new GComboBox<>();
String[] callFixupNames = model.getCallFixupNames();
@@ -377,7 +380,8 @@ public class FunctionEditorDialog extends DialogComponentProvider implements Mod
callFixupComboBox.setEnabled(false);
}
- return callFixupComboBox;
+ panel.add(callFixupComboBox);
+ return panel;
}
private Component buildTable() {
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/program/util/SymbolicPropogator.java b/Ghidra/Features/Base/src/main/java/ghidra/program/util/SymbolicPropogator.java
index 1d2c2b5a3f..0ec9498847 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/program/util/SymbolicPropogator.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/program/util/SymbolicPropogator.java
@@ -405,8 +405,9 @@ public class SymbolicPropogator {
canceled = false;
// only stop flowing on unknown bad calls when the stack depth could be unknown
- boolean callCouldCauseBadStackDepth =
- program.getCompilerSpec().getDefaultCallingConvention().getExtrapop() == PrototypeModel.UNKNOWN_EXTRAPOP;
+ boolean callCouldCauseBadStackDepth = program.getCompilerSpec()
+ .getDefaultCallingConvention()
+ .getExtrapop() == PrototypeModel.UNKNOWN_EXTRAPOP;
while (!contextStack.isEmpty()) {
monitor.checkCanceled();
@@ -834,8 +835,8 @@ public class SymbolicPropogator {
try {
val1 = vContext.getValue(in[0], evaluator);
lval1 = vContext.getConstant(val1, evaluator);
- vt = vContext.getVarnode(
- minInstrAddress.getAddressSpace().getSpaceID(), lval1, 0);
+ vt = vContext.getVarnode(minInstrAddress.getAddressSpace().getSpaceID(),
+ lval1, 0);
makeReference(vContext, instruction, ptype, -1, vt,
instruction.getFlowType(), monitor);
}
@@ -873,8 +874,8 @@ public class SymbolicPropogator {
if (val1.isConstant()) {
// indirect target - assume single code space (same as instruction)
- target = instruction.getAddress().getNewTruncatedAddress(
- val1.getOffset(), true);
+ target = instruction.getAddress()
+ .getNewTruncatedAddress(val1.getOffset(), true);
}
else if (val1.isAddress()) {
// TODO: could this also occur if a memory location was copied ??
@@ -957,8 +958,8 @@ public class SymbolicPropogator {
case PcodeOp.CALLOTHER:
// HACK ALERT!
// if this is a segment op, emulate the segmenting for now.
- String opName = this.program.getLanguage().getUserDefinedOpName(
- (int) in[0].getOffset());
+ String opName = this.program.getLanguage()
+ .getUserDefinedOpName((int) in[0].getOffset());
if (opName.equals("segment") && in.length > 2) {
checkSegmented(out, in[1], in[2], mustClearAll);
}
@@ -1074,8 +1075,8 @@ public class SymbolicPropogator {
}
else if (!evaluator.followFalseConditionalBranches()) {
// pcode addresses are raw addresses, make sure address is in same instruction space
- nextAddr = minInstrAddress.getAddressSpace().getOverlayAddress(
- in[0].getAddress());
+ nextAddr = minInstrAddress.getAddressSpace()
+ .getOverlayAddress(in[0].getAddress());
pcodeIndex = ops.length; // break out of the processing
}
}
@@ -1202,8 +1203,8 @@ public class SymbolicPropogator {
case PcodeOp.INT_RIGHT:
val1 = vContext.getValue(in[0], false, evaluator);
val2 = vContext.getValue(in[1], false, evaluator);
- lresult = vContext.getConstant(val1,
- evaluator) >> vContext.getConstant(val2, evaluator);
+ lresult = vContext.getConstant(val1, evaluator) >> vContext
+ .getConstant(val2, evaluator);
result = vContext.createConstantVarnode(lresult, val1.getSize());
vContext.putValue(out, result, mustClearAll);
break;
@@ -1211,8 +1212,8 @@ public class SymbolicPropogator {
case PcodeOp.INT_SRIGHT:
val1 = vContext.getValue(in[0], true, evaluator);
val2 = vContext.getValue(in[1], false, evaluator);
- lresult = vContext.getConstant(val1,
- evaluator) >>> vContext.getConstant(val2, evaluator);
+ lresult = vContext.getConstant(val1, evaluator) >>> vContext
+ .getConstant(val2, evaluator);
result = vContext.createConstantVarnode(lresult, val1.getSize());
vContext.putValue(out, result, mustClearAll);
break;
@@ -1315,8 +1316,8 @@ public class SymbolicPropogator {
val2 = vContext.getValue(in[1], true, evaluator);
lval1 = vContext.getConstant(val1, evaluator);
lval2 = vContext.getConstant(val2, evaluator);
- lresult = (vContext.getConstant(val1,
- evaluator) < vContext.getConstant(val2, evaluator)) ? 1 : 0;
+ lresult = (vContext.getConstant(val1, evaluator) < vContext
+ .getConstant(val2, evaluator)) ? 1 : 0;
result = vContext.createConstantVarnode(lresult, val1.getSize());
vContext.putValue(out, result, mustClearAll);
break;
@@ -1334,8 +1335,8 @@ public class SymbolicPropogator {
case PcodeOp.INT_SLESSEQUAL:
val1 = vContext.getValue(in[0], true, evaluator);
val2 = vContext.getValue(in[1], true, evaluator);
- lresult = (vContext.getConstant(val1,
- evaluator) <= vContext.getConstant(val2, evaluator)) ? 1 : 0;
+ lresult = (vContext.getConstant(val1, evaluator) <= vContext
+ .getConstant(val2, evaluator)) ? 1 : 0;
result = vContext.createConstantVarnode(lresult, val1.getSize());
vContext.putValue(out, result, mustClearAll);
break;
@@ -1344,8 +1345,8 @@ public class SymbolicPropogator {
val1 = vContext.getValue(in[0], false, evaluator);
val2 = vContext.getValue(in[1], false, evaluator);
- lresult = (vContext.getConstant(val1,
- evaluator) == vContext.getConstant(val2, evaluator)) ? 1 : 0;
+ lresult = (vContext.getConstant(val1, evaluator) == vContext
+ .getConstant(val2, evaluator)) ? 1 : 0;
result = vContext.createConstantVarnode(lresult, val1.getSize());
vContext.putValue(out, result, mustClearAll);
break;
@@ -1353,8 +1354,8 @@ public class SymbolicPropogator {
case PcodeOp.INT_NOTEQUAL:
val1 = vContext.getValue(in[0], false, evaluator);
val2 = vContext.getValue(in[1], false, evaluator);
- lresult = (vContext.getConstant(val1,
- evaluator) != vContext.getConstant(val2, evaluator)) ? 1 : 0;
+ lresult = (vContext.getConstant(val1, evaluator) != vContext
+ .getConstant(val2, evaluator)) ? 1 : 0;
result = vContext.createConstantVarnode(lresult, val1.getSize());
vContext.putValue(out, result, mustClearAll);
break;
@@ -1571,7 +1572,7 @@ public class SymbolicPropogator {
PcodeInjectLibrary snippetLibrary = prog.getCompilerSpec().getPcodeInjectLibrary();
InjectPayload payload =
- snippetLibrary.getPayload(InjectPayload.CALLFIXUP_TYPE, callFixupName, prog, null);
+ snippetLibrary.getPayload(InjectPayload.CALLFIXUP_TYPE, callFixupName);
if (payload == null) {
return null;
}
@@ -1598,7 +1599,7 @@ public class SymbolicPropogator {
PcodeInjectLibrary snippetLibrary = prog.getCompilerSpec().getPcodeInjectLibrary();
InjectPayload payload =
- snippetLibrary.getPayload(InjectPayload.CALLMECHANISM_TYPE, injectionName, prog, null);
+ snippetLibrary.getPayload(InjectPayload.CALLMECHANISM_TYPE, injectionName);
if (payload == null) {
return null;
}
@@ -1843,14 +1844,14 @@ public class SymbolicPropogator {
// } else
if (!vContext.isStackSymbolicSpace(refLocation) && evaluator != null) {
- Address constant = program.getAddressFactory().getAddress(
- (int) targetSpaceID.getOffset(), offset);
+ Address constant = program.getAddressFactory()
+ .getAddress((int) targetSpaceID.getOffset(), offset);
Address newTarget = evaluator.evaluateConstant(vContext, instruction,
pcodeType, constant, 0, reftype);
if (newTarget != null) {
makeReference(vContext, instruction, Reference.MNEMONIC,
- newTarget.getAddressSpace().getSpaceID(), newTarget.getOffset(),
- 0, reftype, pcodeType, false, monitor);
+ newTarget.getAddressSpace().getSpaceID(), newTarget.getOffset(), 0,
+ reftype, pcodeType, false, monitor);
return;
}
}
diff --git a/Ghidra/Features/Decompiler/build.gradle b/Ghidra/Features/Decompiler/build.gradle
index a426377b0a..85a49229f9 100644
--- a/Ghidra/Features/Decompiler/build.gradle
+++ b/Ghidra/Features/Decompiler/build.gradle
@@ -120,7 +120,7 @@ task buildDecompilerHelpHtml(type: Exec) {
rm -f $installHelpPoint/topics/DecompilePlugin/*.html
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
rm ${installHelpPoint}/topics/DecompilePlugin/Decompiler.html
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. **'
which fop 2>&1
which xsltproc 2>&1
- rm -f decompileplugin.fo decompileplugin.pdf decompileplugin_withscaling.xml 2>&1
- rm -rf ./images 2>&1
- mkdir -p ./images 2>&1
- cp $installHelpPoint/topics/DecompilePlugin/images/*.png ./images 2>&1
- cp $installHelpPoint/topics/DecompilePlugin/images/*.gif ./images 2>&1
- cp $installHelpPoint/shared/*.png ./images 2>&1
+ rm -f $buildDir/decompileplugin.fo $buildDir/decompileplugin.pdf $buildDir/decompileplugin_withscaling.xml 2>&1
+ rm -rf $buildDir/images 2>&1
+ mkdir -p $buildDir/images 2>&1
+ cp $installHelpPoint/topics/DecompilePlugin/images/*.png $buildDir/images 2>&1
+ cp $installHelpPoint/topics/DecompilePlugin/images/*.gif $buildDir/images 2>&1
+ cp $installHelpPoint/shared/*.png $buildDir/images 2>&1
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 ./decompileplugin.fo decompileplugin_pdf.xsl decompileplugin_withscaling.xml 2>&1
+ xsltproc --output $buildDir/decompileplugin_withscaling.xml --stringparam profile.condition "withscaling" commonprofile.xsl decompileplugin.xml 2>&1
+ xsltproc --output $buildDir/decompileplugin.fo decompileplugin_pdf.xsl $buildDir/decompileplugin_withscaling.xml 2>&1
echo '** Building decompileplugin.pdf **'
- fop decompileplugin.fo decompileplugin.pdf 2>&1
+ fop $buildDir/decompileplugin.fo $buildDir/decompileplugin.pdf 2>&1
echo '** Done. **'
"""
diff --git a/Ghidra/Features/Decompiler/certification.manifest b/Ghidra/Features/Decompiler/certification.manifest
index f965b0d4c3..9a6aa4192b 100644
--- a/Ghidra/Features/Decompiler/certification.manifest
+++ b/Ghidra/Features/Decompiler/certification.manifest
@@ -30,6 +30,7 @@ src/decompile/datatests/sbyte.xml||GHIDRA||||END|
src/decompile/datatests/threedim.xml||GHIDRA||||END|
src/decompile/datatests/twodim.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_html.xsl||GHIDRA||||END|
src/main/doc/decompileplugin.xml||GHIDRA||||END|
diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/TurnOnLanguage.java b/Ghidra/Features/Decompiler/ghidra_scripts/TurnOnLanguage.java
index 2a86ffda93..0a29834439 100644
--- a/Ghidra/Features/Decompiler/ghidra_scripts/TurnOnLanguage.java
+++ b/Ghidra/Features/Decompiler/ghidra_scripts/TurnOnLanguage.java
@@ -15,18 +15,17 @@
*/
import ghidra.app.script.GhidraScript;
import ghidra.framework.options.Options;
-import ghidra.program.model.lang.BasicCompilerSpec;
+import ghidra.program.database.ProgramCompilerSpec;
public class TurnOnLanguage extends GhidraScript {
@Override
protected void run() throws Exception {
- Options decompilerPropertyList = currentProgram.getOptions(BasicCompilerSpec.DECOMPILER_PROPERTY_LIST_NAME);
- decompilerPropertyList.registerOption(
- BasicCompilerSpec.DECOMPILER_OUTPUT_LANGUAGE,
- BasicCompilerSpec.DECOMPILER_OUTPUT_DEF,
- null,
- BasicCompilerSpec.DECOMPILER_OUTPUT_DESC);
+ Options decompilerPropertyList =
+ currentProgram.getOptions(ProgramCompilerSpec.DECOMPILER_PROPERTY_LIST_NAME);
+ decompilerPropertyList.registerOption(ProgramCompilerSpec.DECOMPILER_OUTPUT_LANGUAGE,
+ ProgramCompilerSpec.DECOMPILER_OUTPUT_DEF, null,
+ ProgramCompilerSpec.DECOMPILER_OUTPUT_DESC);
}
}
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.cc
index e04d172dab..56dd7f979f 100644
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.cc
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.cc
@@ -1218,6 +1218,26 @@ void Architecture::parseCompilerConfig(DocumentStorage &store)
else if (elname == "inferptrbounds")
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);
+ }
+ }
+
// tags instantiate the base symbol table
// They need to know about all spaces, so it must come
// after parsing of and
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.hh
index 9a592cb4a2..974e733e22 100644
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.hh
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.hh
@@ -314,7 +314,7 @@ public:
enum {
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
- 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)
};
private:
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_arch.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_arch.cc
index d34513ef04..b72a8093db 100644
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_arch.cc
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_arch.cc
@@ -284,7 +284,7 @@ void ArchitectureGhidra::buildSpecFile(DocumentStorage &store)
istringstream cstream(cspecxml);
doc = store.parseDocument(cstream);
store.registerTag(doc->getRoot());
-
+
istringstream tstream(tspecxml);
doc = store.parseDocument(tstream);
store.registerTag(doc->getRoot());
@@ -293,10 +293,10 @@ void ArchitectureGhidra::buildSpecFile(DocumentStorage &store)
doc = store.parseDocument(corestream);
store.registerTag(doc->getRoot());
- pspecxml = ""; // Strings aren't used again free memory
- cspecxml = "";
- tspecxml = "";
- corespecxml = "";
+ pspecxml.clear(); // Strings aren't used again free memory
+ cspecxml.clear();
+ tspecxml.clear();
+ corespecxml.clear();
}
void ArchitectureGhidra::postSpecFile(void)
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_arch.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_arch.hh
index f796c7ea76..0953288662 100644
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_arch.hh
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_arch.hh
@@ -82,7 +82,8 @@ class ArchitectureGhidra : public Architecture {
virtual void postSpecFile(void);
virtual void resolveArchitecture(void);
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
void clearWarnings(void) { warnings.clear(); } ///< Clear warnings
Document *getRegister(const string ®name); ///< Retrieve a register description given a name
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_process.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_process.cc
index e78b0c0ced..46efe6eb0f 100644
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_process.cc
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_process.cc
@@ -174,6 +174,10 @@ void RegisterProgram::rawAction(void)
}
}
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
ghidra->init(store);
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/inject_sleigh.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/inject_sleigh.cc
index 4a1e44dbf8..b72d089d2a 100644
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/inject_sleigh.cc
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/inject_sleigh.cc
@@ -293,6 +293,23 @@ int4 PcodeInjectLibrarySleigh::registerDynamicInject(InjectPayload *payload)
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)
{
@@ -399,9 +416,10 @@ void PcodeInjectLibrarySleigh::restoreDebug(const Element *el)
s.unsetf(ios::dec | ios::hex | ios::oct);
s >> type;
int4 id = getPayloadId(type,name);
- InjectPayloadDynamic *payload = (InjectPayloadDynamic *)getPayload(id);
- if (payload->getSource() != "dynamic")
- throw LowlevelError("Mismatch with debug inject XML");
+ InjectPayloadDynamic *payload = dynamic_cast(getPayload(id));
+ if (payload == (InjectPayloadDynamic *)0) {
+ payload = forceDebugDynamic(id);
+ }
payload->restoreEntry(subel);
}
}
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/inject_sleigh.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/inject_sleigh.hh
index ad6f44bff2..600215c658 100644
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/inject_sleigh.hh
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/inject_sleigh.hh
@@ -91,6 +91,7 @@ class PcodeInjectLibrarySleigh : public PcodeInjectLibrary {
vector inst;
InjectContextSleigh contextCache;
int4 registerDynamicInject(InjectPayload *payload);
+ InjectPayloadDynamic *forceDebugDynamic(int4 injectid);
void parseInject(InjectPayload *payload);
protected:
virtual int4 allocateInject(const string &sourceName,const string &name,int4 type);
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/userop.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/userop.cc
index e08b43f037..0d13d3a7b6 100644
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/userop.cc
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/userop.cc
@@ -216,10 +216,10 @@ void SegmentOp::restoreXml(const Element *el)
throw LowlevelError("Bad segment pattern tag: "+subel->getName());
}
if (injectId < 0)
- throw LowlevelError("Missing child in tag");
+ throw LowlevelError("Missing child in tag");
InjectPayload *payload = glb->pcodeinjectlib->getPayload(injectId);
if (payload->sizeOutput() != 1)
- throw LowlevelError(" child of tag must declare one
In the absence of parameter and return value annotations, the decompiler will use the prototype model as
@@ -1042,7 +1043,8 @@
Call-fixups are specified by name. The name and associated p-code chunk are typically defined in the
- compiler specification for the Program.
+ compiler specification for the Program. Users can extend the available set
+ of call-fixups. See “Specification Extensions”.
+ P-code allows for additional, processor specific, operations referred to
+ as user-defined 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.
+
+
+ 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.
+
+
+ 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
+ Callother-Fixup, 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 “Specification Extensions”)
+
+
+
+
+
Internal Decompiler Functions
@@ -1052,10 +1081,13 @@
use of multiple models. Subsequently, each distinct model has a name like __stdcall or
__thiscall. 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.
+ It is possible for users to extend the set of prototype models available to a Program,
+ see “Specification Extensions”.
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:
@@ -1107,5 +1139,150 @@
+
+
+SLEIGH Specification Files
+
+
+ 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:
+
+
+
+
+ Disassemble machine instructions from the underlying bytes and
+
+
+ Produce the raw p-code consumed by the decompiler and other analyzers.
+
+
+
+
+
+
+ Specification files are selected based on the Language Id
+ assigned to the Program at the time it is imported into Ghidra.
+ (See Import Program)
+
+
+
+
x86:LE:32:default:windows
+
AARCH64:LE:64:default:v8A:default
+
MIPS:BE:32:micro:default
+
+
+
+ A Language Id is a label with these 5 formal fields, separated
+ by a ':' character:
+
+
+
+
Processor family
+
Endianess
+
Size of the address bus
+
Process variant
+
Compiler producing the Program
+
+
+
+ A field with the value 'default' indicates either the preferred processor variant or the preferred compiler.
+
+
+ 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
+
+ where <Root> represents the root directory of the Ghidra installation and
+ <Family> is the processor family.
+
+
+ There are several types of specification files that are distinguishable by their suffix.
+ These include:
+
+
+
+
SLEIGH files - *.slaspec or *.sinc
+
+
+ These are the human readable SLEIGH language files. A single specification is
+ rooted in one of the *.slaspec files, which may recursively include
+ one or more *.sinc files. The format of these files is described
+ in the document "SLEIGH: A Language for Rapid Processor Specification".
+
+
+
Compiled SLEIGH files - *.sla
+
+
+ This is a compiled form of a single SLEIGH specification. It is produced
+ automatically by Ghidra from the corresponding *.slaspec.
+
+
+
Compiler specification files - *.cspec
+
+
+ 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:
+
+
+
+
+
<prototype> - describing a specific calling convention
+
<callfixup> - describing a Call-fixup
+
<callotherfixup> - describing a Callother-fixup
+
+
+
+
+
Processor specification files - *.pspec
+
+
+ These files contain configuration information that is specific to a particular
+ processor variant.
+
+
+
+
+
+
+
+
+Modifying Specification Files
+
+
+ Changing any of the specification files described here is not recommended.
+ To make additions to either the compiler specification
+ or the processor specification files, see
+ “Specification Extensions”, which describes a safe and portable way
+ to add specific elements.
+
+
+
+
+
+
+
+ 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.
+
+
+
+ When Ghidra starts, it checks for changes to *.slaspec
+ and *.sinc files and will rebuild the corresponding
+ *.sla 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.
+
- 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
Edit -> Options for <Program>
- and the picking the Decompiler tab. These “Program Options”
- are specific to the particular Program being analyzed.
+ Picking the Decompiler tab shows “Program Options”
+ that only affect the decompiler. Picking the “Specification Extensions” 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.
@@ -573,7 +576,8 @@
Program Options
- 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.
@@ -595,6 +599,284 @@
+
+
+Specification Extensions
+
+
+ This tab displays elements from the Program's compiler specification and
+ processor specification and allows the user to add or remove
+ extensions, including prototype models, call-fixups, and
+ callother-fixups.
+
+
+ Every program has a core set of specification elements,
+ loaded from the “SLEIGH Specification Files”, 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.
+
+
+ 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.
+
+
+ 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:
+
+
+
+
+Extension Type - indicating the type of element
+
+Name - showing the formal name of the element
+
+Status - indicating whether the element is core or an extension
+
+
+
+ The core elements of the specification have a blank Status column, and any extension
+ is labeled either as "extension" or "override".
+
+
+
+Extension Types
+
+
+ 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 compiler specification file,
+ the processor specification file, or provided to Ghidra as an
+ import document.
+
+
+
+
prototype
+
+
+ This element is a “Prototype Model” 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 Function Editor Dialog.
+ See the documentation on “Function Prototypes” for how they affect decompilation.
+
+
+ The XML tag, <prototype> always has a name attribute
+ that defines the formal name of the prototype model, which must be unique across all models.
+
+ 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
+ “Function Prototypes”.
+
+
+ The <callfixup> tag has a name
+ attribute listing the formal name, which must be unique across all call-fixups.
+
+ 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 “User-defined P-code Operations - CALLOTHER”.
+
+
+ The <callotherfixup> tag has a
+ targetop attribute which lists the
+ name of the particular CALLOTHER operation it substitutes for.
+
+ 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.
+
+
+ With no changes pending, the column will show one of the three main values:
+
+
+
+
<blank>
+
+
+ A blank Status column indicates that the element is a core part of the
+ specification, originating from one of the specification files.
+ These elements cannot be changed or removed.
+
+
+
extension
+
+
+ Indicates that the element is a program specific extension that has been
+ added to the specification.
+
+
+
override
+
+
+ Indicates that the element, which must be a callotherfixup,
+ 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.
+
+
+
+
+
+
+
+ If the user has either imported additional extensions or selected an extensions for removal but
+ has not yet clicked the Apply button in the Options dialog, the Status column
+ may show one of the following values, indicating a pending change.
+
+
+
+
install
+
+
+ Indicates a new extension that will be installed.
+
+
+
remove
+
+
+ Indicates an extension that is about to be removed.
+
+
+
replace
+
+
+ Indicates a new extension that will replace a current
+ extension with the same name.
+
+
+
override pending
+
+
+ Indicates a new extension that will override a core element when
+ it is installed.
+
+
+
+
+
+
+
+
+
+Importing a New Extension
+
+
+ The Import button at the bottom of the
+ "Specification Extensions" pane allows the user to import one of the
+ three element types, prototype,
+ callfixup, or callotherfixup,
+ 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 Import button brings up a File Chooser dialog,
+ from which the user must select their prepared XML file. Once Ok 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.
+
+
+ The final change to the program, installing the new extension, will not happen until the
+ Apply button, at the bottom of the Options dialog, is clicked.
+
+
+ The XML file describing the extension must have one of the tags,
+ <prototype>, <callfixup>, or <callotherfixup>,
+ 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 “SLEIGH Specification Files”.
+
+
+ In the case of prototype and callfixup
+ elements, extensions cannot replace existing core elements, so the new extension must not
+ have a name that matches an existing core element. If a new callotherfixup
+ extension has a targetop that matches a core element, the extension is automatically treated as an override.
+
+
+ Existing extensions can be replaced simply by importing a new extension with the same name or targetop.
+
+
+
+
+Removing an Extension
+
+
+ The Remove 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 extension or override.
+ Core elements of the specification cannot be removed.
+ Clicking the Remove button brings up a confirmation dialog, and if
+ Ok is clicked, the selected extension is marked for removal. The Status of the row
+ changes to remove, reflecting this.
+
+
+ The final change to the program, removing the extension, will not happen until the
+ Apply button, at the bottom of the Options dialog, is clicked.
+
+
+ If a prototype or callfixup 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 default convention, which is always a core element.
+ A function with matching call-fixup is changed to have no call-fixup.
+