diff --git a/DevGuide.md b/DevGuide.md
index 09f21da4d0..d424e55fe7 100644
--- a/DevGuide.md
+++ b/DevGuide.md
@@ -34,7 +34,7 @@ You may not need all of these, depending on which portions you are building or d
- https://adoptium.net/releases.html?variant=openjdk11&jvmVariant=hotspot
- Amazon Corretto
- https://docs.aws.amazon.com/corretto/latest/corretto-11-ug/downloads-list.html
-* Gradle 6.4+ or 7.x
+* Gradle 6.8+ or 7.x
- https://gradle.org/releases/
* A C/C++ compiler - We use GCC on Linux, Xcode (Clang) on macOS, and Visual Studio (2017 or later) on Windows.
- https://gcc.gnu.org/
diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/pty/ssh/GhidraSshPtyFactory.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/pty/ssh/GhidraSshPtyFactory.java
index 68fdb1954e..21688cabfe 100644
--- a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/pty/ssh/GhidraSshPtyFactory.java
+++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/pty/ssh/GhidraSshPtyFactory.java
@@ -17,6 +17,7 @@ package agent.gdb.pty.ssh;
import java.io.IOException;
import java.util.Objects;
+import java.util.concurrent.CancellationException;
import javax.swing.JOptionPane;
@@ -28,7 +29,8 @@ import com.jcraft.jsch.ConfigRepository.Config;
import agent.gdb.pty.PtyFactory;
import docking.DockingWindowManager;
import docking.widgets.PasswordDialog;
-import ghidra.util.*;
+import ghidra.util.Msg;
+import ghidra.util.StringUtilities;
public class GhidraSshPtyFactory implements PtyFactory {
private static final String TITLE = "GDB via SSH";
@@ -209,6 +211,10 @@ public class GhidraSshPtyFactory implements PtyFactory {
return session;
}
catch (JSchException e) {
+ if (e.getMessage().equals("Auth cancel")) {
+ Msg.error(this, "SSH connection canceled");
+ throw new CancellationException("SSH connection canceled");
+ }
Msg.error(this, "SSH connection error");
throw new IOException("SSH connection error", e);
}
diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/ExtendedFlatProgramAPI.java b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/ExtendedFlatProgramAPI.java
index c2791db137..9f81ee502f 100644
--- a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/ExtendedFlatProgramAPI.java
+++ b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/ExtendedFlatProgramAPI.java
@@ -1333,7 +1333,7 @@ public class ExtendedFlatProgramAPI extends FlatProgramAPI {
commaIndex--;
}
- String shortenedName = className.substring(0, nextComma) + " ...>";
+ String shortenedName = className.substring(0, nextComma) + "...>";
return shortenedName;
}
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/flow.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/flow.cc
index 9421da8c96..dac7169ccd 100644
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/flow.cc
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/flow.cc
@@ -755,6 +755,8 @@ void FlowInfo::generateOps(void)
addrlist.push_back(data.getAddress());
while(!addrlist.empty()) // Recovering as much as possible except jumptables
fallthru();
+ if (hasInject())
+ injectPcode();
do {
bool collapsed_jumptable = false;
while(!tablelist.empty()) { // For each jumptable found
diff --git a/Ghidra/Features/PDB/developer_scripts/PdbQueryActivator.java b/Ghidra/Features/PDB/developer_scripts/PdbQueryActivator.java
new file mode 100644
index 0000000000..3eab2bce1f
--- /dev/null
+++ b/Ghidra/Features/PDB/developer_scripts/PdbQueryActivator.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.
+ */
+import org.osgi.framework.BundleContext;
+
+import ghidra.app.plugin.core.osgi.GhidraBundleActivator;
+import pdbquery.PdbFactory;
+
+/**
+ * Activator class for the PdbQuery bundle of scripts. On "stop," calls method to close all PDBs.
+ */
+public class PdbQueryActivator extends GhidraBundleActivator {
+ @Override
+ protected void start(BundleContext bc, Object api) {
+ // purposefully empty
+ }
+
+ @Override
+ protected void stop(BundleContext bc, Object api) {
+ PdbFactory.closeAllPdbs(null);
+ }
+
+}
diff --git a/Ghidra/Features/PDB/developer_scripts/PdbQueryCloseAllScript.java b/Ghidra/Features/PDB/developer_scripts/PdbQueryCloseAllScript.java
new file mode 100644
index 0000000000..5d8460b123
--- /dev/null
+++ b/Ghidra/Features/PDB/developer_scripts/PdbQueryCloseAllScript.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.
+ */
+// Closes all PDBs opened in the PdbQuery package.
+//
+//@category PDB
+
+import ghidra.app.script.GhidraScript;
+import pdbquery.PdbFactory;
+
+public class PdbQueryCloseAllScript extends GhidraScript {
+
+ @Override
+ protected void run() throws Exception {
+ PdbFactory.closeAllPdbs(this);
+ }
+}
diff --git a/Ghidra/Features/PDB/developer_scripts/PdbQueryCloseScript.java b/Ghidra/Features/PDB/developer_scripts/PdbQueryCloseScript.java
new file mode 100644
index 0000000000..b1f5431849
--- /dev/null
+++ b/Ghidra/Features/PDB/developer_scripts/PdbQueryCloseScript.java
@@ -0,0 +1,43 @@
+/* ###
+ * IP: GHIDRA
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// Closes a user-selected PDB that was opened in the PdbQuery package.
+//
+//@category PDB
+
+import java.util.List;
+
+import ghidra.app.script.GhidraScript;
+import pdbquery.PdbFactory;
+import pdbquery.PdbFactory.PdbInfo;
+
+public class PdbQueryCloseScript extends GhidraScript {
+
+ @Override
+ protected void run() throws Exception {
+
+ List orderedPdbInfo = PdbFactory.getPdbInfo();
+ if (orderedPdbInfo.isEmpty()) {
+ println("There are no open PDBs. Run " + PdbQueryOpenScript.class.getSimpleName() +
+ " to open a PDB.");
+ return;
+ }
+ PdbInfo lastPdbInfo = PdbFactory.getLastPdbInfoByScriptClass(getClass());
+
+ PdbInfo choice = askChoice("Choose PDB to Close", "PDB Info", orderedPdbInfo, lastPdbInfo);
+
+ PdbFactory.closePdb(this, choice.getFilename());
+ }
+}
diff --git a/Ghidra/Features/PDB/developer_scripts/PdbQueryDatatypeScript.java b/Ghidra/Features/PDB/developer_scripts/PdbQueryDatatypeScript.java
new file mode 100644
index 0000000000..99eeb910e2
--- /dev/null
+++ b/Ghidra/Features/PDB/developer_scripts/PdbQueryDatatypeScript.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.
+ */
+// Queries a PDB in PdbQuery package for data and item type records that contain the search string.
+//
+//@category PDB
+
+import java.util.List;
+
+import ghidra.app.script.GhidraScript;
+import ghidra.app.util.bin.format.pdb2.pdbreader.AbstractPdb;
+import pdbquery.PdbFactory;
+import pdbquery.PdbFactory.PdbInfo;
+import pdbquery.PdbQuery;
+
+public class PdbQueryDatatypeScript extends GhidraScript {
+
+ @Override
+ protected void run() throws Exception {
+
+ List orderedPdbInfo = PdbFactory.getPdbInfo();
+ if (orderedPdbInfo.isEmpty()) {
+ println("There are no open PDBs. Run " + PdbQueryOpenScript.class.getSimpleName() +
+ " to open a PDB.");
+ return;
+ }
+ PdbInfo lastPdbInfo = PdbFactory.getLastPdbInfoByScriptClass(getClass());
+ PdbInfo choice = askChoice("Choose PDB to query", "PDB Info", orderedPdbInfo, lastPdbInfo);
+
+ AbstractPdb pdb = choice.getPdb();
+
+ String searchString = askString("Enter Search String", "String");
+ println("Searching " + choice.getFilename() + " for: " + searchString);
+
+ PdbQuery.searchDataTypes(this, pdb, searchString);
+ PdbQuery.searchItemTypes(this, pdb, searchString);
+ }
+
+}
diff --git a/Ghidra/Features/PDB/developer_scripts/PdbQueryOpenScript.java b/Ghidra/Features/PDB/developer_scripts/PdbQueryOpenScript.java
new file mode 100644
index 0000000000..3be06c4515
--- /dev/null
+++ b/Ghidra/Features/PDB/developer_scripts/PdbQueryOpenScript.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.
+ */
+// Opens a PDB for the PdbQuery package.
+//
+//@category PDB
+
+import java.io.File;
+
+import org.apache.commons.lang3.StringUtils;
+
+import ghidra.app.script.GhidraScript;
+import ghidra.app.util.bin.format.pdb2.pdbreader.PdbIdentifiers;
+import pdb.PdbUtils;
+import pdbquery.PdbFactory;
+
+public class PdbQueryOpenScript extends GhidraScript {
+
+ @Override
+ protected void run() throws Exception {
+ File pdbFile = askFile("Choose a PDB file", "OK");
+ if (pdbFile == null) {
+ println("Aborting: no file chosen.");
+ return;
+ }
+
+ String pdbFilename = pdbFile.getAbsolutePath();
+
+ if (!pdbFile.exists()) {
+ println("Aborting: " + pdbFilename + " is not a valid file.");
+ return;
+ }
+ if (!StringUtils.endsWithIgnoreCase(pdbFilename, ".pdb")) {
+ println("Aborting: filename missing .pdb extension: " + pdbFilename);
+ return;
+ }
+
+ PdbIdentifiers identifiers = PdbUtils.getPdbIdentifiers(pdbFile, monitor);
+
+ String fileAndIdentifiers =
+ pdbFilename + ", " + identifiers + " (File, GUID/Signature, Age, Version, Processor)";
+ if (!askYesNo("Confirm Load", fileAndIdentifiers)) {
+ println("Aborting: " + pdbFilename + " not confirmed.");
+ return;
+ }
+
+ PdbFactory.openPdb(this, pdbFilename, monitor);
+ println("PDB Opened: " + fileAndIdentifiers);
+ }
+}
diff --git a/Ghidra/Features/PDB/developer_scripts/PdbQuerySymbolScript.java b/Ghidra/Features/PDB/developer_scripts/PdbQuerySymbolScript.java
new file mode 100644
index 0000000000..285ed1fe19
--- /dev/null
+++ b/Ghidra/Features/PDB/developer_scripts/PdbQuerySymbolScript.java
@@ -0,0 +1,49 @@
+/* ###
+ * 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.
+ */
+// Queries a PDB in PdbQuery package for Symbol records that contain the search string.
+//
+//@category PDB
+
+import java.util.List;
+
+import ghidra.app.script.GhidraScript;
+import ghidra.app.util.bin.format.pdb2.pdbreader.AbstractPdb;
+import pdbquery.PdbFactory;
+import pdbquery.PdbFactory.PdbInfo;
+import pdbquery.PdbQuery;
+
+public class PdbQuerySymbolScript extends GhidraScript {
+
+ @Override
+ protected void run() throws Exception {
+
+ List orderedPdbInfo = PdbFactory.getPdbInfo();
+ if (orderedPdbInfo.isEmpty()) {
+ println("There are no open PDBs. Run " + PdbQueryOpenScript.class.getSimpleName() +
+ " to open a PDB.");
+ return;
+ }
+ PdbInfo lastPdbInfo = PdbFactory.getLastPdbInfoByScriptClass(getClass());
+ PdbInfo choice = askChoice("Choose PDB to query", "PDB Info", orderedPdbInfo, lastPdbInfo);
+
+ AbstractPdb pdb = choice.getPdb();
+
+ String searchString = askString("Enter Search String", "String");
+ println("Searching " + choice.getFilename() + " for: " + searchString);
+
+ PdbQuery.searchSymbols(this, pdb, searchString);
+ }
+}
diff --git a/Ghidra/Features/PDB/developer_scripts/pdbquery/PdbFactory.java b/Ghidra/Features/PDB/developer_scripts/pdbquery/PdbFactory.java
new file mode 100644
index 0000000000..8c922c8516
--- /dev/null
+++ b/Ghidra/Features/PDB/developer_scripts/pdbquery/PdbFactory.java
@@ -0,0 +1,242 @@
+/* ###
+ * 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 pdbquery;
+
+import java.io.IOException;
+import java.util.*;
+import java.util.Map.Entry;
+
+import ghidra.app.script.GhidraScript;
+import ghidra.app.util.bin.format.pdb2.pdbreader.*;
+import ghidra.util.Msg;
+import ghidra.util.exception.CancelledException;
+import ghidra.util.task.TaskMonitor;
+
+/**
+ * Helper class for the PdbQuery set of scripts that allows the PdbQuery scripts to manage the
+ * opening, holding open (caching), and closing of PDB files so that multiple user queries can
+ * be run against the PDBs. Without this notion, if a user wanted to query a PDB with multiple
+ * runs of a query scripts, each run would have to, once again, open and parse the PDB file
+ * that is desired, which could take minutes or longer.
+ */
+public class PdbFactory {
+
+ private static TreeMap pdbInfoByFile = new TreeMap<>();
+ private static Map, PdbInfo> pdbInfoByScriptClass =
+ new HashMap<>();
+
+ /**
+ * Opens and retains reference to the PDB file specified. Must call
+ * {@link #closePdb(GhidraScript, String)} to close the PDB and remove it from the map; can
+ * alternatively use {@link #closeAllPdbs(GhidraScript)} to close all PDBs in the map.
+ * @param script script for which we are working
+ * @param filename name of PDB file to open and load into map
+ * @param monitor task monitor
+ * @return PDB associated with the filename
+ * @throws CancelledException upon user cancellation
+ * @throws PdbException upon issues parsing the PDB
+ */
+ public static PdbInfo openPdb(GhidraScript script, String filename, TaskMonitor monitor)
+ throws CancelledException, PdbException {
+ PdbInfo pdbInfo = pdbInfoByFile.get(filename);
+ if (pdbInfo != null) {
+ return pdbInfo;
+ }
+
+ println(script, "Opening PDB: " + filename);
+
+ try {
+ AbstractPdb pdb = PdbParser.parse(filename, new PdbReaderOptions(), monitor);
+ PdbIdentifiers identifiers = pdb.getIdentifiers();
+ pdb.deserialize(monitor);
+ PdbReaderMetrics metrics = pdb.getPdbReaderMetrics();
+ pdbInfo = new PdbInfo(filename, identifiers, pdb, metrics);
+ pdbInfoByFile.put(filename, pdbInfo);
+ println(script, "\n" + metrics.getPostProcessingReport());
+ return pdbInfo;
+ }
+ catch (IOException ioe) {
+ println(script, ioe.getMessage());
+ Msg.debug(null, ioe.getMessage());
+ }
+ return null;
+ }
+
+ /**
+ * Closes and unloads the PDB file from the map. Not removed from map if IOException.
+ * @param script script for which we are working
+ * @param filename filename of the PDB file
+ * @return true if successfully closed and removed from the map; false if not found in the map
+ * or if problem closing the PDB.
+ */
+ public static boolean closePdb(GhidraScript script, String filename) {
+ boolean success = closePdbInternal(script, filename);
+ if (success) {
+ pdbInfoByFile.remove(filename);
+ }
+ return success;
+ }
+
+ /**
+ * Closes and unloads the PDB file from the map. Not removed from map if IOException.
+ * @param script script for which we are working
+ * @return true if all PDBs were successfully closed and unloaded from the map
+ */
+ public static boolean closeAllPdbs(GhidraScript script) {
+ boolean allUnloaded = true;
+ Iterator> iterator = pdbInfoByFile.entrySet().iterator();
+ while (iterator.hasNext()) {
+ Entry entry = iterator.next();
+ String filename = entry.getKey();
+ boolean success = closePdbInternal(script, filename);
+ if (success) {
+ iterator.remove();
+ }
+ allUnloaded &= success;
+ }
+ return allUnloaded;
+ }
+
+ private static boolean closePdbInternal(GhidraScript script, String filename) {
+ PdbInfo pdbInfo = pdbInfoByFile.get(filename);
+ AbstractPdb pdb = pdbInfo.getPdb();
+ if (pdb != null) {
+ try {
+ pdb.close();
+ String message = "PDB Closed: " + filename;
+ println(script, message);
+ }
+ catch (IOException ioe) {
+ println(script, ioe.getMessage());
+ Msg.info(null, ioe.getMessage());
+ return false;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns list of PDB information in alphabetical order by filename.
+ * @return the list
+ */
+ public static List getPdbInfo() {
+ List orderedPdbInfo = new ArrayList<>();
+ for (String name : pdbInfoByFile.navigableKeySet()) {
+ orderedPdbInfo.add(pdbInfoByFile.get(name));
+ }
+ return orderedPdbInfo;
+ }
+
+ /**
+ * Sets the cache PdbInfo value for the class argument
+ * @param clazz the class for which to cache the value
+ * @param pdbInfo the PdbInfo value to cache.
+ */
+ public static void setLastPdbInfoByScriptClass(Class extends GhidraScript> clazz,
+ PdbInfo pdbInfo) {
+ pdbInfoByScriptClass.put(clazz, pdbInfo);
+ }
+
+ /**
+ * Returns the PdbInfo cached for the class argument.
+ * @param clazz the class of the script used to look up the cached value for return
+ * @return the PdbInfo
+ */
+ public static PdbInfo getLastPdbInfoByScriptClass(Class extends GhidraScript> clazz) {
+ return pdbInfoByScriptClass.get(clazz);
+ }
+
+ /**
+ * Method for outputting a message to the console (if script is not null); otherwise outputs
+ * the message to Msg.info().
+ * @param script the script
+ * @param message the message to output to the console
+ */
+ private static void println(GhidraScript script, String message) {
+ if (script != null) {
+ script.println(message);
+ }
+ else {
+ Msg.info(PdbFactory.class, message);
+ }
+ }
+
+ /**
+ * Information about a PDB used for specifying and uniquely identifying a PDB along with the
+ * parsed PDB itself and the PDB parsing metrics generated during the parse.
+ */
+ public static class PdbInfo {
+ private String filename; // absolute pathname
+ private PdbIdentifiers identifiers;
+ private AbstractPdb pdb;
+ private PdbReaderMetrics metrics;
+
+ /**
+ * Constructor.
+ * @param filename PDB filename in absolute pathname format
+ * @param identifiers identifiers used to help identify versions of the PDB
+ * @param pdb the parsed PDB
+ * @param metrics the PDB metrics generated when the PDB was opened and parsed
+ */
+ PdbInfo(String filename, PdbIdentifiers identifiers, AbstractPdb pdb,
+ PdbReaderMetrics metrics) {
+ this.filename = filename;
+ this.identifiers = identifiers;
+ this.pdb = pdb;
+ this.metrics = metrics;
+ }
+
+ /**
+ * Returns the PDB filename in absolute path format
+ * @return the filename
+ */
+ public String getFilename() {
+ return filename;
+ }
+
+ /**
+ * Returns the parsed PDB
+ * @return the parsed PDB
+ */
+ public AbstractPdb getPdb() {
+ return pdb;
+ }
+
+ /**
+ * Returns PDB identifiers that help specify its version
+ * @return the identifiers
+ */
+ public PdbIdentifiers getIdentifiers() {
+ return identifiers;
+ }
+
+ /**
+ * Returns the metrics generated during PDB parsing
+ * @return the metrics
+ */
+ public PdbReaderMetrics getPdbReaderMetrics() {
+ return metrics;
+ }
+
+ @Override
+ // The PDB and parsing metrics are purposefully not included in this output.
+ public String toString() {
+ return filename + "; " + identifiers;
+ }
+
+ }
+}
diff --git a/Ghidra/Features/PDB/developer_scripts/pdbquery/PdbQuery.java b/Ghidra/Features/PDB/developer_scripts/pdbquery/PdbQuery.java
new file mode 100644
index 0000000000..5a8adbdc55
--- /dev/null
+++ b/Ghidra/Features/PDB/developer_scripts/pdbquery/PdbQuery.java
@@ -0,0 +1,251 @@
+/* ###
+ * 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 pdbquery;
+
+import java.util.Map;
+
+import ghidra.app.script.GhidraScript;
+import ghidra.app.util.bin.format.pdb2.pdbreader.*;
+import ghidra.app.util.bin.format.pdb2.pdbreader.symbol.AbstractMsSymbol;
+import ghidra.app.util.bin.format.pdb2.pdbreader.type.AbstractMsType;
+import ghidra.app.util.bin.format.pdb2.pdbreader.type.PrimitiveMsType;
+import ghidra.util.Msg;
+import ghidra.util.exception.CancelledException;
+import ghidra.util.task.TaskMonitor;
+
+/**
+ * Helper class with static query methods available to PdbQuery set of scripts. The methods
+ * in this class allow the user to query particular components inside of a PDB.
+ */
+public class PdbQuery {
+
+ /**
+ * Returns the specified PDB data type record.
+ * @param script the script for which we are working
+ * @param pdb the PDB containing the record
+ * @param number the data type record number
+ * @return the data type record
+ */
+ public static AbstractMsType getDataTypeRecord(GhidraScript script, AbstractPdb pdb,
+ int number) {
+ AbstractTypeProgramInterface tpi = pdb.getTypeProgramInterface();
+ if (tpi == null) {
+ println(script, "PDB does not contain a TPI... aborting search.");
+ return null;
+ }
+ if (number < 0 || number >= tpi.getTypeIndexMaxExclusive()) {
+ println(script, "Record number (" + number + ") out of range (" + 0 + " - " +
+ tpi.getTypeIndexMaxExclusive() + ")");
+ return null;
+ }
+ if (number < tpi.getTypeIndexMin()) {
+ // Created on the fly and is not cached. Moreover, it can add yet-unseen records to
+ // the PDB... so this might not be desired... Also... the record number could represent
+ // what is typically a "type" or an "item" and that cannot be distingushed here...
+ // there is not conflict between primitive type and primitive items... they come from
+ // the same pool, but we might return what is an item record in the request for a type
+ // record or vice versa. TODO: investigate all of these issues this further; might
+ // need to eliminate this report or add a lot more code.
+ return new PrimitiveMsType(pdb, number);
+ }
+ RecordNumber recordNumber = RecordNumber.typeRecordNumber(number);
+ AbstractMsType typeRecord = pdb.getTypeRecord(recordNumber);
+ return typeRecord;
+ }
+
+ /**
+ * Returns the specified PDB item record.
+ * @param script the script for which we are working
+ * @param pdb the PDB containing the record
+ * @param number the item record number
+ * @return the item record
+ */
+ public static AbstractMsType getItemTypeRecord(GhidraScript script, AbstractPdb pdb,
+ int number) {
+ AbstractTypeProgramInterface ipi = pdb.getItemProgramInterface();
+ if (ipi == null) {
+ println(script, "PDB does not contain an IPI... aborting search.");
+ return null;
+ }
+ if (number < 0 || number >= ipi.getTypeIndexMaxExclusive()) {
+ println(script, "Record number (" + number + ") out of range (" + 0 + " - " +
+ ipi.getTypeIndexMaxExclusive() + ")");
+ return null;
+ }
+ if (number < ipi.getTypeIndexMin()) {
+ // Created on the fly and is not cached. Moreover, it can add yet-unseen records to
+ // the PDB... so this might not be desired... Also... the record number could represent
+ // what is typically a "type" or an "item" and that cannot be distingushed here...
+ // there is not conflict between primitive type and primitive items... they come from
+ // the same pool, but we might return what is an item record in the request for a type
+ // record or vice versa. TODO: investigate all of these issues this further; might
+ // need to eliminate this report or add a lot more code.
+ return new PrimitiveMsType(pdb, number);
+ }
+ RecordNumber recordNumber = RecordNumber.itemRecordNumber(number);
+ AbstractMsType typeRecord = pdb.getTypeRecord(recordNumber);
+ return typeRecord;
+ }
+
+ /**
+ * Searches PDB data type records that contain the search string. Outputs results to the
+ * console.
+ * @param script the script for which we are working
+ * @param pdb the PDB to search
+ * @param searchString the search string
+ * @throws CancelledException upon user cancellation
+ */
+ public static void searchDataTypes(GhidraScript script, AbstractPdb pdb, String searchString)
+ throws CancelledException {
+ AbstractTypeProgramInterface tpi = pdb.getTypeProgramInterface();
+ if (tpi == null) {
+ println(script, "PDB does not contain a TPI... aborting search.");
+ }
+
+ StringBuilder results = new StringBuilder();
+ results.append('\n');
+
+ int num = tpi.getTypeIndexMaxExclusive() - tpi.getTypeIndexMin();
+ TaskMonitor monitor = script.getMonitor();
+ monitor.initialize(num);
+ println(script, "Searching " + num + " PDB data type components...");
+ for (int indexNumber =
+ tpi.getTypeIndexMin(); indexNumber < tpi.getTypeIndexMaxExclusive(); indexNumber++) {
+ monitor.checkCanceled();
+ RecordNumber recordNumber = RecordNumber.typeRecordNumber(indexNumber);
+ AbstractMsType typeRecord = pdb.getTypeRecord(recordNumber);
+ String recordString = typeRecord.toString();
+ if (recordString.contains(searchString)) {
+ results.append("Data number " + indexNumber + ":\n");
+ results.append(recordString);
+ results.append('\n');
+ }
+ monitor.incrementProgress(1);
+ }
+ println(script, results.toString());
+ }
+
+ /**
+ * Searches PDB item records that contain the search string. Outputs results to the
+ * console.
+ * @param script the script for which we are working
+ * @param pdb the PDB to search
+ * @param searchString the search string
+ * @throws CancelledException upon user cancellation
+ */
+ public static void searchItemTypes(GhidraScript script, AbstractPdb pdb, String searchString)
+ throws CancelledException {
+ AbstractTypeProgramInterface ipi = pdb.getItemProgramInterface();
+ if (ipi == null) {
+ println(script, "PDB does not contain an IPI... aborting search.");
+ return;
+ }
+
+ StringBuilder results = new StringBuilder();
+ results.append('\n');
+
+ int num = ipi.getTypeIndexMaxExclusive() - ipi.getTypeIndexMin();
+ TaskMonitor monitor = script.getMonitor();
+ monitor.initialize(num);
+ println(script, "Searching " + num + " PDB item type components...");
+ for (int indexNumber =
+ ipi.getTypeIndexMin(); indexNumber < ipi.getTypeIndexMaxExclusive(); indexNumber++) {
+ monitor.checkCanceled();
+ RecordNumber recordNumber = RecordNumber.itemRecordNumber(indexNumber);
+ AbstractMsType typeRecord = pdb.getTypeRecord(recordNumber);
+ String recordString = typeRecord.toString();
+ if (recordString.contains(searchString)) {
+ results.append("Item number " + indexNumber + ":\n");
+ results.append(recordString);
+ results.append('\n');
+ }
+ monitor.incrementProgress(1);
+ }
+ println(script, results.toString());
+ }
+
+ /**
+ * Searches PDB symbol records that contain the search string. Outputs results to the
+ * console.
+ * @param script the script for which we are working
+ * @param pdb the PDB to search
+ * @param searchString the search string
+ * @throws CancelledException upon user cancellation
+ */
+ public static void searchSymbols(GhidraScript script, AbstractPdb pdb, String searchString)
+ throws CancelledException {
+
+ StringBuilder results = new StringBuilder();
+ results.append('\n');
+
+ int numModules = pdb.getDebugInfo().getNumModules();
+ TaskMonitor monitor = script.getMonitor();
+ int numSymbols = 0;
+ for (int module = 0; module <= numModules; module++) {
+ monitor.checkCanceled();
+ try {
+ Map symbols =
+ pdb.getDebugInfo().getModuleSymbolsByOffset(module);
+ numSymbols += symbols.size();
+ }
+ catch (PdbException e) {
+ // just skip the module... logging this in the next loop.
+ }
+ }
+
+ monitor.initialize(numSymbols);
+ println(script, "Searching " + numSymbols + " PDB symbol components...");
+ for (int module = 0; module <= numModules; module++) {
+ monitor.checkCanceled();
+ try {
+ Map symbols =
+ pdb.getDebugInfo().getModuleSymbolsByOffset(module);
+ numSymbols += symbols.size();
+ for (Map.Entry entry : symbols.entrySet()) {
+ monitor.checkCanceled();
+ AbstractMsSymbol symbol = entry.getValue();
+ String symbolString = symbol.toString();
+ if (symbolString.contains(searchString)) {
+ results.append("Module " + module + ", Offset " + entry.getKey() + ":\n");
+ results.append(symbolString);
+ results.append('\n');
+ }
+ monitor.incrementProgress(1);
+ }
+ }
+ catch (PdbException e) {
+ Msg.debug(PdbQuery.class, "Skipping module " + module + " due to exception.");
+ }
+ }
+ println(script, results.toString());
+ }
+
+ /**
+ * Method for outputting a message to the console (if script is not null); otherwise outputs
+ * the message to Msg.info().
+ * @param script the script
+ * @param message the message to output to the console
+ */
+ private static void println(GhidraScript script, String message) {
+ if (script != null) {
+ script.println(message);
+ }
+ else {
+ Msg.info(PdbQuery.class, message);
+ }
+ }
+
+}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbApplicator.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbApplicator.java
index 1031cf269f..3f682846ee 100644
--- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbApplicator.java
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbApplicator.java
@@ -739,7 +739,8 @@ public class PdbApplicator {
int num = ipi.getTypeIndexMaxExclusive() - ipi.getTypeIndexMin();
monitor.initialize(num);
setMonitorMessage("PDB: Processing " + num + " item type components...");
- for (int indexNumber = ipi.getTypeIndexMin(); indexNumber < num; indexNumber++) {
+ for (int indexNumber =
+ ipi.getTypeIndexMin(); indexNumber < ipi.getTypeIndexMaxExclusive(); indexNumber++) {
monitor.checkCanceled();
MsTypeApplier applier = getTypeApplier(RecordNumber.itemRecordNumber(indexNumber));
applier.apply();
diff --git a/GhidraDocs/InstallationGuide.html b/GhidraDocs/InstallationGuide.html
index 432c08d994..4a8f6f700a 100644
--- a/GhidraDocs/InstallationGuide.html
+++ b/GhidraDocs/InstallationGuide.html
@@ -18,7 +18,7 @@
Ghidra Installation Guide
-The installation information provided is effective as of Ghidra 10.1 and is subject to change with
+The installation information provided is effective as of Ghidra 10.1.2 and is subject to change with
future releases.
@@ -318,7 +318,7 @@ Ghidra release includes native binaries for the following platforms:
system:
- A supported version of a Java Development Kit
- - Gradle 6 or 7
+ - Gradle 6.8+ or 7.x
- make, gcc, and g++ (Linux/macOS-only)
-
Microsoft Visual Studio
diff --git a/README.md b/README.md
index f23dfb822f..8c8d57c982 100644
--- a/README.md
+++ b/README.md
@@ -44,7 +44,7 @@ To create the latest development build for your platform from this source reposi
##### Install build tools:
* [JDK 11 64-bit][jdk11]
-* [Gradle 6.4+ or 7.x][gradle]
+* [Gradle 6.8+ or 7.x][gradle]
* make, gcc, and g++ (Linux/macOS-only)
* [Microsoft Visual Studio][vs] (Windows-only)