diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbApplicator.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbApplicator.java index 12c07e17ac3..1640cafafdc 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbApplicator.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbApplicator.java @@ -439,6 +439,23 @@ void pdbLogAndInfoMessage(Object originator, String message) { Msg.info(originator, message); } + /** + * Puts error message to {@link PdbLog} and to Msg.error() which will + * also log a stack trace if exception is specified. + * @param originator a Logger instance, "this", or YourClass.class + * @param message the error message to display/log + * @param exc exception whose stack trace should be reported or null + */ + void pdbLogAndErrorMessage(Object originator, String message, Exception exc) { + PdbLog.message(message); + if (exc != null) { + Msg.error(originator, message); + } + else { + Msg.error(originator, message, exc); + } + } + /** * Returns the {@link TaskMonitor} to available for this analyzer. * @return the monitor. @@ -928,6 +945,7 @@ void addMemorySectionRefinement(PeCoffSectionMsSymbol symbol) { //============================================================================================== // CLI-Managed infor methods. //============================================================================================== + // Currently in CLI, but could move. boolean isDll() { return pdbCliManagedInfoManager.isDll(); @@ -938,6 +956,15 @@ boolean isAslr() { return pdbCliManagedInfoManager.isAslr(); } + /** + * Get CLI metadata for specified tableNum and rowNum within the CLI + * metadata stream. + * @param tableNum CLI metadata stream table index + * @param rowNum table row number + * @return CLI metadata or null if specified tableNum not found + * @throws PdbException if CLI metadata stream is not found in program file bytes + * @throws IndexOutOfBoundsException if specified rowNum is invalid + */ CliAbstractTableRow getCliTableRow(int tableNum, int rowNum) throws PdbException { return pdbCliManagedInfoManager.getCliTableRow(tableNum, rowNum); } diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbCliInfoManager.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbCliInfoManager.java index e03f5900347..0262be71ad4 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbCliInfoManager.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbCliInfoManager.java @@ -37,6 +37,9 @@ */ public class PdbCliInfoManager { + private PdbApplicator applicator; + + private boolean initComplete = false; private CliStreamMetadata metadataStream; // TODO: May move these out from this class to a higher level. Would mean passing in @@ -46,22 +49,43 @@ public class PdbCliInfoManager { /** * Manager of CLI-related tables that we might need access to for PDB processing. - * @param applicator {@link PdbApplicator} for which this class is working. + * @param applicator {@link PdbApplicator} for which this class is working (used for logging purposes only). */ PdbCliInfoManager(PdbApplicator applicator) { Objects.requireNonNull(applicator, "applicator may not be null"); - metadataStream = getCliStreamMetadata(applicator); + this.applicator = applicator; + } + + private synchronized void initialize() { + if (initComplete) { + return; + } + initComplete = true; + metadataStream = getCliStreamMetadata(); } boolean isDll() { + initialize(); return isDll; } boolean isAslr() { + initialize(); return isAslr; } - CliAbstractTableRow getCliTableRow(int tableNum, int rowNum) throws PdbException { + /** + * Get CLI metadata for specified tableNum and rowNum within the CLI + * metadata stream. + * @param tableNum CLI metadata stream table index + * @param rowNum table row number + * @return CLI metadata or null if specified tableNum not found + * @throws PdbException if CLI metadata stream is not found in program file bytes + * @throws IndexOutOfBoundsException if specified rowNum is invalid + */ + CliAbstractTableRow getCliTableRow(int tableNum, int rowNum) + throws PdbException, IndexOutOfBoundsException { + initialize(); if (metadataStream == null) { throw new PdbException("CliStreamMetadata is null"); } @@ -72,21 +96,34 @@ CliAbstractTableRow getCliTableRow(int tableNum, int rowNum) throws PdbException return table.getRow(rowNum); } - private CliStreamMetadata getCliStreamMetadata(PdbApplicator applicator) { + /** + * Get CLI stream metadata + * @return CLI stream metadata or null if not found or error occured + */ + private CliStreamMetadata getCliStreamMetadata() { Program program = applicator.getProgram(); if (program == null) { return null; } List allFileBytes = program.getMemory().getAllFileBytes(); + if (allFileBytes.isEmpty()) { + applicator.pdbLogAndErrorMessage(this, + "Unable to retrieve CliStreamMetadata: no FileBytes", null); + return null; + } FileBytes fileBytes = allFileBytes.get(0); // Should be that of main imported file - ByteProvider provider = new FileBytesProvider(fileBytes); - PortableExecutable pe = null; + ByteProvider provider = new FileBytesProvider(fileBytes); // close not required try { GenericFactory factory = MessageLogContinuesFactory.create(applicator.getMessageLog()); - pe = PortableExecutable.createPortableExecutable(factory, provider, SectionLayout.FILE, - true, true); - NTHeader ntHeader = pe.getNTHeader(); + PortableExecutable pe = PortableExecutable.createPortableExecutable(factory, provider, + SectionLayout.FILE, true, true); + NTHeader ntHeader = pe.getNTHeader(); // will be null if header parse fails + if (ntHeader == null) { + applicator.pdbLogAndErrorMessage(this, + "Unable to retrieve CliStreamMetadata: NTHeader file bytes not found", null); + return null; + } OptionalHeader optionalHeader = ntHeader.getOptionalHeader(); int characteristics = ntHeader.getFileHeader().getCharacteristics(); isDll = (characteristics & FileHeader.IMAGE_FILE_DLL) == FileHeader.IMAGE_FILE_DLL; @@ -94,25 +131,32 @@ private CliStreamMetadata getCliStreamMetadata(PdbApplicator applicator) { int optionalHeaderCharaceristics = optionalHeader.getDllCharacteristics(); isAslr = (optionalHeaderCharaceristics & OptionalHeader.IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA) == OptionalHeader.IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA; + if (OptionalHeader.IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR >= dataDirectory.length) { + applicator.pdbLogAndErrorMessage(this, + "Unable to retrieve CliStreamMetadata: Bad index (" + + OptionalHeader.IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR + + ") for COMDescriptorDataDirectory in DataDirectory array of size " + + dataDirectory.length, + null); + return null; + } COMDescriptorDataDirectory comDir = (COMDescriptorDataDirectory) dataDirectory[OptionalHeader.IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR]; ImageCor20Header header = comDir.getHeader(); if (header == null) { + applicator.pdbLogAndErrorMessage(this, + "Unable to retrieve CliStreamMetadata: no COMDir header", null); return null; } return header.getMetadata().getMetadataRoot().getMetadataStream(); } - catch (Exception e) { - applicator.pdbLogAndInfoMessage(this, "Unable to retrieve CliStreamMetadata"); + catch (RuntimeException | IOException e) { + // We do not know what can go wrong. Some of the header parsing might have issues, + // and we'd rather log the error and limp on by with whatever other processing we can + // do than to fail here. + applicator.pdbLogAndErrorMessage(this, + "Unable to retrieve CliStreamMetadata: " + e.getMessage(), e); return null; } - finally { - try { - provider.close(); - } - catch (IOException ioe) { - applicator.pdbLogAndInfoMessage(this, "Problem closing ByteProvider"); - } - } } }