Merge remote-tracking branch 'origin/GP-1715_DefaultEvaluationModel'

into patch (Closes #3906)
This commit is contained in:
Ryan Kurtz 2022-03-03 00:47:51 -05:00
commit 8ded9259b9
5 changed files with 86 additions and 67 deletions

View File

@ -15,12 +15,8 @@
*/
import ghidra.app.script.GhidraScript;
import ghidra.program.database.ProgramDB;
import ghidra.program.model.lang.*;
import ghidra.program.model.listing.IncompatibleLanguageException;
import ghidra.program.util.LanguageTranslator;
import ghidra.program.util.LanguageTranslatorAdapter;
import ghidra.program.model.lang.Language;
import ghidra.util.Msg;
import ghidra.util.exception.AssertException;
public class ForceRedisassembly extends GhidraScript {
@ -33,50 +29,8 @@ public class ForceRedisassembly extends GhidraScript {
}
ProgramDB program = (ProgramDB) currentProgram;
Language lang = program.getLanguage();
Language lang = null;
LanguageTranslator translator =
new MyLanguageTranslator(lang.getLanguageID(), lang.getVersion());
if (!translator.isValid()) {
return;
}
program.setLanguage(translator, program.getCompilerSpec().getCompilerSpecID(), true,
monitor);
}
private static class MyLanguageTranslator extends LanguageTranslatorAdapter {
protected MyLanguageTranslator(LanguageID languageId, int version) {
super(languageId, version, languageId, version);
}
@Override
public boolean isValid() {
if (super.isValid()) {
try {
validateDefaultSpaceMap();
}
catch (IncompatibleLanguageException e) {
throw new AssertException();
}
Register newContextReg = getNewLanguage().getContextBaseRegister();
if (newContextReg != Register.NO_CONTEXT) {
Register oldContextReg = getOldLanguage().getContextBaseRegister();
if (oldContextReg != Register.NO_CONTEXT ||
!isSameRegisterConstruction(oldContextReg, newContextReg)) {
throw new AssertException();
}
}
return true;
}
return false;
}
@Override
public String toString() {
return "[" + getOldLanguageID() + " (Version " + getOldVersion() + ")] -> [" +
getNewLanguageID() + " (Version " + getNewVersion() +
")] {Forced Re-Disassembly Translator}";
}
program.setLanguage(lang, null, true, monitor);
}
}

View File

@ -25,7 +25,9 @@ import ghidra.framework.options.Options;
import ghidra.program.model.lang.*;
import ghidra.program.model.listing.Program;
import ghidra.util.*;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
import ghidra.xml.XmlParseException;
/**
* A Program-specific version of the {@link CompilerSpec}.
@ -119,20 +121,19 @@ public class ProgramCompilerSpec extends BasicCompilerSpec {
ArrayList<PrototypeModel> finalList = new ArrayList<>();
TreeSet<String> currentNames = new TreeSet<>();
for (PrototypeModel model : allmodels) {
currentNames.add(model.getName());
if (usermodels.containsKey(model.getName())) {
continue;
}
currentNames.add(model.getName());
finalList.add(model); // Add original non-userdef models
}
usermodels.clear();
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;
}
Msg.warn(this,
"Cannot override prototype model " + model.getName() + " with extension");
continue;
}
markPrototypeAsExtension(model);
finalList.add(model);
@ -150,7 +151,13 @@ public class ProgramCompilerSpec extends BasicCompilerSpec {
if (evalCalledModel != null) {
evalCalledName = evalCalledModel.getName();
}
modelXrefs(finalList, defaultName, evalName, evalCalledName);
try {
modelXrefs(finalList, defaultName, evalName, evalCalledName);
}
catch (XmlParseException e) {
Msg.warn(this, "Prototype model extensions NOT installed: " + e.getMessage());
usermodels.clear();
}
if (usermodels.isEmpty()) {
usermodels = null;
}
@ -335,7 +342,7 @@ public class ProgramCompilerSpec extends BasicCompilerSpec {
case EVAL_CALLED:
return evalCalledModel; // TODO: Currently no option
}
return null;
return defaultModel;
}
/**
@ -370,6 +377,21 @@ public class ProgramCompilerSpec extends BasicCompilerSpec {
EVALUATION_MODEL_PROPERTY_NAME);
}
/**
* Reset options to default (for this CompilerSpec)
* This is for setLanguage to clear out strings that might belong to the old language.
* @param monitor is the monitor for checking cancellation
* @throws CancelledException if operation is cancelled externally
*/
protected void resetProgramOptions(TaskMonitor monitor) throws CancelledException {
Options decompilerPropertyList = program.getOptions(DECOMPILER_PROPERTY_LIST_NAME);
decompilerPropertyList.restoreDefaultValue(EVALUATION_MODEL_PROPERTY_NAME);
if (decompilerPropertyList.contains(DECOMPILER_OUTPUT_LANGUAGE)) {
decompilerPropertyList.restoreDefaultValue(DECOMPILER_OUTPUT_LANGUAGE);
}
SpecExtension.clearAllExtensions(program, monitor);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {

View File

@ -2051,6 +2051,7 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
setEventsEnabled(false);
try {
boolean redisassemblyRequired = true;
LanguageID oldLanguageId = languageID;
int oldLanguageVersion = languageVersion;
int oldLanguageMinorVersion = languageMinorVersion;
if (translator != null) {
@ -2079,6 +2080,12 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
if (newCompilerSpecID != null) {
compilerSpec = ProgramCompilerSpec.getProgramCompilerSpec(this,
language.getCompilerSpecByID(newCompilerSpecID));
if (!oldLanguageId.equals(languageID) ||
!compilerSpecID.equals(newCompilerSpecID)) {
if (compilerSpec instanceof ProgramCompilerSpec) {
((ProgramCompilerSpec) compilerSpec).resetProgramOptions(monitor);
}
}
}
compilerSpecID = compilerSpec.getCompilerSpecID();
languageVersion = language.getVersion();

View File

@ -513,6 +513,24 @@ public class SpecExtension {
throw new XmlParseException("Unknown compiler spec extension: " + elName);
}
/**
* Clear all spec extension options for the given program.
* This is intended for internal use. ProgramDB.installExtensions() must be called at
* some later time to see the effect.
* @param program is the given Program
* @param monitor is a monitor for the operation
* @throws CancelledException if something externally cancels the operation
*/
protected static void clearAllExtensions(Program program, TaskMonitor monitor)
throws CancelledException {
Options specOptions = program.getOptions(SpecExtension.SPEC_EXTENSION);
List<String> optionNames = specOptions.getOptionNames();
for (String name : optionNames) {
monitor.checkCanceled();
specOptions.removeOption(name);
}
}
/**
* Check that the proposed callfixup extension does not collide with built-in fixups
* @param doc is info about the proposed extension

View File

@ -375,7 +375,7 @@ public class BasicCompilerSpec implements CompilerSpec {
case EVAL_CALLED:
return evalCalledModel;
}
return null;
return defaultModel;
}
@Override
@ -453,17 +453,35 @@ public class BasicCompilerSpec implements CompilerSpec {
* Build the model arrays given a complete list of models.
* The array -models- contains all normal PrototypeModel objects
* The array -allmodels- contains all models, including merge models.
* We also check that a suitable model exists that matches a desired default name.
* In principle, the XML schema should guarantee that the model exists, but if for some reason
* it doesn't, an exception is thrown.
*
* @param modelList is the complete list of models
* @param putativeDefaultName is the desired name of the default model
* @throws XmlParseException if a suitable default model cannot be found
*/
private void buildModelArrays(List<PrototypeModel> modelList) {
private void buildModelArrays(List<PrototypeModel> modelList, String putativeDefaultName)
throws XmlParseException {
if (putativeDefaultName == null) {
throw new XmlParseException("Compiler Spec " + description.getCompilerSpecName() +
" does not provide a default prototype");
}
int fullcount = 0;
int resolvecount = 0;
boolean foundDefault = false;
for (PrototypeModel model : modelList) {
fullcount += 1;
if (model.isMerged()) {
resolvecount += 1;
}
else if (putativeDefaultName.equals(model.getName())) {
foundDefault = true; // Matching name AND not a merged model
}
}
if (!foundDefault) {
throw new XmlParseException("Could not find default model " + putativeDefaultName +
"for Compiler Spec " + description.getCompilerSpecName());
}
models = new PrototypeModel[fullcount - resolvecount];
allmodels = new PrototypeModel[fullcount];
@ -485,16 +503,19 @@ public class BasicCompilerSpec implements CompilerSpec {
/**
* Establish cross referencing to prototype models.
* All xrefs are regenerated from a single complete list of PrototypeModels
* All xrefs are regenerated from a single complete list of PrototypeModels.
* The modelList must provide a model with name matching defaultName or
* an exception is thrown. (In theory the schema guarantees this model always exists)
*
* @param modelList is the complete list of models
* @param defaultName is the name to use for the default model (or null)
* @param defaultName is the name to use for the default model
* @param evalCurrent is the name to use for evaluating the current function (or null)
* @param evalCalled is the name to use for evaluating called functions (or null)
* @throws XmlParseException if there is no model matching defaultName
*/
protected void modelXrefs(List<PrototypeModel> modelList, String defaultName,
String evalCurrent, String evalCalled) {
buildModelArrays(modelList);
String evalCurrent, String evalCalled) throws XmlParseException {
buildModelArrays(modelList, defaultName);
callingConventionMap = new HashMap<>();
for (PrototypeModel model : models) {
String name = model.getName();
@ -503,10 +524,7 @@ public class BasicCompilerSpec implements CompilerSpec {
}
}
defaultModel = null;
if (defaultName != null) {
defaultModel = callingConventionMap.get(defaultName);
}
defaultModel = callingConventionMap.get(defaultName);
evalCurrentModel = defaultModel; // The default evaluation is to assume default model
evalCalledModel = defaultModel;