diff --git a/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/app/plugin/core/datamgr/actions/DataTypeWriterTask.java b/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/app/plugin/core/datamgr/actions/DataTypeWriterTask.java
new file mode 100644
index 0000000000..56aeb1d088
--- /dev/null
+++ b/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/app/plugin/core/datamgr/actions/DataTypeWriterTask.java
@@ -0,0 +1,71 @@
+/* ###
+ * 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.datamgr.actions;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.List;
+
+import com.google.gson.JsonObject;
+
+import docking.widgets.tree.GTree;
+import ghidra.program.model.data.DataType;
+import ghidra.program.model.data.DataTypeManager;
+import ghidra.program.model.data.ISF.IsfDataTypeWriter;
+import ghidra.util.Msg;
+import ghidra.util.exception.CancelledException;
+import ghidra.util.task.Task;
+import ghidra.util.task.TaskMonitor;
+
+public class DataTypeWriterTask extends Task {
+
+ private final DataTypeManager programDataTypeMgr;
+ private final List dataTypeList;
+ private final File file;
+ private final GTree gTree;
+
+ public DataTypeWriterTask(GTree gTree, DataTypeManager programDataTypeMgr, List dataTypeList, File file) {
+ super("Export Data Types", true, false, true);
+ this.gTree = gTree;
+ this.programDataTypeMgr = programDataTypeMgr;
+ this.dataTypeList = dataTypeList;
+ this.file = file;
+ }
+
+ @Override
+ public void run(TaskMonitor monitor) {
+ try {
+ //monitor.setMessage("Export to " + file.getName() + "...");
+ FileWriter baseWriter = file == null ? null : new FileWriter(file);
+ IsfDataTypeWriter dataTypeWriter = new IsfDataTypeWriter(programDataTypeMgr, dataTypeList, baseWriter);
+
+ try {
+ JsonObject object = dataTypeWriter.getRootObject(monitor);
+ if (file != null) {
+ dataTypeWriter.write(object);
+ }
+ } finally {
+ dataTypeWriter.close();
+ }
+ } catch (CancelledException e) {
+ // user cancelled; ignore
+ } catch (IOException e) {
+ Msg.showError(getClass(), gTree, "Export Data Types Failed", "Error exporting Data Types: " + e);
+ return;
+ }
+ }
+}
diff --git a/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/app/plugin/core/datamgr/actions/ExportToIsfAction.java b/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/app/plugin/core/datamgr/actions/ExportToIsfAction.java
index 893caf3bea..61be0bd957 100644
--- a/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/app/plugin/core/datamgr/actions/ExportToIsfAction.java
+++ b/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/app/plugin/core/datamgr/actions/ExportToIsfAction.java
@@ -188,14 +188,14 @@ public class ExportToIsfAction extends DockingAction {
fileChooser.dispose();
}
- private class DataTypeWriterTask extends Task {
+ public class DataTypeWriterTask extends Task {
private final DataTypeManager programDataTypeMgr;
private final List dataTypeList;
private final File file;
private final GTree gTree;
- DataTypeWriterTask(GTree gTree, DataTypeManager programDataTypeMgr,
+ public DataTypeWriterTask(GTree gTree, DataTypeManager programDataTypeMgr,
List dataTypeList, File file) {
super("Export Data Types", true, false, true);
this.gTree = gTree;
@@ -209,12 +209,9 @@ public class ExportToIsfAction extends DockingAction {
try {
monitor.setMessage("Export to " + file.getName() + "...");
IsfDataTypeWriter dataTypeWriter =
- new IsfDataTypeWriter(programDataTypeMgr, new FileWriter(file));
+ new IsfDataTypeWriter(programDataTypeMgr, dataTypeList, new FileWriter(file));
try {
- for (DataType dataType : dataTypeList) {
- dataTypeWriter.requestType(dataType);
- }
JsonObject object = dataTypeWriter.getRootObject(monitor);
dataTypeWriter.write(object);
}
diff --git a/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/dbg/isf/IsfClientHandler.java b/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/dbg/isf/IsfClientHandler.java
index 05f1b64011..c2a747a7ed 100644
--- a/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/dbg/isf/IsfClientHandler.java
+++ b/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/dbg/isf/IsfClientHandler.java
@@ -130,7 +130,7 @@ public class IsfClientHandler {
private String lookType(String ns, String key) throws IOException {
IsfDataTypeWriter isfWriter = createDataTypeWriter(server.getDataTypeManager(ns));
isfWriter.setSkipSymbols(true);
- isfWriter.requestType(key);
+ //isfWriter.requestType(key);
return writeFrom(isfWriter);
}
@@ -198,7 +198,7 @@ public class IsfClientHandler {
private IsfDataTypeWriter createDataTypeWriter(DataTypeManager dtm) throws IOException {
StringWriter out = new StringWriter();
- return new IsfDataTypeWriter(dtm, out);
+ return new IsfDataTypeWriter(dtm, null, out);
}
private String writeFrom(IsfDataTypeWriter dataTypeWriter) throws IOException {
diff --git a/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/AbstractIsfObject.java b/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/AbstractIsfObject.java
new file mode 100644
index 0000000000..e793e7d3eb
--- /dev/null
+++ b/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/AbstractIsfObject.java
@@ -0,0 +1,60 @@
+/* ###
+ * 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.data.ISF;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import ghidra.docking.settings.Settings;
+import ghidra.docking.settings.SettingsDefinition;
+import ghidra.program.model.data.DataType;
+import ghidra.program.model.data.ISF.AbstractIsfWriter.Exclude;
+
+public abstract class AbstractIsfObject implements IsfObject {
+
+ @Exclude
+ public String name;
+ @Exclude
+ public String location;
+ @Exclude
+ public List settings;
+
+ public AbstractIsfObject(DataType dt) {
+ if (dt != null) {
+ name = dt.getName();
+ location = dt.getCategoryPath().getPath();
+ Settings defaultSettings = dt.getDefaultSettings();
+ processSettings(dt, defaultSettings);
+ }
+ }
+
+ protected void processSettings(DataType dt, Settings defaultSettings) {
+ SettingsDefinition[] settingsDefinitions = dt.getSettingsDefinitions();
+ for (SettingsDefinition def : settingsDefinitions) {
+ if (def.hasValue(defaultSettings)) {
+ settings = new ArrayList<>();
+ String[] names = defaultSettings.getNames();
+ for (String n : names) {
+ Object value = defaultSettings.getValue(n);
+ if (value != null) {
+ IsfSetting setting = new IsfSetting(n, value);
+ settings.add(setting);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/AbstractIsfWriter.java b/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/AbstractIsfWriter.java
new file mode 100644
index 0000000000..9afd82d11e
--- /dev/null
+++ b/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/AbstractIsfWriter.java
@@ -0,0 +1,108 @@
+/* ###
+ * 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.data.ISF;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.Writer;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import com.google.gson.ExclusionStrategy;
+import com.google.gson.FieldAttributes;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.stream.JsonWriter;
+
+import ghidra.util.exception.CancelledException;
+import ghidra.util.task.TaskMonitor;
+
+public abstract class AbstractIsfWriter implements Closeable {
+
+ protected JsonWriter writer;
+ protected Gson gson = new GsonBuilder().setPrettyPrinting().create();
+
+ protected JsonObject root = new JsonObject();
+ protected JsonArray objects = new JsonArray();
+
+ public AbstractIsfWriter(Writer baseWriter) throws IOException {
+ if (writer != null) {
+ this.writer = new JsonWriter(baseWriter);
+ writer.setIndent(" ");
+ }
+ this.gson = new GsonBuilder().addSerializationExclusionStrategy(strategy).setPrettyPrinting().create();
+ }
+
+ protected abstract void genRoot(TaskMonitor monitor) throws CancelledException, IOException;
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(ElementType.FIELD)
+ public @interface Exclude {
+ // EMPTY
+ }
+
+ // Am setting this as the default, but it's possible we may want more latitude
+ // in the future
+ protected boolean STRICT = true;
+
+ // @Exclude used for properties that might be desirable for a non-STRICT
+ // implementation.
+ ExclusionStrategy strategy = new ExclusionStrategy() {
+ @Override
+ public boolean shouldSkipClass(Class> clazz) {
+ return false;
+ }
+
+ @Override
+ public boolean shouldSkipField(FieldAttributes field) {
+ return STRICT && field.getAnnotation(Exclude.class) != null;
+ }
+ };
+
+ public JsonObject getRootObject(TaskMonitor monitor) throws CancelledException, IOException {
+ genRoot(monitor);
+ return root;
+ }
+
+ public JsonArray getResults() {
+ return objects;
+ }
+
+ public JsonElement getTree(Object obj) {
+ return gson.toJsonTree(obj);
+ }
+
+ public Object getObject(JsonElement element, Class extends Object> clazz) {
+ return gson.fromJson(element, clazz);
+ }
+
+ public void write(JsonObject object) {
+ gson.toJson(object, writer);
+ }
+
+ public void close() throws IOException {
+ if (writer != null) {
+ writer.flush();
+ writer.close();
+ }
+ }
+
+}
diff --git a/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfBuiltIn.java b/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfBuiltIn.java
index b4311bd6ee..394d19f544 100644
--- a/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfBuiltIn.java
+++ b/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfBuiltIn.java
@@ -17,16 +17,15 @@ package ghidra.program.model.data.ISF;
import ghidra.program.model.data.BuiltInDataType;
-public class IsfBuiltIn implements IsfObject {
+public class IsfBuiltIn extends AbstractIsfObject {
public Integer size;
- public Boolean signed;
public String kind;
public String endian;
public IsfBuiltIn(BuiltInDataType builtin) {
+ super(builtin);
size = IsfUtilities.getLength(builtin);
- signed = IsfUtilities.getSigned(builtin);
kind = IsfUtilities.getBuiltInKind(builtin);
endian = IsfUtilities.getEndianness(builtin);
}
diff --git a/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfComponent.java b/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfComponent.java
index 1c5f07b49c..87bd7cd8a0 100644
--- a/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfComponent.java
+++ b/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfComponent.java
@@ -16,9 +16,9 @@
package ghidra.program.model.data.ISF;
import ghidra.program.model.data.DataTypeComponent;
-import ghidra.program.model.data.ISF.IsfDataTypeWriter.Exclude;
+import ghidra.program.model.data.ISF.AbstractIsfWriter.Exclude;
-public class IsfComponent implements IsfObject {
+public class IsfComponent extends AbstractIsfObject {
public Integer offset;
public IsfObject type;
@@ -30,16 +30,25 @@ public class IsfComponent implements IsfObject {
@Exclude
public String field_name;
@Exclude
+ public Boolean noFieldName;
+ @Exclude
public String comment;
+
public IsfComponent(DataTypeComponent component, IsfObject typeObj) {
+ super(component.getDataType());
offset = component.getOffset();
type = typeObj;
field_name = component.getFieldName();
+ if (field_name == null || field_name.equals("")) {
+ noFieldName = true;
+ }
ordinal = component.getOrdinal();
length = component.getLength();
comment = component.getComment();
+
+ processSettings(component.getDataType(), component.getDefaultSettings());
}
}
diff --git a/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfComposite.java b/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfComposite.java
index 26d91e92d7..fe8cfd17a6 100644
--- a/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfComposite.java
+++ b/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfComposite.java
@@ -15,52 +15,50 @@
*/
package ghidra.program.model.data.ISF;
-import java.util.*;
-
import com.google.gson.JsonObject;
-import ghidra.program.model.data.*;
-import ghidra.program.model.data.ISF.IsfDataTypeWriter.Exclude;
+import ghidra.program.model.data.Composite;
+import ghidra.program.model.data.DataTypeComponent;
+import ghidra.program.model.data.Structure;
import ghidra.util.task.TaskMonitor;
-public class IsfComposite implements IsfObject {
+public class IsfComposite extends AbstractIsfObject {
public String kind;
public Integer size;
public JsonObject fields;
-
- @Exclude
- public int alignment;
-
+
public IsfComposite(Composite composite, IsfDataTypeWriter writer, TaskMonitor monitor) {
+ super(composite);
size = composite.getLength();
kind = composite instanceof Structure ? "struct" : "union";
- alignment = composite.getAlignment();
DataTypeComponent[] components = composite.getComponents();
- Map comps = new HashMap<>();
- for (DataTypeComponent component : components) {
- String key = component.getFieldName();
- if (key == null) {
- key = component.getDefaultFieldName();
- }
- comps.put(key, component);
+ if (components.length == 0) {
+ // NB: composite.getLength always returns > 0
+ size = 0;
}
- ArrayList keylist = new ArrayList<>(comps.keySet());
- Collections.sort(keylist);
-
fields = new JsonObject();
- for (String key : keylist) {
+ for (DataTypeComponent component : components) {
if (monitor.isCancelled()) {
break;
}
- DataTypeComponent component = comps.get(key);
IsfObject type = writer.getObjectTypeDeclaration(component);
- IsfComponent cobj = new IsfComponent(component, type);
+ IsfComponent cobj = getComponent(component, type);
+ String key = component.getFieldName();
+ if (key == null) {
+ key = DataTypeComponent.DEFAULT_FIELD_NAME_PREFIX + component.getOrdinal();
+ if (component.getParent() instanceof Structure) {
+ key += "_0x" + Integer.toHexString(component.getOffset());
+ }
+ }
fields.add(key, writer.getTree(cobj));
}
+ }
+ protected IsfComponent getComponent(DataTypeComponent component, IsfObject type) {
+ return new IsfComponent(component, type);
}
}
diff --git a/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfDataTypeArray.java b/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfDataTypeArray.java
index 5478d3b574..7551629b82 100644
--- a/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfDataTypeArray.java
+++ b/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfDataTypeArray.java
@@ -17,13 +17,14 @@ package ghidra.program.model.data.ISF;
import ghidra.program.model.data.Array;
-public class IsfDataTypeArray implements IsfObject {
+public class IsfDataTypeArray extends AbstractIsfObject {
public String kind;
public Integer count;
public IsfObject subtype;
public IsfDataTypeArray(Array arr, IsfObject typeObj) {
+ super(arr);
kind = IsfUtilities.getKind(arr);
count = arr.getNumElements();
subtype = typeObj;
diff --git a/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfDataTypeBitField.java b/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfDataTypeBitField.java
index 9ee584f02e..ff74ec0819 100644
--- a/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfDataTypeBitField.java
+++ b/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfDataTypeBitField.java
@@ -16,9 +16,9 @@
package ghidra.program.model.data.ISF;
import ghidra.program.model.data.BitFieldDataType;
-import ghidra.program.model.data.ISF.IsfDataTypeWriter.Exclude;
+import ghidra.program.model.data.ISF.AbstractIsfWriter.Exclude;
-public class IsfDataTypeBitField implements IsfObject {
+public class IsfDataTypeBitField extends AbstractIsfObject {
public String kind;
public Integer bit_length;
@@ -31,6 +31,7 @@ public class IsfDataTypeBitField implements IsfObject {
private int storage_size;
public IsfDataTypeBitField(BitFieldDataType bf, int componentOffset, IsfObject typeObj) {
+ super(bf);
kind = IsfUtilities.getKind(bf);
bit_length = bf.getBitSize();
bit_offset = bf.getBitOffset();
diff --git a/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfDataTypeDefault.java b/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfDataTypeDefault.java
index 2fb90b2912..67bd6ae636 100644
--- a/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfDataTypeDefault.java
+++ b/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfDataTypeDefault.java
@@ -17,14 +17,15 @@ package ghidra.program.model.data.ISF;
import ghidra.program.model.data.DataType;
-public class IsfDataTypeDefault implements IsfObject {
+public class IsfDataTypeDefault extends AbstractIsfObject {
public String kind;
- public String name;
+ int size;
public IsfDataTypeDefault(DataType dt) {
+ super(dt);
kind = IsfUtilities.getKind(dt);
- name = dt.getName();
+ size = dt.getLength();
}
}
diff --git a/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfDataTypeTypeDef.java b/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfDataTypeTypeDef.java
index 82e5b32857..c37fff6355 100644
--- a/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfDataTypeTypeDef.java
+++ b/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfDataTypeTypeDef.java
@@ -17,12 +17,13 @@ package ghidra.program.model.data.ISF;
import ghidra.program.model.data.DataType;
-public class IsfDataTypeTypeDef implements IsfObject {
+public class IsfDataTypeTypeDef extends AbstractIsfObject {
public String kind;
public IsfObject subtype;
public IsfDataTypeTypeDef(DataType dt, IsfObject typeObj) {
+ super(dt);
kind = IsfUtilities.getKind(dt);
subtype = typeObj;
}
diff --git a/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfDataTypeWriter.java b/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfDataTypeWriter.java
index de19527332..e12ebf42a1 100644
--- a/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfDataTypeWriter.java
+++ b/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfDataTypeWriter.java
@@ -17,20 +17,43 @@ package ghidra.program.model.data.ISF;
import java.io.IOException;
import java.io.Writer;
-import java.lang.annotation.*;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
import java.util.Map.Entry;
-import com.google.gson.*;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
import com.google.gson.stream.JsonWriter;
import ghidra.program.database.data.ProgramDataTypeManager;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressFormatException;
-import ghidra.program.model.data.*;
+import ghidra.program.model.data.Array;
+import ghidra.program.model.data.BitFieldDataType;
+import ghidra.program.model.data.BuiltInDataType;
+import ghidra.program.model.data.Composite;
+import ghidra.program.model.data.DataOrganization;
+import ghidra.program.model.data.DataOrganizationImpl;
+import ghidra.program.model.data.DataType;
+import ghidra.program.model.data.DataTypeComponent;
+import ghidra.program.model.data.DataTypeManager;
+import ghidra.program.model.data.Dynamic;
import ghidra.program.model.data.Enum;
+import ghidra.program.model.data.FactoryDataType;
+import ghidra.program.model.data.FunctionDefinition;
+import ghidra.program.model.data.Pointer;
+import ghidra.program.model.data.TypeDef;
import ghidra.program.model.listing.Program;
-import ghidra.program.model.symbol.*;
+import ghidra.program.model.symbol.Reference;
+import ghidra.program.model.symbol.ReferenceIterator;
+import ghidra.program.model.symbol.ReferenceManager;
+import ghidra.program.model.symbol.Symbol;
+import ghidra.program.model.symbol.SymbolIterator;
+import ghidra.program.model.symbol.SymbolTable;
import ghidra.util.Msg;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
@@ -40,29 +63,28 @@ import ghidra.util.task.TaskMonitor;
*
* The ISF JSON should be valid for Volatility is STRICT==true.
*/
-public class IsfDataTypeWriter {
+public class IsfDataTypeWriter extends AbstractIsfWriter {
- private Map resolved = new HashMap<>();
+ protected Map resolved = new HashMap<>();
private Map resolvedTypeMap = new HashMap<>();
- private List deferredKeys = new ArrayList<>();
+ public List deferredKeys = new ArrayList<>();
private Writer baseWriter;
- private JsonWriter writer;
- private Gson gson = new GsonBuilder().setPrettyPrinting().create();
- private DataTypeManager dtm;
+ protected DataTypeManager dtm;
private DataOrganization dataOrganization;
- private JsonObject data = new JsonObject();
- private JsonObject metadata = new JsonObject();
- private JsonObject baseTypes = new JsonObject();
- private JsonObject userTypes = new JsonObject();
- private JsonObject enums = new JsonObject();
- private JsonObject symbols = new JsonObject();
+ protected JsonObject data = new JsonObject();
+ protected JsonElement metadata;
+ protected JsonElement baseTypes;
+ protected JsonElement userTypes;
+ protected JsonElement enums;
+ protected JsonElement functions;
+ protected JsonElement symbols;
private List requestedAddresses = new ArrayList<>();
private List requestedSymbols = new ArrayList<>();
- private List requestedTypes = new ArrayList<>();
+ // private List requestedTypes = new ArrayList<>();
private List requestedDataTypes = new ArrayList<>();
private boolean skipSymbols = false;
private boolean skipTypes = false;
@@ -70,11 +92,13 @@ public class IsfDataTypeWriter {
/**
* Constructs a new instance of this class using the given writer
*
- * @param dtm data-type manager corresponding to target program or null for default
+ * @param dtm data-type manager corresponding to target program or null
+ * for default
* @param baseWriter the writer to use when writing data types
* @throws IOException if there is an exception writing the output
*/
- public IsfDataTypeWriter(DataTypeManager dtm, Writer baseWriter) throws IOException {
+ public IsfDataTypeWriter(DataTypeManager dtm, List target, Writer baseWriter) throws IOException {
+ super(baseWriter);
this.dtm = dtm;
if (dtm != null) {
dataOrganization = dtm.getDataOrganization();
@@ -82,66 +106,43 @@ public class IsfDataTypeWriter {
if (dataOrganization == null) {
dataOrganization = DataOrganizationImpl.getDefaultOrganization();
}
- this.baseWriter = baseWriter;
- this.writer = new JsonWriter(baseWriter);
- writer.setIndent(" ");
- this.gson = new GsonBuilder()
- .addSerializationExclusionStrategy(strategy)
- .setPrettyPrinting()
- .create();
+
+ metadata = new JsonObject();
+ baseTypes = new JsonObject();
+ userTypes = new JsonObject();
+ enums = new JsonObject();
+ functions = new JsonObject();
+ symbols = new JsonObject();
+ requestedDataTypes = target;
+ STRICT = true;
}
- @Retention(RetentionPolicy.RUNTIME)
- @Target(ElementType.FIELD)
- public @interface Exclude {
- //EMPTY
- }
-
- // Am setting this as the default, but it's possible we may want more latitude in the future
- private boolean STRICT = true;
-
- // @Exclude used for properties that might be desirable for a non-STRICT implementation.
- ExclusionStrategy strategy = new ExclusionStrategy() {
- @Override
- public boolean shouldSkipClass(Class> clazz) {
- return false;
- }
-
- @Override
- public boolean shouldSkipField(FieldAttributes field) {
- return STRICT && field.getAnnotation(Exclude.class) != null;
- }
- };
-
- /**
- * Exports all data types in the list as ISF JSON.
- *
- * @param monitor the task monitor
- * @return the resultant JSON object
- * @throws IOException if there is an exception writing the output
- * @throws CancelledException if the action is cancelled by the user
- */
- public JsonObject getRootObject(TaskMonitor monitor)
- throws IOException, CancelledException {
-
+ @Override
+ protected void genRoot(TaskMonitor monitor) throws CancelledException, IOException {
genMetadata();
genTypes(monitor);
- genSymbols();
- genRoot();
+ genSymbols(monitor);
- return data;
- }
-
- private void genRoot() {
data.add("metadata", metadata);
data.add("base_types", baseTypes);
data.add("user_types", userTypes);
data.add("enums", enums);
- // Would be nice to support this in the futere, but Volatility does not
- //data.add("typedefs", typedefs);
+ // Would be nice to support this in the future, but Volatility does not
+ // data.add("functions", functions);
data.add("symbols", symbols);
}
+ public void add(JsonElement parent, String optKey, JsonElement child) {
+ if (parent instanceof JsonObject) {
+ JsonObject p = (JsonObject) parent;
+ p.add(optKey, child);
+ }
+ if (parent instanceof JsonArray) {
+ JsonArray p = (JsonArray) parent;
+ p.add(child);
+ }
+ }
+
private void genMetadata() {
String oskey = "UNKNOWN";
if (dtm instanceof ProgramDataTypeManager) {
@@ -154,20 +155,21 @@ public class IsfDataTypeWriter {
oskey = metaData.get("Compiler ID");
if (metaData.containsKey("PDB Loaded")) {
os = gson.toJsonTree(new IsfWinOS(metaData));
- }
- else if (metaData.containsKey("Executable Format")) {
+ } else if (metaData.containsKey("Executable Format")) {
if (metaData.get("Executable Format").contains("ELF")) {
oskey = "linux";
os = gson.toJsonTree(new IsfLinuxOS(gson, metaData));
}
}
- metadata.addProperty("format", "6.2.0");
- metadata.add("producer", producer);
- metadata.add(oskey, os);
+ if (metadata instanceof JsonObject) {
+ ((JsonObject) metadata).addProperty("format", "6.2.0");
+ }
+ add(metadata, "producer", producer);
+ add(metadata, oskey, os);
}
}
- private void genSymbols() {
+ private void genSymbols(TaskMonitor monitor) {
if (!skipSymbols && dtm instanceof ProgramDataTypeManager) {
ProgramDataTypeManager pgmDtm = (ProgramDataTypeManager) dtm;
Program program = pgmDtm.getProgram();
@@ -194,18 +196,15 @@ public class IsfDataTypeWriter {
Symbol symbol = iterator.next();
symbolToJson(imageBase, symbolTable, linkages, map, symbol);
}
- }
- else {
+ } else {
for (Address addr : requestedAddresses) {
- Symbol[] symsFromAddr =
- symbolTable.getSymbols(addr.add(imageBase.getOffset()));
+ Symbol[] symsFromAddr = symbolTable.getSymbols(addr.add(imageBase.getOffset()));
for (Symbol symbol : symsFromAddr) {
symbolToJson(imageBase, symbolTable, linkages, map, symbol);
}
}
}
- }
- else {
+ } else {
for (String key : requestedSymbols) {
SymbolIterator iter = symbolTable.getSymbols(key);
while (iter.hasNext()) {
@@ -215,33 +214,29 @@ public class IsfDataTypeWriter {
}
}
for (Entry entry : map.entrySet()) {
- symbols.add(entry.getKey(), entry.getValue());
+ add(symbols, entry.getKey(), entry.getValue());
}
for (Entry entry : map.entrySet()) {
if (entry.getKey().startsWith("_")) {
String nu = entry.getKey().substring(1);
- if (symbols.get(nu) == null) {
- symbols.add(nu, entry.getValue());
- }
+ add(symbols, nu, entry.getValue());
}
}
}
}
- private void genTypes(TaskMonitor monitor)
- throws CancelledException, IOException {
+ private void genTypes(TaskMonitor monitor) throws CancelledException, IOException {
if (skipTypes) {
return;
}
Map map = new HashMap<>();
if (requestedDataTypes.isEmpty()) {
dtm.getAllDataTypes(requestedDataTypes);
- baseTypes.add("pointer", getTree(new IsfTypedefPointer()));
- baseTypes.add("undefined", getTree(new IsfTypedefPointer()));
+ addSingletons();
}
monitor.initialize(requestedDataTypes.size());
for (DataType dataType : requestedDataTypes) {
- String key = dataType.getName();
+ String key = dataType.getPathName();
map.put(key, dataType);
}
@@ -260,10 +255,9 @@ public class IsfDataTypeWriter {
private void processMap(Map map, List keylist, TaskMonitor monitor)
throws CancelledException, IOException {
JsonObject obj = new JsonObject();
- int cnt = 0;
+ monitor.setMaximum(keylist.size());
for (String key : keylist) {
DataType dataType = map.get(key);
- monitor.checkCancelled();
if (key.contains(".conflict")) {
continue;
}
@@ -272,36 +266,29 @@ public class IsfDataTypeWriter {
continue;
}
if (dataType instanceof FunctionDefinition) {
- // Would be nice to support this in the futere, but Volatility does not
- //typedefs.add(dataType.getName(), obj);
- }
- else if (IsfUtilities.isBaseDataType(dataType)) {
- baseTypes.add(dataType.getName(), obj);
- }
- else if (dataType instanceof TypeDef) {
+ // Would be nice to support this in the future, but Volatility does not
+ add(functions, dataType.getPathName(), obj);
+ } else if (IsfUtilities.isBaseDataType(dataType)) {
+ add(baseTypes, dataType.getPathName(), obj);
+ } else if (dataType instanceof TypeDef) {
DataType baseDataType = ((TypeDef) dataType).getBaseDataType();
if (IsfUtilities.isBaseDataType(baseDataType)) {
- baseTypes.add(dataType.getName(), obj);
- }
- else if (baseDataType instanceof Enum) {
- enums.add(dataType.getName(), obj);
- }
- else {
- userTypes.add(dataType.getName(), obj);
+ add(baseTypes, dataType.getPathName(), obj);
+ } else if (baseDataType instanceof Enum) {
+ add(enums, dataType.getPathName(), obj);
+ } else {
+ add(userTypes, dataType.getPathName(), obj);
}
+ } else if (dataType instanceof Enum) {
+ add(enums, dataType.getPathName(), obj);
+ } else if (dataType instanceof Composite) {
+ add(userTypes, dataType.getPathName(), obj);
}
- else if (dataType instanceof Enum) {
- enums.add(dataType.getName(), obj);
- }
- else if (dataType instanceof Composite) {
- userTypes.add(dataType.getName(), obj);
- }
- monitor.setProgress(++cnt);
+ monitor.increment();
}
}
- private void symbolToJson(Address imageBase, SymbolTable symbolTable,
- Map linkages,
+ private void symbolToJson(Address imageBase, SymbolTable symbolTable, Map linkages,
Map map, Symbol symbol) {
String key = symbol.getName();
Address address = symbol.getAddress();
@@ -313,9 +300,12 @@ public class IsfDataTypeWriter {
sym.addProperty("linkage_name", linkage.getName());
sym.addProperty("address", linkage.getAddress().getOffset());
}
- }
- else {
- sym.addProperty("address", address.subtract(imageBase));
+ } else {
+ if (address.getAddressSpace().equals(imageBase.getAddressSpace())) {
+ sym.addProperty("address", address.subtract(imageBase));
+ } else {
+ sym.addProperty("address", address.getOffset());
+ }
}
map.put(symbol.getName(), sym);
if (!symbol.isPrimary()) {
@@ -332,8 +322,12 @@ public class IsfDataTypeWriter {
gson.toJson(obj, writer);
}
- JsonObject getObjectForDataType(DataType dt, TaskMonitor monitor)
- throws IOException, CancelledException {
+ protected void addSingletons() {
+ add(baseTypes, "pointer", getTree(newTypedefPointer(null)));
+ add(baseTypes, "undefined", getTree(newTypedefPointer(null)));
+ }
+
+ protected JsonObject getObjectForDataType(DataType dt, TaskMonitor monitor) throws IOException, CancelledException {
IsfObject isf = getIsfObject(dt, monitor);
if (isf != null) {
JsonObject jobj = (JsonObject) getTree(isf);
@@ -344,26 +338,27 @@ public class IsfDataTypeWriter {
}
/**
- * Writes the data type as ISF JSON using the underlying writer. For now, ignoring top-level
- * bit-fields and function defs as unsupported by ISF. Typedefs really deserve their own
- * category, but again unsupported.
+ * Writes the data type as ISF JSON using the underlying writer. For now,
+ * ignoring top-level bit-fields and function defs as unsupported by ISF.
+ * Typedefs really deserve their own category, but again unsupported.
*
- * @param dt the data type to write as ISF JSON
+ * @param dt the data type to write as ISF JSON
* @param monitor the task monitor
* @throws IOException if there is an exception writing the output
*/
- private IsfObject getIsfObject(DataType dt, TaskMonitor monitor)
- throws IOException, CancelledException {
+ protected IsfObject getIsfObject(DataType dt, TaskMonitor monitor) throws IOException, CancelledException {
if (dt == null) {
- Msg.error(this, "Shouldn't get here - null datatype passed");
- return null;
+ throw new IOException("Null datatype passed to getIsfObject");
}
if (dt instanceof FactoryDataType) {
Msg.error(this, "Factory data types may not be written - type: " + dt);
}
- if (dt instanceof Pointer || dt instanceof Array || dt instanceof BitFieldDataType) {
+ if (dt instanceof BitFieldDataType) {
+ Msg.error(this, "BitField data types may not be written - type: " + dt);
+ }
+ if (dt instanceof Pointer || dt instanceof Array) {
IsfObject type = getObjectDataType(IsfUtilities.getBaseDataType(dt));
- IsfObject obj = new IsfTypedObject(dt, type);
+ IsfObject obj = newTypedObject(dt, type);
return obj;
}
@@ -377,41 +372,33 @@ public class IsfDataTypeWriter {
if (dt instanceof Dynamic dynamic) {
DataType rep = dynamic.getReplacementBaseType();
return rep == null ? null : getIsfObject(rep, monitor);
- }
- else if (dt instanceof TypeDef typedef) {
+ } else if (dt instanceof TypeDef typedef) {
return getObjectTypeDef(typedef, monitor);
- }
- else if (dt instanceof Composite composite) {
+ } else if (dt instanceof Composite composite) {
return new IsfComposite(composite, this, monitor);
- }
- else if (dt instanceof Enum enumm) {
+ } else if (dt instanceof Enum enumm) {
return new IsfEnum(enumm);
- }
- else if (dt instanceof BuiltInDataType builtin) {
+ } else if (dt instanceof BuiltInDataType builtin) {
return new IsfBuiltIn(builtin);
- }
- else if (dt instanceof BitFieldDataType) {
+ } else if (dt instanceof BitFieldDataType) {
// skip - not hit
- }
- else if (dt instanceof FunctionDefinition) { ///FAIL
+ } else if (dt instanceof FunctionDefinition) { /// FAIL
// skip - not hit
- }
- else if (dt.equals(DataType.DEFAULT)) {
+ } else if (dt.equals(DataType.DEFAULT)) {
// skip - not hit
- }
- else {
+ } else {
Msg.warn(this, "Unable to write datatype. Type unrecognized: " + dt.getClass());
}
return null;
}
- private IsfObject resolve(DataType dt) {
+ public IsfObject resolve(DataType dt) {
if (resolved.containsKey(dt)) {
return resolved.get(dt);
}
- DataType resolvedType = resolvedTypeMap.get(dt.getName());
+ DataType resolvedType = resolvedTypeMap.get(dt.getPathName());
if (resolvedType != null) {
if (resolvedType.isEquivalent(dt)) {
return resolved.get(dt); // skip equivalent type with same name as a resolved type
@@ -425,31 +412,32 @@ public class IsfDataTypeWriter {
}
}
}
- Msg.warn(this, "WARNING! conflicting data type names: " + dt.getPathName() +
- " - " + resolvedType.getPathName());
+ Msg.warn(this,
+ "WARNING! conflicting data type names: " + dt.getPathName() + " - " + resolvedType.getPathName());
return resolved.get(dt);
}
- resolvedTypeMap.put(dt.getName(), dt);
+ resolvedTypeMap.put(dt.getPathName(), dt);
return null;
}
private void clearResolve(String typedefName, DataType baseType) {
if (baseType instanceof Composite || baseType instanceof Enum) {
// auto-typedef generated with composite and enum
- if (typedefName.equals(baseType.getName())) {
+ if (typedefName.equals(baseType.getPathName())) {
resolvedTypeMap.remove(typedefName);
return;
}
}
- // Inherited from DataTypeWriter (logic lost to time):
- // A comment explaining the special 'P' case would be helpful!! Smells like fish.
+ // Inherited from DataTypeWriter (logic lost to time):
+ // A comment explaining the special 'P' case would be helpful!! Smells like
+ // fish.
else if (baseType instanceof Pointer && typedefName.startsWith("P")) {
DataType dt = ((Pointer) baseType).getDataType();
if (dt instanceof TypeDef) {
dt = ((TypeDef) dt).getBaseDataType();
}
- if (dt instanceof Composite && dt.getName().equals(typedefName.substring(1))) {
+ if (dt instanceof Composite && dt.getPathName().equals(typedefName.substring(1))) {
// auto-pointer-typedef generated with composite
resolvedTypeMap.remove(typedefName);
return;
@@ -469,13 +457,11 @@ public class IsfDataTypeWriter {
int elementLen = replacementBaseType.getLength();
if (elementLen > 0) {
int elementCnt = (component.getLength() + elementLen - 1) / elementLen;
- return new IsfDynamicComponent(dynamic, type, elementCnt);
+ return newIsfDynamicComponent(dynamic, type, elementCnt);
}
- Msg.error(this,
- dynamic.getClass().getSimpleName() +
- " returned bad replacementBaseType: " +
- replacementBaseType.getClass().getSimpleName());
+ Msg.error(this, dynamic.getClass().getSimpleName() + " returned bad replacementBaseType: "
+ + replacementBaseType.getClass().getSimpleName());
}
}
return null;
@@ -492,7 +478,7 @@ public class IsfDataTypeWriter {
return getObjectDataType(dataType, -1);
}
- private IsfObject getObjectDataType(DataType dataType, int componentOffset) {
+ public IsfObject getObjectDataType(DataType dataType, int componentOffset) {
if (dataType == null) {
return new IsfDataTypeNull();
}
@@ -509,9 +495,9 @@ public class IsfDataTypeWriter {
IsfObject baseObject = getObjectDataType(IsfUtilities.getBaseDataType(dataType));
return new IsfDataTypeTypeDef(dataType, baseObject);
}
- if (dataType.getName().contains(".conflict")) {
- if (!deferredKeys.contains(dataType.getName())) {
- deferredKeys.add(dataType.getName());
+ if (dataType.getPathName().contains(".conflict")) {
+ if (!deferredKeys.contains(dataType.getPathName())) {
+ deferredKeys.add(dataType.getPathName());
}
}
return new IsfDataTypeDefault(dataType);
@@ -522,27 +508,21 @@ public class IsfDataTypeWriter {
*
* @throws CancelledException if the action is cancelled by the user
*/
- private IsfObject getObjectTypeDef(TypeDef typeDef, TaskMonitor monitor)
- throws CancelledException {
- //UNVERIFIED
+ protected IsfObject getObjectTypeDef(TypeDef typeDef, TaskMonitor monitor) throws CancelledException {
DataType dataType = typeDef.getDataType();
- String typedefName = typeDef.getDisplayName();
- String dataTypeName = dataType.getDisplayName();
- if (IsfUtilities.isIntegral(typedefName, dataTypeName)) {
- return new IsfTypedefIntegral(typeDef);
- }
+ String typedefName = typeDef.getPathName();
- DataType baseType = typeDef.getBaseDataType();
+ DataType baseType = typeDef.getDataType();
try {
if (baseType instanceof BuiltInDataType builtin) {
- return new IsfTypedefBase(typeDef);
+ return newTypedefBase(typeDef);
}
if (!(baseType instanceof Pointer)) {
- return getIsfObject(dataType, monitor);
+ IsfObject isfObject = getIsfObject(dataType, monitor);
+ return newTypedefUser(typeDef, isfObject);
}
- return new IsfTypedefPointer();
- }
- catch (Exception e) {
+ return newTypedefPointer(typeDef);
+ } catch (Exception e) {
Msg.error(this, "TypeDef error: " + e);
}
clearResolve(typedefName, baseType);
@@ -550,11 +530,7 @@ public class IsfDataTypeWriter {
return null;
}
- public JsonElement getTree(Object obj) {
- return gson.toJsonTree(obj);
- }
-
- public void requestAddress(String key) {
+ public void requestAddress(String key) throws IOException {
if (dtm instanceof ProgramDataTypeManager pgmDtm) {
try {
Address address = pgmDtm.getProgram().getMinAddress().getAddress(key);
@@ -563,9 +539,8 @@ public class IsfDataTypeWriter {
return;
}
requestedAddresses.add(address);
- }
- catch (AddressFormatException e) {
- e.printStackTrace();
+ } catch (AddressFormatException e) {
+ throw new IOException("Bad address format: " + key);
}
}
}
@@ -578,24 +553,6 @@ public class IsfDataTypeWriter {
requestedSymbols.add(symbol);
}
- public void requestType(String path) {
- requestedTypes.add(path);
- DataType dataType = dtm.getDataType(path);
- if (dataType == null) {
- Msg.error(this, path + " not found");
- return;
- }
- requestedDataTypes.add(dataType);
- }
-
- public void requestType(DataType dataType) {
- if (dataType == null) {
- Msg.error(this, dataType + " not found");
- return;
- }
- requestedDataTypes.add(dataType);
- }
-
public JsonWriter getWriter() {
return writer;
}
@@ -605,16 +562,6 @@ public class IsfDataTypeWriter {
return baseWriter.toString();
}
- public void close() {
- try {
- writer.flush();
- writer.close();
- }
- catch (IOException e) {
- e.printStackTrace();
- }
- }
-
public void setSkipSymbols(boolean val) {
skipSymbols = val;
}
@@ -623,7 +570,28 @@ public class IsfDataTypeWriter {
skipTypes = val;
}
- public void setStrict(boolean val) {
- STRICT = val;
+ public IsfTypedefBase newTypedefBase(TypeDef typeDef) {
+ return new IsfTypedefBase(typeDef);
}
+
+// public IsfTypedefIntegral newTypedefIntegral(TypeDef typeDef) {
+// return new IsfTypedefIntegral(typeDef);
+// }
+
+ public IsfTypedefPointer newTypedefPointer(TypeDef typeDef) {
+ return new IsfTypedefPointer(typeDef);
+ }
+
+ public IsfObject newTypedefUser(TypeDef typeDef, IsfObject object) {
+ return object;
+ }
+
+ public IsfTypedObject newTypedObject(DataType dt, IsfObject type) {
+ return new IsfTypedObject(dt, type);
+ }
+
+ public IsfObject newIsfDynamicComponent(Dynamic dynamic, IsfObject type, int elementCnt) {
+ return new IsfDynamicComponent(dynamic, type, elementCnt);
+ }
+
}
diff --git a/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfDynamicComponent.java b/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfDynamicComponent.java
index d25a0afe0e..fcad5d72ce 100644
--- a/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfDynamicComponent.java
+++ b/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfDynamicComponent.java
@@ -17,13 +17,14 @@ package ghidra.program.model.data.ISF;
import ghidra.program.model.data.Dynamic;
-public class IsfDynamicComponent implements IsfObject {
+public class IsfDynamicComponent extends AbstractIsfObject {
public String kind;
public Integer count;
public IsfObject subtype;
public IsfDynamicComponent(Dynamic dynamicType, IsfObject type, int elementCnt) {
+ super(dynamicType);
kind = "array";
subtype = type;
count = elementCnt;
diff --git a/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfEnum.java b/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfEnum.java
index 801a7620bb..0c450a6899 100644
--- a/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfEnum.java
+++ b/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfEnum.java
@@ -19,13 +19,14 @@ import com.google.gson.JsonObject;
import ghidra.program.model.data.Enum;
-public class IsfEnum implements IsfObject {
+public class IsfEnum extends AbstractIsfObject {
public Integer size;
public String base;
public JsonObject constants = new JsonObject();
public IsfEnum(Enum enumm) {
+ super(enumm);
size = enumm.getLength();
base = "int";
String[] names = enumm.getNames();
diff --git a/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfFunction.java b/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfFunction.java
index 4af99c709b..bcd0f2aded 100644
--- a/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfFunction.java
+++ b/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfFunction.java
@@ -15,11 +15,14 @@
*/
package ghidra.program.model.data.ISF;
-public class IsfFunction implements IsfObject {
+import ghidra.program.model.data.FunctionDefinition;
+
+public class IsfFunction extends AbstractIsfObject {
public String kind;
- public IsfFunction() {
+ public IsfFunction(FunctionDefinition def) {
+ super(def);
kind = "function";
}
diff --git a/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfFunctionPointer.java b/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfFunctionPointer.java
index 74183df1a2..8660301a0e 100644
--- a/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfFunctionPointer.java
+++ b/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfFunctionPointer.java
@@ -18,14 +18,15 @@ package ghidra.program.model.data.ISF;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.FunctionDefinition;
-public class IsfFunctionPointer implements IsfObject {
+public class IsfFunctionPointer extends AbstractIsfObject {
public String kind;
public IsfObject subtype;
public IsfFunctionPointer(FunctionDefinition def, DataType dt) {
+ super(def);
kind = "pointer";
- subtype = new IsfFunction();
+ subtype = new IsfFunction(def);
//TODO?
}
diff --git a/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfObject.java b/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfObject.java
index fbecf2737c..e6ec8b5638 100644
--- a/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfObject.java
+++ b/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfObject.java
@@ -17,6 +17,6 @@ package ghidra.program.model.data.ISF;
public interface IsfObject {
- // EMPTY by design
-
+ // EMPTY
+
}
diff --git a/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfPointer.java b/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfPointer.java
new file mode 100644
index 0000000000..02119eb5d9
--- /dev/null
+++ b/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfPointer.java
@@ -0,0 +1,32 @@
+/* ###
+ * 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.data.ISF;
+
+import ghidra.program.model.data.Pointer;
+
+public class IsfPointer implements IsfObject {
+
+ public Integer size;
+ public String kind;
+ public String endian;
+
+ public IsfPointer(Pointer ptr) {
+ size = ptr.hasLanguageDependantLength() ? -1 : IsfUtilities.getLength(ptr);
+ kind = "pointer";
+ endian = IsfUtilities.getEndianness(ptr);
+ }
+
+}
diff --git a/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfSetting.java b/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfSetting.java
new file mode 100644
index 0000000000..ac41d8a61b
--- /dev/null
+++ b/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfSetting.java
@@ -0,0 +1,30 @@
+/* ###
+ * 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.data.ISF;
+
+public class IsfSetting implements IsfObject {
+
+ public String name;
+ public String kind;
+ public String value;
+
+ public IsfSetting(String name, Object value) {
+ this.name = name;
+ this.value = value.toString();
+ this.kind = value instanceof String ? "string" : "long";
+ }
+
+}
diff --git a/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfTypedObject.java b/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfTypedObject.java
index 9f7ee6c1d4..bd8aa66044 100644
--- a/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfTypedObject.java
+++ b/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfTypedObject.java
@@ -17,15 +17,16 @@ package ghidra.program.model.data.ISF;
import ghidra.program.model.data.DataType;
-public class IsfTypedObject implements IsfObject {
+public class IsfTypedObject extends AbstractIsfObject {
public String kind;
public Integer size;
public IsfObject type;
public IsfTypedObject(DataType dt, IsfObject typeObj) {
+ super(dt);
kind = IsfUtilities.getKind(dt);
- size = dt.getLength();
+ size = dt.hasLanguageDependantLength() ? -1 : dt.getLength();
type = typeObj;
}
diff --git a/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfTypedefBase.java b/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfTypedefBase.java
index 03fae5baac..6309010245 100644
--- a/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfTypedefBase.java
+++ b/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfTypedefBase.java
@@ -18,18 +18,17 @@ package ghidra.program.model.data.ISF;
import ghidra.program.model.data.BuiltInDataType;
import ghidra.program.model.data.TypeDef;
-public class IsfTypedefBase implements IsfObject {
+public class IsfTypedefBase extends AbstractIsfObject {
public Integer size;
public String kind;
- public Boolean signed;
public String endian;
public IsfTypedefBase(TypeDef typeDef) {
+ super(typeDef);
BuiltInDataType builtin = (BuiltInDataType) typeDef.getBaseDataType();
size = typeDef.getLength();
kind = IsfUtilities.getBuiltInKind(builtin);
- signed = IsfUtilities.getSigned(typeDef);
endian = IsfUtilities.getEndianness(typeDef);
}
diff --git a/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfTypedefIntegral.java b/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfTypedefIntegral.java
index 8ebdb46d77..a63107ca1f 100644
--- a/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfTypedefIntegral.java
+++ b/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfTypedefIntegral.java
@@ -17,11 +17,12 @@ package ghidra.program.model.data.ISF;
import ghidra.program.model.data.TypeDef;
-public class IsfTypedefIntegral implements IsfObject {
+public class IsfTypedefIntegral extends AbstractIsfObject {
public Integer size;
public IsfTypedefIntegral(TypeDef td) {
+ super(td);
size = td.getLength();
}
diff --git a/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfTypedefPointer.java b/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfTypedefPointer.java
index 573292042a..6e029fd6ea 100644
--- a/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfTypedefPointer.java
+++ b/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfTypedefPointer.java
@@ -15,21 +15,30 @@
*/
package ghidra.program.model.data.ISF;
+import ghidra.program.model.data.Pointer;
import ghidra.program.model.data.PointerDataType;
+import ghidra.program.model.data.TypeDef;
-public class IsfTypedefPointer implements IsfObject {
+public class IsfTypedefPointer extends AbstractIsfObject {
public Integer size;
- public Boolean signed;
public String kind;
public String endian;
+ public IsfObject type;
- public IsfTypedefPointer() {
- PointerDataType ptr = new PointerDataType();
- size = ptr.getLength();
- signed = false; //IsfUtilities.getSigned(ptr);
- kind = IsfUtilities.getBuiltInKind(ptr);
+ public IsfTypedefPointer(TypeDef typeDef) {
+ super(typeDef);
+ Pointer ptr;
+ if (typeDef != null) {
+ ptr = (Pointer) typeDef.getBaseDataType();
+ }
+ else {
+ ptr = new PointerDataType();
+ }
+ size = ptr.hasLanguageDependantLength() ? -1 : ptr.getLength();
+ kind = "typedef";
endian = IsfUtilities.getEndianness(ptr);
+ type = new IsfPointer(ptr);
}
}
diff --git a/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfTypedefUser.java b/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfTypedefUser.java
index 84c664d5a5..4c0d73f634 100644
--- a/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfTypedefUser.java
+++ b/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfTypedefUser.java
@@ -15,19 +15,19 @@
*/
package ghidra.program.model.data.ISF;
-import ghidra.program.model.data.DataType;
import ghidra.program.model.data.TypeDef;
-public class IsfTypedefUser implements IsfObject {
+public class IsfTypedefUser extends AbstractIsfObject {
public Integer size;
public String kind;
public IsfObject type;
public IsfTypedefUser(TypeDef typeDef, IsfObject typeObj) {
- DataType baseType = typeDef.getBaseDataType();
+ super(typeDef);
size = typeDef.getLength();
- kind = IsfUtilities.getKind(baseType);
+ kind = "typedef";
+ //kind = IsfUtilities.getKind(baseType);
type = typeObj;
}
diff --git a/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfUtilities.java b/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfUtilities.java
index 531a5ac070..202bcdd407 100644
--- a/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfUtilities.java
+++ b/Ghidra/Debug/Debugger-isf/src/main/java/ghidra/program/model/data/ISF/IsfUtilities.java
@@ -20,46 +20,6 @@ import ghidra.program.model.data.Enum;
public class IsfUtilities {
- // list of Ghidra built-in type names which correspond to C primitive types
- private static String[] INTEGRAL_TYPES = { "char", "short", "int", "long", "long long",
- "__int64", "float", "double", "long double", "void" };
-
- private static String[] INTEGRAL_MODIFIERS =
- { "signed", "unsigned", "const", "static", "volatile", "mutable", };
-
- public static boolean isIntegral(String typedefName, String basetypeName) {
- for (String type : INTEGRAL_TYPES) {
- if (typedefName.equals(type)) {
- return true;
- }
- }
-
- boolean endsWithIntegralType = false;
- for (String type : INTEGRAL_TYPES) {
- if (typedefName.endsWith(" " + type)) {
- endsWithIntegralType = true;
- break;
- }
- }
- boolean containsIntegralModifier = false;
- for (String modifier : INTEGRAL_MODIFIERS) {
- if (typedefName.indexOf(modifier + " ") >= 0 ||
- typedefName.indexOf(" " + modifier) >= 0) {
- return true;
- }
- }
-
- if (endsWithIntegralType && containsIntegralModifier) {
- return true;
- }
-
- if (typedefName.endsWith(" " + basetypeName)) {
- return containsIntegralModifier;
- }
-
- return false;
- }
-
public static DataType getBaseDataType(DataType dt) {
while (dt != null) {
if (dt instanceof Array) {
@@ -117,7 +77,7 @@ public class IsfUtilities {
return "enum";
}
if (dt instanceof TypeDef) {
- return "base"; //"typedef";
+ return "typedef";
}
if (dt instanceof FunctionDefinition) {
return "function";
@@ -133,7 +93,7 @@ public class IsfUtilities {
public static String getBuiltInKind(BuiltInDataType dt) {
if (dt instanceof AbstractIntegerDataType) {
- return dt.getLength() == 1 ? "char" : "int";
+ return dt.getName();
}
if (dt instanceof AbstractFloatDataType) {
return "float";
@@ -145,7 +105,7 @@ public class IsfUtilities {
return "char"; // "string";
}
if (dt instanceof PointerDataType) {
- return "void"; //"pointer";
+ return "pointer";
}
if (dt instanceof VoidDataType) {
return "void";
@@ -185,11 +145,48 @@ public class IsfUtilities {
return dt.getLength();
}
- public static Boolean getSigned(DataType dt) {
- return dt.getDataOrganization().isSignedChar();
- }
-
public static String getEndianness(DataType dt) {
return dt.getDataOrganization().isBigEndian() ? "big" : "little";
}
+
+// // list of Ghidra built-in type names which correspond to C primitive types
+// private static String[] INTEGRAL_TYPES = { "char", "short", "int", "long", "long long",
+// "__int64", "float", "double", "long double", "void" };
+//
+// private static String[] INTEGRAL_MODIFIERS =
+// { "signed", "unsigned", "const", "static", "volatile", "mutable", };
+//
+// public static boolean isIntegral(String typedefName, String basetypeName) {
+// for (String type : INTEGRAL_TYPES) {
+// if (typedefName.equals(type)) {
+// return true;
+// }
+// }
+//
+// boolean endsWithIntegralType = false;
+// for (String type : INTEGRAL_TYPES) {
+// if (typedefName.endsWith(" " + type)) {
+// endsWithIntegralType = true;
+// break;
+// }
+// }
+// boolean containsIntegralModifier = false;
+// for (String modifier : INTEGRAL_MODIFIERS) {
+// if (typedefName.indexOf(modifier + " ") >= 0 ||
+// typedefName.indexOf(" " + modifier) >= 0) {
+// return true;
+// }
+// }
+//
+// if (endsWithIntegralType && containsIntegralModifier) {
+// return true;
+// }
+//
+// if (typedefName.endsWith(" " + basetypeName)) {
+// return containsIntegralModifier;
+// }
+//
+// return false;
+// }
+
}
diff --git a/Ghidra/Features/Base/certification.manifest b/Ghidra/Features/Base/certification.manifest
index 608491ab2a..9b63f669c3 100644
--- a/Ghidra/Features/Base/certification.manifest
+++ b/Ghidra/Features/Base/certification.manifest
@@ -389,7 +389,6 @@ src/main/help/help/topics/MemoryMapPlugin/images/MoveMemory.png||GHIDRA||||END|
src/main/help/help/topics/MemoryMapPlugin/images/SetImageBaseDialog.png||GHIDRA||||END|
src/main/help/help/topics/MemoryMapPlugin/images/SplitMemoryBlock.png||GHIDRA||||END|
src/main/help/help/topics/Misc/Appendix.htm||GHIDRA||||END|
-src/main/help/help/topics/Misc/Tips.htm||NONE||||END|
src/main/help/help/topics/Misc/Welcome_to_Ghidra_Help.htm||GHIDRA||||END|
src/main/help/help/topics/Navigation/Navigation.htm||GHIDRA||||END|
src/main/help/help/topics/Navigation/images/GoToDialog.png||GHIDRA||||END|
diff --git a/Ghidra/Features/Base/src/main/help/help/topics/ExporterPlugin/exporter.htm b/Ghidra/Features/Base/src/main/help/help/topics/ExporterPlugin/exporter.htm
index f6bbdec7bd..2242bc1a0e 100644
--- a/Ghidra/Features/Base/src/main/help/help/topics/ExporterPlugin/exporter.htm
+++ b/Ghidra/Features/Base/src/main/help/help/topics/ExporterPlugin/exporter.htm
@@ -37,6 +37,8 @@
Raw Bytes
XML Export Format
+
+ SARIF Export Format
Export Action
@@ -414,6 +416,18 @@
XML Options are identical the XML Importer Options .
+
+ SARIF
+
+
+ The SARIF Exporter creates SARIF files that conform to Ghidra's Program DTD. You can
+ re-import files in this format using the SARIF Importer .
+
+ The
+ SARIF Options are identical the SARIF Importer Options .
+
Related Topics:
diff --git a/Ghidra/Features/Base/src/main/help/help/topics/ImporterPlugin/importer.htm b/Ghidra/Features/Base/src/main/help/help/topics/ImporterPlugin/importer.htm
index fb439d9c46..7761843a34 100644
--- a/Ghidra/Features/Base/src/main/help/help/topics/ImporterPlugin/importer.htm
+++ b/Ghidra/Features/Base/src/main/help/help/topics/ImporterPlugin/importer.htm
@@ -51,6 +51,7 @@
Raw Binary
Relocatable Object Module Format (OMF)
XML Input Format
+ SARIF Input Format
@@ -680,6 +681,16 @@
Names.
+
+ SARIF Options
+
+
+ The SARIF format is used to load from a SARIF formatted file. The options are simply
+ switches for which types of program information to import and are identical to the options
+ specified above for XML.
+
+
+
Library Search Path
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/exporter/ExporterDialog.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/exporter/ExporterDialog.java
index a7c2764abd..2846aa1915 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/exporter/ExporterDialog.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/exporter/ExporterDialog.java
@@ -64,8 +64,12 @@ import ghidra.util.task.*;
public class ExporterDialog extends DialogComponentProvider implements AddressFactoryService {
private static final String XML_WARNING =
- " Warning: XML is lossy and intended only for transfering data to external tools. " +
- "GZF is the recommended format for saving and sharing program data.";
+ " Warning: XML is lossy and intended only for transfering data to external tools. " +
+ "GZF is the recommended format for saving and sharing program data.";
+
+ private static final String SARIF_WARNING =
+ " Warning: SARIF is lossy and intended only for transfering data to external tools. " +
+ "GZF is the recommended format for saving and sharing program data.";
private static String lastUsedExporterName = GzfExporter.NAME; // default to GZF first time
@@ -394,6 +398,9 @@ public class ExporterDialog extends DialogComponentProvider implements AddressFa
if (getSelectedExporter().getName().contains("XML")) {
setStatusText(XML_WARNING);
}
+ if (getSelectedExporter().getName().contains("SARIF")) {
+ setStatusText(SARIF_WARNING);
+ }
setOkEnabled(true);
}
diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/database/code/CodeManagerTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/database/code/CodeManagerTest.java
index 0239959224..9fc22790f5 100644
--- a/Ghidra/Features/Base/src/test/java/ghidra/program/database/code/CodeManagerTest.java
+++ b/Ghidra/Features/Base/src/test/java/ghidra/program/database/code/CodeManagerTest.java
@@ -470,7 +470,7 @@ public class CodeManagerTest extends AbstractGenericTest {
Instruction inst = listing.getInstructionAt(addr(0x1100));
inst.setProperty("Numbers", 12);
- PropertyMap map = listing.getPropertyMap("Numbers");
+ PropertyMap> map = listing.getPropertyMap("Numbers");
assertNotNull(map);
inst.setProperty("FavoriteColor", new SaveableColor(Palette.RED));
diff --git a/Ghidra/Features/Sarif/Module.manifest b/Ghidra/Features/Sarif/Module.manifest
new file mode 100644
index 0000000000..1d58d92fad
--- /dev/null
+++ b/Ghidra/Features/Sarif/Module.manifest
@@ -0,0 +1 @@
+MODULE FILE LICENSE: lib/java-sarif-2.1.jar MIT
diff --git a/Ghidra/Features/Sarif/build.gradle b/Ghidra/Features/Sarif/build.gradle
new file mode 100644
index 0000000000..9a3162a450
--- /dev/null
+++ b/Ghidra/Features/Sarif/build.gradle
@@ -0,0 +1,36 @@
+/* ###
+ * 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.
+ */
+apply from: "$rootProject.projectDir/gradle/distributableGhidraModule.gradle"
+apply from: "$rootProject.projectDir/gradle/javaProject.gradle"
+apply from: "$rootProject.projectDir/gradle/jacocoProject.gradle"
+apply from: "$rootProject.projectDir/gradle/javaTestProject.gradle"
+apply plugin: 'eclipse'
+
+eclipse.project.name = 'Features Sarif'
+
+dependencies {
+ api project(':Base')
+ api project(':Debugger-isf')
+ //api "javax.activation:activation-1.1.1"
+ api "com.contrastsecurity.sarif:java-sarif-2.1"
+}
+
+test {
+ // temporary to prevent test from running when building
+ // specify a pattern that doesn't match any test files.
+ include "dontruntests"
+}
+
diff --git a/Ghidra/Features/Sarif/certification.manifest b/Ghidra/Features/Sarif/certification.manifest
new file mode 100644
index 0000000000..c54edb4baf
--- /dev/null
+++ b/Ghidra/Features/Sarif/certification.manifest
@@ -0,0 +1,6 @@
+##VERSION: 2.0
+Module.manifest||GHIDRA||||END|
+data/ExtensionPoint.manifest||GHIDRA||||END|
+src/main/help/help/TOC_Source.xml||GHIDRA||||END|
+src/main/help/help/topics/Sarif/SARIF.htm||GHIDRA||||END|
+src/main/java/sarif/DEVNOTES.txt||GHIDRA||||END|
diff --git a/Ghidra/Features/Sarif/data/ExtensionPoint.manifest b/Ghidra/Features/Sarif/data/ExtensionPoint.manifest
new file mode 100644
index 0000000000..10d0b4ff57
--- /dev/null
+++ b/Ghidra/Features/Sarif/data/ExtensionPoint.manifest
@@ -0,0 +1,2 @@
+RunHandler
+ResultHandler
diff --git a/Ghidra/Features/Sarif/src/main/help/help/TOC_Source.xml b/Ghidra/Features/Sarif/src/main/help/help/TOC_Source.xml
new file mode 100644
index 0000000000..5e2d0e0008
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/help/help/TOC_Source.xml
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/Ghidra/Features/Sarif/src/main/help/help/topics/Sarif/SARIF.htm b/Ghidra/Features/Sarif/src/main/help/help/topics/Sarif/SARIF.htm
new file mode 100644
index 0000000000..da6bed2120
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/help/help/topics/Sarif/SARIF.htm
@@ -0,0 +1,81 @@
+
+
+
+
+
+
+ Static Analysis Results Interchange Format (SARIF)
+
+
+
+
+ Using SARIF Files
+
+ SARIF is an OASIS standard for exchanging the results of static analysis performed against
+ a target executable. While not a perfect match for exchanging Ghidra program analysis results,
+ it has at least some level of community acceptance and support within the reverse engineering
+ communities at large. SARIF can be created in any number of ways, but, within Ghidra, SARIF
+ creation is done by means of any exporter. As with all of our exporters, a dialog appears when
+ you request an export, with an options panel for selecting which features you would like to export.
+ Currently, we support bookmarks, code, comments, data types, defined data, entry points, equates,
+ external libraries, functions, memory maps, properties, references, registers, relocations,
+ symbols, and program trees.
+
+ SARIF files can be re-ingested in two ways. A matching importer uses the same options to
+ create a new program or add features to an existing one. The process is probably lossy,
+ although efforts have been made to re-create exported programs with some fidelity. Known issues
+ are documented in the DEVNOTES file. The second way to ingest SARIF files is to configure a tool
+ with the SarifPlugin. The plugin provides a single "Read File" toolbar action. After selecting
+ a file, the SARIF results are displayed in table format, with associated ingest actions applicable
+ to a table selection. The features provide by the SARIF importer and exporter all share the action
+ "Add To Program".
+
+ Writing Custom SARIF Handlers
+
+ While the main SarifProgramResultHandler class handles the majority of program-derived
+ information used on import/export, there are many occasions where you might want to apply the
+ results of some external analysis to a Ghidra program. The "Features Sarif" project contains
+ templates (in the form of abstract classes) for custom handlers: SarifResultHandler.java and
+ SarifRunHandler.java. Both handler types extend ExtensionPoint and are discovered automatically
+ when the a file is read by the SarifPlugin.
+
+ Per-result handlers are used to populate a table displayed to the user and to associate
+ actions with a column in the table. The "getKey" method returns the name for a column, and the
+ "getActionName" a name for the action to be taken. When the user selects that action (in this case,
+ "Commit"), the table provider executes the program task specified by "getTask". In this example,
+ the task grabs the selected rows from the table and, for each row, applies the return type in the
+ column "return_type" to the address in the column "address".
+
+ A more complicated use case might involve the "parse" method, which is called for every result.
+ The default "handle" method takes anything returned by the "parse" method, creates key-value pairs
+ (stored as a map, but...) using "getKey" and the value returned, and adds them to a list of results
+ stored in the data frame. These can be retrieved programmatically later or processed immediately.
+ For example, the SarifPropertyResultHandler looks for results with "AdditionalProperties" labelled
+ either "viewer/table/xxx" or "listing/[comment, highlight, bookmark]". Items in the second category
+ are applied immediately to the current program. Items in the first category are added to the table
+ under column "xxx".
+
+ A third and significantly more complicated example is the ProgramResultHandler, which overrides
+ the "handle" method. This handler converts the set of "AdditionalProperties" into a key-value map.
+ The map is added to a list of results for the data frame, but also converted to a map of lists based
+ on the result's "RuleId". The frame ultimately accrues a map of list of maps, keyed on "RuleId".
+ Its "Add to Program" task takes each per-rule list and applies them to the program using the values
+ stored in the per-result maps.
+
+ A final example, the SarifGraphRunHandler, is a per-run handler and is generally called only once
+ (unless the SARIF files contains multiple runs). This example retrieves a SARIF Graph from the run,
+ converts it to a Ghidra graph object, and displays it. Controls for how and when SARIF graphs are
+ displayed are stored under Edit->Tool Options->Graph->Sarif.
+
+
+ Related Topics:
+
+
+
+
+
+
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/DEVNOTES.txt b/Ghidra/Features/Sarif/src/main/java/sarif/DEVNOTES.txt
new file mode 100644
index 0000000000..24af15197d
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/DEVNOTES.txt
@@ -0,0 +1,13 @@
+Random notes:
+
+1. Symbols require a pre- and post-function pass, pre- to guarantee the correct naming of globals
+ including functions and libraries and post- to guarantee namespaces have already been created
+ for locals
+
+Code differences from export/re-import:
+
+1. There may be missing parameter datatypes for parameters in a FunctionDefinition used inside a structure or union.
+
+2. Datatypes like "RTTIBaseDescriptor *32 _((image-base-relative)) *32 _((image-base-relative))" are not handled correctly.
+
+3. Modified/multiple ProgramTrees will not be imported correctly. Appears to be no way to fix this with the current API.
\ No newline at end of file
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/SarifController.java b/Ghidra/Features/Sarif/src/main/java/sarif/SarifController.java
new file mode 100644
index 0000000000..b5b260c2af
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/SarifController.java
@@ -0,0 +1,289 @@
+/* ###
+ * 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 sarif;
+
+import java.awt.Color;
+import java.awt.image.BufferedImage;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import com.contrastsecurity.sarif.Location;
+import com.contrastsecurity.sarif.LogicalLocation;
+import com.contrastsecurity.sarif.Result;
+import com.contrastsecurity.sarif.SarifSchema210;
+
+import docking.widgets.table.ObjectSelectedListener;
+import ghidra.app.plugin.core.colorizer.ColorizingService;
+import ghidra.app.services.GraphDisplayBroker;
+import ghidra.program.model.address.Address;
+import ghidra.program.model.address.AddressSetView;
+import ghidra.program.model.listing.BookmarkManager;
+import ghidra.program.model.listing.CodeUnit;
+import ghidra.program.model.listing.Function;
+import ghidra.program.model.listing.Program;
+import ghidra.service.graph.AttributedGraph;
+import ghidra.service.graph.EmptyGraphType;
+import ghidra.service.graph.GraphDisplay;
+import ghidra.service.graph.GraphDisplayOptions;
+import ghidra.util.Msg;
+import ghidra.util.classfinder.ClassSearcher;
+import ghidra.util.exception.CancelledException;
+import ghidra.util.exception.GraphException;
+import resources.ResourceManager;
+import sarif.handlers.SarifResultHandler;
+import sarif.handlers.SarifRunHandler;
+import sarif.managers.ProgramSarifMgr;
+import sarif.model.SarifDataFrame;
+import sarif.view.ImageArtifactDisplay;
+import sarif.view.SarifResultsTableProvider;
+
+/**
+ * Controller for handling interactions between the SARIF log file and Ghidra
+ */
+public class SarifController implements ObjectSelectedListener> {
+
+ private SarifPlugin plugin;
+ private Program program;
+
+ private ColorizingService coloringService;
+ private BookmarkManager bookmarkManager;
+ private ProgramSarifMgr programManager;
+
+ private Set providers = new HashSet<>();
+ public Set artifacts = new HashSet<>();
+ public Set graphs = new HashSet<>();
+
+ public Set getSarifResultHandlers() {
+ Set set = new HashSet<>();
+ set.addAll(ClassSearcher.getInstances(SarifResultHandler.class));
+ return set;
+ }
+
+ public Set getSarifRunHandlers() {
+ Set set = new HashSet<>();
+ set.addAll(ClassSearcher.getInstances(SarifRunHandler.class));
+ return set;
+ }
+
+ public SarifController(Program program, SarifPlugin plugin) {
+ this.program = program;
+ this.plugin = plugin;
+ this.coloringService = plugin.getTool().getService(ColorizingService.class);
+ this.programManager = new ProgramSarifMgr(program);
+ }
+
+ public SarifController(ProgramSarifMgr manager) {
+ this.program = null;
+ this.plugin = null;
+ this.coloringService = null; // plugin.getTool().getService(ColorizingService.class);
+ this.programManager = manager;
+ }
+
+ public void dispose() {
+ Set copyProviders = new HashSet<>();
+ copyProviders.addAll(providers);
+ for (SarifResultsTableProvider p : copyProviders) {
+ p.dispose();
+ }
+ Set copyArtifacts = new HashSet<>();
+ copyArtifacts.addAll(artifacts);
+ for (ImageArtifactDisplay a : copyArtifacts) {
+ a.dispose();
+ }
+ for (GraphDisplay g : graphs) {
+ g.close();
+ }
+ }
+
+ public void showTable(boolean makeVisible) {
+ for (SarifResultsTableProvider p : providers) {
+ p.setVisible(makeVisible);
+ }
+ }
+
+ public void showTable(String logName, SarifSchema210 sarif) {
+ SarifDataFrame df = new SarifDataFrame(sarif, this, false);
+ SarifResultsTableProvider provider = new SarifResultsTableProvider(logName, this.plugin, this, df);
+ provider.filterTable.addSelectionListener(this);
+ provider.addToTool();
+ provider.setVisible(true);
+ provider.setTitle(logName);
+ if (!providers.contains(provider)) {
+ providers.add(provider);
+ }
+ }
+
+ public void showImage(String key, BufferedImage img) {
+ if (plugin.displayArtifacts()) {
+ ImageArtifactDisplay display = new ImageArtifactDisplay(plugin.getTool(), key, "Sarif Parse", img);
+ display.setVisible(true);
+ artifacts.add(display);
+ }
+ }
+
+ public void showGraph(AttributedGraph graph) {
+ try {
+ GraphDisplayBroker service = this.plugin.getTool().getService(GraphDisplayBroker.class);
+ boolean append = plugin.appendToGraph();
+ GraphDisplay display = service.getDefaultGraphDisplay(append, null);
+ GraphDisplayOptions graphOptions = new GraphDisplayOptions(new EmptyGraphType());
+ graphOptions.setMaxNodeCount(plugin.getGraphSize());
+
+ if (plugin.displayGraphs()) {
+ display.setGraph(graph, graphOptions, graph.getDescription(), append, null);
+ graphs.add(display);
+ }
+ } catch (GraphException | CancelledException e) {
+ Msg.error(this, "showGraph failed " + e.getMessage());
+ }
+ }
+
+ /**
+ * If a results has "listing/" in a SARIF result, this handles
+ * defining our custom API for those
+ *
+ * @param log
+ * @param result
+ * @param key
+ * @param value
+ */
+ public void handleListingAction(Result result, String key, Object value) {
+ List addrs = getListingAddresses(result);
+ for (Address addr : addrs) {
+ switch (key) {
+ case "comment":
+ /* @formatter:off
+ * docs/GhidraAPI_javadoc/api/constant-values.html#ghidra.program.model.listing.CodeUnit
+ * EOL_COMMENT 0
+ * PRE_COMMENT 1
+ * POST_COMMENT 2
+ * PLATE_COMMENT 3
+ * REPEATABLE_COMMENT 4
+ * @formatter:on
+ */
+ String comment = (String) value;
+ getProgram().getListing().setComment(addr, CodeUnit.PLATE_COMMENT, comment);
+ break;
+ case "highlight":
+ Color color = Color.decode((String) value);
+ coloringService.setBackgroundColor(addr, addr, color);
+ break;
+ case "bookmark":
+ String bookmark = (String) value;
+ getProgram().getBookmarkManager().setBookmark(addr, "Sarif", result.getRuleId(), bookmark);
+ break;
+ }
+ }
+ }
+
+ public void colorBackground(AddressSetView set, Color color) {
+ coloringService.setBackgroundColor(set, color);
+ }
+
+ public void colorBackground(Address addr, Color color) {
+ coloringService.setBackgroundColor(addr, addr, color);
+ }
+
+ public Address longToAddress(Object lval) {
+ if (lval instanceof Long) {
+ return getProgram().getAddressFactory().getDefaultAddressSpace().getAddress((Long) lval);
+ }
+ return getProgram().getAddressFactory().getDefaultAddressSpace().getAddress((Integer) lval);
+ }
+
+ /**
+ * Get listing addresses associated with a result
+ *
+ * @param result
+ * @return
+ */
+ public List getListingAddresses(Result result) {
+ List addrs = new ArrayList<>();
+ if (result.getLocations() != null && result.getLocations().size() > 0) {
+ List locations = result.getLocations();
+ for (Location loc : locations) {
+ Address addr = locationToAddress(loc);
+ if (addr != null) {
+ addrs.add(addr);
+ }
+ }
+ }
+ return addrs;
+ }
+
+ public Address locationToAddress(Location loc) {
+ if (loc.getPhysicalLocation() != null) {
+ return longToAddress(loc.getPhysicalLocation().getAddress().getAbsoluteAddress());
+ }
+ if (loc.getLogicalLocations() != null) {
+ Set logicalLocations = loc.getLogicalLocations();
+ for (LogicalLocation logLoc : logicalLocations) {
+ switch (logLoc.getKind()) {
+ case "function":
+ String fname = logLoc.getName();
+ for (Function func : getProgram().getFunctionManager().getFunctions(true)) {
+ if (fname.equals(func.getName())) {
+ return func.getEntryPoint();
+ }
+ }
+ break;
+ default:
+ Msg.error(this, "Unknown logical location to handle: " + logLoc.toString());
+ }
+ }
+ }
+ return null;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public void objectSelected(Map row) {
+ if (row != null) {
+ if (row.containsKey("CodeFlows")) {
+ for (List flow : (List>) row.get("CodeFlows")) {
+ this.plugin.makeSelection(flow);
+ }
+ }
+ if (row.containsKey("Graphs")) {
+ for (AttributedGraph graph : (List) row.get("Graphs")) {
+ this.showGraph(graph);
+ }
+ }
+ }
+ }
+
+ public void removeProvider(SarifResultsTableProvider provider) {
+ providers.remove(provider);
+ }
+
+ public ProgramSarifMgr getProgramSarifMgr() {
+ return programManager;
+ }
+
+ public Program getProgram() {
+ return program;
+ }
+
+ public void setProgram(Program program) {
+ this.program = program;
+ this.bookmarkManager = program.getBookmarkManager();
+ bookmarkManager.defineType("Sarif", ResourceManager.loadImage("images/peach_16.png"), Color.pink, 0);
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/SarifLoader.java b/Ghidra/Features/Sarif/src/main/java/sarif/SarifLoader.java
new file mode 100644
index 0000000000..6a2ef8cfb1
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/SarifLoader.java
@@ -0,0 +1,350 @@
+/* ###
+ * 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 sarif;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import ghidra.app.plugin.core.analysis.AnalysisWorker;
+import ghidra.app.plugin.core.analysis.AutoAnalysisManager;
+import ghidra.app.util.Option;
+import ghidra.app.util.OptionException;
+import ghidra.app.util.bin.ByteProvider;
+import ghidra.app.util.importer.MessageLog;
+import ghidra.app.util.opinion.AbstractProgramLoader;
+import ghidra.app.util.opinion.LoadException;
+import ghidra.app.util.opinion.LoadSpec;
+import ghidra.app.util.opinion.Loaded;
+import ghidra.app.util.opinion.LoaderTier;
+import ghidra.framework.model.DomainObject;
+import ghidra.framework.model.Project;
+import ghidra.program.model.address.Address;
+import ghidra.program.model.lang.CompilerSpec;
+import ghidra.program.model.lang.CompilerSpecDescription;
+import ghidra.program.model.lang.CompilerSpecNotFoundException;
+import ghidra.program.model.lang.Endian;
+import ghidra.program.model.lang.ExternalLanguageCompilerSpecQuery;
+import ghidra.program.model.lang.Language;
+import ghidra.program.model.lang.LanguageCompilerSpecPair;
+import ghidra.program.model.lang.LanguageDescription;
+import ghidra.program.model.lang.LanguageNotFoundException;
+import ghidra.program.model.listing.Program;
+import ghidra.util.Msg;
+import ghidra.util.exception.CancelledException;
+import ghidra.util.task.TaskMonitor;
+import sarif.export.SarifObject;
+import sarif.managers.ProgramInfo;
+import sarif.managers.ProgramSarifMgr;
+
+public class SarifLoader extends AbstractProgramLoader {
+
+ private static final String FILE_EXTENSION = SarifObject.SARIF ? ".sarif" : ".json";
+ public final static String SARIF_SRC_NAME = "SARIF Input Format";
+
+ @Override
+ public LoaderTier getTier() {
+ return LoaderTier.SPECIALIZED_TARGET_LOADER;
+ }
+
+ @Override
+ public int getTierPriority() {
+ return 50;
+ }
+
+ @Override
+ public boolean supportsLoadIntoProgram() {
+ return true;
+ }
+
+ @Override
+ public Collection findSupportedLoadSpecs(ByteProvider provider) throws IOException {
+
+ List loadSpecs = new ArrayList<>();
+
+ //
+ // Unusual Code Alert!: the parse() method below uses Processor to
+ // location processors
+ // by name when reading SARIF. The Processor class is not fully
+ // populated until the languages have been loaded.
+ //
+ getLanguageService();
+
+ ParseResult result = parse(provider);
+
+ ProgramInfo info = result.lastInfo;
+ if (info == null) {
+ return loadSpecs;
+ }
+
+ if (info.languageID != null) {// non-external language
+ // got a language ID, good...
+ try {
+ LanguageDescription languageDescription =
+ getLanguageService().getLanguageDescription(info.languageID);
+
+ boolean preferred = false;
+ if (info.compilerSpecID == null) {
+ // no compiler spec ID, try to pick "default" (embedded
+ // magic string!!! BAD)
+ for (CompilerSpecDescription csd : languageDescription.getCompatibleCompilerSpecDescriptions()) {
+ LanguageCompilerSpecPair pair = new LanguageCompilerSpecPair(
+ languageDescription.getLanguageID(), csd.getCompilerSpecID());
+ loadSpecs.add(new LoadSpec(this, 0, pair, preferred));
+ }
+ }
+ else {
+ // test existence; throw exception on failure
+ languageDescription.getCompilerSpecDescriptionByID(info.compilerSpecID);
+ // good, we know exactly what this is (make it preferred)
+ LanguageCompilerSpecPair pair =
+ new LanguageCompilerSpecPair(info.languageID, info.compilerSpecID);
+ preferred = true;
+ loadSpecs.add(new LoadSpec(this, 0, pair, preferred));
+ }
+ }
+ catch (CompilerSpecNotFoundException | LanguageNotFoundException lnfe) {
+ // ignore
+ // should fall into loadSpecs.isEmpty() case below
+ }
+
+ }
+ else if (info.processorName != null) {// external language
+ // no ID, look by processor/possibly endian
+ Integer size = extractSize(info.addressModel);
+ Endian endian = Endian.toEndian(info.endian);
+ ExternalLanguageCompilerSpecQuery broadQuery =
+ new ExternalLanguageCompilerSpecQuery(info.processorName,
+ info.getNormalizedExternalToolName(), endian, size, info.compilerSpecID);
+ List pairs =
+ getLanguageService().getLanguageCompilerSpecPairs(broadQuery);
+
+ if (!pairs.isEmpty()) {
+ boolean preferred = false;
+ if (pairs.size() == 1) {
+ preferred = true;
+ }
+ for (LanguageCompilerSpecPair pair : pairs) {
+ loadSpecs.add(new LoadSpec(this, 0, pair, preferred));
+ }
+ }
+ }
+
+ if (loadSpecs.isEmpty() && provider.getName().endsWith(FILE_EXTENSION)) {
+ // just put 'em all in (give endianess preference)
+ List languageDescriptions =
+ getLanguageService().getLanguageDescriptions(false);
+ for (LanguageDescription languageDescription : languageDescriptions) {
+ Collection compilerSpecDescriptions =
+ languageDescription.getCompatibleCompilerSpecDescriptions();
+ for (CompilerSpecDescription compilerSpecDescription : compilerSpecDescriptions) {
+ LanguageCompilerSpecPair pair =
+ new LanguageCompilerSpecPair(languageDescription.getLanguageID(),
+ compilerSpecDescription.getCompilerSpecID());
+ loadSpecs.add(new LoadSpec(this, 0, pair, false));
+ }
+ }
+ }
+ return loadSpecs;
+ }
+
+ private static Pattern ADDRESS_MODEL_PATTERN = Pattern.compile("(\\d+)-bit");
+
+ private Integer extractSize(String addressModel) {
+ if (addressModel != null) {
+ Matcher matcher = ADDRESS_MODEL_PATTERN.matcher(addressModel);
+ if (matcher.find()) {
+ return Integer.parseInt(matcher.group(1));
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public String getPreferredFileName(ByteProvider provider) {
+ String name = provider.getName();
+ if (name.toLowerCase().endsWith(FILE_EXTENSION)) {
+ return name.substring(0, name.length() - FILE_EXTENSION.length());
+ }
+ return name;
+ }
+
+ @Override
+ protected List> loadProgram(ByteProvider provider, String programName,
+ Project project, String programFolderPath, LoadSpec loadSpec, List options,
+ MessageLog log, Object consumer, TaskMonitor monitor)
+ throws IOException, LoadException, CancelledException {
+
+ //throw new RuntimeException("SARIF importer supports only 'Add To Program'");
+ LanguageCompilerSpecPair pair = loadSpec.getLanguageCompilerSpec();
+ Language importerLanguage = getLanguageService().getLanguage(pair.languageID);
+ CompilerSpec importerCompilerSpec =
+ importerLanguage.getCompilerSpecByID(pair.compilerSpecID);
+
+ ParseResult result = parse(provider);
+
+ Address imageBase = null;
+ if (result.lastInfo.imageBase != null) {
+ imageBase = importerLanguage.getAddressFactory().getAddress(result.lastInfo.imageBase);
+ }
+ Program prog = createProgram(provider, programName, imageBase, getName(), importerLanguage,
+ importerCompilerSpec, consumer);
+ List> loadedList =
+ List.of(new Loaded<>(prog, programName, programFolderPath));
+ boolean success = false;
+ try {
+ success = doImport(result.lastSarifMgr, options, log, prog, monitor, false);
+ if (success) {
+ createDefaultMemoryBlocks(prog, importerLanguage, log);
+ return loadedList;
+ }
+ throw new LoadException("Failed to load");
+ }
+ finally {
+ if (!success) {
+ release(loadedList, consumer);
+ }
+ }
+ }
+
+ @Override
+ protected void loadProgramInto(ByteProvider provider, LoadSpec loadSpec,
+ List options, MessageLog log, Program prog, TaskMonitor monitor)
+ throws IOException, LoadException, CancelledException {
+ File file = provider.getFile();
+ doImport(new ProgramSarifMgr(prog, file), options, log, prog, monitor, true);
+ }
+
+ private boolean doImportWork(final ProgramSarifMgr mgr, final List options,
+ final MessageLog log, Program prog, TaskMonitor monitor,
+ final boolean isAddToProgram) throws LoadException {
+ boolean success = true;
+ try {
+ SarifProgramOptions sarifOptions = mgr.getOptions();
+ sarifOptions.setOptions(options);
+ sarifOptions.setAddToProgram(isAddToProgram);
+ mgr.read(prog, monitor);
+ success = true;
+ }
+ catch (Exception e) {
+ String message = "(empty)";
+ if (log != null && !"".equals(log.toString())) {
+ message = log.toString();
+ }
+ Msg.warn(this, "SARIF import exception, log: " + message, e);
+ throw new LoadException(e.getMessage());
+ }
+ return success;
+ }
+
+ private boolean doImport(final ProgramSarifMgr mgr, final List options,
+ final MessageLog log, Program prog, TaskMonitor monitor, final boolean isAddToProgram)
+ throws IOException {
+
+ if (!AutoAnalysisManager.hasAutoAnalysisManager(prog)) {
+ int txId = prog.startTransaction("SARIF Import");
+ try {
+ return doImportWork(mgr, options, log, prog, monitor, isAddToProgram);
+ }
+ finally {
+ prog.endTransaction(txId, true);
+ }
+ }
+
+ AutoAnalysisManager analysisMgr = AutoAnalysisManager.getAnalysisManager(prog);
+ try {
+ return analysisMgr.scheduleWorker(new AnalysisWorker() {
+
+ @Override
+ public String getWorkerName() {
+ return "SARIF Importer";
+ }
+
+ @Override
+ public boolean analysisWorkerCallback(Program program, Object workerContext,
+ TaskMonitor taskMonitor) throws Exception, CancelledException {
+ return doImportWork(mgr, options, log, program, taskMonitor, isAddToProgram);
+ }
+ }, null, false, monitor);
+
+ }
+ catch (CancelledException e) {
+ return false;
+ }
+ catch (InvocationTargetException e) {
+ Throwable cause = e.getCause();
+ if (cause instanceof IOException) {
+ throw (IOException) cause;
+ }
+ throw new RuntimeException(e);
+ }
+ catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static class ParseResult {
+ final ProgramSarifMgr lastSarifMgr;
+ final ProgramInfo lastInfo;
+
+ ParseResult(ProgramSarifMgr lastSarifMgr, ProgramInfo lastInfo) {
+ this.lastSarifMgr = lastSarifMgr;
+ this.lastInfo = lastInfo;
+ }
+ }
+
+ private ParseResult parse(ByteProvider provider) {
+ try {
+ ProgramSarifMgr lastSarifMgr = new ProgramSarifMgr(provider);
+ ProgramInfo lastInfo = lastSarifMgr.getProgramInfo();
+ return new ParseResult(lastSarifMgr, lastInfo);
+ }
+ catch (Throwable e) {
+ // This can happen during the import process when this loader attempts to load
+ // a non-SARIF file (there really should be 2 methods, a speculative version and
+ // a version that expects no exception)
+ Msg.trace(this, "Unable to parse SARIF for " + provider.getName(), e);
+ return new ParseResult(null, null);
+ }
+ }
+
+ @Override
+ public List getDefaultOptions(ByteProvider provider, LoadSpec loadSpec,
+ DomainObject domainObject, boolean loadIntoProgram) {
+ return new SarifProgramOptions().getOptions(loadIntoProgram);
+ }
+
+ @Override
+ public String getName() {
+ return SARIF_SRC_NAME;
+ }
+
+ @Override
+ public String validateOptions(ByteProvider provider, LoadSpec loadSpec, List options, Program program) {
+ try {
+ new SarifProgramOptions().setOptions(options);
+ }
+ catch (OptionException e) {
+ return e.getMessage();
+ }
+ return null;
+ }
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/SarifPlugin.java b/Ghidra/Features/Sarif/src/main/java/sarif/SarifPlugin.java
new file mode 100644
index 0000000000..34e1d8ec4d
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/SarifPlugin.java
@@ -0,0 +1,250 @@
+/* ###
+ * 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 sarif;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.swing.Icon;
+
+import com.contrastsecurity.sarif.SarifSchema210;
+import com.google.gson.JsonSyntaxException;
+
+import docking.action.builder.ActionBuilder;
+import docking.tool.ToolConstants;
+import docking.widgets.filechooser.GhidraFileChooser;
+import ghidra.MiscellaneousPluginPackage;
+import ghidra.app.events.ProgramActivatedPluginEvent;
+import ghidra.app.events.ProgramClosedPluginEvent;
+import ghidra.app.events.ProgramOpenedPluginEvent;
+import ghidra.app.events.ProgramVisibilityChangePluginEvent;
+import ghidra.app.plugin.PluginCategoryNames;
+import ghidra.app.plugin.ProgramPlugin;
+import ghidra.framework.options.Options;
+import ghidra.framework.options.OptionsChangeListener;
+import ghidra.framework.options.ToolOptions;
+import ghidra.framework.plugintool.PluginEvent;
+import ghidra.framework.plugintool.PluginInfo;
+import ghidra.framework.plugintool.PluginTool;
+import ghidra.framework.plugintool.util.PluginStatus;
+import ghidra.program.model.address.Address;
+import ghidra.program.model.address.AddressSet;
+import ghidra.program.model.listing.Program;
+import ghidra.util.HelpLocation;
+import ghidra.util.Msg;
+import ghidra.util.bean.opteditor.OptionsVetoException;
+import resources.ResourceManager;
+import sarif.io.SarifGsonIO;
+import sarif.io.SarifIO;
+
+//@formatter:off
+@PluginInfo(
+ status = PluginStatus.RELEASED,
+ packageName = MiscellaneousPluginPackage.NAME,
+ category = PluginCategoryNames.ANALYSIS,
+ shortDescription = "Sarif Plugin.",
+ description = "From sarif parsing to DL modelling"
+)
+//@formatter:on
+
+/**
+ * A {@link ProgramPlugin} for reading in sarif files
+ */
+public class SarifPlugin extends ProgramPlugin implements OptionsChangeListener {
+ public static final String NAME = "Sarif";
+ public static final Icon SARIF_ICON = ResourceManager.loadImage("images/peach_16.png");
+
+ private Map sarifControllers;
+ private SarifIO io;
+
+ public SarifPlugin(PluginTool tool) {
+ super(tool);
+ this.sarifControllers = new HashMap();
+ this.io = new SarifGsonIO();
+ }
+
+ @Override
+ protected void init() {
+ createActions();
+ initializeOptions();
+ }
+
+ public void readFile(File file) {
+ if (file != null) {
+ try {
+ showSarif(file.getName(), io.readSarif(file));
+ } catch (JsonSyntaxException | IOException e) {
+ Msg.showError(this, tool.getActiveWindow(), "File parse error", "Invalid Sarif File");
+ }
+ }
+ }
+
+ @Override
+ public void processEvent(PluginEvent event) {
+ super.processEvent(event);
+ Program eventProgram = null;
+ if (event instanceof ProgramActivatedPluginEvent) {
+ ProgramActivatedPluginEvent ev = (ProgramActivatedPluginEvent) event;
+ eventProgram = ev.getActiveProgram();
+ }
+ else if (event instanceof ProgramOpenedPluginEvent) {
+ ProgramOpenedPluginEvent ev = (ProgramOpenedPluginEvent) event;
+ eventProgram = ev.getProgram();
+ }
+ else if (event instanceof ProgramClosedPluginEvent) {
+ ProgramClosedPluginEvent ev = (ProgramClosedPluginEvent) event;
+ eventProgram = ev.getProgram();
+ }
+ else if (event instanceof ProgramClosedPluginEvent) {
+ ProgramClosedPluginEvent ev = (ProgramClosedPluginEvent) event;
+ eventProgram = ev.getProgram();
+ }
+ else if (event instanceof ProgramVisibilityChangePluginEvent) {
+ ProgramVisibilityChangePluginEvent ev = (ProgramVisibilityChangePluginEvent) event;
+ eventProgram = ev.getProgram();
+ }
+ SarifController controller = sarifControllers.get(eventProgram);
+ if (controller != null) {
+ if (event instanceof ProgramClosedPluginEvent) {
+ controller.dispose();
+ sarifControllers.remove(eventProgram, controller);
+ }
+ else if (event instanceof ProgramVisibilityChangePluginEvent) {
+ ProgramVisibilityChangePluginEvent ev = (ProgramVisibilityChangePluginEvent) event;
+ controller.showTable(ev.isProgramVisible());
+ }
+ else {
+ controller.showTable(true);
+ }
+ }
+ }
+
+
+ /**
+ * Ultimately both selections end up calling this to actually show something on
+ * the Ghidra gui
+ *
+ * @param logName
+ * @param sarif
+ */
+ public void showSarif(String logName, SarifSchema210 sarif) {
+ currentProgram = getCurrentProgram();
+ if (currentProgram != null) {
+ if (!sarifControllers.containsKey(currentProgram)) {
+ SarifController controller = new SarifController(currentProgram, this);
+ sarifControllers.put(currentProgram, controller);
+ }
+ SarifController currentController = sarifControllers.get(currentProgram);
+ if (currentController != null) {
+ currentController.showTable(logName, sarif);
+ return;
+ }
+ }
+ Msg.showError(this, tool.getActiveWindow(), "File parse error", "No current program");
+ }
+
+ public void makeSelection(List addrs) {
+ AddressSet selection = new AddressSet();
+ for (Address addr : addrs) {
+ selection.add(addr);
+ }
+ this.setSelection(selection);
+ }
+
+ private void createActions() {
+ //@formatter:off
+ new ActionBuilder("Read", getName())
+ .menuPath("Sarif", "Read File")
+ .menuGroup("sarif", "1")
+ .enabledWhen(ctx -> getCurrentProgram() != null)
+ .onAction(e -> {
+ GhidraFileChooser chooser = new GhidraFileChooser(tool.getActiveWindow());
+ this.readFile(chooser.getSelectedFile());
+ })
+ .buildAndInstall(tool);
+ //@formatter:on
+ }
+
+ private void initializeOptions() {
+ ToolOptions options = tool.getOptions(ToolConstants.GRAPH_OPTIONS);
+ options.addOptionsChangeListener(this);
+
+ HelpLocation help = new HelpLocation(getName(), "Options");
+
+ Options sarifOptions = options.getOptions(NAME);
+ registerOptions(sarifOptions, help);
+ loadOptions(sarifOptions);
+ }
+
+ @Override
+ public void optionsChanged(ToolOptions options, String optionName, Object oldValue,
+ Object newValue) throws OptionsVetoException {
+
+ Options sarifOptions = options.getOptions(NAME);
+ loadOptions(sarifOptions);
+ }
+
+ public void registerOptions(Options options, HelpLocation help) {
+
+ options.setOptionsHelpLocation(help);
+
+ options.registerOption("Display Artifacts", displayArtifacts(), help,
+ "Display artifacts by default");
+
+ options.registerOption("Display Graphs", displayGraphs(), help,
+ "Display graphs by default");
+
+ options.registerOption("Max Graph Size", getGraphSize(), help,
+ "Maximum number of nodes per graph");
+
+ options.registerOption("Append Graphs", appendToGraph(), help,
+ "Append to existing graph");
+
+ }
+
+ public void loadOptions(Options options) {
+
+ displayArtifactsByDefault = options.getBoolean("Display Artifacts", displayArtifacts());
+ displayGraphsByDefault = options.getBoolean("Display Graphs", displayGraphs());
+ maxGraphSize = options.getInt("Max Graph Size", getGraphSize());
+ appendToCurrentGraph = options.getBoolean("Append Graphs", appendToGraph());
+
+ }
+
+ private boolean displayGraphsByDefault = false;
+ public boolean displayGraphs() {
+ return displayGraphsByDefault;
+ }
+
+ private boolean displayArtifactsByDefault = false;
+ public boolean displayArtifacts() {
+ return displayArtifactsByDefault;
+ }
+
+ private int maxGraphSize = 1000;
+ public int getGraphSize() {
+ return maxGraphSize;
+ }
+
+ private boolean appendToCurrentGraph = false;
+ public boolean appendToGraph() {
+ return appendToCurrentGraph;
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/SarifProgramOptions.java b/Ghidra/Features/Sarif/src/main/java/sarif/SarifProgramOptions.java
new file mode 100644
index 0000000000..d0d4274a9e
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/SarifProgramOptions.java
@@ -0,0 +1,611 @@
+/* ###
+ * 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 sarif;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import ghidra.app.util.Option;
+import ghidra.app.util.OptionException;
+
+/**
+ * A class to hold SARIF options.
+ *
+ */
+public class SarifProgramOptions {
+ /**Flag to indicate reading/writing memory blocks*/
+ public final static long OPT_MEMORY_BLOCKS = 0x00000001L;
+ /**Flag to indicate reading/writing memory contents*/
+ public final static long OPT_MEMORY_CONTENTS = 0x00000002L;
+ /**Flag to indicate reading/writing instructions*/
+ public final static long OPT_CODE = 0x00000004L;
+ /**Flag to indicate reading/writing root*/
+ public final static long OPT_DATA = 0x00000008L;
+ /**Flag to indicate reading/writing symbols*/
+ public final static long OPT_SYMBOLS = 0x00000010L;
+ /**Flag to indicate reading/writing equates*/
+ public final static long OPT_EQUATES = 0x00000020L;
+ /**Flag to indicate reading/writing comments*/
+ public final static long OPT_COMMENTS = 0x00000040L;
+ /**Flag to indicate reading/writing properties*/
+ public final static long OPT_PROPERTIES = 0x00000080L;
+ /**Flag to indicate reading/writing trees*/
+ public final static long OPT_TREES = 0x00000100L;
+ /**Flag to indicate reading/writing empty program tree nodes*/
+ public final static long OPT_EMPTY_TREE_NODES = 0x00000200L;
+ /**Flag to indicate reading/writing references*/
+ public final static long OPT_REFERENCES = 0x00000400L;
+ /**Flag to indicate reading/writing functions*/
+ public final static long OPT_FUNCTIONS = 0x00000800L;
+ /**
+ * Used to signify that symbols should be overwritten when
+ * necessary. This value is not being included in
+ * the ALL
constant.
+ */
+ public final static long OVERWRITE_SYMBOLS = 0x20000000L;
+
+ /**
+ * Used to signify that references should be overwritten when
+ * necessary. This value is not being included in
+ * the ALL
constant.
+ */
+ public final static long OVERWRITE_REFS = 0x40000000L;
+
+ /**
+ * Used to signify that an existing program is being
+ * updated. This value is not being included in
+ * the ALL
constant.
+ */
+ public final static long ADD_2_PROG = 0x80000000L;
+
+ private boolean addToProgram = false;
+ private boolean memoryBlocks = true;
+ private boolean memoryContents = true;
+ private boolean overwriteMemoryConflicts = false;
+ private boolean instructions = true;
+ private boolean overwriteDataConflicts = true;
+ private boolean data = true;
+ private boolean symbols = true;
+ private boolean overwriteSymbolConflicts = true;
+ private boolean equates = true;
+ private boolean comments = true;
+ private boolean properties = true;
+ private boolean overwritePropertyConflicts = true;
+ private boolean bookmarks = true;
+ private boolean overwriteBookmarkConflicts = true;
+ private boolean trees = true;
+ private boolean references = true;
+ private boolean overwriteReferenceConflicts = true;
+ private boolean functions = true;
+ private boolean registers = true;
+ private boolean relocationTable = true;
+ private boolean entryPoints = true;
+ private boolean externalLibraries = true;
+
+ /**
+ * Returns an array of importer options representing
+ * the flags in this class.
+ * @param isAddToProgram if true then adding to existing program
+ * @return the array of importer options
+ */
+ public List getOptions(boolean isAddToProgram) {
+ this.addToProgram = isAddToProgram;
+
+ ArrayList optionList = new ArrayList<>();
+
+ optionList.add(new Option("Memory Blocks", Boolean.valueOf(isMemoryBlocks())));
+ optionList.add(new Option("Memory Contents", Boolean.valueOf(isMemoryContents())));
+ if (isAddToProgram) {
+ optionList.add(new Option("Overwrite Memory Conflicts",
+ Boolean.valueOf(isOverwriteMemoryConflicts())));
+ }
+ optionList.add(new Option("Instructions", Boolean.valueOf(isInstructions())));
+ optionList.add(new Option("Data", Boolean.valueOf(isData())));
+ if (isAddToProgram) {
+ optionList.add(
+ new Option("Overwrite Data Conflicts", Boolean.valueOf(isOverwriteDataConflicts())));
+ }
+ optionList.add(new Option("Symbols", Boolean.valueOf(isSymbols())));
+ if (isAddToProgram) {
+ optionList.add(new Option("Overwrite Symbol Conflicts",
+ Boolean.valueOf(isOverwriteSymbolConflicts())));
+ }
+ optionList.add(new Option("Equates", Boolean.valueOf(isEquates())));
+ optionList.add(new Option("Comments", Boolean.valueOf(isComments())));
+ optionList.add(new Option("Properties", Boolean.valueOf(isProperties())));
+ if (isAddToProgram) {
+ optionList.add(new Option("Overwrite Property Conflicts",
+ Boolean.valueOf(isOverwritePropertyConflicts())));
+ }
+ optionList.add(new Option("Bookmarks", Boolean.valueOf(isBookmarks())));
+ if (isAddToProgram) {
+ optionList.add(new Option("Overwrite Bookmark Conflicts",
+ Boolean.valueOf(isOverwriteBookmarkConflicts())));
+ }
+ optionList.add(new Option("Trees", Boolean.valueOf(isTrees())));
+ optionList.add(new Option("References", Boolean.valueOf(isReferences())));
+ if (isAddToProgram) {
+ optionList.add(new Option("Overwrite Reference Conflicts",
+ Boolean.valueOf(isOverwriteReferenceConflicts())));
+ }
+ optionList.add(new Option("Functions", Boolean.valueOf(isFunctions())));
+ optionList.add(new Option("Registers", Boolean.valueOf(isRegisters())));
+ optionList.add(new Option("Relocation Table", Boolean.valueOf(isRelocationTable())));
+ optionList.add(new Option("Entry Points", Boolean.valueOf(isEntryPoints())));
+ optionList.add(new Option("External Libraries", Boolean.valueOf(isExternalLibraries())));
+
+ return optionList;
+ }
+
+ /**
+ * Sets the options. This method is not for defining the options, but
+ * rather for setting the values of options. If invalid options
+ * are passed in, then OptionException should be thrown.
+ * @param options the option values for SARIF
+ * @throws OptionException if invalid options are passed in
+ */
+ public void setOptions(List options) throws OptionException {
+ for (Option option : options) {
+ String optName = option.getName();
+ Object optValue = option.getValue();
+
+ if (!(optValue instanceof Boolean)) {
+ throw new OptionException("Invalid type for option: " + optName);
+ }
+
+ boolean val = ((Boolean) optValue).booleanValue();
+
+ if (optName.equals("Memory Blocks")) {
+ setMemoryBlocks(val);
+ }
+ else if (optName.equals("Memory Contents")) {
+ setMemoryContents(val);
+ }
+ else if (optName.equals("Overwrite Memory Conflicts")) {
+ setOverwriteMemoryConflicts(val);
+ }
+ else if (optName.equals("Instructions")) {
+ setInstructions(val);
+ }
+ else if (optName.equals("Data")) {
+ setData(val);
+ }
+ else if (optName.equals("Overwrite Data Conflicts")) {
+ setOverwriteDataConflicts(val);
+ }
+ else if (optName.equals("Symbols")) {
+ setSymbols(val);
+ }
+ else if (optName.equals("Overwrite Symbol Conflicts")) {
+ setOverwriteSymbolConflicts(val);
+ }
+ else if (optName.equals("Equates")) {
+ setEquates(val);
+ }
+ else if (optName.equals("Comments")) {
+ setComments(val);
+ }
+ else if (optName.equals("Properties")) {
+ setProperties(val);
+ }
+ else if (optName.equals("Overwrite Property Conflicts")) {
+ setOverwritePropertyConflicts(val);
+ }
+ else if (optName.equals("Bookmarks")) {
+ setBookmarks(val);
+ }
+ else if (optName.equals("Overwrite Bookmark Conflicts")) {
+ setOverwriteBookmarkConflicts(val);
+ }
+ else if (optName.equals("Trees")) {
+ setTrees(val);
+ }
+ else if (optName.equals("References")) {
+ setReferences(val);
+ }
+ else if (optName.equals("Overwrite Reference Conflicts")) {
+ setOverwriteReferenceConflicts(val);
+ }
+ else if (optName.equals("Functions")) {
+ setFunctions(val);
+ }
+ else if (optName.equals("Registers")) {
+ setRegisters(val);
+ }
+ else if (optName.equals("Relocation Table")) {
+ setRelocationTable(val);
+ }
+ else if (optName.equals("Entry Points")) {
+ setEntryPoints(val);
+ }
+ else if (optName.equals("External Libraries")) {
+ setExternalLibraries(val);
+ }
+ else {
+ throw new OptionException("Unknown option: " + optName);
+ }
+ }
+ }
+
+ /**
+ * Returns true if importing to an existing program.
+ * Importing to an existing program creates a new
+ * set of potential conflicts. For example, memory block
+ * may collide. When this options is true, additional
+ * options are visible.
+ * @return true if importing to an existing program
+ */
+ public boolean isAddToProgram() {
+ return addToProgram;
+ }
+
+ /**
+ * If true, then instructions should be read/written.
+ * @return true if instructions should be read/written
+ */
+ public boolean isInstructions() {
+ return instructions;
+ }
+
+ /**
+ * If true, then comments should be read/written.
+ * @return true if comments should be read/written
+ */
+ public boolean isComments() {
+ return comments;
+ }
+
+ /**
+ * If true, then root should be read/written.
+ * @return true if root should be read/written
+ */
+ public boolean isData() {
+ return data;
+ }
+
+ /**
+ * If true, then equates should be read/written.
+ * @return true if equates should be read/written
+ */
+ public boolean isEquates() {
+ return equates;
+ }
+
+ /**
+ * If true, then functions should be read/written.
+ * @return true if functions should be read/written
+ */
+ public boolean isFunctions() {
+ return functions;
+ }
+
+ /**
+ * If true, then memory blocks should be read/written.
+ * @return true if memory blocks should be read/written
+ */
+ public boolean isMemoryBlocks() {
+ return memoryBlocks;
+ }
+
+ /**
+ * If true, then memory contents should be read/written.
+ * @return true if memory contents should be read/written
+ */
+ public boolean isMemoryContents() {
+ return memoryContents;
+ }
+
+ /**
+ * If true, then properties should be read/written.
+ * @return true if properties should be read/written
+ */
+ public boolean isProperties() {
+ return properties;
+ }
+
+ /**
+ * If true, then references (memory, stack, external) should be read/written.
+ * @return true if references should be read/written
+ */
+ public boolean isReferences() {
+ return references;
+ }
+
+ /**
+ * If true, then symbols should be read/written.
+ * @return true if symbols should be read/written
+ */
+ public boolean isSymbols() {
+ return symbols;
+ }
+
+ /**
+ * If true, then program trees should be read/written.
+ * @return true if program trees should be read/written
+ */
+ public boolean isTrees() {
+ return trees;
+ }
+
+ /**
+ * Sets instructions to be read/written.
+ * @param b true if instructions should read/written
+ */
+ public void setInstructions(boolean b) {
+ instructions = b;
+ }
+
+ /**
+ * Sets comments to be read/written.
+ * @param b true if comments should read/written
+ */
+ public void setComments(boolean b) {
+ comments = b;
+ }
+
+ /**
+ * Sets root to be read/written.
+ * @param b true if root should read/written
+ */
+ public void setData(boolean b) {
+ data = b;
+ }
+
+ /**
+ * Sets equates to be read/written.
+ * @param b true if equates should read/written
+ */
+ public void setEquates(boolean b) {
+ equates = b;
+ }
+
+ /**
+ * Sets functions to be read/written.
+ * @param b true if functions should read/written
+ */
+ public void setFunctions(boolean b) {
+ functions = b;
+ }
+
+ /**
+ * Sets memory blocks to be read/written.
+ * @param b true if memory blocks should read/written
+ */
+ public void setMemoryBlocks(boolean b) {
+ memoryBlocks = b;
+ }
+
+ /**
+ * Sets memory contents to be read/written.
+ * @param b true if memory contents should read/written
+ */
+ public void setMemoryContents(boolean b) {
+ memoryContents = b;
+ }
+
+ /**
+ * Sets properties to be read/written.
+ * @param b true if properties should read/written
+ */
+ public void setProperties(boolean b) {
+ properties = b;
+ }
+
+ /**
+ * Sets references to be read/written.
+ * @param b true if references should read/written
+ */
+ public void setReferences(boolean b) {
+ references = b;
+ }
+
+ /**
+ * Sets symbols to be read/written.
+ * @param b true if symbols should read/written
+ */
+ public void setSymbols(boolean b) {
+ symbols = b;
+ }
+
+ /**
+ * Sets program trees to be read/written.
+ * @param b true if program trees should read/written
+ */
+ public void setTrees(boolean b) {
+ trees = b;
+ }
+
+ /**
+ * If true, then bookmarks should be read/written.
+ * @return true if bookmarks should be read/written
+ */
+ public boolean isBookmarks() {
+ return bookmarks;
+ }
+
+ /**
+ * If true, then registers should be read/written.
+ * @return true if registers should be read/written
+ */
+ public boolean isRegisters() {
+ return registers;
+ }
+
+ /**
+ * If true, then the relocation table should be read/written.
+ * @return true if the relocation table should be read/written
+ */
+ public boolean isRelocationTable() {
+ return relocationTable;
+ }
+
+ /**
+ * Sets bookmarks to be read/written.
+ * @param b true if bookmarks should read/written
+ */
+ public void setBookmarks(boolean b) {
+ bookmarks = b;
+ }
+
+ /**
+ * Sets registers to be read/written.
+ * @param b true if registers should read/written
+ */
+ public void setRegisters(boolean b) {
+ registers = b;
+ }
+
+ /**
+ * Sets relocation tables to be read/written.
+ * @param b true if relocation table should read/written
+ */
+ public void setRelocationTable(boolean b) {
+ relocationTable = b;
+ }
+
+ /**
+ * If true, then the entry points should be read/written.
+ * @return true if the entry points should be read/written
+ */
+ public boolean isEntryPoints() {
+ return entryPoints;
+ }
+
+ /**
+ * If true, then the external libraries should be read/written.
+ * @return true if the external libraries should be read/written
+ */
+ public boolean isExternalLibraries() {
+ return externalLibraries;
+ }
+
+ /**
+ * Sets entry points to be read/written.
+ * @param b true if entry points should read/written
+ */
+ public void setEntryPoints(boolean b) {
+ entryPoints = b;
+ }
+
+ /**
+ * Sets external libraries to be read/written.
+ * @param b true if external libraries should read/written
+ */
+ public void setExternalLibraries(boolean b) {
+ externalLibraries = b;
+ }
+
+ /**
+ * If true, then property conflicts will be overwritten.
+ * @return true if property conflicts will be overwritten
+ */
+ public boolean isOverwritePropertyConflicts() {
+ return overwritePropertyConflicts;
+ }
+
+ /**
+ * If true, then bookmark conflicts will be overwritten.
+ * @return true if bookmark conflicts will be overwritten
+ */
+ public boolean isOverwriteBookmarkConflicts() {
+ return overwriteBookmarkConflicts;
+ }
+
+ /**
+ * If true, then symbol conflicts will be overwritten.
+ * @return true if symbol conflicts will be overwritten
+ */
+ public boolean isOverwriteSymbolConflicts() {
+ return overwriteSymbolConflicts;
+ }
+
+ /**
+ * If true, then reference conflicts will be overwritten.
+ * @return true if reference conflicts will be overwritten
+ */
+ public boolean isOverwriteReferenceConflicts() {
+ return overwriteReferenceConflicts;
+ }
+
+ /**
+ * If true, then memory conflicts will be overwritten.
+ * @return true if memory conflicts will be overwritten
+ */
+ public boolean isOverwriteMemoryConflicts() {
+ return overwriteMemoryConflicts;
+ }
+
+ /**
+ * If true, then root conflicts will be overwritten.
+ * @return true if root conflicts will be overwritten
+ */
+ public boolean isOverwriteDataConflicts() {
+ return overwriteDataConflicts;
+ }
+
+ /**
+ * Sets bookmark conflicts to always be overwritten.
+ * @param b true if bookmark conflicts should always be overwritten
+ */
+ public void setOverwriteBookmarkConflicts(boolean b) {
+ overwriteBookmarkConflicts = b;
+ }
+
+ /**
+ * Sets memory conflicts to always be overwritten.
+ * @param b true if memory conflicts should always be overwritten
+ */
+ public void setOverwriteMemoryConflicts(boolean b) {
+ overwriteMemoryConflicts = b;
+ }
+
+ /**
+ * Sets root conflicts to always be overwritten.
+ * @param b true if root conflicts should always be overwritten
+ */
+ public void setOverwriteDataConflicts(boolean b) {
+ overwriteDataConflicts = b;
+ }
+
+ /**
+ * Sets property conflicts to always be overwritten.
+ * @param b true if property conflicts should always be overwritten
+ */
+ public void setOverwritePropertyConflicts(boolean b) {
+ overwritePropertyConflicts = b;
+ }
+
+ /**
+ * Sets reference conflicts to always be overwritten.
+ * @param b true if reference conflicts should always be overwritten
+ */
+ public void setOverwriteReferenceConflicts(boolean b) {
+ overwriteReferenceConflicts = b;
+ }
+
+ /**
+ * Sets symbol conflicts to always be overwritten.
+ * @param b true if symbol conflicts should always be overwritten
+ */
+ public void setOverwriteSymbolConflicts(boolean b) {
+ overwriteSymbolConflicts = b;
+ }
+
+ public void setAddToProgram(boolean addToProgram) {
+ this.addToProgram = addToProgram;
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/SarifUtils.java b/Ghidra/Features/Sarif/src/main/java/sarif/SarifUtils.java
new file mode 100644
index 0000000000..bf71814d19
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/SarifUtils.java
@@ -0,0 +1,72 @@
+/* ###
+ * 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 sarif;
+
+import java.io.ByteArrayInputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import org.bouncycastle.util.encoders.Base64;
+
+import com.contrastsecurity.sarif.Artifact;
+import com.contrastsecurity.sarif.ArtifactContent;
+import com.contrastsecurity.sarif.ReportingDescriptor;
+import com.contrastsecurity.sarif.ReportingDescriptorReference;
+import com.contrastsecurity.sarif.Run;
+import com.contrastsecurity.sarif.ToolComponent;
+
+public class SarifUtils {
+
+ public static ByteArrayInputStream getArtifactContent(Artifact artifact) {
+ ArtifactContent content = artifact.getContents();
+ String b64 = content.getBinary();
+ byte[] decoded = Base64.decode(b64);
+ return new ByteArrayInputStream(decoded);
+ }
+
+ public static ReportingDescriptor getTaxaValue(ReportingDescriptorReference taxa, ToolComponent taxonomy) {
+ List view = new ArrayList<>(taxonomy.getTaxa());
+ return view.get(taxa.getIndex().intValue());
+ }
+
+ public static ToolComponent getTaxonomy(ReportingDescriptorReference taxa, Set taxonomies) {
+ Object idx = taxa.getToolComponent().getIndex();
+ if (idx == null) {
+ List view = new ArrayList<>(taxonomies);
+ idx= taxa.getIndex();
+ return view.get(idx instanceof Long ? ((Long)idx).intValue() : (Integer) idx);
+ }
+ for (ToolComponent taxonomy : taxonomies) {
+ if (taxonomy.getName().equals(taxa.getToolComponent().getName())) {
+ return taxonomy;
+ }
+ }
+ return null;
+ }
+
+ public static List getTaxonomyNames(Run sarifRun) {
+ List names = new ArrayList<>();
+ Set taxonomies = sarifRun.getTaxonomies();
+ if (taxonomies != null) {
+ for (ToolComponent taxonomy : sarifRun.getTaxonomies()) {
+ names.add(taxonomy.getName());
+ }
+ }
+ return names;
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/AbstractExtWriter.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/AbstractExtWriter.java
new file mode 100644
index 0000000000..058db7ef0b
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/AbstractExtWriter.java
@@ -0,0 +1,35 @@
+/* ###
+ * 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 sarif.export;
+
+import java.io.IOException;
+import java.io.Writer;
+
+import ghidra.program.model.data.ISF.AbstractIsfWriter;
+import ghidra.util.exception.CancelledException;
+import ghidra.util.task.TaskMonitor;
+
+public abstract class AbstractExtWriter extends AbstractIsfWriter {
+
+ public AbstractExtWriter(Writer baseWriter) throws IOException {
+ super(baseWriter);
+ STRICT = false;
+ }
+
+ @Override
+ protected abstract void genRoot(TaskMonitor monitor) throws CancelledException, IOException;
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/SarifExporter.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/SarifExporter.java
new file mode 100644
index 0000000000..2e8eed31a0
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/SarifExporter.java
@@ -0,0 +1,92 @@
+/* ###
+ * 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 sarif.export;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+import ghidra.app.util.DomainObjectService;
+import ghidra.app.util.Option;
+import ghidra.app.util.OptionException;
+import ghidra.app.util.exporter.Exporter;
+import ghidra.app.util.exporter.ExporterException;
+import ghidra.framework.model.DomainObject;
+import ghidra.program.model.address.AddressSetView;
+import ghidra.program.model.listing.Program;
+import ghidra.util.HelpLocation;
+import ghidra.util.exception.CancelledException;
+import ghidra.util.task.TaskMonitor;
+import sarif.SarifProgramOptions;
+import sarif.managers.ProgramSarifMgr;
+
+/**
+ * An implementation of exporter that creates
+ * an SARIF representation of the program.
+ */
+public class SarifExporter extends Exporter {
+ private SarifProgramOptions options = new SarifProgramOptions();
+
+ /**
+ * Constructs a new SARIF exporter.
+ */
+ public SarifExporter() {
+ super("SARIF", "sarif", new HelpLocation("ExporterPlugin", "sarif"));
+ }
+
+ @Override
+ public List getOptions(DomainObjectService domainObjectService) {
+ if (options == null) {
+ options = new SarifProgramOptions();
+ }
+ return options.getOptions(false);
+ }
+
+ @Override
+ public void setOptions(List options) throws OptionException {
+ this.options.setOptions(options);
+ }
+
+ @Override
+ public boolean export(File file, DomainObject domainObj, AddressSetView addrSet, TaskMonitor monitor)
+ throws IOException, ExporterException {
+
+ log.clear();
+
+ if (!(domainObj instanceof Program)) {
+ log.appendMsg("Unsupported type: "+domainObj.getClass().getName());
+ return false;
+ }
+ Program program = (Program)domainObj;
+
+ if (addrSet == null) {
+ addrSet = program.getMemory();
+ }
+
+ ProgramSarifMgr mgr = new ProgramSarifMgr(program, file);
+
+ try {
+ log = mgr.write(program, addrSet, monitor, options);
+ }
+ catch (CancelledException e) {
+ throw new ExporterException("User cancelled SARIF export.");
+ }
+
+ options = null;
+
+ return true;
+ }
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/SarifObject.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/SarifObject.java
new file mode 100644
index 0000000000..808f5bb57a
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/SarifObject.java
@@ -0,0 +1,123 @@
+/* ###
+ * 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 sarif.export;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+
+import ghidra.program.model.address.Address;
+import ghidra.program.model.address.AddressRange;
+import ghidra.program.model.address.AddressRangeIterator;
+import ghidra.program.model.address.AddressSetView;
+import ghidra.program.model.address.AddressSpace;
+import ghidra.program.model.data.ISF.IsfObject;
+
+public class SarifObject implements IsfObject {
+
+ public static boolean SARIF = true;
+
+ protected JsonObject message;
+ protected String kind;
+ protected String level;
+ protected String ruleId;
+ protected JsonArray locations;
+ protected JsonObject properties;
+
+ protected JsonObject element;
+
+ public SarifObject(String key, String ruleKey, JsonElement element) {
+ if (SARIF) {
+ message = new JsonObject();
+ message.addProperty("text", key);
+ kind = "INFORMATIONAL";
+ level = "NONE";
+ ruleId = ruleKey;
+ properties = new JsonObject();
+ properties.add("additionalProperties", element);
+ } else {
+ this.element = (JsonObject) element;
+ this.element.addProperty("key", key);
+ this.element.addProperty("rule", ruleKey);
+ }
+ }
+
+ public SarifObject(String key, String ruleKey, JsonElement tree, Address min, Address max) {
+ this(key, ruleKey, tree);
+ if (min != null) {
+ writeLocations(min, max);
+ }
+ }
+
+ public SarifObject(String key, String ruleKey, JsonElement tree, AddressSetView body) {
+ this(key, ruleKey, tree);
+ if (body != null) {
+ writeLocations(body);
+ }
+ }
+
+ protected void writeLocations(Address min, Address max) {
+ if (SARIF) {
+ locations = new JsonArray();
+ JsonObject element = new JsonObject();
+ locations.add(element);
+ JsonObject ploc = new JsonObject();
+ element.add("physicalLocation", ploc);
+ JsonObject address = new JsonObject();
+ ploc.add("address", address);
+ address.addProperty("absoluteAddress", min.getOffset());
+ address.addProperty("length", max.subtract(min) + 1);
+ Address minAddress = min;
+ if (minAddress.getAddressSpace().getType() != AddressSpace.TYPE_RAM) {
+ JsonObject artifact = new JsonObject();
+ ploc.add("artifactLocation", artifact);
+ artifact.addProperty("uri", minAddress.toString());
+ }
+ }
+ else {
+ element.addProperty("startAddress", min.toString(true));
+ element.addProperty("stopAddress", max.toString(true));
+ }
+ }
+
+ protected void writeLocations(AddressSetView set) {
+ if (SARIF) {
+ locations = new JsonArray();
+ AddressRangeIterator addressRanges = set.getAddressRanges();
+ while (addressRanges.hasNext()) {
+ JsonObject element = new JsonObject();
+ locations.add(element);
+ AddressRange next = addressRanges.next();
+ JsonObject ploc = new JsonObject();
+ element.add("physicalLocation", ploc);
+ JsonObject address = new JsonObject();
+ ploc.add("address", address);
+ address.addProperty("absoluteAddress", next.getMinAddress().getOffset());
+ address.addProperty("length", next.getLength());
+ Address minAddress = next.getMinAddress();
+ if (minAddress.getAddressSpace().getType() != AddressSpace.TYPE_RAM) {
+ JsonObject artifact = new JsonObject();
+ ploc.add("artifactLocation", artifact);
+ artifact.addProperty("uri", minAddress.toString());
+ }
+ }
+ }
+ else {
+ element.addProperty("startAddress", set.getMinAddress().toString(true));
+ element.addProperty("stopAddress", set.getMaxAddress().toString(true));
+ }
+ }
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/SarifWriterTask.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/SarifWriterTask.java
new file mode 100644
index 0000000000..703a5e9617
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/SarifWriterTask.java
@@ -0,0 +1,62 @@
+/* ###
+ * 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 sarif.export;
+
+import java.io.IOException;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+
+import ghidra.program.model.data.ISF.AbstractIsfWriter;
+import ghidra.util.Msg;
+import ghidra.util.exception.CancelledException;
+import ghidra.util.task.Task;
+import ghidra.util.task.TaskMonitor;
+
+public class SarifWriterTask extends Task {
+
+ protected AbstractIsfWriter writer;
+ protected JsonArray results;
+
+ public SarifWriterTask(String tag, AbstractIsfWriter writer, JsonArray results) {
+ super(tag, true, false, true);
+ this.writer = writer;
+ this.results = results;
+ }
+
+ @Override
+ public void run(TaskMonitor monitor) {
+ try {
+ try {
+ writer.getRootObject(monitor);
+ JsonArray res = writer.getResults();
+ for (JsonElement element : res) {
+ if (monitor.isCancelled()) {
+ break;
+ }
+ results.add(element);
+ }
+ } finally {
+ writer.close();
+ }
+ } catch (CancelledException e) {
+ // user cancelled; ignore
+ } catch (IOException e) {
+ Msg.error("Export Data Types Failed", "Error exporting Data Types: " + e);
+ return;
+ }
+ }
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/bkmk/ExtBookmark.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/bkmk/ExtBookmark.java
new file mode 100644
index 0000000000..95a84f42ef
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/bkmk/ExtBookmark.java
@@ -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 sarif.export.bkmk;
+
+import ghidra.program.model.data.ISF.IsfObject;
+import ghidra.program.model.listing.Bookmark;
+
+public class ExtBookmark implements IsfObject {
+
+ String name;
+ String comment;
+ String kind;
+
+ public ExtBookmark(Bookmark b) {
+ String category = b.getCategory();
+ String comment = b.getComment();
+ if (category != null && category.length() != 0) {
+ name = category;
+ }
+ if (comment != null && comment.length() != 0) {
+ this.comment = comment;
+ }
+ kind = b.getType().getTypeString();
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/bkmk/SarifBookmarkWriter.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/bkmk/SarifBookmarkWriter.java
new file mode 100644
index 0000000000..dc8164fde6
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/bkmk/SarifBookmarkWriter.java
@@ -0,0 +1,54 @@
+/* ###
+ * IP: GHIDRA
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package sarif.export.bkmk;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.List;
+
+import ghidra.program.model.listing.Bookmark;
+import ghidra.util.exception.CancelledException;
+import ghidra.util.task.TaskMonitor;
+import sarif.export.AbstractExtWriter;
+import sarif.export.SarifObject;
+import sarif.managers.BookmarksSarifMgr;
+
+public class SarifBookmarkWriter extends AbstractExtWriter {
+
+ private List bookmarks = new ArrayList<>();
+
+ public SarifBookmarkWriter(List target, Writer baseWriter) throws IOException {
+ super(baseWriter);
+ bookmarks = target;
+ }
+
+ @Override
+ protected void genRoot(TaskMonitor monitor) throws CancelledException, IOException {
+ genBookmarks(monitor);
+ root.add("bookmarks", objects);
+ }
+
+ private void genBookmarks(TaskMonitor monitor) throws CancelledException, IOException{
+ monitor.initialize(bookmarks.size());
+ for (Bookmark b : bookmarks) {
+ ExtBookmark isf = new ExtBookmark(b);
+ SarifObject sarif = new SarifObject(BookmarksSarifMgr.SUBKEY, BookmarksSarifMgr.KEY, getTree(isf), b.getAddress(), b.getAddress());
+ objects.add(getTree(sarif));
+ monitor.increment();
+ }
+ }
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/code/ExtCodeBlock.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/code/ExtCodeBlock.java
new file mode 100644
index 0000000000..88702b0358
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/code/ExtCodeBlock.java
@@ -0,0 +1,26 @@
+/* ###
+ * 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 sarif.export.code;
+
+import ghidra.program.model.address.AddressRange;
+import ghidra.program.model.data.ISF.IsfObject;
+
+public class ExtCodeBlock implements IsfObject {
+
+ public ExtCodeBlock(AddressRange range) {
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/code/ExtCodeOverride.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/code/ExtCodeOverride.java
new file mode 100644
index 0000000000..8f404edb33
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/code/ExtCodeOverride.java
@@ -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 sarif.export.code;
+
+import generic.stl.Pair;
+import ghidra.program.model.data.ISF.IsfObject;
+import ghidra.program.model.listing.FlowOverride;
+import ghidra.program.model.listing.Instruction;
+
+public class ExtCodeOverride implements IsfObject {
+
+ String kind;
+
+ public ExtCodeOverride(Pair pair) {
+ kind = pair.second.name();
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/code/SarifCodeWriter.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/code/SarifCodeWriter.java
new file mode 100644
index 0000000000..934fda3077
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/code/SarifCodeWriter.java
@@ -0,0 +1,70 @@
+/* ###
+ * 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 sarif.export.code;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.List;
+
+import generic.stl.Pair;
+import ghidra.program.model.address.AddressRange;
+import ghidra.program.model.listing.FlowOverride;
+import ghidra.program.model.listing.Instruction;
+import ghidra.util.exception.CancelledException;
+import ghidra.util.task.TaskMonitor;
+import sarif.export.AbstractExtWriter;
+import sarif.export.SarifObject;
+import sarif.managers.CodeSarifMgr;
+
+public class SarifCodeWriter extends AbstractExtWriter {
+
+ private List blocks;
+ private List> overrides;
+
+ public SarifCodeWriter(List target0, List> target1, Writer baseWriter) throws IOException {
+ super(baseWriter);
+ this.blocks = target0;
+ this.overrides = target1;
+ }
+
+ @Override
+ protected void genRoot(TaskMonitor monitor) throws CancelledException, IOException {
+ genCode(monitor);
+ genOverrides(monitor);
+ root.add("code", objects);
+ }
+
+ private void genCode(TaskMonitor monitor) throws CancelledException, IOException{
+ monitor.initialize(blocks.size());
+ for (AddressRange range : blocks) {
+ ExtCodeBlock isf = new ExtCodeBlock(range);
+ SarifObject sarif = new SarifObject(CodeSarifMgr.SUBKEY, CodeSarifMgr.KEY, getTree(isf), range.getMinAddress(), range.getMaxAddress());
+ objects.add(getTree(sarif));
+ monitor.increment();
+ }
+ }
+
+ private void genOverrides(TaskMonitor monitor) throws CancelledException, IOException{
+ monitor.initialize(overrides.size());
+ for (Pair pair : overrides) {
+ Instruction inst = pair.first;
+ ExtCodeOverride isf = new ExtCodeOverride(pair);
+ SarifObject sarif = new SarifObject(CodeSarifMgr.SUBKEY2, CodeSarifMgr.KEY, getTree(isf), inst.getMinAddress(), inst.getMaxAddress());
+ objects.add(getTree(sarif));
+ monitor.increment();
+ }
+ }
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/comments/ExtComment.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/comments/ExtComment.java
new file mode 100644
index 0000000000..ec197e1ca3
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/comments/ExtComment.java
@@ -0,0 +1,33 @@
+/* ###
+ * 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 sarif.export.comments;
+
+import generic.stl.Pair;
+import ghidra.program.model.data.ISF.IsfObject;
+
+public class ExtComment implements IsfObject {
+
+ String kind;
+ String value;
+ boolean standard;
+
+ public ExtComment(Pair pair, boolean standard) {
+ kind = pair.first;
+ value = pair.second;
+ this.standard = standard;
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/comments/SarifCommentWriter.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/comments/SarifCommentWriter.java
new file mode 100644
index 0000000000..8655226cce
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/comments/SarifCommentWriter.java
@@ -0,0 +1,82 @@
+/* ###
+ * IP: GHIDRA
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package sarif.export.comments;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.List;
+
+import generic.stl.Pair;
+import ghidra.program.model.address.Address;
+import ghidra.program.model.listing.CodeUnit;
+import ghidra.util.exception.CancelledException;
+import ghidra.util.task.TaskMonitor;
+import sarif.export.AbstractExtWriter;
+import sarif.export.SarifObject;
+import sarif.managers.CommentsSarifMgr;
+
+public class SarifCommentWriter extends AbstractExtWriter {
+
+ private List>> comments0;
+ private List>> comments1;
+
+ public SarifCommentWriter(List>> target, Writer baseWriter) throws IOException {
+ super(baseWriter);
+ this.comments0 = target;
+ }
+
+ public SarifCommentWriter(Writer baseWriter, List>> target) throws IOException {
+ super(baseWriter);
+ this.comments1 = target;
+ }
+
+ @Override
+ protected void genRoot(TaskMonitor monitor) throws CancelledException, IOException {
+ genComments0(monitor);
+ genComments1(monitor);
+ root.add("comments", objects);
+ }
+
+ private void genComments0(TaskMonitor monitor) throws CancelledException, IOException {
+ if (comments0 == null) {
+ return;
+ }
+ monitor.initialize(comments0.size());
+ for (Pair> pair : comments0) {
+ CodeUnit cu = pair.first;
+ ExtComment isf = new ExtComment(pair.second, true);
+ SarifObject sarif = new SarifObject(CommentsSarifMgr.SUBKEY, CommentsSarifMgr.KEY, getTree(isf), cu.getMinAddress(),
+ cu.getMaxAddress());
+ objects.add(getTree(sarif));
+ monitor.increment();
+ }
+ }
+
+ private void genComments1(TaskMonitor monitor) throws CancelledException, IOException {
+ if (comments1 == null) {
+ return;
+ }
+ monitor.initialize(comments1.size());
+ for (Pair> pair : comments1) {
+ Address addr = pair.first;
+ ExtComment isf = new ExtComment(pair.second, false);
+ SarifObject sarif = new SarifObject(CommentsSarifMgr.SUBKEY, CommentsSarifMgr.KEY, getTree(isf), addr, addr);
+ objects.add(getTree(sarif));
+ monitor.increment();
+ }
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/data/ExtDataTypeWriterTask.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/data/ExtDataTypeWriterTask.java
new file mode 100644
index 0000000000..fa9ecde6f1
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/data/ExtDataTypeWriterTask.java
@@ -0,0 +1,63 @@
+/* ###
+ * IP: GHIDRA
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package sarif.export.data;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.List;
+
+import ghidra.program.model.data.DataType;
+import ghidra.program.model.data.DataTypeManager;
+import ghidra.program.model.data.ISF.IsfDataTypeWriter;
+import ghidra.util.Msg;
+import ghidra.util.exception.CancelledException;
+import ghidra.util.task.Task;
+import ghidra.util.task.TaskMonitor;
+
+public class ExtDataTypeWriterTask extends Task {
+
+ private final DataTypeManager programDataTypeMgr;
+ private final List dataTypeList;
+ private final File file;
+
+ public ExtDataTypeWriterTask(DataTypeManager programDataTypeMgr, List dataTypeList, File file) {
+ super("Export Data Types", true, false, true);
+ this.programDataTypeMgr = programDataTypeMgr;
+ this.dataTypeList = dataTypeList;
+ this.file = file;
+ }
+
+ @Override
+ public void run(TaskMonitor monitor) {
+ try {
+ monitor.setMessage("Export to " + file.getName() + "...");
+ IsfDataTypeWriter dataTypeWriter = new ExtIsfDataTypeWriter(programDataTypeMgr, dataTypeList, new FileWriter(file));
+
+ try {
+ dataTypeWriter.getRootObject(monitor);
+ //dataTypeWriter.write(object);
+ } finally {
+ dataTypeWriter.close();
+ }
+ } catch (CancelledException e) {
+ // user cancelled; ignore
+ } catch (IOException e) {
+ Msg.error("Export Data Types Failed", "Error exporting Data Types: " + e);
+ return;
+ }
+ }
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/data/ExtIsfComponent.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/data/ExtIsfComponent.java
new file mode 100644
index 0000000000..58f71ddd91
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/data/ExtIsfComponent.java
@@ -0,0 +1,37 @@
+/* ###
+ * 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 sarif.export.data;
+
+import ghidra.program.model.data.BitFieldDataType;
+import ghidra.program.model.data.DataTypeComponent;
+import ghidra.program.model.data.ISF.IsfComponent;
+import ghidra.program.model.data.ISF.IsfObject;
+
+public class ExtIsfComponent extends IsfComponent {
+
+ Integer bitOffset;
+ Integer bitSize;
+
+ public ExtIsfComponent(DataTypeComponent component, IsfObject typeObj) {
+ super(component, typeObj);
+ if (component.isBitFieldComponent()) {
+ BitFieldDataType dt = (BitFieldDataType) component.getDataType();
+ bitOffset = dt.getBitOffset();
+ bitSize = dt.getBitSize();
+ }
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/data/ExtIsfComposite.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/data/ExtIsfComposite.java
new file mode 100644
index 0000000000..8d9b3e81c7
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/data/ExtIsfComposite.java
@@ -0,0 +1,50 @@
+/* ###
+ * 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 sarif.export.data;
+
+import ghidra.program.model.data.Composite;
+import ghidra.program.model.data.DataTypeComponent;
+import ghidra.program.model.data.ISF.IsfComponent;
+import ghidra.program.model.data.ISF.IsfComposite;
+import ghidra.program.model.data.ISF.IsfDataTypeWriter;
+import ghidra.program.model.data.ISF.IsfObject;
+import ghidra.util.task.TaskMonitor;
+
+public class ExtIsfComposite extends IsfComposite {
+
+ String packed;
+ Integer explicitPackingValue;
+ String alignment;
+ Integer explicitMinimumAlignment;
+
+ public ExtIsfComposite(Composite composite, IsfDataTypeWriter writer, TaskMonitor monitor) {
+ super(composite, writer, monitor);
+ name = composite.getName();
+ location = composite.getCategoryPath().getPath();
+ packed = Boolean.toString(composite.isPackingEnabled());
+ int epval = composite.getExplicitPackingValue();
+ explicitPackingValue = epval > 0 ? epval : null;
+ alignment = Integer.toHexString(composite.getAlignment());
+ int maval = composite.getExplicitMinimumAlignment();
+ explicitMinimumAlignment = maval > 0 ? maval : null;
+ }
+
+ @Override
+ protected IsfComponent getComponent(DataTypeComponent component, IsfObject type) {
+ return new ExtIsfComponent(component, type);
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/data/ExtIsfDataTypeWriter.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/data/ExtIsfDataTypeWriter.java
new file mode 100644
index 0000000000..4f94119d6d
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/data/ExtIsfDataTypeWriter.java
@@ -0,0 +1,144 @@
+/* ###
+ * 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 sarif.export.data;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.List;
+
+import ghidra.program.model.data.Array;
+import ghidra.program.model.data.BitFieldDataType;
+import ghidra.program.model.data.BuiltInDataType;
+import ghidra.program.model.data.Composite;
+import ghidra.program.model.data.DataType;
+import ghidra.program.model.data.DataTypeManager;
+import ghidra.program.model.data.Dynamic;
+import ghidra.program.model.data.Enum;
+import ghidra.program.model.data.FactoryDataType;
+import ghidra.program.model.data.FunctionDefinition;
+import ghidra.program.model.data.Pointer;
+import ghidra.program.model.data.TypeDef;
+import ghidra.program.model.data.ISF.IsfBuiltIn;
+import ghidra.program.model.data.ISF.IsfDataTypeWriter;
+import ghidra.program.model.data.ISF.IsfEnum;
+import ghidra.program.model.data.ISF.IsfObject;
+import ghidra.program.model.data.ISF.IsfTypedObject;
+import ghidra.program.model.data.ISF.IsfTypedefBase;
+import ghidra.program.model.data.ISF.IsfTypedefPointer;
+import ghidra.program.model.data.ISF.IsfUtilities;
+import ghidra.util.Msg;
+import ghidra.util.exception.CancelledException;
+import ghidra.util.task.TaskMonitor;
+
+public class ExtIsfDataTypeWriter extends IsfDataTypeWriter {
+
+ public ExtIsfDataTypeWriter(DataTypeManager dtm, List target, Writer baseWriter) throws IOException {
+ super(dtm, target, baseWriter);
+ STRICT = false;
+ }
+
+ @Override
+ protected void genRoot(TaskMonitor monitor) throws CancelledException, IOException {
+ super.genRoot(monitor);
+ data.add("functions", functions);
+ }
+
+ /**
+ * Writes the root type as ISF JSON using the underlying writer. For now, ignoring top-level
+ * bit-fields and function defs as unsupported by ISF. Typedefs really deserve their own
+ * category, but again unsupported.
+ *
+ * @param dt the root type to write as ISF JSON
+ * @param monitor the task monitor
+ * @throws IOException if there is an exception writing the output
+ */
+ @Override
+ protected IsfObject getIsfObject(DataType dt, TaskMonitor monitor)
+ throws IOException, CancelledException {
+ if (dt == null) {
+ throw new IOException("Null datatype passed to getIsfObject");
+ }
+ if (dt instanceof FactoryDataType) {
+ Msg.error(this, "Factory root types may not be written - type: " + dt);
+ }
+ if (dt instanceof BitFieldDataType) {
+ Msg.error(this, "BitField data types may not be written - type: " + dt);
+ }
+ if (dt instanceof Pointer || dt instanceof Array) {
+ IsfObject type = getObjectDataType(IsfUtilities.getBaseDataType(dt));
+ IsfObject obj = new ExtIsfTypedObject(dt, type);
+ return obj;
+ }
+
+ dt = dt.clone(dtm); // force resize/repack for target root organization
+
+ IsfObject res = resolve(dt);
+ if (res != null) {
+ return res;
+ }
+
+ if (dt instanceof Dynamic dynamic) {
+ DataType rep = dynamic.getReplacementBaseType();
+ return rep == null ? null : getIsfObject(rep, monitor);
+ }
+ else if (dt instanceof BuiltInDataType builtin) {
+ return new IsfBuiltIn(builtin);
+ }
+ else if (dt instanceof TypeDef typedef) {
+ return getObjectTypeDef(typedef, monitor);
+ }
+ else if (dt instanceof Composite composite) {
+ return new ExtIsfComposite(composite, this, monitor);
+ }
+ else if (dt instanceof Enum enumm) {
+ return new IsfEnum(enumm);
+ }
+ else if (dt instanceof FunctionDefinition funcDef) {
+ return new ExtIsfFunction(funcDef);
+ }
+ else {
+ Msg.warn(this, "Unable to write datatype. Type unrecognized: " + dt.getClass());
+ }
+
+ return null;
+ }
+
+ @Override
+ public IsfTypedefBase newTypedefBase(TypeDef typeDef) {
+ return new ExtIsfTypedefBase(typeDef);
+ }
+
+ @Override
+ public IsfTypedefPointer newTypedefPointer(TypeDef typeDef) {
+ return new ExtIsfTypedefPointer(typeDef);
+ }
+
+ public IsfObject newTypedefUser(TypeDef typeDef, IsfObject object) {
+ return new ExtIsfTypedefUser(typeDef, object);
+ }
+
+ @Override
+ public IsfTypedObject newTypedObject(DataType dt, IsfObject type) {
+ return new ExtIsfTypedObject(dt, type);
+ }
+
+ @Override
+ public IsfObject newIsfDynamicComponent(Dynamic dynamic, IsfObject type, int elementCnt) {
+ return new ExtIsfDynamicComponent(dynamic, type, elementCnt);
+ }
+
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/data/ExtIsfDynamicComponent.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/data/ExtIsfDynamicComponent.java
new file mode 100644
index 0000000000..48950a3a51
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/data/ExtIsfDynamicComponent.java
@@ -0,0 +1,30 @@
+/* ###
+ * 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 sarif.export.data;
+
+import ghidra.program.model.data.Dynamic;
+import ghidra.program.model.data.ISF.IsfDynamicComponent;
+import ghidra.program.model.data.ISF.IsfObject;
+
+public class ExtIsfDynamicComponent extends IsfDynamicComponent {
+
+ boolean isVariableLength = true;
+
+ public ExtIsfDynamicComponent(Dynamic dt, IsfObject type, int elementCnt) {
+ super(dt, type, elementCnt);
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/data/ExtIsfFunction.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/data/ExtIsfFunction.java
new file mode 100644
index 0000000000..7906e93bb6
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/data/ExtIsfFunction.java
@@ -0,0 +1,71 @@
+/* ###
+ * 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 sarif.export.data;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+
+import ghidra.program.model.data.DataType;
+import ghidra.program.model.data.FunctionDefinition;
+import ghidra.program.model.data.ParameterDefinition;
+import ghidra.program.model.data.ISF.IsfFunction;
+import ghidra.program.model.data.ISF.IsfUtilities;
+
+public class ExtIsfFunction extends IsfFunction {
+
+ String comment;
+ String callingConventionName;
+ boolean hasVarArgs;
+ boolean hasNoReturn;
+ JsonObject retType;
+ JsonArray params;
+
+ public ExtIsfFunction(FunctionDefinition funcDef) {
+ super(funcDef);
+ comment = funcDef.getDescription();
+ callingConventionName = funcDef.getCallingConventionName();
+ hasVarArgs = funcDef.hasVarArgs();
+ hasNoReturn = funcDef.hasNoReturn();
+
+ retType = new JsonObject();
+ DataType rt = funcDef.getReturnType();
+ if (rt != null && rt != DataType.DEFAULT) {
+ retType.addProperty("name", rt.getName());
+ retType.addProperty("location", rt.getCategoryPath().getPath());
+ retType.addProperty("kind", IsfUtilities.getKind(rt));
+ retType.addProperty("size", rt.getLength());
+ }
+
+ params = new JsonArray();
+ ParameterDefinition[] vars = funcDef.getArguments();
+ for (ParameterDefinition var : vars) {
+ JsonObject param = new JsonObject();
+ params.add(param);
+ DataType dt = var.getDataType();
+ param.addProperty("name", var.getName());
+ param.addProperty("size", var.getLength());
+ param.addProperty("ordinal", var.getOrdinal());
+ param.addProperty("comment", var.getComment());
+ if (dt != null) {
+ param.addProperty("name", dt.getName());
+ param.addProperty("location", dt.getCategoryPath().getPath());
+ param.addProperty("kind", IsfUtilities.getKind(dt));
+ }
+ }
+
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/data/ExtIsfTypedObject.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/data/ExtIsfTypedObject.java
new file mode 100644
index 0000000000..dfccbe4b68
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/data/ExtIsfTypedObject.java
@@ -0,0 +1,58 @@
+/* ###
+ * 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 sarif.export.data;
+
+import ghidra.program.model.data.DataType;
+import ghidra.program.model.data.ISF.IsfBuiltIn;
+import ghidra.program.model.data.ISF.IsfDataTypeDefault;
+import ghidra.program.model.data.ISF.IsfEnum;
+import ghidra.program.model.data.ISF.IsfObject;
+import ghidra.program.model.data.ISF.IsfTypedObject;
+
+public class ExtIsfTypedObject extends IsfTypedObject {
+
+ String typeLocation;
+
+ public ExtIsfTypedObject(DataType dt, IsfObject typeObj) {
+ super(dt, typeObj);
+ if (typeObj instanceof IsfDataTypeDefault) {
+ typeLocation = ((IsfDataTypeDefault) typeObj).location;
+ return;
+ }
+ if (typeObj instanceof ExtIsfComposite) {
+ typeLocation = ((ExtIsfComposite) typeObj).location;
+ }
+ if (typeObj instanceof IsfEnum) {
+ typeLocation = ((IsfEnum) typeObj).location;
+ }
+ if (typeObj instanceof ExtIsfTypedefBase) {
+ typeLocation = ((ExtIsfTypedefBase) typeObj).location;
+ }
+// if (typeObj instanceof IsfTypedefIntegral) {
+// typeLocation = ((IsfTypedefIntegral) typeObj).location;
+// }
+ if (typeObj instanceof ExtIsfTypedefPointer) {
+ typeLocation = ((ExtIsfTypedefPointer) typeObj).location;
+ }
+ if (typeObj instanceof ExtIsfTypedObject) {
+ typeLocation = ((ExtIsfTypedObject) typeObj).location;
+ }
+ if (typeObj instanceof IsfBuiltIn) {
+ typeLocation = ((IsfBuiltIn) typeObj).location;
+ }
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/data/ExtIsfTypedefBase.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/data/ExtIsfTypedefBase.java
new file mode 100644
index 0000000000..9adf550da5
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/data/ExtIsfTypedefBase.java
@@ -0,0 +1,38 @@
+/* ###
+ * 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 sarif.export.data;
+
+import ghidra.program.model.data.BuiltInDataType;
+import ghidra.program.model.data.DataType;
+import ghidra.program.model.data.TypeDef;
+import ghidra.program.model.data.ISF.IsfBuiltIn;
+import ghidra.program.model.data.ISF.IsfObject;
+import ghidra.program.model.data.ISF.IsfTypedefBase;
+
+public class ExtIsfTypedefBase extends IsfTypedefBase {
+
+ public IsfObject type;
+
+ public ExtIsfTypedefBase(TypeDef typedef) {
+ super(typedef);
+ DataType bdt = typedef.getDataType();
+ if (bdt instanceof BuiltInDataType) {
+ type = new IsfBuiltIn((BuiltInDataType) bdt);
+ kind = "typedef";
+ }
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/data/ExtIsfTypedefPointer.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/data/ExtIsfTypedefPointer.java
new file mode 100644
index 0000000000..872e4d95ac
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/data/ExtIsfTypedefPointer.java
@@ -0,0 +1,40 @@
+/* ###
+ * 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 sarif.export.data;
+
+import ghidra.program.model.data.DataType;
+import ghidra.program.model.data.TypeDef;
+import ghidra.program.model.data.ISF.IsfTypedefPointer;
+
+public class ExtIsfTypedefPointer extends IsfTypedefPointer {
+
+ String typeName;
+ String typeLocation;
+ boolean autoNamed;
+ String displayName;
+
+ public ExtIsfTypedefPointer(TypeDef typedef) {
+ super(typedef);
+ if (typedef != null) {
+ DataType base = typedef.getDataType();
+ typeName = base.getName();
+ typeLocation = base.getCategoryPath().getPath();
+ autoNamed = typedef.isAutoNamed();
+ displayName = typedef.getDisplayName();
+ }
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/data/ExtIsfTypedefUser.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/data/ExtIsfTypedefUser.java
new file mode 100644
index 0000000000..c4a56650d3
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/data/ExtIsfTypedefUser.java
@@ -0,0 +1,36 @@
+/* ###
+ * 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 sarif.export.data;
+
+import ghidra.program.model.data.DataType;
+import ghidra.program.model.data.TypeDef;
+import ghidra.program.model.data.ISF.IsfObject;
+import ghidra.program.model.data.ISF.IsfTypedefUser;
+
+public class ExtIsfTypedefUser extends IsfTypedefUser {
+
+ String typeName;
+ String typeLocation;
+
+ public ExtIsfTypedefUser(TypeDef typedef, IsfObject typeObj) {
+ super(typedef, typeObj);
+ DataType baseDataType = typedef.getDataType();
+ kind = "typedef";
+ typeName = baseDataType.getName();
+ typeLocation = baseDataType.getCategoryPath().getPath();
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/data/SarifDataType.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/data/SarifDataType.java
new file mode 100644
index 0000000000..fe67784c85
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/data/SarifDataType.java
@@ -0,0 +1,63 @@
+/* ###
+ * IP: GHIDRA
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package sarif.export.data;
+
+import ghidra.program.model.data.ISF.IsfBuiltIn;
+import ghidra.program.model.data.ISF.IsfComposite;
+import ghidra.program.model.data.ISF.IsfDataTypeWriter;
+import ghidra.program.model.data.ISF.IsfEnum;
+import ghidra.program.model.data.ISF.IsfFunction;
+import ghidra.program.model.data.ISF.IsfObject;
+import ghidra.program.model.data.ISF.IsfTypedObject;
+import ghidra.program.model.data.ISF.IsfTypedefBase;
+import ghidra.program.model.data.ISF.IsfTypedefPointer;
+import ghidra.program.model.data.ISF.IsfTypedefUser;
+import sarif.export.SarifObject;
+import sarif.managers.DataTypesSarifMgr;
+
+public class SarifDataType extends SarifObject {
+
+ public SarifDataType(IsfObject obj, IsfDataTypeWriter writer) {
+ super("DataType", DataTypesSarifMgr.KEY, writer.getTree(obj));
+ message.addProperty("text", objToMessage(obj));
+ }
+
+ private String objToMessage(IsfObject obj) {
+ if (obj instanceof IsfComposite) {
+ return ((IsfComposite) obj).kind.equals("struct") ? "DT.Struct" : "DT.Union";
+ }
+ if (obj instanceof IsfEnum) {
+ return "DT.Enum";
+ }
+ if (obj instanceof IsfFunction) {
+ return "DT.Function";
+ }
+ if (obj instanceof IsfTypedefBase ||
+ //obj instanceof IsfTypedefIntegral ||
+ obj instanceof IsfTypedefPointer ||
+ obj instanceof IsfTypedefUser) {
+ return "DT.Typedef";
+ }
+ if (obj instanceof IsfTypedObject) {
+ return "DT.TypedObject";
+ }
+ if (obj instanceof IsfBuiltIn) {
+ return "DT.Builtin";
+ }
+ return "DT.Base";
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/data/SarifDataTypeWriter.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/data/SarifDataTypeWriter.java
new file mode 100644
index 0000000000..9adaaf0fdd
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/data/SarifDataTypeWriter.java
@@ -0,0 +1,64 @@
+/* ###
+ * 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 sarif.export.data;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.List;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+
+import ghidra.program.model.data.DataType;
+import ghidra.program.model.data.DataTypeManager;
+import ghidra.program.model.data.ISF.IsfObject;
+import ghidra.util.exception.CancelledException;
+import ghidra.util.task.TaskMonitor;
+
+public class SarifDataTypeWriter extends ExtIsfDataTypeWriter {
+
+ private JsonArray types = new JsonArray();
+
+ public SarifDataTypeWriter(DataTypeManager dtm, List target, Writer baseWriter) throws IOException {
+ super(dtm, target, baseWriter);
+ metadata = new JsonObject();
+ baseTypes = types;
+ userTypes = types;
+ enums = types;
+ functions = types;
+ symbols = new JsonObject();
+ }
+
+ @Override
+ protected void addSingletons() {
+ add(baseTypes, "pointer", getTree(new SarifDataType(newTypedefPointer(null), this)));
+ add(baseTypes, "undefined", getTree(new SarifDataType(newTypedefPointer(null), this)));
+ }
+
+ @Override
+ protected JsonObject getObjectForDataType(DataType dt, TaskMonitor monitor)
+ throws IOException, CancelledException {
+ IsfObject isf = new SarifDataType(getIsfObject(dt, monitor), this);
+ JsonObject jobj = (JsonObject) getTree(isf);
+ resolved.put(dt, isf);
+ return jobj;
+ }
+
+ public JsonArray getResults() {
+ return types;
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/dd/ExtComment.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/dd/ExtComment.java
new file mode 100644
index 0000000000..4235be079a
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/dd/ExtComment.java
@@ -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 sarif.export.dd;
+
+import ghidra.program.model.data.ISF.IsfObject;
+import ghidra.program.model.listing.Data;
+
+public class ExtComment implements IsfObject {
+
+ int commentType;
+ String comment;
+
+ public ExtComment(Data data, int type) {
+ commentType = type;
+ comment = data.getComment(type);
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/dd/ExtCommentSet.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/dd/ExtCommentSet.java
new file mode 100644
index 0000000000..37cb7d93a5
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/dd/ExtCommentSet.java
@@ -0,0 +1,75 @@
+/* ###
+ * 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 sarif.export.dd;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import ghidra.program.model.data.ISF.IsfObject;
+import ghidra.program.model.data.ISF.IsfSetting;
+import ghidra.program.model.listing.Data;
+import sarif.managers.CommentsSarifMgr;
+
+public class ExtCommentSet implements IsfObject {
+
+ List comment;
+ List setting;
+ Map embedded;
+
+ public ExtCommentSet(Data data) {
+ exportComments(data);
+ int n = data.getNumComponents();
+ if (n > 0) {
+ for (int i = 0; i < n; i++) {
+ Data component = data.getComponent(i);
+ ExtCommentSet cs = new ExtCommentSet(component);
+ if (cs.comment != null || cs.setting != null || cs.embedded != null) {
+ if (embedded == null) {
+ embedded = new HashMap<>();
+ }
+ embedded.put(i, cs);
+ }
+ }
+ }
+ }
+
+ private void exportComments(Data data) {
+ for (int i = 0; i < CommentsSarifMgr.COMMENT_TYPES.length; i++) {
+ int type = CommentsSarifMgr.COMMENT_TYPES[i];
+ String cval = data.getComment(type);
+ if (cval != null) {
+ if (comment == null) {
+ comment = new ArrayList<>();
+ }
+ ExtComment isf = new ExtComment(data, type);
+ comment.add(isf);
+ }
+ }
+ for (String n : data.getNames()) {
+ Object value = data.getValue(n);
+ if (value != null) {
+ if (setting == null) {
+ setting = new ArrayList<>();
+ }
+ IsfSetting isf = new IsfSetting(n, value);
+ setting.add(isf);
+ }
+ }
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/dd/ExtData.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/dd/ExtData.java
new file mode 100644
index 0000000000..fdf254b4fd
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/dd/ExtData.java
@@ -0,0 +1,36 @@
+/* ###
+ * 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 sarif.export.dd;
+
+import ghidra.program.model.data.DataType;
+import ghidra.program.model.data.ISF.AbstractIsfObject;
+import ghidra.program.model.listing.Data;
+
+public class ExtData extends AbstractIsfObject {
+
+ String typeName;
+ String typeLocation;
+ ExtCommentSet nested;
+
+ public ExtData(Data data) {
+ super(data.getDataType());
+ DataType dt = data.getDataType();
+ typeName = dt.getName();
+ typeLocation = dt.getCategoryPath().getPath();
+ nested = new ExtCommentSet(data);
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/dd/SarifDataWriter.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/dd/SarifDataWriter.java
new file mode 100644
index 0000000000..ca076105a1
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/dd/SarifDataWriter.java
@@ -0,0 +1,55 @@
+/* ###
+ * 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 sarif.export.dd;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.List;
+
+import ghidra.program.model.listing.Data;
+import ghidra.util.exception.CancelledException;
+import ghidra.util.task.TaskMonitor;
+import sarif.export.AbstractExtWriter;
+import sarif.export.SarifObject;
+import sarif.managers.DefinedDataSarifMgr;
+
+public class SarifDataWriter extends AbstractExtWriter {
+
+ private List definedData = new ArrayList<>();
+
+ public SarifDataWriter(List target, Writer baseWriter) throws IOException {
+ super(baseWriter);
+ this.definedData = target;
+ }
+
+ @Override
+ protected void genRoot(TaskMonitor monitor) throws CancelledException, IOException {
+ genData(monitor);
+ root.add("definedData", objects);
+ }
+
+ private void genData(TaskMonitor monitor) throws CancelledException, IOException{
+ monitor.initialize(definedData.size());
+ for (Data d : definedData) {
+ ExtData isf = new ExtData(d);
+ SarifObject sarif = new SarifObject("DefinedData", DefinedDataSarifMgr.KEY, getTree(isf), d.getMinAddress(), d.getMaxAddress());
+ objects.add(getTree(sarif));
+ monitor.increment();
+ }
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/ep/ExtEntryPoint.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/ep/ExtEntryPoint.java
new file mode 100644
index 0000000000..e125aaff54
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/ep/ExtEntryPoint.java
@@ -0,0 +1,26 @@
+/* ###
+ * 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 sarif.export.ep;
+
+import ghidra.program.model.address.Address;
+import ghidra.program.model.data.ISF.IsfObject;
+
+public class ExtEntryPoint implements IsfObject {
+
+ public ExtEntryPoint(Address addr) {
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/ep/SarifEntryPointWriter.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/ep/SarifEntryPointWriter.java
new file mode 100644
index 0000000000..9d967a065b
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/ep/SarifEntryPointWriter.java
@@ -0,0 +1,54 @@
+/* ###
+ * IP: GHIDRA
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package sarif.export.ep;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.List;
+
+import ghidra.program.model.address.Address;
+import ghidra.util.exception.CancelledException;
+import ghidra.util.task.TaskMonitor;
+import sarif.export.AbstractExtWriter;
+import sarif.export.SarifObject;
+import sarif.managers.ExtEntryPointSarifMgr;
+
+public class SarifEntryPointWriter extends AbstractExtWriter {
+
+ private List entryPoints;
+
+ public SarifEntryPointWriter(List request, Writer baseWriter) throws IOException {
+ super(baseWriter);
+ this.entryPoints = request;
+ }
+
+ @Override
+ protected void genRoot(TaskMonitor monitor) throws CancelledException, IOException {
+ genCode(monitor);
+ root.add("entryPoints", objects);
+ }
+
+ private void genCode(TaskMonitor monitor) throws CancelledException, IOException{
+ monitor.initialize(entryPoints.size());
+ for (Address addr : entryPoints) {
+ ExtEntryPoint isf = new ExtEntryPoint(addr);
+ SarifObject sarif = new SarifObject(ExtEntryPointSarifMgr.SUBKEY, ExtEntryPointSarifMgr.KEY, getTree(isf), addr, addr);
+ objects.add(getTree(sarif));
+ monitor.increment();
+ }
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/equates/ExtEquate.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/equates/ExtEquate.java
new file mode 100644
index 0000000000..12132ab261
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/equates/ExtEquate.java
@@ -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 sarif.export.equates;
+
+import ghidra.program.model.data.ISF.IsfObject;
+import ghidra.program.model.symbol.Equate;
+
+public class ExtEquate implements IsfObject {
+
+ String name;
+ long value;
+
+ public ExtEquate(Equate equate) {
+ name = equate.getName();
+ value = equate.getValue();
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/equates/SarifEquateWriter.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/equates/SarifEquateWriter.java
new file mode 100644
index 0000000000..989534d888
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/equates/SarifEquateWriter.java
@@ -0,0 +1,54 @@
+/* ###
+ * IP: GHIDRA
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package sarif.export.equates;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.List;
+
+import ghidra.program.model.symbol.Equate;
+import ghidra.util.exception.CancelledException;
+import ghidra.util.task.TaskMonitor;
+import sarif.export.AbstractExtWriter;
+import sarif.export.SarifObject;
+import sarif.managers.EquatesSarifMgr;
+
+public class SarifEquateWriter extends AbstractExtWriter {
+
+ private List equates;
+
+ public SarifEquateWriter(List request, Writer baseWriter) throws IOException {
+ super(baseWriter);
+ this.equates = request;
+ }
+
+ @Override
+ protected void genRoot(TaskMonitor monitor) throws CancelledException, IOException {
+ genCode(monitor);
+ root.add("equates", objects);
+ }
+
+ private void genCode(TaskMonitor monitor) throws CancelledException, IOException{
+ monitor.initialize(equates.size());
+ for (Equate equate : equates) {
+ ExtEquate isf = new ExtEquate(equate);
+ SarifObject sarif = new SarifObject(EquatesSarifMgr.SUBKEY, EquatesSarifMgr.KEY, getTree(isf), null);
+ objects.add(getTree(sarif));
+ monitor.increment();
+ }
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/extlib/ExtLibrary.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/extlib/ExtLibrary.java
new file mode 100644
index 0000000000..1015bb7fce
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/extlib/ExtLibrary.java
@@ -0,0 +1,33 @@
+/* ###
+ * 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 sarif.export.extlib;
+
+import ghidra.program.model.data.ISF.IsfObject;
+import ghidra.program.model.symbol.SourceType;
+
+public class ExtLibrary implements IsfObject {
+
+ public String name;
+ public String location;
+ public String sourceType;
+
+ public ExtLibrary(String name, String path, SourceType sourceType) {
+ this.name = name;
+ location = path;
+ this.sourceType = sourceType.toString();
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/extlib/ExtLibraryLocation.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/extlib/ExtLibraryLocation.java
new file mode 100644
index 0000000000..eca75b3b6c
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/extlib/ExtLibraryLocation.java
@@ -0,0 +1,54 @@
+/* ###
+ * IP: GHIDRA
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package sarif.export.extlib;
+
+import ghidra.program.model.data.ISF.IsfObject;
+import ghidra.program.model.symbol.ExternalLocation;
+
+public class ExtLibraryLocation implements IsfObject {
+
+ String name;
+ String location;
+ String originalImportedName;
+ String externalAddress;
+ String symbol;
+ boolean isFunction = false;
+ boolean isClass = false;
+ String source;
+
+ public ExtLibraryLocation(ExternalLocation extLoc) {
+ name = extLoc.getLabel();
+ originalImportedName = extLoc.getOriginalImportedName();
+ location = extLoc.getParentNameSpace().getName(true);
+ externalAddress = extLoc.getExternalSpaceAddress().toString();
+ isFunction = extLoc.getFunction() != null;
+ isClass = extLoc.getClass() != null;
+ source = extLoc.getSource().toString();
+ symbol = extLoc.getSymbol().getName();
+ }
+
+// public ExtLibraryLocation(GhidraClass cls, ExternalLocation extLoc) {
+// name = extLoc.getLabel();
+// originalImportedName = extLoc.getOriginalImportedName();
+// location = extLoc.getParentName();
+// //location = cls.getParentNamespace().getName();
+// externalAddress = extLoc.getExternalSpaceAddress().toString();
+// Function f = extLoc.getFunction();
+// isFunction = f != null;
+// source = extLoc.getSource().toString();
+// symbol = extLoc.getSymbol().getName(true);
+// }
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/extlib/SarifClassesNamespaceWriter.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/extlib/SarifClassesNamespaceWriter.java
new file mode 100644
index 0000000000..688691a108
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/extlib/SarifClassesNamespaceWriter.java
@@ -0,0 +1,96 @@
+/* ###
+ * 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 sarif.export.extlib;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import com.google.gson.JsonArray;
+
+import ghidra.program.model.listing.GhidraClass;
+import ghidra.program.model.symbol.ExternalLocation;
+import ghidra.program.model.symbol.ExternalManager;
+import ghidra.program.model.symbol.SourceType;
+import ghidra.program.model.symbol.Symbol;
+import ghidra.program.model.symbol.SymbolIterator;
+import ghidra.program.model.symbol.SymbolTable;
+import ghidra.util.exception.CancelledException;
+import ghidra.util.task.TaskMonitor;
+import sarif.export.AbstractExtWriter;
+import sarif.export.SarifObject;
+import sarif.managers.ExternalLibSarifMgr;
+
+public class SarifClassesNamespaceWriter extends AbstractExtWriter {
+
+ private List classes = new ArrayList<>();
+ private ExternalManager externalManager;
+ private SymbolTable symbolTable;
+
+ public SarifClassesNamespaceWriter(ExternalManager externalManager, SymbolTable symbolTable, List request, Writer baseWriter)
+ throws IOException {
+ super(baseWriter);
+ this.externalManager = externalManager;
+ this.symbolTable = symbolTable;
+ this.classes = request;
+ }
+
+ @Override
+ protected void genRoot(TaskMonitor monitor) throws CancelledException, IOException {
+ genClasses(monitor);
+ root.add("definedData", objects);
+ }
+
+ private void genClasses(TaskMonitor monitor) throws CancelledException, IOException {
+ monitor.initialize(classes.size());
+ Iterator classNamespaces = symbolTable.getClassNamespaces();
+ while (classNamespaces.hasNext()) {
+ GhidraClass next = classNamespaces.next();
+ walkSymbols(next);
+ monitor.increment();
+ }
+ }
+
+ private void walkSymbols(GhidraClass cls) {
+ String clsName = cls.getName(true);
+ String path = externalManager.getExternalLibraryPath(clsName);
+ if (path == null) {
+ path = "";
+ }
+ ExtLibrary lib = new ExtLibrary(clsName, path, SourceType.DEFAULT);
+ SarifObject sarif = new SarifObject(ExternalLibSarifMgr.SUBKEY0, ExternalLibSarifMgr.KEY, getTree(lib), null);
+ objects.add(getTree(sarif));
+
+ SymbolIterator symbols = symbolTable.getSymbols(cls);
+ while (symbols.hasNext()) {
+ Symbol sym = symbols.next();
+ if (cls.isExternal()) {
+ ExternalLocation loc = externalManager.getExternalLocation(sym);
+ ExtLibraryLocation obj = new ExtLibraryLocation(loc);
+ SarifObject sarif2 = new SarifObject(ExternalLibSarifMgr.SUBKEY1, ExternalLibSarifMgr.KEY, getTree(obj),
+ loc.getAddress(), loc.getAddress());
+ objects.add(getTree(sarif2));
+ }
+ }
+ }
+
+ public JsonArray getResults() {
+ return objects;
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/extlib/SarifExternalLibraryWriter.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/extlib/SarifExternalLibraryWriter.java
new file mode 100644
index 0000000000..52aa70e5fd
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/extlib/SarifExternalLibraryWriter.java
@@ -0,0 +1,79 @@
+/* ###
+ * 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 sarif.export.extlib;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.google.gson.JsonArray;
+
+import ghidra.program.model.symbol.ExternalLocation;
+import ghidra.program.model.symbol.ExternalLocationIterator;
+import ghidra.program.model.symbol.ExternalManager;
+import ghidra.program.model.symbol.SourceType;
+import ghidra.util.exception.CancelledException;
+import ghidra.util.task.TaskMonitor;
+import sarif.export.AbstractExtWriter;
+import sarif.export.SarifObject;
+import sarif.managers.ExternalLibSarifMgr;
+
+public class SarifExternalLibraryWriter extends AbstractExtWriter {
+
+ private List externalNames = new ArrayList<>();
+ private ExternalManager externalManager;
+
+ public SarifExternalLibraryWriter(ExternalManager externalManager, List request, Writer baseWriter)
+ throws IOException {
+ super(baseWriter);
+ this.externalManager = externalManager;
+ this.externalNames = request;
+ }
+
+ @Override
+ protected void genRoot(TaskMonitor monitor) throws CancelledException, IOException {
+ genLibraries(monitor);
+ root.add("definedData", objects);
+ }
+
+ private void genLibraries(TaskMonitor monitor) throws CancelledException, IOException {
+ monitor.initialize(externalNames.size());
+ for (String n : externalNames) {
+ String path = externalManager.getExternalLibraryPath(n);
+ if (path == null) {
+ path = "";
+ }
+ ExtLibrary lib = new ExtLibrary(n, path, SourceType.DEFAULT);
+ SarifObject sarif = new SarifObject(ExternalLibSarifMgr.SUBKEY0, ExternalLibSarifMgr.KEY, getTree(lib), null);
+ objects.add(getTree(sarif));
+
+ ExternalLocationIterator externalLocations = externalManager.getExternalLocations(n);
+ while (externalLocations.hasNext()) {
+ ExternalLocation loc = externalLocations.next();
+ ExtLibraryLocation obj = new ExtLibraryLocation(loc);
+ SarifObject sarif2 = new SarifObject(ExternalLibSarifMgr.SUBKEY1, ExternalLibSarifMgr.KEY, getTree(obj), loc.getAddress(), loc.getAddress());
+ objects.add(getTree(sarif2));
+ }
+ monitor.increment();
+ }
+ }
+
+ public JsonArray getResults() {
+ return objects;
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/func/ExtDataType.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/func/ExtDataType.java
new file mode 100644
index 0000000000..157521f103
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/func/ExtDataType.java
@@ -0,0 +1,33 @@
+/* ###
+ * 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 sarif.export.func;
+
+import ghidra.program.model.data.DataType;
+import ghidra.program.model.data.ISF.AbstractIsfObject;
+import ghidra.program.model.data.ISF.IsfUtilities;
+
+public class ExtDataType extends AbstractIsfObject{
+
+ String kind;
+ int size;
+
+ public ExtDataType(DataType dt) {
+ super(dt);
+ kind = IsfUtilities.getKind(dt);
+ size = dt.getLength();
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/func/ExtFunction.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/func/ExtFunction.java
new file mode 100644
index 0000000000..b8b56b6f7c
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/func/ExtFunction.java
@@ -0,0 +1,120 @@
+/* ###
+ * 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 sarif.export.func;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import ghidra.program.model.address.Address;
+import ghidra.program.model.address.GlobalNamespace;
+import ghidra.program.model.data.ISF.IsfObject;
+import ghidra.program.model.listing.Function;
+import ghidra.program.model.listing.GhidraClass;
+import ghidra.program.model.listing.Parameter;
+import ghidra.program.model.symbol.Namespace;
+import ghidra.program.model.symbol.SourceType;
+import ghidra.util.task.TaskMonitor;
+
+public class ExtFunction implements IsfObject {
+
+ String name;
+ String namespace;
+ boolean namespaceIsClass;
+ String location;
+ String comment;
+ String repeatableComment;
+ String value;
+
+ String callingConvention;
+ String callFixup;
+ String signatureSource;
+ String sourceType;
+
+ boolean hasVarArgs;
+ boolean isInline;
+ boolean hasNoReturn;
+ boolean hasCustomStorage;
+ boolean isStackPurgeSizeValid;
+ boolean isLibrary;
+ boolean isGlobal;
+ boolean isExternal;
+
+ boolean isThunk;
+ String thunkAddress;
+
+ ExtFunctionStack stack;
+ List regVars = new ArrayList<>();
+
+ ExtFunctionParam ret;
+ List params = new ArrayList<>();
+
+ public ExtFunction(Function func, TaskMonitor monitor) {
+ super();
+ name = func.getName(true);
+ location = func.getEntryPoint().toString();
+ comment = func.getComment();
+ repeatableComment = func.getRepeatableComment();
+
+ signatureSource = func.getSignatureSource().toString();
+ SourceType srcType = func.getSymbol().getSource();
+ sourceType = srcType.toString();
+ if (srcType != SourceType.DEFAULT) {
+ name = func.getName();
+ }
+ Namespace ns = func.getParentNamespace();
+ if (!(ns instanceof GlobalNamespace)) {
+ namespace = ns.getName(true);
+ if (ns instanceof GhidraClass) {
+ namespaceIsClass = true;
+ }
+ }
+ if (func.getSignatureSource() != SourceType.DEFAULT) {
+ value = func.getPrototypeString(true, true);
+ }
+ callingConvention = func.getCallingConventionName();
+ callFixup = func.getCallFixup();
+
+ hasVarArgs = func.hasVarArgs();
+ isInline = func.isInline();
+ hasNoReturn = func.hasNoReturn();
+ hasCustomStorage = func.hasCustomVariableStorage();
+ isStackPurgeSizeValid = func.isStackPurgeSizeValid();
+ isLibrary = func.isLibrary();
+ isGlobal = func.isGlobal();
+ isExternal = func.isExternal();
+ isThunk = func.isThunk();
+ if (func.isThunk()) {
+ Address thunkAddr = func.getThunkedFunction(false).getEntryPoint();
+ thunkAddress = thunkAddr.toString();
+ }
+
+ stack = new ExtFunctionStack(func.getStackFrame(), hasCustomStorage);
+ if (func.isStackPurgeSizeValid()) {
+ stack.setPurgeSize(func.getStackPurgeSize());
+ }
+
+ Parameter rp = func.getReturn();
+ ret = new ExtFunctionParam(rp);
+ Parameter[] fnParams = func.getParameters();
+ for (Parameter param : fnParams) {
+ if (param.isRegisterVariable()) {
+ regVars.add(new ExtFunctionRegVar(param));
+ }
+ params.add(new ExtFunctionParam(param));
+ }
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/func/ExtFunctionParam.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/func/ExtFunctionParam.java
new file mode 100644
index 0000000000..5be0e66ce6
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/func/ExtFunctionParam.java
@@ -0,0 +1,81 @@
+/* ###
+ * 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 sarif.export.func;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import ghidra.program.model.data.DataType;
+import ghidra.program.model.data.ISF.AbstractIsfObject;
+import ghidra.program.model.lang.Register;
+import ghidra.program.model.listing.Parameter;
+
+public class ExtFunctionParam extends AbstractIsfObject {
+
+ String comment;
+ int ordinal;
+ int size;
+
+ String typeName;
+ String typeLocation;
+ ExtDataType type;
+
+ String formalTypeName;
+ String formalTypeLocation;
+ ExtDataType formalType;
+
+ boolean isAutoParameter;
+ boolean isForcedIndirect;
+
+ List registers;
+ int stackOffset;
+
+ public ExtFunctionParam(Parameter p) {
+ super(p.getDataType());
+ name = p.getName();
+ ordinal = p.getOrdinal();
+ size = p.getLength();
+ comment = p.getComment();
+
+ isAutoParameter = p.isAutoParameter();
+ isForcedIndirect = p.isForcedIndirect();
+
+ DataType dataType = p.getDataType();
+ typeName = dataType.getName();
+ typeLocation = dataType.getCategoryPath().getPath();
+ type = new ExtDataType(dataType);
+
+ dataType = p.getFormalDataType();
+ formalTypeName = dataType.getName();
+ formalTypeLocation = dataType.getCategoryPath().getPath();
+ formalType = new ExtDataType(dataType);
+
+ List regs = p.getRegisters();
+ if (regs != null) {
+ registers = new ArrayList<>();
+ for (Register r : regs) {
+ registers.add(r.getName());
+ }
+ }
+ try {
+ stackOffset = p.getStackOffset();
+ } catch (UnsupportedOperationException uoe) {
+ stackOffset = -1;
+ }
+
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/func/ExtFunctionRegVar.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/func/ExtFunctionRegVar.java
new file mode 100644
index 0000000000..5e8f48fbb9
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/func/ExtFunctionRegVar.java
@@ -0,0 +1,45 @@
+/* ###
+ * 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 sarif.export.func;
+
+import ghidra.program.model.data.DataType;
+import ghidra.program.model.data.ISF.AbstractIsfObject;
+import ghidra.program.model.listing.Parameter;
+
+public class ExtFunctionRegVar extends AbstractIsfObject {
+
+ String register;
+ String comment;
+ int size;
+
+ String typeName;
+ String typeLocation;
+ ExtDataType type;
+
+ public ExtFunctionRegVar(Parameter var) {
+ super(var.getDataType());
+ name = var.getName();
+ register = var.getRegister().getName();
+ size = var.getLength();
+ comment = var.getComment();
+
+ DataType dataType = var.getDataType();
+ typeName = dataType.getName();
+ typeLocation = dataType.getCategoryPath().getPath();
+ type = new ExtDataType(dataType);
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/func/ExtFunctionRet.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/func/ExtFunctionRet.java
new file mode 100644
index 0000000000..d98fdbd71c
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/func/ExtFunctionRet.java
@@ -0,0 +1,33 @@
+/* ###
+ * 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 sarif.export.func;
+
+import ghidra.program.model.data.DataType;
+import ghidra.program.model.data.ISF.AbstractIsfObject;
+import ghidra.program.model.data.ISF.IsfUtilities;
+
+public class ExtFunctionRet extends AbstractIsfObject {
+
+ String kind;
+ int size;
+
+ public ExtFunctionRet(DataType rt) {
+ super(rt);
+ kind = IsfUtilities.getKind(rt);
+ size = rt.getLength();
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/func/ExtFunctionStack.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/func/ExtFunctionStack.java
new file mode 100644
index 0000000000..70a87ca6ee
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/func/ExtFunctionStack.java
@@ -0,0 +1,63 @@
+/* ###
+ * IP: GHIDRA
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package sarif.export.func;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+
+import ghidra.program.model.data.ISF.IsfObject;
+import ghidra.program.model.listing.Parameter;
+import ghidra.program.model.listing.StackFrame;
+import ghidra.program.model.listing.Variable;
+
+public class ExtFunctionStack implements IsfObject {
+
+ int localVarSize;
+ int parameterOffset;
+ int returnAddressOffset;
+ int purgeSize;
+
+ List stackVars = new ArrayList<>();
+
+ public ExtFunctionStack(StackFrame stackFrame, boolean hasCustomStorage) {
+ localVarSize = stackFrame.getLocalSize();
+ parameterOffset = stackFrame.getParameterOffset();
+ returnAddressOffset = stackFrame.getReturnAddressOffset();
+
+ Variable[] vars = stackFrame.getStackVariables();
+ if (hasCustomStorage) {
+ Arrays.sort(vars, new Comparator() {
+ @Override
+ public int compare(Variable o1, Variable o2) {
+ if (o1 instanceof Parameter p1 && o2 instanceof Parameter p2) {
+ return p1.getOrdinal() - p2.getOrdinal();
+ }
+ return o1.getStackOffset() - o2.getStackOffset();
+ }
+ });
+ }
+ for (Variable var : vars) {
+ stackVars.add(new ExtFunctionStackVar(var));
+ }
+ }
+
+ public void setPurgeSize(int purgeSize) {
+ this.purgeSize = purgeSize;
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/func/ExtFunctionStackVar.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/func/ExtFunctionStackVar.java
new file mode 100644
index 0000000000..952646a039
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/func/ExtFunctionStackVar.java
@@ -0,0 +1,45 @@
+/* ###
+ * 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 sarif.export.func;
+
+import ghidra.program.model.data.DataType;
+import ghidra.program.model.data.ISF.AbstractIsfObject;
+import ghidra.program.model.listing.Variable;
+
+public class ExtFunctionStackVar extends AbstractIsfObject {
+
+ String comment;
+ int size;
+ int offset;
+
+ String typeName;
+ String typeLocation;
+ ExtDataType type;
+
+ public ExtFunctionStackVar(Variable var) {
+ super(var.getDataType());
+ name = var.getName();
+ size = var.getLength();
+ offset = var.getStackOffset();
+ comment = var.getComment();
+
+ DataType dataType = var.getDataType();
+ typeName = dataType.getName();
+ typeLocation = dataType.getCategoryPath().getPath();
+ type = new ExtDataType(dataType);
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/func/ExtFunctionWriter.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/func/ExtFunctionWriter.java
new file mode 100644
index 0000000000..f8c1a6ae43
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/func/ExtFunctionWriter.java
@@ -0,0 +1,51 @@
+/* ###
+ * 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 sarif.export.func;
+
+import java.io.IOException;
+import java.io.Writer;
+
+import com.google.gson.JsonObject;
+
+import ghidra.program.model.data.DataTypeManager;
+import ghidra.util.exception.CancelledException;
+import ghidra.util.task.TaskMonitor;
+
+public class ExtFunctionWriter {
+
+ protected JsonObject data = new JsonObject();
+
+ public ExtFunctionWriter(DataTypeManager dtm, Writer baseWriter) throws IOException {
+ }
+
+ /**
+ * Exports all root types in the list as ISF JSON.
+ *
+ * @param monitor the task monitor
+ * @return the resultant JSON object
+ * @throws IOException if there is an exception writing the output
+ * @throws CancelledException if the action is cancelled by the user
+ */
+ public JsonObject getRootObject(TaskMonitor monitor) throws IOException, CancelledException {
+ genFunctions(monitor);
+ return data;
+ }
+
+ private void genFunctions(TaskMonitor monitor) {
+ // TODO Auto-generated method stub
+
+ }
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/func/SarifFunctionWriter.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/func/SarifFunctionWriter.java
new file mode 100644
index 0000000000..6542b66bbc
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/func/SarifFunctionWriter.java
@@ -0,0 +1,85 @@
+/* ###
+ * 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 sarif.export.func;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.google.gson.JsonArray;
+
+import ghidra.program.model.listing.Function;
+import ghidra.program.model.listing.FunctionManager;
+import ghidra.program.model.symbol.Namespace;
+import ghidra.program.model.symbol.Symbol;
+import ghidra.util.exception.CancelledException;
+import ghidra.util.task.TaskMonitor;
+import sarif.export.AbstractExtWriter;
+import sarif.export.SarifObject;
+import sarif.export.symbols.ExtSymbol;
+import sarif.managers.FunctionsSarifMgr;
+import sarif.managers.SymbolTableSarifMgr;
+
+public class SarifFunctionWriter extends AbstractExtWriter {
+
+ private List requestedFunctions = new ArrayList<>();
+
+ public SarifFunctionWriter(FunctionManager mgr, List requestedFunctions, Writer baseWriter) throws IOException {
+ super(baseWriter);
+ this.requestedFunctions = requestedFunctions;
+ }
+
+ public void requestFunction(Function next) {
+ requestedFunctions.add(next);
+ }
+
+ @Override
+ protected void genRoot(TaskMonitor monitor) throws CancelledException, IOException {
+ genFunctions(monitor);
+ root.add("functions", objects);
+ }
+
+ private void genFunctions(TaskMonitor monitor) throws CancelledException, IOException{
+ monitor.initialize(requestedFunctions.size());
+ for (Function f : requestedFunctions) {
+ addSymbol(f.getSymbol());
+ ExtFunction isf = new ExtFunction(f, monitor);
+ SarifObject sarif = new SarifObject("Function", FunctionsSarifMgr.KEY, getTree(isf), f.getBody());
+ objects.add(getTree(sarif));
+ monitor.increment();
+ }
+ }
+
+ private void addSymbol(Symbol s) {
+ Namespace pspace = s.getParentNamespace();
+ if (pspace == null) {
+ return;
+ }
+ if (!pspace.isGlobal()) {
+ Symbol p = s.getParentSymbol();
+ addSymbol(p);
+ }
+ ExtSymbol lib = new ExtSymbol(s);
+ SarifObject sarif = new SarifObject("Symbol", SymbolTableSarifMgr.KEY, getTree(lib), s.getAddress(), s.getAddress());
+ objects.add(getTree(sarif));
+ }
+
+ public JsonArray getResults() {
+ return objects;
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/mm/ExtMemoryMap.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/mm/ExtMemoryMap.java
new file mode 100644
index 0000000000..ee05bbfd5e
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/mm/ExtMemoryMap.java
@@ -0,0 +1,78 @@
+/* ###
+ * 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 sarif.export.mm;
+
+import java.io.IOException;
+
+import ghidra.program.model.address.AddressRange;
+import ghidra.program.model.address.AddressSpace;
+import ghidra.program.model.address.OverlayAddressSpace;
+import ghidra.program.model.data.ISF.IsfObject;
+import ghidra.program.model.mem.MemoryBlock;
+import ghidra.program.model.mem.MemoryBlockSourceInfo;
+import ghidra.program.model.mem.MemoryBlockType;
+import sarif.managers.MemoryMapBytesFile;
+
+public class ExtMemoryMap implements IsfObject {
+
+ String name;
+ String kind;
+ String overlaySpace;
+ String overlayedSpace;
+ String comment;
+ boolean isVolatile;
+ String type;
+ String location;
+
+ public ExtMemoryMap(AddressRange range, MemoryBlock block, MemoryMapBytesFile bf, boolean write) throws IOException {
+
+ String permissions = "";
+ if (block.isRead()) {
+ permissions += "r";
+ }
+ if (block.isWrite()) {
+ permissions += "w";
+ }
+ if (block.isExecute()) {
+ permissions += "x";
+ }
+
+ name = block.getName();
+ kind = permissions;
+ AddressSpace space = range.getAddressSpace();
+ if (space instanceof OverlayAddressSpace) {
+ OverlayAddressSpace oSpace = (OverlayAddressSpace) space;
+ overlaySpace = oSpace.getName();
+ overlayedSpace = oSpace.getOverlayedSpace().getName();
+ }
+ if (block.getComment() != null) {
+ comment = block.getComment();
+ }
+ if (block.isVolatile()) {
+ isVolatile = true;
+ }
+ type = block.getType().name();
+ if (block.getType() == MemoryBlockType.BIT_MAPPED || block.getType() == MemoryBlockType.BYTE_MAPPED) {
+ // bit mapped blocks can only have one sub-block
+ MemoryBlockSourceInfo info = block.getSourceInfos().get(0);
+ location = info.getMappedRange().get().getMinAddress().toString();
+ } else if (block.isInitialized() && write) {
+ location = bf.getFileName() + ":" + bf.getOffset();
+ bf.writeBytes(range);
+ }
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/mm/SarifMemoryMapWriter.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/mm/SarifMemoryMapWriter.java
new file mode 100644
index 0000000000..eb92676bce
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/mm/SarifMemoryMapWriter.java
@@ -0,0 +1,64 @@
+/* ###
+ * 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 sarif.export.mm;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.List;
+
+import generic.stl.Pair;
+import ghidra.program.model.address.AddressRange;
+import ghidra.program.model.mem.MemoryBlock;
+import ghidra.util.exception.CancelledException;
+import ghidra.util.task.TaskMonitor;
+import sarif.export.AbstractExtWriter;
+import sarif.export.SarifObject;
+import sarif.managers.MemoryMapBytesFile;
+import sarif.managers.MemoryMapSarifMgr;
+
+public class SarifMemoryMapWriter extends AbstractExtWriter {
+
+ private List> memory;
+ private MemoryMapBytesFile bytesFile;
+ private boolean write;
+
+ public SarifMemoryMapWriter(List> request, Writer baseWriter,
+ MemoryMapBytesFile bytes, boolean isWriteContents) throws IOException {
+ super(baseWriter);
+ this.memory = request;
+ this.bytesFile = bytes;
+ this.write = isWriteContents;
+ }
+
+ @Override
+ protected void genRoot(TaskMonitor monitor) throws CancelledException, IOException {
+ genMaps(monitor);
+ root.add("memory", objects);
+ }
+
+ private void genMaps(TaskMonitor monitor) throws CancelledException, IOException {
+ monitor.initialize(memory.size());
+ for (Pair m : memory) {
+ AddressRange range = m.first;
+ ExtMemoryMap isf = new ExtMemoryMap(m.first, m.second, bytesFile, write);
+ SarifObject sarif = new SarifObject(MemoryMapSarifMgr.SUBKEY, MemoryMapSarifMgr.KEY, getTree(isf),
+ range.getMinAddress(), range.getMaxAddress());
+ objects.add(getTree(sarif));
+ monitor.increment();
+ }
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/props/ExtProperty.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/props/ExtProperty.java
new file mode 100644
index 0000000000..5aa34c6414
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/props/ExtProperty.java
@@ -0,0 +1,178 @@
+/* ###
+ * 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 sarif.export.props;
+
+import java.awt.Color;
+import java.awt.Font;
+import java.io.File;
+import java.util.Date;
+
+import javax.swing.KeyStroke;
+
+import ghidra.framework.options.CustomOption;
+import ghidra.framework.options.OptionType;
+import ghidra.framework.options.Options;
+import ghidra.program.model.data.ISF.IsfObject;
+import ghidra.util.exception.AssertException;
+
+public class ExtProperty implements IsfObject {
+
+ String name;
+ String type;
+ String value;
+
+ public ExtProperty(String name, String type, String value) {
+ this.name = name;
+ this.type = type;
+ this.value = value;
+ }
+
+ public ExtProperty(String name, Options propList) {
+ this.name = name;
+ OptionType optionType = propList.getType(name);
+ switch (optionType) {
+ case INT_TYPE:
+ type = "int";
+ value = Integer.toString(propList.getInt(name, 0));
+ break;
+ case LONG_TYPE:
+ type = "long";
+ value = Long.toString(propList.getLong(name, 0));
+ break;
+ case STRING_TYPE:
+ type = "string";
+ value = propList.getString(name, "");
+ break;
+ case BOOLEAN_TYPE:
+ type = "bool";
+ value = Boolean.toString(propList.getBoolean(name, true));
+ break;
+ case DOUBLE_TYPE:
+ type = "double";
+ value = Double.toString(propList.getDouble(name, 0));
+ break;
+ case FLOAT_TYPE:
+ type = "float";
+ value = Float.toString(propList.getFloat(name, 0f));
+ break;
+ case DATE_TYPE:
+ type = "date";
+ Date date = propList.getDate(name, (Date) null);
+ long time = date == null ? 0 : date.getTime();
+ value = Long.toHexString(time);
+ break;
+ case COLOR_TYPE:
+ type = "color";
+ Color color = propList.getColor(name, null);
+ int rgb = color.getRGB();
+ value = Integer.toHexString(rgb);
+ break;
+ case ENUM_TYPE:
+ type = "enum";
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ Enum enuum = propList.getEnum(name, null);
+ String enumString = OptionType.ENUM_TYPE.convertObjectToString(enuum);
+ value = escapeElementEntities(enumString);
+ break;
+ case FILE_TYPE:
+ type = "file";
+ File file = propList.getFile(name, null);
+ String path = file.getAbsolutePath();
+ value = path;
+ break;
+ case FONT_TYPE:
+ type = "font";
+ Font font = propList.getFont(name, null);
+ enumString = OptionType.FONT_TYPE.convertObjectToString(font);
+ value = escapeElementEntities(enumString);
+ break;
+ case KEYSTROKE_TYPE:
+ type = "keyStroke";
+ KeyStroke keyStroke = propList.getKeyStroke(name, null);
+ enumString = OptionType.KEYSTROKE_TYPE.convertObjectToString(keyStroke);
+ value = escapeElementEntities(enumString);
+ break;
+ case CUSTOM_TYPE:
+ type = "custom";
+ CustomOption custom = propList.getCustomOption(name, null);
+ enumString = OptionType.CUSTOM_TYPE.convertObjectToString(custom);
+ value = escapeElementEntities(enumString);
+ break;
+ case BYTE_ARRAY_TYPE:
+ type = "bytes";
+ byte[] bytes = propList.getByteArray(name, null);
+ enumString = OptionType.BYTE_ARRAY_TYPE.convertObjectToString(bytes);
+ value = escapeElementEntities(enumString);
+ break;
+ case NO_TYPE:
+ break;
+ default:
+ throw new AssertException();
+ }
+ }
+
+ private static final String LESS_THAN = "<";
+ private static final String GREATER_THAN = ">";
+ private static final String APOSTROPHE = "'";
+ private static final String QUOTE = """;
+ private static final String AMPERSAND = "&";
+
+ /**
+ * Converts any special or reserved characters in the specified SARIF string
+ * into the equivalent Unicode encoding.
+ *
+ * @param sarif the SARIF string
+ * @return the encoded SARIF string
+ */
+ public static String escapeElementEntities(String sarif) {
+ StringBuilder buffer = new StringBuilder();
+ for (int offset = 0; offset < sarif.length();) {
+ int codePoint = sarif.codePointAt(offset);
+ offset += Character.charCount(codePoint);
+
+ if ((codePoint < ' ') && (codePoint != 0x09) && (codePoint != 0x0A) && (codePoint != 0x0D)) {
+ continue;
+ }
+ if (codePoint >= 0x7F) {
+ buffer.append("");
+ buffer.append(Integer.toString(codePoint, 16).toUpperCase());
+ buffer.append(";");
+ continue;
+ }
+ switch (codePoint) {
+ case '<':
+ buffer.append(LESS_THAN);
+ break;
+ case '>':
+ buffer.append(GREATER_THAN);
+ break;
+ case '\'':
+ buffer.append(APOSTROPHE);
+ break;
+ case '"':
+ buffer.append(QUOTE);
+ break;
+ case '&':
+ buffer.append(AMPERSAND);
+ break;
+ default:
+ buffer.appendCodePoint(codePoint);
+ break;
+ }
+ }
+ return buffer.toString();
+ }
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/props/SarifPropertyListWriter.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/props/SarifPropertyListWriter.java
new file mode 100644
index 0000000000..f565866fb8
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/props/SarifPropertyListWriter.java
@@ -0,0 +1,72 @@
+/* ###
+ * 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 sarif.export.props;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Collections;
+import java.util.List;
+
+import ghidra.framework.options.Options;
+import ghidra.program.model.listing.Program;
+import ghidra.util.exception.CancelledException;
+import ghidra.util.task.TaskMonitor;
+import sarif.export.AbstractExtWriter;
+import sarif.export.SarifObject;
+import sarif.managers.PropertiesSarifMgr;
+
+public class SarifPropertyListWriter extends AbstractExtWriter {
+
+ Program program;
+ List options;
+
+ public SarifPropertyListWriter(Program program, List request, Writer baseWriter) throws IOException {
+ super(baseWriter);
+ this.program = program;
+ this.options = request;
+ }
+
+ @Override
+ protected void genRoot(TaskMonitor monitor) throws CancelledException, IOException {
+ genList(monitor);
+ root.add("properties", objects);
+ }
+
+ private void genList(TaskMonitor monitor) throws CancelledException, IOException {
+ monitor.initialize(options.size());
+ for (String listName : options) {
+ Options propList = program.getOptions(listName);
+ List propNames = propList.getOptionNames();
+ Collections.sort(propNames);
+ for (String name : propNames) {
+ if (monitor.isCancelled()) {
+ throw new CancelledException();
+ }
+ if (propList.isAlias(name)) { // don't write out properties that are just mirrors of some other property
+ continue;
+ }
+ if (propList.isDefaultValue(name)) { // don't write out default properties.
+ continue;
+ }
+ String keyName = listName + Options.DELIMITER_STRING + name;
+ ExtProperty isf = new ExtProperty(keyName, propList);
+ SarifObject sarif = new SarifObject(PropertiesSarifMgr.SUBKEY, PropertiesSarifMgr.KEY, getTree(isf), null);
+ objects.add(getTree(sarif));
+ }
+ }
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/props/SarifPropertyMapWriter.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/props/SarifPropertyMapWriter.java
new file mode 100644
index 0000000000..d040498992
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/props/SarifPropertyMapWriter.java
@@ -0,0 +1,169 @@
+/* ###
+ * 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 sarif.export.props;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.List;
+
+import ghidra.program.model.address.Address;
+import ghidra.program.model.address.AddressIterator;
+import ghidra.program.model.address.AddressSetView;
+import ghidra.program.model.listing.Program;
+import ghidra.program.model.util.IntPropertyMap;
+import ghidra.program.model.util.LongPropertyMap;
+import ghidra.program.model.util.ObjectPropertyMap;
+import ghidra.program.model.util.PropertyMap;
+import ghidra.program.model.util.StringPropertyMap;
+import ghidra.program.model.util.VoidPropertyMap;
+import ghidra.util.SaveableColor;
+import ghidra.util.SaveablePoint;
+import ghidra.util.exception.CancelledException;
+import ghidra.util.exception.NoValueException;
+import ghidra.util.task.TaskMonitor;
+import sarif.export.AbstractExtWriter;
+import sarif.export.SarifObject;
+import sarif.managers.PropertiesSarifMgr;
+
+public class SarifPropertyMapWriter extends AbstractExtWriter {
+
+ List> maps;
+ Program program;
+ AddressSetView set;
+
+ public SarifPropertyMapWriter(List> request, Program program, AddressSetView set, Writer baseWriter)
+ throws IOException {
+ super(baseWriter);
+ this.maps = request;
+ this.program = program;
+ this.set = set;
+ }
+
+ @Override
+ protected void genRoot(TaskMonitor monitor) throws CancelledException, IOException {
+ genMap(monitor);
+ root.add("properties", objects);
+ }
+
+ private void genMap(TaskMonitor monitor) throws CancelledException, IOException {
+ monitor.initialize(maps.size());
+ for (PropertyMap> map : maps) {
+ if (map instanceof VoidPropertyMap) {
+ genVoidMap((VoidPropertyMap) map, monitor);
+ } else if (map instanceof IntPropertyMap) {
+ genIntMap((IntPropertyMap) map, monitor);
+ } else if (map instanceof LongPropertyMap) {
+ genLongMap((LongPropertyMap) map, monitor);
+ } else if (map instanceof StringPropertyMap) {
+ genStringMap((StringPropertyMap) map, monitor);
+ } else if (map instanceof ObjectPropertyMap) {
+ genObjectMap((ObjectPropertyMap>) map, monitor);
+ }
+ }
+ }
+
+ private void genVoidMap(VoidPropertyMap map, TaskMonitor monitor) throws CancelledException {
+ AddressIterator iter = set != null ? map.getPropertyIterator(set) : map.getPropertyIterator();
+ while (iter.hasNext()) {
+ Address addr = iter.next();
+ ExtProperty isf = new ExtProperty(map.getName(), "void", null);
+ SarifObject sarif = new SarifObject(PropertiesSarifMgr.SUBKEY, PropertiesSarifMgr.KEY, getTree(isf), addr,
+ addr);
+ objects.add(getTree(sarif));
+ monitor.increment();
+ }
+ }
+
+ private void genIntMap(IntPropertyMap map, TaskMonitor monitor) throws CancelledException {
+ AddressIterator iter = set != null ? map.getPropertyIterator(set) : map.getPropertyIterator();
+ while (iter.hasNext()) {
+ if (monitor.isCancelled()) {
+ throw new CancelledException();
+ }
+ try {
+ Address addr = iter.next();
+ int value = map.getInt(addr);
+ ExtProperty isf = new ExtProperty(map.getName(), "int", Integer.toHexString(value));
+ SarifObject sarif = new SarifObject(PropertiesSarifMgr.SUBKEY, PropertiesSarifMgr.KEY, getTree(isf),
+ addr, addr);
+ objects.add(getTree(sarif));
+ } catch (NoValueException e) {
+ // skip
+ }
+ }
+ }
+
+ private void genLongMap(LongPropertyMap map, TaskMonitor monitor) throws CancelledException {
+ AddressIterator iter = set != null ? map.getPropertyIterator(set) : map.getPropertyIterator();
+ while (iter.hasNext()) {
+ if (monitor.isCancelled()) {
+ throw new CancelledException();
+ }
+ try {
+ Address addr = iter.next();
+ long value = map.getLong(addr);
+ ExtProperty isf = new ExtProperty(map.getName(), "long", Long.toHexString(value));
+ SarifObject sarif = new SarifObject(PropertiesSarifMgr.SUBKEY, PropertiesSarifMgr.KEY, getTree(isf),
+ addr, addr);
+ objects.add(getTree(sarif));
+ } catch (NoValueException e) {
+ // skip
+ }
+ }
+
+ }
+
+ private void genStringMap(StringPropertyMap map, TaskMonitor monitor) throws CancelledException {
+ AddressIterator iter = set != null ? map.getPropertyIterator(set) : map.getPropertyIterator();
+ while (iter.hasNext()) {
+ if (monitor.isCancelled()) {
+ throw new CancelledException();
+ }
+ Address addr = iter.next();
+ String value = map.getString(addr);
+ ExtProperty isf = new ExtProperty(map.getName(), "string", value);
+ SarifObject sarif = new SarifObject(PropertiesSarifMgr.SUBKEY, PropertiesSarifMgr.KEY, getTree(isf), addr,
+ addr);
+ objects.add(getTree(sarif));
+ }
+ }
+
+
+ private void genObjectMap(ObjectPropertyMap> map, TaskMonitor monitor) throws CancelledException {
+ AddressIterator iter = set != null ? map.getPropertyIterator(set) : map.getPropertyIterator();
+ while (iter.hasNext()) {
+ if (monitor.isCancelled()) {
+ throw new CancelledException();
+ }
+ Address addr = iter.next();
+ Object value = map.get(addr);
+ ExtProperty isf;
+ if (value instanceof SaveablePoint) {
+ isf = new ExtProperty(map.getName(), "point", value.toString());
+ }
+ else if (value instanceof SaveableColor) {
+ isf = new ExtProperty(map.getName(), "color", value.toString());
+ }
+ else {
+ return;
+ }
+ SarifObject sarif = new SarifObject(PropertiesSarifMgr.SUBKEY, PropertiesSarifMgr.KEY, getTree(isf), addr,
+ addr);
+ objects.add(getTree(sarif));
+ }
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/ref/ExtEquateReference.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/ref/ExtEquateReference.java
new file mode 100644
index 0000000000..16482c82b1
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/ref/ExtEquateReference.java
@@ -0,0 +1,32 @@
+/* ###
+ * 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 sarif.export.ref;
+
+import ghidra.program.model.symbol.EquateReference;
+
+public class ExtEquateReference {
+
+ String name;
+ int opIndex;
+ long value;
+
+ public ExtEquateReference(EquateReference ref, String name, long value) {
+ this.name = name;
+ this.value = value;
+ opIndex = ref.getOpIndex();
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/ref/ExtExternalReference.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/ref/ExtExternalReference.java
new file mode 100644
index 0000000000..f76b677ec4
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/ref/ExtExternalReference.java
@@ -0,0 +1,55 @@
+/* ###
+ * 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 sarif.export.ref;
+
+import ghidra.program.model.address.Address;
+import ghidra.program.model.symbol.ExternalLocation;
+import ghidra.program.model.symbol.ExternalReference;
+
+public class ExtExternalReference extends ExtReference {
+
+ String name;
+ String origImport;
+ boolean isClass;
+ boolean isFunction;
+
+ String libLabel;
+ String libAddr;
+ String libExtAddr;
+
+ public ExtExternalReference(ExternalReference ref) {
+ super(ref);
+ ExternalLocation extLoc = ref.getExternalLocation();
+ String label = extLoc.getLabel();
+ Address addr = extLoc.getAddress();
+ Address extAddr = extLoc.getExternalSpaceAddress();
+
+ name = extLoc.getParentNameSpace().getName(true);
+ origImport = extLoc.getOriginalImportedName();
+ isClass = extLoc.getClass() != null;
+ isFunction = extLoc.getFunction() != null;
+ if (label != null) {
+ libLabel = label;
+ }
+ if (addr != null) {
+ libAddr = addr.toString();
+ }
+ if (extAddr != null) {
+ libExtAddr = extAddr.toString();
+ }
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/ref/ExtMemoryReference.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/ref/ExtMemoryReference.java
new file mode 100644
index 0000000000..5f24bb11e7
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/ref/ExtMemoryReference.java
@@ -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 sarif.export.ref;
+
+import ghidra.program.model.symbol.OffsetReference;
+import ghidra.program.model.symbol.Reference;
+
+public class ExtMemoryReference extends ExtReference {
+
+ String to;
+ String base;
+ long offset;
+ boolean primary;
+
+ public ExtMemoryReference(Reference ref) {
+ super(ref);
+ to = ref.getToAddress().toString();
+ if (ref.isOffsetReference()) {
+ OffsetReference oref = (OffsetReference) ref;
+ base = oref.getBaseAddress().toString();
+ offset = oref.getOffset();
+ }
+ primary = ref.isPrimary();
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/ref/ExtReference.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/ref/ExtReference.java
new file mode 100644
index 0000000000..c8ca034b07
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/ref/ExtReference.java
@@ -0,0 +1,35 @@
+/* ###
+ * 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 sarif.export.ref;
+
+import ghidra.program.model.data.ISF.IsfObject;
+import ghidra.program.model.symbol.Reference;
+
+public abstract class ExtReference implements IsfObject {
+
+ String index;
+ String kind;
+ int opIndex;
+ String sourceType;
+
+ public ExtReference(Reference ref) {
+ index = Byte.toString(ref.getReferenceType().getValue());
+ kind = ref.getReferenceType().getName();
+ opIndex = ref.getOperandIndex();
+ sourceType = ref.getSource().toString();
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/ref/ExtRegisterReference.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/ref/ExtRegisterReference.java
new file mode 100644
index 0000000000..fd0d415141
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/ref/ExtRegisterReference.java
@@ -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 sarif.export.ref;
+
+import ghidra.program.model.symbol.Reference;
+
+public class ExtRegisterReference extends ExtReference {
+
+ String to;
+ boolean primary;
+
+ public ExtRegisterReference(Reference ref) {
+ super(ref);
+ to = ref.getToAddress().toString();
+ primary = ref.isPrimary();
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/ref/ExtShiftedReference.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/ref/ExtShiftedReference.java
new file mode 100644
index 0000000000..1f5832352b
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/ref/ExtShiftedReference.java
@@ -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 sarif.export.ref;
+
+import ghidra.program.model.symbol.ShiftedReference;
+
+public class ExtShiftedReference extends ExtReference {
+
+ int shift;
+ long value;
+
+ public ExtShiftedReference(ShiftedReference ref) {
+ super(ref);
+ shift = ref.getShift();
+ value = ref.getValue();
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/ref/ExtStackReference.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/ref/ExtStackReference.java
new file mode 100644
index 0000000000..d16e8ddf5a
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/ref/ExtStackReference.java
@@ -0,0 +1,29 @@
+/* ###
+ * 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 sarif.export.ref;
+
+import ghidra.program.model.symbol.StackReference;
+
+public class ExtStackReference extends ExtReference {
+
+ int offset;
+
+ public ExtStackReference(StackReference ref) {
+ super(ref);
+ offset = ref.getStackOffset();
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/ref/SarifEquateRefWriter.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/ref/SarifEquateRefWriter.java
new file mode 100644
index 0000000000..6a7534d2d7
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/ref/SarifEquateRefWriter.java
@@ -0,0 +1,81 @@
+/* ###
+ * 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 sarif.export.ref;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Iterator;
+
+import com.google.gson.JsonArray;
+
+import ghidra.program.model.address.Address;
+import ghidra.program.model.address.AddressSetView;
+import ghidra.program.model.symbol.Equate;
+import ghidra.program.model.symbol.EquateReference;
+import ghidra.program.model.symbol.EquateTable;
+import ghidra.util.exception.CancelledException;
+import ghidra.util.task.TaskMonitor;
+import sarif.export.AbstractExtWriter;
+import sarif.export.SarifObject;
+
+public class SarifEquateRefWriter extends AbstractExtWriter {
+
+ private AddressSetView set;
+ private EquateTable equateTable;
+
+ public SarifEquateRefWriter(EquateTable equateTable, AddressSetView set, Writer baseWriter) throws IOException {
+ super(baseWriter);
+ this.equateTable = equateTable;
+ this.set = set;
+ }
+
+ @Override
+ protected void genRoot(TaskMonitor monitor) throws CancelledException, IOException {
+ genReferences(monitor);
+ root.add("equates", objects);
+ }
+
+ private void genReferences(TaskMonitor monitor) throws CancelledException, IOException{
+
+ Iterator iter = equateTable.getEquates();
+ while (iter.hasNext()) {
+ if (monitor.isCancelled()) {
+ throw new CancelledException();
+ }
+ Equate equate = iter.next();
+ String name = equate.getName();
+ long value = equate.getValue();
+ EquateReference[] refs = equate.getReferences();
+ for (int i = 0; i < refs.length; i++) {
+ if (monitor.isCancelled()) {
+ return;
+ }
+ Address addr = refs[i].getAddress();
+ if (!set.contains(addr)) {
+ continue;
+ }
+ ExtEquateReference eref = new ExtEquateReference(refs[i], name, value);
+ SarifObject sarif = new SarifObject("Ref.Equate", "REFERENCES", getTree(eref), addr, addr);
+ objects.add(getTree(sarif));
+ }
+ }
+ }
+
+ public JsonArray getResults() {
+ return objects;
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/ref/SarifEquateWriter.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/ref/SarifEquateWriter.java
new file mode 100644
index 0000000000..a5f1dbbdce
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/ref/SarifEquateWriter.java
@@ -0,0 +1,81 @@
+/* ###
+ * 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 sarif.export.ref;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Iterator;
+
+import com.google.gson.JsonArray;
+
+import ghidra.program.model.address.Address;
+import ghidra.program.model.address.AddressSetView;
+import ghidra.program.model.symbol.Equate;
+import ghidra.program.model.symbol.EquateReference;
+import ghidra.program.model.symbol.EquateTable;
+import ghidra.util.exception.CancelledException;
+import ghidra.util.task.TaskMonitor;
+import sarif.export.AbstractExtWriter;
+import sarif.export.SarifObject;
+
+public class SarifEquateWriter extends AbstractExtWriter {
+
+ private AddressSetView set;
+ private EquateTable equateTable;
+
+ public SarifEquateWriter(EquateTable equateTable, AddressSetView set, Writer baseWriter) throws IOException {
+ super(baseWriter);
+ this.equateTable = equateTable;
+ this.set = set;
+ }
+
+ @Override
+ protected void genRoot(TaskMonitor monitor) throws CancelledException, IOException {
+ genReferences(monitor);
+ root.add("equates", objects);
+ }
+
+ private void genReferences(TaskMonitor monitor) throws CancelledException, IOException{
+
+ Iterator iter = equateTable.getEquates();
+ while (iter.hasNext()) {
+ if (monitor.isCancelled()) {
+ throw new CancelledException();
+ }
+ Equate equate = iter.next();
+ String name = equate.getName();
+ long value = equate.getValue();
+ EquateReference[] refs = equate.getReferences();
+ for (int i = 0; i < refs.length; i++) {
+ if (monitor.isCancelled()) {
+ return;
+ }
+ Address addr = refs[i].getAddress();
+ if (!set.contains(addr)) {
+ continue;
+ }
+ ExtEquateReference eref = new ExtEquateReference(refs[i], name, value);
+ SarifObject sarif = new SarifObject("Ref.Equate", "REFERENCES", getTree(eref), addr, addr);
+ objects.add(getTree(sarif));
+ }
+ }
+ }
+
+ public JsonArray getResults() {
+ return objects;
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/ref/SarifReferenceWriter.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/ref/SarifReferenceWriter.java
new file mode 100644
index 0000000000..f6b5bd457d
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/ref/SarifReferenceWriter.java
@@ -0,0 +1,114 @@
+/* ###
+ * 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 sarif.export.ref;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.google.gson.JsonArray;
+
+import ghidra.program.model.address.Address;
+import ghidra.program.model.symbol.ExternalLocation;
+import ghidra.program.model.symbol.ExternalReference;
+import ghidra.program.model.symbol.Reference;
+import ghidra.program.model.symbol.ReferenceManager;
+import ghidra.program.model.symbol.ShiftedReference;
+import ghidra.program.model.symbol.StackReference;
+import ghidra.util.exception.CancelledException;
+import ghidra.util.task.TaskMonitor;
+import sarif.export.AbstractExtWriter;
+import sarif.export.SarifObject;
+import sarif.export.extlib.ExtLibraryLocation;
+import sarif.managers.ExternalLibSarifMgr;
+import sarif.managers.MarkupSarifMgr;
+
+public class SarifReferenceWriter extends AbstractExtWriter {
+
+ private List references = new ArrayList<>();
+ private ReferenceManager referenceManager;
+
+ public SarifReferenceWriter(ReferenceManager referenceManager, List request, Writer baseWriter) throws IOException {
+ super(baseWriter);
+ this.referenceManager = referenceManager;
+ this.references = request;
+ }
+
+ public void requestFunction(Address next) {
+ references.add(next);
+ }
+
+ @Override
+ protected void genRoot(TaskMonitor monitor) throws CancelledException, IOException {
+ genReferences(monitor);
+ root.add("references", objects);
+ }
+
+ private void genReferences(TaskMonitor monitor) throws CancelledException, IOException{
+ monitor.initialize(references.size());
+ for (Address addr : references) {
+ Reference[] refs = referenceManager.getReferencesFrom(addr);
+ for (int i = 0; i < refs.length; i++) {
+ if (monitor.isCancelled()) {
+ throw new CancelledException();
+ }
+ Reference ref = refs[i];
+ if (ref.isRegisterReference()) {
+ ExtRegisterReference mref = new ExtRegisterReference(ref);
+ SarifObject sarif = new SarifObject("Ref.Register", MarkupSarifMgr.KEY, getTree(mref), ref.getFromAddress(), ref.getFromAddress());
+ objects.add(getTree(sarif));
+ }
+ if (ref.isMemoryReference()) {
+ ExtMemoryReference mref = new ExtMemoryReference(ref);
+ SarifObject sarif = new SarifObject("Ref.Memory", MarkupSarifMgr.KEY, getTree(mref), ref.getFromAddress(), ref.getFromAddress());
+ objects.add(getTree(sarif));
+ }
+ if (ref.isStackReference()) {
+ ExtStackReference sref = new ExtStackReference((StackReference) ref);
+ SarifObject sarif = new SarifObject("Ref.Stack", MarkupSarifMgr.KEY, getTree(sref), ref.getFromAddress(), ref.getFromAddress());
+ objects.add(getTree(sarif));
+ }
+ if (ref.isShiftedReference()) {
+ ExtShiftedReference sref = new ExtShiftedReference((ShiftedReference) ref);
+ SarifObject sarif = new SarifObject("Ref.Shifted", MarkupSarifMgr.KEY, getTree(sref), ref.getFromAddress(), ref.getFromAddress());
+ objects.add(getTree(sarif));
+ }
+ if (ref.isExternalReference()) {
+ ExternalReference extRef = (ExternalReference) ref;
+
+ // OK, this is overkill, but some of these locations are not written by
+ // either the SarifClassesNamspaceWriter or the SarifExternalLibraryWriter
+ ExternalLocation extLoc = extRef.getExternalLocation();
+ ExtLibraryLocation obj = new ExtLibraryLocation(extLoc);
+ SarifObject sarif0 = new SarifObject(ExternalLibSarifMgr.SUBKEY1, ExternalLibSarifMgr.KEY, getTree(obj),
+ extLoc.getAddress(), extLoc.getAddress());
+ objects.add(getTree(sarif0));
+
+ ExtExternalReference xref = new ExtExternalReference(extRef);
+ SarifObject sarif = new SarifObject("Ref.External", MarkupSarifMgr.KEY, getTree(xref), ref.getFromAddress(), ref.getFromAddress());
+ objects.add(getTree(sarif));
+ }
+ }
+ monitor.increment();
+ }
+ }
+
+ public JsonArray getResults() {
+ return objects;
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/registers/ExtRegisterValue.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/registers/ExtRegisterValue.java
new file mode 100644
index 0000000000..ce9d8b9b49
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/registers/ExtRegisterValue.java
@@ -0,0 +1,32 @@
+/* ###
+ * 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 sarif.export.registers;
+
+import ghidra.program.model.data.ISF.IsfObject;
+import ghidra.program.model.lang.Register;
+
+public class ExtRegisterValue implements IsfObject {
+
+ String name;
+ String value;
+
+ public ExtRegisterValue(Register reg, String value) {
+ name = reg.getName();
+ this.value = value;
+ }
+
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/registers/SarifRegisterValueWriter.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/registers/SarifRegisterValueWriter.java
new file mode 100644
index 0000000000..c6576c6845
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/registers/SarifRegisterValueWriter.java
@@ -0,0 +1,75 @@
+/* ###
+ * 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 sarif.export.registers;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.math.BigInteger;
+import java.util.List;
+
+import ghidra.program.model.address.AddressRange;
+import ghidra.program.model.address.AddressRangeIterator;
+import ghidra.program.model.lang.Register;
+import ghidra.program.model.listing.ProgramContext;
+import ghidra.util.exception.CancelledException;
+import ghidra.util.task.TaskMonitor;
+import sarif.export.AbstractExtWriter;
+import sarif.export.SarifObject;
+import sarif.managers.RegisterValuesSarifMgr;
+
+public class SarifRegisterValueWriter extends AbstractExtWriter {
+
+ List ranges;
+ ProgramContext context;
+ List registers;
+
+ public SarifRegisterValueWriter(ProgramContext context, List registers, List request,
+ Writer baseWriter) throws IOException {
+ super(baseWriter);
+ this.ranges = request;
+ this.context = context;
+ this.registers = registers;
+ }
+
+ @Override
+ protected void genRoot(TaskMonitor monitor) throws CancelledException, IOException {
+ genRegisters(monitor);
+ root.add("registers", objects);
+ }
+
+ private void genRegisters(TaskMonitor monitor) throws CancelledException, IOException {
+ monitor.initialize(ranges.size());
+ for (AddressRange r : ranges) {
+ for (Register reg : registers) {
+ AddressRangeIterator it = context.getRegisterValueAddressRanges(reg, r.getMinAddress(),
+ r.getMaxAddress());
+ while (it.hasNext()) {
+ monitor.checkCancelled();
+ AddressRange valueRange = it.next();
+ BigInteger value = context.getValue(reg, valueRange.getMinAddress(), false);
+ if (value == null) {
+ continue;
+ }
+ ExtRegisterValue isf = new ExtRegisterValue(reg, value.toString(16));
+ SarifObject sarif = new SarifObject(RegisterValuesSarifMgr.SUBKEY, RegisterValuesSarifMgr.KEY, getTree(isf), valueRange.getMinAddress(), valueRange.getMaxAddress());
+ objects.add(getTree(sarif));
+ }
+ }
+ monitor.increment();
+ }
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/relocs/ExtRelocation.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/relocs/ExtRelocation.java
new file mode 100644
index 0000000000..8062c10f17
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/relocs/ExtRelocation.java
@@ -0,0 +1,71 @@
+/* ###
+ * 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 sarif.export.relocs;
+
+import org.apache.commons.lang3.StringUtils;
+
+import ghidra.program.model.data.ISF.IsfObject;
+import ghidra.program.model.reloc.Relocation;
+
+public class ExtRelocation implements IsfObject {
+
+ String name;
+ String kind;
+ String value;
+ String bytes;
+
+ public ExtRelocation(Relocation reloc) {
+ kind = Integer.toString(reloc.getType());
+ value = pack(reloc.getValues());
+ String packedBytes = pack(reloc.getBytes());
+ if (packedBytes != null) {
+ bytes = packedBytes;
+ }
+ String symName = reloc.getSymbolName();
+ if (!StringUtils.isEmpty(symName)) {
+ name = reloc.getSymbolName();
+ }
+ }
+
+ private String pack(long[] values) {
+ if (values == null || values.length == 0) {
+ return "";
+ }
+ StringBuffer buf = new StringBuffer();
+ for (long v : values) {
+ if (buf.length() != 0) {
+ buf.append(',');
+ }
+ buf.append("0x" + Long.toHexString(v));
+ }
+ return buf.toString();
+ }
+
+ private String pack(byte[] values) {
+ if (values == null || values.length == 0) {
+ return null;
+ }
+ StringBuffer buf = new StringBuffer();
+ for (byte v : values) {
+ if (buf.length() != 0) {
+ buf.append(',');
+ }
+ buf.append("0x" + Integer.toHexString(v & 0xff));
+ }
+ return buf.toString();
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/relocs/SarifRelocationWriter.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/relocs/SarifRelocationWriter.java
new file mode 100644
index 0000000000..1e1215cb6d
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/relocs/SarifRelocationWriter.java
@@ -0,0 +1,55 @@
+/* ###
+ * 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 sarif.export.relocs;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.List;
+
+import ghidra.program.model.reloc.Relocation;
+import ghidra.util.exception.CancelledException;
+import ghidra.util.task.TaskMonitor;
+import sarif.export.AbstractExtWriter;
+import sarif.export.SarifObject;
+import sarif.managers.RelocationTableSarifMgr;
+
+public class SarifRelocationWriter extends AbstractExtWriter {
+
+ List relocs;
+
+ public SarifRelocationWriter(List request, Writer baseWriter) throws IOException {
+ super(baseWriter);
+ this.relocs = request;
+ }
+
+ @Override
+ protected void genRoot(TaskMonitor monitor) throws CancelledException, IOException {
+ genRelocation(monitor);
+ root.add("relocations", objects);
+ }
+
+ private void genRelocation(TaskMonitor monitor) throws CancelledException, IOException {
+ monitor.initialize(relocs.size());
+ for (Relocation r : relocs) {
+ ExtRelocation isf = new ExtRelocation(r);
+ SarifObject sarif = new SarifObject(RelocationTableSarifMgr.SUBKEY, RelocationTableSarifMgr.KEY,
+ getTree(isf), r.getAddress(), r.getAddress());
+ objects.add(getTree(sarif));
+ monitor.increment();
+ }
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/symbols/ExtSymbol.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/symbols/ExtSymbol.java
new file mode 100644
index 0000000000..2ba4590908
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/symbols/ExtSymbol.java
@@ -0,0 +1,84 @@
+/* ###
+ * 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 sarif.export.symbols;
+
+import ghidra.program.database.symbol.ClassSymbol;
+import ghidra.program.database.symbol.LibrarySymbol;
+import ghidra.program.database.symbol.NamespaceSymbol;
+import ghidra.program.model.data.ISF.IsfObject;
+import ghidra.program.model.listing.GhidraClass;
+import ghidra.program.model.listing.Library;
+import ghidra.program.model.symbol.Namespace;
+import ghidra.program.model.symbol.Symbol;
+
+public class ExtSymbol implements IsfObject {
+
+ String name;
+ String location;
+ Boolean namespaceIsClass;
+ String kind;
+ String type;
+ String sourceType;
+ boolean primary;
+
+ public ExtSymbol(Symbol symbol) {
+ name = symbol.getName();
+ location = getNamespace(symbol);
+ kind = checkGlobal(symbol) ? "global" : "local";
+ sourceType = symbol.getSource().toString();
+ primary = symbol.isPrimary();
+ if (symbol instanceof ClassSymbol) {
+ type = "class";
+ } else if (symbol instanceof LibrarySymbol) {
+ type = "library";
+ } else if (symbol instanceof NamespaceSymbol) {
+ type = "namespace";
+ }
+ // NB: DO NOT add type==function, as this will affect the execution order
+ //if (symbol instanceof FunctionSymbol fs) {
+ // type = "function";
+ //}
+ }
+
+ /**
+ * Returns the name of symbol qualified with any namespace information. For
+ * example, "User32.dll::SomeClass::printf".
+ */
+ private String getNamespace(Symbol symbol) {
+ StringBuffer buffer = new StringBuffer();
+ Namespace namespace = symbol.getParentNamespace();
+ while (!namespace.isGlobal()) {
+ buffer.insert(0, namespace.getName() + "::");
+ if (namespace instanceof GhidraClass) {
+ namespaceIsClass = true;
+ }
+ namespace = namespace.getParentNamespace();
+ }
+ return buffer.toString();
+ }
+
+ private boolean checkGlobal(Symbol symbol) {
+ if (symbol.isGlobal()) {
+ return true;
+ }
+ Namespace parent = symbol.getParentNamespace();
+ if (parent instanceof Library) {
+ return true;
+ }
+ return false;
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/symbols/SarifSymbolWriter.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/symbols/SarifSymbolWriter.java
new file mode 100644
index 0000000000..b9960545f5
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/symbols/SarifSymbolWriter.java
@@ -0,0 +1,80 @@
+/* ###
+ * 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 sarif.export.symbols;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.google.gson.JsonArray;
+
+import ghidra.program.model.symbol.SourceType;
+import ghidra.program.model.symbol.Symbol;
+import ghidra.program.model.symbol.SymbolType;
+import ghidra.util.exception.CancelledException;
+import ghidra.util.task.TaskMonitor;
+import sarif.export.AbstractExtWriter;
+import sarif.export.SarifObject;
+import sarif.managers.SymbolTableSarifMgr;
+
+public class SarifSymbolWriter extends AbstractExtWriter {
+
+ private List symbols = new ArrayList<>();
+
+ public SarifSymbolWriter(List request, Writer baseWriter) throws IOException {
+ super(baseWriter);
+ this.symbols = request;
+ }
+
+ @Override
+ protected void genRoot(TaskMonitor monitor) throws CancelledException, IOException {
+ genSymbols(monitor);
+ root.add("definedData", objects);
+ }
+
+ private void genSymbols(TaskMonitor monitor) throws CancelledException, IOException{
+ monitor.initialize(symbols.size());
+ for (Symbol s : symbols) {
+ SymbolType symbolType = s.getSymbolType();
+ if (s.getSource() == SourceType.DEFAULT) {
+ continue;
+ }
+
+ if (symbolType != SymbolType.LABEL && symbolType != SymbolType.FUNCTION) {
+ continue;
+ }
+
+ addSymbol(s);
+ monitor.increment();
+ }
+ }
+
+ private void addSymbol(Symbol s) {
+ if (!s.getParentNamespace().isGlobal()) {
+ Symbol p = s.getParentSymbol();
+ addSymbol(p);
+ }
+ ExtSymbol lib = new ExtSymbol(s);
+ SarifObject sarif = new SarifObject("Symbol", SymbolTableSarifMgr.KEY, getTree(lib), s.getAddress(), s.getAddress());
+ objects.add(getTree(sarif));
+ }
+
+ public JsonArray getResults() {
+ return objects;
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/trees/ExtFragment.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/trees/ExtFragment.java
new file mode 100644
index 0000000000..86b1dcfe8c
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/trees/ExtFragment.java
@@ -0,0 +1,42 @@
+/* ###
+ * 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 sarif.export.trees;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import ghidra.program.model.address.AddressRange;
+import ghidra.program.model.address.AddressRangeIterator;
+import ghidra.program.model.data.ISF.IsfObject;
+import ghidra.program.model.listing.ProgramFragment;
+
+public class ExtFragment implements IsfObject {
+
+ String name;
+ List ranges = new ArrayList<>();
+
+ public ExtFragment(ProgramFragment f, List visited) {
+ this.name = f.getName();
+ visited.add(f);
+ AddressRangeIterator iter = f.getAddressRanges();
+ while (iter.hasNext()) {
+ AddressRange range = iter.next();
+ ExtFragmentRange r = new ExtFragmentRange(range);
+ ranges.add(r);
+ }
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/trees/ExtFragmentRange.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/trees/ExtFragmentRange.java
new file mode 100644
index 0000000000..e0f3fbd4f1
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/trees/ExtFragmentRange.java
@@ -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 sarif.export.trees;
+
+import ghidra.program.model.address.AddressRange;
+import ghidra.program.model.data.ISF.IsfObject;
+
+public class ExtFragmentRange implements IsfObject {
+
+ String start;
+ String end;
+
+ public ExtFragmentRange(AddressRange range) {
+ start = range.getMinAddress().toString();
+ end = range.getMaxAddress().toString();
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/trees/ExtModule.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/trees/ExtModule.java
new file mode 100644
index 0000000000..43038a07c4
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/trees/ExtModule.java
@@ -0,0 +1,51 @@
+/* ###
+ * 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 sarif.export.trees;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import ghidra.program.model.data.ISF.IsfObject;
+import ghidra.program.model.listing.Group;
+import ghidra.program.model.listing.ProgramFragment;
+import ghidra.program.model.listing.ProgramModule;
+
+public class ExtModule implements IsfObject {
+
+ String name;
+ String value;
+
+ List modules = new ArrayList<>();
+ List fragments = new ArrayList<>();
+
+ public ExtModule(String name, ProgramModule m, List visited) {
+ this.name = name;
+ if (!visited.contains(m)) {
+ visited.add(m);
+ Group[] children = m.getChildren();
+ for (Group child : children) {
+ if (child instanceof ProgramModule cm) {
+ ExtModule module = new ExtModule(child.getName(), cm, visited);
+ modules.add(module);
+ } else if (child instanceof ProgramFragment cf) {
+ ExtFragment fragment = new ExtFragment(cf, visited);
+ fragments.add(fragment);
+ }
+ }
+ }
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/trees/SarifTreeWriter.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/trees/SarifTreeWriter.java
new file mode 100644
index 0000000000..d1c4659734
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/trees/SarifTreeWriter.java
@@ -0,0 +1,57 @@
+/* ###
+ * 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 sarif.export.trees;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.List;
+
+import generic.stl.Pair;
+import ghidra.program.model.listing.ProgramModule;
+import ghidra.util.exception.CancelledException;
+import ghidra.util.task.TaskMonitor;
+import sarif.export.AbstractExtWriter;
+import sarif.export.SarifObject;
+import sarif.managers.ProgramTreeSarifMgr;
+
+public class SarifTreeWriter extends AbstractExtWriter {
+
+ private List> modules;
+ private List visited = new ArrayList<>();
+
+ public SarifTreeWriter(List> target, Writer baseWriter) throws IOException {
+ super(baseWriter);
+ this.modules = target;
+ }
+
+ @Override
+ protected void genRoot(TaskMonitor monitor) throws CancelledException, IOException {
+ genTree(monitor);
+ root.add("trees", objects);
+ }
+
+ private void genTree(TaskMonitor monitor) throws CancelledException, IOException {
+ monitor.initialize(modules.size());
+ for (Pair pair : modules) {
+ ExtModule isf = new ExtModule(pair.first, pair.second, visited);
+ SarifObject sarif = new SarifObject(ProgramTreeSarifMgr.SUBKEY, ProgramTreeSarifMgr.KEY, getTree(isf), null);
+ objects.add(getTree(sarif));
+ monitor.increment();
+ }
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/handlers/SarifResultHandler.java b/Ghidra/Features/Sarif/src/main/java/sarif/handlers/SarifResultHandler.java
new file mode 100644
index 0000000000..b2728b9313
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/handlers/SarifResultHandler.java
@@ -0,0 +1,106 @@
+/* ###
+ * 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 sarif.handlers;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import com.contrastsecurity.sarif.PropertyBag;
+import com.contrastsecurity.sarif.Result;
+import com.contrastsecurity.sarif.Run;
+
+import docking.ActionContext;
+import docking.action.DockingAction;
+import docking.action.MenuData;
+import ghidra.program.util.ProgramTask;
+import ghidra.util.classfinder.ExtensionPoint;
+import ghidra.util.task.TaskLauncher;
+import sarif.SarifController;
+import sarif.model.SarifDataFrame;
+import sarif.view.SarifResultsTableProvider;
+
+abstract public class SarifResultHandler implements ExtensionPoint {
+
+ protected List headers = new ArrayList<>();
+ protected SarifDataFrame df;
+ protected SarifController controller;
+ protected Run run;
+ protected Result result;
+ protected SarifResultsTableProvider provider;
+
+ public abstract String getKey();
+
+ public boolean isEnabled() {
+ return true;
+ }
+
+ public void handle(SarifDataFrame df, Run run, Result result, Map map) {
+ this.df = df;
+ this.controller = df.getController();
+ this.run = run;
+ this.result = result;
+ Object res = parse();
+ if (res != null) {
+ map.put(getKey(), res);
+ }
+ }
+
+ protected abstract Object parse();
+
+ public String getActionName() {
+ return null;
+ }
+
+ protected Object getProperty(String key) {
+ PropertyBag properties = result.getProperties();
+ if (properties == null) {
+ return null;
+ }
+ Map additionalProperties = properties.getAdditionalProperties();
+ if (additionalProperties == null) {
+ return null;
+ }
+ return additionalProperties.get(key);
+ }
+
+ public ProgramTask getTask(SarifResultsTableProvider provider) {
+ return null;
+ }
+
+ public DockingAction createAction(SarifResultsTableProvider provider) {
+ this.provider = provider;
+ DockingAction rightClick = new DockingAction(getActionName(), getKey()) {
+ @Override
+ public void actionPerformed(ActionContext context) {
+ ProgramTask task = getTask(provider);
+ TaskLauncher.launch(task);
+ }
+
+ @Override
+ public boolean isEnabledForContext(ActionContext context) {
+ return true;
+ }
+
+ @Override
+ public boolean isAddToPopup(ActionContext context) {
+ return true;
+ }
+ };
+ rightClick.setPopupMenuData(new MenuData(new String[] { getActionName() }));
+ return rightClick;
+ }
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/handlers/SarifRunHandler.java b/Ghidra/Features/Sarif/src/main/java/sarif/handlers/SarifRunHandler.java
new file mode 100644
index 0000000000..5d8775435d
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/handlers/SarifRunHandler.java
@@ -0,0 +1,45 @@
+/* ###
+ * 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 sarif.handlers;
+
+import com.contrastsecurity.sarif.Run;
+
+import ghidra.util.classfinder.ExtensionPoint;
+import sarif.SarifController;
+import sarif.model.SarifDataFrame;
+
+abstract public class SarifRunHandler implements ExtensionPoint {
+
+ protected SarifDataFrame df;
+ protected SarifController controller;
+ protected Run run;
+
+ public abstract String getKey();
+
+ public boolean isEnabled() {
+ return true;
+ }
+
+ public void handle(SarifDataFrame df, Run run) {
+ this.df = df;
+ this.controller = df.getController();
+ this.run = run;
+ parse();
+ }
+
+ protected abstract Object parse();
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/handlers/result/SarifAddressResultHandler.java b/Ghidra/Features/Sarif/src/main/java/sarif/handlers/result/SarifAddressResultHandler.java
new file mode 100644
index 0000000000..d61b18fda8
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/handlers/result/SarifAddressResultHandler.java
@@ -0,0 +1,38 @@
+/* ###
+ * 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 sarif.handlers.result
+;
+
+import java.util.List;
+
+import ghidra.program.model.address.Address;
+import sarif.handlers.SarifResultHandler;
+
+public class SarifAddressResultHandler extends SarifResultHandler {
+
+ // If we can parse a listing Address we can make the table navigate there when
+ // selected
+
+ public String getKey() {
+ return "Address";
+ }
+
+ public Address parse() {
+ List listingAddresses = controller.getListingAddresses(result);
+ return listingAddresses.isEmpty() ? null : listingAddresses.get(0);
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/handlers/result/SarifCodeFlowResultHandler.java b/Ghidra/Features/Sarif/src/main/java/sarif/handlers/result/SarifCodeFlowResultHandler.java
new file mode 100644
index 0000000000..0fd1f6973f
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/handlers/result/SarifCodeFlowResultHandler.java
@@ -0,0 +1,59 @@
+/* ###
+ * 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 sarif.handlers.result;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.contrastsecurity.sarif.CodeFlow;
+import com.contrastsecurity.sarif.ThreadFlow;
+import com.contrastsecurity.sarif.ThreadFlowLocation;
+
+import ghidra.program.model.address.Address;
+import sarif.handlers.SarifResultHandler;
+
+public class SarifCodeFlowResultHandler extends SarifResultHandler {
+
+ public String getKey() {
+ return "CodeFlows";
+ }
+
+ public List>> parse() {
+ List>> res = new ArrayList<>();
+ List codeFlows = result.getCodeFlows();
+ if (codeFlows != null) {
+ for (CodeFlow f : codeFlows) {
+ Map> map = new HashMap<>();
+ parseCodeFlow(f, map);
+ res.add(map);
+ }
+ }
+ return res;
+ }
+
+ private void parseCodeFlow(CodeFlow f, Map> map) {
+ for (ThreadFlow t : f.getThreadFlows()) {
+ List addrs = new ArrayList();
+ for (ThreadFlowLocation loc : t.getLocations()) {
+ addrs.add(controller.locationToAddress(loc.getLocation()));
+ }
+ map.put(t.getId(), addrs);
+ }
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/handlers/result/SarifGraphResultHandler.java b/Ghidra/Features/Sarif/src/main/java/sarif/handlers/result/SarifGraphResultHandler.java
new file mode 100644
index 0000000000..068caaffc5
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/handlers/result/SarifGraphResultHandler.java
@@ -0,0 +1,63 @@
+/* ###
+ * IP: GHIDRA
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package sarif.handlers.result;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import com.contrastsecurity.sarif.Edge;
+import com.contrastsecurity.sarif.Graph;
+import com.contrastsecurity.sarif.Node;
+
+import ghidra.service.graph.AttributedGraph;
+import ghidra.service.graph.AttributedVertex;
+import ghidra.service.graph.EmptyGraphType;
+import sarif.handlers.SarifResultHandler;
+
+public class SarifGraphResultHandler extends SarifResultHandler {
+
+ public String getKey() {
+ return "Graphs";
+ }
+
+ public List parse() {
+ List res = new ArrayList();
+ Set graphs = result.getGraphs();
+ if (graphs != null) {
+ for (Graph g : graphs) {
+ res.add(parseGraph(g));
+ }
+ }
+ return res;
+ }
+
+ private AttributedGraph parseGraph(Graph g) {
+ AttributedGraph graph = new AttributedGraph(controller.getProgram().getDescription(), new EmptyGraphType());
+ Map nodeMap = new HashMap();
+ for (Node n : g.getNodes()) {
+ // AttributedVertex node = graph.addVertex(n.getId(), n.getLabel().getText());
+ // node.
+ nodeMap.put(n.getId(), graph.addVertex(n.getId(), n.getLabel().getText()));
+ }
+ for (Edge e : g.getEdges()) {
+ graph.addEdge(nodeMap.get(e.getSourceNodeId()), nodeMap.get(e.getTargetNodeId()));
+ }
+ return graph;
+ }
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/handlers/result/SarifKindResultHandler.java b/Ghidra/Features/Sarif/src/main/java/sarif/handlers/result/SarifKindResultHandler.java
new file mode 100644
index 0000000000..9570bcf193
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/handlers/result/SarifKindResultHandler.java
@@ -0,0 +1,30 @@
+/* ###
+ * 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 sarif.handlers.result;
+
+import sarif.handlers.SarifResultHandler;
+
+public class SarifKindResultHandler extends SarifResultHandler {
+
+ public String getKey() {
+ return "Kind";
+ }
+
+ public String parse() {
+ return result.getKind().toString();
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/handlers/result/SarifLevelResultHandler.java b/Ghidra/Features/Sarif/src/main/java/sarif/handlers/result/SarifLevelResultHandler.java
new file mode 100644
index 0000000000..a88a320e01
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/handlers/result/SarifLevelResultHandler.java
@@ -0,0 +1,30 @@
+/* ###
+ * 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 sarif.handlers.result;
+
+import sarif.handlers.SarifResultHandler;
+
+public class SarifLevelResultHandler extends SarifResultHandler {
+
+ public String getKey() {
+ return "Level";
+ }
+
+ public String parse() {
+ return result.getLevel().toString();
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/handlers/result/SarifProgramResultHandler.java b/Ghidra/Features/Sarif/src/main/java/sarif/handlers/result/SarifProgramResultHandler.java
new file mode 100644
index 0000000000..2591fb200c
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/handlers/result/SarifProgramResultHandler.java
@@ -0,0 +1,115 @@
+/* ###
+ * 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 sarif.handlers.result;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import com.contrastsecurity.sarif.Location;
+import com.contrastsecurity.sarif.PropertyBag;
+import com.contrastsecurity.sarif.Result;
+import com.contrastsecurity.sarif.Run;
+
+import ghidra.program.util.ProgramTask;
+import ghidra.util.task.TaskMonitor;
+import sarif.SarifProgramOptions;
+import sarif.handlers.SarifResultHandler;
+import sarif.managers.ProgramSarifMgr;
+import sarif.model.SarifDataFrame;
+import sarif.view.SarifResultsTableProvider;
+
+public class SarifProgramResultHandler extends SarifResultHandler {
+
+ public String getKey() {
+ return "Message";
+ }
+
+ public void handle(SarifDataFrame df, Run run, Result result, Map map) {
+ this.df = df;
+ this.controller = df.getController();
+
+ this.run = run;
+ this.result = result;
+ map.put(getKey(), result.getMessage().getText());
+ List locs = result.getLocations();
+ if (locs != null) {
+ map.put("Locations", locs);
+ }
+ PropertyBag properties = result.getProperties();
+ if (properties != null) {
+ Map additionalProperties = properties.getAdditionalProperties();
+ if (additionalProperties != null) {
+ for (Entry entry : additionalProperties.entrySet()) {
+ map.put(entry.getKey(), entry.getValue());
+ }
+ }
+ }
+ }
+
+ @Override
+ protected Object parse() {
+ // UNUSED
+ return null;
+ }
+
+ @Override
+ public String getActionName() {
+ return "Add To Program";
+ }
+
+ @Override
+ public ProgramTask getTask(SarifResultsTableProvider provider) {
+ return new CommitToProgramTask(provider);
+ }
+
+ private class CommitToProgramTask extends ProgramTask {
+
+ private SarifResultsTableProvider provider;
+ private ProgramSarifMgr programMgr;
+
+ protected CommitToProgramTask(SarifResultsTableProvider provider) {
+ super(provider.getController().getProgram(), "CommitToProgramTask", true, true, true);
+ this.provider = provider;
+ this.programMgr = provider.getController().getProgramSarifMgr();
+ programMgr.addManagers();
+ }
+
+ protected void doRun(TaskMonitor monitor) {
+ int[] selected = provider.filterTable.getTable().getSelectedRows();
+ Map>> results = new HashMap<>();
+ for (int row : selected) {
+ Map result = provider.getRow(row);
+ String key = (String) result.get("RuleId");
+ List> list = results.get(key);
+ if (list == null) {
+ list = new ArrayList<>();
+ }
+ list.add(result);
+ results.put(key, list);
+ }
+ try {
+ programMgr.readResults(monitor, (SarifProgramOptions) null, results);
+ } catch (IOException e) {
+ throw new RuntimeException("Read failed");
+ }
+ }
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/handlers/result/SarifPropertyResultHandler.java b/Ghidra/Features/Sarif/src/main/java/sarif/handlers/result/SarifPropertyResultHandler.java
new file mode 100644
index 0000000000..6ea8f9bc0e
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/handlers/result/SarifPropertyResultHandler.java
@@ -0,0 +1,79 @@
+/* ###
+ * 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 sarif.handlers.result;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import com.contrastsecurity.sarif.PropertyBag;
+import com.contrastsecurity.sarif.Result;
+import com.contrastsecurity.sarif.Run;
+
+import db.Transaction;
+import ghidra.program.model.address.Address;
+import sarif.handlers.SarifResultHandler;
+import sarif.model.SarifColumnKey;
+import sarif.model.SarifDataFrame;
+
+public class SarifPropertyResultHandler extends SarifResultHandler {
+
+ public String getKey() {
+ return "Property";
+ }
+
+ public List parse() {
+ return controller.getListingAddresses(result);
+ }
+
+ @Override
+ public void handle(SarifDataFrame df, Run run, Result result, Map map) {
+ this.controller = df.getController();
+ List columns = df.getColumns();
+ List columnNames = new ArrayList<>();
+ for (SarifColumnKey c : columns) {
+ columnNames.add(c.getName());
+ }
+ PropertyBag properties = result.getProperties();
+ if (properties == null) {
+ return;
+ }
+ Map additional = properties.getAdditionalProperties();
+ if (additional == null) {
+ return;
+ }
+ try (Transaction t = controller.getProgram().openTransaction("SARIF custom properties.")) {
+ for (String key : additional.keySet()) {
+ String[] splits = key.split("/");
+ switch (splits[0]) {
+ case "viewer":
+ switch (splits[1]) {
+ case "table":
+ if (!columnNames.contains(splits[2])) {
+ columns.add(new SarifColumnKey(splits[2], false));
+ }
+ map.put(splits[2], additional.get(key));
+ }
+ break;
+ case "listing":
+ controller.handleListingAction(result, splits[1], additional.get(key));
+ break;
+ }
+ }
+ t.commit();
+ }
+ }
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/handlers/result/SarifRuleIdResultHandler.java b/Ghidra/Features/Sarif/src/main/java/sarif/handlers/result/SarifRuleIdResultHandler.java
new file mode 100644
index 0000000000..af8c9bbc0e
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/handlers/result/SarifRuleIdResultHandler.java
@@ -0,0 +1,30 @@
+/* ###
+ * 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 sarif.handlers.result;
+
+import sarif.handlers.SarifResultHandler;
+
+public class SarifRuleIdResultHandler extends SarifResultHandler {
+
+ public String getKey() {
+ return "RuleId";
+ }
+
+ public String parse() {
+ return result.getRuleId();
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/handlers/result/SarifToolResultHandler.java b/Ghidra/Features/Sarif/src/main/java/sarif/handlers/result/SarifToolResultHandler.java
new file mode 100644
index 0000000000..867c750af4
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/handlers/result/SarifToolResultHandler.java
@@ -0,0 +1,30 @@
+/* ###
+ * 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 sarif.handlers.result;
+
+import sarif.handlers.SarifResultHandler;
+
+public class SarifToolResultHandler extends SarifResultHandler {
+
+ public String getKey() {
+ return "Tool";
+ }
+
+ public String parse() {
+ return run.getTool().getDriver().getName();
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/handlers/result/sample/SarifReturnTypeResultHandler.java b/Ghidra/Features/Sarif/src/main/java/sarif/handlers/result/sample/SarifReturnTypeResultHandler.java
new file mode 100644
index 0000000000..6c66e75baf
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/handlers/result/sample/SarifReturnTypeResultHandler.java
@@ -0,0 +1,140 @@
+/* ###
+ * 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 sarif.handlers.result.sample;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.contrastsecurity.sarif.ReportingDescriptor;
+import com.contrastsecurity.sarif.ReportingDescriptorReference;
+import com.contrastsecurity.sarif.ToolComponent;
+
+import ghidra.program.model.data.BooleanDataType;
+import ghidra.program.model.data.CharDataType;
+import ghidra.program.model.data.DataType;
+import ghidra.program.model.data.DoubleDataType;
+import ghidra.program.model.data.IntegerDataType;
+import ghidra.program.model.data.LongDataType;
+import ghidra.program.model.data.LongDoubleDataType;
+import ghidra.program.model.data.PointerDataType;
+import ghidra.program.model.data.UnsignedIntegerDataType;
+import ghidra.program.model.data.UnsignedLongDataType;
+import ghidra.program.model.data.VoidDataType;
+import ghidra.program.model.listing.Function;
+import ghidra.program.util.ProgramTask;
+import ghidra.util.exception.InvalidInputException;
+import ghidra.util.task.TaskMonitor;
+import sarif.handlers.SarifResultHandler;
+import sarif.view.SarifResultsTableProvider;
+
+public class SarifReturnTypeResultHandler extends SarifResultHandler {
+
+ @Override
+ public String getKey() {
+ return "return type";
+ }
+
+ @Override
+ public String parse() {
+ ToolComponent tc = df.getComponentMap().get(getKey());
+ ReportingDescriptorReference ref = df.getTaxa().get(getKey());
+ if (tc != null) {
+ List view = new ArrayList<>(tc.getTaxa());
+ if (ref != null) {
+ int index = ref.getIndex().intValue();
+ if (index < view.size()) {
+ return view.get(index).getId();
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public String getActionName() {
+ return "Commit";
+ }
+
+ @Override
+ public ProgramTask getTask(SarifResultsTableProvider provider) {
+ return new ReturnTypeTaxonomyTask(provider);
+ }
+
+ private class ReturnTypeTaxonomyTask extends ProgramTask {
+
+ private SarifResultsTableProvider provider;
+
+ protected ReturnTypeTaxonomyTask(SarifResultsTableProvider provider) {
+ super(provider.getController().getProgram(), "ReturnTypeTaxonomyTask", true, true, true);
+ this.provider = provider;
+ }
+
+ protected void doRun(TaskMonitor monitor) {
+ int col = provider.getIndex("return type");
+ int[] selected = provider.filterTable.getTable().getSelectedRows();
+ for (int row : selected) {
+ Function func = provider.getController().getProgram().getFunctionManager()
+ .getFunctionContaining(provider.model.getAddress(row));
+ String value = (String) provider.getValue(row, col);
+ setReturnType(func, value);
+ }
+ }
+
+ private boolean setReturnType(Function func, String type) {
+ if (type != null) {
+ try {
+ func.setReturnType(parseDataType(type), func.getSignatureSource());
+ return true;
+ } catch (InvalidInputException e) {
+ throw new RuntimeException("Error setting return type for "+func);
+ }
+ }
+ return false;
+ }
+
+ private DataType parseDataType(String datatype) {
+ switch (datatype) {
+ case "int":
+ return new IntegerDataType();
+ case "uint":
+ case "__ssize_t":
+ return new UnsignedIntegerDataType();
+ case "bool":
+ return new BooleanDataType();
+ case "char":
+ return new CharDataType();
+ case "char *":
+ case "FILE *":
+ case "void *":
+ case "whcar_t *":
+ case "tm *":
+ return new PointerDataType();
+ case "void":
+ return new VoidDataType();
+ case "double":
+ return new DoubleDataType();
+ case "long":
+ return new LongDataType();
+ case "longdouble":
+ return new LongDoubleDataType();
+ case "ulong":
+ return new UnsignedLongDataType();
+ }
+ return null;
+ }
+
+ }
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/handlers/run/SarifGraphRunHandler.java b/Ghidra/Features/Sarif/src/main/java/sarif/handlers/run/SarifGraphRunHandler.java
new file mode 100644
index 0000000000..b2289176de
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/handlers/run/SarifGraphRunHandler.java
@@ -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 sarif.handlers.run;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import com.contrastsecurity.sarif.Edge;
+import com.contrastsecurity.sarif.Graph;
+import com.contrastsecurity.sarif.Node;
+import com.contrastsecurity.sarif.Run;
+
+import ghidra.service.graph.AttributedGraph;
+import ghidra.service.graph.AttributedVertex;
+import ghidra.service.graph.EmptyGraphType;
+import sarif.handlers.SarifRunHandler;
+import sarif.model.SarifDataFrame;
+
+public class SarifGraphRunHandler extends SarifRunHandler {
+
+ @Override
+ public String getKey() {
+ return "graphs";
+ }
+
+ @Override
+ public List parse() {
+ List res = new ArrayList<>();
+ Set graphs = run.getGraphs();
+ if (graphs != null) {
+ for (Graph g : graphs) {
+ String description = g.getDescription() == null ? controller.getProgram().getDescription()
+ : g.getDescription().getText();
+ AttributedGraph graph = new AttributedGraph(description, new EmptyGraphType());
+ Map nodeMap = new HashMap();
+ for (Node n : g.getNodes()) {
+ nodeMap.put(n.getId(), graph.addVertex(n.getId(), n.getLabel().getText()));
+ }
+ for (Edge e : g.getEdges()) {
+ graph.addEdge(nodeMap.get(e.getSourceNodeId()), nodeMap.get(e.getTargetNodeId()));
+ }
+ res.add(graph);
+ }
+ }
+ return res;
+ }
+
+ @Override
+ public void handle(SarifDataFrame df, Run run) {
+ this.df = df;
+ this.controller = df.getController();
+ this.run = run;
+ List res = parse();
+ if (res != null) {
+ for (AttributedGraph g : res) {
+ controller.showGraph(g);
+ }
+ }
+ }
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/io/SarifGsonIO.java b/Ghidra/Features/Sarif/src/main/java/sarif/io/SarifGsonIO.java
new file mode 100644
index 0000000000..9f59efe4fb
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/io/SarifGsonIO.java
@@ -0,0 +1,38 @@
+/* ###
+ * 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 sarif.io;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+
+import com.contrastsecurity.sarif.SarifSchema210;
+import com.google.gson.Gson;
+import com.google.gson.JsonSyntaxException;
+
+public class SarifGsonIO implements SarifIO {
+
+ public SarifSchema210 readSarif(File file) throws JsonSyntaxException, IOException {
+ Gson gson = new Gson();
+ return gson.fromJson(Files.readString(file.toPath()), SarifSchema210.class);
+ }
+
+ public SarifSchema210 readSarif(String str) throws JsonSyntaxException, IOException {
+ Gson gson = new Gson();
+ return gson.fromJson(str, SarifSchema210.class);
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/io/SarifIO.java b/Ghidra/Features/Sarif/src/main/java/sarif/io/SarifIO.java
new file mode 100644
index 0000000000..480581e466
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/io/SarifIO.java
@@ -0,0 +1,30 @@
+/* ###
+ * 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 sarif.io;
+
+import java.io.File;
+import java.io.IOException;
+
+import com.contrastsecurity.sarif.SarifSchema210;
+import com.google.gson.JsonSyntaxException;
+
+public interface SarifIO {
+
+ public SarifSchema210 readSarif(File file) throws JsonSyntaxException, IOException;
+
+ public SarifSchema210 readSarif(String str) throws JsonSyntaxException, IOException;
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/io/SarifJacksonIO.java b/Ghidra/Features/Sarif/src/main/java/sarif/io/SarifJacksonIO.java
new file mode 100644
index 0000000000..f0c4f68c48
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/io/SarifJacksonIO.java
@@ -0,0 +1,38 @@
+/* ###
+ * 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 sarif.io;
+
+import java.io.File;
+import java.io.IOException;
+
+import com.contrastsecurity.sarif.SarifSchema210;
+import com.google.gson.JsonSyntaxException;
+
+public class SarifJacksonIO implements SarifIO {
+
+ public SarifSchema210 readSarif(File file) throws JsonSyntaxException, IOException {
+// ObjectMapper mapper = new ObjectMapper();
+// return mapper.readValue(Files.readString(file.toPath()), SarifSchema210.class);
+ throw new IOException("Jackson not currently supported");
+ }
+
+ public SarifSchema210 readSarif(String str) throws JsonSyntaxException, IOException {
+// ObjectMapper mapper = new ObjectMapper();
+// return mapper.readValue(str, SarifSchema210.class);
+ throw new IOException("Jackson not currently supported");
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/managers/BookmarksSarifMgr.java b/Ghidra/Features/Sarif/src/main/java/sarif/managers/BookmarksSarifMgr.java
new file mode 100644
index 0000000000..be3f713ca6
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/managers/BookmarksSarifMgr.java
@@ -0,0 +1,132 @@
+/* ###
+ * 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 sarif.managers;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import com.google.gson.JsonArray;
+
+import ghidra.app.util.importer.MessageLog;
+import ghidra.program.model.address.Address;
+import ghidra.program.model.address.AddressFormatException;
+import ghidra.program.model.address.AddressIterator;
+import ghidra.program.model.address.AddressOverflowException;
+import ghidra.program.model.address.AddressSetView;
+import ghidra.program.model.listing.Bookmark;
+import ghidra.program.model.listing.BookmarkManager;
+import ghidra.program.model.listing.BookmarkType;
+import ghidra.program.model.listing.Program;
+import ghidra.util.exception.CancelledException;
+import ghidra.util.task.TaskLauncher;
+import ghidra.util.task.TaskMonitor;
+import sarif.SarifProgramOptions;
+import sarif.export.SarifWriterTask;
+import sarif.export.bkmk.SarifBookmarkWriter;
+
+public class BookmarksSarifMgr extends SarifMgr {
+
+ public static String KEY = "BOOKMARKS";
+ public static String SUBKEY = "Bookmark";
+
+ private BookmarkManager bookmarkMgr;
+
+ public BookmarksSarifMgr(Program program, MessageLog log) {
+ super(KEY, program, log);
+ this.bookmarkMgr = program.getBookmarkManager();
+ }
+
+ ////////////////////////////
+ // SARIF READ CURRENT DTD //
+ ////////////////////////////
+
+ @Override
+ public boolean read(Map result, SarifProgramOptions options, TaskMonitor monitor)
+ throws AddressFormatException, CancelledException {
+ processBookmark(result, options == null || options.isOverwriteBookmarkConflicts());
+ return true;
+ }
+
+ private void processBookmark(Map result, boolean overwrite) {
+
+ Address addr = null;
+ try {
+ addr = getLocation(result);
+ } catch (AddressOverflowException e1) {
+ log.appendException(e1);
+ }
+
+ String type = (String) result.getOrDefault("kind", BookmarkType.NOTE);
+ String category = (String) result.getOrDefault("name", "");
+ String comment = (String) result.getOrDefault("comment", "");
+
+ try {
+ boolean hasExistingBookmark = bookmarkMgr.getBookmark(addr, type, category) != null;
+ if (overwrite || !hasExistingBookmark) {
+ bookmarkMgr.setBookmark(addr, type, category, comment);
+ }
+ if (!overwrite && hasExistingBookmark) {
+ log.appendMsg("Conflicting '" + type + "' BOOKMARK ignored at: " + addr);
+ }
+ } catch (Exception e) {
+ log.appendException(e);
+ return;
+ }
+
+ }
+
+
+ /////////////////////////////
+ // SARIF WRITE CURRENT DTD //
+ /////////////////////////////
+
+ void write(JsonArray results, AddressSetView set, TaskMonitor monitor) throws IOException, CancelledException {
+ monitor.setMessage("Writing BOOKMARKS ...");
+ //writeBookmarks(results, set, monitor);
+
+ List request = new ArrayList<>();
+ BookmarkType[] types = bookmarkMgr.getBookmarkTypes();
+ for (int i = 0; i < types.length; i++) {
+ if (monitor.isCancelled()) {
+ throw new CancelledException();
+ }
+ String typeStr = types[i].getTypeString();
+ AddressSetView bmSet = bookmarkMgr.getBookmarkAddresses(typeStr);
+ if (set != null) {
+ bmSet = set.intersect(bmSet);
+ }
+ AddressIterator iter = bmSet.getAddresses(true);
+ while (iter.hasNext()) {
+ Address addr = iter.next();
+ Bookmark[] bookmarks = bookmarkMgr.getBookmarks(addr, typeStr);
+ for (int n = 0; n < bookmarks.length; n++) {
+ request.add(bookmarks[n]);
+ }
+ }
+ }
+
+ writeAsSARIF(request, results);
+ }
+
+ public static void writeAsSARIF(List request, JsonArray results)
+ throws IOException {
+ SarifBookmarkWriter writer = new SarifBookmarkWriter(request, null);
+ new TaskLauncher(new SarifWriterTask(SUBKEY, writer, results), null);
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/managers/CodeSarifMgr.java b/Ghidra/Features/Sarif/src/main/java/sarif/managers/CodeSarifMgr.java
new file mode 100644
index 0000000000..6e0492b856
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/managers/CodeSarifMgr.java
@@ -0,0 +1,167 @@
+/* ###
+ * 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 sarif.managers;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import com.google.gson.JsonArray;
+
+import generic.stl.Pair;
+import ghidra.app.util.importer.MessageLog;
+import ghidra.program.disassemble.Disassembler;
+import ghidra.program.disassemble.DisassemblerMessageListener;
+import ghidra.program.model.address.Address;
+import ghidra.program.model.address.AddressOverflowException;
+import ghidra.program.model.address.AddressRange;
+import ghidra.program.model.address.AddressRangeImpl;
+import ghidra.program.model.address.AddressSet;
+import ghidra.program.model.address.AddressSetView;
+import ghidra.program.model.listing.FlowOverride;
+import ghidra.program.model.listing.Instruction;
+import ghidra.program.model.listing.InstructionIterator;
+import ghidra.program.model.listing.Listing;
+import ghidra.program.model.listing.Program;
+import ghidra.util.exception.CancelledException;
+import ghidra.util.task.TaskLauncher;
+import ghidra.util.task.TaskMonitor;
+import sarif.SarifProgramOptions;
+import sarif.export.SarifWriterTask;
+import sarif.export.code.SarifCodeWriter;
+
+public class CodeSarifMgr extends SarifMgr implements DisassemblerMessageListener {
+
+ public static String KEY = "CODE";
+ public static String SUBKEY = "Code";
+ public static String SUBKEY2 = "Override";
+
+ public CodeSarifMgr(Program program, MessageLog log) {
+ super(KEY, program, log);
+ }
+
+ ////////////////////////////
+ // SARIF READ CURRENT DTD //
+ ////////////////////////////
+
+ @Override
+ public boolean read(Map result, SarifProgramOptions options, TaskMonitor monitor)
+ throws CancelledException {
+
+ AddressSet set = new AddressSet();
+
+ try {
+ getLocations(result, set);
+ } catch (AddressOverflowException e) {
+ log.appendException(e);
+ }
+
+ String msg = (String) result.get("Message");
+
+ AddressSet disset = set.intersect(program.getMemory());
+ if (!disset.equals(set)) {
+ log.appendMsg("Disassembly address set changed to " + disset.toString());
+ }
+ disassemble(disset, monitor);
+
+ if (msg.equals(SUBKEY2)) {
+ Instruction inst = program.getListing().getInstructionAt(set.getMinAddress());
+ String override = (String) result.get("kind");
+ inst.setFlowOverride(FlowOverride.valueOf(override));
+ }
+ return true;
+ }
+
+ public void disassemble(AddressSet set, TaskMonitor monitor) {
+ Disassembler disassembler = Disassembler.getDisassembler(program, monitor, this);
+ try {
+ Listing listing = program.getListing();
+ while (!set.isEmpty() && !monitor.isCancelled()) {
+ Address start = set.getMinAddress();
+ AddressSet disset = disassembler.disassemble(start, set);
+ if (disset.isEmpty()) {
+ Instruction instr = listing.getInstructionAt(start);
+ if (instr == null) {
+ AddressRange skipRange = set.iterator().next();
+ log.appendMsg("Expected valid Instruction at " + start);
+ log.appendMsg("...skipping code range " + skipRange.getMinAddress() + " to "
+ + skipRange.getMaxAddress());
+ set.delete(skipRange);
+ } else {
+ set.deleteRange(instr.getMinAddress(), instr.getMaxAddress());
+ }
+ } else {
+ set.delete(disset);
+ }
+ }
+ } catch (Exception e) {
+ log.appendMsg("Error during disassembly: " + e.getMessage());
+ }
+ }
+
+ @Override
+ public void disassembleMessageReported(String msg) {
+ log.appendMsg("Error from disassembler: " + msg);
+ }
+
+ /////////////////////////////
+ // SARIF WRITE CURRENT DTD //
+ /////////////////////////////
+
+ void write(JsonArray results, AddressSetView set, TaskMonitor monitor) throws IOException, CancelledException {
+ monitor.setMessage("Writing CODE ...");
+
+ List request = new ArrayList<>();
+ List> requestOverride = new ArrayList<>();
+ InstructionIterator it = null;
+ if (set == null) {
+ it = program.getListing().getInstructions(true);
+ } else {
+ it = program.getListing().getInstructions(set, true);
+ }
+
+ while (it.hasNext()) {
+ Instruction inst = it.next();
+ Address start = inst.getMinAddress();
+ Address end = inst.getMaxAddress();
+ while (it.hasNext()) {
+ inst = it.next();
+ FlowOverride override = inst.getFlowOverride();
+ if (!override.equals(FlowOverride.NONE)) {
+ requestOverride.add(new Pair(inst, override));
+ }
+
+ if (!end.isSuccessor(inst.getMinAddress())) {
+ request.add(new AddressRangeImpl(start, end));
+ start = inst.getMinAddress();
+ }
+ end = inst.getMaxAddress();
+ monitor.checkCancelled();
+ }
+ request.add(new AddressRangeImpl(start, end));
+ }
+
+ writeAsSARIF(request, requestOverride, results);
+ }
+
+ public static void writeAsSARIF(List request, List> request2,
+ JsonArray results) throws IOException {
+ SarifCodeWriter writer = new SarifCodeWriter(request, request2, null);
+ new TaskLauncher(new SarifWriterTask(SUBKEY, writer, results), null);
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/managers/CommentsSarifMgr.java b/Ghidra/Features/Sarif/src/main/java/sarif/managers/CommentsSarifMgr.java
new file mode 100644
index 0000000000..5d69f48f82
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/managers/CommentsSarifMgr.java
@@ -0,0 +1,217 @@
+/* ###
+ * 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 sarif.managers;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import com.google.gson.JsonArray;
+
+import generic.stl.Pair;
+import ghidra.app.util.CommentTypes;
+import ghidra.app.util.importer.MessageLog;
+import ghidra.program.model.address.Address;
+import ghidra.program.model.address.AddressFormatException;
+import ghidra.program.model.address.AddressIterator;
+import ghidra.program.model.address.AddressSetView;
+import ghidra.program.model.listing.CodeUnit;
+import ghidra.program.model.listing.CodeUnitIterator;
+import ghidra.program.model.listing.Instruction;
+import ghidra.program.model.listing.Program;
+import ghidra.util.exception.CancelledException;
+import ghidra.util.task.TaskLauncher;
+import ghidra.util.task.TaskMonitor;
+import sarif.SarifProgramOptions;
+import sarif.export.SarifWriterTask;
+import sarif.export.comments.SarifCommentWriter;
+
+/**
+ * SARIF manager for all types of comments.
+ */
+public class CommentsSarifMgr extends SarifMgr {
+
+ public static String KEY = "COMMENTS";
+ public static String SUBKEY = "Comment";
+
+ public static int[] COMMENT_TYPES;
+ public static String[] COMMENT_TAGS;
+
+ public static String[] COMMENT_TAGS2 = { "end-of-line", "pre", "post", "plate", "repeatable" };
+
+ static {
+ COMMENT_TYPES = CommentTypes.getTypes();
+
+ COMMENT_TAGS = new String[COMMENT_TYPES.length];
+ for (int i = 0; i < COMMENT_TAGS.length; i++) {
+
+ switch (COMMENT_TYPES[i]) {
+ case CodeUnit.PRE_COMMENT:
+ COMMENT_TAGS[i] = "pre";
+ break;
+ case CodeUnit.POST_COMMENT:
+ COMMENT_TAGS[i] = "post";
+ break;
+ case CodeUnit.EOL_COMMENT:
+ COMMENT_TAGS[i] = "end-of-line";
+ break;
+ case CodeUnit.PLATE_COMMENT:
+ COMMENT_TAGS[i] = "plate";
+ break;
+ case CodeUnit.REPEATABLE_COMMENT:
+ COMMENT_TAGS[i] = "repeatable";
+ break;
+ }
+ }
+ }
+
+ CommentsSarifMgr(Program program, MessageLog log) {
+ super(KEY, program, log);
+ }
+
+ ////////////////////////////
+ // SARIF READ CURRENT DTD //
+ ////////////////////////////
+
+ /**
+ * Process the entry point section of the SARIF file.
+ *
+ * @param result sarif reader
+ * @param monitor monitor that can be canceled
+ */
+ @Override
+ public boolean read(Map result, SarifProgramOptions options, TaskMonitor monitor)
+ throws AddressFormatException, CancelledException {
+ processComment(result, result);
+ return true;
+ }
+
+ private void processComment(Map result, Map result2) throws AddressFormatException {
+ try {
+ Address addr = getLocation(result);
+ String typeStr = (String) result.get("kind");
+ String comment = (String) result.get("value");
+ boolean standard = (boolean) result.get("standard");
+ int commentType = getCommentType(typeStr);
+ if (commentType < 0) {
+ log.appendMsg("Unknown comment type: " + typeStr);
+ return;
+ }
+ if (standard) {
+ CodeUnit cu = listing.getCodeUnitContaining(addr);
+ if (cu != null) {
+ // if a comment already exists, then merge...
+ //
+ String currCmt = cu.getComment(commentType);
+ if (currCmt == null || currCmt.length() == 0) {
+ cu.setComment(commentType, comment);
+ } else if (currCmt.indexOf(comment) < 0) {
+ log.appendMsg("Merged " + typeStr + " comment at " + addr);
+ cu.setComment(commentType, currCmt + "\n" + comment);
+ }
+ }
+ } else {
+ String currCmt = listing.getComment(commentType, addr);
+ if (currCmt == null || currCmt.length() == 0) {
+ listing.setComment(addr, commentType, comment);
+ } else if (currCmt.indexOf(comment) < 0) {
+ log.appendMsg("Merged " + typeStr + " comment at " + addr);
+ listing.setComment(addr, commentType, currCmt + "\n" + comment);
+ }
+ }
+ } catch (Exception e) {
+ log.appendException(e);
+ }
+ }
+
+ private int getCommentType(String typeStr) {
+ for (int i = 0; i < COMMENT_TAGS.length; i++) {
+ if (COMMENT_TAGS[i].equals(typeStr)) {
+ return COMMENT_TYPES[i];
+ }
+ }
+ return -1; // unknown comment
+ }
+
+ /////////////////////////////
+ // SARIF WRITE CURRENT DTD //
+ /////////////////////////////
+
+ /**
+ * Write out the SARIF for the external entry points.
+ *
+ * @param results writer for SARIF
+ * @param set address set that is either the entire program or a selection
+ * @param monitor monitor that can be canceled should be written
+ * @throws IOException
+ */
+ void write(JsonArray results, AddressSetView set, TaskMonitor monitor) throws IOException, CancelledException {
+ monitor.setMessage("Writing COMMENTS ...");
+
+ if (set == null) {
+ set = program.getMemory();
+ }
+
+ List>> request0 = new ArrayList<>();
+ CodeUnitIterator iter = listing.getCodeUnitIterator(CodeUnit.COMMENT_PROPERTY, set, true);
+
+ while (iter.hasNext()) {
+ monitor.checkCancelled();
+ CodeUnit cu = iter.next();
+ for (int i = 0; i < COMMENT_TYPES.length; i++) {
+ String[] comments = cu.getCommentAsArray(COMMENT_TYPES[i]);
+ for (String c : comments) {
+ Pair pair = new Pair<>(COMMENT_TAGS[i], c);
+ request0.add(new Pair>(cu, pair));
+ }
+ }
+ }
+
+ writeAsSARIF0(request0, results);
+
+ List>> request1 = new ArrayList<>();
+ for (int i = 0; i < COMMENT_TAGS2.length; i++) {
+ AddressIterator aiter = listing.getCommentAddressIterator(i, set, true);
+ while (aiter.hasNext()) {
+ monitor.checkCancelled();
+ Address a = aiter.next();
+ CodeUnit cu = listing.getCodeUnitContaining(a);
+ if (cu instanceof Instruction && !a.equals(cu.getMinAddress())) {
+ String c = listing.getComment(i, a);
+ Pair pair = new Pair<>(COMMENT_TAGS2[i], c);
+ request1.add(new Pair>(a, pair));
+ }
+ }
+ }
+
+ writeAsSARIF1(request1, results);
+
+ }
+
+ public static void writeAsSARIF0(List>> request, JsonArray results)
+ throws IOException {
+ SarifCommentWriter writer = new SarifCommentWriter(request, null);
+ new TaskLauncher(new SarifWriterTask(SUBKEY, writer, results), null);
+ }
+
+ public static void writeAsSARIF1(List>> request, JsonArray results)
+ throws IOException {
+ SarifCommentWriter writer = new SarifCommentWriter(null, request);
+ new TaskLauncher(new SarifWriterTask(SUBKEY, writer, results), null);
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/managers/DataTypesSarifMgr.java b/Ghidra/Features/Sarif/src/main/java/sarif/managers/DataTypesSarifMgr.java
new file mode 100644
index 0000000000..86b5d49140
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/managers/DataTypesSarifMgr.java
@@ -0,0 +1,708 @@
+/* ###
+ * 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 sarif.managers;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.xml.sax.SAXParseException;
+
+import com.google.gson.JsonArray;
+
+import ghidra.app.util.importer.MessageLog;
+import ghidra.docking.settings.Settings;
+import ghidra.program.model.address.AddressFormatException;
+import ghidra.program.model.data.ArrayDataType;
+import ghidra.program.model.data.CategoryPath;
+import ghidra.program.model.data.CharDataType;
+import ghidra.program.model.data.Composite;
+import ghidra.program.model.data.DataType;
+import ghidra.program.model.data.DataTypeComponent;
+import ghidra.program.model.data.DataTypeConflictHandler;
+import ghidra.program.model.data.DataTypeManager;
+import ghidra.program.model.data.Dynamic;
+import ghidra.program.model.data.Enum;
+import ghidra.program.model.data.EnumDataType;
+import ghidra.program.model.data.FunctionDefinition;
+import ghidra.program.model.data.FunctionDefinitionDataType;
+import ghidra.program.model.data.InvalidDataTypeException;
+import ghidra.program.model.data.LongDoubleDataType;
+import ghidra.program.model.data.PascalString255DataType;
+import ghidra.program.model.data.PascalStringDataType;
+import ghidra.program.model.data.PascalUnicodeDataType;
+import ghidra.program.model.data.PointerDataType;
+import ghidra.program.model.data.Structure;
+import ghidra.program.model.data.StructureDataType;
+import ghidra.program.model.data.TypeDef;
+import ghidra.program.model.data.TypedefDataType;
+import ghidra.program.model.data.Union;
+import ghidra.program.model.data.UnionDataType;
+import ghidra.program.model.data.UnsignedInteger3DataType;
+import ghidra.program.model.listing.Program;
+import ghidra.program.model.symbol.SourceType;
+import ghidra.util.InvalidNameException;
+import ghidra.util.Msg;
+import ghidra.util.exception.CancelledException;
+import ghidra.util.exception.DuplicateNameException;
+import ghidra.util.exception.InvalidInputException;
+import ghidra.util.task.TaskLauncher;
+import ghidra.util.task.TaskMonitor;
+import sarif.SarifProgramOptions;
+import sarif.export.SarifWriterTask;
+import sarif.export.data.SarifDataTypeWriter;
+
+/**
+ * This manager is responsible for reading and writing datatypes in SARIF.
+ */
+public class DataTypesSarifMgr extends SarifMgr {
+
+ public static String KEY = "DATATYPE";
+
+ private final static int MAX_PASSES = 10;
+ private final static int DEFAULT_SIZE = 1;
+
+ private static Map foreignTypedefs = Map.of("ascii", CharDataType.dataType, "string1",
+ PascalString255DataType.dataType, "string2", PascalStringDataType.dataType,
+ // string4 - pascal string with 4-byte length prefix
+ "unicode2", PascalUnicodeDataType.dataType,
+ // unicode4 - pascal unicode string with 4-byte length prefix
+ "tbyte", LongDoubleDataType.dataType, // 10-byte float
+ // oword - 16-byte value
+ // packed real
+ "3byte", UnsignedInteger3DataType.dataType);
+
+ private DataTypeManager dataManager;
+ private DtParser dtParser;
+ private Map dataTypes = new HashMap<>();
+ private Map isPacked = new HashMap<>();
+ private Map packingValue = new HashMap<>();
+
+ /**
+ * Constructs a new root types SARIF manager.
+ *
+ * @param dataManager the root type manager to read from or write to
+ * @param log the message log for recording datatype warnings
+ */
+ public DataTypesSarifMgr(Program program, MessageLog log) {
+ super(KEY, program, log);
+ this.dataManager = program.getListing().getDataTypeManager();
+ }
+
+ ////////////////////////////
+ // SARIF READ CURRENT DTD //
+ ////////////////////////////
+
+ @Override
+ protected void readResults(List> list, SarifProgramOptions options, TaskMonitor monitor)
+ throws AddressFormatException, CancelledException {
+ if (list != null) {
+ monitor.setMessage("Processing " + key + "...");
+ boolean processedAll;
+
+ int pass = 0;
+ do {
+ monitor.setMaximum(list.size() * MAX_PASSES);
+ monitor.checkCancelled();
+ processedAll = true;
+ for (Map result : list) {
+ if (monitor.isCancelled()) {
+ break;
+ }
+ boolean res = read(result, options, monitor);
+ // if (!res) System.err.println(result);
+ processedAll &= res;
+ monitor.increment();
+ }
+ pass++;
+ } while (!processedAll && pass < MAX_PASSES);
+ } else {
+ monitor.setMessage("Skipping over " + key + " ...");
+ }
+ }
+
+ /**
+ * Reads the datatypes encoded in SARIF from the specified SARIF parser and
+ * recreates them in a datatype manager.
+ *
+ * @param result the SARIF parser
+ * @param monitor the task monitor
+ * @throws SAXParseException if an SARIF parse error occurs
+ * @throws CancelledException if the user cancels the read operation
+ */
+ @Override
+ public boolean read(Map result, SarifProgramOptions options, TaskMonitor monitor)
+ throws CancelledException {
+ try {
+ dtParser = new DtParser(dataManager);
+ return process(result);
+ } finally {
+ dataManager.close();
+ dtParser = null;
+ }
+ }
+
+ private boolean process(Map result) {
+ String name = (String) result.get("Message");
+
+ try {
+ if (name.equals("DT.Struct")) {
+ return processStructure(result);
+ }
+ if (name.equals("DT.Union")) {
+ return processUnion(result);
+ }
+ if (name.equals("DT.Enum")) {
+ return processEnum(result);
+ }
+ if (name.equals("DT.Typedef")) {
+ return processTypeDef(result);
+ }
+ if (name.equals("DT.TypedObject")) {
+ return processTypedObject(result);
+ }
+ if (name.equals("DT.Builtin")) {
+ return processBuiltin(result);
+ }
+ if (name.equals("DT.Function")) {
+ return processFunctionDef(result);
+ }
+ log.appendMsg("Unrecognized datatype tag: " + name);
+ } catch (Exception e) {
+ log.appendException(e);
+ }
+ return true;
+ }
+
+ public void addDataType(String key, DataType dt) {
+ Boolean packed = isPacked.get(key);
+ if (packed != null) {
+ Composite composite = (Composite) dt;
+ composite.setPackingEnabled(packed);
+ Integer packVal = packingValue.get(key);
+ if (packVal != null) {
+ composite.setExplicitPackingValue(packVal);
+ }
+ }
+ dataTypes.put(key, dt);
+ dataManager.addDataType(dt, DataTypeConflictHandler.REPLACE_HANDLER);
+ }
+
+ @SuppressWarnings("unchecked")
+ private boolean processFunctionDef(Map result) throws InvalidInputException {
+ boolean processedAll = true;
+ String name = (String) result.get("name");
+ CategoryPath path = getCategoryPath(result);
+ FunctionDefinition fd = new FunctionDefinitionDataType(path, name, dataManager);
+ processSettings(result, fd.getDefaultSettings());
+ fd.setVarArgs((boolean) result.get("hasVarArgs"));
+ fd.setNoReturn((boolean) result.get("hasNoReturn"));
+ fd.setCallingConvention((String) result.get("callingConventionName"));
+
+ dataTypes.put(getPath(fd), fd);
+
+ Map retType = (Map) result.get("retType");
+ DataType returnType = findDataType(retType);
+ if (returnType != null) {
+ fd.setReturnType(returnType);
+ }
+
+ List> params = (List>) result.get("params");
+ for (Map param : params) {
+ processedAll &= processFunctionMembers(param, fd);
+ }
+
+ addDataType(getPath(fd), fd);
+ return processedAll;
+ }
+
+ @SuppressWarnings("unchecked")
+ private boolean processEnum(Map result) {
+ String name = (String) result.get("name");
+ String enuumComment = (String) result.get("comment");
+ CategoryPath cp = getCategoryPath(result);
+ int size = (int) (double) result.get("size");
+
+ EnumDataType enuum = new EnumDataType(cp, name, size, dataManager);
+ processSettings(result, enuum.getDefaultSettings());
+
+ enuum.setDescription(enuumComment);
+ Map constants = (Map) result.get("constants");
+ for (Entry entry : constants.entrySet()) {
+ processEnumMembers(entry, enuum);
+ }
+
+ addDataType(getPath(enuum), enuum);
+ return true;
+ }
+
+ private boolean processTypeDef(Map result) throws InvalidNameException, DuplicateNameException {
+ String name = (String) result.get("name");
+ // String displayName = (String) result.get("displayName");
+ Boolean isAutoNamed = (Boolean) result.get("autoNamed");
+ String typeLoc = (String) result.get("typeLocation");
+ CategoryPath cp = typeLoc == null ? CategoryPath.ROOT : new CategoryPath(typeLoc);
+ DataType dt = findDataType(result);
+ if (dt == null) {
+ log.appendMsg(name + " NOT FOUND");
+ return false;
+ }
+
+ int dtSize = dt.getLength();
+ int size = (int) (double) result.get("size");
+ if (size != -1 && size != dtSize) {
+ log.appendMsg("SIZE=" + result.get("size") + " specified on type-def " + name
+ + " does not agree with length of datatype " + dt.getPathName() + " (" + dtSize + ")");
+ }
+
+ cp = getCategoryPath(result);
+ TypeDef td = new TypedefDataType(cp, name, dt, dataManager);
+ processSettings(result, td.getDefaultSettings());
+ if (isAutoNamed != null && isAutoNamed) {
+ td.enableAutoNaming();
+ }
+ // if (displayName != null) {
+ // td.setName(displayName);
+ // }
+
+ try {
+ if (name.equals(dt.getPathName()) && dt instanceof TypeDef) {
+ td = (TypeDef) dt;
+ }
+ td.setCategoryPath(cp);
+ dt = td;
+ } catch (DuplicateNameException e) {
+ log.appendMsg("Unable to place typedef '" + name + "' in category '" + cp + "'");
+ }
+
+ addDataType(getPath(td), td);
+ return true;
+ }
+
+ @SuppressWarnings("unchecked")
+ private boolean processStructure(Map result) throws InvalidDataTypeException {
+ String name = (String) result.get("name");
+ CategoryPath path = getCategoryPath(result);
+ int size = DEFAULT_SIZE;
+ if (result.get("size") != null) {
+ size = (int) (double) result.get("size");
+ }
+ Structure struct = new StructureDataType(path, name, size, dataManager);
+ processSettings(result, struct.getDefaultSettings());
+
+ String comment = getRegularComment(result);
+ if (comment != null) {
+ struct.setDescription(comment);
+ }
+ Object alignmentMin = result.get("explicitMinimumAlignment");
+ if (alignmentMin != null) {
+ struct.setExplicitMinimumAlignment((int) (double) alignmentMin);
+ }
+
+ String packing = (String) result.get("packed");
+ if (packing != null) {
+ isPacked.put(getPath(struct), Boolean.valueOf(packing));
+ // NB: not this - struct.setPackingEnabled(Boolean.valueOf(packing));
+ Object epval = result.get("explicitPackingValue");
+ if (epval != null) {
+ packingValue.put(getPath(struct), (int) (double) epval);
+ }
+ }
+
+ dataTypes.put(getPath(struct), struct);
+
+ boolean processedAll = true;
+ Map fields = (Map) result.get("fields");
+ for (Entry entry : fields.entrySet()) {
+ processedAll &= processStructMembers(entry, struct);
+ }
+ if (processedAll) {
+ addDataType(getPath(struct), struct);
+ } else {
+ dataTypes.put(getPath(struct), struct);
+ }
+ return processedAll;
+ }
+
+ @SuppressWarnings("unchecked")
+ private boolean processUnion(Map result) throws InvalidDataTypeException {
+ String name = (String) result.get("name");
+ CategoryPath path = getCategoryPath(result);
+ String comment = getRegularComment(result);
+ Union union = new UnionDataType(path, name);
+ processSettings(result, union.getDefaultSettings());
+
+ if (comment != null) {
+ union.setDescription(comment);
+ }
+ Object alignmentMin = result.get("explicitMinimumAlignment");
+ if (alignmentMin != null) {
+ union.setExplicitMinimumAlignment((int) (double) alignmentMin);
+ }
+
+ String packing = (String) result.get("packed");
+ if (packing != null) {
+ isPacked.put(getPath(union), Boolean.valueOf(packing));
+ // NB: not this - struct.setPackingEnabled(Boolean.valueOf(packing));
+ Object epval = result.get("explicitPackingValue");
+ if (epval != null) {
+ packingValue.put(getPath(union), (int) (double) epval);
+ }
+ }
+
+ dataTypes.put(getPath(union), union);
+
+ boolean processedAll = true;
+ Map fields = (Map) result.get("fields");
+ for (Entry entry : fields.entrySet()) {
+ processedAll &= processUnionMembers(entry, union);
+ }
+ if (processedAll) {
+ addDataType(getPath(union), union);
+ } else {
+ dataTypes.put(getPath(union), union);
+ }
+ return processedAll;
+ }
+
+ @SuppressWarnings("unchecked")
+ private boolean processTypedObject(Map result) {
+ // String name = (String) result.get("name");
+ CategoryPath cp = new CategoryPath((String) result.get("typeLocation"));
+ String kind = (String) result.get("kind");
+ int size = (int) (double) result.get("size");
+
+ Map type = (Map) result.get("type");
+ DataType baseType = findDataType(type, cp);
+ if (baseType != null) {
+ if (kind.equals("pointer")) {
+ DataType p = new PointerDataType(baseType, size, dataManager);
+ addDataType(getPath(p), p);
+ return true;
+ }
+ throw new RuntimeException("Unexpected baseType kind=" + kind);
+ }
+ return false;
+ }
+
+ private boolean processBuiltin(Map result) {
+ String name = (String) result.get("name");
+ CategoryPath cp = getCategoryPath(result);
+ DataType dt = findDataType(result, cp);
+ if (dt != null) {
+ addDataType(getPath(cp, name), dt);
+ return true;
+ }
+ return false;
+ }
+
+ private String getRegularComment(Map result) {
+ return (String) result.get("comment");
+ }
+
+ private boolean processFunctionMembers(Map param, FunctionDefinition fn) {
+ DataType dt = findDataType(param);
+ if (dt != null) {
+ int ordinal = (int) (double) param.get("ordinal");
+ String name = (String) param.get("name");
+ String comment = (String) param.get("comment");
+ int size = dt.getLength();
+ if (size <= 0) {
+ size = (int) (double) param.get("size");
+ }
+ fn.replaceArgument(ordinal, name, dt, comment, SourceType.USER_DEFINED);
+ return true;
+ }
+ return false;
+ }
+
+ private void processEnumMembers(Entry entry, Enum enuum) {
+ String entryName = (String) entry.getKey();
+ Long entryValue = (long) (double) entry.getValue();
+ enuum.add(entryName, entryValue, null);
+ }
+
+ @SuppressWarnings("unchecked")
+ private boolean processStructMembers(Entry entry, Structure struct)
+ throws InvalidDataTypeException {
+ boolean processedAll = true;
+ Map field = (Map) entry.getValue();
+ int offset = (int) (double) field.get("offset");
+ Map type = (Map) field.get("type");
+ DataType memberDT = findDataType(type);
+ if (memberDT != null) {
+ processSettings(type, memberDT.getDefaultSettings());
+ if (memberDT instanceof Dynamic dynamicDT) {
+ if (!dynamicDT.canSpecifyLength()) {
+ return false;
+ }
+ } else if (memberDT.getLength() <= 0) {
+ return false;
+ }
+ String memberName = entry.getKey();
+ Boolean noFieldName = (Boolean) field.get("hasNoFieldName");
+ if (noFieldName != null && noFieldName) {
+ memberName = null;
+ }
+ String memberComment = (String) field.get("comment");
+ int compSize = (int) (double) field.get("length");
+
+ // NOTE: Size consistency checking was removed since some types are filled-out
+ // lazily and may not have there ultimate size at this point.
+
+ if (field.get("bitOffset") != null) {
+ int bitOffset = (int) (double) field.get("bitOffset");
+ int bitSize = (int) (double) field.get("bitSize");
+ // NB: we're using "insert" and the team has suggested "add" is a better choice,
+ // but, because of the multi-pass approach, I don't think I can guarantee an
+ // in-order load.
+ DataTypeComponent dtc = struct.insertBitFieldAt(offset, memberDT.getLength(), bitOffset, memberDT,
+ bitSize, memberName, memberComment);
+ processSettings(field, dtc.getDefaultSettings());
+ return processedAll;
+ }
+
+ DataTypeComponent dtc;
+ if (offset == struct.getLength()) {
+ dtc = struct.add(memberDT, compSize, memberName, memberComment);
+ } else {
+ dtc = struct.replaceAtOffset(offset, memberDT, compSize, memberName, memberComment);
+ }
+
+ processSettings(field, dtc.getDefaultSettings());
+ } else {
+ processedAll = type.get("kind").equals("pointer");
+ }
+ return processedAll;
+ }
+
+ @SuppressWarnings("unchecked")
+ private boolean processUnionMembers(Entry entry, Union union) throws InvalidDataTypeException {
+ boolean processedAll = true;
+ Map member = (Map) entry.getValue();
+ String memberName = (String) entry.getKey();
+ Boolean noFieldName = (Boolean) member.get("hasNoFieldName");
+ if (noFieldName != null && noFieldName) {
+ memberName = null;
+ }
+
+ Map type = (Map) member.get("type");
+ DataType memberDT = findDataType(type);
+ if (memberDT != null) {
+ processSettings(type, memberDT.getDefaultSettings());
+ String memberComment = (String) member.get("comment");
+ int dtSize = memberDT.getLength();
+ if (member.get("bitSize") != null) {
+ int bitSize = (int) (double) member.get("bitSize");
+ union.addBitField(memberDT, bitSize, memberName, memberComment);
+ return processedAll;
+ }
+ DataTypeComponent dtc = union.add(memberDT, dtSize, memberName, memberComment);
+ processSettings(member, dtc.getDefaultSettings());
+ }
+ return processedAll;
+ }
+
+ private CategoryPath getCategoryPath(Map result) {
+ String nameSpace = (String) result.get("location");
+ CategoryPath cp = nameSpace == null ? CategoryPath.ROOT : new CategoryPath(nameSpace);
+ return cp;
+ }
+
+ @SuppressWarnings("unchecked")
+ private void processSettings(Map result, Settings defaultSettings) {
+ List> settings = (List>) result.get("settings");
+ if (settings != null) {
+ for (Map map : settings) {
+ String settingName = (String) map.get("name");
+ String settingValue = (String) map.get("value");
+ if (map.get("kind").equals("long")) {
+ long val = 0;
+ try {
+ val = Long.valueOf(settingValue);
+ } catch (NumberFormatException nfe) {
+ Msg.error(this, nfe);
+ }
+ if (!settingName.equals("ptr_type")) {
+ defaultSettings.setLong(settingName, val);
+ }
+ } else {
+ defaultSettings.setString(settingName, settingValue);
+ }
+ }
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private DataType findDataType(Map type) {
+ String loc = (String) type.get("location");
+ if (loc == null) {
+ Map subtype = (Map) type.get("subtype");
+ if (subtype != null) {
+ loc = (String) subtype.get("location");
+ }
+ }
+ return findDataType(type, new CategoryPath(loc));
+ }
+
+ @SuppressWarnings("unchecked")
+ private DataType findDataType(Map type, CategoryPath cp) {
+ String kind = (String) type.get("kind");
+ String name = (String) type.get("name");
+ if (kind == null) {
+ return null;
+ }
+ if (kind.equals("pointer") || (kind.equals("array"))) {
+ DataType byName = findExistingDataType(cp, kind, name);
+ if (byName != null) {
+ if (byName instanceof FunctionDefinition) {
+ return new PointerDataType(byName, dataManager);
+ }
+ return byName;
+ }
+ Map subtype = (Map) type.get("subtype");
+ if (subtype != null) {
+ DataType base = findDataType(subtype, cp);
+ if (base == null) {
+ return null;
+ // throw new RuntimeException("Subtype not found: " + subtype);
+ }
+ if (kind.equals("pointer")) {
+ return new PointerDataType(base, dataManager);
+ } else {
+ int count = (int) (double) type.get("count");
+ return new ArrayDataType(base, count, base.getLength(), dataManager);
+ }
+ }
+ }
+ if (name == null) {
+ return null;
+ }
+ if (kind.equals("typedef")) {
+ Map subtype = (Map) type.get("type");
+ String typeName = (String) type.get("typeName");
+ if (typeName != null) {
+ cp = new CategoryPath((String) type.get("typeLocation"));
+ name = typeName;
+ } else if (subtype != null) {
+ DataType base = findDataType(subtype);
+ if (base == null) {
+ return null;
+ }
+ return base;
+ }
+ }
+ if (kind.equals("bitfield")) {
+ Map subtype = (Map) type.get("type");
+ String typeName = (String) type.get("typeName");
+ if (typeName != null) {
+ cp = new CategoryPath((String) type.get("typeLocation"));
+ name = typeName;
+ } else if (subtype != null) {
+ DataType base = findDataType(subtype);
+ if (base == null) {
+ return null;
+ }
+ return base;
+ }
+ }
+ return findExistingDataType(cp, kind, name);
+ }
+
+ private DataType findExistingDataType(CategoryPath cp, String kind, String name) {
+ DataType dt = dtParser.parseDataType(name, cp, -1);
+ if (dt == null && addForeignTypedefIfNeeded(name)) {
+ dt = dtParser.parseDataType(name, cp, -1);
+ }
+ if (dt != null) {
+ return dt;
+ }
+ dt = dataTypes.get(getPath(cp, name));
+ if (dt == null && kind.equals("typedef")) {
+ dt = dataTypes.get(cp + "/functions/" + name);
+ }
+ if (dt != null) {
+ return dt;
+ }
+ return dataTypes.get("/" + name);
+ }
+
+ private boolean addForeignTypedefIfNeeded(String dtName) {
+ int ptrIndex = dtName.indexOf('*');
+ int index = dtName.indexOf('[');
+ String baseName = dtName.trim();
+ if (index < 0 || index > ptrIndex) {
+ index = ptrIndex;
+ }
+ if (index > 0) {
+ baseName = dtName.substring(0, index).trim();
+ }
+ DataType ourType = foreignTypedefs.get(baseName);
+ if (ourType != null && dataManager.getDataType("/" + baseName) == null) {
+ TypedefDataType newTypedef = new TypedefDataType(CategoryPath.ROOT, baseName, ourType, dataManager);
+ dataManager.resolve(newTypedef, null);
+ return true;
+ }
+ return false;
+ }
+
+ private String getPath(DataType dt) {
+ String displayName = dt.getPathName();
+ String path = dt.getCategoryPath().getPath();
+ if (!path.equals("/")) {
+ path += "/";
+ }
+ return path + displayName;
+ }
+
+ private String getPath(CategoryPath cp, String displayName) {
+ String path = cp.getPath();
+ if (!path.equals("/")) {
+ path += "/";
+ }
+ return path + displayName;
+ }
+
+ /////////////////////////////
+ // SARIF WRITE CURRENT DTD //
+ /////////////////////////////
+
+ /**
+ * Writes datatypes into SARIF using the specified SARIF writer.
+ *
+ * @param results the SARIF writer
+ * @param monitor the task monitor
+ * @throws CancelledException if the user cancels the write operation
+ */
+ public void write(JsonArray results, TaskMonitor monitor) throws IOException, CancelledException {
+ monitor.setMessage("Writing DATA TYPES ...");
+
+ List dataTypeList = new ArrayList<>();
+ dataManager.getAllDataTypes(dataTypeList);
+
+ writeAsSARIF(program, dataTypeList, results);
+ }
+
+ public static void writeAsSARIF(Program program, List dataTypeList, JsonArray results)
+ throws IOException {
+ SarifDataTypeWriter writer = new SarifDataTypeWriter(program.getDataTypeManager(), dataTypeList, null);
+ new TaskLauncher(new SarifWriterTask("DataTypes", writer, results), null);
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/managers/DefinedDataSarifMgr.java b/Ghidra/Features/Sarif/src/main/java/sarif/managers/DefinedDataSarifMgr.java
new file mode 100644
index 0000000000..9052d8e427
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/managers/DefinedDataSarifMgr.java
@@ -0,0 +1,224 @@
+/* ###
+ * 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 sarif.managers;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import com.google.gson.JsonArray;
+
+import ghidra.app.util.importer.MessageLog;
+import ghidra.docking.settings.Settings;
+import ghidra.program.model.address.Address;
+import ghidra.program.model.address.AddressOverflowException;
+import ghidra.program.model.address.AddressSet;
+import ghidra.program.model.address.AddressSetView;
+import ghidra.program.model.data.CategoryPath;
+import ghidra.program.model.data.DataType;
+import ghidra.program.model.data.DataTypeInstance;
+import ghidra.program.model.data.DataTypeManager;
+import ghidra.program.model.listing.CodeUnit;
+import ghidra.program.model.listing.CodeUnitIterator;
+import ghidra.program.model.listing.Data;
+import ghidra.program.model.listing.DataIterator;
+import ghidra.program.model.listing.Listing;
+import ghidra.program.model.listing.Program;
+import ghidra.program.model.mem.DumbMemBufferImpl;
+import ghidra.program.model.util.CodeUnitInsertionException;
+import ghidra.util.exception.CancelledException;
+import ghidra.util.task.TaskLauncher;
+import ghidra.util.task.TaskMonitor;
+import sarif.SarifProgramOptions;
+import sarif.export.SarifWriterTask;
+import sarif.export.dd.SarifDataWriter;
+
+public class DefinedDataSarifMgr extends SarifMgr {
+
+ public static String KEY = "DEFINED_DATA";
+ public static String SUBKEY = "Defined Data";
+
+ DefinedDataSarifMgr(Program program, MessageLog log) {
+ super(KEY, program, log);
+ }
+
+ ////////////////////////////
+ // SARIF READ CURRENT DTD //
+ ////////////////////////////
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public boolean read(Map result, SarifProgramOptions options, TaskMonitor monitor)
+ throws CancelledException {
+
+ Listing listing = program.getListing();
+ DataTypeManager dataManager = program.getDataTypeManager();
+
+ try {
+ DtParser dtParser = new DtParser(dataManager);
+
+ AddressSet set = getLocations(result, null);
+ Address addr = set.getMinAddress();
+ if (addr == null) {
+ return false;
+ }
+ int size = (int) set.getMaxAddress().subtract(addr) + 1;
+
+ String dataTypeName = (String) result.get("typeName");
+ String typeLocation = (String) result.get("typeLocation");
+ CategoryPath path = typeLocation != null ? new CategoryPath(typeLocation) : CategoryPath.ROOT;
+
+ DataType dt = dtParser.parseDataType(dataTypeName, path, size);
+ if (dt == null) {
+ if (dataTypeName.contains(" ")) {
+ dataTypeName = dataTypeName.substring(0, dataTypeName.indexOf(" "));
+ dt = dtParser.parseDataType(dataTypeName, path, size);
+ }
+ }
+ if (dt == null) {
+ log.appendMsg("Defined root: unknown datatype: " + dataTypeName + " in category: " + path);
+ return false;
+ }
+
+ try {
+ if (options == null || options.isOverwriteDataConflicts()) {
+ clearExistingData(addr, size, dt, listing);
+ }
+
+ Data data = listing.createData(addr, dt, size);
+ processSettings(result, data);
+ Map comments = (Map) result.get("nested");
+ processComment(comments, data);
+
+ } catch (CodeUnitInsertionException e) {
+ Data d = listing.getDefinedDataAt(addr);
+ if (d == null || !d.getDataType().isEquivalent(dt)) {
+ log.appendMsg(e.getMessage());
+ }
+ } catch (Exception e) {
+ log.appendException(e);
+ }
+ } catch (AddressOverflowException e1) {
+ log.appendException(e1);
+ }
+ return true;
+ }
+
+ @SuppressWarnings("unchecked")
+ private void processSettings(Map res, Data data) {
+ List> settingsMap = (List>) res.get("settings");
+ if (settingsMap == null) {
+ return;
+ }
+ for (Map map : settingsMap) {
+ Settings settings = data.getDefaultSettings();
+ if (map != null) {
+ String name = (String) map.get("name");
+ String kind = (String) map.get("kind");
+ String value = (String) map.get("value");
+ Object existing = settings.getValue(name);
+ if (existing == null || !existing.toString().equals(value)) {
+ settings.setValue(name, kind.equals("long") ? Long.parseLong(value) : value);
+ }
+ }
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private void processComment(Map c, Data data) {
+ if (c == null || c.isEmpty()) {
+ return;
+ }
+ List> localComments = (List>) c.get("comment");
+ if (localComments != null) {
+ for (Map lmap : localComments) {
+ String comment = (String) lmap.get("comment");
+ int type = (int) (double) lmap.get("commentType");
+ String existing = data.getComment(type);
+ if (existing == null || !existing.equals(comment)) {
+ data.setComment(type, comment);
+ }
+ }
+ }
+ List> localSettings = (List>) c.get("setting");
+ if (localSettings != null) {
+ for (Map lmap : localSettings) {
+ String name = (String) lmap.get("name");
+ String kind = (String) lmap.get("kind");
+ String value = (String) lmap.get("value");
+ Object existing = data.getValue(name);
+ if (existing == null || !existing.toString().equals(value)) {
+ data.setValue(name, kind.equals("long") ? Long.parseLong(value) : value);
+ }
+ }
+ }
+ Map> embedded = (Map>) c.get("embedded");
+ if (embedded != null) {
+ for (String index : embedded.keySet()) {
+ Data component = data.getComponent(Integer.parseInt(index));
+ Map c2 = embedded.get(index);
+ processComment(c2, component);
+ }
+ }
+ }
+
+ private void clearExistingData(Address addr, int size, DataType dt, Listing listing) {
+ DumbMemBufferImpl buf = new DumbMemBufferImpl(program.getMemory(), addr);
+ DataTypeInstance dti = DataTypeInstance.getDataTypeInstance(dt, buf, size, false);
+ if (dti != null) {
+ boolean doClear = false;
+ Address maxAddr = addr.add(dti.getLength() - 1);
+ CodeUnitIterator codeUnits = listing.getCodeUnitIterator(CodeUnit.DEFINED_DATA_PROPERTY, new AddressSet(addr, maxAddr), true);
+ while (codeUnits.hasNext()) {
+ CodeUnit cu = codeUnits.next();
+ if (cu instanceof Data) {
+ if (((Data) cu).isDefined()) {
+ doClear = true;
+ }
+ } else {
+ return; // don't clear instructions
+ }
+ }
+ if (doClear) {
+ listing.clearCodeUnits(addr, maxAddr, false);
+ }
+ }
+ }
+
+ /////////////////////////////
+ // SARIF WRITE CURRENT DTD //
+ /////////////////////////////
+
+ void write(JsonArray results, AddressSetView addrset, TaskMonitor monitor) throws IOException, CancelledException {
+ monitor.setMessage("Writing DATA ...");
+
+ List request = new ArrayList<>();
+ Listing listing = program.getListing();
+ DataIterator iter = listing.getDefinedData(addrset, true);
+ while (iter.hasNext()) {
+ request.add(iter.next());
+ }
+
+ writeAsSARIF(program, request, results);
+ }
+
+ public static void writeAsSARIF(Program program, List request, JsonArray results) throws IOException {
+ SarifDataWriter writer = new SarifDataWriter(request, null);
+ new TaskLauncher(new SarifWriterTask("DefinedData", writer, results), null);
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/managers/DtParser.java b/Ghidra/Features/Sarif/src/main/java/sarif/managers/DtParser.java
new file mode 100644
index 0000000000..55b3e5023b
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/managers/DtParser.java
@@ -0,0 +1,55 @@
+/* ###
+ * 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 sarif.managers;
+
+import ghidra.program.model.data.CategoryPath;
+import ghidra.program.model.data.DataType;
+import ghidra.program.model.data.DataTypeManager;
+import ghidra.program.model.data.InvalidDataTypeException;
+import ghidra.util.data.DataTypeParser;
+import ghidra.util.data.DataTypeParser.AllowedDataTypes;
+import ghidra.util.exception.CancelledException;
+
+/**
+ * DtParser
+ */
+class DtParser {
+
+ private DataTypeParser parser;
+
+ DtParser(DataTypeManager dtManager) {
+ this.parser = new DataTypeParser(dtManager, dtManager, null, AllowedDataTypes.DYNAMIC);
+ }
+
+ /**
+ * Parse the specified dtName within the specified category.
+ * @param dtName
+ * @param category
+ * @param size optional root-type size, or -1 for unspecified
+ * @return root-type if found, or null if not found
+ */
+ DataType parseDataType(String dtName, CategoryPath category, int size) {
+ DataType dt;
+ try {
+ dt = parser.parse(dtName, category);
+ }
+ catch (InvalidDataTypeException | CancelledException e) {
+ return null;
+ }
+ return dt;
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/managers/EquatesSarifMgr.java b/Ghidra/Features/Sarif/src/main/java/sarif/managers/EquatesSarifMgr.java
new file mode 100644
index 0000000000..2a1fa98209
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/managers/EquatesSarifMgr.java
@@ -0,0 +1,118 @@
+/* ###
+ * 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 sarif.managers;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import com.google.gson.JsonArray;
+
+import ghidra.app.util.importer.MessageLog;
+import ghidra.program.model.address.AddressSetView;
+import ghidra.program.model.listing.Program;
+import ghidra.program.model.symbol.Equate;
+import ghidra.program.model.symbol.EquateTable;
+import ghidra.util.exception.CancelledException;
+import ghidra.util.exception.DuplicateNameException;
+import ghidra.util.task.TaskLauncher;
+import ghidra.util.task.TaskMonitor;
+import sarif.SarifProgramOptions;
+import sarif.export.SarifWriterTask;
+import sarif.export.equates.SarifEquateWriter;
+
+/**
+ * SARIF manager for Equates.
+ */
+public class EquatesSarifMgr extends SarifMgr {
+
+ public static String KEY = "EQUATES";
+ public static String SUBKEY = "Equate";
+
+ private EquateTable equateTable;
+
+ EquatesSarifMgr(Program program, MessageLog log) {
+ super(KEY, program, log);
+ this.equateTable = program.getEquateTable();
+ }
+
+ ////////////////////////////
+ // SARIF READ CURRENT DTD //
+ ////////////////////////////
+
+ /**
+ * Process the entry point section of the SARIF file.
+ *
+ * @param result sarif reader
+ * @param monitor monitor that can be canceled
+ */
+ @Override
+ public boolean read(Map result, SarifProgramOptions options, TaskMonitor monitor)
+ throws CancelledException {
+ processEquate(result, monitor);
+ return true;
+ }
+
+ private void processEquate(Map result, TaskMonitor monitor) {
+ String name = (String) result.get("name");
+ long value = (long) (double) result.get("value");
+
+ try {
+ equateTable.createEquate(name, value);
+ } catch (DuplicateNameException e) {
+ Equate eq = equateTable.getEquate(name);
+ long prevVal = eq.getValue();
+ if (prevVal != value) {
+ log.appendMsg("Cannot create equate [" + name + "] with value [" + value
+ + "]; previously defined with value [" + prevVal + "]");
+ }
+ } catch (Exception e) {
+ log.appendException(e);
+ }
+ }
+
+ /////////////////////////////
+ // SARIF WRITE CURRENT DTD //
+ /////////////////////////////
+
+ /**
+ * Write out the SARIF for the Equates.
+ *
+ * @param results writer for SARIF
+ * @param set address set that is either the entire program or a selection
+ * @param monitor monitor that can be canceled should be written
+ * @throws IOException
+ */
+ void write(JsonArray results, AddressSetView set, TaskMonitor monitor) throws IOException, CancelledException {
+ monitor.setMessage("Writing EQUATES ...");
+
+ List request = new ArrayList<>();
+ Iterator iter = equateTable.getEquates();
+ while (iter.hasNext()) {
+ request.add(iter.next());
+ }
+
+ writeAsSARIF(request, results);
+ }
+
+ public static void writeAsSARIF(List request, JsonArray results) throws IOException {
+ SarifEquateWriter writer = new SarifEquateWriter(request, null);
+ new TaskLauncher(new SarifWriterTask(SUBKEY, writer, results), null);
+ }
+
+}
diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/managers/ExtEntryPointSarifMgr.java b/Ghidra/Features/Sarif/src/main/java/sarif/managers/ExtEntryPointSarifMgr.java
new file mode 100644
index 0000000000..9b4f605801
--- /dev/null
+++ b/Ghidra/Features/Sarif/src/main/java/sarif/managers/ExtEntryPointSarifMgr.java
@@ -0,0 +1,106 @@
+/* ###
+ * 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 sarif.managers;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import com.google.gson.JsonArray;
+
+import ghidra.app.util.importer.MessageLog;
+import ghidra.program.model.address.Address;
+import ghidra.program.model.address.AddressFormatException;
+import ghidra.program.model.address.AddressIterator;
+import ghidra.program.model.address.AddressSetView;
+import ghidra.program.model.listing.Program;
+import ghidra.program.model.symbol.SymbolTable;
+import ghidra.util.exception.CancelledException;
+import ghidra.util.task.TaskLauncher;
+import ghidra.util.task.TaskMonitor;
+import sarif.SarifProgramOptions;
+import sarif.export.SarifWriterTask;
+import sarif.export.ep.SarifEntryPointWriter;
+
+/**
+ * SARIF manager for External Entry Points.
+ */
+public class ExtEntryPointSarifMgr extends SarifMgr {
+
+ public static String KEY = "ENTRY_POINTS";
+ public static String SUBKEY = "Entry Point";
+
+ private SymbolTable symbolTable;
+
+ ExtEntryPointSarifMgr(Program program, MessageLog log) {
+ super(KEY, program, log);
+ symbolTable = program.getSymbolTable();
+ }
+
+ ////////////////////////////
+ // SARIF READ CURRENT DTD //
+ ////////////////////////////
+
+ /**
+ * Process the entry point section of the SARIF file.
+ *
+ * @param result sarif
+ * @param monitor monitor that can be canceled
+ */
+ @Override
+ public boolean read(Map result, SarifProgramOptions options, TaskMonitor monitor)
+ throws AddressFormatException, CancelledException {
+ try {
+ Address addr = getLocation(result);
+ symbolTable.addExternalEntryPoint(addr);
+ return true;
+ } catch (Exception e) {
+ log.appendException(e);
+ }
+ return false;
+ }
+
+ /////////////////////////////
+ // SARIF WRITE CURRENT DTD //
+ /////////////////////////////
+
+ /**
+ * Write out the SARIF for the external entry points.
+ *
+ * @param results writer for SARIF
+ * @param set address set that is either the entire program or a selection
+ * @param monitor monitor that can be canceled should be written
+ * @throws IOException
+ */
+ void write(JsonArray results, AddressSetView set, TaskMonitor monitor) throws IOException, CancelledException {
+ monitor.setMessage("Writing ENTRY POINTS ...");
+
+ List