Merge remote-tracking branch 'origin/Ghidra_9.2'

This commit is contained in:
ghidra1
2020-09-30 12:08:45 -04:00
37 changed files with 1032 additions and 535 deletions
@@ -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();
}
@@ -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<byte[]> getImages() {
runImageAnalyzer();
List<byte[]> imageList = new ArrayList<byte[]>();
DataIterator it = program.getListing().getDefinedData(true);
while (it.hasNext()) {
accumulateImageData(imageList, it.next());
}
return imageList;
}
private void accumulateImageData(List<byte[]> 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);
}
}
}
@@ -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,
@@ -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);
}
}
@@ -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<String> 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<String> potentialPdbNames = pdbAttributes.getPotentialPdbFilenames();
@@ -1150,7 +1145,8 @@ public class PdbParser {
getSymbolsRepositoryPaths(symbolsRepositoryPath, guidSubdirPaths);
Set<File> 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) {
@@ -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");
}
}
@@ -55,8 +55,6 @@ public abstract class AbstractSymbolInformation {
protected List<Integer> hashBucketOffsets = new ArrayList<>();
protected Set<SymbolHashRecord> hashRecords = new TreeSet<>();
protected List<Long> modifiedHashRecordSymbolOffsets = new ArrayList<>();
protected Map<Integer, Integer> thunkTargetOffsetsByTableOffset = new HashMap<>();
protected Map<Integer, Integer> absoluteOffsetsBySectionNumber = new HashMap<>();
protected List<AbstractMsSymbol> symbols = new ArrayList<>();
@@ -170,8 +168,7 @@ public abstract class AbstractSymbolInformation {
protected void generateSymbolsList(TaskMonitor monitor)
throws PdbException, CancelledException {
symbols = new ArrayList<>();
Map<Long, AbstractMsSymbol> symbolsByOffset =
pdb.getDatabaseInterface().getSymbolsByOffset();
Map<Long, AbstractMsSymbol> 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");
}
@@ -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:
@@ -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 <a href="https://docs.microsoft.com/en-us/windows/desktop/sysinfo/image-file-machine-constants">
* Image File Machine Constants</a>
* @see <a href="http://metadataconsulting.blogspot.com/2014/06/imagefilemachine-extensive-machine-type.html">
@@ -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));
@@ -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.
* <P>
* 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}.
@@ -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";
}
@@ -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;
}
/**
@@ -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.
* <P>
* 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;
}
}
@@ -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.
* <P>
* 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 {
@@ -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 + ")");
@@ -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<Long> addressMapSymbolOffsets = new ArrayList<>();
private Map<Integer, Integer> thunkTargetOffsetsByTableOffset = new HashMap<>();
private Map<Integer, Integer> 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<Integer, Integer> 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<Integer, Integer> 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
@@ -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);
@@ -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
@@ -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<String> checkPathsForPdb(String symbolsRepositoryPath,
@@ -467,12 +473,12 @@ public class PdbLocator {
List<String> 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 {
* <P>
*/
private static void getWindowsPaths(Set<String> guidSubdirPaths, Set<File> 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);
@@ -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<String> 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;
}
}
/**
@@ -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) {
@@ -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<ImageSectionHeader> 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<SegmentMapDescription> segmentMapList = pdb.getDatabaseInterface().getSegmentMapList();
List<SegmentMapDescription> 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.
* <P>
* 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<SegmentMapDescription> segmentMapList = pdb.getDatabaseInterface().getSegmentMapList();
List<SegmentMapDescription> 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<AbstractMsSymbol> linkerSymbolList =
applicator.getSymbolGroupForModule(num).getSymbols();
@@ -356,12 +356,12 @@ public class PdbApplicator {
private List<SymbolGroup> createSymbolGroups() throws CancelledException, PdbException {
List<SymbolGroup> 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<Long, AbstractMsSymbol> 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<String> 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<Long> 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<Long> 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<Long> 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<Long> 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) {
@@ -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<Long> 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<Long> 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<Long> offsets = globalSymbolInformation.getModifiedHashRecordSymbolOffsets();
applicator.setMonitorMessage("PDB: Applying global symbols...");
monitor.initialize(offsets.size());
@@ -670,7 +670,7 @@ public class PdbResearch {
Map<Address, List<Stuff>> 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);
@@ -57,7 +57,7 @@ public class PdbVbtManager extends VbtManager {
Map<String, Address> myAddressByMangledName = new HashMap<>();
PublicSymbolInformation publicSymbolInformation =
applicator.getPdb().getDatabaseInterface().getPublicSymbolInformation();
applicator.getPdb().getDebugInfo().getPublicSymbolInformation();
List<Long> offsets = publicSymbolInformation.getModifiedHashRecordSymbolOffsets();
applicator.setMonitorMessage("PDB: Searching for virtual base table symbols...");
monitor.initialize(offsets.size());
@@ -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<PdbApplicatorRestrictions> 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<String> 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;
}
}
@@ -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
@@ -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
}
}
}
}
@@ -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;
@@ -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<String> 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());
@@ -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());
}
/**
@@ -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);
}
@@ -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);
}
@@ -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);
@@ -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
*/
@@ -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) {