diff --git a/GPL/CabExtract/build.gradle b/GPL/CabExtract/build.gradle
index 46994edbc7..374a463115 100644
--- a/GPL/CabExtract/build.gradle
+++ b/GPL/CabExtract/build.gradle
@@ -17,6 +17,10 @@ eclipse.project.name = 'GPL CabExtract'
project.ext.cabextract = "cabextract-1.6"
+/*********************************************************************************
+ * Deprecated - will be removed
+ *********************************************************************************/
+
/*********************************************************************************
* CabExtract platform specific tasks
*
diff --git a/Ghidra/Configurations/Public_Release/data/PDB_SYMBOL_SERVER_URLS.pdburl b/Ghidra/Configurations/Public_Release/data/PDB_SYMBOL_SERVER_URLS.pdburl
index a01d71bf03..06da4577c6 100644
--- a/Ghidra/Configurations/Public_Release/data/PDB_SYMBOL_SERVER_URLS.pdburl
+++ b/Ghidra/Configurations/Public_Release/data/PDB_SYMBOL_SERVER_URLS.pdburl
@@ -1 +1,6 @@
-Internet,https://msdl.microsoft.com/download/symbols
+Internet|https://msdl.microsoft.com/download/symbols/|WARNING: Check your organization's security policy before downloading files from the internet.
+Internet|https://chromium-browser-symsrv.commondatastorage.googleapis.com|WARNING: Check your organization's security policy before downloading files from the internet.
+Internet|https://symbols.mozilla.org/|WARNING: Check your organization's security policy before downloading files from the internet.
+Internet|https://software.intel.com/sites/downloads/symbols/|WARNING: Check your organization's security policy before downloading files from the internet.
+Internet|https://driver-symbols.nvidia.com/|WARNING: Check your organization's security policy before downloading files from the internet.
+Internet|https://download.amd.com/dir/bin|WARNING: Check your organization's security policy before downloading files from the internet.
diff --git a/Ghidra/Features/Base/src/main/help/help/topics/ImporterPlugin/load_pdb.html b/Ghidra/Features/Base/src/main/help/help/topics/ImporterPlugin/load_pdb.html
deleted file mode 100644
index e54775e07a..0000000000
--- a/Ghidra/Features/Base/src/main/help/help/topics/ImporterPlugin/load_pdb.html
+++ /dev/null
@@ -1,156 +0,0 @@
-
-
-
-
-
-
- Load PDB File
-
-
-
-
-
Load PDB File
-
-
A program database (PDB) file holds debugging and project state information about a program
- and can be created in a number of ways. Historically, it has been created using a Microsoft
- compiler and written in C/C++, C#, and Visual Basic.
- A user generates a PDB file using the /ZI or /Zi flag (for C/C++ programs) or the
- /debug flag (for Visual Basic/C# programs).
-
-
There are two mechanisms for processing a PDB file. First, the platform-independent
- PDB Universal Reader/Analyzer, which can read a raw PDB file and apply it. Its capabilities
- are expected to be expanded in future releases. Second, the legacy capability that uses the
- DIA SDK to read information from the PDB file. This mechanism can only run
- on a Windows platform, however it creates an XML representation of information gleaned using
- the DIA SDK. These XML files can be saved and then used on Windows and non-Windows platforms
- hosting Ghidra.
-
-
If loading a PDB, this should be done prior to other analysis, except in special cases,
- such as when only loading data types.
-
-
Restricted loading of data types or public symbols is
- supported by PDB Universal.
-
-
To Load a PDB
-
-
-
-
From the menu-bar of a tool, select File Load PDB File
-
-
In the file chooser, select the PDB file (*.PDB or *.PDB.XML)
-
-
Click the "Select PDB" button
-
-
-
-
PDB Universal is automatically used for *.PDB on non-Windows platforms
-
PDB MSDIA is used for *.PDB.XML files
-
-
-
When a user chooses a PDB or XML file to load for a program, Ghidra will verify its
- signature to be valid for the program. At this time, the PDB MSDIA loader cannot be used to
- force-load a mismatched PDB. To perform a force-load of a PDB file, the user must choose the
- PDB Universal loader if given the option. Force-loading an mismatched file can have
- consequences, such as loading incorrect data types and symbols located at the wrong
- addresses.
-
-
- PDB files may also be loaded using the PDB Analyzer, which is available through
- Auto Analysis or as
- a One Shot Analyzer.
-
-
-
-
Information Loaded From PDB
-
-
-
-
Structure and union definitions
-
-
Typedefs
-
-
Enumerations
-
-
Class definitions
-
-
Function prototypes
-
-
Stack variable names and data types
-
-
Source line numbers
-
-
Instruction and data symbols
-
-
-
-
Loading Errors
-
-
-
Before the PDB file is loaded into the program, then PDB signature and age are matched
- against the information stored in the executable. If these values do not match, then the PDB
- will not be loaded.
-
-
-
-
Figure 1
-
-
-
The DIA SDK-Based Capability
-
-
*.PDB.XML files can be created in three different ways:
-
-
-
From the Ghidra GUI in Windows, use the
- Ghidra Script Manager
- to run the CreatePdbXmlFilesScript.java script. Follow the prompts to choose
- the .PDB file (or directory containing .PDB file(s)) to be converted to .PDB.XML form.
- When given a directory, the script recursively traverses all subfolders to find .PDB
- files. A created .PDB.XML file is placed in the same location as the corresponding original
- .PDB file.
-
-
From a Windows command line, navigate to the following directory:
- <ghidra install root>/support
- and run the createPdbXmlFiles.bat script. The script takes one argument representing
- either one .PDB file or a directory of .PDB files. When given a directory, the script
- recursively traverses all subdirectories to find .PDB files. A created .PDB.XML file is
- placed in the same location as the corresponding original .PDB file. Sample calls to the
- script are shown below.
-
Run the included pdb.exe executable (found in the <ghidra install
- root>/Ghidra/Features/PDB/os/win64 directory) and redirect (save) its output to an
- XML file as shown below:
-
- pdb.exe samplePdb.pdb > samplePdb.pdb.xml
-
-
-
-
NOTE: Execution of pdb.exe has runtime dependencies which must be satisfied.
- Please refer to the README_PDB document for details.
-
-
Debug Interface Access SDK
-
-
-
The Microsoft Debug Interface Access Software Development Kit (DIA SDK) provides access to
- debug information stored in program database (.PDB) files generated by Microsoft
- post-compiler tools. Because the format of the .PDB file generated by the post-compiler tools
- undergoes constant revision, exposing the format is impractical. Using the DIA API, you can
- develop applications that search for and browse debug information stored in a .PDB file. Such
- applications could, for example, report stack trace-back information and analyze performance
- data.
-
-
If you are attempting to load a PDB on a
- Windows machine and see an error message such as "Unable to locate the DIA SDK,"
- you will need to add and register one or more files on your computer. Refer to the
- README_PDB document for detailed instructions.
-
-
-
-
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/BinaryReader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/BinaryReader.java
index c07c127c00..c7006f9a4f 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/BinaryReader.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/BinaryReader.java
@@ -66,11 +66,13 @@ public class BinaryReader {
this.provider = provider;
setLittleEndian(isLittleEndian);
}
-
+
/**
- * Returns a clone of this reader positioned at the new index.
+ * Returns a clone of this reader, with its own independent current position,
+ * positioned at the new index.
+ *
* @param newIndex the new index
- * @return a clone of this reader positioned at the new index
+ * @return an independent clone of this reader positioned at the new index
*/
public BinaryReader clone(long newIndex) {
BinaryReader clone = new BinaryReader(provider, isLittleEndian());
@@ -88,6 +90,36 @@ public class BinaryReader {
return clone(currentIndex);
}
+ /**
+ * Returns a BinaryReader that is in BigEndian mode.
+ *
+ * @return either this same instance (if already BigEndian), or a new instance
+ * (at the same location) in BigEndian mode
+ */
+ public BinaryReader asBigEndian() {
+ if (isBigEndian()) {
+ return this;
+ }
+ BinaryReader result = clone(currentIndex);
+ result.setLittleEndian(false);
+ return result;
+ }
+
+ /**
+ * Returns a BinaryReader that is in LittleEndian mode.
+ *
+ * @return either this same instance (if already LittleEndian), or a new instance
+ * (at the same location) in LittleEndian mode
+ */
+ public BinaryReader asLittleEndian() {
+ if (!isBigEndian()) {
+ return this;
+ }
+ BinaryReader result = clone(currentIndex);
+ result.setLittleEndian(true);
+ return result;
+ }
+
/**
* Returns true if this reader will extract values in little endian,
* otherwise in big endian.
@@ -97,6 +129,15 @@ public class BinaryReader {
return converter instanceof LittleEndianDataConverter;
}
+ /**
+ * Returns true if this reader will extract values in big endian.
+ *
+ * @return true is big endian, false is little endian
+ */
+ public boolean isBigEndian() {
+ return converter instanceof BigEndianDataConverter;
+ }
+
/**
* Sets the endian of this binary reader.
* @param isLittleEndian true for little-endian and false for big-endian
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pdb/PdbFactory.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pdb/PdbFactory.java
deleted file mode 100644
index 7dbf1cecaf..0000000000
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pdb/PdbFactory.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/* ###
- * IP: GHIDRA
- * REVIEWED: YES
- *
- * 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.util.bin.format.pdb;
-
-import java.io.*;
-
-import ghidra.app.util.bin.*;
-import ghidra.framework.*;
-
-public class PdbFactory {
- static {
- PluggableServiceRegistry.registerPluggableService(PdbFactory.class,
- new PdbFactory());
- }
-
- public static PdbInfoDotNetIface getPdbInfoDotNetInstance(
- BinaryReader reader, int ptr) throws IOException {
- PdbFactory factory = PluggableServiceRegistry
- .getPluggableService(PdbFactory.class);
- return factory.doGetPdbInfoDotNetInstance(reader, ptr);
- }
-
- public static PdbInfoIface getPdbInfoInstance(BinaryReader reader, int ptr)
- throws IOException {
- PdbFactory factory = PluggableServiceRegistry
- .getPluggableService(PdbFactory.class);
- return factory.doGetPdbInfoInstance(reader, ptr);
- }
-
- protected PdbInfoDotNetIface doGetPdbInfoDotNetInstance(
- BinaryReader reader, int ptr) throws IOException {
- return null;
- }
-
- protected PdbInfoIface doGetPdbInfoInstance(BinaryReader reader, int ptr)
- throws IOException {
- return null;
- }
-}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pdb/PdbInfo.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pdb/PdbInfo.java
new file mode 100644
index 0000000000..4e841fc272
--- /dev/null
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pdb/PdbInfo.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 ghidra.app.util.bin.format.pdb;
+
+import java.io.IOException;
+
+import ghidra.app.util.bin.BinaryReader;
+import ghidra.framework.options.Options;
+
+/**
+ * Bag of information about a Pdb symbol file, usually extracted from information present in a PE
+ * binary.
+ *
+ */
+public interface PdbInfo {
+
+ /**
+ * Read either a {@link PdbInfoCodeView} object or a {@link PdbInfoDotNet} object
+ * from the BinaryReader of a PE binary.
+ *
+ * @param reader BinaryReader
+ * @param offset position of the debug info
+ * @return new PdbInfoCodeView or PdbInfoDotNet object
+ * @throws IOException if error
+ */
+ public static PdbInfo read(BinaryReader reader, long offset) throws IOException {
+ if (PdbInfoCodeView.isMatch(reader, offset)) {
+ return PdbInfoCodeView.read(reader, offset);
+ }
+ if (PdbInfoDotNet.isMatch(reader, offset)) {
+ return PdbInfoDotNet.read(reader, offset);
+ }
+ return null;
+ }
+
+ /**
+ * Returns true if this instance is valid.
+ *
+ * @return boolean true if valid (magic signature matches and fields have valid data)
+ */
+ boolean isValid();
+
+ /**
+ * Writes the various PDB info fields to a program's options.
+ *
+ * @param options Options of a Program to write to
+ */
+ void serializeToOptions(Options options);
+
+}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pdb/PdbInfoCodeView.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pdb/PdbInfoCodeView.java
new file mode 100644
index 0000000000..3a47e7bb1a
--- /dev/null
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pdb/PdbInfoCodeView.java
@@ -0,0 +1,113 @@
+/* ###
+ * 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.util.bin.format.pdb;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+import org.apache.commons.io.FilenameUtils;
+
+import ghidra.app.util.bin.BinaryReader;
+import ghidra.app.util.bin.StructConverter;
+import ghidra.app.util.bin.format.pe.debug.DebugCodeViewConstants;
+import ghidra.framework.options.Options;
+import ghidra.program.model.data.*;
+import ghidra.util.Conv;
+
+/**
+ * Older style pdb information, using a simple 32bit hash to link the pdb to its binary.
+ */
+public class PdbInfoCodeView implements StructConverter, PdbInfo {
+ private static final int MAGIC =
+ DebugCodeViewConstants.SIGNATURE_NB << 16 | DebugCodeViewConstants.VERSION_10;
+
+ /**
+ * Returns true if the pdb information at the specified offset is a {@link PdbInfoCodeView}
+ * type (based on the signature at that offset).
+ *
+ * @param reader {@link BinaryReader}
+ * @param offset offset of the Pdb information
+ * @return boolean true if it is a {@link PdbInfoCodeView} type
+ * @throws IOException if error reading data
+ */
+ public static boolean isMatch(BinaryReader reader, long offset) throws IOException {
+ //read value out as big endian
+ int value = reader.asBigEndian().readInt(offset);
+ return MAGIC == value;
+ }
+
+ /**
+ * Reads the pdb information from a PE binary.
+ *
+ * @param reader {@link BinaryReader}
+ * @param offset offset of the Pdb information
+ * @return new {@link PdbInfoCodeView} instance, never null
+ * @throws IOException if error reading data
+ */
+ public static PdbInfoCodeView read(BinaryReader reader, long offset) throws IOException {
+ reader = reader.clone(offset);
+
+ PdbInfoCodeView result = new PdbInfoCodeView();
+ result.magic = reader.readNextByteArray(4);
+ result.offset = reader.readNextInt();
+ result.sig = reader.readNextInt();
+ result.age = reader.readNextInt();
+ result.pdbPath = reader.readNextAsciiString();
+ result.pdbName = FilenameUtils.getName(result.pdbPath);
+
+ return result;
+ }
+
+ private byte[] magic;
+ private int offset;
+ private int sig;
+ private int age;
+ private String pdbName;
+ private String pdbPath;
+
+ private PdbInfoCodeView() {
+ // nothing
+ }
+
+ @Override
+ public boolean isValid() {
+ return magic.length == 4 && !pdbName.isBlank();
+ }
+
+ @Override
+ public void serializeToOptions(Options options) {
+ options.setString(PdbParserConstants.PDB_VERSION,
+ new String(magic, StandardCharsets.US_ASCII));
+ options.setString(PdbParserConstants.PDB_SIGNATURE, Conv.toHexString(sig));
+ options.setString(PdbParserConstants.PDB_AGE, Integer.toHexString(age));
+ options.setString(PdbParserConstants.PDB_FILE, pdbName);
+ }
+
+ @Override
+ public DataType toDataType() {
+ StructureDataType struct = new StructureDataType("PdbInfo", 0);
+ struct.add(new StringDataType(), magic.length, "signature", null);
+ struct.add(new DWordDataType(), "offset", null);
+ struct.add(new DWordDataType(), "sig", null);
+ struct.add(new DWordDataType(), "age", null);
+ if (pdbName.length() > 0) {
+ struct.add(new StringDataType(), pdbName.length(), "pdbname", null);
+ }
+ struct.setCategoryPath(new CategoryPath("/PDB"));
+ return struct;
+ }
+
+}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pdb/PdbInfoDotNet.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pdb/PdbInfoDotNet.java
new file mode 100644
index 0000000000..605ff1aa51
--- /dev/null
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pdb/PdbInfoDotNet.java
@@ -0,0 +1,131 @@
+/* ###
+ * 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.util.bin.format.pdb;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+import org.apache.commons.io.FilenameUtils;
+
+import ghidra.app.util.bin.BinaryReader;
+import ghidra.app.util.bin.StructConverter;
+import ghidra.app.util.bin.format.pe.debug.DebugCodeViewConstants;
+import ghidra.app.util.datatype.microsoft.GUID;
+import ghidra.app.util.datatype.microsoft.GuidDataType;
+import ghidra.framework.options.Options;
+import ghidra.program.model.data.*;
+
+/**
+ * Newer style pdb information, using a GUID to link the pdb to its binary.
+ */
+public class PdbInfoDotNet implements StructConverter, PdbInfo {
+ private static final int MAGIC =
+ DebugCodeViewConstants.SIGNATURE_DOT_NET << 16 | DebugCodeViewConstants.VERSION_DOT_NET;
+
+ /**
+ * Returns true if the pdb information at the specified offset is a {@link PdbInfoDotNet}
+ * type (based on the signature at that offset).
+ *
+ * @param reader {@link BinaryReader}
+ * @param offset offset of the Pdb information
+ * @return boolean true if it is a {@link PdbInfoDotNet} type
+ * @throws IOException if error reading data
+ */
+ public static boolean isMatch(BinaryReader reader, long offset) throws IOException {
+ //read value out as big endian
+ int value = reader.asBigEndian().readInt(offset);
+ return MAGIC == value;
+ }
+
+ /**
+ * Reads an instance from the stream.
+ *
+ * @param reader {@link BinaryReader} to read from
+ * @param offset position of the pdb info
+ * @return new instance, never null
+ * @throws IOException if IO error or data format error
+ */
+ public static PdbInfoDotNet read(BinaryReader reader, long offset) throws IOException {
+ reader = reader.clone(offset);
+
+ PdbInfoDotNet result = new PdbInfoDotNet();
+ result.magic = reader.readNextByteArray(4);
+ result.guid = new GUID(reader);
+ result.age = reader.readNextInt();
+ result.pdbPath = reader.readNextAsciiString();
+ result.pdbName = FilenameUtils.getName(result.pdbPath);
+
+ return result;
+ }
+
+ /**
+ * Creates an instance from explicit values.
+ *
+ * @param pdbPath String path / filename of the pdb file
+ * @param age age
+ * @param guid {@link GUID}
+ * @return new instance, never null
+ */
+ public static PdbInfoDotNet fromValues(String pdbPath, int age, GUID guid) {
+ PdbInfoDotNet result = new PdbInfoDotNet();
+ result.pdbPath = pdbPath;
+ result.pdbName = FilenameUtils.getName(result.pdbPath);
+ result.age = age;
+ result.guid = guid;
+ result.magic = "????".getBytes();
+
+ return result;
+ }
+
+
+ private byte[] magic;
+ private GUID guid;
+ private int age;
+ private String pdbName;
+ private String pdbPath;
+
+ private PdbInfoDotNet() {
+ // empty
+ }
+
+ @Override
+ public boolean isValid() {
+ return magic.length == 4 && !pdbName.isBlank() && guid != null;
+ }
+
+ @Override
+ public void serializeToOptions(Options options) {
+ options.setString(PdbParserConstants.PDB_VERSION,
+ new String(magic, StandardCharsets.US_ASCII));
+ options.setString(PdbParserConstants.PDB_GUID, guid.toString());
+ options.setString(PdbParserConstants.PDB_AGE, Integer.toHexString(age));
+ options.setString(PdbParserConstants.PDB_FILE, pdbName);
+ }
+
+ @Override
+ public DataType toDataType() {
+ StructureDataType struct = new StructureDataType("DotNetPdbInfo", 0);
+ struct.add(new StringDataType(), magic.length, "signature", null);
+ struct.add(new GuidDataType(), "guid", null);
+ struct.add(new DWordDataType(), "age", null);
+ if (pdbName.length() > 0) {
+ struct.add(new StringDataType(), pdbName.length(), "pdbname", null);
+ }
+ struct.setCategoryPath(new CategoryPath("/PDB"));
+ return struct;
+ }
+
+}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pdb/PdbInfoDotNetIface.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pdb/PdbInfoDotNetIface.java
deleted file mode 100644
index 381668ed56..0000000000
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pdb/PdbInfoDotNetIface.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/* ###
- * IP: GHIDRA
- * REVIEWED: YES
- *
- * 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.util.bin.format.pdb;
-
-import ghidra.app.util.bin.*;
-import ghidra.app.util.datatype.microsoft.*;
-
-public interface PdbInfoDotNetIface extends StructConverter {
-
- public abstract String getPdbName();
-
- public abstract int getAge();
-
- public abstract int getSignature();
-
- public abstract GUID getGUID();
-
- public abstract byte[] getMagic();
-}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pdb/PdbInfoIface.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pdb/PdbInfoIface.java
deleted file mode 100644
index ebb4ffac61..0000000000
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pdb/PdbInfoIface.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/* ###
- * IP: GHIDRA
- * REVIEWED: YES
- *
- * 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.util.bin.format.pdb;
-
-import ghidra.app.util.bin.*;
-
-public interface PdbInfoIface extends StructConverter {
-
- public abstract byte[] getMagic();
-
- public abstract int getOffset();
-
- public abstract int getSig();
-
- public abstract int getAge();
-
- public abstract String getPdbName();
-}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/DebugDataDirectory.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/DebugDataDirectory.java
index 4e9168a266..27569734f1 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/DebugDataDirectory.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/DebugDataDirectory.java
@@ -19,8 +19,8 @@ import java.io.IOException;
import java.io.RandomAccessFile;
import ghidra.app.util.bin.format.FactoryBundledWithBinaryReader;
-import ghidra.app.util.bin.format.pdb.PdbInfoDotNetIface;
-import ghidra.app.util.bin.format.pdb.PdbInfoIface;
+import ghidra.app.util.bin.format.pdb.PdbInfoCodeView;
+import ghidra.app.util.bin.format.pdb.PdbInfoDotNet;
import ghidra.app.util.bin.format.pe.debug.*;
import ghidra.app.util.importer.MessageLog;
import ghidra.program.model.address.Address;
@@ -112,12 +112,12 @@ public class DebugDataDirectory extends DataDirectory {
if (dcv != null) {
Address dataAddr = getDataAddress(dcv.getDebugDirectory(), isBinary, space, ntHeader);
if (dataAddr != null) {
- PdbInfoIface pdbInfo = dcv.getPdbInfo();
+ PdbInfoCodeView pdbInfo = dcv.getPdbInfo();
if (pdbInfo != null) {
setPlateComment(program, dataAddr, "CodeView PDB Info");
PeUtils.createData(program, dataAddr, pdbInfo.toDataType(), log);
}
- PdbInfoDotNetIface dotNetPdbInfo = dcv.getDotNetPdbInfo();
+ PdbInfoDotNet dotNetPdbInfo = dcv.getDotNetPdbInfo();
if (dotNetPdbInfo != null) {
setPlateComment(program, dataAddr, ".NET PDB Info");
PeUtils.createData(program, dataAddr, dotNetPdbInfo.toDataType(), log);
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/debug/DebugCodeView.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/debug/DebugCodeView.java
index e61a0522a1..1c9154eca1 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/debug/DebugCodeView.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/debug/DebugCodeView.java
@@ -15,30 +15,25 @@
*/
package ghidra.app.util.bin.format.pe.debug;
+import java.io.IOException;
+
import ghidra.app.util.bin.StructConverter;
import ghidra.app.util.bin.format.FactoryBundledWithBinaryReader;
-import ghidra.app.util.bin.format.pdb.PdbFactory;
-import ghidra.app.util.bin.format.pdb.PdbInfoDotNetIface;
-import ghidra.app.util.bin.format.pdb.PdbInfoIface;
+import ghidra.app.util.bin.format.pdb.PdbInfoCodeView;
+import ghidra.app.util.bin.format.pdb.PdbInfoDotNet;
import ghidra.app.util.bin.format.pe.OffsetValidator;
-import ghidra.program.model.data.ArrayDataType;
-import ghidra.program.model.data.CategoryPath;
-import ghidra.program.model.data.DataType;
-import ghidra.program.model.data.Structure;
-import ghidra.program.model.data.StructureDataType;
+import ghidra.program.model.data.*;
import ghidra.util.Msg;
import ghidra.util.exception.DuplicateNameException;
-import java.io.IOException;
-
/**
* A class to represent the code view debug information.
*/
public class DebugCodeView implements StructConverter {
private DebugDirectory debugDir;
private DebugCodeViewSymbolTable symbolTable;
- private PdbInfoIface pdbInfo;
- private PdbInfoDotNetIface dotNetPdbInfo;
+ private PdbInfoCodeView pdbInfo;
+ private PdbInfoDotNet dotNetPdbInfo;
/**
* Constructor.
@@ -70,8 +65,8 @@ public class DebugCodeView implements StructConverter {
return;
}
- dotNetPdbInfo = PdbFactory.getPdbInfoDotNetInstance(reader, ptr);
- pdbInfo = PdbFactory.getPdbInfoInstance(reader, ptr);
+ dotNetPdbInfo = PdbInfoDotNet.isMatch(reader, ptr) ? PdbInfoDotNet.read(reader, ptr) : null;
+ pdbInfo = PdbInfoCodeView.isMatch(reader, ptr) ? PdbInfoCodeView.read(reader, ptr) : null;
if (DebugCodeViewSymbolTable.isMatch(reader, ptr)) {
symbolTable =
DebugCodeViewSymbolTable.createDebugCodeViewSymbolTable(reader,
@@ -106,17 +101,18 @@ public class DebugCodeView implements StructConverter {
* Returns the code view .PDB info.
* @return the code view .PDB info
*/
- public PdbInfoIface getPdbInfo() {
+ public PdbInfoCodeView getPdbInfo() {
return pdbInfo;
}
- public PdbInfoDotNetIface getDotNetPdbInfo() {
+ public PdbInfoDotNet getDotNetPdbInfo() {
return dotNetPdbInfo;
}
/**
* @see ghidra.app.util.bin.StructConverter#toDataType()
*/
+ @Override
public DataType toDataType() throws DuplicateNameException {
Structure es = new StructureDataType("DebugCodeView", 0);
es.add(WORD, "Signature", null);
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/datatype/microsoft/GUID.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/datatype/microsoft/GUID.java
index 0d7d073536..82424c5b49 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/datatype/microsoft/GUID.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/datatype/microsoft/GUID.java
@@ -21,11 +21,7 @@ import java.util.Arrays;
import ghidra.app.util.bin.BinaryReader;
import ghidra.program.model.mem.MemBuffer;
import ghidra.program.model.mem.MemoryAccessException;
-import ghidra.util.BigEndianDataConverter;
-import ghidra.util.Conv;
-import ghidra.util.DataConverter;
-import ghidra.util.LittleEndianDataConverter;
-import ghidra.util.NumericUtilities;
+import ghidra.util.*;
/**
* GUIDs identify objects such as interfaces, manager entry-point vectors (EPVs),
@@ -61,54 +57,52 @@ public class GUID {
/**
* Creates a GUID object using the GUID string form.
- * @param guidString - "6B29FC40-CA47-1067-B31D-00DD010662DA"
+ * @param guidString - either with or without dashes between parts -
+ * "6B29FC40-CA47-1067-B31D-00DD010662DA", or "6B29FC40CA471067B31D00DD010662DA", and
+ * with or without leading and trailing "{" "}" characters
* @throws IllegalArgumentException if string does not represent a valid GUID
*/
- public GUID(String guidString) {
- if (guidString.length() != 36) {
- throw new IllegalArgumentException("Invalid GUID string.");
- }
- int pos = guidString.indexOf('-');
- if (pos == -1) {
- throw new IllegalArgumentException("Invalid GUID string.");
- }
- data1 = (int) NumericUtilities.parseHexLong(guidString.substring(0, pos));
-
- guidString = guidString.substring(pos + 1);
- pos = guidString.indexOf('-');
- if (pos == -1) {
- throw new IllegalArgumentException("Invalid GUID string.");
- }
- data2 = (short) Integer.parseInt(guidString.substring(0, pos), 16);
-
- guidString = guidString.substring(pos + 1);
- pos = guidString.indexOf('-');
- if (pos == -1) {
- throw new IllegalArgumentException("Invalid GUID string.");
- }
- data3 = (short) Integer.parseInt(guidString.substring(0, pos), 16);
-
- guidString = guidString.substring(pos + 1);
- pos = guidString.indexOf('-');
- if (pos == -1) {
- throw new IllegalArgumentException("Invalid GUID string.");
- }
- int value = Integer.parseInt(guidString.substring(0, pos), 16);
+ public GUID(String guidString) throws IllegalArgumentException {
+ String[] parts = getGUIDParts(guidString);
+ data1 = (int) NumericUtilities.parseHexLong(parts[0]);
+ data2 = (short) Integer.parseInt(parts[1], 16);
+ data3 = (short) Integer.parseInt(parts[2], 16);
+ int value = Integer.parseInt(parts[3], 16);
data4[0] = (byte) (value >> 8);
data4[1] = (byte) (value & 0xff);
+ data4[2] = (byte) Integer.parseInt(parts[4].substring(0, 2), 16);
+ data4[3] = (byte) Integer.parseInt(parts[4].substring(2, 4), 16);
+ data4[4] = (byte) Integer.parseInt(parts[4].substring(4, 6), 16);
+ data4[5] = (byte) Integer.parseInt(parts[4].substring(6, 8), 16);
+ data4[6] = (byte) Integer.parseInt(parts[4].substring(8, 10), 16);
+ data4[7] = (byte) Integer.parseInt(parts[4].substring(10, 12), 16);
+ }
- guidString = guidString.substring(pos + 1);
- data4[2] = (byte) Integer.parseInt(guidString.substring(0, 2), 16);
- guidString = guidString.substring(2);
- data4[3] = (byte) Integer.parseInt(guidString.substring(0, 2), 16);
- guidString = guidString.substring(2);
- data4[4] = (byte) Integer.parseInt(guidString.substring(0, 2), 16);
- guidString = guidString.substring(2);
- data4[5] = (byte) Integer.parseInt(guidString.substring(0, 2), 16);
- guidString = guidString.substring(2);
- data4[6] = (byte) Integer.parseInt(guidString.substring(0, 2), 16);
- guidString = guidString.substring(2);
- data4[7] = (byte) Integer.parseInt(guidString.substring(0, 2), 16);
+ private String[] getGUIDParts(String guidString) throws IllegalArgumentException {
+ String[] results = new String[5];
+ guidString = (guidString.startsWith("{") && guidString.endsWith("}"))
+ ? guidString.substring(1, guidString.length() - 1)
+ : guidString;
+ if (guidString.length() == 36 && guidString.charAt(8) == '-' &&
+ guidString.charAt(13) == '-' && guidString.charAt(18) == '-' &&
+ guidString.charAt(23) == '-') {
+ results[0] = guidString.substring(0, 8);
+ results[1] = guidString.substring(9, 13);
+ results[2] = guidString.substring(14, 18);
+ results[3] = guidString.substring(19, 23);
+ results[4] = guidString.substring(24);
+ }
+ else if (guidString.length() == 32) {
+ results[0] = guidString.substring(0, 8);
+ results[1] = guidString.substring(8, 12);
+ results[2] = guidString.substring(12, 16);
+ results[3] = guidString.substring(16, 20);
+ results[4] = guidString.substring(20);
+ }
+ else {
+ throw new IllegalArgumentException("Invalid GUID string.");
+ }
+ return results;
}
/**
@@ -172,23 +166,23 @@ public class GUID {
@Override
public String toString() {
- StringBuffer buffer = new StringBuffer();
- buffer.append(Conv.toHexString(data1));
- buffer.append("-");
- buffer.append(Conv.toHexString(data2));
- buffer.append("-");
- buffer.append(Conv.toHexString(data3));
- buffer.append("-");
- buffer.append(Conv.toHexString(data4[0]));
- buffer.append(Conv.toHexString(data4[1]));
- buffer.append("-");
- buffer.append(Conv.toHexString(data4[2]));
- buffer.append(Conv.toHexString(data4[3]));
- buffer.append(Conv.toHexString(data4[4]));
- buffer.append(Conv.toHexString(data4[5]));
- buffer.append(Conv.toHexString(data4[6]));
- buffer.append(Conv.toHexString(data4[7]));
- return buffer.toString();
+ StringBuilder sb = new StringBuilder();
+ sb.append(Conv.toHexString(data1));
+ sb.append("-");
+ sb.append(Conv.toHexString(data2));
+ sb.append("-");
+ sb.append(Conv.toHexString(data3));
+ sb.append("-");
+ sb.append(Conv.toHexString(data4[0]));
+ sb.append(Conv.toHexString(data4[1]));
+ sb.append("-");
+ sb.append(Conv.toHexString(data4[2]));
+ sb.append(Conv.toHexString(data4[3]));
+ sb.append(Conv.toHexString(data4[4]));
+ sb.append(Conv.toHexString(data4[5]));
+ sb.append(Conv.toHexString(data4[6]));
+ sb.append(Conv.toHexString(data4[7]));
+ return sb.toString();
}
/**
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/AbstractPeDebugLoader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/AbstractPeDebugLoader.java
index b5ac32c59c..a0215524ea 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/AbstractPeDebugLoader.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/AbstractPeDebugLoader.java
@@ -17,11 +17,11 @@ package ghidra.app.util.opinion;
import java.util.*;
-import ghidra.app.util.bin.format.pdb.*;
+import ghidra.app.util.bin.format.pdb.PdbInfoCodeView;
+import ghidra.app.util.bin.format.pdb.PdbInfoDotNet;
import ghidra.app.util.bin.format.pe.FileHeader;
import ghidra.app.util.bin.format.pe.SectionHeader;
import ghidra.app.util.bin.format.pe.debug.*;
-import ghidra.app.util.datatype.microsoft.GUID;
import ghidra.app.util.demangler.DemangledObject;
import ghidra.app.util.demangler.DemanglerUtil;
import ghidra.framework.options.Options;
@@ -112,56 +112,14 @@ abstract class AbstractPeDebugLoader extends AbstractLibrarySupportLoader {
Options proplist = program.getOptions(Program.PROGRAM_INFO);
- PdbInfoIface cvPdbInfo = dcv.getPdbInfo();
+ PdbInfoCodeView cvPdbInfo = dcv.getPdbInfo();
if (cvPdbInfo != null) {
- byte[] magic = cvPdbInfo.getMagic();
- int sig = cvPdbInfo.getSig();
- int age = cvPdbInfo.getAge();
- String name = cvPdbInfo.getPdbName();
-
- proplist.setString(PdbParserConstants.PDB_VERSION, Conv.toString(magic));
- proplist.setString(PdbParserConstants.PDB_SIGNATURE, Conv.toHexString(sig));
- proplist.setString(PdbParserConstants.PDB_AGE, Integer.toHexString(age));
- proplist.setString(PdbParserConstants.PDB_FILE, name);
-/*
- DebugDirectory dd = dcv.getDebugDirectory();
- if (dd.getAddressOfRawData() > 0) {
- Address address = space.getAddress(imageBase + dd.getAddressOfRawData());
- listing.setComment(address, CodeUnit.PLATE_COMMENT, "CodeView PDB Info");
- try {
- listing.createData(address, cvPdbInfo.toDataType());
- }
- catch (IOException e) {}
- catch (DuplicateNameException e) {}
- catch (CodeUnitInsertionException e) {}
- }
-*/
+ cvPdbInfo.serializeToOptions(proplist);
}
- PdbInfoDotNetIface dotnetPdbInfo = dcv.getDotNetPdbInfo();
+ PdbInfoDotNet dotnetPdbInfo = dcv.getDotNetPdbInfo();
if (dotnetPdbInfo != null) {
- byte[] magic = dotnetPdbInfo.getMagic();
- GUID guid = dotnetPdbInfo.getGUID();
- int age = dotnetPdbInfo.getAge();
- String name = dotnetPdbInfo.getPdbName();
-
- proplist.setString(PdbParserConstants.PDB_VERSION, Conv.toString(magic));
- proplist.setString(PdbParserConstants.PDB_GUID, guid.toString());
- proplist.setString(PdbParserConstants.PDB_AGE, Integer.toHexString(age));
- proplist.setString(PdbParserConstants.PDB_FILE, name);
-/*
- DebugDirectory dd = dcv.getDebugDirectory();
- if (dd.getAddressOfRawData() > 0) {
- Address address = space.getAddress(imageBase + dd.getAddressOfRawData());
- listing.setComment(address, CodeUnit.PLATE_COMMENT, ".NET PDB Info");
- try {
- listing.createData(address, dotnetPdbInfo.toDataType());
- }
- catch (IOException e) {}
- catch (DuplicateNameException e) {}
- catch (CodeUnitInsertionException e) {}
- }
-*/
+ dotnetPdbInfo.serializeToOptions(proplist);
}
DebugCodeViewSymbolTable dcvst = dcv.getSymbolTable();
diff --git a/Ghidra/Features/PDB/certification.manifest b/Ghidra/Features/PDB/certification.manifest
index fa7172b4dc..dce9dc413e 100644
--- a/Ghidra/Features/PDB/certification.manifest
+++ b/Ghidra/Features/PDB/certification.manifest
@@ -1,4 +1,7 @@
##VERSION: 2.0
+##MODULE IP: Crystal Clear Icons - LGPL 2.1
+##MODULE IP: FAMFAMFAM Icons - CC 2.5
+##MODULE IP: Nuvola Icons - LGPL 2.1
##MODULE IP: Oxygen Icons - LGPL 3.0
Module.manifest||GHIDRA||||END|
src/global/docs/README_PDB.html||GHIDRA||||END|
@@ -13,13 +16,19 @@ src/main/help/help/shared/redo.png||GHIDRA||||END|
src/main/help/help/shared/tip.png||Oxygen Icons - LGPL 3.0|||Oxygen icon theme (dual license; LGPL or CC-SA-3.0)|END|
src/main/help/help/shared/undo.png||GHIDRA||||END|
src/main/help/help/shared/warning.png||Oxygen Icons - LGPL 3.0|||Oxygen icon theme (dual license; LGPL or CC-SA-3.0)|END|
+src/main/help/help/topics/Pdb/LoadPDBNew.html||GHIDRA||||END|
src/main/help/help/topics/Pdb/PDB.htm||GHIDRA||||END|
-src/main/help/help/topics/Pdb/download_pdb_file.html||GHIDRA||||END|
-src/main/help/help/topics/Pdb/images/KnownSymbolServerURLsDialog.png||GHIDRA||||END|
-src/main/help/help/topics/Pdb/images/PdbOrXmlDialog.png||GHIDRA||||END|
-src/main/help/help/topics/Pdb/images/PeSpecifiedPathDialog.png||GHIDRA||||END|
-src/main/help/help/topics/Pdb/images/SuccessDialog.png||GHIDRA||||END|
-src/main/help/help/topics/Pdb/images/SymbolServerURLDialog.png||GHIDRA||||END|
+src/main/help/help/topics/Pdb/images/LoadPdb_Advanced_NeedsConfig.png||GHIDRA||||END|
+src/main/help/help/topics/Pdb/images/LoadPdb_Advanced_Screenshot.png||GHIDRA||||END|
+src/main/help/help/topics/Pdb/images/LoadPdb_Initial_Screenshot.png||GHIDRA||||END|
+src/main/help/help/topics/Pdb/images/Plus2.png||GHIDRA||||END|
+src/main/help/help/topics/Pdb/images/SymbolServerConfig_AddButtonMenu.png||GHIDRA||||END|
+src/main/help/help/topics/Pdb/images/SymbolServerConfig_Screenshot.png||GHIDRA||||END|
+src/main/help/help/topics/Pdb/images/disk.png||FAMFAMFAM Icons - CC 2.5||||END|
+src/main/help/help/topics/Pdb/images/down.png||GHIDRA||||END|
+src/main/help/help/topics/Pdb/images/error.png||Nuvola Icons - LGPL 2.1||||END|
+src/main/help/help/topics/Pdb/images/reload3.png||Crystal Clear Icons - LGPL 2.1||||END|
+src/main/help/help/topics/Pdb/images/up.png||GHIDRA||||END|
src/pdb/README.txt||GHIDRA||||END|
src/pdb/pdb.sln||GHIDRA||||END|
src/pdb/pdb.vcxproj||GHIDRA||||END|
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/GhidraPdbFactory.java b/Ghidra/Features/PDB/developer_scripts/PdbExamplePrescript.java
similarity index 50%
rename from Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/GhidraPdbFactory.java
rename to Ghidra/Features/PDB/developer_scripts/PdbExamplePrescript.java
index 66f34b5bbe..66f1a8dadf 100644
--- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/GhidraPdbFactory.java
+++ b/Ghidra/Features/PDB/developer_scripts/PdbExamplePrescript.java
@@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
- * REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,29 +13,23 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package ghidra.app.util.bin.format.pdb;
+//Example preScript to force a PdbAnalyzer to use a custom PDB
+//symbol file when analyzing a binary.
+//@category PDB
+import java.io.File;
-import ghidra.app.util.bin.*;
+import ghidra.app.plugin.core.analysis.PdbAnalyzer;
+import ghidra.app.script.GhidraScript;
-import java.io.*;
-
-public class GhidraPdbFactory extends PdbFactory {
+public class PdbExamplePrescript extends GhidraScript {
@Override
- protected PdbInfoDotNetIface doGetPdbInfoDotNetInstance(
- BinaryReader reader, int ptr) throws IOException {
- if (PdbInfoDotNet.isMatch(reader, ptr)) {
- return new PdbInfoDotNet(reader, ptr);
- }
- return null;
- }
+ protected void run() throws Exception {
+ // contrived example of choosing a pdb file with custom logic
+ File pdbFile = new File(getProgramFile().getPath() + ".pdb");
- @Override
- protected PdbInfoIface doGetPdbInfoInstance(BinaryReader reader, int ptr)
- throws IOException {
- if (PdbInfo.isMatch(reader, ptr)) {
- return new PdbInfo(reader, ptr);
- }
- return null;
+ PdbAnalyzer.setPdbFileOption(currentProgram, pdbFile);
+ // or
+ //PdbUniversalAnalyzer.setPdbFileOption(currentProgram, pdbFile);
}
}
diff --git a/Ghidra/Features/PDB/developer_scripts/PdbSymbolServerExamplePrescript.java b/Ghidra/Features/PDB/developer_scripts/PdbSymbolServerExamplePrescript.java
new file mode 100644
index 0000000000..f8b29e9d89
--- /dev/null
+++ b/Ghidra/Features/PDB/developer_scripts/PdbSymbolServerExamplePrescript.java
@@ -0,0 +1,53 @@
+/* ###
+ * 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.
+ */
+//Example preScript to configure the PDB symbol server service to use the ~/symbols directory
+//as the location to store symbol files, and to search Microsoft's public
+//symbol server.
+//The ~/symbols directory should already exist and be initialized as a symbol
+//storage location.
+//@category PDB
+import java.util.List;
+
+import java.io.File;
+import java.net.URI;
+
+import ghidra.app.plugin.core.analysis.PdbAnalyzer;
+import ghidra.app.plugin.core.analysis.PdbUniversalAnalyzer;
+import ghidra.app.script.GhidraScript;
+import pdb.PdbPlugin;
+import pdb.symbolserver.*;
+
+public class PdbSymbolServerExamplePrescript extends GhidraScript {
+
+ @Override
+ protected void run() throws Exception {
+ File homeDir = new File(System.getProperty("user.home"));
+ File symDir = new File(homeDir, "symbols");
+ LocalSymbolStore localSymbolStore = new LocalSymbolStore(symDir);
+ HttpSymbolServer msSymbolServer =
+ new HttpSymbolServer(URI.create("https://msdl.microsoft.com/download/symbols/"));
+ SymbolServerService symbolServerService =
+ new SymbolServerService(localSymbolStore, List.of(msSymbolServer));
+
+ PdbPlugin.saveSymbolServerServiceConfig(symbolServerService);
+
+ // You only need to enable the "allow remote" option on the specific
+ // analyzer you are using
+ PdbUniversalAnalyzer.setAllowRemoteOption(currentProgram, true);
+ PdbAnalyzer.setAllowRemoteOption(currentProgram, true);
+ }
+}
+
diff --git a/Ghidra/Features/PDB/src/global/docs/README_PDB.html b/Ghidra/Features/PDB/src/global/docs/README_PDB.html
index 3b39a8ef11..decf438fcd 100644
--- a/Ghidra/Features/PDB/src/global/docs/README_PDB.html
+++ b/Ghidra/Features/PDB/src/global/docs/README_PDB.html
@@ -50,7 +50,7 @@ native execution issue and the use of an intermediate XML format.
Although GHIDRA has been primarily designed to utilize locally stored PDB files during analysis,
the ability to interactively download individual PDB files from a web-based Microsoft Symbol Server
is also provided. This capability is accessed via the GUI while a program is open via the
-File->Download PDB File... action.
+File → Load PDB File... action.
In an effort to manage large collections of symbol files, Microsoft specified a scheme to organize
+ symbol files into directory structures.
+
+
Ghidra can search Microsoft-style symbol servers (web-based HTTP/HTTPS) or local file system symbol
+ storage directories as well as unorganized, non-MS symbol storage directories for the PE executable's
+ matching PDB symbol file.
+
+
+
Microsoft symbol servers / symbol storage directories:
+
+
Organize symbol files (typically PDB files) into a directory hierarchy using information
+ from the symbol file being stored.
+
In a single-level symbol server directory hierarchy, a symbol file named myprogram.pdb might be stored
+ as:
myprogram.pdb is the name of the file and the name of the initial subdirectory off the root of the server.
+
012345670123012301230123456789AB is the 32 character hexadecimal value (made up for this example) of the GUID
+ "012345678-0123-0123-0123-0123456789AB" of the PDB file.
+
This value might instead be a 8 character hexadecimal value of the ID of the symbol file if it was created by an older version of the tool chain.
+
1 is the hexadecimal value of the 'age' (build number) of the PDB file. Note: most PDB files will have an age value of 1.
+
+
In a two-level symbol server directory hierarchy, the same symbol file would be stored
+ as: my/myprogram.pdb/012345670123012301230123456789AB1/myprogram.pdb, where the
+ first two letters of the symbol file's name are used to create a bucketing directory called "my" where all symbol files starting with
+ "my" are stored.
+
A two-level symbol server directory hierarchy is indicated by the presence of a file called index2.txt in the root directory of
+ the hierarchy.
+
Symbol storage directories are expected to have a pingme.txt file and a special directory called 000admin.
+
Compressed symbol files (*.pd_):
+
+
Microsoft symbol server tools can compress symbol files to save space. The compressed file is stored in place of the original file, renamed
+ to have a trailing underscore ("_") character in the file extension.
+
Before use, Ghidra will decompress the file into the Local Symbol Storage directory, using whatever organization scheme
+ that directory is configured with to store the uncompressed version of the file.
+
+
Remote symbol files hosted on a HTTP server will be copied to and stored in the configured Local Symbol Storage directory before they
+ can be used.
+
+
Unorganized directories:
+
+
Symbol files are are found by matching the leading part of the filename found in the unorganized directory with the sought-after symbol
+ file's name.
+
+
helloworld.pdb and hellokitty.pdb would both be found as possible matches when searching for
+ hello.pdb.
+
During searches, each possible matching PDB symbol file will be opened and partially parsed to extract its GUID and age values to determine if
+ it matches the user's full search criteria.
+
+
+
+
+
+
Menu Actions
+
+
+
Load PDB File
+
+
Allows the user to pick a PDB file or search for a PDB file and apply it to the currently open program in the CodeBrowser.
+
Use this action instead of the PDB Analyzer if the PDB file can't be found automatically with the currently configured
+ symbol server search locations, if you need to force load a non-exact PDB file, or you need to use other PDB options.
+
Steps:
+
+
Invoke File → Load PDB File...
+
+
The PDB Location field will either have the path of an existing matching PDB file, or
+ it will prompt the user to use the browse button to select a file or use the
+ Advanced screen to search for the file.
+
A PDB.XML file can be selected using the browse button. This will limit the selected parser to be the MSDIA parser.
+
Use the information displayed in the Program PDB Information panel to help you decide
+ which PDB file to choose.
+
If needed, click the Advanced button:
+
+
The Local Symbol Storage location is required to enable searching. If missing, set it to a directory where Ghidra can store PDB files.
+
+
For example, /home/your_id/Symbols or C:\Users\your_name\Symbols.
+
If the location is a new empty directory, the user will be prompted to initialize the directory as a Microsoft symbol storage directory.
+
+
Add additional search locations by clicking the button.
+ The Microsoft symbol server and Program's Import Location are good defaults.
+
Save any changes to the configuration by clicking the button.
Click the Search button to search the configured locations.
+
+
The Local Symbol Storage location is searched first, followed by any locations listed in the
+ Additional Search Paths list, in listed order.
+
Select one of the found PDB files and click the Load button to start the import process.
+
Remote symbol files will be downloaded to your Local Symbol Storage location before continuing.
+
+
+
+
+
+
Symbol Server Config
+
+
Allows the user to configure the location where PDB symbol files are stored and additional locations to search for
+ existing PDB files. This is also available in the Load PDB File, Advanced screen.
+
Steps:
+
+
Invoke Edit → Symbol Server Config
+
+
The Local Symbol Storage location is required to be able to search. If missing, set it to
+ a directory where Ghidra can store PDB files.
+
+
For example, /home/your_id/Symbols or C:\Users\your_name\Symbols.
+
If the location is a new empty directory, the user will be prompted to initialize the directory as a Microsoft symbol storage directory.
+
+
Add additional search locations by clicking the button.
+
Save any changes to the configuration by clicking the button.
+
Search locations can be disabled by toggling the enabled checkbox at the beginning of the row.
+
+
+
(Add)
+
+
Allows the user to add a location to the search path list. Pick from the offered types of locations, or pick
+ a predefined location.
+
+
+
Directory - allows the user to pick an existing directory that will be searched for symbol files.
+ See level 1/level 2 or
+ unorganized directory descriptions.
+
URL - allows the user to enter a HTTP or HTTPS URL to a web-based symbol server.
+
Program's Import Location - automatically references the directory from which the program was imported.
+
Import _NT_SYMBOL_PATH - parses the current value of the _NT_SYMBOL_PATH system environment variable to extract
+ URLs and symbol directory locations to be added to the Ghidra configuration. If no environment value is present,
+ the user can paste their own value into the text field.
+
+
All items listed after the menu dividing line are automatically added from resource files that have a
+ *.pdburl extension. The default file included with Ghidra is called PDB_SYMBOL_SERVER_URLS.pdburl and
+ is located in the Ghidra/Configurations/Public_Release/data directory under the Ghidra install directory.
+
+
+
(Delete)
+
+
Deletes the currently selected locations from the Additional Search Paths table.
+
+
+
(Up/Down)
+
+
Moves the currently selected item up or down in the Additional Search Paths table.
+
+
+
(Refresh)
+
+
Updates the status column of the locations listed in the Additional Search Paths
+ table. Symbol servers or storage locations that are unreachable or misconfigured will show an error status in that column.
+
+
+
(Save)
+
+
Saves the currently displayed search and storage locations to the preferences file. This is shared between all Ghidra tools.
+
+
+
+
+
+
PDB Search - Search Options
+
+
These options control how PDB symbol files are found.
+
+
Ignore Age - allows matching symbol files with the correct GUID, but incorrect age value. Only affects searches of local symbol directories.
+
Ignore GUID/ID - allows matching symbol files with the correct name, but incorrect GUID or age. Only affects searches of local symbol directories.
+
+
Additionally, there are override checkboxes in the Program PDB Information panel in the Advanced screen. These override values only
+ change the search criteria, they are not persisted to your program's metadata.
+
+
PDB Name Checkbox - this checkbox allows entering a custom value for the desired PDB file name.
+
PDB Unique ID Checkbox - this checkbox allows entering a custom GUID or ID value.
+
PDB Age Checkbox - this checkbox allows entering a custom age value.
+
+
After changing a search option, you will need to perform another search to use the new options.
+
+
+
+
+
PDB Parser
+
+
These options control which PDB parser will be used and any options used during parsing after the Load button is pressed.
+
+
Universal - Platform-independent PDB analyzer (No PDB.XML support).
+
MSDIA - Legacy PDB Analyzer. Requires MS DIA-SDK for raw PDB processing (Windows only), or preprocessed PDB.XML file.
+
+
Control (Universal only) - Controls how the PDB is applied to the Program
+
+
Process All: Applies Data Types and Public, Global, and Module Symbols.
+
Data Types Only: Applies Data Types and Typedefs found in the Global Symbols.
+
Public Symbols Only: Applies only Public symbols to the program. It ignores Global symbols and Module symbols.
+
+
+
+
+
Troubleshooting
+
+
+
If you are connecting to a Symbol Server that requires user authentication using PKI,
+ you must first set your PKI Certificate before attempting to download from the server. See
+
+ PKI Certificate for more details.
+
+
+
diff --git a/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/PDB.htm b/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/PDB.htm
index 388ef33464..c349ce849f 100644
--- a/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/PDB.htm
+++ b/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/PDB.htm
@@ -1,36 +1,130 @@
-
+
- Searching
-
+ Microsoft Program Databases (PDB)
-
-
PDB
+
Using Microsoft Program Database (PDB) Files
-
Ghidra offers the ability to download and apply PDB debug information for programs that run
- on Microsoft Windows operating systems.
- The Download PDB File feature allows users to
- download and optionally load/apply a PDB file that matches the user's current program, given an
- accessible Symbol Server.
- The Load PDB File feature
- allows users to apply a local PDB file to the current program. The PDB Analyzer also
- automatically applies PDB symbols (attempting a search for matching PDB files locally) during
- Auto-Analysis.
+
A program database (PDB) file holds debugging and project state information about a program
+ and can be created in a number of ways. Historically, it has been created using a Microsoft
+ compiler and written in C/C++, C#, and Visual Basic.
+ A user generates a PDB file using the /ZI or /Zi flag (for C/C++ programs) or the
+ /debug flag (for Visual Basic/C# programs).
+
There are two mechanisms for processing a PDB file. First, the platform-independent
+ PDB Universal Reader/Analyzer, which can read a raw PDB file and apply it. Its capabilities
+ are expected to be expanded in future releases. Second, the legacy capability that uses the
+ DIA SDK to read information from the PDB file. This mechanism can only run
+ on a Windows platform, however it creates an XML representation of information gleaned using
+ the DIA SDK. These XML files can be saved and then used on Windows and non-Windows platforms
+ hosting Ghidra.
+
+
If loading a PDB, this should be done prior to other analysis, except in special cases,
+ such as when only loading data types.
+
+
Restricted loading of data types or public symbols is
+ supported by PDB Universal.
*.PDB.XML files can be created in three different ways:
+
+
+
From the Ghidra GUI in Windows, use the
+ Ghidra Script Manager
+ to run the CreatePdbXmlFilesScript.java script. Follow the prompts to choose
+ the .PDB file (or directory containing .PDB file(s)) to be converted to .PDB.XML form.
+ When given a directory, the script recursively traverses all subfolders to find .PDB
+ files. A created .PDB.XML file is placed in the same location as the corresponding original
+ .PDB file.
+
+
From a Windows command line, navigate to the following directory:
+ <ghidra install root>/support
+ and run the createPdbXmlFiles.bat script. The script takes one argument representing
+ either one .PDB file or a directory of .PDB files. When given a directory, the script
+ recursively traverses all subdirectories to find .PDB files. A created .PDB.XML file is
+ placed in the same location as the corresponding original .PDB file. Sample calls to the
+ script are shown below.
+
Run the included pdb.exe executable (found in the <ghidra install
+ root>/Ghidra/Features/PDB/os/win64 directory) and redirect (save) its output to an
+ XML file as shown below:
+
+ pdb.exe samplePdb.pdb > samplePdb.pdb.xml
+
+
+
+
NOTE: Execution of pdb.exe has runtime dependencies which must be satisfied.
+ Please refer to the README_PDB document for details.
+
+
Debug Interface Access SDK
+
+
+
The Microsoft Debug Interface Access Software Development Kit (DIA SDK) provides access to
+ debug information stored in program database (.PDB) files generated by Microsoft
+ post-compiler tools. Because the format of the .PDB file generated by the post-compiler tools
+ undergoes constant revision, exposing the format is impractical. Using the DIA API, you can
+ develop applications that search for and browse debug information stored in a .PDB file. Such
+ applications could, for example, report stack trace-back information and analyze performance
+ data.
+
+
If you are attempting to load a PDB on a
+ Windows machine and see an error message such as "Unable to locate the DIA SDK,"
+ you will need to add and register one or more files on your computer. Refer to the
+ README_PDB document for detailed instructions.
+
Ghidra offers the ability to download and apply a PDB file that corresponds to the program
- currently open in the CodeBrowser. Successful downloading requires, at a minimum, that:
-
-
-
-
A Symbol Server URL is available and accessible from the client or computer where you are running Ghidra.
-
The program open in the CodeBrowser is a PE file that was compiled by a Microsoft compiler.
-
-
-
-
A Note for Windows Users
-
-
If set, Ghidra parses the _NT_SYMBOL_PATH
- environment variable that is used to specify a PDB download location and Symbol Server URL(s).
- The syntax for _NT_SYMBOL_PATH is shown below:
-
-
srv*[local symbols location]*[Symbol Server URL]
-
-
The _NT_SYMBOL_PATH symbols location is used to pre-populate the dialog that asks
- for the local storage location (as long as that location is valid). The _NT_SYMBOL_PATH
- Symbol Server URL is used to pre-populate the dialog that asks for the Symbol Server location.
-
-
Although multiple Symbol Server URLs can be
- specified in the _NT_SYMBOL_PATH variable, Ghidra only uses the first listed URL.
-
-
-
To Download a PDB
-
-
-
-
-
From the menu-bar of a tool, select File Download PDB File
-
-
-
-
A dialog appears asking whether you want to download a PDB or XML (PDB.XML) file. Select
- the type of file you want to download and click OK.
-
-
-
-
A Symbol Server should always have PDB
- files available for download. In contrast, .PDB.XML files are Ghidra-created files, and are
- only available to download from the Symbol Server if Ghidra tools have been used to create
- them and the server's admin has made them available. If you choose to download a .PDB.XML
- file and it is not found on the server, you will see a dialog message telling you so. For
- more information on creating and using .PDB.XML files, see the
- Load PDB File section.
-
-
-
-
Before attempting to download the file, an attempt will first be made to locate it
- using file and path names associated with the program. A dialog appears asking whether you
- want to include the PE-Header-Specified Path, which could include a Universal Naming
- Convention (UNC) path of a location that might not be trusted. Select OK if you want to
- perform this potentially unsafe retrieval.
-
-
-
-
-
-
A dialog appears asking where to save the downloaded file. Pick a location to store your
- PDB files. A common location on Windows is C:\Symbols.
-
-
-
-
At this point, if a PDB file of the type you have chosen (either .PDB or .PDB.XML) already
- exists in the selected location, you will see a message indicating that a potential matching
- PDB has been found. You will then be asked if you would like to continue with the download.
-
-
-
If you select "No", jump to Step 7.
-
-
If you select "Yes", please keep the following things in mind relating to a found
- .PDB or .PDB.XML file:
-
-
-
If the found file is not in a directory that contains the current binary's GUID
- (i.e., C:\Symbols\<pdbfilename>\<GUID>), then the file is not
- guaranteed to be an exact match for the current binary (when there is no GUID subfolder,
- a matching file is found based on expected PDB filename).
-
-
-
-
If there is any doubt about whether the found PDB file matches, it is a good
- idea to try to download the matching file, anyway (the matching file will be saved
- in a directory of the form <download location>/<pdbfilename>/
- <GUID>).
-
-
-
-
If you do choose to continue to apply the found PDB file, and its GUID does not
- match the GUID of the current binary, you will be warned and given the option of
- canceling the application of the PDB file.
-
-
-
-
-
-
Next, you will see a dialog asking for the Symbol Server URL.
-
-
-
-
-
-
-
- If a list of known URLs exists in your distribution (the file will have the extension
- .pdburl), the dialog will also include a button with the text "Choose from
- known URLs". When this button is pressed, a separate dialog appears showing known Symbol
- Server URLs.
-
-
-
-
-
-
-
- You may choose any of these URLs or manually type one in. If manually typing in a URL,
- be sure to include the protocol (http or https).
-
-
-
-
Always be sure to check your organization's
- security policy before downloading any file from the internet.
-
-
-
-
-
Next, if the Symbol Server contains a matching PDB that is the same file type that you
- chose earlier, it will return with a message indicating that the download was successful.
- The message also contains the path where you can find the downloaded file.
-
-
-
-
-
If the download was successful or an existing PDB file was found, you may be asked
- whether you want to apply the PDB to the program.
-
-
If Yes is chosen, see
- Load PDB File for continued help.
-
-
-
-
-
-
Troubleshooting
-
-
-
If you are connecting to a Symbol Server that requires user authentication using PKI,
- you must first set your PKI Certificate before attempting to download from the server. See
-
- PKI Certificate for more details.
-
-
-
diff --git a/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/KnownSymbolServerURLsDialog.png b/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/KnownSymbolServerURLsDialog.png
deleted file mode 100644
index a9ec4e4bc6..0000000000
Binary files a/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/KnownSymbolServerURLsDialog.png and /dev/null differ
diff --git a/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/LoadPdb_Advanced_NeedsConfig.png b/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/LoadPdb_Advanced_NeedsConfig.png
new file mode 100644
index 0000000000..d713737905
Binary files /dev/null and b/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/LoadPdb_Advanced_NeedsConfig.png differ
diff --git a/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/LoadPdb_Advanced_Screenshot.png b/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/LoadPdb_Advanced_Screenshot.png
new file mode 100644
index 0000000000..14c0387613
Binary files /dev/null and b/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/LoadPdb_Advanced_Screenshot.png differ
diff --git a/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/LoadPdb_Initial_Screenshot.png b/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/LoadPdb_Initial_Screenshot.png
new file mode 100644
index 0000000000..3c89aaaf55
Binary files /dev/null and b/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/LoadPdb_Initial_Screenshot.png differ
diff --git a/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/PdbOrXmlDialog.png b/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/PdbOrXmlDialog.png
deleted file mode 100644
index 6dde654855..0000000000
Binary files a/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/PdbOrXmlDialog.png and /dev/null differ
diff --git a/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/PeSpecifiedPathDialog.png b/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/PeSpecifiedPathDialog.png
deleted file mode 100644
index 7c76ccbf63..0000000000
Binary files a/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/PeSpecifiedPathDialog.png and /dev/null differ
diff --git a/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/Plus2.png b/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/Plus2.png
new file mode 100644
index 0000000000..add4ad53dd
Binary files /dev/null and b/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/Plus2.png differ
diff --git a/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/SuccessDialog.png b/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/SuccessDialog.png
deleted file mode 100644
index 5e15d6678c..0000000000
Binary files a/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/SuccessDialog.png and /dev/null differ
diff --git a/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/SymbolServerConfig_AddButtonMenu.png b/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/SymbolServerConfig_AddButtonMenu.png
new file mode 100644
index 0000000000..c3295e7c60
Binary files /dev/null and b/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/SymbolServerConfig_AddButtonMenu.png differ
diff --git a/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/SymbolServerConfig_Screenshot.png b/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/SymbolServerConfig_Screenshot.png
new file mode 100644
index 0000000000..531dd314cb
Binary files /dev/null and b/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/SymbolServerConfig_Screenshot.png differ
diff --git a/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/SymbolServerURLDialog.png b/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/SymbolServerURLDialog.png
deleted file mode 100644
index 303cdeb4c8..0000000000
Binary files a/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/SymbolServerURLDialog.png and /dev/null differ
diff --git a/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/disk.png b/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/disk.png
new file mode 100644
index 0000000000..99d532e8b1
Binary files /dev/null and b/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/disk.png differ
diff --git a/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/down.png b/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/down.png
new file mode 100644
index 0000000000..ff58c76dc1
Binary files /dev/null and b/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/down.png differ
diff --git a/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/error.png b/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/error.png
new file mode 100644
index 0000000000..2997461b21
Binary files /dev/null and b/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/error.png differ
diff --git a/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/reload3.png b/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/reload3.png
new file mode 100644
index 0000000000..b80c72091f
Binary files /dev/null and b/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/reload3.png differ
diff --git a/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/up.png b/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/up.png
new file mode 100644
index 0000000000..b76e535c48
Binary files /dev/null and b/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/up.png differ
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/plugin/core/analysis/PdbAnalyzer.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/plugin/core/analysis/PdbAnalyzer.java
index 4038669443..7ed136d02e 100644
--- a/Ghidra/Features/PDB/src/main/java/ghidra/app/plugin/core/analysis/PdbAnalyzer.java
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/plugin/core/analysis/PdbAnalyzer.java
@@ -17,19 +17,14 @@ package ghidra.app.plugin.core.analysis;
import java.io.File;
-import org.apache.commons.lang3.StringUtils;
-
import ghidra.app.services.*;
-import ghidra.app.util.bin.format.pdb.*;
+import ghidra.app.util.bin.format.pdb.PdbException;
+import ghidra.app.util.bin.format.pdb.PdbParser;
import ghidra.app.util.importer.MessageLog;
-import ghidra.app.util.opinion.PeLoader;
-import ghidra.app.util.pdb.PdbLocator;
-import ghidra.framework.options.OptionType;
import ghidra.framework.options.Options;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.listing.Program;
import ghidra.util.Msg;
-import ghidra.util.SystemUtilities;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
@@ -40,25 +35,14 @@ public class PdbAnalyzer extends AbstractAnalyzer {
static final String NAME = "PDB MSDIA";
static final boolean DEFAULT_ENABLEMENT = !PdbUniversalAnalyzer.DEFAULT_ENABLEMENT;
private static final String DESCRIPTION =
- "PDB Analyzer.\n" + "Requires MS DIA-SDK for raw PDB processing (Windows only).\n" +
- "Also supports pre-processed XML files.";
+ "PDB Analyzer.\n" +
+ "Requires MS DIA-SDK for raw PDB processing (Windows only).\n" +
+ "Also supports pre-processed XML files.\n" +
+ "PDB Symbol Server searching is configured in Edit -> Symbol Server Config.\n";
private static final String ERROR_TITLE = "Error in PDB Analyzer";
- private static final String SYMBOLPATH_OPTION_NAME = "Symbol Repository Path";
- private static final String SYMBOLPATH_OPTION_DESCRIPTION =
- "Directory path to root of Microsoft Symbol Repository Directory";
-
- private File symbolsRepositoryDir = PdbLocator.DEFAULT_SYMBOLS_DIR;
-
- //==============================================================================================
- // Include the PE-Header-Specified PDB path for searching for appropriate PDB file.
- private static final String OPTION_NAME_INCLUDE_PE_PDB_PATH =
- "Unsafe: Include PE PDB Path in PDB Search";
- private static final String OPTION_DESCRIPTION_INCLUDE_PE_PDB_PATH =
- "If checked, specifically searching for PDB in PE-Header-Specified Location.";
-
- private boolean includePeSpecifiedPdbPath = false;
+ private boolean searchRemoteLocations = false;
// only try once per transaction due to extensive error logging which may get duplicated
private long lastTransactionId = -1;
@@ -101,75 +85,21 @@ public class PdbAnalyzer extends AbstractAnalyzer {
return false;
}
- File pdb = lookForPdb(program, log);
-
- if (pdb == null) {
- Msg.info(this, "PDB analyzer failed to locate PDB file");
+ File pdbFile = PdbAnalyzerCommon.findPdb(this, program, searchRemoteLocations, monitor);
+ if (pdbFile == null) {
+ // warnings have already been logged
return false;
}
- Msg.info(this, "PDB analyzer parsing file: " + pdb.getAbsolutePath());
AutoAnalysisManager mgr = AutoAnalysisManager.getAnalysisManager(program);
- return parsePdb(pdb, program, mgr, monitor, log);
- }
-
- private static class PdbMissingState implements AnalysisState {
- // object existence indicates missing PDB has already been reported
- }
-
- File lookForPdb(Program program, MessageLog log) {
- String message = "";
- File pdb;
-
- try {
-
- pdb = PdbParser.findPDB(program, includePeSpecifiedPdbPath, symbolsRepositoryDir);
-
- if (pdb == null) {
-
- PdbMissingState missingState =
- AnalysisStateInfo.getAnalysisState(program, PdbMissingState.class);
- if (missingState != null) {
- return null; // already notified user
- }
- AnalysisStateInfo.putAnalysisState(program, new PdbMissingState());
-
- String pdbName = program.getOptions(Program.PROGRAM_INFO).getString(
- PdbParserConstants.PDB_FILE, (String) null);
- if (StringUtils.isBlank(pdbName)) {
- message = "Program has no associated PDB file.";
- }
- else {
- message = "Unable to locate PDB file \"" + pdbName + "\" with matching GUID.";
- }
- if (SystemUtilities.isInHeadlessMode()) {
- message += "\n Use a script to set the PDB file location. I.e.,\n" +
- " setAnalysisOption(currentProgram, \"PDB.Symbol Repository Path\", \"/path/to/pdb/folder\");\n" +
- " This must be done using a pre-script (prior to analysis).";
- }
- else {
- message += "\n You may set the PDB \"Symbol Repository Path\"" +
- "\n using \"Edit->Options for [program]\" prior to analysis." +
- "\nIt is important that a PDB is used during initial analysis " +
- "\nif available.";
- }
- }
-
- return pdb;
- }
- finally {
- if (message.length() > 0) {
- log.appendMsg(getName(), message);
- log.setStatus(message);
- }
- }
-
+ return parsePdb(pdbFile, program, mgr, monitor, log);
}
boolean parsePdb(File pdb, Program program, AutoAnalysisManager mgr, TaskMonitor monitor,
MessageLog log) {
DataTypeManagerService dataTypeManagerService = mgr.getDataTypeManagerService();
- PdbParser parser = new PdbParser(pdb, program, dataTypeManagerService, true, monitor);
+ PdbParser parser =
+ new PdbParser(pdb, program, dataTypeManagerService, true, false, monitor);
String message;
@@ -201,32 +131,52 @@ public class PdbAnalyzer extends AbstractAnalyzer {
@Override
public boolean canAnalyze(Program program) {
- return PeLoader.PE_NAME.equals(program.getExecutableFormat());
+ return PdbAnalyzerCommon.canAnalyzeProgram(program);
+ //return PeLoader.PE_NAME.equals(program.getExecutableFormat());
}
@Override
public void registerOptions(Options options, Program program) {
- symbolsRepositoryDir = PdbLocator.getDefaultPdbSymbolsDir();
-
- options.registerOption(SYMBOLPATH_OPTION_NAME, OptionType.FILE_TYPE, symbolsRepositoryDir,
- null, SYMBOLPATH_OPTION_DESCRIPTION);
-
- options.registerOption(OPTION_NAME_INCLUDE_PE_PDB_PATH, includePeSpecifiedPdbPath, null,
- OPTION_DESCRIPTION_INCLUDE_PE_PDB_PATH);
+ options.registerOption(PdbAnalyzerCommon.OPTION_NAME_SEARCH_REMOTE_LOCATIONS,
+ searchRemoteLocations, null,
+ PdbAnalyzerCommon.OPTION_DESCRIPTION_SEARCH_REMOTE_LOCATIONS);
}
@Override
public void optionsChanged(Options options, Program program) {
-
- File symbolsDir = options.getFile(SYMBOLPATH_OPTION_NAME, symbolsRepositoryDir);
- if (!symbolsDir.equals(symbolsRepositoryDir)) {
- symbolsRepositoryDir = symbolsDir;
- PdbLocator.setDefaultPdbSymbolsDir(symbolsDir);
- }
-
- includePeSpecifiedPdbPath =
- options.getBoolean(OPTION_NAME_INCLUDE_PE_PDB_PATH, includePeSpecifiedPdbPath);
+ searchRemoteLocations = options.getBoolean(
+ PdbAnalyzerCommon.OPTION_NAME_SEARCH_REMOTE_LOCATIONS, searchRemoteLocations);
}
+ /**
+ * Sets the PDB file that will be used by the analyzer when it is next invoked
+ * on the specified program.
+ *
+ * Normally the analyzer would locate the PDB file on its own, but if a
+ * headless script wishes to override the analyzer's behaivor, it can
+ * use this method to specify a file.
+ *
+ * @param program {@link Program}
+ * @param pdbFile the pdb file
+ */
+ public static void setPdbFileOption(Program program, File pdbFile) {
+ PdbAnalyzerCommon.setPdbFileOption(NAME, program, pdbFile);
+ }
+
+ /**
+ * Sets the "allow remote" option that will be used by the analyzer when it is next invoked
+ * on the specified program.
+ *
+ * Normally when the analyzer attempts to locate a matching PDB file it
+ * will default to NOT searching remote symbol servers. A headless script could
+ * use this method to allow the analyzer to search remote symbol servers.
+ *
+ * @param program {@link Program}
+ * @param allowRemote boolean flag, true means analyzer can search remote symbol
+ * servers
+ */
+ public static void setAllowRemoteOption(Program program, boolean allowRemote) {
+ PdbAnalyzerCommon.setAllowRemoteOption(NAME, program, allowRemote);
+ }
}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/plugin/core/analysis/PdbAnalyzerCommon.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/plugin/core/analysis/PdbAnalyzerCommon.java
new file mode 100644
index 0000000000..f9149b9984
--- /dev/null
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/plugin/core/analysis/PdbAnalyzerCommon.java
@@ -0,0 +1,182 @@
+/* ###
+ * 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.analysis;
+
+import java.util.Set;
+
+import java.io.File;
+
+import ghidra.app.services.Analyzer;
+import ghidra.app.util.opinion.PeLoader;
+import ghidra.framework.options.Options;
+import ghidra.program.model.listing.Program;
+import ghidra.util.Msg;
+import ghidra.util.SystemUtilities;
+import ghidra.util.task.TaskMonitor;
+import pdb.PdbPlugin;
+import pdb.symbolserver.FindOption;
+import pdb.symbolserver.SymbolFileInfo;
+
+/**
+ * Shared configuration values and pdb searching logic
+ */
+public class PdbAnalyzerCommon {
+ static final String OPTION_DESCRIPTION_SEARCH_REMOTE_LOCATIONS =
+ "If checked, allow searching remote symbol servers for PDB files.";
+ static final String OPTION_NAME_SEARCH_REMOTE_LOCATIONS = "Search remote symbol servers";
+
+ static final String OPTION_DESCRIPTION_PDB_FILE = "Path to a manually chosen PDB file.";
+ static final String OPTION_NAME_PDB_FILE = "PDB File";
+
+ // TODO: I changed this method from what was lifted in the old code. I check for null string
+ // and I also check for MSCOFF_NAME (TODO: check on the validity of this!!!). Also, changed
+ // the comparison to a substring search from a .equals).
+ /**
+ * Returns true if the specified program is supported by either of the
+ * Pdb analyzers.
+ *
+ * @param program {@link Program}
+ * @return boolean true if program is supported by Pdb analyzers
+ */
+ public static boolean canAnalyzeProgram(Program program) {
+ String executableFormat = program.getExecutableFormat();
+ return executableFormat != null && (executableFormat.indexOf(PeLoader.PE_NAME) != -1);
+ // TODO: Check for MSCOFF_NAME. Initial investigation shows that the .debug$T section of
+ // the MSCOFF (*.obj) file has type records and the .debug$S section has symbol records.
+ // More than that, in at least one instance, there has been a TypeServer2MsType type
+ // record that give the GUID, age, and name of the PDB file associated with the MSCOFF
+ // file. At this point in time, these two sections of the MSCOFF are read (header and
+ // raw data), but we do not interpret these sections any further. Suggest that we "might"
+ // want to parse some of these records at load time? Maybe not. We could, at analysis
+ // time, add the ability to process these two sections (as part of analysis (though we
+ // will not be aware of a PDB file yet), and upon discovery of a TypeServer2MsType (or
+ // perhaps other?), proceed to find the file (if possible) and also process that file.
+ // We posit that if a record indicates a separate PDB for the types (Note: MSFT indicates
+ // that only data types will be found in an MSCOFF PDB file), then that will likely be
+ // the only record in the .debug$T section.
+ // TODO: If the MSCOFF file is located in a MSCOFF ARCHIVE (*.lib), there can be a PDB
+ // associated with the archive. We currently do not pass on this association of the
+ // PDB archive to each underlying MSCOFF file. Moreover, we believe that we are not
+ // currently discovering the associated MSCOFF ARCHIVE PDB file when processing the
+ // MSCOFF ARCHIVE. Initial indication is that each MSCOFF within the archive will have
+ // the PDB file that it needs listed, even if redundant for each MSCOFF within the
+ // archive.
+// return executableFormat != null && (executableFormat.indexOf(PeLoader.PE_NAME) != -1 ||
+// executableFormat.indexOf(MSCoffLoader.MSCOFF_NAME) != -1);
+
+ }
+
+ /**
+ * Common logic to set a manual Pdb file that the specified analyzer will find and use
+ * when it is invoked later
+ * Each specific analyzer has a public method that calls this to supply the
+ * actual analyzer name to make it easier for script writers to call.
+ *
+ * @param analyzerName name of analyzer
+ * @param program {@link Program}
+ * @param pdbFile the file
+ */
+ static void setPdbFileOption(String analyzerName, Program program, File pdbFile) {
+ Options options = program.getOptions(Program.ANALYSIS_PROPERTIES);
+ options.setFile(analyzerName + "." + OPTION_NAME_PDB_FILE, pdbFile);
+ }
+
+ /**
+ * Common logic to set the "allow remote" option that the specified analyzer will find and use
+ * when it is invoked later
+ * Each specific analyzer has a public method that calls this to supply the
+ * actual analyzer name to make it easier for script writers to call.
+ *
+ * @param analyzerName name of analyzer
+ * @param program {@link Program}
+ * @param allowRemote boolean flag, true means the analyzer can search remote
+ * symbol servers
+ */
+ static void setAllowRemoteOption(String analyzerName, Program program, boolean allowRemote) {
+ Options options = program.getOptions(Program.ANALYSIS_PROPERTIES);
+ options.setBoolean(analyzerName + "." + OPTION_NAME_SEARCH_REMOTE_LOCATIONS, allowRemote);
+ }
+
+ /**
+ * Common pdb searching logic between both analyzers.
+ *
+ * @param pdbAnalyzer the analyzer doing the searching
+ * @param program the program
+ * @param allowRemote boolean flag, true means searching remote symbol servers
+ * is allowed
+ * @param monitor {@link TaskMonitor} to let user cancel
+ * @return File pointing to the found pdb, or null if not found or error
+ */
+ static File findPdb(Analyzer pdbAnalyzer, Program program, boolean allowRemote,
+ TaskMonitor monitor) {
+
+ SymbolFileInfo symbolFileInfo = SymbolFileInfo.fromMetadata(program.getMetadata());
+ if (symbolFileInfo == null) {
+ Msg.info(pdbAnalyzer,
+ "Skipping PDB processing: missing PDB information in program metadata");
+ return null;
+ }
+
+ // First look in the program's analysis options to see if there is a
+ // manually specified pdbFile. (see setPdbFileOption)
+ // If not set, then do a search using the currently configured symbol servers.
+ Options options = program.getOptions(Program.ANALYSIS_PROPERTIES);
+ String pdbFileOptionName = pdbAnalyzer.getName() + "." + OPTION_NAME_PDB_FILE;
+
+ // check existence first to avoid creating option value
+ File pdbFile = options.contains(pdbFileOptionName)
+ ? options.getFile(pdbFileOptionName, null)
+ : null;
+ if (pdbFile == null) {
+ Set findOpts = allowRemote
+ ? FindOption.of(FindOption.ALLOW_REMOTE)
+ : FindOption.NO_OPTIONS;
+ pdbFile = PdbPlugin.findPdb(program, findOpts, monitor);
+ }
+ if (pdbFile == null) {
+ Msg.info(pdbAnalyzer,
+ "Skipping PDB processing: failed to locate PDB file in configured locations");
+ if (SystemUtilities.isInHeadlessMode()) {
+ Msg.info(pdbAnalyzer,
+ "Use a script to set the PDB file location. I.e.,\n" +
+ " PdbAnalyzer.setPdbFileOption(currentProgram, new File(\"/path/to/pdb/file.pdb\")); or\n" +
+ " PdbUniversalAnalyzer.setPdbFileOption(currentProgram, new File(\"/path/to/pdb/file.pdb\"));\n" +
+ "Or set the symbol server search configuration using:" +
+ " PdbPlugin.saveSymbolServerServiceConfig(...);\n" +
+ " This must be done using a pre-script (prior to analysis).");
+ }
+ else {
+ Msg.info(pdbAnalyzer,
+ "You may set the PDB \"Symbol Server Config\"" +
+ "\n using \"Edit->Symbol Server Config\" prior to analysis." +
+ "\nIt is important that a PDB is used during initial analysis " +
+ "\nif available.");
+ }
+ }
+ else {
+ Msg.info(pdbAnalyzer, "PDB analyzer parsing file: " + pdbFile);
+ if (!pdbFile.isFile()) {
+ Msg.error(pdbAnalyzer,
+ "Skipping PDB processing: specified file does not exist or is not readable: " +
+ pdbFile);
+ return null;
+ }
+
+ }
+ return pdbFile;
+ }
+
+}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/plugin/core/analysis/PdbUniversalAnalyzer.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/plugin/core/analysis/PdbUniversalAnalyzer.java
index b8a0cf364b..4ba7249cb5 100644
--- a/Ghidra/Features/PDB/src/main/java/ghidra/app/plugin/core/analysis/PdbUniversalAnalyzer.java
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/plugin/core/analysis/PdbUniversalAnalyzer.java
@@ -19,12 +19,9 @@ import java.io.File;
import java.io.IOException;
import java.util.Date;
-import org.apache.commons.lang3.StringUtils;
-
import ghidra.app.services.*;
import ghidra.app.util.bin.format.pdb2.pdbreader.*;
import ghidra.app.util.importer.MessageLog;
-import ghidra.app.util.opinion.PeLoader;
import ghidra.app.util.pdb.PdbLocator;
import ghidra.app.util.pdb.PdbProgramAttributes;
import ghidra.app.util.pdb.pdbapplicator.PdbApplicator;
@@ -35,7 +32,6 @@ import ghidra.framework.options.Options;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.listing.Program;
import ghidra.util.Msg;
-import ghidra.util.SystemUtilities;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
@@ -63,7 +59,8 @@ public class PdbUniversalAnalyzer extends AbstractAnalyzer {
static final boolean DEFAULT_ENABLEMENT = true;
private static final String DESCRIPTION =
"Platform-independent PDB analyzer (No XML support).\n" +
- "NOTE: still undergoing development, so options may change.";
+ "NOTE: still undergoing development, so options may change.\n" +
+ "PDB Symbol Server searching is configured in Edit -> Symbol Server Config.\n";
//==============================================================================================
// Force-load a PDB file.
@@ -79,18 +76,7 @@ public class PdbUniversalAnalyzer extends AbstractAnalyzer {
private File DEFAULT_FORCE_LOAD_FILE = new File(PdbLocator.DEFAULT_SYMBOLS_DIR, "sample.pdb");
private File forceLoadFile;
- // Symbol Repository Path.
- private static final String OPTION_NAME_SYMBOLPATH = "Symbol Repository Path";
- private static final String OPTION_DESCRIPTION_SYMBOLPATH =
- "Directory path to root of Microsoft Symbol Repository Directory";
- private File symbolsRepositoryDir;
-
- // Include the PE-Header-Specified PDB path for searching for appropriate PDB file.
- private static final String OPTION_NAME_INCLUDE_PE_PDB_PATH =
- "Unsafe: Include PE PDB Path in PDB Search";
- private static final String OPTION_DESCRIPTION_INCLUDE_PE_PDB_PATH =
- "If checked, specifically searching for PDB in PE-Header-Specified Location.";
- private boolean includePeSpecifiedPdbPath = false;
+ private boolean searchRemoteLocations = false;
//==============================================================================================
// Additional instance data
@@ -162,34 +148,21 @@ public class PdbUniversalAnalyzer extends AbstractAnalyzer {
return true;
}
- if (failMissingFilename(programAttributes, log) ||
- failMissingAttributes(programAttributes, log)) {
- return true;
- }
-
- String pdbFilename;
- if (doForceLoad) {
- if (!confirmFile(forceLoadFile)) {
+ File pdbFile = null;
+ if (doForceLoad && forceLoadFile != null) {
+ if (!forceLoadFile.isFile()) {
logFailure("Force-load PDB file does not exist: " + forceLoadFile, log);
return false;
}
- pdbFilename = forceLoadFile.getAbsolutePath();
+ pdbFile = forceLoadFile;
}
else {
- PdbLocator locator = new PdbLocator(symbolsRepositoryDir);
- pdbFilename =
- locator.findPdb(program, programAttributes, !SystemUtilities.isInHeadlessMode(),
- includePeSpecifiedPdbPath, monitor, log, getName());
- if (pdbFilename == null) {
- if (!confirmDirectory(symbolsRepositoryDir)) {
- logFailure("PDB symbol repository directory not found: " + symbolsRepositoryDir,
- log);
- }
- Msg.info(this, "PDB analyzer failed to locate PDB file");
- return false;
- }
+ pdbFile = PdbAnalyzerCommon.findPdb(this, program, searchRemoteLocations, monitor);
+ }
+ if (pdbFile == null) {
+ // warnings have already been logged
+ return false;
}
- Msg.info(this, "PDB analyzer parsing file: " + pdbFilename);
PdbLog.message(
"================================================================================");
@@ -197,61 +170,33 @@ public class PdbUniversalAnalyzer extends AbstractAnalyzer {
PdbLog.message("Ghidra Version: " + Application.getApplicationVersion());
PdbLog.message(NAME);
PdbLog.message(DESCRIPTION);
- PdbLog.message("PDB Filename: " + pdbFilename + "\n");
+ PdbLog.message("PDB Filename: " + pdbFile + "\n");
- try (AbstractPdb pdb = PdbParser.parse(pdbFilename, pdbReaderOptions, monitor)) {
- monitor.setMessage("PDB: Parsing " + pdbFilename + "...");
+ try (AbstractPdb pdb = PdbParser.parse(pdbFile.getPath(), pdbReaderOptions, monitor)) {
+ monitor.setMessage("PDB: Parsing " + pdbFile + "...");
pdb.deserialize(monitor);
- PdbApplicator applicator = new PdbApplicator(pdbFilename, pdb);
+ PdbApplicator applicator = new PdbApplicator(pdbFile.getPath(), pdb);
applicator.applyTo(program, program.getDataTypeManager(), program.getImageBase(),
pdbApplicatorOptions, monitor, log);
}
catch (PdbException | IOException e) {
log.appendMsg(getName(),
- "Issue processing PDB file: " + pdbFilename + ":\n " + e.toString());
+ "Issue processing PDB file: " + pdbFile + ":\n " + e.toString());
return false;
}
return true;
}
- // TODO: I changed this method from what was lifted in the old code. I check for null string
- // and I also check for MSCOFF_NAME (TODO: check on the validity of this!!!). Also, changed
- // the comparison to a substring search from a .equals).
@Override
public boolean canAnalyze(Program program) {
- String executableFormat = program.getExecutableFormat();
- return executableFormat != null && (executableFormat.indexOf(PeLoader.PE_NAME) != -1);
- // TODO: Check for MSCOFF_NAME. Initial investigation shows that the .debug$T section of
- // the MSCOFF (*.obj) file has type records and the .debug$S section has symbol records.
- // More than that, in at least one instance, there has been a TypeServer2MsType type
- // record that give the GUID, age, and name of the PDB file associated with the MSCOFF
- // file. At this point in time, these two sections of the MSCOFF are read (header and
- // raw data), but we do not interpret these sections any further. Suggest that we "might"
- // want to parse some of these records at load time? Maybe not. We could, at analysis
- // time, add the ability to process these two sections (as part of analysis (though we
- // will not be aware of a PDB file yet), and upon discovery of a TypeServer2MsType (or
- // perhaps other?), proceed to find the file (if possible) and also process that file.
- // We posit that if a record indicates a separate PDB for the types (Note: MSFT indicates
- // that only data types will be found in an MSCOFF PDB file), then that will likely be
- // the only record in the .debug$T section.
- // TODO: If the MSCOFF file is located in a MSCOFF ARCHIVE (*.lib), there can be a PDB
- // associated with the archive. We currently do not pass on this association of the
- // PDB archive to each underlying MSCOFF file. Moreover, we believe that we are not
- // currently discovering the associated MSCOFF ARCHIVE PDB file when processing the
- // MSCOFF ARCHIVE. Initial indication is that each MSCOFF within the archive will have
- // the PDB file that it needs listed, even if redundant for each MSCOFF within the
- // archive.
-// return executableFormat != null && (executableFormat.indexOf(PeLoader.PE_NAME) != -1 ||
-// executableFormat.indexOf(MSCoffLoader.MSCOFF_NAME) != -1);
+ return PdbAnalyzerCommon.canAnalyzeProgram(program);
}
@Override
public void registerOptions(Options options, Program program) {
- symbolsRepositoryDir = PdbLocator.getDefaultPdbSymbolsDir();
-
// PDB file location information
if (developerMode) {
options.registerOption(OPTION_NAME_DO_FORCELOAD, Boolean.FALSE, null,
@@ -259,10 +204,9 @@ public class PdbUniversalAnalyzer extends AbstractAnalyzer {
options.registerOption(OPTION_NAME_FORCELOAD_FILE, OptionType.FILE_TYPE,
DEFAULT_FORCE_LOAD_FILE, null, OPTION_DESCRIPTION_FORCELOAD_FILE);
}
- options.registerOption(OPTION_NAME_SYMBOLPATH, OptionType.FILE_TYPE, symbolsRepositoryDir,
- null, OPTION_DESCRIPTION_SYMBOLPATH);
- options.registerOption(OPTION_NAME_INCLUDE_PE_PDB_PATH, includePeSpecifiedPdbPath, null,
- OPTION_DESCRIPTION_INCLUDE_PE_PDB_PATH);
+ options.registerOption(PdbAnalyzerCommon.OPTION_NAME_SEARCH_REMOTE_LOCATIONS,
+ searchRemoteLocations, null,
+ PdbAnalyzerCommon.OPTION_DESCRIPTION_SEARCH_REMOTE_LOCATIONS);
pdbReaderOptions.registerOptions(options);
pdbApplicatorOptions.registerAnalyzerOptions(options);
@@ -279,14 +223,8 @@ public class PdbUniversalAnalyzer extends AbstractAnalyzer {
forceLoadFile = options.getFile(OPTION_NAME_FORCELOAD_FILE, forceLoadFile);
}
- File symbolsDir = options.getFile(OPTION_NAME_SYMBOLPATH, symbolsRepositoryDir);
- if (!symbolsDir.equals(symbolsRepositoryDir)) {
- symbolsRepositoryDir = symbolsDir;
- PdbLocator.setDefaultPdbSymbolsDir(symbolsDir);
- }
-
- includePeSpecifiedPdbPath =
- options.getBoolean(OPTION_NAME_INCLUDE_PE_PDB_PATH, includePeSpecifiedPdbPath);
+ searchRemoteLocations = options.getBoolean(
+ PdbAnalyzerCommon.OPTION_NAME_SEARCH_REMOTE_LOCATIONS, searchRemoteLocations);
pdbReaderOptions.loadOptions(options);
pdbApplicatorOptions.loadAnalyzerOptions(options);
@@ -294,51 +232,40 @@ public class PdbUniversalAnalyzer extends AbstractAnalyzer {
//==============================================================================================
- private boolean failMissingFilename(PdbProgramAttributes attributes, MessageLog log) {
- if (doForceLoad) {
- return false; // PDB File property not used for forced load
- }
- if (StringUtils.isEmpty(attributes.getPdbFile())) {
- logFailure("Missing 'PDB File' program property, unable to locate PDB", log);
- return true;
- }
- return false;
- }
-
private void logFailure(String msg, MessageLog log) {
log.appendMsg(getName(), msg);
log.appendMsg(getName(), "Skipping PDB processing");
log.setStatus(msg);
}
- private boolean failMissingAttributes(PdbProgramAttributes attributes, MessageLog log) {
- if (doForceLoad) {
- return false; // Attributes not used for forced load
- }
- // RSDS version should only have GUID; non-RSDS version should only have Signature.
- String error;
- if ("RSDS".equals(attributes.getPdbVersion())) {
- if (!StringUtils.isEmpty(attributes.getPdbGuid())) {
- return false; // Don't fail.
- }
- error = "Missing 'PDB GUID' program property, unable to locate PDB.";
- }
- else {
- if (!StringUtils.isEmpty(attributes.getPdbSignature())) {
- return false; // Don't fail.
- }
- error = "Missing 'PDB Signature' program property, unable to locate PDB.";
- }
- logFailure(error, log);
- return true;
+ /**
+ * Sets the PDB file that will be used by the analyzer when it is next invoked
+ * on the specified program.
+ *
+ * Normally the analyzer would locate the PDB file on its own, but if a
+ * headless script wishes to override the analyzer's behaivor, it can
+ * use this method to specify a file.
+ *
+ * @param program {@link Program}
+ * @param pdbFile the pdb file
+ */
+ public static void setPdbFileOption(Program program, File pdbFile) {
+ PdbAnalyzerCommon.setPdbFileOption(NAME, program, pdbFile);
}
- private boolean confirmDirectory(File path) {
- return path.isDirectory();
+ /**
+ * Sets the "allow remote" option that will be used by the analyzer when it is next invoked
+ * on the specified program.
+ *
+ * Normally when the analyzer attempts to locate a matching PDB file it
+ * will default to NOT searching remote symbol servers. A headless script could
+ * use this method to allow the analyzer to search remote symbol servers.
+ *
+ * @param program {@link Program}
+ * @param allowRemote boolean flag, true means analyzer can search remote symbol
+ * servers
+ */
+ public static void setAllowRemoteOption(Program program, boolean allowRemote) {
+ PdbAnalyzerCommon.setAllowRemoteOption(NAME, program, allowRemote);
}
-
- private boolean confirmFile(File path) {
- return path.isFile();
- }
-
}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/PdbInfo.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/PdbInfo.java
deleted file mode 100644
index 4abeab3c76..0000000000
--- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/PdbInfo.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/* ###
- * 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.util.bin.format.pdb;
-
-import ghidra.app.util.bin.BinaryReader;
-import ghidra.app.util.bin.format.pe.debug.DebugCodeViewConstants;
-import ghidra.program.model.data.*;
-import ghidra.util.exception.DuplicateNameException;
-
-import java.io.IOException;
-
-public class PdbInfo implements PdbInfoIface {
- public final static int MAGIC =
- DebugCodeViewConstants.SIGNATURE_NB << 16 |
- DebugCodeViewConstants.VERSION_10;
-
- public static boolean isMatch(BinaryReader reader, int ptr) throws IOException {
- //read value out as big endian
- int value = reader.readByte(ptr ) << 24 |
- reader.readByte(ptr+1) << 16 |
- reader.readByte(ptr+2) << 8 |
- reader.readByte(ptr+3);
- return MAGIC == value;
- }
-
- private byte [] magic;
- private int offset;
- private int sig;
- private int age;
- private String pdbName;
-
- public PdbInfo(BinaryReader reader, int ptr) throws IOException {
- long origIndex = reader.getPointerIndex();
- reader.setPointerIndex(ptr);
- try {
- magic = reader.readNextByteArray(4);
- offset = reader.readNextInt();
- sig = reader.readNextInt();
- age = reader.readNextInt();
- pdbName = reader.readNextAsciiString();
- }
- finally {
- reader.setPointerIndex(origIndex);
- }
- }
-
- public byte [] getMagic() {
- return magic;
- }
-
- public int getOffset() {
- return offset;
- }
-
- public int getSig() {
- return sig;
- }
-
- public int getAge() {
- return age;
- }
-
- public String getPdbName() {
- return pdbName;
- }
-
- public DataType toDataType() throws DuplicateNameException, IOException {
- StructureDataType struct = new StructureDataType("PdbInfo", 0);
- struct.add(new StringDataType(), magic.length, "signature", null);
- struct.add(new DWordDataType(), "offset", null);
- struct.add(new DWordDataType(), "sig", null);
- struct.add(new DWordDataType(), "age", null);
- struct.add(new StringDataType(), pdbName.length(), "pdbname", null);
- struct.setCategoryPath(new CategoryPath("/PDB"));
- return struct;
- }
-}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/PdbInfoDotNet.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/PdbInfoDotNet.java
deleted file mode 100644
index 27ff8ecacc..0000000000
--- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/PdbInfoDotNet.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/* ###
- * 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.util.bin.format.pdb;
-
-import ghidra.app.util.bin.BinaryReader;
-import ghidra.app.util.bin.format.pe.debug.DebugCodeViewConstants;
-import ghidra.app.util.datatype.microsoft.GUID;
-import ghidra.app.util.datatype.microsoft.GuidDataType;
-import ghidra.program.model.data.*;
-import ghidra.util.exception.DuplicateNameException;
-
-import java.io.IOException;
-
-public class PdbInfoDotNet implements PdbInfoDotNetIface {
- public final static int MAGIC = DebugCodeViewConstants.SIGNATURE_DOT_NET << 16 |
- DebugCodeViewConstants.VERSION_DOT_NET;
-
- public static boolean isMatch(BinaryReader reader, int ptr) throws IOException {
- //read value out as big endian
- int value =
- reader.readByte(ptr) << 24 | reader.readByte(ptr + 1) << 16 |
- reader.readByte(ptr + 2) << 8 | reader.readByte(ptr + 3);
- return MAGIC == value;
- }
-
- private byte[] magic;
- private GUID guid;
- private int age;
- private String pdbName;
-
- public PdbInfoDotNet(BinaryReader reader, int ptr) throws IOException {
- long origIndex = reader.getPointerIndex();
- reader.setPointerIndex(ptr);
- try {
- magic = reader.readNextByteArray(4);
- guid = new GUID(reader);
- age = reader.readNextInt();
- pdbName = reader.readNextAsciiString();
- }
- finally {
- reader.setPointerIndex(origIndex);
- }
- }
-
- public String getPdbName() {
- return pdbName;
- }
-
- public int getAge() {
- return age;
- }
-
- public int getSignature() {
- return guid.getData1();
- }
-
- public GUID getGUID() {
- return guid;
- }
-
- public byte[] getMagic() {
- return magic;
- }
-
- public DataType toDataType() throws DuplicateNameException, IOException {
- StructureDataType struct = new StructureDataType("DotNetPdbInfo", 0);
- struct.add(new StringDataType(), magic.length, "signature", null);
- struct.add(new GuidDataType(), "guid", null);
- struct.add(new DWordDataType(), "age", null);
- if (pdbName.length() > 0) {
- struct.add(new StringDataType(), pdbName.length(), "pdbname", null);
- }
- struct.setCategoryPath(new CategoryPath("/PDB"));
- return struct;
- }
-}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/PdbParser.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/PdbParser.java
index e949271c8e..ea605100b5 100644
--- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/PdbParser.java
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/PdbParser.java
@@ -15,9 +15,10 @@
*/
package ghidra.app.util.bin.format.pdb;
-import java.io.*;
import java.util.*;
+import java.io.*;
+
import org.xml.sax.SAXException;
import docking.widgets.OptionDialog;
@@ -27,9 +28,7 @@ import ghidra.app.plugin.core.datamgr.util.DataTypeArchiveUtility;
import ghidra.app.services.DataTypeManagerService;
import ghidra.app.util.NamespaceUtils;
import ghidra.app.util.SymbolPath;
-import ghidra.app.util.importer.LibrarySearchPathManager;
import ghidra.app.util.importer.MessageLog;
-import ghidra.app.util.pdb.PdbLocator;
import ghidra.app.util.pdb.PdbProgramAttributes;
import ghidra.framework.*;
import ghidra.framework.options.Options;
@@ -83,6 +82,7 @@ public class PdbParser {
private PdbErrorHandler errHandler;
private PdbErrorReaderThread thread;
private boolean parsed = false;
+ private boolean allowNonExactMatch;
private CategoryPath pdbCategory;
@@ -94,13 +94,40 @@ public class PdbParser {
private PdbDataTypeParser dataTypeParser;
private Map namespaceMap = new TreeMap<>(); // false: simple namespace, true: class namespace
+ /**
+ * Creates a PdbParser instance.
+ *
+ * @param pdbFile the pdb file to parse, either .pdb or .pdb.xml
+ * @param program the {@link Program} to modify
+ * @param service {@link DataTypeManagerService}
+ * @param forceAnalysis boolean flag, currently always true, needs to be refactored out
+ * @param allowNonExactMatch boolean flag, if true skips warning user about mismatch
+ * between the program's PDB guid/id/age and the specified PDB file's guid/id/age, which
+ * can terminate the pdb import in headless
+ * @param monitor {@link TaskMonitor}, null ok
+ */
public PdbParser(File pdbFile, Program program, DataTypeManagerService service,
- boolean forceAnalysis, TaskMonitor monitor) {
- this(pdbFile, program, service, getPdbAttributes(program), forceAnalysis, monitor);
+ boolean forceAnalysis, boolean allowNonExactMatch, TaskMonitor monitor) {
+ this(pdbFile, program, service, getPdbAttributes(program), forceAnalysis,
+ allowNonExactMatch, monitor);
}
+ /**
+ * Creates a PdbParser instance.
+ *
+ * @param pdbFile the pdb file to parse, either .pdb or .pdb.xml
+ * @param program the {@link Program} to modify
+ * @param service {@link DataTypeManagerService}
+ * @param programAttributes the PDB information specified by the program
+ * @param forceAnalysis boolean flag, currently always true, needs to be refactored out
+ * @param allowNonExactMatch boolean flag, if true skips warning user about mismatch
+ * between the program's PDB guid/id/age and the specified PDB file's guid/id/age, which
+ * can terminate the pdb import in headless
+ * @param monitor {@link TaskMonitor}, null ok
+ */
public PdbParser(File pdbFile, Program program, DataTypeManagerService service,
- PdbProgramAttributes programAttributes, boolean forceAnalysis, TaskMonitor monitor) {
+ PdbProgramAttributes programAttributes, boolean forceAnalysis,
+ boolean allowNonExactMatch, TaskMonitor monitor) {
this.pdbFile = pdbFile;
this.pdbCategory = new CategoryPath(CategoryPath.ROOT, pdbFile.getName());
this.program = program;
@@ -108,8 +135,9 @@ public class PdbParser {
this.service = service;
this.forceAnalysis = forceAnalysis;
this.monitor = monitor != null ? monitor : TaskMonitor.DUMMY;
- this.isXML = pdbFile.getAbsolutePath().endsWith(PdbFileType.XML.toString());
+ this.isXML = pdbFile.getName().toLowerCase().endsWith(PdbFileType.XML.toString());
this.programAttributes = programAttributes;
+ this.allowNonExactMatch = allowNonExactMatch;
}
/**
@@ -184,12 +212,12 @@ public class PdbParser {
}
private void checkFileType() throws PdbException {
- String pdbFilename = pdbFile.getName();
+ String pdbFilename = pdbFile.getName().toLowerCase();
if (!pdbFilename.endsWith(PdbFileType.PDB.toString()) &&
!pdbFilename.endsWith(PdbFileType.XML.toString())) {
throw new PdbException(
- "\nInvalid file type (expecting .pdb or .pdb.xml): '" + pdbFilename + "'");
+ "\nInvalid file type (expecting .pdb or .pdb.xml): '" + pdbFile.getName() + "'");
}
}
@@ -616,18 +644,21 @@ public class PdbParser {
pdbGuid = pdbGuid.toUpperCase();
pdbGuid = "{" + pdbGuid + "}";
- if (!xmlGuid.equals(pdbGuid)) {
- warning = "PDB signature does not match.\n" + "Program GUID: " + pdbGuid +
- "\nXML GUID: " + xmlGuid; }
- else {
- // Also check that PDB ages match, if they are both available
- if ((xmlAge != null) && (pdbAge != null)) {
+ if (!allowNonExactMatch) {
+ if (!xmlGuid.equals(pdbGuid)) {
+ warning = "PDB signature does not match.\n" + "Program GUID: " + pdbGuid +
+ "\nXML GUID: " + xmlGuid;
+ }
+ else {
+ // Also check that PDB ages match, if they are both available
+ if ((xmlAge != null) && (pdbAge != null)) {
- int pdbAgeDecimal = Integer.parseInt(pdbAge, 16);
- int xmlAgeDecimal = Integer.parseInt(xmlAge);
+ int pdbAgeDecimal = Integer.parseInt(pdbAge, 16);
+ int xmlAgeDecimal = Integer.parseInt(xmlAge);
- if (xmlAgeDecimal != pdbAgeDecimal) {
- warning = "PDB ages do not match.";
+ if (xmlAgeDecimal != pdbAgeDecimal) {
+ warning = "PDB ages do not match.";
+ }
}
}
}
@@ -1042,17 +1073,6 @@ public class PdbParser {
return new PdbProgramAttributes(program);
}
- /**
- * Find the PDB associated with the given program using its attributes.
- * The PDB path information within the program information will not be used.
- *
- * @param program program for which to find a matching PDB
- * @return matching PDB for program, or null
- */
- public static File findPDB(Program program) {
- return findPDB(getPdbAttributes(program), false, null, null);
- }
-
/**
* Determine if the PDB has previously been loaded for the specified program.
* @param program program for which to find a matching PDB
@@ -1062,273 +1082,6 @@ public class PdbParser {
return getPdbAttributes(program).isPdbLoaded();
}
- /**
- * Find the PDB associated with the given program using its attributes, specifying the
- * location where symbols are stored.
- *
- * @param program program for which to find a matching PDB
- * @param includePeSpecifiedPdbPath to also check the PE-header-specified PDB path
- * @param symbolsRepositoryDir location where downloaded symbols are stored
- * @return matching PDB for program, or null
- */
- public static File findPDB(Program program, boolean includePeSpecifiedPdbPath,
- File symbolsRepositoryDir) {
- return findPDB(getPdbAttributes(program), includePeSpecifiedPdbPath, symbolsRepositoryDir,
- null);
- }
-
- /**
- * Find a matching PDB file using attributes associated with the program. User can specify the
- * type of file to search from (.pdb or .pdb.xml).
- *
- * @param pdbAttributes PDB attributes associated with the program
- * @param includePeSpecifiedPdbPath to also check the PE-header-specified PDB path
- * @param symbolsRepositoryDir location of the local symbols repository (can be null)
- * @param fileType type of file to search for (can be null)
- * @return matching PDB file (or null, if not found)
- */
- public static File findPDB(PdbProgramAttributes pdbAttributes,
- boolean includePeSpecifiedPdbPath, File symbolsRepositoryDir, PdbFileType fileType) {
-
- // Store potential names of PDB files and potential locations of those files,
- // so that all possible combinations can be searched.
- // LinkedHashSet is used when we need to preserve order
- Set guidSubdirPaths = new HashSet<>();
-
- String guidAgeString = pdbAttributes.getGuidAgeCombo();
- if (guidAgeString == null) {
- return null;
- }
-
- List potentialPdbNames = pdbAttributes.getPotentialPdbFilenames();
- for (String potentialName : potentialPdbNames) {
- guidSubdirPaths.add(File.separator + potentialName + File.separator + guidAgeString);
- }
-
- return checkPathsForPdb(symbolsRepositoryDir, guidSubdirPaths, potentialPdbNames, fileType,
- pdbAttributes, includePeSpecifiedPdbPath);
- }
-
- /**
- * Check potential paths in a specific order. If the symbolsRepositoryPath parameter is
- * supplied and the directory exists, that directory will be searched first for the
- * matching PDB file.
- *
- * If the file type is supplied, then only that file type will be searched for. Otherwise,
- * the search process depends on the current operating system that Ghidra is running from:
- *
- * - Windows: look in the symbolsRepositoryPath for a matching .pdb file. If one does not
- * exist, look for a .pdb.xml file in symbolsRepositoryPath. If not found, then
- * search for a matching .pdb file, then .pdb.xml file, in other directories.
- * - non-Windows: look in the symbolsRepositoryPath for a matching .pdb.xml file. If one does
- * not exist, look for a .pdb file. If a .pdb file is found, return an error saying
- * that it was found, but could not be processed. If no matches found in
- * symbolsRepositoryPath, then look for .pdb.xml file, then .pdb.xml file in other
- * directories.
- *
- * @param symbolsRepositoryDir location of the local symbols repository (can be null)
- * @param guidSubdirPaths subdirectory paths (that include the PDB's GUID) that may contain
- * a matching PDB
- * @param potentialPdbNames all potential filenames for the PDB file(s) that match the program
- * @param fileType file type to search for (can be null)
- * @param pdbAttributes PDB attributes associated with the program
- * @return matching PDB file, if found (else null)
- */
- private static File checkPathsForPdb(File symbolsRepositoryDir, Set guidSubdirPaths,
- List potentialPdbNames, PdbFileType fileType,
- PdbProgramAttributes pdbAttributes, boolean includePeSpecifiedPdbPath) {
-
- File foundPdb = null;
- Set symbolsRepoPaths =
- getSymbolsRepositoryPaths(symbolsRepositoryDir, guidSubdirPaths);
- Set predefinedPaths =
- getPredefinedPaths(guidSubdirPaths, pdbAttributes, includePeSpecifiedPdbPath);
- boolean fileTypeSpecified = (fileType != null);
- boolean checkForXml;
-
- // If the file type is specified, look for that type of file only.
- if (fileTypeSpecified) {
- checkForXml = (fileType == PdbFileType.XML) ? true : false;
-
- foundPdb = checkForPDBorXML(symbolsRepoPaths, potentialPdbNames, checkForXml);
-
- if (foundPdb != null) {
- return foundPdb;
- }
-
- foundPdb = checkForPDBorXML(predefinedPaths, potentialPdbNames, checkForXml);
-
- return foundPdb;
- }
-
- // If the file type is not specified, look for both file types, starting with the
- // file type that's most appropriate for the Operating System (PDB for Windows, XML for
- // non-Windows).
- checkForXml = onWindows ? false : true;
-
- // Start by searching in symbolsRepositoryPath, if available.
- if (!symbolsRepoPaths.isEmpty()) {
- foundPdb = checkSpecificPathsForPdb(symbolsRepoPaths, potentialPdbNames, checkForXml);
- }
-
- if (foundPdb != null) {
- return foundPdb;
- }
-
- return checkSpecificPathsForPdb(predefinedPaths, potentialPdbNames, checkForXml);
-
- }
-
- private static File checkSpecificPathsForPdb(Set paths, List potentialPdbNames,
- boolean checkForXmlFirst) {
-
- File foundPdb = checkForPDBorXML(paths, potentialPdbNames, checkForXmlFirst);
-
- if (foundPdb != null) {
- return foundPdb;
- }
-
- foundPdb = checkForPDBorXML(paths, potentialPdbNames, !checkForXmlFirst);
-
- return foundPdb;
- }
-
- private static Set getSymbolsRepositoryPaths(File symbolsRepositoryDir,
- Set guidSubdirPaths) {
-
- Set symbolsRepoPaths = new LinkedHashSet<>();
-
- // Collect sub-directories of the symbol repository that exist
- if (symbolsRepositoryDir != null && symbolsRepositoryDir.isDirectory()) {
-
- for (String guidSubdir : guidSubdirPaths) {
- File testDir = new File(symbolsRepositoryDir, guidSubdir);
- if (testDir.isDirectory()) {
- symbolsRepoPaths.add(testDir);
- }
- }
-
- // Check outer folder last
- symbolsRepoPaths.add(symbolsRepositoryDir);
- }
-
- return symbolsRepoPaths;
- }
-
- // Get list of "paths we know about" to search for PDBs
- private static Set getPredefinedPaths(Set guidSubdirPaths,
- PdbProgramAttributes pdbAttributes, boolean includePeSpecifiedPdbPath) {
-
- Set predefinedPaths = new LinkedHashSet<>();
-
- getPathsFromAttributes(pdbAttributes, includePeSpecifiedPdbPath, predefinedPaths);
- getSymbolPaths(PdbLocator.DEFAULT_SYMBOLS_DIR, guidSubdirPaths, predefinedPaths);
- getSymbolPaths(PdbLocator.WINDOWS_SYMBOLS_DIR, guidSubdirPaths, predefinedPaths);
- getLibraryPaths(guidSubdirPaths, predefinedPaths);
-
- return predefinedPaths;
- }
-
- private static void getLibraryPaths(Set guidSubdirPaths, Set predefinedPaths) {
- String[] libraryPaths = LibrarySearchPathManager.getLibraryPaths();
-
- File libFile, subDir;
-
- for (String path : libraryPaths) {
-
- if ((libFile = new File(path)).isDirectory()) {
- predefinedPaths.add(libFile);
-
- // Check alternate locations
- for (String guidSubdir : guidSubdirPaths) {
- if ((subDir = new File(path, guidSubdir)).isDirectory()) {
- predefinedPaths.add(subDir);
- }
- }
- }
- }
- }
-
- private static void getSymbolPaths(File symbolsDir, Set guidSubdirPaths,
- Set predefinedPaths) {
- // Don't have to call .exists(), since .isDirectory() does that already
- if (symbolsDir == null || !symbolsDir.isDirectory()) {
- return;
- }
- predefinedPaths.add(symbolsDir);
-
- // Check alternate locations
- String specialPdbPath = symbolsDir.getAbsolutePath();
-
- for (String guidSubdir : guidSubdirPaths) {
- File testDir = new File(specialPdbPath + guidSubdir);
- if (testDir.isDirectory()) {
- predefinedPaths.add(testDir);
- }
- }
- }
-
- private static void getPathsFromAttributes(PdbProgramAttributes pdbAttributes,
- boolean includePeSpecifiedPdbPath, Set predefinedPaths) {
- if (pdbAttributes != null) {
-
- String currentPath = pdbAttributes.getPdbFile();
-
- if (currentPath != null && includePeSpecifiedPdbPath) {
- File parentDir = new File(currentPath).getParentFile();
-
- if (parentDir != null && parentDir.exists()) {
- predefinedPaths.add(parentDir);
- }
- }
-
- currentPath = pdbAttributes.getExecutablePath();
-
- if (currentPath != null && !currentPath.equals("unknown")) {
- File parentDir = new File(currentPath).getParentFile();
-
- if (parentDir != null && parentDir.exists()) {
- predefinedPaths.add(parentDir);
- }
- }
- }
- }
-
- /**
- * Returns the first PDB-type file found. Assumes list of potentialPdbDirs is in the order
- * in which the directories should be searched.
- *
- * @param potentialPdbDirs potential PDB directories
- * @param potentialPdbNames potential PDB names
- * @param findXML - if true, only searches for the .pdb.xml version of the .pdb file
- * @return the first file found
- */
- private static File checkForPDBorXML(Set potentialPdbDirs, List potentialPdbNames,
- boolean findXML) {
-
- File pdb;
-
- for (File pdbPath : potentialPdbDirs) {
-
- for (String filename : potentialPdbNames) {
-
- if (findXML) {
- pdb = new File(pdbPath, filename + PdbFileType.XML.toString());
- }
- else {
- pdb = new File(pdbPath, filename);
- }
-
- // Note: isFile() also checks for existence
- if (pdb.isFile()) {
- return pdb;
- }
- }
- }
-
- return null;
- }
-
PdbDataTypeParser getDataTypeParser() {
if (program == null) {
throw new AssertException("Parser was not constructed with program");
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/PdbIdentifiers.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/PdbIdentifiers.java
index cc7ac1ddfc..996877ceab 100644
--- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/PdbIdentifiers.java
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/PdbIdentifiers.java
@@ -15,12 +15,14 @@
*/
package ghidra.app.util.bin.format.pdb2.pdbreader;
+import java.util.Objects;
+
import ghidra.app.util.datatype.microsoft.GUID;
/**
* This class holds fields used to identify a PDB.
*
- * These are Version, Signature, Age, and GUID. Som identifiers can be null if not found in
+ * These are Version, Signature, Age, and GUID. Some identifiers can be null if not found in
* the specific version of the PDB.
*/
public class PdbIdentifiers {
@@ -38,7 +40,7 @@ public class PdbIdentifiers {
* @param age age used to verify PDB against age stored in program
* @param guid The GUID (can be null for older PDBs).
*/
- PdbIdentifiers(int version, int signature, int age, GUID guid, Processor processor) {
+ public PdbIdentifiers(int version, int signature, int age, GUID guid, Processor processor) {
this.version = version;
this.signature = signature;
this.age = age;
@@ -78,4 +80,33 @@ public class PdbIdentifiers {
return guid;
}
+
+ @Override
+ public String toString() {
+ return ((guid != null) ? guid.toString() : String.format("%08X", signature)) + ", " + age +
+ ", " + version + ", " + processor;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(age, guid, processor, signature, version);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ PdbIdentifiers other = (PdbIdentifiers) obj;
+ return age == other.age && Objects.equals(guid, other.guid) &&
+ processor == other.processor && signature == other.signature &&
+ version == other.version;
+ }
+
}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/PdbLog.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/PdbLog.java
index 919e6e6b92..50934f9304 100644
--- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/PdbLog.java
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/PdbLog.java
@@ -32,7 +32,8 @@ public class PdbLog {
private static Writer nullWriter;
private static Writer fileWriter;
- private static boolean enabled = false;
+ private static final boolean SYSTEM_LOGGING_ENABLED = Boolean.getBoolean("pdb.logging");
+ private static boolean enabled = SYSTEM_LOGGING_ENABLED;
/**
* Enable or disable future messages to be output to the appropriate log resource. This
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/PdbReaderOptions.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/PdbReaderOptions.java
index ceca848f52..e6b93a2a96 100644
--- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/PdbReaderOptions.java
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/PdbReaderOptions.java
@@ -15,14 +15,12 @@
*/
package ghidra.app.util.bin.format.pdb2.pdbreader;
-import java.io.IOException;
import java.nio.charset.Charset;
import java.util.List;
import ghidra.framework.options.Options;
import ghidra.program.model.data.CharsetInfo;
import ghidra.util.HelpLocation;
-import ghidra.util.Msg;
/**
* Options used while reading a PDB ({@link AbstractPdb}) that control various aspects. These
@@ -34,16 +32,6 @@ public class PdbReaderOptions extends Exception {
// Developer turn on/off options that are in still in development.
private static final boolean developerMode = false;
- // Perform logging of PDB information for debugging/development.
- // NOTE: This logging mechanism is not intended to live the full life of this tool, but to
- // aid in getting feedback from the field during its early development.
- private static final String OPTION_NAME_PDB_READER_APPLICATOR_LOGGING =
- "[Dev] PDB Reader/Applicator Debug Logging";
- private static final String OPTION_DESCRIPTION_PDB_READER_APPLICATOR_LOGGING =
- "If checked, logs information to the pdb.analyzer.log file for debug/development.";
- private static final boolean DEFAULT_PDB_READER_APPLICATOR_LOGGING = false;
- private boolean pdbLogging;
-
// Sets the one-byte Charset to be used for PDB processing.
// NOTE: This "Option" is not intended as a permanent part of this analyzer. Should be
// replaced by target-specific Charset.
@@ -83,9 +71,6 @@ public class PdbReaderOptions extends Exception {
public void registerOptions(Options options) {
HelpLocation help = null;
- options.registerOption(OPTION_NAME_PDB_READER_APPLICATOR_LOGGING, pdbLogging, null,
- OPTION_DESCRIPTION_PDB_READER_APPLICATOR_LOGGING);
-
if (developerMode) {
options.registerOption(OPTION_NAME_ONE_BYTE_CHARSET_NAME, oneByteCharsetName, help,
OPTION_DESCRIPTION_ONE_BYTE_CHARSET_NAME);
@@ -96,9 +81,6 @@ public class PdbReaderOptions extends Exception {
public void loadOptions(Options options) {
- pdbLogging = options.getBoolean(OPTION_NAME_PDB_READER_APPLICATOR_LOGGING, pdbLogging);
- setPdbLogging();
-
if (developerMode) {
oneByteCharsetName =
options.getString(OPTION_NAME_ONE_BYTE_CHARSET_NAME, oneByteCharsetName);
@@ -113,23 +95,12 @@ public class PdbReaderOptions extends Exception {
* Set the options to their default values
*/
public void setDefaults() {
- pdbLogging = DEFAULT_PDB_READER_APPLICATOR_LOGGING;
- setPdbLogging();
oneByteCharsetName = DEFAULT_ONE_BYTE_CHARSET_NAME;
wideCharCharsetName = DEFAULT_TWO_BYTE_CHARSET_NAME;
setOneByteCharsetForName(oneByteCharsetName);
setWideCharCharsetForName(wideCharCharsetName);
}
- private void setPdbLogging() {
- try {
- PdbLog.setEnabled(pdbLogging);
- }
- catch (IOException e) {
- Msg.info(this, "Failure opening PDB log file: ", e);
- }
- }
-
/**
* Returns list of Charsets names that encode one byte characters.
* @return Charsets that encode one byte characters.
diff --git a/Ghidra/Features/PDB/src/main/java/pdb/AskPdbOptionsDialog.java b/Ghidra/Features/PDB/src/main/java/pdb/AskPdbOptionsDialog.java
deleted file mode 100644
index 5fd619fe33..0000000000
--- a/Ghidra/Features/PDB/src/main/java/pdb/AskPdbOptionsDialog.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/* ###
- * 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 pdb;
-
-import java.awt.BorderLayout;
-import java.awt.Component;
-
-import javax.swing.*;
-
-import docking.DialogComponentProvider;
-import docking.DockingWindowManager;
-import docking.widgets.combobox.GComboBox;
-import ghidra.app.util.bin.format.pdb.PdbParser;
-import ghidra.app.util.pdb.pdbapplicator.PdbApplicatorControl;
-import ghidra.util.layout.PairLayout;
-
-class AskPdbOptionsDialog extends DialogComponentProvider {
-
- private boolean isCanceled;
-
- private boolean useMsDiaParser;
- private PdbApplicatorControl control = PdbApplicatorControl.ALL;
-
- /**
- * Popup PDB loader options
- * @param parent parent component or null
- * @param isPdbFile true if file to be loaded is a PDB file, false
- * if MsDia XML file.
- */
- AskPdbOptionsDialog(Component parent, boolean isPdbFile) {
- super("Load PDB Options", true, true, true, false);
-
- JPanel panel = new JPanel(new BorderLayout(10, 10));
- panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
-
- JPanel optionsPanel = new JPanel(new PairLayout(10, 10));
-
- final GComboBox controlCombo =
- new GComboBox<>(PdbApplicatorControl.values());
- controlCombo.setSelectedItem(PdbApplicatorControl.ALL);
- controlCombo.addActionListener(e -> {
- control = (PdbApplicatorControl) controlCombo.getSelectedItem();
- });
-
- optionsPanel.add(new JLabel("PDB Parser:"));
-
- if (isPdbFile) {
- useMsDiaParser = false; // Use PDB Universal by default
- if (PdbParser.onWindows) {
- final GComboBox combo =
- new GComboBox<>(new String[] { "PDB Universal", "PDB MSDIA" });
- combo.setSelectedIndex(0);
- controlCombo.setEnabled(!useMsDiaParser);
- combo.addActionListener(e -> {
- useMsDiaParser = (combo.getSelectedIndex() == 1);
- controlCombo.setEnabled(!useMsDiaParser);
- if (useMsDiaParser) {
- controlCombo.setSelectedItem(PdbApplicatorControl.ALL);
- }
- });
- optionsPanel.add(combo);
- }
- else {
- useMsDiaParser = false;
- JLabel label = new JLabel("PDB Universal");
- //label.setForeground(Color.red); // set color to emphasize prototype status
- optionsPanel.add(label);
- }
- }
- else {
- useMsDiaParser = true; // XML file only supported by MsDia parser
- return; // no interaction currently required
- }
-
- optionsPanel.add(new JLabel("Control:"));
- optionsPanel.add(controlCombo);
-
- panel.add(optionsPanel, BorderLayout.CENTER);
-
- addWorkPanel(panel);
-
- addApplyButton();
- addCancelButton();
-
- setDefaultButton(applyButton);
- setRememberSize(false);
-
- DockingWindowManager.showDialog(parent, AskPdbOptionsDialog.this);
- }
-
- @Override
- protected void applyCallback() {
- isCanceled = false;
- close();
- }
-
- @Override
- protected void cancelCallback() {
- isCanceled = true;
- close();
- }
-
- boolean isCanceled() {
- return isCanceled;
- }
-
- boolean useMsDiaParser() {
- return useMsDiaParser;
- }
-
- PdbApplicatorControl getApplicatorControl() {
- return control;
- }
-
-}
diff --git a/Ghidra/Features/PDB/src/main/java/pdb/AskPdbUrlDialog.java b/Ghidra/Features/PDB/src/main/java/pdb/AskPdbUrlDialog.java
deleted file mode 100644
index 015bc2bece..0000000000
--- a/Ghidra/Features/PDB/src/main/java/pdb/AskPdbUrlDialog.java
+++ /dev/null
@@ -1,230 +0,0 @@
-/* ###
- * 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 pdb;
-
-import java.awt.*;
-import java.awt.event.*;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Scanner;
-
-import javax.swing.*;
-
-import docking.DialogComponentProvider;
-import docking.DockingWindowManager;
-import docking.widgets.dialogs.ObjectChooserDialog;
-import docking.widgets.label.GDLabel;
-import generic.jar.ResourceFile;
-import generic.util.WindowUtilities;
-import ghidra.framework.Application;
-import ghidra.framework.preferences.Preferences;
-import ghidra.util.MessageType;
-
-public class AskPdbUrlDialog extends DialogComponentProvider {
-
- private boolean isCanceled;
- private JLabel label;
- private JTextField textField;
- private KeyListener keyListener;
- private List choices = null;
-
- protected AskPdbUrlDialog(String dialogTitle, String message) {
- this(null, dialogTitle, message, null);
- }
-
- public AskPdbUrlDialog(String dialogTitle, String message, Object defaultValue) {
- this(null, dialogTitle, message, defaultValue);
- }
-
- public AskPdbUrlDialog(Component parent, String title, String message) {
- this(parent, title, message, null);
- }
-
- public AskPdbUrlDialog(final Component parent, String title, String message,
- Object defaultValue) {
- super(title, true, true, true, false);
-
- // create the key listener all the text fields will use
- keyListener = new KeyAdapter() {
- @Override
- public void keyPressed(KeyEvent e) {
- int keyCode = e.getKeyCode();
- if (keyCode == KeyEvent.VK_ENTER) {
- okCallback();
- }
- }
- };
-
- JPanel panel = new JPanel(new BorderLayout(10, 10));
- panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
-
- label = new GDLabel(message);
- panel.add(label, BorderLayout.WEST);
-
- textField = new JTextField(40);
- textField.setName("JTextField");//for JUnits...
- textField.addKeyListener(keyListener);
- textField.setText(defaultValue == null ? "" : defaultValue.toString());
- textField.selectAll();
- panel.add(textField, BorderLayout.CENTER);
-
- if (urlFileAvailable()) {
- JButton urlButton = new JButton("Choose from known URLs");
- urlButton.addActionListener(e -> urlCallback());
-
- panel.add(urlButton, BorderLayout.EAST);
- }
-
- addWorkPanel(panel);
-
- addOKButton();
- addCancelButton();
-
- setDefaultButton(okButton);
- setRememberSize(false);
-
- DockingWindowManager.showDialog(parent, AskPdbUrlDialog.this);
- }
-
- @Override
- protected void addOKButton() {
- okButton = new JButton("Download from URL");
- okButton.setMnemonic('K');
- okButton.setName("OK");
- okButton.addActionListener(e -> okCallback());
- addButton(okButton);
- }
-
- private boolean urlFileAvailable() {
- List urlFiles = Application.findFilesByExtensionInApplication(".pdburl");
-
- if (urlFiles.size() == 0) {
- return false;
- }
-
- try {
- InputStream urlFileContents = null;
- String currentLine;
- choices = new ArrayList<>();
-
- for (ResourceFile urlFile : urlFiles) {
- urlFileContents = urlFile.getInputStream();
-
- Scanner scanner = new Scanner(urlFileContents);
- try {
- while (scanner.hasNextLine()) {
-
- currentLine = scanner.nextLine();
-
- // Find first comma, split on that
- int commaIndex = currentLine.indexOf(',');
-
- if (commaIndex > -1) {
- choices.add(new URLChoice(currentLine.substring(0, commaIndex).trim(),
- currentLine.substring(commaIndex + 1).trim()));
- }
- }
- }
- finally {
- scanner.close();
- }
- }
- }
- catch (IOException ioe) {
- return false;
- }
- return true;
- }
-
- private void saveCurrentDimensions() {
- Rectangle bounds = getBounds();
- Window window = WindowUtilities.windowForComponent(getComponent());
-
- if (window != null) {
- Point location = window.getLocation();
- bounds.x = location.x;
- bounds.y = location.y;
- }
-
- StringBuffer buffer = new StringBuffer();
- buffer.append(bounds.x).append(":");
- buffer.append(bounds.y).append(":");
- buffer.append(bounds.width).append(":");
- buffer.append(bounds.height).append(":");
- Preferences.setProperty("Ask Dialog Bounds", buffer.toString());
- }
-
- public Object getValue() {
- return textField.getText();
- }
-
- @Override
- protected void okCallback() {
- isCanceled = false;
- if (textField.getText().length() == 0) {
- setStatusText("Please enter a valid URL.");
- return;
- }
- saveCurrentDimensions();
- close();
- }
-
- @Override
- protected void cancelCallback() {
- isCanceled = true;
- saveCurrentDimensions();
- close();
- }
-
- private void urlCallback() {
-
- ObjectChooserDialog urlDialog = new ObjectChooserDialog<>("Choose a URL",
- URLChoice.class, choices, "getNetwork", "getUrl");
-
- DockingWindowManager activeInstance = DockingWindowManager.getActiveInstance();
- activeInstance.showDialog(urlDialog);
-
- URLChoice pickedUrl = urlDialog.getSelectedObject();
-
- if (pickedUrl != null) {
- textField.setText(pickedUrl.getUrl());
-
- if (pickedUrl.getNetwork().equalsIgnoreCase("internet")) {
- setStatusText(
- "WARNING: Check your organization's security policy before downloading files from the internet.",
- MessageType.ERROR);
- }
- else {
- setStatusText(null);
- }
- }
- }
-
- public boolean isCanceled() {
- return isCanceled;
- }
-
- public String getValueAsString() {
- Object val = getValue();
- if ("".equals(val)) {
- return null;
- }
- return val != null ? val.toString() : null;
- }
-
-}
diff --git a/Ghidra/Features/PDB/src/main/java/pdb/LoadPdbTask.java b/Ghidra/Features/PDB/src/main/java/pdb/LoadPdbTask.java
index 985a0fb2c7..9d6fd80aa3 100644
--- a/Ghidra/Features/PDB/src/main/java/pdb/LoadPdbTask.java
+++ b/Ghidra/Features/PDB/src/main/java/pdb/LoadPdbTask.java
@@ -19,23 +19,18 @@ import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
-import docking.DockingWindowManager;
-import docking.widgets.OptionDialog;
-import docking.widgets.dialogs.MultiLineMessageDialog;
import ghidra.app.plugin.core.analysis.*;
import ghidra.app.plugin.core.datamgr.archive.DuplicateIdException;
import ghidra.app.services.DataTypeManagerService;
import ghidra.app.util.bin.format.pdb.PdbException;
import ghidra.app.util.bin.format.pdb.PdbParser;
-import ghidra.app.util.bin.format.pdb2.pdbreader.*;
+import ghidra.app.util.bin.format.pdb2.pdbreader.AbstractPdb;
+import ghidra.app.util.bin.format.pdb2.pdbreader.PdbReaderOptions;
import ghidra.app.util.importer.MessageLog;
-import ghidra.app.util.pdb.PdbLocator;
-import ghidra.app.util.pdb.PdbProgramAttributes;
import ghidra.app.util.pdb.pdbapplicator.*;
import ghidra.framework.options.Options;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.listing.Program;
-import ghidra.util.Msg;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.*;
@@ -45,6 +40,8 @@ class LoadPdbTask extends Task {
private final Program program;
private final boolean useMsDiaParser;
private final PdbApplicatorControl control; // PDB Universal Parser only
+ private String resultMessages;
+ private Exception resultException;
LoadPdbTask(Program program, File pdbFile, boolean useMsDiaParser, PdbApplicatorControl control,
DataTypeManagerService service) {
@@ -57,7 +54,7 @@ class LoadPdbTask extends Task {
}
@Override
- public void run(final TaskMonitor monitor) {
+ public void run(TaskMonitor monitor) {
WrappingTaskMonitor wrappedMonitor = new WrappingTaskMonitor(monitor) {
@Override
@@ -99,42 +96,30 @@ class LoadPdbTask extends Task {
try {
AutoAnalysisManager.getAnalysisManager(program).scheduleWorker(worker, null, true,
wrappedMonitor);
- if (log.hasMessages()) {
- MultiLineMessageDialog dialog = new MultiLineMessageDialog("Load PDB File",
- "There were warnings/errors loading the PDB file.", log.toString(),
- MultiLineMessageDialog.WARNING_MESSAGE, false);
- DockingWindowManager.showDialog(null, dialog);
- }
}
- catch (InterruptedException | CancelledException e1) {
+ catch (InterruptedException | CancelledException e) {
// ignore
}
catch (InvocationTargetException e) {
- String message;
-
- Throwable t = e.getCause();
-
- if (t == null) {
- message = "Unknown cause";
- }
- else {
- message = t.getMessage();
-
- if (message == null) {
- message = t.toString();
- }
- }
-
- message = "Error processing PDB file: " + pdbFile + ".\n" + message;
-
- Msg.showError(getClass(), null, "Load PDB Failed", message, t);
+ resultException = e;
+ }
+ if (log.hasMessages()) {
+ resultMessages = log.toString();
}
}
+ String getResultMessages() {
+ return resultMessages;
+ }
+
+ Exception getResultException() {
+ return resultException;
+ }
+
private boolean parseWithMsDiaParser(MessageLog log, TaskMonitor monitor)
throws IOException, CancelledException {
- PdbParser parser = new PdbParser(pdbFile, program, service, true, monitor);
+ PdbParser parser = new PdbParser(pdbFile, program, service, true, true, monitor);
try {
parser.parse();
parser.openDataTypeArchives();
@@ -147,9 +132,6 @@ class LoadPdbTask extends Task {
return false;
}
- // NOTE: OptionDialog will not display an empty line
- private static final String BLANK_LINE = " \n";
-
private boolean parseWithNewParser(MessageLog log, TaskMonitor monitor)
throws IOException, CancelledException {
@@ -159,32 +141,8 @@ class LoadPdbTask extends Task {
pdbApplicatorOptions.setProcessingControl(control);
- PdbProgramAttributes programAttributes = new PdbProgramAttributes(program);
-
try (AbstractPdb pdb = ghidra.app.util.bin.format.pdb2.pdbreader.PdbParser.parse(
pdbFile.getAbsolutePath(), pdbReaderOptions, monitor)) {
-
- PdbIdentifiers identifiers = pdb.getIdentifiers();
- if (!PdbLocator.verifyPdbSignature(programAttributes, identifiers)) {
-
- StringBuilder builder = new StringBuilder();
- builder.append("Selected PDB does not match program's PDB specification!\n");
- builder.append(BLANK_LINE);
- builder.append("Program's PDB specification:\n");
- builder.append(PdbLocator.formatPdbIdentifiers(programAttributes));
- builder.append(BLANK_LINE);
- builder.append("Selected PDB file specification:\n");
- builder.append(
- PdbLocator.formatPdbIdentifiers(pdbFile.getAbsolutePath(), identifiers));
- builder.append(BLANK_LINE);
- builder.append("Do you wish to force load this PDB?");
-
- if (OptionDialog.YES_OPTION != OptionDialog.showYesNoDialog(null,
- "Confirm PDB Load", builder.toString())) {
- return false;
- }
- }
-
monitor.setMessage("PDB: Parsing " + pdbFile + "...");
pdb.deserialize(monitor);
PdbApplicator applicator = new PdbApplicator(pdbFile.getAbsolutePath(), pdb);
diff --git a/Ghidra/Features/PDB/src/main/java/pdb/PdbInitializer.java b/Ghidra/Features/PDB/src/main/java/pdb/PdbInitializer.java
deleted file mode 100644
index 3ec5f94b63..0000000000
--- a/Ghidra/Features/PDB/src/main/java/pdb/PdbInitializer.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/* ###
- * IP: GHIDRA
- * REVIEWED: YES
- *
- * 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 pdb;
-
-import ghidra.app.util.bin.format.pdb.*;
-import ghidra.framework.*;
-
-public class PdbInitializer implements ModuleInitializer {
- public void run() {
- PluggableServiceRegistry.registerPluggableService(PdbFactory.class,
- new GhidraPdbFactory());
- }
- @Override
- public String getName() {
- return "PDB Support Module";
- }
-}
diff --git a/Ghidra/Features/PDB/src/main/java/pdb/PdbPlugin.java b/Ghidra/Features/PDB/src/main/java/pdb/PdbPlugin.java
index a8b9729860..4180b875b2 100644
--- a/Ghidra/Features/PDB/src/main/java/pdb/PdbPlugin.java
+++ b/Ghidra/Features/PDB/src/main/java/pdb/PdbPlugin.java
@@ -16,30 +16,36 @@
package pdb;
import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.util.*;
+import java.util.stream.Collectors;
import javax.swing.SwingConstants;
-import docking.action.MenuData;
+import docking.DockingWindowManager;
+import docking.action.builder.ActionBuilder;
+import docking.tool.ToolConstants;
import docking.widgets.OptionDialog;
-import docking.widgets.filechooser.GhidraFileChooser;
-import docking.widgets.filechooser.GhidraFileChooserMode;
+import docking.widgets.dialogs.MultiLineMessageDialog;
import ghidra.app.CorePluginPackage;
import ghidra.app.context.ProgramActionContext;
-import ghidra.app.context.ProgramContextAction;
import ghidra.app.plugin.PluginCategoryNames;
import ghidra.app.plugin.core.analysis.AutoAnalysisManager;
+import ghidra.app.plugin.core.analysis.PdbAnalyzerCommon;
import ghidra.app.services.DataTypeManagerService;
-import ghidra.app.util.bin.format.pdb.PdbParser;
-import ghidra.app.util.pdb.pdbapplicator.PdbApplicatorControl;
import ghidra.framework.plugintool.*;
import ghidra.framework.plugintool.util.PluginStatus;
+import ghidra.framework.preferences.Preferences;
import ghidra.program.model.listing.Program;
-import ghidra.program.util.GhidraProgramUtilities;
import ghidra.util.HelpLocation;
import ghidra.util.Msg;
-import ghidra.util.filechooser.ExtensionFileFilter;
-import ghidra.util.task.TaskBuilder;
-import ghidra.util.task.TaskLauncher;
+import ghidra.util.exception.CancelledException;
+import ghidra.util.task.*;
+import pdb.symbolserver.*;
+import pdb.symbolserver.ui.ConfigPdbDialog;
+import pdb.symbolserver.ui.LoadPdbDialog;
+import pdb.symbolserver.ui.LoadPdbDialog.LoadPdbResults;
//@formatter:off
@PluginInfo(
@@ -51,9 +57,14 @@ import ghidra.util.task.TaskLauncher;
)
//@formatter:on
public class PdbPlugin extends Plugin {
+ private static final String PDB_SYMBOL_SERVER_OPTIONS = "PdbSymbolServer";
+ private static final String SYMBOL_STORAGE_DIR_OPTION =
+ PDB_SYMBOL_SERVER_OPTIONS + ".Symbol_Storage_Directory";
+ private static final String SYMBOL_SEARCH_PATH_OPTION =
+ PDB_SYMBOL_SERVER_OPTIONS + ".Symbol_Search_Path";
- private ProgramContextAction loadPdbAction;
- private GhidraFileChooser pdbChooser;
+ // the name of the help directory under src/main/help/help/topics
+ public static final String PDB_PLUGIN_HELP_TOPIC = "Pdb";
public PdbPlugin(PluginTool tool) {
super(tool);
@@ -62,33 +73,34 @@ public class PdbPlugin extends Plugin {
}
private void createActions() {
- loadPdbAction = new ProgramContextAction("Load PDB File", this.getName()) {
+ new ActionBuilder("Load PDB File", this.getName())
+ .supportsDefaultToolContext(true)
+ .withContext(ProgramActionContext.class)
+ .validContextWhen(pac -> pac.getProgram() != null &&
+ PdbAnalyzerCommon.canAnalyzeProgram(pac.getProgram()))
+ .menuPath(ToolConstants.MENU_FILE, "Load PDB File...")
+ .menuGroup("Import PDB", "3")
+ .helpLocation(new HelpLocation(PDB_PLUGIN_HELP_TOPIC, "Load PDB File"))
+ .onAction(pac -> loadPDB(pac))
+ .buildAndInstall(tool);
- @Override
- public boolean isEnabledForContext(ProgramActionContext context) {
- return context.getProgram() != null;
- }
-
- @Override
- protected void actionPerformed(ProgramActionContext programContext) {
- loadPDB();
- }
- };
-
- MenuData menuData =
- new MenuData(new String[] { "&File", "Load PDB File..." }, null, "Import PDB");
- menuData.setMenuSubGroup("3"); // below the major actions in the "Import/Export" group
- loadPdbAction.setMenuBarData(menuData);
-
- loadPdbAction.setEnabled(false);
- loadPdbAction.setHelpLocation(new HelpLocation("ImporterPlugin", loadPdbAction.getName()));
- tool.addAction(loadPdbAction);
+ new ActionBuilder("Symbol Server Config", this.getName())
+ .menuPath(ToolConstants.MENU_EDIT, "Symbol Server Config")
+ .menuGroup(ToolConstants.TOOL_OPTIONS_MENU_GROUP)
+ .helpLocation(new HelpLocation(PDB_PLUGIN_HELP_TOPIC, "Symbol Server Config"))
+ .onAction(ac -> configPDB())
+ .buildAndInstall(tool);
}
- private void loadPDB() {
- Program program = GhidraProgramUtilities.getCurrentProgram(tool);
- AutoAnalysisManager aam = AutoAnalysisManager.getAnalysisManager(program);
- if (aam.isAnalyzing()) {
+ private void configPDB() {
+ ConfigPdbDialog.showSymbolServerConfig();
+ }
+
+ private void loadPDB(ProgramActionContext pac) {
+ Program program = pac.getProgram();
+ AutoAnalysisManager currentAutoAnalysisManager =
+ AutoAnalysisManager.getAnalysisManager(program);
+ if (currentAutoAnalysisManager.isAnalyzing()) {
Msg.showWarn(getClass(), null, "Load PDB",
"Unable to load PDB file while analysis is running.");
return;
@@ -109,27 +121,20 @@ public class PdbPlugin extends Plugin {
}
}
+ File pdbFile = null;
try {
- File pdb = getPdbFile(program);
- if (pdb == null) {
+ LoadPdbResults loadPdbResults = LoadPdbDialog.choosePdbForProgram(program);
+ if (loadPdbResults == null) {
tool.setStatusInfo("Loading PDB was cancelled.");
return;
}
-
- boolean isPdbFile = pdb.getName().toLowerCase().endsWith(".pdb");
-
- AskPdbOptionsDialog optionsDialog = new AskPdbOptionsDialog(null, isPdbFile);
- if (optionsDialog.isCanceled()) {
- return;
- }
-
- boolean useMsDiaParser = optionsDialog.useMsDiaParser();
- PdbApplicatorControl control = optionsDialog.getApplicatorControl();
+ pdbFile = loadPdbResults.pdbFile;
tool.setStatusInfo("");
- DataTypeManagerService service = tool.getService(DataTypeManagerService.class);
- if (service == null) {
+ DataTypeManagerService dataTypeManagerService =
+ tool.getService(DataTypeManagerService.class);
+ if (dataTypeManagerService == null) {
Msg.showWarn(getClass(), null, "Load PDB",
"Unable to locate DataTypeService in the current tool.");
return;
@@ -138,34 +143,144 @@ public class PdbPlugin extends Plugin {
// note: We intentionally use a 0-delay here. Our underlying task may show modal
// dialog prompts. We want the task progress dialog to be showing before any
// prompts appear.
-
- LoadPdbTask task = new LoadPdbTask(program, pdb, useMsDiaParser, control, service);
- TaskBuilder.withTask(task)
+ LoadPdbTask loadPdbTask = new LoadPdbTask(program, pdbFile,
+ loadPdbResults.useMsDiaParser, loadPdbResults.control, dataTypeManagerService);
+ TaskBuilder.withTask(loadPdbTask)
.setStatusTextAlignment(SwingConstants.LEADING)
.setLaunchDelay(0);
- new TaskLauncher(task, null, 0);
+ new TaskLauncher(loadPdbTask, null, 0);
+
+ // Check for error messages & exceptions and handle them here
+ // (previously handled by the task, but dialog parenting issues in a modal
+ // task cause timing issues)
+ if (loadPdbTask.getResultException() != null) {
+ throw loadPdbTask.getResultException();
+ }
+ else if (loadPdbTask.getResultMessages() != null) {
+ MultiLineMessageDialog dialog = new MultiLineMessageDialog("Load PDB File",
+ "There were warnings/errors loading PDB file: " + pdbFile,
+ loadPdbTask.getResultMessages(),
+ MultiLineMessageDialog.WARNING_MESSAGE, false);
+ DockingWindowManager.showDialog(null, dialog);
+ }
}
- catch (Exception pe) {
- Msg.showError(getClass(), null, "Error Loading PDB", pe.getMessage(), pe);
+ catch (Exception e) {
+ String message = null;
+ if (e instanceof InvocationTargetException && e.getCause() != null) {
+ message =
+ Objects.requireNonNullElse(e.getCause().getMessage(), e.getCause().toString());
+ }
+ else {
+ message = Objects.requireNonNullElse(e.getMessage(), e.toString());
+ }
+ Msg.showError(this, null, "Error Loading PDB",
+ "Error processing PDB file: " + pdbFile + "\n" + message, e);
}
}
- private File getPdbFile(Program program) {
- File pdbFile = PdbParser.findPDB(program);
- if (pdbChooser == null) {
- pdbChooser = new GhidraFileChooser(tool.getToolFrame());
- pdbChooser.setTitle("Select PDB file to load:");
- pdbChooser.setApproveButtonText("Select PDB");
- pdbChooser.setFileSelectionMode(GhidraFileChooserMode.FILES_ONLY);
- pdbChooser.setFileFilter(new ExtensionFileFilter(new String[] { "pdb", "xml" },
- "Program Database Files and PDB XML Representations"));
- }
+ //-------------------------------------------------------------------------------------------------------
- if (pdbFile != null) {
- pdbChooser.setSelectedFile(pdbFile);
- }
+ /**
+ * Searches the currently configured symbol server paths for a Pdb symbol file.
+ *
+ * @param program the program associated with the requested pdb file
+ * @param findOptions options that control how to search for the symbol file
+ * @param monitor a {@link TaskMonitor} that allows the user to cancel
+ * @return a File that points to the found Pdb symbol file, or null if no file was found
+ */
+ public static File findPdb(Program program, Set findOptions, TaskMonitor monitor) {
- File selectedFile = pdbChooser.getSelectedFile();
- return selectedFile;
+ try {
+ SymbolFileInfo symbolFileInfo = SymbolFileInfo.fromMetadata(program.getMetadata());
+ if (symbolFileInfo == null) {
+ return null;
+ }
+ // make a copy and add in the ONLY_FIRST_RESULT option
+ findOptions = findOptions.isEmpty() ? EnumSet.noneOf(FindOption.class)
+ : EnumSet.copyOf(findOptions);
+ findOptions.add(FindOption.ONLY_FIRST_RESULT);
+
+ SymbolServerInstanceCreatorContext temporarySymbolServerInstanceCreatorContext =
+ SymbolServerInstanceCreatorRegistry.getInstance().getContext(program);
+
+ SymbolServerService temporarySymbolServerService =
+ getSymbolServerService(temporarySymbolServerInstanceCreatorContext);
+
+ List results =
+ temporarySymbolServerService.find(symbolFileInfo, findOptions, monitor);
+ if (!results.isEmpty()) {
+ return temporarySymbolServerService.getSymbolFile(results.get(0), monitor);
+ }
+ }
+ catch (CancelledException e) {
+ // ignore
+ }
+ catch (IOException e) {
+ Msg.error(PdbPlugin.class, "Error getting symbol file", e);
+ }
+ return null;
+ }
+
+ /**
+ * Returns a new instance of a {@link SymbolServerService} configured with values from the
+ * application's preferences, defaulting to a minimal instance if there is no config.
+ *
+ * @param symbolServerInstanceCreatorContext an object that provides the necessary context to
+ * the SymbolServerInstanceCreatorRegistry to create the SymbolServers that are listed in the
+ * config values
+ * @return a new {@link SymbolServerService} instance, never null
+ */
+ public static SymbolServerService getSymbolServerService(
+ SymbolServerInstanceCreatorContext symbolServerInstanceCreatorContext) {
+ SymbolServer temporarySymbolServer =
+ symbolServerInstanceCreatorContext.getSymbolServerInstanceCreatorRegistry()
+ .newSymbolServer(Preferences.getProperty(SYMBOL_STORAGE_DIR_OPTION, "", true),
+ symbolServerInstanceCreatorContext);
+ SymbolStore symbolStore =
+ (temporarySymbolServer instanceof SymbolStore) ? (SymbolStore) temporarySymbolServer
+ : new SameDirSymbolStore(symbolServerInstanceCreatorContext.getRootDir());
+ List symbolServers =
+ symbolServerInstanceCreatorContext.getSymbolServerInstanceCreatorRegistry()
+ .createSymbolServersFromPathList(getSymbolSearchPaths(),
+ symbolServerInstanceCreatorContext);
+ return new SymbolServerService(symbolStore, symbolServers);
+ }
+
+ /**
+ * Persists the {@link SymbolStore} and {@link SymbolServer}s contained in the
+ * {@link SymbolServerService}.
+ *
+ * @param symbolServerService {@link SymbolServerService} to save, or null if clear p
+ * reference values
+ */
+ public static void saveSymbolServerServiceConfig(SymbolServerService symbolServerService) {
+ if (symbolServerService != null) {
+ Preferences.setProperty(SYMBOL_STORAGE_DIR_OPTION,
+ symbolServerService.getSymbolStore().getName());
+
+ String path = symbolServerService.getSymbolServers()
+ .stream()
+ .map(SymbolServer::getName)
+ .collect(Collectors.joining(";"));
+ Preferences.setProperty(SYMBOL_SEARCH_PATH_OPTION, path);
+ }
+ else {
+ Preferences.setProperty(SYMBOL_STORAGE_DIR_OPTION, null);
+ Preferences.setProperty(SYMBOL_SEARCH_PATH_OPTION, null);
+ }
+ }
+
+ private static List getSymbolSearchPaths() {
+ String searchPathStr = Preferences.getProperty(SYMBOL_SEARCH_PATH_OPTION, "", true);
+
+ String[] pathParts = searchPathStr.split(";");
+ List result = new ArrayList<>();
+ for (String part : pathParts) {
+ part = part.trim();
+ if (!part.isEmpty()) {
+ result.add(part);
+ }
+ }
+ return result;
}
}
diff --git a/Ghidra/Features/PDB/src/main/java/pdb/PdbSymbolServerPlugin.java b/Ghidra/Features/PDB/src/main/java/pdb/PdbSymbolServerPlugin.java
deleted file mode 100644
index 2e3037d425..0000000000
--- a/Ghidra/Features/PDB/src/main/java/pdb/PdbSymbolServerPlugin.java
+++ /dev/null
@@ -1,856 +0,0 @@
-/* ###
- * 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 pdb;
-
-import java.io.*;
-import java.net.*;
-import java.nio.file.Files;
-import java.nio.file.StandardCopyOption;
-import java.util.List;
-import java.util.Properties;
-
-import docking.action.MenuData;
-import docking.widgets.OptionDialog;
-import docking.widgets.filechooser.GhidraFileChooser;
-import docking.widgets.filechooser.GhidraFileChooserMode;
-import ghidra.app.CorePluginPackage;
-import ghidra.app.context.ProgramActionContext;
-import ghidra.app.context.ProgramContextAction;
-import ghidra.app.plugin.PluginCategoryNames;
-import ghidra.app.plugin.core.analysis.AutoAnalysisManager;
-import ghidra.app.services.DataTypeManagerService;
-import ghidra.app.util.bin.format.pdb.PdbException;
-import ghidra.app.util.bin.format.pdb.PdbParser;
-import ghidra.app.util.bin.format.pdb.PdbParser.PdbFileType;
-import ghidra.app.util.pdb.PdbLocator;
-import ghidra.app.util.pdb.PdbProgramAttributes;
-import ghidra.app.util.pdb.pdbapplicator.PdbApplicatorControl;
-import ghidra.framework.Application;
-import ghidra.framework.plugintool.*;
-import ghidra.framework.plugintool.util.PluginStatus;
-import ghidra.framework.preferences.Preferences;
-import ghidra.net.http.HttpUtil;
-import ghidra.program.model.listing.Program;
-import ghidra.program.util.GhidraProgramUtilities;
-import ghidra.util.*;
-import ghidra.util.exception.CancelledException;
-import ghidra.util.task.TaskLauncher;
-
-/**
- * Plugin that allows users to download PDB files from a Symbol Server URL.
- *
- * PDB files can be of type .pdb, .pdb.xml, and .cab:
- * - .pdb files are Microsoft's native representation of debug symbols
- * - .pdb.xml files are representations of .pdb files using XML. Ghidra provides a script
- * for users to transform .pdb files into .pdb.xml files.
- * - .cab (cabinet) files are compressed .pdb files. A Symbol Server set up using Microsoft
- * tools will allow download of .cab files, relying on the user to extract a .pdb from
- * the .cab file.
- *
- * The Symbol Server can be a URL to a hosted file system or a server that was set up using Microsoft
- * tools. This code will also take care of PKI authentication, if needed by the server.
- */
-//@formatter:off
-@PluginInfo(
- status = PluginStatus.RELEASED,
- packageName = CorePluginPackage.NAME,
- category = PluginCategoryNames.COMMON,
- shortDescription = "Download PDB Files from a Symbol Server",
- description = "This plugin manages the downloading of PDB files from a Symbol Server."
-)
-//@formatter:on
-public class PdbSymbolServerPlugin extends Plugin {
-
- private static final String symbolServerEnvVar = "_NT_SYMBOL_PATH";
-
- private static final String PDB_URL_PROPERTY = "PDB Symbol Server";
-
- private static String expectedPdbContentType = "application/octet-stream";
- private static String expectedXmlContentType = "text/xml";
- private static Properties urlProperties = null;
-
- // Store last-selected value(s) for askXxx methods
- private static String serverUrl = null;
- private static File localDir = null;
- private PdbFileType fileType = PdbFileType.PDB;
- private boolean includePePdbPath = false;
-
- enum RetrieveFileType {
- PDB, XML, CAB
- }
-
- enum ReturnPdbStatus {
- DOWNLOADED, EXISTING, NOT_FOUND;
- }
-
- public PdbSymbolServerPlugin(PluginTool tool) {
- super(tool);
- createActions();
-
- urlProperties = new Properties();
- // Version # appears to be debugger version. 6.3.9600.17298
- urlProperties.setProperty("User-Agent", "Microsoft-Symbol-Server/6.3.9600.17298");
- }
-
- /**
- * Sets the {@link PdbFileType}
- * @param fileType the {@link PdbFileType}
- */
- public void setPdbFileType(PdbFileType fileType) {
- this.fileType = fileType;
- }
-
- private void createActions() {
- ProgramContextAction downloadPdbAction =
- new ProgramContextAction("Download_PDB_File", this.getName()) {
-
- @Override
- public boolean isEnabledForContext(ProgramActionContext context) {
- return context.getProgram() != null;
- }
-
- @Override
- protected void actionPerformed(ProgramActionContext programContext) {
- downloadPDB();
- }
- };
-
- MenuData menuData =
- new MenuData(new String[] { "&File", "Download PDB File..." }, null, "Import PDB");
- menuData.setMenuSubGroup("4");
- downloadPdbAction.setMenuBarData(menuData);
-
- downloadPdbAction.setEnabled(false);
- downloadPdbAction.setHelpLocation(new HelpLocation("Pdb", downloadPdbAction.getName()));
- tool.addAction(downloadPdbAction);
- }
-
- private void downloadPDB() {
- Program program = GhidraProgramUtilities.getCurrentProgram(tool);
-
- try {
-
- PdbFileAndStatus returnPdb = getPdbFile(program);
-
- File returnedPdbFile = returnPdb.getPdbFile();
-
- switch (returnPdb.getPdbStatus()) {
- case NOT_FOUND:
- Msg.showInfo(getClass(), null, "Error", "Could not download the " + fileType +
- " file for this version of " + program.getName() + " from " + serverUrl);
- break;
-
- case DOWNLOADED:
- Msg.showInfo(getClass(), null, "File Retrieved", "Downloaded and saved file '" +
- returnedPdbFile.getName() + "' to \n" + returnedPdbFile.getParent());
- // no break here, since we want it to continue
-
- case EXISTING:
- tryToLoadPdb(returnedPdbFile, program);
- break;
- }
- }
- catch (CancelledException ce) {
- tool.setStatusInfo("Downloading PDB from Symbol Server was cancelled.");
- return;
- }
- catch (PdbException pe) {
- Msg.showInfo(getClass(), null, "Error", "Error: " + pe.getMessage());
- }
- catch (IOException ioe) {
- Msg.showInfo(getClass(), null, "Error",
- ioe.getClass().getSimpleName() + ": " + ioe.getMessage());
-
- // If URL connection failed, then reset the dialog to show the default symbol server
- // (instead of the last one we attempted to connect to).
- if (ioe instanceof UnknownHostException) {
- serverUrl = null;
- }
- }
- }
-
- /**
- * Retrieves PDB, using GUI to interact with user to get PDB and Symbol Server Information
- *
- * @param program program for which to retrieve the PDB file
- * @return the retrieved PDB file (could be in .pdb or .xml form)
- * @throws CancelledException upon user cancellation
- * @throws IOException if an I/O issue occurred
- * @throws PdbException if there was a problem with the PDB attributes
- */
- private PdbFileAndStatus getPdbFile(Program program)
- throws CancelledException, IOException, PdbException {
-
- try {
- PdbProgramAttributes pdbAttributes = PdbParser.getPdbAttributes(program);
-
- if (pdbAttributes.getGuidAgeCombo() == null) {
- throw new PdbException(
- "Incomplete PDB information (GUID/Signature and/or age) associated with this program.\n" +
- "Either the program is not a PE, or it was not compiled with debug information.");
- }
-
- // 1. Ask if user wants .pdb or .pdb.xml file
- fileType = askForFileExtension();
-
- // 1.5 Ask if should search PE-specified PDB path.
- includePePdbPath = askIncludePeHeaderPdbPath();
-
- String symbolEnv = System.getenv(symbolServerEnvVar);
- if (symbolEnv != null) {
- parseSymbolEnv(symbolEnv);
- }
-
- // 2. Ask for local storage location
- localDir = askForLocalStorageLocation();
-
- // 3. See if PDB can be found locally
- File pdbFile = PdbParser.findPDB(pdbAttributes, includePePdbPath, localDir, fileType);
-
- // 4. If not found locally, ask if it should be retrieved
- if (pdbFile != null && pdbFile.getName().endsWith(fileType.toString())) {
-
- String htmlString =
- HTMLUtilities.toWrappedHTML("Found potential* matching PDB at: \n " +
- pdbFile.getAbsolutePath() + "\n\n* Match determined by file name only; " +
- "not vetted for matching GUID/version." +
- "\n\nContinue with download?\n\n" +
- "(downloaded file will be saved in a directory of the form " +
- localDir.getAbsolutePath() + File.separator + "<pdbFilename>" +
- File.separator + "<GUID>" + File.separator + ")");
-
- // Warn that there is already a matching file
- int response =
- OptionDialog.showYesNoDialog(null, "Potential Matching PDB Found", htmlString);
-
- switch (response) {
- case 0:
- // User cancelled
- throw new CancelledException();
-
- case 1:
- // Yes -- do nothing here
- break;
-
- case 2:
- // No
- return new PdbFileAndStatus(pdbFile, ReturnPdbStatus.EXISTING);
-
- default:
- // do nothing
- }
- }
-
- // 5. Ask for Symbol Server location
- serverUrl = askForSymbolServerUrl();
-
- // Fix up URL
- if (!serverUrl.endsWith("/")) {
- serverUrl += "/";
- }
-
- File downloadedPdb = attemptToDownloadPdb(pdbAttributes, serverUrl, localDir);
-
- if (downloadedPdb != null) {
- return new PdbFileAndStatus(downloadedPdb, ReturnPdbStatus.DOWNLOADED);
- }
-
- return new PdbFileAndStatus();
- }
- finally {
- // Store the dialog choices
- Preferences.store();
- }
- }
-
- private void parseSymbolEnv(String envString) {
-
- // Expect the environment string to be of the form:
- // srv*[local cache]*[private symbol server]*https://msdl.microsoft.com/download/symbols
- // srv*c:\symbols*https://msdl.microsoft.com/download/symbols
-
- if (!envString.startsWith("srv") && !envString.startsWith("SRV")) {
- return;
- }
-
- String[] envParts = envString.split("\\*");
-
- if (envParts.length < 3) {
- return;
- }
-
- File storageDir = new File(envParts[1]);
- if (storageDir.isDirectory()) {
- localDir = storageDir;
- }
-
- serverUrl = envParts[2];
-
- Msg.info(getClass(), "Using server URL: " + serverUrl);
- }
-
- private PdbFileType askForFileExtension() throws CancelledException {
- //@formatter:off
- int choice = OptionDialog.showOptionDialog(
- null,
- "pdb or pdb.xml",
- "Download a .pdb or .pdb.xml file?",
- "PDB",
- "XML");
- //@formatter:on
-
- if (choice == OptionDialog.CANCEL_OPTION) {
- throw new CancelledException();
- }
- return (choice == OptionDialog.OPTION_ONE) ? PdbFileType.PDB : PdbFileType.XML;
- }
-
- private boolean askIncludePeHeaderPdbPath() throws CancelledException {
- //@formatter:off
- int choice = OptionDialog.showOptionDialog(
- null,
- "PE-specified PDB Path",
- "Unsafe: Include PE-specified PDB Path in search for existing PDB",
- "Yes",
- "No");
- //@formatter:on
-
- if (choice == OptionDialog.CANCEL_OPTION) {
- throw new CancelledException();
- }
- return (choice == OptionDialog.OPTION_ONE);
- }
-
- String askForSymbolServerUrl() throws CancelledException {
-
- AskPdbUrlDialog dialog;
- String dialogResponse = null;
- String storedURL;
-
- if (serverUrl != null) {
- storedURL = serverUrl;
- }
- else {
- storedURL = Preferences.getProperty(PDB_URL_PROPERTY);
-
- if (storedURL == null) {
- storedURL = "";
- }
- }
-
- while (dialogResponse == null) {
- dialog = new AskPdbUrlDialog("Symbol Server URL", "What is the Symbol Server URL?",
- storedURL);
-
- if (dialog.isCanceled()) {
- throw new CancelledException();
- }
-
- dialogResponse = dialog.getValueAsString();
-
- // Make sure user has included either 'http' or 'https'
- if (!dialogResponse.startsWith("http")) {
- Msg.showInfo(getClass(), null, "Incomplete URL",
- "URL should start with either 'http' or 'https'.");
- dialogResponse = null;
- continue;
- }
-
- // Make sure that URL has valid syntax
- try {
- new URL(dialogResponse);
- }
- catch (MalformedURLException malExc) {
- Msg.showInfo(getClass(), null, "Malformed URL", malExc.toString());
- dialogResponse = null;
- }
- }
-
- Preferences.setProperty(PDB_URL_PROPERTY, dialogResponse);
-
- return dialogResponse;
- }
-
- private File askForLocalStorageLocation() throws CancelledException {
-
- final GhidraFileChooser fileChooser = new GhidraFileChooser(tool.getActiveWindow());
-
- // Need to store the variable in an array to allow the final variable to be reassigned.
- // Using an array prevents the compiler from warning about "The final local variable
- // cannot be assigned, since it is defined in an enclosing type."
- final File[] chosenDir = new File[1];
-
- File testDirectory = null;
-
- // localDir is not null if we already parsed the _NT_SYMBOL_PATH environment var
- if (localDir != null) {
- testDirectory = localDir;
- }
- else {
- testDirectory = PdbLocator.getDefaultPdbSymbolsDir();
- }
-
- final File storedDirectory = testDirectory;
-
- Runnable r = () -> {
- while (chosenDir[0] == null && !fileChooser.wasCancelled()) {
- fileChooser.setSelectedFile(storedDirectory);
-
- fileChooser.setTitle("Select Location to Save Retrieved File");
- fileChooser.setApproveButtonText("OK");
- fileChooser.setFileSelectionMode(GhidraFileChooserMode.DIRECTORIES_ONLY);
- chosenDir[0] = fileChooser.getSelectedFile();
-
- if (chosenDir[0] != null) {
- if (!chosenDir[0].exists()) {
- Msg.showInfo(getClass(), null, "Directory does not exist",
- "The directory '" + chosenDir[0].getAbsolutePath() +
- "' does not exist. Please create it or choose a valid directory.");
- chosenDir[0] = null;
- }
- else if (chosenDir[0].isFile()) {
- Msg.showInfo(getClass(), null, "Invalid Directory",
- "The location '" + chosenDir[0].getAbsolutePath() +
- "' represents a file, not a directory. Please choose a directory.");
- chosenDir[0] = null;
- }
- }
- }
- };
- SystemUtilities.runSwingNow(r);
-
- if (fileChooser.wasCancelled()) {
- throw new CancelledException();
- }
-
- PdbLocator.setDefaultPdbSymbolsDir(chosenDir[0]);
-
- return chosenDir[0];
- }
-
- /**
- * Attempt to download a file from a URL and save it to the specified location.
- *
- * @param fileUrl URL from which to download the file
- * @param fileDestination location at which to save the downloaded file
- * @return whether download/save succeeded
- * @throws IOException if an I/O issue occurred
- * @throws PdbException if issue with PKI certificate
- */
- boolean retrieveFile(String fileUrl, File fileDestination) throws IOException, PdbException {
- return retrieveFile(fileUrl, fileDestination, null);
- }
-
- /**
- * Attempt to download a file from a URL and save it to the specified location.
- *
- * @param fileUrl URL from which to download the file
- * @param fileDestination location at which to save the downloaded file
- * @param retrieveProperties optional HTTP request header values to be included (may be null)
- * @return whether download/save succeeded
- * @throws IOException if an I/O issue occurred
- * @throws PdbException if issue with PKI certificate
- */
- boolean retrieveFile(String fileUrl, File fileDestination, Properties retrieveProperties)
- throws IOException, PdbException {
-
- String expectedContentType =
- (fileType == PdbFileType.PDB) ? expectedPdbContentType : expectedXmlContentType;
-
- try {
- String contentType =
- HttpUtil.getFile(fileUrl, retrieveProperties, true, fileDestination);
-
- if (contentType != null && !contentType.equals(expectedContentType)) {
- fileDestination.delete();
- return false;
- }
- }
- catch (IOException ioe) {
-
- // No PKI Certificate installed
- if (ioe.getMessage().equals("Forbidden")) {
- throw new PdbException(
- "PKI Certificate needed for user authentication.\nTo set a " +
- "certificate, use the Project Window's 'Edit -> Set PKI Certificate' Action.");
- }
-
- if (!ioe.getMessage().equals("Not Found")) {
- throw ioe;
- }
- }
-
- return fileDestination.exists();
-
- }
-
- /**
- * Take given file and move it to the specified destination folder in the location
- * <destination folder>/<pdbFilename>/>guidAgeString< (subfolders that do not
- * already exist will be created).
- *
- * @param destinationFolder root folder to which the given file will be moved
- * @param pdbFilename name of PDB file (subfolder with this name will be created under destination
- * folder, if it doesn't already exist)
- * @param guidAgeString guidAge string of the PDB (subfolder with this name will be created under
- * <destination folder>/<pdbFilename> folder, if it doesn't already exist)
- * @param downloadFilename name of final moved file (can be same as pdbFilename)
- * @param tempFile actual file to be moved
- * @return file that was moved (and optionally renamed) in its new location
- * @throws IOException if there was an IO-related problem making the directory or moving the file
- */
- File createSubFoldersAndMoveFile(File destinationFolder, String pdbFilename,
- String guidAgeString, String downloadFilename, File tempFile) throws IOException {
-
- File pdbOuterSaveDir = makeDirectory(destinationFolder, pdbFilename);
- File pdbInnerSaveDir = makeDirectory(pdbOuterSaveDir, guidAgeString);
-
- File finalDestFile = new File(pdbInnerSaveDir, downloadFilename);
-
- try {
- Files.move(tempFile.toPath(), finalDestFile.toPath(),
- StandardCopyOption.REPLACE_EXISTING);
- }
- catch (IOException e) {
- tempFile.delete();
- throw new IOException("Could not save file: " + finalDestFile.getAbsolutePath());
- }
-
- return finalDestFile;
- }
-
- private File makeDirectory(File parentFolder, String directoryName) throws IOException {
- File newDir = new File(parentFolder, directoryName);
-
- if (newDir.isFile()) {
- throw new IOException("Trying to create folder " + newDir.getAbsolutePath() +
- ",\nbut it shares the same name as an existing file.\n" +
- "Please try downloading PDB again, selecting a " +
- "non-conflicting destination folder.");
- }
-
- if (!newDir.isDirectory()) {
- boolean madeDir = newDir.mkdir();
- if (!madeDir) {
- throw new IOException(
- "Trying to create parent folders to store PDB file. Could not create directory " +
- newDir.getAbsolutePath() + ".");
- }
- }
-
- return newDir;
- }
-
- /**
- * Expand cabinet (.cab) files (Windows compressed format).
- *
- * When on Windows, use the 'expand' command (should already be included with the OS).
- * When on Unix/Mac, use 'cabextract', which has been included with Ghidra.
- *
- * @param cabFile file to expand/uncompress
- * @param targetFilename file to save uncompressed *.pdb to
- * @return the file that was uncompressed
- * @throws PdbException if failure with cabinet extraction
- * @throws IOException if issue starting the {@link ProcessBuilder}
- */
- File uncompressCabFile(File cabFile, String targetFilename) throws PdbException, IOException {
-
- String cabextractPath = null;
- String[] cabextractCmdLine;
-
- if (PdbParser.onWindows) {
- File cabextractExe = new File("C:\\Windows\\System32\\expand.exe");
-
- if (!cabextractExe.exists()) {
- throw new PdbException(
- "Expected to find cabinet expansion utility 'expand.exe' in " +
- cabextractExe.getParent());
- }
-
- cabextractPath = cabextractExe.getAbsolutePath();
-
- // expand -R