diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/AbstractPdb.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/AbstractPdb.java index 71606f0330..0376e5be90 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/AbstractPdb.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/AbstractPdb.java @@ -4,9 +4,9 @@ * 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. @@ -669,7 +669,7 @@ public abstract class AbstractPdb implements AutoCloseable { writer.write("\nversionNumber: " + versionNumber); writer.write("\nsignature: " + Integer.toHexString(signature)); writer.write("\nage: " + pdbAge); - writer.write("End DirectoryHeader-----------------------------------------"); + writer.write("\nEnd DirectoryHeader-----------------------------------------"); } /** diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/FileChecksumsC13Section.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/FileChecksumsC13Section.java index cce96a380a..3055cf7576 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/FileChecksumsC13Section.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/FileChecksumsC13Section.java @@ -4,9 +4,9 @@ * 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. @@ -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.Msg; import ghidra.util.exception.CancelledException; @@ -30,6 +29,7 @@ import ghidra.util.task.TaskMonitor; public class FileChecksumsC13Section extends C13Section { private List fileChecksums = new ArrayList<>(); + private Map fileChecksumsByOffset = new HashMap<>(); /** * Parse and return a {@link FileChecksumsC13Section}. @@ -50,8 +50,10 @@ public class FileChecksumsC13Section extends C13Section { super(ignore); while (reader.numRemaining() >= C13FileChecksum.getBaseRecordSize()) { monitor.checkCancelled(); + int offset = reader.getIndex(); C13FileChecksum fileChecksum = new C13FileChecksum(reader); fileChecksums.add(fileChecksum); + fileChecksumsByOffset.put(offset, fileChecksum); } if (reader.hasMore()) { Msg.debug(FileChecksumsC13Section.class, @@ -67,6 +69,15 @@ public class FileChecksumsC13Section extends C13Section { return fileChecksums; } + /** + * Returns the C13 file checksum for the offset of the record in the checksum table + * @param offset the offset of the record + * @return the checksum or null if record not found + */ + public C13FileChecksum getFileChecksumByOffset(int offset) { + return fileChecksumsByOffset.get(offset); + } + @Override public String toString() { return String.format("%s: num checksums = %d", getClass().getSimpleName(), diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/Module.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/Module.java index 4969689b6b..dd7dac3801 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/Module.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/Module.java @@ -4,9 +4,9 @@ * 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. @@ -336,7 +336,7 @@ public class Module { writer.append(String.format("Offset: 0X%08X\n", symbolIter.getCurrentOffset())); writer.append(symbol.toString()); } - writer.write("End Symbols-------------------------------------------------\n"); + writer.write("\nEnd Symbols-------------------------------------------------\n"); } private void dumpC11Lines(Writer writer) throws IOException, CancelledException, PdbException { diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/ModuleInformation.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/ModuleInformation.java index d8e6e46db0..3afceac6a8 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/ModuleInformation.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/ModuleInformation.java @@ -4,9 +4,9 @@ * 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. @@ -132,6 +132,14 @@ public abstract class ModuleInformation { return moduleName; } + /** + * Returns the name of the object file + * @return name of the object file + */ + public String getObjectFileName() { + return objectFileName; + } + /** * Returns {@link SectionContribution} of the module * @return {@link SectionContribution} of the module @@ -140,6 +148,15 @@ public abstract class ModuleInformation { return sectionContribution; } + /** + * Returns the filename for the index + * @param index the index for which the filename was stored + * @return the filename + */ + public String getFilenameByIndex(int index) { + return filenamesArray.get(index); + } + /** * Returns the filename for the offset * @param offset the offset for which the filename was stored @@ -205,11 +222,12 @@ public abstract class ModuleInformation { // Package-Protected Internals //============================================================================================== /** - * Stores the filename for the offset given + * Stores the filename for the offset given. Also adds name to array, so order of call matters * @param offset the offset for which to store the filename * @param filename the filename to store */ protected void addFilenameByOffset(int offset, String filename) { + filenamesArray.add(filename); filenameByOffset.put(offset, filename); } diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/ModuleInformation600.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/ModuleInformation600.java index bb5e14f567..9308773aa3 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/ModuleInformation600.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/ModuleInformation600.java @@ -4,9 +4,9 @@ * 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. @@ -46,6 +46,22 @@ public class ModuleInformation600 extends ModuleInformation { reader.parseNullTerminatedString(pdb.getPdbReaderOptions().getOneByteCharset()); } + /** + * Not yet sure what this field represents + * @return the value + */ + public long getNameIndexSourceFile() { + return nameIndexSourceFile; + } + + /** + * Not yet sure what this field represents + * @return the value + */ + public long getNameCompilerPdbPath() { + return nameIndexCompilerPdbPath; + } + @Override protected void dumpAdditionals(Writer writer) throws IOException { writer.write("\nnameIndexSourceFile: " + nameIndexSourceFile); diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/type/FunctionIdMsType.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/type/FunctionIdMsType.java index 059d8f94a2..8fb768f7c4 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/type/FunctionIdMsType.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/type/FunctionIdMsType.java @@ -4,9 +4,9 @@ * 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. @@ -45,6 +45,19 @@ public class FunctionIdMsType extends AbstractMsType { reader.skipPadding(); } + @Override + public String getName() { + return name; + } + + public RecordNumber getScopeIdRecordNumber() { + return scopeIdRecordNumber; + } + + public RecordNumber getFunctionTypeRecordNumber() { + return functionTypeRecordNumber; + } + @Override public int getPdbId() { return PDB_ID; diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/type/MemberFunctionIdMsType.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/type/MemberFunctionIdMsType.java index 450e7a043c..81d819e7e9 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/type/MemberFunctionIdMsType.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/type/MemberFunctionIdMsType.java @@ -4,9 +4,9 @@ * 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. @@ -45,6 +45,19 @@ public class MemberFunctionIdMsType extends AbstractMsType { reader.skipPadding(); } + @Override + public String getName() { + return name; + } + + public RecordNumber getParentTypeRecordNumber() { + return parentTypeRecordNumber; + } + + public RecordNumber getFunctionTypeRecordNumber() { + return functionTypeRecordNumber; + } + @Override public int getPdbId() { return PDB_ID; diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/DefaultPdbApplicator.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/DefaultPdbApplicator.java index 1f63b9dcc8..93772aeb30 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/DefaultPdbApplicator.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/DefaultPdbApplicator.java @@ -176,6 +176,8 @@ public class DefaultPdbApplicator implements PdbApplicator { private PdbApplicatorMetrics pdbApplicatorMetrics; + private boolean preWorkDone = false; + //============================================================================================== private Program program; @@ -197,6 +199,9 @@ public class DefaultPdbApplicator implements PdbApplicator { private AbstractMsSymbol compileSymbolForLinkerModule = null; private boolean processedLinkerModule = false; + //============================================================================================== + private PdbSourceLinesApplicator linesApplicator; + //============================================================================================== // If we have symbols and memory with VBTs in them, then a better VbtManager is created. VbtManager vbtManager; @@ -342,6 +347,14 @@ public class DefaultPdbApplicator implements PdbApplicator { } } + //============================================================================================== + // For use by Function Symbol appliers, but might also get used during testing + void setFunctionLength(Address address, int length) { + if (linesApplicator != null) { + linesApplicator.setFunctionLength(address, length); + } + } + //============================================================================================== private void doPdbTypesAndMainSymbolsWork() throws PdbException, CancelledException { switch (applicatorOptions.getProcessingControl()) { @@ -372,6 +385,12 @@ public class DefaultPdbApplicator implements PdbApplicator { private void doPdbFunctionInternalsWork() throws PdbException, CancelledException { if (program != null) { doDeferredFunctionProcessing(); + // Processing is done here because we want function bodies to be processed, + // as that allows us to fetch the function start, given any address within + // the function + if (applicatorOptions.applySourceLineNumbers()) { + linesApplicator.process(); + } // Options options = program.getOptions(Program.PROGRAM_INFO); // options.setBoolean(PdbParserConstants.PDB_LOADED, true); } @@ -582,6 +601,11 @@ public class DefaultPdbApplicator implements PdbApplicator { // Investigations into source/line info recordNumbersByFileName = new HashMap<>(); recordNumbersByModuleNumber = new HashMap<>(); + + if (program != null && applicatorOptions.applySourceLineNumbers()) { + linesApplicator = new PdbSourceLinesApplicator(this); + } + } /** @@ -591,7 +615,9 @@ public class DefaultPdbApplicator implements PdbApplicator { * @throws PdbException upon error in processing components */ private void doPdbPreWork() throws CancelledException, PdbException { - + if (preWorkDone) { + return; + } pdbApplicatorMetrics = pdbAnalysisLookupState.getPdbApplicatorMetrics(); pdbAddressManager = pdbAnalysisLookupState.getPdbAddressManager(); complexTypeMapper = pdbAnalysisLookupState.getComplexTypeMapper(); @@ -613,6 +639,7 @@ public class DefaultPdbApplicator implements PdbApplicator { else { vbtManager = new VbtManager(getDataTypeManager()); } + preWorkDone = true; } private void validateAndSetParameters(Program programParam, @@ -1369,7 +1396,7 @@ public class DefaultPdbApplicator implements PdbApplicator { * @return the Address */ Address getAddress(int segment, long offset) { - return pdbAddressManager.getRawAddress(segment, offset); + return pdbAddressManager.getAddress(segment, offset); } /** diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/FunctionSymbolApplier.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/FunctionSymbolApplier.java index 909e1f2ead..7538dee4b0 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/FunctionSymbolApplier.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/FunctionSymbolApplier.java @@ -243,6 +243,10 @@ public class FunctionSymbolApplier extends AbstractBlockContextApplier String name = symbol.getName(); Address address = applicator.getAddress(symbol); + // Save off the function length for lines processing + Long functionLength = symbol.getProcedureLength(); + applicator.setFunctionLength(address, functionLength.intValue()); + function = applicator.getExistingFunction(address); if (function == null) { // Skip all interim symbols records diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbAddressManager.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbAddressManager.java index 98e39f6159..6595225881 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbAddressManager.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbAddressManager.java @@ -358,7 +358,7 @@ public class PdbAddressManager { private void determineMemoryBlocks() throws CancelledException { AbstractPdb pdb = applicator.getPdb(); PdbDebugInfo debugInfo = pdb.getDebugInfo(); - if(debugInfo == null) { + if (debugInfo == null) { return; } segmentMapList = debugInfo.getSegmentMapList(); diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbApplicatorMetrics.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbApplicatorMetrics.java index b024db1169..5ce4b37a8b 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbApplicatorMetrics.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbApplicatorMetrics.java @@ -4,9 +4,9 @@ * 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. @@ -112,6 +112,8 @@ public class PdbApplicatorMetrics { private Set> unexpectedGlobalSymbols = new HashSet<>(); private Set> unexpectedPublicSymbols = new HashSet<>(); private boolean witnessEnumerateNarrowing = false; + private boolean witnessC11Lines = false; + private boolean witnessC13InlineeLines = false; /** * Method to capture data/item type that cannot be applied. @@ -215,6 +217,22 @@ public class PdbApplicatorMetrics { unexpectedMemberFunctionContainerTypes.add(type.getClass()); } + /** + * Method to capture witnessing of C11Lines. + */ + void witnessC11Lines() { + witnessC11Lines = true; + } + + /** + * Method to capture witnessing of C13InlineeLines. + */ + void witnessC13InlineeLines() { + // C13InlineeLines are prevalent, but we want to be able to inform the user that + // we haven't processed them + witnessC13InlineeLines = true; + } + //============================================================================================== /** @@ -233,6 +251,7 @@ public class PdbApplicatorMetrics { builder.append(reportUnexpectedPublicSymbols()); builder.append(reportUnexpectedGlobalSymbols()); builder.append(reportEnumerateNarrowing()); + builder.append(reportSourceLineProcessing()); // can be removed once we can process if (builder.length() == 0) { return; // nothing reported @@ -324,4 +343,17 @@ public class PdbApplicatorMetrics { return ""; } + // Routine can be modified to remove each as we can process each. Routine can be removed once + // we can process both. + private String reportSourceLineProcessing() { + String result = ""; + if (witnessC11Lines) { + result = "Could not process C11Lines\n"; + } + if (witnessC13InlineeLines) { + result += "Could not process C13InlineeLines\n"; + } + return result; + } + } diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbApplicatorOptions.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbApplicatorOptions.java index 75d5d102f1..83c3415257 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbApplicatorOptions.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbApplicatorOptions.java @@ -4,9 +4,9 @@ * 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. @@ -41,6 +41,15 @@ public class PdbApplicatorOptions { private static final PdbApplicatorControl DEFAULT_CONTROL = PdbApplicatorControl.ALL; private PdbApplicatorControl control; + // Apply Source Line Numbers. + private static final String OPTION_NAME_APPLY_SOURCE_LINE_NUMBERS = + "Apply Source Line Numbers"; // DWARF says "Output Source Line Info", but we use "Apply" + private static final String OPTION_DESCRIPTION_APPLY_SOURCE_LINE_NUMBERS = + "Create source map entries containing the source code filename, line number, address, and" + + " length at each location provided in the PDB data."; + private static final boolean DEFAULT_APPLY_SOURCE_LINE_NUMBERS = false; + private boolean applySourceLineNumbers; + // Apply Code Block Comments. private static final String OPTION_NAME_APPLY_CODE_SCOPE_BLOCK_COMMENTS = "Apply Code Scope Block Comments"; @@ -181,6 +190,11 @@ public class PdbApplicatorOptions { private void registerOptions(Options options, boolean enableControl) { HelpLocation help = null; + //TODO: Uncomment the following for GP-3883 +// options.registerOption(OPTION_NAME_APPLY_SOURCE_LINE_NUMBERS, +// applySourceLineNumbers, help, +// OPTION_DESCRIPTION_APPLY_SOURCE_LINE_NUMBERS); + if (DEVELOPER_MODE || enableControl) { options.registerOption(OPTION_NAME_PROCESSING_CONTROL, PdbApplicatorControl.ALL, help, OPTION_DESCRIPTION_PROCESSING_CONTROL); @@ -189,6 +203,11 @@ public class PdbApplicatorOptions { // PdbApplicatorOptions if (DEVELOPER_MODE) { + //TODO: Remove the following line for GP-3883 + options.registerOption(OPTION_NAME_APPLY_SOURCE_LINE_NUMBERS, + applySourceLineNumbers, help, + OPTION_DESCRIPTION_APPLY_SOURCE_LINE_NUMBERS); + options.registerOption(OPTION_NAME_APPLY_CODE_SCOPE_BLOCK_COMMENTS, applyCodeScopeBlockComments, help, OPTION_DESCRIPTION_APPLY_CODE_SCOPE_BLOCK_COMMENTS); @@ -232,6 +251,10 @@ public class PdbApplicatorOptions { private void loadOptions(Options options, boolean enableControl) { + //TODO: Uncomment the following for GP-3883 +// applySourceLineNumbers = options.getBoolean( +// OPTION_NAME_APPLY_SOURCE_LINE_NUMBERS, applySourceLineNumbers); + if (DEVELOPER_MODE || enableControl) { control = options.getEnum(OPTION_NAME_PROCESSING_CONTROL, PdbApplicatorControl.ALL); } @@ -239,6 +262,10 @@ public class PdbApplicatorOptions { // PdbApplicatorOptions if (DEVELOPER_MODE) { + //TODO: Remove the following line for GP-3883 + applySourceLineNumbers = options.getBoolean( + OPTION_NAME_APPLY_SOURCE_LINE_NUMBERS, applySourceLineNumbers); + applyCodeScopeBlockComments = options.getBoolean( OPTION_NAME_APPLY_CODE_SCOPE_BLOCK_COMMENTS, applyCodeScopeBlockComments); @@ -288,6 +315,7 @@ public class PdbApplicatorOptions { * Set the options to their default values */ public void setDefaults() { + applySourceLineNumbers = DEFAULT_APPLY_SOURCE_LINE_NUMBERS; applyCodeScopeBlockComments = DEFAULT_APPLY_CODE_SCOPE_BLOCK_COMMENTS; applyInstructionLabels = DEFAULT_APPLY_INSTRUCTION_LABELS; excludeInstructionLabels = DEFAULT_EXCLUDE_INSTRUCTION_LABELS; @@ -300,6 +328,22 @@ public class PdbApplicatorOptions { compositeLayout = DEFAULT_CLASS_LAYOUT; } + /** + * Enable/disable developmental debug of applying source line numbers. + * @param applySourceLineNumbers {@code true} to turn on applySourceLineNumbers + */ + public void setApplySourceLineNumbers(boolean applySourceLineNumbers) { + this.applySourceLineNumbers = applySourceLineNumbers; + } + + /** + * Returns {@code true} if applySourceLineNumbers is "on." + * @return {@code true} if applySourceLineNumbers is "on." + */ + public boolean applySourceLineNumbers() { + return applySourceLineNumbers; + } + /** * Enable/disable developmental debug. * @param applyCodeScopeBlockComments {@code true} to turn applyCodeScopeBlockComments on diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbSourceLinesApplicator.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbSourceLinesApplicator.java new file mode 100644 index 0000000000..11f4871e5b --- /dev/null +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbSourceLinesApplicator.java @@ -0,0 +1,378 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.util.pdb.pdbapplicator; + +import java.util.*; + +import ghidra.app.util.bin.format.pdb2.pdbreader.*; +import ghidra.app.util.bin.format.pdb2.pdbreader.Module; +import ghidra.app.util.bin.format.pdb2.pdbreader.type.*; +import ghidra.app.util.importer.MessageLog; +import ghidra.program.model.address.Address; +import ghidra.program.model.listing.*; +import ghidra.util.Msg; +import ghidra.util.exception.CancelledException; + +/** + * Helper class to PdbApplicator for applying source line information + */ +public class PdbSourceLinesApplicator { + + private DefaultPdbApplicator applicator; + private AbstractPdb pdb; + private Program program; + private MessageLog log; + + private Map functionLengthByAddress; + +// private SourceFileManager manager; + + //============================================================================================== + /** + * Constructor for PdbSourceLinesApplicator + * @param applicator the PdbApplicator that we are helping + */ + public PdbSourceLinesApplicator(DefaultPdbApplicator applicator) { + Objects.requireNonNull(applicator, "applicator cannot be null"); + this.applicator = applicator; + this.program = applicator.getProgram(); + Objects.requireNonNull(program, "program cannot be null"); + this.pdb = applicator.getPdb(); + Objects.requireNonNull(pdb, "pdb cannot be null"); + this.log = applicator.getMessageLog(); + + functionLengthByAddress = new HashMap<>(); + + //manager = program.getSourceFileManager(); + } + + //============================================================================================== + /** + * When determined elsewhere, and before {@code process()} is called, this method should + * be used to populate the lengths of functions in to this lines applier. If not done, then + * some source line code lengths might not be correct + * @param address the address + * @param length the function length + */ + public void setFunctionLength(Address address, int length) { + functionLengthByAddress.put(address, length); + } + + //============================================================================================== + /** + * Process all Module line information + * @throws CancelledException upon user cancellation + */ + public void process() throws CancelledException { + PdbDebugInfo debugInfo = pdb.getDebugInfo(); + if (debugInfo == null) { + Msg.info(this, "PDB: Missing DebugInfo - cannot process line numbers."); + return; + } + if (!program.hasExclusiveAccess()) { + Msg.showWarn(this, null, "Cannot Apply SourceMap Information", + "Exclusive access to the program is required to apply source map information"); + return; + } + + // Not processing user defined "Types" source information. TODO: ??? + + int numModules = debugInfo.getNumModules(); + for (int num = 1; num <= numModules; num++) { + pdb.checkCancelled(); + Module module = debugInfo.getModule(num); + processC11Lines(module); + processC13Sections(module); + } + } + + //============================================================================================== + // Suppress: for moduleInfo due to commented out call to processC11Line. Remove suppress when + // no longer commented out. + @SuppressWarnings("unused") + private void processC11Lines(Module module) + throws CancelledException { + ModuleInformation moduleInfo = module.getModuleInformation(); + try { + C11Lines c11Lines = module.getLineInformation(); + if (c11Lines != null) { + // TODO: Figure out how to process and what to do with inline information. When + // ready, uncomment the following code +// processC11Line(moduleInfo, c11Lines); + // TODO: remove this and underlying Metrics logic once we have a viable processing + // technique for inlinee lines + applicator.getPdbApplicatorMetrics().witnessC11Lines(); + } + } + catch (PdbException e) { + log.appendMsg("PDB: Failed to process C11Lines due to " + e.getMessage()); + return; + } + } + + // Suppress: for not being used and for unfinished implementation with unused variables + @SuppressWarnings("unused") + // TODO: Figure out how to process and what to do with inline information. When + // ready, uncomment the following code + private void processC11Line(ModuleInformation moduleInfo, + C11Lines c11Lines) { + Msg.info(this, "PDB: Unimplemented... unable to process C11 Lines"); + // TODO: See C11Lines dump method for indications of how we might process + int cFile = c11Lines.getNumFiles(); + int cSet = c11Lines.getNumSegments(); + List baseSrcFile = c11Lines.getBaseSrcFiles(); + List startEnd = c11Lines.getStartEnd(); + List seg = c11Lines.getSegments(); + List ccSegs = c11Lines.getPerFileNumSegments(); + List> baseSrcLines = c11Lines.getPerFileBaseSrcLines(); + List> startEnds = c11Lines.getPerFileStartEndRecords(); + List names = c11Lines.getFileNames(); + List> segmentNumbers = c11Lines.getPerFileSegmentNumbers(); + List>> offsets = c11Lines.getPerFilePerSegmentOffsets(); + List>> lineNumbers = c11Lines.getPerFilePerSegmentLineNumbers(); + // do something + } + + //============================================================================================== + private void processC13Sections(Module module) + throws CancelledException { + + ModuleInformation moduleInfo = module.getModuleInformation(); + + C13SectionIterator c13FileChecksumIterator; + C13SectionIterator linesIterator; + C13SectionIterator ilLinesIterator; + C13SectionIterator inlineeLinesIterator; + try { + c13FileChecksumIterator = + module.getC13SectionFilteredIterator(FileChecksumsC13Section.class); + linesIterator = module.getC13SectionFilteredIterator(LinesC13Section.class); + ilLinesIterator = module.getC13SectionFilteredIterator(IlLinesC13Section.class); + inlineeLinesIterator = + module.getC13SectionFilteredIterator(InlineeLinesC13Section.class); + } + catch (PdbException e) { + log.appendMsg("PDB: Failed to process C13Sections due to " + e.getMessage()); + return; + } + + // Must do file checksums first, as they have the file information for the source lines + // Make sure there is one and only one + FileChecksumsC13Section fileChecksumsSection = null; + while (c13FileChecksumIterator.hasNext()) { + pdb.checkCancelled(); + FileChecksumsC13Section section = c13FileChecksumIterator.next(); + if (fileChecksumsSection != null) { + Msg.warn(this, "More than on FileChecksumC13Section found in module " + + moduleInfo.getModuleName()); + break; + } + fileChecksumsSection = section; + } + if (fileChecksumsSection == null) { + // No information for this module + return; + } + + // Process lines, ilLines, and inlineeLines + while (linesIterator.hasNext()) { + pdb.checkCancelled(); + LinesC13Section linesSection = linesIterator.next(); + processC13FileRecords(moduleInfo, fileChecksumsSection, linesSection, false); + } + while (ilLinesIterator.hasNext()) { + pdb.checkCancelled(); + IlLinesC13Section ilLinesSection = ilLinesIterator.next(); + processC13FileRecords(moduleInfo, fileChecksumsSection, ilLinesSection, true); + } + // TODO: Figure out how to process and what to do with inline information. When + // ready, uncomment the following code +// while (inlineeLinesIterator.hasNext()) { +// monitor.checkCancelled(); +// InlineeLinesC13Section inlineeSection = inlineeLinesIterator.next(); +// processC13InlineeLines(moduleInfo, fileChecksumsSection, inlineeSection); +// } + // TODO: remove this and underlying Metrics logic once we have a viable processing + // technique for inlinee lines + if (inlineeLinesIterator.hasNext()) { + applicator.getPdbApplicatorMetrics().witnessC13InlineeLines(); + } + } + + //============================================================================================== + private void processC13FileRecords(ModuleInformation moduleInfo, + FileChecksumsC13Section fileChecksumsC13Section, AbstractLinesC13Section c13Lines, + boolean isIlLines) + throws CancelledException { + + // Something else to look into: ModuleInformation600 version has more fields that we do + // not know their usefulness at this time. These are: + // String moduleName = moduleInfo.getModuleName(); + // String objectFileName = moduleInfo.getObjectFileName(); + List fileRecords = c13Lines.getFileRecords(); + + long offCon = c13Lines.getOffCon(); + int segCon = c13Lines.getSegCon(); + // Currently not using getFlags() and getLenCon() + for (C13FileRecord fileRecord : fileRecords) { + pdb.checkCancelled(); + int fileId = fileRecord.getFileId(); + SourceFile sourceFile = getSourceFile(fileChecksumsC13Section, fileId); + + // Everything we've see to this point shows that the address come in an increasing, + // but non-strictly increasing order. Also, there is not a field to designate the + // number of bytes of memory that pertain to each line record, but everything we've + // seen seems to indicate that we can do the difference between the record addresses + // to get the length needed. However, the last record doesn't have a "next" record + // to do the difference with, but that is where function length comes into play. + // And if we work in reverse order (we do not care if the records are created and + // put into the DB in reverse order), then we can easily calculate the lengths. + long lastValue = -1; + Long numLines = fileRecord.getNLines(); + List lineRecords = fileRecord.getLineRecords(); + for (int index = numLines.intValue() - 1; index >= 0; index--) { + pdb.checkCancelled(); + C13LineRecord lineRecord = lineRecords.get(index); + Long lineNumStart = lineRecord.getLineNumStart(); + // If we wanted the line end value, we could calculate it as: + // Long lineNumEnd = lineNumStart + lineRecord.getDeltaLineEnd() + long offset = lineRecord.getOffset(); + long actualOffset = offset + offCon; + Address address = applicator.getAddress(segCon, actualOffset); + if (lastValue == -1) { + FunctionManager functionManager = program.getFunctionManager(); + Function function = functionManager.getFunctionContaining(address); + if (function != null) { + Address functionAddress = function.getEntryPoint(); + lastValue = functionLengthByAddress.getOrDefault(functionAddress, -1); + } + // If function was null, then lastValue stays -1 until overwritten + } + Long length = lastValue - offset; + // TODO: remove next line once we initialize an appropriate lastValue + length = Long.max(length, 0); // last record gets length zero if lastValue was -1 + lastValue = offset; + + // Note: we are not currently using boolean isStatement = lineRecord.isStatement() + + // Note: we are not using this call, but users might be interested in the fact of + // 0xfeefee and 0xf00f00. + // lineRecord.isSpecialLine(); + + // TODO: There might be something we can do with the boolean isIlLines + // Seems that the pdb.xml is not necessarily doing anything different + + applyRecord(sourceFile, address, lineNumStart.intValue(), + length.intValue()); + + // Note: We are not processing column records, but they are available. + } + } + } + + //============================================================================================== + // Suppress: for not being used and for unfinished implementation with unused variables + @SuppressWarnings("unused") + private void processC13InlineeLines(ModuleInformation moduleInfo, + FileChecksumsC13Section fileChecksumsC13Section, InlineeLinesC13Section c13InlineeLines) + throws CancelledException { + + // Something else to look into: ModuleInformation600 version has more fields that we do + // not know their usefulness at this time + //String moduleName = moduleInfo.getModuleName(); + //String objectFileName = moduleInfo.getObjectFileName(); + List inlineeLines = c13InlineeLines.getInlineeLines(); + + for (C13InlineeSourceLine inlineeLine : inlineeLines) { + pdb.checkCancelled(); + + int fileId = inlineeLine.getFileId(); + SourceFile sourceFile = getSourceFile(fileChecksumsC13Section, fileId); + + Long inlinee = inlineeLine.getInlinee(); + RecordNumber recordNumber = RecordNumber.itemRecordNumber(inlinee.intValue()); + AbstractMsType type = applicator.getTypeRecord(recordNumber); + //TODO: might want to create TypeAppliers for MemberFunctionIdMsType and + // FunctionIdMsType and any of their derivatives and use them to do logic for the + // types. + if (type instanceof FunctionIdMsType functionId) { + String name = functionId.getName(); + RecordNumber scopeIdRecordNumber = functionId.getScopeIdRecordNumber(); + AbstractMsType scope = applicator.getTypeRecord(scopeIdRecordNumber); + // TODO: DO MORE WORK + } + else if (type instanceof MemberFunctionIdMsType memberFunctionId) { + String name = memberFunctionId.getName(); + RecordNumber parentTypeRecordNumber = memberFunctionId.getParentTypeRecordNumber(); + AbstractMsType parent = applicator.getTypeRecord(parentTypeRecordNumber); + // TODO: DO MORE WORK + } + else { + // TODO: DO MORE WORK + } + + long lineNum = inlineeLine.getSourceLineNum(); + + if (inlineeLine instanceof C13ExtendedInlineeSourceLine extendedInlineeLine) { + int numIds = extendedInlineeLine.getNumExtraFileIds(); + List ids = extendedInlineeLine.getExtraFileIds(); + for (int id : ids) { + SourceFile inlineeSourceFile = getSourceFile(fileChecksumsC13Section, id); + // TODO: DO MORE WORK + } + } + } + } + + //============================================================================================== + // Temporary mock class + private static class SourceFile { + // Empty + } + + private SourceFile getSourceFile(FileChecksumsC13Section fileChecksums, int fileId) { + return new SourceFile(); + } + + //============================================================================================== + private void applyRecord(SourceFile sourceFile, Address address, int start, int length) { + // Need to use getCodeUnitContaining(address) instead of getCodeUnitAt(address) because + // there is a situation where the PDB associates a line number with the base part of an + // instructions instead of the prefix part, such as with MSFT tool-chain emits a + // "REP RET" (f3 c3) sequence, where the "REP" is an instruction prefix, in order to + // avoid a branch prediction penalty for AMD processors. However, Microsoft associates + // the line number of the instruction with the address of the "RET" (c3) instead of with + // the address of the "REP" (f3) portion (beginning) of the instruction. + CodeUnit cu = program.getListing().getCodeUnitContaining(address); + if (cu == null) { + log.appendMsg("PDB", + "Skipping source map info (no code unit found at " + address + ")"); + return; + } + +// try { +// manager.addSourceMapEntry(sourceFile, start, address, length); +// } +// catch (LockException e) { +// throw new AssertException("LockException after exclusive access verified!"); +// } +// catch (AddressOverflowException e) { +// log.appendMsg("PDB", "AddressOverflow for source map info: %s, %d, %s, %d" +// .formatted(sourceFile.getPath(), start, address.toString(), length)); +// } + } + +} diff --git a/Ghidra/Features/PDB/src/test/java/ghidra/app/util/pdb/pdbapplicator/PdbStubProgram.java b/Ghidra/Features/PDB/src/test/java/ghidra/app/util/pdb/pdbapplicator/PdbStubProgram.java new file mode 100644 index 0000000000..084483b45e --- /dev/null +++ b/Ghidra/Features/PDB/src/test/java/ghidra/app/util/pdb/pdbapplicator/PdbStubProgram.java @@ -0,0 +1,170 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.util.pdb.pdbapplicator; + +import ghidra.framework.model.DomainFile; +import ghidra.program.model.StubProgram; +import ghidra.program.model.address.*; +import ghidra.program.model.listing.*; +import ghidra.program.model.mem.*; +import ghidra.program.model.symbol.*; + +/** + * Stub Program for some PDB tests + */ + +public class PdbStubProgram extends StubProgram { + + private DomainFile domainFile; + + private AddressSpace space; + private AddressFactory factory; + private static long imageBaseVal = 0x400000L; + private Address imageBase; + private SymbolTable symbolTable; + private Memory memory; + private Listing listing; + + public PdbStubProgram(DomainFile domainFile) { + this.domainFile = domainFile; + space = new GenericAddressSpace("xx", 64, AddressSpace.TYPE_RAM, 0); + factory = new DefaultAddressFactory(new AddressSpace[] { space }); + imageBase = factory.getAddress(space.getSpaceID(), imageBaseVal); + symbolTable = new PdbStubSymbolTable(); + memory = new PdbStubMemory(imageBase); + listing = new PdbStubListing(); + } + + @Override + public DomainFile getDomainFile() { + return domainFile; + } + + @Override + public Address getImageBase() { + return imageBase; + } + + @Override + public SymbolTable getSymbolTable() { + return symbolTable; + } + + @Override + public Memory getMemory() { + return memory; + } +// @Override +// public ProgramDataTypeManager getDataTypeManager() { +// return dataTypeManager; +// } +// + + @Override + public AddressFactory getAddressFactory() { + return factory; + } + + @Override + public Listing getListing() { + return listing; + } + + @Override + public boolean hasExclusiveAccess() { + return true; + } + + private class PdbStubSymbolTable extends StubSymbolTable { + @Override + public SymbolIterator getAllSymbols(boolean includeDynamicSymbols) { + return SymbolIterator.EMPTY_ITERATOR; + } +// +// @Override +// public SymbolIterator getPrimarySymbolIterator(AddressSetView asv, boolean forward) { +// return SymbolIterator.EMPTY_ITERATOR; +// } + } + + private class PdbStubMemory extends StubMemory { + + private static final int NUM_BLOCKS = 1000; + private static final long BLOCK_SIZE = 1000000; + private static final MemoryBlock[] blocks = new MemoryBlock[NUM_BLOCKS]; + + PdbStubMemory(Address imageBase) { + Address address = imageBase; + for (int block = 0; block < NUM_BLOCKS; block++) { + // We are pre-incrementing so that we do not get a "zero" address for our + // processing + address = address.add(BLOCK_SIZE); + blocks[block] = new PdbMemoryBlock(address, BLOCK_SIZE); + } + } + + @Override + public MemoryBlock[] getBlocks() { + return blocks; + } + } + + private class PdbMemoryBlock extends MemoryBlockStub { + + private Address address; + private long size; + + PdbMemoryBlock(Address address, long size) { + this.address = address; + this.size = size; + } + + @Override + public Address getStart() { + return address; + } + + @Override + public long getSize() { + return size; + } + } + + private class PdbStubListing extends StubListing { + + @Override + public CodeUnit getCodeUnitContaining(Address addr) { + return new PdbInstructionStub(addr); + } + + } + + private class PdbInstructionStub extends InstructionStub { + + Address address; + + PdbInstructionStub(Address addr) { + address = addr; + } + + @Override + public Address getAddress() { + return address; + } + + } + +} diff --git a/Ghidra/Features/PDB/src/test/java/ghidra/app/util/pdb/pdbapplicator/StubPdbApplicator.java b/Ghidra/Features/PDB/src/test/java/ghidra/app/util/pdb/pdbapplicator/StubPdbApplicator.java index 92ce43c345..e3391f3a7a 100644 --- a/Ghidra/Features/PDB/src/test/java/ghidra/app/util/pdb/pdbapplicator/StubPdbApplicator.java +++ b/Ghidra/Features/PDB/src/test/java/ghidra/app/util/pdb/pdbapplicator/StubPdbApplicator.java @@ -29,7 +29,7 @@ import ghidra.util.task.TaskMonitor; public class StubPdbApplicator implements PdbApplicator { private AbstractPdb pdb = null; - private long originalImageBase = 0L; + private long originalImageBase = 0x400000L; private Program program = null;