From 425667e640a6881dfc6e994ca2d21bc714e10347 Mon Sep 17 00:00:00 2001 From: dev747368 <48332326+dev747368@users.noreply.github.com> Date: Tue, 21 Jul 2020 18:34:35 -0400 Subject: [PATCH 1/9] GP-42 Initial implementation of Pdb symbol store / symbol servers --- GPL/CabExtract/build.gradle | 4 + .../data/PDB_SYMBOL_SERVER_URLS.pdburl | 7 +- .../help/topics/ImporterPlugin/load_pdb.html | 156 --- .../ghidra/app/util/bin/BinaryReader.java | 47 +- .../app/util/bin/format/pdb/PdbFactory.java | 53 - .../app/util/bin/format/pdb/PdbInfo.java | 63 ++ .../util/bin/format/pdb/PdbInfoCodeView.java | 113 +++ .../util/bin/format/pdb/PdbInfoDotNet.java | 131 +++ .../bin/format/pdb/PdbInfoDotNetIface.java | 33 - .../app/util/bin/format/pdb/PdbInfoIface.java | 32 - .../bin/format/pe/DebugDataDirectory.java | 8 +- .../bin/format/pe/debug/DebugCodeView.java | 28 +- .../app/util/datatype/microsoft/GUID.java | 124 ++- .../util/opinion/AbstractPeDebugLoader.java | 54 +- Ghidra/Features/PDB/certification.manifest | 21 +- .../PdbExamplePrescript.java} | 33 +- .../PdbSymbolServerExamplePrescript.java | 53 + .../PDB/src/global/docs/README_PDB.html | 2 +- .../PDB/src/main/help/help/TOC_Source.xml | 4 +- .../main/help/help/topics/Pdb/LoadPDBNew.html | 226 +++++ .../PDB/src/main/help/help/topics/Pdb/PDB.htm | 130 ++- .../help/topics/Pdb/download_pdb_file.html | 184 ---- .../images/KnownSymbolServerURLsDialog.png | Bin 14100 -> 0 bytes .../images/LoadPdb_Advanced_NeedsConfig.png | Bin 0 -> 46293 bytes .../images/LoadPdb_Advanced_Screenshot.png | Bin 0 -> 81637 bytes .../Pdb/images/LoadPdb_Initial_Screenshot.png | Bin 0 -> 31547 bytes .../help/topics/Pdb/images/PdbOrXmlDialog.png | Bin 9684 -> 0 bytes .../Pdb/images/PeSpecifiedPathDialog.png | Bin 8995 -> 0 bytes .../help/help/topics/Pdb/images/Plus2.png | Bin 0 -> 752 bytes .../help/topics/Pdb/images/SuccessDialog.png | Bin 14850 -> 0 bytes .../SymbolServerConfig_AddButtonMenu.png | Bin 0 -> 10777 bytes .../images/SymbolServerConfig_Screenshot.png | Bin 0 -> 14525 bytes .../Pdb/images/SymbolServerURLDialog.png | Bin 11146 -> 0 bytes .../main/help/help/topics/Pdb/images/disk.png | Bin 0 -> 620 bytes .../main/help/help/topics/Pdb/images/down.png | Bin 0 -> 192 bytes .../help/help/topics/Pdb/images/error.png | Bin 0 -> 1150 bytes .../help/help/topics/Pdb/images/reload3.png | Bin 0 -> 979 bytes .../main/help/help/topics/Pdb/images/up.png | Bin 0 -> 193 bytes .../app/plugin/core/analysis/PdbAnalyzer.java | 150 +-- .../core/analysis/PdbAnalyzerCommon.java | 182 ++++ .../core/analysis/PdbUniversalAnalyzer.java | 173 +--- .../app/util/bin/format/pdb/PdbInfo.java | 90 -- .../util/bin/format/pdb/PdbInfoDotNet.java | 89 -- .../app/util/bin/format/pdb/PdbParser.java | 347 +------ .../format/pdb2/pdbreader/PdbIdentifiers.java | 35 +- .../main/java/pdb/AskPdbOptionsDialog.java | 128 --- .../src/main/java/pdb/AskPdbUrlDialog.java | 230 ----- .../PDB/src/main/java/pdb/LoadPdbTask.java | 40 +- .../PDB/src/main/java/pdb/PdbInitializer.java | 31 - .../PDB/src/main/java/pdb/PdbPlugin.java | 226 +++-- .../main/java/pdb/PdbSymbolServerPlugin.java | 856 ---------------- .../PDB/src/main/java/pdb/PdbUtils.java | 117 +++ .../PDB/src/main/java/pdb/URLChoice.java | 34 - .../symbolserver/AbstractSymbolServer.java | 135 +++ .../symbolserver/DisabledSymbolServer.java | 127 +++ .../java/pdb/symbolserver/FindOption.java | 61 ++ .../pdb/symbolserver/HttpSymbolServer.java | 150 +++ .../pdb/symbolserver/LocalSymbolStore.java | 389 ++++++++ .../pdb/symbolserver/SameDirSymbolStore.java | 164 +++ .../java/pdb/symbolserver/SymbolFileInfo.java | 274 +++++ .../pdb/symbolserver/SymbolFileLocation.java | 114 +++ .../java/pdb/symbolserver/SymbolServer.java | 110 ++ .../symbolserver/SymbolServerInputStream.java | 60 ++ .../SymbolServerInstanceCreatorContext.java | 64 ++ .../SymbolServerInstanceCreatorRegistry.java | 220 ++++ .../pdb/symbolserver/SymbolServerService.java | 288 ++++++ .../java/pdb/symbolserver/SymbolStore.java | 91 ++ .../pdb/symbolserver/ui/ConfigPdbDialog.java | 79 ++ .../pdb/symbolserver/ui/FilePromptDialog.java | 195 ++++ .../pdb/symbolserver/ui/LoadPdbDialog.java | 942 ++++++++++++++++++ .../pdb/symbolserver/ui/SymbolFilePanel.java | 182 ++++ .../pdb/symbolserver/ui/SymbolFileRow.java | 53 + .../symbolserver/ui/SymbolFileTableModel.java | 289 ++++++ .../symbolserver/ui/SymbolServerPanel.java | 594 +++++++++++ .../pdb/symbolserver/ui/SymbolServerRow.java | 76 ++ .../ui/SymbolServerTableModel.java | 309 ++++++ .../ui/WellKnownSymbolServerLocation.java | 112 +++ .../util/bin/format/pdb/PdbParserTest.java | 915 +---------------- .../pdb/symbolserver/DummySymbolServer.java | 85 ++ .../symbolserver/HttpSymbolServerTest.java | 40 + .../symbolserver/LocalSymbolServerTest.java | 225 +++++ ...mbolServerInstanceCreatorRegistryTest.java | 101 ++ .../symbolserver/SymbolServerServiceTest.java | 160 +++ .../java/docking/DialogComponentProvider.java | 9 +- .../widgets/textfield/IntegerTextField.java | 14 +- .../src/main/java/ghidra/net/HttpClients.java | 88 ++ .../ghidra/net/SSLContextInitializer.java | 4 + .../main/java/ghidra/net/http/HttpUtil.java | 31 +- .../util/filechooser/ExtensionFileFilter.java | 183 +--- .../ghidra/util/task/MonitoredRunnable.java | 15 +- .../java/ghidra/net/http/HttpUtilTest.java | 48 + .../java/help/screenshot/PdbScreenShots.java | 221 ++-- .../SymbolServerService2Test.java | 138 +++ 93 files changed, 7715 insertions(+), 3874 deletions(-) delete mode 100644 Ghidra/Features/Base/src/main/help/help/topics/ImporterPlugin/load_pdb.html delete mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pdb/PdbFactory.java create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pdb/PdbInfo.java create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pdb/PdbInfoCodeView.java create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pdb/PdbInfoDotNet.java delete mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pdb/PdbInfoDotNetIface.java delete mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pdb/PdbInfoIface.java rename Ghidra/Features/PDB/{src/main/java/ghidra/app/util/bin/format/pdb/GhidraPdbFactory.java => developer_scripts/PdbExamplePrescript.java} (50%) create mode 100644 Ghidra/Features/PDB/developer_scripts/PdbSymbolServerExamplePrescript.java create mode 100644 Ghidra/Features/PDB/src/main/help/help/topics/Pdb/LoadPDBNew.html delete mode 100644 Ghidra/Features/PDB/src/main/help/help/topics/Pdb/download_pdb_file.html delete mode 100644 Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/KnownSymbolServerURLsDialog.png create mode 100644 Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/LoadPdb_Advanced_NeedsConfig.png create mode 100644 Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/LoadPdb_Advanced_Screenshot.png create mode 100644 Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/LoadPdb_Initial_Screenshot.png delete mode 100644 Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/PdbOrXmlDialog.png delete mode 100644 Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/PeSpecifiedPathDialog.png create mode 100644 Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/Plus2.png delete mode 100644 Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/SuccessDialog.png create mode 100644 Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/SymbolServerConfig_AddButtonMenu.png create mode 100644 Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/SymbolServerConfig_Screenshot.png delete mode 100644 Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/SymbolServerURLDialog.png create mode 100644 Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/disk.png create mode 100644 Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/down.png create mode 100644 Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/error.png create mode 100644 Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/reload3.png create mode 100644 Ghidra/Features/PDB/src/main/help/help/topics/Pdb/images/up.png create mode 100644 Ghidra/Features/PDB/src/main/java/ghidra/app/plugin/core/analysis/PdbAnalyzerCommon.java delete mode 100644 Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/PdbInfo.java delete mode 100644 Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/PdbInfoDotNet.java delete mode 100644 Ghidra/Features/PDB/src/main/java/pdb/AskPdbOptionsDialog.java delete mode 100644 Ghidra/Features/PDB/src/main/java/pdb/AskPdbUrlDialog.java delete mode 100644 Ghidra/Features/PDB/src/main/java/pdb/PdbInitializer.java delete mode 100644 Ghidra/Features/PDB/src/main/java/pdb/PdbSymbolServerPlugin.java create mode 100644 Ghidra/Features/PDB/src/main/java/pdb/PdbUtils.java delete mode 100644 Ghidra/Features/PDB/src/main/java/pdb/URLChoice.java create mode 100644 Ghidra/Features/PDB/src/main/java/pdb/symbolserver/AbstractSymbolServer.java create mode 100644 Ghidra/Features/PDB/src/main/java/pdb/symbolserver/DisabledSymbolServer.java create mode 100644 Ghidra/Features/PDB/src/main/java/pdb/symbolserver/FindOption.java create mode 100644 Ghidra/Features/PDB/src/main/java/pdb/symbolserver/HttpSymbolServer.java create mode 100644 Ghidra/Features/PDB/src/main/java/pdb/symbolserver/LocalSymbolStore.java create mode 100644 Ghidra/Features/PDB/src/main/java/pdb/symbolserver/SameDirSymbolStore.java create mode 100644 Ghidra/Features/PDB/src/main/java/pdb/symbolserver/SymbolFileInfo.java create mode 100644 Ghidra/Features/PDB/src/main/java/pdb/symbolserver/SymbolFileLocation.java create mode 100644 Ghidra/Features/PDB/src/main/java/pdb/symbolserver/SymbolServer.java create mode 100644 Ghidra/Features/PDB/src/main/java/pdb/symbolserver/SymbolServerInputStream.java create mode 100644 Ghidra/Features/PDB/src/main/java/pdb/symbolserver/SymbolServerInstanceCreatorContext.java create mode 100644 Ghidra/Features/PDB/src/main/java/pdb/symbolserver/SymbolServerInstanceCreatorRegistry.java create mode 100644 Ghidra/Features/PDB/src/main/java/pdb/symbolserver/SymbolServerService.java create mode 100644 Ghidra/Features/PDB/src/main/java/pdb/symbolserver/SymbolStore.java create mode 100644 Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/ConfigPdbDialog.java create mode 100644 Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/FilePromptDialog.java create mode 100644 Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/LoadPdbDialog.java create mode 100644 Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/SymbolFilePanel.java create mode 100644 Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/SymbolFileRow.java create mode 100644 Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/SymbolFileTableModel.java create mode 100644 Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/SymbolServerPanel.java create mode 100644 Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/SymbolServerRow.java create mode 100644 Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/SymbolServerTableModel.java create mode 100644 Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/WellKnownSymbolServerLocation.java create mode 100644 Ghidra/Features/PDB/src/test/java/pdb/symbolserver/DummySymbolServer.java create mode 100644 Ghidra/Features/PDB/src/test/java/pdb/symbolserver/HttpSymbolServerTest.java create mode 100644 Ghidra/Features/PDB/src/test/java/pdb/symbolserver/LocalSymbolServerTest.java create mode 100644 Ghidra/Features/PDB/src/test/java/pdb/symbolserver/SymbolServerInstanceCreatorRegistryTest.java create mode 100644 Ghidra/Features/PDB/src/test/java/pdb/symbolserver/SymbolServerServiceTest.java create mode 100644 Ghidra/Framework/Generic/src/main/java/ghidra/net/HttpClients.java create mode 100644 Ghidra/Framework/Generic/src/test/java/ghidra/net/http/HttpUtilTest.java create mode 100644 Ghidra/Test/IntegrationTest/src/test/java/pdb/symbolserver/SymbolServerService2Test.java 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

- -
-
    -
  1. From the menu-bar of a tool, select File Load PDB File
  2. - -
  3. In the file chooser, select the PDB file (*.PDB or *.PDB.XML)
  4. - -
  5. Click the "Select PDB" button
  6. -
- -
- -

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

- -
-
    -
  1. Structure and union definitions
  2. - -
  3. Typedefs
  4. - -
  5. Enumerations
  6. - -
  7. Class definitions
  8. - -
  9. Function prototypes
  10. - -
  11. Stack variable names and data types
  12. - -
  13. Source line numbers
  14. - -
  15. Instruction and data symbols
  16. -
-
- -

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: - -

-

-

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.

DIA SDK Dependency

diff --git a/Ghidra/Features/PDB/src/main/help/help/TOC_Source.xml b/Ghidra/Features/PDB/src/main/help/help/TOC_Source.xml index 73d8d8d971..16c4f1abb5 100644 --- a/Ghidra/Features/PDB/src/main/help/help/TOC_Source.xml +++ b/Ghidra/Features/PDB/src/main/help/help/TOC_Source.xml @@ -51,8 +51,8 @@ - - + + diff --git a/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/LoadPDBNew.html b/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/LoadPDBNew.html new file mode 100644 index 0000000000..eca6a8b6de --- /dev/null +++ b/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/LoadPDBNew.html @@ -0,0 +1,226 @@ + + + + + + Load PDB + + + + + +

Load PDB

+ + +

Symbol Servers and Symbol Storage

+ +
+

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.

+ + +
+ +

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:

+ +
+
+ +
+

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:

+ + +

 (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.

+ +

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.

+ +

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.

+ +
+
+ +

Troubleshooting

+
+ +
+ +


+ +

Related Topics:

+ + +


+ + + 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.

+ +

To Load a PDB

+ +
+

PDB files can be loaded in two ways:

+ +
+ +

Information Loaded From PDB

+ +
+
    +
  1. Structure and union definitions
  2. + +
  3. Typedefs
  4. + +
  5. Enumerations
  6. + +
  7. Class definitions
  8. + +
  9. Function prototypes
  10. + +
  11. Stack variable names and data types
  12. + +
  13. Source line numbers
  14. + +
  15. Instruction and data symbols
  16. +
+
+ +

The DIA SDK-Based Capability

+ +

*.PDB.XML files can be created in three different ways: + +

+

+

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. +

+

Related Topics:

diff --git a/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/download_pdb_file.html b/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/download_pdb_file.html deleted file mode 100644 index a161cc6e43..0000000000 --- a/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/download_pdb_file.html +++ /dev/null @@ -1,184 +0,0 @@ - - - - - - - Download PDB File - - - - -

Download PDB File

- -

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:

- -
-
    -
  1. A Symbol Server URL is available and accessible from the client or computer where you are running Ghidra.
  2. -
  3. The program open in the CodeBrowser is a PE file that was compiled by a Microsoft compiler.
  4. -
-
- -

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

- -
- -
    -
  1. From the menu-bar of a tool, select File Download PDB File
  2. - -
    - -
  3. 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.
  4. - -

    - -

    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.

    - -
    - -
  5. 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.
  6. - -

    - -
    - -
  7. 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.
  8. - -
    - -
  9. 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.
      • -
      -
    - -
    - -
  10. 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.

    -
  11. - -
    - -
  12. 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.
  13. - -

    -
    - -
  14. 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.
  15. - -

    If Yes is chosen, see - Load PDB File for continued help.

    - -
-
- -

-

Troubleshooting

-
- -
- - -


- -

Related Topics:

- - -


- - - 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 a9ec4e4bc6f0b9ba44af6d92a27468f02f22ab84..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14100 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 MwDR-fXU0MQ1}Xn$XaE2J literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..38da23a6abdf255acc278016d8d589cae69d14e1 GIT binary patch literal 81637 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..5a980f17a341fa87c774407bc53bbb1a0ae60369 GIT binary patch literal 31547 zcmc$`bwE|!wmysn0s=}32q@i1i*&=LyFr>w3P?ACibx5YZrF6Q>5}f+bSqt(?))u$ zJ?Fmn+;i@C|NHoF&o$?obIlmz8P9koL5lK{7-&RjNJvN+(o$l|NJ#f3kdW@dAKwQ` zKBYz;A|ZL)mlhLNb=BWaLea#XxcE$%mBo@LHF&R>t zmY(_rjojb&rE-*h)O@s_5bN?PHFwp7kAuuk$gQ1ow5hY1^F3%$(_y_rGLO?hU&%t9 zx!JgsGQ3>kTM{PFDa<0>Xpu_3b>RW<;{y^c1`?8QC=&D@64KDKR1To*?focZBqSk8 zUpVlC1AU%^e^DZHc?5r zo9)R!UPA>70S*SkkH{-#iW~@+-q4RNc^83I84@!rNKQtG0KyIQm=$L2=8tH(phYIfQ_U3A55=jKx=Yz7 zCckeuMwj6voHQF{zs(bORa&pkpEs!@o@)iUa~?$|EE!f1JIPK>y=d<217hBE>^ z$l{ckq^uDnhLh{vCIY{HiUbfqpG9#Q=JghaYmT{P1-Hx@+Z07*X(LtF^SXbx^onkL zUo(wuBdm7rZ#v+QmGt!YxD@bk{atnhcL+0{pElssyg$#HrVW|93|ER2;C1&4`Gl*lR$<<7|5I3;2+>T< zsfFef9A=iwPmk*&d6+Met-fZi70(+=ni^#){lPa(fW^oI4QX zf~?qloOMh06n>JVxLQQbd!AvS2SpS!&o5$jv11^v_vl#QKl(j}^SO7FF_`OV`Jls7 z^ZOgmw4V(3b?kh_421AVarR8K;WwR zA|tSvqG^ec4ZBa*-a^?)Wgp}vLKCu}1QuzYtWKdlhN6>a*kWucr|xuav+rx!_mM4I&Li^Ju(o^QV1Kol`QXGUQUg zbSOJ*vdeYJR#i$BEii3Qo8D%*?SbR~7%hs?1Iw$O>)r0EJ@Geh7i6vIGRjxKLQj0` zxqjXl=TTv(un=`hZ`~`SPJJn}^5LZj^qD@lNKSuq27yI_GiYYU#Ygy7uf`SyQo=x5 z#VNGvYOF!G$8JGSfULEs>Fhz(c)O0#t~yU9oMOo^^PLZ2-Yyqy zxy*G~$>|Q-kxJcBTIf?|TU9$wMvY!|Wm*W($cI+Yn<_V%@p7AWkA}Q^F*?nx1CeIv zE@CD2(a~1c>X&!l&)Hmu6F01CrN~@hP<$>~g7A8fLHpt}L&4tsrQ_7Fs=d}I2W*z| z-nSRL5In&x5t`B^BSVP9Tl4aQ39xCaS@xiQI#|+Lz4z6iL9O4M{=q(eB}Le@E}dKj z<8-#1k5WO=q#fxcR!X59%MiA-L8^1P{q1EB);4MApab4+(BsPkz^sv_acQdCd@wgg zd8Atzu*hqA>UlzCV>erwg27MkPabPgXMOC<)%(_N?+a>$7PRzrG2Sem+lDW1)n-?n zWq#hhOGbpmJ6s*7#ciiwp@|PcBi_CRsH#`~5}Pt`)PAl5#+pxx+tccrJoP%by3HhA0a|hGBl=njuKbm7H5%uAkVfTK$=?SZ zA2C9WUo_bkt$6y(ID52=z{38$!G}IAn9*UImi2kc%dnk zR&c4+)OsxYv;2r505NF5L?*|tdCsvsTR!=u_SvYPoT)$h$K_R< z7TT3F;HoI=s;2(^yaG6(8UFb1w;z92a2xiSu$>YOG|=Cp=qBOm=aZ^)XO|t*3Bd5+0zvQ@8Ym7KkwyIbnf`vLol@<2HxI!6`9qa&53a-@o9bQmHZ2+Tyl-jn`x_A zSJfb49BcLybz-ZjLSC#~kgCgIz=Osh_@wwNVHt)>=e;jMG6M50jT~vFlG9jmm+oCs z=Bvj~-;xhchz3~XL7moO2(Bu31-4a3U^Maxo!*x<4(w4k9<`elM2H3?RCPZn^NH2S4Zf}e$R}yGLP?#9S3>G!!T#p)X=q`%*6u|QFSVPh;k>yIZ%HI$4 zUO2A$6vWf#qv11U+2>`zzbu)1wcu5qHP*TY%fGqITFG&VgOr|-fO(du>)VR2L8w+( zDYddIz_J;a$7ji94~(qYRxh$x&bDCzE);&UDc7SR#CeP@{{Z&5nq}XL+@|@!J|h+@ z$~l$ehomZT0oH#9Dfq6Ab_QD_0Zqzp$xAnCKns{>XOWIgDMf(f^PIo zWs7t7SN1pnVzeLk34T#KZnd1hD2Ntt^#2WPa1tdI-g21Aug{8aphx4t_RmLoDry#T z?t=helu984zJo^(|1&v+53;{;SnT{+ks%eRC!i2VMujFT1v9~^-0p0wZlgR5W? zP|6#4i=i{(iMBN5cf#bWX^A$EPXcm=d-J%4cmOI>wJe5};E_rT8)$24y8M(vO14tmlvb!N;k5oX4g7USLaZS@U)JXBxp9a{LzPIgV&W?faN^G#jw zme5g9wJiJ1j^qjuD6(^_NvdfU8=R1l5KRuPkrxrX2amwHexO zV@6a3qw*s&)FyRzTja=6U2mP4GfNbB&ro`2z?`4kD7DA#(56FB=IP0EIdf@OdP}#i zbaO^qV;6bzpv)!@Ham!V0{A`VLnNely5n3JriwgYms|5FejsD{MC3XT^I?CV>>I&3 z334#|J4w~R`k`A2>eaW5jmhVX7bT9oSp*8;v5%^+BBhO4`nDJ&o<9+c`VmFDRJYXQ zdz7s|g7@(`<^oPcWAy@QTL%w2KSI|IVc>U28avRTkm^$=YBHO8i<&Qugy3ss{K4bc_R|^uawO-_PHE=zC~M$cnrDwA29x zmq>uWOt&`Ca4SzB6Iq!z6XoT?S5)wI*Sgod4o`;u1kqe)RXgO|33=rdS1$zaQPa*I zI~}!>>BVMYF)_2gxY=Ob-{%3TkV>_U&quK)_^1ra%7Nntlq2?3M-Rd|lUZ#*`70cw zSa<7A;iRtZv|jv)%%9V1U!w?qvHi`kM_dD#q2=Uckw@;k*0nLmBGgT6g%tr_H>xvd z`ZL{!`CWB=%Qn&TQK(i^((x=cg{DuLsB=c>3kN_Wo*i%(%y0{3ZKmwIGafI9k=*<^(oE$-!kq^-iv(uwL7o30V4z16Zge*TQWnf?cY6iGmDs^Icz*Iuuzo zUQD4WBrOPPv-@jgf(#w+QZGIe;ujZk9j;Y0k=(=aM41~3=9BSPQR)>PckQ4rx2+3t zc+LIJ^4pr;+HN}6pN^*)3U6vEOZdgRy;CUfECP~mEqoGGw|ViakrA1^7Fi#=anc&dzr`h`P`jhreXR!YH1 zg@|dVNdJ(6Ricxc5^9!ZzG}LPYqka|Km59t;t8}wA`u2!h3nG&tyMj5I45oYA$*Ki z_>`=jI@4@<=>56)fe6@5sr%3fXp{Qv)p5A5lqe;IGImhVk=XaVkMN@NdCcH1@*p<} z(IHJ`x4?q!t?%#{pt2B!=zVgGh;LHTl+={!tO-3pl)wX9FHq!D9u!#hb}yCcOv zgg@>65+!Ct&L5GvnF{++muA0D7snH5B0g?Dt9{oet6`x8h@3SdSxR|!nkWh%_r(n* zD-%Oib&^GBwzdoe`6NbA1i9(INtcv01qaF zo-3uns5=IC%{0XT4#M}XaI?zqB5Do}#$rWaG^z56d$aX1*b9KiKOfEqehBfVH{*b;jKqpTOV~BjmA$o!I{>l4;Ue}idw`Z{ zz9L2};wXf18+mY^ESAZ7ZtRCzSv7Kg zUQxK0)x)N^ufQ>2oOaw&rSWiVt?U?|OUkloc*-#HepMX(9#ZQ3kQbD0(N)}5`A57T za&+(&^opzhSw+5sOGwCu^QX?IGo+#8i3KL>HyqwBtVB{^gH94sJF|;{^rdE2 z!m?TO?o(z&a_yCF^jRDM6K*Ifa8kN;(mAM!tE^Jnz&^@uUCLXw=+1V|J7e9Go)LC# zrp-&ey`3j8F4j_q7gdX%%4zBL-lIW04|o&eoY#5g#}J}Ab;l83gA`9CwG`2RG^Jz36ZB=3Z_?D2cvWjfin_J7If40o!^={&% z!>saw;3PpZlBRtQPIP9~@W(l^u$Cjy1IzLunlS_&}1_{W+HA8bONA)$&D$ z%QtsxwBqny3Ju=aCv(o+JjJvfR;E~9sFL8sMztv16=V%I650qIJ1l{$3CBHRZ=h3|pZ?9xi~DEzgO{UNW7A_C%PryVks! ztagq0LMwM9_Nb`xd%Xu^il7gMxjrm>M8=aEvMllhbwmmum;T zk(I|AbxZc9-N)k5Jq0%bk=yX_e2%es22pOO-81_yMmkRPZ-Ugc+te?e551yLbmF`% zzLf?Se}RiBL`~>=Ww;6WpIf~-Ob>j~W-cVj8qYhJ;`?0ADSUtV5_!`6g*bj;O(?lk~q)B>xbXWmD3Z0InfpTr9GHTPtO zB$F$bGpBKd7Af^`t(k9zoiQfN4eULqiK9N)4MY8JA4zthIU&Tlkl$obM@jCT?@yje7Va&kCJP&@o0UI`# z`||HS9ct721Yl$f@V13dBWP&mJyBDv>) zEz*w6fb;f{h8%>$ue4&&>9t>^Rd-KDA)t*va)n<8vk@kF9U{4-^aZ~MlUJtJ&b!Sz zcrbI)Be`(wsCQ#UIVDi9Aj<#*kgdFozzm8#oKpsGIrEQ zUt8+GWRN{sH!-suF|OZQB^(dcne^fQHo?8t6R)J#|3uE({hRhdkWBF>6>ZZlWR##b z+mDOll02718De&Q1m|iPz?{O8p4D5Oy!K#N#r+n$`z-2QSqw}hA5~w!9g)2?D(sBF zn0c<{;uzm3cU;HO3|P`-S~+WKg@)DX53%{WiFgm0N~>b8@XBd%D^OunF z`J?$RR^KY_<_2-BHt!obefO8I#RseuNEi{4jBig$R6S)QPZiARWHQaor7m{eTI|Mu z<{03asRKc8E+|&ZX@Tg~x|(=l6n?p*@6DOYRFCzMJ4%py^X|fOfaENGv)$WPQyF2WOpQ7qvSZ8@;G&qqt^*!_O*1& z`t3WNF!f5S-NTt)QLYb`{A*>Cu_JTNr=0cz@oDS&g)o@H1w*ut&5R2n$LYIR)#2p( zMXfI;!X!^iSh>n}1Q*yhf6>JVCw+S|GZW_0l#uYBBb9M#k+)sFStqN znPo9YX|s5g`rg^WK`DQsoNU31&cZh-s#5)+B6;rZS^-%l5AUoMNrN3?!|a~Q(gobC z=LVS+j-zw<+wk-f;}Hd#<1+FLCP-6dr~ zn_Jd|RInzvWMt~9NUr9De6~K8`A#=pme(Lwh%n#^*73@iI6D1?Bo(h>uskrr)C&oEUgKYPyg^7PSZizFL4a%+0UIq~{q`WC;eN+%U~2&YO> zFNmVd-fX2G+{e>TwiB$|GJ5)r{SMo>MHqmb4Q0XFH55$rq}WKvZhmwv$3X6`y)Kcg_n@j zy27ezUE-gg@wSL#6#1>xOAssI^>pP z8LVy}_|4yypghpK?b#Qb#0~0fmx*pM`r3DrelF@n|oZf*g%8t)D zaRrh!-zcX2e5IyKzv(jiaRe&_#NgxYAwF?}=!byN|J7An7Q*0}dLR2y3J*eE>3e%+ z8@)Wot_D-Ow!D{=bis=s(W9utwAU;+-pv)B>2BaGgzP@&v3+7}M{M?A&fR%grqP$0pZwK-%wjtXAV_L^-*30yi16%xy}r$yo!yha#O&lQBrYL#?6aP z&hxz=yocd%i7zJ98D^p#qUB62?DX>$@e4}qIt(?JlUNrLDtF_?E)9fCK?X8ZX(Ufjm=EfdP~~{NzN1c!uts4yqu181pxAFDngKR+cyvC8p2Sh!`a?dF z`$$4w7*e3|P(6lkid~a!)dIhXFs5s)Qg1vEt#{#tS<4;m+_NAAK95Kl*tWA7?Z|2M zogxi8fUMwUQ!@;H18}#zhuaZV75HOqJYk&EHaqwlqMBQFwPkKd6BD{+3ShyF*A6!k zpWW)OsYwXIlo4-I`qZMMND@+Cq)3|ud*&0zNF;0X8#9x7)pGMPxl%qLS^ARk;c-;Mai2AjbJM-+7kbbuegofZA>i5QZYG$c`M_Bc zw-c8)-dOfV@VMt-zVLF}vn^%cinj1qjtdlW!MOg8%V8qvTZ@SgH;wTOo=dVL@q(^d zA^w2_e~kS3^40ZdpTGZ}ienv&1h&Si138*n3=6xU5ny6;bMTmXx`fGa-@u`xTYxKV zB1b8-KKyhZmC(h2aY|!N%PDbhqj0&e#lIx7*ZJ ^}@i^xgSJ6j~eX8qjQ6R}jz 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 R;NzE9U&LfYONF(4{}+{csf7Rl 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 0000000000000000000000000000000000000000..add4ad53dd68e79d7e2cdf13644dfbb51373fbfd GIT binary patch literal 752 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|I14-?iy0WW zg+Z8+Vb&Z8pn}NEkcg59UmvUF{9L`nl>DSry^7odplSvNn+hu+GdHy)QK2F?C$HG5 z!d3~a!V1U+3F|8Y3;nDA{o-C@9zzrKDK}xwt{K19`Se z86_nJR{Hwo<>h+i#(Mch>H3D2mX`VkM*2oZx=P7{9O-#x!EwNQn0$BtH5O0c3d|4@L;p!@;Rg)2@GTdPZ!4!3&E+A zeY2Px1zM{oP0UewapEFGp zR<64?<)7E&RV4vG?lc~sWw-Yo`nl|p=ynpGwnJ1OUkg4X=^jDa0{yb;2R#Ix=#Vb1YE4p%@$YnD(`5@fvSAQl9$x#{5m<7cK`TIB(gsdD`!+KKY-L;fpquiZ2hg5jShHUmR7~ zy7q$j{FTyAPm8=iwc-7Oo{I0M=4qwJ8oU2yKBYQy(%;q3=008-r1NIho?O#i|M&kt ZC;lMYt>DW!r{PyfPf%MN{A?dfPfJJ?^9sFfLEo{0Z$MR&JjrwK^1qs(@ba` zbn)jQ$qlkjBf;iXN4!%GE|@!1T6D#lpZ$8q>a;{SZAMY{GPEWA8{tMaOw<%?HZVd8 zFcU~v1MXDGetA$RZ&T~tt!L{5ZZJO{y!o#K*3He$vNM-6H^7n5)d!1%PkGIzsjjkkN4Oa|IH?1SWormcz2x-{Sb> zJh;YbiO>D|M+*~-wwoF&W!<^Q);hD=s8VC)4RmNTl zLz~@oQWloet!UXzo2~Gr`d0(1!95g0hAOq}ViU779iIAgogld+r?C8ma!q+h%{sHW z81p|NObBL+f8HkLCSu5o;Mm)7F#T2zPUg@03mqph1gUXQFcV~$jN}4hc`7!mbCTo* ze=R`LeFeqPdi4LqCJ12|k4mrKi6G?oaIsxyK9$AkL?H(LAs<6D5tJgT?WH|5A~IL# zlguf1j_f;!=ottNjvY|3C5TVKma%S;k&(Mw3t4hJJlY{Ta`KO6g9p>5afhj?tAFp# zm|Y?mQ|XB)_z4X*k}V9$2;%;560YKJcy-?_C9-aZs_(qy!h9+II|Dn?iwL81$TWgN z?6N?MRemvFm_&hY9qEb3Vx7z=3p*4;7}f(f3>$~RXAmrlBA8MX{7qcgpjv2t;4m?4 z4px>!wVbIMmIP*w=p5_t@aGV#!?TrNq=PW5_WsQgk7?a>S;TON&-#&kb4C`@7!_>s z5X6F8{W~q+S5>U4V0@_km!pEh!k$g|utg3|w&zQuS_hUu6;GL2kq7eXpr=VJ#h(}z zdJs{A-4x+OGRnPY>uve@`6EjqYlNiz7K8tpJ8|0GOL~BK$jF6r5*{Zi;A5ds9zznc zvN)=ndt)6;U^_RJh+_2LgZE_X_zpQdNNdE;5$ylu2p)EwZb?h}pI&>lxkC;V7sUSV z!!_roOvmHNPAp1d{CazJ(pm8xbO~hVx^I)!ZidE0MqcApfByU2W*9qiN_=8kStbT8 zcXDLOB3sG{j85iy@nueR1(z`xrrgPMeIfUf&e6S2yFp-3?Ot57vMBmiNUhDy7*#F6Fb5

    U z6Ya%8;1rgFk%D&QW%|FwPSr&gU9sP8u)4jwD=jH02@5MZg0tgpe?K|LE)*Qa;^hjn z3&fXA-If?Hq_!w`SgGRecz0!*2|Ja^ z!R%FSUnf}C#gFd8$CTP{Ic@sV)OME}HQfIq?~EMR{h zuQeoJ>rYN-xW= z8+W5rzwWQ=R@m$*$?Vq~l@}{@!8_IsJg%-Nd10FpcM&qy-jh1=UM~v@Fn={>laqZp zmhn2AZP|d^^Ka26{_93FxVm~9DJeCJeZA{8mUu_tHANO{O)f&^%Ws++9v;jTC+2iuZr%_;U~rY$sSmjzq=y| z`#Wge9E~l~_oc_gy{(q(E3%OiQe5nLEfIfAB_N_@ot~&qygPMTJeTuIs~gt)e;dpv z#O}(>60BYq<_}#wNdIhB4Y`el|6}P!hhJGufKv7CEnzu*;Lg!#%F*F)`Z0oRHRZhT zvnwKU7M-4GY!i%PfMgb*?7Oho zA`xQ_dIS_}{AEAvC!j(f(EUbHA3r0B96TNV~_Ih^9sSMdPB4KbPc{%}6W z7UeHBi-{kkMnOf)#^!o|wr*5K)%TzC+_NM+(fAzRQG}3A6v&8p6&?5BbfSQ~muH_3 zIXEMm*%sQ{O-^Mbwdj9oZLmFj2kGkS+9S=D!R2K2Ji1){qB_syTZUNIO0zDeCm|EO zw>0OKwzBdV^QI&(G`ze}G%+bGzJrG9xAq!R@tY_fV@qvdmV%so{B#jTjdxW(VzB_w ze(&MyR;c;bLC}!g$!bk)XGLbFY#n&<&JSJyuFwf0-{RTq!H8$xatpO|+3W%Pf~|m# zf_h4*75Iq_*0^gtP5D?rqTbH}Rt&H*QSDx2WEq5Kl{#1+yQao#^=G%OGJ5c1$3kuO zuv)Sw+COC*=nRFH*fAe1QPCEsPWI;AZytjoYFlkDCA4vWB8JTumrMrppXzE)iP;8p z5JqX`IpdBN?o}Ph{E9|<kSP)?2iA?** zkUks)Y7lfRz#}LSV&shZ`24f~h}wsTPzoZm@bN6+p{$}(fRRG-&u!q>L&f_1&qqL` zWc>JVi2uLgRAByZ9xnzV{=U6FtFy2$3=WACa@Kyax{tQycC}e0Xs}-FIY-Xo_F|)> zJDtqr?qvmL8(!@@#z=W+Zf^Ls7>UQ%Th$PgUKi0&n2{_&8G$KiWoI~2jRg1nS2q3j z^tab#uP8sk>*d|)YH#F+k25-9jhfQm-!BS}3H9)}I$R+jWP-4BawekJc^w!O^n7!)J~*|xJ~1&mi@F+9=?{>SSnel&VL1pUt&ZR!kI$J3}( zkv3fQ)?FTRMQv{9aBd&^P+_K4Fqq8dX0Lt+p-CktElgvzsdwlJyt#4Cjl>!RoUKNi z<=I>xBJ8<`=ZCYGJMHbaf145sff+wtZ8De32F=Gn2~wle5;+U^zMA3}nxRD7H{O9l zBrvkJrZG-=^Le76AcMJ&`j`!ARNp`@lLwX*VN~eP_E963_w8Jcc(`Cbl2PW zXe7T|$JZ{9;^!<^&V^)lx)fUFCr4RN=!x+pCP8Mi_E|ft{HLDIiqMC~%JV}=;nb}4 zR{b_t*#i_naaVsS*#lrx(Pg9y^jvtSGB`;Sk-)>B4LvaY8~u#sUXJMoQC}my=?Ch|}pbYX)ufGUB6Op|G!}2>cX( zJ>omuebNRWpwoU-g{M$vncnXGeP5z)H4Ta&TvvO!GYH!u0-#5ayNZkv78dr5zH-6w zapoXj=$6rc0=}1Gj?pByvYnO=rE;NQYOyGcOT+%!^nd~XhB`obU~A*;R5 z!U)AQM&hj$)f)>}I|iQ{HJGh-F9vUs7IO`jax2gV zta^qkQ)iDZe=JaBZcE8NvZEY`3>&JXu7~?e8`zXni56w=@jT_bXh#UJfm%guJDp<2;!_bAo#^HF0IFojh%_mQN&StOsGk~pv)kp)b*mVS>e%Qe5W#tAO zY}z|I*4~}lA~M*iL%FGP)0v&KmDbfqCg?Nj7`R}+4E510i+9M;$o6Q$A{z~lS$ z=@T?E0a-c%!|Cv_1R7cbuh$(6d%G*b{j$KrUJ=XnsMCo=QjbNz$jFH0Y;{~v2Qv+g zbPoB-?*6{~#r?7|{lpuCkH3-Es&+v4fw!#;w+>5TZ@CGn?Ou&biqd(pC3GygerS@m zCgJnUv3>p_`ixa$Y!AfT=Nlc6Vh~Ia`H*HOB{K-3=bKIPloVQPGLnbK zdz~4%b#K!4=F5A@wHkeAF@nP-VtZ@~Yeh>8d$mYX$BKBw9{QpmWyQ5 za2_tP0=hva(%GBp>PW{dEiFMoLI2{cG+2w<-FSI!*C(yF%r{l$2<(&^LoHtg+uCOU2_%)AEVS4<$&iEJGmnVGE43x&gwdU|?v%7FMb zDDT&(C(&GyEO>rtX{ioBa#)kIG>53ODJiIYhD34O8YqjU?(Jr6NG7=dL4ZP>Fot@n@hPk=airoxivIS~iO-LZnOrV#^XPJH^wBER zAuOi{GxV|m)8>3&P0|a=}qLwUblVtNuPYW2ZM2ua}9DIj=TIER@*H0^;sCW6TH)!UfX)$VL z4>{A?Or$aBFbKZW*F!?#^xSXy5mx@X-k(ZODUtrZlsg3d>+R(0T>t&<}>Tvh!CS`6C(GT&gf%5F7_`V# zlJfHS9i5EtPSg3F4G1y!P}FdUjSc4Wl#z$U9~w?yK)|l6>#Th@TiYz4LK)U=mZKMB zf<-)+RXisWTP>H4$Jni=y(TPa)E26yq-%*o8@s*lmdy)lmO6d7btHb$>yo%0Aom_M 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 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 0000000000000000000000000000000000000000..c3295e7c607134c09263bfd189ebe87e14fc2219 GIT binary patch literal 10777 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 literal 0 HcmV?d00001 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 303cdeb4c861a606caf83fcf76e5b126098be11f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11146 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..ff58c76dc1251f346088ea9f2b86f160e980c9ef GIT binary patch literal 192 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPFP2=EDU1=0;*+yJH;fV_r=0|yQ? zG&KANivMT$|DVBQ%P$}eWC1||yCM@%oV_H-FBmNFpFzr-*$pUU;pyTSQgLf-_eMbm z1s&A-ej;gyuG&s$%wkdfKA;H|vEO|L+|vIXqda)u00bMdO00RB}K{Dv*srmW&2A}`{b@l!In*{#;03!nf05}B&00;d100H{?6d?%- zyA%rxDwF2sr{VqmVBr7&00IC(0KWeNEf^RBIxKZ(&pwO*#H2t0suh(zW)PY0|U%qDJc;W zhK2wHJv{&>-`@iP;NV8n|NrO;0s_t*0Rape|Np->G&C?EYH9%gI5+?x6chm{<>fBD z=jR6k06_r0{{;2<`3MIA0RZ{j+yM9w4*(8YS^!x1_y+yR$dtzd0u>Yi0a7t1CommV zRRI40008&V(Etee_y7e31pxv7|1<)K`7s~gtFKR)bh@pZ~vp z?W*(mF$*KmfIq-MV+Cs9`T3JkR9TsUgO?Yqnd9kGhFiyuF%(_8^y1Lpzi~hZ?FI-S zW&uu4hA)5ro;}0B@Pg&jCpYC6FIfKl`o+NX^CtrrH#Y+d(6B!*UNHQA{hHz0ty>J` zH*UN*`uA@XQ1f0OW&j8vW^HkC20>s{@h~%=d&SB5TkzK}7qO>L8Gn8J$nfX=dxl>? zjXyqrX7~m)u>Ib>cSnBz4iIEuILrgo3#tYPZABX`0h*=L9U_bx==aKsH zz`#>QYFCB-7i%Bqn~jZGkDHtMFECZI0L7Vs zt}#_mV3GLu&u!n^w+{gVh)L%2XKihu=D$FV|A3eu7@&`Vn!CS!d$IG~JHLgmUuT~G z`SX_uP$Ls4!2x511(;sU<>c4|nVFLS0*LuF7ncG*C#QinFwR+lwwwlPuK)i1#rcmP zqu3Z353w^aoWK6%%S$m97B?LsAr?Vkn)n2aou;#Af9(AFbt*srF~|Z#S4UiY(JC>q zf1AX`|LgJdKLpF literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..b80c72091f65b5983642df8a262d9ddabdb41784 GIT binary patch literal 979 zcmV;^11$WBP)dNaS~s#g$a^svC|EEli)sk{VEyIt z?#El_pPzr8Vfe-HoZ&ab0Du5u0TE!+{xPsJ3oH9hXQ;8l1x9CjDePH1**0M>bnD^82|!^ks%eV8DyFgiv!C|;V5C2zu*5dd|vYT z%g;+c3xQ7F0QBMypc)Q_{|p*H{!}1-55&$uU4H=rhy_SQ0{zYmqz(T_{$Y9f|0M&% z5g_jr!%?94SD->khR+O_nWPxL@CY#+`E-TB8t8>?pqv3v)meZ5Vq}N`D%D{yWl#ok z)qz+Di0=dO4X_~$K+Q?O@R`q~&%hul&%p5Z9s|RVM?mrKkT3uUASR&W{1_fFoMDg# z3a|q4Tc{x*`VB)R(5Cqe3JeVY*%-LqzG0a4n}@-JK?W$t2T>0YK+Fuf3||GRZ#jpj)Kg}=~7%F89;=o{I1v>N_LzlX?da|yiK7*yRl^Cxm?=oOGD1o#B1Q0WW zKEy9Th0hsYGF)MJ2K3#3hI|HLp!tGe<15%~*y;p51sSe0Tw{31_K<;Di&^B;-A~p) zZ*B$%AV!9Gr~&_h_&qdDumQ0o*w92^3Z2Jj&B*Xy4X8i^DE1nt{2Gvd8_2)Na2gFMGaQgLhPsg1l0 z3OvjQ*9RG 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/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..4940348759 100644 --- a/Ghidra/Features/PDB/src/main/java/pdb/LoadPdbTask.java +++ b/Ghidra/Features/PDB/src/main/java/pdb/LoadPdbTask.java @@ -20,7 +20,6 @@ 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; @@ -29,8 +28,6 @@ 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.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; @@ -45,19 +42,21 @@ class LoadPdbTask extends Task { private final Program program; private final boolean useMsDiaParser; private final PdbApplicatorControl control; // PDB Universal Parser only + private boolean debugLogging; LoadPdbTask(Program program, File pdbFile, boolean useMsDiaParser, PdbApplicatorControl control, - DataTypeManagerService service) { + boolean debugLogging, DataTypeManagerService service) { super("Load PDB", true, false, true, true); this.program = program; this.pdbFile = pdbFile; this.useMsDiaParser = useMsDiaParser; this.control = control; + this.debugLogging = debugLogging; this.service = service; } @Override - public void run(final TaskMonitor monitor) { + public void run(TaskMonitor monitor) { WrappingTaskMonitor wrappedMonitor = new WrappingTaskMonitor(monitor) { @Override @@ -134,7 +133,7 @@ class LoadPdbTask extends Task { 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,44 +146,19 @@ 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 { + PdbLog.setEnabled(debugLogging); + PdbReaderOptions pdbReaderOptions = new PdbReaderOptions(); // use defaults PdbApplicatorOptions pdbApplicatorOptions = new PdbApplicatorOptions(); 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..c3e31a9e13 100644 --- a/Ghidra/Features/PDB/src/main/java/pdb/PdbPlugin.java +++ b/Ghidra/Features/PDB/src/main/java/pdb/PdbPlugin.java @@ -16,30 +16,33 @@ package pdb; import java.io.File; +import java.io.IOException; +import java.util.*; +import java.util.stream.Collectors; import javax.swing.SwingConstants; -import docking.action.MenuData; +import docking.action.builder.ActionBuilder; +import docking.tool.ToolConstants; 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.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 +54,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 +70,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; @@ -110,26 +119,17 @@ public class PdbPlugin extends Plugin { } 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(); - 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 +138,122 @@ 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, loadPdbResults.pdbFile, + loadPdbResults.useMsDiaParser, loadPdbResults.control, + loadPdbResults.debugLogging, dataTypeManagerService); + TaskBuilder.withTask(loadPdbTask) .setStatusTextAlignment(SwingConstants.LEADING) .setLaunchDelay(0); - new TaskLauncher(task, null, 0); + new TaskLauncher(loadPdbTask, null, 0); } catch (Exception pe) { Msg.showError(getClass(), null, "Error Loading PDB", pe.getMessage(), pe); } } - 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 .cab -F: - // -R renames from .cab to .pdb - // -F specifies which files within cab to expand - cabextractCmdLine = new String[] { cabextractPath, "-R", cabFile.getAbsolutePath(), - "-F:" + targetFilename, cabFile.getParent() }; - } - else { - - // On Mac/Linux - try { - cabextractPath = Application.getOSFile("cabextract").getAbsolutePath(); - } - catch (FileNotFoundException e) { - throw new PdbException("Unable to find 'cabextract' executable."); - } - - // -q for quiet - // -d to specify where to extract to - // -F to specify filter pattern of file(s) to extract - cabextractCmdLine = new String[] { cabextractPath, "-q", "-d", cabFile.getParent(), - "-F", targetFilename, cabFile.getAbsolutePath() }; - } - - ProcessBuilder builder = new ProcessBuilder(cabextractCmdLine); - Process currentProcess = builder.start(); - - try { - int exitValue = currentProcess.waitFor(); - - if (exitValue != 0) { - throw new PdbException("Abnormal termination of 'cabextract' process."); - } - } - catch (InterruptedException ie) { - // do nothing - } - - // Look for the file - FilenameFilter pdbFilter = (dir, filename) -> { - String lowercaseName = filename.toLowerCase(); - return (lowercaseName.endsWith(fileType.toString())); - }; - - File[] files = cabFile.getParentFile().listFiles(pdbFilter); - if (files != null) { - for (File childFile : files) { - if (childFile.getName().equals(targetFilename)) { - return childFile; - } - } - } - - return null; - } - - /** - * Download a file, then move it to its final destination. URL for download is created by - * combining downloadURL and PDB file attributes. Final move destination is also determined - * by the PDB file attributes. - * - * @param pdbAttributes PDB attributes (GUID, age, potential PDB locations, etc.) - * @param downloadUrl Root URL to search for the PDB - * @param saveToLocation Final root directory to save the file - * @return the downloaded and moved file - * @throws IOException if an I/O issue occurred - * @throws PdbException if issue with PKI certificate or cabinet extraction - */ - private File attemptToDownloadPdb(PdbProgramAttributes pdbAttributes, String downloadUrl, - File saveToLocation) throws PdbException, IOException { - - // Get location of the user's 'temp' directory - String tempDirPath = System.getProperty("java.io.tmpdir"); - File tempDir = new File(tempDirPath); - - RetrieveFileType retrieveType = - (fileType == PdbFileType.XML) ? RetrieveFileType.XML : RetrieveFileType.PDB; - - // Attempt retrieval from connection (encrypted or non-encrypted are handled) by HttpUtil - File createdFile = downloadExtractAndMoveFile(pdbAttributes, downloadUrl, tempDir, - saveToLocation, retrieveType); - - if (createdFile != null) { - return createdFile; - } - - // If Microsoft-specific server, need to do more (i.e., filename will be named *.pd_ and in - // .cab format). Need to change http connection properties to be able to pull back file. - - // Attempt retrieval as if it was a Microsoft-specific URL - if (retrieveType == RetrieveFileType.PDB) { - return downloadExtractAndMoveFile(pdbAttributes, downloadUrl, tempDir, saveToLocation, - RetrieveFileType.CAB); - } - - return null; - } - - /** - * Download a file, then move it to its final destination. URL for download is created by - * combining downloadURL and PDB file attributes. Final move destination is also determined - * by the PDB file attributes. - * - * @param pdbAttributes PDB attributes (GUID, age, potential PDB locations, etc.) - * @param downloadUrl Root URL to search for the PDB - * @param tempSaveDirectory Temporary local directory to save downloaded file (which will be moved) - * @param finalSaveDirectory Final root directory to save the file - * @param retrieveFileType the {@link RetrieveFileType} - * @return the downloaded and moved file - * @throws IOException if an I/O issue occurred - * @throws PdbException if issue with PKI certificate or cabinet extraction - */ - File downloadExtractAndMoveFile(PdbProgramAttributes pdbAttributes, String downloadUrl, - File tempSaveDirectory, File finalSaveDirectory, RetrieveFileType retrieveFileType) - throws IOException, PdbException { - - // TODO: This should be performed by a monitored Task with ability to cancel - - String guidAgeString = pdbAttributes.getGuidAgeCombo(); - List potentialPdbFilenames = pdbAttributes.getPotentialPdbFilenames(); - File tempFile = null; - String tempFileExtension = (retrieveFileType == RetrieveFileType.CAB) ? "cab" : "pdb"; - - File returnFile = null; - - try { - - tempFile = new File(tempSaveDirectory, "TempPDB." + tempFileExtension); - - // Attempt retrieval from connection (encrypted or non-encrypted are handled) - for (String pdbFilename : potentialPdbFilenames) { - - String downloadFilename = pdbFilename; - String currentUrl = downloadUrl + pdbFilename + "/" + guidAgeString + "/"; - - boolean retrieveSuccess = false; - - switch (retrieveFileType) { - case CAB: - currentUrl += downloadFilename; - currentUrl = currentUrl.substring(0, currentUrl.length() - 1) + "_"; - retrieveSuccess = retrieveFile(currentUrl, tempFile, urlProperties); - - if (!retrieveSuccess) { - continue; - } - - File extractedFile = uncompressCabFile(tempFile, pdbFilename); - - if (extractedFile == null) { - throw new IOException( - "Unable to uncompress .cab file extracted for " + pdbFilename); - } - returnFile = extractedFile; - - break; - - case PDB: - currentUrl += downloadFilename; - retrieveSuccess = retrieveFile(currentUrl, tempFile); - - if (!retrieveSuccess) { - continue; - } - - returnFile = tempFile; - break; - - case XML: - downloadFilename += ".xml"; - currentUrl += downloadFilename; - retrieveSuccess = retrieveFile(currentUrl, tempFile); - - if (!retrieveSuccess) { - continue; - } - - returnFile = tempFile; - break; - } - - return createSubFoldersAndMoveFile(finalSaveDirectory, pdbFilename, guidAgeString, - downloadFilename, returnFile); - - } - } - finally { - if (tempFile != null && tempFile.exists()) { - tempFile.delete(); - } - } - return null; - } - - private void tryToLoadPdb(File downloadedPdb, Program currentProgram) { - - AutoAnalysisManager aam = AutoAnalysisManager.getAnalysisManager(currentProgram); - if (aam.isAnalyzing()) { - Msg.showWarn(getClass(), null, "Load PDB", - "Unable to load PDB file while analysis is running."); - return; - } - - boolean analyzed = - currentProgram.getOptions(Program.PROGRAM_INFO).getBoolean(Program.ANALYZED, false); - - String message = "Would you like to apply the following PDB:\n\n" + - downloadedPdb.getAbsolutePath() + "\n\n to " + currentProgram.getName() + "?"; - if (analyzed) { - message += "\n \nWARNING: Loading PDB after analysis has been performed may produce" + - "\npoor results. PDBs should generally be loaded prior to analysis or" + - "\nautomatically during auto-analysis."; - } - - String htmlString = HTMLUtilities.toWrappedHTML(message); - int response = OptionDialog.showYesNoDialog(null, "Load PDB?", htmlString); - if (response != OptionDialog.YES_OPTION) { - return; - } - - AskPdbOptionsDialog optionsDialog = - new AskPdbOptionsDialog(null, fileType == PdbFileType.PDB); - if (optionsDialog.isCanceled()) { - return; - } - - boolean useMsDiaParser = optionsDialog.useMsDiaParser(); - PdbApplicatorControl control = optionsDialog.getApplicatorControl(); - - tool.setStatusInfo(""); - - try { - DataTypeManagerService service = tool.getService(DataTypeManagerService.class); - if (service == null) { - Msg.showWarn(getClass(), null, "Load PDB", - "Unable to locate DataTypeService in the current tool."); - return; - } - - TaskLauncher - .launch( - new LoadPdbTask(currentProgram, downloadedPdb, useMsDiaParser, control, - service)); - } - catch (Exception pe) { - Msg.showError(getClass(), null, "Error", pe.getMessage()); - } - } - - class PdbFileAndStatus { - - private File pdbFile; - private ReturnPdbStatus pdbStatus; - - public PdbFileAndStatus() { - pdbFile = null; - pdbStatus = ReturnPdbStatus.NOT_FOUND; - } - - public PdbFileAndStatus(File pdbFile, ReturnPdbStatus pdbStatus) { - this.pdbFile = pdbFile; - this.pdbStatus = pdbStatus; - } - - public File getPdbFile() { - return pdbFile; - } - - public ReturnPdbStatus getPdbStatus() { - return pdbStatus; - } - } -} diff --git a/Ghidra/Features/PDB/src/main/java/pdb/PdbUtils.java b/Ghidra/Features/PDB/src/main/java/pdb/PdbUtils.java new file mode 100644 index 0000000000..0d6548dfd2 --- /dev/null +++ b/Ghidra/Features/PDB/src/main/java/pdb/PdbUtils.java @@ -0,0 +1,117 @@ +/* ### + * 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.util.List; + +import java.io.*; + +import org.apache.commons.io.FilenameUtils; +import org.xml.sax.SAXException; + +import ghidra.app.util.bin.format.pdb2.pdbreader.*; +import ghidra.app.util.datatype.microsoft.GUID; +import ghidra.formats.gfilesystem.*; +import ghidra.util.exception.CancelledException; +import ghidra.util.task.TaskMonitor; +import ghidra.xml.*; +import utilities.util.FileUtilities; + +public class PdbUtils { + + /** + * Attempts to extract {@link PdbIdentifiers} from the specified file, which + * can be either a pdb or pdb.xml file. + *

    + * + * @param file File to examine + * @param monitor {@link TaskMonitor}to allow cancel and progress + * @return new {@link PdbIdentifiers} instance with GUID/ID and age info, or null if + * not a valid pdb or pdb.xml file + */ + public static PdbIdentifiers getPdbIdentifiers(File file, TaskMonitor monitor) { + String extension = FilenameUtils.getExtension(file.getName()).toLowerCase(); + switch (extension) { + case "pdb": + try (AbstractPdb pdb = + PdbParser.parse(file.getPath(), new PdbReaderOptions(), monitor)) { + PdbIdentifiers identifiers = pdb.getIdentifiers(); + return identifiers; + } + catch (Exception e) { + return null; + } + case "xml": + XmlPullParser parser = null; + try { + parser = XmlPullParserFactory.create(file, null, false); + + XmlElement xmlelem = parser.peek(); + + if (!"pdb".equals(xmlelem.getName())) { + return null; + } + + String guidStr = xmlelem.getAttribute("guid"); + GUID guid = new GUID(guidStr); + int age = Integer.parseInt(xmlelem.getAttribute("age")); + + return new PdbIdentifiers(0, 0, age, guid, null); + } + catch (SAXException | IOException | RuntimeException e) { + // don't care, return null + return null; + } + finally { + if (parser != null) { + parser.dispose(); + } + } + default: + return null; + } + } + + /** + * Extracts a singleton file from a cab file that only has 1 file + * + * @param cabFile Compressed cab file that only has 1 file embedded in it + * @param destFile where to write the extracted file to + * @param monitor {@link TaskMonitor} to allow canceling + * @return original name of the file + * @throws CancelledException if cancelled + * @throws IOException if error reading / writing file or cab file has more than 1 file in it + */ + public static String extractSingletonCabToFile(File cabFile, File destFile, TaskMonitor monitor) + throws CancelledException, IOException { + FileSystemService fsService = FileSystemService.getInstance(); + FSRL cabFSRL = fsService.getLocalFSRL(cabFile); + try (GFileSystem fs = fsService.openFileSystemContainer(cabFSRL, monitor)) { + if (fs != null) { + List rootListing = fs.getListing(null); + if (rootListing.size() == 1) { + GFile f = rootListing.get(0); + try (InputStream is = fs.getInputStream(f, monitor)) { + FileUtilities.copyStreamToFile(is, destFile, false, monitor); + return f.getName(); + } + } + } + } + throw new IOException("Unable to find file to extract"); + } + +} diff --git a/Ghidra/Features/PDB/src/main/java/pdb/URLChoice.java b/Ghidra/Features/PDB/src/main/java/pdb/URLChoice.java deleted file mode 100644 index 1e51892d52..0000000000 --- a/Ghidra/Features/PDB/src/main/java/pdb/URLChoice.java +++ /dev/null @@ -1,34 +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; - -public class URLChoice { - private String network; - private String url; - - public URLChoice(String network, String url) { - this.network = network; - this.url = url; - } - - public String getNetwork() { - return network; - } - - public String getUrl() { - return url; - } -} diff --git a/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/AbstractSymbolServer.java b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/AbstractSymbolServer.java new file mode 100644 index 0000000000..bb05641817 --- /dev/null +++ b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/AbstractSymbolServer.java @@ -0,0 +1,135 @@ +/* ### + * 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.symbolserver; + +import java.util.List; +import java.util.Set; + +import java.io.IOException; + +import org.apache.commons.io.FilenameUtils; + +import ghidra.util.Msg; +import ghidra.util.task.TaskMonitor; + +/** + * Common functionality of File and Http symbol servers. + */ +public abstract class AbstractSymbolServer implements SymbolServer { + protected static final String INDEX_TWO_FILENAME = "index2.txt"; + protected static final String PINGME_FILENAME = "pingme.txt"; // per MS custom + + protected int storageLevel = -1; + + @Override + public List find(SymbolFileInfo symbolFileInfo, Set options, + TaskMonitor monitor) { + initStorageLevelIfNeeded(monitor); + + try { + // "ke/kernelstuff.pdb/12345ABCFF0/" + String uniqueFileDir = getUniqueFileDir(symbolFileInfo); + + // "ke/kernelstuff.pdb/12345ABCFF0/kernelstuff.pdb" or + // "ke/kernelstuff.pdb/12345ABCFF0/kernelstuff.pd_" + String filePath = getFirstExists(uniqueFileDir, monitor, symbolFileInfo.getName(), + getCompressedFilename(symbolFileInfo)); + + return (filePath != null) + ? List.of(new SymbolFileLocation(filePath, this, symbolFileInfo)) + : List.of(); + } + catch (IOException ioe) { + Msg.warn(this, "Error searching for " + symbolFileInfo.getName(), ioe); + return List.of(); + } + } + + protected int detectStorageLevel(TaskMonitor monitor) { + return exists(INDEX_TWO_FILENAME, monitor) ? 2 : 1; + } + + protected void initStorageLevelIfNeeded(TaskMonitor monitor) { + if (storageLevel < 0) { + storageLevel = detectStorageLevel(monitor); + } + } + + protected String getFileDir(String filename) throws IOException { + switch (storageLevel) { + case 0: + return ""; + case 1: + return filename + "/"; + case 2: + if (filename.length() <= 2) { + throw new IOException( + "Symbol filename too short to store in two-level index: " + filename); + } + return filename.substring(0, 2).toLowerCase() + "/" + filename + "/"; + default: + throw new IllegalArgumentException( + "Unsupported Symbol Server storage level: " + storageLevel); + } + } + + protected String getUniqueFileDir(SymbolFileInfo symbolFileInfo) throws IOException { + switch (storageLevel) { + case 0: + return ""; + case 1: + case 2: + // "ke/kernelstuff.pdb/" or just "kernelstuff.pdb/" + String fileRoot = getFileDir(symbolFileInfo.getName()); + + // "ke/kernelstuff.pdb/12345ABCFF0/" + String uniqueFileDir = fileRoot + symbolFileInfo.getUniqueDirName() + "/"; + + return uniqueFileDir; + default: + throw new IllegalArgumentException( + "Unsupported Symbol Server storage level: " + storageLevel); + } + } + + protected String getFirstExists(String subDir, TaskMonitor monitor, String... filenames) { + for (String filename : filenames) { + String pathname = subDir + filename; + if (exists(pathname, monitor)) { + return pathname; + } + } + return null; + } + + static String makeCompressedExtension(String fileTypeExtension) { + return (!fileTypeExtension.isEmpty() + ? fileTypeExtension.substring(0, fileTypeExtension.length() - 1) + : "") + + "_"; + } + + static String getCompressedFilename(SymbolFileInfo symbolFileInfo) { + return FilenameUtils.getBaseName(symbolFileInfo.getName()) + "." + + makeCompressedExtension(FilenameUtils.getExtension(symbolFileInfo.getName())); + } + + static String getCompressedFilename(String filename) { + return FilenameUtils.getBaseName(filename) + "." + + makeCompressedExtension(FilenameUtils.getExtension(filename)); + } + +} diff --git a/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/DisabledSymbolServer.java b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/DisabledSymbolServer.java new file mode 100644 index 0000000000..dea6f2d56a --- /dev/null +++ b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/DisabledSymbolServer.java @@ -0,0 +1,127 @@ +/* ### + * 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.symbolserver; + +import java.util.List; +import java.util.Set; + +import java.io.IOException; + +import ghidra.util.task.TaskMonitor; + +/** + * A wrapper around a real symbol server that indicates that the symbol server has been disabled. + *

    + * Any find() operations will return an empty list, but file retrieval will still be passed through + * to the original symbol server instance. + */ +public class DisabledSymbolServer implements SymbolServer { + + private static String DISABLED_PREFIX = "disabled://"; + + /** + * Predicate that tests if the location string is an instance of a disabled location. + * + * @param loc location string + * @return boolean true if the string should be handled by the DisabledSymbolServer class + */ + public static boolean isDisabledSymbolServerLocation(String loc) { + return loc.startsWith(DISABLED_PREFIX); + } + + /** + * Factory method to create new instances from a location string. + * + * @param locationString location string + * @param context {@link SymbolServerInstanceCreatorContext} + * @return new instance, or null if invalid location string + */ + public static SymbolServer createInstance(String locationString, + SymbolServerInstanceCreatorContext context) { + SymbolServer delegate = + context.getSymbolServerInstanceCreatorRegistry() + .newSymbolServer(locationString.substring(DISABLED_PREFIX.length()), context); + return (delegate != null) ? new DisabledSymbolServer(delegate) : null; + } + + private SymbolServer delegate; + + /** + * Creates a new instance, wrapping an existing SymbolServer. + * + * @param delegate the SymbolServer that is being disabled + */ + public DisabledSymbolServer(SymbolServer delegate) { + this.delegate = delegate; + } + + /** + * Returns the wrapped (disabled) SymbolServer. + * + * @return wrapped / disabled SymbolServer + */ + public SymbolServer getSymbolServer() { + return delegate; + } + + @Override + public String getName() { + return DISABLED_PREFIX + delegate.getName(); + } + + @Override + public String getDescriptiveName() { + return "Disabled - " + delegate.getDescriptiveName(); + } + + @Override + public boolean isValid(TaskMonitor monitor) { + return delegate.isValid(monitor); + } + + @Override + public boolean exists(String filename, TaskMonitor monitor) { + return false; + } + + @Override + public List find(SymbolFileInfo fileInfo, Set findOptions, + TaskMonitor monitor) { + return List.of(); + } + + @Override + public SymbolServerInputStream getFileStream(String filename, TaskMonitor monitor) + throws IOException { + return delegate.getFileStream(filename, monitor); + } + + @Override + public String getFileLocation(String filename) { + return delegate.getFileLocation(filename); + } + + @Override + public boolean isLocal() { + return delegate.isLocal(); + } + + @Override + public String toString() { + return String.format("DisabledSymbolServer: [ %s ]", delegate.toString()); + } + +} diff --git a/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/FindOption.java b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/FindOption.java new file mode 100644 index 0000000000..55c7dbbf05 --- /dev/null +++ b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/FindOption.java @@ -0,0 +1,61 @@ +/* ### + * 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.symbolserver; + +import java.util.EnumSet; +import java.util.List; +import java.util.Set; + +/** + * Options that control how Pdb files are searched for on a SymbolServer. + */ +public enum FindOption { + /** + * Allow connections to remote symbol servers + */ + ALLOW_REMOTE, + /** + * Only return the first result + */ + ONLY_FIRST_RESULT, + /** + * Match any Pdb with the same name, regardless of GUID / signature id / age. + * (implies ANY_AGE) + */ + ANY_ID, + /** + * Match any Pdb with the same name and ID, regardless of age. + */ + ANY_AGE; + + /** + * Static constant empty set of no FindOptions. + */ + public static final Set NO_OPTIONS = Set.of(); + + /** + * Create a container of FindOptions. + * + * @param findOptions varargs list of FindOption enum values + * @return set of the specified FindOptions + */ + public static Set of(FindOption... findOptions) { + EnumSet result = EnumSet.noneOf(FindOption.class); + result.addAll(List.of(findOptions)); + return result; + } + +} diff --git a/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/HttpSymbolServer.java b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/HttpSymbolServer.java new file mode 100644 index 0000000000..a9d6980c4a --- /dev/null +++ b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/HttpSymbolServer.java @@ -0,0 +1,150 @@ +/* ### + * 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.symbolserver; + +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URI; +import java.net.http.HttpRequest; +import java.net.http.HttpRequest.BodyPublishers; +import java.net.http.HttpResponse; +import java.net.http.HttpResponse.BodyHandlers; +import java.time.Duration; + +import ghidra.net.HttpClients; +import ghidra.util.Msg; +import ghidra.util.task.TaskMonitor; + +/** + * A {@link SymbolServer} that is accessed via HTTP. + *

    + * + */ +public class HttpSymbolServer extends AbstractSymbolServer { + private static final String GHIDRA_USER_AGENT = "Ghidra_HttpSymbolServer_client"; + private static final int HTTP_STATUS_OK = HttpURLConnection.HTTP_OK; + private static final int HTTP_REQUEST_TIMEOUT_MS = 10000; // 10 seconds + + /** + * Predicate that tests if the location string is an instance of a HttpSymbolServer location. + * + * @param locationString symbol server location string + * @return boolean true if the string should be handled by the HttpSymbolServer class + */ + public static boolean isHttpSymbolServerLocation(String locationString) { + return locationString.startsWith("http://") || locationString.startsWith("https://"); + } + + private final URI serverURI; + + /** + * Creates a new instance of a HttpSymbolServer. + * + * @param serverURI URI / URL of the symbol server + */ + public HttpSymbolServer(URI serverURI) { + String path = serverURI.getPath(); + this.serverURI = + path.endsWith("/") ? serverURI : serverURI.resolve(serverURI.getPath() + "/"); + } + + @Override + public String getName() { + return serverURI.toString(); + } + + @Override + public boolean isValid(TaskMonitor monitor) { + // NOTE: checking a http symbolserver's state by testing the + // existence of a file is not 100% universally correct, as different + // webserver implementations will handle this differently, but + // no better options are apparent. + // Just getting any HTTP response, including a 404 not found, isn't a + // good indication that the symbol server is valid as it could be + // a missing subtree of a parent web site. + return exists("", monitor) || exists(PINGME_FILENAME, monitor); + } + + private HttpRequest.Builder request(String str) { + return HttpRequest.newBuilder(serverURI.resolve(str)) + .timeout(Duration.ofMillis(HTTP_REQUEST_TIMEOUT_MS)) + .setHeader("User-Agent", GHIDRA_USER_AGENT); + } + + @Override + public boolean exists(String filename, TaskMonitor monitor) { + try { + HttpRequest request = request(filename).method("HEAD", BodyPublishers.noBody()).build(); + + Msg.debug(this, + logPrefix() + ": Checking exist for [" + filename + "]: " + request.toString()); + HttpResponse response = + HttpClients.getHttpClient().send(request, BodyHandlers.discarding()); + int statusCode = response.statusCode(); + Msg.debug(this, logPrefix() + ": Response: " + response.statusCode()); + + return statusCode == HTTP_STATUS_OK; + } + catch (InterruptedException | IOException e) { + // ignore, return false + return false; + } + } + + @Override + public SymbolServerInputStream getFileStream(String filename, TaskMonitor monitor) + throws IOException { + try { + HttpRequest request = request(filename).GET().build(); + Msg.debug(this, + logPrefix() + ": Getting file [" + filename + "]: " + request.toString()); + HttpResponse response = + HttpClients.getHttpClient().send(request, BodyHandlers.ofInputStream()); + int statusCode = response.statusCode(); + Msg.debug(this, logPrefix() + ": Http response: " + response.statusCode()); + if (statusCode == HTTP_STATUS_OK) { + long contentLen = response.headers().firstValueAsLong("Content-Length").orElse(-1); + return new SymbolServerInputStream(response.body(), contentLen); + } + throw new IOException("Unable to get file: " + statusCode); + } + catch (InterruptedException e) { + throw new IOException("Http get interrupted"); + } + } + + @Override + public String getFileLocation(String filename) { + return serverURI.resolve(filename).toString(); + } + + @Override + public boolean isLocal() { + return false; + } + + @Override + public String toString() { + return String.format("HttpSymbolServer: [ url: %s, storageLevel: %d]", serverURI.toString(), + storageLevel); + } + + private String logPrefix() { + return getClass().getSimpleName() + "[" + serverURI + "]"; + } + +} diff --git a/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/LocalSymbolStore.java b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/LocalSymbolStore.java new file mode 100644 index 0000000000..9d991e0112 --- /dev/null +++ b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/LocalSymbolStore.java @@ -0,0 +1,389 @@ +/* ### + * 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.symbolserver; + +import java.util.*; + +import java.io.*; + +import org.apache.commons.io.FilenameUtils; + +import ghidra.util.Msg; +import ghidra.util.task.TaskMonitor; +import utilities.util.FileUtilities; + +/** + * Stores Pdb symbol files in a local directory. + *

    + * This is both a {@link SymbolServer} and a {@link SymbolStore} + *

    + */ +public class LocalSymbolStore extends AbstractSymbolServer implements SymbolStore { + private static final String ADMIN_DIRNAME = "000admin"; // per MS custom + + /** + * Predicate that returns true if the location string is a LocalSymbolStore path + * + * @param locationString symbol server location string + * @return boolean true if a LocalSymbolStore path + */ + public static boolean isLocalSymbolStoreLocation(String locationString) { + if (locationString == null || locationString.isBlank()) { + return false; + } + + File dir = new File(locationString); + return dir.isAbsolute() && dir.isDirectory(); + } + + /** + * Creates a (hopefully) MS-compatible symbol server directory location. + *

    + * + * @param rootDir Directory location of the new symbol store + * @param indexLevel the 'level' of the storage directory. Typical directories + * are either level 1, with pdb files stored directly under + * the root directory, or level 2, using the first 2 + * characters of the pdb filename as a bucket to place each + * pdb file-directory in. Level 0 indexLevel is a special + * Ghidra construct that is just a user-friendlier plain + * directory with a collection of Pdb files + * @throws IOException if error creating directory or admin files + */ + public static void create(File rootDir, int indexLevel) throws IOException { + FileUtilities.checkedMkdirs(rootDir); + switch (indexLevel) { + case 0: + // don't have to do anything + break; + case 2: + File index2File = new File(rootDir, INDEX_TWO_FILENAME); + if (!index2File.exists()) { + FileUtilities.writeStringToFile(index2File, + "created by Ghidra LocalSymbolStore " + new Date()); + } + // fall thru to create pingme and admin dir + case 1: + File pingmeFile = new File(rootDir, PINGME_FILENAME); + if (!pingmeFile.exists()) { + FileUtilities.writeStringToFile(pingmeFile, + "created by Ghidra LocalSymbolStore " + new Date()); + } + File adminDir = new File(rootDir, ADMIN_DIRNAME); + if (!adminDir.isDirectory()) { + FileUtilities.checkedMkdir(adminDir); + } + break; + default: + throw new IOException("Unsupported storage index level: " + indexLevel); + } + } + + private final File rootDir; + + /** + * Creates an instance of LocalSymbolStore. + * + * @param rootDir the root directory of the symbol storage + */ + public LocalSymbolStore(File rootDir) { + this.rootDir = rootDir; + } + + /** + * Returns the root directory of this symbol store. + * + * @return root directory of this symbol store + */ + public File getRootDir() { + return rootDir; + } + + @Override + public String getName() { + return rootDir.getPath(); + } + + @Override + public File getAdminDir() { + return (storageLevel == 0) ? rootDir : new File(rootDir, ADMIN_DIRNAME); + } + + @Override + public boolean isValid(TaskMonitor monitor) { + return isValid(); + } + + /** + * Non-task monitor variant of {@link #isValid(TaskMonitor)}. + * + * @return boolean true if this is a valid symbol store + */ + public boolean isValid() { + return rootDir.isDirectory(); + } + + @Override + public boolean exists(String filename, TaskMonitor monitor) { + File f = new File(rootDir, filename); + return f.isFile(); + } + + @Override + protected int detectStorageLevel(TaskMonitor monitor) { + // if the PINGME files exists, it means this directory was initialized as + // a real symbol server. If not, its probably just a normal directory + // that contains files. + File pingMeFile = new File(rootDir, PINGME_FILENAME); + File adminDir = new File(rootDir, ADMIN_DIRNAME); + if (pingMeFile.isFile() && adminDir.isDirectory()) { + return super.detectStorageLevel(monitor); + } + return 0; + } + + @Override + public List find(SymbolFileInfo symbolFileInfo, Set options, + TaskMonitor monitor) { + + initStorageLevelIfNeeded(monitor); + + List matches = new ArrayList<>(); + + // search for exact matches using the built-in logic in AbstractSymbolServer + if (storageLevel != 0) { + matches.addAll(super.find(symbolFileInfo, options, monitor)); + } + + if (storageLevel == 0 || options.contains(FindOption.ANY_AGE) || + options.contains(FindOption.ANY_ID)) { + + try { + if (storageLevel == 0) { + searchLevel0(rootDir, this, symbolFileInfo, options, matches, monitor); + } + else { + searchLevelN(symbolFileInfo, options, matches, monitor); + } + } + catch (IOException ioe) { + Msg.warn(this, "Error searching for " + symbolFileInfo.getName() + " in " + rootDir, + ioe); + } + } + + return matches; + } + + static void searchLevel0(File rootDir, SymbolStore symbolStore, SymbolFileInfo symbolFileInfo, + Set options, List matches, TaskMonitor monitor) { + + // if its a "0 level" bag-of-files, we have to open each Pdb to find its UID and + // AGE (after filtering for similar filenames as requested pdb file) + for (File f : list(rootDir, + ff -> ff.isFile() && isFilenameStartsWithMatch(symbolFileInfo, ff))) { + if (monitor.isCancelled()) { + break; + } + SymbolFileInfo fileInfo = SymbolFileInfo.fromFile(f, monitor); + if (fileInfo != null) { + if (hasSymbolFileInfoMatch(symbolFileInfo, fileInfo, options)) { + matches.add(new SymbolFileLocation(f.getName(), symbolStore, fileInfo)); + } + } + } + } + + private void searchLevelN(SymbolFileInfo symbolFileInfo, Set options, + List matches, + TaskMonitor monitor) throws IOException { + + // enbiggen the search by grubing through our subdirectories. + // "ke/kernelstuff.pdb/" or just "kernelstuff.pdb/" + String fileDir = getFileDir(symbolFileInfo.getName()); + + // since its a normal 1 or 2 level, we can get UID and AGE info from the subpath + // without opening the symbol file + for (File subDir : list(new File(rootDir, fileDir), File::isDirectory)) { + if (monitor.isCancelled()) { + break; + } + searchSubDir(subDir, symbolFileInfo, fileDir, options, matches); + } + } + + private void searchSubDir(File subDir, SymbolFileInfo symbolFileInfo, String relativeFileDir, + Set options, List results) { + + String symbolFileName = symbolFileInfo.getName(); + SymbolFileInfo subDirSymbolFileInfo = + SymbolFileInfo.fromSubdirectoryPath(symbolFileName, subDir.getName()); + + if (subDirSymbolFileInfo != null && !symbolFileInfo.isExactMatch(subDirSymbolFileInfo)) { + // don't examine this subfolder if its fingerprints indicate its an exact match, + // since exact matches will already have been added to the results + + // "ke/kernelstuff.pdb/112233440/" + String uniqueDir = relativeFileDir + subDir.getName() + "/"; + + if (hasSymbolFileInfoMatch(symbolFileInfo, subDirSymbolFileInfo, options)) { + String matchingFile = getFirstExists(uniqueDir, null, symbolFileName, + getCompressedFilename(symbolFileName)); + + if (matchingFile != null) { + results.add(new SymbolFileLocation(matchingFile, this, subDirSymbolFileInfo)); + } + } + } + } + + @Override + public String getFileLocation(String filename) { + return getFile(filename).getPath(); + } + + @Override + public File getFile(String path) { + return new File(rootDir, path); + } + + @Override + public String giveFile(SymbolFileInfo symbolFileInfo, File file, String filename, + TaskMonitor monitor) throws IOException { + initStorageLevelIfNeeded(monitor); + filename = FilenameUtils.getName(filename); // make sure no relative path shenanigans + String relativeDestinationFilename = getUniqueFileDir(symbolFileInfo) + filename; + File destinationFile = new File(rootDir, relativeDestinationFilename); + FileUtilities.checkedMkdirs(destinationFile.getParentFile()); + if (destinationFile.isFile()) { + Msg.info(this, logPrefix() + ": File already exists: " + destinationFile); + if (!file.delete()) { + Msg.warn(this, logPrefix() + ": Unable to delete source file: " + file); + } + return relativeDestinationFilename; + } + monitor.setMessage("Storing " + filename + " in local symbol store "); + if (!file.renameTo(destinationFile)) { + throw new IOException("Could not move " + file + " to " + destinationFile); + } + + return relativeDestinationFilename; + } + + @Override + public String putStream(SymbolFileInfo symbolFileInfo, + SymbolServerInputStream symbolServerInputStream, String filename, TaskMonitor monitor) + throws IOException { + initStorageLevelIfNeeded(monitor); + filename = FilenameUtils.getName(filename); // make sure no relative path shenanigans + String relativeDestinationFilename = getUniqueFileDir(symbolFileInfo) + filename; + File destinationFile = new File(rootDir, relativeDestinationFilename); + FileUtilities.checkedMkdirs(destinationFile.getParentFile()); + if (destinationFile.isFile()) { + Msg.info(this, logPrefix() + ": File already exists: " + destinationFile); + return relativeDestinationFilename; + } + + File destinationFileTmp = new File(rootDir, relativeDestinationFilename + ".tmp"); + destinationFileTmp.delete(); + + monitor.setMessage("Storing " + filename + " in local symbol store "); + + if (symbolServerInputStream.getExpectedLength() >= 0) { + monitor.initialize(symbolServerInputStream.getExpectedLength()); + } + try { + long bytesCopied = FileUtilities.copyStreamToFile( + symbolServerInputStream.getInputStream(), destinationFileTmp, false, monitor); + if (symbolServerInputStream.getExpectedLength() >= 0 && + bytesCopied != symbolServerInputStream.getExpectedLength()) { + throw new IOException("Copy length mismatch, expected " + + symbolServerInputStream.getExpectedLength() + " bytes, got " + bytesCopied); + } + if (!destinationFileTmp.renameTo(destinationFile)) { + throw new IOException( + "Error renaming temp file " + destinationFileTmp + " to " + destinationFile); + } + return relativeDestinationFilename; + } + finally { + destinationFileTmp.delete(); + } + + } + + @Override + public SymbolServerInputStream getFileStream(String filename, TaskMonitor monitor) + throws IOException { + File file = new File(rootDir, filename); + return new SymbolServerInputStream(new FileInputStream(file), file.length()); + } + + @Override + public boolean isLocal() { + return true; + } + + @Override + public String toString() { + return String.format("LocalSymbolStore: [ rootDir: %s, storageLevel: %d]", + rootDir.getPath(), storageLevel); + } + + private String logPrefix() { + return getClass().getSimpleName() + "[" + rootDir + "]"; + } + + // ----------------------------------------------------------------------------------- + // Static helpers + + static File[] list(File dir, FileFilter filter) { + File[] files = dir.listFiles(filter); + return files != null ? files : new File[] {}; + } + + static boolean isFilenameStartsWithMatch(SymbolFileInfo symbolFileInfo, File file) { + String symbolFilenameNoExtension = FilenameUtils.getBaseName(symbolFileInfo.getName()); + String fileNoExtension = FilenameUtils.getBaseName(file.getName()); + + // use case-insensitive compare since these are PDB files, which + // come from a Windows env + if (!fileNoExtension.toLowerCase().startsWith(symbolFilenameNoExtension.toLowerCase())) { + return false; + } + + // match on ext ("pdb"), compressed ext ("pd_") + String symbolFilenameExtension = + FilenameUtils.getExtension(symbolFileInfo.getName()).toLowerCase(); + String fileExtension = FilenameUtils.getExtension(file.getName()).toLowerCase(); + return fileExtension.equals(symbolFilenameExtension) || + fileExtension.equals(makeCompressedExtension(symbolFilenameExtension)); + } + + static boolean hasSymbolFileInfoMatch(SymbolFileInfo symbolFileInfo, + SymbolFileInfo otherSymbolFileInfo, Set options) { + boolean idMatches = + symbolFileInfo.getUniqueName().equalsIgnoreCase(otherSymbolFileInfo.getUniqueName()); + boolean ageMatches = symbolFileInfo.getIdentifiers() + .getAge() == otherSymbolFileInfo.getIdentifiers().getAge(); + + if (!options.contains(FindOption.ANY_ID)) { + return idMatches && (ageMatches || options.contains(FindOption.ANY_AGE)); + } + return true; + } + +} diff --git a/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/SameDirSymbolStore.java b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/SameDirSymbolStore.java new file mode 100644 index 0000000000..18034e8399 --- /dev/null +++ b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/SameDirSymbolStore.java @@ -0,0 +1,164 @@ +/* ### + * 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.symbolserver; + +import java.util.*; + +import java.io.*; + +import ghidra.util.task.TaskMonitor; + +/** + * A Pdb symbol server / symbol store, similar to the {@link LocalSymbolStore}, + * but limited to searching just the single directory that the original executable is located in. + *

    + * Matches symbol files that have a similar name to the requested symbol file (but the identifier + * info - guid/id & age must still match as per the find options specified). + * + */ +public class SameDirSymbolStore implements SymbolStore { + + /** + * Descriptive string + */ + public static String PROGRAMS_IMPORT_LOCATION_DESCRIPTION_STR = "Program's Import Location"; + + /** + * Factory helper, indicates if the specified location is the special + * magic string that indicates the location is the "same dir" symbol store. + * + * @param locationString Symbol server location string + * @return boolean true if the location string is the special magic "same dir" string (".") + */ + public static boolean isSameDirLocation(String locationString) { + return ".".equals(locationString); + } + + /** + * Reuse / abuse the {@link SameDirSymbolStore} to be the container/wrapper for an already known + * symbol file. Useful to wrap a file that was picked by the user in an + * {@link SymbolFileLocation}. + * + * @param symbolFile symbol file + * @param symbolFileInfo symbol file information + * @return a new {@link SymbolFileLocation} with a {@link SameDirSymbolStore} parent + */ + public static SymbolFileLocation createManuallySelectedSymbolFileLocation(File symbolFile, + SymbolFileInfo symbolFileInfo) { + SameDirSymbolStore samedirSymbolStore = new SameDirSymbolStore(symbolFile.getParentFile()); + SymbolFileLocation symbolFileLocation = + new SymbolFileLocation(symbolFile.getName(), samedirSymbolStore, symbolFileInfo); + return symbolFileLocation; + } + + private final File rootDir; + + /** + * Create a new instance, based on the directory where the program was originally imported from. + * + * @param rootDir directory path where the program was originally imported from, or null if not + * bound to an actual Program + */ + public SameDirSymbolStore(File rootDir) { + this.rootDir = rootDir; + } + + @Override + public File getAdminDir() { + return rootDir; + } + + @Override + public File getFile(String path) { + return new File(rootDir, path); + } + + @Override + public String giveFile(SymbolFileInfo symbolFileInfo, File f, String filename, + TaskMonitor monitor) throws IOException { + throw new IOException("Unsupported"); + } + + @Override + public String putStream(SymbolFileInfo symbolFileInfo, SymbolServerInputStream streamInfo, + String filename, TaskMonitor monitor) throws IOException { + throw new IOException("Unsupported"); + } + + @Override + public String getName() { + return "."; + } + + @Override + public String getDescriptiveName() { + return String.format(PROGRAMS_IMPORT_LOCATION_DESCRIPTION_STR + " - %s", + isValid() ? rootDir.getPath() : "unspecified"); + } + + @Override + public boolean isValid(TaskMonitor monitor) { + return isValid(); + } + + private boolean isValid() { + return rootDir != null && rootDir.isDirectory(); + } + + @Override + public boolean exists(String filename, TaskMonitor monitor) { + return isValid() && getFile(filename).isFile(); + } + + @Override + public List find(SymbolFileInfo fileInfo, Set findOptions, + TaskMonitor monitor) { + + List results = new ArrayList<>(); + + if (isValid()) { + LocalSymbolStore.searchLevel0(rootDir, this, fileInfo, findOptions, results, monitor); + } + + return results; + } + + @Override + public SymbolServerInputStream getFileStream(String filename, TaskMonitor monitor) + throws IOException { + if (!isValid(monitor)) { + throw new IOException("Unknown rootdir"); + } + File file = getFile(filename); + return new SymbolServerInputStream(new FileInputStream(file), file.length()); + } + + @Override + public String getFileLocation(String filename) { + return getFile(filename).getPath(); + } + + @Override + public boolean isLocal() { + return true; + } + + @Override + public String toString() { + return String.format("SameDirSymbolStore: [ dir: %s ]", rootDir); + } + +} diff --git a/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/SymbolFileInfo.java b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/SymbolFileInfo.java new file mode 100644 index 0000000000..4f970ebb69 --- /dev/null +++ b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/SymbolFileInfo.java @@ -0,0 +1,274 @@ +/* ### + * 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.symbolserver; + +import java.util.Map; +import java.util.Objects; + +import java.io.File; + +import org.apache.commons.io.FilenameUtils; + +import ghidra.app.util.bin.format.pdb.PdbParserConstants; +import ghidra.app.util.bin.format.pdb2.pdbreader.PdbIdentifiers; +import ghidra.app.util.datatype.microsoft.GUID; +import ghidra.util.task.TaskMonitor; +import pdb.PdbUtils; + +/** + * Information about a pdb symbol file: its filename and its + * {@link PdbIdentifiers pdb guid/id fingerprints} + * + */ +public class SymbolFileInfo { + private static final int MIN_SIG_HEX_STR_LEN = 8; + private static final int GUID_HEX_STR_LEN = 32; + + /** + * Create a SymbolFileInfo instance that represents an unknown / bad + * file. + * + * @param path path string to use + * @return new SymbolFileInfo with a PdbIdentifier with bogus / default values + */ + public static SymbolFileInfo unknown(String path) { + return new SymbolFileInfo(path, new PdbIdentifiers(0, 0, 0, null, null)); + } + + /** + * Create a SymbolFileInfo instance from the metadata found in a program + * + * @param metadata Map of String-to-String values taken from a program + * @return new SymbolFileInfo instance, or null if no Pdb info found + */ + public static SymbolFileInfo fromMetadata(Map metadata) { + try { + int sig = + Integer.parseUnsignedInt( + metadata.getOrDefault(PdbParserConstants.PDB_SIGNATURE, "0"), 16); + String guidString = metadata.getOrDefault(PdbParserConstants.PDB_GUID, ""); + GUID guid = (guidString != null && !guidString.isBlank()) ? new GUID(guidString) : null; + int age = Integer + .parseUnsignedInt(metadata.getOrDefault(PdbParserConstants.PDB_AGE, "0"), 16); + String path = metadata.getOrDefault(PdbParserConstants.PDB_FILE, ""); + + PdbIdentifiers pdbIdentifiers = new PdbIdentifiers(0, sig, age, guid, null); + + return new SymbolFileInfo(path, pdbIdentifiers); + } + catch (IllegalArgumentException e) { + return null; + } + } + + /** + * Create a new {@link SymbolFileInfo} instance using information scraped from a pdb symbol + * server subdir path. + * + * @param path name of the pdb file + * @param uniqueSubdir string that is a combo of 32_hexchar_GUID + age or + * 8_hexchar_signature + age + * @return new {@link SymbolFileInfo} instance, or null if invalid info in path + * or subdir names + */ + public static SymbolFileInfo fromSubdirectoryPath(String path, String uniqueSubdir) { + try { + if (MIN_SIG_HEX_STR_LEN < uniqueSubdir.length() && + uniqueSubdir.length() < GUID_HEX_STR_LEN) { + int sig = Integer.parseUnsignedInt(uniqueSubdir.substring(0, 8), 16); + int age = Integer.parseUnsignedInt(uniqueSubdir.substring(8), 16); + + return new SymbolFileInfo(path, new PdbIdentifiers(0, sig, age, null, null)); + } + else if (uniqueSubdir.length() > GUID_HEX_STR_LEN) { + String guidString = uniqueSubdir.substring(0, GUID_HEX_STR_LEN); + GUID guid = new GUID(guidString); + + int age = Integer.parseUnsignedInt(uniqueSubdir.substring(GUID_HEX_STR_LEN), 16); + + return new SymbolFileInfo(path, new PdbIdentifiers(0, 0, age, guid, null)); + } + + } + catch (IllegalArgumentException e) { + // ignore + } + return null; + } + + /** + * Creates a new instance using the specified path and guid/id string and age. + * + * @param path String pdb path filename + * @param uid String GUID or signature id + * @param age int value + * @return new {@link SymbolFileInfo} instance made of specified path and identity info, + * or null if bad GUID / signature id string + */ + public static SymbolFileInfo fromValues(String path, String uid, int age) { + try { + GUID guid = new GUID(uid); + return new SymbolFileInfo(path, new PdbIdentifiers(0, 0, age, guid, null)); + } + catch (IllegalArgumentException e) { + // ignore, try older codeview + } + try { + int sig = Integer.parseUnsignedInt(uid, 16); + return new SymbolFileInfo(path, new PdbIdentifiers(0, sig, age, null, null)); + } + catch (IllegalArgumentException e) { + // fail + } + return null; + } + + /** + * Create a new instance using the specified path and {@link PdbIdentifiers}. + * + * @param path String pdb path filename + * @param pdbIdent {@link PdbIdentifiers} + * @return new {@link SymbolFileInfo} instance made of specified path and ident info + */ + public static SymbolFileInfo fromPdbIdentifiers(String path, PdbIdentifiers pdbIdent) { + return new SymbolFileInfo(path, pdbIdent); + } + + /** + * Create a new instance using the information found inside the specified file. + *

    + * The file will be opened and parsed to determine its GUID/ID and age. + * + * @param pdbFile pdb file to create a SymbolFileInfo for + * @param monitor {@link TaskMonitor} for progress and cancel + * @return new {@link SymbolFileInfo} instance or null if file is not a valid pdb or pdb.xml + * file + */ + public static SymbolFileInfo fromFile(File pdbFile, TaskMonitor monitor) { + PdbIdentifiers pdbIdentifiers = PdbUtils.getPdbIdentifiers(pdbFile, monitor); + return (pdbIdentifiers != null) ? new SymbolFileInfo(pdbFile.getName(), pdbIdentifiers) + : null; + } + + private final PdbIdentifiers pdbIdentifiers; + private final String pdbPath; + + private SymbolFileInfo(String pdbPath, PdbIdentifiers pdbIdentifiers) { + this.pdbPath = pdbPath; + this.pdbIdentifiers = pdbIdentifiers; + } + + /** + * Returns the {@link PdbIdentifiers} of this instance. + * + * @return {@link PdbIdentifiers} of this instance + */ + public PdbIdentifiers getIdentifiers() { + return pdbIdentifiers; + } + + /** + * The name of the pdb file, derived from the {@link #getPath() path} value. + * + * @return String name of the pdb file + */ + public String getName() { + return FilenameUtils.getName(pdbPath); + } + + /** + * The 'path' of the pdb file, which contains the full path and filename recovered from the + * original binary's debug data. Typically, this is just a plain name string without any + * path info. + * + * @return original pdb path string recovered from binary's debug data + */ + public String getPath() { + return pdbPath; + } + + /** + * A string that represents the unique fingerprint of a Pdb file. Does not + * include the age. + * + * @return either GUID str or signature hexstring + */ + public String getUniqueName() { + return (pdbIdentifiers.getGuid() != null) + ? pdbIdentifiers.getGuid().toString().replace("-", "").toUpperCase() + : String.format("%08X", pdbIdentifiers.getSignature()); + + } + + /** + * Returns a string that is a combination of the GUID/ID and the age, in a format + * used by symbol servers to create subdirectories in their directory structure. + * + * @return String combination of GUID/ID and age, e.g. "112233441" + */ + public String getUniqueDirName() { + return getUniqueName() + Integer.toUnsignedString(pdbIdentifiers.getAge(), 16); + } + + /** + * Returns true if this SymbolFileInfo instance exactly matches the {@link PdbIdentifiers} + * info of the other instance. + * + * @param other {@link SymbolFileInfo} to compare + * @return boolean true if exact match of {@link PdbIdentifiers} info + */ + public boolean isExactMatch(SymbolFileInfo other) { + return getUniqueName().equalsIgnoreCase(other.getUniqueName()) && + pdbIdentifiers.getAge() == other.getIdentifiers().getAge(); + } + + /** + * Returns a description of this instance. + * + * @return String description + */ + public String getDescription() { + return getName() + ", " + getIdentifiers(); + } + + @Override + public String toString() { + return String.format("SymbolFileInfo: [ pdb: %s, uid: %s]", getName(), + getIdentifiers().toString()); + } + + @Override + public int hashCode() { + return Objects.hash(pdbIdentifiers, pdbPath); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + SymbolFileInfo other = (SymbolFileInfo) obj; + return Objects.equals(pdbIdentifiers, other.pdbIdentifiers) && + Objects.equals(pdbPath, other.pdbPath); + } + +} diff --git a/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/SymbolFileLocation.java b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/SymbolFileLocation.java new file mode 100644 index 0000000000..29febaa841 --- /dev/null +++ b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/SymbolFileLocation.java @@ -0,0 +1,114 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package pdb.symbolserver; + +import java.util.Objects; + +/** + * Represents a symbol file on a {@link SymbolServer} or an associated file. + */ +public class SymbolFileLocation { + private final SymbolFileInfo fileInfo; + private final String path; + private final SymbolServer symbolServer; + + /** + * Creates a new instance. + * + * @param path raw path to file (relative to the {@link SymbolServer}) + * @param symbolServer {@link SymbolServer} the file resides on + * @param fileInfo the {@link SymbolFileInfo pdb file} that this file is associated with + */ + public SymbolFileLocation(String path, SymbolServer symbolServer, SymbolFileInfo fileInfo) { + this.path = path; + this.symbolServer = symbolServer; + this.fileInfo = fileInfo; + } + + /** + * The raw path inside the SymbolServer to the file. + * + * @return raw path inside the SymbolServer to the file + */ + public String getPath() { + return path; + } + + /** + * The {@link SymbolServer} that holds the file. + * + * @return the {@link SymbolServer} that holds the file + */ + public SymbolServer getSymbolServer() { + return symbolServer; + } + + /** + * The {@link SymbolFileInfo pdb file} that this file is associated with. + * + * @return the {@link SymbolFileInfo pdb file} that this file is associated with + */ + public SymbolFileInfo getFileInfo() { + return fileInfo; + } + + /** + * Returns true if this file is an 'exact match' for the + * specified {@link SymbolFileInfo other pdb file}. + * + * @param otherSymbolFileInfo the other pdb file's info + * @return boolean true if exact match (GUID & age match), false if not an exact match + */ + public boolean isExactMatch(SymbolFileInfo otherSymbolFileInfo) { + return fileInfo.isExactMatch(otherSymbolFileInfo); + } + + /** + * The 'absolute' location of this file, including the symbol server's location. + * + * @return a string representing the 'absolute' location of this file + */ + public String getLocationStr() { + return symbolServer.getFileLocation(path); + } + + @Override + public String toString() { + return path + " in " + symbolServer.getName() + " for " + fileInfo.getDescription(); + } + + @Override + public int hashCode() { + return Objects.hash(fileInfo, path, symbolServer); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + SymbolFileLocation other = (SymbolFileLocation) obj; + return Objects.equals(fileInfo, other.fileInfo) && Objects.equals(path, other.path) && + symbolServer == other.symbolServer; + } + +} diff --git a/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/SymbolServer.java b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/SymbolServer.java new file mode 100644 index 0000000000..e958183a75 --- /dev/null +++ b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/SymbolServer.java @@ -0,0 +1,110 @@ +/* ### + * 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.symbolserver; + +import java.util.List; +import java.util.Set; + +import java.io.IOException; +import java.io.InputStream; + +import ghidra.util.task.TaskMonitor; + +/** + * Represents the common functionality of different types of symbol servers: querying for + * files containing symbol information and getting those files. + * + */ +public interface SymbolServer { + + /** + * Name of the symbol server, suitable to use as the identity of this instance, + * and which will allow the SymbolServerInstanceCreatorRegistry to recreate an instance. + * + * @return string name + */ + String getName(); + + /** + * Descriptive name of the symbol server, used in UI lists, etc. + * + * @return string descriptive name + */ + default String getDescriptiveName() { + return getName(); + } + + /** + * Returns true if the symbol server is valid and can be queried. + * @param monitor {@link TaskMonitor} + * + * @return boolean true if symbol server is working + */ + boolean isValid(TaskMonitor monitor); + + /** + * Returns true if the raw filename exists in the symbol server. + * + * @param filename raw path filename string + * @param monitor {@link TaskMonitor} + * @return boolean true if file exists + */ + boolean exists(String filename, TaskMonitor monitor); + + /** + * Searches for a symbol file on the server. + *

    + * HttpSymbolServers only support exact matches, LocalSymbolStores can + * possibly have fuzzy matches. + * + * @param fileInfo {@link SymbolFileInfo} bag of information about the file to search for + * @param findOptions set of {@link FindOption} to control the search. + * See {@link FindOption#NO_OPTIONS} or + * {@link FindOption#of(FindOption...) FindOptions.of(option1, option2...)} + * @param monitor {@link TaskMonitor} + * @return list of {@link SymbolFileLocation location information instances} about matches + */ + List find(SymbolFileInfo fileInfo, Set findOptions, + TaskMonitor monitor); + + /** + * Returns a wrapped InputStream for the specified raw path filename. + * + * @param filename raw path filename + * @param monitor {@link TaskMonitor} + * @return {@link SymbolServerInputStream} wrapped {@link InputStream}, never null + * @throws IOException if error or not found + */ + SymbolServerInputStream getFileStream(String filename, TaskMonitor monitor) throws IOException; + + /** + * Returns a location description string of a specific file contained in this symbol server. + *

    + * + * @param filename raw path and name of a file in this server + * (typically from {@link SymbolFileLocation#getPath()} + * @return a descriptive string with the 'absolute' location of this file + */ + String getFileLocation(String filename); + + /** + * Returns true if this {@link SymbolServer} is 'local', meaning + * it can be searched without security issues / warning the user. + * + * @return boolean true if this symbolserver is 'local', false if remote + */ + boolean isLocal(); +} diff --git a/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/SymbolServerInputStream.java b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/SymbolServerInputStream.java new file mode 100644 index 0000000000..5073c092b7 --- /dev/null +++ b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/SymbolServerInputStream.java @@ -0,0 +1,60 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package pdb.symbolserver; + +import java.io.*; + +/** + * A {@link InputStream} wrapper returned from a {@link SymbolServer} + * that also contains the expected length of the stream. + */ +public class SymbolServerInputStream implements Closeable { + private final InputStream inputStream; + private final long expectedLength; + + /** + * Create a new instance. + * + * @param inputStream {@link InputStream} to wrap + * @param expectedLength the expected length of the input stream + */ + public SymbolServerInputStream(InputStream inputStream, long expectedLength) { + this.inputStream = inputStream; + this.expectedLength = expectedLength; + } + + /** + * Returns the wrapped input stream + * @return the wrapped input stream + */ + public InputStream getInputStream() { + return inputStream; + } + + /** + * Returns the expected length of the input stream + * + * @return expected length of the input stream + */ + public long getExpectedLength() { + return expectedLength; + } + + @Override + public void close() throws IOException { + inputStream.close(); + } +} diff --git a/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/SymbolServerInstanceCreatorContext.java b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/SymbolServerInstanceCreatorContext.java new file mode 100644 index 0000000000..4e880ec480 --- /dev/null +++ b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/SymbolServerInstanceCreatorContext.java @@ -0,0 +1,64 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package pdb.symbolserver; + +import java.io.File; + +/** + * Context for the {@link SymbolServerInstanceCreatorRegistry} when creating new + * {@link SymbolServer} instances. + *

    + * This allows the method that is creating a new SymbolServer to know the location the + * Ghidra program was imported from, as well as to reach back to the registry itself and + * use it to create other SymbolServer instances (if necessary). + *

    + * Created via {@link SymbolServerInstanceCreatorRegistry#getContext()} or + * {@link SymbolServerInstanceCreatorRegistry#getContext(ghidra.program.model.listing.Program)} + */ +public class SymbolServerInstanceCreatorContext { + private final File rootDir; + private final SymbolServerInstanceCreatorRegistry symbolServerInstanceCreatorRegistry; + + SymbolServerInstanceCreatorContext( + SymbolServerInstanceCreatorRegistry symbolServerInstanceCreatorRegistry) { + this(null, symbolServerInstanceCreatorRegistry); + } + + SymbolServerInstanceCreatorContext(File rootDir, + SymbolServerInstanceCreatorRegistry symbolServerInstanceCreatorRegistry) { + this.rootDir = rootDir; + this.symbolServerInstanceCreatorRegistry = symbolServerInstanceCreatorRegistry; + } + + /** + * The {@link SymbolServerInstanceCreatorRegistry} associated with this context. + * + * @return the {@link SymbolServerInstanceCreatorRegistry} + */ + public SymbolServerInstanceCreatorRegistry getSymbolServerInstanceCreatorRegistry() { + return symbolServerInstanceCreatorRegistry; + } + + /** + * The root directory of the imported binary. + * + * @return directory of the binary, or null if no associated program + */ + public File getRootDir() { + return rootDir; + } + +} diff --git a/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/SymbolServerInstanceCreatorRegistry.java b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/SymbolServerInstanceCreatorRegistry.java new file mode 100644 index 0000000000..9905b4477a --- /dev/null +++ b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/SymbolServerInstanceCreatorRegistry.java @@ -0,0 +1,220 @@ +/* ### + * 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.symbolserver; + +import java.util.*; +import java.util.function.Predicate; + +import java.io.File; +import java.net.URI; + +import org.apache.commons.io.FilenameUtils; + +import ghidra.program.model.listing.Program; +import ghidra.util.Msg; + +/** + * Registry of {@link SymbolServer} instance creators. + */ +public class SymbolServerInstanceCreatorRegistry { + + private static final SymbolServerInstanceCreatorRegistry instance = + new SymbolServerInstanceCreatorRegistry(); + + /** + * A static singleton pre-configured with the default symbol server implementations. + * + * @return static singleton {@link SymbolServerInstanceCreatorRegistry} instance. + */ + public static SymbolServerInstanceCreatorRegistry getInstance() { + return instance; + } + + private final TreeMap symbolServerInstanceCreatorsByPriority = + new TreeMap<>(); + + private SymbolServerInstanceCreatorRegistry() { + registerDefaultSymbolServerInstanceCreators(); + } + + /** + * Registers a new SymbolServer implementation so that instances of + * it can be created by the user and saved / restored from preferences. + * + * @param priority relative order of precedence of polling this + * implementation's predicate to detect the specific SymbolServer + * implementation from a locationString. + * @param locationStringMatcher predicate that returns true / false if the specified String is + * handled by this SymbolServer implementation + * @param symbolServerInstanceCreator a method that creates a SymbolServer + * instance based on the specified location string and context + */ + public void registerSymbolServerInstanceCreator(int priority, + Predicate locationStringMatcher, + SymbolServerInstanceCreator symbolServerInstanceCreator) { + SymbolServerInstanceCreatorInfo symbolServerInstanceCreatorInfo = + new SymbolServerInstanceCreatorInfo(locationStringMatcher, symbolServerInstanceCreator); + + symbolServerInstanceCreatorsByPriority.put(priority, symbolServerInstanceCreatorInfo); + } + + /** + * Converts a list of symbol server location strings to a list of SymbolServer instances. + * + * @param locationStrings list of symbol server location strings + * @param symbolServerInstanceCreatorContext a {@link SymbolServerInstanceCreatorContext} + * - see {@link #getContext()} or {@link #getContext(Program)} + * @return list of {@link SymbolServer} + */ + public List createSymbolServersFromPathList(List locationStrings, + SymbolServerInstanceCreatorContext symbolServerInstanceCreatorContext) { + List result = new ArrayList<>(); + for (String locationString : locationStrings) { + SymbolServer symbolServer = + newSymbolServer(locationString, symbolServerInstanceCreatorContext); + if (symbolServer != null) { + result.add(symbolServer); + } + } + return result; + } + + /** + * Creates a new SymbolServer instance, using the registered SymbolServer types. + * + * @param symbolServerLocationString SymbolServer location - see {@link SymbolServer#getName()} + * @param symbolServerInstanceCreatorContext a {@link SymbolServerInstanceCreatorContext} + * - see {@link #getContext()} + * or {@link #getContext(Program)} + * @return new SymbolServer instance, or null if bad location string + */ + public SymbolServer newSymbolServer(String symbolServerLocationString, + SymbolServerInstanceCreatorContext symbolServerInstanceCreatorContext) { + return newSymbolServer(symbolServerLocationString, symbolServerInstanceCreatorContext, + SymbolServer.class); + } + + /** + * Creates a new SymbolServer instance, using the registered SymbolServer types. + * + * @param symbolServerLocationString SymbolServer location - see {@link SymbolServer#getName()} + * @param symbolServerInstanceCreatorContext a {@link SymbolServerInstanceCreatorContext} + * - see {@link #getContext()} + * @param expectedSymbolServerClass expected class of the new symbol server being created + * @return new SymbolServer instance, or null if bad location string + */ + public T newSymbolServer(String symbolServerLocationString, + SymbolServerInstanceCreatorContext symbolServerInstanceCreatorContext, + Class expectedSymbolServerClass) { + if (symbolServerLocationString == null || symbolServerLocationString.isBlank()) { + return null; + } + for (SymbolServerInstanceCreatorInfo symbolServerInstanceCreatorInfo : symbolServerInstanceCreatorsByPriority + .values()) { + if (symbolServerInstanceCreatorInfo.getLocationStringMatcher() + .test(symbolServerLocationString)) { + SymbolServer result = + symbolServerInstanceCreatorInfo.getSymbolServerInstanceCreator() + .createSymbolServerFromLocationString( + symbolServerLocationString, symbolServerInstanceCreatorContext); + if (result == null) { + return null; + } + if (!expectedSymbolServerClass.isInstance(result)) { + Msg.debug(this, "SymbolServer location unexpected class type. Wanted " + + expectedSymbolServerClass.getName() + ", got " + + result.getClass().getName()); + return null; + } + return expectedSymbolServerClass.cast(result); + } + } + Msg.debug(SymbolServerService.class, + "Symbol server location [" + symbolServerLocationString + "] not valid, skipping."); + return null; + } + + /** + * Creates a {@link SymbolServerInstanceCreatorContext} that is not bound to a Program. + * + * @return new {@link SymbolServerInstanceCreatorContext} + */ + public SymbolServerInstanceCreatorContext getContext() { + return new SymbolServerInstanceCreatorContext(this); + } + + /** + * Creates a new {@link SymbolServerInstanceCreatorContext} that is bound to a Program. + * + * @param program Ghidra program + * @return new {@link SymbolServerInstanceCreatorContext} + */ + public SymbolServerInstanceCreatorContext getContext(Program program) { + File exeLocation = new File(FilenameUtils.getFullPath(program.getExecutablePath())); + return new SymbolServerInstanceCreatorContext(exeLocation, this); + } + + private void registerDefaultSymbolServerInstanceCreators() { + registerSymbolServerInstanceCreator(0, DisabledSymbolServer::isDisabledSymbolServerLocation, + DisabledSymbolServer::createInstance); + registerSymbolServerInstanceCreator(100, HttpSymbolServer::isHttpSymbolServerLocation, + (loc, context) -> new HttpSymbolServer(URI.create(loc))); + registerSymbolServerInstanceCreator(200, SameDirSymbolStore::isSameDirLocation, + (loc, context) -> new SameDirSymbolStore(context.getRootDir())); + registerSymbolServerInstanceCreator(300, LocalSymbolStore::isLocalSymbolStoreLocation, + (loc, context) -> new LocalSymbolStore(new File(loc))); + } + + /** + * Functional interface that creates a new {@link SymbolServer} instance using a + * location string and a context instance. + *

    + * See {@link #createSymbolServerFromLocationString(String, SymbolServerInstanceCreatorContext)} + */ + public interface SymbolServerInstanceCreator { + /** + * Creates a new {@link SymbolServer} instance using the specified location string + * and the context available in the symbolServerInstanceCreatorContext. + * + * @param symbolServerLocationString location string + * @param symbolServerInstanceCreatorContext context + * @return new {@link SymbolServer} instance, null if error + */ + SymbolServer createSymbolServerFromLocationString(String symbolServerLocationString, + SymbolServerInstanceCreatorContext symbolServerInstanceCreatorContext); + } + + private static class SymbolServerInstanceCreatorInfo { + private Predicate locationStringMatcher; + private SymbolServerInstanceCreator symbolServerInstanceCreator; + + SymbolServerInstanceCreatorInfo(Predicate locationStringMatcher, + SymbolServerInstanceCreator symbolServerInstanceCreator) { + this.locationStringMatcher = locationStringMatcher; + this.symbolServerInstanceCreator = symbolServerInstanceCreator; + } + + Predicate getLocationStringMatcher() { + return locationStringMatcher; + } + + SymbolServerInstanceCreator getSymbolServerInstanceCreator() { + return symbolServerInstanceCreator; + } + + } + +} diff --git a/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/SymbolServerService.java b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/SymbolServerService.java new file mode 100644 index 0000000000..75cbd02c4a --- /dev/null +++ b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/SymbolServerService.java @@ -0,0 +1,288 @@ +/* ### + * 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.symbolserver; + +import java.util.*; +import java.util.stream.Collectors; + +import java.io.File; +import java.io.IOException; + +import org.apache.commons.io.FilenameUtils; + +import ghidra.util.Msg; +import ghidra.util.exception.CancelledException; +import ghidra.util.task.TaskMonitor; +import pdb.PdbUtils; + +/** + * A (lowercase-'S') service that searches for and fetches symbol files + * from a set of local and remote {@link SymbolServer symbolservers}. (not to be + * confused with a Plugin service) + *

    + * Instances of this class are meant to be easily created when needed + * and just as easily thrown away when not used or when the search + * path configuration changes. + *

    + * A SymbolServerService instance requires a {@link SymbolStore} and + * optionally a list of {@link SymbolServer}s. + */ +public class SymbolServerService { + + private SymbolStore symbolStore; // also the first element of the symbolServers list + private List symbolServers; + + /** + * Creates a new SymbolServerService instance. + *

    + * @param symbolStore a {@link SymbolStore} - where all + * remote files are placed when downloaded. Also treated as a SymbolServer + * and searched first + * @param symbolServers a list of {@link SymbolServer symbol servers} - searched in order + */ + public SymbolServerService(SymbolStore symbolStore, List symbolServers) { + this.symbolStore = symbolStore; + this.symbolServers = new ArrayList<>(); + this.symbolServers.add(symbolStore); + this.symbolServers.addAll(symbolServers); + } + + /** + * Returns true if this SymbolServerService is fully valid. + * Will be false if the symbol storage location isn't a {@link LocalSymbolStore}. + * + * @return boolean true if this instance is valid, false if not valid + */ + public boolean isValid() { + return symbolStore instanceof LocalSymbolStore; + } + + /** + * Returns the {@link SymbolStore}, which is the primary / first location queried and + * used to store any symbol files retrieved from a remote symbol server. + * + * @return the {@link SymbolStore} + */ + public SymbolStore getSymbolStore() { + return symbolStore; + } + + /** + * Returns the list of {@link SymbolServer}s. + * + * @return the list of {@link SymbolServer}s + */ + public List getSymbolServers() { + return new ArrayList<>(symbolServers.subList(1, symbolServers.size())); + } + + /** + * Returns the number of configured symbol servers that are considered 'remote'. + * @return number of remote symbol servers + */ + public int getRemoteSymbolServerCount() { + int remoteSymbolServerCount = (int) getSymbolServers() + .stream() + .filter(ss -> !ss.isLocal()) + .count(); + + return remoteSymbolServerCount; + } + + /** + * Searches all {@link SymbolServer symbol servers} for a matching pdb symbol file. + * + * @param symbolFileInfo {@link SymbolFileInfo} bag of information + * about the file to search for + * @param monitor {@link TaskMonitor} to update with search progress and to + * allow the user to cancel the operation + * @return a list of {@link SymbolFileLocation} instances + * @throws CancelledException if cancelled + */ + public List find(SymbolFileInfo symbolFileInfo, TaskMonitor monitor) + throws CancelledException { + return find(symbolFileInfo, FindOption.NO_OPTIONS, monitor); + } + + /** + * Searches all {@link SymbolServer symbol servers} for a matching pdb symbol file. + *

    + * Returns a list of matches. + *

    + * Use {@link SymbolFileLocation#isExactMatch(SymbolFileInfo)} to test elements in the + * result list for exactness. + *

    + * + * @param symbolFileInfo Pdb file info to search for + * @param findOptions set of {@link FindOption} to control the search. + * See {@link FindOption#NO_OPTIONS} or + * {@link FindOption#of(FindOption...) FindOptions.of(option1, option2...)} + * @param monitor {@link TaskMonitor} + * @return list of {@link SymbolFileLocation}s + * @throws CancelledException if operation canceled by user + */ + public List find(SymbolFileInfo symbolFileInfo, + Set findOptions, TaskMonitor monitor) throws CancelledException { + + List allFindResults = new ArrayList<>(); + Set uniqueSymbolFilePaths = new HashSet<>(); + + for_each_symbol_server_loop: for (SymbolServer symbolServer : symbolServers) { + monitor.checkCanceled(); + if (!symbolServer.isLocal() && !findOptions.contains(FindOption.ALLOW_REMOTE)) { + Msg.debug(this, + logPrefix() + ": skipping non-local symbol server " + + symbolServer.getDescriptiveName()); + continue; + } + + Msg.debug(this, logPrefix() + ": querying " + symbolServer.getDescriptiveName() + + " for " + symbolFileInfo.getDescription()); + + List symbolServerFindResults = + symbolServer.find(symbolFileInfo, findOptions, monitor); + + Msg.debug(this, + logPrefix() + ": got " + symbolServerFindResults.size() + " results from " + + symbolServer.getDescriptiveName()); + + // only add unique file locations + for (SymbolFileLocation symbolFileLocation : symbolServerFindResults) { + if (uniqueSymbolFilePaths.add(symbolFileLocation.getLocationStr())) { + allFindResults.add(symbolFileLocation); + if (findOptions.contains(FindOption.ONLY_FIRST_RESULT)) { + break for_each_symbol_server_loop; + } + } + } + } + + Msg.debug(this, logPrefix() + ": found " + allFindResults.size() + " matches"); + + return allFindResults; + + } + + /** + * Returns the local file path of the symbol file specified by symbolFileLocation. + * + * @param symbolFileLocation {@link SymbolFileLocation}, returned + * by {@link #find(SymbolFileInfo, Set, TaskMonitor) find()} + * @param monitor {@link TaskMonitor} + * @return {@link File} path to the local pdb file, never null + * @throws CancelledException if user cancels operation + * @throws IOException if error or problem getting file + */ + public File getSymbolFile(SymbolFileLocation symbolFileLocation, TaskMonitor monitor) + throws CancelledException, IOException { + Msg.debug(this, + logPrefix() + ": getting symbol file: " + symbolFileLocation.getLocationStr()); + + SymbolFileLocation localSymbolFileLocation = + ensureLocalUncompressedFile(symbolFileLocation, monitor); + + Msg.debug(this, + logPrefix() + ": local file now: " + localSymbolFileLocation.getLocationStr()); + + SymbolStore symbolStore = (SymbolStore) localSymbolFileLocation.getSymbolServer(); + + return symbolStore.getFile(localSymbolFileLocation.getPath()); + } + + /** + * Converts a possibly remote {@link SymbolFileLocation} to a location that is local and + * uncompressed. + * + * @param symbolFileLocation possibly remote {@link SymbolFileLocation} + * @param monitor {@link TaskMonitor} to display progress and allow canceling + * @return {@link SymbolFileLocation} that is local (possibly the same instance if already + * local) + * @throws CancelledException if canceled + * @throws IOException if error + */ + public SymbolFileLocation getLocalSymbolFileLocation(SymbolFileLocation symbolFileLocation, + TaskMonitor monitor) throws CancelledException, IOException { + Msg.debug(this, + logPrefix() + ": getting symbol file: " + symbolFileLocation.getLocationStr()); + + SymbolFileLocation localSymbolFileLocation = + ensureLocalUncompressedFile(symbolFileLocation, monitor); + + return localSymbolFileLocation; + } + + private SymbolFileLocation ensureLocalUncompressedFile(SymbolFileLocation symbolFileLocation, + TaskMonitor monitor) throws IOException, CancelledException { + if (!(symbolFileLocation.getSymbolServer() instanceof SymbolStore)) { + Msg.debug(this, logPrefix() + ": copying file " + symbolFileLocation.getLocationStr() + + " from remote to local " + symbolStore.getName()); + + // copy from remote store to our main local symbol store + String remoteFilename = FilenameUtils.getName(symbolFileLocation.getPath()); + try (SymbolServerInputStream symbolServerInputStream = + symbolFileLocation.getSymbolServer() + .getFileStream(symbolFileLocation.getPath(), monitor)) { + String newPath = + symbolStore.putStream(symbolFileLocation.getFileInfo(), symbolServerInputStream, + remoteFilename, monitor); + symbolFileLocation = + new SymbolFileLocation(newPath, symbolStore, symbolFileLocation.getFileInfo()); + } + } + + // symbolFileLocation now must be on a SymbolStore, so safe to cast + SymbolStore localSymbolStore = (SymbolStore) symbolFileLocation.getSymbolServer(); + + if (SymbolStore.isCompressedFilename(symbolFileLocation.getPath())) { + File cabFile = localSymbolStore.getFile(symbolFileLocation.getPath()); + File temporaryExtractFile = new File(symbolStore.getAdminDir(), + "ghidra_cab_extract_tmp_" + System.currentTimeMillis()); + + Msg.debug(this, + logPrefix() + ": decompressing file " + symbolFileLocation.getLocationStr()); + + String originalName = + PdbUtils.extractSingletonCabToFile(cabFile, temporaryExtractFile, monitor); + String uncompressedPath = + symbolStore.giveFile(symbolFileLocation.getFileInfo(), temporaryExtractFile, + originalName, monitor); + + symbolFileLocation = new SymbolFileLocation(uncompressedPath, symbolStore, + symbolFileLocation.getFileInfo()); + + Msg.debug(this, + logPrefix() + ": new decompressed file " + symbolFileLocation.getLocationStr()); + } + + return symbolFileLocation; + } + + private String logPrefix() { + return getClass().getSimpleName(); + } + + @Override + public String toString() { + return String.format( + "SymbolServerService:\n\tsymbolStore: %s,\n\tsymbolServers:\n\t\t%s\n", + symbolStore.toString(), + symbolServers.subList(1, symbolServers.size()) + .stream() + .map(SymbolServer::toString) + .collect(Collectors.joining("\n\t\t"))); + } + +} diff --git a/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/SymbolStore.java b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/SymbolStore.java new file mode 100644 index 0000000000..229bc78b37 --- /dev/null +++ b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/SymbolStore.java @@ -0,0 +1,91 @@ +/* ### + * 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.symbolserver; + +import java.io.File; +import java.io.IOException; + +import ghidra.util.task.TaskMonitor; + +/** + * A local writable {@link SymbolServer}. + */ +public interface SymbolStore extends SymbolServer { + + /** + * Returns the 'admin' directory of this SymbolStore, which allows files created here + * to be efficiently {@link #giveFile(SymbolFileInfo, File, String, TaskMonitor) given} + * to the store. + *

    + * + * @return directory + */ + File getAdminDir(); + + /** + * Returns an absolute {@link File} instance based on the specified relative path + * to a file inside the symbol store. + *

    + * + * @param path relative local path + * @return absolute {@link File} based on the specified relative path + */ + File getFile(String path); + + /** + * Offers the specified file to the SymbolStore. The file should be + * located in the admin directory of the SymbolStore to ensure no problems + * with ingesting the file. + *

    + * The file will be 'consumed' by this SymbolStore, and the caller's + * responsibility to the file ends. + * + * @param symbolFileInfo {@link SymbolFileInfo} bag of information about the file + * @param file {@link File} to ingest + * @param filename real name of the ingested file + * @param monitor {@link TaskMonitor} + * @return relative raw local path to the newly ingested file + * @throws IOException if error + */ + String giveFile(SymbolFileInfo symbolFileInfo, File file, String filename, TaskMonitor monitor) + throws IOException; + + /** + * Places the contents of the stream into a file in this SymbolStore. + *

    + * + * @param symbolFileInfo {@link SymbolFileInfo} bag of information about the file + * @param symbolServerInputStream the stream to ingest + * @param filename real name of the ingested file + * @param monitor {@link TaskMonitor} + * @return relative raw local path to the newly ingested file + * @throws IOException if error + */ + String putStream(SymbolFileInfo symbolFileInfo, SymbolServerInputStream symbolServerInputStream, + String filename, TaskMonitor monitor) throws IOException; + + /** + * Returns true if the specified filename indicates that the file is a compressed + * cab file. + * + * @param filename filename + * @return boolean true if filename indicates that the file is compressed + */ + public static boolean isCompressedFilename(String filename) { + return filename.endsWith("_"); + } + +} diff --git a/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/ConfigPdbDialog.java b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/ConfigPdbDialog.java new file mode 100644 index 0000000000..cbeb0de846 --- /dev/null +++ b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/ConfigPdbDialog.java @@ -0,0 +1,79 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package pdb.symbolserver.ui; + +import docking.DialogComponentProvider; +import docking.DockingWindowManager; +import docking.widgets.OptionDialog; +import pdb.symbolserver.SymbolServerInstanceCreatorRegistry; +import pdb.symbolserver.SymbolServerService; + +/** + * Dialog that allows the user to configure the Pdb search locations and symbol directory + */ +public class ConfigPdbDialog extends DialogComponentProvider { + + public static void showSymbolServerConfig() { + ConfigPdbDialog choosePdbDialog = new ConfigPdbDialog(); + DockingWindowManager.showDialog(choosePdbDialog); + } + + private SymbolServerPanel symbolServerConfigPanel; + + public ConfigPdbDialog() { + super("Configure Symbol Server Search", true, false, true, false); + + build(); + } + + @Override + protected void cancelCallback() { + close(); + } + + @Override + protected void okCallback() { + if (symbolServerConfigPanel.isConfigChanged() && + OptionDialog.showYesNoDialog(getComponent(), + "Save Configuration", + "Symbol server configuration changed. Save?") == OptionDialog.YES_OPTION) { + symbolServerConfigPanel.saveConfig(); + } + close(); + } + + private void build() { + symbolServerConfigPanel = new SymbolServerPanel(this::onSymbolServerServiceChange, + SymbolServerInstanceCreatorRegistry.getInstance().getContext()); + + addButtons(); + addWorkPanel(symbolServerConfigPanel); + setRememberSize(false); + okButton.setEnabled(symbolServerConfigPanel.getSymbolServerService() != null); + setMinimumSize(400, 250); + } + + private void onSymbolServerServiceChange(SymbolServerService newService) { + okButton.setEnabled(newService != null); + rootPanel.revalidate(); + } + + private void addButtons() { + addOKButton(); + addCancelButton(); + setDefaultButton(cancelButton); + } +} diff --git a/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/FilePromptDialog.java b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/FilePromptDialog.java new file mode 100644 index 0000000000..2b6fe7cf6a --- /dev/null +++ b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/FilePromptDialog.java @@ -0,0 +1,195 @@ +/* ### + * 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.symbolserver.ui; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.io.File; + +import javax.swing.*; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; + +import docking.DialogComponentProvider; +import docking.DockingWindowManager; +import docking.options.editor.ButtonPanelFactory; +import docking.widgets.OptionDialog; +import docking.widgets.filechooser.GhidraFileChooser; +import docking.widgets.filechooser.GhidraFileChooserMode; +import docking.widgets.label.GHtmlLabel; +import ghidra.util.filechooser.GhidraFileFilter; +import ghidra.util.layout.PairLayout; + +/** + * Non-public, package-only dialog that prompts the user to enter a path + * in a text field (similar to an {@link OptionDialog}) and allows them to click + * a "..." browse button to pick the file and/or directory via a + * {@link GhidraFileChooser} dialog. + */ +class FilePromptDialog extends DialogComponentProvider { + + /** + * Prompts the user to enter the path to a directory, + * or to pick it using a browser dialog. + * + * @param title the dialog title + * @param prompt HTML enabled prompt + * @param initialValue initial value to pre-populate the input field with + * @return the {@link File} the user entered / picked, or null if canceled + */ + public static File chooseDirectory(String title, String prompt, File initialValue) { + return chooseFile(title, prompt, "Choose", null, initialValue, + GhidraFileChooserMode.DIRECTORIES_ONLY); + } + + /** + * Prompts the user to entry the path to a file and/or directory, + * or to pick it using a browser dialog. + *

    + * + * @param title the dialog title + * @param prompt HTML enabled prompt + * @param chooseButtonText text of the choose button in the browser dialog + * @param directory the initial directory of the browser dialog + * @param initialFileValue the initial value to pre-populate the input field with + * @param chooserMode {@link GhidraFileChooserMode} of the browser dialog + * @param fileFilters optional {@link GhidraFileFilter filters} + * @return the {@link File} the user entered / picked, or null if canceled + */ + public static File chooseFile(String title, String prompt, String chooseButtonText, + File directory, File initialFileValue, GhidraFileChooserMode chooserMode, + GhidraFileFilter... fileFilters) { + FilePromptDialog filePromptDialog = new FilePromptDialog(title, prompt, chooseButtonText, + directory, initialFileValue, chooserMode, fileFilters); + DockingWindowManager.showDialog(filePromptDialog); + return filePromptDialog.chosenValue; + } + + private GhidraFileChooser chooser; + private GhidraFileFilter[] fileFilters; + private File directory; + private File file; + private String approveButtonText; + private JTextField filePathTextField; + private GhidraFileChooserMode chooserMode; + private File chosenValue; + + protected FilePromptDialog(String title, String prompt, String approveButtonText, + File directory, File file, GhidraFileChooserMode chooserMode, + GhidraFileFilter... fileFilters) { + super(title, true, false, true, false); + + this.approveButtonText = approveButtonText; + this.directory = directory; + this.file = file; + this.chooserMode = chooserMode; + this.fileFilters = fileFilters; + setRememberSize(false); + + build(prompt); + updateButtonEnablement(); + } + + private void build(String prompt) { + + GHtmlLabel promptLabel = new GHtmlLabel(prompt); + filePathTextField = new JTextField(file != null ? file.getPath() : null, 40); + filePathTextField.getDocument().addDocumentListener(new DocumentListener() { + @Override + public void removeUpdate(DocumentEvent e) { + updateButtonEnablement(); + } + + @Override + public void insertUpdate(DocumentEvent e) { + updateButtonEnablement(); + } + + @Override + public void changedUpdate(DocumentEvent e) { + updateButtonEnablement(); + } + }); + JButton browseButton = ButtonPanelFactory.createButton(ButtonPanelFactory.BROWSE_TYPE); + browseButton.addActionListener(e -> browse()); + + JPanel textFieldWithButtonPanel = new JPanel(new BorderLayout()); + textFieldWithButtonPanel.add(filePathTextField, BorderLayout.CENTER); + textFieldWithButtonPanel.add(browseButton, BorderLayout.EAST); + + JPanel mainPanel = new JPanel(new PairLayout()); + mainPanel.add(promptLabel); + mainPanel.add(textFieldWithButtonPanel); + Dimension size = mainPanel.getPreferredSize(); + size.width = Math.max(size.width, 500); + mainPanel.setPreferredSize(size); + mainPanel.setMinimumSize(size); + JPanel newMain = new JPanel(new BorderLayout()); + newMain.add(mainPanel, BorderLayout.CENTER); + + addWorkPanel(newMain); + addOKButton(); + addCancelButton(); + } + + private void updateButtonEnablement() { + okButton.setEnabled(!filePathTextField.getText().isBlank()); + } + + @Override + protected void okCallback() { + chosenValue = new File(filePathTextField.getText()); + close(); + } + + @Override + protected void cancelCallback() { + chosenValue = null; + close(); + } + + private void browse() { + initChooser(); + String filePathText = filePathTextField.getText(); + filePathText = filePathText.isBlank() && file != null ? file.getPath() : ""; + if (!filePathText.isBlank()) { + chooser.setSelectedFile(new File(filePathText)); + } + File selectedFile = chooser.getSelectedFile(); + if (selectedFile != null) { + filePathTextField.setText(selectedFile.getPath()); + } + filePathTextField.requestFocusInWindow(); + } + + private void initChooser() { + + if (chooser == null) { + chooser = new GhidraFileChooser(rootPanel); + for (GhidraFileFilter gff : fileFilters) { + chooser.addFileFilter(gff); + } + chooser.setMultiSelectionEnabled(false); + chooser.setApproveButtonText(approveButtonText); + chooser.setFileSelectionMode(chooserMode); + chooser.setTitle(getTitle()); + + if (directory != null) { + chooser.setCurrentDirectory(directory); + } + } + } +} diff --git a/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/LoadPdbDialog.java b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/LoadPdbDialog.java new file mode 100644 index 0000000000..00087d802c --- /dev/null +++ b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/LoadPdbDialog.java @@ -0,0 +1,942 @@ +/* ### + * 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.symbolserver.ui; + +import java.awt.*; +import java.awt.event.*; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.function.Supplier; + +import javax.swing.*; + +import docking.DialogComponentProvider; +import docking.DockingWindowManager; +import docking.event.mouse.GMouseListenerAdapter; +import docking.options.editor.ButtonPanelFactory; +import docking.widgets.OptionDialog; +import docking.widgets.checkbox.GCheckBox; +import docking.widgets.combobox.GComboBox; +import docking.widgets.filechooser.GhidraFileChooser; +import docking.widgets.filechooser.GhidraFileChooserMode; +import docking.widgets.label.GIconLabel; +import docking.widgets.label.GLabel; +import docking.widgets.textfield.HintTextField; +import docking.widgets.textfield.IntegerTextField; +import ghidra.app.util.bin.format.pdb.PdbParser; +import ghidra.app.util.pdb.pdbapplicator.PdbApplicatorControl; +import ghidra.framework.preferences.Preferences; +import ghidra.program.model.listing.Program; +import ghidra.util.*; +import ghidra.util.exception.CancelledException; +import ghidra.util.filechooser.ExtensionFileFilter; +import ghidra.util.filechooser.GhidraFileFilter; +import ghidra.util.layout.PairLayout; +import ghidra.util.task.*; +import pdb.PdbPlugin; +import pdb.symbolserver.*; +import resources.Icons; +import resources.ResourceManager; + +/** + * A dialog that allows the user to pick or search for a Pdb file for a program. + */ +public class LoadPdbDialog extends DialogComponentProvider { + + private static final String LAST_PDBFILE_PREFERENCE_KEY = "Pdb.LastFile"; + static final Icon MATCH_OK_ICON = + ResourceManager.loadImage("images/checkmark_green.gif", 16, 16); + static final Icon MATCH_BAD_ICON = + ResourceManager.loadImage("images/emblem-important.png", 16, 16); + public static final GhidraFileFilter PDB_FILES_FILTER = + ExtensionFileFilter.forExtensions("Microsoft Program Databases", "pdb", "pd_", "pdb.xml"); + + public static class LoadPdbResults { + public File pdbFile; + public PdbApplicatorControl control; + public boolean useMsDiaParser; + public boolean debugLogging; + } + + /** + * Shows a modal dialog to the user, allowing them to pick or search for a Pdb + * file.

    + * The selected file and parser options are returned in a LoadPdbResults instance. + * + * @param program the Ghidra {@link Program} that has Pdb info + * @return LoadPdbResults instance with the selected file and options, or null if canceled + */ + public static LoadPdbResults choosePdbForProgram(Program program) { + LoadPdbDialog choosePdbDlg = new LoadPdbDialog(program); + DockingWindowManager.showDialog(choosePdbDlg); + File pdbFile = choosePdbDlg.getLocalSymbolFile(choosePdbDlg.selectedSymbolFile); + if (pdbFile == null) { + return null; + } + LoadPdbResults results = new LoadPdbResults(); + results.pdbFile = pdbFile; + results.control = + (PdbApplicatorControl) choosePdbDlg.restrictionsCombo.getSelectedItem(); + results.useMsDiaParser = choosePdbDlg.msdiaParserButton.isSelected(); + results.debugLogging = choosePdbDlg.debugLoggingCheckbox.isSelected(); + return results; + } + + private SymbolFileLocation selectedSymbolFile; + + private SymbolServerService symbolServerService; + private SymbolServerInstanceCreatorContext symbolServerInstanceCreatorContext; + + private SymbolFileInfo programSymbolFileInfo; + + private List> statusTextSuppliers = new ArrayList<>(); + private boolean hasPerformedSearch; + private boolean searchCanceled; + + private Program program; + + private SymbolServerPanel symbolServerConfigPanel; + private SymbolFilePanel symbolFilePanel; + + private JTextField programNameTextField; + private JTextField pdbPathTextField; + private GCheckBox overridePdbPathCheckBox; + private JTextField pdbUniqueIdTextField; + private GCheckBox overridePdbUniqueIdCheckBox; + private IntegerTextField pdbAgeTextField; + private GCheckBox overridePdbAgeCheckBox; + private HintTextField pdbLocationTextField; + private GIconLabel exactMatchIconLabel; + + private JToggleButton advancedToggleButton; + + private GhidraFileChooser chooser; + + private JButton choosePdbLocationButton; + private JButton loadPdbButton; + + private JPanel pdbLocationPanel; + private JPanel programPdbPanel; + private JComponent workComp; + + private JPanel parserOptionsPanel; + private JRadioButton universalParserButton; + private JRadioButton msdiaParserButton; + private GComboBox restrictionsCombo; + private GCheckBox debugLoggingCheckbox; + + /** + * Creates a new instance of the LoadPdbDialog class. + * + * @param program the ghidra {@link Program} that is loading the Pdb + */ + public LoadPdbDialog(Program program) { + super("Load PDB for " + program.getName(), true, true, true, true); + setRememberSize(false); + + this.program = program; + this.programSymbolFileInfo = SymbolFileInfo.fromMetadata(program.getMetadata()); + if (programSymbolFileInfo == null) { + programSymbolFileInfo = SymbolFileInfo.unknown("missing"); + } + this.symbolServerInstanceCreatorContext = + SymbolServerInstanceCreatorRegistry.getInstance().getContext(program); + this.symbolServerService = + PdbPlugin.getSymbolServerService(symbolServerInstanceCreatorContext); + + build(); + } + + @Override + protected void dialogShown() { + pdbPathTextField.setText(programSymbolFileInfo.getPath()); + pdbUniqueIdTextField.setText(programSymbolFileInfo.getUniqueName()); + pdbAgeTextField.setValue(programSymbolFileInfo.getIdentifiers().getAge()); + programNameTextField.setText(program.getName()); + cancelButton.requestFocusInWindow(); + + executeMonitoredRunnable("Search for PDB using built-in locations", true, true, 0, + this::doInitialDefaultSearch); + } + + private void doInitialDefaultSearch(TaskMonitor monitor) { + try { + List results = + symbolServerService.find(programSymbolFileInfo, FindOption.NO_OPTIONS, monitor); + if (!results.isEmpty()) { + SymbolFileLocation symbolFileLocation = + symbolServerService.getLocalSymbolFileLocation(results.get(0), monitor); + File symbolFile = getLocalSymbolFile(symbolFileLocation); + Swing.runLater(() -> { + setSearchResults(results); + setPdbLocationValue(symbolFileLocation, symbolFile); + setSelectedPdbFile(symbolFileLocation); + selectRowByLocation(symbolFileLocation); + updateStatusText(); + updateButtonEnablement(); + updateParserOptionEnablement(true); + }); + } + } + catch (CancelledException | IOException e) { + // ignore + } + } + + @Override + protected void cancelCallback() { + selectedSymbolFile = null; + close(); + } + + /** + * For screenshot use only + * + * @param options set of {@link FindOption} enum + */ + public void setSearchOptions(Set options) { + symbolFilePanel.setFindOptions(options); + } + + /** + * For screenshot use only + * + * @param pathStr path of symbol storage directory + */ + public void setSymbolStorageDirectoryTextOnly(String pathStr) { + symbolServerConfigPanel.setSymbolStorageDirectoryTextOnly(pathStr); + } + + /** + * For screenshot use only + * + * @param symbolServers list of symbol servers + */ + public void setSymbolServers(List symbolServers) { + symbolServerConfigPanel.setSymbolServers(symbolServers); + } + + /** + * For screenshot use only + */ + public void pushAddLocationBution() { + symbolServerConfigPanel.pushAddLocationButton(); + } + + private void setSelectedPdbFile(SymbolFileLocation symbolFileLocation) { + this.selectedSymbolFile = symbolFileLocation; + } + + /** + * Sets the contents of the search results table. + *

    + * Public only for screenshot usage, treat as private otherwise. + * + * @param results list of {@link SymbolFileLocation}s to add to results + */ + public void setSearchResults(List results) { + hasPerformedSearch = true; + symbolFilePanel.getTableModel().setSearchResults(programSymbolFileInfo, results); + } + + /** + * Selects a row in the results table. + *

    + * Public only for screenshot usage. Treat as private. + * + * @param symbolFileLocation {@link SymbolFileLocation} to select in results table + */ + public void selectRowByLocation(SymbolFileLocation symbolFileLocation) { + for (int i = 0; i < symbolFilePanel.getTableModel().getModelData().size(); i++) { + SymbolFileRow symbolFileRow = symbolFilePanel.getTableModel().getModelData().get(i); + if (symbolFileRow.getLocation().equals(symbolFileLocation)) { + symbolFilePanel.getTable().selectRow(i); + return; + } + } + symbolFilePanel.getTable().clearSelection(); + } + + private StatusText getSelectedPdbNoticeText() { + if (selectedSymbolFile == null) { + return null; + } + if (selectedSymbolFile.getFileInfo() == null) { + return new StatusText("Unable to read Pdb information", MessageType.ERROR, false); + } + return !selectedSymbolFile.isExactMatch(programSymbolFileInfo) + ? new StatusText("WARNING: Selected PDB is not an exact match!", + MessageType.WARNING, false) + : null; + } + + private String getSymbolFileToolText(SymbolFileLocation symbolFileLocation) { + return symbolFileLocation != null + ? String.format( + "" + + "" + + "" + + "" + + "" + + "
    PDB Name:%s
    Path:%s
    GUID/ID:%s
    Age:%x
    Is Exact Match:%b" + + "
    ", + HTMLUtilities.escapeHTML(symbolFileLocation.getFileInfo().getName()), + HTMLUtilities.escapeHTML(symbolFileLocation.getLocationStr()), + symbolFileLocation.getFileInfo().getUniqueName(), + symbolFileLocation.getFileInfo().getIdentifiers().getAge(), + symbolFileLocation.getFileInfo().isExactMatch(programSymbolFileInfo)) + : null; + } + + private void updateButtonEnablement() { + boolean hasLocation = selectedSymbolFile != null; + loadPdbButton.setEnabled(hasLocation); + } + + private void setSymbolServerService(SymbolServerService symbolServerService) { + this.symbolServerService = symbolServerService; + symbolFilePanel.setEnablement(symbolServerService != null); + updateStatusText(); + } + + private SymbolFileInfo getCurrentSymbolFileInfo() { + String pdbPath = pdbPathTextField.getText(); + String uid = pdbUniqueIdTextField.getText(); + int age = pdbAgeTextField.getIntValue(); + + return SymbolFileInfo.fromValues(pdbPath, uid, age); + } + + private void searchForPdbs(ActionEvent e) { + if (symbolServerService == null || !symbolServerService.isValid()) { + return; + } + if (pdbAgeTextField.getText().isBlank()) { + Msg.showWarn(this, null, "Bad PDB Age", "Invalid PDB Age value"); + return; + } + SymbolFileInfo symbolFileInfo = getCurrentSymbolFileInfo(); + if (symbolFileInfo == null) { + Msg.showWarn(this, null, "Bad PDB GUID/Id", + "Invalid PDB GUID / UID value: " + pdbUniqueIdTextField.getText()); + return; + } + Set findOptions = symbolFilePanel.getFindOptions(); + executeMonitoredRunnable("Search for PDBs", true, true, 0, monitor -> { + try { + searchCanceled = false; + List results = + symbolServerService.find(symbolFileInfo, findOptions, monitor); + Swing.runLater(() -> { + setSearchResults(results); + if (results.size() == 1) { + selectRowByLocation(results.get(0)); + } + updateStatusText(); + updateButtonEnablement(); + }); + } + catch (CancelledException e1) { + searchCanceled = true; + Swing.runLater(() -> updateStatusText()); + } + }); + + } + + private void build() { + buildSymbolFilePanel(); + buildSSConfigPanel(); + buildPdbLocationPanel(); + buildProgramPdbPanel(); + buildParserOptionsPanel(); + setHelpLocation(new HelpLocation(PdbPlugin.PDB_PLUGIN_HELP_TOPIC, "Load PDB File")); + + addStatusTextSupplier(() -> hasPerformedSearch && advancedToggleButton.isSelected() + ? symbolServerConfigPanel.getSymbolServerWarnings() + : null); + addStatusTextSupplier(this::getSelectedPdbNoticeText); + addStatusTextSupplier(this::getConfigChangedWarning); + addStatusTextSupplier(this::getAllowRemoteWarning); + addStatusTextSupplier(this::getFoundCountInfo); + + addButtons(); + layoutSimple(); + + updateStatusText(); + updateButtonEnablement(); + // later dialogShow() will be called + } + + private void buildSSConfigPanel() { + symbolServerConfigPanel = + new SymbolServerPanel(this::setSymbolServerService, symbolServerInstanceCreatorContext); + } + + private void buildSymbolFilePanel() { + symbolFilePanel = new SymbolFilePanel(this::searchForPdbs); // panel will be added in layoutAdvanced() + + symbolFilePanel.getTable() + .getSelectionModel() + .addListSelectionListener(e -> updateSelectedRow()); + symbolFilePanel.addMouseListener(new GMouseListenerAdapter() { + @Override + public void doubleClickTriggered(MouseEvent e) { + if (loadPdbButton.isEnabled()) { + e.consume(); + loadPdbButton.doClick(); + } + } + }); + } + + private void updateSelectedRow() { + SymbolFileRow row = symbolFilePanel.getSelectedRow(); + setSelectedPdbFile(row != null ? row.getLocation() : null); + updateStatusText(); + updateButtonEnablement(); + updateParserOptionEnablement(true); + } + + private JPanel buildProgramPdbPanel() { + + programNameTextField = new BetterNonEditableTextField(20); + programNameTextField.setEditable(false); + + pdbPathTextField = new BetterNonEditableTextField(20); + pdbPathTextField.setEditable(false); + + overridePdbPathCheckBox = new GCheckBox(); + overridePdbPathCheckBox.setVisible(false); + overridePdbPathCheckBox.setToolTipText("Override PDB name (when searching)."); + overridePdbPathCheckBox.addItemListener(e -> { + pdbPathTextField.setEditable(overridePdbPathCheckBox.isSelected()); + if (overridePdbPathCheckBox.isSelected()) { + pdbPathTextField.requestFocusInWindow(); + } + else { + pdbPathTextField.setText(programSymbolFileInfo.getPath()); + } + }); + DockingWindowManager.getHelpService() + .registerHelp(overridePdbPathCheckBox, + new HelpLocation(PdbPlugin.PDB_PLUGIN_HELP_TOPIC, + SymbolFilePanel.SEARCH_OPTIONS_HELP_ANCHOR)); + + pdbUniqueIdTextField = new BetterNonEditableTextField(36); + pdbUniqueIdTextField.setEditable(false); + pdbUniqueIdTextField.setToolTipText( + "PDB GUID - either 36 or 32 hexadecimal characters:
    " + + "  '012345678-0123-0123-0123-0123456789ABC' or '0123456780123012301230123456789ABC', or
    " + + "PDB Signature Id - 8 hexadecimal character Id:
    " + + "  '11223344'"); + + overridePdbUniqueIdCheckBox = new GCheckBox(); + overridePdbUniqueIdCheckBox.setVisible(false); + overridePdbUniqueIdCheckBox.setToolTipText("Override PDB unique id (when searching)."); + overridePdbUniqueIdCheckBox.addItemListener(e -> { + pdbUniqueIdTextField.setEditable(overridePdbUniqueIdCheckBox.isSelected()); + if (overridePdbUniqueIdCheckBox.isSelected()) { + pdbUniqueIdTextField.requestFocusInWindow(); + } + else { + pdbUniqueIdTextField.setText(programSymbolFileInfo.getUniqueName()); + } + }); + DockingWindowManager.getHelpService() + .registerHelp(overridePdbUniqueIdCheckBox, + new HelpLocation(PdbPlugin.PDB_PLUGIN_HELP_TOPIC, + SymbolFilePanel.SEARCH_OPTIONS_HELP_ANCHOR)); + + pdbAgeTextField = new IntegerTextField(8); + pdbAgeTextField.setAllowNegativeValues(false); + pdbAgeTextField.setShowNumberMode(true); + pdbAgeTextField.setHexMode(); + pdbAgeTextField.setEditable(false); + + overridePdbAgeCheckBox = new GCheckBox(); + overridePdbAgeCheckBox.setVisible(false); + overridePdbAgeCheckBox.setToolTipText("Override PDB age (when searching)."); + overridePdbAgeCheckBox.addItemListener(e -> { + pdbAgeTextField.setEditable(overridePdbAgeCheckBox.isSelected()); + if (overridePdbAgeCheckBox.isSelected()) { + pdbAgeTextField.requestFocus(); + } + else { + pdbAgeTextField.setValue(programSymbolFileInfo.getIdentifiers().getAge()); + } + }); + DockingWindowManager.getHelpService() + .registerHelp(overridePdbAgeCheckBox, + new HelpLocation(PdbPlugin.PDB_PLUGIN_HELP_TOPIC, + SymbolFilePanel.SEARCH_OPTIONS_HELP_ANCHOR)); + + programPdbPanel = new JPanel(new PairLayout(5, 5)); + programPdbPanel.setBorder(BorderFactory.createTitledBorder("Program PDB Information")); + programPdbPanel.add(new GLabel("Program:", SwingConstants.RIGHT)); + programPdbPanel.add(programNameTextField); + + programPdbPanel.add( + join(null, new GLabel("PDB Name:", SwingConstants.RIGHT), overridePdbPathCheckBox)); + programPdbPanel.add(pdbPathTextField); + + programPdbPanel.add(join(null, new GLabel("PDB Unique Id:", SwingConstants.RIGHT), + overridePdbUniqueIdCheckBox)); + programPdbPanel.add(pdbUniqueIdTextField); + + programPdbPanel.add( + join(null, new GLabel("PDB Age:", SwingConstants.RIGHT), overridePdbAgeCheckBox)); + programPdbPanel.add(join(pdbAgeTextField.getComponent(), new JPanel(), null)); + + return programPdbPanel; + } + + private JPanel buildPdbLocationPanel() { + pdbLocationTextField = new HintTextField("Browse [...] for PDB file or use 'Advanced'"); + pdbLocationTextField.setEditable(false); + + choosePdbLocationButton = ButtonPanelFactory.createButton(ButtonPanelFactory.BROWSE_TYPE); + choosePdbLocationButton.addActionListener(e -> choosePdbFile()); + + exactMatchIconLabel = new GIconLabel(Icons.EMPTY_ICON); + + pdbLocationPanel = new JPanel(new PairLayout(5, 5)); + pdbLocationPanel.setBorder(BorderFactory.createTitledBorder("PDB Location")); + pdbLocationPanel.add(new GLabel("PDB Location:", SwingConstants.RIGHT)); + pdbLocationPanel + .add(join(exactMatchIconLabel, pdbLocationTextField, choosePdbLocationButton)); + return pdbLocationPanel; + } + + private void updateParserOptionEnablement(boolean trySetUniversal) { + if (trySetUniversal) { + universalParserButton.setSelected(true); + msdiaParserButton.setSelected(false); + } + + boolean isXML = (selectedSymbolFile != null && + selectedSymbolFile.getPath().toLowerCase().endsWith(".pdb.xml")); + boolean isWindows = PdbParser.onWindows; + msdiaParserButton.setEnabled(isXML || isWindows); + if (isXML) { + msdiaParserButton.setSelected(true); + } + if (msdiaParserButton.isSelected() && !msdiaParserButton.isEnabled()) { + msdiaParserButton.setSelected(false); + } + if (!isWindows && !isXML) { + universalParserButton.setSelected(true); + } + universalParserButton.setEnabled(!isXML); + if (universalParserButton.isSelected() && !universalParserButton.isEnabled()) { + universalParserButton.setSelected(false); + } + restrictionsCombo.setEnabled(universalParserButton.isSelected()); + debugLoggingCheckbox.setEnabled(universalParserButton.isSelected()); + } + + private JPanel buildParserOptionsPanel() { + + ActionListener l = (e) -> updateParserOptionEnablement(false); + universalParserButton = new JRadioButton("Universal"); + universalParserButton + .setToolTipText("Platform-independent PDB analyzer (No PDB.XML support)."); + msdiaParserButton = new JRadioButton("MSDIA"); + msdiaParserButton.setToolTipText( + "Legacy PDB Analyzer.
    " + + "Requires MS DIA-SDK for raw PDB processing (Windows only), or preprocessed PDB.XML file."); + universalParserButton.setSelected(true); + universalParserButton.addActionListener(l); + msdiaParserButton.addActionListener(l); + + ButtonGroup buttonGroup = new ButtonGroup(); + buttonGroup.add(msdiaParserButton); + buttonGroup.add(universalParserButton); + + JPanel radioButtons = new JPanel(new FlowLayout(FlowLayout.LEFT)); + radioButtons.add(universalParserButton); + radioButtons.add(msdiaParserButton); + + restrictionsCombo = new GComboBox<>(PdbApplicatorControl.values()); + restrictionsCombo.setSelectedItem(PdbApplicatorControl.ALL); + + debugLoggingCheckbox = new GCheckBox(); + debugLoggingCheckbox.setToolTipText( + "If checked, logs information to the pdb.analyzer.log file for debug/development."); + + parserOptionsPanel = new JPanel(new PairLayout(5, 5)); + parserOptionsPanel.setBorder(BorderFactory.createTitledBorder("PDB Parser")); + DockingWindowManager.getHelpService() + .registerHelp(parserOptionsPanel, + new HelpLocation(PdbPlugin.PDB_PLUGIN_HELP_TOPIC, + "PDB Parser Panel")); + + parserOptionsPanel.add(new GLabel("Parser:")); + parserOptionsPanel.add(radioButtons); + + parserOptionsPanel.add(new GLabel("Control:")); + parserOptionsPanel.add(restrictionsCombo); + + parserOptionsPanel.add(new GLabel("[Dev] PDB Reader/Applicator Debug Logging:")); + parserOptionsPanel.add(debugLoggingCheckbox); + + return parserOptionsPanel; + } + + private void addButtons() { + + loadPdbButton = new JButton("Load"); + loadPdbButton.setName("Load"); + + loadPdbButton.addActionListener(e -> { + if (selectedSymbolFile == null || + (!selectedSymbolFile.isExactMatch(programSymbolFileInfo) && + OptionDialog.showYesNoDialog(loadPdbButton, "Mismatched Pdb File Warning", + "The selected file is not an exact match for the current program.
    " + + "Note: Invalid disassembly may be produced!
    Continue anyway?") != OptionDialog.YES_OPTION)) { + return; + } + executeMonitoredRunnable("Prepare Selected Symbol File", + true, true, 0, this::prepareSelectedSymbolFileAndClose); + }); + addButton(loadPdbButton); + + addCancelButton(); + setDefaultButton(cancelButton); + + advancedToggleButton = new JToggleButton("Advanced >>"); + advancedToggleButton.addActionListener(e -> toggleAdvancedSearch()); + buttonPanel.add(advancedToggleButton); + } + + private void prepareSelectedSymbolFileAndClose(TaskMonitor monitor) { + try { + if (selectedSymbolFile != null && symbolServerService != null) { + selectedSymbolFile = + symbolServerService.getLocalSymbolFileLocation(selectedSymbolFile, monitor); + } + Swing.runLater(() -> close()); + return; + } + catch (IOException ioe) { + Msg.showError(this, getComponent(), "Error Getting Symbol File", ioe); + } + catch (CancelledException ce) { + // ignore + } + } + + private StatusText getConfigChangedWarning() { + return advancedToggleButton.isSelected() && symbolServerConfigPanel.isConfigChanged() + ? new StatusText( + "Symbol Server Search Config Changed. Click \"Save Configuration\" button to save.", + MessageType.INFO, false) + : null; + } + + private StatusText getAllowRemoteWarning() { + int remoteSymbolServerCount = + symbolServerService != null ? symbolServerService.getRemoteSymbolServerCount() : 0; + Set findOptions = symbolFilePanel.getFindOptions(); + return hasPerformedSearch && advancedToggleButton.isSelected() && + remoteSymbolServerCount != 0 && !findOptions.contains(FindOption.ALLOW_REMOTE) + ? new StatusText( + "Remote servers were excluded. Select \"Allow remote\" checkbox to search remote servers.", + MessageType.INFO, false) + : null; + } + + private StatusText getFoundCountInfo() { + if (advancedToggleButton.isSelected()) { + if (searchCanceled) { + return new StatusText("Search canceled", MessageType.INFO, false); + } + if (hasPerformedSearch) { + int foundCount = symbolFilePanel.getTableModel().getModelData().size(); + return new StatusText( + "Found " + foundCount + " file" + (foundCount != 1 ? "s" : ""), + MessageType.INFO, false); + } + } + return null; + } + + private void toggleAdvancedSearch() { + boolean isAdvanced = advancedToggleButton.isSelected(); + advancedToggleButton.setText("Advanced " + (isAdvanced ? "<<" : ">>")); + + overridePdbAgeCheckBox.setVisible(isAdvanced); + overridePdbPathCheckBox.setVisible(isAdvanced); + overridePdbUniqueIdCheckBox.setVisible(isAdvanced); + setPdbLocationValue(null, null); + + if (isAdvanced) { + if (symbolServerService == null || !symbolServerService.isValid()) { + setSelectedPdbFile(null); + } + layoutAdvanced(); + } + else { + if (selectedSymbolFile != null) { + File localSymbolFile = getLocalSymbolFile(selectedSymbolFile); + if (localSymbolFile != null) { + setPdbLocationValue(selectedSymbolFile, localSymbolFile); + } + } + else { + setSelectedPdbFile(null); + } + layoutSimple(); + } + + updateStatusText(); + updateButtonEnablement(); + updateParserOptionEnablement(false); + repack(); + } + + private void layoutSimple() { + Box box = Box.createVerticalBox(); + box.add(programPdbPanel); + box.add(pdbLocationPanel); + box.add(parserOptionsPanel); + + JPanel panel = new JPanel(new BorderLayout()); + panel.add(box, BorderLayout.NORTH); + + overrideWorkPanel(panel); + } + + private void overrideWorkPanel(JComponent workComp) { + if (this.workComp != null && this.workComp.getParent() != null) { + this.workComp.getParent().remove(this.workComp); + } + this.workComp = workComp; + addWorkPanel(workComp); + } + + private void layoutAdvanced() { + Box topPanel = Box.createHorizontalBox(); + topPanel.add(programPdbPanel); + topPanel.add(symbolServerConfigPanel); + + JPanel mainPanel = new JPanel(new BorderLayout()); + mainPanel.add(topPanel, BorderLayout.NORTH); + mainPanel.add(symbolFilePanel, BorderLayout.CENTER); + mainPanel.add(parserOptionsPanel, BorderLayout.SOUTH); + + overrideWorkPanel(mainPanel); + } + + private void choosePdbFile() { + File file = getChooser().getSelectedFile(); + if (file != null && file.isFile()) { + Preferences.setProperty(LAST_PDBFILE_PREFERENCE_KEY, file.getPath()); + executeMonitoredRunnable("Get PDB Info", true, true, 0, monitor -> { + SymbolFileInfo pdbSymbolFileInfo = SymbolFileInfo.fromFile(file, monitor); + if (pdbSymbolFileInfo == null) { + pdbSymbolFileInfo = SymbolFileInfo.unknown(file.getName()); + } + SymbolFileLocation symbolFileLocation = + SameDirSymbolStore.createManuallySelectedSymbolFileLocation(file, + pdbSymbolFileInfo); + Swing.runLater(() -> { + setSearchResults(List.of(symbolFileLocation)); + setSelectedPdbFile(symbolFileLocation); + setPdbLocationValue(symbolFileLocation, file); + selectRowByLocation(symbolFileLocation); + hasPerformedSearch = false; + updateStatusText(); + updateButtonEnablement(); + updateParserOptionEnablement(true); + }); + }); + + } + } + + private void setPdbLocationValue(SymbolFileLocation symbolFileLocation, File file) { + boolean isExactMatch = symbolFileLocation != null + ? symbolFileLocation.isExactMatch(programSymbolFileInfo) + : false; + pdbLocationTextField.setText(file != null ? file.getPath() : ""); + pdbLocationTextField.setToolTipText(getSymbolFileToolText(symbolFileLocation)); + exactMatchIconLabel + .setIcon(file == null ? null : isExactMatch ? MATCH_OK_ICON : MATCH_BAD_ICON); + exactMatchIconLabel.setToolTipText( + file == null ? null : isExactMatch ? "Exact match" : "Not exact match"); + + } + + private GhidraFileChooser getChooser() { + + if (chooser == null) { + chooser = new GhidraFileChooser(getComponent()); + chooser.addFileFilter(PDB_FILES_FILTER); + chooser.setMultiSelectionEnabled(false); + chooser.setApproveButtonText("Choose"); + chooser.setFileSelectionMode(GhidraFileChooserMode.FILES_ONLY); + chooser.setTitle("Select PDB"); + + String lastFile = Preferences.getProperty(LAST_PDBFILE_PREFERENCE_KEY); + if (lastFile != null) { + chooser.setSelectedFile(new File(lastFile)); + } + } + + return chooser; + } + + /** + * Adds a supplier of status text messages. The supplier will be polled + * whenever the updateStatusText() method is called. + *

    + * Use this status text scheme instead of {@link #setStatusText(String)} if + * there are multiple locations that need to provide a status message at the + * bottom of the dialog. + * + * @param supplier StatusText supplier + */ + private void addStatusTextSupplier(Supplier supplier) { + statusTextSuppliers.remove(supplier); + statusTextSuppliers.add(supplier); + } + + /** + * Polls all {@link #addStatusTextSupplier(Supplier) registered} StatusText suppliers and + * sets the status message at the bottom of the dialog to the resulting message. + *

    + * Not compatible with {@link #setStatusText(String)}. Either use it, or this. + */ + private void updateStatusText() { + StringBuilder sb = new StringBuilder(); + boolean alert = false; + MessageType mt = MessageType.INFO; + for (Supplier supplier : statusTextSuppliers) { + StatusText statusText = supplier.get(); + if (statusText != null && statusText.message != null && !statusText.message.isEmpty()) { + if (sb.length() != 0) { + sb.append("
    "); + } + sb.append(HTMLUtilities.colorString(getStatusColor(statusText.messageType), + statusText.message)); + alert |= statusText.alert; + if (mt.ordinal() < statusText.messageType.ordinal()) { + mt = statusText.messageType; + } + } + } + if (sb.length() != 0) { + setStatusText("" + sb.toString(), mt, alert); + } + else { + clearStatusText(); + } + + } + + private File getLocalSymbolFile(SymbolFileLocation symbolFileLocation) { + if (symbolFileLocation == null) { + return null; + } + SymbolServer symbolServer = symbolFileLocation.getSymbolServer(); + if (!(symbolServer instanceof SymbolStore)) { + return null; + } + SymbolStore symbolStore = (SymbolStore) symbolServer; + File file = symbolStore.getFile(symbolFileLocation.getPath()); + return SymbolStore.isCompressedFilename(file.getName()) ? null : file; + } + + /** + * Execute a non-modal task that has progress and can be cancelled. + *

    + * See {@link #executeProgressTask(Task, int)}. + * + * @param taskTitle String title of task + * @param canCancel boolean flag, if true task can be canceled by the user + * @param hasProgress boolean flag, if true the task has a progress meter + * @param delay int number of milliseconds to delay before showing the task's + * progress + * @param runnable {@link MonitoredRunnable} to run + */ + private void executeMonitoredRunnable(String taskTitle, boolean canCancel, + boolean hasProgress, int delay, MonitoredRunnable runnable) { + Task task = new Task(taskTitle, canCancel, hasProgress, false) { + @Override + public void run(TaskMonitor monitor) throws CancelledException { + runnable.monitoredRun(monitor); + } + }; + executeProgressTask(task, delay); + } + + //----------------------------------------------------------------------------------- + + static class StatusText { + + public StatusText(String message, MessageType messageType, boolean alert) { + this.message = message; + this.messageType = messageType; + this.alert = alert; + } + + public String message; + public MessageType messageType; + public boolean alert; + } + + static JPanel join(JComponent left, JComponent main, JComponent right) { + JPanel panel = new JPanel(new BorderLayout()); + if (left != null) { + panel.add(left, BorderLayout.WEST); + } + panel.add(main, BorderLayout.CENTER); + if (right != null) { + panel.add(right, BorderLayout.EAST); + } + + return panel; + } + + /** + * A customized JTextField that changes the background of non-editable + * text fields to be the same color as the parent container's background. + */ + static class BetterNonEditableTextField extends JTextField { + + BetterNonEditableTextField(int columns) { + super(columns); + } + + @Override + public Color getBackground() { + Container parent = getParent(); + if (parent != null && isEditable() == false) { + Color bg = parent.getBackground(); + // mint a new Color object to avoid it being + // ignored because the parent handed us a DerivedColor + // instance + return new Color(bg.getRGB()); + } + return super.getBackground(); + } + } + +} diff --git a/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/SymbolFilePanel.java b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/SymbolFilePanel.java new file mode 100644 index 0000000000..b4848cfee5 --- /dev/null +++ b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/SymbolFilePanel.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 pdb.symbolserver.ui; + +import java.util.EnumSet; +import java.util.Set; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.event.ActionListener; + +import javax.swing.*; +import javax.swing.table.TableColumn; + +import docking.DockingWindowManager; +import docking.widgets.checkbox.GCheckBox; +import docking.widgets.label.GHtmlLabel; +import docking.widgets.label.GLabel; +import ghidra.util.HelpLocation; +import ghidra.util.table.GhidraTable; +import pdb.PdbPlugin; +import pdb.symbolserver.FindOption; + +/** + * Displays the results of a 'find' operation in a table. + * Also allows the user to tweak search options. + */ +class SymbolFilePanel extends JPanel { + static final String SEARCH_OPTIONS_HELP_ANCHOR = "PDB_Search_Search_Options"; + private SymbolFileTableModel tableModel; + private GhidraTable table; + + private JPanel tablePanel; + private JPanel welcomePanel; + + private JButton searchButton; + private GCheckBox allowRemote; + private GCheckBox ignorePdbUid; + private GCheckBox ignorePdbAge; + + SymbolFilePanel(ActionListener searchButtonActionListener) { + super(new BorderLayout()); + + build(); + setEnablement(false); + searchButton.addActionListener(searchButtonActionListener); + } + + SymbolFileTableModel getTableModel() { + return tableModel; + } + + GhidraTable getTable() { + return table; + } + + Set getFindOptions() { + Set findOptions = EnumSet.noneOf(FindOption.class); + if (allowRemote.isSelected()) { + findOptions.add(FindOption.ALLOW_REMOTE); + } + if (ignorePdbAge.isSelected()) { + findOptions.add(FindOption.ANY_AGE); + } + if (ignorePdbUid.isSelected()) { + findOptions.add(FindOption.ANY_ID); + } + return findOptions; + } + + void setFindOptions(Set findOptions) { + allowRemote.setSelected(findOptions.contains(FindOption.ALLOW_REMOTE)); + ignorePdbAge.setSelected(findOptions.contains(FindOption.ANY_AGE)); + ignorePdbUid.setSelected(findOptions.contains(FindOption.ANY_ID)); + } + + void setEnablement(boolean hasSymbolServerService) { + searchButton.setEnabled(hasSymbolServerService); + + if (welcomePanel != null && hasSymbolServerService) { + remove(welcomePanel); + welcomePanel = null; + add(tablePanel, BorderLayout.CENTER); + revalidate(); + } + } + + SymbolFileRow getSelectedRow() { + return table.getSelectedRow() != -1 + ? tableModel.getRowObject(table.getSelectedRow()) + : null; + } + + int getSelectedRowIndex() { + return table.getSelectedRow(); + } + + private void build() { + setBorder(BorderFactory.createTitledBorder("PDB Search")); + add(buildButtonPanel(), BorderLayout.NORTH); + buildTable(); // don't add it yet + add(buildWelcomePanel(), BorderLayout.CENTER); + } + + private JPanel buildWelcomePanel() { + welcomePanel = new JPanel(); + welcomePanel.add(new GHtmlLabel( + "

    Local Symbol Storage location must be set first!")); + welcomePanel.setPreferredSize(tablePanel.getPreferredSize()); + + return welcomePanel; + } + + private JPanel buildTable() { + this.tableModel = new SymbolFileTableModel(); + this.table = new GhidraTable(tableModel); + + table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + + TableColumn isMatchColumn = table.getColumnModel().getColumn(0); + isMatchColumn.setResizable(false); + isMatchColumn.setPreferredWidth(32); + isMatchColumn.setMaxWidth(32); + isMatchColumn.setMinWidth(32); + + // a few extra rows than needed since the table component + // will be resized according to the number of warning text + // lines at the bottom of the dialog + table.setVisibleRowCount(8); + table.setPreferredScrollableViewportSize(new Dimension(100, 100)); + + tablePanel = new JPanel(new BorderLayout()); + tablePanel.add(new JScrollPane(table), BorderLayout.CENTER); + + return tablePanel; + } + + private JPanel buildButtonPanel() { + searchButton = new JButton("Search"); + + allowRemote = new GCheckBox("Allow Remote"); + allowRemote.setToolTipText("Allow searching remote symbol servers."); + + ignorePdbUid = new GCheckBox("Ignore GUID/ID"); + ignorePdbUid.setToolTipText("Find any PDB with same name (local locations only)."); + + ignorePdbAge = new GCheckBox("Ignore Age"); + ignorePdbAge.setToolTipText("Find PDB with any age value (local locations only)."); + + JPanel panel = new JPanel(); + panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS)); + + panel.add(new GLabel("Search Options:")); + panel.add(Box.createHorizontalStrut(10)); + panel.add(ignorePdbAge); + panel.add(Box.createHorizontalStrut(10)); + panel.add(ignorePdbUid); + panel.add(Box.createHorizontalStrut(10)); + panel.add(allowRemote); + panel.add(Box.createHorizontalGlue()); + panel.add(searchButton); + + DockingWindowManager.getHelpService() + .registerHelp(panel, + new HelpLocation(PdbPlugin.PDB_PLUGIN_HELP_TOPIC, SEARCH_OPTIONS_HELP_ANCHOR)); + + return panel; + } +} diff --git a/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/SymbolFileRow.java b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/SymbolFileRow.java new file mode 100644 index 0000000000..94e0418727 --- /dev/null +++ b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/SymbolFileRow.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. + */ +package pdb.symbolserver.ui; + +import pdb.symbolserver.*; + +/** + * A row in the {@link SymbolFilePanel} find results table + */ +class SymbolFileRow { + private SymbolFileLocation symbolFileLocation; + private boolean isExactMatch; + + SymbolFileRow(SymbolFileLocation symbolFileLocation, boolean isExactMatch) { + this.symbolFileLocation = symbolFileLocation; + this.isExactMatch = isExactMatch; + } + + SymbolFileInfo getSymbolFileInfo() { + return symbolFileLocation.getFileInfo(); + } + + SymbolFileLocation getLocation() { + return symbolFileLocation; + } + + boolean isExactMatch() { + return isExactMatch; + } + + boolean isAvailableLocal() { + return symbolFileLocation.getSymbolServer() instanceof SymbolStore; + } + + void update(SymbolFileLocation newLocation, boolean newIsExactMatch) { + this.symbolFileLocation = newLocation; + this.isExactMatch = newIsExactMatch; + } + +} diff --git a/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/SymbolFileTableModel.java b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/SymbolFileTableModel.java new file mode 100644 index 0000000000..e36d080322 --- /dev/null +++ b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/SymbolFileTableModel.java @@ -0,0 +1,289 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package pdb.symbolserver.ui; + +import java.util.ArrayList; +import java.util.List; + +import java.awt.Component; + +import javax.swing.*; + +import docking.widgets.table.*; +import ghidra.docking.settings.Settings; +import ghidra.framework.plugintool.ServiceProvider; +import ghidra.framework.plugintool.ServiceProviderStub; +import ghidra.util.table.column.AbstractGColumnRenderer; +import ghidra.util.table.column.GColumnRenderer; +import pdb.symbolserver.SymbolFileInfo; +import pdb.symbolserver.SymbolFileLocation; + +/** + * Table model for the SymbolFilePanel table. + */ +class SymbolFileTableModel + extends GDynamicColumnTableModel> { + + private List rows = new ArrayList<>(); + + SymbolFileTableModel() { + super(new ServiceProviderStub()); + setDefaultTableSortState(null); + } + + void setRows(List rows) { + this.rows = rows; + fireTableDataChanged(); + } + + void setSearchResults(SymbolFileInfo symbolFileInfo, List results) { + List newRows = new ArrayList<>(); + for (SymbolFileLocation symbolFileLocation : results) { + newRows.add(new SymbolFileRow(symbolFileLocation, + symbolFileLocation.isExactMatch(symbolFileInfo))); + } + rows = newRows; + fireTableDataChanged(); + } + + @Override + public String getName() { + return "Symbol Files"; + } + + @Override + public List getModelData() { + return rows; + } + + @Override + public List getDataSource() { + return rows; + } + + @Override + protected TableColumnDescriptor createTableColumnDescriptor() { + TableColumnDescriptor descriptor = new TableColumnDescriptor<>(); + + descriptor.addVisibleColumn(new PdbExactMatchColumn()); + descriptor.addVisibleColumn(new PdbFileNameColumn()); + descriptor.addHiddenColumn(new PdbFilePathColumn()); + descriptor.addVisibleColumn(new GuidColumn()); + descriptor.addVisibleColumn(new PdbAgeColumn()); + descriptor.addHiddenColumn(new PdbVersionColumn()); + descriptor.addVisibleColumn(new PdbFileStatusColumn()); + descriptor.addVisibleColumn(new PdbFileLocationColumn()); + + return descriptor; + } + + private class PdbExactMatchColumn + extends AbstractDynamicTableColumnStub { + + BooleanIconColumnRenderer renderer = + new BooleanIconColumnRenderer(LoadPdbDialog.MATCH_OK_ICON, + LoadPdbDialog.MATCH_BAD_ICON, null, "Exact Match", "Not Exact Match", null); + + @Override + public Boolean getValue(SymbolFileRow rowObject, Settings settings, + ServiceProvider serviceProvider) throws IllegalArgumentException { + return rowObject.isExactMatch(); + } + + @Override + public GColumnRenderer getColumnRenderer() { + return renderer; + } + + @Override + public String getColumnName() { + return "Exact Match"; + } + + @Override + public String getColumnDisplayName(Settings settings) { + return ""; + } + + } + + private class PdbFileNameColumn extends AbstractDynamicTableColumnStub { + + @Override + public String getValue(SymbolFileRow rowObject, Settings settings, + ServiceProvider serviceProvider) throws IllegalArgumentException { + return rowObject.getSymbolFileInfo().getName(); + } + + @Override + public String getColumnName() { + return "PDB Filename"; + } + + @Override + public int getColumnPreferredWidth() { + return 200; + } + + } + + private class PdbFilePathColumn extends AbstractDynamicTableColumnStub { + + @Override + public String getValue(SymbolFileRow rowObject, Settings settings, + ServiceProvider serviceProvider) throws IllegalArgumentException { + return rowObject.getSymbolFileInfo().getPath(); + } + + @Override + public String getColumnName() { + return "PDB Filepath"; + } + + } + + private class GuidColumn extends AbstractDynamicTableColumnStub { + + @Override + public String getValue(SymbolFileRow rowObject, Settings settings, + ServiceProvider serviceProvider) throws IllegalArgumentException { + return rowObject.getSymbolFileInfo().getUniqueName(); + } + + @Override + public String getColumnName() { + return "GUID / Signature"; + } + + @Override + public int getColumnPreferredWidth() { + return 300; + } + + } + + private class PdbVersionColumn extends AbstractDynamicTableColumnStub { + + @Override + public String getValue(SymbolFileRow rowObject, Settings settings, + ServiceProvider serviceProvider) throws IllegalArgumentException { + return Integer.toString(rowObject.getSymbolFileInfo().getIdentifiers().getVersion()); + } + + @Override + public String getColumnName() { + return "PDB Version"; + } + + } + + private class PdbAgeColumn extends AbstractDynamicTableColumnStub { + + @Override + public Integer getValue(SymbolFileRow rowObject, Settings settings, + ServiceProvider serviceProvider) throws IllegalArgumentException { + return rowObject.getSymbolFileInfo().getIdentifiers().getAge(); + } + + @Override + public String getColumnName() { + return "PDB Age"; + } + + @Override + public int getColumnPreferredWidth() { + return 120; + } + + } + + private class PdbFileStatusColumn + extends AbstractDynamicTableColumnStub { + + @Override + public String getValue(SymbolFileRow row, Settings settings, + ServiceProvider serviceProvider) throws IllegalArgumentException { + return row.isAvailableLocal() ? "Local" : "Remote"; + } + + @Override + public String getColumnName() { + return "PDB File Status"; + } + + @Override + public int getColumnPreferredWidth() { + return 120; + } + + } + + private class PdbFileLocationColumn + extends AbstractDynamicTableColumnStub { + + @Override + public String getValue(SymbolFileRow row, Settings settings, + ServiceProvider serviceProvider) throws IllegalArgumentException { + return row.getLocation().getLocationStr(); + } + + @Override + public String getColumnName() { + return "File Location"; + } + + } + + /** + * Table column renderer to render a boolean value as an icon + */ + private static class BooleanIconColumnRenderer extends AbstractGColumnRenderer { + + private Icon[] icons; + private String[] toolTipStrings; + + BooleanIconColumnRenderer(Icon trueIcon, Icon falseIcon, Icon missingIcon, + String trueTooltip, String falseTooltip, String missingTooltip) { + this.icons = new Icon[] { missingIcon, falseIcon, trueIcon }; + this.toolTipStrings = new String[] { missingTooltip, falseTooltip, trueTooltip }; + } + + private int getValueOrdinal(GTableCellRenderingData data) { + Boolean booleanValue = (Boolean) data.getValue(); + + return booleanValue == null ? 0 : booleanValue.booleanValue() ? 2 : 1; + } + + @Override + public Component getTableCellRendererComponent(GTableCellRenderingData data) { + + JLabel renderer = (JLabel) super.getTableCellRendererComponent(data); + + int ordinal = getValueOrdinal(data); + renderer.setHorizontalAlignment(SwingConstants.CENTER); + renderer.setText(""); + renderer.setIcon(icons[ordinal]); + renderer.setToolTipText(toolTipStrings[ordinal]); + return renderer; + } + + @Override + public String getFilterString(Boolean booleanValue, Settings settings) { + return booleanValue == null ? "" : booleanValue.toString(); + } + + } +} diff --git a/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/SymbolServerPanel.java b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/SymbolServerPanel.java new file mode 100644 index 0000000000..d811efae8b --- /dev/null +++ b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/SymbolServerPanel.java @@ -0,0 +1,594 @@ +/* ### + * 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.symbolserver.ui; + +import java.util.*; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.io.File; +import java.io.IOException; +import java.net.URI; + +import javax.swing.*; +import javax.swing.table.TableColumn; + +import docking.DockingWindowManager; +import docking.options.editor.ButtonPanelFactory; +import docking.widgets.OptionDialog; +import docking.widgets.filechooser.GhidraFileChooser; +import docking.widgets.filechooser.GhidraFileChooserMode; +import docking.widgets.label.GHtmlLabel; +import docking.widgets.label.GLabel; +import docking.widgets.table.GTable; +import docking.widgets.textfield.HintTextField; +import ghidra.framework.preferences.Preferences; +import ghidra.util.*; +import ghidra.util.layout.PairLayout; +import pdb.PdbPlugin; +import pdb.symbolserver.*; +import pdb.symbolserver.ui.LoadPdbDialog.StatusText; +import resources.Icons; +import utilities.util.FileUtilities; + +/** + * Panel that allows the user to configure a SymbolServerService: a local + * symbol storage directory and a list of search locations. + */ +class SymbolServerPanel extends JPanel { + private static final String MS_SYMBOLSERVER_ENVVAR = "_NT_SYMBOL_PATH"; + + private List knownSymbolServers = + WellKnownSymbolServerLocation.loadAll(); + + private SymbolStore localSymbolStore; + private SymbolServerInstanceCreatorContext symbolServerInstanceCreatorContext; + + private SymbolServerTableModel tableModel; + private GTable table; + private JPanel additionalSearchLocationsPanel; + private JPanel defaultConfigNotice; + private GhidraFileChooser chooser; + private Consumer changeCallback; + + private JButton refreshSearchLocationsStatusButton; + private JButton moveLocationUpButton; + private JButton moveLocationDownButton; + private JButton deleteLocationButton; + private JButton addLocationButton; + private JPanel symbolStorageLocationPanel; + private HintTextField symbolStorageLocationTextField; + private JButton chooseSymbolStorageLocationButton; + private JButton saveSearchLocationsButton; + private boolean configChanged; + + SymbolServerPanel(Consumer changeCallback, + SymbolServerInstanceCreatorContext symbolServerInstanceCreatorContext) { + this.symbolServerInstanceCreatorContext = symbolServerInstanceCreatorContext; + + build(); + + DockingWindowManager.getHelpService() + .registerHelp(this, + new HelpLocation(PdbPlugin.PDB_PLUGIN_HELP_TOPIC, "Symbol Server Config")); + + SymbolServerService temporarySymbolServerService = + PdbPlugin.getSymbolServerService(symbolServerInstanceCreatorContext); + if (temporarySymbolServerService.getSymbolStore() instanceof LocalSymbolStore) { + setSymbolStorageLocation( + ((LocalSymbolStore) temporarySymbolServerService.getSymbolStore()).getRootDir(), + false); + } + tableModel.addSymbolServers(temporarySymbolServerService.getSymbolServers()); + setConfigChanged(false); + + this.changeCallback = changeCallback; + } + + private void build() { + setLayout(new BorderLayout()); + setBorder(BorderFactory.createTitledBorder("Symbol Server Search Config")); + + buildSymbolStorageLocationPanel(); + JPanel buttonPanel = buildButtonPanel(); + JScrollPane tableScrollPane = buildTable(); + defaultConfigNotice = new JPanel(); + defaultConfigNotice.add( + new GHtmlLabel( + "

    " + + "Missing / invalid configuration.

    " + + "Using default search location:
    " + + "Program's Import Location
    ", + SwingConstants.CENTER)); + defaultConfigNotice.setPreferredSize(tableScrollPane.getPreferredSize()); + + additionalSearchLocationsPanel = new JPanel(); + additionalSearchLocationsPanel + .setLayout(new BoxLayout(additionalSearchLocationsPanel, BoxLayout.Y_AXIS)); + additionalSearchLocationsPanel.add(buttonPanel); + additionalSearchLocationsPanel.add(tableScrollPane); + + add(symbolStorageLocationPanel, BorderLayout.NORTH); + add(additionalSearchLocationsPanel, BorderLayout.CENTER); + } + + private void updateLayout(boolean showTable) { + if (showTable == (additionalSearchLocationsPanel.getParent() != null)) { + return; + } + + remove(additionalSearchLocationsPanel); + remove(defaultConfigNotice); + add(showTable ? additionalSearchLocationsPanel : defaultConfigNotice, BorderLayout.CENTER); + invalidate(); + } + + /** + * Returns a new {@link SymbolServerService} instance representing the currently + * displayed configuration, or null if the displayed configuration is not valid. + * + * @return new {@link SymbolServerService} or null + */ + SymbolServerService getSymbolServerService() { + return (localSymbolStore != null) + ? new SymbolServerService(localSymbolStore, tableModel.getSymbolServers()) + : null; + } + + void setSymbolServers(List symbolServers) { + tableModel.setSymbolServers(symbolServers); + } + + /** + * The union of the changed status of the local storage path and the additional + * search paths table model changed status. + * + * @return boolean true if the config has changed + */ + boolean isConfigChanged() { + return configChanged || tableModel.isDataChanged(); + } + + void setConfigChanged(boolean configChanged) { + this.configChanged = configChanged; + tableModel.setDataChanged(configChanged); + } + + private JScrollPane buildTable() { + tableModel = new SymbolServerTableModel(); + table = new GTable(tableModel); + table.setVisibleRowCount(4); + table.setUserSortingEnabled(false); + table.getSelectionManager().addListSelectionListener(e -> { + updateButtonEnablement(); + }); + tableModel.addTableModelListener(e -> { + updateButtonEnablement(); + fireChanged(); + }); + + TableColumn enabledColumn = table.getColumnModel().getColumn(0); + enabledColumn.setResizable(false); + enabledColumn.setPreferredWidth(32); + enabledColumn.setMaxWidth(32); + enabledColumn.setMinWidth(32); + + TableColumn statusColumn = table.getColumnModel().getColumn(1); + statusColumn.setResizable(false); + statusColumn.setPreferredWidth(32); + statusColumn.setMaxWidth(32); + statusColumn.setMinWidth(32); + + table.setPreferredScrollableViewportSize(new Dimension(100, 100)); + + return new JScrollPane(table); + } + + private JPanel buildButtonPanel() { + refreshSearchLocationsStatusButton = + ButtonPanelFactory.createImageButton(Icons.REFRESH_ICON, "Refresh Status", + ButtonPanelFactory.ARROW_SIZE); + refreshSearchLocationsStatusButton.addActionListener(e -> refreshSearchLocationStatus()); + DockingWindowManager.getHelpService() + .registerHelp(refreshSearchLocationsStatusButton, + new HelpLocation(PdbPlugin.PDB_PLUGIN_HELP_TOPIC, + "SymbolServerConfig Refresh Status")); + + moveLocationUpButton = ButtonPanelFactory.createButton(ButtonPanelFactory.ARROW_UP_TYPE); + moveLocationUpButton.addActionListener(e -> moveLocation(-1)); + moveLocationUpButton.setToolTipText("Move location up"); + DockingWindowManager.getHelpService() + .registerHelp(moveLocationUpButton, + new HelpLocation(PdbPlugin.PDB_PLUGIN_HELP_TOPIC, + "SymbolServerConfig MoveUpDown")); + + moveLocationDownButton = + ButtonPanelFactory.createButton(ButtonPanelFactory.ARROW_DOWN_TYPE); + moveLocationDownButton.addActionListener(e -> moveLocation(1)); + moveLocationDownButton.setToolTipText("Move location down"); + DockingWindowManager.getHelpService() + .registerHelp(moveLocationDownButton, + new HelpLocation(PdbPlugin.PDB_PLUGIN_HELP_TOPIC, + "SymbolServerConfig MoveUpDown")); + + deleteLocationButton = ButtonPanelFactory.createImageButton(Icons.DELETE_ICON, "Delete", + ButtonPanelFactory.ARROW_SIZE); + deleteLocationButton.addActionListener(e -> deleteLocation()); + DockingWindowManager.getHelpService() + .registerHelp(deleteLocationButton, + new HelpLocation(PdbPlugin.PDB_PLUGIN_HELP_TOPIC, + "SymbolServerConfig Delete")); + + addLocationButton = ButtonPanelFactory.createImageButton(Icons.ADD_ICON, "Add", + ButtonPanelFactory.ARROW_SIZE); + addLocationButton.addActionListener(e -> addLocation()); + DockingWindowManager.getHelpService() + .registerHelp(addLocationButton, + new HelpLocation(PdbPlugin.PDB_PLUGIN_HELP_TOPIC, + "SymbolServerConfig Add")); + + saveSearchLocationsButton = + ButtonPanelFactory.createImageButton(Icons.get("images/disk.png"), + "Save Configuration", ButtonPanelFactory.ARROW_SIZE); + saveSearchLocationsButton.addActionListener(e -> saveConfig()); + DockingWindowManager.getHelpService() + .registerHelp(saveSearchLocationsButton, + new HelpLocation(PdbPlugin.PDB_PLUGIN_HELP_TOPIC, + "SymbolServerConfig Save")); + + JPanel buttonPanel = new JPanel(); + buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.X_AXIS)); + buttonPanel.add(new GLabel("Additional Search Paths:")); + buttonPanel.add(Box.createHorizontalGlue()); + buttonPanel.add(addLocationButton); + buttonPanel.add(deleteLocationButton); + buttonPanel.add(moveLocationUpButton); + buttonPanel.add(moveLocationDownButton); + buttonPanel.add(refreshSearchLocationsStatusButton); + buttonPanel.add(saveSearchLocationsButton); + + return buttonPanel; + } + + private JPanel buildSymbolStorageLocationPanel() { + symbolStorageLocationTextField = new HintTextField(" Required "); + symbolStorageLocationTextField.setEditable(false); + + chooseSymbolStorageLocationButton = + ButtonPanelFactory.createButton(ButtonPanelFactory.BROWSE_TYPE); + chooseSymbolStorageLocationButton.addActionListener(e -> chooseSymbolStorageLocation()); + + symbolStorageLocationPanel = new JPanel(new PairLayout(5, 5)); + GLabel symbolStorageLocLabel = new GLabel("Local Symbol Storage:", SwingConstants.RIGHT); + symbolStorageLocLabel + .setToolTipText("User-specified directory where PDB files are stored. Required."); + symbolStorageLocationPanel.add(symbolStorageLocLabel); + symbolStorageLocationPanel.add(LoadPdbDialog.join(null, symbolStorageLocationTextField, + chooseSymbolStorageLocationButton)); + return symbolStorageLocationPanel; + } + + private void updateButtonEnablement() { + boolean hasLocalSymbolStore = localSymbolStore != null; + boolean singleRow = table.getSelectedRowCount() == 1; + boolean moreThanOneRow = table.getRowCount() > 1; + + refreshSearchLocationsStatusButton.setEnabled(hasLocalSymbolStore && !tableModel.isEmpty()); + moveLocationUpButton.setEnabled(hasLocalSymbolStore && singleRow && moreThanOneRow); + moveLocationDownButton.setEnabled(hasLocalSymbolStore && singleRow && moreThanOneRow); + addLocationButton.setEnabled(hasLocalSymbolStore); + deleteLocationButton.setEnabled(hasLocalSymbolStore && table.getSelectedRowCount() > 0); + saveSearchLocationsButton.setEnabled(hasLocalSymbolStore && isConfigChanged()); + updateLayout(hasLocalSymbolStore); + } + + StatusText getSymbolServerWarnings() { + Map warningsByLocation = new HashMap<>(); + for (WellKnownSymbolServerLocation ssloc : knownSymbolServers) { + if (ssloc.getWarning() != null && !ssloc.getWarning().isBlank()) { + warningsByLocation.put(ssloc.getLocation(), ssloc.getWarning()); + } + } + String warning = tableModel.getDataSource() + .stream() + .map(row -> warningsByLocation.get(row.getSymbolServer().getName())) + .filter(Objects::nonNull) + .distinct() + .collect(Collectors.joining("
    \n")); + + return !warning.isEmpty() ? new StatusText(warning, MessageType.WARNING, false) : null; + } + + private void setSymbolStorageLocation(File symbolStorageDir, boolean allowGUIPrompt) { + if (symbolStorageDir == null) { + return; + } + if (!symbolStorageDir.exists()) { + if (!allowGUIPrompt) { + return; + } + + int opt = OptionDialog.showOptionDialog(this, "Create Local Symbol Storage Directory?", + "Symbol storage directory
    " + + HTMLUtilities.escapeHTML(symbolStorageDir.getPath()) + + "
    does not exist. Create?", + "Yes", OptionDialog.QUESTION_MESSAGE); + if (opt == OptionDialog.CANCEL_OPTION) { + return; + } + try { + FileUtilities.checkedMkdirs(symbolStorageDir); + } + catch (IOException e) { + Msg.showError(this, this, "Failure", "Failed to create symbol storage directory " + + symbolStorageDir + ": " + e.getMessage()); + return; + } + } + + if (allowGUIPrompt && isEmptyDirectory(symbolStorageDir)) { + if (OptionDialog.showYesNoDialog(this, + "Initialize Symbol Storage Directory?", + "Initialize new directory as Microsoft symbol storage directory?") == OptionDialog.YES_OPTION) { + try { + LocalSymbolStore.create(symbolStorageDir, + 1 /* level1 MS symbol storage directory */); + } + catch (IOException e) { + Msg.showError(this, this, "Initialize Failure", + "Failed to initialize symbol storage directory " + symbolStorageDir, e); + } + } + } + + localSymbolStore = + symbolServerInstanceCreatorContext.getSymbolServerInstanceCreatorRegistry() + .newSymbolServer(symbolStorageDir.getPath(), symbolServerInstanceCreatorContext, + SymbolStore.class); + symbolStorageLocationTextField.setText(symbolStorageDir.getPath()); + fireChanged(); + } + + private void fireChanged() { + if (changeCallback != null) { + changeCallback.accept(getSymbolServerService()); + } + } + + private void chooseSymbolStorageLocation() { + configChanged = true; + setSymbolStorageLocation(getChooser().getSelectedFile(), true); + updateButtonEnablement(); + } + + private void importLocations() { + String envVar = (String) JOptionPane.showInputDialog(this, + "Enter value:

    Example: SVR*c:\\symbols*https://msdl.microsoft.com/download/symbols/

    ", + "Enter Symbol Server Search Path Value", JOptionPane.QUESTION_MESSAGE, null, null, + Objects.requireNonNullElse(System.getenv(MS_SYMBOLSERVER_ENVVAR), "")); + if (envVar == null) { + return; + } + + List symbolServerPaths = getSymbolPathsFromEnvStr(envVar); + if (!symbolServerPaths.isEmpty()) { + // if the first item in the path list looks like a local symbol storage path, + // allow the user to set it as the storage dir (and remove it from the elements + // that will be added to the search list) + String firstSearchPath = symbolServerPaths.get(0); + SymbolServer symbolServer = + symbolServerInstanceCreatorContext.getSymbolServerInstanceCreatorRegistry() + .newSymbolServer(firstSearchPath, symbolServerInstanceCreatorContext); + if (symbolServer instanceof LocalSymbolStore && + ((LocalSymbolStore) symbolServer).isValid()) { + int choice = OptionDialog.showYesNoCancelDialog(this, "Set Symbol Storage Location", + "Set symbol storage location to " + firstSearchPath + "?"); + if (choice == OptionDialog.CANCEL_OPTION) { + return; + } + if (choice == OptionDialog.YES_OPTION) { + symbolServerPaths.remove(0); + configChanged = true; + setSymbolStorageLocation(((LocalSymbolStore) symbolServer).getRootDir(), true); + symbolStorageLocationTextField.setText(symbolServer.getName()); + } + } + } + + tableModel.addSymbolServers( + symbolServerInstanceCreatorContext.getSymbolServerInstanceCreatorRegistry() + .createSymbolServersFromPathList(symbolServerPaths, + symbolServerInstanceCreatorContext)); + fireChanged(); + } + + private List getSymbolPathsFromEnvStr(String envString) { + // Expect the environment string to be in the MS symbol server search path form: + // srv*[local cache]*[private symbol server]*https://msdl.microsoft.com/download/symbols + // srv*c:\symbols*https://msdl.microsoft.com/download/symbols;srv*c:\additional*https://symbol.server.tld/ + String[] envParts = envString.split("[*;]"); + List results = new ArrayList<>(); + Set locationStringDeduplicationSet = new HashSet<>(); + for (int i = 0; i < envParts.length; i++) { + String locationString = envParts[i].trim(); + if (!locationString.isBlank() && !locationString.equalsIgnoreCase("srv") && + !locationStringDeduplicationSet.contains(locationString)) { + results.add(locationString); + locationStringDeduplicationSet.add(locationString); + } + } + + return results; + } + + private void addLocation() { + JPopupMenu menu = createAddLocationPopupMenu(); + menu.show(addLocationButton, 0, 0); + } + + private JPopupMenu createAddLocationPopupMenu() { + JPopupMenu menu = new JPopupMenu(); + JMenuItem addDirMenuItem = new JMenuItem("Directory"); + addDirMenuItem.addActionListener(e -> addDirectoryLocation()); + menu.add(addDirMenuItem); + + JMenuItem addURLMenuItem = new JMenuItem("URL"); + addURLMenuItem.addActionListener(e -> addUrlLocation()); + menu.add(addURLMenuItem); + + JMenuItem addProgLocMenuItem = + new JMenuItem(SameDirSymbolStore.PROGRAMS_IMPORT_LOCATION_DESCRIPTION_STR); + addProgLocMenuItem.addActionListener(e -> addSameDirLocation()); + menu.add(addProgLocMenuItem); + + JMenuItem importEnvMenuItem = new JMenuItem("Import _NT_SYMBOL_PATH"); + importEnvMenuItem.addActionListener(e -> importLocations()); + menu.add(importEnvMenuItem); + + if (!knownSymbolServers.isEmpty()) { + menu.add(new JSeparator()); + for (WellKnownSymbolServerLocation ssloc : knownSymbolServers) { + JMenuItem mi = new JMenuItem(ssloc.getLocation()); + mi.addActionListener(e -> addKnownLocation(ssloc)); + mi.setToolTipText(" [from " + ssloc.getFileOrigin() + "]"); + menu.add(mi); + } + } + DockingWindowManager.getHelpService() + .registerHelp(menu, + new HelpLocation(PdbPlugin.PDB_PLUGIN_HELP_TOPIC, "SymbolServerConfig_Add")); + return menu; + } + + private void addSameDirLocation() { + SameDirSymbolStore sameDirSymbolStore = + new SameDirSymbolStore(symbolServerInstanceCreatorContext.getRootDir()); + tableModel.addSymbolServer(sameDirSymbolStore); + } + + private void addKnownLocation(WellKnownSymbolServerLocation ssloc) { + SymbolServer symbolServer = + symbolServerInstanceCreatorContext.getSymbolServerInstanceCreatorRegistry() + .newSymbolServer(ssloc.getLocation(), symbolServerInstanceCreatorContext); + if (symbolServer != null) { + tableModel.addSymbolServer(symbolServer); + } + } + + private void addUrlLocation() { + String urlLocationString = OptionDialog.showInputSingleLineDialog(this, "Enter URL", + "Enter the URL of a Symbol Server: ", "https://"); + if (urlLocationString == null || urlLocationString.isBlank()) { + return; + } + urlLocationString = urlLocationString.toLowerCase(); + if (!(urlLocationString.startsWith("http://") || + urlLocationString.startsWith("https://"))) { + Msg.showWarn(this, this, "Bad URL", "Invalid URL: " + urlLocationString); + return; + } + try { + HttpSymbolServer httpSymbolServer = new HttpSymbolServer(URI.create(urlLocationString)); + tableModel.addSymbolServer(httpSymbolServer); + } + catch (IllegalArgumentException e) { + Msg.showWarn(this, this, "Bad URL", "Invalid URL: " + urlLocationString); + } + } + + private void addDirectoryLocation() { + File dir = FilePromptDialog.chooseDirectory("Enter Path", "Symbol Storage Location: ", + null); + if (dir == null) { + return; + } + if (!dir.exists() || !dir.isDirectory()) { + Msg.showError(this, this, "Bad path", "Invalid path: " + dir); + return; + } + LocalSymbolStore localSymbolStore = new LocalSymbolStore(dir); + tableModel.addSymbolServer(localSymbolStore); + } + + private void deleteLocation() { + int selectedRow = table.getSelectedRow(); + tableModel.deleteRows(table.getSelectedRows()); + if (selectedRow >= 0 && selectedRow < table.getRowCount()) { + table.selectRow(selectedRow); + } + } + + private void moveLocation(int delta) { + if (table.getSelectedRowCount() == 1) { + tableModel.moveRow(table.getSelectedRow(), delta); + } + } + + private void refreshSearchLocationStatus() { + tableModel.refreshSymbolServerLocationStatus(); + updateButtonEnablement(); + } + + /* package */ void saveConfig() { + SymbolServerService temporarySymbolServerService = getSymbolServerService(); + if (temporarySymbolServerService != null) { + PdbPlugin.saveSymbolServerServiceConfig(temporarySymbolServerService); + Preferences.store(); + setConfigChanged(false); + fireChanged(); + updateButtonEnablement(); + } + } + + private GhidraFileChooser getChooser() { + + if (chooser == null) { + chooser = new GhidraFileChooser(this); + chooser.setMultiSelectionEnabled(false); + chooser.setApproveButtonText("Choose"); + chooser.setFileSelectionMode(GhidraFileChooserMode.DIRECTORIES_ONLY); + chooser.setTitle("Select Symbol Storage Dir"); + } + + return chooser; + } + + /* screen shot usage */ void pushAddLocationButton() { + addLocation(); + } + + /* screen shot usage */ void setSymbolStorageDirectoryTextOnly(String pathStr) { + symbolStorageLocationTextField.setText(pathStr); + } + + /** + * Returns true if the given file path is a directory that contains no files. + *

    + * + * @param directory path to a location on the file system + * @return true if is a directory and it contains no files + */ + private static boolean isEmptyDirectory(File directory) { + if (directory.isDirectory()) { + File[] dirContents = directory.listFiles(); + return dirContents != null && dirContents.length == 0; + } + return false; + } + +} diff --git a/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/SymbolServerRow.java b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/SymbolServerRow.java new file mode 100644 index 0000000000..58de169690 --- /dev/null +++ b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/SymbolServerRow.java @@ -0,0 +1,76 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package pdb.symbolserver.ui; + +import pdb.symbolserver.DisabledSymbolServer; +import pdb.symbolserver.SymbolServer; + +/** + * Represents a row in the {@link SymbolServerTableModel} + */ +class SymbolServerRow { + + public enum LocationStatus { + UNKNOWN, VALID, INVALID + } + + private SymbolServer symbolServer; + private LocationStatus status = LocationStatus.UNKNOWN; + + SymbolServerRow(SymbolServer symbolServer) { + this.symbolServer = symbolServer; + } + + SymbolServer getSymbolServer() { + return symbolServer; + } + + void setSymbolServer(SymbolServer symbolServer) { + this.symbolServer = symbolServer; + } + + boolean isEnabled() { + return !(symbolServer instanceof DisabledSymbolServer); + } + + void setEnabled(boolean enabled) { + if (isEnabled() == enabled) { + return; + } + if (enabled) { + DisabledSymbolServer dss = (DisabledSymbolServer) symbolServer; + symbolServer = dss.getSymbolServer(); + } + else { + symbolServer = new DisabledSymbolServer(symbolServer); + } + } + + LocationStatus getStatus() { + return status; + } + + void setStatus(LocationStatus status) { + this.status = status; + } + + @Override + public String toString() { + return String.format("SymbolServerRow: [ status: %s, server: %s]", status.toString(), + symbolServer.toString()); + } + +} diff --git a/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/SymbolServerTableModel.java b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/SymbolServerTableModel.java new file mode 100644 index 0000000000..bc33ebb483 --- /dev/null +++ b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/SymbolServerTableModel.java @@ -0,0 +1,309 @@ +/* ### + * 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.symbolserver.ui; + +import static java.util.stream.Collectors.toList; +import static pdb.symbolserver.ui.SymbolServerRow.LocationStatus.INVALID; +import static pdb.symbolserver.ui.SymbolServerRow.LocationStatus.VALID; + +import java.util.ArrayList; +import java.util.List; + +import java.awt.Component; + +import javax.swing.*; + +import docking.widgets.table.*; +import ghidra.docking.settings.Settings; +import ghidra.framework.plugintool.ServiceProvider; +import ghidra.framework.plugintool.ServiceProviderStub; +import ghidra.util.Swing; +import ghidra.util.table.column.AbstractGColumnRenderer; +import ghidra.util.table.column.GColumnRenderer; +import ghidra.util.task.TaskLauncher; +import pdb.symbolserver.SymbolServer; +import resources.Icons; + +/** + * Table model for the {@link SymbolServerPanel} table + */ +class SymbolServerTableModel + extends GDynamicColumnTableModel> { + + private List rows = new ArrayList<>(); + private boolean dataChanged; + + SymbolServerTableModel() { + super(new ServiceProviderStub()); + setDefaultTableSortState(null); + } + + boolean isEmpty() { + return rows.isEmpty(); + } + + void setSymbolServers(List symbolServers) { + rows.clear(); + for (SymbolServer symbolServer : symbolServers) { + rows.add(new SymbolServerRow(symbolServer)); + } + fireTableDataChanged(); + } + + List getSymbolServers() { + return rows.stream() + .map(SymbolServerRow::getSymbolServer) + .collect(toList()); + } + + void addSymbolServer(SymbolServer ss) { + SymbolServerRow row = new SymbolServerRow(ss); + rows.add(row); + dataChanged = true; + fireTableDataChanged(); + } + + void addSymbolServers(List symbolServers) { + for (SymbolServer symbolServer : symbolServers) { + rows.add(new SymbolServerRow(symbolServer)); + } + dataChanged = true; + fireTableDataChanged(); + } + + void deleteRows(int[] rowIndexes) { + for (int i = rowIndexes.length - 1; i >= 0; i--) { + rows.remove(rowIndexes[i]); + } + dataChanged = true; + fireTableDataChanged(); + } + + void refreshSymbolServerLocationStatus() { + List rowsCopy = new ArrayList<>(this.rows); + TaskLauncher.launchNonModal("Refresh Symbol Server Location Status", monitor -> { + monitor.initialize(rowsCopy.size()); + monitor.setMessage("Refreshing symbol server status"); + try { + for (SymbolServerRow row : rowsCopy) { + if (monitor.isCancelled()) { + break; + } + monitor.setMessage("Checking " + row.getSymbolServer().getName()); + row.setStatus(row.getSymbolServer().isValid(monitor) ? VALID : INVALID); + } + } + finally { + Swing.runLater(SymbolServerTableModel.this::fireTableDataChanged); + } + }); + } + + void moveRow(int rowIndex, int deltaIndex) { + int destIndex = rowIndex + deltaIndex; + if (rowIndex < 0 || rowIndex >= rows.size() || destIndex < 0 || destIndex >= rows.size()) { + return; + } + + SymbolServerRow symbolServerRow1 = rows.get(rowIndex); + SymbolServerRow symbolServerRow2 = rows.get(destIndex); + rows.set(destIndex, symbolServerRow1); + rows.set(rowIndex, symbolServerRow2); + + dataChanged = true; + + fireTableDataChanged(); + } + + boolean isDataChanged() { + return dataChanged; + } + + void setDataChanged(boolean b) { + this.dataChanged = b; + } + + @Override + public String getName() { + return "Symbol Server Locations"; + } + + @Override + public List getModelData() { + return rows; + } + + @Override + public List getDataSource() { + return rows; + } + + @Override + public boolean isSortable(int columnIndex) { + return false; + } + + @Override + public void setValueAt(Object aValue, int rowIndex, int columnIndex) { + DynamicTableColumn column = getColumn(columnIndex); + if (column instanceof EnabledColumn) { + SymbolServerRow row = getRowObject(rowIndex); + row.setEnabled((Boolean) aValue); + dataChanged = true; + fireTableDataChanged(); + } + } + + @Override + public boolean isCellEditable(int rowIndex, int columnIndex) { + DynamicTableColumn column = getColumn(columnIndex); + return column instanceof EnabledColumn; + } + + @Override + protected TableColumnDescriptor createTableColumnDescriptor() { + TableColumnDescriptor descriptor = new TableColumnDescriptor<>(); + + descriptor.addVisibleColumn(new EnabledColumn()); + descriptor.addVisibleColumn(new StatusColumn()); + descriptor.addVisibleColumn(new LocationColumn()); + + return descriptor; + } + + //------------------------------------------------------------------------------------------- + + private static class StatusColumn extends + AbstractDynamicTableColumnStub { + + private static final Icon VALID_ICON = Icons.get("images/checkmark_green.gif"); + private static final Icon INVALID_ICON = Icons.ERROR_ICON; + + private static Icon[] icons = new Icon[] { null, VALID_ICON, INVALID_ICON }; + private static String[] toolTips = new String[] { null, "Status: Ok", "Status: Failed" }; + + EnumIconColumnRenderer renderer = + new EnumIconColumnRenderer<>(SymbolServerRow.LocationStatus.class, icons, toolTips); + + @Override + public SymbolServerRow.LocationStatus getValue(SymbolServerRow rowObject, Settings settings, + ServiceProvider serviceProvider) throws IllegalArgumentException { + return rowObject.getStatus(); + } + + @Override + public String getColumnDisplayName(Settings settings) { + return ""; + } + + @Override + public String getColumnName() { + return "Status"; + } + + @Override + public GColumnRenderer getColumnRenderer() { + return renderer; + } + + } + + private static class EnabledColumn + extends AbstractDynamicTableColumnStub { + + @Override + public String getColumnDisplayName(Settings settings) { + return ""; + } + + @Override + public Boolean getValue(SymbolServerRow rowObject, Settings settings, + ServiceProvider serviceProvider) throws IllegalArgumentException { + return rowObject.isEnabled(); + } + + @Override + public String getColumnName() { + return "Enabled"; + } + + } + + private static class LocationColumn + extends AbstractDynamicTableColumnStub { + + @Override + public String getValue(SymbolServerRow rowObject, Settings settings, + ServiceProvider serviceProvider) throws IllegalArgumentException { + return rowObject.getSymbolServer().getDescriptiveName(); + } + + @Override + public String getColumnName() { + return "Location"; + } + + @Override + public int getColumnPreferredWidth() { + return 250; + } + + } + + /** + * Table column renderer to render an enum value as a icon + * + * @param enum type + */ + private static class EnumIconColumnRenderer> + extends AbstractGColumnRenderer { + + private Icon[] icons; + private String[] toolTips; + + EnumIconColumnRenderer(Class enumClass, Icon[] icons, String[] toolTips) { + if (enumClass.getEnumConstants().length != icons.length || + icons.length != toolTips.length) { + throw new IllegalArgumentException(); + } + this.icons = icons; + this.toolTips = toolTips; + } + + @Override + public Component getTableCellRendererComponent(GTableCellRenderingData data) { + + JLabel renderer = (JLabel) super.getTableCellRendererComponent(data); + + E e = (E) data.getValue(); + renderer.setHorizontalAlignment(SwingConstants.CENTER); + renderer.setText(""); + renderer.setIcon(e != null ? icons[e.ordinal()] : null); + renderer.setToolTipText(e != null ? toolTips[e.ordinal()] : null); + return renderer; + } + + @Override + protected String getText(Object value) { + return ""; + } + + @Override + public String getFilterString(E t, Settings settings) { + return t == null ? "" : t.toString(); + } + } +} diff --git a/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/WellKnownSymbolServerLocation.java b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/WellKnownSymbolServerLocation.java new file mode 100644 index 0000000000..c452fd3683 --- /dev/null +++ b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/WellKnownSymbolServerLocation.java @@ -0,0 +1,112 @@ +/* ### + * 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.symbolserver.ui; + +import java.io.IOException; +import java.util.*; + +import generic.jar.ResourceFile; +import ghidra.framework.Application; +import ghidra.util.Msg; +import utilities.util.FileUtilities; + +/** + * Represents a well-known symbol server location. + *

    + * See the PDB_SYMBOL_SERVER_URLS.pdburl file. + */ +class WellKnownSymbolServerLocation { + private String locationCategory; + private String location; + private String warning; + private String fileOrigin; + + WellKnownSymbolServerLocation(String location, String locationCategory, String warning, + String fileOrigin) { + this.location = location; + this.locationCategory = locationCategory; + this.warning = warning; + this.fileOrigin = fileOrigin; + } + + String getLocationCategory() { + return locationCategory; + } + + String getLocation() { + return location; + } + + String getWarning() { + return warning; + } + + String getFileOrigin() { + return fileOrigin; + } + + @Override + public int hashCode() { + return Objects.hash(location, locationCategory, warning); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + WellKnownSymbolServerLocation other = (WellKnownSymbolServerLocation) obj; + return Objects.equals(location, other.location) && + Objects.equals(locationCategory, other.locationCategory) && + Objects.equals(warning, other.warning); + } + + /** + * Loads all symbol server location files (*.pdburl) and returns a list of entries. + * + * @return list of {@link WellKnownSymbolServerLocation} elements + */ + public static List loadAll() { + List pdbUrlFiles = Application.findFilesByExtensionInApplication(".pdburl"); + + List results = new ArrayList<>(); + for (ResourceFile file : pdbUrlFiles) { + try { + List lines = FileUtilities.getLines(file); + for (String line : lines) { + // format: location_category|location_string|warning_string + // example: "Internet|https://msdl.microsoft.com/download/symbols|Warning: be careful!" + String[] fields = line.split("\\|"); + if (fields.length > 1) { + results.add(new WellKnownSymbolServerLocation(fields[1], fields[0], + fields.length > 2 ? fields[2] : null, file.getName())); + } + } + } + catch (IOException e) { + Msg.warn(WellKnownSymbolServerLocation.class, "Unable to read pdburl file: " + file); + } + } + return results; + } + +} diff --git a/Ghidra/Features/PDB/src/test/java/ghidra/app/util/bin/format/pdb/PdbParserTest.java b/Ghidra/Features/PDB/src/test/java/ghidra/app/util/bin/format/pdb/PdbParserTest.java index 9e82411e99..b187a20369 100644 --- a/Ghidra/Features/PDB/src/test/java/ghidra/app/util/bin/format/pdb/PdbParserTest.java +++ b/Ghidra/Features/PDB/src/test/java/ghidra/app/util/bin/format/pdb/PdbParserTest.java @@ -15,17 +15,16 @@ */ package ghidra.app.util.bin.format.pdb; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import java.io.*; -import java.util.ArrayList; -import java.util.List; -import org.junit.*; +import org.junit.Before; +import org.junit.Test; import ghidra.app.plugin.core.analysis.AutoAnalysisManager; import ghidra.app.services.DataTypeManagerService; -import ghidra.app.util.bin.format.pdb.PdbParser.PdbFileType; import ghidra.app.util.importer.MessageLog; import ghidra.framework.options.Options; import ghidra.program.database.ProgramBuilder; @@ -35,79 +34,34 @@ import ghidra.program.model.listing.FunctionManager; import ghidra.program.model.listing.Program; import ghidra.test.AbstractGhidraHeadlessIntegrationTest; import ghidra.util.task.TaskMonitor; -import utilities.util.FileUtilities; public class PdbParserTest extends AbstractGhidraHeadlessIntegrationTest { private ProgramBuilder builder; - private static String notepadGUID = "36cfd5f9-888c-4483-b522-b9db242d8478"; + private static final String notepadGUID = "36cfd5f9-888c-4483-b522-b9db242d8478"; + private static final String programBasename = "notepad"; // Note: this is in hex. Code should translate it to decimal when creating GUID/Age folder name - private static String notepadAge = "21"; - - // Name of subfolder that stores the actual PDB file - private static String guidAgeCombo = "36CFD5F9888C4483B522B9DB242D847833"; - private String programBasename = "notepad"; + private static final String notepadAge = "21"; private File tempDir, fileLocation; private Program testProgram; - //private static String guidAgeCombo = PdbParserNEW.getGuidAgeString(notepadGUID, notepadAge); - - // Bogus symbol repository directory or null directory should not break anything - private static final File noSuchSymbolsRepoDir = null; - private String pdbFilename, pdbXmlFilename; - private String exeFolderName = "exe", pdbXmlFolderName = "pdb_xml", - symbolsFolderName = "symbols"; - - private File pdbFile = null, pdbXmlFile = null, pdbXmlDir = null, symbolsFolder = null; - - private List createdFiles; - - enum PdbLocation { - NONE, SYMBOLS_SUBDIR, SYMBOLS_NO_SUBDIR, SAME_AS_EXE_SUBDIR, SAME_AS_EXE_NO_SUBDIR - } - - enum PdbXmlLocation { - NONE, SAME_AS_PDB, OWN_DIR - } TestFunction[] programFunctions = new TestFunction[] { new TestFunction("function1", "0x110", "0x35") }; - public PdbParserTest() { - super(); - } - @Before public void setUp() throws Exception { // Get temp directory in which to store files - String tempDirPath = getTestDirectoryPath(); - tempDir = new File(tempDirPath); - - fileLocation = new File(tempDir, exeFolderName); - + tempDir = createTempDirectory("pdb_parser"); + fileLocation = new File(tempDir, "exe"); testProgram = buildProgram(fileLocation.getAbsolutePath()); pdbFilename = programBasename + ".pdb"; - pdbXmlFilename = pdbFilename + PdbFileType.XML.toString(); - - createdFiles = null; - } - - @After - public void tearDown() throws Exception { - - if (fileLocation != null) { - FileUtilities.deleteDir(fileLocation); - } - if (createdFiles != null) { - deleteCreatedFiles(createdFiles); - } - - System.gc(); + pdbXmlFilename = programBasename + ".pdb.xml"; } private Program buildProgram(String exeLocation) throws Exception { @@ -138,838 +92,10 @@ public class PdbParserTest extends AbstractGhidraHeadlessIntegrationTest { return currentTestProgram; } - private List createFiles(PdbLocation pdbLoc, PdbXmlLocation pdbXmlLoc) { - pdbFile = null; - pdbXmlFile = null; - - pdbXmlDir = new File(tempDir, pdbXmlFolderName); - - List filesCreated = new ArrayList<>(); - - createDirectory(fileLocation); - filesCreated.add(fileLocation); - - File subDir, subSubDir; - - switch (pdbLoc) { - // Put PDB file in the /symbols/ folder with subdirectories that - // include the PDB name and GUID. I.e., the full path to the PDB is: - // /symbols/notepad.pdb/36CFD5F9888C4483B522B9DB242D84782/notepad.pdb - case SYMBOLS_SUBDIR: - symbolsFolder = new File(tempDir, symbolsFolderName); - createDirectory(symbolsFolder); - filesCreated.add(symbolsFolder); - - subDir = new File(symbolsFolder, pdbFilename); - createDirectory(subDir); - filesCreated.add(subDir); - - subSubDir = new File(subDir, guidAgeCombo); - createDirectory(subSubDir); - filesCreated.add(subSubDir); - - pdbFile = new File(subSubDir, pdbFilename); - break; - - // Put the PDB file directly into the /symbols folder. - // I.e., /symbols/notepad.pdb - case SYMBOLS_NO_SUBDIR: - symbolsFolder = new File(tempDir, symbolsFolderName); - createDirectory(symbolsFolder); - filesCreated.add(symbolsFolder); - - pdbFile = new File(symbolsFolder, pdbFilename); - break; - - // Put the PDB file in the same folder as the binary with subdirectories that - // include the PDB name and GUID. - // I.e., /exe/notepad.pdb/36CFD5F9888C4483B522B9DB242D84782/notepad.pdb - case SAME_AS_EXE_SUBDIR: - subDir = new File(fileLocation, pdbFilename); - createDirectory(subDir); - filesCreated.add(subDir); - - subSubDir = new File(subDir, guidAgeCombo); - createDirectory(subSubDir); - filesCreated.add(subSubDir); - - pdbFile = new File(subSubDir, pdbFilename); - break; - - // Put the PDB file in the same folder as the binary - // I.e., /exe/notepad.pdb - case SAME_AS_EXE_NO_SUBDIR: - pdbFile = new File(fileLocation, pdbFilename); - break; - - case NONE: - // Do nothing - break; - - default: - fail("Unrecognized pdbLocation choice: " + pdbLoc); - } - - if (pdbFile != null) { - createFile(pdbFile); - filesCreated.add(pdbFile); - } - - switch (pdbXmlLoc) { - // Put the PDB XML file in the same location as the PDB file - case SAME_AS_PDB: - if (pdbFile != null) { - pdbXmlFile = new File(pdbFile.getParentFile(), pdbXmlFilename); - } - else { - fail("Does not make sense to create a .pdb.xml file in the same directory as " + - "the .pdb file when there is no .pdb file!"); - } - break; - - // Put the PDB XML file in the /pdb_xml directory - case OWN_DIR: - // Create directory that will server as .pdb.xml location - createDirectory(pdbXmlDir); - filesCreated.add(pdbXmlDir); - - pdbXmlFile = new File(pdbXmlDir, pdbXmlFilename); - break; - - // Do not create a PDB XML file - case NONE: - break; - - default: - fail("Unrecognized pdbXmlLocation choice: " + pdbXmlLoc); - - } - - if (pdbXmlFile != null) { - createFile(pdbXmlFile); - filesCreated.add(pdbXmlFile); - } - - verifyFilesCreated(pdbLoc, pdbXmlLoc); - - return filesCreated; - } - - private void verifyFilesCreated(PdbLocation pdbLoc, PdbXmlLocation pdbXmlLoc) { - - File expectedDir1, expectedDir2; - - switch (pdbLoc) { - case NONE: - assertNull(pdbFile); - break; - - case SAME_AS_EXE_SUBDIR: - assertNotNull(pdbFile); - expectedDir1 = new File(fileLocation, pdbFilename); - expectedDir2 = new File(expectedDir1, guidAgeCombo); - assertEquals(expectedDir2, pdbFile.getParentFile()); - break; - - case SAME_AS_EXE_NO_SUBDIR: - assertNotNull(pdbFile); - assertEquals(fileLocation, pdbFile.getParentFile()); - break; - - case SYMBOLS_SUBDIR: - assertNotNull(pdbFile); - expectedDir1 = new File(symbolsFolder, pdbFilename); - expectedDir2 = new File(expectedDir1, guidAgeCombo); - assertEquals(expectedDir2, pdbFile.getParentFile()); - break; - - case SYMBOLS_NO_SUBDIR: - assertNotNull(pdbFile); - assertEquals(symbolsFolder, pdbFile.getParentFile()); - break; - - default: - fail("Unrecognized pdbLocation choice: " + pdbLoc); - } - - switch (pdbXmlLoc) { - case SAME_AS_PDB: - assertNotNull(pdbXmlFile); - assertEquals(pdbXmlFile.getParentFile(), pdbFile.getParentFile()); - break; - - case OWN_DIR: - assertNotNull(pdbXmlFile); - assertEquals(pdbXmlFile.getParentFile(), pdbXmlDir); - break; - - case NONE: - assertNull(pdbXmlFile); - break; - } - } - - private void deleteCreatedFiles(List filesToDelete) { - // Delete in the reverse order the files were added - for (int i = filesToDelete.size() - 1; i >= 0; i--) { - filesToDelete.get(i).delete(); - } - } - - /** - * notepad.pdb is here: /symbols/notepad.pdb/36CFD5F9888C4483B522B9DB242D84782/ - * notepad.pdb.xml does not exist - * Repo location left alone (default) - * - * PDB file should not be found, since default repo does not point to symbols folder. - * - * @throws Exception - */ - @Test - public void testFindPdb1() throws Exception { - - createdFiles = null; - - createdFiles = createFiles(PdbLocation.SYMBOLS_SUBDIR, PdbXmlLocation.NONE); - File pdb = PdbParser.findPDB(testProgram, false, noSuchSymbolsRepoDir); - - // Should not find anything since repo is set to an invalid path - assertNull(pdb); - - } - - /** - * notepad.pdb is here: /symbols/notepad.pdb/36CFD5F9888C4483B522B9DB242D84782/ - * notepad.pdb.xml does not exist - * Repo location set to /symbols - * - * On Windows, PDB file should be found. - * On non-Windows, Pdb file should be found. - * - * @throws Exception - */ - @Test - public void testFindPdb2() throws Exception { - - createdFiles = createFiles(PdbLocation.SYMBOLS_SUBDIR, PdbXmlLocation.NONE); - - File pdb = PdbParser.findPDB(testProgram, false, symbolsFolder); - - assertNotNull(pdb); - assertEquals(pdbFile, pdb); - - } - - /** - * notepad.pdb is here: /symbols/notepad.pdb/36CFD5F9888C4483B522B9DB242D84782/ - * notepad.pdb.xml is here: /symbols/notepad.pdb/36CFD5F9888C4483B522B9DB242D84782/ - * Repo location set to /symbols - * - * On Windows, PDB file should be found. - * On non-Windows, PDB XML file should be found. - * - * @throws Exception - */ - @Test - public void testFindPdb3() throws Exception { - - createdFiles = createFiles(PdbLocation.SYMBOLS_SUBDIR, PdbXmlLocation.SAME_AS_PDB); - - File pdb = PdbParser.findPDB(testProgram, false, symbolsFolder); - - assertNotNull(pdb); - - if (PdbParser.onWindows) { - assertEquals(pdbFile, pdb); - } - else { - assertEquals(pdbXmlFile, pdb); - } - } - - /** - * notepad.pdb is here: /symbols/notepad.pdb/36CFD5F9888C4483B522B9DB242D84782/ - * notepad.pdb.xml is here: /pdb_xml/ - * Repo location set to /symbols - * - * On Windows, PDB file should be found. - * On non-Windows, PDB files should be found. - * - * @throws Exception - */ - @Test - public void testFindPdb4() throws Exception { - - createdFiles = createFiles(PdbLocation.SYMBOLS_SUBDIR, PdbXmlLocation.OWN_DIR); - - File pdb = PdbParser.findPDB(testProgram, false, symbolsFolder); - - assertNotNull(pdb); - assertEquals(pdb, pdb); - - } - - /** - * notepad.pdb is here: /symbols/notepad.pdb/36CFD5F9888C4483B522B9DB242D84782/ - * notepad.pdb.xml is here: /pdb_xml/ - * Repo location set to /pdb_xml - * - * On Windows, PDB XML file should be found. - * On non-Windows, PDB XML file should be found. - * - * @throws Exception - */ - @Test - public void testFindPdb5() throws Exception { - - createdFiles = createFiles(PdbLocation.SYMBOLS_SUBDIR, PdbXmlLocation.OWN_DIR); - - File pdb = PdbParser.findPDB(testProgram, false, pdbXmlDir); - - assertNotNull(pdb); - - if (PdbParser.onWindows) { - assertEquals(pdbXmlFile, pdb); - } - else { - assertEquals(pdbXmlFile, pdb); - } - } - - /** - * notepad.pdb is here: /symbols/ - * notepad.pdb.xml does not exist - * Repo location set to /symbols - * - * On Windows, PDB file should be found. - * On non-Windows, PDB file should be found. - * - * @throws Exception - */ - @Test - public void testFindPdb6() throws Exception { - - createdFiles = createFiles(PdbLocation.SYMBOLS_NO_SUBDIR, PdbXmlLocation.NONE); - - File pdb = PdbParser.findPDB(testProgram, false, symbolsFolder); - - assertNotNull(pdb); - assertEquals(pdbFile, pdb); - - } - - /** - * notepad.pdb is here: /symbols/ - * notepad.pdb.xml does not exist - * Repo location set to /pdb_xml/ - * - * PDB file should not be found, since default repo does not point to symbols folder. - * - * @throws Exception - */ - @Test - public void testFindPdb7() throws Exception { - - createdFiles = createFiles(PdbLocation.SYMBOLS_NO_SUBDIR, PdbXmlLocation.NONE); - - File pdb = PdbParser.findPDB(testProgram, false, pdbXmlDir); - - // Should not find anything since repo is set to an invalid path - assertNull(pdb); - } - - /** - * notepad.pdb is here: /symbols/ - * notepad.pdb.xml is here: /symbols/ - * Repo location set to /symbols - * - * On Windows, PDB file should be found. - * On non-Windows, PDB XML file should be found. - * - * @throws Exception - */ - @Test - public void testFindPdb8() throws Exception { - - createdFiles = createFiles(PdbLocation.SYMBOLS_NO_SUBDIR, PdbXmlLocation.SAME_AS_PDB); - - File pdb = PdbParser.findPDB(testProgram, false, symbolsFolder); - - assertNotNull(pdb); - - if (PdbParser.onWindows) { - assertEquals(pdbFile, pdb); - } - else { - assertEquals(pdbXmlFile, pdb); - } - } - - /** - * notepad.pdb is here: /symbols/ - * notepad.pdb.xml is here: /pdb_xml/ - * Repo location set to /symbols - * - * On Windows, PDB file should be found. - * On non-Windows, PDB file should be found. - * - * @throws Exception - */ - @Test - public void testFindPdb9() throws Exception { - - createdFiles = createFiles(PdbLocation.SYMBOLS_NO_SUBDIR, PdbXmlLocation.OWN_DIR); - - File pdb = PdbParser.findPDB(testProgram, false, symbolsFolder); - - assertNotNull(pdb); - assertEquals(pdbFile, pdb); - - } - - /** - * notepad.pdb is here: /symbols/ - * notepad.pdb.xml is here: /pdb_xml/ - * Repo location set to /pdb_xml - * - * On Windows, PDB XML file should be found. - * On non-Windows, PDB XML file should be found. - * - * @throws Exception - */ - @Test - public void testFindPdb10() throws Exception { - - createdFiles = createFiles(PdbLocation.SYMBOLS_NO_SUBDIR, PdbXmlLocation.OWN_DIR); - - File pdb = PdbParser.findPDB(testProgram, false, pdbXmlDir); - - assertNotNull(pdb); - - if (PdbParser.onWindows) { - assertEquals(pdbXmlFile, pdb); - } - else { - assertEquals(pdbXmlFile, pdb); - } - } - - /** - * notepad.pdb is here: /exe/notepad.pdb/36CFD5F9888C4483B522B9DB242D84782/ - * notepad.pdb.xml does not exist - * Repo location set to /exe/notepad.pdb/36CFD5F9888C4483B522B9DB242D84782/ - * - * On Windows, PDB file should be found - * On non-Windows, PDB file should be found. - * - * @throws Exception - */ - @Test - public void testFindPdb11() throws Exception { - - createdFiles = createFiles(PdbLocation.SAME_AS_EXE_SUBDIR, PdbXmlLocation.NONE); - - File pdb = PdbParser.findPDB(testProgram, false, pdbFile.getParentFile()); - - assertNotNull(pdb); - assertEquals(pdbFile, pdb); - - } - - /** - * notepad.pdb is here: /exe/notepad.pdb/36CFD5F9888C4483B522B9DB242D84782/ - * notepad.pdb.xml is here: /exe/notepad.pdb/36CFD5F9888C4483B522B9DB242D84782/ - * Repo location set to default location - * - * PDB file should not be found, since default repo does not point to exe folder. - * - * @throws Exception - */ - @Test - public void testFindPdb12() throws Exception { - - createdFiles = createFiles(PdbLocation.SAME_AS_EXE_SUBDIR, PdbXmlLocation.SAME_AS_PDB); - - File pdb = PdbParser.findPDB(testProgram, false, noSuchSymbolsRepoDir); - - // Should not find anything since repo is set to an invalid path - assertNull(pdb); - } - - /** - * notepad.pdb is here: /exe/notepad.pdb/36CFD5F9888C4483B522B9DB242D84782/ - * notepad.pdb.xml is here: /exe/notepad.pdb/36CFD5F9888C4483B522B9DB242D84782/ - * Repo location set to /exe/notepad.pdb/36CFD5F9888C4483B522B9DB242D84782/ - * - * On Windows, PDB file should be found. - * On non-Windows, PDB XML file should be found. - * - * @throws Exception - */ - @Test - public void testFindPdb13() throws Exception { - - createdFiles = createFiles(PdbLocation.SAME_AS_EXE_SUBDIR, PdbXmlLocation.SAME_AS_PDB); - - File pdb = PdbParser.findPDB(testProgram, false, pdbFile.getParentFile()); - - assertNotNull(pdb); - - if (PdbParser.onWindows) { - assertEquals(pdbFile, pdb); - } - else { - assertEquals(pdbXmlFile, pdb); - } - } - - /** - * notepad.pdb is here: /exe/notepad.pdb/36CFD5F9888C4483B522B9DB242D84782/ - * notepad.pdb.xml is here: /pdb_xml/ - * Repo location set to /exe/notepad.pdb/36CFD5F9888C4483B522B9DB242D84782/ - * - * On Windows, PDB file should be found. - * On non-Windows, PDB file should be found. - * - * @throws Exception - */ - @Test - public void testFindPdb14() throws Exception { - - createdFiles = createFiles(PdbLocation.SAME_AS_EXE_SUBDIR, PdbXmlLocation.OWN_DIR); - - File pdb = PdbParser.findPDB(testProgram, false, pdbFile.getParentFile()); - - assertNotNull(pdb); - assertEquals(pdbFile, pdb); - - } - - /** - * notepad.pdb is here: /exe/notepad.pdb/36CFD5F9888C4483B522B9DB242D84782/ - * notepad.pdb.xml is here: /pdb_xml/ - * Repo location set to /pdb_xml/ - * - * On Windows, PDB XML file should be found. - * On non-Windows, PDB XML file should be found. - * - * @throws Exception - */ - @Test - public void testFindPdb15() throws Exception { - - createdFiles = createFiles(PdbLocation.SAME_AS_EXE_SUBDIR, PdbXmlLocation.OWN_DIR); - - File pdb = PdbParser.findPDB(testProgram, false, pdbXmlFile.getParentFile()); - - assertNotNull(pdb); - - if (PdbParser.onWindows) { - assertEquals(pdbXmlFile, pdb); - } - else { - assertEquals(pdbXmlFile, pdb); - } - } - - /** - * notepad.pdb is here: /exe/ - * notepad.pdb.xml does not exist - * Repo location set to default location - * - * Special case: Even if repo location is set to a location that doesn't contain a .pdb or - * .pdb.xml file, it will still be found if there is a file in the same folder as the binary - * - * On Windows, PDB file should be found. - * On non-Windows, PDB file should be found. - * - * @throws Exception - */ - @Test - public void testFindPdb16() throws Exception { - - createdFiles = createFiles(PdbLocation.SAME_AS_EXE_NO_SUBDIR, PdbXmlLocation.NONE); - - File pdb = PdbParser.findPDB(testProgram, false, noSuchSymbolsRepoDir); - - assertNotNull(pdb); - assertEquals(pdbFile, pdb); - - } - - /** - * notepad.pdb is here: /exe/ - * notepad.pdb.xml does not exist - * Repo location set to /exe/ - * - * On Windows, PDB file should be found. - * On non-Windows, PDB file should be found. - * - * @throws Exception - */ - @Test - public void testFindPdb17() throws Exception { - - createdFiles = createFiles(PdbLocation.SAME_AS_EXE_NO_SUBDIR, PdbXmlLocation.NONE); - - File pdb = PdbParser.findPDB(testProgram, false, pdbFile.getParentFile()); - - assertNotNull(pdb); - assertEquals(pdbFile, pdb); - - } - - /** - * notepad.pdb is here: /exe/ - * notepad.pdb.xml does not exist - * Repo location set to /pdb_xml/ - * - * Special case: Even if repo location is set to a location that doesn't contain a .pdb or - * .pdb.xml file, it will still be found if there is a file in the same folder as the binary - * - * On Windows, PDB file should be found. - * On non-Windows, PDB file should be found. - * - * @throws Exception - */ - @Test - public void testFindPdb18() throws Exception { - - createdFiles = createFiles(PdbLocation.SAME_AS_EXE_NO_SUBDIR, PdbXmlLocation.NONE); - - File pdb = PdbParser.findPDB(testProgram, false, pdbXmlDir); - - assertNotNull(pdb); - assertEquals(pdbFile, pdb); - - } - - /** - * notepad.pdb is here: /exe/ - * notepad.pdb.xml is here: /exe/ - * Repo location set to the default location - * - * Special case: Even if repo location is set to a location that doesn't contain a .pdb or - * .pdb.xml file, it will still be found if there is a file in the same folder as the binary - * - * On Windows, PDB file should be found. - * On non-Windows, PDB XML file should be found. - * - * @throws Exception - */ - @Test - public void testFindPdb19() throws Exception { - - createdFiles = createFiles(PdbLocation.SAME_AS_EXE_NO_SUBDIR, PdbXmlLocation.SAME_AS_PDB); - - File pdb = PdbParser.findPDB(testProgram, false, noSuchSymbolsRepoDir); - - assertNotNull(pdb); - - if (PdbParser.onWindows) { - assertEquals(pdbFile, pdb); - } - else { - assertEquals(pdbXmlFile, pdb); - } - } - - /** - * notepad.pdb is here: /exe/ - * notepad.pdb.xml is here: /exe/ - * Repo location set to /exe/ - * - * On Windows, PDB file should be found. - * On non-Windows, PDB XML file should be found. - * - * @throws Exception - */ - @Test - public void testFindPdb20() throws Exception { - - createdFiles = createFiles(PdbLocation.SAME_AS_EXE_NO_SUBDIR, PdbXmlLocation.SAME_AS_PDB); - - File pdb = PdbParser.findPDB(testProgram, false, pdbFile.getParentFile()); - - assertNotNull(pdb); - - if (PdbParser.onWindows) { - assertEquals(pdbFile, pdb); - } - else { - assertEquals(pdbXmlFile, pdb); - } - } - - /** - * notepad.pdb is here: /exe/ - * notepad.pdb.xml is here: /pdb_xml/ - * Repo location set to default location - * - * On Windows, PDB file should be found (Repo path is invalid, so it looks in same location as exe). - * On non-Windows, PDB file should be found (Repo path is invalid, so it looks in same location as exe). - * - * @throws Exception - */ - @Test - public void testFindPdb21() throws Exception { - - createdFiles = createFiles(PdbLocation.SAME_AS_EXE_NO_SUBDIR, PdbXmlLocation.OWN_DIR); - - File pdb = PdbParser.findPDB(testProgram, false, noSuchSymbolsRepoDir); - - assertNotNull(pdb); - assertEquals(pdbFile, pdb); - } - - /** - * notepad.pdb is here: /exe/ - * notepad.pdb.xml is here: /pdb_xml/ - * Repo location set to /exe/ - * - * On Windows, PDB file should be found - * On non-Windows, PDB file should be found. - * - * @throws Exception - */ - @Test - public void testFindPdb22() throws Exception { - - createdFiles = createFiles(PdbLocation.SAME_AS_EXE_NO_SUBDIR, PdbXmlLocation.OWN_DIR); - - File pdb = PdbParser.findPDB(testProgram, false, pdbFile.getParentFile()); - - assertNotNull(pdb); - assertEquals(pdbFile, pdb); - } - - /** - * notepad.pdb is here: /exe/ - * notepad.pdb.xml is here: /pdb_xml/ - * Repo location set to /pdb_xml/ - * - * Special case: Even if repo location is set to a location that doesn't contain a .pdb or - * .pdb.xml file, it will still be found if there is a file in the same folder as the binary - * - * On Windows, PDB XML file should be found (looks first in user-defined repo location) - * On non-Windows, PDB XML file should be found - * - * @throws Exception - */ - @Test - public void testFindPdb23() throws Exception { - - createdFiles = createFiles(PdbLocation.SAME_AS_EXE_NO_SUBDIR, PdbXmlLocation.OWN_DIR); - - File pdb = PdbParser.findPDB(testProgram, false, pdbXmlDir); - - assertNotNull(pdb); - - if (PdbParser.onWindows) { - assertEquals(pdbXmlFile, pdb); - } - else { - assertEquals(pdbXmlFile, pdb); - } - } - - /** - * notepad.pdb does not exist - * notepad.pdb.xml does not exist - * Repo location set to the default location - * - * On Windows, no file should be found - * On non-Windows, no file should be found - * - * @throws Exception - */ - @Test - public void testFindPdb24() throws Exception { - - createdFiles = createFiles(PdbLocation.NONE, PdbXmlLocation.NONE); - - File pdb = PdbParser.findPDB(testProgram, false, noSuchSymbolsRepoDir); - assertNull(pdb); - - } - - /** - * notepad.pdb does not exist - * notepad.pdb.xml is here: /pdb_xml/ - * Repo location set to the default location - * - * On Windows, no file should be found - * On non-Windows, no file should be found - * - * @throws Exception - */ - @Test - public void testFindPdb25() throws Exception { - - createdFiles = createFiles(PdbLocation.NONE, PdbXmlLocation.OWN_DIR); - - File pdb = PdbParser.findPDB(testProgram, false, noSuchSymbolsRepoDir); - assertNull(pdb); - - } - - /** - * notepad.pdb does not exist - * notepad.pdb.xml is here: /pdb_xml/ - * Repo location set to /pdb_xml/ - * - * On Windows, PDB XML file should be found - * On non-Windows, PDB XML file should be found - * - * @throws Exception - */ - @Test - public void testFindPdb26() throws Exception { - - createdFiles = createFiles(PdbLocation.NONE, PdbXmlLocation.OWN_DIR); - - File pdb = PdbParser.findPDB(testProgram, false, pdbXmlDir); - - assertNotNull(pdb); - - if (PdbParser.onWindows) { - assertEquals(pdbXmlFile, pdb); - } - else { - assertEquals(pdbXmlFile, pdb); - } - } - - private void createDirectory(File directory) { - directory.mkdir(); - if (!directory.isDirectory()) { - fail("Should have created directory: " + directory); - } - } - - private void createFile(File file) { - boolean createSuccess; - - try { - createSuccess = file.createNewFile(); - - if (!createSuccess) { - fail("Failed creation of file: " + file); - } - } - catch (IOException ioe) { - fail("Exception while creating: " + file); - } - } - - private void buildPdbXml() throws Exception { - // Write to the pdb.xml file - FileWriter xmlFileWriter; - - try { - xmlFileWriter = new FileWriter(pdbXmlFile); - BufferedWriter xmlBuffWriter = new BufferedWriter(xmlFileWriter); + private File buildPdbXml() throws IOException { + File destFile = new File(tempDir, pdbXmlFilename); + try (BufferedWriter xmlBuffWriter = new BufferedWriter(new FileWriter(destFile))) { xmlBuffWriter.write(""); xmlBuffWriter.write("\n"); - xmlBuffWriter.close(); - } - catch (IOException ioe) { - fail("IOException writing to temporary file (" + pdbXmlFile + "). " + - ioe.toString()); } + return destFile; } @Test public void testApplyFunctions() throws Exception { - createdFiles = createFiles(PdbLocation.NONE, PdbXmlLocation.OWN_DIR); - - buildPdbXml(); - - File pdb = PdbParser.findPDB(testProgram, false, pdbXmlDir); + File pdbXmlFile = buildPdbXml(); AutoAnalysisManager mgr = AutoAnalysisManager.getAnalysisManager(testProgram); DataTypeManagerService dataTypeManagerService = mgr.getDataTypeManagerService(); PdbParser parser = - new PdbParser(pdb, testProgram, dataTypeManagerService, false, TaskMonitor.DUMMY); + new PdbParser(pdbXmlFile, testProgram, dataTypeManagerService, false, false, + TaskMonitor.DUMMY); parser.openDataTypeArchives(); parser.parse(); diff --git a/Ghidra/Features/PDB/src/test/java/pdb/symbolserver/DummySymbolServer.java b/Ghidra/Features/PDB/src/test/java/pdb/symbolserver/DummySymbolServer.java new file mode 100644 index 0000000000..ce1d1f608c --- /dev/null +++ b/Ghidra/Features/PDB/src/test/java/pdb/symbolserver/DummySymbolServer.java @@ -0,0 +1,85 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package pdb.symbolserver; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.List; +import java.util.Set; + +import ghidra.util.task.TaskMonitor; + +/** + * A "remote" symbol server that answers affirmatively for any query. + */ +public class DummySymbolServer implements SymbolServer { + + private final byte[] dummyPayload; + private final boolean returnCompressedFilenames; + + public DummySymbolServer(String dummyPayload) { + this(dummyPayload.getBytes(), false); + } + + public DummySymbolServer(byte[] dummyPayload, boolean returnCompressedFilenames) { + this.dummyPayload = dummyPayload; + this.returnCompressedFilenames = returnCompressedFilenames; + } + + @Override + public String getName() { + return "dummy"; + } + + @Override + public boolean isValid(TaskMonitor monitor) { + return true; + } + + @Override + public boolean exists(String filename, TaskMonitor monitor) { + return true; + } + + @Override + public List find(SymbolFileInfo pdbInfo, Set findOptions, + TaskMonitor monitor) { + String name = pdbInfo.getName(); + if (returnCompressedFilenames) { + name = name.substring(0, name.length() - 1) + "_"; + } + SymbolFileLocation symLoc = new SymbolFileLocation(name, this, pdbInfo); + return List.of(symLoc); + } + + @Override + public SymbolServerInputStream getFileStream(String filename, TaskMonitor monitor) + throws IOException { + return new SymbolServerInputStream(new ByteArrayInputStream(dummyPayload), + dummyPayload.length); + } + + @Override + public String getFileLocation(String filename) { + return "dummy-" + filename; + } + + @Override + public boolean isLocal() { + return false; + } + +} diff --git a/Ghidra/Features/PDB/src/test/java/pdb/symbolserver/HttpSymbolServerTest.java b/Ghidra/Features/PDB/src/test/java/pdb/symbolserver/HttpSymbolServerTest.java new file mode 100644 index 0000000000..bcaccd4f8b --- /dev/null +++ b/Ghidra/Features/PDB/src/test/java/pdb/symbolserver/HttpSymbolServerTest.java @@ -0,0 +1,40 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package pdb.symbolserver; + +import static org.junit.Assert.*; + +import java.net.URI; +import java.util.List; + +import ghidra.util.task.TaskMonitor; + +public class HttpSymbolServerTest { + + //@Test + public void test() { + // This test is not enabled by default as it depends on an third-party resource + HttpSymbolServer httpSymbolServer = + new HttpSymbolServer(URI.create("http://msdl.microsoft.com/download/symbols/")); + SymbolFileInfo pdbInfo = + SymbolFileInfo.fromValues("kernelbase.pdb", "C1C44EDD93E1B8BA671874B5C1490C2D", 1); + + List results = + httpSymbolServer.find(pdbInfo, FindOption.NO_OPTIONS, TaskMonitor.DUMMY); + assertEquals(1, results.size()); + } + +} diff --git a/Ghidra/Features/PDB/src/test/java/pdb/symbolserver/LocalSymbolServerTest.java b/Ghidra/Features/PDB/src/test/java/pdb/symbolserver/LocalSymbolServerTest.java new file mode 100644 index 0000000000..72f38b7797 --- /dev/null +++ b/Ghidra/Features/PDB/src/test/java/pdb/symbolserver/LocalSymbolServerTest.java @@ -0,0 +1,225 @@ +/* ### + * 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.symbolserver; + +import static org.junit.Assert.*; + +import java.util.List; + +import java.io.File; +import java.io.IOException; + +import org.junit.Before; +import org.junit.Test; + +import generic.test.AbstractGenericTest; +import ghidra.util.task.TaskMonitor; +import utilities.util.FileUtilities; + +/** + * Test searching for symbol files in a local directory structure. + *

    + * Directory level 1, 2 are MS compatible layouts of pdb symbol files. + * Directory level 0 is a ghidra-ism where pdb symbol files can + * be found in a un-organized directory with non-exact file names. + *

    + * Testing level 0 searching is a TODO item because creating test + * files that can be parsed isn't possible right now. (level 1, 2 + * directories can skip parsing the file since the guid/age is + * in the path) + */ +public class LocalSymbolServerTest extends AbstractGenericTest { + + private File temporaryDir; + + private File mkFile(File file) throws IOException { + FileUtilities.checkedMkdirs(file.getParentFile()); + FileUtilities.writeStringToFile(file, "test"); + return file; + } + + @Before + public void setup() throws IOException { + temporaryDir = createTempDirectory("localsymbolserver"); + } + + @Test + public void testCreate_Level0() throws IOException { + File root = new File(temporaryDir, "symbols"); + LocalSymbolStore.create(root, 0); + + assertTrue("Should not create files", root.list().length == 0); + } + + @Test + public void testCreate_Level1() throws IOException { + File root = new File(temporaryDir, "symbols"); + LocalSymbolStore.create(root, 1); + + assertTrue("Pingme should exist", new File(root, "pingme.txt").exists()); + assertTrue("Admin dir should exist", new File(root, "000admin").exists()); + assertFalse("Index2 should not exist", new File(root, "index2.txt").exists()); + } + + @Test + public void testCreate_Level2() throws IOException { + File root = new File(temporaryDir, "symbols"); + LocalSymbolStore.create(root, 2); + + assertTrue("Pingme should exist", new File(root, "pingme.txt").exists()); + assertTrue("Admin dir should exist", new File(root, "000admin").exists()); + assertTrue("Index2 should exist", new File(root, "index2.txt").exists()); + } + + @Test + public void findExact_Level1() throws IOException { + File root = new File(temporaryDir, "symbols"); + LocalSymbolStore.create(root, 1); + LocalSymbolStore localSymbolStore = new LocalSymbolStore(root); + + File pdbFile = mkFile(new File(root, "file1.pdb/112233445/file1.pdb")); + mkFile(new File(root, "file1.pdb/112233446/file1.pdb")); + + List results = + localSymbolStore.find(SymbolFileInfo.fromValues("file1.pdb", "11223344", 5), + FindOption.NO_OPTIONS, TaskMonitor.DUMMY); + + assertEquals(1, results.size()); + + String resultLocation = localSymbolStore.getFileLocation(results.get(0).getPath()); + assertEquals(pdbFile.getPath(), resultLocation); + } + + @Test + public void findAnyAges_Level1() throws IOException { + // find pdbs with the same UID, but any AGE + File root = new File(temporaryDir, "symbols"); + LocalSymbolStore.create(root, 1); + LocalSymbolStore localSymbolStore = new LocalSymbolStore(root); + + mkFile(new File(root, "file1.pdb/112233445/file1.pdb")); + mkFile(new File(root, "file1.pdb/112233446/file1.pdb")); + mkFile(new File(root, "file1.pdb/112233450/file1.pdb")); + + List results = + localSymbolStore.find(SymbolFileInfo.fromValues("file1.pdb", "11223344", 0), + FindOption.of(FindOption.ANY_AGE), TaskMonitor.DUMMY); + + assertEquals(2, results.size()); + assertFalse(results.stream() + .map(symbolFileLocation -> symbolFileLocation.getFileInfo().getUniqueName()) + .anyMatch(s -> !s.equals("11223344"))); + } + + @Test + public void findAnyUIDs_Level1() throws IOException { + File root = new File(temporaryDir, "symbols"); + LocalSymbolStore.create(root, 1); + LocalSymbolStore localSymbolStore = new LocalSymbolStore(root); + + mkFile(new File(root, "file1.pdb/112233400/file1.pdb")); + mkFile(new File(root, "file1.pdb/112233410/file1.pdb")); + mkFile(new File(root, "file1.pdb/112233420/file1.pdb")); + + List results = + localSymbolStore.find(SymbolFileInfo.fromValues("file1.pdb", "11223344", 0), + FindOption.of(FindOption.ANY_ID), TaskMonitor.DUMMY); + + assertEquals(3, results.size()); + } + + @Test + public void findExact_Level2() throws IOException { + File root = new File(temporaryDir, "symbols"); + LocalSymbolStore.create(root, 2); + LocalSymbolStore localSymbolStore = new LocalSymbolStore(root); + + File similarPdbFile1 = mkFile(new File(root, "fi/file1.pdb/112233445/file1.pdb")); + mkFile(new File(root, "fi/file1.pdb/112233446/file1.pdb")); + + List results = + localSymbolStore.find(SymbolFileInfo.fromValues("file1.pdb", "11223344", 5), + FindOption.NO_OPTIONS, TaskMonitor.DUMMY); + + assertEquals(1, results.size()); + + String resultLocation = localSymbolStore.getFileLocation(results.get(0).getPath()); + assertEquals(similarPdbFile1.getPath(), resultLocation); + } + + @Test + public void giveFile_Level0() throws IOException { + File root = new File(temporaryDir, "symbols"); + LocalSymbolStore.create(root, 0); + + LocalSymbolStore localSymbolStore = new LocalSymbolStore(root); + + File file1 = mkFile(new File(temporaryDir, "file1.pdb")); + localSymbolStore.giveFile(SymbolFileInfo.fromValues("file1.pdb", "11223344", 0), file1, + "file1.pdb", TaskMonitor.DUMMY); + + assertFalse(file1.exists()); + + // can't search for the pdb file because a level0 LocalSymbolStore would + // need to open up any 'pdb' files it finds to read the guid/id and age, + // and we can't create good pdbs right now that would enable this. + + File expectedFile = new File(root, "file1.pdb"); + assertTrue(expectedFile.exists()); + } + + @Test + public void giveFile_Level1() throws IOException { + File root = new File(temporaryDir, "symbols"); + LocalSymbolStore.create(root, 1); + LocalSymbolStore localSymbolStore = new LocalSymbolStore(root); + + File file1 = mkFile(new File(temporaryDir, "file1.pdb")); + localSymbolStore.giveFile(SymbolFileInfo.fromValues("file1.pdb", "11223344", 0), file1, + "file1.pdb", TaskMonitor.DUMMY); + + assertFalse(file1.exists()); + + List results = + localSymbolStore.find(SymbolFileInfo.fromValues("file1.pdb", "11223344", 0), + FindOption.NO_OPTIONS, TaskMonitor.DUMMY); + assertEquals(1, results.size()); + assertEquals("file1.pdb/112233440/file1.pdb", results.get(0).getPath()); + assertEquals("11223344", results.get(0).getFileInfo().getUniqueName()); + assertEquals(0, results.get(0).getFileInfo().getIdentifiers().getAge()); + } + + @Test + public void giveFile_Level2() throws IOException { + File root = new File(temporaryDir, "symbols"); + LocalSymbolStore.create(root, 1); + LocalSymbolStore localSymbolStore = new LocalSymbolStore(root); + + File file1 = mkFile(new File(temporaryDir, "file1.pdb")); + localSymbolStore.giveFile(SymbolFileInfo.fromValues("file1.pdb", "11223344", 0), file1, + "file1.pdb", TaskMonitor.DUMMY); + + assertFalse(file1.exists()); + + List results = + localSymbolStore.find(SymbolFileInfo.fromValues("file1.pdb", "11223344", 0), + FindOption.NO_OPTIONS, TaskMonitor.DUMMY); + assertEquals(1, results.size()); + assertEquals("file1.pdb/112233440/file1.pdb", results.get(0).getPath()); + assertEquals("11223344", results.get(0).getFileInfo().getUniqueName()); + assertEquals(0, results.get(0).getFileInfo().getIdentifiers().getAge()); + } +} diff --git a/Ghidra/Features/PDB/src/test/java/pdb/symbolserver/SymbolServerInstanceCreatorRegistryTest.java b/Ghidra/Features/PDB/src/test/java/pdb/symbolserver/SymbolServerInstanceCreatorRegistryTest.java new file mode 100644 index 0000000000..00d0c2fc07 --- /dev/null +++ b/Ghidra/Features/PDB/src/test/java/pdb/symbolserver/SymbolServerInstanceCreatorRegistryTest.java @@ -0,0 +1,101 @@ +/* ### + * 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.symbolserver; + +import static org.junit.Assert.*; + +import java.util.List; + +import java.io.File; +import java.io.IOException; + +import org.junit.Before; +import org.junit.Test; + +import generic.test.AbstractGenericTest; + +public class SymbolServerInstanceCreatorRegistryTest extends AbstractGenericTest { + + private SymbolServerInstanceCreatorRegistry symbolServerInstanceCreatorRegistry = + SymbolServerInstanceCreatorRegistry.getInstance(); + private SymbolServerInstanceCreatorContext symbolServerInstanceCreatorContext = + symbolServerInstanceCreatorRegistry.getContext(); + private File temporaryDir; + + @Before + public void setup() throws IOException { + temporaryDir = createTempDirectory("localsymbolserver"); + } + + @Test + public void testCreateLocalSymbolStore() { + SymbolServer symbolServer = symbolServerInstanceCreatorRegistry + .newSymbolServer(temporaryDir.getPath(), symbolServerInstanceCreatorContext); + assertNotNull(symbolServer); + assertTrue(symbolServer instanceof LocalSymbolStore); + } + + @Test + public void testCreateHttpSymbolServer() { + SymbolServer symbolServer = symbolServerInstanceCreatorRegistry + .newSymbolServer("http://localhost/blah", symbolServerInstanceCreatorContext); + assertNotNull(symbolServer); + assertTrue(symbolServer instanceof HttpSymbolServer); + } + + @Test + public void testCreateHttpsSymbolServer() { + SymbolServer symbolServer = symbolServerInstanceCreatorRegistry + .newSymbolServer("https://localhost/blah", symbolServerInstanceCreatorContext); + assertNotNull(symbolServer); + assertTrue(symbolServer instanceof HttpSymbolServer); + } + + @Test + public void testCreateSameDirSymbolStore() { + SymbolServer symbolServer = symbolServerInstanceCreatorRegistry.newSymbolServer(".", + symbolServerInstanceCreatorContext); + assertNotNull(symbolServer); + assertTrue(symbolServer instanceof SameDirSymbolStore); + } + + @Test + public void testCreateDisabledSymbolServer() { + SymbolServer symbolServer = symbolServerInstanceCreatorRegistry + .newSymbolServer("disabled://.", symbolServerInstanceCreatorContext); + assertNotNull(symbolServer); + assertTrue(symbolServer instanceof DisabledSymbolServer); + assertTrue( + ((DisabledSymbolServer) symbolServer).getSymbolServer() instanceof SameDirSymbolStore); + } + + @Test + public void testBogusLocation() { + SymbolServer symbolServer = symbolServerInstanceCreatorRegistry.newSymbolServer("blah://", + symbolServerInstanceCreatorContext); + assertNull(symbolServer); + } + + @Test + public void testPath() { + List symbolServerResultList = + symbolServerInstanceCreatorRegistry.createSymbolServersFromPathList( + List.of(".", "http://localhost/blah"), symbolServerInstanceCreatorContext); + assertEquals(2, symbolServerResultList.size()); + assertTrue(symbolServerResultList.get(0) instanceof SameDirSymbolStore); + assertTrue(symbolServerResultList.get(1) instanceof HttpSymbolServer); + } +} diff --git a/Ghidra/Features/PDB/src/test/java/pdb/symbolserver/SymbolServerServiceTest.java b/Ghidra/Features/PDB/src/test/java/pdb/symbolserver/SymbolServerServiceTest.java new file mode 100644 index 0000000000..c32f8ca3c7 --- /dev/null +++ b/Ghidra/Features/PDB/src/test/java/pdb/symbolserver/SymbolServerServiceTest.java @@ -0,0 +1,160 @@ +/* ### + * 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.symbolserver; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; + +import org.junit.Before; +import org.junit.Test; + +import generic.test.AbstractGenericTest; +import ghidra.util.exception.CancelledException; +import ghidra.util.task.TaskMonitor; +import utilities.util.FileUtilities; + +/** + * Also see SymbolServerService2Test in the _Integration Tests module for tests that + * decompress compressed pdb files. + */ +public class SymbolServerServiceTest extends AbstractGenericTest { + private File temporaryDir; + private File localSymbolStore1Root; + private File localSymbolStore2Root; + private LocalSymbolStore localSymbolStore1; + private LocalSymbolStore localSymbolStore2; + + private File mkFile(File file) throws IOException { + FileUtilities.checkedMkdirs(file.getParentFile()); + FileUtilities.writeStringToFile(file, "test"); + return file; + } + + @Before + public void setup() throws IOException { + temporaryDir = createTempDirectory("symbolservers"); + localSymbolStore1Root = new File(temporaryDir, "symbols1"); + localSymbolStore2Root = new File(temporaryDir, "symbols2"); + LocalSymbolStore.create(localSymbolStore1Root, 1); + LocalSymbolStore.create(localSymbolStore2Root, 1); + + localSymbolStore1 = new LocalSymbolStore(localSymbolStore1Root); + localSymbolStore2 = new LocalSymbolStore(localSymbolStore2Root); + } + + @Test + public void test_Exact_AlreadyLocal() throws IOException, CancelledException { + File pdbFile1 = mkFile(new File(localSymbolStore1Root, "file1.pdb/112233440/file1.pdb")); + File pdbFile2 = mkFile(new File(localSymbolStore2Root, "file1.pdb/112233440/file1.pdb")); + + SymbolServerService symbolServerService = new SymbolServerService(localSymbolStore1, + List.of(localSymbolStore1, localSymbolStore2)); + List results = + symbolServerService.find(SymbolFileInfo.fromValues("file1.pdb", "11223344", 0), + TaskMonitor.DUMMY); + + assertEquals(2, results.size()); + + File foundPdbFile1 = symbolServerService.getSymbolFile(results.get(0), TaskMonitor.DUMMY); + File foundPdbFile2 = symbolServerService.getSymbolFile(results.get(1), TaskMonitor.DUMMY); + + assertEquals(pdbFile1, foundPdbFile1); + assertEquals(pdbFile2, foundPdbFile2); + } + + @Test + public void test_AnyAge() throws IOException, CancelledException { + // search for similar pdbs, across multiple storage servers + mkFile(new File(localSymbolStore1Root, "file1.pdb/000000001/file1.pdb")); + mkFile(new File(localSymbolStore1Root, "file1.pdb/112233441/file1.pdb")); + mkFile(new File(localSymbolStore2Root, "file1.pdb/112233442/file1.pdb")); + + SymbolServerService symbolServerService = + new SymbolServerService(localSymbolStore1, List.of(localSymbolStore2)); + List results = + symbolServerService.find(SymbolFileInfo.fromValues("file1.pdb", "11223344", 0), + FindOption.of(FindOption.ANY_AGE), TaskMonitor.DUMMY); + + assertEquals(2, results.size()); + Set uids = results.stream() + .map(symbolFileLocation -> symbolFileLocation.getFileInfo().getUniqueName()) + .collect(Collectors.toSet()); + assertEquals(1, uids.size()); + assertTrue(uids.contains("11223344")); + } + + @Test + public void test_AnyUID() throws IOException, CancelledException { + // search for similar pdbs, across multiple storage servers + mkFile(new File(localSymbolStore1Root, "file2.pdb/000000001/file2.pdb")); + mkFile(new File(localSymbolStore1Root, "file1.pdb/000000001/file1.pdb")); + mkFile(new File(localSymbolStore1Root, "file1.pdb/112233441/file1.pdb")); + mkFile(new File(localSymbolStore2Root, "file1.pdb/112233442/file1.pdb")); + + SymbolServerService symbolServerService = + new SymbolServerService(localSymbolStore1, List.of(localSymbolStore2)); + List results = + symbolServerService.find(SymbolFileInfo.fromValues("file1.pdb", "11223344", 0), + FindOption.of(FindOption.ANY_ID), TaskMonitor.DUMMY); + + assertEquals(3, results.size()); + Set uids = results.stream() + .map(symbolFileLocation -> symbolFileLocation.getFileInfo().getUniqueName()) + .collect(Collectors.toSet()); + assertEquals(2, uids.size()); + assertTrue(uids.contains("11223344")); + assertTrue(uids.contains("00000000")); + } + + @Test + public void test_Remote() throws IOException, CancelledException { + String payload = "testdummy"; + SymbolServerService symbolServerService = + new SymbolServerService(localSymbolStore1, + List.of(localSymbolStore2, new DummySymbolServer(payload))); + SymbolFileInfo searchPdb = SymbolFileInfo.fromValues("file1.pdb", "11223344", 0); + List results = + symbolServerService.find(searchPdb, FindOption.of(FindOption.ALLOW_REMOTE), + TaskMonitor.DUMMY); + + assertEquals(1, results.size()); + assertTrue(results.get(0).isExactMatch(searchPdb)); + + File pdbFile = symbolServerService.getSymbolFile(results.get(0), TaskMonitor.DUMMY); + assertEquals(payload, Files.readString(pdbFile.toPath())); + } + + @Test + public void test_NoRemote() throws CancelledException { + String payload = "testdummy"; + SymbolServerService symbolServerService = + new SymbolServerService(localSymbolStore1, List.of(new DummySymbolServer(payload))); + SymbolFileInfo searchPdb = SymbolFileInfo.fromValues("file1.pdb", "11223344", 0); + List results = + symbolServerService.find(searchPdb, FindOption.NO_OPTIONS, TaskMonitor.DUMMY); + + assertEquals(0, results.size()); + } + +} diff --git a/Ghidra/Framework/Docking/src/main/java/docking/DialogComponentProvider.java b/Ghidra/Framework/Docking/src/main/java/docking/DialogComponentProvider.java index 24f7964841..7cd9f9e1d2 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/DialogComponentProvider.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/DialogComponentProvider.java @@ -15,9 +15,10 @@ */ package docking; +import java.util.*; + import java.awt.*; import java.awt.event.*; -import java.util.*; import javax.swing.*; import javax.swing.Timer; @@ -62,14 +63,14 @@ public class DialogComponentProvider protected JPanel rootPanel; private JPanel mainPanel; private JComponent workPanel; - private JPanel buttonPanel; + protected JPanel buttonPanel; private JPanel statusPanel; protected JButton okButton; protected JButton applyButton; protected JButton cancelButton; protected JButton dismissButton; private boolean isAlerting; - private JLabel statusLabel; + private GDHtmlLabel statusLabel; private JPanel statusProgPanel; // contains status panel and progress panel private Timer showTimer; private TaskScheduler taskScheduler; @@ -697,7 +698,7 @@ public class DialogComponentProvider }); } - private Color getStatusColor(MessageType type) { + protected Color getStatusColor(MessageType type) { switch (type) { case ALERT: return Color.orange; diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/textfield/IntegerTextField.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/textfield/IntegerTextField.java index d7686304b1..ae14f37cdb 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/textfield/IntegerTextField.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/textfield/IntegerTextField.java @@ -15,11 +15,12 @@ */ package docking.widgets.textfield; +import java.util.ArrayList; +import java.util.List; + import java.awt.*; import java.awt.event.*; import java.math.BigInteger; -import java.util.ArrayList; -import java.util.List; import javax.swing.*; import javax.swing.event.*; @@ -372,6 +373,15 @@ public class IntegerTextField { textField.setEnabled(enabled); } + /** + * Sets the editable mode for the JTextField component + * + * @param editable boolean flag, if true component is editable + */ + public void setEditable(boolean editable) { + textField.setEditable(editable); + } + /** * Requests focus to the JTextField */ diff --git a/Ghidra/Framework/Generic/src/main/java/ghidra/net/HttpClients.java b/Ghidra/Framework/Generic/src/main/java/ghidra/net/HttpClients.java new file mode 100644 index 0000000000..985e28516b --- /dev/null +++ b/Ghidra/Framework/Generic/src/main/java/ghidra/net/HttpClients.java @@ -0,0 +1,88 @@ +/* ### + * 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.net; + +import java.io.IOException; +import java.net.http.HttpClient; +import java.net.http.HttpClient.Redirect; +import java.security.NoSuchAlgorithmException; + +import javax.net.ssl.SSLContext; + +public class HttpClients { + /** + * Note: java.net.http.HttpClient instances can allocate system resources (file handles), + * and frequently creating a new HttpClient could exhaust system resources. + *

    + * There is no "close()" on a HttpClient to release resources. The system resources + * allocated by HttpClient instances will be released when the instance is gc'd. + * However, since the resources in question (filehandles) are not tied to memory pressure, + * its possible a gc() won't happen before running out of file handles if a few hundred + * HttpClient instances have been created / discarded. + *

    + * Also note, there is no per-connection ability to disable hostname verification in a + * SSL/TLS connection. There is a global flag: + * -Djdk.internal.httpclient.disableHostnameVerification + * + */ + private static HttpClient client; + + /** + * Creates a HttpClient Builder using Ghidra SSL/TLS context info. + * + * @return a new HttpClient Builder + * @throws IOException if error in PKI settings or crypto configuration + */ + public static HttpClient.Builder newHttpClientBuilder() throws IOException { + if (!ApplicationKeyManagerFactory.initialize()) { + if (ApplicationKeyManagerFactory.getKeyStore() != null) { + throw new IOException("Failed to initialize PKI certificate keystore"); + } + } + + try { + return HttpClient.newBuilder() + .sslContext(SSLContext.getDefault()) + .followRedirects(Redirect.NORMAL); + } + catch (NoSuchAlgorithmException nsae) { + throw new IOException("Missing algorithm", nsae); + } + } + + /** + * Returns a shared, plain (no special options) {@link HttpClient}. + * + * @return a {@link HttpClient} + * @throws IOException if error in PKI settings or crypto configuration + */ + public static synchronized HttpClient getHttpClient() throws IOException { + if (client == null) { + client = newHttpClientBuilder().build(); + } + + return client; + } + + /** + * Clears the currently cached {@link HttpClient}, forcing it to be + * rebuilt during the next call to {@link #getHttpClient()}. + */ + public static synchronized void clearHttpClient() { + client = null; + } + +} diff --git a/Ghidra/Framework/Generic/src/main/java/ghidra/net/SSLContextInitializer.java b/Ghidra/Framework/Generic/src/main/java/ghidra/net/SSLContextInitializer.java index fa631e97fa..3708f05ed4 100644 --- a/Ghidra/Framework/Generic/src/main/java/ghidra/net/SSLContextInitializer.java +++ b/Ghidra/Framework/Generic/src/main/java/ghidra/net/SSLContextInitializer.java @@ -110,6 +110,10 @@ public class SSLContextInitializer implements ModuleInitializer { // Establish default HTTPS socket factory HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory()); + // Force the HttpClient to be re-created by the next request to + // HttpClients.getHttpClient() so that the new SSLContext is used + HttpClients.clearHttpClient(); + return true; } diff --git a/Ghidra/Framework/Generic/src/main/java/ghidra/net/http/HttpUtil.java b/Ghidra/Framework/Generic/src/main/java/ghidra/net/http/HttpUtil.java index ed85bdf285..2f406ccdb2 100644 --- a/Ghidra/Framework/Generic/src/main/java/ghidra/net/http/HttpUtil.java +++ b/Ghidra/Framework/Generic/src/main/java/ghidra/net/http/HttpUtil.java @@ -15,39 +15,15 @@ */ package ghidra.net.http; -import ghidra.net.ApplicationKeyManagerFactory; -import ghidra.util.Msg; - import java.io.*; import java.net.*; import java.util.Properties; +import ghidra.net.ApplicationKeyManagerFactory; +import ghidra.util.Msg; + public class HttpUtil { - public static void main(String[] args) { - - Properties properties = new Properties(); - properties.setProperty("User-Agent", "Microsoft-Symbol-Server/6.3.9600.17298"); - - String urlStr = - "http://msdl.microsoft.com/download/symbols/write.pdb/4FD8CA6696F445A7B969AB9BBD76E4591/write.pd_"; - - String homeDir = System.getProperty("user.home"); - File f = new File(homeDir + "/Downloads", "write.pdb.deleteme"); - - try { - getFile(urlStr, properties, true, f); - System.out.println("getFile completed: " + f); - } - catch (MalformedURLException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } /** * Execute an HTTP/HTTPS GET request and return the resulting HttpURLConnection. @@ -155,4 +131,5 @@ public class HttpUtil { return connection.getContentType(); } + } diff --git a/Ghidra/Framework/Generic/src/main/java/ghidra/util/filechooser/ExtensionFileFilter.java b/Ghidra/Framework/Generic/src/main/java/ghidra/util/filechooser/ExtensionFileFilter.java index 8a253c8def..fe026f04c1 100644 --- a/Ghidra/Framework/Generic/src/main/java/ghidra/util/filechooser/ExtensionFileFilter.java +++ b/Ghidra/Framework/Generic/src/main/java/ghidra/util/filechooser/ExtensionFileFilter.java @@ -15,25 +15,23 @@ */ package ghidra.util.filechooser; +import java.util.*; +import java.util.stream.Collectors; + import java.io.File; import java.io.FileFilter; -import java.util.Enumeration; -import java.util.Hashtable; /** * A convenience implementation of FileFilter that filters out * all files except for those type extensions that it knows about. - * - * Extensions are of the type ".foo", which is typically found on - * Windows and Unix boxes, but not on Mac. Case is ignored. - * + *

    + * Extensions are of the type "foo" (no leading dot). Case is ignored. + *

    * Example - create a new filter that filters out all files * but gif and jpg image files: *

      *     GhidraFileChooser chooser = new GhidraFileChooser();
    - *     ExtensionFileFilter filter = new ExtensionFileFilter(
    - *                   new String{"gif", "jpg"}, "JPEG and GIF Images")
    - *     chooser.addFileFilter(filter);
    + *     chooser.addFileFilter(ExtensionFilFilter.forExtensions("JPEG and GIF Images", "gif", "jpg"));
      *
    */ public class ExtensionFileFilter implements GhidraFileFilter { @@ -50,19 +48,16 @@ public class ExtensionFileFilter implements GhidraFileFilter { return eff; } - private Hashtable filters = null; - private String description = null; - private String fullDescription = null; - private boolean useExtensionsInDescription = true; + private List extensions; + private String description; + private String fullDescription; /** * Creates a file filter that accepts the given file type. - * Example: new ExtensionFileFilter("jpg", "JPEG Image Images"); + * Example: new ExtensionFileFilter("jpg", "JPEG Images"); * - * Note that the "." before the extension is not needed. If - * provided, it will be ignored. - * - * @see #addExtension + * @param extension file extension to match, without leading dot + * @param description descriptive string of the filter */ public ExtensionFileFilter(String extension, String description) { this(new String[] { extension }, description); @@ -72,16 +67,15 @@ public class ExtensionFileFilter implements GhidraFileFilter { * Creates a file filter from the given string array and description. * Example: new ExtensionFileFilter(String {"gif", "jpg"}, "Gif and JPG Images"); * - * Note that the "." before the extension is not needed and will be ignored. - * - * @see #addExtension + * @param filters array of file name extensions, each without a leading dot + * @param description descriptive string of the filter */ public ExtensionFileFilter(String[] filters, String description) { - this.filters = new Hashtable(filters.length); - for (String filter : filters) { - addExtension(filter);//add filters one by one - } - setDescription(description); + this.extensions = Arrays.asList(filters) + .stream() + .map(String::toLowerCase) + .collect(Collectors.toList()); + this.description = description; } /** @@ -90,7 +84,6 @@ public class ExtensionFileFilter implements GhidraFileFilter { * * Files that begin with "." are ignored. * - * @see #getExtension * @see FileFilter#accept */ @Override @@ -101,133 +94,37 @@ public class ExtensionFileFilter implements GhidraFileFilter { if (model.isDirectory(f)) { return true; } - if (filters.size() == 0) { + if (extensions.isEmpty()) { return true; } - String extension = getExtension(f); - return extension != null && filters.get(extension) != null; - } - - /** - * Return the extension portion of the file's name . - * - * @see #getExtension - * @see FileFilter#accept - */ - public String getExtension(File f) { - if (f != null) { - String filename = f.getName(); - int i = filename.lastIndexOf('.'); - if (i > 0 && i < filename.length() - 1) { - return filename.substring(i + 1).toLowerCase(); + String filename = f.getName().toLowerCase(); + if (filename.startsWith(".")) { + return false; + } + int fnLen = filename.length(); + for (String ext : extensions) { + int extLen = ext.length(); + int extStart = fnLen - extLen; + if (extStart > 0 && filename.substring(extStart).equals(ext) && + filename.charAt(extStart - 1) == '.') { + return true; } } - return null; + return false; } - /** - * Adds a filetype "dot" extension to filter against. - * - * For example: the following code will create a filter that filters - * out all files except those that end in ".jpg" and ".tif": - * - * ExtensionFileFilter filter = new ExtensionFileFilter(); - * filter.addExtension("jpg"); - * filter.addExtension("tif"); - * - * Note that the "." before the extension is not needed and will be ignored. - */ - public void addExtension(String extension) { - if (filters == null) { - filters = new Hashtable(5); - } - filters.put(extension.toLowerCase(), this); - fullDescription = null; - } - - /** - * Returns the human readable description of this filter. For - * example: "JPEG and GIF Image Files (*.jpg, *.gif)" - */ @Override public String getDescription() { if (fullDescription == null) { - fullDescription = ""; - if (description == null || isExtensionListInDescription()) { - if (description != null) { - fullDescription = description; - } - fullDescription += " ("; - // build the description from the extension list + fullDescription = Objects.requireNonNullElse(description, ""); - if (filters.size() == 0) { - fullDescription += "*.*"; - } - else { - boolean firstExt = true; - Enumeration extensions = filters.keys(); - if (extensions != null) { - while (extensions.hasMoreElements()) { - if (!firstExt) { - fullDescription += ","; - } - else { - firstExt = false; - } - fullDescription += "*." + extensions.nextElement(); - } - } - } - fullDescription += ")"; - } - else { - fullDescription = description; - } + // add prettified extensions to the description string + fullDescription += " ("; + fullDescription += extensions.isEmpty() + ? "*.*" + : extensions.stream().map(s -> "*." + s).collect(Collectors.joining(",")); + fullDescription += ")"; } return fullDescription; } - - /** - * Sets the human readable description of this filter. For - * example: filter.setDescription("Gif and JPG Images"); - * - * @see #setDescription - * @see #setExtensionListInDescription - * @see #isExtensionListInDescription - */ - public void setDescription(String description) { - this.description = description; - fullDescription = null; - } - - /** - * Determines whether the extension list (.jpg, .gif, etc) should - * show up in the human readable description. - * - * Only relevant if a description was provided in the constructor - * or using setDescription(); - * - * @see #getDescription - * @see #setDescription - * @see #isExtensionListInDescription - */ - public void setExtensionListInDescription(boolean b) { - useExtensionsInDescription = b; - fullDescription = null; - } - - /** - * Returns whether the extension list (.jpg, .gif, etc) should - * show up in the human readable description. - * - * Only relevant if a description was provided in the constructor - * or using setDescription(); - * - * @see #getDescription - * @see #setDescription - * @see #setExtensionListInDescription - */ - public final boolean isExtensionListInDescription() { - return useExtensionsInDescription; - } } diff --git a/Ghidra/Framework/Generic/src/main/java/ghidra/util/task/MonitoredRunnable.java b/Ghidra/Framework/Generic/src/main/java/ghidra/util/task/MonitoredRunnable.java index f5365e5b57..f4dfde9d95 100644 --- a/Ghidra/Framework/Generic/src/main/java/ghidra/util/task/MonitoredRunnable.java +++ b/Ghidra/Framework/Generic/src/main/java/ghidra/util/task/MonitoredRunnable.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. @@ -16,12 +15,16 @@ */ package ghidra.util.task; +/** + * Similar to a {@link Runnable} except the {@link #monitoredRun(TaskMonitor) run} + * method is given a monitor to report progress and check for cancellation. + */ public interface MonitoredRunnable { /** - * Similar to a runnable except that is given a monitor to report progress and check for - * cancellation. - * @param monitor the TaskMonitor to use. - */ - public void monitoredRun(TaskMonitor monitor); + * Similar to a runnable except the run method is given a monitor + * to report progress and check for cancellation. + * @param monitor the TaskMonitor to use. + */ + void monitoredRun(TaskMonitor monitor); } diff --git a/Ghidra/Framework/Generic/src/test/java/ghidra/net/http/HttpUtilTest.java b/Ghidra/Framework/Generic/src/test/java/ghidra/net/http/HttpUtilTest.java new file mode 100644 index 0000000000..b61fce7e1b --- /dev/null +++ b/Ghidra/Framework/Generic/src/test/java/ghidra/net/http/HttpUtilTest.java @@ -0,0 +1,48 @@ +/* ### + * 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.net.http; + +import java.util.Properties; + +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; + +public class HttpUtilTest { + public static void main(String[] args) { + + Properties properties = new Properties(); + properties.setProperty("User-Agent", "Microsoft-Symbol-Server/6.3.9600.17298"); + + String urlStr = + "http://msdl.microsoft.com/download/symbols/write.pdb/4FD8CA6696F445A7B969AB9BBD76E4591/write.pd_"; + + String homeDir = System.getProperty("user.home"); + File f = new File(homeDir + "/Downloads", "write.pdb.deleteme"); + + try { + HttpUtil.getFile(urlStr, properties, true, f); + System.out.println("getFile completed: " + f); + } + catch (MalformedURLException e) { + e.printStackTrace(); + } + catch (IOException e) { + e.printStackTrace(); + } + } + +} diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/PdbScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/PdbScreenShots.java index 3de680ed42..63c56cb4fe 100644 --- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/PdbScreenShots.java +++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/PdbScreenShots.java @@ -15,102 +15,183 @@ */ package help.screenshot; -import java.awt.Dimension; -import java.awt.Window; -import java.util.ArrayList; import java.util.List; -import org.junit.Test; +import java.io.File; +import java.io.IOException; +import java.net.URI; -import docking.widgets.dialogs.ObjectChooserDialog; -import ghidra.app.util.pdb.PdbLocator; -import ghidra.util.Msg; -import pdb.URLChoice; +import org.apache.commons.io.FilenameUtils; +import org.junit.*; + +import ghidra.app.util.bin.format.pdb.PdbInfo; +import ghidra.app.util.bin.format.pdb.PdbInfoDotNet; +import ghidra.app.util.datatype.microsoft.GUID; +import ghidra.framework.options.Options; +import ghidra.program.model.listing.Program; +import pdb.PdbPlugin; +import pdb.symbolserver.*; +import pdb.symbolserver.ui.ConfigPdbDialog; +import pdb.symbolserver.ui.LoadPdbDialog; public class PdbScreenShots extends GhidraScreenShotGenerator { - @Test - public void testPdbOrXmlDialog() throws Exception { + private static final String GUID1_STR = "012345670123012301230123456789AB"; - performAction("Download_PDB_File", "PdbSymbolServerPlugin", false); + private int tx; + private File temporaryDir; - Window pdbDialog = waitForWindow("pdb or pdb.xml"); - pdbDialog.setSize(new Dimension(750, 200)); - captureWindow(pdbDialog); + @Override + @Before + public void setUp() throws Exception { + super.setUp(); - pressButtonByText(pdbDialog, "Cancel"); + temporaryDir = createTempDirectory("example_pdb"); + tx = program.startTransaction("set analyzed flag"); + Options proplist = program.getOptions(Program.PROGRAM_INFO); + proplist.setBoolean(Program.ANALYZED, false); + PdbInfo pdbInfo = PdbInfoDotNet.fromValues("HelloWorld.pdb", 1, new GUID(GUID1_STR)); + pdbInfo.serializeToOptions(proplist); + proplist.setString("Executable Location", + new File(temporaryDir, program.getName()).getPath()); + } + + @Override + @After + public void tearDown() throws Exception { + program.endTransaction(tx, false); + super.tearDown(); } @Test - public void testPeSpecifiedPathDialog() throws Exception { - - performAction("Download_PDB_File", "PdbSymbolServerPlugin", false); - - Window pdbDialog = waitForWindow("pdb or pdb.xml"); - pressButtonByText(pdbDialog, "PDB"); - - Window peSpecifiedPathDialog = waitForWindow("PE-specified PDB Path"); - captureWindow(peSpecifiedPathDialog); - - pressButtonByText(peSpecifiedPathDialog, "Cancel"); + public void testSymbolServerConfig_Screenshot() throws IOException { + PdbPlugin.saveSymbolServerServiceConfig(null); + ConfigPdbDialog configPdbDialog = new ConfigPdbDialog(); + showDialogWithoutBlocking(tool, configPdbDialog); + waitForSwing(); + captureDialog(ConfigPdbDialog.class); } @Test - public void testSymbolServerURLDialog() throws Exception { - - // Set up for local directory - PdbLocator.setDefaultPdbSymbolsDir(getTestDataDirectory()); - - performAction("Download_PDB_File", "PdbSymbolServerPlugin", false); - - Window pdbDialog = waitForWindow("pdb or pdb.xml"); - pressButtonByText(pdbDialog, "PDB"); - - Window peSpecifiedPathDialog = waitForWindow("PE-specified PDB Path"); - pressButtonByText(peSpecifiedPathDialog, "Yes"); - - Window saveLocationDialog = waitForWindow("Select Location to Save Retrieved File"); - pressButtonByText(saveLocationDialog, "OK"); - - Window urlDialog = waitForWindow("Symbol Server URL"); - urlDialog.setSize(new Dimension(850, 135)); - - captureWindow(urlDialog); - - pressButtonByText(urlDialog, "Cancel"); + public void testLoadPdb_Initial_Screenshot() throws IOException { + LoadPdbDialog loadPdbDialog = new LoadPdbDialog(program); + showDialogWithoutBlocking(tool, loadPdbDialog); + captureDialog(loadPdbDialog); + pressButtonByText(loadPdbDialog, "Cancel"); } @Test - public void testKnownSymbolServerURLsDialog() throws Exception { - - List urlChoices = new ArrayList<>(); - urlChoices.add(new URLChoice("Internet", "https://msdl.microsoft.com/download/symbols")); - urlChoices.add(new URLChoice("My Network", "https://my_symbol_server.my.org")); - - final ObjectChooserDialog urlDialog = new ObjectChooserDialog<>("Choose a URL", - URLChoice.class, urlChoices, "getNetwork", "getUrl"); + public void testSymbolServerConfig_AddButtonMenu() throws IOException { + File localSymbolStore1Root = new File(temporaryDir, "symbols"); + LocalSymbolStore.create(localSymbolStore1Root, 1); + LocalSymbolStore localSymbolStore1 = + new LocalSymbolStoreWithFakePath(localSymbolStore1Root, "/home/user/symbols"); + SymbolServerService symbolServerService = + new SymbolServerService(localSymbolStore1, List.of()); + PdbPlugin.saveSymbolServerServiceConfig(symbolServerService); + LoadPdbDialog choosePdbDialog = new LoadPdbDialog(program); + showDialogWithoutBlocking(tool, choosePdbDialog); + waitForSwing(); + pressButtonByText(choosePdbDialog, "Advanced >>"); runSwing(() -> { - // Do nothing + choosePdbDialog.pushAddLocationBution(); }); - showDialogWithoutBlocking(tool, urlDialog); - captureDialog(); - - pressButtonByText(urlDialog, "Cancel"); + waitForSwing(); + captureMenu(); } @Test - public void testSuccessDialog() throws Exception { + public void testLoadPdb_Advanced_NeedsConfig() throws IOException { + PdbPlugin.saveSymbolServerServiceConfig(null); + LoadPdbDialog choosePdbDialog = new LoadPdbDialog(program); + showDialogWithoutBlocking(tool, choosePdbDialog); + waitForSwing(); + pressButtonByText(choosePdbDialog, "Advanced >>"); + waitForSwing(); + captureDialog(LoadPdbDialog.class); + pressButtonByText(choosePdbDialog, "Cancel"); + } - // Can't really get success message without actually downloading a file. - // So, fake out the message by showing the same sort of dialog the user would see. - Msg.showInfo(getClass(), null, "File Retrieved", - "Downloaded and saved file 'example.pdb' to \n" + - "C:\\Symbols\\example.pdb\\1123A456B7889012C3DDFA4556789B011"); + @Test + public void testLoadPdb_Advanced_Screenshot() throws IOException { + // Show the advanced side of the LoadPdbDialog, with + // some faked search locations and search results so we + // can have pretty paths + File localSymbolStore1Root = new File(temporaryDir, "symbols"); + LocalSymbolStore.create(localSymbolStore1Root, 1); + LocalSymbolStore localSymbolStore1 = + new LocalSymbolStoreWithFakePath(localSymbolStore1Root, "/home/user/symbols"); + SameDirSymbolStoreWithFakePath sameDirSymbolStoreWithFakePath = + new SameDirSymbolStoreWithFakePath(temporaryDir, "/home/user/examples"); + List symbolServers = List.of(sameDirSymbolStoreWithFakePath, + new HttpSymbolServer(URI.create("https://msdl.microsoft.com/download/symbols/"))); + SymbolServerService symbolServerService = + new SymbolServerService(localSymbolStore1, symbolServers); + PdbPlugin.saveSymbolServerServiceConfig(symbolServerService); - Window successDialog = waitForWindow("File Retrieved"); - captureWindow(successDialog); + LoadPdbDialog loadPdbDialog = new LoadPdbDialog(program); + showDialogWithoutBlocking(tool, loadPdbDialog); + waitForSwing(); + pressButtonByText(loadPdbDialog, "Advanced >>"); + List symbolFileLocations = List.of( + new SymbolFileLocation("HelloWorld.pdb/" + GUID1_STR + "1/HelloWorld.pdb", + localSymbolStore1, SymbolFileInfo.fromValues("HelloWorld.pdb", GUID1_STR, 1)), + new SymbolFileLocation("HelloWorld.pdb/" + GUID1_STR + "2/HelloWorld.pdb", + localSymbolStore1, SymbolFileInfo.fromValues("HelloWorld.pdb", GUID1_STR, 2)), + new SymbolFileLocation("HelloWorld.pdb", sameDirSymbolStoreWithFakePath, + SymbolFileInfo.fromValues("HelloWorld.pdb", GUID1_STR, 1)), + new SymbolFileLocation("HelloWorld_ver2.pdb", sameDirSymbolStoreWithFakePath, + SymbolFileInfo.fromValues("HelloWorld.pdb", GUID1_STR, 2))); + runSwing(() -> { + loadPdbDialog + .setSearchOptions(FindOption.of(FindOption.ALLOW_REMOTE, FindOption.ANY_AGE)); + loadPdbDialog.setSymbolServers(symbolServers); + loadPdbDialog.setSymbolStorageDirectoryTextOnly("/home/user/symbols"); + loadPdbDialog.setSearchResults(symbolFileLocations); + loadPdbDialog.selectRowByLocation(symbolFileLocations.get(0)); + }); + waitForSwing(); + captureDialog(LoadPdbDialog.class); + pressButtonByText(loadPdbDialog, "Cancel"); + } - pressButtonByText(successDialog, "OK"); + private static class LocalSymbolStoreWithFakePath extends LocalSymbolStore { + private String fakeRootDirPath; + + public LocalSymbolStoreWithFakePath(File rootDir, String fakeRootDirPath) { + super(rootDir); + this.fakeRootDirPath = fakeRootDirPath; + } + + @Override + public String getDescriptiveName() { + return fakeRootDirPath; + } + + @Override + public String getFileLocation(String filename) { + return FilenameUtils.concat(fakeRootDirPath, filename); + } + } + + private static class SameDirSymbolStoreWithFakePath extends SameDirSymbolStore { + private String fakeRootDirPath; + + public SameDirSymbolStoreWithFakePath(File rootDir, String fakeRootDirPath) { + super(rootDir); + this.fakeRootDirPath = fakeRootDirPath; + } + + @Override + public String getDescriptiveName() { + return String.format(PROGRAMS_IMPORT_LOCATION_DESCRIPTION_STR + " - %s", + fakeRootDirPath); + } + + @Override + public String getFileLocation(String filename) { + return FilenameUtils.concat(fakeRootDirPath, filename); + } } } diff --git a/Ghidra/Test/IntegrationTest/src/test/java/pdb/symbolserver/SymbolServerService2Test.java b/Ghidra/Test/IntegrationTest/src/test/java/pdb/symbolserver/SymbolServerService2Test.java new file mode 100644 index 0000000000..947d47b932 --- /dev/null +++ b/Ghidra/Test/IntegrationTest/src/test/java/pdb/symbolserver/SymbolServerService2Test.java @@ -0,0 +1,138 @@ +/* ### + * 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.symbolserver; + +import static org.junit.Assert.assertEquals; + +import java.util.List; + +import java.io.File; +import java.io.IOException; + +import org.apache.commons.io.FilenameUtils; +import org.junit.Before; +import org.junit.Test; + +import com.google.common.io.BaseEncoding; + +import ghidra.test.AbstractGhidraHeadedIntegrationTest; +import ghidra.util.exception.CancelledException; +import ghidra.util.task.TaskMonitor; +import utilities.util.FileUtilities; + +/** + * Tests for Pdb SymbolServer stuff that need to be in the Integration module because they depend + * on FileFormat's file system stuff to decompress .cab files + */ +public class SymbolServerService2Test extends AbstractGhidraHeadedIntegrationTest { + private File temporaryDir; + private File localSymbolStore1Root; + private LocalSymbolStore localSymbolStore1; + + // Bytes for a very small .cab file that contains a singleton file named 'test.pdb' with + // contents of "test" + byte[] smallCabFileBytes = BaseEncoding.base16() + .decode(("4d5343460000000055000000000000002c000000000000000301010001" + + "00000000000000450000000100010004000000000000000000a248bc5c2000746573742e7064620" + + "066652e4908000400434b2b492d2e0100").toUpperCase()); + + private File mkFile(File file, byte[] bytes) throws IOException { + FileUtilities.checkedMkdirs(file.getParentFile()); + FileUtilities.writeBytes(file, bytes); + return file; + } + + @Before + public void setup() throws IOException { + temporaryDir = createTempDirectory("symbolservers"); + localSymbolStore1Root = new File(temporaryDir, "symbols1"); + LocalSymbolStore.create(localSymbolStore1Root, 1); + + localSymbolStore1 = new LocalSymbolStore(localSymbolStore1Root); + } + + @Test + public void testLocalCab() throws IOException, CancelledException { + mkFile(new File(localSymbolStore1Root, "test.pdb/112233441/test.pd_"), smallCabFileBytes); + + SymbolServerService symbolServerService = + new SymbolServerService(localSymbolStore1, List.of()); + List results = + symbolServerService.find(SymbolFileInfo.fromValues("test.pdb", "11223344", 1), + FindOption.NO_OPTIONS, TaskMonitor.DUMMY); + + assertEquals(1, results.size()); + assertEquals("test.pd_", FilenameUtils.getName(results.get(0).getPath())); + + File pdbFile = symbolServerService.getSymbolFile(results.get(0), TaskMonitor.DUMMY); + assertEquals("test\n" /* extra \n because FileUtilities.getText() adds it */, + FileUtilities.getText(pdbFile)); + + // search again and we should only find the now decompressed pdb file + List results2 = + symbolServerService.find(SymbolFileInfo.fromValues("test.pdb", "11223344", 1), + FindOption.NO_OPTIONS, TaskMonitor.DUMMY); + + assertEquals(1, results2.size()); + assertEquals("test.pdb", FilenameUtils.getName(results2.get(0).getPath())); + } + + @Test + public void testRemoteCab() throws IOException, CancelledException { + + SymbolServerService symbolServerService = + new SymbolServerService(localSymbolStore1, + List.of(new DummySymbolServer(smallCabFileBytes, true))); + + List results = + symbolServerService.find(SymbolFileInfo.fromValues("test.pdb", "11223344", 1), + FindOption.of(FindOption.ALLOW_REMOTE), TaskMonitor.DUMMY); + + assertEquals(1, results.size()); + System.out.println(results.get(0).getLocationStr()); + + File pdbFile = symbolServerService.getSymbolFile(results.get(0), TaskMonitor.DUMMY); + assertEquals("test\n" /* extra \n because FileUtilities.getText() adds it */, + FileUtilities.getText(pdbFile)); + } + + @Test + public void testRemoteCabAlreadyExistLocal() throws IOException, CancelledException { + + SymbolServerService symbolServerService = + new SymbolServerService(localSymbolStore1, + List.of(new DummySymbolServer(smallCabFileBytes, true))); + + List results = + symbolServerService.find(SymbolFileInfo.fromValues("test.pdb", "11223344", 1), + FindOption.of(FindOption.ALLOW_REMOTE), TaskMonitor.DUMMY); + + assertEquals(1, results.size()); + System.out.println(results.get(0).getLocationStr()); + + // cheese the file into the local symbol store after the remote file has been found + // but before it has been downloaded + mkFile(new File(localSymbolStore1Root, "test.pdb/112233441/test.pdb"), + "nottest".getBytes()); + + // normally this would download the remote file and decompress it + File pdbFile = symbolServerService.getSymbolFile(results.get(0), TaskMonitor.DUMMY); + + // ensure that the original file wasn't overwritten by the new file + assertEquals("nottest\n" /* extra \n because FileUtilities.getText() adds it */, + FileUtilities.getText(pdbFile)); + } +} From 3eb23e0c72e9913256d72290e53588fe22e7a04a Mon Sep 17 00:00:00 2001 From: dev747368 <48332326+dev747368@users.noreply.github.com> Date: Wed, 12 May 2021 19:53:53 -0400 Subject: [PATCH 2/9] save --- .../Features/PDB/src/main/help/help/topics/Pdb/LoadPDBNew.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/LoadPDBNew.html b/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/LoadPDBNew.html index eca6a8b6de..9da6f81bd7 100644 --- a/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/LoadPDBNew.html +++ b/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/LoadPDBNew.html @@ -198,7 +198,7 @@
    • 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 - All, Data Types Only, Public Symbols Only.
    • +
    • Control - Process All, Data Types Only, Public Symbols Only.
    From 084d2f17357e09812d6f0b3a81b2e3ff0266efe4 Mon Sep 17 00:00:00 2001 From: dev747368 <48332326+dev747368@users.noreply.github.com> Date: Thu, 13 May 2021 16:14:32 -0400 Subject: [PATCH 3/9] GP-42 fix result dialog, age text field ui --- .../PDB/src/main/java/pdb/LoadPdbTask.java | 43 ++++++---------- .../PDB/src/main/java/pdb/PdbPlugin.java | 38 ++++++++++++-- .../pdb/symbolserver/ui/LoadPdbDialog.java | 50 +++++++++++++------ .../widgets/textfield/HexOrDecimalInput.java | 21 +++++++- 4 files changed, 103 insertions(+), 49 deletions(-) diff --git a/Ghidra/Features/PDB/src/main/java/pdb/LoadPdbTask.java b/Ghidra/Features/PDB/src/main/java/pdb/LoadPdbTask.java index 4940348759..09ed60a6c9 100644 --- a/Ghidra/Features/PDB/src/main/java/pdb/LoadPdbTask.java +++ b/Ghidra/Features/PDB/src/main/java/pdb/LoadPdbTask.java @@ -19,8 +19,6 @@ import java.io.File; import java.io.IOException; import java.lang.reflect.InvocationTargetException; -import docking.DockingWindowManager; -import docking.widgets.dialogs.MultiLineMessageDialog; import ghidra.app.plugin.core.analysis.*; import ghidra.app.plugin.core.datamgr.archive.DuplicateIdException; import ghidra.app.services.DataTypeManagerService; @@ -32,7 +30,6 @@ 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.*; @@ -43,6 +40,8 @@ class LoadPdbTask extends Task { private final boolean useMsDiaParser; private final PdbApplicatorControl control; // PDB Universal Parser only private boolean debugLogging; + private String resultMessages; + private Exception resultException; LoadPdbTask(Program program, File pdbFile, boolean useMsDiaParser, PdbApplicatorControl control, boolean debugLogging, DataTypeManagerService service) { @@ -98,39 +97,27 @@ 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, true, monitor); diff --git a/Ghidra/Features/PDB/src/main/java/pdb/PdbPlugin.java b/Ghidra/Features/PDB/src/main/java/pdb/PdbPlugin.java index c3e31a9e13..b3b65364c0 100644 --- a/Ghidra/Features/PDB/src/main/java/pdb/PdbPlugin.java +++ b/Ghidra/Features/PDB/src/main/java/pdb/PdbPlugin.java @@ -17,14 +17,17 @@ 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.DockingWindowManager; import docking.action.builder.ActionBuilder; import docking.tool.ToolConstants; import docking.widgets.OptionDialog; +import docking.widgets.dialogs.MultiLineMessageDialog; import ghidra.app.CorePluginPackage; import ghidra.app.context.ProgramActionContext; import ghidra.app.plugin.PluginCategoryNames; @@ -118,12 +121,14 @@ public class PdbPlugin extends Plugin { } } + File pdbFile = null; try { LoadPdbResults loadPdbResults = LoadPdbDialog.choosePdbForProgram(program); if (loadPdbResults == null) { tool.setStatusInfo("Loading PDB was cancelled."); return; } + pdbFile = loadPdbResults.pdbFile; tool.setStatusInfo(""); @@ -138,16 +143,39 @@ 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 loadPdbTask = new LoadPdbTask(program, loadPdbResults.pdbFile, - loadPdbResults.useMsDiaParser, loadPdbResults.control, - loadPdbResults.debugLogging, dataTypeManagerService); + LoadPdbTask loadPdbTask = + new LoadPdbTask(program, pdbFile, loadPdbResults.useMsDiaParser, + loadPdbResults.control, loadPdbResults.debugLogging, dataTypeManagerService); TaskBuilder.withTask(loadPdbTask) .setStatusTextAlignment(SwingConstants.LEADING) .setLaunchDelay(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); } } diff --git a/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/LoadPdbDialog.java b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/LoadPdbDialog.java index 00087d802c..f0c01be21a 100644 --- a/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/LoadPdbDialog.java +++ b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/LoadPdbDialog.java @@ -37,8 +37,8 @@ import docking.widgets.filechooser.GhidraFileChooser; import docking.widgets.filechooser.GhidraFileChooserMode; import docking.widgets.label.GIconLabel; import docking.widgets.label.GLabel; +import docking.widgets.textfield.HexOrDecimalInput; import docking.widgets.textfield.HintTextField; -import docking.widgets.textfield.IntegerTextField; import ghidra.app.util.bin.format.pdb.PdbParser; import ghidra.app.util.pdb.pdbapplicator.PdbApplicatorControl; import ghidra.framework.preferences.Preferences; @@ -92,7 +92,7 @@ public class LoadPdbDialog extends DialogComponentProvider { LoadPdbResults results = new LoadPdbResults(); results.pdbFile = pdbFile; results.control = - (PdbApplicatorControl) choosePdbDlg.restrictionsCombo.getSelectedItem(); + (PdbApplicatorControl) choosePdbDlg.applicatorControlCombo.getSelectedItem(); results.useMsDiaParser = choosePdbDlg.msdiaParserButton.isSelected(); results.debugLogging = choosePdbDlg.debugLoggingCheckbox.isSelected(); return results; @@ -119,7 +119,7 @@ public class LoadPdbDialog extends DialogComponentProvider { private GCheckBox overridePdbPathCheckBox; private JTextField pdbUniqueIdTextField; private GCheckBox overridePdbUniqueIdCheckBox; - private IntegerTextField pdbAgeTextField; + private HexOrDecimalInput pdbAgeTextField; private GCheckBox overridePdbAgeCheckBox; private HintTextField pdbLocationTextField; private GIconLabel exactMatchIconLabel; @@ -138,7 +138,7 @@ public class LoadPdbDialog extends DialogComponentProvider { private JPanel parserOptionsPanel; private JRadioButton universalParserButton; private JRadioButton msdiaParserButton; - private GComboBox restrictionsCombo; + private GComboBox applicatorControlCombo; private GCheckBox debugLoggingCheckbox; /** @@ -327,7 +327,8 @@ public class LoadPdbDialog extends DialogComponentProvider { if (symbolServerService == null || !symbolServerService.isValid()) { return; } - if (pdbAgeTextField.getText().isBlank()) { + if (pdbAgeTextField.getText().isBlank() || + pdbAgeTextField.getValue() > NumericUtilities.MAX_UNSIGNED_INT32_AS_LONG) { Msg.showWarn(this, null, "Bad PDB Age", "Invalid PDB Age value"); return; } @@ -390,7 +391,8 @@ public class LoadPdbDialog extends DialogComponentProvider { } private void buildSymbolFilePanel() { - symbolFilePanel = new SymbolFilePanel(this::searchForPdbs); // panel will be added in layoutAdvanced() + // panel will be added in layoutAdvanced() + symbolFilePanel = new SymbolFilePanel(this::searchForPdbs); symbolFilePanel.getTable() .getSelectionModel() @@ -464,9 +466,8 @@ public class LoadPdbDialog extends DialogComponentProvider { new HelpLocation(PdbPlugin.PDB_PLUGIN_HELP_TOPIC, SymbolFilePanel.SEARCH_OPTIONS_HELP_ANCHOR)); - pdbAgeTextField = new IntegerTextField(8); - pdbAgeTextField.setAllowNegativeValues(false); - pdbAgeTextField.setShowNumberMode(true); + pdbAgeTextField = new BetterNonEditableHexTextField(8); + pdbAgeTextField.setAllowNegative(false); pdbAgeTextField.setHexMode(); pdbAgeTextField.setEditable(false); @@ -502,7 +503,7 @@ public class LoadPdbDialog extends DialogComponentProvider { programPdbPanel.add( join(null, new GLabel("PDB Age:", SwingConstants.RIGHT), overridePdbAgeCheckBox)); - programPdbPanel.add(join(pdbAgeTextField.getComponent(), new JPanel(), null)); + programPdbPanel.add(join(pdbAgeTextField, new JPanel(), null)); return programPdbPanel; } @@ -547,7 +548,7 @@ public class LoadPdbDialog extends DialogComponentProvider { if (universalParserButton.isSelected() && !universalParserButton.isEnabled()) { universalParserButton.setSelected(false); } - restrictionsCombo.setEnabled(universalParserButton.isSelected()); + applicatorControlCombo.setEnabled(universalParserButton.isSelected()); debugLoggingCheckbox.setEnabled(universalParserButton.isSelected()); } @@ -573,8 +574,8 @@ public class LoadPdbDialog extends DialogComponentProvider { radioButtons.add(universalParserButton); radioButtons.add(msdiaParserButton); - restrictionsCombo = new GComboBox<>(PdbApplicatorControl.values()); - restrictionsCombo.setSelectedItem(PdbApplicatorControl.ALL); + applicatorControlCombo = new GComboBox<>(PdbApplicatorControl.values()); + applicatorControlCombo.setSelectedItem(PdbApplicatorControl.ALL); debugLoggingCheckbox = new GCheckBox(); debugLoggingCheckbox.setToolTipText( @@ -591,7 +592,7 @@ public class LoadPdbDialog extends DialogComponentProvider { parserOptionsPanel.add(radioButtons); parserOptionsPanel.add(new GLabel("Control:")); - parserOptionsPanel.add(restrictionsCombo); + parserOptionsPanel.add(applicatorControlCombo); parserOptionsPanel.add(new GLabel("[Dev] PDB Reader/Applicator Debug Logging:")); parserOptionsPanel.add(debugLoggingCheckbox); @@ -928,7 +929,7 @@ public class LoadPdbDialog extends DialogComponentProvider { @Override public Color getBackground() { Container parent = getParent(); - if (parent != null && isEditable() == false) { + if (parent != null && !isEditable()) { Color bg = parent.getBackground(); // mint a new Color object to avoid it being // ignored because the parent handed us a DerivedColor @@ -939,4 +940,23 @@ public class LoadPdbDialog extends DialogComponentProvider { } } + static class BetterNonEditableHexTextField extends HexOrDecimalInput { + + BetterNonEditableHexTextField(int columns) { + super(columns); + } + + @Override + public Color getBackground() { + Container parent = getParent(); + if (parent != null && !isEditable()) { + Color bg = parent.getBackground(); + // mint a new Color object to avoid it being + // ignored because the parent handed us a DerivedColor + // instance + return new Color(bg.getRGB()); + } + return super.getBackground(); + } + } } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/textfield/HexOrDecimalInput.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/textfield/HexOrDecimalInput.java index 8d9700c7e9..9614fa62ad 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/textfield/HexOrDecimalInput.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/textfield/HexOrDecimalInput.java @@ -33,8 +33,16 @@ public class HexOrDecimalInput extends JTextField { this(null); } + public HexOrDecimalInput(int columns) { + super(columns); + init(null); + } + public HexOrDecimalInput(Long initialValue) { - super(); + init(initialValue); + } + + private void init(Long initialValue) { currentValue = initialValue; setDocument(new MyDocument()); updateText(); @@ -55,6 +63,17 @@ public class HexOrDecimalInput extends JTextField { return currentValue; } + public int getIntValue() { + if (currentValue == null) { + return 0; + } + return currentValue.intValue(); + } + + public void setValue(int newValue) { + setValue((long) newValue); + } + public void setValue(Long newValue) { if (!allowsNegative && newValue != null && newValue.longValue() < 0) { currentValue = null; From 0fe06ffb2f383261de3853de09334f15b81c22c7 Mon Sep 17 00:00:00 2001 From: dev747368 <48332326+dev747368@users.noreply.github.com> Date: Thu, 13 May 2021 18:27:50 -0400 Subject: [PATCH 4/9] GP-42 update screen shots --- .../images/LoadPdb_Advanced_NeedsConfig.png | Bin 46293 -> 46023 bytes .../images/LoadPdb_Advanced_Screenshot.png | Bin 81637 -> 81388 bytes .../Pdb/images/LoadPdb_Initial_Screenshot.png | Bin 31547 -> 31278 bytes 3 files changed, 0 insertions(+), 0 deletions(-) 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 index 51a5afaa6c7ffb0c569e0d403e3a0aa8817ac59d..5815e5b51ac790c9357358c3c9e4bd8a5af44ab5 100644 GIT binary patch literal 46023 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 MwDR-fXU0MQ1}Xn$XaE2J 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 index 38da23a6abdf255acc278016d8d589cae69d14e1..2768ba23e41b4da202cb5af139426e99a54b1f4a 100644 GIT binary patch delta 57777 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 xhtkoTYWMdV4qo0|jx^ppo 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 Ussu0Yd$7Ss-%PJU=Rwqe0H;mxZ~y=R 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 index 5a980f17a341fa87c774407bc53bbb1a0ae60369..b9b5b155b0c6fa5c7831e1ffc6f51ac3135b7027 100644 GIT binary patch literal 31278 zcmbTebzD^6*EWoW3Ifs!0@5WQQqo;RcY`1`q;yHBgn)p+P(u$4F_d%+p`{ z@4@fy`-}UzpZk8_mw)`sKKq=r*FI~nxYo4{R#ue8!XUvwLqo%om61?IL%S=5hIR+_ z=q@l4m==A2hURf3Dh=Io}Yr=+W=i!}{3+#0&XfLIjBsT5R1NrwF&hnn&cMuE@qC?P05`XTVejV*P(mTNn zB}t&9ruXsKoUhxp;3OxB`62ejk^e0;90|Sp=mbQ5873IqO+A4Krte--0B_^=V^u8pG;SuJ= z#~F4P#-g(?^tqLvVfAmF&2wVyJ*)-l-$ghcCT={$P9@gNA)YvXkv@G%Az2+kfO0M%9 z>-qgc?aD7Kh=mwDmIr&rhT}rG?Mg0^`?QY)1|;Xmi&?cmdW>lL zz$X!{6Ov19P=6oHUBu@*jmPsVNbwN_{q#{rHVMWr{$q+0OdqisB$#)9sW|kIC52_Y z`ldO|Pr8gXoeCj>6qiyo4Q>5)ae62zd=B05+UdW9lLGP_uYQqMJl$WFQ7Vhid+uY2r&JAdZlhY48 z-sgKEWR6FAD6H3+|9;qg6TanT+-0EPx+~pKEj2NtU(>Yp-GYSgd`@jT^V7shhF0MW zd|TgL#T}F}HhtZKtNWz?cl(}Ky?~(epfn$L=Jb$5$4ZQv1bw;j#IHyS`SecFjZQ(M z6g+I%i5Qe7LTcg%anYz)%$j}Rm5<1PB9fDyx>dfvAEQjUK1D!o>j!01n{C2tLAPsF zlL`OlUxFgkh{>Rwa-*>i|JpHV^on;3JL^^JR8YDKxJ)6G(zQ;kpm6H3u*a{H%_V-E zluXtB9&bdNl0k9go~E+8r8-7H`d2Ge=b#t!&^Gmimh$KsMFe0x9-&8?FG_~V%p3|06fE1RYwOth<`AuZR~eb~q`8rmeF(&V9EP)nE=hx;-PKAVwq1Su z-BW9VaUh){U@M1FLH(&^Rt>|)}mnlFrFB z@}pmad(l&F6atcGWpo)yvK-5$L&J8{8yz1nj^DYtFe^-8G0%-IztZg-PTx6N$I##Q zZ>~~^2h{&ptB!7AF|tPKJJ+3p$3o9~53>xvI2vDhHVH=+?2tZ_oO>$<4!{wHb{}Vj zcfzvLm*^h9Yrcrq*(+srCgf)$(gbHZxyBvw71)eU<#G^%`JW>579cCEhHZ9i=Qj8g z-=z%-Ar-1bQ%zY744PV-JP}txy+d$9%izr3fvT~p8|tBlt~hNLXB{UJ{zyVe! zeZ@SyWBUH#1AXqGdH${Kvwcq6qM4FZEJ3T!)CrbI(dxG!*}o!Pnm z6YgUnH!2yt4w$pc3M^~vI=~8R<;KtBX;pEJdL>FTe}eK=NKX9&CeN3yT5XJZ^saM` zI2B+2DG=Q<+r-ah<%78$CB%E?zWLeu)%ePHW377}qnBJ1ik(YLw&W1>^;>xwciZZH zMOA#p>E%XYY}m=^LwFan{I&yW=F}fQ}?rO3a870-Y6LzQn!=wHVMceUHSD5bb`S8 zc{{2Iw*dMc@7UJxq;N5DIBf~^`LD>D|ZUp-dek#vKvQZ>j05jbYNed_R7Dy%-JjW>;!cU^@T&BK>1e z^YMq>-vM8(;E~`_E2p^pj45zxj-jb9KD@XnNIw2e+%V@R>2p|7P?aNu+u>Zm*uKbN zL4^g~_Q%kuo^8Gv&(7P-9+eqEeAiPt(0k&5`z!^cDAA_2TfMnJ`G?uCSinw3oC#OCug&r+fI3pmRA@@q`nEdI(9m8eRg#se`L*7qxUKH>&l|i;Pm;s zLH{N-i_(cQQRF(Th;HHNsj_cX<+s5fY$>@$v{l;_zr17osHpRZdZV?lUkLS1>zc+$ zB_SJY$;E!1jPeLwlNtzcYy;|2tH%0Uz(+h|zvp&(+EwG~n>rMSUEq z(A8T>{g!P*Ep?MQ&Ho%r$~eeAhcoJFfsS{~PWiwSwJTdM?1384D2F|8B#=zLR&^J% z?WA`iR`(;da`cXrF#W~nfzOo9Y1=e?c&3Hhkt*^d;@w(WSGy31p{<Tu_`YL6xxF!e%S+G3$B%Q}itbT%tQ@B69F;5T?_@# z)DKzu%9)>fU&tYb-%n+jh^j2y1tPk`@1D$I!Q&xO-))=5zu!;NoQ}q@<`#E;QlFJW z!zQ>m3B9(ZjnY(fDqO}SAv%BcA=4JWGJ1{ge1+J7@#te}?(y8?>a-kD*^62VvWo6a z>qaUdN~AE91D7RweWWQ}`Ef3)ain#T ztp~;jD^3=R$RUH)yB|quJ?fPjqet25Sr+0zy-M$0ZDOZ~h8kLfpFy?VC@T4X_^mH) zlc`3tMIMfmWZ1^~tEsf95hWK_9ZkIrqmA<6kM_tbt9kOp>2~$<9*H1p;kD+bT4H&y z>?$7pz2Hxscr%-u{+Mc8v{A!XmC_naAy>j?CWQx$iDNqbK~v*z3fw_?Yndd~`XpWs ziuA$F?xX4V>7)r~3}PQW~nZ!G!4 zJQuclE=IqsZAU-gd{o32*|RN0%3=lhp6Xq+LqG1Aw1$Y`}OUy z7Wf5xi2F$~{v9;G<=@tfVX${cbx5@3X)@9azM{u!8nK4Zmga_f{sjo~x|1K3a#eCs z!d#m0K)H8IDgLrUj-#9ABYemi7Q7?>p6P1W-9f|U<4(;Uu5GrBZ^YR1D;G))%+qhY z%Goi-pO^X2*_5G;)_0@(t%ir7DCl6#Macwh&AC?c*M*u7SqwJ!N+ql+e;Vm1h=a$? zWo?)Y7a-LfS#scVEb&7L;C%3^+QS*6BXh&vHw2R+W`6Wlg-X)=s2rVCTx4|1h`FQj zW=?-zD=QE?us8=pZRJdT?Sp==WZSNZ9DB9nvFkDfUwtW8sVj4w`sTzJ z(LBBZ*Px~Jqvy3EV1$NlTg(ZUTxSP%8r{?4?yoc2hm{&1wZ+aJyf!N~7;Lz;>eL@PrkVHCl`e%dbvMH#Sce%Gp=KAKr6% zz_SNj^&3eq1cfTe3wzD&^Q_eNczGDrI(KjEPpqH_DS+$epI-lNpv)x4dkX$)Dgnat zL@0uy<9xlwE?Q}KHtK!u+!0Mb5vK?Y6vV*t%@2Est5PIA+C&fBd3ewGdl9Bs_Tbaw zulFTLsGdhg$;fAB12z$QHhGBo1UuB703Sc@^c5wG5ThU^??ORzeA3um8Q{dxNj{5% z(l)-l3HM5TeGeYj( zCei+)*I4awQGPPwG}Nki!NG^k$a^}Ws9IJdB=O~}tc{qJ6uFC*HjE)w6Y@JH4!8#` z>``vezg9>er7j3@Z91`YH+5D85xIzZ%M!-y@7tT1u>o1SpH$#~o!TZGFCU+xA!70> zrs18}ZC($(k9ykv6(V6mB^a5xo?iAd?MKxqWV6_CMu~>e z4$K0=#13|kjgO5s(xvk10kS_}3R;@OI|T_46?X#7#t(@qqeyUDA0 z>ky$tLv_+)PI&lh!89!_1qxssD*UK7j+pRU-|0t}Ed>rxMd8ba=waLf43_MFJHvl~ z4qWWSte(IHcPxKgLXfq|<)vMav=pE-Im8440e^-X8yhi02!U6vKX{;IED?Z$#;>kE zlk@&_AkHjhI?3@4@Sy(v0q9Tz^S! z&P12QsI?-~s}Sux@uIybD{C9X9C17@FgDcQkr|d?z%#;_$ky6X-c@S}ETfj|& z>FU;2&brIU2MP!?pIt9aMaQM|;--4^%NwVnq3uFzsX^~u7hJKvS8XdH9vRq-xJ++{ z*$MJPby#)_a+o|l&DJgiH@muR4O(9zzpLlx>pycb8TM0*CED`(b~Jh%{AFQWxd~y$O$l2t%mG^=!8-e@4A08)fxr)0P+frz-0iW&;il=y$->^lj7ZfA3IkBI z*?s#&zBD<04!NK^6g#26`&WNRfvNB{<}V!d@GCRMYWJ)Y0O7&{8zXHRrcyfimz73eedA0*=`?MNAut`_mV=S&JCB( z-Wj~tuF$u)>>}_hC}6%HWN|>L_c;m^WKtXF#uN1c(hAlK%cl`;b}C6e7=LQM*E|U3 zw{jDy5#ZTgbZ1uMhyzPg5D_g(+EbWwzjJ#t(d?G)l9(X$`A%M6EmOcl>1R)-<76i) z5iLsE%kioQhTCAU%^( z3jV^@w$08qHNFaTRpLQmSnuX~ONi0F(-Pd7*-`QZkCd9ss1&#VgyuMUxn!>apCd$qhtw z0Z5p0ylYBJ0t~}n5}f$$+~e}=^pEO4+>7jU?b&-goa}XMGto&6F>tmjc#P(r-R}!` zh@%5Bth!AcR(Xzix#p!zq^ieB*=|f&1y;^?*kk~K41#0lRrv}-*g%CC`8=;%PHBOM z^*yspa|1CKeF2!d3#&X2$+a0PGI@?y>W$>$3%dVla7+0M;vSD z79_lAmTn{)Zy;ROS+%->aYWuJtk5h4CG+A&W z{ECW`BfCshF}7%euawUvYGI+#abBbpZ`krekYG1`@zoNdYP^V4f7lO||t?Us_ z+i~Z;4lZ=%38n&1SiA)|%dX9lb0Xx0q8A;ob?S^EpNZb;`o4ApT^y(C8yiIpYT~2q z+BM;%fTn5TjIiOV_+uj{dLg6pF1IVMEY^)CX2Ou)go>M`WvJ1-S_^Pe#av>Iwt~P5 zdKT6WjM0Pi@=S72-bk3Vq-0OZFLdsT3UM6MVqUjc*EEBHgb_rlWZfrJN0cj82}xBW z{TnsI@5vuePD84V1pnx&#z)eVqldA!7i>bfByqHCzd_j5wbBP+(%U*;%{y?AvNg6J z8*M@(G6yZfzibUMuMwg)4)`Y&>yiq~=_>Wmej>lVbbnJT2g=J~`gQ==h?dr_u8N6O zS_}LTNWz0iY2odeepF-qg?2>Pz@|QT@r~WHBHQU94Qiw|G=9R3qI%WoBn2zG4AJM( zgBQHOw-g$Vacm$R{A;xcGmp9U6UvgG+H|BVU9qBz;7RJn^Q0?P39v35NJZtkx%2(L zNSiBNfx=czjp7vS+5q})S(1Yngik7)bz*aIT&fXT3Y(Qw9L8@jCJJbTLZ^T)F6sYE zEnOPq#Z2H8&Q9W#Df4~C@2lah`>DajN>SF;7#$(Sd62b4upE$bni<;8(5vnffG&B6 z0A}=sS?>Cy5TH~;LxGaK7@djmI(oaBn2gIUtp!R?4PeHq6sNlwSmE2M2H^A3Nc(3W z!@1aDS`PrHWGC^39x~kg@9)AF*P`X+1ulxz{ej|FIsVs#^#8=~fh<-R-)Rm;47_7R zYYe&MB0H=TVZZQ-cS zi*g#ExjGhD(6au(S%krXcx=%z#M_lqV;fFThR9L`g^u;lSZ?O(57`~+S_CiK<+ZO6 zG-{fBrFd`~E(@{ffjdNu{^gIs3d^Cjhl}6k_}g6AD^k~E5GoCbve!rJD8mBJ9e1i> z3#6-Nk2N_chSxKnQ?ifAD^$r_d&a^Y+jVu@q-`wb$yh3#B~|#T%5Chwkn!+eei={d zCP=S_lrP`sXHn53gOTE)d}b4G#i$|r4_0NyxEg>SEI7z+pMkHLuass`P%E*1#ev5%P%3y zZt!3iR#+Jo6r74sxpPF zJD=#(F@pkg4+?nT(LWKCfHj)j#mT5NEjS9> zQBBPe@KF3#i7c%{Mts}%Cf2P?Sj+G@Bw0sr?)5wXsR`(eOy%E@G@M?dEOJ zJ6P|?{q{11A8g;FreRHl@gBv57PyL6pm$1MBrR*ezY4$YvpJ1xrvj<%19cR`36ng? zoe|Gs;l#7~L5VLZdg`SKWxnxy`5RrXi{{xCPDObsD;jVL65HkNyqpHqX|M*=edR7( zPNQ;(v;_)bnMtVj;>^X28r8+gCS#=T| z3B(kT&^C0=vWO!D{VLL+s>~i@t_SVW&d!7nZpxJ3#*+TDN7##%vK{w!z zax$*z-(_v(R1IzX(TEm*srp`Tzo z=w_ksvNR#LS;nB?QW-rrXWmwUX{WJjnFkj_L;IS(0*V%zoHzO1Dq*OyipT$@eZ}xm zB6Ex;Tt-Ns%Xv0@YX}rNHCDPc$|lC)1Y%wyXy(`JIeE{QYXHV258sM=2yu!~<#+jO zo#0m$Ohw(r=33woyrRbiHTI)iZB`w$OX4Mqw%!Q_rQvy>#|oQCt&+-HnYyK%;VqJ# z2svv#mLmYfd*ZF3Yi1a@9@d7z-0l~|5 z6%6v}$it(Fh|L~sp7rUfTEJsyw@6I6J_IP4X4SsrKil3TbZYR-%DD-Xc7O-Fu_{BU ztinRUH(YM>2k_pHfYPs*2qo3o#+O?B_!L&K)pt+FruT2>K1wW9&N&FeLT8A84bzF*PuN3tDA&Q_n^v&~f z_DUDmdh(hc z9V!QXEpLkmj}aA<*XJ8zN5#z~Rw^VK;e71YBGPiusbX?H>uKFZ#)6F`GHYpf{iyYO!IeY009xQ*rVn4^fN4px&8|H~Lz||9{lW+GN{ONn!`l)!$$0KG`tyJ+T!iQE$+y7mf0s!H7J?idfs1~gcmCANE&f}7`@f;aX=QZFjgoA@ zJBmKNhTX;ksWV%CHyDvnto!U|Zqe8JTHB7Ftg%_KWS&aeUR)Z8jvH>~ z%f)$iLEEk_n0cxG7uNSVzC$LoZUZ{CCp>zZq$}!T=jrVi`7ireH3Cu;DazTba)Vjc93{pp?9h(cip0~7L zPw02Qh@JB4Dfs9WoGkUb^F&u*?En);wKOz@O0j!;(tI{0+eQTyhbhdzo*J+7%FETQ z^Ihq2=UpSm^L9b*!j<2VthL`y<{tB&Xuql+SsrzLcG8l(xH%8ojh3sp*k@)4siRM< zF!C%?t)A#xkiMz8umD$WB-UGeu+HYyGQsR{gt5uHS#m7PutpcTm(?UX%n(*CV46Oz zWpDnDbl5D31wAU#=|>b6;zJVIOFmwlK<1XTui*-aMS!5g`fy^AG7VYgcrR@blb=B1dscX{I8Z#Kyn&1E>#UAp}b`;DzqL_R_ z$RVd9yq7b>_~9FMGW1tFqkx5R)r-ZgwO#jP-RgQphW@dZP3)y&*d6P&S|R{WxNLo* zhNQh?^ERheXryLqa>7y3%;)#OOY`K?UyBE9_s;?zO34KJ<~|Vwq1fbi_>T6klJBiQ z-H)nlu;AV{O@G-Cn2(RI%U_7|-i_4rk1~KrcF#3WeXWd3X+$)fy$$t-)>*8Op}+hG zHZh9kTgDg*_zIq=o$R_>+b;yKP;9Q+3vA0D(dV$HI-^;oKaP>gEAuJ?m8ST{m4ei? zGI|lFebZ02DQe0XuFm&`=$Zuax%l!C3Shj>NdBv6vj^Zia-PqSWX#ILxtf4&cF`*R zmAFkyNFcnYJ2)hx8v5VD00S?ctmv1y8ZIIGYP#hqx&yf^^<%P;UUHM+L^-Z%4KEm< zOBmg?V%os)Sd$KXel+?5o{+0d^I-dh`GBvq`VqXnfos5{D-wdx7X8gpY-3@ADpX4i z-l&b18~wNzu!nv{yrX-XlAN1ce(+UtB?IKq*3JL6F=u2r8!;6~E_^;RD+?80lmLW6)o!DV&&xfh&ZqzOgbGJ zCw8qJI62@Qvr5$1M~~5x9^3PR`b1?0rP>YlzCn>Ur|&TIgg=;hMKi91Sn_*I`!@Z$ zK*d}QE=6ODuyBTD0YQk zN8u2rULchgPy+h52g65_{k^eb%bdNl_M8D^g5)(Z8uI(N_x zR~>E*r1kb_n?BPXY^-=cQl*Ho?R6!`wbuvzNU zI7AAQk+eU6px_dHQ`4KUpq6t$9nN{KIOXFZa^z;k&Zz~SPabJZ6p~ZROifvS$G_Z{ z0Uc>Soz&}Z-&8aAPMG7(T1K)bj&~g|k64HqMs#oHd?wa*5}WQXj=JN8Z)GK9xHM=! z*$#tN{E-o~U|=NG{yu_Tjul<1UA$hsezb*l<>#YlQ7^TGtte8CSMK-LYuHN=c7Low zHDkdWHu0m@_d(C6JM2Fv?bxB_8Hye#N=BI*n$z11}ERX7GG)>8FD6T1&~>nWHNO8m9kC#J8mH>};4y z8bN3_vhv2h`N8mBSXD_rX6oCqZ=)3Hp5Igy!bdtJ>F=cw5yi#|Vetv%Mkh@4+k|1% z+VAla3n%(sZVxnmh9!MhOXaIvE39GHPjay`)zu zm^h?*iu>@izhV2@+VcAba~)S+LCfzgO4*hr57|!}=!s)&k5z|@YD;X-#-z`xdD};X zDc-*qz6mVR*dEKJx9S8&hoL>dC1|juuf(j}Z zcGQz6_j6M`K1Ad>yay3EIJR6&*(xX$@Sh~s8eW~FQHBW%`D)W2aA#Gp?yxny&@0t+ zcknJ$Kq`4#Ta|GBwR?Hsw2egaANJ6^j2JMfnsqj1si642#+e6BM9=!j^k8$%#6_o) zbgA$gX1>kgaffd4?Khk=)>+k4^AjO8-l#i$kA>Ztz{+qg>-Fn_;{U*vPS3*r9XHxL zAbtMNsP$k{r!@kVN7I*-f52BWJkV`K^r|097 zQZan6{(uAg`5(`?1(-hBy#@+PDnO8o-hamR69AkfJ^>NB#L5U-G&#UWP5vDCErIcH~k4$Dym|8BoiQ( z`k6Zy3UH}#F#(8hV~5`@GW2y{3WTIMe+|_CD$~jm7;J%{wnWTF^(W#^D>vpPoGXEC z9~CXmy7gkDWz+|ug^;>Jpc)7BlPk2DAE2~RK?q{YWtET*`BTV3(EN=A0UQu zM%iao)WB725vMti-3VIn#x+g1yx92%IXCg3A)M1A!t@Qk3aCrK41Lo=L0uSqitmq% zhP^5d-l2p56?${5Ra0<(eVcZKZR*>%h2G7}oo_x@Dv9) zR8x&B&h*cG*sXV_OO15~$`5!ue6*M&+z*NPX0Irgs-DA6t~2BS(#psgJZ(3 zX~h%WqCtI74LnC`FeoPmT6beS3X7UFRWyOnF@u!Y7&JdJv-zrgPmVO`P>+_wpp;eSTT9Ijk%THtO`PKb~6h^JUF`xRk{&CI!Q!rWk!GV|dA-nXxMf z6bsQQb8wdi6i=Cy1SIE=T9^8-puEzx7wyq3Sy<0qID1}M7Yy$~X`u!+OGo$bNWx_^ zbE3Lg*0`D5ZwzqQkNF@%b+zuMTFj*tWL`>>&dfoTfjPuGmd->>1@mUIDuz0^oMef|Cf!l)*2u1SHHE+B2IpJoTz~f1{VIJ`+(I# z>s!o;kupJ4qV9fa`eU)X@28+{YxLs~A_5}f)Tm@F{Z|D)y#lVmwfbSFM4*|3M=sumPKrbKsp*c{=4vB;Jg4gCE`(+t*s?StQ zVCpLE$B%${oNs7e{s*;ybKY?G-W%gvPJ^gPWir=Neiu@)cOGAgk zSJEzL4$F53_yf*1cNy9L&?e$*I$0RJF9<-lHHF8wT#H3H*}u6KG{m*jDoB*}DF1iU zma0>N@o**23mO_TUVZkk}B zfp0W;?P+z4e`qW$Q8mmgA9Wtey1aQ)>auy#-x}&*2K^t1cNjND>?&G`x754Ar?*k4sT_auU>Fw%s^7!+2_CjA$5yIz~^=002 zzsK>L)m44EUClb zW{C{k>oexP7Bki*_9L%N+(+2zqXmgt7)}JS)|w`?LR@{sL7%v1G067au;&)hDJC`$ ztfc8E{~K4$y(s9BCYVfXdgK)gvr_GwnNO~-g!rPVr-hZkkG+CkSKHC=Ly-L-E?K&y z&BrJ>7 z#k4lCGiX9ihZxMK-UA;}U%SJz(W-w+m(RGr!XM87zLc6E;Z0gwIp@=ZSv|iF(NxbE z8GgnDn&~%!DS*eZe|b%#1L`fpvBJV!z$q*~LK?9&lvl>R%5}gMxFgkX)!tFWcAJAD)#)8pnGM-%$FMQl`X`n@*+g&fmw-3GaWp6Z1_Vo>t?i zt3Ref{8oGWVD5>a=x5K$x!EfnR=I4}$KM;9G{{9iBhee@$4Z%VpjIm^h=!UT?h$6? zyxog!8x-wvU8W-<(+{lKIqo$sSBH{RIUPTWQ|#a&Cr*Ap?G6zX+;9JRY@T#NpaIJ3 zs7Bgmmd3EFnQ*U|6Dn!xV&8(OB66_pTZ@JU<@%56=Ag!{0kUAzzg|un@nG8tjVtsp z@VU^hnFl6|2tP$XrHm{kgMenH*wwEs%4t-Xe`hG~K&qb8)y`)+{ zdgOHqe0x8imrwRxL{}jsjpUL4VrZ$`!&Nv^6Ma7YGRBQCF`36prOj-ihttzo)VknB zbLgQl0{fHsXhF49;@RTNQ&pVDUpXb-Q2n7#9jTG9Y!^=LQnO37(b2%ovn_~CR|r>@ z<(x1u#Xr=uDtI~L zmZ+(is+3(i!C#(hkfA^sbD_q9wQ_{?2RY-+d;Y@nQp&oevs3Eh4cA9SpFe+wKp;B}Hz`KL1hUQoV`F2!7ZWpY%XL2@^7B8Q z?b8ZZI;c54yN61oNkL(!zCD;K@|{TQ12(nP0n)nJx<&HRVJem8L)771>~wkk zv)G7>4cA79RM2O>Ap*g=lEW)<*X893*7vanGEC-PipWZraS>_j16RfOng*SaHwoa> z6E#-ciGPsR7&R`;L#)6u;M2J3Ne!rW<^PFO!)tNt$($Kj*x1-~5>(aHLPJ8X_ac0E ztvO9#?0quzSI2rjpN^ZujacIjFd+_GX9p`cS37`?xX7%7+kgL7W+1pZ>-DWl0E58+ z0Rh67yI*P!YUHWi1gxUVAHkQFm#_Cb+197#=jRU(4{uIxt|_uX3Rc*BbK#$O+%t(H z-9Z=Yzk}@?E{a9d#UD{yEHH#=f=?`C%mr^)En>0?iAsBP-dNhBYiXfihZ~(2@lQ_V z2UovO(9^5!nHsp2URz(zAIPOn>EEP)Pq1)fLQ4F%t`v~Hyc8*v-QhT+M`Lp`iugLR zK3`W%`B%y*y9AY3c*rG@W)QmK7u5s8I-1{!eq;s}PZZy+NdGH$|xa=vG%(*R7(0zzJf= zo68kpIS|Ow1r}~5e03PD&Sb{CvbdP@#YWzQ7dE76I6MnVO?T_%?IQEbcJ~6#6p8Gl)v9eJ6SWv?Eh^Wm4Xy@Rtn_9y zIcf>Dru2>`iQxUQ--FzH$kuJFr^iKj!TCu*QN2u03Jh>`xDqrm?{VVnx#onTMwcX2 zRB#iOCpl5zp)YMzcOtd6=L$J1k3QiliOs zGaTU!nk>$KyTl;WVBW)$NWN9&ujzg5c4{v&sg9heG1zTB7!#Wk%reZYt)`#C3~P!p zab0A*;ras~_|rc~8Nr9(HdZg;U{c`|U?T)s%7oH-Zccn};<^`sK9AP{Dt6w}oQ~1h z@F70lA_oi@$g6Gxrs#XtX*3uMWA0f`YLzO@-2QZpHpoX=Udkc0owoel9sn z$B2$4d|!9gbkJ6frm`Sbh)~5^U;d(~8b6iIdl`8xs1|oL7Zb8Cu~b@yIrqF|)87kW z0sgO4Lw^ljAeE7CrRZg0vEWx1mc!rBM50J;TB#pkpL@#T`o~}dJUimY7QVT3B43&7 zEAlyF4iFP1Jv73w>P;1<&r`Xd?ym4l(wq8g4g9N=QBJOqi=q*naZ)WwBgkj}Q;iZg z*LPG|SgKceElpXRW!8t7)F8v}H#If&2E@zl z`bFXJByITdSyoxuVGQb5{>7L)l;;RnC)z>DAH9_|6l^nDY99TsT@t9+!3!*}3|*y! zN`X5}2U{nH$6V&4zKG_2o`F9vtN5`aWIh)hPZd@{;xcNwzAbka;9wXkT|F9~Fy=~H zw`g;B0t6+QW%uwC=jULysN@pT0V3jq!a|a<7F6?O{l#y|ROE?ijAo28SoQ{+r~vEK zULeF};v1xuF67lBy{kBxNf`?+gUG;$@%pDmo(CitC5>@ptE7StL_@L~3)7_Oz>IQCW)mT`!dN`Jt3Ih#Pfbg-k+n#;bHJN9$ZCdW_Ou0e)<+HvpVbuz` zsE{CQ)7To;gE-fp9vF2$t`! zTAGP`I`xfN%TD!y*>4PGDAT5(Rx#`Oo?1`%XgmZC03ZP1iyHD~+)d*^lT?3ceI3Hq z(QttvM_l(=zx(L<=C`_O7h`-`XdV`bmX>yKw;^doo0Fw>MccVJ2kQA)vMEb(l`V*A zKt?8n4{{8;DCL~?bBVZ<+XB38*k)8!=ji9`TTFx_Wl@5{moUY*rUtP2PTfm9>42iuwzCL`={=jQUHK*T^LXprTvQ1>tF>EHT87$L{Q=?xK!iv&Q^ z2ktIJ3L=y0>onKV+MUX#U*dQT>z&3JBy^FPQeA2Mkvq#&kOnz)Zm7RSd2jPvw5MXK zzuTE*WqQ7b%YD3!`qQyTKEz1f_TPLKr+Nlo5(g4iG-wKdX>$CjCv0SvC0(NM#+=q4 z{mC-?f6!6UF4YF2K5qvJ1KCv%^PiqhPF|pq_+KqAPcTA5mD+olSz|S4Td9HUFe{*L zPv+knUfP6DfrcGVO;O`unELhS@>_0CAI)nV)FRtIk{|^6ZSxY&pR}x_J1&Y49s;V6 z2BxJ&QUU;#jMP68Dh(VsE|BYef2_u_pM!}mL&ao~_J( zq&e=-v4kNh`@6!}_V+tQ<{eqC5YRqKyr*DoMw1G>#JOTspJtridslj#+krp9Fyi_a zLmc;Df%n^O??rZ{G_61U3=2Wf^q$Dd-~kPJW|yjKXW*{zZWQupRTu!cO2zYd{?$;L zcq-3fauAc-)R7iayOBtLv}I~$f-3CvlVuJU?qIdxW0O6eIo|8D+hUO)=Q|`vo_|ad zE300NBt^X7y&vL_<-4E;LL+<=1^!G9+RN){tJqN#c7NA*dfq23WL#g0WK_J;D6TQp_?JO5Xb| zP~g*8w%5j@2x!xXc+l^0+6y3-|I#bKCbid$ay>>A)jQ{oO(hsxbG17tWXd=J8^w~~$z0H?Hy(=DK+@vg;n7~0w;ho@3kwK|^qz;7-eIqP z0Dmxjz$Zr&2=q2EfyfzB;Vy}8jO>+Fr{JocX%GC0`@viffnys5bpHM?_!b;L^4d%( z;^Uhib%ho!`pjk&p%jl$4g1?q-8@2`Cb7Qo2D(Y1nkX2Ket+sk3T5C5l5u@;t>Fam1*_OmQN znvKmSa0C~&90kLR`P$&eCwB0{A}{IDIXtL|A+W6_nbbI&}qu*6haI*ph%U< zu}X}J!yvoEY{1agX<|So3`I~B$bv<<)Bht2*2EuC{Sr`ZR=c%fr*_gpO;(ki{Aq#= z!IFX7F1P1=5BLuy9;poBg&=lW;i<7Y&GX&{*Q4A`s! zUi88rm=r+H3w=`JpE)l;O4?1A4JV9_?N9v>1LVTf!OHV)(Sme;sUT;fkDs%@pr@*R z^GDKXYZk5Xk_J#&*8R~u5~BWN_oV;Plm6!(0zmmjhZ>EBU<#GO!{%V7!Nc^yOuF9f z{z`N~#v>HMfvpa78wctJKHMMU*!{WM8XrdA7a5=u;B%R8QEfTNl4C5mK83eF0XCtC zmX&NjKNmHW%bALKI)WS7dUM@l;CV(T`m}1xVa?UhHm6L;868*%KfV`mj;wRs%O7_? zJKXb9Qgc7y=jwA|XUKfit}&DfP}v>N-u*N`dSo2|D~(iS@#-;=G&zwrVdUgKpDWAzeP&+1esJ4jiF*O!g#3M}#0R zv8*_A`Pf3W(b$`dcjWkSWQb5=M`?F9;aHQPJy~8})n(@<#@a~h)k@lR!8*3rd2r$Z zoHhThI$n)B8l~@@M!a!eZG*?$(K5+X!bXmE@R4+1T|;G*CAo?Z_Alugg-Zv5{Gzt0muS~|>|_RSY= zMMKac!{DA8q3L+J9W$a)a&0kz_yBKOu^Yg}NX0`RK1)5Ur?a@wku&GCr)S>ql=!sK zLE&|N)|2Kra^Rb|n3vAyeZqCEL^<)wJI^gV6<}Sd^Op0w@tzI+r!m~Bgs{n06Jef6 z@V^8kFhF6W2j5xPn5@i7n7F$b)Si??cT~o~0fq}$EG6XO=F~G$InR#Y(VI!o=6ihV zY~Xhw-fP|^$cb{^cToJ*N+7^1nwExpPiTYhXnXihj%w2*HOy9|!9sZlum=I3$18sd)6wVY^57oD zugCL5? z@*ueXQNRG*vu7zo2}W%>EQ<4&9}WX_!#MyOSX#5jZz4dZqPFjn{&{a=uN`x9raK2|(rMO|mjvwn#si&K@yT`zqY_UT= zw*EZEF!@FHgsYY8W&)`A@bR`e<|rLk^VV3=aWEFdXeg`W*co^9P&H1+SApT|w$~Z&)Z=mg zMHkVBF#Tax#?yV;1V$ZeA&!L@<)Z-UTX3Qmp5H$ND6{2hl;@A_0WoQgVrPDJ)E8`L z>tCn5vrQj@3h{w7m;B?9mn5X5q{Oe7rOEo%YwwEOB%DpxaMV(iBN9)hr5Me1xvB=% zRnZD)Eei7<#LrFw<+#M;c(N-Rie$9~MW|9#uyizou$ivGt$cKcbPP&PYI)h#w;e>J zDO{~0bA7j!76XF46{wU(w7Y#!^H_s)&>vkXrxnIWOSI4AmGEAFg2u2)M8g%e`TmS$^Enpv zM_*I6^>Mhb z_Mr_S_T7eP$AztPn!ThLbz#J3hBsXAXGycwn|>vHU?JOgH_0?69#Fhl%~#U4vPjJc8_duaK+tY_m9j^loO8- zIem>*QqaDj16*vS8bHD;p&VOf9x?HaKnRj>;IqBj*rPdkg-G}n<@+>TWX~lwlEBb7m)rb6OHB%4dwlvsCnNIz4esPLDrWnVfFPkpTY3#JKJ3N{IK80P8UjQz?Z4=G5|zlX?6zDhVdA(HG?5eu zJMg#Um0M9MuG$diF!8Y26t$JI(H^dJ)0NUr2;F;ANFK9mnNqN8?DApzER`t=pe5%E z{8+W*hV7{y%k@Z0gS5kNtRPlOVEWNV1wBIU8@NKF4(c7OuQLf2lUpMrQ!g&pN)zCw zoAp)l32M+dQ%nTLExhq`yggZ zI8`t_9djmIK~Tv24eM_pRdEoFe8F+LQk2NK)>)DgmGY=i_Wgf3nb245J!I#ek6i;1#w>>>wxI$vLp3N?L z-4QW3n;-Eix!mPZ;s`dl{AEWLIKXs%*-Nto2WXJ#+IHvLYiT#XOwUO%KDQinC<(`@ zU!N<6ZQrelrt6bEh)&K;9&uT;v%W)Qf{6=Gq@}I;Pnj_plOkkM5IiE6g+U|L z?%uev=_B>ShmiXPE90BNBl?%r8LQvW&Dv$fdw!T93dRDN`m(KY4(E=p&F*jQRynFw zyk%x`G?f`fk!f2JWG=RR95N)!V&WK(JJG$aAx}xrcSpXK9IDUiK?*L`hOZ;GLT7)0 zwpUafK`x?$#o`z|XKE@~Vk{D*O;cfMd^fAp;kZF3Ug^hcp>#^Af~Ijj4WMyO#}KPc z&Yzg{{DPu(bMeZ2$KIF6hUdTkeI{tb&^RK_vi$f1&8?|CaL(F5Iati^4+qF?NM=i z2T(UtbHpHiNK=E%5k~&I^HsGNVEe22O3SaC#8waDTIeN6^TM*|hHHebM8!shM4{Dz z`h?F?^0?2`rpKT|v{XFgC&?FWugIu+E|k1JGCf}4o^=c;N$ty<9=^_gyYx<9eOvTG zR-@bX%VedV_0%svNg=@n^JxiraXr+^Kt{!EwtJG3%~u!}Ap2@eX?MhG)EIR&OntJg zJQ3qc1#1>OeE7u5&w1O>s+>{X_P$8am+r}ShFAkN6ZiKX=!q6Ve4heKFgDOE==C{Rx4G~J94WSm2x1j#Hp*h zgw3yE;HvfQVEd(07A3IHd%h?^s*e^qgP`V+s$Zc}sB!2{g209yJxd>Q&n#J~@wcx; zEWMR5T6e(WvV+496Q!O~a8}b;P)>D4H=oESCiW70PL_=F;l14>;^rirv$>Hmt&*O> z^OVo1O*!5`dgf1r9<^^!&9a(AK#H@ab2q|gabvqamJDG!60>!_NFGqyFC%VPCJpT{ z%L}Mq-Ei3RkCw|7r)1YF<|3M3J-`SLAB%U%&`}%w@!o1FmXa6sIhya7SfSB^d&Cpz z!Y!N_)wPO~vY43a?Ck8An3&2QQ5DFuS6*4@NY~8)0NZ9~?BIw^)4(CKpHRQz)NBA- zKeT^qp8hH`I>ZBUL`G#ykT5(?|O+J06oWLtK$I^&x z^x7Fe1z#4rSsd(4Ngh=-JNQn|x$>jC?uL%IT^z7m8o7M4dfVGRkRUvJubNxSDT&Tg z+r}yxksN>^Yz2l_@!VV4&xX~Oe8nv7``VP9M`Sa4M z>&5)U(LVst$bcKei^VYw%j=2t;Vx+(8F_0jl2-G&wwdozqXV(;Z1uKfmTBE|5XFy; zT}i33MP%}tJF1k%wS-jVOcqtz9btC!>jw=mn2rr#7+RAN zy01W@)(x`+7uKw-FgfVvER$rwLc9{=Q}2U z4f}B^A(o0BSj7lR3C`TgyfXg4SI@HTxG<1aY1`oh*)r6mY%LFTeAjfGSIvVr@FV67 zySX++=>=6i=br2rykv2ee%|UBw3AjFeK)>1f{m75ex`iCp}dR~17L__wag@K_a>%@ z%t@^4hIaGDQ&YIU4f1-AC5uH4(s--!nV>-PO04@?N}@=?5?U%xs$~Xss(oFXkHF9; zbM^+|{&-C2V9f8_By2YO#F>T!V0(g?LFqV22ZoLuH2f;RcAdoG+wI$qZG!8;>_GAh|Rs9b|>!@UsRl~ zj@OQrtAeM;2}rlb-?jNU3}wO0YWKpWctkh#5+%KWI9WpnZ2*3~a!TpQ(={4T!8$F` ztd9dmv96v^MXzfz9cj5i6HzaB83{HcwH6wbV=+hMlrTuM(wHj`v0VbxH3Gr_%Wcr)#xdD0v03@K!ap*}H~^m1b#< zQ7bKn@KV_`1O+AL&teVj4guTcXR_61d|MZ-m&ZMeH<;;$saL1cmMe$p(r7N2ivw{w z!&Du~$0>Sngr4#cu;vDEey%00FM1?j;BhGq41w)j(+A%bZWMw_+nt=V0iLkb_od%| z@G=q=*$LY8)uz|!m@VHL!f~~~AhYryn4$7Oj>9ha&VueL?sKBM_0NpDFew4Xo7%nq zcgLT_u?AY?_^tqlQ2~qn^W&@%j9)FH&^t{Nm%i<)W0lvZE7kxJ!wX6o+>62<(!lcV z;yxrtO?h^R*(vAEmR$5-*r(Nh`5DMU0G5Y58CWy-Y`r|V5We15;{E%i^=e9HJ{DOk zsQ)as8r4|u{APPxJ$a2!oEd^N(Uo=*56++Jtkt_|&pH2mA)p(Eq8;2TW=S<5I}k2h z6!}ta8rDXIDr3s;io4!ytkVyPVyso#Tf+#ITzuJzqZaF-=3U6hU_@X?8=DcVh z$cd{GDQ_SQqa|bBcAmE3SyGR~?>u+$u*%>yDz{dKqav_o_QQ=O6F*&ClIpBF@`F|| zPZACs&kuGd-{|7kZTenQkSoMBgIq;Jwv38Jv7QW;e9#xgrYt@^3VC55_%2v~or0p2 zcfA9`7i?XTPt^!_*llXY0keA&sx<3DCN+ok>MjRLTN&+-+X1x)%3cx=E_nG72Y4jU z(}bV=;ktJA=mp`d_G8#6Pi3WWtk(2+?51&Ub8j3A`JAiQz^kz#Hl`(cNeHcT!kT3+ z_x@|pf+9a_kR92imw#)!}mF$afPSPu@G+_~|?}Qu?4L zSW|mzjjy9yV&t29|NS(D*vkfByIhlf-)4DQEz(V_io|NcJcamamEko6<-Ry`f)KB| zB*TA0g6H)15r>r#)0p5~oF<8t6};@_PzfuN$!l&AUCWtl0!I}cW;n3~G$mlLxG&G? zNcD$z$8M!G_UHr`N#&84?FChJ2m!%shxH^1&r9_ef9C5tNQyLfv%Ii+2AY-q%&IM; z&-DWjTCRE}BL#X<4S5A$d{;&aacT9>M|!U#@tWI=>g+H(4m-T_m zB>BnW7+T@uXIsybH4PFd%8iST<2|O^rTMfSkDHBV{IYC*foUNx}q6~wx1|-@ILS_caiqMC2n9SnA59Pz9 zH2UW^2ljnrJ-$abkB&PX@`aAx9BHXEm$6)ZrqNWGx7u%OQCR9o8|{9uFLT^A!qv&c zzN!1%NxV7Cls$8DYT*;ay7*Hn^1DmkWagHGQOoMjQ_raImQ%94fl3iet#WQh z3!E;1$!J`)5XBZsp!}JUYZRh^z%Xr)0*8ZEQ8waKN|FQ`g|-Uv8sG1KQO| zvj&qZ*uWGFtqMt*Gz0F&=B_688`Y`js;!<^;}a9It_s2iqvIO(u3y%Ztr-)Yz0Du{ zzo6C!EaT9uD+{FU)C`{#W^g0BT^XCFiGqwQqGr{exw$til~upT^l3OkHR(y!=AUpa z+eHx-1%aFm4h^!|CK(Goe4UaN{<2U9WH+D84SrV2dA`iQM4oi|Vk@MY=qzxV8UBkJ z$)=y+<5CgSHSBk+9x1?#$vqdVnJc056LDR0;ivpUcBbAwuJ9#cpkSe=B;MlC5&@O8~&(c8)2z3#qHwuB6 z_vM))wBx5X{N6~SW%BuVW7i#};AL>zQb?N(2smnM#?p6XSUJEYn}!>ssu~S;524*WGaEF_N|-JKqP765TB;9R_x#@1NfxI1A4WzP!u)-g93Kw9wPFLb)?FOR)?I3Txutd z+dXkNmDmclE+Cbd?vS-BA0^+j<*EIWfVa!7INQxEdZeA$E|Yc1WaKE0ppPa`gz~lt zw)nO6KXQ6nI&obq@w#ueNPK2)AyIt&4LKw_$PW`k=x0>AxCf<>yv{4CbyU{jeN~)DMHN8s*w#Un$>|<^< zgZBLZ_fT>n@3@Lf4b3=j+NAhc*3dT09;ewd7@XhHM7K&a$tca{HyW=h#VV>H==!|j zX4P@jN51a59dnwRRK;xH@KnmOHu+PX)@uVNt78=TIO=`PGk-JpxUIz33ri8|Qm>!Q z@p`Pj1HymVtrC-fwA$M_BNYaH3Hy+dGX0!Pd~2GeZLPi3K}MJ;F>TCQwfPQW*;-)I zxG{yVGmOyl0mF`jkrhZvPZ`?xR#_BkxwzS8?zX=&;?*m9KjQtk!<_}5(x*TT zxobYGN}q}frlydy6@YzWs=TGw8-$~cJh`_r$P}s#-+GBmg;aBCJNWCrmn5ncoU*c}L#X-6m^ls?g4{LP;Ha3q!2px&kS{FMB@OJ*8>j3Gf>51aL}{ zkz`y^14FQI>i4UFwCMWP$TrNfYhXFUf=(jx2Rt=} zR3+ZWRicJc$F-P1y>WYkShO9~CnG!tY-S#alli`<7xJL8;3=Vk$-II%yH?A% z5tIG?s#YRShUx`Py_zyqvdeUhP(zJ_c#zh81*p+3FxC8e8crQ~dj{og6H(}VGet^j zf~JaMX)&C$%q3F|V7)j~-CO)=dMHB;c}KY~A%-BJWaeq5e!=Ujp{1|hL7m^b`b$z5 z5CXkn#}%ReB0~(3p=^^_4)hymlpx)Ie*Q$ZJribD@ent}NEcbO4wU~oaCpen1(8x=qh+C|;s}>8ltpXKOcdqTl(gRPs8+bO%6QVHdBOWg6iPOU z_5caohqT;lTVz30Ac-R^YT2%$IWhJ$`7IBZDnRD*1`}w!)zA9kj)Y;rx#tR_G+^g% zx>yk4$_#h^KRjld-)<4v)Tl2tbJOGZ5RX8bYk<+^gz1qq}5Re8l}yf>aXdBIno0tD{E}iIEY)-|gGx6J}w@%hSPW=}9t;GGj7ZtBX(E zRi;;Swq03{$?;}SNl90cVK)l5ttNjTA}ee0d2PN_0FD`7JCaLitb;!X_wbM ziy7!LTQaEmdj1RkGOCgNi1XOo#aglOKV36KYEcGMfzVke553;iUfuh1gEKOTvO8q~ z*6%h(fSBB%^@`x{0ou?D;H051HLMaiBXHoSHMVBVus+rA*^Gk;v}A z_YY9-@e;8;G2rRzsSaoLo3;wZ#C*rJ#v5>7hE!EID#cdLs3`_zC7i7jn>C+6?)QgR z?Uh_y{~9Z8rgW%Xdoc3WOfMnY>+!$(k<~UPD@C3qeUrs#JS*Fv&tAY&&!>jP13g9n zMq;20s#50Y=>YDUS z7voeKz}4S$BoL7CW zWy<;FhlSvL`7s^Up3!8Ek@1C|&}-AH7-&g{>p)g@#}M~i_M{udtzr0`Hv0jr?;wA+ z`7m*%I}BEiZL!%P^RjQEeMT@?N0WAq0u}tcy6}4=4p>e_MbUa$*bVP>{(Bw@e4$Mh z_;?(h)WY!z#xHEQ_&1$keEfQnT5J82dY3B`k)J7k7OsWP)LAc&74y;zK5h68R@Hs~ zIex#NCEEM1L1Lb^eV!FE5C3?_U;_*il-tr_9a!L&3ROU$K33?NZj@Gg6nwH1T1@1& zo6HB3&^0X^@+n2d=vq-#wX#kX_KeRcX*oNp);xgubsbgl>#lon4vI)m9c7K2NOe67 zDmVXGPr<-CZwKzy zJQ=UMGyGg~2J(?aRSL|8B5icE^ABsN)y?vQzi+PiV30OdL7VNewfr4q% zSQ8Uu>{e&B2?G1EF6!mzJ9NN5%puVkactHb!XrF8lGY!f)geWY#)ID4Fo5idRl+kk zcGuKbh%9&=sY*hS&;d$+d4lJdP)7n+B1({~ALRH5^bUinxL6f36@iPONL3m7Xz@PU z*ze*?F3bSyC-;7V?aHM%tkBs9BAapjWb??CrM2cY?`ikY&v944zM}7;%=R-;Z|ARB)CCCWqaForbuU-^L9~ z6H>YgQB73PG#`@WslRMrS>ZI%&m$x}ODLgo$lVU4H?F8eO_nmt)86UF8JBNKO|Sm` zm7esl*Ct>(%nweMWM*YVoopb`>8eSY)}-*mDAVNC${RJ3IPE9Z(;9ZWwMd`$%>E0G zGg(s?*%~}BjI3~r)_BpN|EY$-cu35*qGsx!yx3^Ao|j>QN5r3Y5L{|h^Lt+;d9WORT|GW;&8DsjqGfzqR(=dIJWg=45!QSx0>y0#Rj zXu0F6jv2o_!0jt{Efwxenl+-Qm6K6@3B1L!K$89K+ZO*!N!+I^2CApMWNZoEhoiM?=Y0Jo{p0RxE zKF3=oVSxy76WnBlkQDNuE!kb}j%2)hhxu+yNDVYDM}0=i((1N`Py4&6acL>}m9%2V zY%)D1!&voY*2tpK4yIfdK~jSg2Kj8tf6d7txx%MOX-yA{@NvOalZsuL_tMG+6OZF2 zNS+9>8HWyC1||CiM2IXVwbxR3+@**S1~Y}7JRI32)=tF1Pi{}&79+X!_XJNQXMmbR zrtOBuFuYLIY-~P~9+?~&>=qZ!#xuEF%HlkrVEMhh5K7$gy?5q?n!x%@4ffGC>b*mF zEI8y-^Q18bJQk^`x+hJG^h6j@&c@p_F~P6d$Cel`^BcE3pxy^mLt7iN2N)5Qmu1NA znP6)+Ki0!A1r>^w7|=SZ1~>#sIQU?G*2r)zkus5$mqXlc*GXN#^E;{d)03WdO8Dq> zHAOga5x>Z|0#&YzN{AhM`Mv^?^^28nS{AZ9kT=M72c`dyl#-pDOW;9ybf z6S|)tACu4qm;E@6RhithD#LW89xEpatgH-2EWjGyTTgYuMjKcqqXYL^%VQAysUh}6 z^QfXaLLi%F%GRt>%u3Q&%@)C1oSf6(_xz>~#;s_q^Vv%!(3x8J!rS^y#USh02|X_+ zzT`}iI{&Vz#z}qj76-D5_L=isHF?dfnDv*jp{k$JL2d~rv5APGMhQd)Mo5t+dSH)x z?SzJPe(Sr1MVGwcW#9aiT9F{%sWjI)^?p{+Sh7ROt3mxmN2M=ik(y~&l2##~@s-~h;!b|KS-JquHuuc0*$Z-BCh(5uT^YKRU0znReZ2P{B8^GzmgQ_-7q`(npqhmfcsioJ~e!0D(x&PVR%j-M{;gvOSNNjd5&E* zNR-lwqfTs3_ihD26?d=8FZ9D>sp5KD`i7TG+)JI_fMl$bfdh7HzQi6+YqRp;0`uVN z5FWouK89*j6UlKY{!oJ3Tk7iH=0^2VwHt?i@}0xeJro znNqvg2VoiF9bIcHEuRGJc`M2q%73pX8;Yn)TrXhJ(Db@U9koZI(1(jM_>Cl*qKLtn zNa>Sy>$8Ohf`gw=Li_MTRtagP@6R`^J;m|ldBMo7Knw<1GP#N#?Yt9iO^@j9$uECl z%>Y~Fe1i`LYBTr=>RI^4nu=C8N8%iULw6FTG_v3g+Ze22gZ;;j@$3?sssqlQb;ee9 z#mIFJd8scRV54SQt)}I*Vu}rB44k1w51cZSNi94NGQmUez=-J_9^szkS5Gc$pZtul z*o=uEU0;X0H6%?QQd-tiAc;=&-S>5^5q+= z+O6tZ79y1G1dmJDD{VoW#HtQw1Kwt4cvwWWnr1#VN|oQIU}WrOGn(icvk2=t@t{l@ zywb=P0~~Osi-rvpF2-Le+VF*Hz0NPXQlhIuYmY#+`2H5+G5J|v0|#4-&&k5tGvD8! z?ho><%6x>Nu-OoTiI{0hNmb$fc1pmNIyXoq$i^sPg{)SFsj10L8IT--P`|Sf0 z=bIb?X+k}U2YMvP@Elh8zjD6ugj(87Uu|C)r1$(A8DEOwf-T2cMMa(dg2n)#+&`S8 zi?JPJIQxzYb+~nSmRn(Zte%(1p0u0K_*s^p&{X|Bb6RXijeAXWE{hF7)z zrh|FttPSaw^q&BMwf?3MS)BeWcv0_WHth!WuXy5**DI?p_U}^9Ca|adI?Mo^#`z}j*~&kV{zVMf5%8rZ)oWFf?R+In- zDLOdPz!J@0ChJdRmUrc+|5Zkn!PdOQlzWt98=<2CVlGS^zL$S*DBt)tETbRffc&>e zlHp(_uc%~%@#WAUOK)#KIw3P?ACibx5YZrF6Q>5}f+bSqt(?))u$ zJ?Fmn+;i@C|NHoF&o$?obIlmz8P9koL5lK{7-&RjNJvN+(o$l|NJ#f3kdW@dAKwQ` zKBYz;A|ZL)mlhLNb=BWaLea#XxcE$%mBo@LHF&R>t zmY(_rjojb&rE-*h)O@s_5bN?PHFwp7kAuuk$gQ1ow5hY1^F3%$(_y_rGLO?hU&%t9 zx!JgsGQ3>kTM{PFDa<0>Xpu_3b>RW<;{y^c1`?8QC=&D@64KDKR1To*?focZBqSk8 zUpVlC1AU%^e^DZHc?5r zo9)R!UPA>70S*SkkH{-#iW~@+-q4RNc^83I84@!rNKQtG0KyIQm=$L2=8tH(phYIfQ_U3A55=jKx=Yz7 zCckeuMwj6voHQF{zs(bORa&pkpEs!@o@)iUa~?$|EE!f1JIPK>y=d<217hBE>^ z$l{ckq^uDnhLh{vCIY{HiUbfqpG9#Q=JghaYmT{P1-Hx@+Z07*X(LtF^SXbx^onkL zUo(wuBdm7rZ#v+QmGt!YxD@bk{atnhcL+0{pElssyg$#HrVW|93|ER2;C1&4`Gl*lR$<<7|5I3;2+>T< zsfFef9A=iwPmk*&d6+Met-fZi70(+=ni^#){lPa(fW^oI4QX zf~?qloOMh06n>JVxLQQbd!AvS2SpS!&o5$jv11^v_vl#QKl(j}^SO7FF_`OV`Jls7 z^ZOgmw4V(3b?kh_421AVarR8K;WwR zA|tSvqG^ec4ZBa*-a^?)Wgp}vLKCu}1QuzYtWKdlhN6>a*kWucr|xuav+rx!_mM4I&Li^Ju(o^QV1Kol`QXGUQUg zbSOJ*vdeYJR#i$BEii3Qo8D%*?SbR~7%hs?1Iw$O>)r0EJ@Geh7i6vIGRjxKLQj0` zxqjXl=TTv(un=`hZ`~`SPJJn}^5LZj^qD@lNKSuq27yI_GiYYU#Ygy7uf`SyQo=x5 z#VNGvYOF!G$8JGSfULEs>Fhz(c)O0#t~yU9oMOo^^PLZ2-Yyqy zxy*G~$>|Q-kxJcBTIf?|TU9$wMvY!|Wm*W($cI+Yn<_V%@p7AWkA}Q^F*?nx1CeIv zE@CD2(a~1c>X&!l&)Hmu6F01CrN~@hP<$>~g7A8fLHpt}L&4tsrQ_7Fs=d}I2W*z| z-nSRL5In&x5t`B^BSVP9Tl4aQ39xCaS@xiQI#|+Lz4z6iL9O4M{=q(eB}Le@E}dKj z<8-#1k5WO=q#fxcR!X59%MiA-L8^1P{q1EB);4MApab4+(BsPkz^sv_acQdCd@wgg zd8Atzu*hqA>UlzCV>erwg27MkPabPgXMOC<)%(_N?+a>$7PRzrG2Sem+lDW1)n-?n zWq#hhOGbpmJ6s*7#ciiwp@|PcBi_CRsH#`~5}Pt`)PAl5#+pxx+tccrJoP%by3HhA0a|hGBl=njuKbm7H5%uAkVfTK$=?SZ zA2C9WUo_bkt$6y(ID52=z{38$!G}IAn9*UImi2kc%dnk zR&c4+)OsxYv;2r505NF5L?*|tdCsvsTR!=u_SvYPoT)$h$K_R< z7TT3F;HoI=s;2(^yaG6(8UFb1w;z92a2xiSu$>YOG|=Cp=qBOm=aZ^)XO|t*3Bd5+0zvQ@8Ym7KkwyIbnf`vLol@<2HxI!6`9qa&53a-@o9bQmHZ2+Tyl-jn`x_A zSJfb49BcLybz-ZjLSC#~kgCgIz=Osh_@wwNVHt)>=e;jMG6M50jT~vFlG9jmm+oCs z=Bvj~-;xhchz3~XL7moO2(Bu31-4a3U^Maxo!*x<4(w4k9<`elM2H3?RCPZn^NH2S4Zf}e$R}yGLP?#9S3>G!!T#p)X=q`%*6u|QFSVPh;k>yIZ%HI$4 zUO2A$6vWf#qv11U+2>`zzbu)1wcu5qHP*TY%fGqITFG&VgOr|-fO(du>)VR2L8w+( zDYddIz_J;a$7ji94~(qYRxh$x&bDCzE);&UDc7SR#CeP@{{Z&5nq}XL+@|@!J|h+@ z$~l$ehomZT0oH#9Dfq6Ab_QD_0Zqzp$xAnCKns{>XOWIgDMf(f^PIo zWs7t7SN1pnVzeLk34T#KZnd1hD2Ntt^#2WPa1tdI-g21Aug{8aphx4t_RmLoDry#T z?t=helu984zJo^(|1&v+53;{;SnT{+ks%eRC!i2VMujFT1v9~^-0p0wZlgR5W? zP|6#4i=i{(iMBN5cf#bWX^A$EPXcm=d-J%4cmOI>wJe5};E_rT8)$24y8M(vO14tmlvb!N;k5oX4g7USLaZS@U)JXBxp9a{LzPIgV&W?faN^G#jw zme5g9wJiJ1j^qjuD6(^_NvdfU8=R1l5KRuPkrxrX2amwHexO zV@6a3qw*s&)FyRzTja=6U2mP4GfNbB&ro`2z?`4kD7DA#(56FB=IP0EIdf@OdP}#i zbaO^qV;6bzpv)!@Ham!V0{A`VLnNely5n3JriwgYms|5FejsD{MC3XT^I?CV>>I&3 z334#|J4w~R`k`A2>eaW5jmhVX7bT9oSp*8;v5%^+BBhO4`nDJ&o<9+c`VmFDRJYXQ zdz7s|g7@(`<^oPcWAy@QTL%w2KSI|IVc>U28avRTkm^$=YBHO8i<&Qugy3ss{K4bc_R|^uawO-_PHE=zC~M$cnrDwA29x zmq>uWOt&`Ca4SzB6Iq!z6XoT?S5)wI*Sgod4o`;u1kqe)RXgO|33=rdS1$zaQPa*I zI~}!>>BVMYF)_2gxY=Ob-{%3TkV>_U&quK)_^1ra%7Nntlq2?3M-Rd|lUZ#*`70cw zSa<7A;iRtZv|jv)%%9V1U!w?qvHi`kM_dD#q2=Uckw@;k*0nLmBGgT6g%tr_H>xvd z`ZL{!`CWB=%Qn&TQK(i^((x=cg{DuLsB=c>3kN_Wo*i%(%y0{3ZKmwIGafI9k=*<^(oE$-!kq^-iv(uwL7o30V4z16Zge*TQWnf?cY6iGmDs^Icz*Iuuzo zUQD4WBrOPPv-@jgf(#w+QZGIe;ujZk9j;Y0k=(=aM41~3=9BSPQR)>PckQ4rx2+3t zc+LIJ^4pr;+HN}6pN^*)3U6vEOZdgRy;CUfECP~mEqoGGw|ViakrA1^7Fi#=anc&dzr`h`P`jhreXR!YH1 zg@|dVNdJ(6Ricxc5^9!ZzG}LPYqka|Km59t;t8}wA`u2!h3nG&tyMj5I45oYA$*Ki z_>`=jI@4@<=>56)fe6@5sr%3fXp{Qv)p5A5lqe;IGImhVk=XaVkMN@NdCcH1@*p<} z(IHJ`x4?q!t?%#{pt2B!=zVgGh;LHTl+={!tO-3pl)wX9FHq!D9u!#hb}yCcOv zgg@>65+!Ct&L5GvnF{++muA0D7snH5B0g?Dt9{oet6`x8h@3SdSxR|!nkWh%_r(n* zD-%Oib&^GBwzdoe`6NbA1i9(INtcv01qaF zo-3uns5=IC%{0XT4#M}XaI?zqB5Do}#$rWaG^z56d$aX1*b9KiKOfEqehBfVH{*b;jKqpTOV~BjmA$o!I{>l4;Ue}idw`Z{ zz9L2};wXf18+mY^ESAZ7ZtRCzSv7Kg zUQxK0)x)N^ufQ>2oOaw&rSWiVt?U?|OUkloc*-#HepMX(9#ZQ3kQbD0(N)}5`A57T za&+(&^opzhSw+5sOGwCu^QX?IGo+#8i3KL>HyqwBtVB{^gH94sJF|;{^rdE2 z!m?TO?o(z&a_yCF^jRDM6K*Ifa8kN;(mAM!tE^Jnz&^@uUCLXw=+1V|J7e9Go)LC# zrp-&ey`3j8F4j_q7gdX%%4zBL-lIW04|o&eoY#5g#}J}Ab;l83gA`9CwG`2RG^Jz36ZB=3Z_?D2cvWjfin_J7If40o!^={&% z!>saw;3PpZlBRtQPIP9~@W(l^u$Cjy1IzLunlS_&}1_{W+HA8bONA)$&D$ z%QtsxwBqny3Ju=aCv(o+JjJvfR;E~9sFL8sMztv16=V%I650qIJ1l{$3CBHRZ=h3|pZ?9xi~DEzgO{UNW7A_C%PryVks! ztagq0LMwM9_Nb`xd%Xu^il7gMxjrm>M8=aEvMllhbwmmum;T zk(I|AbxZc9-N)k5Jq0%bk=yX_e2%es22pOO-81_yMmkRPZ-Ugc+te?e551yLbmF`% zzLf?Se}RiBL`~>=Ww;6WpIf~-Ob>j~W-cVj8qYhJ;`?0ADSUtV5_!`6g*bj;O(?lk~q)B>xbXWmD3Z0InfpTr9GHTPtO zB$F$bGpBKd7Af^`t(k9zoiQfN4eULqiK9N)4MY8JA4zthIU&Tlkl$obM@jCT?@yje7Va&kCJP&@o0UI`# z`||HS9ct721Yl$f@V13dBWP&mJyBDv>) zEz*w6fb;f{h8%>$ue4&&>9t>^Rd-KDA)t*va)n<8vk@kF9U{4-^aZ~MlUJtJ&b!Sz zcrbI)Be`(wsCQ#UIVDi9Aj<#*kgdFozzm8#oKpsGIrEQ zUt8+GWRN{sH!-suF|OZQB^(dcne^fQHo?8t6R)J#|3uE({hRhdkWBF>6>ZZlWR##b z+mDOll02718De&Q1m|iPz?{O8p4D5Oy!K#N#r+n$`z-2QSqw}hA5~w!9g)2?D(sBF zn0c<{;uzm3cU;HO3|P`-S~+WKg@)DX53%{WiFgm0N~>b8@XBd%D^OunF z`J?$RR^KY_<_2-BHt!obefO8I#RseuNEi{4jBig$R6S)QPZiARWHQaor7m{eTI|Mu z<{03asRKc8E+|&ZX@Tg~x|(=l6n?p*@6DOYRFCzMJ4%py^X|fOfaENGv)$WPQyF2WOpQ7qvSZ8@;G&qqt^*!_O*1& z`t3WNF!f5S-NTt)QLYb`{A*>Cu_JTNr=0cz@oDS&g)o@H1w*ut&5R2n$LYIR)#2p( zMXfI;!X!^iSh>n}1Q*yhf6>JVCw+S|GZW_0l#uYBBb9M#k+)sFStqN znPo9YX|s5g`rg^WK`DQsoNU31&cZh-s#5)+B6;rZS^-%l5AUoMNrN3?!|a~Q(gobC z=LVS+j-zw<+wk-f;}Hd#<1+FLCP-6dr~ zn_Jd|RInzvWMt~9NUr9De6~K8`A#=pme(Lwh%n#^*73@iI6D1?Bo(h>uskrr)C&oEUgKYPyg^7PSZizFL4a%+0UIq~{q`WC;eN+%U~2&YO> zFNmVd-fX2G+{e>TwiB$|GJ5)r{SMo>MHqmb4Q0XFH55$rq}WKvZhmwv$3X6`y)Kcg_n@j zy27ezUE-gg@wSL#6#1>xOAssI^>pP z8LVy}_|4yypghpK?b#Qb#0~0fmx*pM`r3DrelF@n|oZf*g%8t)D zaRrh!-zcX2e5IyKzv(jiaRe&_#NgxYAwF?}=!byN|J7An7Q*0}dLR2y3J*eE>3e%+ z8@)Wot_D-Ow!D{=bis=s(W9utwAU;+-pv)B>2BaGgzP@&v3+7}M{M?A&fR%grqP$0pZwK-%wjtXAV_L^-*30yi16%xy}r$yo!yha#O&lQBrYL#?6aP z&hxz=yocd%i7zJ98D^p#qUB62?DX>$@e4}qIt(?JlUNrLDtF_?E)9fCK?X8ZX(Ufjm=EfdP~~{NzN1c!uts4yqu181pxAFDngKR+cyvC8p2Sh!`a?dF z`$$4w7*e3|P(6lkid~a!)dIhXFs5s)Qg1vEt#{#tS<4;m+_NAAK95Kl*tWA7?Z|2M zogxi8fUMwUQ!@;H18}#zhuaZV75HOqJYk&EHaqwlqMBQFwPkKd6BD{+3ShyF*A6!k zpWW)OsYwXIlo4-I`qZMMND@+Cq)3|ud*&0zNF;0X8#9x7)pGMPxl%qLS^ARk;c-;Mai2AjbJM-+7kbbuegofZA>i5QZYG$c`M_Bc zw-c8)-dOfV@VMt-zVLF}vn^%cinj1qjtdlW!MOg8%V8qvTZ@SgH;wTOo=dVL@q(^d zA^w2_e~kS3^40ZdpTGZ}ienv&1h&Si138*n3=6xU5ny6;bMTmXx`fGa-@u`xTYxKV zB1b8-KKyhZmC(h2aY|!N%PDbhqj0&e#lIx7*ZJ ^}@i^xgSJ6j~eX8qjQ6R}jz zF?%exz@8a$UEf>k2Gtr~GoP*`aiL70i;*ls*bdo^^`F&$4@gM3BtD+M2xm`eyMFNG z;$*{?@7mw;e#Lp_1H}h&IsfDnz6(8oqs+L&%Je)^7W7HW(p0v1V#bgQM)P3d&~X4U zOfAlibhNeA?)GIo>#++Q%ZFV_(yU=_TANL$t+{GcVcFytEb)u_ocTRj@x8~u>$Gt7 zWsbS~u|tADiIs;j`-~siHafu<6`K|!*?aNkxuV1j9geqWK&E%fOS*p~iku07BmF|9O)pgAVsnKjD1w zDP}bsgD(`s(@bzyDi7RhW07gMy| z?=G+v=QMS?WCYoCo7To@k40+u=!U$iSA3I{)MBVlJFGw3ODfYFKYBGdRM6mOcsbxj zW48xlHtL10(QIO9)>`fJj!Q`3V^YK<2Gg%1xYvG4UC+;uF_dHQ`+O_*x;j8^Y+Pwi zF1M-eZ^Di-h{oGG_e(-x3kFh?+3t>8IzZs`QsD2Uoq5}M731^odg8F%uI9ZjjQOHc z%BDTx_j3HQl!!c9XNT&yQsX=xltl$@f{Yu@-r5GO&KqWDPTV1(U#B1hfD$*08L{R@ z8*J@o7WTl-FZ(kipMVL0 zP|<1V))LbSq)K?H&q|s1nx-+-!Vpa-&LWMB`;}WSDfqj?KhjxW{um>9lAXkqia1Jo zpp7vVQ}jIhw>?P(M^U?uQZjzi-+f6~wo6x)m-*)Q*LmPc)_|l+jaQKGIifFSwum`s zibv>RN4kBl=#O&NY?PqTOB^ zmIj2}=cEh4SB_3cr_(;im@T}b*S5E>j8Fz2CDj6$EN^$zD5n@e!o_~vUeVMFw+^2Q z$Ermm#)>2NYs&M=_a*x-*jkNsk6nzpDw4L^?FIwtlXa%F9x;{u45-goqIz&jty<+- z-O-2$h+#sr>uJPOVPBPd8TU{R5zrbCX19e~Z;u{VDS-Vr}LFCZ&+@UDl!y#HONF z;a{t-98bPc#aZg`KE8hY=bUQuF8pXZcKYaf)tQ*ApSHoH19O2HvnM6x9BkGTH4}9V zP42qKFVlV#MUqLe>?M(eA^h`Bylc?5#7h*K<1!8|#N?XSslw_9)rNiA{>Ss290iz9@)nuL|Cu#HJ<<9_7Rq+QFB5ay(ebVp z?{(dRwKjACksNmPSHyrGP#PsKDC%2q3p+q3@j-v*gS0>5kdPeqvJ?v? zrAAWoMFO7!VBFGD`{MA;Sh3qjwg49eP*t=DPHZ4)^z;#s$Vr7wNCCK82scUT|Bhpu zV;Ol+BsOKHYjh!K`J49|;Wx>S&cosI9X4{-pP5hT&!h`OW4upp72O`I6Q}GkIK34H zBOxixOM!v3VjB(Ad<~Bq2P*egkDBHG&L8b}s)kql_zEuw%!Ondgzx8XK5&9wD(>0309g2#MB^b+4T`fOMEFIb%e;0-zqxYX=QxHPyYkylh5&Aq|doAwvrVBBP)?EqH`CKE!aUJsze*6i+IQOVP^zz?frv9AQRHy^Ggz{LlJMKRqtAUN%*Z|F578BLU~~whHIzM&l}x#OF4*kCfu7v zCgM7I?&>37#d6hl3#<|q2nxR(WPv=s$BuhOW!bU}x2zvyeg_gr4u9uYAlznueCHwF z6paV+*0EC<6!8zLqpo6BA{WNMY(wdO5v5&W3C+%y%zNQQsbVAa%(4cQab&y9Na-Z2#-Aq%ajWpXCJSfoMsNWQgI5?sLg=g-Yi|h>&-WEH{t?W zZo2C64vQC}mmxR%f*+`FRHJFG5rX+1qJNHzaPu#P@9;9mgr@(f}U56W`GbeDC~ z>AO##Pjomt<;aqj_9S~ntH1CFJY@X$`G^A`i{W{!(x<%|(wTk5O?pez(9yW$|8t?~ zLTB6kb13=_olU9~lPb77v5gi%2ASevxw#s=z1eLN^kV6!zhxiUZ1VAnGdMahwfAl} zxamb;0K~9ZuDZ4@>qZb|d*Rq4DBX8&9UKOf?CxI7c{#i*X1U^!T)8m&V1+i^oMWap z+p`_Ea}Nl!Ur#tz1|7o_4toA%FeI)!>THt@5!K@73YZ{vf0uqzGdt6}Oh=n%SZZI) zK`k9(EWgP-Mej{@hLXOMQ|yF;tW_{BKh5M5Q@xDCJF-79_UZd0{;^Hxq>L} zbPUSVb3@f<&K*We#6MpB&E;j z(X0);5y=8OmJHghAO2T8Rgx$`wnB>kEGg=Mkyitx9@WDz`3zued?h|T6eA)hjQD3g ziCNTI1!~#;uC!ND-#FC&z3ih6N4+0J!7z{R_LL++2qZh$*V{*z`K#c8?rK-vGs zTDe+5R{2E@$RnQ2Cy*E1$t%cn8{jSKAEH#Lyo?#@Zg%Y&pK9dk|1a{yU$+d@d1k)_ zibQvW8`6i6w+tZw*FigJZ12LO z*(cP-HCL4<`#iW*bJJXo7Tm-tqUG|T6srh~K02i+f^6g+2g0$|$_6Fgx@R(j{PgK9 z0L9Vw5*Er)T1ocWDZxcKu$~Xl4^}G1UDVMWs)LD~n=6&F)IZ_#Q*UGTEy)oB`^zy+ zIEq@~dNzn|y_#OCL0$i6`J{fjCKL)LGZ0tmV)Nh+N?u}H=<5qO9I>JN&h@}qOsy-p zRh{Sid&W+($1;QcsHd~AT{9<^I9VbOLw)n0ws&V~53|I81T(GN2yCnNE*ziF9_81|F`t7q7Jm3CM$)zP2x)9N0b1 z{rtxg1tjM|;3lR91_!my!@ZpoEH8iBb^&s(S7{Ni46!)v1^G6QAA9XR{?oETcsQTmL z$miSvqkj{rsmw)51lQ$HTuFT!E=85jjoXNG1EgqEHSq2F#S0n(prw%HQ=k>42Iz0n zwp~mU4A5cyhhzDnYspg7TtXfWUGIo`MQwf6gZk%p=2%RMpYr8@7rI$BR<)9+M=B=| zKjny4)qllsS(|x81ymmD`d5XW?Kw&W>PX5;#PeWNmcI-)wckIbew4XyX_@wHEp$CW zX7aDJB8~~G@bCJR)y)56*MIwoM~^-WO3U{4Ffr4+4G<|;-o(HD(M-<`qu~OK)&QLt%*IwWZ@4FJOiJ@n5LVIztu7_ zUebRB{8faDy$U97<}22{ye7N3$`-sW{9CP54ob>Q{3urtuj6oV!AR;5b3g>3*~=0F zOG!yF(e6h%Zx*cCd0%!S2Rgs{mWYuDbDhw~7Kd|}l@RlJ_`KuFR#aH{F*@B0K7c>) zqjS*r*n9^TG#W1b4BdP{KO+l7ZL~f(fXmu0V^)QP=-Tx5b73eFVPh+!gZQ8%yj~|0 z3%5nWX?l+-%s(>7rKNnG&GhAbfWb&`E|ZULu$)PQV(=klXzYhst$CjIWzXW(C_}GD z)lT{0GoOwSV9J_ja$!C7)7n;f+(&Qz6@_77HM zyunLZtFKWqxut=Q1?(?0nr>`9iqt)o80b(UiVT_IR9u7!O8=w;@|gVEQ$-Dfb}U#^ zL%GL^y_OHqrd5^xHOt=>n&@nP`AygvpBjPaEUdXu?ZM6Rf*X{IS zgL|N_pwCiF9W}s4t)Cbxb}HWu;;$uhQB#k5nUjWuADC$hS}17)D65Pdnw5o*i+i{BpwCS&OVb{MWrS)r6$OPTbIk44;oxiw z3KT&;3n!KbOlz=kpED=RCrEk3uG3qEJ9WL*+@ z*W;@kUk9?==|bcm>rvC6U+?-9&x7JpIWoBw_@D^E51Hu)b$>Q?5y#1JY?|AIF#9R8 z2_5=aQk2hM%7RlZBC3WH(JGYgQ50N+K3!8&sbig+02|lk;wGQt45#72dkx+ZEEX#r z5q{l~iYgszs01_!vzNBtD7UH1em!f4L=e-kir4@gMO&GbnOQQUkRot>GGmt;9ZeT? zy^)>r_3Kv)r}VF;f>(PVRq0KM%}q^n{V&VS2XFSne1;@UO{p+M4A#0)wz=Ke?r~;d zTZ)nz%{Z#?(-&4($Let@rE^BUzbSs$*(EnSB4T49*TIk}_F4L4xd{9Nky;Qp*)RIS zj!2aW(NvzH*ljK?^@^APKTJ+HsuN))?(os`RyxqGJ@laXI$enFWpJQ10BS_f9 z|4Wc?ju$iDFq=MlfnD}4?7a0wf$a1;ojef(4F(A3Zp&m?sXCf<3yP@Edo+BLbC66|%Ub&6nE92-5WRZbSN6IiB zM2n_EpdEj8KIoGrLKN;SOoeMy{R{-zR00>j$tNE^Z&DVN(1$mIRH)vfab;S*4Vi7z zgQ7yRu9xdOUVWiQ92cQ;D}7wJ-f@9Rj-7FE(^IOzOCzmv6UR{Nk~2gqF%YMAK9>oW zFTRhTF8C;rO!1bg>S$_8X{2;yQb1W`t=Zq6o0^)cN@WaB2Q%TyYmU4K*S1)s>-XAi zE+bg5_^cLlV#9Q2ipw`3)Lte1rD_w$hAnyFrsM7^gN;M;Ny0WfYW7aafgNhv1s=uD zOM)0~@oKuSD4d*Fb=DE~L7or;JV7Mh8PqF>ubdX2GRyxS%ddol!kYfyNCay}DOLN}8zL)bs z#L|55c}?U={)@oKoNzsnVZW;t79T@&aNhRt@T}B){b%QNoN+dk%;;EGkmF2+#`Ez_ zf?++5wExIo*cxCM_hgLo5MgM#FbHT|w7D$tN`w-((l46^eKiNKwX%WOaCT@@pn*0U zmICMR6JaGkwSFX@GEx@6;Q||WZ}wfC?Fo5PH?`s?OX4BpXWXQ8?}sLIzP>C_rESZZ zw+D>JO?a!@o1=d2g{~Ec>C{_gMUR^+9<31a$L&^V%z4DZ9E{=|7b0>9@cRpTxnIeP z1@uReSgOWlxqeYu;G0?FWva9WG4#NEj(PAhH4`X*|If}Dv(h6_OW$SAQ@KV|Ev&At zHY)dp%)P_8<_Bh~k&cZ%7)UK zkB4t}&AF#>rf2}kQ>$OMGI1|+UA6Im*=xyu-K>&I3vVA56)lVs>Nri#lQdO6eLu7 zD_>kC`~#J6>|KmXfVU6)8?W+6;W;9GESp#kv{I!u-WMA*U*8;sTtdjjlylM>efA6GqEep|-E@&Ir159|{n9LiMulR0*16TbpELBj zZZkyEIk&BFF-gJ}o(|LCOiG-1^N=VUUxI_IlHa34f48zIlee#)F@P zKkvv`t2ruL5s(fZd3h8g8Um)K6lVf?K+m;^>{_PRd`B0t6~J3uHu*VMjlUrfphlG) z20YOgm-xLMV>kVR%;&0_nq7^yoq9fs>|ft(bBU9kFpn^Zn`A$~P~A5p2^_ekO%_*r zM~-4bi+yZ_UIT2v6Hx0_#hFkfe9{q;b?ySLAL){thNv%f=We|kD&b(TRJddUto-2Q zmL37uFnG<)e_2j+SUQ#GOQX#h$UY@f9{qG;#e`y-*U1yu_5F%<@bz84RcVBq9UmG^ zfhndG6Z3vlrRChJUDK`TZv-VzTNsxv4(_KP+YOPt1C;Tupi5Uo)~@&25?0f#)dS&= zBY%J;>?M?HVxfuDKOkUK1m`wH^3>~G9O`q`8;0=^Lmoh5ckhBLM|}zi2si?z!elhO z!jj%zo|5_4zO;Bh9r)UzF#_%{LMjBtcz9Q7)E}5blK_&P%g~g!I@&h}ojx~f3xc=G zEJKPMFEM0(cO03`@rPCd$eom^`+1pdw8yrK*&NK6k{{)N}=(0K%8;_xpc*$UVMe*v74B5jzbf zov68SJmPon^`(}CasZd4?)cC`VX&Wz=+dGJn5m z_jRivN;Q*H<7pBi2bBjy=MD2n-Nh{lCPLaX*hk;~^a_^L~^J9qrje&YsHGk>ODQ z&mqKp7k5&zCIMkG_Pkv|`CWA8Yle9jcK!JWDE~uD4b=Fzq?rf;f?t`Qkx9QntTT1& z<8v5Tt$duPeqj^B^!Aigz;S86}e%n?%u zH>Hx9y-JhUmt!uOmjBx&-Qw0Wpq)^EeKedl!OIgjvRu!@Kv7LUZkaVYfU;D}y*$8x zfuH+hw;dbEeu}+Z`iN*rc;QrKGsDZhoXzPg{ zioBK+A6xl1!g|`N1;j_gIo?>J%5ns|H=^cEjfKncInb+xA;%Z^a&8JQ9b@T#P|~%yoF=+`USwElPh+U7 z_zv5wS(GqSpJg@I7M}K8PGT4*0lrOUwIYg|1ueIh^?52Ff1J|X;R&L%;9mnd**F0g z2HN}6YrhS)yHwt*aNl(`G=)UoU5g!$*0oRT;c8gj;X!!t9%$*)sn9kY84_D^F26$? zSzS5T>bnm=w}>d=1(q3KE(wwaXwajbuW9TS8%W7Dp-$Rf3JsMo#{B~kou1f6pSStr zOTB;NvD7%%?=H$fB9V9Ha)~nEI8-^BOT7DPGHH5IO2dM4ijhyBBk#OLE>;T=0T$f= zi8eM5*M$IAUppAMWbAqZDhj0T6d;CA@oKWO6|ovzxaNA@Uqu?kjmK*r&y!{leO=3p zaoezW@f;|6VI<-ds*?`Mhdm?pCr}ovyL)HuXgU0=BRi~NUsJsTWW{?D{3PV#UdJGx zfS*$Q$jSd}?>nQK?7B8Z6dy!DrAe32kzS>PQbO;&OAS?eFVa+csL}<3^xhMSfHY|m zdRIE3mjIzo(C2x-_xonn%$oT#KW6^k>t5&NoO_?WuWRppo$L<;@B0sZQG9_b4{I?$ zgz{i;q2+qs#yC(QU0FD#7Yhfp!Yg^gzrE|*c1K5#S*tIJ;*5t*>Fs_bEBdo2x@?RvTgNveQ zEzJsHBK!OaDsQZLuG+~}VZ4|5is(sFnk;T%YGp<9<(eVw7SX$LdX^e zCF*m6_(fL2`q6!F=LgK%APf#^Fk&5)X`1)WFi6Wh+<@;+N2;tA`Y?A)MrItZ{EXZs zfRarqo+@gU6wgR6U1MPRU$_Zm1xPYX3EOCr$$}L59VaC|j$T6!Zv{53=d})tmOth~ z%+#eF{;OE_8d;X$qfbs=@A<6t%uVyjXv=@?l{lbTK`!l*-I=#oJ$XI3+avo$?qeAY zUkI0q+v_i`#HsrAnh$8v!PuLI|2J5)troo%(+R(h!{9d^f7VldQyT7uX7wAB+@ z+M?5)c(1~B17?2npcMXQ&R3qnpSc>k^j$Mmr2LioWm#h3FUt<=pe zyfNs?ug80B5HXHZi8kopGGBJy=Z}Cbp* z#$rh`+_Or0c6tHfdPkz*6VxKoWUMBr(XeA;s6ABfi#@`2abBGNedEDCGujTz5+ATt?>3(=OxB&7J`^9)^3}n=JCnH9 zx^AkbRKcf?Y0JQ>!!pQo@wFE((y`T)VbOW&9id4+JmONknxC>6kr8Nb{O*<4oH4?a z#GL;n5Z#~qSW~*JPT-Tv7b&W?hV1KSv5$V;lUN@4DQ}*gPnzaQ$?NJQLF{yjKk2r! zLu^oV_UO|P%s7WNc5dy5U|6cdDn}TQNZ*Oe0|X-2YB-4B1W{MRZG6a&%-5t+cSmKlozhpKvTVLGr+gHzU#fU-rip? zJ4oz@no_?$)jX{9al(fU`jjKju@U}KY0-Rs-WydfX??^@hr*|{$J925Gz zWk@dMIckHK`~nw@&XB_JUSN(n;@E8855lWi??q|BVrmdj)W$R%>DY3Cdm91l1^GT( zb+jHHuI%{J5A{S8R}+aHv9TssB8v4uht`ZTq*1~dE?1Z}#l*(Ldq#gTJ`kXKH*`ctZ+%uvZ|x)wJ$k8<=-NPyn+-jaldr!GE?cs&2jnl=UlM@R zb#$M(`X5Q;vG-$6so1b3Y8!j47`-m2GLJ$lt*S65mO_{hor!M`XL4c|%x5P#YT&XW z5E&97zhtn~@NIP9Qp>J^E`Rmmg-c?0zC38;z11MBEj8Nm|Coy_EiQ1{i)e8su z_;)Z3EzF6_c0$z4NG$Ye&FgFm;ISF>nY7ls-*&!xDpDAS4*cPH6UDXg+uh5-_%(W>#MxIZj#5fl6owVVn3tH6spb7%}#TAjTYlN>+A&( zYwRC*b(tPuJZy{+UWw;W9jFO3>9?RITUgh`1 zS9Cj(%huZE3uoux=^>@`rFqLbbH# zMro`q91b{^B$ep|M|6VBjEw7k3+vu{Y(G2fB}27(7X4ZS2aH`+U~kuYw#y@cXv&i& z0YUQwrRQjB9>nk%T_BYp_<$x5pRuLLzRt!N>XGW9avE~|F<+IRV`2Un2zzneGweOP zcVSG^^O^+D=&why{fR^ZA9#Zr$eKz4xyw8?F4ST^=d(_Mmsh8O7&dDSi#N#5SaF`| z=D_QN6u)7U%U9vvhA`Ke7n-(9=xG;hL8O9F+n4D4^5Pvg(las8~$_~ zEwX)6=pN)bT$QjW>VL8fW1jE(_H8w5)KspcE||!5*i}xxUyKR`VxGVB*&fJa6bJS$Ugbo*RUbu|^P6o} z!R-6;ogQzkK(bhTUQ`IwX~QX$6zlVWLwA7ftD>xhg~;gdABN5ABFr?UER*E`M12@I z{M$Lk9P?x#B@mx56DD&8x0DYE8CJW5gI&kU8j{ZdJW%3&Yv3Y`nTs+Sm^~x>kl_b|@ zTKMkTMD5Ja#;XJs@r2rT5NsvL+s;$HB4szK7GY{98VxFloFxon8Eu(VCbcRoo1c7x zfc8hRNOH|gyxsrqKQtPS9G&2&X#MFXqQH6BVK~A83Emvd%U?3{ZBVMAD9mpY(mnt3 z7$iZBzJ=Dt=S=a{uCgSAFVfhG`dJ?LuFbE`M)&a76BI6)2frlPFlW>8v&YxYoHDGY zRSbIGZt(i<1q=N$H3p4QZB<$MyB!Y}2pYdw$By)~ikj`<`f@(wBV$fXtyKJ!a5@KO zWk6{qn^DnUGD7{=BrZ@%(t)DAN!Pn z(9X)KT4wMQDzzlzalA|-uZqW9n{q5be*jWY!_GyqqPWILK#HMb^|ET28XisRDj%t& zGA-YG$CiE)tE7lssfP4vdn@Bl^PEVK^{0-5oh4(`V7w58sfddD0Uh~^B%Q!v7&TYpbB;BIvK->4an znqx=pBrgrad2toHb*Ud(#{)j{W+u_#e=}dIC*3T2pYQ-u+j#k-32}Btt|dW-!nEL&g_eZeQNP)kya7@z{labl*1>Qj?OWy(C9Y0a z%9uds8kLfP5tkd^Im}Xi@|QwPvk7w_cmE;sTVcBED4cY+~oD@ik^cA03hqo;R*I7BSjk)>Yuj4A~y_? zjM>a~rAo~|dDnSLVta-Sn|e4hm1NXk#HR)8oQ`Mg8L|2Gz2sDO5A_W<6$2$yRhehp zS}hT)v6b0)*AK{kRD_4&Nj5h%_n7nW6GZN?&P%o1Gt1j<4N4mwN~~p`Xz;CFx|n`e zE0Ql4UE7EoG9x|HZ^q3ZhW1las~(jxr>d`#OY*;$;@~<&dfo#k&mD4GZ9Q)xdRLd5 z5`Db_oubeo*|ROK`IbW4H1S%p>Lz>^{U%%ExS;jg36HCMyv7sL`)p(0sMLnHfHu!~ z>5nJf-3D*yt?xjXDnPxx6++bUUN17Il{Z@8_xbB56Cw&Dzi=$RLc3BPI3gXctT;JN zZt2S}RQ92h^?M8yslhyrw#M|HJax4+%CrESMyygbQf5;94flMG1=_73f~ zNMW)dJR|2-N16h}+`4rWo)4BjS#OWaz>-ZPc)oFIC4HG=E|CxI}(vZZzuC~v;>O@^Tsj_zn z$2HU3a?HRWaNq5H5TL$L_t|QzSFoOmgA2Al^w7&fPQyXzB&o~NBHfEj-!A) z6|ksz&V9USvqh0|sp)A?=dnJ-!Nxa_7>q@!t_)x^c?@dcVG(jXeB+Bi!so$QcPcE? zx3YY$Y44DkS9_)2A1865leNJ;FxdUC=r&>>*G;YcyHql*_{6(6SPr2=60kuutcIStd|L9Q%%fk4FffaZ+-eE+quIak`9{|9&_?4W5vx%( zIKEAgl%#!78Dj65CFHL5UFYUEP>6S#xb9?>D^B8?R=GJis%X2xmEeiZy3tp=nA%f5 zxk@WIq$fV;<%DH2Y70S0QmP97EJ=pFB;%4BfA7v@rmw@KgRukbNsv`?sjjfyNSawf z84($mf8Tae%==*3HAqi&LRH`#D-4z z-UFbU8Ex*OH~bFoyZ^=OUk%t^6!dcJho`8-QCQp|l`$gu>5rbntHk@_J|jcH)%liF zAi7w~RR;PW`n+nBwbtdK?ogsP04K;=;ZZnXFPmFEtUbS-z#HmqidBd47Q;d70?JeL2;ZSC@uV659g5| zW&ZWK(8w?z>cltJB;d-3hC2$%{`KOE5d<6;Z+LzV=0A~8-s+!#VOOw<3xDt5!685f z9HCo*s8nS+@%#ZV;GwhYDLftgdPS#t{t5I>1sr>hd_+yHcH-Npkt~bpSqm#!p_tJP z2Z9b;@iihh0Q@jUY4;S#7PDbFk)wv_6er8&+k5z4sM_3keQ5SRm|@wPW*kNYnqOVn zbma&*m(C$57*?CS@3q(r1`_D#I?uj8;hgc4lBQco1>?hidN+p=5=pc%@HnF~Q zt$X(PP||66L?QNDwUnI}U0{RT@pGfh>rSi2ZNoQXWFUCGgyT|(&h9+_pv&jvYlNv} z{`pF+7{8?72+K!dG4p0SQ~q>lUxn(YY4xGi4#R`jY^h?R`tmGEn+tXYk9fu=cI~*_=PhNDjx}uP*R5%4 z$l=QEfvq!)?{U77V;hK9t~vy*ML=pI%OJJ-LtGz#!cS5S64ab_3Mw%#G750F3d$T_ z`n7MKCCl29zF>KKeAY0Wbpv_)$C{5AXrm1Rher%8x8L1$G&)&u7NVUvXH0UI-$k+V z>@^Hz4_Z=C<#Sz#XuOOkubO!q=MV9^hyX;3{-l$~6ctEIhm5P5^6+m&jzORn6l;P!d2sn%D1nL!wDoNV=R zyq#H9V+Am&Wkun18 z?5YtC03~V9+}S~Uejbhi$4}?AJc>;(_#aeootc8gS5>?4s02~_2lx*uMO=b!&&t}M zjDFXzVNKQyjmCp=E1ws4^~JRN#QF8?^f((@&%SygIoc`!L=$xtxQdi}wWUmq@X&;` z@YcSK`wu$gS9XtAz1kr&zLzn7Azr@P#x4S%K-kob)4<4VUUYDT&1HzVhfx`?zv_5@DH6sEugbJefv8w}#xnVR&LJKJ} zkb8E8fq}n=l|l&-MMx9@xQeaQnY}uJ47S`kBlZpT?#l^=+G4k5AYwzI-x?H z;1RRY^h37Mq)#J`?&9`Ps;fxKKkyA0%x(;I?pm+AtQ^4J&GzWx513>dGhlUNBW^3?+un5@A;ZtVBpK%SVFM%pp~Qh&sUkr1=0t+UIxuJL`AXO)#ybgoF_6M z4H3)ykK&Pu-KRBNHs10H$V|Lxah%loIG=};DYp7MgHNM>`Le+YW%b9BF}SHh=xW8W zFpB=1kcYHNYY`6{x8JVwiK>106A8U(!QC7$9tZw%`1&eaq`ENqmzZ|ovCPfcE4-SQ z!pF1GFIxK^m#e$mLc2dPC)Cjew5)n?-?|b%d2=m#?{a<(@+H>4u#3=r(AxJ6PA5#3 z`oRHTtA-fPYm~oK6|+QkocKF_21H?uFfx^QsHisYu^4=abUh-0P~>652}Gv~J5iY1BJFyDL^D{miEzcfF2bIC~Fq zW#r!C)4*i(rrcX39XUIkg_EhByiK5B<&_nj>t(gJyeAwzR{J)B$UpfDC!Fc;ZMyH< zQyl9N(^v7P;J$D)+y-R;m^+KM%Ed3SRrfd9tl{OSqb-M^ZJ}jAoe(p3 zuL*aI-uKh5>eoxYj_~>)YmU!a``nIOMSl6M_JM8z>o=2C5|&fLTf~~FpiOFDW8Dv- z@vlQhb(c5C0Rk4{wF;!j1mov8(f2vYwA9_E3eHHd+a_qP44)Z$sRQ+esDcVO}OvqJLs}3Hq$$G;I%oQUsY7*hK!yl==f2KUH7Q8 zT{08tNnypAjk#ki2*iVBmNv%$YygBK7HvhXp)v$J;ECfEJ@mzg!2SSyKv{@|bV{jE zW>?0P$F_&DoV4EYG(7`ZLQKO=^>E3pOLPwtYIw6B4fK7mUNMPgS&yFo`Mkz5`rf!7 z8K0`hE12-WC9waDGY&DS%Y3P8qTuvXwkD$@>BIG#fP%i?KQq$+Jm@gMRw!?s_{A~_ zC`}Ch7haUrC%%QT3lALtY&f@2c8^sGUC90+MIxmy=dHvCduRXG?PI4nrjJ0nq}$}> z&&(5jFfkQrBpUMuHAumLv^4Zhn$D2?#bGw~5*c)o^DFrz8Hnx^P^3j4G9U9n^7fHI z?jPJ&%mnW+hjY6|^cNTFBG0;M@7!tDqxdC2B}WG~1sq7dl6NTz2Ev?q0D{PXfG0J% zX$GCYKB}XC=kKYKei*!EJ4wPO_oe|0?tAPX~A%Mt!5{D<~v}zyl z4)__sObYPv9>MK4RXhfLEBhc!hwX~+uV^Hhc|9?0p; zWU{A90RrTDK9^(pV!0NFQk0p54Ktxf_qlypM_cT6hOtv}6T#b_tOb{Ua=!nY4C4Rt z>oc-wtN04rvV>ewGbn!IH0wByQCR}!iG4%ow3`k7b2|Hf`dfpvBkaf$BFL6-KIZ{p zz|q3YdtM|o@F0fox=K{o6b+TO8~5Nsa0y!-j0Lt2ysN*)__V4J{VxRRSNVQOLX03F zi2GQ8JWB~kh^)^ArnloFIJDWA-Pw+lj-P=U2K+rJa0`ejM_$yDBo*)&-4mGeWJFAo zX5rnW1Pz=C&JO1d3k=Uucn(hty?kf&aCgW(dQgQ*6on!5p*E5i&1bGD+iAhC9lysg zVh%r$(VpbHIXiN?IohHYzd2Hg3#t9U8}a+a7~Z&A@XW0CitJlq7JeG~or45xv>FMm zu0Za!U0(_GO6p6INzX{9$>=lQ40F0+OQprf$wn8aLDyu5KNH;k$|55o=5ml5PKigW z+o{3gEPnRQqf7~XSu)@Z)4FTj*oC08ToJmxgVF6Sh!XU?uH;AEW01VMar+vdSnfh; z4(;EA$g$f#KV4FiZE^kH)ZD}IXK*=kIi4a);wER|_{HO&DJFsMm7kZ<;zB%#pu_+r zIr;C!}#l>$3 z{yd-2+R*>NOV7+wX6v_4C=|^bH>34L4N^88(46}V+l>tL`4R}*Sbe?Q{{sYhH&*^? z@)d*`yNUKFJ-d19d;Hz2gef$!Ws0gLnvEf>N(V8vPxN{j(bX*=ftahZGp8vc*;o4- zDZn`^p((xueI15XYRvywzpi?pozEr1+l1iqxgmdGk*5p@oslH2E5CO;SHuT^!)7w( zG?7TY>7vNqC5lNy6p7D!fk!Ghi?XoCwN%vkXe8P@#UG`==L30nNV_wDQi#pI&NH7X zU^vwrG4Reyg#w>1^nIJ7kN&Sys~SqSD80C^1o&skY%{e2d9~#i;|nieD#`kWBEgkg zMMT)3P2=?wMH6?i@~g^n7wfL)dE)|w$Z}aSCULlOmgo%{=u@h@+1H-;K6zbN>Q6q) z@ifAfm&;Q*Zz4$!)gwU$++lHJLtD%W3D*`XcHy4mn;%IAg7`DzP2o^hdoQsa z$>*Ku#`10Dg%tuCJFF78M)9sVh`){o(iY=#fm>;bV= z&S@{M_sz{!pJO9Vye4b7q`{t;#CY1&ZQ`<=tc+&!Z*fP%tT}o}79t+aH6kh*nb#Pc zDDutCT&IQeS3IV=^cccxhKRb>!#F5yEC{9oIOgB+ss=zPg*+n8Neg(3V%89@6=Mgk zc+r~aE>K0?l>#BW5i}F64(gISts>9;@}{0oT6Orwsx@c-hB!hmtqgRMx3?%w zo>NVK^6u{!&q=Gsv3!p9Us_Uxpqh!sZ=A)@`OPp}vd9?KEr(DJQr-dn#DUNOfgjm> zH036hJbSPusHxajv!IE~kWGilhcn!&)(0aOODU5c{>5CAJmJp12SJkkmxZv6-+sX1 z2E2?L9ZOUkIX7owzFncw?ltP{MS?uU; z`oQEuB@gGkPs0_K28E*dOs1sOo1I4&Q}e`=%NcA~Gd&O22pZB6tjH1vm^K{GwI!*2 zXY`4pG-chl&%YdMCCf|y$N48t9OoDDBACLagFjPp zh-j*!k}O(f)BR}gRt)jtb&C&^V68AYCs}@yu^?3ad}3HUC*t4s zEW6_E!(meo;n)h_5=EDYf-#=M;cZ>sxyD#46|HsyE0&gZ!{=Q;1+mun-z{;G2m}yOL2E{wDM>c6vZzwAkyOF#y)4Z zUt1Z?ua1>u(UvQ|VScKgjH%Q!xJcdQmz1v##!`npG{OOYp>x^oosW_RutklWmtFTH3a0%7QMYRET^&oMaqTb5>fx=Hdh(Dn5Qvzv&-X9x6;au~0r zY{HT76YMnBZ3BcEA_EYEY%&IEtPDwk(^S3W-Qm9vfQMlgmD&v3lYPkYQG z%YY@WMW@O!y`OBKPV4t7_?%SU%(BJ3S=5rPq397O|3a ziL-v|2O?8yL-CVq7Gd-l{^)3Uo`=`6)S33U{sTi}nG-Gw$KaMGw8P!Ew?c|K3FDxXKQD`f_>9a468rSgnH{Ml zU2B3XJ;qU8tt4r)3o!vzFgcpP!(>2sbxjloU)fs&`~5rnywpoA8)L_*TSmlN<2qS~ z@UA?TBb>M!4`Xt)22-VJ+iK^1*;xxp>*oPs3Pc+~%wK%Bj!w8DRA2LeQotU9e;Za*5rQBd$~AYD{3Fv=VL z3C78^`m{B0858yB{QFnGrZ%rGS8}m2y$#y5Hs^k8)Yy}7sJ>7)0ZFDjHWBe_+PiI8 zf<8g5$>FWjAH024q>{q(u>lTX667-|)oqh`#= zKa)@z%wSCKo$?$mb+p~gIr*FXBOj=E{>AhrG9pZEHaM|Lm+SnNlAMntbC-tQG|RPKz7lKdN3mD9%iSZjqy`TY4Gko`n@rZBZ+ z=^-Ls{_F=f#QhdJI&`j;sbt+8qw@$keRu)9s`SZFX#>^*b}Atd4bwkF>6ZBbcgi5P z+MQwj3$uov6;(ARNFD)mw&wR4<9E8`Pc3li2GgW`#*H Date: Fri, 14 May 2021 14:39:57 -0400 Subject: [PATCH 5/9] GP-42 change pdb logging enablement to jvm property, fix button tooltip --- .../bin/format/pdb2/pdbreader/PdbLog.java | 3 ++- .../pdb2/pdbreader/PdbReaderOptions.java | 13 ---------- .../PDB/src/main/java/pdb/LoadPdbTask.java | 9 +++---- .../PDB/src/main/java/pdb/PdbPlugin.java | 5 ++-- .../pdb/symbolserver/ui/LoadPdbDialog.java | 11 -------- .../symbolserver/ui/SymbolServerPanel.java | 25 +++++++++++++------ .../Common/support/launch.properties | 3 +++ 7 files changed, 27 insertions(+), 42 deletions(-) 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..7f92e23c20 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 @@ -97,7 +95,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 = @@ -114,22 +111,12 @@ public class PdbReaderOptions extends Exception { */ 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/LoadPdbTask.java b/Ghidra/Features/PDB/src/main/java/pdb/LoadPdbTask.java index 09ed60a6c9..9d6fd80aa3 100644 --- a/Ghidra/Features/PDB/src/main/java/pdb/LoadPdbTask.java +++ b/Ghidra/Features/PDB/src/main/java/pdb/LoadPdbTask.java @@ -24,7 +24,8 @@ 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.pdbapplicator.*; import ghidra.framework.options.Options; @@ -39,18 +40,16 @@ class LoadPdbTask extends Task { private final Program program; private final boolean useMsDiaParser; private final PdbApplicatorControl control; // PDB Universal Parser only - private boolean debugLogging; private String resultMessages; private Exception resultException; LoadPdbTask(Program program, File pdbFile, boolean useMsDiaParser, PdbApplicatorControl control, - boolean debugLogging, DataTypeManagerService service) { + DataTypeManagerService service) { super("Load PDB", true, false, true, true); this.program = program; this.pdbFile = pdbFile; this.useMsDiaParser = useMsDiaParser; this.control = control; - this.debugLogging = debugLogging; this.service = service; } @@ -136,8 +135,6 @@ class LoadPdbTask extends Task { private boolean parseWithNewParser(MessageLog log, TaskMonitor monitor) throws IOException, CancelledException { - PdbLog.setEnabled(debugLogging); - PdbReaderOptions pdbReaderOptions = new PdbReaderOptions(); // use defaults PdbApplicatorOptions pdbApplicatorOptions = new PdbApplicatorOptions(); diff --git a/Ghidra/Features/PDB/src/main/java/pdb/PdbPlugin.java b/Ghidra/Features/PDB/src/main/java/pdb/PdbPlugin.java index b3b65364c0..4180b875b2 100644 --- a/Ghidra/Features/PDB/src/main/java/pdb/PdbPlugin.java +++ b/Ghidra/Features/PDB/src/main/java/pdb/PdbPlugin.java @@ -143,9 +143,8 @@ 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 loadPdbTask = - new LoadPdbTask(program, pdbFile, loadPdbResults.useMsDiaParser, - loadPdbResults.control, loadPdbResults.debugLogging, dataTypeManagerService); + LoadPdbTask loadPdbTask = new LoadPdbTask(program, pdbFile, + loadPdbResults.useMsDiaParser, loadPdbResults.control, dataTypeManagerService); TaskBuilder.withTask(loadPdbTask) .setStatusTextAlignment(SwingConstants.LEADING) .setLaunchDelay(0); diff --git a/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/LoadPdbDialog.java b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/LoadPdbDialog.java index f0c01be21a..5c1f1ae32d 100644 --- a/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/LoadPdbDialog.java +++ b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/LoadPdbDialog.java @@ -71,7 +71,6 @@ public class LoadPdbDialog extends DialogComponentProvider { public File pdbFile; public PdbApplicatorControl control; public boolean useMsDiaParser; - public boolean debugLogging; } /** @@ -94,7 +93,6 @@ public class LoadPdbDialog extends DialogComponentProvider { results.control = (PdbApplicatorControl) choosePdbDlg.applicatorControlCombo.getSelectedItem(); results.useMsDiaParser = choosePdbDlg.msdiaParserButton.isSelected(); - results.debugLogging = choosePdbDlg.debugLoggingCheckbox.isSelected(); return results; } @@ -139,7 +137,6 @@ public class LoadPdbDialog extends DialogComponentProvider { private JRadioButton universalParserButton; private JRadioButton msdiaParserButton; private GComboBox applicatorControlCombo; - private GCheckBox debugLoggingCheckbox; /** * Creates a new instance of the LoadPdbDialog class. @@ -549,7 +546,6 @@ public class LoadPdbDialog extends DialogComponentProvider { universalParserButton.setSelected(false); } applicatorControlCombo.setEnabled(universalParserButton.isSelected()); - debugLoggingCheckbox.setEnabled(universalParserButton.isSelected()); } private JPanel buildParserOptionsPanel() { @@ -577,10 +573,6 @@ public class LoadPdbDialog extends DialogComponentProvider { applicatorControlCombo = new GComboBox<>(PdbApplicatorControl.values()); applicatorControlCombo.setSelectedItem(PdbApplicatorControl.ALL); - debugLoggingCheckbox = new GCheckBox(); - debugLoggingCheckbox.setToolTipText( - "If checked, logs information to the pdb.analyzer.log file for debug/development."); - parserOptionsPanel = new JPanel(new PairLayout(5, 5)); parserOptionsPanel.setBorder(BorderFactory.createTitledBorder("PDB Parser")); DockingWindowManager.getHelpService() @@ -594,9 +586,6 @@ public class LoadPdbDialog extends DialogComponentProvider { parserOptionsPanel.add(new GLabel("Control:")); parserOptionsPanel.add(applicatorControlCombo); - parserOptionsPanel.add(new GLabel("[Dev] PDB Reader/Applicator Debug Logging:")); - parserOptionsPanel.add(debugLoggingCheckbox); - return parserOptionsPanel; } diff --git a/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/SymbolServerPanel.java b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/SymbolServerPanel.java index d811efae8b..17d932adf2 100644 --- a/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/SymbolServerPanel.java +++ b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/SymbolServerPanel.java @@ -15,15 +15,14 @@ */ package pdb.symbolserver.ui; -import java.util.*; -import java.util.function.Consumer; -import java.util.stream.Collectors; - import java.awt.BorderLayout; import java.awt.Dimension; import java.io.File; import java.io.IOException; import java.net.URI; +import java.util.*; +import java.util.function.Consumer; +import java.util.stream.Collectors; import javax.swing.*; import javax.swing.table.TableColumn; @@ -200,8 +199,7 @@ class SymbolServerPanel extends JPanel { } private JPanel buildButtonPanel() { - refreshSearchLocationsStatusButton = - ButtonPanelFactory.createImageButton(Icons.REFRESH_ICON, "Refresh Status", + refreshSearchLocationsStatusButton = createImageButton(Icons.REFRESH_ICON, "Refresh Status", ButtonPanelFactory.ARROW_SIZE); refreshSearchLocationsStatusButton.addActionListener(e -> refreshSearchLocationStatus()); DockingWindowManager.getHelpService() @@ -226,7 +224,7 @@ class SymbolServerPanel extends JPanel { new HelpLocation(PdbPlugin.PDB_PLUGIN_HELP_TOPIC, "SymbolServerConfig MoveUpDown")); - deleteLocationButton = ButtonPanelFactory.createImageButton(Icons.DELETE_ICON, "Delete", + deleteLocationButton = createImageButton(Icons.DELETE_ICON, "Delete", ButtonPanelFactory.ARROW_SIZE); deleteLocationButton.addActionListener(e -> deleteLocation()); DockingWindowManager.getHelpService() @@ -234,7 +232,7 @@ class SymbolServerPanel extends JPanel { new HelpLocation(PdbPlugin.PDB_PLUGIN_HELP_TOPIC, "SymbolServerConfig Delete")); - addLocationButton = ButtonPanelFactory.createImageButton(Icons.ADD_ICON, "Add", + addLocationButton = createImageButton(Icons.ADD_ICON, "Add", ButtonPanelFactory.ARROW_SIZE); addLocationButton.addActionListener(e -> addLocation()); DockingWindowManager.getHelpService() @@ -591,4 +589,15 @@ class SymbolServerPanel extends JPanel { return false; } + private static JButton createImageButton(ImageIcon buttonIcon, String alternateText, + Dimension preferredSize) { + + JButton button = ButtonPanelFactory.createButton(""); + button.setIcon(buttonIcon); + button.setToolTipText(alternateText); + button.setPreferredSize(preferredSize); + + return button; + } + } diff --git a/Ghidra/RuntimeScripts/Common/support/launch.properties b/Ghidra/RuntimeScripts/Common/support/launch.properties index 560263c377..7ea1cc87f4 100644 --- a/Ghidra/RuntimeScripts/Common/support/launch.properties +++ b/Ghidra/RuntimeScripts/Common/support/launch.properties @@ -107,3 +107,6 @@ VMARGS=--illegal-access=permit # Limit on XML parsing. See https://docs.oracle.com/javase/tutorial/jaxp/limits/limits.html #VMARGS=-Djdk.xml.totalEntitySizeLimit=50000000 + +# Enables debug logging during Pdb import and analysis +#VMARGS=-Dpdb.logging=true From 522ac50dfc33152ece00f26aca591998d4f489b0 Mon Sep 17 00:00:00 2001 From: dev747368 <48332326+dev747368@users.noreply.github.com> Date: Mon, 17 May 2021 14:37:16 -0400 Subject: [PATCH 6/9] save --- .../format/pdb2/pdbreader/PdbReaderOptions.java | 16 ---------------- .../java/pdb/symbolserver/ui/LoadPdbDialog.java | 5 ++++- .../Common/support/launch.properties | 2 +- 3 files changed, 5 insertions(+), 18 deletions(-) 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 7f92e23c20..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 @@ -32,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. @@ -81,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); @@ -94,8 +81,6 @@ public class PdbReaderOptions extends Exception { public void loadOptions(Options options) { - pdbLogging = options.getBoolean(OPTION_NAME_PDB_READER_APPLICATOR_LOGGING, pdbLogging); - if (developerMode) { oneByteCharsetName = options.getString(OPTION_NAME_ONE_BYTE_CHARSET_NAME, oneByteCharsetName); @@ -110,7 +95,6 @@ public class PdbReaderOptions extends Exception { * Set the options to their default values */ public void setDefaults() { - pdbLogging = DEFAULT_PDB_READER_APPLICATOR_LOGGING; oneByteCharsetName = DEFAULT_ONE_BYTE_CHARSET_NAME; wideCharCharsetName = DEFAULT_TWO_BYTE_CHARSET_NAME; setOneByteCharsetForName(oneByteCharsetName); diff --git a/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/LoadPdbDialog.java b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/LoadPdbDialog.java index 5c1f1ae32d..ee0c1a001b 100644 --- a/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/LoadPdbDialog.java +++ b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/LoadPdbDialog.java @@ -546,6 +546,9 @@ public class LoadPdbDialog extends DialogComponentProvider { universalParserButton.setSelected(false); } applicatorControlCombo.setEnabled(universalParserButton.isSelected()); + if (!applicatorControlCombo.isEnabled()) { + applicatorControlCombo.setSelectedItem(PdbApplicatorControl.ALL); + } } private JPanel buildParserOptionsPanel() { @@ -647,7 +650,7 @@ public class LoadPdbDialog extends DialogComponentProvider { return hasPerformedSearch && advancedToggleButton.isSelected() && remoteSymbolServerCount != 0 && !findOptions.contains(FindOption.ALLOW_REMOTE) ? new StatusText( - "Remote servers were excluded. Select \"Allow remote\" checkbox to search remote servers.", + "Remote servers were excluded. Select \"Allow Remote\" checkbox to search remote servers.", MessageType.INFO, false) : null; } diff --git a/Ghidra/RuntimeScripts/Common/support/launch.properties b/Ghidra/RuntimeScripts/Common/support/launch.properties index 7ea1cc87f4..19a22926f8 100644 --- a/Ghidra/RuntimeScripts/Common/support/launch.properties +++ b/Ghidra/RuntimeScripts/Common/support/launch.properties @@ -108,5 +108,5 @@ VMARGS=--illegal-access=permit # Limit on XML parsing. See https://docs.oracle.com/javase/tutorial/jaxp/limits/limits.html #VMARGS=-Djdk.xml.totalEntitySizeLimit=50000000 -# Enables debug logging during Pdb import and analysis +# Enables Pdb debug logging during import and analysis to .ghidra/.ghidra_ver/pdb.analyzer.log #VMARGS=-Dpdb.logging=true From 4861a84aca7fb3d0c3efb1c5d680eaa652d5db14 Mon Sep 17 00:00:00 2001 From: dev747368 <48332326+dev747368@users.noreply.github.com> Date: Mon, 17 May 2021 17:01:33 -0400 Subject: [PATCH 7/9] GP-42 tweak symbol dir bootstrap logic, misc text & help --- .../main/help/help/topics/Pdb/LoadPDBNew.html | 9 +++- .../images/LoadPdb_Advanced_NeedsConfig.png | Bin 46023 -> 43566 bytes .../images/LoadPdb_Advanced_Screenshot.png | Bin 81388 -> 78576 bytes .../Pdb/images/LoadPdb_Initial_Screenshot.png | Bin 31278 -> 28637 bytes .../pdb/symbolserver/LocalSymbolStore.java | 51 +++++++++++++++++- .../pdb/symbolserver/ui/LoadPdbDialog.java | 13 ++--- .../symbolserver/ui/SymbolServerPanel.java | 6 ++- 7 files changed, 67 insertions(+), 12 deletions(-) diff --git a/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/LoadPDBNew.html b/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/LoadPDBNew.html index 9da6f81bd7..f60a94d9f5 100644 --- a/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/LoadPDBNew.html +++ b/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/LoadPDBNew.html @@ -184,7 +184,7 @@ 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 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.

    @@ -198,7 +198,12 @@
    • 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 - Process All, Data Types Only, Public Symbols Only.
    • +
    +

    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.
    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 index 5815e5b51ac790c9357358c3c9e4bd8a5af44ab5..949d45f8ba4ea33bd93e9597f3f1c809ead96940 100644 GIT binary patch literal 43566 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 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 index 949d45f8ba4ea33bd93e9597f3f1c809ead96940..d713737905d5875242372eebaf211e0b9e13aea7 100644 GIT binary patch delta 19888 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 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 index 74072b1047d5d74e40db864e3c2ecaf6f95aece6..14c0387613bd86bb15f384cee4a6d29e4a5321d6 100644 GIT binary patch literal 78344 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
      6KODzc3Cubo8O> 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 statusTextSuppliers = new ArrayList<>(); - private boolean hasPerformedSearch; + private Set lastSearchOptions; private boolean searchCanceled; private Program program; @@ -181,7 +182,7 @@ public class LoadPdbDialog extends DialogComponentProvider { symbolServerService.getLocalSymbolFileLocation(results.get(0), monitor); File symbolFile = getLocalSymbolFile(symbolFileLocation); Swing.runLater(() -> { - setSearchResults(results); + setSearchResults(results, null); setPdbLocationValue(symbolFileLocation, symbolFile); setSelectedPdbFile(symbolFileLocation); selectRowByLocation(symbolFileLocation); @@ -247,8 +248,8 @@ public class LoadPdbDialog extends DialogComponentProvider { * * @param results list of {@link SymbolFileLocation}s to add to results */ - public void setSearchResults(List results) { - hasPerformedSearch = true; + public void setSearchResults(List results, Set findOptions) { + lastSearchOptions = findOptions; symbolFilePanel.getTableModel().setSearchResults(programSymbolFileInfo, results); } @@ -320,7 +321,7 @@ public class LoadPdbDialog extends DialogComponentProvider { return SymbolFileInfo.fromValues(pdbPath, uid, age); } - private void searchForPdbs(ActionEvent e) { + private void searchForPdbs(boolean allowRemote) { if (symbolServerService == null || !symbolServerService.isValid()) { return; } @@ -336,13 +337,16 @@ public class LoadPdbDialog extends DialogComponentProvider { return; } Set findOptions = symbolFilePanel.getFindOptions(); + if (allowRemote) { + findOptions.add(FindOption.ALLOW_REMOTE); + } executeMonitoredRunnable("Search for PDBs", true, true, 0, monitor -> { try { searchCanceled = false; List results = symbolServerService.find(symbolFileInfo, findOptions, monitor); Swing.runLater(() -> { - setSearchResults(results); + setSearchResults(results, findOptions); if (results.size() == 1) { selectRowByLocation(results.get(0)); } @@ -366,7 +370,7 @@ public class LoadPdbDialog extends DialogComponentProvider { buildParserOptionsPanel(); setHelpLocation(new HelpLocation(PdbPlugin.PDB_PLUGIN_HELP_TOPIC, "Load PDB File")); - addStatusTextSupplier(() -> hasPerformedSearch && advancedToggleButton.isSelected() + addStatusTextSupplier(() -> lastSearchOptions != null && advancedToggleButton.isSelected() ? symbolServerConfigPanel.getSymbolServerWarnings() : null); addStatusTextSupplier(this::getSelectedPdbNoticeText); @@ -647,11 +651,10 @@ public class LoadPdbDialog extends DialogComponentProvider { private StatusText getAllowRemoteWarning() { int remoteSymbolServerCount = symbolServerService != null ? symbolServerService.getRemoteSymbolServerCount() : 0; - Set findOptions = symbolFilePanel.getFindOptions(); - return hasPerformedSearch && advancedToggleButton.isSelected() && - remoteSymbolServerCount != 0 && !findOptions.contains(FindOption.ALLOW_REMOTE) + return lastSearchOptions != null && advancedToggleButton.isSelected() && + remoteSymbolServerCount != 0 && !lastSearchOptions.contains(FindOption.ALLOW_REMOTE) ? new StatusText( - "Remote servers were excluded. Select \"Allow Remote\" checkbox to search remote servers.", + "Remote servers were excluded. Use \"Search All\" button to also search remote servers.", MessageType.INFO, false) : null; } @@ -661,7 +664,7 @@ public class LoadPdbDialog extends DialogComponentProvider { if (searchCanceled) { return new StatusText("Search canceled", MessageType.INFO, false); } - if (hasPerformedSearch) { + if (lastSearchOptions != null) { int foundCount = symbolFilePanel.getTableModel().getModelData().size(); return new StatusText( "Found " + foundCount + " file" + (foundCount != 1 ? "s" : ""), @@ -751,11 +754,10 @@ public class LoadPdbDialog extends DialogComponentProvider { SameDirSymbolStore.createManuallySelectedSymbolFileLocation(file, pdbSymbolFileInfo); Swing.runLater(() -> { - setSearchResults(List.of(symbolFileLocation)); + setSearchResults(List.of(symbolFileLocation), null); setSelectedPdbFile(symbolFileLocation); setPdbLocationValue(symbolFileLocation, file); selectRowByLocation(symbolFileLocation); - hasPerformedSearch = false; updateStatusText(); updateButtonEnablement(); updateParserOptionEnablement(true); diff --git a/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/SymbolFilePanel.java b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/SymbolFilePanel.java index b4848cfee5..2dc709a66f 100644 --- a/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/SymbolFilePanel.java +++ b/Ghidra/Features/PDB/src/main/java/pdb/symbolserver/ui/SymbolFilePanel.java @@ -15,12 +15,10 @@ */ package pdb.symbolserver.ui; -import java.util.EnumSet; -import java.util.Set; - import java.awt.BorderLayout; import java.awt.Dimension; -import java.awt.event.ActionListener; +import java.util.EnumSet; +import java.util.Set; import javax.swing.*; import javax.swing.table.TableColumn; @@ -39,6 +37,9 @@ import pdb.symbolserver.FindOption; * Also allows the user to tweak search options. */ class SymbolFilePanel extends JPanel { + interface SearchCallback { + void searchForPdbs(boolean allowRemote); + } static final String SEARCH_OPTIONS_HELP_ANCHOR = "PDB_Search_Search_Options"; private SymbolFileTableModel tableModel; private GhidraTable table; @@ -46,17 +47,18 @@ class SymbolFilePanel extends JPanel { private JPanel tablePanel; private JPanel welcomePanel; - private JButton searchButton; - private GCheckBox allowRemote; + private JButton searchLocalButton; + private JButton searchAllButton; private GCheckBox ignorePdbUid; private GCheckBox ignorePdbAge; - SymbolFilePanel(ActionListener searchButtonActionListener) { + SymbolFilePanel(SearchCallback searchButtonsCallback) { super(new BorderLayout()); build(); setEnablement(false); - searchButton.addActionListener(searchButtonActionListener); + searchLocalButton.addActionListener(e -> searchButtonsCallback.searchForPdbs(false)); + searchAllButton.addActionListener(e -> searchButtonsCallback.searchForPdbs(true)); } SymbolFileTableModel getTableModel() { @@ -69,9 +71,6 @@ class SymbolFilePanel extends JPanel { Set getFindOptions() { Set findOptions = EnumSet.noneOf(FindOption.class); - if (allowRemote.isSelected()) { - findOptions.add(FindOption.ALLOW_REMOTE); - } if (ignorePdbAge.isSelected()) { findOptions.add(FindOption.ANY_AGE); } @@ -82,13 +81,13 @@ class SymbolFilePanel extends JPanel { } void setFindOptions(Set findOptions) { - allowRemote.setSelected(findOptions.contains(FindOption.ALLOW_REMOTE)); ignorePdbAge.setSelected(findOptions.contains(FindOption.ANY_AGE)); ignorePdbUid.setSelected(findOptions.contains(FindOption.ANY_ID)); } void setEnablement(boolean hasSymbolServerService) { - searchButton.setEnabled(hasSymbolServerService); + searchLocalButton.setEnabled(hasSymbolServerService); + searchAllButton.setEnabled(hasSymbolServerService); if (welcomePanel != null && hasSymbolServerService) { remove(welcomePanel); @@ -149,13 +148,15 @@ class SymbolFilePanel extends JPanel { } private JPanel buildButtonPanel() { - searchButton = new JButton("Search"); - - allowRemote = new GCheckBox("Allow Remote"); - allowRemote.setToolTipText("Allow searching remote symbol servers."); + searchLocalButton = new JButton("Search Local"); + searchLocalButton.setToolTipText("Search local symbol servers only."); + searchAllButton = new JButton("Search All"); + searchAllButton.setToolTipText("Search local and remote symbol servers."); ignorePdbUid = new GCheckBox("Ignore GUID/ID"); - ignorePdbUid.setToolTipText("Find any PDB with same name (local locations only)."); + ignorePdbUid.setToolTipText( + "Find any PDB with same name (local locations only). Age ignored also."); + ignorePdbUid.addChangeListener(l -> updateSearchOptionEnablement()); ignorePdbAge = new GCheckBox("Ignore Age"); ignorePdbAge.setToolTipText("Find PDB with any age value (local locations only)."); @@ -168,10 +169,10 @@ class SymbolFilePanel extends JPanel { panel.add(ignorePdbAge); panel.add(Box.createHorizontalStrut(10)); panel.add(ignorePdbUid); - panel.add(Box.createHorizontalStrut(10)); - panel.add(allowRemote); panel.add(Box.createHorizontalGlue()); - panel.add(searchButton); + panel.add(searchLocalButton); + panel.add(Box.createHorizontalStrut(10)); + panel.add(searchAllButton); DockingWindowManager.getHelpService() .registerHelp(panel, @@ -179,4 +180,8 @@ class SymbolFilePanel extends JPanel { return panel; } + + private void updateSearchOptionEnablement() { + ignorePdbAge.setEnabled(!ignorePdbUid.isSelected()); + } } diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/PdbScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/PdbScreenShots.java index 63c56cb4fe..a59dd267c3 100644 --- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/PdbScreenShots.java +++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/PdbScreenShots.java @@ -15,11 +15,11 @@ */ package help.screenshot; -import java.util.List; - import java.io.File; import java.io.IOException; import java.net.URI; +import java.util.List; +import java.util.Set; import org.apache.commons.io.FilenameUtils; import org.junit.*; @@ -143,12 +143,12 @@ public class PdbScreenShots extends GhidraScreenShotGenerator { SymbolFileInfo.fromValues("HelloWorld.pdb", GUID1_STR, 1)), new SymbolFileLocation("HelloWorld_ver2.pdb", sameDirSymbolStoreWithFakePath, SymbolFileInfo.fromValues("HelloWorld.pdb", GUID1_STR, 2))); + Set findOptions = FindOption.of(FindOption.ALLOW_REMOTE, FindOption.ANY_AGE); runSwing(() -> { - loadPdbDialog - .setSearchOptions(FindOption.of(FindOption.ALLOW_REMOTE, FindOption.ANY_AGE)); + loadPdbDialog.setSearchOptions(findOptions); loadPdbDialog.setSymbolServers(symbolServers); loadPdbDialog.setSymbolStorageDirectoryTextOnly("/home/user/symbols"); - loadPdbDialog.setSearchResults(symbolFileLocations); + loadPdbDialog.setSearchResults(symbolFileLocations, findOptions); loadPdbDialog.selectRowByLocation(symbolFileLocations.get(0)); }); waitForSwing(); From 0f080eb80bc19420f770193d0877d6d53d0d8623 Mon Sep 17 00:00:00 2001 From: dev747368 <48332326+dev747368@users.noreply.github.com> Date: Mon, 17 May 2021 18:13:16 -0400 Subject: [PATCH 9/9] GP-42 fix html - remove text about remote option --- .../Features/PDB/src/main/help/help/topics/Pdb/LoadPDBNew.html | 2 -- 1 file changed, 2 deletions(-) diff --git a/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/LoadPDBNew.html b/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/LoadPDBNew.html index 2c916b99dc..6d8ff3b3d9 100644 --- a/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/LoadPDBNew.html +++ b/Ghidra/Features/PDB/src/main/help/help/topics/Pdb/LoadPDBNew.html @@ -177,8 +177,6 @@
      • 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.
      • -
      • Allow Remote - allows the searching of web-based symbol servers. Off by default to prevent sharing possibly sensitive information (PDB name, - GUID, age) with web-based symbol servers outside your organization.

      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.

      9NdkBcWgKJtAV=$vkDn+T- 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

    &Q>$#W_Y)}5kQVmxbl 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 z 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