diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/function/CreateThunkFunctionCmd.java b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/function/CreateThunkFunctionCmd.java index 6c5880e2fe..8737cd9da4 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/function/CreateThunkFunctionCmd.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/function/CreateThunkFunctionCmd.java @@ -327,7 +327,7 @@ public class CreateThunkFunctionCmd extends BackgroundCommand { Symbol s = program.getSymbolTable().getPrimarySymbol(referencedFunctionAddr); if (s != null) { ExternalLocation extLoc = (ExternalLocation) s.getObject(); - Msg.debug(this, + Msg.trace(this, "Converting external location to function as a result of thunk at: " + entry); return extLoc.createFunction(); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/program/examiner/ProgramExaminer.java b/Ghidra/Features/Base/src/main/java/ghidra/program/examiner/ProgramExaminer.java new file mode 100644 index 0000000000..e26091fa21 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/program/examiner/ProgramExaminer.java @@ -0,0 +1,232 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.program.examiner; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import ghidra.GhidraException; +import ghidra.GhidraTestApplicationLayout; +import ghidra.app.plugin.core.analysis.EmbeddedMediaAnalyzer; +import ghidra.app.util.bin.*; +import ghidra.app.util.importer.AutoImporter; +import ghidra.app.util.importer.MessageLog; +import ghidra.framework.Application; +import ghidra.framework.HeadlessGhidraApplicationConfiguration; +import ghidra.program.model.data.*; +import ghidra.program.model.lang.*; +import ghidra.program.model.listing.*; +import ghidra.program.model.mem.MemoryAccessException; +import ghidra.program.util.DefaultLanguageService; +import ghidra.util.exception.CancelledException; +import ghidra.util.task.TaskMonitorAdapter; +import utility.application.ApplicationLayout; + +/** + * Wrapper for Ghidra code to find images (and maybe other artifacts later) in a program + * + * NOTE: This is intended for end-user use and has no direct references within Ghidra. + * Typical use of the class entails generating a ghidra.jar (see BuildGhidraJarScript.java) + * and referencing this class from end-user code. + */ +public class ProgramExaminer { + + private MessageLog messageLog; + private Program program; + + private static Language defaultLanguage; + + /** + * Constructs a new ProgramExaminer. + * @param bytes the bytes of the potential program to be examined. + * @throws GhidraException if any exception occurs while processing the bytes. + */ + public ProgramExaminer(byte[] bytes) throws GhidraException { + this(createByteProvider(bytes)); + } + + /** + * Constructs a new ProgramExaminer. + * @param file file object containing the bytes to be examined. + * @throws GhidraException if any exception occurs while processing the bytes. + */ + public ProgramExaminer(File file) throws GhidraException { + this(createByteProvider(file)); + } + + /** + * Returns a string indication the program format. i.e. PE, elf, raw + */ + public String getType() { + return program.getExecutableFormat(); + } + + private ProgramExaminer(ByteProvider provider) throws GhidraException { + initializeGhidra(); + messageLog = new MessageLog(); + try { + program = AutoImporter.importByUsingBestGuess(provider, null, this, messageLog, + TaskMonitorAdapter.DUMMY_MONITOR); + + if (program == null) { + program = AutoImporter.importAsBinary(provider, null, defaultLanguage, null, this, + messageLog, TaskMonitorAdapter.DUMMY_MONITOR); + } + if (program == null) { + throw new GhidraException( + "Can't create program from input: " + messageLog.toString()); + } + } + catch (Exception e) { + messageLog.appendException(e); + throw new GhidraException(e); + } + finally { + try { + provider.close(); + } + catch (IOException e) { + // tried to close + } + } + } + + public synchronized static void initializeGhidra() throws GhidraException { + if (!Application.isInitialized()) { + ApplicationLayout layout; + try { + layout = + new GhidraTestApplicationLayout(new File(System.getProperty("java.io.tmpdir"))); + } + catch (IOException e) { + throw new GhidraException(e); + } + HeadlessGhidraApplicationConfiguration config = + new HeadlessGhidraApplicationConfiguration(); + config.setInitializeLogging(false); + Application.initializeApplication(layout, config); + } + if (defaultLanguage == null) { + LanguageService languageService = DefaultLanguageService.getLanguageService(); + try { + defaultLanguage = languageService + .getDefaultLanguage(Processor.findOrPossiblyCreateProcessor("DATA")); + } + catch (LanguageNotFoundException e) { + throw new GhidraException("Can't load default language: DATA"); + } + } + } + + /** + * Releases file/database resources. + */ + public void dispose() { + program.release(this); + } + + /** + * Returns a list of byte[] containing image data. The bytes will be either a png, a gif, or + * a bitmap + */ + public List getImages() { + runImageAnalyzer(); + + List imageList = new ArrayList(); + DataIterator it = program.getListing().getDefinedData(true); + while (it.hasNext()) { + accumulateImageData(imageList, it.next()); + } + return imageList; + } + + private void accumulateImageData(List imageList, Data data) { + if (!isImage(data)) { + return; + } + + try { + imageList.add(data.getBytes()); + } + catch (MemoryAccessException e) { + // suppress (this shouldn't happen + } + } + + private void runImageAnalyzer() { + int txID = program.startTransaction("find images"); + try { + EmbeddedMediaAnalyzer imageAnalyzer = new EmbeddedMediaAnalyzer(); + imageAnalyzer.added(program, program.getMemory(), TaskMonitorAdapter.DUMMY_MONITOR, + messageLog); + } + catch (CancelledException e) { + // using Dummy, can't happen + } + finally { + program.endTransaction(txID, true); + } + } + + private boolean isImage(Data data) { + DataType dataType = data.getDataType(); + if (dataType instanceof PngDataType) { + return true; + } + if (dataType instanceof GifDataType) { + return true; + } + if (dataType instanceof BitmapResourceDataType) { + return true; + } + if (dataType instanceof IconResourceDataType) { + return true; + } + if (dataType instanceof JPEGDataType) { + return true; + } + return false; + } + +//================================================================================================== +// static methods +//================================================================================================== + + private static ByteProvider createByteProvider(byte[] bytes) throws GhidraException { + if (bytes == null) { + throw new GhidraException("Attempted to process a null byte[]."); + } + if (bytes.length == 0) { + throw new GhidraException("Attempted to process an empty byte[]."); + } + return new ByteArrayProvider("Bytes", bytes); + } + + private static ByteProvider createByteProvider(File file) throws GhidraException { + if (file == null) { + throw new GhidraException("Attempted to process a null file"); + } + try { + return new RandomAccessByteProvider(file); + } + catch (IOException e) { + throw new GhidraException(e); + } + } + +} diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/plugin/core/analysis/PdbAnalyzer.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/plugin/core/analysis/PdbAnalyzer.java index 51b5f36323..969a09972d 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/plugin/core/analysis/PdbAnalyzer.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/plugin/core/analysis/PdbAnalyzer.java @@ -17,10 +17,13 @@ package ghidra.app.plugin.core.analysis; import java.io.File; +import org.apache.commons.lang3.StringUtils; + import ghidra.app.services.*; import ghidra.app.util.bin.format.pdb.*; import ghidra.app.util.importer.MessageLog; import ghidra.app.util.opinion.PeLoader; +import ghidra.app.util.pdb.PdbLocator; import ghidra.framework.options.Options; import ghidra.framework.preferences.Preferences; import ghidra.program.model.address.AddressSetView; @@ -34,15 +37,19 @@ import ghidra.util.task.TaskMonitor; * Finds and applies PDB debug information to the given Windows executable. */ public class PdbAnalyzer extends AbstractAnalyzer { - private static final String NAME = "PDB"; - private static final String DESCRIPTION = "Automatically loads a PDB file if found."; + static final String NAME = "PDB"; + static final boolean DEFAULT_ENABLEMENT = !PdbUniversalAnalyzer.DEFAULT_ENABLEMENT; + private static final String DESCRIPTION = + "PDB Analyzer.\n" + "Requires MS DIA-SDK for raw PDB processing (Windows only).\n" + + "Also supports pre-processed XML files."; 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 static final String SYMBOLPATH_OPTION_DEFAULT_VALUE = "C:\\Symbols"; + private static final String SYMBOLPATH_OPTION_DEFAULT_VALUE = + PdbLocator.DEFAULT_SYMBOLS_DIR.getAbsolutePath(); private String symbolsRepositoryPath = SYMBOLPATH_OPTION_DEFAULT_VALUE; @@ -58,7 +65,7 @@ public class PdbAnalyzer extends AbstractAnalyzer { //============================================================================================== public PdbAnalyzer() { super(NAME, DESCRIPTION, AnalyzerType.BYTE_ANALYZER); - setDefaultEnablement(true); + setDefaultEnablement(DEFAULT_ENABLEMENT); setPriority(AnalysisPriority.FORMAT_ANALYSIS.after()); setSupportsOneTimeAnalysis(); } @@ -70,12 +77,19 @@ public class PdbAnalyzer extends AbstractAnalyzer { return true; } - File pdb = lookForPdb(program, includePeSpecifiedPdbPath, log); - - if (pdb == null) { + if (PdbUniversalAnalyzer.isEnabled(program)) { + log.appendMsg(getName(), + "Stopped: Cannot run with " + PdbUniversalAnalyzer.NAME + " Analyzer enabled"); return false; } - Msg.info(this, getClass().getSimpleName() + " configured to use: " + pdb.getAbsolutePath()); + + File pdb = lookForPdb(program, log); + + if (pdb == null) { + Msg.info(this, "PDB analyzer failed to locate PDB file"); + return false; + } + Msg.info(this, "PDB analyzer parsing file: " + pdb.getAbsolutePath()); AutoAnalysisManager mgr = AutoAnalysisManager.getAnalysisManager(program); return parsePdb(pdb, program, mgr, monitor, log); @@ -85,7 +99,7 @@ public class PdbAnalyzer extends AbstractAnalyzer { // object existence indicates missing PDB has already been reported } - File lookForPdb(Program program, boolean includePeSpecifiedPdbPath, MessageLog log) { + File lookForPdb(Program program, MessageLog log) { String message = ""; File pdb; @@ -104,7 +118,7 @@ public class PdbAnalyzer extends AbstractAnalyzer { String pdbName = program.getOptions(Program.PROGRAM_INFO).getString( PdbParserConstants.PDB_FILE, (String) null); - if (pdbName == null) { + if (StringUtils.isBlank(pdbName)) { message = "Program has no associated PDB file."; } else { @@ -125,9 +139,6 @@ public class PdbAnalyzer extends AbstractAnalyzer { return pdb; } - catch (PdbException pe) { - message += pe.getMessage(); - } finally { if (message.length() > 0) { log.appendMsg(getName(), message); @@ -135,7 +146,6 @@ public class PdbAnalyzer extends AbstractAnalyzer { } } - return null; } boolean parsePdb(File pdb, Program program, AutoAnalysisManager mgr, TaskMonitor monitor, 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 0b6c2e8ce9..c87a874edd 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,9 +19,10 @@ import java.io.File; import java.io.IOException; import java.util.Date; -import docking.widgets.OkDialog; -import docking.widgets.OptionDialog; +import org.apache.commons.lang3.StringUtils; + import ghidra.app.services.*; +import ghidra.app.util.bin.format.pdb.PdbParserConstants; import ghidra.app.util.bin.format.pdb2.pdbreader.*; import ghidra.app.util.importer.MessageLog; import ghidra.app.util.opinion.PeLoader; @@ -31,14 +32,11 @@ import ghidra.app.util.pdb.pdbapplicator.*; import ghidra.framework.Application; import ghidra.framework.options.OptionType; import ghidra.framework.options.Options; -import ghidra.framework.preferences.Preferences; import ghidra.program.model.address.AddressSetView; import ghidra.program.model.data.CharsetInfo; import ghidra.program.model.listing.Program; import ghidra.util.Msg; import ghidra.util.SystemUtilities; -import ghidra.util.bean.opteditor.OptionsVetoException; -import ghidra.util.exception.AssertException; import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitor; @@ -61,15 +59,12 @@ public class PdbUniversalAnalyzer extends AbstractAnalyzer { private static final boolean developerMode = false; //============================================================================================== - private static final String NAME = "PDB Universal Reader/Analyzer"; + static final String NAME = "PDB Universal"; + // TODO: decide which PDB Analyzer should be enabled by default for release + static final boolean DEFAULT_ENABLEMENT = false; private static final String DESCRIPTION = - "[PDB EVOLUTIONARY PROTOTYPE V1] [OPTIONS MAY CHANGE]\n" + - "Platform-indepent PDB analysis\n" + - "(No XML. Will not run with DIA-based PDB Analyzer.)"; - - private final static String PDB_STORAGE_PROPERTY = "PDB Storage Directory"; - // TODO; look into what we can do with the following (old analyzer says this is a "thing") - //public final static File SPECIAL_PDB_LOCATION = new File("C:/WINDOWS/Symbols"); + "[Prototype V1] Platform-indepent PDB analyzer (No XML support).\n" + + "NOTE: still undergoing development, so options may change."; //============================================================================================== // Force-load a PDB file. @@ -82,14 +77,15 @@ public class PdbUniversalAnalyzer extends AbstractAnalyzer { private static final String OPTION_NAME_FORCELOAD_FILE = "Force-Load FilePath"; private static final String OPTION_DESCRIPTION_FORCELOAD_FILE = "This file is force-loaded if the '" + OPTION_NAME_DO_FORCELOAD + "' option is checked"; - private File forceLoadFile = null; + 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 symbolsRepositoryPath = null; // value set in constructor - private File defaultSymbolsRepositoryPath = Application.getUserTempDirectory(); + private File DEFAULT_SYMBOLS_DIR = PdbLocator.DEFAULT_SYMBOLS_DIR; + 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 = @@ -208,53 +204,51 @@ public class PdbUniversalAnalyzer extends AbstractAnalyzer { public PdbUniversalAnalyzer() { super(NAME, DESCRIPTION, AnalyzerType.BYTE_ANALYZER); setPrototype(); - // false for now; after proven... then TODO: true always - setDefaultEnablement(false); - // or... - // false for now if on Windows; after proven... then TODO: true always - // setDefaultEnablement(!onWindows); + setDefaultEnablement(DEFAULT_ENABLEMENT); setPriority(AnalysisPriority.FORMAT_ANALYSIS.after()); setSupportsOneTimeAnalysis(); - String pdbStorageLocation = Preferences.getProperty(PDB_STORAGE_PROPERTY, null, true); - if (pdbStorageLocation != null) { - symbolsRepositoryPath = new File(pdbStorageLocation); - } - else { - symbolsRepositoryPath = defaultSymbolsRepositoryPath; - } - if (!symbolsRepositoryPath.isDirectory()) { - symbolsRepositoryPath = new File(File.separator); - } - pdbStorageLocation = symbolsRepositoryPath.getAbsolutePath(); - forceLoadFile = new File(pdbStorageLocation + File.separator + "sample.pdb"); - pdbReaderOptions = new PdbReaderOptions(); pdbApplicatorOptions = new PdbApplicatorOptions(); } + static boolean isEnabled(Program program) { + Options analysisOptions = program.getOptions(Program.ANALYSIS_PROPERTIES); + return analysisOptions.getBoolean(NAME, DEFAULT_ENABLEMENT); + } + @Override public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log) throws CancelledException { +// NOTE: Legacy PDB Analyzer currently yields to this analyzer if both are enabled +// if (PdbAnalyzer.isEnabled(program)) { +// log.appendMsg(getName(), +// "Stopped: Cannot run with Legacy PDB Analyzer enabled"); +// return false; +// } + // TODO: // Work on use cases... // Code for checking if the PDB is already loaded (... assumes it was analyzed as well). // Need different logic... perhaps we could have multiple PDBs loaded already and need // to decide if any of those is the "correct" one or even look for another one. + // NOTE: if PDB previously applied the PDB load should be used instead of analyzer. // Probably still need to have a loader separate from the loader/analyzer, but then have // the ability to analyze (apply) any given already-loaded PDB to the program. A PDB // loader should probably be able to load an optionally apply data types to its own // category manager. Question would be if/how we "might" try to resolve any of these // against the "main" category data types. - if (oldPdbAnalyzerEnabled(program)) { - log.appendMsg(getName(), "Stopped: Cannot run with DIA-based PDB Analyzer enabled"); - return false; - } - PdbProgramAttributes programAttributes = new PdbProgramAttributes(program); - if (failMissingFilename(programAttributes, log) || + if (programAttributes.isPdbLoaded()) { + Msg.info(this, "Skipping PDB analysis since it has previouslu run."); + Msg.info(this, + ">> Clear 'PDB Loaded' program property or use Load PDB action if " + + "additional PDB processing required."); + } + if (programAttributes.isPdbLoaded() || + failMissingFilename(programAttributes, log) || failMissingAttributes(programAttributes, log)) { return true; } @@ -263,18 +257,27 @@ public class PdbUniversalAnalyzer extends AbstractAnalyzer { String pdbFilename; if (doForceLoad) { + if (!confirmFile(forceLoadFile)) { + logFailure("Force-load PDB file does not exist: " + forceLoadFile, log); + return false; + } pdbFilename = forceLoadFile.getAbsolutePath(); } else { - PdbLocator locator = new PdbLocator(symbolsRepositoryPath); + 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; } } - Msg.info(this, getClass().getSimpleName() + " configured to use: " + pdbFilename); + Msg.info(this, "PDB analyzer parsing file: " + pdbFilename); PdbLog.message( "================================================================================"); @@ -290,17 +293,14 @@ public class PdbUniversalAnalyzer extends AbstractAnalyzer { PdbApplicator applicator = new PdbApplicator(pdbFilename, pdb); applicator.applyTo(program, program.getDataTypeManager(), program.getImageBase(), pdbApplicatorOptions, monitor, log); + + Options options = program.getOptions(Program.PROGRAM_INFO); + options.setBoolean(PdbParserConstants.PDB_LOADED, true); + } - catch (PdbException e) { - String message = "Issue processing PDB file: " + pdbFilename + - ". Detailed issues may follow:\n" + e.toString(); - log.appendMsg(getName(), message); - return false; - } - catch (IOException e) { - String message = "Issue processing PDB file: " + pdbFilename + - ". Detailed issues may follow:\n" + e.toString(); - log.appendMsg(getName(), message); + catch (PdbException | IOException e) { + log.appendMsg(getName(), + "Issue processing PDB file: " + pdbFilename + ":\n " + e.toString()); return false; } @@ -341,11 +341,12 @@ public class PdbUniversalAnalyzer extends AbstractAnalyzer { @Override public void registerOptions(Options options, Program program) { // PDB file location information - options.registerOption(OPTION_NAME_DO_FORCELOAD, doForceLoad, null, + options.registerOption(OPTION_NAME_DO_FORCELOAD, Boolean.FALSE, null, OPTION_DESCRIPTION_DO_FORCELOAD); - options.registerOption(OPTION_NAME_FORCELOAD_FILE, OptionType.FILE_TYPE, forceLoadFile, + 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, symbolsRepositoryPath, + options.registerOption(OPTION_NAME_SYMBOLPATH, OptionType.FILE_TYPE, DEFAULT_SYMBOLS_DIR, null, OPTION_DESCRIPTION_SYMBOLPATH); options.registerOption(OPTION_NAME_INCLUDE_PE_PDB_PATH, includePeSpecifiedPdbPath, null, OPTION_DESCRIPTION_INCLUDE_PE_PDB_PATH); @@ -397,34 +398,11 @@ public class PdbUniversalAnalyzer extends AbstractAnalyzer { @Override public void optionsChanged(Options options, Program program) { - // This test can go away once this new analyzer completely replaces the old analyzer. - if (!SystemUtilities.isInHeadlessMode() && oldPdbAnalyzerEnabled(program) && - thisPdbAnalyzerEnabled(program)) { - OkDialog.show("Warning", - "Cannot use PDB Universal Analyzer and DIA-based PDB Analyzer at the same time"); - } - doForceLoad = options.getBoolean(OPTION_NAME_DO_FORCELOAD, doForceLoad); - File pdbFile = options.getFile(OPTION_NAME_FORCELOAD_FILE, forceLoadFile); - if (doForceLoad) { - if (pdbFile == null) { - throw new OptionsVetoException("Force-load file field is missing"); - } - else if (!confirmFile(pdbFile)) { - throw new OptionsVetoException(pdbFile + " force-load file does not exist"); - } - } - forceLoadFile = pdbFile; + forceLoadFile = options.getFile(OPTION_NAME_FORCELOAD_FILE, forceLoadFile); - File path = options.getFile(OPTION_NAME_SYMBOLPATH, symbolsRepositoryPath); - if (path == null) { - throw new OptionsVetoException("Symbol Path field is missing"); - } - else if (!confirmDirectory(path)) { - throw new OptionsVetoException(path + " is not a valid directory"); - } - symbolsRepositoryPath = path; + symbolsRepositoryDir = options.getFile(OPTION_NAME_SYMBOLPATH, DEFAULT_SYMBOLS_DIR); includePeSpecifiedPdbPath = options.getBoolean(OPTION_NAME_INCLUDE_PE_PDB_PATH, includePeSpecifiedPdbPath); @@ -459,67 +437,43 @@ public class PdbUniversalAnalyzer extends AbstractAnalyzer { } //============================================================================================== - private boolean oldPdbAnalyzerEnabled(Program program) { - String oldPdbNAME = "PDB"; - // This assert is just to catch us if we change the new NAME to what the old name is - // without first eliminating the call to this method and this method altogether. - if (NAME.contentEquals(oldPdbNAME)) { - throw new AssertException( - "Developer error: old and new PDB analyzers were not renamed correctly"); - } - Options analysisOptions = program.getOptions(Program.ANALYSIS_PROPERTIES); - // Don't have access to ghidra.app.plugin.core.analysis.PdbAnalyzer.NAME so using string. - boolean isPdbEnabled = analysisOptions.getBoolean(oldPdbNAME, false); - return isPdbEnabled; - } - - private boolean thisPdbAnalyzerEnabled(Program program) { - Options analysisOptions = program.getOptions(Program.ANALYSIS_PROPERTIES); - // Don't have access to ghidra.app.plugin.core.analysis.PdbAnalyzer.NAME so using string. - boolean isPdbEnabled = analysisOptions.getBoolean(NAME, false); - return isPdbEnabled; - } private boolean failMissingFilename(PdbProgramAttributes attributes, MessageLog log) { - if (attributes.getPdbFile() == null || attributes.getPdbFile().isEmpty()) { - String message = "No PdbFile specified in program... Skipping PDB processing."; - log.appendMsg(getName(), message); - log.setStatus(message); + 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 warning; + String error; if ("RSDS".equals(attributes.getPdbVersion())) { - if (attributes.getPdbGuid() != null) { + if (!StringUtils.isEmpty(attributes.getPdbGuid())) { return false; // Don't fail. } - warning = "No Program GUID to match with PDB."; + error = "Missing 'PDB GUID' program property, unable to locate PDB."; } else { - if (attributes.getPdbSignature() != null) { + if (!StringUtils.isEmpty(attributes.getPdbSignature())) { return false; // Don't fail. } - warning = "No Program Signature to match with PDB."; + error = "Missing 'PDB Signature' program property, unable to locate PDB."; } - String message; - if (!SystemUtilities.isInHeadlessMode()) { - int option = OptionDialog.showYesNoDialog(null, "Continue Loading PDB?", - warning + "\n " + "\nContinue anyway?" + "\n " + - "\nPlease note: Invalid disassembly may be produced!"); - if (option == OptionDialog.OPTION_ONE) { - message = warning + ".. Continuing PDB processing."; - log.appendMsg(getName(), message); - log.setStatus(message); - return false; - } - } - message = warning + ".. Skipping PDB processing."; - log.appendMsg(getName(), message); - log.setStatus(message); + logFailure(error, log); return true; } @@ -530,8 +484,7 @@ public class PdbUniversalAnalyzer extends AbstractAnalyzer { catch (IOException e) { // Probably could not open the file. if (log != null) { - log.appendMsg(getClass().getSimpleName(), - "IOException when trying to open PdbLog file: "); + log.appendMsg(getName(), "IOException when trying to open PDB log file: "); log.appendException(e); } } 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 4253c005e8..0d0349f3a7 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 @@ -22,6 +22,7 @@ import org.xml.sax.SAXException; import docking.widgets.OptionDialog; import ghidra.app.cmd.label.SetLabelPrimaryCmd; +import ghidra.app.plugin.core.datamgr.archive.DuplicateIdException; import ghidra.app.plugin.core.datamgr.util.DataTypeArchiveUtility; import ghidra.app.services.DataTypeManagerService; import ghidra.app.util.NamespaceUtils; @@ -229,11 +230,10 @@ public class PdbParser { /** * Open Windows Data Type Archives - * * @throws IOException if an i/o error occurs opening the data type archive - * @throws Exception if any other error occurs + * @throws DuplicateIdException unexpected archive error */ - public void openDataTypeArchives() throws IOException, Exception { + public void openDataTypeArchives() throws IOException, DuplicateIdException { if (program != null) { List archiveList = DataTypeArchiveUtility.getArchiveList(program); @@ -315,7 +315,7 @@ public class PdbParser { checkPdbLoaded(); errHandler.setMessageLog(log); - Msg.debug(this, "Found PDB for " + program.getName()); + Msg.debug(this, "Found PDB for " + program.getName() + ": " + pdbFile); try { ApplyDataTypes applyDataTypes = null; @@ -391,7 +391,7 @@ public class PdbParser { options.setBoolean(PdbParserConstants.PDB_LOADED, true); if (dataTypeParser != null && dataTypeParser.hasMissingBitOffsetError()) { - log.error("PDB", + log.appendMsg("PDB", "One or more bitfields were specified without bit-offset data.\nThe use of old pdb.xml data could be the cause."); } } @@ -619,8 +619,8 @@ public class PdbParser { pdbGuid = "{" + pdbGuid + "}"; if (!xmlGuid.equals(pdbGuid)) { - warning = "PDB signature does not match."; - } + 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)) { @@ -1045,13 +1045,13 @@ public class PdbParser { } /** - * Find the PDB associated with the given program using its attributes + * 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 - * @throws PdbException if there was a problem with the PDB attributes */ - public static File findPDB(Program program) throws PdbException { + public static File findPDB(Program program) { return findPDB(getPdbAttributes(program), false, null, null); } @@ -1072,10 +1072,9 @@ public class PdbParser { * @param includePeSpecifiedPdbPath to also check the PE-header-specified PDB path * @param symbolsRepositoryPath location where downloaded symbols are stored * @return matching PDB for program, or null - * @throws PdbException if there was a problem with the PDB attributes */ public static File findPDB(Program program, boolean includePeSpecifiedPdbPath, - String symbolsRepositoryPath) throws PdbException { + String symbolsRepositoryPath) { return findPDB(getPdbAttributes(program), includePeSpecifiedPdbPath, symbolsRepositoryPath, null); } @@ -1089,11 +1088,9 @@ public class PdbParser { * @param symbolsRepositoryPath 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) - * @throws PdbException if there was a problem with the PDB attributes */ public static File findPDB(PdbProgramAttributes pdbAttributes, - boolean includePeSpecifiedPdbPath, String symbolsRepositoryPath, PdbFileType fileType) - throws PdbException { + boolean includePeSpecifiedPdbPath, String symbolsRepositoryPath, PdbFileType fileType) { // Store potential names of PDB files and potential locations of those files, // so that all possible combinations can be searched. @@ -1102,9 +1099,7 @@ public class PdbParser { String guidAgeString = pdbAttributes.getGuidAgeCombo(); if (guidAgeString == 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."); + return null; } List potentialPdbNames = pdbAttributes.getPotentialPdbFilenames(); @@ -1150,7 +1145,8 @@ public class PdbParser { getSymbolsRepositoryPaths(symbolsRepositoryPath, guidSubdirPaths); Set predefinedPaths = getPredefinedPaths(guidSubdirPaths, pdbAttributes, includePeSpecifiedPdbPath); - boolean fileTypeSpecified = (fileType != null), checkForXml; + boolean fileTypeSpecified = (fileType != null); + boolean checkForXml; // If the file type is specified, look for that type of file only. if (fileTypeSpecified) { diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/AbstractPdb.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/AbstractPdb.java index 8b8e54e34a..cf4da71875 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/AbstractPdb.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/AbstractPdb.java @@ -25,6 +25,7 @@ import ghidra.app.util.bin.format.pdb2.pdbreader.msf.MsfStream; import ghidra.app.util.bin.format.pdb2.pdbreader.symbol.AbstractMsSymbol; import ghidra.app.util.bin.format.pdb2.pdbreader.type.AbstractMsType; import ghidra.app.util.datatype.microsoft.GUID; +import ghidra.util.exception.AssertException; import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitor; @@ -59,10 +60,11 @@ public abstract class AbstractPdb implements AutoCloseable { protected int versionNumber = 0; protected int signature = 0; //Number of times PDB updated. - protected int age = 0; + protected int pdbAge = 0; + protected int dbiAge = 0; protected AbstractTypeProgramInterface typeProgramInterface; - protected AbstractDatabaseInterface databaseInterface; + protected PdbDebugInfo debugInfo; protected Processor targetProcessor = Processor.UNKNOWN; @@ -141,9 +143,26 @@ public abstract class AbstractPdb implements AutoCloseable { /** * Returns the main {@link PdbIdentifiers} found in the PDB Directory. * @return {@link PdbIdentifiers} of information. + * @throws IOException On file seek or read, invalid parameters, bad file configuration, or + * inability to read required bytes. + * @throws PdbException Upon error in processing components. */ - public PdbIdentifiers getIdentifiers() { - return new PdbIdentifiers(versionNumber, signature, age, guid); + public PdbIdentifiers getIdentifiers() throws IOException, PdbException { + parseDBI(); + if (debugInfo != null) { + try { + // dbiAge and targetProcessor set during deserialization of new DBI header + debugInfo.deserialize(true, TaskMonitor.DUMMY); + } + catch (CancelledException e) { + throw new AssertException(e); // unexpected + } + } + int age = pdbAge; + if (dbiAge > 0) { + age = dbiAge; + } + return new PdbIdentifiers(versionNumber, signature, age, guid, targetProcessor); } /** @@ -216,7 +235,7 @@ public abstract class AbstractPdb implements AutoCloseable { * @return Age of the PDB. */ public int getAge() { - return age; + return pdbAge; } /** @@ -259,12 +278,13 @@ public abstract class AbstractPdb implements AutoCloseable { * @see Processor * @see RegisterName */ + // TODO: this method should be package protected public void setTargetProcessor(Processor targetProcessorIn) { /** - * Should we allow an overwrite? The {@link DatabaseInterfaceNew} value (mapped from + * Should we allow an overwrite? The {@link PdbNewDebugInfo} value (mapped from * {@link ImageFileMachine}) should be processed and laid down first. Subsequent values * can come from {@link AbstractCompile2MsSymbol} and {@link Compile3MsSymbol}. Note: - * {@link DatabaseInterface} does not carry {@link ImageFileMachine}, and thus no mapping + * {@link PdbDebugInfo} does not carry {@link ImageFileMachine}, and thus no mapping * is applied. */ if (targetProcessor == Processor.UNKNOWN) { @@ -272,9 +292,18 @@ public abstract class AbstractPdb implements AutoCloseable { } } + /** + * Set the age as specified by the new DBI header. A value of 0 corresponds + * to the old DBI header. + * @param dbiAge age as specified by the new DBI header + */ + void setDbiAge(int dbiAge) { + this.dbiAge = dbiAge; + } + /** * Returns the {@link AbstractTypeProgramInterface} component. - * @return {@link AbstractTypeProgramInterface} component. + * @return {@link AbstractTypeProgramInterface} component or null if not available. */ public AbstractTypeProgramInterface getTypeProgramInterface() { return typeProgramInterface; @@ -283,18 +312,19 @@ public abstract class AbstractPdb implements AutoCloseable { /** * Returns the ItemProgramInterface (of type {@link AbstractTypeProgramInterface}) * component. - * @return ItemProgramInterface (of type {@link AbstractTypeProgramInterface}) component. + * @return ItemProgramInterface (of type {@link AbstractTypeProgramInterface}) component + * or null if not available. */ public AbstractTypeProgramInterface getItemProgramInterface() { return itemProgramInterface; } /** - * Returns the {@link AbstractDatabaseInterface} component. - * @return {@link AbstractDatabaseInterface} component. + * Returns the {@link PdbDebugInfo} component. + * @return {@link PdbDebugInfo} component or null if not available. */ - public AbstractDatabaseInterface getDatabaseInterface() { - return databaseInterface; + public PdbDebugInfo getDebugInfo() { + return debugInfo; } /** @@ -302,7 +332,7 @@ public abstract class AbstractPdb implements AutoCloseable { * @return {@link SymbolRecords} component. */ public SymbolRecords getSymbolRecords() { - return databaseInterface.getSymbolRecords(); + return debugInfo.getSymbolRecords(); } /** @@ -481,7 +511,6 @@ public abstract class AbstractPdb implements AutoCloseable { } TypeProgramInterfaceParser tpiParser = new TypeProgramInterfaceParser(); - DatabaseInterfaceParser dbiParser = new DatabaseInterfaceParser(); typeProgramInterface = tpiParser.parse(this, monitor); if (typeProgramInterface != null) { @@ -500,14 +529,22 @@ public abstract class AbstractPdb implements AutoCloseable { //dumpDependencyGraph(); } - databaseInterface = dbiParser.parse(this, monitor); - if (databaseInterface != null) { - databaseInterface.deserialize(monitor); + parseDBI(); + if (debugInfo != null) { + debugInfo.deserialize(false, monitor); } substreamsDeserialized = true; } + private PdbDebugInfo parseDBI() throws IOException, PdbException { + if (debugInfo == null) { + PdbDebugInfoParser dbiParser = new PdbDebugInfoParser(); + debugInfo = dbiParser.parse(this); + } + return debugInfo; + } + /** * Returns a {@link PdbByteReader} initialized with the complete contents of the * {@link MsfStream} referenced by {@code streamNumber}. @@ -606,7 +643,7 @@ public abstract class AbstractPdb implements AutoCloseable { protected void deserializeVersionSignatureAge(PdbByteReader reader) throws PdbException { versionNumber = reader.parseInt(); signature = reader.parseInt(); - age = reader.parseInt(); + pdbAge = reader.parseInt(); } /** @@ -621,7 +658,7 @@ public abstract class AbstractPdb implements AutoCloseable { builder.append("\nsignature: "); builder.append(Integer.toHexString(signature)); builder.append("\nage: "); - builder.append(age); + builder.append(pdbAge); return builder.toString(); } @@ -704,10 +741,10 @@ public abstract class AbstractPdb implements AutoCloseable { itemProgramInterface.dump(writer); writer.write("End ItemProgramInterface------------------------------------\n"); } - if (databaseInterface != null) { - writer.write("DatabaseInterface-------------------------------------------\n"); - databaseInterface.dump(writer); - writer.write("End DatabaseInterface---------------------------------------\n"); + if (debugInfo != null) { + writer.write("DebugInfo---------------------------------------------------\n"); + debugInfo.dump(writer); + writer.write("End DebugInfo-----------------------------------------------\n"); } } diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/AbstractSymbolInformation.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/AbstractSymbolInformation.java index fc82313a9f..ec1a44b566 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/AbstractSymbolInformation.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/AbstractSymbolInformation.java @@ -55,8 +55,6 @@ public abstract class AbstractSymbolInformation { protected List hashBucketOffsets = new ArrayList<>(); protected Set hashRecords = new TreeSet<>(); protected List modifiedHashRecordSymbolOffsets = new ArrayList<>(); - protected Map thunkTargetOffsetsByTableOffset = new HashMap<>(); - protected Map absoluteOffsetsBySectionNumber = new HashMap<>(); protected List symbols = new ArrayList<>(); @@ -170,8 +168,7 @@ public abstract class AbstractSymbolInformation { protected void generateSymbolsList(TaskMonitor monitor) throws PdbException, CancelledException { symbols = new ArrayList<>(); - Map symbolsByOffset = - pdb.getDatabaseInterface().getSymbolsByOffset(); + Map symbolsByOffset = pdb.getDebugInfo().getSymbolsByOffset(); for (SymbolHashRecord record : hashRecords) { monitor.checkCanceled(); long offset = record.getOffset() - 2; // Modified offset @@ -190,10 +187,10 @@ public abstract class AbstractSymbolInformation { */ protected void dumpHashRecords(StringBuilder builder) { builder.append("HashRecords-------------------------------------------------\n"); - builder.append("numHashRecords: " + hashRecords.size()); + builder.append("numHashRecords: " + hashRecords.size() + "\n"); for (SymbolHashRecord record : hashRecords) { builder.append( - String.format("0X%08X 0X%04X", record.getOffset(), record.getReferenceCount())); + String.format("0X%08X 0X%04X\n", record.getOffset(), record.getReferenceCount())); } builder.append("\nEnd HashRecords--------------------------------------------\n"); } diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/DebugData.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/DebugData.java index 2ba9d38400..774cf7bcb4 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/DebugData.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/DebugData.java @@ -269,9 +269,9 @@ public class DebugData { // } // TODO: More work possible. See XData processing and notes there. This is very // incomplete. - if (pdb.getDatabaseInterface() instanceof DatabaseInterfaceNew) { + if (pdb.getDebugInfo() instanceof PdbNewDebugInfo) { //Processor target = pdb.getTargetProcessor(); - DatabaseInterfaceNew dbi = (DatabaseInterfaceNew) pdb.getDatabaseInterface(); + PdbNewDebugInfo dbi = (PdbNewDebugInfo) pdb.getDebugInfo(); ImageFileMachine machine = dbi.getMachineType(); switch (machine) { case IA64: diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/ImageFileMachine.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/ImageFileMachine.java index 93df2b1f74..db99726af0 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/ImageFileMachine.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/ImageFileMachine.java @@ -22,11 +22,11 @@ import ghidra.app.util.bin.format.pdb2.pdbreader.symbol.AbstractCompile2MsSymbol import ghidra.app.util.bin.format.pdb2.pdbreader.symbol.Compile3MsSymbol; /** - * Machine Type seen in the {@link DatabaseInterfaceNew} header. We also map in the Processor. + * Machine Type seen in the {@link PdbNewDebugInfo} header. We also map in the Processor. * We are not exactly sure about why there are different but similar items: Machine Type and * Processor. The {@link Processor} is what is specified in {@link AbstractCompile2MsSymbol} and * {@link Compile3MsSymbol} and what we save off in {@link AbstractPdb}, but - * {@link ImageFileMachine} is what we see in the header of {@link DatabaseInterfaceNew}. + * {@link ImageFileMachine} is what we see in the header of {@link PdbNewDebugInfo}. * @see * Image File Machine Constants * @see diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/ImageSectionHeader.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/ImageSectionHeader.java index f6de393be4..016afa4e7a 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/ImageSectionHeader.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/ImageSectionHeader.java @@ -21,9 +21,9 @@ import java.util.Objects; /** * Image Section Header information, as part of {@link DebugData} structures within - * {@link DatabaseInterfaceNew} of {@link AbstractPdb} types. Contains section information; + * {@link PdbNewDebugInfo} of {@link AbstractPdb} types. Contains section information; * an older set of section information seems to be located in {@link SegmentMapDescription}, - * which might be used for {@link DatabaseInterface} types, but we do not yet have data to + * which might be used for {@link PdbOldDebugInfo} types, but we do not yet have data to * confirm this. */ public class ImageSectionHeader { @@ -167,7 +167,7 @@ public class ImageSectionHeader { // TODO: See the to-do above regarding unionPAVS. writer.write(String.format("unionPAVS: 0X%08X\n", unionPAVS)); writer.write(String.format("virtualAddress: 0X%08X\n", virtualAddress)); - writer.write(String.format("rawDataSize: 0X%08XX\n", rawDataSize)); + writer.write(String.format("rawDataSize: 0X%08X\n", rawDataSize)); writer.write(String.format("rawDataPointer: 0X%08X\n", rawDataPointer)); writer.write(String.format("relocationsPointer: 0X%08X\n", relocationsPointer)); writer.write(String.format("lineNumbersPointer: 0X%08X\n", lineNumbersPointer)); diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/AbstractDatabaseInterface.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/PdbDebugInfo.java similarity index 91% rename from Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/AbstractDatabaseInterface.java rename to Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/PdbDebugInfo.java index 40791cc9f6..18100e47fe 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/AbstractDatabaseInterface.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/PdbDebugInfo.java @@ -24,13 +24,13 @@ import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitor; /** - * This class represents Database Interface component of a PDB file. This class is only - * suitable for reading; not for writing or modifying a PDB. + * This class represents DebugInfo (DBI) component of a PDB file. + * This class is only suitable for reading; not for writing or modifying a PDB. *

* We have intended to implement according to the Microsoft PDB API (source); see the API for * truth. */ -public abstract class AbstractDatabaseInterface { +public abstract class PdbDebugInfo { protected static final int VERSION_NUMBER_SIZE = 4; @@ -81,7 +81,7 @@ public abstract class AbstractDatabaseInterface { * @param pdb {@link AbstractPdb} that owns this Database Interface. * @param streamNumber The stream number of the stream containing the Database Interface. */ - public AbstractDatabaseInterface(AbstractPdb pdb, int streamNumber) { + public PdbDebugInfo(AbstractPdb pdb, int streamNumber) { Objects.requireNonNull(pdb, "pdb cannot be null"); this.pdb = pdb; this.streamNumber = streamNumber; @@ -99,7 +99,10 @@ public abstract class AbstractDatabaseInterface { } /** - * Deserializes the {@link AbstractDatabaseInterface}-based instance. + * Deserializes the {@link PdbDebugInfo}-based instance. + * The pdb is updated with dbiAge and targetProcessor during deserialization + * of new DBI header. + * @param headerOnly if true only the DBI header fields will be parsed * @param monitor {@link TaskMonitor} used for checking cancellation. * @return The version number of the Database Interface. * @throws IOException On file seek or read, invalid parameters, bad file configuration, or @@ -107,13 +110,19 @@ public abstract class AbstractDatabaseInterface { * @throws PdbException upon error parsing a field. * @throws CancelledException Upon user cancellation. */ - public long deserialize(TaskMonitor monitor) + public long deserialize(boolean headerOnly, TaskMonitor monitor) throws IOException, PdbException, CancelledException { - PdbByteReader reader = pdb.getReaderForStreamNumber(streamNumber, monitor); - deserializeHeader(reader); - deserializeInternalSubstreams(reader, monitor); - deserializeAdditionalSubstreams(monitor); - + if (headerOnly) { + PdbByteReader reader = + pdb.getReaderForStreamNumber(streamNumber, 0, getHeaderLength(), monitor); + deserializeHeader(reader); + } + else { + PdbByteReader reader = pdb.getReaderForStreamNumber(streamNumber, monitor); + deserializeHeader(reader); + deserializeInternalSubstreams(reader, monitor); + deserializeAdditionalSubstreams(monitor); + } return versionNumber; } @@ -285,6 +294,12 @@ public abstract class AbstractDatabaseInterface { */ protected abstract void deserializeHeader(PdbByteReader reader) throws PdbException; + /** + * Get the header length in bytes as it appears at offset 0 within the DBI stream + * @return DBI header length + */ + protected abstract int getHeaderLength(); + /** * Deserializes the SubStreams internal to the Database Interface stream. * @param reader {@link PdbByteReader} from which to deserialize the data. @@ -375,7 +390,7 @@ public abstract class AbstractDatabaseInterface { } //TODO: Don't know when SectionContribution200 is the type to use. Don't know if // this part could be the default of processSectionContribs within - // DatabaseInterface and if the above part (test for SVC600 and SVC1400 would + // DebugInfo and if the above part (test for SVC600 and SVC1400 would // be the override method for DatabaseInformationNew. else { while (substreamReader.hasMore()) { @@ -507,26 +522,26 @@ public abstract class AbstractDatabaseInterface { protected abstract String parseFileInfoName(PdbByteReader reader) throws PdbException; /** - * Debug method for dumping information from this {@link AbstractDatabaseInterface}-based + * Debug method for dumping information from this {@link PdbDebugInfo}-based * instance. * @param writer {@link Writer} to which to dump the information. * @throws IOException Upon IOException writing to the {@link Writer}. */ protected void dump(Writer writer) throws IOException { - writer.write("DatabaseInterfaceHeader-------------------------------------\n"); + writer.write("DebugInfoHeader-------------------------------------\n"); dumpHeader(writer); - writer.write("\nEnd DatabaseInterfaceHeader---------------------------------\n"); - writer.write("DatabaseInterfaceInternalSubstreams-------------------------\n"); + writer.write("\nEnd DebugInfoHeader---------------------------------\n"); + writer.write("DebugInfoInternalSubstreams-------------------------\n"); dumpInternalSubstreams(writer); - writer.write("\nEnd DatabaseInterfaceInternalSubstreams---------------------\n"); - writer.write("DatabaseInterfaceAdditionalSubstreams-----------------------\n"); + writer.write("\nEnd DebugInfoInternalSubstreams---------------------\n"); + writer.write("DebugInfoAdditionalSubstreams-----------------------\n"); dumpAdditionalSubstreams(writer); - writer.write("\nEnd DatabaseInterfaceAdditionalSubstreams-------------------\n"); + writer.write("\nEnd DebugInfoAdditionalSubstreams-------------------\n"); } /** * Debug method for dumping additional substreams from this - * {@link AbstractDatabaseInterface}-based instance. + * {@link PdbDebugInfo}-based instance. * @param writer {@link Writer} to which to dump the information. * @throws IOException Upon IOException writing to the {@link Writer}. */ @@ -540,7 +555,7 @@ public abstract class AbstractDatabaseInterface { /** * Debug method for dumping module information for all of the {@link AbstractModuleInformation} - * modules from this {@link AbstractDatabaseInterface}-based instance. + * modules from this {@link PdbDebugInfo}-based instance. * @param writer {@link Writer} to which to dump the information. * @throws IOException Upon IOException writing to the {@link Writer}. */ @@ -554,7 +569,7 @@ public abstract class AbstractDatabaseInterface { /** * Debug method for dumping section contribution for all of the * {@link AbstractSectionContribution} components from this - * {@link AbstractDatabaseInterface}-based instance. + * {@link PdbDebugInfo}-based instance. * @param writer {@link Writer} to which to dump the information. * @throws IOException Upon IOException writing to the {@link Writer}. */ @@ -567,7 +582,7 @@ public abstract class AbstractDatabaseInterface { /** * Debug method for dumping segment map information for all of the - * {@link SegmentMapDescription} components from this {@link AbstractDatabaseInterface}-based + * {@link SegmentMapDescription} components from this {@link PdbDebugInfo}-based * instance. * @param writer {@link Writer} to which to dump the information. * @throws IOException Upon IOException writing to the {@link Writer}. diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/DatabaseInterfaceParser.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/PdbDebugInfoParser.java similarity index 63% rename from Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/DatabaseInterfaceParser.java rename to Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/PdbDebugInfoParser.java index 518da9ac5f..b0644a9321 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/DatabaseInterfaceParser.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/PdbDebugInfoParser.java @@ -17,14 +17,15 @@ package ghidra.app.util.bin.format.pdb2.pdbreader; import java.io.IOException; +import ghidra.util.exception.AssertException; import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitor; /** - * Parser for detecting the appropriate {@link AbstractDatabaseInterface} format for the filename - * given. It then creates and returns the appropriate {@link AbstractDatabaseInterface} object. + * Parser for detecting the appropriate {@link PdbDebugInfo} format for the filename + * given. It then creates and returns the appropriate {@link PdbDebugInfo} object. */ -public class DatabaseInterfaceParser { +public class PdbDebugInfoParser { private static final int DATABASE_INTERFACE_STREAM_NUMBER = 3; @@ -45,53 +46,55 @@ public class DatabaseInterfaceParser { /** * Parses information to determine the version of Database Interface to create. * @param pdb {@link AbstractPdb} that owns this Database Interface. - * @param monitor {@link TaskMonitor} used for checking cancellation. - * @return {@link AbstractDatabaseInterface} of the appropriate Database Interface or null if + * @return {@link PdbDebugInfo} of the appropriate Database Interface or null if * the stream does not have enough information to be parsed. * @throws IOException On file seek or read, invalid parameters, bad file configuration, or * inability to read required bytes. * @throws PdbException Upon error in processing components. - * @throws CancelledException Upon user cancellation. */ - public AbstractDatabaseInterface parse(AbstractPdb pdb, TaskMonitor monitor) - throws IOException, PdbException, CancelledException { - AbstractDatabaseInterface databaseInterface; + public PdbDebugInfo parse(AbstractPdb pdb) throws IOException, PdbException { + PdbDebugInfo debugInfo; + try { + int streamNumber = getStreamNumber(); + // Only reading 8-bytes - no need for monitor + PdbByteReader reader = + pdb.getReaderForStreamNumber(streamNumber, 0, 8, TaskMonitor.DUMMY); + if (reader.getLimit() == 0) { + return null; + } - int streamNumber = getStreamNumber(); - PdbByteReader reader = pdb.getReaderForStreamNumber(streamNumber, 0, 8, monitor); - if (reader.getLimit() == 0) { - return null; - } + // In support of debug. + debugReader = reader; + PdbLog.message(this::debugDump); - // In support of debug. - debugReader = reader; - PdbLog.message(this::debugDump); + int headerSignature = reader.parseInt(); + int versionNumber = reader.parseInt(); - int headerSignature = reader.parseInt(); - int versionNumber = reader.parseInt(); - - if (headerSignature == DBIHDR700_SIG) { - switch (versionNumber) { - case DBI41_ID: - case DBI50_ID: - case DBI60_ID: - case DBI70_ID: - case DBI110_ID: - databaseInterface = new DatabaseInterfaceNew(pdb, streamNumber); - break; - default: - throw new PdbException("Unknown DBI Version"); + if (headerSignature == DBIHDR700_SIG) { + switch (versionNumber) { + case DBI41_ID: + case DBI50_ID: + case DBI60_ID: + case DBI70_ID: + case DBI110_ID: + debugInfo = new PdbNewDebugInfo(pdb, streamNumber); + break; + default: + throw new PdbException("Unknown DBI Version"); + } + } + else { + debugInfo = new PdbOldDebugInfo(pdb, streamNumber); } } - else { - databaseInterface = new DatabaseInterface(pdb, streamNumber); + catch (CancelledException e) { + throw new AssertException(); } - - return databaseInterface; + return debugInfo; } private String debugDump() { - return "DatabaseInterfaceParser data on stream " + getStreamNumber() + ":\n" + + return "DebugInfoParser data on stream " + getStreamNumber() + ":\n" + debugReader.dump() + "\n"; } 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 c54fd2654a..cc7ac1ddfc 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 @@ -25,23 +25,25 @@ import ghidra.app.util.datatype.microsoft.GUID; */ public class PdbIdentifiers { - private int version; - private int signature; - private int age; - private GUID guid; + private final int version; + private final int signature; + private final int age; + private final GUID guid; + private final Processor processor; /** * Constructor. * @param version The version number. * @param signature The signature. - * @param age The age. + * @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) { + PdbIdentifiers(int version, int signature, int age, GUID guid, Processor processor) { this.version = version; this.signature = signature; this.age = age; this.guid = guid; + this.processor = processor == null ? Processor.UNKNOWN : processor; } /** diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/DatabaseInterfaceNew.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/PdbNewDebugInfo.java similarity index 94% rename from Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/DatabaseInterfaceNew.java rename to Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/PdbNewDebugInfo.java index e8934ada6f..852503f275 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/DatabaseInterfaceNew.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/PdbNewDebugInfo.java @@ -24,23 +24,24 @@ import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitor; /** - * This class is the version of {@link AbstractDatabaseInterface} for newer PDB files. + * This class is the version of {@link PdbDebugInfo} for newer PDB files. *

* This class uses {@link ModuleInformation600}. */ -public class DatabaseInterfaceNew extends AbstractDatabaseInterface { +public class PdbNewDebugInfo extends PdbDebugInfo { //============================================================================================== // Internals //============================================================================================== private static final long HEADER_MAGIC = 0xeffeeffeL; + private static final int DBI_HEADER_LENGTH = 64; protected Hasher hasher; //Might belong in parent? Used in parent (even older Hasher?) // The source of these values can overlay other fields in older versions of this type. protected long versionSignature = 0; // unsigned 32-bit - protected long age = 0; // unsigned 32-bit + protected long dbiAge = 0; // unsigned 32-bit protected int universalVersion = 0; // unsigned 16-bit protected int pdbDllBuildVersion = 0; // unsigned 16-bit protected int pdbDllReleaseBuildVersion = 0; // unsigned 16-bit @@ -63,10 +64,10 @@ public class DatabaseInterfaceNew extends AbstractDatabaseInterface { //============================================================================================== /** * Constructor. - * @param pdb {@link AbstractPdb} that owns this {@link DatabaseInterfaceNew}. - * @param streamNumber The stream number that contains the {@link DatabaseInterfaceNew} data. + * @param pdb {@link AbstractPdb} that owns this {@link PdbNewDebugInfo}. + * @param streamNumber The stream number that contains the {@link PdbNewDebugInfo} data. */ - public DatabaseInterfaceNew(AbstractPdb pdb, int streamNumber) { + public PdbNewDebugInfo(AbstractPdb pdb, int streamNumber) { super(pdb, streamNumber); debugData = new DebugData(pdb); } @@ -80,7 +81,7 @@ public class DatabaseInterfaceNew extends AbstractDatabaseInterface { } /** - * Returns the {@link DebugData} for this {@link DatabaseInterfaceNew}. + * Returns the {@link DebugData} for this {@link PdbNewDebugInfo}. * @return the {@link DebugData}. */ public DebugData getDebugData() { @@ -95,7 +96,7 @@ public class DatabaseInterfaceNew extends AbstractDatabaseInterface { //System.out.println(reader.dump(0x200)); versionSignature = reader.parseUnsignedIntVal(); versionNumber = reader.parseUnsignedIntVal(); - age = reader.parseUnsignedIntVal(); + dbiAge = reader.parseUnsignedIntVal(); streamNumberGlobalStaticSymbolsHashMaybe = reader.parseUnsignedShortVal(); @@ -119,10 +120,16 @@ public class DatabaseInterfaceNew extends AbstractDatabaseInterface { flags = reader.parseUnsignedShortVal(); machineType = ImageFileMachine.fromValue(reader.parseUnsignedShortVal()); - pdb.setTargetProcessor(machineType.getProcessor()); - padReserve = reader.parseUnsignedIntVal(); + // update PDB with age and processor + pdb.setTargetProcessor(machineType.getProcessor()); + pdb.setDbiAge((int) dbiAge); + } + + @Override + protected int getHeaderLength() { + return DBI_HEADER_LENGTH; } @Override @@ -187,7 +194,7 @@ public class DatabaseInterfaceNew extends AbstractDatabaseInterface { builder.append("\nversionNumber: "); builder.append(versionNumber); builder.append("\nage: "); - builder.append(age); + builder.append(dbiAge); builder.append("\nstreamNumberGlobalStaticSymbols: "); builder.append(streamNumberGlobalStaticSymbolsHashMaybe); builder.append(String.format("\nuniversalVersion: 0x%04x", universalVersion)); @@ -338,4 +345,12 @@ public class DatabaseInterfaceNew extends AbstractDatabaseInterface { } } + /** + * Get age from deserialized DBI header + * @return age from deserialized DBI header + */ + long getAge() { + return dbiAge; + } + } diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/DatabaseInterface.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/PdbOldDebugInfo.java similarity index 92% rename from Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/DatabaseInterface.java rename to Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/PdbOldDebugInfo.java index 13cce76858..7a25e3743e 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/DatabaseInterface.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/PdbOldDebugInfo.java @@ -22,21 +22,23 @@ import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitor; /** - * This class is the version of {@link AbstractDatabaseInterface} for older PDB files. + * This class is the version of {@link PdbDebugInfo} for older PDB files. *

* This class uses {@link ModuleInformation500}. */ -class DatabaseInterface extends AbstractDatabaseInterface { +class PdbOldDebugInfo extends PdbDebugInfo { + + private static final int OLD_DBI_HEADER_LENGTH = 22; //============================================================================================== // API //============================================================================================== /** * Constructor. - * @param pdb {@link AbstractPdb} that owns this {@link DatabaseInterface}. - * @param streamNumber The number of the stream that contains the {@link DatabaseInterface}. + * @param pdb {@link AbstractPdb} that owns this {@link PdbOldDebugInfo}. + * @param streamNumber The number of the stream that contains the {@link PdbOldDebugInfo}. */ - public DatabaseInterface(AbstractPdb pdb, int streamNumber) { + public PdbOldDebugInfo(AbstractPdb pdb, int streamNumber) { super(pdb, streamNumber); } @@ -54,6 +56,11 @@ class DatabaseInterface extends AbstractDatabaseInterface { lengthFileInformation = reader.parseInt(); } + @Override + protected int getHeaderLength() { + return OLD_DBI_HEADER_LENGTH; + } + @Override protected void deserializeInternalSubstreams(PdbByteReader reader, TaskMonitor monitor) throws PdbException, CancelledException { diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/PdbReaderMetrics.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/PdbReaderMetrics.java index fefb049abd..eac54494f7 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/PdbReaderMetrics.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/PdbReaderMetrics.java @@ -878,7 +878,7 @@ public class PdbReaderMetrics { public void witnessedSectionSegmentNumber(int segment) { if (numSegments == -1) { - numSegments = pdb.getDatabaseInterface().getSegmentMapList().size(); + numSegments = pdb.getDebugInfo().getSegmentMapList().size(); } if (segment < 0 || segment > numSegments) { PdbLog.message("segment " + segment + " out of range [0," + numSegments + ")"); diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/PublicSymbolInformation.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/PublicSymbolInformation.java index f87e026109..bf81106b84 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/PublicSymbolInformation.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/PublicSymbolInformation.java @@ -17,8 +17,7 @@ package ghidra.app.util.bin.format.pdb2.pdbreader; import java.io.IOException; import java.io.Writer; -import java.util.ArrayList; -import java.util.List; +import java.util.*; import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitor; @@ -50,6 +49,8 @@ public class PublicSymbolInformation extends AbstractSymbolInformation { // These should correspond with symbolOffsets that come from HashRecords. private List addressMapSymbolOffsets = new ArrayList<>(); + private Map thunkTargetOffsetsByTableOffset = new HashMap<>(); + private Map absoluteOffsetsBySectionNumber = new HashMap<>(); //============================================================================================== // API @@ -180,12 +181,9 @@ public class PublicSymbolInformation extends AbstractSymbolInformation { dumpHashBasics(builder); dumpHashRecords(builder); -// deserializeAddressMap(addressMapReader, monitor); -// deserializeThunkMap(thunkMapReader, monitor); -// sectionMapLength = reader.numRemaining(); -// numSections = sectionMapLength / 8; -// deserializeSectionMap(sectionMapReader, monitor); -// + dumpAddressMap(builder); + dumpThunkMap(builder); + dumpSectionMap(builder); builder.append("\nEnd PublicSymbolInformation---------------------------------\n"); writer.write(builder.toString()); @@ -209,6 +207,20 @@ public class PublicSymbolInformation extends AbstractSymbolInformation { } } + /** + * Debug method for dumping Address Map information from this {@link AbstractSymbolInformation}. + * @param builder {@link StringBuilder} to which to dump the information. + */ + private void dumpAddressMap(StringBuilder builder) { + builder.append("AddressMapSymbolOffsets-------------------------------------\n"); + builder.append("numAddressMapSymbolOffsets: " + addressMapSymbolOffsets.size() + "\n"); + int num = 0; + for (Long val : addressMapSymbolOffsets) { + builder.append(String.format("0X%08X: 0X%012X\n", num++, val)); + } + builder.append("\nEnd AddressMapSymbolOffsets---------------------------------\n"); + } + /** * Deserializes the Thunk Map for these public symbols. * @param reader {@link PdbByteReader} containing the data buffer to process. @@ -227,6 +239,20 @@ public class PublicSymbolInformation extends AbstractSymbolInformation { } } + /** + * Debug method for dumping Thunk Map information from this {@link AbstractSymbolInformation}. + * @param builder {@link StringBuilder} to which to dump the information. + */ + private void dumpThunkMap(StringBuilder builder) { + builder.append("ThunkMap----------------------------------------------------\n"); + builder.append( + "numThunkTargetOffsetsByTableOffset: " + thunkTargetOffsetsByTableOffset.size() + "\n"); + for (Map.Entry entry : thunkTargetOffsetsByTableOffset.entrySet()) { + builder.append(String.format("0X%08X 0X%08X\n", entry.getKey(), entry.getValue())); + } + builder.append("\nEnd ThunkMap------------------------------------------------\n"); + } + /** * Deserializes the Section Map for these public symbols. * @param reader {@link PdbByteReader} containing the data buffer to process. @@ -245,6 +271,20 @@ public class PublicSymbolInformation extends AbstractSymbolInformation { } } + /** + * Debug method for dumping Section Map information from this {@link AbstractSymbolInformation}. + * @param builder {@link StringBuilder} to which to dump the information. + */ + private void dumpSectionMap(StringBuilder builder) { + builder.append("SectionMap--------------------------------------------------\n"); + builder.append( + "numAbsoluteOffsetsBySectionNumber: " + absoluteOffsetsBySectionNumber.size() + "\n"); + for (Map.Entry entry : absoluteOffsetsBySectionNumber.entrySet()) { + builder.append(String.format("0X%08X 0X%08X\n", entry.getKey(), entry.getValue())); + } + builder.append("\nEnd SectionMap----------------------------------------------\n"); + } + /** * Debug method for dumping the {@link PublicSymbolInformation} header. * @param builder {@link StringBuilder} to which to dump the information. @@ -269,7 +309,7 @@ public class PublicSymbolInformation extends AbstractSymbolInformation { builder.append(thunkMapLength); builder.append("\nthunkTableLength: "); builder.append(thunkTableLength); - builder.append("\nEnd PublicSymbolInformationHeader--------------------------\n"); + builder.append("\nEnd PublicSymbolInformationHeader---------------------------\n"); } // Issue: MSFT does not initialize PSGSIHDR with nSects(0) (our numSections), so spurious diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/SymbolRecords.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/SymbolRecords.java index 26a534f24c..d1ae2dd177 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/SymbolRecords.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/SymbolRecords.java @@ -76,11 +76,11 @@ public class SymbolRecords { int streamNumber; PdbByteReader reader; - streamNumber = pdb.getDatabaseInterface().getSymbolRecordsStreamNumber(); + streamNumber = pdb.getDebugInfo().getSymbolRecordsStreamNumber(); reader = pdb.getReaderForStreamNumber(streamNumber, monitor); symbolsByOffset = deserializeSymbolRecords(reader, monitor); - for (AbstractModuleInformation module : pdb.getDatabaseInterface().moduleInformationList) { + for (AbstractModuleInformation module : pdb.getDebugInfo().moduleInformationList) { streamNumber = module.getStreamNumberDebugInformation(); if (streamNumber != 0xffff) { // System.out.println("\n\nStreamNumber: " + streamNumber); diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/PdbCategories.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/PdbCategories.java index 8ce9935278..a889f76e62 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/PdbCategories.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/PdbCategories.java @@ -18,7 +18,7 @@ package ghidra.app.util.pdb; import java.util.*; import ghidra.app.util.SymbolPath; -import ghidra.app.util.bin.format.pdb2.pdbreader.AbstractDatabaseInterface; +import ghidra.app.util.bin.format.pdb2.pdbreader.PdbDebugInfo; import ghidra.program.model.data.Category; import ghidra.program.model.data.CategoryPath; @@ -134,7 +134,7 @@ public class PdbCategories { /** * Returns the {@link CategoryPath} for a typedef with the give {@link SymbolPath} and - * module number; 1 <= moduleNumber <= {@link AbstractDatabaseInterface#getNumModules()}, + * module number; 1 <= moduleNumber <= {@link PdbDebugInfo#getNumModules()}, * except that modeleNumber of 0 represents publics/globals. * @param moduleNumber module number * @param symbolPath SymbolPath of the symbol diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/PdbLocator.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/PdbLocator.java index 73d662c396..da70c57f41 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/PdbLocator.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/PdbLocator.java @@ -70,10 +70,13 @@ import ghidra.util.task.TaskMonitor; */ public class PdbLocator { - public static final File SPECIAL_PDB_LOCATION = new File("C:/WINDOWS/Symbols"); public static final boolean onWindows = (Platform.CURRENT_PLATFORM.getOperatingSystem() == OperatingSystem.WINDOWS); + private static final File USER_HOME = new File(System.getProperty("user.home")); + public static final File DEFAULT_SYMBOLS_DIR = + onWindows ? new File("C:\\Symbols") : new File(USER_HOME, "Symbols"); + private File symbolsRepositoryPath; /** * Only holds identifies in PDBs up until a matching one was found--nothing beyond that. @@ -219,8 +222,8 @@ public class PdbLocator { includePeSpecifiedPdbPath, symbolsRepositoryPath.getAbsolutePath()); if (orderedListOfExistingFileNames.isEmpty()) { - String pdbName = program.getOptions(Program.PROGRAM_INFO).getString( - PdbParserConstants.PDB_FILE, (String) null); + String pdbName = program.getOptions(Program.PROGRAM_INFO) + .getString(PdbParserConstants.PDB_FILE, (String) null); if (pdbName == null) { message = "Program has no associated PDB file."; } @@ -452,6 +455,9 @@ public class PdbLocator { * a matching PDB * @param potentialPdbNames all potential filenames for the PDB file(s) that match the program * @param pdbAttributes PDB attributes associated with the program + * @param includePeSpecifiedPdbPath if true include paths derived from the PDB file path + * determined at time of import. NOTE: This option is considered unsafe and should not be + * enabled unless binary source is trusted and PDB file path is reasonable for this system. * @return matching PDB file, if found (else null) */ private static List checkPathsForPdb(String symbolsRepositoryPath, @@ -467,12 +473,12 @@ public class PdbLocator { List orderedListOfExistingFileNames = new ArrayList<>(); if (symbolsRepositoryPath != null) { - orderedListOfExistingFileNames.addAll( - checkSpecificPathsForPdb(symbolsRepoPaths, potentialPdbNames)); + orderedListOfExistingFileNames + .addAll(checkSpecificPathsForPdb(symbolsRepoPaths, potentialPdbNames)); } - orderedListOfExistingFileNames.addAll( - checkSpecificPathsForPdb(predefinedPaths, potentialPdbNames)); + orderedListOfExistingFileNames + .addAll(checkSpecificPathsForPdb(predefinedPaths, potentialPdbNames)); return orderedListOfExistingFileNames; @@ -561,12 +567,13 @@ public class PdbLocator { *

*/ private static void getWindowsPaths(Set guidSubdirPaths, Set predefinedPaths) { - // Don't have to call .exists(), since .isDirectory() does that already - if (onWindows && SPECIAL_PDB_LOCATION.isDirectory()) { - predefinedPaths.add(SPECIAL_PDB_LOCATION); + // TODO: Need to provide better control of symbol directory preference + // instead of only using default + if (DEFAULT_SYMBOLS_DIR.isDirectory()) { + predefinedPaths.add(DEFAULT_SYMBOLS_DIR); // Check alternate locations - String specialPdbPath = SPECIAL_PDB_LOCATION.getAbsolutePath(); + String specialPdbPath = DEFAULT_SYMBOLS_DIR.getAbsolutePath(); for (String guidSubdir : guidSubdirPaths) { File testDir = new File(specialPdbPath + guidSubdir); diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/PdbProgramAttributes.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/PdbProgramAttributes.java index d381ea44ff..23803c0a13 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/PdbProgramAttributes.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/PdbProgramAttributes.java @@ -17,6 +17,8 @@ package ghidra.app.util.pdb; import java.util.*; +import org.apache.commons.lang3.StringUtils; + import ghidra.app.util.bin.format.pdb.PdbParserConstants; import ghidra.framework.options.Options; import ghidra.program.model.listing.Program; @@ -119,10 +121,9 @@ public class PdbProgramAttributes { // Want to preserve add order while only keeping unique entries Set set = new LinkedHashSet<>(); - if (pdbFile != null) { + if (!StringUtils.isBlank(pdbFile)) { set.add(getFilename(pdbFile).toLowerCase()); set.add(getFilename(pdbFile)); - set.add(pdbFile); } // getExecutablePath can return "unknown" @@ -162,17 +163,22 @@ public class PdbProgramAttributes { */ private void createGuidAgeString() { - if ((pdbGuid == null && pdbSignature == null) || pdbAge == null) { + if ((StringUtils.isBlank(pdbGuid) && StringUtils.isBlank(pdbSignature)) || + StringUtils.isBlank(pdbAge)) { guidAgeCombo = null; return; } - guidAgeCombo = (pdbGuid == null) ? pdbSignature : pdbGuid; - guidAgeCombo = guidAgeCombo.replaceAll("-", ""); - guidAgeCombo = guidAgeCombo.toUpperCase(); - - int pdbAgeDecimal = Integer.parseInt(pdbAge, 16); - guidAgeCombo += pdbAgeDecimal; + try { + int pdbAgeDecimal = Integer.parseInt(pdbAge, 16); + guidAgeCombo = (pdbGuid == null) ? pdbSignature : pdbGuid; + guidAgeCombo = guidAgeCombo.replaceAll("-", ""); + guidAgeCombo = guidAgeCombo.toUpperCase(); + guidAgeCombo += pdbAgeDecimal; + } + catch (NumberFormatException e) { + return; + } } /** diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/ComplexTypeApplierMapper.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/ComplexTypeApplierMapper.java index 0fbbcaef61..51157a5228 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/ComplexTypeApplierMapper.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/ComplexTypeApplierMapper.java @@ -18,6 +18,7 @@ package ghidra.app.util.pdb.pdbapplicator; import java.util.*; import ghidra.app.util.SymbolPath; +import ghidra.app.util.bin.format.pdb2.pdbreader.AbstractTypeProgramInterface; import ghidra.app.util.bin.format.pdb2.pdbreader.RecordNumber; import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitor; @@ -50,8 +51,13 @@ public class ComplexTypeApplierMapper { //============================================================================================== //============================================================================================== void mapAppliers(TaskMonitor monitor) throws CancelledException { - int indexLimit = applicator.getPdb().getTypeProgramInterface().getTypeIndexMaxExclusive(); - int indexNumber = applicator.getPdb().getTypeProgramInterface().getTypeIndexMin(); + AbstractTypeProgramInterface typeProgramInterface = + applicator.getPdb().getTypeProgramInterface(); + if (typeProgramInterface == null) { + return; + } + int indexLimit = typeProgramInterface.getTypeIndexMaxExclusive(); + int indexNumber = typeProgramInterface.getTypeIndexMin(); monitor.initialize(indexLimit - indexNumber); applicator.setMonitorMessage("PDB: Mapping Composites..."); while (indexNumber < indexLimit) { diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbAddressManager.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbAddressManager.java index 8213c93c0d..5d95eb9b86 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbAddressManager.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbAddressManager.java @@ -257,9 +257,9 @@ public class PdbAddressManager { // trying to use this. long segmentZeroLength = 0x7fffffff; allSegmentsInfo.add(new SegmentInfo(imageBase, segmentZeroLength)); - AbstractDatabaseInterface dbi = applicator.getPdb().getDatabaseInterface(); - if (dbi instanceof DatabaseInterfaceNew) { - DebugData debugData = ((DatabaseInterfaceNew) dbi).getDebugData(); + PdbDebugInfo dbi = applicator.getPdb().getDebugInfo(); + if (dbi instanceof PdbNewDebugInfo) { + DebugData debugData = ((PdbNewDebugInfo) dbi).getDebugData(); List imageSectionHeaders = debugData.getImageSectionHeaders(); for (ImageSectionHeader imageSectionHeader : imageSectionHeaders) { long virtualAddress = imageSectionHeader.getVirtualAddress(); @@ -270,12 +270,12 @@ public class PdbAddressManager { allSegmentsInfo.add(new SegmentInfo(imageBase.add(virtualAddress), size)); } } - // else instance of DatabaseInterface; TODO: what can we do here? + // else instance of PdbDebugInfo; TODO: what can we do here? // Maybe get information from the program itself. // TODO: what should we do with these? Not doing anything at the moment AbstractPdb pdb = applicator.getPdb(); - List segmentMapList = pdb.getDatabaseInterface().getSegmentMapList(); + List segmentMapList = pdb.getDebugInfo().getSegmentMapList(); for (SegmentMapDescription segmentMapDescription : segmentMapList) { segmentMapDescription.getSegmentOffset(); segmentMapDescription.getLength(); @@ -428,7 +428,7 @@ public class PdbAddressManager { // future. /** * Tries to align section/segment information of the PDB in {@link SegmentMapDescription} from - * the {@link AbstractDatabaseInterface} header substream with the memory blocks of the + * the {@link PdbDebugInfo} header substream with the memory blocks of the * {@link Program}. Initializes the lookup table to be used for processing the PDB. *

* We have seen cases where blocks of the program are combined into a single block representing @@ -441,7 +441,7 @@ public class PdbAddressManager { @SuppressWarnings("unused") // for method not being called and local variables ununsed. private void reconcileMemoryBlocks() throws PdbException { // ImageSectionHeader imageSectionHeader = -// pdb.getDatabaseInterface().getDebugData().getImageSectionHeader(); +// pdb.getDebugInfo().getDebugData().getImageSectionHeader(); AbstractPdb pdb = applicator.getPdb(); Program program = applicator.getProgram(); @@ -451,7 +451,7 @@ public class PdbAddressManager { Memory mem = program.getMemory(); MemoryBlock[] blocks = mem.getBlocks(); - List segmentMapList = pdb.getDatabaseInterface().getSegmentMapList(); + List segmentMapList = pdb.getDebugInfo().getSegmentMapList(); /** * Program has additional "Headers" block set up by the {@link PeLoader}. */ @@ -510,15 +510,15 @@ public class PdbAddressManager { @SuppressWarnings("unused") // for method not being called. private boolean garnerSectionSegmentInformation() throws PdbException { AbstractPdb pdb = applicator.getPdb(); - if (pdb.getDatabaseInterface() == null) { + if (pdb.getDebugInfo() == null) { return false; } // ImageSectionHeader imageSectionHeader = -// pdb.getDatabaseInterface().getDebugData().getImageSectionHeader(); +// pdb.getDebugInfo().getDebugData().getImageSectionHeader(); int num = 1; - for (AbstractModuleInformation module : pdb.getDatabaseInterface().getModuleInformationList()) { + for (AbstractModuleInformation module : pdb.getDebugInfo().getModuleInformationList()) { if ("* Linker *".equals(module.getModuleName())) { List linkerSymbolList = applicator.getSymbolGroupForModule(num).getSymbols(); diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbApplicator.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbApplicator.java index d04c4d6b58..531a9ac13e 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbApplicator.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbApplicator.java @@ -356,12 +356,12 @@ public class PdbApplicator { private List createSymbolGroups() throws CancelledException, PdbException { List mySymbolGroups = new ArrayList<>(); - int num = pdb.getDatabaseInterface().getNumModules(); + int num = pdb.getDebugInfo().getNumModules(); // moduleNumber zero is our global/public group. for (int moduleNumber = 0; moduleNumber <= num; moduleNumber++) { monitor.checkCanceled(); Map symbols = - pdb.getDatabaseInterface().getModuleSymbolsByOffset(moduleNumber); + pdb.getDebugInfo().getModuleSymbolsByOffset(moduleNumber); SymbolGroup symbolGroup = new SymbolGroup(symbols, moduleNumber); mySymbolGroups.add(symbolGroup); } @@ -491,7 +491,7 @@ public class PdbApplicator { /** * Returns the {@link CategoryPath} for a typedef with with the give {@link SymbolPath} and - * module number; 1 <= moduleNumber <= {@link AbstractDatabaseInterface#getNumModules()}, + * module number; 1 <= moduleNumber <= {@link PdbDebugInfo#getNumModules()}, * except that modeleNumber of 0 represents publics/globals. * @param moduleNumber module number * @param symbolPath SymbolPath of the symbol @@ -539,11 +539,11 @@ public class PdbApplicator { throws CancelledException, PdbException { List categoryNames = new ArrayList<>(); - int num = pdb.getDatabaseInterface().getNumModules(); + int num = pdb.getDebugInfo().getNumModules(); for (int index = 1; index <= num; index++) { monitor.checkCanceled(); String moduleName = - pdb.getDatabaseInterface().getModuleInformation(index).getModuleName(); + pdb.getDebugInfo().getModuleInformation(index).getModuleName(); categoryNames.add(moduleName); } @@ -610,7 +610,7 @@ public class PdbApplicator { //============================================================================================== //============================================================================================== int findModuleNumberBySectionOffsetContribution(int section, long offset) throws PdbException { - for (AbstractSectionContribution sectionContribution : pdb.getDatabaseInterface().getSectionContributionList()) { + for (AbstractSectionContribution sectionContribution : pdb.getDebugInfo().getSectionContributionList()) { int sectionContributionOffset = sectionContribution.getOffset(); int maxSectionContributionOffset = sectionContributionOffset + sectionContribution.getLength(); @@ -624,6 +624,9 @@ public class PdbApplicator { //============================================================================================== private void processDataTypesSequentially() throws CancelledException, PdbException { AbstractTypeProgramInterface tpi = pdb.getTypeProgramInterface(); + if (tpi == null) { + return; + } int num = tpi.getTypeIndexMaxExclusive() - tpi.getTypeIndexMin(); monitor.initialize(num); setMonitorMessage("PDB: Processing " + num + " data type components..."); @@ -688,6 +691,9 @@ public class PdbApplicator { //============================================================================================== private void processItemTypesSequentially() throws CancelledException, PdbException { AbstractTypeProgramInterface ipi = pdb.getItemProgramInterface(); + if (ipi == null) { + return; + } int num = ipi.getTypeIndexMaxExclusive() - ipi.getTypeIndexMin(); monitor.initialize(num); setMonitorMessage("PDB: Processing " + num + " item type components..."); @@ -725,6 +731,9 @@ public class PdbApplicator { //============================================================================================== private void resolveSequentially() throws CancelledException { AbstractTypeProgramInterface tpi = pdb.getTypeProgramInterface(); + if (tpi == null) { + return; + } int num = tpi.getTypeIndexMaxExclusive() - tpi.getTypeIndexMin(); monitor.initialize(num); setMonitorMessage("PDB: Resolving " + num + " data type components..."); @@ -772,7 +781,7 @@ public class PdbApplicator { // public AbstractMsSymbol getSymbolForModuleAndOffset(int moduleNumber, long offset) // throws PdbException { -// return pdb.getDatabaseInterface().getSymbolForModuleAndOffsetOfRecord(moduleNumber, offset); +// return pdb.getDebugInfo().getSymbolForModuleAndOffsetOfRecord(moduleNumber, offset); // } //============================================================================================== @@ -931,7 +940,7 @@ public class PdbApplicator { //============================================================================================== private void processModuleSymbols() throws CancelledException { int totalCount = 0; - int num = pdb.getDatabaseInterface().getNumModules(); + int num = pdb.getDebugInfo().getNumModules(); for (int moduleNumber = 1; moduleNumber <= num; moduleNumber++) { monitor.checkCanceled(); SymbolGroup symbolGroup = getSymbolGroupForModule(moduleNumber); @@ -990,7 +999,7 @@ public class PdbApplicator { SymbolGroup symbolGroup = getSymbolGroup(); PublicSymbolInformation publicSymbolInformation = - pdb.getDatabaseInterface().getPublicSymbolInformation(); + pdb.getDebugInfo().getPublicSymbolInformation(); List offsets = publicSymbolInformation.getModifiedHashRecordSymbolOffsets(); setMonitorMessage("PDB: Applying " + offsets.size() + " public symbol components..."); monitor.initialize(offsets.size()); @@ -1020,7 +1029,7 @@ public class PdbApplicator { SymbolGroup symbolGroup = getSymbolGroup(); GlobalSymbolInformation globalSymbolInformation = - pdb.getDatabaseInterface().getGlobalSymbolInformation(); + pdb.getDebugInfo().getGlobalSymbolInformation(); List offsets = globalSymbolInformation.getModifiedHashRecordSymbolOffsets(); setMonitorMessage("PDB: Applying global symbols..."); monitor.initialize(offsets.size()); @@ -1051,7 +1060,7 @@ public class PdbApplicator { SymbolGroup symbolGroup = getSymbolGroup(); GlobalSymbolInformation globalSymbolInformation = - pdb.getDatabaseInterface().getGlobalSymbolInformation(); + pdb.getDebugInfo().getGlobalSymbolInformation(); List offsets = globalSymbolInformation.getModifiedHashRecordSymbolOffsets(); setMonitorMessage("PDB: Applying typedefs..."); monitor.initialize(offsets.size()); @@ -1081,11 +1090,11 @@ public class PdbApplicator { @SuppressWarnings("unused") // for method not being called. private void processNonPublicOrGlobalSymbols() throws CancelledException, PdbException { Set offsetsRemaining = getSymbolGroup().getOffsets(); - for (long off : pdb.getDatabaseInterface().getPublicSymbolInformation().getModifiedHashRecordSymbolOffsets()) { + for (long off : pdb.getDebugInfo().getPublicSymbolInformation().getModifiedHashRecordSymbolOffsets()) { monitor.checkCanceled(); offsetsRemaining.remove(off); } - for (long off : pdb.getDatabaseInterface().getGlobalSymbolInformation().getModifiedHashRecordSymbolOffsets()) { + for (long off : pdb.getDebugInfo().getGlobalSymbolInformation().getModifiedHashRecordSymbolOffsets()) { monitor.checkCanceled(); offsetsRemaining.remove(off); } @@ -1108,9 +1117,9 @@ public class PdbApplicator { //============================================================================================== private int findLinkerModuleNumber() { - if (pdb.getDatabaseInterface() != null) { + if (pdb.getDebugInfo() != null) { int num = 1; - for (AbstractModuleInformation module : pdb.getDatabaseInterface().getModuleInformationList()) { + for (AbstractModuleInformation module : pdb.getDebugInfo().getModuleInformationList()) { if (isLinkerModule(module.getModuleName())) { return num; } @@ -1159,7 +1168,7 @@ public class PdbApplicator { int linkerModuleNumber = findLinkerModuleNumber(); int totalCount = 0; - int num = pdb.getDatabaseInterface().getNumModules(); + int num = pdb.getDebugInfo().getNumModules(); for (int index = 1; index <= num; index++) { monitor.checkCanceled(); if (index == linkerModuleNumber) { diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbResearch.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbResearch.java index 84b448ceae..497c5b5883 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbResearch.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbResearch.java @@ -411,7 +411,7 @@ public class PdbResearch { throws CancelledException, PdbException { SymbolGroup symbolGroup = applicator.getSymbolGroup(); GlobalSymbolInformation globalSymbolInformation = - applicator.getPdb().getDatabaseInterface().getGlobalSymbolInformation(); + applicator.getPdb().getDebugInfo().getGlobalSymbolInformation(); List offsets = globalSymbolInformation.getModifiedHashRecordSymbolOffsets(); applicator.setMonitorMessage("PDB: Applying typedefs..."); monitor.initialize(offsets.size()); @@ -619,7 +619,7 @@ public class PdbResearch { SymbolGroup symbolGroup = applicator.getSymbolGroup(); PublicSymbolInformation publicSymbolInformation = - pdb.getDatabaseInterface().getPublicSymbolInformation(); + pdb.getDebugInfo().getPublicSymbolInformation(); List offsets = publicSymbolInformation.getModifiedHashRecordSymbolOffsets(); applicator.setMonitorMessage( "PDB: Applying " + offsets.size() + " public symbol components..."); @@ -646,7 +646,7 @@ public class PdbResearch { SymbolGroup symbolGroup = applicator.getSymbolGroup(); GlobalSymbolInformation globalSymbolInformation = - pdb.getDatabaseInterface().getGlobalSymbolInformation(); + pdb.getDebugInfo().getGlobalSymbolInformation(); List offsets = globalSymbolInformation.getModifiedHashRecordSymbolOffsets(); applicator.setMonitorMessage("PDB: Applying global symbols..."); monitor.initialize(offsets.size()); @@ -670,7 +670,7 @@ public class PdbResearch { Map> map, TaskMonitor monitor) throws CancelledException { AbstractPdb pdb = applicator.getPdb(); int totalCount = 0; - int num = pdb.getDatabaseInterface().getNumModules(); + int num = pdb.getDebugInfo().getNumModules(); for (int moduleNumber = 1; moduleNumber <= num; moduleNumber++) { monitor.checkCanceled(); SymbolGroup symbolGroup = applicator.getSymbolGroupForModule(moduleNumber); @@ -688,7 +688,7 @@ public class PdbResearch { monitor.checkCanceled(); // String moduleName = -// pdb.getDatabaseInterface().getModuleInformation(index).getModuleName(); +// pdb.getDebugInfo().getModuleInformation(index).getModuleName(); // Process module symbols list SymbolGroup symbolGroup = applicator.getSymbolGroupForModule(moduleNumber); diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbVbtManager.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbVbtManager.java index 6049d8341a..e0f1367c21 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbVbtManager.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbVbtManager.java @@ -57,7 +57,7 @@ public class PdbVbtManager extends VbtManager { Map myAddressByMangledName = new HashMap<>(); PublicSymbolInformation publicSymbolInformation = - applicator.getPdb().getDatabaseInterface().getPublicSymbolInformation(); + applicator.getPdb().getDebugInfo().getPublicSymbolInformation(); List offsets = publicSymbolInformation.getModifiedHashRecordSymbolOffsets(); applicator.setMonitorMessage("PDB: Searching for virtual base table symbols..."); monitor.initialize(offsets.size()); diff --git a/Ghidra/Features/PDB/src/main/java/pdb/AskPdbOptionsDialog.java b/Ghidra/Features/PDB/src/main/java/pdb/AskPdbOptionsDialog.java new file mode 100644 index 0000000000..780954e932 --- /dev/null +++ b/Ghidra/Features/PDB/src/main/java/pdb/AskPdbOptionsDialog.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; + +import java.awt.*; + +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.PdbApplicatorRestrictions; +import ghidra.util.layout.PairLayout; + +class AskPdbOptionsDialog extends DialogComponentProvider { + + private boolean isCanceled; + + private boolean useMsDiaParser = true; + private PdbApplicatorRestrictions restrictions = PdbApplicatorRestrictions.NONE; + + /** + * 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 restrictionsCombo = + new GComboBox<>(PdbApplicatorRestrictions.values()); + restrictionsCombo.setSelectedItem(PdbApplicatorRestrictions.NONE); + restrictionsCombo.addActionListener(e -> { + restrictions = (PdbApplicatorRestrictions) restrictionsCombo.getSelectedItem(); + }); + + optionsPanel.add(new JLabel("PDB Parser:")); + + if (isPdbFile) { + if (PdbParser.onWindows) { + final GComboBox combo = + new GComboBox<>(new String[] { "PDB MSDIA", "PDB Universal (Prototype)" }); + combo.setSelectedIndex(0); + restrictionsCombo.setEnabled(!useMsDiaParser); + combo.addActionListener(e -> { + useMsDiaParser = (combo.getSelectedIndex() == 0); + restrictionsCombo.setEnabled(!useMsDiaParser); + if (useMsDiaParser) { + restrictionsCombo.setSelectedItem(PdbApplicatorRestrictions.NONE); + } + }); + optionsPanel.add(combo); + } + else { + useMsDiaParser = false; + JLabel label = new JLabel("PDB Universal (Prototype)"); + 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("Restrictions:")); + optionsPanel.add(restrictionsCombo); + + 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; + } + + PdbApplicatorRestrictions getApplicatorRestrictions() { + return restrictions; + } + +} diff --git a/Ghidra/Features/PDB/src/main/java/pdb/AskPdbUrlDialog.java b/Ghidra/Features/PDB/src/main/java/pdb/AskPdbUrlDialog.java index 41750b5c1f..015bc2bece 100644 --- a/Ghidra/Features/PDB/src/main/java/pdb/AskPdbUrlDialog.java +++ b/Ghidra/Features/PDB/src/main/java/pdb/AskPdbUrlDialog.java @@ -19,7 +19,6 @@ import java.awt.*; import java.awt.event.*; import java.io.IOException; import java.io.InputStream; -import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.List; import java.util.Scanner; @@ -98,18 +97,8 @@ public class AskPdbUrlDialog extends DialogComponentProvider { setDefaultButton(okButton); setRememberSize(false); - if (SwingUtilities.isEventDispatchThread()) { - DockingWindowManager.showDialog(parent, this); - } - else { - try { - SwingUtilities.invokeAndWait( - () -> DockingWindowManager.showDialog(parent, AskPdbUrlDialog.this)); - } - catch (InvocationTargetException | InterruptedException e) { - // TODO: handle this? - } - } + + DockingWindowManager.showDialog(parent, AskPdbUrlDialog.this); } @Override diff --git a/Ghidra/Features/PDB/src/main/java/pdb/LoadPdbTask.java b/Ghidra/Features/PDB/src/main/java/pdb/LoadPdbTask.java index 21ed709833..57aa002343 100644 --- a/Ghidra/Features/PDB/src/main/java/pdb/LoadPdbTask.java +++ b/Ghidra/Features/PDB/src/main/java/pdb/LoadPdbTask.java @@ -16,15 +16,20 @@ package pdb; 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; import ghidra.app.util.bin.format.pdb.PdbException; import ghidra.app.util.bin.format.pdb.PdbParser; +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; import ghidra.program.model.address.AddressSetView; import ghidra.program.model.listing.Program; @@ -37,11 +42,16 @@ class LoadPdbTask extends Task { private File pdbFile; private DataTypeManagerService service; private final Program program; + private final boolean useMsDiaParser; + private final PdbApplicatorRestrictions restrictions; // PDB Universal Parser only - LoadPdbTask(Program program, File pdbFile, DataTypeManagerService service) { + LoadPdbTask(Program program, File pdbFile, boolean useMsDiaParser, + PdbApplicatorRestrictions restrictions, DataTypeManagerService service) { super("Loading PDB...", true, false, false); this.program = program; this.pdbFile = pdbFile; + this.useMsDiaParser = useMsDiaParser; + this.restrictions = restrictions; this.service = service; } @@ -58,33 +68,38 @@ class LoadPdbTask extends Task { @Override public boolean analysisWorkerCallback(Program currentProgram, Object workerContext, - TaskMonitor currentMonitor) throws Exception, CancelledException, PdbException { + TaskMonitor currentMonitor) throws CancelledException { - PdbParser parser = - new PdbParser(pdbFile, program, service, true, currentMonitor); - - parser.parse(); - parser.openDataTypeArchives(); - parser.applyTo(log); - - analyzeSymbols(currentMonitor, log); - return !monitor.isCancelled(); + try { + if (useMsDiaParser) { + if (!parseWithMsDiaParser(log, monitor)) { + return false; + } + } + else if (!parseWithNewParser(log, monitor)) { + return false; + } + analyzeSymbols(currentMonitor, log); + } + catch (IOException e) { + log.appendMsg("PDB IO Error: " + e.getMessage()); + } + return false; } }; - boolean analyzed = - program.getOptions(Program.PROGRAM_INFO).getBoolean(Program.ANALYZED, false); - if (analyzed) { - Msg.showWarn(this, null, "PDB Warning", - "Loading PDB after analysis has been performed will produce" + - "\npoor results. PDBs should be loaded prior to analysis or" + - "\nautomatically during auto-analysis."); - } + try { AutoAnalysisManager.getAnalysisManager(program) .scheduleWorker(worker, null, true, monitor); + 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) { // ignore @@ -105,17 +120,53 @@ class LoadPdbTask extends Task { } } + message = "Error processing PDB file: " + pdbFile + ".\n" + message; + Msg.showError(getClass(), null, "Load PDB Failed", message, t); } - 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); - } } + private boolean parseWithMsDiaParser(MessageLog log, TaskMonitor monitor) + throws IOException, CancelledException { + PdbParser parser = new PdbParser(pdbFile, program, service, true, monitor); + try { + parser.parse(); + parser.openDataTypeArchives(); + parser.applyTo(log); + return true; + } + catch (PdbException | DuplicateIdException e) { + log.appendMsg("PDB Error: " + e.getMessage()); + } + return false; + } + + private boolean parseWithNewParser(MessageLog log, TaskMonitor monitor) + throws IOException, CancelledException { + + PdbReaderOptions pdbReaderOptions = new PdbReaderOptions(); // use defaults + + PdbApplicatorOptions pdbApplicatorOptions = new PdbApplicatorOptions(); + + pdbApplicatorOptions.setRestrictions(restrictions); + + try (AbstractPdb pdb = + ghidra.app.util.bin.format.pdb2.pdbreader.PdbParser.parse(pdbFile.getAbsolutePath(), + pdbReaderOptions, monitor)) { + monitor.setMessage("PDB: Parsing " + pdbFile + "..."); + pdb.deserialize(monitor); + PdbApplicator applicator = new PdbApplicator(pdbFile.getAbsolutePath(), pdb); + applicator.applyTo(program, program.getDataTypeManager(), program.getImageBase(), + pdbApplicatorOptions, monitor, log); + return true; + } + catch (ghidra.app.util.bin.format.pdb2.pdbreader.PdbException e) { + log.appendMsg("PDB Error: " + e.getMessage()); + } + return false; + } + private void analyzeSymbols(TaskMonitor monitor, MessageLog log) { MicrosoftDemanglerAnalyzer demanglerAnalyzer = new MicrosoftDemanglerAnalyzer(); @@ -135,9 +186,8 @@ class LoadPdbTask extends Task { demanglerAnalyzer.added(program, addrs, monitor, log); } catch (CancelledException e) { - // Don't care about CancelledException + // ignore cancel } - } } } diff --git a/Ghidra/Features/PDB/src/main/java/pdb/PdbPlugin.java b/Ghidra/Features/PDB/src/main/java/pdb/PdbPlugin.java index c6be31a80a..d97f39651a 100644 --- a/Ghidra/Features/PDB/src/main/java/pdb/PdbPlugin.java +++ b/Ghidra/Features/PDB/src/main/java/pdb/PdbPlugin.java @@ -18,6 +18,7 @@ package pdb; import java.io.File; import docking.action.MenuData; +import docking.widgets.OptionDialog; import docking.widgets.filechooser.GhidraFileChooser; import ghidra.app.CorePluginPackage; import ghidra.app.context.ProgramActionContext; @@ -25,8 +26,8 @@ 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.pdb.pdbapplicator.PdbApplicatorRestrictions; import ghidra.framework.plugintool.*; import ghidra.framework.plugintool.util.PluginStatus; import ghidra.program.model.listing.Program; @@ -89,13 +90,40 @@ public class PdbPlugin extends Plugin { return; } + boolean analyzed = + program.getOptions(Program.PROGRAM_INFO).getBoolean(Program.ANALYZED, false); + + if (analyzed) { + int response = + OptionDialog.showOptionDialogWithCancelAsDefaultButton(null, "Load PDB Warning", + "Loading PDB after running analysis may produce poor results." + + "\nPDBs should generally be loaded prior to analysis or" + + "\nautomatically during auto-analysis.", + "Continue"); + if (response != OptionDialog.OPTION_ONE) { + return; + } + } + try { File pdb = getPdbFile(program); if (pdb == 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(); + PdbApplicatorRestrictions restrictions = optionsDialog.getApplicatorRestrictions(); + tool.setStatusInfo(""); + DataTypeManagerService service = tool.getService(DataTypeManagerService.class); if (service == null) { Msg.showWarn(getClass(), null, "Load PDB", @@ -103,14 +131,15 @@ public class PdbPlugin extends Plugin { return; } - TaskLauncher.launch(new LoadPdbTask(program, pdb, service)); + TaskLauncher + .launch(new LoadPdbTask(program, pdb, useMsDiaParser, restrictions, service)); } catch (Exception pe) { Msg.showError(getClass(), null, "Error", pe.getMessage()); } } - private File getPdbFile(Program program) throws PdbException { + private File getPdbFile(Program program) { File pdbFile = PdbParser.findPDB(program); if (pdbChooser == null) { pdbChooser = new GhidraFileChooser(tool.getToolFrame()); @@ -121,7 +150,9 @@ public class PdbPlugin extends Plugin { "Program Database Files and PDB XML Representations")); } - pdbChooser.setSelectedFile(pdbFile); + if (pdbFile != null) { + pdbChooser.setSelectedFile(pdbFile); + } File selectedFile = pdbChooser.getSelectedFile(); return selectedFile; diff --git a/Ghidra/Features/PDB/src/main/java/pdb/PdbSymbolServerPlugin.java b/Ghidra/Features/PDB/src/main/java/pdb/PdbSymbolServerPlugin.java index 0c3e13f657..b4b5ba903e 100644 --- a/Ghidra/Features/PDB/src/main/java/pdb/PdbSymbolServerPlugin.java +++ b/Ghidra/Features/PDB/src/main/java/pdb/PdbSymbolServerPlugin.java @@ -30,10 +30,13 @@ 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.*; +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.PdbProgramAttributes; +import ghidra.app.util.pdb.pdbapplicator.PdbApplicatorRestrictions; import ghidra.framework.Application; import ghidra.framework.plugintool.*; import ghidra.framework.plugintool.util.PluginStatus; @@ -193,6 +196,12 @@ public class PdbSymbolServerPlugin extends Plugin { 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(); @@ -298,7 +307,7 @@ public class PdbSymbolServerPlugin extends Plugin { int choice = OptionDialog.showOptionDialog( null, "pdb or pdb.xml", - "Download a .pdb or .pdb.xml file? (.pdb.xml can be processed on non-Windows systems)", + "Download a .pdb or .pdb.xml file?", "PDB", "XML"); //@formatter:on @@ -697,6 +706,8 @@ public class PdbSymbolServerPlugin extends Plugin { 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; @@ -775,34 +786,39 @@ public class PdbSymbolServerPlugin extends Plugin { private void tryToLoadPdb(File downloadedPdb, Program currentProgram) { - // Only ask to load PDB if file type is applicable for current OS - if (fileType == PdbFileType.PDB && !PdbParser.onWindows) { + AutoAnalysisManager aam = AutoAnalysisManager.getAnalysisManager(currentProgram); + if (aam.isAnalyzing()) { + Msg.showWarn(getClass(), null, "Load PDB", + "Unable to load PDB file while analysis is running."); return; } - String htmlString = - HTMLUtilities.toWrappedHTML("Would you like to apply the following PDB:\n\n" + - downloadedPdb.getAbsolutePath() + "\n\n to " + currentProgram.getName() + "?"); + boolean analyzed = + currentProgram.getOptions(Program.PROGRAM_INFO).getBoolean(Program.ANALYZED, false); - int response = OptionDialog.showYesNoDialog(null, "Load PDB?", htmlString); - - switch (response) { - case 0: - // User cancelled - return; - - case 1: - // Yes -- do nothing here - break; - - case 2: - // No - return; - - default: - // do nothing + 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(); + PdbApplicatorRestrictions restrictions = optionsDialog.getApplicatorRestrictions(); + tool.setStatusInfo(""); try { @@ -813,7 +829,10 @@ public class PdbSymbolServerPlugin extends Plugin { return; } - TaskLauncher.launch(new LoadPdbTask(currentProgram, downloadedPdb, service)); + TaskLauncher + .launch( + new LoadPdbTask(currentProgram, downloadedPdb, useMsDiaParser, restrictions, + service)); } catch (Exception pe) { Msg.showError(getClass(), null, "Error", pe.getMessage()); 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 39214e299a..0eb4c79ff9 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 @@ -358,17 +358,13 @@ public class PdbParserTest extends AbstractGhidraHeadlessIntegrationTest { @Test public void testFindPdb2() throws Exception { - try { - createdFiles = createFiles(PdbLocation.SYMBOLS_SUBDIR, PdbXmlLocation.NONE); + createdFiles = createFiles(PdbLocation.SYMBOLS_SUBDIR, PdbXmlLocation.NONE); - File pdb = PdbParser.findPDB(testProgram, false, symbolsFolder.getAbsolutePath()); + File pdb = PdbParser.findPDB(testProgram, false, symbolsFolder.getAbsolutePath()); + + assertNotNull(pdb); + assertEquals(pdbFile.getAbsolutePath(), pdb.getAbsolutePath()); - assertNotNull(pdb); - assertEquals(pdbFile.getAbsolutePath(), pdb.getAbsolutePath()); - } - catch (PdbException pdbe) { - fail("Unexpected PdbException!"); - } } /** @@ -411,17 +407,13 @@ public class PdbParserTest extends AbstractGhidraHeadlessIntegrationTest { @Test public void testFindPdb4() throws Exception { - try { - createdFiles = createFiles(PdbLocation.SYMBOLS_SUBDIR, PdbXmlLocation.OWN_DIR); + createdFiles = createFiles(PdbLocation.SYMBOLS_SUBDIR, PdbXmlLocation.OWN_DIR); - File pdb = PdbParser.findPDB(testProgram, false, symbolsFolder.getAbsolutePath()); + File pdb = PdbParser.findPDB(testProgram, false, symbolsFolder.getAbsolutePath()); + + assertNotNull(pdb); + assertEquals(pdb.getAbsolutePath(), pdb.getAbsolutePath()); - assertNotNull(pdb); - assertEquals(pdb.getAbsolutePath(), pdb.getAbsolutePath()); - } - catch (PdbException pdbe) { - fail("Unexpected PdbException!"); - } } /** @@ -464,17 +456,13 @@ public class PdbParserTest extends AbstractGhidraHeadlessIntegrationTest { @Test public void testFindPdb6() throws Exception { - try { - createdFiles = createFiles(PdbLocation.SYMBOLS_NO_SUBDIR, PdbXmlLocation.NONE); + createdFiles = createFiles(PdbLocation.SYMBOLS_NO_SUBDIR, PdbXmlLocation.NONE); - File pdb = PdbParser.findPDB(testProgram, false, symbolsFolder.getAbsolutePath()); + File pdb = PdbParser.findPDB(testProgram, false, symbolsFolder.getAbsolutePath()); + + assertNotNull(pdb); + assertEquals(pdbFile.getAbsolutePath(), pdb.getAbsolutePath()); - assertNotNull(pdb); - assertEquals(pdbFile.getAbsolutePath(), pdb.getAbsolutePath()); - } - catch (PdbException pdbe) { - fail("Unexpected PdbException!"); - } } /** @@ -537,17 +525,13 @@ public class PdbParserTest extends AbstractGhidraHeadlessIntegrationTest { @Test public void testFindPdb9() throws Exception { - try { - createdFiles = createFiles(PdbLocation.SYMBOLS_NO_SUBDIR, PdbXmlLocation.OWN_DIR); + createdFiles = createFiles(PdbLocation.SYMBOLS_NO_SUBDIR, PdbXmlLocation.OWN_DIR); - File pdb = PdbParser.findPDB(testProgram, false, symbolsFolder.getAbsolutePath()); + File pdb = PdbParser.findPDB(testProgram, false, symbolsFolder.getAbsolutePath()); + + assertNotNull(pdb); + assertEquals(pdbFile.getAbsolutePath(), pdb.getAbsolutePath()); - assertNotNull(pdb); - assertEquals(pdbFile.getAbsolutePath(), pdb.getAbsolutePath()); - } - catch (PdbException pdbe) { - fail("Unexpected PdbException!"); - } } /** @@ -590,17 +574,13 @@ public class PdbParserTest extends AbstractGhidraHeadlessIntegrationTest { @Test public void testFindPdb11() throws Exception { - try { - createdFiles = createFiles(PdbLocation.SAME_AS_EXE_SUBDIR, PdbXmlLocation.NONE); + createdFiles = createFiles(PdbLocation.SAME_AS_EXE_SUBDIR, PdbXmlLocation.NONE); - File pdb = PdbParser.findPDB(testProgram, false, pdbFile.getParent()); + File pdb = PdbParser.findPDB(testProgram, false, pdbFile.getParent()); + + assertNotNull(pdb); + assertEquals(pdbFile.getAbsolutePath(), pdb.getAbsolutePath()); - assertNotNull(pdb); - assertEquals(pdbFile.getAbsolutePath(), pdb.getAbsolutePath()); - } - catch (PdbException pdbe) { - fail("Unexpected PdbException!"); - } } /** @@ -663,17 +643,13 @@ public class PdbParserTest extends AbstractGhidraHeadlessIntegrationTest { @Test public void testFindPdb14() throws Exception { - try { - createdFiles = createFiles(PdbLocation.SAME_AS_EXE_SUBDIR, PdbXmlLocation.OWN_DIR); + createdFiles = createFiles(PdbLocation.SAME_AS_EXE_SUBDIR, PdbXmlLocation.OWN_DIR); - File pdb = PdbParser.findPDB(testProgram, false, pdbFile.getParent()); + File pdb = PdbParser.findPDB(testProgram, false, pdbFile.getParent()); + + assertNotNull(pdb); + assertEquals(pdbFile.getAbsolutePath(), pdb.getAbsolutePath()); - assertNotNull(pdb); - assertEquals(pdbFile.getAbsolutePath(), pdb.getAbsolutePath()); - } - catch (PdbException pdbe) { - fail("Unexpected PdbException!"); - } } /** @@ -719,17 +695,13 @@ public class PdbParserTest extends AbstractGhidraHeadlessIntegrationTest { @Test public void testFindPdb16() throws Exception { - try { - createdFiles = createFiles(PdbLocation.SAME_AS_EXE_NO_SUBDIR, PdbXmlLocation.NONE); + createdFiles = createFiles(PdbLocation.SAME_AS_EXE_NO_SUBDIR, PdbXmlLocation.NONE); - File pdb = PdbParser.findPDB(testProgram, false, defaultSymbolsRepoPath); + File pdb = PdbParser.findPDB(testProgram, false, defaultSymbolsRepoPath); + + assertNotNull(pdb); + assertEquals(pdbFile.getAbsolutePath(), pdb.getAbsolutePath()); - assertNotNull(pdb); - assertEquals(pdbFile.getAbsolutePath(), pdb.getAbsolutePath()); - } - catch (PdbException pdbe) { - fail("Unexpected PdbException!"); - } } /** @@ -745,17 +717,13 @@ public class PdbParserTest extends AbstractGhidraHeadlessIntegrationTest { @Test public void testFindPdb17() throws Exception { - try { - createdFiles = createFiles(PdbLocation.SAME_AS_EXE_NO_SUBDIR, PdbXmlLocation.NONE); + createdFiles = createFiles(PdbLocation.SAME_AS_EXE_NO_SUBDIR, PdbXmlLocation.NONE); - File pdb = PdbParser.findPDB(testProgram, false, pdbFile.getParent()); + File pdb = PdbParser.findPDB(testProgram, false, pdbFile.getParent()); + + assertNotNull(pdb); + assertEquals(pdbFile.getAbsolutePath(), pdb.getAbsolutePath()); - assertNotNull(pdb); - assertEquals(pdbFile.getAbsolutePath(), pdb.getAbsolutePath()); - } - catch (PdbException pdbe) { - fail("Unexpected PdbException!"); - } } /** @@ -774,17 +742,13 @@ public class PdbParserTest extends AbstractGhidraHeadlessIntegrationTest { @Test public void testFindPdb18() throws Exception { - try { - createdFiles = createFiles(PdbLocation.SAME_AS_EXE_NO_SUBDIR, PdbXmlLocation.NONE); + createdFiles = createFiles(PdbLocation.SAME_AS_EXE_NO_SUBDIR, PdbXmlLocation.NONE); - File pdb = PdbParser.findPDB(testProgram, false, pdbXmlDir.getAbsolutePath()); + File pdb = PdbParser.findPDB(testProgram, false, pdbXmlDir.getAbsolutePath()); + + assertNotNull(pdb); + assertEquals(pdbFile.getAbsolutePath(), pdb.getAbsolutePath()); - assertNotNull(pdb); - assertEquals(pdbFile.getAbsolutePath(), pdb.getAbsolutePath()); - } - catch (PdbException pdbe) { - fail("Unexpected PdbException!"); - } } /** @@ -857,17 +821,12 @@ public class PdbParserTest extends AbstractGhidraHeadlessIntegrationTest { @Test public void testFindPdb21() throws Exception { - try { - createdFiles = createFiles(PdbLocation.SAME_AS_EXE_NO_SUBDIR, PdbXmlLocation.OWN_DIR); + createdFiles = createFiles(PdbLocation.SAME_AS_EXE_NO_SUBDIR, PdbXmlLocation.OWN_DIR); - File pdb = PdbParser.findPDB(testProgram, false, defaultSymbolsRepoPath); + File pdb = PdbParser.findPDB(testProgram, false, defaultSymbolsRepoPath); - assertNotNull(pdb); - assertEquals(pdbFile.getAbsolutePath(), pdb.getAbsolutePath()); - } - catch (PdbException pdbe) { - fail("Unexpected PdbException!"); - } + assertNotNull(pdb); + assertEquals(pdbFile.getAbsolutePath(), pdb.getAbsolutePath()); } /** @@ -883,18 +842,12 @@ public class PdbParserTest extends AbstractGhidraHeadlessIntegrationTest { @Test public void testFindPdb22() throws Exception { - try { - createdFiles = createFiles(PdbLocation.SAME_AS_EXE_NO_SUBDIR, PdbXmlLocation.OWN_DIR); + createdFiles = createFiles(PdbLocation.SAME_AS_EXE_NO_SUBDIR, PdbXmlLocation.OWN_DIR); - File pdb = - PdbParser.findPDB(testProgram, false, pdbFile.getParentFile().getAbsolutePath()); + File pdb = PdbParser.findPDB(testProgram, false, pdbFile.getParentFile().getAbsolutePath()); - assertNotNull(pdb); - assertEquals(pdbFile.getAbsolutePath(), pdb.getAbsolutePath()); - } - catch (PdbException pdbe) { - fail("Unexpected PdbException!"); - } + assertNotNull(pdb); + assertEquals(pdbFile.getAbsolutePath(), pdb.getAbsolutePath()); } /** diff --git a/Ghidra/Features/PDB/src/test/java/ghidra/app/util/bin/format/pdb2/pdbreader/DummyDatabaseInterface.java b/Ghidra/Features/PDB/src/test/java/ghidra/app/util/bin/format/pdb2/pdbreader/DummyDatabaseInterface.java index 4c645e6c4a..c67d3d15e0 100644 --- a/Ghidra/Features/PDB/src/test/java/ghidra/app/util/bin/format/pdb2/pdbreader/DummyDatabaseInterface.java +++ b/Ghidra/Features/PDB/src/test/java/ghidra/app/util/bin/format/pdb2/pdbreader/DummyDatabaseInterface.java @@ -15,15 +15,12 @@ */ package ghidra.app.util.bin.format.pdb2.pdbreader; -import ghidra.app.util.bin.format.pdb2.pdbreader.AbstractPdb; -import ghidra.app.util.bin.format.pdb2.pdbreader.DatabaseInterface; - /** - * This class is an extension of {@link DatabaseInterface}, whose sole purpose + * This class is an extension of {@link PdbOldDebugInfo}, whose sole purpose * is to allow for testing of internal components of {@link AbstractPdb} classes. It is not * part of the production PDB Reader. */ -class DummyDatabaseInterface extends DatabaseInterface { +class DummyDebugInfo extends PdbOldDebugInfo { //============================================================================================== // Package-Protected Internals @@ -31,9 +28,9 @@ class DummyDatabaseInterface extends DatabaseInterface { /** * IMPORTANT: This method is for testing only. It allows us to set a basic object. * Note: not all values are initialized. - * @param pdb The AbstractPdb foundation for the DatabaseInterface. + * @param pdb The AbstractPdb foundation for the {@link PdbOldDebugInfo}. */ - DummyDatabaseInterface(AbstractPdb pdb) { + DummyDebugInfo(AbstractPdb pdb) { super(pdb, 0); } diff --git a/Ghidra/Features/PDB/src/test/java/ghidra/app/util/bin/format/pdb2/pdbreader/DummyDatabaseInterfaceNew.java b/Ghidra/Features/PDB/src/test/java/ghidra/app/util/bin/format/pdb2/pdbreader/DummyDatabaseInterfaceNew.java index 84e3bc683e..7ce11c1397 100644 --- a/Ghidra/Features/PDB/src/test/java/ghidra/app/util/bin/format/pdb2/pdbreader/DummyDatabaseInterfaceNew.java +++ b/Ghidra/Features/PDB/src/test/java/ghidra/app/util/bin/format/pdb2/pdbreader/DummyDatabaseInterfaceNew.java @@ -15,15 +15,12 @@ */ package ghidra.app.util.bin.format.pdb2.pdbreader; -import ghidra.app.util.bin.format.pdb2.pdbreader.AbstractPdb; -import ghidra.app.util.bin.format.pdb2.pdbreader.DatabaseInterfaceNew; - /** - * This class is an extension of {@link DatabaseInterfaceNew}, whose sole purpose + * This class is an extension of {@link PdbNewDebugInfo}, whose sole purpose * is to allow for testing of internal components of {@link AbstractPdb} classes. It is not * part of the production PDB Reader. */ -class DummyDatabaseInterfaceNew extends DatabaseInterfaceNew { +class DummyDebugInfoNew extends PdbNewDebugInfo { //============================================================================================== // Package-Protected Internals @@ -31,9 +28,9 @@ class DummyDatabaseInterfaceNew extends DatabaseInterfaceNew { /** * IMPORTANT: This method is for testing only. It allows us to set a basic object. * Note: not all values are initialized. - * @param pdb The AbstractPdb foundation for the DatabaseInterface. + * @param pdb The AbstractPdb foundation for the {@link PdbNewDebugInfo}. */ - DummyDatabaseInterfaceNew(AbstractPdb pdb) { + DummyDebugInfoNew(AbstractPdb pdb) { super(pdb, -1); } diff --git a/Ghidra/Features/PDB/src/test/java/ghidra/app/util/bin/format/pdb2/pdbreader/DummyPdb700.java b/Ghidra/Features/PDB/src/test/java/ghidra/app/util/bin/format/pdb2/pdbreader/DummyPdb700.java index ecca0b8823..83c179aee6 100644 --- a/Ghidra/Features/PDB/src/test/java/ghidra/app/util/bin/format/pdb2/pdbreader/DummyPdb700.java +++ b/Ghidra/Features/PDB/src/test/java/ghidra/app/util/bin/format/pdb2/pdbreader/DummyPdb700.java @@ -48,7 +48,7 @@ public class DummyPdb700 extends Pdb700 { super(null, new PdbReaderOptions()); typeProgramInterface = new DummyTypeProgramInterface800(this, tpiIndexMin, tpiIndexMaxExclusive); - databaseInterface = new DummyDatabaseInterfaceNew(this); + debugInfo = new DummyDebugInfoNew(this); hasIdStream = true; itemProgramInterface = new DummyTypeProgramInterface800(this, ipiIndexMin, ipiIndexMaxExclusive); diff --git a/Ghidra/Features/PDB/src/test/java/ghidra/app/util/bin/format/pdb2/pdbreader/DummyTypeProgramInterface800.java b/Ghidra/Features/PDB/src/test/java/ghidra/app/util/bin/format/pdb2/pdbreader/DummyTypeProgramInterface800.java index b0912933f2..cdf7e713b0 100644 --- a/Ghidra/Features/PDB/src/test/java/ghidra/app/util/bin/format/pdb2/pdbreader/DummyTypeProgramInterface800.java +++ b/Ghidra/Features/PDB/src/test/java/ghidra/app/util/bin/format/pdb2/pdbreader/DummyTypeProgramInterface800.java @@ -28,7 +28,7 @@ public class DummyTypeProgramInterface800 extends TypeProgramInterface800 { /** * IMPORTANT: This method is for testing only. It allows us to set a basic object. * Note: not all values are initialized. - * @param pdb The AbstractPdb foundation for the DatabaseInterface. + * @param pdb The AbstractPdb foundation for the {@link TypeProgramInterface800}. * @param typeIndexMin int. The IndexMin to set/use. * @param typeIndexMaxExclusive int. One greater than the MaxIndex to set/use */ diff --git a/Ghidra/Framework/Generic/src/main/java/ghidra/app/util/importer/MessageLog.java b/Ghidra/Framework/Generic/src/main/java/ghidra/app/util/importer/MessageLog.java index fa9717a3b7..a37bcf00b6 100644 --- a/Ghidra/Framework/Generic/src/main/java/ghidra/app/util/importer/MessageLog.java +++ b/Ghidra/Framework/Generic/src/main/java/ghidra/app/util/importer/MessageLog.java @@ -165,10 +165,9 @@ public class MessageLog { private String toStringWithWarning() { StringBuilder output = new StringBuilder(); if (count > maxSize) { - output.append('\n').append('\n'); output.append("There were too many messages to display.\n"); - output.append((count - maxSize)).append(" messages have been truncated."); - output.append('\n').append('\n'); + output.append((count - maxSize)).append(" messages have been truncated.\n"); + output.append('\n'); } for (String s : messages) {