Merge remote-tracking branch 'origin/GP-4725_ghizard_PDBUniversal_source_line_investigations--SQUASHED'

This commit is contained in:
Ryan Kurtz 2024-09-27 11:12:42 -04:00
commit 22f95a5eba
15 changed files with 753 additions and 27 deletions

View File

@ -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-----------------------------------------");
}
/**

View File

@ -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<C13FileChecksum> fileChecksums = new ArrayList<>();
private Map<Integer, C13FileChecksum> 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(),

View File

@ -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 {

View File

@ -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);
}

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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);
}
/**

View File

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

View File

@ -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();

View File

@ -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<Class<? extends AbstractMsSymbol>> unexpectedGlobalSymbols = new HashSet<>();
private Set<Class<? extends AbstractMsSymbol>> 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;
}
}

View File

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

View File

@ -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<Address, Integer> 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<Integer> baseSrcFile = c11Lines.getBaseSrcFiles();
List<C11LinesStartEnd> startEnd = c11Lines.getStartEnd();
List<Integer> seg = c11Lines.getSegments();
List<Integer> ccSegs = c11Lines.getPerFileNumSegments();
List<List<Integer>> baseSrcLines = c11Lines.getPerFileBaseSrcLines();
List<List<C11LinesStartEnd>> startEnds = c11Lines.getPerFileStartEndRecords();
List<String> names = c11Lines.getFileNames();
List<List<Integer>> segmentNumbers = c11Lines.getPerFileSegmentNumbers();
List<List<List<Long>>> offsets = c11Lines.getPerFilePerSegmentOffsets();
List<List<List<Integer>>> lineNumbers = c11Lines.getPerFilePerSegmentLineNumbers();
// do something
}
//==============================================================================================
private void processC13Sections(Module module)
throws CancelledException {
ModuleInformation moduleInfo = module.getModuleInformation();
C13SectionIterator<FileChecksumsC13Section> c13FileChecksumIterator;
C13SectionIterator<LinesC13Section> linesIterator;
C13SectionIterator<IlLinesC13Section> ilLinesIterator;
C13SectionIterator<InlineeLinesC13Section> 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<C13FileRecord> 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<C13LineRecord> 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<C13InlineeSourceLine> 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<Integer> 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));
// }
}
}

View File

@ -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;
}
}
}

View File

@ -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;