GT-3112 - PDB Universal - applicator/analyzer (112 squashed commits)

This commit is contained in:
ghizard 2020-09-01 10:48:01 -04:00
parent e1efbe228e
commit a1d7ed2cb2
85 changed files with 27376 additions and 43 deletions

View file

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication">
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
<listEntry value="/Framework Utility/src/main/java/ghidra/GhidraLauncher.java"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
<listEntry value="1"/>
</listAttribute>
<stringAttribute key="org.eclipse.debug.core.source_locator_id" value="org.eclipse.jdt.launching.sourceLocator.JavaSourceLookupDirector"/>
<stringAttribute key="org.eclipse.debug.core.source_locator_memento" value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;sourceLookupDirector&gt;&#10; &lt;sourceContainers duplicates=&quot;false&quot;&gt;&#10; &lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#10;&amp;lt;classpathContainer path=&amp;quot;org.eclipse.jdt.launching.JRE_CONTAINER&amp;quot;/&amp;gt;&amp;#10;&quot; typeId=&quot;org.eclipse.jdt.launching.sourceContainer.classpathContainer&quot;/&gt;&#10; &lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#10;&amp;lt;default/&amp;gt;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.default&quot;/&gt;&#10; &lt;/sourceContainers&gt;&#10;&lt;/sourceLookupDirector&gt;&#10;"/>
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
<listEntry value="org.eclipse.eclemma.ui.launchGroup.coverage"/>
</listAttribute>
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_CLASSPATH_ONLY_JAR" value="false"/>
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_START_ON_FIRST_THREAD" value="true"/>
<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry path=&quot;5&quot; projectName=&quot;Framework Utility&quot; type=&quot;1&quot;/&gt;&#10;"/>
</listAttribute>
<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11"/>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="ghidra.GhidraLauncher"/>
<listAttribute key="org.eclipse.jdt.launching.MODULEPATH"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="ghidra.GhidraRun"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="_Integration Test"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-XX:+IgnoreUnrecognizedVMOptions -Djava.system.class.loader=ghidra.GhidraClassLoader -Dfile.encoding=UTF8 -Duser.country=US -Duser.language=en -Dsun.java2d.pmoffscreen=false -Dsun.java2d.xrender=true -Dsun.java2d.d3d=false -Xdock:name=&quot;Ghidra&quot; -Dvisualvm.display.name=Ghidra"/>
<stringAttribute key="org.eclipse.jdt.launching.WORKING_DIRECTORY" value="${workspace_loc:Framework Utility}"/>
</launchConfiguration>

View file

@ -10,6 +10,7 @@
##MODULE IP: Oxygen Icons - LGPL 3.0 ##MODULE IP: Oxygen Icons - LGPL 3.0
##MODULE IP: Tango Icons - Public Domain ##MODULE IP: Tango Icons - Public Domain
.gitignore||GHIDRA||||END| .gitignore||GHIDRA||||END|
.launch/Ghidra Code Coverage.launch||GHIDRA||||END|
.launch/Ghidra.launch||GHIDRA||||END| .launch/Ghidra.launch||GHIDRA||||END|
Module.manifest||GHIDRA||||END| Module.manifest||GHIDRA||||END|
build.gradle||GHIDRA||||END| build.gradle||GHIDRA||||END|

View file

@ -306,6 +306,15 @@ public abstract class DemangledObject implements Demangled {
public boolean applyTo(Program program, Address address, DemanglerOptions options, public boolean applyTo(Program program, Address address, DemanglerOptions options,
TaskMonitor monitor) throws Exception { TaskMonitor monitor) throws Exception {
return applyPlateCommentOnly(program, address);
}
/**
* @param program The program for which to apply the comment
* @param address The address for the comment
* @return {@code true} if a comment was applied
*/
public boolean applyPlateCommentOnly(Program program, Address address) {
if (mangled.equals(name)) { if (mangled.equals(name)) {
return false; return false;
} }
@ -443,11 +452,8 @@ public abstract class DemangledObject implements Demangled {
List<Symbol> symbols = symbolTable.getSymbols(namespaceName, namespace); List<Symbol> symbols = symbolTable.getSymbols(namespaceName, namespace);
Symbol namespaceSymbol = Symbol namespaceSymbol =
symbols.stream() symbols.stream().filter(s -> (s.getSymbolType() == SymbolType.NAMESPACE ||
.filter(s -> (s.getSymbolType() == SymbolType.NAMESPACE || s.getSymbolType() == SymbolType.CLASS)).findFirst().orElse(null);
s.getSymbolType() == SymbolType.CLASS))
.findFirst()
.orElse(null);
if (namespaceSymbol == null) { if (namespaceSymbol == null) {
try { try {
namespace = namespace =

View file

@ -395,6 +395,122 @@ public class ConflictHandlerTest extends AbstractGhidraHeadedIntegrationTest {
struct1a_pathname, struct1b_pathname); struct1a_pathname, struct1b_pathname);
} }
/**
* Tests the
* {@link DataTypeConflictHandler#REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER RESORAAH}
* conflict handler to be sure that, if all else is the same, the aligned version is chosen
* over the unaligned version.
* <p>
* Success is the aligned version is chosen over the unaligned version.
*/
@Test
public void testChooseNewAlignedOverExistingUnalignedWhenAllElseIsEqualForEmptyStructures() {
// Unaligned exists first.
Structure empty1Unaligned = new StructureDataType(root, "empty1", 0, dataMgr);
Composite empty1AlignedToAdd = (Composite) empty1Unaligned.copy(dataMgr);
empty1AlignedToAdd.setInternallyAligned(true);
String empty1UnalignedString = empty1Unaligned.toString();
String empty1AlignedToAddString = empty1AlignedToAdd.toString();
Structure empty1AddResult = (Structure) dataMgr.addDataType(empty1AlignedToAdd,
DataTypeConflictHandler.REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER);
String empty1AddResultString = empty1AddResult.toString();
assertEquals(empty1AlignedToAddString, empty1AddResultString);
assertNotEquals(empty1UnalignedString, empty1AddResultString);
}
/**
* Tests the
* {@link DataTypeConflictHandler#REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER RESORAAH}
* conflict handler to be sure that, if all else is the same, the aligned version is chosen
* over the unaligned version.
* <p>
* Success is the aligned version is chosen over the unaligned version.
*/
@Test
public void testChooseNewAlignedOverExistingUnalignedWhenAllElseIsEqualForNonEmptyStructures() {
// Unaligned exists first.
StructureDataType struct1Unaligned = createPopulated(dataMgr);
Composite struct1AlignedToAdd = (Composite) struct1Unaligned.copy(dataMgr);
struct1AlignedToAdd.setInternallyAligned(true);
String struct1UnalignedString = struct1Unaligned.toString();
String struct1AlignedToAddString = struct1AlignedToAdd.toString();
Structure struct1AddResult = (Structure) dataMgr.addDataType(struct1AlignedToAdd,
DataTypeConflictHandler.REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER);
String struct1AddResultString = struct1AddResult.toString();
assertEquals(struct1AlignedToAddString, struct1AddResultString);
assertNotEquals(struct1UnalignedString, struct1AddResultString);
}
/**
* Tests the
* {@link DataTypeConflictHandler#REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER RESORAAH}
* conflict handler to be sure that, if all else is the same, the new unaligned version is
* chosen over the existing unaligned version.
* <p>
* Success is the new unaligned version is chosen over the existing aligned version.
*/
// TODO: consider whether we want to change the logic of the conflict handler to favor
// aligned over unaligned.
@Test
public void testChooseNewUnalignedOverExistingAlignedWhenAllElseIsEqualForEmptyStructures() {
// Aligned exists first.
Structure empty2Aligned = new StructureDataType(root, "empty2", 0, dataMgr);
Composite empty2UnalignedToAdd = (Composite) empty2Aligned.copy(dataMgr);
// aligning only after making unaligned copy.
empty2Aligned.setInternallyAligned(true);
String empty2AlignedString = empty2Aligned.toString();
String empty2UnalignedToAddString = empty2UnalignedToAdd.toString();
Structure empty2AddResult = (Structure) dataMgr.addDataType(empty2UnalignedToAdd,
DataTypeConflictHandler.REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER);
String empty2AddResultString = empty2AddResult.toString();
assertEquals(empty2UnalignedToAddString, empty2AddResultString);
assertNotEquals(empty2AlignedString, empty2AddResultString);
}
/**
* Tests the
* {@link DataTypeConflictHandler#REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER RESORAAH}
* conflict handler to be sure that, if all else is the same, the new unaligned version is
* chosen over the existing aligned version.
* <p>
* Success is the new unaligned version is chosen over the existing aligned version.
*/
// TODO: consider whether we want to change the logic of the conflict handler to favor
// aligned over unaligned.
@Test
public void testChooseNewUnalignedOverExistingAlignedWhenAllElseIsEqualForNonEmptyStructures() {
// Aligned exists first.
StructureDataType struct2Aligned = createPopulated(dataMgr);
Composite struct2UnalignedToAdd = (Composite) struct2Aligned.copy(dataMgr);
// aligning only after making unaligned copy.
struct2Aligned.setInternallyAligned(true);
String struct2AlignedString = struct2Aligned.toString();
String struct2UnalignedToAddString = struct2UnalignedToAdd.toString();
Structure struct2AddResult = (Structure) dataMgr.addDataType(struct2UnalignedToAdd,
DataTypeConflictHandler.REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER);
String struct2AddResultString = struct2AddResult.toString();
assertEquals(struct2UnalignedToAddString, struct2AddResultString);
assertNotEquals(struct2AlignedString, struct2AddResultString);
}
/**
* Tests the
* {@link DataTypeConflictHandler#REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER RESORAAH}
* conflict handler to be sure that, if all else is the same, the aligned version is chosen
* over the unaligned version.
* <p>
* Success is the aligned version is chosen over the unaligned version.
*/
@Test @Test
public void testResolveDataTypeNonStructConflict() throws Exception { public void testResolveDataTypeNonStructConflict() throws Exception {
DataTypeManager dtm = new StandAloneDataTypeManager("Test"); DataTypeManager dtm = new StandAloneDataTypeManager("Test");

View file

@ -27,11 +27,11 @@ import ghidra.program.model.mem.*;
import ghidra.util.exception.*; import ghidra.util.exception.*;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
class MyTestMemory extends AddressSet implements Memory { public class MyTestMemory extends AddressSet implements Memory {
byte[] myMemoryBytes; byte[] myMemoryBytes;
MemoryBlock myMemoryBlock; MemoryBlock myMemoryBlock;
MyTestMemory(byte[] bytes) { public MyTestMemory(byte[] bytes) {
super(); super();
this.myMemoryBytes = bytes; this.myMemoryBytes = bytes;
AddressSpace space = new GenericAddressSpace("Mem", 32, AddressSpace.TYPE_RAM, 0); AddressSpace space = new GenericAddressSpace("Mem", 32, AddressSpace.TYPE_RAM, 0);
@ -108,8 +108,7 @@ class MyTestMemory extends AddressSet implements Memory {
@Override @Override
public MemoryBlock createByteMappedBlock(String name, Address start, Address mappedAddress, public MemoryBlock createByteMappedBlock(String name, Address start, Address mappedAddress,
long length, ByteMappingScheme byteMappingScheme, boolean overlay) long length, ByteMappingScheme byteMappingScheme, boolean overlay) throws LockException,
throws LockException,
MemoryConflictException, AddressOverflowException, IllegalArgumentException { MemoryConflictException, AddressOverflowException, IllegalArgumentException {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }

View file

@ -0,0 +1,108 @@
/* ###
* 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.
*/
// Text-dump all data types from the user-specified DataTypeManager to the user-specified file.
//
//@category Data Types
import java.io.File;
import java.io.FileWriter;
import java.util.Iterator;
import docking.widgets.OptionDialog;
import ghidra.app.script.GhidraScript;
import ghidra.app.services.DataTypeManagerService;
import ghidra.app.util.ToolTipUtils;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.data.*;
import ghidra.util.HTMLUtilities;
import ghidra.util.Msg;
import ghidra.util.exception.CancelledException;
public class DeveloperDumpAllTypesScript extends GhidraScript {
@Override
protected void run() throws Exception, CancelledException {
DataTypeManager manager = userChooseDataTypeManager();
if (manager == null) {
return;
}
File dumpFile = askFile("Choose an output file", "OK");
if (dumpFile == null) {
Msg.info(this, "Canceled execution due to no output file");
return;
}
if (dumpFile.exists()) {
if (!askYesNo("Confirm Overwrite", "Overwrite file: " + dumpFile.getName())) {
Msg.info(this, "Operation canceled");
return;
}
}
FileWriter fileWriter = new FileWriter(dumpFile);
String message = "Outputting DataTypes from: " + manager.getName();
Iterator<DataType> allDataTypes = manager.getAllDataTypes();
while (allDataTypes.hasNext()) {
monitor.checkCanceled();
DataType dataType = allDataTypes.next();
DataTypePath dataTypePath = dataType.getDataTypePath();
String pathString = dataTypePath.toString();
String htmlString = ToolTipUtils.getToolTipText(dataType);
String plainString = HTMLUtilities.fromHTML(htmlString);
fileWriter.append(pathString);
fileWriter.append("\n");
fileWriter.append(plainString);
fileWriter.append("\n");
fileWriter.append("------------------------------------------------------------\n");
}
fileWriter.close();
message = "Results located in: " + dumpFile.getAbsoluteFile();
monitor.setMessage(message);
Msg.info(this, message);
}
private DataTypeManager userChooseDataTypeManager() {
PluginTool tool = state.getTool();
DataTypeManagerService service = tool.getService(DataTypeManagerService.class);
DataTypeManager[] dataTypeManagers = service.getDataTypeManagers();
String names[] = new String[dataTypeManagers.length];
String initialDtmChoice = names[0];
try {
initialDtmChoice = currentProgram.getDataTypeManager().getName();
}
catch (Exception e) {
// Ignore... assuming no program or dtm.
}
for (int i = 0; i < dataTypeManagers.length; i++) {
names[i] = dataTypeManagers[i].getName();
}
String userChoice =
OptionDialog.showInputChoiceDialog(null, "Choose a Data Type Manager or Cancel",
"Choose", names, initialDtmChoice, OptionDialog.CANCEL_OPTION);
if (userChoice == null) {
return null;
}
for (int i = 0; i < dataTypeManagers.length; i++) {
if (names[i].contentEquals(userChoice)) {
return dataTypeManagers[i];
}
}
return null; // should not reach this line.
}
}

View file

@ -0,0 +1,101 @@
/* ###
* 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.
*/
// Script for ghizard developer investigating template and ICF squashing.
//
//@category PDB
import java.io.*;
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.symbol.*;
import ghidra.util.Msg;
import mdemangler.*;
import mdemangler.object.MDObjectCPP;
public class DumpAllSymbolsDemangledScript extends GhidraScript {
@Override
public void run() throws Exception {
File dumpFile = askFile("Choose an output file", "OK");
if (dumpFile == null) {
Msg.info(this, "Canceled execution due to no output file");
return;
}
if (dumpFile.exists()) {
if (!askYesNo("Confirm Overwrite", "Overwrite file: " + dumpFile.getName())) {
Msg.info(this, "Operation canceled");
return;
}
}
FileWriter fileWriter = new FileWriter(dumpFile);
BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
SymbolTable st = currentProgram.getSymbolTable();
SymbolIterator it = st.getDefinedSymbols();
while (it.hasNext() && !monitor.isCancelled()) {
Symbol s = it.next();
if (s.getSource() == SourceType.DEFAULT) {
continue;
}
Address addr = s.getAddress();
if (addr.isExternalAddress() || Address.NO_ADDRESS.equals(addr)) {
continue;
}
String name = s.getName();
String demangled = getDemangledString(name);
if (demangled != null && !demangled.isBlank()) {
name = demangled;
}
bufferedWriter.append(addr + " " + name + "\n");
}
bufferedWriter.close();
}
/**
* Gets a demangled string for the indicated mangled string.
* @param mangledString the mangled string to be decoded
* @return the associated demangled string
*/
private static String getDemangledString(String mangledString) {
MDMangGhidra demangler = new MDMangGhidra();
try {
MDParsableItem parsableItem = demangler.demangle(mangledString, true);
if (parsableItem instanceof MDObjectCPP) {
MDObjectCPP mdObject = (MDObjectCPP) parsableItem;
return mdObject.getQualifiedName().toString();
}
// if (parsableItem instanceof MDFunctionType) {
// MDFunctionType functionType = (MDFunctionType) parsableItem;
// return functionType.getName();
// }
// if (parsableItem instanceof MDDataType) {
// MDDataType dataType = (MDDataType) parsableItem;
// return dataType.getName();
// }
return parsableItem.toString();
}
catch (MDException e) {
// Couldn't demangle.
Msg.info(null, e.getMessage());
return null;
}
}
}

View file

@ -0,0 +1,88 @@
/* ###
* 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.
*/
// Dump PDB information from AbstractPdb for PDB developer use.
//
//@category PDB
import java.io.*;
import ghidra.app.script.GhidraScript;
import ghidra.app.util.bin.format.pdb2.pdbreader.*;
import ghidra.util.Msg;
public class PdbDeveloperDumpScript extends GhidraScript {
@Override
protected void run() throws Exception {
File pdbFile = askFile("Choose a PDB file", "OK");
if (pdbFile == null) {
Msg.info(this, "Canceled execution due to no input file");
return;
}
if (!pdbFile.exists()) {
String message = pdbFile.getAbsolutePath() + " is not a valid file.";
Msg.info(this, message);
popup(message);
return;
}
String pdbFileName = pdbFile.getAbsolutePath();
if (!pdbFileName.endsWith(".pdb") && !pdbFileName.endsWith(".PDB")) {
String message = "Aborting: Expected input file to have extension of type .pdb (got '" +
pdbFileName + "').";
Msg.info(this, message);
popup(message);
return;
}
File dumpFile = askFile("Choose an output file", "OK");
if (dumpFile == null) {
Msg.info(this, "Canceled execution due to no output file");
return;
}
if (dumpFile.exists()) {
if (!askYesNo("Confirm Overwrite", "Overwrite file: " + dumpFile.getName())) {
Msg.info(this, "Operation canceled");
return;
}
}
String message = "Processing PDB Dump of: " + pdbFileName;
monitor.setMessage(message);
Msg.info(this, message);
try (AbstractPdb pdb = PdbParser.parse(pdbFileName, new PdbReaderOptions(), monitor)) {
pdb.deserialize(monitor);
FileWriter fileWriter = new FileWriter(dumpFile);
BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
outputHeaderMessage(bufferedWriter, pdbFileName);
pdb.dumpDirectory(bufferedWriter);
pdb.dumpSubStreams(bufferedWriter);
bufferedWriter.close();
}
catch (IOException ioe) {
Msg.info(this, ioe.getMessage());
popup(ioe.getMessage());
}
message = "Results located in: " + dumpFile.getAbsoluteFile();
monitor.setMessage(message);
Msg.info(this, message);
}
private void outputHeaderMessage(BufferedWriter bufferedWriter, String name) throws Exception {
bufferedWriter.append(getClass().getSimpleName() + " dump of: " + name +
"\nWARNING: FORMAT SUBJECT TO CHANGE WITHOUT NOTICE--DO NOT PARSE\n\n");
}
}

View file

@ -0,0 +1,590 @@
/* ###
* 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.plugin.core.analysis;
import java.io.File;
import java.io.IOException;
import java.util.Date;
import docking.widgets.OkDialog;
import docking.widgets.OptionDialog;
import ghidra.app.services.*;
import ghidra.app.util.bin.format.pdb2.pdbreader.*;
import ghidra.app.util.importer.MessageLog;
import ghidra.app.util.opinion.PeLoader;
import ghidra.app.util.pdb.PdbLocatorSelector;
import ghidra.app.util.pdb.PdbProgramAttributes;
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.CancelledException;
import ghidra.util.task.TaskMonitor;
// TODO: Need to refactor packaging/classes/methods associated with "Features PDB" that are
// taken and used here. Need to make them available to both previous and this new analyzers
// in their own special ways. Make utility classes that are better split/organized. Some
// items are also used in other plugins/loaders (e.g, PdbException).
// For example, we do not use the XML format here. The code that was taken was strewn across
// classes and should be reorganized. For example, why is PdbParserNEW in control of the logic
// of where a PDB file might be found?
/**
* PDB Universal Reader/Analyzer. Uses raw PDB files (not XML-converted PDBs). Attempts to
* apply the information to a program.
*/
public class Pdb2Analyzer extends AbstractAnalyzer {
// Developer turn on/off options that are in still in development.
private static final boolean developerMode = false;
//==============================================================================================
private static final String NAME = "PDB Universal Reader/Analyzer";
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");
//==============================================================================================
// Force-load a PDB file.
private static final String OPTION_NAME_DO_FORCELOAD = "Do Force-Load";
private static final String OPTION_DESCRIPTION_DO_FORCELOAD =
"If checked, uses the 'Force Load' file without validation.";
private boolean doForceLoad = false;
// The file to force-load.
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;
// 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();
// Include the PE-Header-Specified PDB path for searching for appropriate PDB file.
private static final String OPTION_NAME_INCLUDE_PE_PDB_PATH =
"Unsafe: Include PE PDB Path in PDB Search";
private static final String OPTION_DESCRIPTION_INCLUDE_PE_PDB_PATH =
"If checked, specifically searching for PDB in PE-Header-Specified Location.";
private boolean includePeSpecifiedPdbPath = false;
//==============================================================================================
// Logging options
//==============================================================================================
// Perform logging of PDB information for debugging/development.
// NOTE: This logging mechanism is not intended to live the full life of this tool, but to
// aid in getting feedback from the field during its early development.
private static final String OPTION_NAME_PDB_READER_ANALYZER_LOGGING =
"[PDB Reader/Analyzer Debug Logging]";
private static final String OPTION_DESCRIPTION_PDB_READER_ANALYZER_LOGGING =
"If checked, logs information to the pdb.analyzer.log file for debug/development.";
private boolean pdbLogging = false;
//==============================================================================================
// PdbReader options
//==============================================================================================
// Sets the one-byte Charset to be used for PDB processing.
// NOTE: This "Option" is not intended as a permanent part of this analyzer. Should be
// replaced by target-specific Charset.
private static final String OPTION_NAME_ONE_BYTE_CHARSET_NAME = "PDB One-Byte Charset Name";
private static final String OPTION_DESCRIPTION_ONE_BYTE_CHARSET_NAME =
"Charset used for processing of one-byte (or multi) encoded Strings: " +
PdbReaderOptions.getOneByteCharsetNames();
private String oneByteCharsetName = CharsetInfo.UTF8;
// Sets the wchar_t Charset to be used for PDB processing.
// NOTE: This "Option" is not intended as a permanent part of this analyzer. Should be
// replaced by target-program-specific Charset.
private static final String OPTION_NAME_WCHAR_CHARSET_NAME = "PDB Wchar_t Charset Name";
private static final String OPTION_DESCRIPTION_WCHAR_CHARSET_NAME =
"Charset used for processing of wchar_t encoded Strings: " +
PdbReaderOptions.getTwoByteCharsetNames();
private String wideCharCharsetName = CharsetInfo.UTF16;
//==============================================================================================
// PdbApplicator options
//==============================================================================================
// Apply Data Types Only.
private static final String OPTION_NAME_APPLY_DATA_TYPES_ONLY = "Apply Data Types Only";
private static final String OPTION_DESCRIPTION_APPLY_DATA_TYPES_ONLY =
"If checked, only applies data types to program.";
private boolean applyDataTypesOnly;
// Apply Public Symbols Only.
private static final String OPTION_NAME_PUBLIC_SYMBOLS_ONLY = "Apply Public Symbols Only";
private static final String OPTION_DESCRIPTION_PUBLIC_SYMBOLS_ONLY =
"If checked, only applies public symbols to program. Does not apply data types.";
private boolean applyPublicSymbolsOnly;
// Apply Code Block Comments.
private static final String OPTION_NAME_APPLY_CODE_SCOPE_BLOCK_COMMENTS =
"Apply Code Scope Block Comments";
private static final String OPTION_DESCRIPTION_APPLY_CODE_SCOPE_BLOCK_COMMENTS =
"If checked, pre/post-comments will be applied when code scope blocks are specified.";
private boolean applyCodeScopeBlockComments;
// Apply Instruction Labels information.
private static final String OPTION_NAME_APPLY_INSTRUCTION_LABELS = "Apply Instruction Labels";
private static final String OPTION_DESCRIPTION_APPLY_INSTRUCTION_LABELS =
"If checked, labels associated with instructions will be applied.";
private boolean applyInstructionLabels;
// Attempt to map address using existing mangled symbols.
private static final String OPTION_NAME_ADDRESS_REMAP = "Address Remap Using Existing Symbols";
private static final String OPTION_DESCRIPTION_ADDRESS_REMAP =
"If checked, attempts to remap address to those matching existing public symbols.";
private boolean remapAddressUsingExistingPublicMangledSymbols;
// Allow a mangled symbol to be demoted from being a primary symbol if another symbol and
// associated explicit data type will be laid down at the location. This option exists
// because we expect the PDB explicit data type will be more accurate than trying to
// have the demangler lay down the data type.
private static final String OPTION_NAME_ALLOW_DEMOTE_MANGLED_PRIMARY =
"Allow demote mangled symbol from primary";
private static final String OPTION_DESCRIPTION_ALLOW_DEMOTE_MANGLED_PRIMARY =
"If checked, allows a mangled symbol to be demoted from primary if a possibly " +
"better data type can be laid down with a nonmangled symbol.";
private boolean allowDemotePrimaryMangledSymbol;
// Apply Function Variables
private static final String OPTION_NAME_APPLY_FUNCTION_VARIABLES = "Apply Function Variables";
private static final String OPTION_DESCRIPTION_APPLY_FUNCTION_VARIABLES =
"If checked, attempts to apply function parameters and local variables for program functions.";
private boolean applyFunctionVariables;
// Sets the composite layout mode.
// Legacy
// - similar to existing DIA-based PDB Analyzer, only placing current composite direct
// members (none from parent classes.
// Warning: the remaining experimental layout modes may not be kept and are not guaranteed
// to result in data types that will be compatible with future Ghidra releases:
// Complex with Basic Fallback
// - Performs Complex layout, but if the current class has no parent classes, it will not
// encapsulate the current class's 'direct' members.
// Simple
// - Performs Complex layout, except in rare instances where , so in most cases is the same
// as 'Complex with Basic Fallback' layout.
// Complex
// - Puts all current class members and 'direct' parents' 'direct' components into an
// encapsulating 'direct' container
private static final String OPTION_NAME_COMPOSITE_LAYOUT_MODE = "Composite Layout Mode";
private static final String OPTION_DESCRIPTION_COMPOSITE_LAYOUT_MODE =
"Legacy mode like original PDB Analyzer. Warning: other modes have no compatibility" +
" guarantee with future Ghidra releases or minor PDB Analyzer changes";
private CompositeLayoutMode compositeLayoutMode;
//==============================================================================================
// Additional instance data
//==============================================================================================
private AbstractPdb myPdb = null;
private PdbReaderOptions pdbReaderOptions;
private PdbApplicatorOptions pdbApplicatorOptions;
private MessageLog messageLog = null;
//==============================================================================================
//==============================================================================================
public Pdb2Analyzer() {
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);
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();
}
@Override
public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log)
throws CancelledException {
messageLog = log;
// 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.
// 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 other PDB Analyzer enabled");
return false;
}
PdbProgramAttributes programAttributes = new PdbProgramAttributes(program);
if (failMissingFilename(programAttributes, log) ||
failMissingAttributes(programAttributes, log)) {
return true;
}
String pdbFilename;
if (doForceLoad) {
pdbFilename = forceLoadFile.getAbsolutePath();
}
else {
PdbLocatorSelector locator = new PdbLocatorSelector(symbolsRepositoryPath);
pdbFilename =
locator.findPdb(program, programAttributes, !SystemUtilities.isInHeadlessMode(),
includePeSpecifiedPdbPath, monitor, log, getName());
if (pdbFilename == null) {
return false;
}
}
Msg.info(this, "Using PDB: " + pdbFilename);
PdbLog.message(
"================================================================================");
PdbLog.message(new Date(System.currentTimeMillis()).toString() + "\n");
PdbLog.message("Ghidra Version: " + Application.getApplicationVersion());
PdbLog.message(NAME);
PdbLog.message(DESCRIPTION);
PdbLog.message("PDB Filename: " + pdbFilename + "\n");
try (AbstractPdb pdb = PdbParser.parse(pdbFilename, pdbReaderOptions, monitor)) {
myPdb = pdb;
monitor.setMessage("PDB: Parsing " + pdbFilename + "...");
pdb.deserialize(monitor);
PdbApplicator applicator = new PdbApplicator(pdbFilename, myPdb);
applicator.applyTo(program, program.getDataTypeManager(), program.getImageBase(),
pdbApplicatorOptions, monitor, log);
}
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);
return false;
}
return true;
}
// TODO: I changed this method from what was lifted in the old code. I check for null string
// and I also check for MSCOFF_NAME (TODO: check on the validity of this!!!). Also, changed
// the comparison to a substring search from a .equals).
@Override
public boolean canAnalyze(Program program) {
String executableFormat = program.getExecutableFormat();
return executableFormat != null && (executableFormat.indexOf(PeLoader.PE_NAME) != -1);
// TODO: Check for MSCOFF_NAME. Initial investigation shows that the .debug$T section of
// the MSCOFF (*.obj) file has type records and the .debug$S section has symbol records.
// More than that, in at least one instance, there has been a TypeServer2MsType type
// record that give the GUID, age, and name of the PDB file associated with the MSCOFF
// file. At this point in time, these two sections of the MSCOFF are read (header and
// raw data), but we do not interpret these sections any further. Suggest that we "might"
// want to parse some of these records at load time? Maybe not. We could, at analysis
// time, add the ability to process these two sections (as part of analysis (though we
// will not be aware of a PDB file yet), and upon discovery of a TypeServer2MsType (or
// perhaps other?), proceed to find the file (if possible) and also process that file.
// We posit that if a record indicates a separate PDB for the types (Note: MSFT indicates
// that only data types will be found in an MSCOFF PDB file), then that will likely be
// the only record in the .debug$T section.
// TODO: If the MSCOFF file is located in a MSCOFF ARCHIVE (*.lib), there can be a PDB
// associated with the archive. We currently do not pass on this association of the
// PDB archive to each underlying MSCOFF file. Moreover, we believe that we are not
// currently discovering the associated MSCOFF ARCHIVE PDB file when processing the
// MSCOFF ARCHIVE. Initial indication is that each MSCOFF within the archive will have
// the PDB file that it needs listed, even if redundant for each MSCOFF within the
// archive.
// return executableFormat != null && (executableFormat.indexOf(PeLoader.PE_NAME) != -1 ||
// executableFormat.indexOf(MSCoffLoader.MSCOFF_NAME) != -1);
}
@Override
public void registerOptions(Options options, Program program) {
// TODO: keeping around while still developing, but eventually delete these.
// options.removeOption(OPTION_NAME_DO_FORCELOAD);
// options.removeOption(OPTION_NAME_FORCELOAD_FILE);
// options.removeOption(OPTION_NAME_INCLUDE_PE_PDB_PATH);
// options.removeOption(OPTION_NAME_PDB_READER_ANALYZER_LOGGING);
// options.removeOption(OPTION_NAME_APPLY_INSTRUCTION_LABELS);
// options.removeOption(OPTION_NAME_SYMBOLPATH);
// options.removeOption(OPTION_NAME_ONE_BYTE_CHARSET_NAME);
// options.removeOption(OPTION_NAME_WCHAR_CHARSET_NAME);
// options.removeOption("Analyzers.PDB Universal Reader/Analyzer.Symbol Repository Path");
// PDB file location information
options.registerOption(OPTION_NAME_DO_FORCELOAD, doForceLoad, null,
OPTION_DESCRIPTION_DO_FORCELOAD);
options.registerOption(OPTION_NAME_FORCELOAD_FILE, OptionType.FILE_TYPE, forceLoadFile,
null, OPTION_DESCRIPTION_FORCELOAD_FILE);
options.registerOption(OPTION_NAME_SYMBOLPATH, OptionType.FILE_TYPE, symbolsRepositoryPath,
null, OPTION_DESCRIPTION_SYMBOLPATH);
options.registerOption(OPTION_NAME_INCLUDE_PE_PDB_PATH, includePeSpecifiedPdbPath, null,
OPTION_DESCRIPTION_INCLUDE_PE_PDB_PATH);
// PdbReaderOptions
getPdbReaderOptions();
options.registerOption(OPTION_NAME_PDB_READER_ANALYZER_LOGGING, pdbLogging, null,
OPTION_DESCRIPTION_PDB_READER_ANALYZER_LOGGING);
options.registerOption(OPTION_NAME_ONE_BYTE_CHARSET_NAME, oneByteCharsetName, null,
OPTION_DESCRIPTION_ONE_BYTE_CHARSET_NAME);
options.registerOption(OPTION_NAME_WCHAR_CHARSET_NAME, wideCharCharsetName, null,
OPTION_DESCRIPTION_WCHAR_CHARSET_NAME);
// PdbApplicatorOptions
getPdbApplicatorOptions();
options.registerOption(OPTION_NAME_APPLY_DATA_TYPES_ONLY, applyDataTypesOnly, null,
OPTION_DESCRIPTION_APPLY_DATA_TYPES_ONLY);
options.registerOption(OPTION_NAME_PUBLIC_SYMBOLS_ONLY, applyPublicSymbolsOnly, null,
OPTION_DESCRIPTION_PUBLIC_SYMBOLS_ONLY);
options.registerOption(OPTION_NAME_APPLY_CODE_SCOPE_BLOCK_COMMENTS,
applyCodeScopeBlockComments, null, OPTION_DESCRIPTION_APPLY_CODE_SCOPE_BLOCK_COMMENTS);
if (developerMode) {
// Mechanism to apply instruction labels is not yet implemented-> does nothing
options.registerOption(OPTION_NAME_APPLY_INSTRUCTION_LABELS, applyInstructionLabels,
null, OPTION_DESCRIPTION_APPLY_INSTRUCTION_LABELS);
options.registerOption(OPTION_NAME_ADDRESS_REMAP,
remapAddressUsingExistingPublicMangledSymbols, null,
OPTION_DESCRIPTION_ADDRESS_REMAP);
options.registerOption(OPTION_NAME_ALLOW_DEMOTE_MANGLED_PRIMARY,
allowDemotePrimaryMangledSymbol, null,
OPTION_DESCRIPTION_ALLOW_DEMOTE_MANGLED_PRIMARY);
options.registerOption(OPTION_NAME_APPLY_FUNCTION_VARIABLES, applyFunctionVariables,
null, OPTION_DESCRIPTION_APPLY_FUNCTION_VARIABLES);
options.registerOption(OPTION_NAME_COMPOSITE_LAYOUT_MODE, compositeLayoutMode, null,
OPTION_DESCRIPTION_COMPOSITE_LAYOUT_MODE);
}
}
@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;
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;
includePeSpecifiedPdbPath =
options.getBoolean(OPTION_NAME_INCLUDE_PE_PDB_PATH, includePeSpecifiedPdbPath);
pdbLogging = options.getBoolean(OPTION_NAME_PDB_READER_ANALYZER_LOGGING, pdbLogging);
setPdbLogging();
// PdbReaderOptions
oneByteCharsetName =
options.getString(OPTION_NAME_ONE_BYTE_CHARSET_NAME, oneByteCharsetName);
wideCharCharsetName =
options.getString(OPTION_NAME_WCHAR_CHARSET_NAME, wideCharCharsetName);
setPdbReaderOptions();
// PdbApplicatorOptions
applyDataTypesOnly =
options.getBoolean(OPTION_NAME_APPLY_DATA_TYPES_ONLY, applyDataTypesOnly);
applyPublicSymbolsOnly =
options.getBoolean(OPTION_NAME_PUBLIC_SYMBOLS_ONLY, applyPublicSymbolsOnly);
applyCodeScopeBlockComments = options.getBoolean(
OPTION_NAME_APPLY_CODE_SCOPE_BLOCK_COMMENTS, applyCodeScopeBlockComments);
if (developerMode) {
// Mechanism to apply instruction labels is not yet implemented-> does nothing
applyInstructionLabels =
options.getBoolean(OPTION_NAME_APPLY_INSTRUCTION_LABELS, applyInstructionLabels);
remapAddressUsingExistingPublicMangledSymbols = options.getBoolean(
OPTION_NAME_ADDRESS_REMAP, remapAddressUsingExistingPublicMangledSymbols);
allowDemotePrimaryMangledSymbol = options.getBoolean(
OPTION_NAME_ALLOW_DEMOTE_MANGLED_PRIMARY, allowDemotePrimaryMangledSymbol);
applyFunctionVariables =
options.getBoolean(OPTION_NAME_APPLY_FUNCTION_VARIABLES, applyFunctionVariables);
compositeLayoutMode =
options.getEnum(OPTION_NAME_COMPOSITE_LAYOUT_MODE, compositeLayoutMode);
}
setPdbApplicatorOptions();
}
//==============================================================================================
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.
assert !NAME.contentEquals(oldPdbNAME);
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);
return true;
}
return false;
}
private boolean failMissingAttributes(PdbProgramAttributes attributes, MessageLog log) {
// RSDS version should only have GUID; non-RSDS version should only have Signature.
String warning;
if ("RSDS".equals(attributes.getPdbVersion())) {
if (attributes.getPdbGuid() != null) {
return false; // Don't fail.
}
warning = "No Program GUID to match with PDB.";
}
else {
if (attributes.getPdbSignature() != null) {
return false; // Don't fail.
}
warning = "No Program Signature to match with 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);
return true;
}
private void setPdbLogging() {
try {
PdbLog.setEnabled(pdbLogging);
}
catch (IOException e) {
// Probably could not open the file.
if (messageLog != null) {
messageLog.appendException(e);
}
}
}
private void getPdbReaderOptions() {
oneByteCharsetName = pdbReaderOptions.getOneByteCharsetName();
wideCharCharsetName = pdbReaderOptions.getTwoByteCharsetName();
}
private void setPdbReaderOptions() {
pdbReaderOptions.setOneByteCharsetForName(oneByteCharsetName);
pdbReaderOptions.setWideCharCharsetForName(wideCharCharsetName);
}
private void getPdbApplicatorOptions() {
applyCodeScopeBlockComments = pdbApplicatorOptions.applyCodeScopeBlockComments();
applyDataTypesOnly = pdbApplicatorOptions.applyDataTypesOnly();
applyPublicSymbolsOnly = pdbApplicatorOptions.applyPublicSymbolsOnly();
applyInstructionLabels = pdbApplicatorOptions.applyInstructionLabels();
remapAddressUsingExistingPublicMangledSymbols =
pdbApplicatorOptions.remapAddressUsingExistingPublicMangledSymbols();
allowDemotePrimaryMangledSymbol = pdbApplicatorOptions.allowDemotePrimaryMangledSymbol();
applyFunctionVariables = pdbApplicatorOptions.applyFunctionVariables();
compositeLayoutMode = pdbApplicatorOptions.getCompositeLayoutMode();
}
private void setPdbApplicatorOptions() {
pdbApplicatorOptions.setApplyDataTypesOnly(applyDataTypesOnly);
pdbApplicatorOptions.setApplyPublicSymbolsOnly(applyPublicSymbolsOnly);
pdbApplicatorOptions.setApplyCodeScopeBlockComments(applyCodeScopeBlockComments);
pdbApplicatorOptions.setApplyInstructionLabels(applyInstructionLabels);
pdbApplicatorOptions.setRemapAddressUsingExistingPublicMangledSymbols(
remapAddressUsingExistingPublicMangledSymbols);
pdbApplicatorOptions.setAllowDemotePrimaryMangledSymbol(allowDemotePrimaryMangledSymbol);
pdbApplicatorOptions.setApplyFunctionVariables(applyFunctionVariables);
pdbApplicatorOptions.setCompositeLayoutMode(compositeLayoutMode);
}
private boolean confirmDirectory(File path) {
return path.isDirectory();
}
private boolean confirmFile(File path) {
return path.isFile();
}
}

View file

@ -75,6 +75,7 @@ public class PdbAnalyzer extends AbstractAnalyzer {
if (pdb == null) { if (pdb == null) {
return false; return false;
} }
Msg.info(this, "Using PDB: " + pdb.getAbsolutePath());
AutoAnalysisManager mgr = AutoAnalysisManager.getAnalysisManager(program); AutoAnalysisManager mgr = AutoAnalysisManager.getAnalysisManager(program);
return parsePdb(pdb, program, mgr, monitor, log); return parsePdb(pdb, program, mgr, monitor, log);

View file

@ -35,7 +35,7 @@ import ghidra.util.task.TaskMonitor;
* Container members are characterized by a null data-type name, zero length, and will be * Container members are characterized by a null data-type name, zero length, and will be
* identified as either a structure or union. * identified as either a structure or union.
*/ */
class DefaultCompositeMember extends CompositeMember { public class DefaultCompositeMember extends CompositeMember {
private static int MAX_CONSTRUCTION_DEPTH = 20; private static int MAX_CONSTRUCTION_DEPTH = 20;
@ -1150,7 +1150,7 @@ class DefaultCompositeMember extends CompositeMember {
* @return true if members successfully added to composite * @return true if members successfully added to composite
* @throws CancelledException if monitor is cancelled * @throws CancelledException if monitor is cancelled
*/ */
static boolean applyDataTypeMembers(Composite composite, boolean isClass, public static boolean applyDataTypeMembers(Composite composite, boolean isClass,
int preferredCompositeSize, List<? extends PdbMember> members, int preferredCompositeSize, List<? extends PdbMember> members,
Consumer<String> errorConsumer, TaskMonitor monitor) throws CancelledException { Consumer<String> errorConsumer, TaskMonitor monitor) throws CancelledException {

View file

@ -22,7 +22,7 @@ import ghidra.util.exception.AssertException;
* <code>PdbBitField</code> provides ability to hang onto bitfield as a datatype. * <code>PdbBitField</code> provides ability to hang onto bitfield as a datatype.
* This will be transformed to a normal BitFieldDataType when cloned. * This will be transformed to a normal BitFieldDataType when cloned.
*/ */
class PdbBitField extends BitFieldDataType { public class PdbBitField extends BitFieldDataType {
private int bitOffsetWithinBaseType; private int bitOffsetWithinBaseType;

View file

@ -21,7 +21,7 @@ import ghidra.util.exception.CancelledException;
* <code>PdbMember</code> convey PDB member information used for datatype * <code>PdbMember</code> convey PDB member information used for datatype
* reconstruction. * reconstruction.
*/ */
abstract class PdbMember { public abstract class PdbMember {
final String memberName; final String memberName;
final String memberDataTypeName; final String memberDataTypeName;

View file

@ -28,6 +28,7 @@ import ghidra.app.util.NamespaceUtils;
import ghidra.app.util.SymbolPath; import ghidra.app.util.SymbolPath;
import ghidra.app.util.importer.LibrarySearchPathManager; import ghidra.app.util.importer.LibrarySearchPathManager;
import ghidra.app.util.importer.MessageLog; import ghidra.app.util.importer.MessageLog;
import ghidra.app.util.pdb.PdbProgramAttributes;
import ghidra.framework.*; import ghidra.framework.*;
import ghidra.framework.options.Options; import ghidra.framework.options.Options;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
@ -739,6 +740,7 @@ public class PdbParser {
EnumDataType createEnum(String name, int length) { EnumDataType createEnum(String name, int length) {
SymbolPath path = new SymbolPath(name); SymbolPath path = new SymbolPath(name);
// Ghidra does not like size of zero.
length = Integer.max(length, 1); length = Integer.max(length, 1);
return new EnumDataType(getCategory(path.getParent(), true), path.getName(), length, return new EnumDataType(getCategory(path.getParent(), true), path.getName(), length,
dataMgr); dataMgr);

View file

@ -40,7 +40,7 @@ public class WrappedDataType {
* @param isNoType if true wrapped type corresponds to NoType as * @param isNoType if true wrapped type corresponds to NoType as
* used by PDB forced to have a size of 1-byte. * used by PDB forced to have a size of 1-byte.
*/ */
protected WrappedDataType(DataType dataType, boolean isZeroLengthArray, boolean isNoType) { public WrappedDataType(DataType dataType, boolean isZeroLengthArray, boolean isNoType) {
this.dataType = dataType; this.dataType = dataType;
this.isZeroLengthArray = isZeroLengthArray; this.isZeroLengthArray = isZeroLengthArray;
this.isNoType = isNoType; this.isNoType = isNoType;

View file

@ -41,8 +41,6 @@ public class PdbReaderOptions extends Exception {
private Charset oneByteCharset; private Charset oneByteCharset;
private Charset twoByteCharset; private Charset twoByteCharset;
private boolean debug;
/** /**
* Constructor. * Constructor.
*/ */
@ -113,6 +111,14 @@ public class PdbReaderOptions extends Exception {
return twoByteCharsetName; return twoByteCharsetName;
} }
/**
* Returns the name of the Wchar Charset in use for PDB processing.
* @return the name of the Wchar Charset.
*/
public String getWideCharCharsetName() {
return twoByteCharsetName;
}
/** /**
* Returns the one-byte Charset in use for PDB processing. * Returns the one-byte Charset in use for PDB processing.
* @return the Charset. * @return the Charset.
@ -130,21 +136,11 @@ public class PdbReaderOptions extends Exception {
} }
/** /**
* Enable/disable developmental debug. * Returns the Wchar Charset in use for PDB processing.
* @param debug {@code true} to turn debug on; default is {@code false}. * @return the Wchar Charset.
* @return this, so options can be daisy-chained.
*/ */
public PdbReaderOptions setDebug(boolean debug) { public Charset getWideCharCharset() {
this.debug = debug; return twoByteCharset;
return this;
}
/**
* Returns true if debug is "on."
* @return {@code true} if debug is "on."
*/
public boolean isDebug() {
return debug;
} }
} }

View file

@ -18,7 +18,6 @@ package ghidra.app.util.bin.format.pdb2.pdbreader;
import java.util.Objects; import java.util.Objects;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.*; import ghidra.app.util.bin.format.pdb2.pdbreader.type.*;
import ghidra.util.exception.AssertException;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
/** /**

View file

@ -23,7 +23,8 @@ import ghidra.app.util.bin.format.pdb2.pdbreader.*;
* Note: we do not necessarily understand each of these symbol type classes. Refer to the * Note: we do not necessarily understand each of these symbol type classes. Refer to the
* base class for more information. * base class for more information.
*/ */
public class SeparatedCodeFromCompilerSupportMsSymbol extends AbstractMsSymbol { public class SeparatedCodeFromCompilerSupportMsSymbol extends AbstractMsSymbol
implements AddressMsSymbol {
public static final int PDB_ID = 0x1132; public static final int PDB_ID = 0x1132;
@ -78,6 +79,72 @@ public class SeparatedCodeFromCompilerSupportMsSymbol extends AbstractMsSymbol {
return "SEPCODE"; return "SEPCODE";
} }
/**
* Returns the parent pointer.
* @return Parent pointer.
*/
public long getParentPointer() {
return parentPointer;
}
/**
* Returns the end pointer.
* @return End pointer.
*/
public long getEndPointer() {
return endPointer;
}
/**
* Returns the block length.
* @return Block length.
*/
public long getBlockLength() {
return blockLength;
}
/**
* Returns the indication of if is lexical scope.
* @return {@code true} if is lexical scope.
*/
public boolean isLexicalScope() {
return isLexicalScope;
}
/**
* Returns the indication of if returns to parent.
* @return {@code true} if returns to parent.
*/
public boolean returnsToParent() {
return returnsToParent;
}
@Override
public long getOffset() {
return offset;
}
/**
* Returns the parent offset.
* @return Parent offset.
*/
public long getOffsetParent() {
return offsetParent;
}
@Override
public int getSegment() {
return section;
}
/**
* Returns the parent segment.
* @return Parent segment.
*/
public long getSegmentParent() {
return sectionParent;
}
/** /**
* Internal method that breaks out the flag values from the aggregate integral type. * Internal method that breaks out the flag values from the aggregate integral type.
* @param flagsIn {@code long} containing unsigned int value. * @param flagsIn {@code long} containing unsigned int value.

View file

@ -0,0 +1,211 @@
/* ###
* 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;
import java.util.*;
import ghidra.app.util.SymbolPath;
import ghidra.app.util.bin.format.pdb2.pdbreader.AbstractDatabaseInterface;
import ghidra.program.model.data.Category;
import ghidra.program.model.data.CategoryPath;
/**
* Sets up PDB base {@link CategoryPath} information for the PDB file and provides
* {@link CategoryPath CategoryPaths} based on {@link SymbolPath SymbolPaths}, name lists, and
* qualified namespace strings. Also maintains the state of the active Module {@link CategoryPath}
* while parsing a PDB and the count (state) of anonymous functions for their creation within
* the particular PDB's {@link Category}.
*/
public class PdbCategoryUtils {
private CategoryPath pdbRootCategory;
private CategoryPath pdbUncategorizedCategory;
private CategoryPath anonymousFunctionsCategory;
private CategoryPath anonymousTypesCategory;
private CategoryPath baseModuleTypedefsCategory;
private List<CategoryPath> typedefCategories = new ArrayList<>();
private int anonymousFunctionCount;
//==============================================================================================
// NOTE: a TODO could be to add optional GUID and AGE values. This could be as sub-categories
// or combined with the file name. This could be helpful when allowing multiple PDBs to be
// loaded (at least their data types).
/**
* Constructor
* @param pdbCategoryName pathname of the PDB file that this category represents.
* @param moduleNames module names
*/
public PdbCategoryUtils(String pdbCategoryName, List<String> moduleNames) {
Objects.requireNonNull(pdbCategoryName, "pdbCategoryName cannot be null");
pdbRootCategory = new CategoryPath(CategoryPath.ROOT, pdbCategoryName);
pdbUncategorizedCategory = new CategoryPath(pdbRootCategory, "_UNCATEGORIZED_");
setTypedefCategoryPaths(moduleNames);
anonymousFunctionsCategory = new CategoryPath(pdbRootCategory, "!_anon_funcs_");
anonymousFunctionCount = 0;
anonymousTypesCategory = new CategoryPath(pdbRootCategory, "!_anon_types_");
}
/**
* Get root CategoryPath for the PDB.
* @return the root CategoryPath.
*/
public CategoryPath getRootCategoryPath() {
return pdbRootCategory;
}
/**
* Get uncategorized CategoryPath for the PDB.
* @return the uncategorized CategoryPath.
*/
public CategoryPath getUnCategorizedCategoryPath() {
return pdbUncategorizedCategory;
}
private void setTypedefCategoryPaths(List<String> moduleNames) {
baseModuleTypedefsCategory = new CategoryPath(pdbRootCategory, "!_module_typedefs_");
// non-module typedefs go with all other global types, not in
// baseModuleTypedefsCategory.
typedefCategories.add(pdbRootCategory);
for (String name : moduleNames) {
CategoryPath categoryPath = (name == null) ? baseModuleTypedefsCategory
: new CategoryPath(baseModuleTypedefsCategory, name);
typedefCategories.add(categoryPath);
}
}
// /**
// * Get the name with any namespace stripped.
// * @param name Name with optional namespace prefix.
// * @return Name without namespace prefix.
// */
// public String stripNamespace(String name) {
// int index = name.lastIndexOf(Namespace.DELIMITER);
// if (index <= 0) {
// return name;
// }
// return name.substring(index + Namespace.DELIMITER.length());
// }
//
// /**
// * Get the name with any namespace stripped.
// * @param name Name with optional namespace prefix.
// * @return Name without namespace prefix.
// */
// public String stripNamespaceBetter(String name) {
// List<String> names = SymbolPathParser.parse(name);
// return names.get(names.size() - 1);
// }
//
/**
* Get the {@link CategoryPath} associated with the {@link SymbolPath} specified, rooting
* it either at the PDB Category.
* @param symbolPath Symbol path to be used to create the CategoryPath. Null represents global
* namespace.
* @return {@link CategoryPath} created for the input.
*/
public CategoryPath getCategory(SymbolPath symbolPath) {
CategoryPath category = pdbRootCategory;
if (symbolPath == null) { // global namespace
return category;
}
return recurseGetCategoryPath(category, symbolPath);
}
/**
* Returns the {@link CategoryPath} for a typedef with the give {@link SymbolPath} and
* module number; 1 <= moduleNumber <= {@link AbstractDatabaseInterface#getNumModules()},
* except that modeleNumber of 0 represents publics/globals.
* @param moduleNumber module number
* @param symbolPath SymbolPath of the symbol
* @return the CategoryPath
*/
public CategoryPath getTypedefsCategory(int moduleNumber, SymbolPath symbolPath) {
CategoryPath category = null;
if (moduleNumber >= 0 && moduleNumber < typedefCategories.size()) {
category = typedefCategories.get(moduleNumber);
}
if (category == null) {
// non-module typedefs go with all other global types, not in
// baseModuleTypedefsCategory.
category = pdbRootCategory;
}
if (symbolPath == null) { // global namespace
return category;
}
return recurseGetCategoryPath(category, symbolPath);
}
/**
* Recursion method used by {@link #getCategory(SymbolPath)} and
* {@link #getTypedefsCategory(int, SymbolPath)}. Returns a
* new {@link CategoryPath} for the
* @param category the {@ink CategoryPath} on which to build
* @param symbolPath the current {@link SymbolPath} from which the current name is pulled.
* @return the new {@link CategoryPath} for the recursion level
*/
private CategoryPath recurseGetCategoryPath(CategoryPath category, SymbolPath symbolPath) {
SymbolPath parent = symbolPath.getParent();
if (parent != null) {
category = recurseGetCategoryPath(category, parent);
}
return new CategoryPath(category, symbolPath.getName());
}
/**
* Returns the {@link CategoryPath} for Anonymous Functions Category for the PDB.
* @return the {@link CategoryPath}
*/
public CategoryPath getAnonymousFunctionsCategory() {
return anonymousFunctionsCategory;
}
/**
* Returns the {@link CategoryPath} for Anonymous Types Category for the PDB.
* @return the {@link CategoryPath}
*/
public CategoryPath getAnonymousTypesCategory() {
return anonymousTypesCategory;
}
/**
* Returns the name of what should be the next Anonymous Function (based on the count of
* the number of anonymous functions) so that there is a unique name for the function.
* @return the name for the next anonymous function.
*/
public String getNextAnonymousFunctionName() {
return String.format("_func_%08X", anonymousFunctionCount);
}
/**
* Updates the count of the anonymous functions. This is a separate call from
* {@link #getNextAnonymousFunctionName()} because the count should only be updated after
* the previous anonymous function has been successfully created/stored.
*/
public void incrementNextAnonymousFunctionName() {
anonymousFunctionCount++;
}
}

View file

@ -0,0 +1,671 @@
/* ###
* 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;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.*;
import docking.widgets.OptionDialog;
import ghidra.app.util.bin.format.pdb.PdbParserConstants;
import ghidra.app.util.bin.format.pdb2.pdbreader.*;
import ghidra.app.util.datatype.microsoft.GUID;
import ghidra.app.util.importer.LibrarySearchPathManager;
import ghidra.app.util.importer.MessageLog;
import ghidra.framework.OperatingSystem;
import ghidra.framework.Platform;
import ghidra.program.model.listing.Program;
import ghidra.util.Msg;
import ghidra.util.SystemUtilities;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
/**
* Searches for and presents PDB path information if order specified by
* <a href="https://docs.microsoft.com/en-us/windows/desktop/debug/symbol-paths">
* Symbol Paths</a>.
* <P>
* Gist is: TODO.
* <PRE>
* Our design:
* Looking for files with extension .pdb or .PDB using an ordered set of paths and resources.
*
* TODO:
* Option to look exclusively for the above, exclusively for something else, or inclusive of
* other extensions with a preference order. So if we want to inclusively look for .pdb
* and .pdb.xml with a preference of .pdb, and if we have a path search order of path A,
* then path B, the question is whether we would choose a .pdb file in path B before a .pdb.xml
* in path A. Same question for other extensions such as .dbg.
*
* Could do similar to SymSetSearchPath() with semicolon-separated paths.
*
* Could have an configuration list that gives the search order for all resources to be searched.
* For instance: path of the loaded executable, path specified in header of loaded executable,
* local symbol paths, symbol servers at specified URLs.
* -> Could allow any of these to not be active; could require optional user interaction ("are
* you sure") for the path specified in the header of the loaded executable, option of read
* and/or write to local symbol cache.
*
* Depending on whether is or used to be supported by MSFT, allow (with option?) the 2-tier
* symbol server directory structure.
*
* Should we allow different or use different structures for local paths vs. symbol server paths?
* For instance, the extension-tier (if *.dll, search an appropriate set of paths that have a
* "dll" component).
*
* </PRE>
*/
public class PdbLocatorSelector {
public static final File SPECIAL_PDB_LOCATION = new File("C:/WINDOWS/Symbols");
public static final boolean onWindows =
(Platform.CURRENT_PLATFORM.getOperatingSystem() == OperatingSystem.WINDOWS);
private File symbolsRepositoryPath;
/**
* Only holds identifies in PDBs up until a matching one was found--nothing beyond that.
*/
private Map<String, PdbIdentifiers> identifiersByFilePath = new HashMap<>();
public PdbLocatorSelector(File symbolsRepositoryPath) {
this.symbolsRepositoryPath = symbolsRepositoryPath;
}
//==============================================================================================
// TODO: Ideas for future.
// public SymbolPathSearcher(String message) {
// }
// Search order preference:
// EXTENSION_PREFERENCE_ORDER(pdb, pdb.xml, dbg)--at least one; no duplicates
// e.g., {PDB, PDB_XML, DBG} means *.pdb before *.pdb.xml before *.dbg
// PATH_BEFORE_EXTENSION or EXTENSION_BEFORE_PATH
// e.g.,
// PATH_BEFORE_EXTENSION means: (assuming EXTENION_PREFERENCE_ORDER is {PDB, PDB_XML}),
// at first path in list look for *.pdb, then *.pdb.xml, then if failed to
// find at the first path, try the same tWO ordered searches at the second path in
// the path list, etc.
// EXTENSION_BEFORE_PATH means: (assuming EXTENION_PREFERENCE_ORDER is {PDB, PDB_XML}),
// at first path in list look for *.pdb, then search for *.pdb at the second path, etc.
// If after the last path a *.pdb is not found, then repeat with list of paths, now
// looking for *.pdb.xml
// Use a list of enums to specify search order?
// queryUser true if can interact with user (non-headless)
// need paths for symbol server vs. paths for local cache?
// specify filetype (pdb, pdb.xml, dbg, other?)
// upper/lower case conversion
// public void find(boolean includePeSpecifiedPdbPath, boolean queryUser) {
// }
//==============================================================================================
/*
* TODO 20190403: Need to consider documentation at
* <a href="https://docs.microsoft.com/en-us/windows/desktop/debug/symbol-paths">
* Symbol Paths</a>.
* <P>
* Says order:
* <li>C:\MySymbols
* <li>C:\MySymbols\ext, where ext is extension of binary (e.g., dll, exe, sys)
* <li>C:\MySymbols\symbols\ext
* <P>
*/
// 20190321 Thoughts for fixing this up:
// Processing logic:
//
// Tenet: Program may or may not specify the following about an associated PDB:
// - filename of PDB
// - version (magic: RSDS)
// - signature
// - age
// - GUID (probably only in later versions)
//
// Tenet: PDB files may or may not specify the following about themselves:
// - version (date field indicating PDB version; also versioning dates of subcomponents)
// - signature
// - age
// - GUID (only in later versions)
//
// Tenet: Can be headed (GUI) or headless processing
//
// Logic Basics:
// - Need at least the filename.
// - No user interaction if headless.
//
// Use cases:
// - Given Program PDB info, search/find matching PDB, and apply all possible; else fail
// - Given Program PDB info, if matching PDB not found, but similar exist, present
// all possibles to user for choice or cancel. Choice would include the filename
// location along with the basic information pieces indicated above. Option to
// apply data types only (to a DataTypeManager) or to additionally apply symbols.
// (Could dig into each PDB for possible compile information, but requires full
// parsing.) Future may allow for a way to bring in symbol information into a
// temporary space that could allow the user to selectively choose symbols to apply
// to Program locations.
// - Given Program PDB info, if headless and exact match not found, option flag to fail
// with no information or to fail with the filenames and associated basic info.
// - Regardless of PDB info, if headless use "force" option with a single filename,
// which will force parsing and application of PDB file. (No option to force
// partially apply any parts of the PDB--think on this more.)
// Additional use cases:
// - Any PDB file can be loaded for general perusing (maybe).
// - Multiple PDB files can be loaded at any given time.
// - Multiple PDB files can have been loaded and had their data type processed and put
// into data type Categories labeled with the PDB information (might need GUID
// and other attributes used to help uniquely identify each (perhaps part of the
// Category name)). Perhaps the symbols for each PDB are available--perhaps just
// have each PDB available for further symbol/data type application.
//
// Today's Logic:
public String findPdb(Program program, PdbProgramAttributes programAttributes,
boolean userInteractive, boolean includePeSpecifiedPdbPath, TaskMonitor monitor,
MessageLog log, String messagePrefix) throws CancelledException {
List<String> orderedListOfExistingFileNames =
lookForPdb(program, includePeSpecifiedPdbPath, log, messagePrefix);
if (orderedListOfExistingFileNames.isEmpty()) {
String message = "Cannot find candidate PDB files.";
log.appendMsg(messagePrefix, message);
return null;
}
String pdbFilename = null;
try {
pdbFilename = choosePdb(orderedListOfExistingFileNames, programAttributes, monitor, log,
messagePrefix);
}
catch (PdbException e) {
String message = "Could not find appropriate PDB file. Detailed issues may follow:\n" +
e.toString();
log.appendMsg(messagePrefix, message);
return null;
}
return pdbFilename;
}
//==============================================================================================
/**
* @param program Program under analysis.
* @param includePeSpecifiedPdbPath {@code true} if looking for PDB in PE-Header-Specified
* path location, which may be unsafe security-wise.
* @param log MessageLog to report to.
* @param messagePrefix prefix string for any error message written to the log
* @return Ordered List<String> of potential filenames
*/
private List<String> lookForPdb(Program program, boolean includePeSpecifiedPdbPath,
MessageLog log, String messagePrefix) {
String message = "";
try {
List<String> orderedListOfExistingFileNames = findPDB(new PdbProgramAttributes(program),
includePeSpecifiedPdbPath, symbolsRepositoryPath.getAbsolutePath());
if (orderedListOfExistingFileNames.isEmpty()) {
String pdbName = program.getOptions(Program.PROGRAM_INFO).getString(
PdbParserConstants.PDB_FILE, (String) null);
if (pdbName == null) {
message = "Program has no associated PDB file.";
}
else {
message = "Unable to locate PDB file \"" + pdbName + "\" with matching GUID.";
}
if (SystemUtilities.isInHeadlessMode()) {
message += "\n Use a script to set the PDB Symbol Location. I.e.,\n" +
" setAnalysisOption(currentProgram, \"PDB.Symbol Repository Path\", " +
"\"/path/to/pdb/folder\");\n" +
" This must be done using a pre-script (prior to analysis).";
}
else {
message +=
"\n You can manually load PDBs using the \"File->Load PDB File...\" action.";
if (pdbName != null) {
message += "\n Alternatively, you may set the PDB Symbol Repository Path" +
"\n using \"Edit->Options for [program]\" prior to analysis.";
}
}
}
return orderedListOfExistingFileNames;
}
catch (PdbException pe) {
message += pe.getMessage();
}
finally {
if (message.length() > 0) {
log.appendMsg(messagePrefix, message);
log.setStatus(message);
}
}
return null;
}
//==============================================================================================
private String choosePdb(List<String> orderedListOfExistingFileNames,
PdbProgramAttributes programAttributes, TaskMonitor monitor, MessageLog log,
String messagePrefix) throws PdbException, CancelledException {
String choice = findMatchingPdb(orderedListOfExistingFileNames, programAttributes, monitor);
if (choice == null) {
//choice = userSelectPdb(programAttributes, messagePrefix, log, monitor);
String message = getNoMatchMessage(programAttributes, monitor);
log.appendMsg(message);
}
return choice;
}
//==============================================================================================
private String findMatchingPdb(List<String> orderedListOfExistingFileNames,
PdbProgramAttributes programAttributes, TaskMonitor monitor)
throws PdbException, CancelledException {
String message = "";
for (String filepath : orderedListOfExistingFileNames) {
monitor.checkCanceled();
monitor.setMessage("Attempting to find/process PDB file " + filepath + "...");
try (AbstractPdb tryPdb = PdbParser.parse(filepath, new PdbReaderOptions(), monitor)) {
PdbIdentifiers identifiers = tryPdb.getIdentifiers();
identifiersByFilePath.put(filepath, identifiers);
if (verifyPdbSignature(programAttributes, identifiers)) {
monitor.setMessage("Parsing validated PDB file " + filepath + ".");
return filepath;
}
}
catch (Exception e) {
// Ignore FileNotFoundException.
if (!e.getClass().equals(FileNotFoundException.class)) {
// Noting any exceptions. Will only report if we have not successfully
// found a matching PDB without an exception being thrown for it.
message += "Exception for attempted PDB " + filepath + ": " + e.toString();
}
}
}
if (!message.isEmpty()) {
throw new PdbException(message);
}
return null;
}
//==============================================================================================
@SuppressWarnings("unused") // for method not being called.
private String userSelectPdb(PdbProgramAttributes programAttributes, String messagePrefix,
MessageLog log, TaskMonitor monitor) throws CancelledException {
String[] fileNames = new String[identifiersByFilePath.size()];
String[] fileIdents = new String[identifiersByFilePath.size()];
int i = 0;
for (Map.Entry<String, PdbIdentifiers> entry : identifiersByFilePath.entrySet()) {
monitor.checkCanceled();
fileNames[i] = entry.getKey();
fileIdents[i++] = formatPdbIdentifiers(entry.getKey(), entry.getValue()).toString();
}
String message = "Could not match program with PDB.\n";
if (identifiersByFilePath.size() != 0) {
message += "PDB File Options (Name Signature Age GUID):\n";
for (String fileIdent : fileIdents) {
message += fileIdent + "\n";
}
}
if (SystemUtilities.isInHeadlessMode()) {
message = "In Headless mode... skipping PDB processing.\n";
log.appendMsg(messagePrefix, message);
log.setStatus(message);
return null;
}
String header =
"Choose PDB or Cancel (for: " + formatPdbIdentifiers(programAttributes) + ")";
String userChoice = OptionDialog.showInputChoiceDialog(null, header, "Choose", fileIdents,
fileIdents[0], OptionDialog.CANCEL_OPTION);
if (userChoice == null) {
return null;
}
for (i = 0; i < fileIdents.length; i++) {
if (userChoice.contentEquals(fileIdents[i])) {
message = "User PDB Choice: " + userChoice;
Msg.info(this, message); // Info for console.
return fileNames[i];
}
}
return null;
}
//==============================================================================================
private String getNoMatchMessage(PdbProgramAttributes programAttributes, TaskMonitor monitor)
throws CancelledException {
StringBuilder builder = new StringBuilder();
builder.append("ERROR: Could not run PDB Analyzer because a matched PDB was not found.\n");
builder.append("PDB specification from PE header:\n");
builder.append(formatPdbIdentifiers(programAttributes));
builder.append("Discovered non-matches:\n");
for (Map.Entry<String, PdbIdentifiers> entry : identifiersByFilePath.entrySet()) {
monitor.checkCanceled();
builder.append(formatPdbIdentifiers(entry.getKey(), entry.getValue()));
}
return builder.toString();
}
private StringBuilder formatPdbIdentifiers(PdbProgramAttributes attributes) {
Integer signature = (attributes.getPdbSignature() == null) ? null
: Integer.valueOf(attributes.getPdbSignature());
return formatPdbIdentifiers(attributes.getPdbFile(), signature,
Integer.valueOf(attributes.getPdbAge()), attributes.getPdbGuid());
}
private StringBuilder formatPdbIdentifiers(String file, PdbIdentifiers identifiers) {
return formatPdbIdentifiers(file, identifiers.getSignature(), identifiers.getAge(),
identifiers.getGuid().toString());
}
private StringBuilder formatPdbIdentifiers(String file, Integer signature, int age,
String guidString) {
StringBuilder builder = new StringBuilder();
builder.append(" Location: ").append(file);
if (signature != null) {
builder.append(String.format("; Signature: 0X%08X", signature));
}
builder.append("; Age: ");
builder.append(age);
if (guidString != null) {
builder.append("; GUID: ");
builder.append(guidString);
}
builder.append('\n');
return builder;
}
//==============================================================================================
/**
* Find a matching PDB file using attributes associated with the program. User can specify the
* type of file to search from (.pdb or .pdb.xml).
*
* @param pdbAttributes PDB attributes associated with the program.
* @param includePeSpecifiedPdbPath {@code true} if looking for PDB in PE-Header-Specified
* path location, which may be unsafe security-wise.
* @param symbolsRepositoryPathIn Location of the local symbols repository (can be null).
* @return matching PDB file (or null, if not found).
* @throws PdbException if there was a problem with the PDB attributes.
*/
private List<String> findPDB(PdbProgramAttributes pdbAttributes,
boolean includePeSpecifiedPdbPath, String symbolsRepositoryPathIn) throws PdbException {
// Store potential names of PDB files and potential locations of those files,
// so that all possible combinations can be searched.
// LinkedHashSet is used when we need to preserve order
Set<String> guidSubdirPaths = new HashSet<>();
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.");
}
List<String> potentialPdbNames = pdbAttributes.getPotentialPdbFilenames();
for (String potentialName : potentialPdbNames) {
guidSubdirPaths.add(File.separator + potentialName + File.separator + guidAgeString);
}
return checkPathsForPdb(symbolsRepositoryPathIn, guidSubdirPaths, potentialPdbNames,
pdbAttributes, includePeSpecifiedPdbPath);
}
//==============================================================================================
/**
* Check potential paths in a specific order. If the symbolsRepositoryPath parameter is
* supplied and the directory exists, that directory will be searched first for the
* matching PDB file.
*
* If the file type is supplied, then only that file type will be searched for. Otherwise,
* the search process depends on the current operating system that Ghidra is running from:
*
* - Windows: look in the symbolsRepositoryPath for a matching .pdb file. If one does not
* exist, look for a .pdb.xml file in symbolsRepositoryPath. If not found, then
* search for a matching .pdb file, then .pdb.xml file, in other directories.
* - non-Windows: look in the symbolsRepositoryPath for a matching .pdb.xml file. If one does
* not exist, look for a .pdb file. If a .pdb file is found, return an error saying
* that it was found, but could not be processed. If no matches found in
* symbolsRepositoryPath, then look for .pdb.xml file, then .pdb.xml file in other
* directories.
*
* @param symbolsRepositoryPath location of the local symbols repository (can be null)
* @param guidSubdirPaths subdirectory paths (that include the PDB's GUID) that may contain
* 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
* @return matching PDB file, if found (else null)
*/
private static List<String> checkPathsForPdb(String symbolsRepositoryPath,
Set<String> guidSubdirPaths, List<String> potentialPdbNames,
PdbProgramAttributes pdbAttributes, boolean includePeSpecifiedPdbPath) {
Set<File> symbolsRepoPaths =
getSymbolsRepositoryPaths(symbolsRepositoryPath, guidSubdirPaths);
Set<File> predefinedPaths =
getPredefinedPaths(guidSubdirPaths, pdbAttributes, includePeSpecifiedPdbPath);
// Start by searching in symbolsRepositoryPath, if available.
List<String> orderedListOfExistingFileNames = new ArrayList<>();
if (symbolsRepositoryPath != null) {
orderedListOfExistingFileNames.addAll(
checkSpecificPathsForPdb(symbolsRepoPaths, potentialPdbNames));
}
orderedListOfExistingFileNames.addAll(
checkSpecificPathsForPdb(predefinedPaths, potentialPdbNames));
return orderedListOfExistingFileNames;
}
//==============================================================================================
private static List<String> checkSpecificPathsForPdb(Set<File> paths,
List<String> potentialPdbNames) {
List<String> orderedListOfExistingFileNames = checkForPDBorXML(paths, potentialPdbNames);
return orderedListOfExistingFileNames;
}
//==============================================================================================
private static Set<File> getSymbolsRepositoryPaths(String symbolsRepositoryPath,
Set<String> guidSubdirPaths) {
Set<File> symbolsRepoPaths = new LinkedHashSet<>();
// Collect sub-directories of the symbol repository that exist
File symRepoFile;
if (symbolsRepositoryPath != null &&
(symRepoFile = new File(symbolsRepositoryPath)).isDirectory()) {
for (String guidSubdir : guidSubdirPaths) {
File testDir = new File(symRepoFile, guidSubdir);
if (testDir.isDirectory()) {
symbolsRepoPaths.add(testDir);
}
}
// Check outer folder last
symbolsRepoPaths.add(symRepoFile);
}
return symbolsRepoPaths;
}
//==============================================================================================
// Get list of "paths we know about" to search for PDBs
private static Set<File> getPredefinedPaths(Set<String> guidSubdirPaths,
PdbProgramAttributes pdbAttributes, boolean includePeSpecifiedPdbPath) {
Set<File> predefinedPaths = new LinkedHashSet<>();
getPathsFromAttributes(pdbAttributes, includePeSpecifiedPdbPath, predefinedPaths);
getWindowsPaths(guidSubdirPaths, predefinedPaths);
getLibraryPaths(guidSubdirPaths, predefinedPaths);
return predefinedPaths;
}
//==============================================================================================
private static void getLibraryPaths(Set<String> guidSubdirPaths, Set<File> predefinedPaths) {
String[] libraryPaths = LibrarySearchPathManager.getLibraryPaths();
File libFile, subDir;
for (String path : libraryPaths) {
if ((libFile = new File(path)).isDirectory()) {
predefinedPaths.add(libFile);
// Check alternate locations
for (String guidSubdir : guidSubdirPaths) {
if ((subDir = new File(path, guidSubdir)).isDirectory()) {
predefinedPaths.add(subDir);
}
}
}
}
}
//==============================================================================================
/**
* TODO 20190403: Need to consider documentation at
* <a href="https://docs.microsoft.com/en-us/windows/desktop/debug/symbol-paths">
* Symbol Paths</a>.
* <P>
* Says order:
* <li>C:\MySymbols
* <li>C:\MySymbols\ext, where ext is extension of binary (e.g., dll, exe, sys)
* <li>C:\MySymbols\symbols\ext
* <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);
// Check alternate locations
String specialPdbPath = SPECIAL_PDB_LOCATION.getAbsolutePath();
for (String guidSubdir : guidSubdirPaths) {
File testDir = new File(specialPdbPath + guidSubdir);
if (testDir.isDirectory()) {
predefinedPaths.add(testDir);
}
}
}
}
//==============================================================================================
private static void getPathsFromAttributes(PdbProgramAttributes pdbAttributes,
boolean includePeSpecifiedPdbPath, Set<File> predefinedPaths) {
if (pdbAttributes != null) {
String currentPath = pdbAttributes.getPdbFile();
if (currentPath != null && includePeSpecifiedPdbPath) {
File parentDir = new File(currentPath).getParentFile();
if (parentDir != null && parentDir.exists()) {
predefinedPaths.add(parentDir);
}
}
currentPath = pdbAttributes.getExecutablePath();
if (currentPath != null && !currentPath.equals("unknown")) {
File parentDir = new File(currentPath).getParentFile();
if (parentDir != null && parentDir.exists()) {
predefinedPaths.add(parentDir);
}
}
}
}
//==============================================================================================
/**
* Returns the first PDB-type file found. Assumes list of potentialPdbDirs is in the order
* in which the directories should be searched.
*
* @param potentialPdbDirs List<String> of paths
* @param potentialPdbNames List<String> of file names
* @return Ordered List<String> of potential filenames
*/
private static List<String> checkForPDBorXML(Set<File> potentialPdbDirs,
List<String> potentialPdbNames) {
List<String> orderedListOfExistingFileNames = new ArrayList<>();
File file;
for (File pdbPath : potentialPdbDirs) {
for (String filename : potentialPdbNames) {
file = new File(pdbPath, filename);
// Note: isFile() also checks for existence
if (file.isFile()) {
orderedListOfExistingFileNames.add(file.getAbsolutePath());
}
}
}
return orderedListOfExistingFileNames;
}
//==============================================================================================
private boolean verifyPdbSignature(PdbProgramAttributes programAttributes,
PdbIdentifiers identifiers) throws PdbException {
String attributesGuidString = programAttributes.getPdbGuid();
if (attributesGuidString == null) {
// TODO: note that PdbProgramAttributes does not seem to be getting this value from
// the program--getting it as a default value (e.g., from options or elsewhere).
// Should see if it is available in the program somewhere?
String attributesSignatureString = programAttributes.getPdbSignature();
if (attributesSignatureString == null) {
throw new PdbException("No PDB GUID or Signature in file.\n Cannot match.");
}
int attributesSignature = Integer.parseInt(attributesSignatureString);
if (attributesSignature != identifiers.getSignature()) {
return false; // no match
}
}
else {
GUID pdbGuid = identifiers.getGuid();
if (!attributesGuidString.equals(pdbGuid.toString())) {
return false; // no match
}
}
int attributesAge = Integer.parseInt(programAttributes.getPdbAge(), 16);
if (attributesAge != identifiers.getAge()) {
return false;
}
return true;
}
}

View file

@ -0,0 +1,101 @@
/* ###
* 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;
import java.util.ArrayList;
import java.util.List;
import ghidra.app.util.SymbolPath;
import ghidra.program.model.symbol.SymbolUtilities;
public class PdbNamespaceUtils {
/**
* Fixes {@link SymbolPath} name, eliminating invalid characters and making the terminal
* name of the namespace unique by the index number when necessary. For example,
* anonymous and unnamed components such as {@code <unnamed-tag>} and {@code <unnamed-type>}
* are fixed up. Example:
* <pre>
* {@code _SYSTEM_INFO::<unnamed-tag>}
* </pre>
* @param symbolPath the source {@link SymbolPath}
* @param index the index number used be used as part of a unique tag name.
* @return the resulting ({@link SymbolPath}
*/
public static SymbolPath getFixUpSymbolPathNameOnly(SymbolPath symbolPath, int index) {
symbolPath = symbolPath.replaceInvalidChars();
return new SymbolPath(symbolPath.getParent(), fixUnnamed(symbolPath.getName(), index));
}
/**
* Fixes {@link SymbolPath} name, eliminating invalid characters
* @param symbolPath the source {@link SymbolPath}
* @return the resulting ({@link SymbolPath}
*/
public static SymbolPath getFixUpSymbolPathNameOnly(SymbolPath symbolPath) {
symbolPath = symbolPath.replaceInvalidChars();
return symbolPath;
}
/**
* Fixes {@code <unnamed-tag>} and {@code <unnamed-type>} components of a namespace.
* <P>
* NOTE: This could be an issue when there are multiple unnamed items, such as in, for example:
* <pre>
* {@code _SYSTEM_INFO::<unnamed-tag>::<unnamed-tag>}
* </pre>
* @param symbolPath the source {@link SymbolPath}
* @param index the index number used be used as part of a unique tag name.
* @return the resulting ({@link SymbolPath}
*/
public static SymbolPath getFixUpSymbolPath(SymbolPath symbolPath, int index) {
symbolPath = symbolPath.replaceInvalidChars();
List<String> modList = new ArrayList<>();
for (String str : symbolPath.asList()) {
modList.add(SymbolUtilities.replaceInvalidChars(fixUnnamed(str, index), true));
}
return new SymbolPath(modList);
// return getFixUpSymbolPathRecurse(symbolPath, index);
}
// private static SymbolPath getFixUpSymbolPathRecurse(SymbolPath symbolPath, int index) {
// SymbolPath parent = symbolPath.getParent();
// if (parent != null) {
// parent = getFixUpSymbolPathRecurse(parent, index);
// }
// return new SymbolPath(parent, fixName(symbolPath.getName(), index));
// }
//
/**
* Fixes-up {@code <unnamed-tag>} or {@code <unnamed-type>} component of a name.
* @param name original name.
* @param index the index number used be used as part of a unique tag name.
* @return the resulting name.
*/
public static String fixUnnamed(String name, int index) {
if ("<unnamed-tag>".equals(name)) {
return String.format("<unnamed-tag_%08X>", index);
}
if ("<anonymous-tag>".equals(name)) {
return String.format("<anonymous-tag_%08X>", index);
}
if ("<unnamed-type>".equals(name)) {
return String.format("<unnamed-type_%08X>", index);
}
return name;
}
}

View file

@ -13,11 +13,11 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package ghidra.app.util.bin.format.pdb; package ghidra.app.util.pdb;
import java.util.*; import java.util.*;
import ghidra.app.util.bin.format.pdb.PdbParser.PdbFileType; import ghidra.app.util.bin.format.pdb.PdbParserConstants;
import ghidra.framework.options.Options; import ghidra.framework.options.Options;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
@ -37,6 +37,7 @@ public class PdbProgramAttributes {
private String guidAgeCombo; private String guidAgeCombo;
private String pdbFile; private String pdbFile;
private String pdbVersion;
private boolean pdbLoaded; private boolean pdbLoaded;
private boolean programAnalyzed; private boolean programAnalyzed;
private String executablePath; private String executablePath;
@ -53,6 +54,7 @@ public class PdbProgramAttributes {
programAnalyzed = propList.getBoolean(Program.ANALYZED, false); programAnalyzed = propList.getBoolean(Program.ANALYZED, false);
pdbSignature = propList.getString(PdbParserConstants.PDB_SIGNATURE, (String) null); pdbSignature = propList.getString(PdbParserConstants.PDB_SIGNATURE, (String) null);
pdbFile = propList.getString(PdbParserConstants.PDB_FILE, (String) null); pdbFile = propList.getString(PdbParserConstants.PDB_FILE, (String) null);
pdbVersion = propList.getString(PdbParserConstants.PDB_VERSION, (String) null);
executablePath = program.getExecutablePath(); executablePath = program.getExecutablePath();
@ -68,6 +70,7 @@ public class PdbProgramAttributes {
programAnalyzed = analyzed; programAnalyzed = analyzed;
pdbSignature = signature; pdbSignature = signature;
pdbFile = file; pdbFile = file;
pdbVersion = "RSDS"; // TODO: possibly receive this as argument.
executablePath = execPath; executablePath = execPath;
createGuidAgeString(); createGuidAgeString();
@ -89,6 +92,10 @@ public class PdbProgramAttributes {
return pdbFile; return pdbFile;
} }
public String getPdbVersion() {
return pdbVersion;
}
public boolean isPdbLoaded() { public boolean isPdbLoaded() {
return pdbLoaded; return pdbLoaded;
} }
@ -110,21 +117,22 @@ public class PdbProgramAttributes {
if (potentialPdbFilenames == null) { if (potentialPdbFilenames == null) {
// Want to preserve add order while only keeping unique entries // Want to preserve add order while only keeping unique entries
Set<String> setOfPotentialFilenames = new LinkedHashSet<String>(); Set<String> setOfPotentialFilenames = new LinkedHashSet<>();
if (pdbFile != null) { if (pdbFile != null) {
setOfPotentialFilenames.add(getFilename(pdbFile).toLowerCase()); setOfPotentialFilenames.add(getFilename(pdbFile).toLowerCase());
setOfPotentialFilenames.add(getFilename(pdbFile));
setOfPotentialFilenames.add(pdbFile); setOfPotentialFilenames.add(pdbFile);
} }
// getExecutablePath can return "unknown" // getExecutablePath can return "unknown"
if (!executablePath.equals("unknown")) { if (!executablePath.equals("unknown")) {
String executableFilename = getFilename(executablePath); String executableFilename = getFilename(executablePath);
setOfPotentialFilenames.add(getBinaryBasename(executableFilename).toLowerCase() + setOfPotentialFilenames.add(
PdbFileType.PDB.toString()); getBinaryBasename(executableFilename).toLowerCase() + ".pdb");
} }
potentialPdbFilenames = new ArrayList<String>(setOfPotentialFilenames); potentialPdbFilenames = new ArrayList<>(setOfPotentialFilenames);
} }
return potentialPdbFilenames; return potentialPdbFilenames;
@ -179,7 +187,7 @@ public class PdbProgramAttributes {
* @param fullPath from which to extract the filename * @param fullPath from which to extract the filename
* @return the name of the file specified by the fullPath * @return the name of the file specified by the fullPath
*/ */
private static String getFilename(String fullPath) { private String getFilename(String fullPath) {
// Remove any trailing slashes // Remove any trailing slashes
String editedPath = fullPath; String editedPath = fullPath;
editedPath = editedPath.replaceAll("[\\/]$", ""); editedPath = editedPath.replaceAll("[\\/]$", "");
@ -191,8 +199,7 @@ public class PdbProgramAttributes {
return editedPath; return editedPath;
} }
int indexToUse = int indexToUse = (lastIndexForwardSlash > lastIndexBackSlash) ? lastIndexForwardSlash
(lastIndexForwardSlash > lastIndexBackSlash) ? lastIndexForwardSlash
: lastIndexBackSlash; : lastIndexBackSlash;
return editedPath.substring(indexToUse + 1); return editedPath.substring(indexToUse + 1);

View file

@ -0,0 +1,111 @@
/* ###
* 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.app.util.SymbolPath;
import ghidra.app.util.SymbolPathParser;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.RecordNumber;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.*;
import ghidra.app.util.pdb.PdbNamespaceUtils;
import ghidra.program.model.data.DataType;
public abstract class AbstractComplexTypeApplier extends AbstractMsTypeApplier {
protected SymbolPath symbolPath;
protected SymbolPath fixedSymbolPath;
protected AbstractComplexTypeApplier definitionApplier = null;
protected AbstractComplexTypeApplier fwdRefApplier = null;
public static AbstractComplexTypeApplier getComplexApplier(PdbApplicator applicator,
RecordNumber recordNumber) throws PdbException {
return (AbstractComplexTypeApplier) applicator.getApplierSpec(recordNumber,
AbstractComplexTypeApplier.class);
}
/**
* Constructor for complex type applier.
* @param applicator {@link PdbApplicator} for which this class is working.
* @param msType {@link AbstractEnumMsType} to process.
* @throws IllegalArgumentException Upon invalid arguments.
*/
public AbstractComplexTypeApplier(PdbApplicator applicator, AbstractMsType msType)
throws IllegalArgumentException {
super(applicator, msType);
String fullPathName = msType.getName();
symbolPath = new SymbolPath(SymbolPathParser.parse(fullPathName));
}
public SymbolPath getSymbolPath() {
return symbolPath;
}
public boolean isForwardReference() {
return ((AbstractComplexMsType) msType).getMsProperty().isForwardReference();
}
public boolean isFinal() {
return ((AbstractComplexMsType) msType).getMsProperty().isSealed();
}
public void setFwdRefApplier(AbstractComplexTypeApplier fwdRefApplier) {
this.fwdRefApplier = fwdRefApplier;
}
<T extends AbstractComplexTypeApplier> T getFwdRefApplier(Class<T> typeClass) {
if (!typeClass.isInstance(fwdRefApplier)) {
return null;
}
return typeClass.cast(fwdRefApplier);
}
public void setDefinitionApplier(AbstractComplexTypeApplier definitionApplier) {
this.definitionApplier = definitionApplier;
}
<T extends AbstractComplexTypeApplier> T getDefinitionApplier(Class<T> typeClass) {
if (!typeClass.isInstance(definitionApplier)) {
return null;
}
return typeClass.cast(definitionApplier);
}
protected SymbolPath getFixedSymbolPath() { //return mine or my def's (and set mine)
if (fixedSymbolPath != null) {
return fixedSymbolPath;
}
if (definitionApplier != null && definitionApplier.getFixedSymbolPath() != null) {
fixedSymbolPath = definitionApplier.getFixedSymbolPath();
return fixedSymbolPath;
}
SymbolPath fixed = PdbNamespaceUtils.getFixUpSymbolPathNameOnly(symbolPath, index);
if (symbolPath.equals(fixed)) {
fixedSymbolPath = symbolPath;
}
else {
fixedSymbolPath = fixed;
}
return fixedSymbolPath;
}
DataType getDataTypeInternal() {
return dataType;
}
}

View file

@ -0,0 +1,229 @@
/* ###
* 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.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.RecordNumber;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.AbstractMsType;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.CallingConvention;
import ghidra.program.model.data.*;
import ghidra.util.exception.CancelledException;
/**
* Applier for certain function types.
*/
public abstract class AbstractFunctionTypeApplier extends AbstractMsTypeApplier {
private boolean isDeferred = false;
private FunctionDefinitionDataType functionDefinition;
private AbstractMsTypeApplier returnApplier;
ArgumentsListTypeApplier argsListApplier;
CallingConvention callingConvention;
boolean hasThisPointer;
/**
* Constructor for the applicator that applies a "function" type, transforming it into a
* Ghidra DataType.
* @param applicator {@link PdbApplicator} for which this class is working.
* @param msType {@link AbstractMsType} to processes
* @throws IllegalArgumentException Upon invalid arguments.
*/
public AbstractFunctionTypeApplier(PdbApplicator applicator, AbstractMsType msType)
throws IllegalArgumentException {
super(applicator, msType);
String funcName = applicator.getNextAnonymousFunctionName();
functionDefinition = new FunctionDefinitionDataType(
applicator.getAnonymousFunctionsCategory(), funcName, applicator.getDataTypeManager());
// Updating before trying to apply... if applyFunction fails, then this name will go
// unused for the most part, but we also will not get a conflict on the name.
applicator.incrementNextAnonymousFunctionName();
dataType = functionDefinition;
}
//==============================================================================================
public void setDeferred() {
isDeferred = true;
}
@Override
public boolean isDeferred() {
return isDeferred;
}
@Override
void deferredApply() throws PdbException, CancelledException {
if (isDeferred()) {
applyInternal();
}
}
//==============================================================================================
/**
* Returns the function definition being created by this applier.
* @return the function definition.
*/
public FunctionDefinitionDataType getFunctionDefinition() {
return functionDefinition;
}
@Override
DataType getCycleBreakType() {
if (dataType != null) {
return dataType;
}
return functionDefinition;
}
AbstractMsTypeApplier getReturnTypeApplier() {
return applicator.getTypeApplier(getReturnRecordNumber());
}
ArgumentsListTypeApplier getArgsListApplier() {
AbstractMsTypeApplier argsApplier = applicator.getTypeApplier(getArgListRecordNumber());
if (argsApplier instanceof ArgumentsListTypeApplier) {
return (ArgumentsListTypeApplier) applicator.getTypeApplier(getArgListRecordNumber());
}
return null;
}
protected abstract CallingConvention getCallingConvention();
protected abstract boolean hasThisPointer() throws CancelledException, PdbException;
protected abstract RecordNumber getReturnRecordNumber();
protected abstract RecordNumber getArgListRecordNumber();
/**
* Method to create the {@link DataType} based upon the type indices of the calling
* convention, return type, and arguments list.
* @param callingConventionParam Identification of the {@link AbstractMsType} record of the
* {@link CallingConvention}.
* @param hasThisPointerParam true if has a this pointer
* @return {@link DataType} created or null upon issue.
* @throws PdbException when unexpected function internals are found.
* @throws CancelledException Upon user cancellation
*/
protected DataType applyFunction(CallingConvention callingConventionParam,
boolean hasThisPointerParam) throws PdbException, CancelledException {
// String funcName = applicator.getCategoryUtils().getNextAnonymousFunctionName();
// FunctionDefinitionDataType functionDefinition = new FunctionDefinitionDataType(
// applicator.getCategoryUtils().getAnonymousFunctionsCategory(), funcName,
// applicator.getDataTypeManager());
this.callingConvention = callingConventionParam;
this.hasThisPointer = hasThisPointerParam;
returnApplier = getReturnTypeApplier();
argsListApplier = getArgsListApplier();
applyOrDeferForDependencies();
// applyInternal();
// 20190725 remove for second pass in applicator
// // TODO: what handler should we really use?
// DataType resolvedFunctionDefinition = applicator.resolve(functionDefinition);
//
// if (resolvedFunctionDefinition == null) {
// applicator.getLog().appendMsg("Function definition type not resolved for " + functionDefinition.getName());
// return null;
// }
// if (!(resolvedFunctionDefinition instanceof FunctionDefinition)) {
// // Error... can this happen?
// // Remove what was just created?
// applicator.getLog().appendMsg("Non-function resolved for " + functionDefinition.getName());
// return null;
// }
// // Only update if successful.
// applicator.getCategoryUtils().incrementNextAnonymousFunctionName();
// return resolvedFunctionDefinition;
return functionDefinition;
}
private void applyOrDeferForDependencies() throws CancelledException {
if (returnApplier.isDeferred()) {
applicator.addApplierDependency(this, returnApplier);
setDeferred();
}
if (argsListApplier != null) {
argsListApplier.checkForDependencies(this);
}
if (!isDeferred()) {
applyInternal();
}
}
public void applyInternal() throws CancelledException {
if (isApplied()) {
return;
}
if (!setReturnType()) {
return;
}
if (argsListApplier != null) {
argsListApplier.applyTo(this);
}
setCallingConvention(applicator, callingConvention, hasThisPointer);
setApplied();
}
private boolean setReturnType() {
DataType returnDataType = returnApplier.getDataType();
if (returnDataType == null) {
applicator.appendLogMsg("Return type is null in " + functionDefinition.getName());
return false;
}
functionDefinition.setReturnType(returnDataType);
return true;
}
private void setCallingConvention(PdbApplicator applicator, CallingConvention callingConvention,
boolean hasThisPointer) {
GenericCallingConvention convention;
if (hasThisPointer) {
convention = GenericCallingConvention.thiscall;
}
else {
// Since we are a member function, we will always assume a _thiscall...
// but how do we know it is not a atatic member function (no "this")?
switch (callingConvention) {
// TODO: figure all of these out.
case THISCALL: // "this" passed in register (we have not yet seen this)
convention = GenericCallingConvention.thiscall; // Is this correct if in reg?
break;
case NEAR_C: // we have seen this one
convention = GenericCallingConvention.cdecl;
break;
case NEAR_VECTOR: // we have seen this one
convention = GenericCallingConvention.vectorcall;
break;
default:
// applicator.getLog().appendMsg(
// "TODO: calling convention not implemented for value " + callingConventionVal +
// " in " + funcName);
//convention = GenericCallingConvention.cdecl;
convention = GenericCallingConvention.cdecl;
break;
}
}
functionDefinition.setGenericCallingConvention(convention);
}
}

View file

@ -0,0 +1,75 @@
/* ###
* 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.Objects;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.symbol.AbstractMsSymbol;
import ghidra.app.util.pdb.pdbapplicator.SymbolGroup.AbstractMsSymbolIterator;
import ghidra.util.exception.CancelledException;
public abstract class AbstractMsSymbolApplier {
protected PdbApplicator applicator;
protected AbstractMsSymbolIterator iter;
protected long currentOffset;
public AbstractMsSymbolApplier(PdbApplicator applicator, AbstractMsSymbolIterator iter) {
Objects.requireNonNull(applicator, "applicator cannot be null");
Objects.requireNonNull(iter, "symbolGroup cannot be null");
this.applicator = applicator;
this.iter = iter;
currentOffset = iter.getCurrentOffset();
}
/**
* Sets the offset of the {@link SymbolGroup} back to the state when this applicator was
* created.
*/
protected void resetOffset() {
iter.initGetByOffset(currentOffset);
}
/**
* Apply the next and any desired subsequent {@link AbstractMsSymbol AbstractMsSymbols} from
* the {@link SymbolGroup} to a program.
* @throws PdbException if there was a problem processing the data.
* @throws CancelledException upon user cancellation
*/
public abstract void apply() throws PdbException, CancelledException;
/**
* Applies logic of this class to another {@link AbstractMsSymbolApplier} instead of to
* "the program."
* @param applyToApplier the applier to which the logic of this class is applied.
* @throws PdbException if there was a problem processing the data.
* @throws CancelledException upon user cancellation.
*/
public abstract void applyTo(AbstractMsSymbolApplier applyToApplier)
throws PdbException, CancelledException;
/**
* Manages block nesting for symbols/appliers that represent the beginning or end of blocks.
* The default is to do nothing. Otherwise the appliers should implement the appropriate
* logic.
* @param applierParam the applier which is managing blocks, which is typically
* {@link FunctionSymbolApplier}.
*/
public void manageBlockNesting(AbstractMsSymbolApplier applierParam) {
// Do nothing by default.
}
}

View file

@ -0,0 +1,219 @@
/* ###
* 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.math.BigInteger;
import java.util.*;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.RecordNumber;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.AbstractMsType;
import ghidra.program.model.data.DataType;
import ghidra.util.exception.CancelledException;
/**
* Abstract class representing the applier/wrapper for a specific {@link AbstractMsType}. The
* {link {@link #apply()} method creates an associated {@link DataType}, if
* applicable. Methods associated with the {@link AbstractMsTypeApplier} or derived class will
* make fields available to the user, first by trying to get them from the {@link DataType},
* otherwise getting them from the {@link AbstractMsType}.
*/
public abstract class AbstractMsTypeApplier implements Comparable<AbstractMsTypeApplier> {
protected PdbApplicator applicator;
protected AbstractMsType msType;
protected int index;
protected DataType dataType;
// separate copy for now. Might eventually just replace dataType (above)--would have to
// change getDataType().
protected DataType resolvedDataType;
protected boolean resolved = false;
protected boolean applied = false;
protected Set<AbstractMsTypeApplier> waitSet = new HashSet<>();
/**
* Constructor.
* @param applicator {@link PdbApplicator} for which this class is working.
* @param msType {@link AbstractMsType} to apply.
* @throws IllegalArgumentException Upon invalid arguments.
*/
public AbstractMsTypeApplier(PdbApplicator applicator, AbstractMsType msType)
throws IllegalArgumentException {
if (msType == null) {
throw new IllegalArgumentException("PDB Type Applying null AbstractMsType");
}
this.applicator = applicator;
this.msType = msType;
RecordNumber recordNumber = msType.getRecordNumber();
if (recordNumber != null) {
index = recordNumber.getNumber();
}
else {
index = -1;
}
dataType = null;
}
public boolean isApplied() {
return applied;
}
public void setApplied() {
applied = true;
}
boolean isDeferred() {
return false;
}
void deferredApply() throws PdbException, CancelledException {
// default is to do nothing, as most appliers are not deferrable (or should not be).
}
public AbstractMsTypeApplier getDependencyApplier() {
return this;
}
/**
* Resolves the type through the DataTypeManager and makes the resolved type primary.
*/
public void resolve() {
if (resolved) {
return;
}
if (dataType != null) {
resolvedDataType = applicator.resolve(dataType);
}
resolved = true;
}
/**
* Returns the {@link AbstractMsType} associated with this applier/wrapper.
* @return {@link AbstractMsType} associated with this applier/wrapper.
*/
public AbstractMsType getMsType() {
return msType;
}
/**
* Returns the {@link DataType} associated with this applier/wrapper.
* @return {@link DataType} associated with this applier/wrapper.
*/
public DataType getDataType() {
if (resolved) {
return resolvedDataType;
}
return dataType;
}
/**
* Returns either a DataTypeDB or an type (IMPL that might be an empty container) that
* suffices to break cyclical dependencies in data type generation.
* @return the data type.
*/
DataType getCycleBreakType() {
return getDataType();
}
/**
* Apply the {@link AbstractMsType} in an attempt to create a Ghidra type.
* @throws PdbException if there was a problem processing the data.
* @throws CancelledException upon user cancellation
*/
public abstract void apply() throws PdbException, CancelledException;
/**
* Returns the size of the type or 0 if unknown.
* @return the size; zero if unknown.
*/
public abstract BigInteger getSize();
/**
* Returns the (long) size of the type or 0 if unknown. Or Long.MAX_VALUE if too large.
* @return the size; zero if unknown.
*/
public long getSizeLong() {
return PdbApplicator.bigIntegerToLong(applicator, getSize());
}
/**
* Returns the (int) size of the type or 0 if unknown. Or Integer.MAX_VALUE if too large.
* @return the size; zero if unknown.
*/
public int getSizeInt() {
return PdbApplicator.bigIntegerToInt(applicator, getSize());
}
@Override
public String toString() {
return msType.toString();
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + index;
result = prime * result + msType.getClass().getSimpleName().hashCode();
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
AbstractMsTypeApplier other = (AbstractMsTypeApplier) obj;
if (index != other.index) {
return false;
}
if (!msType.getClass().getSimpleName().equals(other.msType.getClass().getSimpleName())) {
return false;
}
return true;
}
@Override
public int compareTo(AbstractMsTypeApplier o) {
int val = hashCode() - o.hashCode();
return val;
}
protected void waitSetPut(AbstractMsTypeApplier applier) {
waitSet.add(applier);
}
protected boolean waitSetRemove(AbstractMsTypeApplier applier) {
return waitSet.remove(applier);
}
protected boolean waitSetIsEmpty() {
return waitSet.isEmpty();
}
protected AbstractMsTypeApplier waitSetGetNext() {
List<AbstractMsTypeApplier> list = new ArrayList<>(waitSet);
return list.get(0);
}
}

View file

@ -0,0 +1,227 @@
/* ###
* 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.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.RecordNumber;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.AbstractArgumentsListMsType;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.AbstractMsType;
import ghidra.program.model.data.*;
import ghidra.util.exception.CancelledException;
/**
* Applier for {@link AbstractArgumentsListMsType} types.
*/
public class ArgumentsListTypeApplier extends AbstractMsTypeApplier {
private boolean isDeferred = false;
private static AbstractMsType validateType(AbstractMsType type)
throws IllegalArgumentException {
if (!(type instanceof AbstractArgumentsListMsType)) {
throw new IllegalArgumentException(
"PDB Incorrectly applying " + type.getClass().getSimpleName() + " to " +
ArgumentsListTypeApplier.class.getSimpleName());
}
return type;
}
/**
* Constructor for the applicator that applies a arguments list.
* @param applicator {@link PdbApplicator} for which this class is working.
* @param msType {@link AbstractArgumentsListMsType} to processes.
* @throws IllegalArgumentException Upon invalid arguments.
*/
public ArgumentsListTypeApplier(PdbApplicator applicator, AbstractMsType msType)
throws IllegalArgumentException {
super(applicator, validateType(msType));
}
//==============================================================================================
public void setDeferred() {
isDeferred = true;
}
@Override
public boolean isDeferred() {
return isDeferred;
}
@Override
void deferredApply() throws PdbException, CancelledException {
// Do nothing... Just need dependency tie of each argument to function.
}
//==============================================================================================
// TODO: would be nice if we did not have to implement this method. Want the applyTo() below.
@Override
public void apply() throws PdbException, CancelledException {
// addMyDependenciesOnly();
// // Silently do nothing.
}
@Override
public BigInteger getSize() {
return BigInteger.ZERO;
}
// private void addMyDependenciesOnly() throws CancelledException, PdbException {
// AbstractArgumentsListMsType argsList = (AbstractArgumentsListMsType) msType;
// List<AbstractTypeIndex> list = argsList.getArgTypeIndexList();
// for (AbstractTypeIndex element : list) {
// applicator.checkCanceled();
// AbstractMsTypeApplier argApplier = applicator.getTypeApplier(element.get());
//
// if (argApplier instanceof PrimitiveTypeApplier &&
// ((PrimitiveTypeApplier) argApplier).isNoType()) {
// // Arguments list is empty. (There better not have been any arguments up until
// // now.)
// break;
// }
//
// if (argApplier instanceof AbstractDeferrableMsTypeApplier &&
// ((AbstractDeferrableMsTypeApplier) argApplier).isDeferred()) {
// applicator.addApplierDependency(this, argApplier);
// setDeferred();
// }
// }
// }
//
public void checkForDependencies(AbstractFunctionTypeApplier functionApplier)
throws CancelledException {
AbstractArgumentsListMsType argsList = (AbstractArgumentsListMsType) msType;
List<RecordNumber> args = argsList.getArgRecordNumbers();
for (RecordNumber arg : args) {
applicator.checkCanceled();
AbstractMsTypeApplier argApplier = applicator.getTypeApplier(arg);
if (argApplier instanceof PrimitiveTypeApplier &&
((PrimitiveTypeApplier) argApplier).isNoType()) {
// Arguments list is empty. (There better not have been any arguments up until
// now.)
break;
}
// if (argApplier instanceof AbstractDeferrableMsTypeApplier &&
// ((AbstractDeferrableMsTypeApplier) argApplier).isDeferred()) {
// applicator.addApplierDependency(functionApplier, argApplier);
// functionApplier.setDeferred();
// }
if (argApplier.isDeferred()) {
applicator.addApplierDependency(functionApplier, argApplier);
functionApplier.setDeferred();
}
}
}
/**
* Apply this to function ({@link AbstractFunctionTypeApplier}).
* @param functionApplier the {@link AbstractFunctionTypeApplier} to which to apply the
* arguments.
* @throws CancelledException Upon user cancellation
*/
public void applyTo(AbstractFunctionTypeApplier functionApplier) throws CancelledException {
FunctionDefinitionDataType functionDefinition = functionApplier.getFunctionDefinition();
AbstractArgumentsListMsType argsList = (AbstractArgumentsListMsType) msType;
List<RecordNumber> args = argsList.getArgRecordNumbers();
List<ParameterDefinition> parameterDefinitionList = new ArrayList<>();
int parameterCount = 0;
for (RecordNumber arg : args) {
applicator.checkCanceled();
AbstractMsTypeApplier argApplier = applicator.getTypeApplier(arg);
if (argApplier instanceof PrimitiveTypeApplier &&
((PrimitiveTypeApplier) argApplier).isNoType()) {
// Arguments list is empty. (There better not have been any arguments up until
// now.)
break;
}
DataType argDataType = argApplier.getDataType();
if (argDataType == null) {
String message =
"PDB Warning: No type conversion for " + argApplier.getMsType().toString() +
" for parameter " + parameterCount + " of " + functionDefinition.getName();
applicator.appendLogMsg(message);
}
else {
ParameterDefinition parameterDefinition =
new ParameterDefinitionImpl(null, argDataType, "");
parameterDefinitionList.add(parameterDefinition);
parameterCount++;
}
}
functionDefinition.setArguments(parameterDefinitionList.toArray(
new ParameterDefinition[parameterDefinitionList.size()]));
}
// /**
// * Apply this to function ({@link AbstractFunctionTypeApplier}).
// * @param functionApplier the {@link AbstractFunctionTypeApplier} to which to apply the
// * arguments.
// * @throws PdbException when unexpected function internals are found.
// * @throws CancelledException Upon user cancellation
// */
// public void applyTo(AbstractFunctionTypeApplier functionApplier)
// throws CancelledException, PdbException {
// FunctionDefinitionDataType functionDefinition = functionApplier.getFunctionDefinition();
//
// AbstractArgumentsListMsType argsList = (AbstractArgumentsListMsType) msType;
// List<AbstractTypeIndex> list = argsList.getArgTypeIndexList();
// List<ParameterDefinition> parameterDefinitionList = new ArrayList<>();
// int parameterCount = 0;
// for (AbstractTypeIndex element : list) {
// applicator.getMonitor().checkCanceled();
// AbstractMsTypeApplier argApplier = applicator.getTypeApplier(element.get());
//
// if (argApplier instanceof PrimitiveTypeApplier &&
// ((PrimitiveTypeApplier) argApplier).isNoType()) {
// // Arguments list is empty. (There better not have been any arguments up until
// // now.)
// break;
// }
//
// if (argApplier instanceof AbstractDeferrableMsTypeApplier &&
// ((AbstractDeferrableMsTypeApplier) argApplier).isDeferred()) {
// applicator.addApplierDependency(functionApplier, argApplier);
// functionApplier.setDeferred();
// }
//
//// applicator.addApplierDependency(functionApplier, argApplier);
// DataType argDataType = argApplier.getDataType();
// if (argDataType == null) {
// String message =
// "PDB Warning: No type conversion for " + argApplier.getMsType().toString() +
// " for parameter " + parameterCount + " of " + functionDefinition.getName();
// applicator.getLog().appendMsg(message);
// }
// else {
// ParameterDefinition parameterDefinition =
// new ParameterDefinitionImpl(null, argDataType, "");
// parameterDefinitionList.add(parameterDefinition);
// parameterCount++;
// }
// }
// functionDefinition.setArguments(parameterDefinitionList.toArray(
// new ParameterDefinition[parameterDefinitionList.size()]));
// }
//
}

View file

@ -0,0 +1,207 @@
/* ###
* 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.math.BigInteger;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.AbstractArrayMsType;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.AbstractMsType;
import ghidra.program.model.data.*;
import ghidra.util.Msg;
import ghidra.util.exception.CancelledException;
/**
* Applier for {@link AbstractArrayMsType} types.
*/
public class ArrayTypeApplier extends AbstractMsTypeApplier {
private boolean isDeferred = false;
private AbstractMsTypeApplier underlyingTypeApplier = null;
private boolean isFlexibleArray = false;
private static AbstractMsType validateType(AbstractMsType type)
throws IllegalArgumentException {
if (!(type instanceof AbstractArrayMsType)) {
throw new IllegalArgumentException("PDB Incorrectly applying " +
type.getClass().getSimpleName() + " to " + ArrayTypeApplier.class.getSimpleName());
}
return type;
}
/**
* Constructor for the applicator that applies a "array" type, transforming it into a
* Ghidra DataType.
* @param applicator {@link PdbApplicator} for which this class is working.
* @param msType {@link AbstractArrayMsType} to processes.
* @throws IllegalArgumentException Upon invalid arguments.
*/
public ArrayTypeApplier(PdbApplicator applicator, AbstractMsType msType)
throws IllegalArgumentException {
super(applicator, validateType(msType));
}
//==============================================================================================
public void setDeferred() {
isDeferred = true;
}
@Override
public boolean isDeferred() {
return isDeferred;
}
public boolean isFlexibleArray() {
return isFlexibleArray;
}
@Override
void deferredApply() throws PdbException, CancelledException {
// No work done here. Just deferring resolve.
}
//==============================================================================================
@Override
public BigInteger getSize() {
return ((AbstractArrayMsType) msType).getSize();
}
@Override
public void apply() throws PdbException, CancelledException {
applyOrDeferForDependencies();
}
private void applyOrDeferForDependencies() {
AbstractArrayMsType type = (AbstractArrayMsType) msType;
underlyingTypeApplier = applicator.getTypeApplier(type.getElementTypeRecordNumber());
if (underlyingTypeApplier instanceof ModifierTypeApplier) {
underlyingTypeApplier =
((ModifierTypeApplier) underlyingTypeApplier).getModifiedTypeApplier();
}
underlyingTypeApplier = underlyingTypeApplier.getDependencyApplier();
applyType(type); // applying now, but resolve() might get deferred.
}
// private void recurseAddDependency(AbstractMsTypeApplier dependee)
// throws CancelledException, PdbException {
// if (dependee instanceof ModifierTypeApplier) {
// ModifierTypeApplier modifierApplier = (ModifierTypeApplier) dependee;
// recurseAddDependency(modifierApplier.getModifiedTypeApplier());
// }
// else if (dependee instanceof CompositeTypeApplier) {
// CompositeTypeApplier defApplier =
// ((CompositeTypeApplier) dependee).getDefinitionApplier();
// if (defApplier != null) {
// applicator.addApplierDependency(this, defApplier);
// }
// else {
// applicator.addApplierDependency(this, dependee);
// }
// setDeferred();
// }
// else if (dependee instanceof ArrayTypeApplier) {
// applicator.addApplierDependency(this, dependee);
// setDeferred();
// }
// else if (dependee instanceof BitfieldTypeApplier) {
// int x =
// ((AbstractBitfieldMsType) ((BitfieldTypeApplier) dependee).getMsType()).getElementTypeIndex();
// AbstractMsTypeApplier underlyingApplier = applicator.getTypeApplier(x);
// if (underlyingApplier instanceof EnumTypeApplier) {
// applicator.addApplierDependency(this, underlyingApplier);
// setDeferred();
// }
// }
// //We are assuming that bitfields on typedefs will not be defined.
// }
//
private void applyType(AbstractArrayMsType type) {
applyArrayMsType((AbstractArrayMsType) msType);
}
private void applyArrayMsType(AbstractArrayMsType type) {
if (isApplied()) {
return;
}
long longUnderlyingSize =
PdbApplicator.bigIntegerToLong(applicator, underlyingTypeApplier.getSize());
DataType underlyingDataType = underlyingTypeApplier.getDataType();
if (longUnderlyingSize > Integer.MAX_VALUE) {
String msg = "PDB " + type.getClass().getSimpleName() + ": Underlying type too large " +
underlyingDataType.getName();
Msg.warn(this, msg);
underlyingDataType = Undefined1DataType.dataType;
longUnderlyingSize = 1L;
}
else if (longUnderlyingSize == 0L) {
String msg = "PDB " + type.getClass().getSimpleName() +
": Zero-sized underlying type " + underlyingDataType.getName();
Msg.warn(this, msg);
underlyingDataType = Undefined1DataType.dataType;
longUnderlyingSize = 1L;
}
long longArraySize = getSizeLong();
long longNumElements = longArraySize / longUnderlyingSize;
if (longNumElements > Integer.MAX_VALUE) {
String msg = "PDB " + type.getClass().getSimpleName() +
": Array num elements too large: " + longUnderlyingSize;
Msg.warn(this, msg);
longNumElements = 1L;
}
else if (longArraySize == 0) {
//flexible array
longNumElements = 0L;
}
else if (longArraySize % longUnderlyingSize != 0L) {
String msg = "PDB " + type.getClass().getSimpleName() +
": Array num elements calculation error underlying type " + longArraySize + " % " +
longUnderlyingSize;
Msg.warn(this, msg);
// bad calculation. Underlying type does not evenly fit into array total size.
underlyingDataType = Undefined1DataType.dataType;
longUnderlyingSize = 1L;
longNumElements = longArraySize;
}
int numElements = (int) longNumElements;
ArrayDataType arrayDataType;
// TODO: Need to find way to pass errorComment on to encompassing composite or other
if (numElements == 0) {
// flexible array
arrayDataType = new ArrayDataType(underlyingDataType, 1, underlyingDataType.getLength(),
applicator.getDataTypeManager());
isFlexibleArray = true;
}
else {
arrayDataType = new ArrayDataType(underlyingDataType, numElements, -1,
applicator.getDataTypeManager());
isFlexibleArray = false;
}
setApplied();
dataType = arrayDataType;
}
}

View file

@ -0,0 +1,147 @@
/* ###
* 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.math.BigInteger;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.RecordNumber;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.*;
import ghidra.util.exception.CancelledException;
/**
* Applier for {@link AbstractBaseClassMsType}, {@link AbstractVirtualBaseClassMsType}, and
* {@link AbstractIndirectVirtualBaseClassMsType} types.
*/
public class BaseClassTypeApplier extends AbstractMsTypeApplier {
private static AbstractMsType validateType(AbstractMsType type)
throws IllegalArgumentException {
if (!(type instanceof AbstractBaseClassMsType) &&
!(type instanceof AbstractVirtualBaseClassMsType) &&
!(type instanceof AbstractIndirectVirtualBaseClassMsType)) {
throw new IllegalArgumentException(
"PDB Incorrectly applying " + type.getClass().getSimpleName() + " to " +
BaseClassTypeApplier.class.getSimpleName());
}
return type;
}
/**
* Constructor for base class applier.
* @param applicator {@link PdbApplicator} for which this class is working.
* @param msType {@link AbstractBaseClassMsType}, {@link AbstractVirtualBaseClassMsType}, or
* {@link AbstractIndirectVirtualBaseClassMsType} to processes.
* @throws IllegalArgumentException Upon invalid arguments.
*/
public BaseClassTypeApplier(PdbApplicator applicator, AbstractMsType msType)
throws IllegalArgumentException {
super(applicator, validateType(msType));
}
// The MsTypes for which we are working do not have a size in and of themselves, but the
// classes/structures to which they refer have a size, even if zero.
// For here, we are only reporting what "we" have, not what the underlying sizes are.
// ...and a value of zero is our "don't know" and "not represented" value.
@Override
public BigInteger getSize() {
return BigInteger.ZERO;
}
/**
* Returns the offset of the Base Class within the inheriting class.
* @return the offset.
* @throws PdbException if field is not available.
*/
public BigInteger getOffset() throws PdbException {
if (msType instanceof AbstractBaseClassMsType) {
return ((AbstractBaseClassMsType) msType).getOffset();
}
throw new PdbException("Offset is not a valid field");
}
/**
* Returns the offset of the base base pointer within the class.
* @return the offset.
* @throws PdbException if field is not available.
*/
public BigInteger getBasePointerOffset() throws PdbException {
if (msType instanceof AbstractBaseClassMsType) {
throw new PdbException("Base Pointer Offset is not valid field");
}
else if (msType instanceof AbstractVirtualBaseClassMsType) {
return ((AbstractVirtualBaseClassMsType) msType).getBasePointerOffset();
}
return ((AbstractIndirectVirtualBaseClassMsType) msType).getBasePointerOffset();
}
/**
* Returns the attributes of the base class within the inheriting class.
* @return the attributes;
*/
public ClassFieldMsAttributes getAttributes() {
if (msType instanceof AbstractBaseClassMsType) {
return ((AbstractBaseClassMsType) msType).getAttributes();
}
else if (msType instanceof AbstractVirtualBaseClassMsType) {
return ((AbstractVirtualBaseClassMsType) msType).getAttributes();
}
return ((AbstractIndirectVirtualBaseClassMsType) msType).getAttributes();
}
/**
* Returns the record number of the base class.
* @return the record number;
*/
public RecordNumber getBaseClassRecordNumber() {
if (msType instanceof AbstractBaseClassMsType) {
return ((AbstractBaseClassMsType) msType).getBaseClassRecordNumber();
}
else if (msType instanceof AbstractVirtualBaseClassMsType) {
return ((AbstractVirtualBaseClassMsType) msType).getBaseClassRecordNumber();
}
return ((AbstractIndirectVirtualBaseClassMsType) msType).getBaseClassRecordNumber();
}
/**
* Returns whether there is a Virtual Base Pointer type index available.
* @return {@code true} if available.
*/
public boolean hasVirtualBasePointerTypeIndex() {
return (!(msType instanceof AbstractBaseClassMsType));
}
/**
* Returns the record number of the virtual base pointer.
* @return the record number;
* @throws PdbException if not a virtual base class.
*/
public RecordNumber getVirtualBasePointerRecordNumber() throws PdbException {
if (msType instanceof AbstractVirtualBaseClassMsType) {
return ((AbstractVirtualBaseClassMsType) msType).getVirtualBasePointerRecordNumber();
}
else if (msType instanceof AbstractIndirectVirtualBaseClassMsType) {
return ((AbstractIndirectVirtualBaseClassMsType) msType).getVirtualBasePointerRecordNumber();
}
throw new PdbException("Not a virtual base class");
}
@Override
public void apply() throws PdbException, CancelledException {
// do nothing at the moment.
}
}

View file

@ -0,0 +1,112 @@
/* ###
* 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.math.BigInteger;
import ghidra.app.util.bin.format.pdb.PdbBitField;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.AbstractBitfieldMsType;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.AbstractMsType;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.InvalidDataTypeException;
import ghidra.util.exception.CancelledException;
/**
* Applier for {@link AbstractBitfieldMsType} types.
*/
public class BitfieldTypeApplier extends AbstractMsTypeApplier {
private AbstractMsTypeApplier elementTypeApplier = null;
private static AbstractMsType validateType(AbstractMsType type)
throws IllegalArgumentException {
if (!(type instanceof AbstractBitfieldMsType)) {
throw new IllegalArgumentException(
"PDB Incorrectly applying " + type.getClass().getSimpleName() + " to " +
BitfieldTypeApplier.class.getSimpleName());
}
return type;
}
/**
* Constructor for bitfield applier.
* @param applicator {@link PdbApplicator} for which this class is working.
* @param msType {@link AbstractBitfieldMsType} to processes
* @throws IllegalArgumentException Upon invalid arguments.
*/
public BitfieldTypeApplier(PdbApplicator applicator, AbstractMsType msType)
throws IllegalArgumentException {
super(applicator, validateType(msType));
}
@Override
public BigInteger getSize() {
if (elementTypeApplier == null) {
return BigInteger.ZERO;
}
return elementTypeApplier.getSize();
}
@Override
public void apply() throws PdbException, CancelledException {
// The bitfield does not get resolved/commited to the DataTypeManager.
dataType = applyBitfieldMsType((AbstractBitfieldMsType) msType);
}
private DataType applyBitfieldMsType(AbstractBitfieldMsType type) {
elementTypeApplier = applicator.getTypeApplier(type.getElementRecordNumber());
if (elementTypeApplier instanceof ModifierTypeApplier) {
elementTypeApplier =
((ModifierTypeApplier) elementTypeApplier).getModifiedTypeApplier();
}
if (!(elementTypeApplier instanceof PrimitiveTypeApplier ||
(elementTypeApplier instanceof EnumTypeApplier))) {
applicator.appendLogMsg(
"Unable to process underlying type for Bitfield: " + type.getName());
return null;
}
DataType baseDataType = elementTypeApplier.getDataType();
DataType bitFieldDataType = null;
try {
bitFieldDataType = new Pdb2BitField(baseDataType.clone(applicator.getDataTypeManager()),
type.getBitLength(), type.getBitPosition());
}
catch (InvalidDataTypeException e) {
applicator.appendLogMsg(
"Problem creating PdbBitField for " + type.getName() + ", error: " + e.toString());
return null;
}
return bitFieldDataType;
}
@Override
public void resolve() {
// Do not resolve Bitfield Types... will be resolved with composite!!!
}
/**
* <code>Pdb2BitField</code> provides ability to hang onto bitfield as a datatype.
* This will be transformed to a normal BitFieldDataType when cloned.
*/
private class Pdb2BitField extends PdbBitField {
private Pdb2BitField(DataType baseDataType, int bitSize, int bitOffsetWithinBaseType)
throws InvalidDataTypeException {
super(baseDataType, bitSize, bitOffsetWithinBaseType);
}
}
}

View file

@ -0,0 +1,134 @@
/* ###
* 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.HashMap;
import java.util.Map;
import ghidra.app.cmd.comments.SetCommentCmd;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.CodeUnit;
import ghidra.program.model.listing.Program;
public class BlockCommentsManager {
private static final String BLOCK_INDENT = " ";
// private int symbolBlockNestingLevel;
private Map<Address, String> blockPreComments;
private Map<Address, String> blockPostComments;
BlockCommentsManager() {
// symbolBlockNestingLevel = 0;
blockPreComments = new HashMap<>();
blockPostComments = new HashMap<>();
}
void applyTo(Program program) {
applyTo(program, 0L);
}
void applyTo(Program program, long addressDelta) {
finalizeBlockComments(program, addressDelta);
}
// int beginBlock(Address startAddress, String name, long length) {
// ++symbolBlockNestingLevel;
// addBlockComment(startAddress, name, length, symbolBlockNestingLevel);
// return symbolBlockNestingLevel;
// }
//
// int endBlock() throws PdbException {
// if (--symbolBlockNestingLevel < 0) {
// // TODO: eliminate exception and handle another way.
// throw new PdbException("Block Nesting went negative");
// }
// if (symbolBlockNestingLevel == 0) {
// //currentFunctionSymbolApplier = null;
// }
// return symbolBlockNestingLevel;
// }
//
// int getBlockNestingLevel() {
// return symbolBlockNestingLevel;
// }
//
void addPreComment(Address address, String preComment) {
String existingPreComment = blockPreComments.get(address);
preComment =
(existingPreComment == null) ? preComment : existingPreComment + "\n" + preComment;
blockPreComments.put(address, preComment);
}
void addPostComment(Address address, String postComment) {
String existingPostComment = blockPostComments.get(address);
postComment =
(existingPostComment == null) ? postComment : postComment + "\n" + existingPostComment;
blockPostComments.put(address, postComment);
}
void addBlockComment(Address startAddress, String name, long length, int nestingLevel) {
String indent = "";
for (int i = 1; i < nestingLevel; i++) {
indent += BLOCK_INDENT;
}
String baseComment = "level " + nestingLevel + ", length " + length;
String preComment = indent + "PDB: Block Beg, " + baseComment;
if (!name.isEmpty()) {
preComment += " (" + name + ")";
}
String postComment = indent + "PDB: Block End, " + baseComment;
addPreComment(startAddress, preComment);
// String existingPreComment = blockPreComments.get(startAddress);
// preComment =
// (existingPreComment == null) ? preComment : existingPreComment + "\n" + preComment;
// blockPreComments.put(startAddress, preComment);
Address endAddress = startAddress.add(((length <= 0) ? 0 : length - 1));
addPostComment(endAddress, postComment);
// Address endCodeUnitAddress =
// program.getListing().getCodeUnitContaining(endAddress).getAddress();
//
// processPostComment(endCodeUnitAddress, postComment);
// String existingPostComment = blockPostComments.get(endCodeUnitAddress);
// postComment =
// (existingPostComment == null) ? postComment : postComment + "\n" + existingPostComment;
// blockPostComments.put(endCodeUnitAddress, postComment);
}
private void finalizeBlockComments(Program program, long addressDelta) {
for (Map.Entry<Address, String> entry : blockPreComments.entrySet()) {
appendBlockComment(program, entry.getKey().add(addressDelta), entry.getValue(),
CodeUnit.PRE_COMMENT);
}
for (Map.Entry<Address, String> entry : blockPostComments.entrySet()) {
Address endCodeUnitAddress = program.getListing().getCodeUnitContaining(
entry.getKey().add(addressDelta)).getAddress();
appendBlockComment(program, endCodeUnitAddress, entry.getValue(),
CodeUnit.POST_COMMENT);
}
}
private void appendBlockComment(Program program, Address address, String text,
int commentType) {
String comment = program.getListing().getComment(commentType, address);
comment = (comment == null) ? text : comment + "\n" + text;
SetCommentCmd.createComment(program, address, comment, commentType);
}
}

View file

@ -0,0 +1,65 @@
/* ###
* 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.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbLog;
import ghidra.app.util.bin.format.pdb2.pdbreader.symbol.AbstractBlockMsSymbol;
import ghidra.app.util.bin.format.pdb2.pdbreader.symbol.AbstractMsSymbol;
import ghidra.app.util.pdb.pdbapplicator.SymbolGroup.AbstractMsSymbolIterator;
import ghidra.program.model.address.Address;
import ghidra.util.Msg;
import ghidra.util.exception.AssertException;
import ghidra.util.exception.CancelledException;
/**
* Applier for {@link AbstractBlockMsSymbol} symbols.
*/
public class BlockSymbolApplier extends AbstractMsSymbolApplier {
private AbstractBlockMsSymbol symbol;
public BlockSymbolApplier(PdbApplicator applicator, AbstractMsSymbolIterator iter) {
super(applicator, iter);
AbstractMsSymbol abstractSymbol = iter.next();
if (!(abstractSymbol instanceof AbstractBlockMsSymbol)) {
throw new AssertException(
"Invalid symbol type: " + abstractSymbol.getClass().getSimpleName());
}
symbol = (AbstractBlockMsSymbol) abstractSymbol;
}
@Override
public void apply() throws PdbException, CancelledException {
String message = "Cannot apply " + this.getClass().getSimpleName() + " directly to program";
Msg.info(this, message);
PdbLog.message(message);
}
@Override
public void applyTo(AbstractMsSymbolApplier applyToApplier) {
// Do nothing
}
@Override
public void manageBlockNesting(AbstractMsSymbolApplier applierParam) {
if (applierParam instanceof FunctionSymbolApplier) {
FunctionSymbolApplier functionSymbolApplier = (FunctionSymbolApplier) applierParam;
Address address = applicator.reladdr(symbol);
functionSymbolApplier.beginBlock(address, symbol.getName(), symbol.getLength());
}
}
}

View file

@ -0,0 +1,231 @@
/* ###
* 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.SymbolPath;
import ghidra.app.util.bin.format.pdb2.pdbreader.RecordNumber;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
/**
* Two-way Maps forward references with corresponding definitions for composites and enums.
* Uses the fwdref and definition members of the AbstractComplexTypeApplier.
*/
// We have probably tried 5 or more ways of doing this, all with mixed results. The current
// implementation seems to yield the best results at the moment. Keeping some of the old code
// around until we are solid on our algorithm and until we document some of the various algorithms
// tried.
public class ComplexTypeApplierMapper {
private PdbApplicator applicator;
// private Map<SymbolPath, AbstractComplexTypeApplier> complexTypeAppliersBySymbolPath;
private Map<SymbolPath, LinkedList<AbstractComplexTypeApplier>> compositeAppliersQueueBySymbolPath;
private Map<SymbolPath, LinkedList<AbstractComplexTypeApplier>> enumAppliersQueueBySymbolPath;
//==============================================================================================
public ComplexTypeApplierMapper(PdbApplicator applicator) {
Objects.requireNonNull(applicator, "applicator cannot be null");
this.applicator = applicator;
// complexTypeAppliersBySymbolPath = new HashMap<>();
compositeAppliersQueueBySymbolPath = new HashMap<>();
enumAppliersQueueBySymbolPath = new HashMap<>();
}
//==============================================================================================
//==============================================================================================
void mapAppliers(TaskMonitor monitor) throws CancelledException {
int indexLimit = applicator.getPdb().getTypeProgramInterface().getTypeIndexMaxExclusive();
int indexNumber = applicator.getPdb().getTypeProgramInterface().getTypeIndexMin();
monitor.initialize(indexLimit - indexNumber);
applicator.setMonitorMessage("PDB: Mapping Composites...");
while (indexNumber < indexLimit) {
monitor.checkCanceled();
PdbResearch.checkBreak(indexNumber);
AbstractMsTypeApplier applier =
applicator.getTypeApplier(RecordNumber.typeRecordNumber(indexNumber++));
// From real data, we know that an enum and a composite both had the same SymbolPath,
// so enums and composites must be maintained separately so they do not get matched
// with each other.
if (applier instanceof CompositeTypeApplier) {
// mapComplexApplierBySymbolPath(compositeAppliersFwdRefQueueBySymbolPath,
// (AbstractComplexTypeApplier) applier);
mapComplexApplierTwoWayBySymbolPath(compositeAppliersQueueBySymbolPath,
(AbstractComplexTypeApplier) applier);
}
else if (applier instanceof EnumTypeApplier) {
// mapComplexApplierBySymbolPath(enumAppliersFwdRefQueueBySymbolPath,
// (AbstractComplexTypeApplier) applier);
mapComplexApplierTwoWayBySymbolPath(enumAppliersQueueBySymbolPath,
(AbstractComplexTypeApplier) applier);
}
// if (applier instanceof AbstractComplexTypeApplier) {
// mapComplexApplierByQueue((AbstractComplexTypeApplier) applier);
// //mapComplexApplierForwardOnly((AbstractComplexTypeApplier) applier);
// //mapComplexApplier((AbstractComplexTypeApplier) applier);
// }
monitor.incrementProgress(1);
}
}
private void mapComplexApplierTwoWayBySymbolPath(
Map<SymbolPath, LinkedList<AbstractComplexTypeApplier>> applierQueueBySymbolPath,
AbstractComplexTypeApplier complexApplier) {
SymbolPath symbolPath = complexApplier.getSymbolPath();
Objects.requireNonNull(symbolPath, "SymbolPath may not be null");
LinkedList<AbstractComplexTypeApplier> appliers = applierQueueBySymbolPath.get(symbolPath);
if (appliers == null) {
appliers = new LinkedList<>();
applierQueueBySymbolPath.put(symbolPath, appliers);
// Putting fwdref or def (doesn't matter which it is)
if (!appliers.add(complexApplier)) {
// Error
}
}
else if (appliers.peekFirst().isForwardReference() == complexApplier.isForwardReference()) {
// Only need to look at first on list, as all on list are the same fwdref or def.
// If same as what is on list, add to the list.
if (!appliers.add(complexApplier)) {
// Error
}
}
else {
if (complexApplier.isForwardReference()) {
AbstractComplexTypeApplier defApplier = appliers.removeFirst();
defApplier.setFwdRefApplier(complexApplier);
complexApplier.setDefinitionApplier(defApplier);
}
else {
AbstractComplexTypeApplier fwdApplier = appliers.removeFirst();
fwdApplier.setDefinitionApplier(complexApplier);
complexApplier.setFwdRefApplier(fwdApplier);
}
if (appliers.isEmpty()) {
// Do not need to keep all of these around.
applierQueueBySymbolPath.remove(symbolPath);
}
}
}
// private void mapComplexApplier(AbstractComplexTypeApplier complexApplier) {
// SymbolPath symbolPath = complexApplier.getSymbolPath();
//
// AbstractComplexTypeApplier cachedComplexApplier =
// getComplexTypeApplierBySymbolPath(symbolPath, complexApplier.getClass());
// if (cachedComplexApplier == null) {
// // Setting cache if not already set or setting to definition.
// putComplexTypeApplierBySymbolPath(symbolPath, complexApplier);
// }
// else if (cachedComplexApplier.isForwardReference()) {
// if (!complexApplier.isForwardReference()) {
// cachedComplexApplier.setDefinitionApplier(complexApplier);
// complexApplier.setFwdRefApplier(cachedComplexApplier);
// }
// // Setting cache to new applier, whether fwd ref or definition.
// putComplexTypeApplierBySymbolPath(symbolPath, complexApplier);
// }
// else { // cached is definition
// if (!complexApplier.isForwardReference()) { // we are definition
// // Setting cache if not already set or setting to definition.
// putComplexTypeApplierBySymbolPath(symbolPath, complexApplier);
// }
// else { // we are forward ref
// AbstractComplexTypeApplier fwdRef =
// cachedComplexApplier.getFwdRefApplier(complexApplier.getClass());
// if (fwdRef == null) {
// // cached definition did not have a forward ref but we are one, so hook it up?
// // problem is if a definition follows... ugh. Not sure want to do this.
// complexApplier.setDefinitionApplier(cachedComplexApplier);
// cachedComplexApplier.setFwdRefApplier(complexApplier);
// // would like to cache a forward ref, but are are tying it to a previous
// // definition, so not.
// }
// else {
// // Setting cache if not already set or setting to definition.
// putComplexTypeApplierBySymbolPath(symbolPath, complexApplier);
// }
// }
// }
// }
//
// // Only caching forward ref and then mapping only following def to fwdref.
// // Clearing cache after that def so next def does not map.
// private void mapComplexApplierForwardOnly(AbstractComplexTypeApplier complexApplier) {
// SymbolPath symbolPath = complexApplier.getSymbolPath();
//
// if (complexApplier.isForwardReference()) {
// putComplexTypeApplierBySymbolPath(symbolPath, complexApplier);
// }
// else {
// AbstractComplexTypeApplier cachedComplexApplier =
// getComplexTypeApplierBySymbolPath(symbolPath, complexApplier.getClass());
// if (cachedComplexApplier != null) {
// cachedComplexApplier.setDefinitionApplier(complexApplier);
// complexApplier.setFwdRefApplier(cachedComplexApplier);
// // set cache back to null
// complexTypeAppliersBySymbolPath.remove(symbolPath);
// }
// }
// }
//
// private void putComplexTypeApplierBySymbolPath(SymbolPath symbolPath,
// AbstractComplexTypeApplier applier) {
// Objects.requireNonNull(symbolPath, "SymbolPath may not be null");
// Objects.requireNonNull(applier, "CompositeTypeApplier may not be null");
// complexTypeAppliersBySymbolPath.put(symbolPath, applier);
// }
//
// private <T extends AbstractComplexTypeApplier> T getComplexTypeApplierBySymbolPath(
// SymbolPath symbolPath, Class<T> typeClass) {
// Objects.requireNonNull(symbolPath, "SymbolPath may not be null");
// Objects.requireNonNull(typeClass, "typeClass may not be null");
// AbstractComplexTypeApplier applier = complexTypeAppliersBySymbolPath.get(symbolPath);
// if (!typeClass.isInstance(applier)) {
// return null;
// }
// return typeClass.cast(applier);
// }
//
// //==============================================================================================
// private void mapComplexApplierBySymbolPath(
// Map<SymbolPath, LinkedList<AbstractComplexTypeApplier>> applierQueueBySymbolPath,
// AbstractComplexTypeApplier complexApplier) {
// SymbolPath symbolPath = complexApplier.getSymbolPath();
// Objects.requireNonNull(symbolPath, "SymbolPath may not be null");
//
// LinkedList<AbstractComplexTypeApplier> fwdList = applierQueueBySymbolPath.get(symbolPath);
// if (fwdList == null) {
// fwdList = new LinkedList<>();
// applierQueueBySymbolPath.put(symbolPath, fwdList);
// }
//
// if (complexApplier.isForwardReference()) {
// if (!fwdList.add(complexApplier)) {
// // Error
// }
// }
// else if (!fwdList.isEmpty()) {
// AbstractComplexTypeApplier fwdApplier = fwdList.removeFirst();
// fwdApplier.setDefinitionApplier(complexApplier);
// complexApplier.setFwdRefApplier(fwdApplier);
// }
// }
//
}

View file

@ -0,0 +1,52 @@
/* ###
* 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.HashMap;
import java.util.Map;
public enum CompositeLayoutMode {
MEMBERS_ONLY("Legacy", 0, OoComponentLayoutMode.MEMBERS_ONLY),
BASIC_SIMPLE_COMPLEX("Complex with Basic Fallback", 1, OoComponentLayoutMode.BASIC),
SIMPLE_COMPLEX("Complex with Simple Fallback", 2, OoComponentLayoutMode.SIMPLE),
COMPLEX("Complex Always", 3, OoComponentLayoutMode.COMPLEX);
private static final Map<Integer, CompositeLayoutMode> BY_VALUE = new HashMap<>();
static {
for (CompositeLayoutMode val : values()) {
BY_VALUE.put(val.value, val);
}
}
public final String label;
private final int value;
private OoComponentLayoutMode layoutMode;
@Override
public String toString() {
return label;
}
private CompositeLayoutMode(String label, int value, OoComponentLayoutMode layoutMode) {
this.label = label;
this.value = value;
this.layoutMode = layoutMode;
}
public OoComponentLayoutMode getLayoutMode() {
return layoutMode;
}
}

View file

@ -0,0 +1,820 @@
/* ###
* 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.math.BigInteger;
import java.util.*;
import ghidra.app.util.SymbolPath;
import ghidra.app.util.SymbolPathParser;
import ghidra.app.util.bin.format.pdb.DefaultCompositeMember;
import ghidra.app.util.bin.format.pdb2.pdbreader.*;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.*;
import ghidra.program.model.data.*;
import ghidra.util.Msg;
import ghidra.util.exception.AssertException;
import ghidra.util.exception.CancelledException;
/*
* Non java-doc:
* Some truths:
* For AbstractMsCompositeType: do not count on "count" to be zero when MsProperty is fwdref
* (we have seen example of count==0 and not fwdref, though the majority of the time the
* two go hand-in-hand. When these did not equate, it might have been when there was no
* need for a fwdref and possibly only one definition--this would require a closer look).
*/
/**
* Applier for {@link AbstractCompositeMsType} types.
*/
public class CompositeTypeApplier extends AbstractComplexTypeApplier {
// DO NOT DELETE. Might eliminate one path or might make an analyzer option.
private static boolean applyBaseClasses = true;
//private static boolean applyBaseClasses = false;
private boolean isDeferred = false;
private CppCompositeType classType;
// private final static DataType NO_TYPE_DATATYPE =
// new TypedefDataType("<NoType>", Undefined1DataType.dataType);
//
private Map<Integer, String> componentComments;
private List<Default2PdbMember> members;
private static AbstractMsType validateType(AbstractMsType type)
throws IllegalArgumentException {
if (!(type instanceof AbstractCompositeMsType)) {
throw new IllegalArgumentException(
"PDB Incorrectly applying " + type.getClass().getSimpleName() + " to " +
CompositeTypeApplier.class.getSimpleName());
}
return type;
}
/**
* Constructor for composite type applier, for transforming a composite into a
* Ghidra DataType.
* @param applicator {@link PdbApplicator} for which this class is working.
* @param msType {@link AbstractCompositeMsType} to process.
* @throws IllegalArgumentException Upon invalid arguments.
*/
public CompositeTypeApplier(PdbApplicator applicator, AbstractMsType msType)
throws IllegalArgumentException {
super(applicator, validateType(msType));
String fullPathName = ((AbstractCompositeMsType) msType).getName();
symbolPath = new SymbolPath(SymbolPathParser.parse(fullPathName));
}
public CppCompositeType getClassType() {
if (definitionApplier != null) {
return ((CompositeTypeApplier) definitionApplier).getClassTypeInternal();
}
return classType;
}
CppCompositeType getClassTypeInternal() {
return classType;
}
List<Default2PdbMember> getMembers() {
return members;
}
// Mapping of fwdRef/def must be done prior to this call.
private void getOrCreateComposite() {
if (dataType != null) {
return;
}
if (isForwardReference()) {
if (definitionApplier != null) {
dataType = definitionApplier.getDataTypeInternal();
classType = ((CompositeTypeApplier) definitionApplier).getClassTypeInternal();
if (dataType != null) {
return;
}
}
}
else {
if (fwdRefApplier != null) {
dataType = fwdRefApplier.getDataTypeInternal();
classType = ((CompositeTypeApplier) fwdRefApplier).getClassTypeInternal();
if (dataType != null) {
return;
}
}
}
dataType = createEmptyComposite((AbstractCompositeMsType) msType);
String mangledName = ((AbstractCompositeMsType) msType).getMangledName();
classType = new CppCompositeType((Composite) dataType, mangledName);
classType.setName(getName());
classType.setSize(PdbApplicator.bigIntegerToInt(applicator, getSize()));
if (msType instanceof AbstractClassMsType) {
classType.setClass();
}
else if (msType instanceof AbstractStructureMsType) {
classType.setStruct();
}
else if (msType instanceof AbstractUnionMsType) {
classType.setUnion();
}
classType.setFinal(isFinal());
}
@Override
public void apply() throws PdbException, CancelledException {
getOrCreateComposite();
Composite composite = (Composite) dataType;
AbstractCompositeMsType type = (AbstractCompositeMsType) msType;
MsProperty property = type.getMsProperty();
if (property.isForwardReference() && definitionApplier != null) {
return;
}
if (!(composite instanceof CompositeDataTypeImpl)) {
return; // A resolved version exists (multiple definitions).
}
applyOrDeferForDependencies();
}
@Override
public void resolve() {
if (!isForwardReference()) {
super.resolve();
}
}
private void applyOrDeferForDependencies() throws PdbException, CancelledException {
AbstractCompositeMsType type = (AbstractCompositeMsType) msType;
MsProperty property = type.getMsProperty();
if (property.isForwardReference() && definitionApplier != null) {
return;
}
// Add self
applicator.addApplierDependency(this);
// Add any dependees: base classes and members
FieldListTypeApplier fieldListApplier = FieldListTypeApplier.getFieldListApplierSpecial(
applicator, type.getFieldDescriptorListRecordNumber());
// Currently do not need this dependency, as we currently do not need any contents
// of the base class for filling in this class
for (AbstractMsTypeApplier baseApplierIterated : fieldListApplier.getBaseClassList()) {
if (baseApplierIterated instanceof BaseClassTypeApplier) {
BaseClassTypeApplier baseTypeApplier = (BaseClassTypeApplier) baseApplierIterated;
AbstractMsTypeApplier applier =
applicator.getTypeApplier(baseTypeApplier.getBaseClassRecordNumber());
if (applier instanceof CompositeTypeApplier) {
CompositeTypeApplier dependencyApplier =
((CompositeTypeApplier) applier).getDependencyApplier();
applicator.addApplierDependency(this, dependencyApplier);
// CompositeTypeApplier defApplier =
// ((CompositeTypeApplier) applier).getDefinitionApplier();
// if (defApplier != null) {
// applicator.addApplierDependency(this, defApplier);
// }
// else {
// applicator.addApplierDependency(this, applier);
// }
setDeferred();
}
}
}
for (AbstractMsTypeApplier memberTypeApplierIterated : fieldListApplier.getMemberList()) {
applicator.checkCanceled();
if (memberTypeApplierIterated instanceof MemberTypeApplier) {
MemberTypeApplier memberTypeApplier = (MemberTypeApplier) memberTypeApplierIterated;
AbstractMsTypeApplier fieldApplier = memberTypeApplier.getFieldTypeApplier();
recurseAddDependency(fieldApplier);
}
// if (memberTypeApplierIterated instanceof NestedTypeApplier) {
// recurseAddDependency(memberTypeApplierIterated);
// }
else if (memberTypeApplierIterated instanceof VirtualFunctionTablePointerTypeApplier) {
applicator.addApplierDependency(this, memberTypeApplierIterated);
}
}
if (!isDeferred()) {
applyInternal();
}
}
private void recurseAddDependency(AbstractMsTypeApplier dependee)
throws CancelledException, PdbException {
if (dependee instanceof ModifierTypeApplier) {
ModifierTypeApplier modifierApplier = (ModifierTypeApplier) dependee;
recurseAddDependency(modifierApplier.getModifiedTypeApplier());
}
else if (dependee instanceof CompositeTypeApplier) {
CompositeTypeApplier defApplier =
((CompositeTypeApplier) dependee).getDefinitionApplier(CompositeTypeApplier.class);
if (defApplier != null) {
applicator.addApplierDependency(this, defApplier);
}
else {
applicator.addApplierDependency(this, dependee);
}
setDeferred();
}
else if (dependee instanceof ArrayTypeApplier) {
applicator.addApplierDependency(this, dependee);
setDeferred();
}
// else if (dependee instanceof NestedTypeApplier) {
// NestedTypeApplier nestedTypeApplier = (NestedTypeApplier) dependee;
// AbstractMsTypeApplier nestedDefinitionApplier =
// nestedTypeApplier.getNestedTypeDefinitionApplier();
// // Need to make sure that "this" class id dependent on all elements composing the
// // nested definition, but we need to create the nested definition during the
// // creation of this class. (NestedTypeApplier and NestedTypeMsType do not really
// // have their own RecordNumber).
// applicator.addApplierDependency(this, nestedDefinitionApplier);
// setDeferred();
// }
else if (dependee instanceof BitfieldTypeApplier) {
RecordNumber recNum =
((AbstractBitfieldMsType) ((BitfieldTypeApplier) dependee).getMsType()).getElementRecordNumber();
AbstractMsTypeApplier underlyingApplier = applicator.getTypeApplier(recNum);
if (underlyingApplier instanceof EnumTypeApplier) {
applicator.addApplierDependency(this, underlyingApplier);
setDeferred();
}
}
//We are assuming that bitfields on typedefs will not be defined.
}
public void applyInternal() throws CancelledException, PdbException {
if (isApplied()) {
return;
}
Composite composite = (Composite) dataType;
AbstractCompositeMsType type = (AbstractCompositeMsType) msType;
boolean applyCpp = applyBaseClasses;
if (type instanceof AbstractUnionMsType) {
applyCpp = false;
if (hasBaseClasses()) {
String msg = "Unexpected base classes for union type: " + type.getName();
PdbLog.message(msg);
Msg.info(this, msg);
}
}
if (applyCpp) {
applyCpp(composite, type);
}
else {
applyBasic(composite, type);
}
setApplied();
}
//==============================================================================================
void applyBasic(Composite composite, AbstractCompositeMsType type)
throws CancelledException, PdbException {
//boolean isClass = (type instanceof AbstractClassMsType || actsLikeClass(applicator, type));
boolean isClass = (type instanceof AbstractClassMsType);
int size = getSizeInt();
// Fill in composite definition details.
FieldListTypeApplier fieldListApplier = FieldListTypeApplier.getFieldListApplierSpecial(
applicator, type.getFieldDescriptorListRecordNumber());
clearComponents(composite);
members = new ArrayList<>();
componentComments = new HashMap<>();
addMembers(composite, fieldListApplier);
if (!DefaultCompositeMember.applyDataTypeMembers(composite, isClass, size, members,
msg -> reconstructionWarn(msg), applicator.getCancelOnlyWrappingMonitor())) {
clearComponents(composite);
}
setComponentComments(composite);
}
private void setComponentComments(Composite composite) {
if (composite instanceof Structure) {
Structure structure = (Structure) composite;
for (Map.Entry<Integer, String> entry : componentComments.entrySet()) {
DataTypeComponent component = structure.getComponentAt(entry.getKey());
if (component == null) {
String message = "Could not set comment for 'missing' componenent " +
entry.getKey() + " for: " + structure.getName();
PdbLog.message(message);
Msg.info(this, message);
return;
}
component.setComment(entry.getValue());
}
}
}
//==============================================================================================
void applyCpp(Composite composite, AbstractCompositeMsType type)
throws PdbException, CancelledException {
// Fill in composite definition details.
FieldListTypeApplier fieldListApplier = FieldListTypeApplier.getFieldListApplierSpecial(
applicator, type.getFieldDescriptorListRecordNumber());
clearComponents(composite);
members = new ArrayList<>(); // TODO: temporary for old "basic" mechanism
componentComments = new HashMap<>(); // TODO: temporary for old "basic" mechanism
addClassTypeBaseClasses(composite, fieldListApplier);
addMembers(composite, fieldListApplier);
if (!classType.validate()) {
// TODO: Investigate. We should do this check for some classes somewhere. Should
// we do it here. Set breakpoint here to investigate.
}
classType.createLayout(applicator.getPdbApplicatorOptions().getCompositeLayoutMode(),
applicator.getVbtManager(), applicator.getCancelOnlyWrappingMonitor());
}
//==============================================================================================
private void reconstructionWarn(String msg) {
//TODO: if statement/contents temporary
if (msg.contains("failed to align") && hasHiddenComponents()) {
msg = msg.replaceFirst("PDB", "PDB CLASS");
}
Msg.warn(this, msg);
}
//==============================================================================================
public void setDeferred() {
isDeferred = true;
}
@Override
public boolean isDeferred() {
return isDeferred;
}
@Override
void deferredApply() throws PdbException, CancelledException {
if (isDeferred()) {
applyInternal();
}
}
//==============================================================================================
//==============================================================================================
@Override
public CompositeTypeApplier getDependencyApplier() {
if (definitionApplier != null && definitionApplier instanceof CompositeTypeApplier) {
return (CompositeTypeApplier) definitionApplier;
}
return this;
}
public String getName() {
return getMsType().getName();
}
@Override
public DataType getDataType() {
if (resolved) {
return resolvedDataType;
}
getOrCreateComposite();
return dataType;
}
@Override
DataType getCycleBreakType() {
if (isForwardReference() && definitionApplier != null && definitionApplier.isApplied()) {
return definitionApplier.getDataType();
}
return dataType;
}
public boolean hasUniqueName() {
return ((AbstractCompositeMsType) msType).getMsProperty().hasUniqueName();
}
@Override
public BigInteger getSize() {
return ((AbstractCompositeMsType) getDependencyApplier().getMsType()).getSize();
}
// TODO:
// Taken from PdbUtil without change. Would have had to change access on class PdbUtil and
// this ensureSize method to public to make it accessible. Can revert to using PdbUtil
// once we move this new module from Contrib to Features/PDB.
final static void clearComponents(Composite composite) {
if (composite instanceof Structure) {
((Structure) composite).deleteAll();
}
else {
while (composite.getNumComponents() > 0) {
composite.delete(0);
}
}
}
private Composite createEmptyComposite(AbstractCompositeMsType type) {
SymbolPath fixedPath = getFixedSymbolPath();
CategoryPath categoryPath = applicator.getCategory(fixedPath.getParent());
Composite composite;
if (type instanceof AbstractClassMsType) {
applicator.predefineClass(fixedPath);
composite = new StructureDataType(categoryPath, fixedPath.getName(), 0,
applicator.getDataTypeManager());
}
else if (type instanceof AbstractStructureMsType) {
composite = new StructureDataType(categoryPath, fixedPath.getName(), 0,
applicator.getDataTypeManager());
}
else if (type instanceof AbstractUnionMsType) {
composite = new UnionDataType(categoryPath, fixedPath.getName(),
applicator.getDataTypeManager());
}
else { // InterfaceMsType
String message = "Unsupported datatype (" + type.getClass().getSimpleName() + "): " +
fixedPath.getPath();
applicator.appendLogMsg(message);
return null;
}
return composite;
}
private boolean hasBaseClasses() {
AbstractCompositeMsType defType;
if (definitionApplier == null) {
if (isForwardReference()) {
return false;
}
defType = (AbstractCompositeMsType) msType;
}
else {
defType = (AbstractCompositeMsType) definitionApplier.getMsType();
}
AbstractMsTypeApplier applier =
applicator.getTypeApplier(defType.getFieldDescriptorListRecordNumber());
if (!(applier instanceof FieldListTypeApplier)) {
return false;
}
FieldListTypeApplier fieldListApplier = (FieldListTypeApplier) applier;
AbstractFieldListMsType fieldListType =
((AbstractFieldListMsType) fieldListApplier.getMsType());
if (fieldListType.getBaseClassList().size() != 0) {
return true;
}
return (fieldListType.getBaseClassList().size() != 0);
}
private boolean hasHiddenComponents() {
AbstractCompositeMsType defType;
if (definitionApplier == null) {
if (isForwardReference()) {
return false;
}
defType = (AbstractCompositeMsType) msType;
}
else {
defType = (AbstractCompositeMsType) definitionApplier.getMsType();
}
// Note: if a "class" only has structure fields--does not have member functions, base
// class, virtual inheritance, etc., then it acts like a structure, meaning that there
// should be no extra fields for pvft, pvbt, base and virtual class components.
// So... it might not be good to return "true" for just checking if the type is an
// instanceof AbstractClassMsType.
AbstractMsTypeApplier applier =
applicator.getTypeApplier(defType.getFieldDescriptorListRecordNumber());
if (!(applier instanceof FieldListTypeApplier)) {
return false;
}
FieldListTypeApplier fieldListApplier = (FieldListTypeApplier) applier;
AbstractFieldListMsType fieldListType =
((AbstractFieldListMsType) fieldListApplier.getMsType());
return (fieldListType.getMethodList().size() != 0 ||
fieldListType.getBaseClassList().size() != 0);
}
private void addClassTypeBaseClasses(Composite composite, FieldListTypeApplier fieldListApplier)
throws PdbException {
AbstractCompositeMsType type = (AbstractCompositeMsType) msType;
for (AbstractMsTypeApplier baseApplierIterated : fieldListApplier.getBaseClassList()) {
if (!(baseApplierIterated instanceof BaseClassTypeApplier)) {
applicator.appendLogMsg(baseApplierIterated.getClass().getSimpleName() +
" seen where BaseClassTypeApplier expected for " + type.getName());
continue;
}
BaseClassTypeApplier baseTypeApplier = (BaseClassTypeApplier) baseApplierIterated;
AbstractMsTypeApplier baseClassTypeApplier =
applicator.getTypeApplier(baseTypeApplier.getBaseClassRecordNumber());
if (!(baseClassTypeApplier instanceof CompositeTypeApplier)) {
applicator.appendLogMsg(baseApplierIterated.getClass().getSimpleName() +
" seen where CompositeTypeApplier expected for " + type.getName());
continue;
}
AbstractMsType baseClassMsType = baseTypeApplier.getMsType();
if (baseClassMsType instanceof AbstractBaseClassMsType) {
applyDirectBaseClass((AbstractBaseClassMsType) baseClassMsType);
}
else if (baseClassMsType instanceof AbstractVirtualBaseClassMsType) {
applyDirectVirtualBaseClass((AbstractVirtualBaseClassMsType) baseClassMsType);
}
else if (baseClassMsType instanceof AbstractIndirectVirtualBaseClassMsType) {
applyIndirectVirtualBaseClass(
(AbstractIndirectVirtualBaseClassMsType) baseClassMsType);
}
else {
throw new AssertException(
"Unknown base class type: " + baseClassMsType.getClass().getSimpleName());
}
}
}
private void applyDirectBaseClass(AbstractBaseClassMsType base) throws PdbException {
CppCompositeType underlyingClassType =
getUnderlyingClassType(base.getBaseClassRecordNumber());
if (underlyingClassType == null) {
return;
}
ClassFieldMsAttributes atts = base.getAttributes();
int offset = PdbApplicator.bigIntegerToInt(applicator, base.getOffset());
classType.addDirectBaseClass(underlyingClassType, convertAttributes(atts), offset);
}
private void applyDirectVirtualBaseClass(AbstractVirtualBaseClassMsType base)
throws PdbException {
CppCompositeType underlyingCt = getUnderlyingClassType(base.getBaseClassRecordNumber());
if (underlyingCt == null) {
return;
}
DataType vbtptr =
getVirtualBaseTablePointerDataType(base.getVirtualBasePointerRecordNumber());
ClassFieldMsAttributes atts = base.getAttributes();
int basePointerOffset =
PdbApplicator.bigIntegerToInt(applicator, base.getBasePointerOffset());
int offsetFromVbt = PdbApplicator.bigIntegerToInt(applicator, base.getBaseOffsetFromVbt());
classType.addDirectVirtualBaseClass(underlyingCt, convertAttributes(atts),
basePointerOffset, vbtptr, offsetFromVbt);
}
private void applyIndirectVirtualBaseClass(AbstractIndirectVirtualBaseClassMsType base)
throws PdbException {
CppCompositeType underlyingCt = getUnderlyingClassType(base.getBaseClassRecordNumber());
if (underlyingCt == null) {
return;
}
DataType vbtptr =
getVirtualBaseTablePointerDataType(base.getVirtualBasePointerRecordNumber());
ClassFieldMsAttributes atts = base.getAttributes();
int basePointerOffset =
PdbApplicator.bigIntegerToInt(applicator, base.getBasePointerOffset());
int offsetFromVbt = PdbApplicator.bigIntegerToInt(applicator, base.getBaseOffsetFromVbt());
classType.addIndirectVirtualBaseClass(underlyingCt, convertAttributes(atts),
basePointerOffset, vbtptr, offsetFromVbt);
}
private CppCompositeType getUnderlyingClassType(RecordNumber recordNumber) {
AbstractMsTypeApplier baseUnderlyingApplier = applicator.getTypeApplier(recordNumber);
if (!(baseUnderlyingApplier instanceof CompositeTypeApplier)) {
applicator.appendLogMsg(baseUnderlyingApplier.getClass().getSimpleName() +
" seen where CompositeTypeApplier expected for base class.");
return null;
}
CompositeTypeApplier baseApplier = (CompositeTypeApplier) baseUnderlyingApplier;
CppCompositeType underlyingClassType = baseApplier.getClassType();
if (underlyingClassType == null) {
applicator.appendLogMsg("Underlying base class type is null.");
}
return underlyingClassType;
}
private DataType getVirtualBaseTablePointerDataType(RecordNumber recordNumber) {
AbstractMsTypeApplier vbptrApplier = applicator.getTypeApplier(recordNumber);
if (vbptrApplier != null) {
if (vbptrApplier instanceof PointerTypeApplier) {
return vbptrApplier.getDataType();
}
}
applicator.appendLogMsg("Generating a generic Virtual Base Table Pointer.");
return new PointerDataType();
}
private static CppCompositeType.ClassFieldAttributes convertAttributes(
ClassFieldMsAttributes atts) {
CppCompositeType.Access myAccess;
switch (atts.getAccess()) {
case PUBLIC:
myAccess = CppCompositeType.Access.PUBLIC;
break;
case PROTECTED:
myAccess = CppCompositeType.Access.PROTECTED;
break;
case PRIVATE:
myAccess = CppCompositeType.Access.PRIVATE;
break;
default:
myAccess = CppCompositeType.Access.BLANK;
break;
}
CppCompositeType.Property myProperty;
switch (atts.getProperty()) {
case VIRTUAL:
myProperty = CppCompositeType.Property.VIRTUAL;
break;
case STATIC:
myProperty = CppCompositeType.Property.STATIC;
break;
case FRIEND:
myProperty = CppCompositeType.Property.FRIEND;
break;
default:
myProperty = CppCompositeType.Property.BLANK;
break;
}
return new CppCompositeType.ClassFieldAttributes(myAccess, myProperty);
}
private void addMembers(Composite composite, FieldListTypeApplier fieldListApplier) {
AbstractCompositeMsType type = (AbstractCompositeMsType) msType;
for (AbstractMsTypeApplier memberTypeApplierIterated : fieldListApplier.getMemberList()) {
boolean handled = true;
if (memberTypeApplierIterated instanceof MemberTypeApplier) {
MemberTypeApplier memberTypeApplier = (MemberTypeApplier) memberTypeApplierIterated;
String memberName = memberTypeApplier.getName();
int offset =
PdbApplicator.bigIntegerToInt(applicator, memberTypeApplier.getOffset());
ClassFieldMsAttributes memberAttributes = memberTypeApplier.getAttribute();
memberAttributes.getAccess(); // TODO: do something with this and other attributes
AbstractMsTypeApplier fieldApplier = memberTypeApplier.getFieldTypeApplier();
if (fieldApplier instanceof CompositeTypeApplier) {
CompositeTypeApplier defApplier =
((CompositeTypeApplier) fieldApplier).getDefinitionApplier(
CompositeTypeApplier.class);
if (defApplier != null) {
fieldApplier = defApplier;
}
}
DataType fieldDataType = fieldApplier.getDataType();
boolean isFlexibleArray;
if (fieldApplier instanceof ArrayTypeApplier) {
isFlexibleArray = ((ArrayTypeApplier) fieldApplier).isFlexibleArray();
}
else {
isFlexibleArray = false;
}
if (fieldDataType == null) {
if (fieldApplier instanceof PrimitiveTypeApplier &&
((PrimitiveTypeApplier) fieldApplier).isNoType()) {
Default2PdbMember member =
new Default2PdbMember(applicator, memberName, fieldApplier, offset);
members.add(member);
componentComments.put(offset, "NO_TYPE");
}
else {
applicator.appendLogMsg("PDB Warning: No conversion for " + memberName +
" " + fieldApplier.getMsType().getClass().getSimpleName() +
" in composite " + composite.getName());
}
}
else {
Default2PdbMember member =
new Default2PdbMember(applicator, memberName, fieldApplier, offset);
members.add(member);
classType.addMember(memberName, fieldDataType, isFlexibleArray,
convertAttributes(memberAttributes), offset);
}
}
else if (memberTypeApplierIterated instanceof EnumerateTypeApplier) {
EnumerateTypeApplier enumerateTypeApplier =
(EnumerateTypeApplier) memberTypeApplierIterated;
String fieldName = enumerateTypeApplier.getName();
Numeric numeric = enumerateTypeApplier.getNumeric();
// TODO: some work
PdbLog.message("Don't know how to apply EnumerateTypeApplier fieldName " +
fieldName + " and value " + numeric + " within " + msType.getName());
}
else if (memberTypeApplierIterated instanceof VirtualFunctionTablePointerTypeApplier) {
VirtualFunctionTablePointerTypeApplier vtPtrApplier =
(VirtualFunctionTablePointerTypeApplier) memberTypeApplierIterated;
String vftPtrMemberName = vtPtrApplier.getMemberName();
int offset = vtPtrApplier.getOffset();
Default2PdbMember member =
new Default2PdbMember(applicator, vftPtrMemberName, vtPtrApplier, offset);
members.add(member);
//classType.addMember(vftPtrMemberName, vtPtrApplier.getDataType(), false, offset);
classType.addVirtualFunctionTablePointer(vftPtrMemberName,
vtPtrApplier.getDataType(), offset);
}
else if (memberTypeApplierIterated instanceof NestedTypeApplier) {
// Need to make sure that "this" class id dependent on all elements composing the
// nested definition, but we need to create the nested definition during the
// creation of this class. (NestedTypeApplier and NestedTypeMsType do not really
// have their own RecordNumber).
// 20200114: think this is a nested typedef.
NestedTypeApplier nestedTypeApplier = (NestedTypeApplier) memberTypeApplierIterated;
String memberTypeName = nestedTypeApplier.getTypeName();
String memberName = nestedTypeApplier.getMemberName(); // use this
// TODO: we are assuming that the offset is zero (0) for the call. Need to dig
// more to confirm this. Is ever anything but just one nested type? The pdb.exe
// generates these all at offset 0.
// TODO: Nested types are currently an issue for
// DefaultCompositeMember.applyDataTypeMembers().
// Need to investigate what to do here. It could be just when the specific
// composite is a member of itself.
if (type.getName().equals(memberTypeName)) {
// We are skipping because we've had issues and do not know what is going on
// at the moment. (I think they were dependency issues... been a while.)
// See not above the "if" condition.
// PdbLog.message("Skipping Composite Nested type member: " + memberName +
// " within " + type.getName());
// TODO: Investigate. What does it mean when the internally defined type
// conficts with the name of the outer type.
continue;
}
// TODO: believe the thing to do is to show that these are types that are
// defined within the namespace of this containing type. This might be
// the place to do it... that is if we don't identify them separately
// falling under the namespace of this composite.
// AbstractMsTypeApplier nestedDefinitionApplier =
// nestedTypeApplier.getNestedTypeDefinitionApplier().getDependencyApplier();
//
// DataType ndt = nestedDefinitionApplier.getDataType(); //use this
// int ndtl = ndt.getLength(); //use this
//
// AbstractMsType ndms = nestedTypeApplier.getMsType();
//
// BigInteger val = nestedTypeApplier.getSize();
// int offset = 0; // ???? TODO..,
// DataType nt = nestedTypeApplier.getDataType();
// ClassFieldMsAttributes a = nestedTypeApplier.getAttributes();
//
// // TODO: hoping this is right... 20200521... how/where do we get offset?
// Default2PdbMember member =
// new Default2PdbMember(applicator, memberName, nestedDefinitionApplier, offset);
// members.add(member);
}
else if (memberTypeApplierIterated instanceof NoTypeApplier) {
AbstractMsType msNoType = memberTypeApplierIterated.getMsType();
if (msNoType instanceof AbstractStaticMemberMsType) {
// TODO: Investigate anything that hits here (set break point), see if we
// see dot apply the information. If so, probably should create an applier
// for the contained MS type.
}
else {
handled = false;
}
}
else {
handled = false;
}
if (!handled) {
applicator.appendLogMsg(
memberTypeApplierIterated.getClass().getSimpleName() + " with contained " +
memberTypeApplierIterated.getMsType().getClass().getSimpleName() +
" unexpected for " + msType.getName());
}
}
}
// /**
// * <code>NoType</code> provides ability to hang NoType into a composite type by faking
// * it with a zero-length bitfield. This is a bit of a kludge
// * This will be transformed to a normal BitFieldDataType when cloned.
// */
// private class NoType extends PdbBitField {
// private NoType(PdbApplicator applicator) throws InvalidDataTypeException {
// super(new CharDataType(applicator.getDataTypeManager()), 0, 0);
// }
// }
//
}

View file

@ -0,0 +1,270 @@
/* ###
* 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.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.RecordNumber;
import ghidra.app.util.bin.format.pdb2.pdbreader.symbol.AbstractDataMsSymbol;
import ghidra.app.util.bin.format.pdb2.pdbreader.symbol.AbstractMsSymbol;
import ghidra.app.util.pdb.pdbapplicator.SymbolGroup.AbstractMsSymbolIterator;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.DumbMemBufferImpl;
import ghidra.util.exception.AssertException;
import ghidra.util.exception.CancelledException;
/**
* Applier for {@link AbstractDataMsSymbol} symbols.
*/
public class DataSymbolApplier extends AbstractMsSymbolApplier {
private AbstractDataMsSymbol symbol;
public DataSymbolApplier(PdbApplicator applicator, AbstractMsSymbolIterator iter) {
super(applicator, iter);
AbstractMsSymbol abstractSymbol = iter.next();
if (!(abstractSymbol instanceof AbstractDataMsSymbol)) {
throw new AssertException(
"Invalid symbol type: " + abstractSymbol.getClass().getSimpleName());
}
symbol = (AbstractDataMsSymbol) abstractSymbol;
}
@Override
public void applyTo(AbstractMsSymbolApplier applyToApplier)
throws PdbException, CancelledException {
if (applyToApplier instanceof FunctionSymbolApplier) {
FunctionSymbolApplier functionSymbolApplier = (FunctionSymbolApplier) applyToApplier;
if (symbol.getSegment() == 0 && symbol.getOffset() == 0L) {
return; // silently return.
}
AbstractMsTypeApplier applier = getTypeApplier();
DataType dataType = applier.getDataType();
if (dataType == null) { // TODO: check that we can have null here.
return; // silently return.
}
String name = symbol.getName();
Address address = applicator.reladdr(symbol);
functionSymbolApplier.setLocalVariable(address, name, dataType);
}
}
@Override
public void apply() throws CancelledException, PdbException {
// skip if both zero (i.e,. process if either is not zero)
if (symbol.getSegment() != 0 || symbol.getOffset() != 0L) {
Address symbolAddress = applicator.reladdr(symbol);
Address remapAddress = applicator.getRemapAddressByAddress(symbolAddress);
RecordNumber typeRecordNumber = symbol.getTypeRecordNumber();
boolean forcePrimary = applicator.shouldForcePrimarySymbol(symbolAddress, true);
String name = symbol.getName();
if (!applicator.createSymbol(symbolAddress, name, forcePrimary)) {
applicator.appendLogMsg("Unable to create symbol " + name + " at " + symbolAddress);
}
createData(symbolAddress, typeRecordNumber);
}
}
AbstractMsTypeApplier getTypeApplier() {
return applicator.getTypeApplier(symbol.getTypeRecordNumber());
}
void createData(Address address, RecordNumber typeRecordNumber) {
AbstractMsTypeApplier applier = applicator.getTypeApplier(typeRecordNumber);
if (applier == null) {
applicator.appendLogMsg("Error: Failed to resolve datatype RecordNumber " +
typeRecordNumber + " at " + address);
return;
}
DataType dataType = applier.getDataType();
if (dataType == null) {
if (!(applier instanceof PrimitiveTypeApplier) ||
!((PrimitiveTypeApplier) applier).isNoType()) {
applicator.appendLogMsg("Error: Failed to resolve datatype " +
applier.getMsType().getName() + " at " + address);
}
return;
}
if (!(dataType instanceof FunctionDefinition)) {
//TODO: might want to do an ApplyDatatypeCmd here!!!
DumbMemBufferImpl memBuffer =
new DumbMemBufferImpl(applicator.getProgram().getMemory(), address);
DataTypeInstance dti = DataTypeInstance.getDataTypeInstance(dataType, memBuffer);
if (dti == null) {
applicator.appendLogMsg(
"Error: Failed to apply datatype " + dataType.getName() + " at " + address);
return;
}
createData(address, dti.getDataType(), dti.getLength());
}
}
private void createData(Address address, DataType dataType, int dataTypeLength) {
// Ensure that we do not clear previously established code and data
Data existingData = null;
CodeUnit cu = applicator.getProgram().getListing().getCodeUnitContaining(address);
if (cu != null) {
if ((cu instanceof Instruction) || !address.equals(cu.getAddress())) {
applicator.appendLogMsg("Warning: Did not create data type \"" +
dataType.getDisplayName() + "\" at address " + address + " due to conflict");
return;
}
Data d = (Data) cu;
if (d.isDefined()) {
existingData = d;
}
}
if (dataType == null) {
return;
}
if (dataType.getLength() <= 0 && dataTypeLength <= 0) {
applicator.appendLogMsg("Unknown dataTypeLength specified at address " + address +
" for " + dataType.getName());
return;
}
// TODO: This is really bad logic and should be refactored
// All conflicting data, not just the one containing address,
// needs to be considered and not blindly cleared.
if (existingData != null) {
DataType existingDataType = existingData.getDataType();
if (isEquivalent(existingData, existingData.getLength(), dataType)) {
return;
}
if (isEquivalent2(existingDataType, dataType)) {
return;
}
if (existingDataType.isEquivalent(dataType)) {
return;
}
}
if (existingData == null) {
try {
applicator.getProgram().getListing().clearCodeUnits(address,
address.add(dataTypeLength - 1), false);
if (dataType.getLength() == -1) {
applicator.getProgram().getListing().createData(address, dataType,
dataTypeLength);
}
else {
applicator.getProgram().getListing().createData(address, dataType);
}
}
catch (Exception e) {
applicator.appendLogMsg("Unable to create " + dataType.getDisplayName() + " at 0x" +
address + ": " + e.getMessage());
}
}
else if (isDataReplaceable(existingData)) {
try {
applicator.getProgram().getListing().clearCodeUnits(address,
address.add(dataTypeLength - 1), false);
applicator.getProgram().getListing().createData(address, dataType, dataTypeLength);
}
catch (Exception e) {
applicator.appendLogMsg("Unable to replace " + dataType.getDisplayName() +
" at 0x" + address + ": " + e.getMessage());
}
}
else {
DataType existingDataType = existingData.getDataType();
String existingDataTypeString =
existingDataType == null ? "null" : existingDataType.getDisplayName();
applicator.appendLogMsg("Warning: Did not create data type \"" +
dataType.getDisplayName() + "\" at address " + address +
". Preferring existing datatype \"" + existingDataTypeString + "\"");
}
}
private boolean isDataReplaceable(Data data) {
DataType dataType = data.getDataType();
if (dataType instanceof Pointer) {
Pointer pointer = (Pointer) dataType;
DataType pointerDataType = pointer.getDataType();
if (pointerDataType == null || pointerDataType.isEquivalent(DataType.DEFAULT)) {
return true;
}
}
else if (dataType instanceof Array) {
Array array = (Array) dataType;
DataType arrayDataType = array.getDataType();
if (arrayDataType == null || arrayDataType.isEquivalent(DataType.DEFAULT)) {
return true;
}
}
// All forms of Undefined data are replaceable
// TODO: maybe it should check the length of the data type before putting it down.
if (Undefined.isUndefined(dataType)) {
return true;
}
return false;
}
private boolean isEquivalent(Data existingData, int existingDataTypeLength,
DataType newDataType) {
if (existingData.hasStringValue()) {
if (newDataType instanceof ArrayDataType) {
Array array = (Array) newDataType;
DataType arrayDataType = array.getDataType();
if (arrayDataType instanceof ArrayStringable) {
if (array.getLength() == existingDataTypeLength) {
return true;
}
}
}
}
return false;
}
/**
* "char[12] *" "char * *"
*
* "ioinfo * *" "ioinfo[64] *"
*/
private boolean isEquivalent2(DataType datatype1, DataType datatype2) {
if (datatype1 == datatype2) {
return true;
}
if (datatype1 == null || datatype2 == null) {
return false;
}
if (datatype1 instanceof Array) {
Array array1 = (Array) datatype1;
if (datatype2 instanceof Array) {
Array array2 = (Array) datatype2;
return isEquivalent2(array1.getDataType(), array2.getDataType());
}
}
else if (datatype1 instanceof Pointer) {
Pointer pointer1 = (Pointer) datatype1;
if (datatype2 instanceof Array) {
Array array2 = (Array) datatype2;
return isEquivalent2(pointer1.getDataType(), array2.getDataType());
}
}
return datatype1.isEquivalent(datatype2);
}
}

View file

@ -0,0 +1,108 @@
/* ###
* 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.math.BigInteger;
import ghidra.app.util.bin.format.pdb.*;
import ghidra.program.model.data.DataType;
import ghidra.util.exception.CancelledException;
/**
* <code>PdbMember</code> convey PDB member information used for datatype
* reconstruction.
*/
public class Default2PdbMember extends PdbMember {
private AbstractMsTypeApplier applier;
private DataType dataType;
/**
* Default PDB member construction
* @param applicator {@link PdbApplicator} for which we are working.
* @param name member field name. For bitfields this also conveys the bit-size
* and optionally the bit-offset.
* @param applier fieldApplier for the field datatype or base datatype associated with the
* bitfield.
* @param offset member's byte offset within the root composite.
*/
Default2PdbMember(PdbApplicator applicator, String name, AbstractMsTypeApplier applier,
int offset) {
super(name, (applier.getDataType()).getName(), offset, null);
this.applier = applier;
dataType = null;
}
/**
* Default PDB member construction
* @param applicator {@link PdbApplicator} for which we are working.
* @param name member field name. For bitfields this also conveys the bit-size
* and optionally the bit-offset.
* @param dataType for the field.
* @param offset member's byte offset within the root composite.
*/
Default2PdbMember(PdbApplicator applicator, String name, DataType dataType, int offset) {
super(name, dataType.getName(), offset, null);
this.applier = null;
this.dataType = dataType;
}
AbstractMsTypeApplier getApplier() {
return applier;
}
private DataType getDataTypeInternal() {
if (applier != null) {
return applier.getDataType();
}
return dataType;
}
@Override
public String getDataTypeName() {
return getDataTypeInternal().getName();
}
@Override
public String toString() {
String str = "name=" + getName();
DataType dt = getDataTypeInternal();
if (dt instanceof PdbBitField) {
PdbBitField bfDt = (PdbBitField) dataType;
str += ", type=" + bfDt.getBaseDataType().getName();
str += ", offset=" + getOffset();
str += ", bitSize=" + bfDt.getDeclaredBitSize() + ", bitOffset=" +
bfDt.getBitOffsetWithinBase();
}
else {
str += ", type=" + dataType.getName();
str += ", offset=" + getOffset();
}
return str;
}
@Override
protected WrappedDataType getDataType() throws CancelledException {
DataType dt = getDataTypeInternal();
if (applier != null && applier instanceof ArrayTypeApplier) {
if (BigInteger.ZERO.compareTo(applier.getSize()) == 0) {
return new WrappedDataType(dt, true, false);
}
}
return new WrappedDataType(dt, false, false);
}
}

View file

@ -0,0 +1,75 @@
/* ###
* 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.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbLog;
import ghidra.app.util.bin.format.pdb2.pdbreader.symbol.*;
import ghidra.app.util.pdb.pdbapplicator.SymbolGroup.AbstractMsSymbolIterator;
import ghidra.util.Msg;
import ghidra.util.exception.AssertException;
import ghidra.util.exception.CancelledException;
/**
* Applier for {@link AbstractDefinedSingleAddressRangeMsSymbol} symbols.
*/
public class DefinedSingleAddressRangeSymbolApplier extends AbstractMsSymbolApplier {
private AbstractDefinedSingleAddressRangeMsSymbol symbol;
public DefinedSingleAddressRangeSymbolApplier(PdbApplicator applicator,
AbstractMsSymbolIterator iter) {
super(applicator, iter);
AbstractMsSymbol abstractSymbol = iter.next();
if (!(abstractSymbol instanceof AbstractDefinedSingleAddressRangeMsSymbol)) {
throw new AssertException(
"Invalid symbol type: " + abstractSymbol.getClass().getSimpleName());
}
symbol = (AbstractDefinedSingleAddressRangeMsSymbol) abstractSymbol;
}
@Override
public void apply() throws PdbException, CancelledException {
String message = "Cannot apply " + this.getClass().getSimpleName() + " directly to program";
Msg.info(this, message);
PdbLog.message(message);
}
@Override
public void applyTo(AbstractMsSymbolApplier applyToApplier)
throws PdbException, CancelledException {
if (applyToApplier instanceof LocalOptimizedSymbolApplier) {
// TODO: eventually apply.
// LocalOptimizedSymbolApplier localSymbolApplier =
// (LocalOptimizedSymbolApplier) applyToApplier;
symbol.getAddressRange();
symbol.getAddressGapList();
if (symbol instanceof EnregisteredSymbolDARMsSymbol) {
EnregisteredSymbolDARMsSymbol sym1 = (EnregisteredSymbolDARMsSymbol) symbol;
sym1.getRangeAttribute();
sym1.getRegister();
}
else if (symbol instanceof EnregisteredFieldOfSymbolDARMsSymbol) {
EnregisteredFieldOfSymbolDARMsSymbol sym1 =
(EnregisteredFieldOfSymbolDARMsSymbol) symbol;
sym1.getOffsetInParent();
sym1.getRangeAttribute();
sym1.getRegister();
}
}
}
}

View file

@ -0,0 +1,70 @@
/* ###
* 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.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbLog;
import ghidra.app.util.bin.format.pdb2.pdbreader.symbol.AbstractMsSymbol;
import ghidra.app.util.bin.format.pdb2.pdbreader.symbol.EndMsSymbol;
import ghidra.app.util.pdb.pdbapplicator.SymbolGroup.AbstractMsSymbolIterator;
import ghidra.util.Msg;
import ghidra.util.exception.AssertException;
/**
* Applier for {@link EndMsSymbol} symbols.
*/
public class EndSymbolApplier extends AbstractMsSymbolApplier {
public EndSymbolApplier(PdbApplicator applicator, AbstractMsSymbolIterator iter) {
super(applicator, iter);
AbstractMsSymbol abstractSymbol = iter.next();
if (!(abstractSymbol instanceof EndMsSymbol)) {
throw new AssertException(
"Invalid symbol type: " + abstractSymbol.getClass().getSimpleName());
}
}
@Override
public void apply() throws PdbException {
String message =
String.format("Cannot apply %s directly to program (module:0X%04X, offset:0X%08X)",
this.getClass().getSimpleName(), iter.getModuleNumber(), iter.getCurrentOffset());
Msg.info(this, message);
PdbLog.message(message);
}
@Override
public void applyTo(AbstractMsSymbolApplier applyToApplier) {
if (!(applyToApplier instanceof FunctionSymbolApplier)) {
return;
}
FunctionSymbolApplier functionSymbolApplier = (FunctionSymbolApplier) applyToApplier;
// functionSymbolApplier.endBlock();
}
@Override
public void manageBlockNesting(AbstractMsSymbolApplier applierParam) {
if (applierParam instanceof FunctionSymbolApplier) {
FunctionSymbolApplier functionSymbolApplier = (FunctionSymbolApplier) applierParam;
functionSymbolApplier.endBlock();
}
else if (applierParam instanceof SeparatedCodeSymbolApplier) {
SeparatedCodeSymbolApplier separatedCodeSymbolApplier =
(SeparatedCodeSymbolApplier) applierParam;
separatedCodeSymbolApplier.endBlock();
}
}
}

View file

@ -0,0 +1,321 @@
/* ###
* 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.math.BigInteger;
import java.util.List;
import ghidra.app.util.SymbolPath;
import ghidra.app.util.bin.format.pdb2.pdbreader.*;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.*;
import ghidra.program.model.data.*;
import ghidra.util.Msg;
import ghidra.util.exception.CancelledException;
/**
* Applier for {@link AbstractEnumMsType} types.
*/
public class EnumTypeApplier extends AbstractComplexTypeApplier {
//private AbstractMsTypeApplier underlyingApplier = null;
// private int length = 0;
// private boolean isSigned = false;
//
private static AbstractMsType validateType(AbstractMsType type)
throws IllegalArgumentException {
if (!(type instanceof AbstractEnumMsType)) {
throw new IllegalArgumentException("PDB Incorrectly applying " +
type.getClass().getSimpleName() + " to " + EnumTypeApplier.class.getSimpleName());
}
return type;
}
/**
* Constructor for enum type applier, for transforming a enum into a
* Ghidra DataType.
* @param applicator {@link PdbApplicator} for which this class is working.
* @param msType {@link AbstractEnumMsType} to process.
* @throws IllegalArgumentException Upon invalid arguments.
*/
public EnumTypeApplier(PdbApplicator applicator, AbstractMsType msType)
throws IllegalArgumentException {
super(applicator, validateType(msType));
}
@Override
public BigInteger getSize() {
AbstractMsTypeApplier underlyingApplier = getUnderlyingTypeApplier();
if (underlyingApplier == null) {
return BigInteger.ZERO;
}
return underlyingApplier.getSize();
}
private long getMask() {
switch (getLength()) {
case 1:
return 0xffL;
case 2:
return 0xffffL;
case 4:
return 0xffffffffL;
default:
return 0xffffffffffffffffL;
}
}
private int getLength() {
// Minimum length allowed by Ghidra is 1 for enum, so all returns are min 1.
AbstractMsTypeApplier underlyingApplier = getUnderlyingTypeApplier();
if (underlyingApplier == null) {
return 1;
}
DataType underlyingType = underlyingApplier.getDataType();
if (underlyingType == null) {
return 1;
}
return Integer.max(underlyingType.getLength(), 1);
}
public boolean isSigned() {
AbstractMsTypeApplier underlyingApplier = getUnderlyingTypeApplier();
if (underlyingApplier == null) {
return false;
}
DataType underlyingType = underlyingApplier.getDataType();
if (underlyingType == null) {
return false;
}
if (underlyingType instanceof AbstractIntegerDataType) {
return ((AbstractIntegerDataType) underlyingType).isSigned();
}
return false;
}
@Override
public EnumTypeApplier getDependencyApplier() {
if (definitionApplier != null && definitionApplier instanceof EnumTypeApplier) {
return (EnumTypeApplier) definitionApplier;
}
return this;
}
public String getName() {
return getMsType().getName();
}
private AbstractMsTypeApplier getUnderlyingTypeApplier() {
AbstractMsTypeApplier under = null;
AbstractMsTypeApplier applier = (definitionApplier != null) ? definitionApplier : this;
RecordNumber underlyingRecordNumber =
((AbstractEnumMsType) applier.getMsType()).getUnderlyingRecordNumber();
under = applicator.getTypeApplier(underlyingRecordNumber);
if (under == null) {
applicator.appendLogMsg("Missing applier for underlying type index " +
underlyingRecordNumber + " in Enum " + msType.getName());
}
return under;
}
private EnumDataType createEmptyEnum(AbstractEnumMsType type) throws PdbException {
SymbolPath fixedPath = getFixedSymbolPath();
CategoryPath categoryPath = applicator.getCategory(fixedPath.getParent());
// MsProperty property = type.getMsProperty();
// if (property.isForwardReference()) {
// int a = 1;
// a = a + 1;
// }
//// RecordNumber underlyingRecordNumber = type.getUnderlyingRecordNumber();
//// underlyingApplier = applicator.getApplier(underlyingRecordNumber);
// determineUnderlyingTypeApplier();
//
// if (underlyingApplier == null) {
// return null;
// }
// DataType underlyingType = underlyingApplier.getDataType();
// if (underlyingType != null) {
// length = underlyingType.getLength();
// if (underlyingType instanceof AbstractIntegerDataType) {
// isSigned = ((AbstractIntegerDataType) underlyingType).isSigned();
// }
// else if (!(underlyingType instanceof VoidDataType)) {
// String msg = "Cannot processes enum with underlying type: " +
// underlyingType.getClass().getSimpleName();
// Msg.info(this, msg);
// PdbLog.message(msg);
// throw new PdbException(msg);
// }
// }
// else {
// AbstractMsType underlying = underlyingApplier.getMsType();
// if (underlying instanceof PrimitiveMsType) {
// length = ((PrimitiveMsType) underlying).getTypeSize();
// //TODO: can we set isSigned in here? ((PrimitiveMsType) underlying)
// // TODO: there might be more
// // TODO: investigate getSize() on AbstractMsType?
// // then: length = underlying.getSize();
// }
// }
// // Ghidra does not like size of zero.
// length = Integer.max(length, 1);
EnumDataType enumDataType = new EnumDataType(categoryPath, fixedPath.getName(), getLength(),
applicator.getDataTypeManager());
return enumDataType;
}
@Override
public void apply() throws PdbException, CancelledException {
getOrCreateEnum();
AbstractEnumMsType type = (AbstractEnumMsType) msType;
MsProperty property = type.getMsProperty();
if (property.isForwardReference()) {
return;
}
applyEnumMsType((AbstractEnumMsType) msType);
}
@Override
public void resolve() {
if (!isForwardReference()) {
super.resolve();
}
}
// Mapping of fwdRef/def must be done prior to this call.
private void getOrCreateEnum() throws PdbException {
AbstractEnumMsType neededType = (AbstractEnumMsType) msType;
if (dataType != null) {
return;
}
if (isForwardReference()) {
if (definitionApplier != null) {
dataType = definitionApplier.getDataTypeInternal();
if (dataType != null) {
return;
}
neededType = (AbstractEnumMsType) definitionApplier.getMsType();
}
}
else {
if (fwdRefApplier != null) {
dataType = fwdRefApplier.getDataTypeInternal();
if (dataType != null) {
return;
}
}
}
dataType = createEmptyEnum(neededType);
}
private EnumDataType applyEnumMsType(AbstractEnumMsType type) throws PdbException {
String fullPathName = type.getName();
// // TODO: evaluate whether we do full SymbolPath... see others
// SymbolPath fixedPath = getFixedSymbolPath();
//
// RecordNumber underlyingRecordNumber = type.getUnderlyingRecordNumber();
// MsProperty property = type.getMsProperty();
// if (property.isForwardReference()) {
// int a = 1;
// a = a + 1;
// }
// underlyingApplier = applicator.getApplier(underlyingRecordNumber);
//
// if (underlyingApplier == null) {
// applicator.appendLogMsg("Missing applier for underlying type index " +
// underlyingRecordNumber + " in Enum " + fullPathName);
// return null;
// }
// DataType underlyingType = underlyingApplier.getDataType();
// int length = 0;
// if (underlyingType != null) {
// length = underlyingType.getLength();
// }
// else {
// AbstractMsType underlying = underlyingApplier.getMsType();
// if (underlying instanceof PrimitiveMsType) {
// length = ((PrimitiveMsType) underlying).getTypeSize();
// // TODO: there might be more
// // TODO: investigate getSize() on AbstractMsType?
// // then: length = underlying.getSize();
// }
// }
// // Ghidra does not like size of zero.
// length = Integer.max(length, 1);
//
// CategoryPath categoryPath = applicator.getCategory(fixedPath.getParent());
// EnumDataType enumDataType = new EnumDataType(categoryPath, fixedPath.getName(), length,
// applicator.getDataTypeManager());
//
RecordNumber fieldListRecordNumber = type.getFieldDescriptorListRecordNumber();
FieldListTypeApplier fieldListApplier =
FieldListTypeApplier.getFieldListApplierSpecial(applicator, fieldListRecordNumber);
// Note: not doing anything with getNamespaceList() or getMethodsList() at this time.
List<AbstractMsTypeApplier> memberList = fieldListApplier.getMemberList();
int numElements = type.getNumElements();
if (memberList.size() != numElements) {
String message = "Enum expecting " + numElements + " elements, but only " +
memberList.size() + " available for " + fullPathName;
Msg.info(this, message);
PdbLog.message(message);
}
EnumDataType enumDataType = (EnumDataType) dataType;
int length = getLength();
boolean isSigned = isSigned();
for (AbstractMsTypeApplier memberApplier : memberList) {
if (memberApplier instanceof EnumerateTypeApplier) {
EnumerateTypeApplier enumerateApplier = (EnumerateTypeApplier) memberApplier;
SymbolPath memberSymbolPath = new SymbolPath(enumerateApplier.getName());
enumDataType.add(memberSymbolPath.getName(),
narrowingConversion(length, isSigned, enumerateApplier.getNumeric()));
}
else { // (member instanceof AbstractMemberMsType)
// I do not believe (until proven otherwise) that an Enum will have members of
// type AbstractMemberMsType.
PdbLog.message(getClass().getSimpleName() + ": unexpected " +
memberApplier.getClass().getSimpleName());
}
}
return enumDataType;
}
private long narrowingConversion(int outputSize, boolean outputSigned, Numeric numeric) {
if (!numeric.isIntegral()) {
Msg.info(this, "Non-integral numeric found: " + numeric);
return 0;
}
if (!numeric.isIntegral()) {
PdbLog.message("Using zero in place of non-integral enumerate: " + numeric);
return 0L; //
}
return numeric.getIntegral().longValue() & getMask();
// return NarrowingConverter.narrowBigToLong(outputSize, outputSigned, numeric.getSize(),
// numeric.isSigned(), numeric.getIntegral());
}
}

View file

@ -0,0 +1,93 @@
/* ###
* 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.math.BigInteger;
import ghidra.app.util.bin.format.pdb2.pdbreader.Numeric;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.AbstractEnumerateMsType;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.AbstractMsType;
import ghidra.app.util.pdb.PdbNamespaceUtils;
import ghidra.program.model.data.DataType;
/**
* Applier for {@link AbstractEnumerateMsType} types.
*/
public class EnumerateTypeApplier extends AbstractMsTypeApplier {
private String fieldName;
private Numeric numeric;
private static AbstractMsType validateType(AbstractMsType type)
throws IllegalArgumentException {
if (!(type instanceof AbstractEnumerateMsType)) {
throw new IllegalArgumentException(
"PDB Incorrectly applying " + type.getClass().getSimpleName() + " to " +
EnumerateTypeApplier.class.getSimpleName());
}
return type;
}
/**
* Constructor for enumerate type applier, for transforming a enumerate into a
* Ghidra DataType.
* @param applicator {@link PdbApplicator} for which this class is working.
* @param msType {@link AbstractEnumerateMsType} to process.
* @throws IllegalArgumentException Upon invalid arguments.
*/
public EnumerateTypeApplier(PdbApplicator applicator, AbstractMsType msType)
throws IllegalArgumentException {
super(applicator, validateType(msType));
}
@Override
public BigInteger getSize() {
return BigInteger.ZERO;
}
@Override
public void apply() {
dataType = applyEnumerateMsType((AbstractEnumerateMsType) msType);
// DataType dataType = applyEnumerateMsType((AbstractEnumerateMsType) msType);
// ghDataType = dataType; // temporary while below is commented-out
// TODO: uncomment when above method not returning null
// ghDataTypeDB = applicator.resolve(dataType);
}
public String getName() {
return fieldName;
}
public Numeric getNumeric() {
return numeric;
}
private DataType applyEnumerateMsType(AbstractEnumerateMsType type) {
//TODO: currently dropping these on the floor. The access methods above do the same work.
fieldName = PdbNamespaceUtils.fixUnnamed(type.getName(), index);
// TODO: Need to build test sample with these.
// TODO: Need to see if can do real number; need to modify Numeric for both
// integral and real numbers. Possibly need to make Numeric a "type" instead
// of just something to read using ByteReader.
numeric = type.getNumeric();
return null;
}
}

View file

@ -0,0 +1,183 @@
/* ###
* 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.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import ghidra.app.util.bin.format.pdb2.pdbreader.*;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.*;
import ghidra.program.model.data.DataType;
import ghidra.util.Msg;
import ghidra.util.exception.CancelledException;
/**
* Applier for {@link AbstractFieldListMsType} types and {@code NO_TYPE} when in place of the
* former type.
*/
public class FieldListTypeApplier extends AbstractMsTypeApplier {
private List<AbstractMsTypeApplier> baseClassList = new ArrayList<>();
private List<AbstractMsTypeApplier> memberList = new ArrayList<>();
private List<AbstractMsTypeApplier> methodList = new ArrayList<>();
private boolean isEmpty;
// return can be null
static FieldListTypeApplier getFieldListApplierSpecial(PdbApplicator applicator,
RecordNumber recordNumber) throws PdbException {
AbstractMsTypeApplier applier =
applicator.getApplierOrNoTypeSpec(recordNumber, FieldListTypeApplier.class);
FieldListTypeApplier fieldListApplier = null;
if (applier instanceof FieldListTypeApplier) {
return (FieldListTypeApplier) applicator.getApplierOrNoTypeSpec(recordNumber,
FieldListTypeApplier.class);
}
try {
if (recordNumber.getCategory() == RecordCategory.TYPE) {
fieldListApplier = new FieldListTypeApplier(applicator,
applicator.getPdb().getTypeRecord(recordNumber), true);
}
}
catch (IllegalArgumentException e) {
applicator.appendLogMsg(e.getMessage());
}
return fieldListApplier;
}
/**
* Constructor.
* @param applicator {@link PdbApplicator} for which this class is working.
* @param msType {@link AbstractFieldListMsType} or {@link PrimitiveMsType} of {@code NO_TYPE}
* @throws IllegalArgumentException Upon invalid arguments.
*/
public FieldListTypeApplier(PdbApplicator applicator, AbstractMsType msType)
throws IllegalArgumentException {
this(applicator, msType, false);
}
/**
* Constructor with override for NO_TYPE Primitive.
* @param applicator {@link PdbApplicator} for which this class is working.
* @param msType {@link AbstractFieldListMsType} or {@link PrimitiveMsType} of {@code NO_TYPE}
* @param noType {@code true} to specify that {@code msType} is NO_TYPE.
* @throws IllegalArgumentException Upon invalid arguments.
*/
public FieldListTypeApplier(PdbApplicator applicator, AbstractMsType msType, boolean noType)
throws IllegalArgumentException {
super(applicator, msType);
if (noType && msType instanceof PrimitiveMsType && ((PrimitiveMsType) msType).isNoType()) {
this.isEmpty = true;
}
else {
if (!(msType instanceof AbstractFieldListMsType)) {
throw new IllegalArgumentException("PDB Incorrectly applying " +
msType.getClass().getSimpleName() + " to " + this.getClass().getSimpleName());
}
this.isEmpty = false;
}
}
/**
* Indicates that the list is empty
* @return {@code true} if list is empty.
*/
public boolean isEmpty() {
return isEmpty;
}
@Override
public BigInteger getSize() {
return BigInteger.ZERO;
}
@Override
public void apply() throws PdbException, CancelledException {
dataType = applyFieldListMsType((AbstractFieldListMsType) msType);
}
public List<AbstractMsTypeApplier> getBaseClassList() {
return baseClassList;
}
public List<AbstractMsTypeApplier> getMemberList() {
return memberList;
}
public List<AbstractMsTypeApplier> getMethodList() {
return methodList;
}
private DataType applyFieldListMsType(AbstractFieldListMsType type)
throws PdbException, CancelledException {
applyBaseClasses(type.getBaseClassList());
applyMembers(type.getMemberList());
applyMethods(type.getMethodList());
for (AbstractIndexMsType indexType : type.getIndexList()) {
AbstractMsTypeApplier referencedTypeApplier =
applicator.getTypeApplier(indexType.getReferencedRecordNumber());
if (referencedTypeApplier instanceof FieldListTypeApplier) {
FieldListTypeApplier subApplier = (FieldListTypeApplier) referencedTypeApplier;
baseClassList.addAll(subApplier.getBaseClassList());
memberList.addAll(subApplier.getMemberList());
methodList.addAll(subApplier.getMethodList());
}
else {
String message = "referenceTypeApplier is not FieldListTypeApplier";
Msg.info(this, message);
PdbLog.message(message);
}
}
return null;
}
private void applyBaseClasses(List<MsTypeField> baseClasses)
throws CancelledException, PdbException {
for (MsTypeField typeIterated : baseClasses) {
// Use dummy index of zero.
AbstractMsTypeApplier applier =
applicator.getTypeApplier((AbstractMsType) typeIterated);
applier.apply(); // Need to apply here, as these are embedded records
baseClassList.add(applier);
}
}
private void applyMembers(List<MsTypeField> members) throws CancelledException, PdbException {
for (MsTypeField typeIterated : members) {
// Use dummy index of zero.
AbstractMsTypeApplier applier =
applicator.getTypeApplier((AbstractMsType) typeIterated);
applier.apply(); // Need to apply here, as these are embedded records
memberList.add(applier);
}
}
private void applyMethods(List<MsTypeField> methods) throws CancelledException, PdbException {
for (MsTypeField typeIterated : methods) {
// Use dummy index of zero.
AbstractMsTypeApplier applier =
applicator.getTypeApplier((AbstractMsType) typeIterated);
// TODO: note that these are likely NoTypeAppliers at the moment, as we had not
// yet implemented appliers for AbstractOneMethodMsType and
// AbstractOverloadedMethodMsType
applier.apply(); // Need to apply here, as these are embedded records
methodList.add(applier);
}
}
}

View file

@ -0,0 +1,57 @@
/* ###
* 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.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.symbol.AbstractMsSymbol;
import ghidra.app.util.bin.format.pdb2.pdbreader.symbol.ExtraFrameAndProcedureInformationMsSymbol;
import ghidra.app.util.pdb.pdbapplicator.SymbolGroup.AbstractMsSymbolIterator;
import ghidra.util.exception.AssertException;
import ghidra.util.exception.CancelledException;
/**
* Applier for {@link ExtraFrameAndProcedureInformationMsSymbol} symbols.
*/
public class FrameAndProcedureInformationSymbolApplier extends AbstractMsSymbolApplier {
private ExtraFrameAndProcedureInformationMsSymbol symbol;
public FrameAndProcedureInformationSymbolApplier(PdbApplicator applicator,
AbstractMsSymbolIterator iter) {
super(applicator, iter);
AbstractMsSymbol abstractSymbol = iter.next();
if (!(abstractSymbol instanceof ExtraFrameAndProcedureInformationMsSymbol)) {
throw new AssertException(
"Invalid symbol type: " + abstractSymbol.getClass().getSimpleName());
}
symbol = (ExtraFrameAndProcedureInformationMsSymbol) abstractSymbol;
}
@Override
public void applyTo(AbstractMsSymbolApplier applyToApplier)
throws PdbException, CancelledException {
if (applyToApplier instanceof FunctionSymbolApplier) {
FunctionSymbolApplier functionSymbolApplier = (FunctionSymbolApplier) applyToApplier;
// public void applyTo(FunctionSymbolApplier functionSymbolApplier) {
functionSymbolApplier.setSpecifiedFrameSize(symbol.getProcedureFrameTotalLength());
}
}
@Override
public void apply() {
// Quietly do nothing
}
}

View file

@ -0,0 +1,456 @@
/* ###
* 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.cmd.disassemble.DisassembleCommand;
import ghidra.app.cmd.function.*;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.RecordNumber;
import ghidra.app.util.bin.format.pdb2.pdbreader.symbol.*;
import ghidra.app.util.pdb.pdbapplicator.SymbolGroup.AbstractMsSymbolIterator;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSet;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.FunctionDefinition;
import ghidra.program.model.lang.Register;
import ghidra.program.model.listing.*;
import ghidra.program.model.symbol.SourceType;
import ghidra.util.exception.AssertException;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
/**
* Applier for {@link AbstractProcedureStartMsSymbol} and {@link AbstractThunkMsSymbol} symbols.
*/
public class FunctionSymbolApplier extends AbstractMsSymbolApplier {
private static final String BLOCK_INDENT = " ";
private AbstractProcedureMsSymbol procedureSymbol;
private AbstractThunkMsSymbol thunkSymbol;
private Address specifiedAddress;
private Address remapAddress;
private Function function = null;
private long specifiedFrameSize = 0;
private long currentFrameSize = 0;
private BlockCommentsManager comments;
private int symbolBlockNestingLevel;
private Address currentBlockAddress;
// might not need this, but investigating whether it will help us. TODO remove?
private int baseParamOffset = 0;
// private List<RegisterRelativeSymbolApplier> stackVariableAppliers = new ArrayList<>();
private List<AbstractMsSymbolApplier> allAppliers = new ArrayList<>();
private RegisterChangeCalculator registerChangeCalculator;
public FunctionSymbolApplier(PdbApplicator applicator, AbstractMsSymbolIterator iter)
throws CancelledException, NoSuchElementException {
super(applicator, iter);
AbstractMsSymbol abstractSymbol = iter.next();
symbolBlockNestingLevel = 0;
comments = new BlockCommentsManager();
currentBlockAddress = null;
if (abstractSymbol instanceof AbstractProcedureMsSymbol) {
procedureSymbol = (AbstractProcedureMsSymbol) abstractSymbol;
specifiedAddress = applicator.reladdr(procedureSymbol);
}
else if (abstractSymbol instanceof AbstractThunkMsSymbol) {
thunkSymbol = (AbstractThunkMsSymbol) abstractSymbol;
specifiedAddress = applicator.reladdr(thunkSymbol);
}
else {
throw new AssertException(
"Invalid symbol type: " + abstractSymbol.getClass().getSimpleName());
}
remapAddress = applicator.getRemapAddressByAddress(specifiedAddress);
manageBlockNesting(this);
while (notDone()) {
applicator.checkCanceled();
AbstractMsSymbolApplier applier = applicator.getSymbolApplier(iter);
allAppliers.add(applier);
applier.manageBlockNesting(this);
}
}
@Override
public void manageBlockNesting(AbstractMsSymbolApplier applierParam) {
if (applierParam instanceof FunctionSymbolApplier) {
FunctionSymbolApplier functionSymbolApplier = (FunctionSymbolApplier) applierParam;
if (procedureSymbol != null) {
long start = procedureSymbol.getDebugStartOffset();
long end = procedureSymbol.getDebugEndOffset();
Address blockAddress = remapAddress.add(start);
long length = end - start;
functionSymbolApplier.beginBlock(blockAddress, procedureSymbol.getName(), length);
}
else if (thunkSymbol != null) {
functionSymbolApplier.beginBlock(remapAddress, thunkSymbol.getName(),
thunkSymbol.getLength());
}
}
}
/**
* Returns the {@link Function} for this applier.
* @return the Function
*/
public Function getFunction() {
return function;
}
/**
* Returns the current frame size.
* @return the current frame size.
*/
public long getCurrentFrameSize() {
return currentFrameSize;
}
/**
* Returns the frame size as specified by the PDB
* @return the frame size.
*/
public long getSpecifiedFrameSize() {
return specifiedFrameSize;
}
/**
* Set the specified frame size.
* @param specifiedFrameSize the frame size.
*/
void setSpecifiedFrameSize(long specifiedFrameSize) {
this.specifiedFrameSize = specifiedFrameSize;
currentFrameSize = specifiedFrameSize;
}
/**
* Get the function name
* @return the function name
*/
public String getName() {
if (procedureSymbol != null) {
return procedureSymbol.getName();
}
else if (thunkSymbol != null) {
return thunkSymbol.getName();
}
return "";
}
@Override
public void applyTo(AbstractMsSymbolApplier applyToApplier) {
// Do nothing.
}
@Override
public void apply() throws PdbException, CancelledException {
boolean result = applyTo(applicator.getCancelOnlyWrappingMonitor());
if (result == false) {
throw new PdbException("failure in applying " + this.getClass().getSimpleName() +
" to program: " + getName());
}
}
public boolean applyTo(TaskMonitor monitor) throws PdbException, CancelledException {
return applyTo(applicator.getProgram(), remapAddress, monitor);
}
public boolean applyTo(Program program, Address address, TaskMonitor monitor)
throws PdbException, CancelledException {
boolean functionSuccess = applyFunction(program, address, monitor);
if (functionSuccess == false) {
return false;
}
registerChangeCalculator = new RegisterChangeCalculator(procedureSymbol, function, monitor);
baseParamOffset = VariableUtilities.getBaseStackParamOffset(function);
for (AbstractMsSymbolApplier applier : allAppliers) {
applier.applyTo(this);
}
// comments
long addressDelta = address.subtract(specifiedAddress);
comments.applyTo(program, addressDelta);
// line numbers
// TODO: not done yet
// ApplyLineNumbers applyLineNumbers = new ApplyLineNumbers(pdbParser, xmlParser, program);
// applyLineNumbers.applyTo(monitor, log);
return true;
}
public Integer getRegisterPrologChange(Register register) {
return registerChangeCalculator.getRegChange(applicator, register);
}
public int getBaseParamOffset() {
return baseParamOffset;
}
/**
* Sets a local variable (address, name, type)
* @param address Address of the variable.
* @param name name of the variable.
* @param dataType data type of the variable.
*/
void setLocalVariable(Address address, String name, DataType dataType) {
if (currentBlockAddress == null) {
return; // silently return.
}
// Currently just placing a comment.
String comment = getIndent(symbolBlockNestingLevel + 1) + "static local (stored at " +
address + ") " + dataType.getName() + " " + name;
comments.addPreComment(currentBlockAddress, comment);
}
private boolean applyFunction(Program program, Address address, TaskMonitor monitor) {
Listing listing = program.getListing();
boolean forcePrimary = applicator.shouldForcePrimarySymbol(address, true);
applicator.createSymbol(address, getName(), forcePrimary);
function = listing.getFunctionAt(address);
if (function == null) {
function = createFunction(program, address, monitor);
}
if (function != null && !function.isThunk() &&
(function.getSignatureSource() == SourceType.DEFAULT ||
function.getSignatureSource() == SourceType.ANALYSIS)) {
// Set the function definition
setFunctionDefinition(program, address, monitor);
}
if (function == null) {
return false;
}
currentFrameSize = 0;
return true;
}
private Function createFunction(Program program, Address address, TaskMonitor monitor) {
// Does function already exist?
Function myFunction = program.getListing().getFunctionAt(address);
if (myFunction != null) {
// Actually not sure if we should set to 0 or calculate from the function here.
// Need to investigate more, so at least keeping it as a separate 'else' for now.
return myFunction;
}
// Disassemble
Instruction instr = program.getListing().getInstructionAt(address);
if (instr == null) {
DisassembleCommand cmd = new DisassembleCommand(address, null, true);
cmd.applyTo(program, monitor);
}
myFunction = createFunctionCommand(program, address, monitor);
return myFunction;
}
private boolean setFunctionDefinition(Program program, Address address, TaskMonitor monitor) {
if (procedureSymbol == null) {
// TODO: is there anything we can do with thunkSymbol?
// long x = thunkSymbol.getParentPointer();
return true;
}
// Rest presumes procedureSymbol.
RecordNumber typeRecordNumber = procedureSymbol.getTypeRecordNumber();
AbstractMsTypeApplier applier = applicator.getTypeApplier(typeRecordNumber);
if (applier == null) {
applicator.appendLogMsg("Error: Failed to resolve datatype RecordNumber " +
typeRecordNumber + " at " + address);
return false;
}
if (!(applier instanceof AbstractFunctionTypeApplier)) {
if (!((applier instanceof PrimitiveTypeApplier) &&
((PrimitiveTypeApplier) applier).isNoType())) {
applicator.appendLogMsg("Error: Failed to resolve datatype RecordNumber " +
typeRecordNumber + " at " + address);
return false;
}
}
DataType dataType = applier.getDataType();
// Since we know the applier is an AbstractionFunctionTypeApplier, then dataType is either
// FunctionDefinition or no type (typedef).
if (dataType instanceof FunctionDefinition) {
FunctionDefinition def = (FunctionDefinition) dataType;
ApplyFunctionSignatureCmd sigCmd =
new ApplyFunctionSignatureCmd(address, def, SourceType.IMPORTED);
if (!sigCmd.applyTo(program, monitor)) {
applicator.appendLogMsg(
"failed to apply signature to function at address " + address.toString());
return false;
}
}
return true;
}
private Function createFunctionCommand(Program program, Address entryPoint,
TaskMonitor monitor) {
CreateFunctionCmd funCmd = new CreateFunctionCmd(entryPoint);
if (!funCmd.applyTo(program, monitor)) {
applicator.appendLogMsg("Failed to apply function at address " + entryPoint.toString() +
"; attempting to use possible existing function");
return program.getListing().getFunctionAt(entryPoint);
}
return funCmd.getFunction();
}
private boolean notDone() {
return (symbolBlockNestingLevel > 0) && iter.hasNext();
}
public int endBlock() {
if (--symbolBlockNestingLevel < 0) {
applicator.appendLogMsg(
"Block Nesting went negative for " + getName() + " at " + remapAddress);
}
if (symbolBlockNestingLevel == 0) {
//currentFunctionSymbolApplier = null;
}
return symbolBlockNestingLevel;
}
public void beginBlock(Address startAddress, String name, long length) {
int nestingLevel = beginBlock(startAddress);
if (!applicator.getPdbApplicatorOptions().applyCodeScopeBlockComments()) {
return;
}
String indent = getIndent(nestingLevel);
String baseComment = "level " + nestingLevel + ", length " + length;
String preComment = indent + "PDB: Block Beg, " + baseComment;
if (!name.isEmpty()) {
preComment += " (" + name + ")";
}
comments.addPreComment(startAddress, preComment);
String postComment = indent + "PDB: Block End, " + baseComment;
Address endAddress = startAddress.add(((length <= 0) ? 0 : length - 1));
comments.addPostComment(endAddress, postComment);
}
private int beginBlock(Address startAddress) {
currentBlockAddress = startAddress;
++symbolBlockNestingLevel;
return symbolBlockNestingLevel;
}
private String getIndent(int indentLevel) {
String indent = "";
for (int i = 1; i < indentLevel; i++) {
indent += BLOCK_INDENT;
}
return indent;
}
// Method copied from ApplyStackVariables (ghidra.app.util.bin.format.pdb package)
// on 20191119. TODO: Do we need something like this?
/**
* Get the stack offset after it settles down.
* @param monitor TaskMonitor
* @return stack offset that stack variables will be relative to.
* @throws CancelledException upon user cancellation.
*/
private int getFrameBaseOffset(TaskMonitor monitor) throws CancelledException {
int retAddrSize = function.getProgram().getDefaultPointerSize();
if (retAddrSize != 8) {
// don't do this for 32 bit.
return -retAddrSize; // 32 bit has a -4 byte offset
}
Register frameReg = function.getProgram().getCompilerSpec().getStackPointer();
Address entryAddr = function.getEntryPoint();
AddressSet scopeSet = new AddressSet();
scopeSet.addRange(entryAddr, entryAddr.add(64));
CallDepthChangeInfo valueChange =
new CallDepthChangeInfo(function, scopeSet, frameReg, monitor);
InstructionIterator instructions =
function.getProgram().getListing().getInstructions(scopeSet, true);
int max = 0;
while (instructions.hasNext()) {
monitor.checkCanceled();
Instruction next = instructions.next();
int newValue = valueChange.getDepth(next.getMinAddress());
if (newValue < -(20 * 1024) || newValue > (20 * 1024)) {
continue;
}
if (Math.abs(newValue) > Math.abs(max)) {
max = newValue;
}
}
return max;
}
private static class RegisterChangeCalculator {
private Map<Register, Integer> registerChangeByRegisterName = new HashMap<>();
private CallDepthChangeInfo callDepthChangeInfo;
private Address debugStart;
private RegisterChangeCalculator(AbstractProcedureMsSymbol procedureSymbol,
Function function, TaskMonitor monitor) throws CancelledException {
callDepthChangeInfo = createCallDepthChangInfo(procedureSymbol, function, monitor);
}
private CallDepthChangeInfo createCallDepthChangInfo(
AbstractProcedureMsSymbol procedureSymbol, Function function, TaskMonitor monitor)
throws CancelledException {
if (procedureSymbol == null) {
return null;
}
Register frameReg = function.getProgram().getCompilerSpec().getStackPointer();
Address entryAddr = function.getEntryPoint();
debugStart = entryAddr.add(procedureSymbol.getDebugStartOffset());
AddressSet scopeSet = new AddressSet();
scopeSet.addRange(entryAddr, debugStart);
return new CallDepthChangeInfo(function, scopeSet, frameReg, monitor);
}
public Integer getRegChange(PdbApplicator applicator, Register register) {
if (callDepthChangeInfo == null || register == null) {
return null;
}
Integer change = registerChangeByRegisterName.get(register);
if (change != null) {
return change;
}
change = callDepthChangeInfo.getRegDepth(debugStart, register);
registerChangeByRegisterName.put(register, change);
return change;
}
}
}

View file

@ -0,0 +1,73 @@
/* ###
* 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.app.util.NamespaceUtils;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.symbol.AbstractLabelMsSymbol;
import ghidra.app.util.bin.format.pdb2.pdbreader.symbol.AbstractMsSymbol;
import ghidra.app.util.pdb.pdbapplicator.SymbolGroup.AbstractMsSymbolIterator;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.FunctionManager;
import ghidra.program.model.symbol.Namespace;
import ghidra.util.exception.AssertException;
import ghidra.util.exception.CancelledException;
/**
* Applier for {@link AbstractLabelMsSymbol} symbols.
*/
public class LabelSymbolApplier extends AbstractMsSymbolApplier {
private AbstractLabelMsSymbol symbol;
public LabelSymbolApplier(PdbApplicator applicator, AbstractMsSymbolIterator iter) {
super(applicator, iter);
AbstractMsSymbol abstractSymbol = iter.next();
if (!(abstractSymbol instanceof AbstractLabelMsSymbol)) {
throw new AssertException(
"Invalid symbol type: " + abstractSymbol.getClass().getSimpleName());
}
symbol = (AbstractLabelMsSymbol) abstractSymbol;
}
@Override
public void apply() throws PdbException, CancelledException {
if (!applicator.getPdbApplicatorOptions().applyInstructionLabels()) {
return;
}
// Place compiler generated symbols (e.g., $LN9) within containing function when possible
String name = symbol.getName();
Address symbolAddress = applicator.reladdr(symbol);
FunctionManager functionManager = applicator.getProgram().getFunctionManager();
// TODO: What do we do with labels such as this?... "__catch$?test_eh1@@YAHXZ$7"
if (name.startsWith("$") && !name.contains(Namespace.DELIMITER)) {
Function f = functionManager.getFunctionContaining(symbolAddress);
if (f != null && !f.getName().equals(name)) {
name = NamespaceUtils.getNamespaceQualifiedName(f, name, true);
}
}
boolean forcePrimary = applicator.shouldForcePrimarySymbol(symbolAddress, false);
if (!applicator.createSymbol(symbolAddress, name, forcePrimary)) {
applicator.appendLogMsg("Unable to create symbol " + name + " at " + symbolAddress);
}
}
@Override
public void applyTo(AbstractMsSymbolApplier applyToApplier) {
// Do nothing
}
}

View file

@ -0,0 +1,77 @@
/* ###
* 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.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbLog;
import ghidra.app.util.bin.format.pdb2.pdbreader.symbol.*;
import ghidra.app.util.pdb.pdbapplicator.SymbolGroup.AbstractMsSymbolIterator;
import ghidra.util.Msg;
import ghidra.util.exception.AssertException;
import ghidra.util.exception.CancelledException;
/**
* Applier for {@link AbstractLocalSymbolInOptimizedCodeMsSymbol} symbols.
*/
public class LocalOptimizedSymbolApplier extends AbstractMsSymbolApplier {
private AbstractLocalSymbolInOptimizedCodeMsSymbol symbol;
public LocalOptimizedSymbolApplier(PdbApplicator applicator, AbstractMsSymbolIterator iter) {
super(applicator, iter);
AbstractMsSymbol abstractSymbol = iter.next();
if (!(abstractSymbol instanceof AbstractLocalSymbolInOptimizedCodeMsSymbol)) {
throw new AssertException(
"Invalid symbol type: " + abstractSymbol.getClass().getSimpleName());
}
symbol = (AbstractLocalSymbolInOptimizedCodeMsSymbol) abstractSymbol;
}
@Override
public void apply() throws PdbException, CancelledException {
String message = "Cannot apply " + this.getClass().getSimpleName() + " directly to program";
Msg.info(this, message);
PdbLog.message(message);
}
@Override
public void applyTo(AbstractMsSymbolApplier applyToApplier)
throws PdbException, CancelledException {
if (!applicator.getPdbApplicatorOptions().applyFunctionVariables()) {
return;
}
if (applyToApplier instanceof FunctionSymbolApplier) {
FunctionSymbolApplier functionSymbolApplier = (FunctionSymbolApplier) applyToApplier;
doWork(functionSymbolApplier);
}
}
private void doWork(FunctionSymbolApplier functionSymbolApplier)
throws CancelledException, PdbException {
// TODO: Not doing anything with the information yet.
symbol.getLocalVariableFlags();
symbol.getName();
symbol.getTypeRecordNumber();
while (iter.hasNext() &&
(iter.peek() instanceof AbstractDefinedSingleAddressRangeMsSymbol)) {
applicator.checkCanceled();
DefinedSingleAddressRangeSymbolApplier rangeApplier =
new DefinedSingleAddressRangeSymbolApplier(applicator, iter);
rangeApplier.applyTo(this);
}
}
}

View file

@ -0,0 +1,274 @@
/* ###
* 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.math.BigInteger;
import ghidra.app.util.SymbolPath;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.RecordNumber;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.*;
import ghidra.program.model.data.DataType;
import ghidra.util.exception.CancelledException;
/**
* Applier for {@link AbstractMemberFunctionMsType} types.
*/
public class MemberFunctionTypeApplier extends AbstractFunctionTypeApplier {
// private boolean hasThisPointer = false;
private AbstractMsTypeApplier thisPointerApplier = null;
private CompositeTypeApplier containingCompositeApplier = null;
private static AbstractMsType validateType(AbstractMsType type)
throws IllegalArgumentException {
if (!(type instanceof AbstractMemberFunctionMsType)) {
throw new IllegalArgumentException(
"PDB Incorrectly applying " + type.getClass().getSimpleName() + " to " +
MemberFunctionTypeApplier.class.getSimpleName());
}
return type;
}
/**
* Constructor for the applicator that applies {@link AbstractMemberFunctionMsType},
* transforming it into a Ghidra {@link DataType}.
* @param applicator {@link PdbApplicator} for which this class is working.
* @param msType {@link AbstractMemberFunctionMsType} to processes.
* @throws IllegalArgumentException Upon type mismatch.
*/
public MemberFunctionTypeApplier(PdbApplicator applicator, AbstractMsType msType)
throws IllegalArgumentException {
super(applicator, validateType(msType));
}
@Override
public BigInteger getSize() {
return BigInteger.ZERO;
}
@Override
protected CallingConvention getCallingConvention() {
return ((AbstractMemberFunctionMsType) msType).getCallingConvention();
}
@Override
protected boolean hasThisPointer() throws CancelledException, PdbException {
AbstractMsTypeApplier applier = applicator.getTypeApplier(
((AbstractMemberFunctionMsType) msType).getThisPointerRecordNumber());
if ((applier instanceof PrimitiveTypeApplier &&
((PrimitiveTypeApplier) applier).isNoType())) {
return false; // such as for static member functions
}
return true;
}
@Override
protected RecordNumber getReturnRecordNumber() {
return ((AbstractMemberFunctionMsType) msType).getReturnRecordNumber();
}
@Override
protected RecordNumber getArgListRecordNumber() {
return ((AbstractMemberFunctionMsType) msType).getArgListRecordNumber();
}
@Override
public void apply() throws PdbException, CancelledException {
predefineClasses();
applyFunction(getCallingConvention(), hasThisPointer());
}
private void predefineClasses() throws CancelledException, PdbException {
AbstractMemberFunctionMsType procType = (AbstractMemberFunctionMsType) msType;
if (hasThisPointer()) {
thisPointerApplier = getThisPointerApplier(procType);
applicator.getPdbApplicatorMetrics().witnessMemberFunctionThisPointer(
thisPointerApplier);
if (thisPointerApplier instanceof PointerTypeApplier) {
AbstractMsTypeApplier underlyingApplier =
getThisUnderlyingApplier((PointerTypeApplier) thisPointerApplier);
applicator.getPdbApplicatorMetrics().witnessMemberFunctionThisPointerUnderlyingType(
underlyingApplier);
if (underlyingApplier instanceof CompositeTypeApplier) {
predefineClass((CompositeTypeApplier) underlyingApplier);
}
}
}
AbstractComplexTypeApplier containingApplier = getContainingComplexApplier(procType);
applicator.getPdbApplicatorMetrics().witnessMemberFunctionContainingType(containingApplier);
if (containingApplier instanceof CompositeTypeApplier) {
// Do nothing at this time if Enum or something else
predefineClass((CompositeTypeApplier) containingApplier);
}
}
private void predefineClass(CompositeTypeApplier applier) {
SymbolPath containingClassSymbolPath = applier.getFixedSymbolPath();
applicator.predefineClass(containingClassSymbolPath);
}
// private AbstractPointerMsType getThisType(AbstractMemberFunctionMsType procType)
// throws CancelledException, PdbException {
// int thisPointerTypeIndex = procType.getThisPointerTypeIndex();
// AbstractMsTypeApplier applier = applicator.getTypeApplier(thisPointerTypeIndex);
//
// if ((applier instanceof PrimitiveTypeApplier &&
// ((PrimitiveTypeApplier) applier).isNoType())) {
// return null; // such as for static member functions
// }
// if (!(applier instanceof PointerTypeApplier)) {
// applicator.getLog().appendMsg("thisApplier is invalid type for " + msType.getName());
// return null;
// }
// AbstractMsType thisMsType = applier.getMsType();
// // shouldn't need to do this next test, as an applier should only get this type
// if (!(thisMsType instanceof AbstractPointerMsType)) {
// applicator.getLog().appendMsg("thisMsType is invalid type for " + msType.getName());
// return null;
// }
// return (AbstractPointerMsType) thisMsType;
//
// }
//
// private void processContainingClass(AbstractMemberFunctionMsType procType)
// throws CancelledException, PdbException {
// int containingClassTypeIndex = procType.getContainingClassTypeIndex();
//
// CompositeTypeApplier containingCompositeApplier =
// applicator.getCompositeApplier(containingClassTypeIndex);
////
//// ApplyCompositeType containingCompositeApplier =
//// (ApplyCompositeType) applicator.getExpectedTypeApplier(containingClassTypeIndex,
//// ApplyCompositeType.class);
//
//// AbstractApplyMsType containingTypeApplier =
//// applicator.getTypeApplier(containingClassTypeIndex, false);
//// if (containingTypeApplier == null) {
//// applicator.getLog().appendMsg(
//// "containingClassApplier is null for " + msType.getName());
//// return null;
//// }
//// if (!(containingTypeApplier instanceof ApplyCompositeType)) {
//// applicator.getLog().appendMsg(
//// "containingClassApplier is invalid type for " + msType.getName());
//// return null;
//// }
//// ApplyCompositeType containingCompositeApplier = (ApplyCompositeType) containingTypeApplier;
// SymbolPath containingClassSymbolPath = containingCompositeApplier.getFixedSymbolPath();
// applicator.predefineClass(containingClassSymbolPath);
//
// }
// private boolean hasThisPointer(AbstractMemberFunctionMsType procType)
// throws CancelledException, PdbException {
// int thisPointerTypeIndex = procType.getThisPointerTypeIndex();
// AbstractMsTypeApplier applier = applicator.getTypeApplier(thisPointerTypeIndex);
// if ((applier instanceof PrimitiveTypeApplier &&
// ((PrimitiveTypeApplier) applier).isNoType())) {
// return false; // such as for static member functions
// }
// return true;
// }
private AbstractMsTypeApplier getThisPointerApplier(AbstractMemberFunctionMsType procType) {
AbstractMsTypeApplier applier =
applicator.getTypeApplier(procType.getThisPointerRecordNumber());
// if ((applier instanceof PrimitiveTypeApplier &&
// ((PrimitiveTypeApplier) applier).isNoType())) {
// return null; // such as for static member functions
// }
// // Cannot just check of PointerTypeApplier because could instead be a
// // PrimitiveTypeApplier with PointerDataType as dataType
// if (!(applier.getDataType() instanceof PointerDataType)) {
// applicator.appendLogMsg(applier.getMsType().getClass().getSimpleName() +
// " this type is invalid type for " + msType.getClass().getSimpleName());
// return null;
// }
applicator.addApplierDependency(this, applier);
return applier;
}
private AbstractMsTypeApplier getThisUnderlyingApplier(PointerTypeApplier thisApplier) {
return thisApplier.getUnmodifiedUnderlyingTypeApplier();
}
private AbstractComplexTypeApplier getContainingComplexApplier(
AbstractMemberFunctionMsType procType) throws PdbException {
return AbstractComplexTypeApplier.getComplexApplier(applicator,
procType.getContainingClassRecordNumber());
}
// private void processPointerUnderlyingType()
// throws CancelledException, PdbException {
//
// AbstractMsTypeApplier thisUnderlyingTypeApplier = thisPointerApplier.getUnmodifiedUnderlyingTypeApplier();
//
// if (!(thisUnderlyingTypeApplier instanceof CompositeTypeApplier)) {
// applicator.getLog().appendMsg(
// "thisUnderlyingTypeApplier is invalid type for " + msType.getName());
// return;
// }
// CompositeTypeApplier thisUnderlyingCompositeApplier =
// (CompositeTypeApplier) thisUnderlyingTypeApplier;
// SymbolPath thisUnderlyingClassSymbolPath =
// thisUnderlyingCompositeApplier.getFixedSymbolPath();
// applicator.predefineClass(thisUnderlyingClassSymbolPath);
// }
// private AbstractPointerMsType processThisPointer(AbstractMemberFunctionMsType procType)
// throws CancelledException, PdbException {
// int thisPointerTypeIndex = procType.getThisPointerTypeIndex();
// AbstractMsTypeApplier thisPointerApplier = applicator.getTypeApplier(thisPointerTypeIndex);
//
// if ((thisPointerApplier instanceof PrimitiveTypeApplier &&
// ((PrimitiveTypeApplier) thisPointerApplier).isNoType())) {
// return null; // such as for static member functions
// }
// if (!(thisPointerApplier instanceof PointerTypeApplier)) {
// applicator.getLog().appendMsg("thisApplier is invalid type for " + msType.getName());
// return null;
// }
// return (AbstractPointerMsType) thisPointerApplier.getMsType();
// }
//
// private void processPointerUnderlyingType(AbstractPointerMsType thisPointerMsType)
// throws CancelledException, PdbException {
//
// int thisUnderlyingTypeIndex = thisPointerMsType.getUnderlyingTypeIndex();
// AbstractMsTypeApplier thisUnderlyingTypeApplier =
// applicator.getTypeApplier(thisUnderlyingTypeIndex);
//
// // TODO: does not recurse below one level of modifiers... consider doing a recursion.
// if (thisUnderlyingTypeApplier instanceof ModifierTypeApplier) {
// ModifierTypeApplier x = (ModifierTypeApplier) thisUnderlyingTypeApplier;
// int y = ((AbstractModifierMsType) (x.getMsType())).getModifiedTypeIndex();
// thisUnderlyingTypeApplier = applicator.getTypeApplier(y);
// }
// if (!(thisUnderlyingTypeApplier instanceof CompositeTypeApplier)) {
// applicator.getLog().appendMsg(
// "thisUnderlyingTypeApplier is invalid type for " + msType.getName());
// return;
// }
// CompositeTypeApplier thisUnderlyingCompositeApplier =
// (CompositeTypeApplier) thisUnderlyingTypeApplier;
// SymbolPath thisUnderlyingClassSymbolPath =
// thisUnderlyingCompositeApplier.getFixedSymbolPath();
// applicator.predefineClass(thisUnderlyingClassSymbolPath);
// }
}

View file

@ -0,0 +1,97 @@
/* ###
* 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.math.BigInteger;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.*;
import ghidra.program.model.data.DataType;
import ghidra.util.exception.CancelledException;
/**
* Applier for {@link AbstractMemberMsType} types.
*/
public class MemberTypeApplier extends AbstractMsTypeApplier {
private static AbstractMsType validateType(AbstractMsType type)
throws IllegalArgumentException {
if (!(type instanceof AbstractMemberMsType)) {
throw new IllegalArgumentException("PDB Incorrectly applying " +
type.getClass().getSimpleName() + " to " + MemberTypeApplier.class.getSimpleName());
}
return type;
}
/**
* Constructor for member type applier.
* @param applicator {@link PdbApplicator} for which this class is working.
* @param msType {@link AbstractMemberMsType} to process.
* @throws IllegalArgumentException Upon invalid arguments.
*/
public MemberTypeApplier(PdbApplicator applicator, AbstractMsType msType)
throws IllegalArgumentException {
super(applicator, validateType(msType));
}
@Override
public BigInteger getSize() {
return BigInteger.ZERO;
}
@Override
public void apply() throws PdbException, CancelledException {
dataType = applyMemberMsType((AbstractMemberMsType) msType);
// DataType dataType = applyMemberMsType((AbstractMemberMsType) msType);
// ghDataType = dataType; // temporary while below is commented-out
// TODO: uncomment when above method not returning null
// ghDataTypeDB = applicator.resolve(dataType);
}
public String getName() {
return ((AbstractMemberMsType) msType).getName();
}
public BigInteger getOffset() {
return ((AbstractMemberMsType) msType).getOffset();
}
public ClassFieldMsAttributes getAttribute() {
return ((AbstractMemberMsType) msType).getAttribute();
}
public AbstractMsTypeApplier getFieldTypeApplier() {
return applicator.getTypeApplier(((AbstractMemberMsType) msType).getFieldTypeRecordNumber());
}
private DataType applyMemberMsType(AbstractMemberMsType type) {
// String memberName = type.getName();
// BigInteger memberOffset = type.getOffset();
// ClassFieldMsAttributes memberAttributes = type.getAttribute();
// int fieldTypeIndex = type.getFieldTypeRecordIndex();
//
// AbstractMsTypeApplier fieldTypeApplier = applicator.getTypeApplier(fieldTypeIndex);
//
// DataType fieldDataType = fieldTypeApplier.getDataType();
//
//// DataType fieldDataType = getConvertedDataType(applicator, fieldTypeIndex);
return null;
}
}

View file

@ -0,0 +1,136 @@
/* ###
* 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.math.BigInteger;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.AbstractModifierMsType;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.AbstractMsType;
import ghidra.program.model.data.DataType;
import ghidra.util.exception.CancelledException;
/**
* Applier for {@link AbstractModifierMsType} types.
*/
//public class ModifierTypeApplier extends AbstractMsTypeApplier {
public class ModifierTypeApplier extends AbstractMsTypeApplier {
private boolean isDeferred = false;
private AbstractMsTypeApplier modifiedTypeApplier = null;
private static AbstractMsType validateType(AbstractMsType type)
throws IllegalArgumentException {
if (!(type instanceof AbstractModifierMsType)) {
throw new IllegalArgumentException(
"PDB Incorrectly applying " + type.getClass().getSimpleName() + " to " +
ModifierTypeApplier.class.getSimpleName());
}
return type;
}
/**
* Constructor for modifier type applier.
* @param applicator {@link PdbApplicator} for which this class is working.
* @param msType {@link AbstractModifierMsType} to processes.
* @throws IllegalArgumentException Upon invalid arguments.
*/
public ModifierTypeApplier(PdbApplicator applicator, AbstractMsType msType)
throws IllegalArgumentException {
super(applicator, validateType(msType));
}
//==============================================================================================
public void setDeferred() {
isDeferred = true;
}
@Override
public boolean isDeferred() {
return isDeferred;
}
@Override
void deferredApply() throws PdbException, CancelledException {
// Do nothing. Already applied. Just needs late resolve
}
//==============================================================================================
@Override
public BigInteger getSize() {
if (modifiedTypeApplier == null) {
return BigInteger.ZERO;
}
return modifiedTypeApplier.getSize();
}
@Override
public void apply() throws PdbException, CancelledException {
// dataType = applyModifierMsType((AbstractModifierMsType) msType);
applyOrDeferForDependencies();
}
private void applyOrDeferForDependencies() {
AbstractModifierMsType type = (AbstractModifierMsType) msType;
applyModifierMsType(type);
AbstractMsTypeApplier modifiedApplier =
applicator.getTypeApplier(type.getModifiedRecordNumber());
if (modifiedApplier.isDeferred()) {
applicator.addApplierDependency(this, modifiedApplier);
setDeferred();
}
else {
// applyModifierMsType(type);
// defer(false);
}
}
@Override
public DataType getDataType() {
return modifiedTypeApplier.getDataType();
}
private DataType applyModifierMsType(AbstractModifierMsType type) {
modifiedTypeApplier = applicator.getTypeApplier(type.getModifiedRecordNumber());
return modifiedTypeApplier.getDataType();
}
// ghDataTypeDB = applicator.resolve(dataType);
// boolean underlyingIsCycleBreakable() {
// // TODO: need to deal with InterfaceTypeApplier (will it be incorporated into
// // CompostieTypeapplier?) Is it in this list of places to break (i.e., can it contain)?
// return (modifiedTypeApplier != null &&
// (modifiedTypeApplier instanceof CompositeTypeApplier ||
// modifiedTypeApplier instanceof EnumTypeApplier));
// }
@Override
DataType getCycleBreakType() {
// hope to eliminate the null check if/when modifierTypeApplier is created at time of
// construction
if (modifiedTypeApplier == null) {
return null;
}
return modifiedTypeApplier.getCycleBreakType();
}
public AbstractMsTypeApplier getModifiedTypeApplier() {
return modifiedTypeApplier;
}
}

View file

@ -0,0 +1,169 @@
/* ###
* 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.math.BigInteger;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.RecordNumber;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.*;
import ghidra.program.model.data.DataType;
import ghidra.util.exception.CancelledException;
/**
* Applier for {@link AbstractNestedTypeMsType} and {@link AbstractNestedTypeExtMsType} types.
*/
public class NestedTypeApplier extends AbstractMsTypeApplier {
private AbstractMsTypeApplier nestedTypeDefinitionApplier = null;
private static AbstractMsType validateType(AbstractMsType type)
throws IllegalArgumentException {
if (!(type instanceof AbstractNestedTypeMsType) &&
!(type instanceof AbstractNestedTypeExtMsType)) {
throw new IllegalArgumentException("PDB Incorrectly applying " +
type.getClass().getSimpleName() + " to " + NestedTypeApplier.class.getSimpleName());
}
return type;
}
/**
* Constructor for nested type applier.
* @param applicator {@link PdbApplicator} for which this class is working.
* @param msType {@link AbstractNestedTypeMsType} or {@link AbstractNestedTypeExtMsType} to
* process.
* @throws IllegalArgumentException Upon invalid arguments.
*/
public NestedTypeApplier(PdbApplicator applicator, AbstractMsType msType)
throws IllegalArgumentException {
super(applicator, validateType(msType));
}
@Override
public BigInteger getSize() {
if (nestedTypeDefinitionApplier == null) {
return BigInteger.ZERO;
}
return nestedTypeDefinitionApplier.getSize();
}
/**
* Returns the name of this nested type.
* @return Name of the nested type.
*/
public String getTypeName() {
if (nestedTypeDefinitionApplier == null) {
return "";
}
return nestedTypeDefinitionApplier.getMsType().getName();
}
/**
* Returns the nested (member?) name for this nested type.
* @return (Member?) Name for the nested type.
*/
public String getMemberName() {
if (nestedTypeDefinitionApplier == null) {
return "";
}
if (msType instanceof AbstractNestedTypeMsType) {
return ((AbstractNestedTypeMsType) msType).getName();
}
return ((AbstractNestedTypeExtMsType) msType).getName();
}
AbstractMsTypeApplier getNestedTypeDefinitionApplier() {
return applicator.getTypeApplier(getNestedTypeDefinitionRecordNumber());
}
RecordNumber getNestedTypeDefinitionRecordNumber() {
if (msType instanceof AbstractNestedTypeMsType) {
return ((AbstractNestedTypeMsType) msType).getNestedTypeDefinitionRecordNumber();
}
return ((AbstractNestedTypeExtMsType) msType).getNestedTypeDefinitionRecordNumber();
}
/**
* Indicates if there are attributes. Returns false if not "applied" yet.
* @return [@code true} if there are attributes.
*/
public boolean hasAttributes() {
if (nestedTypeDefinitionApplier == null) {
return false;
}
if (nestedTypeDefinitionApplier.getMsType() instanceof AbstractNestedTypeMsType) {
return false;
}
return true;
}
/**
* Returns the attributes if they exist.
* @return the attributes or null if they do not exist.
*/
public ClassFieldMsAttributes getAttributes() {
AbstractMsType type = nestedTypeDefinitionApplier.getMsType();
if (type instanceof AbstractNestedTypeExtMsType) {
return ((AbstractNestedTypeExtMsType) type).getClassFieldAttributes();
}
return null;
}
@Override
public void apply() throws PdbException, CancelledException {
if (msType instanceof AbstractNestedTypeMsType) {
dataType = applyNestedTypeMsType((AbstractNestedTypeMsType) msType);
}
else {
dataType = applyNestedTypeExtMsType((AbstractNestedTypeExtMsType) msType);
}
}
private DataType applyNestedTypeMsType(AbstractNestedTypeMsType type) {
nestedTypeDefinitionApplier = applicator.getTypeApplier(type.getNestedTypeDefinitionRecordNumber());
return nestedTypeDefinitionApplier.getDataType();
}
private DataType applyNestedTypeExtMsType(AbstractNestedTypeExtMsType type) {
nestedTypeDefinitionApplier = applicator.getTypeApplier(type.getNestedTypeDefinitionRecordNumber());
return nestedTypeDefinitionApplier.getDataType();
}
// ghDataTypeDB = applicator.resolve(dataType);
// boolean underlyingIsCycleBreakable() {
// // TODO: need to deal with InterfaceTypeApplier (will it be incorporated into
// // CompostieTypeapplier?) Is it in this list of places to break (i.e., can it contain)?
// return (modifiedTypeApplier != null &&
// (modifiedTypeApplier instanceof CompositeTypeApplier ||
// modifiedTypeApplier instanceof EnumTypeApplier));
// }
@Override
DataType getCycleBreakType() {
// hope to eliminate the null check if/when modifierTypeApplier is created at time of
// construction
//TODO: look into this
return dataType;
// if (modifiedTypeApplier == null) {
// return null;
// }
// return modifiedTypeApplier.getCycleBreakType(applicator);
}
public AbstractMsTypeApplier getNestedTypeApplier() {
return nestedTypeDefinitionApplier;
}
}

View file

@ -0,0 +1,45 @@
/* ###
* 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.app.util.bin.format.pdb2.pdbreader.symbol.AbstractMsSymbol;
import ghidra.app.util.pdb.pdbapplicator.SymbolGroup.AbstractMsSymbolIterator;
/**
* A dummy {@link AbstractMsSymbolApplier}, which, at a minimum, reads the symbol from the
* {@link SymbolGroup}, allowing proper sequencing of other symbols within the
* {@link SymbolGroup}.
*/
public class NoSymbolApplier extends AbstractMsSymbolApplier {
AbstractMsSymbol symbol;
public NoSymbolApplier(PdbApplicator applicator, AbstractMsSymbolIterator iter) {
super(applicator, iter);
symbol = iter.next();
}
@Override
public void applyTo(AbstractMsSymbolApplier applyToApplier) {
// Do nothing.
}
@Override
public void apply() {
// Do nothing.
}
}

View file

@ -0,0 +1,50 @@
/* ###
* 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.math.BigInteger;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.AbstractMsType;
/**
* Used for creating a wrapper for when there is not associated type to the PDB type (or if we
* have not yet created the association).
*/
public class NoTypeApplier extends AbstractMsTypeApplier {
/**
* Constructor for nested type applier.
* @param applicator {@link PdbApplicator} for which this class is working.
* @param msType {@link AbstractMsType} to process.
* @throws IllegalArgumentException Upon invalid arguments.
*/
public NoTypeApplier(PdbApplicator applicator, AbstractMsType msType)
throws IllegalArgumentException {
super(applicator, msType);
}
@Override
public BigInteger getSize() {
return BigInteger.ZERO;
}
@Override
public void apply() {
// Do nothing (maybe should log something... not sure)
// applicator.getLog().appendMsg("");
}
}

View file

@ -0,0 +1,22 @@
/* ###
* 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;
//----------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------
enum OoComponentLayoutMode {
UNKNOWN, MEMBERS_ONLY, BASIC, SIMPLE, COMPLEX
}

View file

@ -0,0 +1,406 @@
/* ###
* 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.symbol.*;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.util.exception.CancelledException;
/**
* Manages Address/Section/Segment-related PDB items.
* Has method for providing real addresses.
*/
public class PdbAddressManager {
private static final Address badAddress = Address.NO_ADDRESS; // using NO_ADDRESS as a marker
//==============================================================================================
private Map<Integer, Long> realAddressesBySection;
private List<PeCoffGroupMsSymbol> memoryGroupRefinement;
private List<PeCoffSectionMsSymbol> memorySectionRefinement;
private List<SegmentInfo> allSegmentsInfo;
private Map<Address, Address> remapAddressByAddress;
private PdbApplicator applicator;
private Address imageBase;
//==============================================================================================
// API
//==============================================================================================
/**
* Manager
* @param applicator {@link PdbApplicator} for which this class is working.
* @param imageBase Address from which all other addresses are based.
* @throws PdbException If Program is null;
*/
PdbAddressManager(PdbApplicator applicator, Address imageBase) throws PdbException {
Objects.requireNonNull(applicator, "applicator may not be null");
Objects.requireNonNull(imageBase, "imageBase may not be null");
this.applicator = applicator;
this.imageBase = imageBase;
realAddressesBySection = new HashMap<>();
memoryGroupRefinement = new ArrayList<>();
memorySectionRefinement = new ArrayList<>();
allSegmentsInfo = new ArrayList<>();
remapAddressByAddress = new HashMap<>();
determineMemoryBlocks();
}
/**
* Returns the Address for the given section and offset.
* @param symbol The {@link AddressMsSymbol}
* @return The Address
*/
// Returns an address using the section and offset.
Address reladdr(AddressMsSymbol symbol) {
return reladdr(symbol.getSegment(), symbol.getOffset());
}
/**
* Returns the Address for the given section and offset. Will attempt to map the address
* to a new address if the {@link PdbApplicatorOptions}
* @param segment The segment
* @param offset The offset
* @return The Address, which can be {@code Address.NO_ADDRESS} if it was the original address.
*/
// Returns an address using the section and offset.
Address reladdr(int segment, long offset) {
if (segment < 0 || segment > allSegmentsInfo.size()) {
return Address.NO_ADDRESS;
}
else if (segment == allSegmentsInfo.size()) {
// Was getting issues of _IMAGE_DOSHEADER showing up with a segemnt index one
// beyond the end.
segment = 0; // The anomaly of last segment meaning the first.
}
SegmentInfo segmentInfo = allSegmentsInfo.get(segment);
if (offset >= segmentInfo.getLength()) {
return Address.NO_ADDRESS;
}
return segmentInfo.getStartAddress().add(offset);
}
/**
* Write the mapped address for a query address, where where the mapping is
* derived by using a the address of a PDB symbol as the key and finding the address of
* a symbol in the program of the same "unique" name. This is accomplished using public
* mangled symbols. If the program symbol came from the PDB, then it maps to itself.
* @param address the query address
* @param remapAddress the mapped address
*/
void putRemapAddressByAddress(Address address, Address remapAddress) {
Address lookup = remapAddressByAddress.get(address);
if (lookup == null) {
remapAddressByAddress.put(address, remapAddress);
}
else if (!lookup.equals(remapAddress) && lookup != badAddress) {
applicator.appendLogMsg("Trying to map a mapped address to a new address... key: " +
address + ", currentMap: " + lookup + ", newMap: " + remapAddress);
remapAddressByAddress.put(address, badAddress);
}
}
/**
* Returns the Address of an existing symbol for the query address, where the mapping is
* derived by using a the address of a PDB symbol as the key and finding the address of
* a symbol in the program of the same "unique" name. This is accomplished using public
* mangled symbols. If the program symbol came from the PDB, then it maps to itself.
* @param address the query address
* @return the remapAddress
*/
Address getRemapAddressByAddress(Address address) {
if (!Address.NO_ADDRESS.equals(address) &&
applicator.getPdbApplicatorOptions().remapAddressUsingExistingPublicMangledSymbols()) {
return remapAddressByAddress.getOrDefault(address, address);
}
return address;
}
/**
* Method for callee to set the real address for the section.
* @param sectionNum the section number
* @param realAddress The Address
*/
void putRealAddressesBySection(int sectionNum, long realAddress) {
realAddressesBySection.put(sectionNum, realAddress);
}
/**
* Method for callee to add a Memory Group symbol to the Memory Group list.
* @param symbol the symbol.
*/
void addMemoryGroupRefinement(PeCoffGroupMsSymbol symbol) {
memoryGroupRefinement.add(symbol);
}
/**
* Method for callee to add a Memory Section symbol to the Memory Section list.
* @param symbol the symbol.
*/
void addMemorySectionRefinement(PeCoffSectionMsSymbol symbol) {
memorySectionRefinement.add(symbol);
}
/**
* Dumps memory section refinement to log.
* @throws CancelledException Upon user cancellation
*/
void logReport() throws CancelledException {
logMemorySectionRefinement();
logMemoryGroupRefinement();
}
//==============================================================================================
//==============================================================================================
private class SegmentInfo {
private Address start;
private long length;
SegmentInfo(Address startIn, long lengthIn) {
start = startIn;
length = lengthIn;
}
public Address getStartAddress() {
return start;
}
public long getLength() {
return length;
}
}
private void determineMemoryBlocks() {
// Set section/segment 0 to image base. (should be what is header), but what is its size?
// TODO... made up size for now... is there something else? We could
long segmentZeroLength = 0x7fffffff;
allSegmentsInfo.add(new SegmentInfo(imageBase, segmentZeroLength));
AbstractDatabaseInterface dbi = applicator.getPdb().getDatabaseInterface();
if (dbi instanceof DatabaseInterfaceNew) {
DebugData debugData = ((DatabaseInterfaceNew) dbi).getDebugData();
List<ImageSectionHeader> imageSectionHeaders = debugData.getImageSectionHeaders();
for (ImageSectionHeader imageSectionHeader : imageSectionHeaders) {
long virtualAddress = imageSectionHeader.getVirtualAddress();
// TODO: not sure when unionPAVS is physical address vs. virtual size. Perhaps
// it keys off whether virtualAddress is not some special value such as
// 0x00000000 or 0xffffffff.
long size = imageSectionHeader.getUnionPAVS();
allSegmentsInfo.add(new SegmentInfo(imageBase.add(virtualAddress), size));
}
}
// else instance of DatabaseInterface; TODO: what can we do here?
// TODO: what should we do with these? Not doing anything at the moment
AbstractPdb pdb = applicator.getPdb();
List<SegmentMapDescription> segmentMapList = pdb.getDatabaseInterface().getSegmentMapList();
for (SegmentMapDescription segmentMapDescription : segmentMapList) {
segmentMapDescription.getSegmentOffset();
segmentMapDescription.getLength();
}
}
//==============================================================================================
//==============================================================================================
/**
* Dumps memory section refinement to log.
* @throws CancelledException Upon user cancellation
*/
private void logMemorySectionRefinement() throws CancelledException {
// Offer memory refinement (could have done it as symbols came in; we just collected
// them and are dealing with them now... ultimately not sure if we will want to use
// this refinement.
// Look at SectionFlags.java for characteristics information.
// TODO: should we perform refinement of program memory blocks?
PdbLog.message("\nMemorySectionRefinement");
for (PeCoffSectionMsSymbol sym : memorySectionRefinement) {
applicator.checkCanceled();
String name = sym.getName();
int section = sym.getSectionNumber();
int relativeVirtualAddress = sym.getRva();
int align = sym.getAlign();
int length = sym.getLength();
int characteristics = sym.getCharacteristics();
Address address = imageBase.add(relativeVirtualAddress);
PdbLog.message(String.format(
"%s: [%04X(%08X)](%s) Align:%02X, Len:%08X, Characteristics:%08X", name, section,
relativeVirtualAddress, address.toString(), align, length, characteristics));
}
}
/**
* Dumps group section refinement to log.
* @throws CancelledException Upon user cancellation
*/
private void logMemoryGroupRefinement() throws CancelledException {
// Note that I've seen example where PE header has two .data sections (one for initialized
// and the other for uninitialized, 0x2800 and 0x250 in size, respectively), but the PDB
// information shows two sections totally 0x2a50 size, but sizes of 0x2750 and 0x0300,
// the latter of which is marked as ".bss" in name. This suggests that the PE header is
// more focused on what needs initialized, which includes 0x2750 of .data and 0xa0 of
// .bss, the remaining 0x250 of .bss is not initialized. These .bss portion that needs
// initialized is lumped with the .data section in the PE header... only my opinion...
// however, this leaves us with question of what we do here. Do believe the PDB over
// the PE? (See vcamp110.arm.pdb and (arm) vcamp110.dll).
// TODO: should we perform refinement of program memory blocks?
PdbLog.message("\nMemoryGroupRefinement");
for (PeCoffGroupMsSymbol sym : memoryGroupRefinement) {
applicator.checkCanceled();
String name = sym.getName();
int segment = sym.getSegment();
long offset = sym.getOffset();
int length = sym.getLength();
int characteristics = sym.getCharacteristics();
Address address = reladdr(sym);
PdbLog.message(String.format("%s: [%04X:%08X](%s) Len:%08X, Characteristics:%08X", name,
segment, offset, address.toString(), length, characteristics));
}
}
//==============================================================================================
//==============================================================================================
// TODO: This is not complete... It was a research thought, which might get looked at in the
// 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
* {@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
* one segment in the PDB.
* <P>
* The PDB's {@link SegmentMapDescription} is not always fully populated, though the length
* field seems to be consistently available.
* @throws PdbException if there was a problem processing the data.
*/
@SuppressWarnings("unused") // for method not being called and local variables ununsed.
private void reconcileMemoryBlocks() throws PdbException {
// ImageSectionHeader imageSectionHeader =
// pdb.getDatabaseInterface().getDebugData().getImageSectionHeader();
AbstractPdb pdb = applicator.getPdb();
Program program = applicator.getProgram();
if (program == null) {
return;
}
Memory mem = program.getMemory();
MemoryBlock[] blocks = mem.getBlocks();
List<SegmentMapDescription> segmentMapList = pdb.getDatabaseInterface().getSegmentMapList();
/**
* Program has additional "Headers" block set up by the {@link PeLoader}.
*/
int progIndexLimit = blocks.length;
int pdbIndexLimit = segmentMapList.size();
int progIndex = 1;
int pdbIndex = 0;
// Set section/segment 0 to image base. (should be what is header)
List<SegmentInfo> myAllSegmentsInfo = new ArrayList<>(); // Contender set of segment info (vs what is in class)
allSegmentsInfo.add(new SegmentInfo(blocks[0].getStart(), blocks[0].getSize()));
// Try to match memory in order, grouping as needed.
long blockAccum = 0;
while (progIndex < progIndexLimit && pdbIndex < pdbIndexLimit) {
SegmentMapDescription segmentDescription = segmentMapList.get(pdbIndex);
Address blockStart = blocks[progIndex].getStart();
while (blockAccum < segmentDescription.getLength() && progIndex < progIndexLimit) {
// progIndex++;
Address addr1 = blocks[progIndex].getStart();
Address addr2 = blockStart.add(blockAccum);
if (!blocks[progIndex].getStart().equals(blockStart.add(blockAccum))) {
// Problem... blocks are not adjacent... TODO: how do we reconcile?
throw new PdbException("Memory block reconciliation failure");
}
blockAccum += blocks[progIndex].getSize();
if (blockAccum == segmentDescription.getLength()) {
myAllSegmentsInfo.add(new SegmentInfo(blockStart, blockAccum));
progIndex++;
pdbIndex++;
blockAccum = 0;
break;
}
else if (blockAccum > segmentDescription.getLength()) {
// Problem... TODO: how do we reconcile?
throw new PdbException(
"Memory block reconciliation failure--needs reverse aggregation");
}
progIndex++;
}
}
if (pdbIndex == pdbIndexLimit - 1 &&
segmentMapList.get(pdbIndex).getLength() == 0xffffffffL) {
pdbIndex++;
}
if (progIndex != progIndexLimit || pdbIndex != pdbIndexLimit) {
// Problem... TODO: didn't both end iterations together
throw new PdbException("Memory block reconciliation failure--remaining data");
}
}
//==============================================================================================
// TODO: This is not complete... It was a research thought, which might get looked at in the
// future.
@SuppressWarnings("unused") // for method not being called.
private boolean garnerSectionSegmentInformation() throws PdbException {
AbstractPdb pdb = applicator.getPdb();
if (pdb.getDatabaseInterface() == null) {
return false;
}
// ImageSectionHeader imageSectionHeader =
// pdb.getDatabaseInterface().getDebugData().getImageSectionHeader();
int num = 1;
for (AbstractModuleInformation module : pdb.getDatabaseInterface().getModuleInformationList()) {
if ("* Linker *".equals(module.getModuleName())) {
List<AbstractMsSymbol> linkerSymbolList =
applicator.getSymbolGroupForModule(num).getSymbols();
for (AbstractMsSymbol symbol : linkerSymbolList) {
if (symbol instanceof PeCoffSectionMsSymbol) {
PeCoffSectionMsSymbol section = (PeCoffSectionMsSymbol) symbol;
int sectionNum = section.getSectionNumber();
long realAddress = section.getRva();
section.getLength();
section.getCharacteristics();
section.getAlign();
section.getName();
realAddressesBySection.put(sectionNum, realAddress);
}
if (symbol instanceof PeCoffGroupMsSymbol) {
PeCoffGroupMsSymbol group = (PeCoffGroupMsSymbol) symbol;
group.getName();
group.getSegment();
group.getLength();
group.getOffset();
group.getCharacteristics();
}
}
return true;
}
num++;
}
return false;
}
}

View file

@ -0,0 +1,278 @@
/* ###
* 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.HashSet;
import java.util.Set;
import ghidra.app.util.bin.format.pdb2.pdbreader.symbol.*;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.AbstractMsType;
import ghidra.program.model.data.DataTypeManager;
import ghidra.program.model.listing.Program;
/**
* Metrics captured during the application of a PDB. This is a Ghidra class separate from the
* PDB API that we have crafted to help us quantify and qualify the ability apply the PDB
* to a {@link DataTypeManager} and/or {@link Program}.
*/
public class PdbApplicatorMetrics {
/**
* List of symbols seen (by their ID) as Global symbols.
*/
private static Set<Integer> expectedGlobalSymbols = new HashSet<>();
static {
// AbstractReferenceMsSymbol
expectedGlobalSymbols.add(DataReferenceStMsSymbol.PDB_ID);
expectedGlobalSymbols.add(DataReferenceMsSymbol.PDB_ID);
expectedGlobalSymbols.add(LocalProcedureReferenceMsSymbol.PDB_ID);
expectedGlobalSymbols.add(LocalProcedureReferenceStMsSymbol.PDB_ID);
expectedGlobalSymbols.add(ProcedureReferenceMsSymbol.PDB_ID);
expectedGlobalSymbols.add(ProcedureReferenceStMsSymbol.PDB_ID);
expectedGlobalSymbols.add(AnnotationReferenceMsSymbol.PDB_ID);
expectedGlobalSymbols.add(TokenReferenceToManagedProcedureMsSymbol.PDB_ID);
// AbstractDataMsSymbol
expectedGlobalSymbols.add(GlobalData16MsSymbol.PDB_ID);
expectedGlobalSymbols.add(GlobalData3216MsSymbol.PDB_ID);
expectedGlobalSymbols.add(GlobalData32MsSymbol.PDB_ID);
expectedGlobalSymbols.add(GlobalData32StMsSymbol.PDB_ID);
expectedGlobalSymbols.add(LocalData16MsSymbol.PDB_ID);
expectedGlobalSymbols.add(LocalData3216MsSymbol.PDB_ID);
expectedGlobalSymbols.add(LocalData32MsSymbol.PDB_ID);
expectedGlobalSymbols.add(LocalData32StMsSymbol.PDB_ID);
expectedGlobalSymbols.add(GlobalManagedDataMsSymbol.PDB_ID);
expectedGlobalSymbols.add(GlobalManagedDataStMsSymbol.PDB_ID);
expectedGlobalSymbols.add(LocalManagedDataMsSymbol.PDB_ID);
expectedGlobalSymbols.add(LocalManagedDataStMsSymbol.PDB_ID);
// AbstractUserDefinedTypeMsSymbol
expectedGlobalSymbols.add(CobolUserDefinedType16MsSymbol.PDB_ID);
expectedGlobalSymbols.add(CobolUserDefinedTypeMsSymbol.PDB_ID);
expectedGlobalSymbols.add(CobolUserDefinedTypeStMsSymbol.PDB_ID);
expectedGlobalSymbols.add(UserDefinedType16MsSymbol.PDB_ID);
expectedGlobalSymbols.add(UserDefinedTypeMsSymbol.PDB_ID);
expectedGlobalSymbols.add(UserDefinedTypeStMsSymbol.PDB_ID);
// AbstractConstantMsSymbol
expectedGlobalSymbols.add(Constant16MsSymbol.PDB_ID);
expectedGlobalSymbols.add(ConstantMsSymbol.PDB_ID);
expectedGlobalSymbols.add(ConstantStMsSymbol.PDB_ID);
expectedGlobalSymbols.add(ManagedConstantMsSymbol.PDB_ID);
}
/**
* List of symbols seen (by their ID) as Public symbols.
*/
private static Set<Integer> expectedLinkerSymbols = new HashSet<>();
static {
expectedLinkerSymbols.add(PeCoffSectionMsSymbol.PDB_ID);
expectedLinkerSymbols.add(TrampolineMsSymbol.PDB_ID);
expectedLinkerSymbols.add(ObjectNameMsSymbol.PDB_ID);
expectedLinkerSymbols.add(Compile3MsSymbol.PDB_ID);
expectedLinkerSymbols.add(Compile2MsSymbol.PDB_ID);
expectedLinkerSymbols.add(Compile2StMsSymbol.PDB_ID);
expectedLinkerSymbols.add(EnvironmentBlockMsSymbol.PDB_ID);
}
private Set<Class<? extends AbstractMsType>> cannotApplyTypes = new HashSet<>();
private Set<Class<? extends AbstractMsType>> unexpectedMemberFunctionThisPointerTypes =
new HashSet<>();
private Set<Class<? extends AbstractMsType>> unexpectedMemberFunctionThisPointerUnderlyingTypes =
new HashSet<>();
private Set<Class<? extends AbstractMsType>> unexpectedMemberFunctionContainerTypes =
new HashSet<>();
private Set<Class<? extends AbstractMsSymbol>> cannotApplySymbols = new HashSet<>();
private Set<Class<? extends AbstractMsSymbol>> unexpectedGlobalSymbols = new HashSet<>();
private Set<Class<? extends AbstractMsSymbol>> unexpectedPublicSymbols = new HashSet<>();
private boolean witnessEnumerateNarrowing = false;
/**
* Method to capture data/item type that cannot be applied.
* @param type The data/item type witnessed.
*/
public void witnessCannotApplyDataType(AbstractMsType type) {
cannotApplyTypes.add(type.getClass());
}
/**
* Method to capture symbol type that cannot be applied.
* @param symbol The symbol type witnessed.
*/
public void witnessCannotApplySymbolType(AbstractMsSymbol symbol) {
cannotApplySymbols.add(symbol.getClass());
}
/**
* Method to capture symbol type that was unexpected as a Global symbol.
* @param symbol The symbol type witnessed.
*/
public void witnessGlobalSymbolType(AbstractMsSymbol symbol) {
if (!expectedGlobalSymbols.contains(symbol.getPdbId())) {
unexpectedGlobalSymbols.add(symbol.getClass());
}
}
/**
* Method to capture symbol type that was unexpected as a Public symbol.
* @param symbol The symbol type witnessed.
*/
public void witnessPublicSymbolType(AbstractMsSymbol symbol) {
if (!(symbol instanceof AbstractPublicMsSymbol)) {
unexpectedPublicSymbols.add(symbol.getClass());
}
}
/**
* Method to capture symbol type that was unexpected as a Linker symbol.
* @param symbol The symbol type witnessed.
*/
public void witnessLinkerSymbolType(AbstractMsSymbol symbol) {
if (!expectedLinkerSymbols.contains(symbol.getPdbId())) {
// do nothing for now
}
}
/**
* Method to capture witnessing of Enumerate narrowing.
*/
public void witnessEnumerateNarrowing() {
witnessEnumerateNarrowing = true;
}
/**
* Method to capture unusual this pointer types.
* @param applier The {@AbstractMsTypeApplier} for the supposed this pointer.
*/
public void witnessMemberFunctionThisPointer(AbstractMsTypeApplier applier) {
// We know that we have seen PrimitiveMsTypes that are pointer types.
if (applier instanceof PointerTypeApplier) {
return;
}
unexpectedMemberFunctionThisPointerTypes.add(applier.getMsType().getClass());
}
/**
* Method to capture unusual underlying types for a normal pointer for this pointer.
* @param applier The {@AbstractMsTypeApplier} for the supposed this pointer.
*/
public void witnessMemberFunctionThisPointerUnderlyingType(AbstractMsTypeApplier applier) {
if (applier instanceof CompositeTypeApplier) {
return;
}
unexpectedMemberFunctionThisPointerUnderlyingTypes.add(applier.getMsType().getClass());
}
/**
* Method to capture unusual containing types for a member function.
* @param applier The {@AbstractMsTypeApplier} for the supposed this pointer.
*/
public void witnessMemberFunctionContainingType(AbstractMsTypeApplier applier) {
if (applier instanceof CompositeTypeApplier) {
return;
}
unexpectedMemberFunctionContainerTypes.add(applier.getMsType().getClass());
}
//==============================================================================================
/**
* Return some post-processing metrics for applying the PDB
* @return {@link String} of pretty output.
*/
public String getPostProcessingReport() {
StringBuilder builder = new StringBuilder();
builder.append("===Begin PdbApplicatorMetrics Report===\n");
builder.append(reportNonappliableTypes());
builder.append(reportUnunsualThisPointerTypes());
builder.append(reportUnunsualThisPointerUnderlyingTypes());
builder.append(reportUnunsualMemberFunctionContainerTypes());
builder.append(reportNonappliableSymbols());
builder.append(reportUnexpectedPublicSymbols());
builder.append(reportUnexpectedGlobalSymbols());
builder.append(reportEnumerateNarrowing());
builder.append("====End PdbApplicatorMetrics Report====\n");
return builder.toString();
}
private String reportNonappliableTypes() {
StringBuilder builder = new StringBuilder();
for (Class<? extends AbstractMsType> clazz : cannotApplyTypes) {
builder.append("Could not apply one or more instances of PDB data type: " +
clazz.getSimpleName() + "\n");
}
return builder.toString();
}
private String reportUnunsualThisPointerTypes() {
StringBuilder builder = new StringBuilder();
for (Class<? extends AbstractMsType> clazz : unexpectedMemberFunctionThisPointerTypes) {
builder.append("Unusual this pointer type: " + clazz.getSimpleName() + "\n");
}
return builder.toString();
}
private String reportUnunsualThisPointerUnderlyingTypes() {
StringBuilder builder = new StringBuilder();
for (Class<? extends AbstractMsType> clazz : unexpectedMemberFunctionThisPointerUnderlyingTypes) {
builder.append("Unusual this pointer underlying type: " + clazz.getSimpleName() + "\n");
}
return builder.toString();
}
private String reportUnunsualMemberFunctionContainerTypes() {
StringBuilder builder = new StringBuilder();
for (Class<? extends AbstractMsType> clazz : unexpectedMemberFunctionContainerTypes) {
builder.append("Unusual member function container: " + clazz.getSimpleName() + "\n");
}
return builder.toString();
}
private String reportNonappliableSymbols() {
StringBuilder builder = new StringBuilder();
for (Class<? extends AbstractMsSymbol> clazz : cannotApplySymbols) {
builder.append("Could not apply one or more instances of PDB symbol type: " +
clazz.getSimpleName() + "\n");
}
return builder.toString();
}
private String reportUnexpectedPublicSymbols() {
StringBuilder builder = new StringBuilder();
for (Class<? extends AbstractMsSymbol> clazz : unexpectedPublicSymbols) {
builder.append("Unexpected one or more instances of PDB public symbol type: " +
clazz.getSimpleName() + "\n");
}
return builder.toString();
}
private String reportUnexpectedGlobalSymbols() {
StringBuilder builder = new StringBuilder();
for (Class<? extends AbstractMsSymbol> clazz : unexpectedGlobalSymbols) {
builder.append("Unexpected one or more instances of PDB global symbol type: " +
clazz.getSimpleName() + "\n");
}
return builder.toString();
}
private String reportEnumerateNarrowing() {
if (witnessEnumerateNarrowing) {
return "Enumerate narrowing was witnessed\n";
}
return "";
}
}

View file

@ -0,0 +1,213 @@
/* ###
* 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.app.util.bin.format.pdb2.pdbreader.AbstractPdb;
/**
* Options used while using a {@link PdbApplicator} to apply a PDB ({@link AbstractPdb}) to a
* Ghidra program. These can be optional values used during our development of this PdbApplicator,
* and thus might not be found in the finished product.
*/
public class PdbApplicatorOptions extends Exception {
private static final boolean defaultApplyCodeScopeBlockComments = false;
private static final boolean defaultApplyInstructionLabels = false;
private static final boolean defaultApplyDataTypesOnly = false;
private static final boolean defaultApplyPublicSymbolsOnly = false;
private static final boolean defaultRemapAddressesUsingExistingPublicSymbols = false;
private static final boolean defaultAllowDemotePrimaryMangledSymbol = true;
// TODO: set the following to true if we come up with a reasonably good solution
private static final boolean defaultApplyFunctionVariables = false;
private static final CompositeLayoutMode defaultCompositeLayoutMode =
CompositeLayoutMode.MEMBERS_ONLY;
// private static final CompositeLayoutMode defaultCompositeLayoutMode =
// CompositeLayoutMode.BASIC_SIMPLE_COMPLEX;
// private static final CompositeLayoutMode defaultCompositeLayoutMode =
// CompositeLayoutMode.SIMPLE_COMPLEX;
// private static final CompositeLayoutMode defaultCompositeLayoutMode =
// CompositeLayoutMode.COMPLEX;
//==============================================================================================
private boolean applyCodeScopeBlockComments;
private boolean applyInstructionLabels;
private boolean applyDataTypesOnly;
private boolean applyPublicSymbolsOnly;
private boolean remapAddressesUsingExistingPublicSymbols;
private boolean allowDemotePrimaryMangledSymbol;
private boolean applyFunctionVariables; // investigation. might produce bad results.
private CompositeLayoutMode compositeLayoutMode;
/**
* Constructor
*/
public PdbApplicatorOptions() {
setDefaults();
}
/**
* Set the options back to their default values
*/
public void setDefaults() {
applyCodeScopeBlockComments = defaultApplyCodeScopeBlockComments;
applyInstructionLabels = defaultApplyInstructionLabels;
applyDataTypesOnly = defaultApplyDataTypesOnly;
applyPublicSymbolsOnly = defaultApplyPublicSymbolsOnly;
remapAddressesUsingExistingPublicSymbols = defaultRemapAddressesUsingExistingPublicSymbols;
allowDemotePrimaryMangledSymbol = defaultAllowDemotePrimaryMangledSymbol;
applyFunctionVariables = defaultApplyFunctionVariables;
compositeLayoutMode = defaultCompositeLayoutMode;
}
/**
* Enable/disable developmental debug.
* @param applyCodeScopeBlockComments {@code true} to turn applyCodeScopeBlockComments on
*/
public void setApplyCodeScopeBlockComments(boolean applyCodeScopeBlockComments) {
this.applyCodeScopeBlockComments = applyCodeScopeBlockComments;
}
/**
* Returns {@code true} if applyCodeScopeBlockComments is "on."
* @return {@code true} if applyCodeScopeBlockComments is "on."
*/
public boolean applyCodeScopeBlockComments() {
return applyCodeScopeBlockComments;
}
/**
* Enable/disable developmental debug.
* @param applyInstructionLabels {@code true} to turn applyInstructionLabels on
*/
public void setApplyInstructionLabels(boolean applyInstructionLabels) {
this.applyInstructionLabels = applyInstructionLabels;
}
/**
* Returns {@code true} if applyInstructionLabels is "on."
* @return {@code true} if applyInstructionLabels is "on."
*/
public boolean applyInstructionLabels() {
return applyInstructionLabels;
}
/**
* Enable/disable the option to only apply data types (skipping symbol information).
* In other words, the default is to apply symbols.
* @param applyDataTypesOnly {@code true} to turn applyDataTypesOnly on
*/
public void setApplyDataTypesOnly(boolean applyDataTypesOnly) {
this.applyDataTypesOnly = applyDataTypesOnly;
}
/**
* Returns {@code true} if applyDataTypesOnly is "on."
* @return {@code true} if applyDataTypesOnly is "on."
*/
public boolean applyDataTypesOnly() {
return applyDataTypesOnly;
}
/**
* Enable/disable the option to only apply public symbols.
* @param applyPublicSymbolsOnly {@code true} to turn applyPublicSymbolsOnly on
*/
public void setApplyPublicSymbolsOnly(boolean applyPublicSymbolsOnly) {
this.applyPublicSymbolsOnly = applyPublicSymbolsOnly;
}
/**
* Returns {@code true} if applyPublicSymbolsOnly is "on."
* @return {@code true} if applyPublicSymbolsOnly is "on."
*/
public boolean applyPublicSymbolsOnly() {
return applyPublicSymbolsOnly;
}
/**
* Enable/disable the option to attempt to map addresses using existing mangled symbols
* (typically public symbols).
* @param enable {@code true} to turn remapAddressesUsingExistingPublicSymbols on
*/
public void setRemapAddressUsingExistingPublicMangledSymbols(boolean enable) {
this.remapAddressesUsingExistingPublicSymbols = enable;
}
/**
* Returns {@code true} if remapAddressesUsingExistingPublicSymbols is "on."
* @return {@code true} if remapAddressesUsingExistingPublicSymbols is "on."
*/
public boolean remapAddressUsingExistingPublicMangledSymbols() {
return remapAddressesUsingExistingPublicSymbols;
}
/**
* Enable/disable the option to allow another symbol be set to primary when the existing
* primary symbol is a mangled symbol, regardless of the Symbol SourceType. This is
* typically used when we can get better data type information from the PDB record than
* we can from the demangler.
* @param enable {@code true} to turn allowDemotePrimaryMangledSymbol on
*/
public void setAllowDemotePrimaryMangledSymbol(boolean enable) {
this.allowDemotePrimaryMangledSymbol = enable;
}
/**
* Returns {@code true} if allowDemotePrimaryMangledSymbol is "on."
* @return {@code true} if allowDemotePrimaryMangledSymbol is "on."
*/
public boolean allowDemotePrimaryMangledSymbol() {
return allowDemotePrimaryMangledSymbol;
}
/**
* Enable/disable the option to apply function params and locals, which might produce improper
* results.
* @param applyFunctionVariables {@code true} to turn applyPublicSymbolsOnly on
*/
public void setApplyFunctionVariables(boolean applyFunctionVariables) {
this.applyFunctionVariables = applyFunctionVariables;
}
/**
* Returns {@code true} if applyFunctionVariables is "on."
* @return {@code true} if applyFunctionVariables is "on."
*/
public boolean applyFunctionVariables() {
return applyFunctionVariables;
}
/**
* Set the CompositeLayoutMode.
* @param compositeLayoutMode composite layout mode
*/
public void setCompositeLayoutMode(CompositeLayoutMode compositeLayoutMode) {
this.compositeLayoutMode = compositeLayoutMode;
}
/**
* Returns the mode for physically layout out composites.
* @return the layout mode.
*/
public CompositeLayoutMode getCompositeLayoutMode() {
return compositeLayoutMode;
}
}

View file

@ -0,0 +1,698 @@
/* ###
* 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.PdbLog;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.PrimitiveMsType;
import ghidra.program.model.data.*;
import ghidra.util.exception.AssertException;
/**
* Takes care of allocating unique instances of primitive data types for the {@link PdbApplicator},
* and is used principally by the many instances of {@link PrimitiveTypeApplier}.
*/
public class PdbPrimitiveTypeApplicator {
private final static DataType NO_TYPE_DATATYPE =
new TypedefDataType("<NoType>", Undefined1DataType.dataType);
//==============================================================================================
private DataTypeManager dataTypeManager;
//==============================================================================================
private DataType voidGhidraPrimitive = null;
private DataType charGhidraPrimitive = null;
private DataType signedCharGhidraPrimitive = null;
private DataType unsignedCharGhidraPrimitive = null;
//private Map<Integer, DataType> booleanGhidraPrimitives = new HashMap<>();
private Map<Integer, DataType> integralGhidraPrimitives = new HashMap<>();
private Map<Integer, DataType> unsignedIntegralGhidraPrimitives = new HashMap<>();
private Map<Integer, DataType> floatGhidraPrimitives = new HashMap<>();
private Map<Integer, DataType> complexGhidraPrimitives = new HashMap<>();
private Map<String, DataType> otherPrimitives = new HashMap<>();
//==============================================================================================
/**
* Constructor
* @param dataTypeManager The {@link DataTypeManager} associated with these types.
*/
public PdbPrimitiveTypeApplicator(DataTypeManager dataTypeManager) {
Objects.requireNonNull(dataTypeManager, "dataTypeManager cannot be null");
this.dataTypeManager = dataTypeManager;
}
/**
* Returns the {@link DataTypeManager} associated with this analyzer.
* @return DataTypeManager which this analyzer is using.
*/
private DataTypeManager getDataTypeManager() {
return dataTypeManager;
}
DataType resolve(DataType dataType) {
return getDataTypeManager().resolve(dataType,
DataTypeConflictHandler.REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER);
}
//==============================================================================================
DataType getNoType(PrimitiveMsType type) {
return NO_TYPE_DATATYPE;
}
DataType getVoidType() {
if (voidGhidraPrimitive == null) {
DataType dataType = new VoidDataType(getDataTypeManager());
voidGhidraPrimitive = resolve(dataType);
}
return voidGhidraPrimitive;
}
DataType getCharType() {
if (charGhidraPrimitive == null) {
DataType dataType = new CharDataType(getDataTypeManager());
charGhidraPrimitive = resolve(dataType);
}
return charGhidraPrimitive;
}
DataType getSignedCharType() {
if (signedCharGhidraPrimitive == null) {
DataType dataType = new SignedCharDataType(getDataTypeManager());
signedCharGhidraPrimitive = resolve(dataType);
}
return signedCharGhidraPrimitive;
}
DataType getUnsignedCharType() {
if (unsignedCharGhidraPrimitive == null) {
DataType dataType = new UnsignedCharDataType(getDataTypeManager());
unsignedCharGhidraPrimitive = resolve(dataType);
}
return unsignedCharGhidraPrimitive;
}
DataType getUnicode16Type() {
// For now, we are returning WideChar16 for Unicode16.
return new WideChar16DataType(getDataTypeManager());
}
DataType getUnicode32Type() {
// For now, we are returning WideChar32 for Unicode32.
return new WideChar32DataType(getDataTypeManager());
}
WideCharDataType getWideCharType() {
return new WideCharDataType(getDataTypeManager());
}
DataType get8BitIntegerType() {
String name = "int8";
DataType dataType = otherPrimitives.get(name);
if (dataType != null) {
return dataType;
}
DataType type;
if (getDataTypeManager().getDataOrganization().getIntegerSize() == 1) {
type = IntegerDataType.dataType;
}
else {
type = createTypedef(name, getIntegralType(1));
}
DataType resolved = resolve(type);
otherPrimitives.put(name, resolved);
return resolved;
}
DataType get8BitUnsignedIntegerType() {
String name = "uint8";
DataType dataType = otherPrimitives.get(name);
if (dataType != null) {
return dataType;
}
DataType type;
if (getDataTypeManager().getDataOrganization().getIntegerSize() == 1) {
type = UnsignedIntegerDataType.dataType;
}
else {
type = createTypedef(name, getUnsignedIntegralType(1));
}
DataType resolved = resolve(type);
otherPrimitives.put(name, resolved);
return resolved;
}
DataType get16BitIntegerType() {
String name = "int16";
DataType dataType = otherPrimitives.get(name);
if (dataType != null) {
return dataType;
}
DataType type;
if (getDataTypeManager().getDataOrganization().getIntegerSize() == 2) {
type = IntegerDataType.dataType;
}
else {
type = createTypedef(name, getIntegralType(2));
}
DataType resolved = resolve(type);
otherPrimitives.put(name, resolved);
return resolved;
}
DataType get16BitUnsignedIntegerType() {
String name = "uint16";
DataType dataType = otherPrimitives.get(name);
if (dataType != null) {
return dataType;
}
DataType type;
if (getDataTypeManager().getDataOrganization().getIntegerSize() == 2) {
type = UnsignedIntegerDataType.dataType;
}
else {
type = createTypedef(name, getUnsignedIntegralType(2));
}
DataType resolved = resolve(type);
otherPrimitives.put(name, resolved);
return resolved;
}
DataType get16BitShortType() {
String name = "short16";
DataType dataType = otherPrimitives.get(name);
if (dataType != null) {
return dataType;
}
DataType type;
if (getDataTypeManager().getDataOrganization().getShortSize() == 2) {
type = ShortDataType.dataType;
}
else {
type = createTypedef(name, getIntegralType(2));
}
DataType resolved = resolve(type);
otherPrimitives.put(name, resolved);
return resolved;
}
DataType get16BitUnsignedShortType() {
String name = "ushort16";
DataType dataType = otherPrimitives.get(name);
if (dataType != null) {
return dataType;
}
DataType type;
if (getDataTypeManager().getDataOrganization().getShortSize() == 2) {
type = UnsignedShortDataType.dataType;
}
else {
type = createTypedef(name, getUnsignedIntegralType(2));
}
DataType resolved = resolve(type);
otherPrimitives.put(name, resolved);
return resolved;
}
DataType get32BitIntegerType() {
String name = "int32";
DataType dataType = otherPrimitives.get(name);
if (dataType != null) {
return dataType;
}
DataType type;
if (getDataTypeManager().getDataOrganization().getIntegerSize() == 4) {
type = IntegerDataType.dataType;
}
else {
type = createTypedef(name, getIntegralType(4));
}
DataType resolved = resolve(type);
otherPrimitives.put(name, resolved);
return resolved;
}
DataType get32BitUnsignedIntegerType() {
String name = "uint32";
DataType dataType = otherPrimitives.get(name);
if (dataType != null) {
return dataType;
}
DataType type;
if (getDataTypeManager().getDataOrganization().getIntegerSize() == 4) {
type = UnsignedIntegerDataType.dataType;
}
else {
type = createTypedef(name, getUnsignedIntegralType(4));
}
DataType resolved = resolve(type);
otherPrimitives.put(name, resolved);
return resolved;
}
DataType get32BitLongType() {
String name = "long32";
DataType dataType = otherPrimitives.get(name);
if (dataType != null) {
return dataType;
}
DataType type;
if (getDataTypeManager().getDataOrganization().getLongSize() == 4) {
type = LongDataType.dataType;
}
else {
type = createTypedef(name, getIntegralType(4));
}
DataType resolved = resolve(type);
otherPrimitives.put(name, resolved);
return resolved;
}
DataType get32BitUnsignedLongType() {
String name = "ulong32";
DataType dataType = otherPrimitives.get(name);
if (dataType != null) {
return dataType;
}
DataType type;
if (getDataTypeManager().getDataOrganization().getLongSize() == 4) {
type = UnsignedLongDataType.dataType;
}
else {
type = createTypedef(name, getUnsignedIntegralType(4));
}
DataType resolved = resolve(type);
otherPrimitives.put(name, resolved);
return resolved;
}
DataType get64BitLongType() {
String name = "long64";
DataType dataType = otherPrimitives.get(name);
if (dataType != null) {
return dataType;
}
DataType type;
if (getDataTypeManager().getDataOrganization().getLongSize() == 8) {
type = LongDataType.dataType;
}
else {
type = createTypedef(name, getIntegralType(8));
}
DataType resolved = resolve(type);
otherPrimitives.put(name, resolved);
return resolved;
}
DataType get64BitUnsignedLongType() {
String name = "ulong64";
DataType dataType = otherPrimitives.get(name);
if (dataType != null) {
return dataType;
}
DataType type;
if (getDataTypeManager().getDataOrganization().getLongSize() == 8) {
type = UnsignedLongDataType.dataType;
}
else {
type = createTypedef(name, getUnsignedIntegralType(8));
}
DataType resolved = resolve(type);
otherPrimitives.put(name, resolved);
return resolved;
}
DataType get64BitIntegerType() {
String name = "int64";
DataType dataType = otherPrimitives.get(name);
if (dataType != null) {
return dataType;
}
DataType type;
if (getDataTypeManager().getDataOrganization().getIntegerSize() == 8) {
type = IntegerDataType.dataType;
}
else {
type = createTypedef(name, getIntegralType(8));
}
DataType resolved = resolve(type);
otherPrimitives.put(name, resolved);
return resolved;
}
DataType get64BitUnsignedIntegerType() {
String name = "uint64";
DataType dataType = otherPrimitives.get(name);
if (dataType != null) {
return dataType;
}
DataType type;
if (getDataTypeManager().getDataOrganization().getIntegerSize() == 8) {
type = UnsignedIntegerDataType.dataType;
}
else {
type = createTypedef(name, getUnsignedIntegralType(8));
}
DataType resolved = resolve(type);
otherPrimitives.put(name, resolved);
return resolved;
}
DataType get128BitLongType() {
String name = "ulong128";
DataType dataType = otherPrimitives.get(name);
if (dataType != null) {
return dataType;
}
DataType type;
if (getDataTypeManager().getDataOrganization().getLongSize() == 16) {
type = LongDataType.dataType;
}
else {
type = createTypedef(name, getIntegralType(16));
}
DataType resolved = resolve(type);
otherPrimitives.put(name, resolved);
return resolved;
}
DataType get128BitUnsignedLongType() {
String name = "ulong128";
DataType dataType = otherPrimitives.get(name);
if (dataType != null) {
return dataType;
}
DataType type;
if (getDataTypeManager().getDataOrganization().getLongSize() == 16) {
type = UnsignedLongDataType.dataType;
}
else {
type = createTypedef(name, getUnsignedIntegralType(16));
}
DataType resolved = resolve(type);
otherPrimitives.put(name, resolved);
return resolved;
}
DataType get128BitIntegerType() {
String name = "int128";
DataType dataType = otherPrimitives.get(name);
if (dataType != null) {
return dataType;
}
DataType type;
if (getDataTypeManager().getDataOrganization().getIntegerSize() == 16) {
type = IntegerDataType.dataType;
}
else {
type = createTypedef(name, getIntegralType(16));
}
DataType resolved = resolve(type);
otherPrimitives.put(name, resolved);
return resolved;
}
DataType get128BitUnsignedIntegerType() {
String name = "uint128";
DataType dataType = otherPrimitives.get(name);
if (dataType != null) {
return dataType;
}
DataType type;
if (getDataTypeManager().getDataOrganization().getIntegerSize() == 16) {
type = UnsignedIntegerDataType.dataType;
}
else {
type = createTypedef(name, getUnsignedIntegralType(16));
}
DataType resolved = resolve(type);
otherPrimitives.put(name, resolved);
return resolved;
}
private DataType createTypedef(String name, DataType dataType) {
DataType typedefDataType = new TypedefDataType(name, dataType);
return resolve(typedefDataType);
}
DataType createTypedefNamedSizedType(String name, int size) {
DataType dataType = new TypedefDataType(name, Undefined.getUndefinedDataType(size));
return resolve(dataType);
}
DataType createTypedefNamedSizedType(PrimitiveMsType type) {
return createTypedefNamedSizedType(type.getName(), type.getTypeSize());
}
DataType createUnmappedPdbType(PrimitiveMsType type) {
String name = String.format("UnmappedPdbType%04X", type.getNumber());
return createTypedefNamedSizedType(name, 1);
}
private DataType getIntegralType(int size) {
DataType dataType = integralGhidraPrimitives.get(size);
if (dataType != null) {
return dataType;
}
DataType resolved =
resolve(AbstractIntegerDataType.getSignedDataType(size, getDataTypeManager()));
integralGhidraPrimitives.put(size, resolved);
return resolved;
}
private DataType getUnsignedIntegralType(int size) {
DataType dataType = unsignedIntegralGhidraPrimitives.get(size);
if (dataType != null) {
return dataType;
}
DataType resolved =
resolve(AbstractIntegerDataType.getUnsignedDataType(size, getDataTypeManager()));
unsignedIntegralGhidraPrimitives.put(size, resolved);
return resolved;
}
DataType get16BitRealType() {
return getRealType(2, "float16");
}
DataType get32BitRealType() {
return getRealType(4, "float32");
}
DataType get32BitPartialPrecisionRealType() {
// TODO: Do we have / can we craft this type?
return createTypedefNamedSizedType("T_REAL32PP", 4);
}
DataType get48BitRealType() {
return getRealType(6, "float48");
}
DataType get64BitRealType() {
return getRealType(8, "float64");
}
DataType get80BitRealType() {
return getRealType(10, "float80");
}
DataType get128BitRealType() {
return getRealType(16, "float128");
}
/*
* First get type from "other" list, which are typedefs to underlying primitives. If it does
* not exist, then find the proper underlying primitive, create the typedef, and cache this
* newly minted (typedef) unique primitive type.
*/
private DataType getRealType(int size, String name) {
DataType dataType = otherPrimitives.get(name);
if (dataType != null) {
return dataType;
}
dataType = floatGhidraPrimitives.get(size);
DataType resolved;
if (dataType == null) {
resolved = resolve(AbstractFloatDataType.getFloatDataType(size, getDataTypeManager()));
floatGhidraPrimitives.put(size, resolved);
if (resolved instanceof Undefined) { // Not a real type implemented in Ghidra.
DataType type = createTypedef(name, resolved);
resolved = resolve(type);
}
}
else {
resolved = dataType;
}
otherPrimitives.put(name, resolved);
return resolved;
}
DataType get16BitComplexType() {
return getComplexType(16);
}
DataType get32BitComplexType() {
return getComplexType(4);
}
DataType get32BitPartialPrecisionComplexType() {
return createTypedefNamedSizedType("T_CPLX32PP", 8);
}
DataType get48BitComplexType() {
return getComplexType(48);
}
DataType get64BitComplexType() {
return getComplexType(8);
}
DataType get80BitComplexType() {
return getComplexType(10);
}
DataType get128BitComplexType() {
return getComplexType(128);
}
private DataType getComplexType(int size) {
DataType dataType = complexGhidraPrimitives.get(size);
if (dataType != null) {
return dataType;
}
switch (size) {
case 32:
// Case 32 is presumably 32 bits per real/imag. This is 4 bytes each, or 8
// bytes total
// Use the internal type.
dataType = new Complex8DataType(getDataTypeManager());
break;
case 64:
// Case 64 is presumably 64 bits per real/imag. This is 8 bytes each, or 16
// bytes total
// Use the internal type.
dataType = new Complex16DataType(getDataTypeManager());
break;
case 80:
// TODO: Replace with Complex20DataType when it is available.
dataType = createTypedefNamedSizedType("T_CPLX80", 20);
break;
case 128:
// Case 128 is presumably 128 bits per real/imag. This is 16 bytes each, or 32
// bytes total
// Use the internal type.
dataType = new Complex32DataType(getDataTypeManager());
break;
default:
String message = "Programming error: Complex size not supported" + size;
PdbLog.message(message);
throw new AssertException(message);
}
DataType resolved = resolve(dataType);
complexGhidraPrimitives.put(size, resolved);
return resolved;
}
DataType get8BitBooleanType() {
return getBooleanType(1, "T_BOOL08");
}
DataType get16BitBooleanType() {
return getBooleanType(2, "T_BOOL16");
}
DataType get32BitBooleanType() {
return getBooleanType(4, "T_BOOL32");
}
DataType get64BitBooleanType() {
return getBooleanType(8, "T_BOOL64");
}
DataType get128BitBooleanType() {
return getBooleanType(16, "T_BOOL128");
}
/*
* First get type from "other" list, which are typedefs to underlying primitives. If it does
* not exist, then find the proper underlying primitive, create the typedef, and cache this
* newly minted (typedef) unique primitive type.
*/
private DataType getBooleanType(int size, String name) {
DataType dataType = otherPrimitives.get(name);
if (dataType != null) {
return dataType;
}
if (size == getBooleanSize()) { // TODO: see TODO inside called method.
dataType = new BooleanDataType(getDataTypeManager());
}
else {
dataType = getIntegralType(size);
}
DataType resolved = resolve(dataType);
otherPrimitives.put(name, resolved);
return resolved;
}
/*
* Mimics a Ghidra getBoolean type that has a data organizationhierarchy for which one can
* call getSize() on the type to determine the size of the boolean on the current
* architecture.
*/
private int getBooleanSize() {
return 1;
// TODO: change to the following line when it is available.
//return applicator.getDataTypeManager().getDataOrganization().getBooleanSize();
}
private Pointer getPointerType(int ptrSize, DataType baseType) {
if (getDataTypeManager().getDataOrganization().getPointerSize() == ptrSize) {
return getDataTypeManager().getPointer(baseType);
}
return getDataTypeManager().getPointer(baseType, ptrSize);
}
Pointer get16NearPointerType(PrimitiveMsType type, DataType baseType) {
return getPointerType(type.getTypeSize(), baseType);
}
Pointer get1616FarPointerType(PrimitiveMsType type, DataType baseType) {
return getPointerType(type.getTypeSize(), baseType);
}
Pointer get1616HugePointerType(PrimitiveMsType type, DataType baseType) {
return getPointerType(type.getTypeSize(), baseType);
}
Pointer get32PointerType(PrimitiveMsType type, DataType baseType) {
return getPointerType(type.getTypeSize(), baseType);
}
Pointer get1632PointerType(PrimitiveMsType type, DataType baseType) {
return getPointerType(type.getTypeSize(), baseType);
}
Pointer get64PointerType(PrimitiveMsType type, DataType baseType) {
return getPointerType(type.getTypeSize(), baseType);
}
Pointer get128PointerType(PrimitiveMsType type, DataType baseType) {
return getPointerType(type.getTypeSize(), baseType);
}
}

View file

@ -0,0 +1,63 @@
/* ###
* 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.HashMap;
import java.util.Map;
import ghidra.program.model.lang.Register;
import ghidra.program.model.listing.Program;
import ghidra.util.Msg;
/**
* Maps PDB register name to program {@link Register}.
*/
public class PdbRegisterNameToProgramRegisterMapper {
// Only need to map names that are different (probably not counting case, as rsp and RSP
// are equivalent.
private static Map<String, String> registerNameMap = new HashMap<>();
static {
//registerNameMap.put("rsp", "RSP");
registerNameMap.put("fbp", "RBP");
}
private Program program;
private Map<String, Register> pdbRegisterNameToRegisterMap;
public PdbRegisterNameToProgramRegisterMapper(Program program) {
this.program = program;
pdbRegisterNameToRegisterMap = new HashMap<>();
}
Register getRegister(String pdbRegisterName) {
Register register = pdbRegisterNameToRegisterMap.get(pdbRegisterName);
if (register != null) {
return register;
}
String registerName = registerNameMap.get(pdbRegisterName);
if (registerName == null) {
registerName = pdbRegisterName;
}
register = program.getRegister(registerName);
pdbRegisterNameToRegisterMap.put(pdbRegisterName, register);
if (register == null) {
Msg.info(this, "Program register not found for " + registerName);
}
return register;
}
}

View file

@ -0,0 +1,233 @@
/* ###
* 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.symbol.AbstractMsSymbol;
import ghidra.app.util.bin.format.pdb2.pdbreader.symbol.AbstractPublicMsSymbol;
import ghidra.app.util.pdb.pdbapplicator.SymbolGroup.AbstractMsSymbolIterator;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.DataTypeManager;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.util.Msg;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
/**
* Manages virtual base table lookup for PDB classes.
*/
public class PdbVbtManager extends VbtManager {
Map<String, Address> addressByMangledName;
Memory memory;
private static Memory getMemory(PdbApplicator applicator) throws PdbException {
Program program = applicator.getProgram();
if (program == null) {
throw new PdbException("Program null for VbtManager");
}
return program.getMemory();
}
// TODO: Research whether we ever find VBT symbols put into the program by the "loader."
// If we find some this way, then need to modify PdbVbtManager to also look
// through the loader symbol for them.
private static Map<String, Address> findVirtualBaseTableSymbols(PdbApplicator applicator)
throws CancelledException {
TaskMonitor monitor = applicator.getMonitor();
SymbolGroup symbolGroup = applicator.getSymbolGroup();
Map<String, Address> myAddressByMangledName = new HashMap<>();
PublicSymbolInformation publicSymbolInformation =
applicator.getPdb().getDatabaseInterface().getPublicSymbolInformation();
List<Long> offsets = publicSymbolInformation.getModifiedHashRecordSymbolOffsets();
applicator.setMonitorMessage("PDB: Searching for virtual base table symbols...");
monitor.initialize(offsets.size());
AbstractMsSymbolIterator iter = symbolGroup.iterator();
for (long offset : offsets) {
monitor.checkCanceled();
iter.initGetByOffset(offset);
if (!iter.hasNext()) {
break;
}
AbstractMsSymbol symbol = iter.peek();
if (symbol instanceof AbstractPublicMsSymbol) {
AbstractPublicMsSymbol pubSymbol = (AbstractPublicMsSymbol) symbol;
String name = pubSymbol.getName();
if (name.startsWith("??_8")) {
Address address = applicator.reladdr(pubSymbol);
myAddressByMangledName.put(name, address);
}
}
monitor.incrementProgress(1);
}
return myAddressByMangledName;
}
/**
* Virtual Base Table Lookup Manager
* @param applicator {@link PdbApplicator} for which this class is working.
* @throws PdbException If Program is null;
* @throws CancelledException upon user cancellation
*/
PdbVbtManager(PdbApplicator applicator) throws PdbException, CancelledException {
this(applicator.getDataTypeManager(), getMemory(applicator),
findVirtualBaseTableSymbols(applicator));
}
PdbVbtManager(DataTypeManager dataTypeManager, Memory memory,
Map<String, Address> addressByMangledName) {
super(dataTypeManager);
this.memory = memory;
this.addressByMangledName = addressByMangledName;
}
// /**
// * Builds the tables
// * @throws PdbException If Program is null;
// * @throws CancelledException upon user cancellation.
// */
// void CreateVirtualBaseTables() throws PdbException, CancelledException {
// createVirtualBaseTables();
// }
//
PdbVirtualBaseTable createVirtualBaseTableByName(String mangledName, int entrySize) {
Address address = addressByMangledName.get(mangledName);
if (address == null) {
return null;
//throw new PdbException("Cannot find address for table name: " + mangledName);
}
return createVirtualBaseTable(address, entrySize);
}
PdbVirtualBaseTable createVirtualBaseTable(Address address, int entrySize) {
VirtualBaseTable vbt = vbtByAddress.get(address);
if (vbt != null) {
String message =
"PDB: warning virtual base table already exists for address: " + address;
PdbLog.message(message);
Msg.info(this, message);
}
else {
vbt = new PdbVirtualBaseTable(memory, address, entrySize);
vbtByAddress.put(address, vbt);
}
if (!(vbt instanceof PdbVirtualBaseTable)) {
int a = 1;
a = a + 1;
}
return (PdbVirtualBaseTable) vbt;
}
/**
* Returns offset for vbtable (mangled name) and ordinal
* @param vbtMangledName mangled name of vbtable
* @param ordinal index into table
* @param size size of a vbt entry offset value
* @return the offset
* @throws PdbException if no address exists for mangled name
*/
long getOffset(String vbtMangledName, int ordinal, int size) throws PdbException {
Address address = addressByMangledName.get(vbtMangledName);
if (address == null) {
throw new PdbException(
"Virtual Base Table does not exist for symbol: " + vbtMangledName);
}
return getOffset(address, ordinal, size);
}
/**
* Returns the offset from the virtual base table entry
* @param address Address of virtual base table
* @param ordinal index into table
* @param size size of a vbt entry offset value
* @return the offset
* @throws PdbException if no table exists for address or no entry exists for ordinal
*/
long getOffset(Address address, int ordinal, int size) throws PdbException {
VirtualBaseTable table = vbtByAddress.get(address);
if (table == null) {
throw new PdbException("Virtual Base Table does not exist for address: " + address);
}
if (!(table instanceof PdbVirtualBaseTable)) {
throw new PdbException("Not a PDB Virtual Base Table for address: " + address);
}
VirtualBaseTableEntry entry =
((PdbVirtualBaseTable) table).getOrParseEntryByOrdinal(ordinal);
return entry.getOffset();
}
private void createVirtualBaseTables() {
for (Map.Entry<String, Address> entry : addressByMangledName.entrySet()) {
Address address = entry.getValue();
createVirtualBaseTable(address);
}
}
static VirtualBaseTableEntry parseVbtEntryFromMemory(Memory memory, Address address,
int ordinal, int size) throws PdbException {
if (size != 4 && size != 8) {
throw new IllegalArgumentException("Invalid size (" + size + "): must be 4 or 8.");
}
Address readAddress = address.add(ordinal * size);
long offset;
try {
offset = (size == 4) ? (long) memory.getInt(readAddress) : memory.getLong(readAddress);
}
catch (MemoryAccessException e) {
throw new PdbException(
"MemoryAccessException while trying to parse virtual base table entry at address: " +
readAddress);
}
return new VirtualBaseTableEntry(offset);
}
static class PdbVirtualBaseTable extends VirtualBaseTable {
private Memory memory;
private Address address;
private int entrySize;
PdbVirtualBaseTable(Memory memory, Address address, int entrySize) {
super();
this.memory = memory;
this.address = address;
this.entrySize = entrySize;
}
@Override
VirtualBaseTableEntry getEntry(int ordinal) throws PdbException {
return getOrParseEntryByOrdinal(ordinal);
//return entryByOrdinal.get(ordinal);
}
VirtualBaseTableEntry getOrParseEntryByOrdinal(int ordinal) throws PdbException {
VirtualBaseTableEntry entry = entryByOrdinal.get(ordinal);
if (entry == null) {
entry = parseVbtEntryFromMemory(memory, address, ordinal, entrySize);
addEntry(ordinal, entry);
}
return entry;
}
}
}

View file

@ -0,0 +1,66 @@
/* ###
* 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.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.symbol.AbstractMsSymbol;
import ghidra.app.util.bin.format.pdb2.pdbreader.symbol.PeCoffGroupMsSymbol;
import ghidra.app.util.pdb.pdbapplicator.SymbolGroup.AbstractMsSymbolIterator;
import ghidra.program.model.address.Address;
import ghidra.util.exception.AssertException;
import ghidra.util.exception.CancelledException;
/**
* Applier for {@link PeCoffGroupMsSymbol} symbols.
*/
public class PeCoffGroupSymbolApplier extends AbstractMsSymbolApplier {
private PeCoffGroupMsSymbol symbol;
public PeCoffGroupSymbolApplier(PdbApplicator applicator, AbstractMsSymbolIterator iter) {
super(applicator, iter);
AbstractMsSymbol abstractSymbol = iter.next();
if (!(abstractSymbol instanceof PeCoffGroupMsSymbol)) {
throw new AssertException(
"Invalid symbol type: " + abstractSymbol.getClass().getSimpleName());
}
symbol = (PeCoffGroupMsSymbol) abstractSymbol;
}
@Override
public void apply() throws PdbException, CancelledException {
applicator.addMemoryGroupRefinement(symbol);
Address symbolAddress = applicator.reladdr(symbol);
if (!Address.NO_ADDRESS.equals(symbolAddress)) {
boolean forcePrimary = applicator.shouldForcePrimarySymbol(symbolAddress, false);
String name = symbol.getName();
symbol.getLength();
symbol.getCharacteristics();
if (!applicator.createSymbol(symbolAddress, name, forcePrimary)) {
applicator.appendLogMsg("Unable to create symbol " + name + " at " + symbolAddress);
}
}
else {
// monitor.setMessage("Skipping NO_ADDRESS symbol " + symb.getName() + "...");
}
}
@Override
public void applyTo(AbstractMsSymbolApplier applyToApplier) {
// Do nothing
}
}

View file

@ -0,0 +1,58 @@
/* ###
* 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.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.symbol.AbstractMsSymbol;
import ghidra.app.util.bin.format.pdb2.pdbreader.symbol.PeCoffSectionMsSymbol;
import ghidra.app.util.pdb.pdbapplicator.SymbolGroup.AbstractMsSymbolIterator;
import ghidra.util.exception.AssertException;
import ghidra.util.exception.CancelledException;
/**
* Applier for {@link PeCoffSectionMsSymbol} symbols.
*/
public class PeCoffSectionSymbolApplier extends AbstractMsSymbolApplier {
private PeCoffSectionMsSymbol symbol;
public PeCoffSectionSymbolApplier(PdbApplicator applicator, AbstractMsSymbolIterator iter) {
super(applicator, iter);
AbstractMsSymbol abstractSymbol = iter.next();
if (!(abstractSymbol instanceof PeCoffSectionMsSymbol)) {
throw new AssertException(
"Invalid symbol type: " + abstractSymbol.getClass().getSimpleName());
}
symbol = (PeCoffSectionMsSymbol) abstractSymbol;
}
@Override
public void apply() throws PdbException, CancelledException {
int sectionNum = symbol.getSectionNumber();
long realAddress = symbol.getRva();
symbol.getLength();
symbol.getCharacteristics();
symbol.getAlign();
symbol.getName();
applicator.putRealAddressesBySection(sectionNum, realAddress);
applicator.addMemorySectionRefinement(symbol);
}
@Override
public void applyTo(AbstractMsSymbolApplier applyToApplier) {
// Do nothing
}
}

View file

@ -0,0 +1,119 @@
/* ###
* 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.math.BigInteger;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.RecordNumber;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.*;
import ghidra.program.model.data.*;
import ghidra.util.exception.CancelledException;
/**
* Applier for {@link AbstractPointerMsType} types.
*/
public class PointerTypeApplier extends AbstractMsTypeApplier {
private static AbstractMsType validateType(AbstractMsType type)
throws IllegalArgumentException {
if (!(type instanceof AbstractPointerMsType)) {
throw new IllegalArgumentException(
"PDB Incorrectly applying " + type.getClass().getSimpleName() + " to " +
PointerTypeApplier.class.getSimpleName());
}
return type;
}
private boolean isFunctionPointer = false;
/**
* Constructor for pointer type applier, for transforming a enum into a
* Ghidra DataType.
* @param applicator {@link PdbApplicator} for which this class is working.
* @param msType {@link AbstractPointerMsType} to process
* @throws IllegalArgumentException Upon invalid arguments.
*/
public PointerTypeApplier(PdbApplicator applicator, AbstractMsType msType)
throws IllegalArgumentException {
super(applicator, validateType(msType));
}
public boolean isFunctionPointer() {
return isFunctionPointer;
}
@Override
public BigInteger getSize() {
return ((AbstractPointerMsType) msType).getSize();
}
@Override
public void apply() throws PdbException, CancelledException {
if (msType instanceof DummyMsType) {
dataType = new PointerDataType(applicator.getDataTypeManager());
}
else {
dataType = applyAbstractPointerMsType((AbstractPointerMsType) msType);
}
}
@Override
public void resolve() {
// Do not resolve pointer types... will be resolved naturally, as needed
}
public AbstractMsTypeApplier getUnmodifiedUnderlyingTypeApplier() {
AbstractMsTypeApplier thisUnderlyingTypeApplier =
applicator.getTypeApplier(((AbstractPointerMsType) msType).getUnderlyingRecordNumber());
// TODO: does not recurse below one level of modifiers... consider doing a recursion.
if (thisUnderlyingTypeApplier instanceof ModifierTypeApplier) {
ModifierTypeApplier x = (ModifierTypeApplier) thisUnderlyingTypeApplier;
RecordNumber recNum =
((AbstractModifierMsType) (x.getMsType())).getModifiedRecordNumber();
thisUnderlyingTypeApplier = applicator.getTypeApplier(recNum);
}
return thisUnderlyingTypeApplier;
}
private DataType applyAbstractPointerMsType(AbstractPointerMsType type) {
AbstractMsTypeApplier underlyingApplier =
applicator.getTypeApplier(type.getUnderlyingRecordNumber());
if (underlyingApplier instanceof ProcedureTypeApplier) {
isFunctionPointer = true;
}
//DataType underlyingType = underlyingApplier.getCycleBreakType(); // out 20191211
DataType underlyingType = underlyingApplier.getCycleBreakType();
if (underlyingType == null) {
// TODO: we have seen underlyingTypeApplier is for NoTypeApplier for VtShapeMsType
// Figure it out, and perhaps create an applier that creates a structure or something?
return new VoidDataType(applicator.getDataTypeManager());
// throw new PdbException("Underlying type missing for pointer.");
}
int size = getSizeInt();
if (size == applicator.getDataOrganization().getPointerSize()) {
size = -1; // Use default
}
Pointer pointer =
new PointerDataType(underlyingType, size, applicator.getDataTypeManager());
return pointer;
}
}

View file

@ -0,0 +1,90 @@
/* ###
* 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.math.BigInteger;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.RecordNumber;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.*;
import ghidra.program.model.data.DataType;
import ghidra.util.exception.CancelledException;
/**
* Applier for {@link AbstractProcedureMsType} types.
*/
public class ProcedureTypeApplier extends AbstractFunctionTypeApplier {
private static AbstractMsType validateType(AbstractMsType type)
throws IllegalArgumentException {
if (!(type instanceof AbstractProcedureMsType)) {
throw new IllegalArgumentException(
"PDB Incorrectly applying " + type.getClass().getSimpleName() + " to " +
ProcedureTypeApplier.class.getSimpleName());
}
return type;
}
/**
* Constructor for the applicator that applies {@link AbstractProcedureMsType},
* transforming it into a Ghidra {@link DataType}.
* @param applicator {@link PdbApplicator} for which this class is working.
* @param msType {@link AbstractProcedureMsType} to processes.
* @throws IllegalArgumentException Upon invalid arguments.
*/
public ProcedureTypeApplier(PdbApplicator applicator, AbstractMsType msType)
throws IllegalArgumentException {
super(applicator, validateType(msType));
}
@Override
public BigInteger getSize() {
return BigInteger.ZERO;
}
@Override
protected CallingConvention getCallingConvention() {
return ((AbstractProcedureMsType) msType).getCallingConvention();
}
@Override
protected boolean hasThisPointer() {
return false;
}
@Override
protected RecordNumber getReturnRecordNumber() {
return ((AbstractProcedureMsType) msType).getReturnRecordNumber();
}
@Override
protected RecordNumber getArgListRecordNumber() {
return ((AbstractProcedureMsType) msType).getArgListRecordNumber();
}
@Override
public void apply() throws PdbException, CancelledException {
applyFunction(getCallingConvention(), hasThisPointer());
// AbstractProcedureMsType procType = (AbstractProcedureMsType) msType;
// applyFunction(procType.getCallingConvention(), false, procType.getReturnTypeIndex(),
// procType.getArgListTypeIndex());
// DataType definition = applyFunction(procType.getCallingConvention(), false,
// procType.getReturnTypeIndex(), procType.getArgListTypeIndex());
// ghDataTypeDB = applicator.resolve(definition);
}
}

View file

@ -0,0 +1,99 @@
/* ###
* 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.List;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbLog;
import ghidra.app.util.bin.format.pdb2.pdbreader.symbol.AbstractMsSymbol;
import ghidra.app.util.bin.format.pdb2.pdbreader.symbol.AbstractPublicMsSymbol;
import ghidra.app.util.pdb.pdbapplicator.SymbolGroup.AbstractMsSymbolIterator;
import ghidra.program.model.address.Address;
import ghidra.program.model.symbol.Symbol;
import ghidra.util.Msg;
import ghidra.util.exception.AssertException;
import ghidra.util.exception.CancelledException;
/**
* Applier for {@link AbstractPublicMsSymbol} symbols.
*/
public class PublicSymbolApplier extends AbstractMsSymbolApplier {
private AbstractPublicMsSymbol symbol;
private Address symbolAddress = null;
private Address existingSymbolAddress = null;
public PublicSymbolApplier(PdbApplicator applicator, AbstractMsSymbolIterator iter) {
super(applicator, iter);
AbstractMsSymbol abstractSymbol = iter.next();
if (!(abstractSymbol instanceof AbstractPublicMsSymbol)) {
throw new AssertException(
"Invalid symbol type: " + abstractSymbol.getClass().getSimpleName());
}
symbol = (AbstractPublicMsSymbol) abstractSymbol;
}
@Override
public void applyTo(AbstractMsSymbolApplier applyToApplier) {
// Do nothing.
}
@Override
public void apply() throws CancelledException, PdbException {
symbolAddress = applicator.reladdr(symbol);
if (!Address.NO_ADDRESS.equals(symbolAddress)) {
if (getName().startsWith("?")) { // mangled... should be unique
List<Symbol> existingSymbols =
applicator.getProgram().getSymbolTable().getGlobalSymbols(getName());
if (existingSymbols.size() == 1) {
existingSymbolAddress = existingSymbols.get(0).getAddress();
applicator.putRemapAddressByAddress(symbolAddress, existingSymbolAddress);
}
else if (existingSymbols.size() == 0) {
String name = symbol.getName();
if (!applicator.createSymbol(symbolAddress, name, true)) {
applicator.appendLogMsg(
"Unable to create symbol " + name + " at " + symbolAddress);
}
}
else {
applicator.appendLogMsg(
"Unexpected multiple mangled symbols of same name: " + getName());
}
}
}
else {
String message = "Could not apply symbol at NO_ADDRESS: " + symbol.getName();
Msg.info(this, message);
PdbLog.message(message);
}
}
Address getAddress() {
return symbolAddress;
}
Address getAddressRemappedThroughPublicSymbol() {
return (existingSymbolAddress != null) ? existingSymbolAddress : symbolAddress;
}
String getName() {
return symbol.getName();
}
}

View file

@ -0,0 +1,71 @@
/* ###
* 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.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.symbol.AbstractMsSymbol;
import ghidra.app.util.bin.format.pdb2.pdbreader.symbol.AbstractReferenceMsSymbol;
import ghidra.app.util.pdb.pdbapplicator.SymbolGroup.AbstractMsSymbolIterator;
import ghidra.util.exception.AssertException;
import ghidra.util.exception.CancelledException;
/**
* Applier for {@link AbstractReferenceMsSymbol} symbols.
*/
public class ReferenceSymbolApplier extends AbstractMsSymbolApplier {
private AbstractReferenceMsSymbol symbol;
public ReferenceSymbolApplier(PdbApplicator applicator, AbstractMsSymbolIterator iter) {
super(applicator, iter);
AbstractMsSymbol abstractSymbol = iter.next();
if (!(abstractSymbol instanceof AbstractReferenceMsSymbol)) {
throw new AssertException(
"Invalid symbol type: " + abstractSymbol.getClass().getSimpleName());
}
symbol = (AbstractReferenceMsSymbol) abstractSymbol;
}
@Override
public void applyTo(AbstractMsSymbolApplier applyToApplier) {
// Do nothing.
}
@Override
public void apply() throws CancelledException, PdbException {
// Potential recursive call via applicator.procSym().
AbstractMsSymbolIterator refIter = getInitializedReferencedSymbolGroup().iterator();
applicator.procSym(refIter);
}
SymbolGroup getInitializedReferencedSymbolGroup() {
SymbolGroup refSymbolGroup = getReferencedSymbolGroup();
AbstractMsSymbolIterator refIter = refSymbolGroup.iterator();
refIter.initGetByOffset(getOffsetInReferencedSymbolGroup());
return refSymbolGroup;
}
SymbolGroup getReferencedSymbolGroup() {
int refModuleNumber = symbol.getModuleIndex();
return applicator.getSymbolGroupForModule(refModuleNumber);
}
long getOffsetInReferencedSymbolGroup() {
// Adjusting offset to the offset we use for parsing the complete record.
return symbol.getOffsetActualSymbolInDollarDollarSymbols() - 4;
}
}

View file

@ -0,0 +1,156 @@
/* ###
* 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.Objects;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbLog;
import ghidra.app.util.bin.format.pdb2.pdbreader.symbol.AbstractMsSymbol;
import ghidra.app.util.bin.format.pdb2.pdbreader.symbol.AbstractRegisterRelativeAddressMsSymbol;
import ghidra.app.util.pdb.pdbapplicator.SymbolGroup.AbstractMsSymbolIterator;
import ghidra.program.model.data.DataType;
import ghidra.program.model.lang.Register;
import ghidra.program.model.listing.*;
import ghidra.program.model.symbol.SourceType;
import ghidra.util.Msg;
import ghidra.util.exception.*;
/**
* Applier for {@link AbstractRegisterRelativeAddressMsSymbol} symbols.
*/
public class RegisterRelativeSymbolApplier extends AbstractMsSymbolApplier {
private AbstractRegisterRelativeAddressMsSymbol symbol;
public RegisterRelativeSymbolApplier(PdbApplicator applicator, AbstractMsSymbolIterator iter) {
super(applicator, iter);
AbstractMsSymbol abstractSymbol = iter.next();
if (!(abstractSymbol instanceof AbstractRegisterRelativeAddressMsSymbol)) {
throw new AssertException(
"Invalid symbol type: " + abstractSymbol.getClass().getSimpleName());
}
symbol = (AbstractRegisterRelativeAddressMsSymbol) abstractSymbol;
}
@Override
public void apply() throws PdbException, CancelledException {
String message = "Cannot apply " + this.getClass().getSimpleName() + " directly to program";
Msg.info(this, message);
PdbLog.message(message);
}
@Override
public void applyTo(AbstractMsSymbolApplier applyToApplier)
throws PdbException, CancelledException {
if (!applicator.getPdbApplicatorOptions().applyFunctionVariables()) {
return;
}
if (applyToApplier instanceof FunctionSymbolApplier) {
FunctionSymbolApplier functionSymbolApplier = (FunctionSymbolApplier) applyToApplier;
createFunctionVariable(functionSymbolApplier);
}
}
private boolean createFunctionVariable(FunctionSymbolApplier applier) {
Objects.requireNonNull(applier, "FunctionSymbolApplier cannot be null");
Function function = applier.getFunction();
if (function == null) {
applicator.appendLogMsg("Could not create stack variable for non-existent function.");
return false;
}
//Variable[] allVariables = function.getAllVariables();
String registerName = symbol.getRegisterNameString();
Register register = applicator.getRegister(registerName);
Register sp = applicator.getProgram().getCompilerSpec().getStackPointer();
if (register != sp) {
// have seen fbp here.
// TODO; can we do something more generic below that does not rely on stack frame?
// would like to do RSP + X, or RBP + X, or RDX + X.
return false;
}
Integer registerChange = applier.getRegisterPrologChange(register);
StackFrame stackFrame = function.getStackFrame();
int baseParamOffset = applier.getBaseParamOffset();
long frameSize = applier.getCurrentFrameSize();
// long relativeOffset = symbol.getOffset() - applier.getCurrentFrameSize();
if (registerChange == null) {
registerChange = 0;
}
long relativeOffset = symbol.getOffset() + registerChange;
// long relativeOffset = symbol.getOffset() + x;
// if (relativeOffset > Integer.MAX_VALUE) {
// applicator.appendLogMsg("Offset too large for our applier.");
// //return false;
// }
int offset = (int) (relativeOffset & 0xffffffffL);
AbstractMsTypeApplier dataTypeApplier =
applicator.getTypeApplier(symbol.getTypeRecordNumber());
DataType dt = dataTypeApplier.getDataType();
if (dt != null) {
// Variable m16 = stackFrame.getVariableContaining(-16);
// Variable m8 = stackFrame.getVariableContaining(-8);
// Variable x0 = stackFrame.getVariableContaining(0);
// Variable x8 = stackFrame.getVariableContaining(8);
// Variable x16 = stackFrame.getVariableContaining(16);
// Variable x24 = stackFrame.getVariableContaining(24);
// Variable x32 = stackFrame.getVariableContaining(32);
// Variable x40 = stackFrame.getVariableContaining(40);
// Variable x48 = stackFrame.getVariableContaining(48);
// Variable x56 = stackFrame.getVariableContaining(56);
Variable variable = stackFrame.getVariableContaining(offset);
try {
if (variable == null || variable.getStackOffset() != offset) {
if (variable != null) {
stackFrame.clearVariable(variable.getStackOffset());
}
try {
variable = stackFrame.createVariable(symbol.getName(), offset, dt,
SourceType.IMPORTED);
}
catch (DuplicateNameException e) {
variable = stackFrame.createVariable(
symbol.getName() + "@" + Integer.toHexString(offset), offset, dt,
SourceType.IMPORTED);
}
}
else {
variable.setDataType(dt, false, true, SourceType.ANALYSIS);
try {
variable.setName(symbol.getName(), SourceType.IMPORTED);
}
catch (DuplicateNameException e) {
variable.setName(symbol.getName() + "@" + Integer.toHexString(offset),
SourceType.IMPORTED);
}
}
}
catch (Exception e) {
applicator.appendLogMsg("Unable to create stack variable " + symbol.getName() +
" at offset " + offset + " in " + function.getName());
return false;
}
}
return true;
}
}

View file

@ -0,0 +1,135 @@
/* ###
* 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.cmd.comments.SetCommentCmd;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.symbol.SeparatedCodeFromCompilerSupportMsSymbol;
import ghidra.app.util.pdb.pdbapplicator.SymbolGroup.AbstractMsSymbolIterator;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.CodeUnit;
import ghidra.util.Msg;
import ghidra.util.exception.CancelledException;
/**
* Applier for {@link SeparatedCodeFromCompilerSupportMsSymbol} symbol.
*/
// TODO: Need to evaluate relationship to function symbols.
// TODO: Need to create anonymous name for this as a function?
public class SeparatedCodeSymbolApplier extends AbstractMsSymbolApplier {
private SeparatedCodeFromCompilerSupportMsSymbol symbol;
private String craftedName;
private Address specifiedAddress;
private BlockCommentsManager comments;
private int symbolBlockNestingLevel;
private Address currentBlockAddress;
private List<AbstractMsSymbolApplier> allAppliers = new ArrayList<>();
private static AbstractMsSymbolIterator validateSymbol(AbstractMsSymbolIterator iter) {
if (!(iter.peek() instanceof SeparatedCodeFromCompilerSupportMsSymbol)) {
throw new IllegalArgumentException("Not a SeparatedCodeFromCompilerSupportMsSymbol");
}
return iter;
}
public SeparatedCodeSymbolApplier(PdbApplicator applicator, AbstractMsSymbolIterator iter)
throws CancelledException, NoSuchElementException {
super(applicator, validateSymbol(iter));
symbol = (SeparatedCodeFromCompilerSupportMsSymbol) iter.next();
specifiedAddress = applicator.reladdr(symbol);
// Make up name. TODO: decide if need better anonymous name
craftedName = String.format("CompilerSeparatedCode%s", specifiedAddress);
symbolBlockNestingLevel = 0;
comments = new BlockCommentsManager();
currentBlockAddress = null;
manageBlockNesting(this);
while (notDone()) {
applicator.checkCanceled();
AbstractMsSymbolApplier applier = applicator.getSymbolApplier(iter);
if (!(applier instanceof EndSymbolApplier)) {
Msg.info(this, "Unexpected applier in " + getClass().getSimpleName() + ": " +
applier.getClass().getSimpleName());
}
allAppliers.add(applier);
applier.manageBlockNesting(this);
}
}
@Override
public void manageBlockNesting(AbstractMsSymbolApplier applierParam) {
beginBlock(specifiedAddress);
}
@Override
public void applyTo(AbstractMsSymbolApplier applyToApplier) {
// Do nothing.
}
@Override
public void apply() throws PdbException, CancelledException {
// DO NOTHING FOR NOW. TODO: should we have a configuration option?
// Note: these comments can be noise in the decompiler an code browser
setComments(false);
}
private void setComments(boolean enabled) {
if (enabled) {
String existingComment = applicator.getProgram().getListing().getComment(
CodeUnit.PRE_COMMENT, specifiedAddress);
String p = "*************************************************************\n";
String newComment =
String.format(p + "* Separated code (from the compiler): %s - %s *\n" + p,
specifiedAddress.toString(),
specifiedAddress.add(symbol.getBlockLength() - 1).toString());
String comment =
(existingComment == null) ? newComment : existingComment + "\n" + newComment;
SetCommentCmd.createComment(applicator.getProgram(), specifiedAddress, comment,
CodeUnit.PRE_COMMENT);
}
}
private boolean notDone() {
return (symbolBlockNestingLevel > 0) && iter.hasNext();
}
public int endBlock() {
if (--symbolBlockNestingLevel < 0) {
applicator.appendLogMsg("Block Nesting went negative at " + specifiedAddress);
}
return symbolBlockNestingLevel;
}
private int beginBlock(Address startAddress) {
currentBlockAddress = startAddress;
++symbolBlockNestingLevel;
return symbolBlockNestingLevel;
}
}

View file

@ -0,0 +1,659 @@
/* ###
* 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.NoSuchElementException;
import ghidra.app.util.bin.format.pdb2.pdbreader.symbol.*;
import ghidra.app.util.pdb.pdbapplicator.SymbolGroup.AbstractMsSymbolIterator;
import ghidra.util.exception.CancelledException;
public class SymbolApplierParser {
private PdbApplicator applicator;
SymbolApplierParser(PdbApplicator applicator) {
this.applicator = applicator;
}
// TODO: 20191120... Do we need a SymbolApplier cache for Symbols like we have the TypeApplier
// cache (by index) for Types/Items? Would we use a record number (index) from within
// the AbstractMsSymbol (do one for AbstractMsType as well)? Symbols are different in that
// we are using SymbolGroup as a member instead of MsType.
AbstractMsSymbolApplier getSymbolApplier(AbstractMsSymbolIterator iter)
throws CancelledException, NoSuchElementException {
AbstractMsSymbol symbol = iter.peek();
if (symbol == null) {
applicator.appendLogMsg("PDB Warning: No AbstractMsSymbol");
return null;
}
AbstractMsSymbolApplier applier = null;
switch (symbol.getPdbId()) {
// // 0x0000 block
// case CompileFlagsMsSymbol.PDB_ID:
// symbol = new CompileFlagsMsSymbol(pdb, reader);
// break;
// case Register16MsSymbol.PDB_ID:
// symbol = new Register16MsSymbol(pdb, reader);
// break;
// case Constant16MsSymbol.PDB_ID:
// symbol = new Constant16MsSymbol(pdb, reader);
// break;
case UserDefinedType16MsSymbol.PDB_ID:
applier = new TypedefSymbolApplier(applicator, iter);
break;
// case StartSearchMsSymbol.PDB_ID:
// symbol = new StartSearchMsSymbol(pdb, reader);
// break;
case EndMsSymbol.PDB_ID:
applier = new EndSymbolApplier(applicator, iter);
break;
// case SkipMsSymbol.PDB_ID:
// symbol = new SkipMsSymbol(pdb, reader);
// break;
// case CvReservedMsSymbol.PDB_ID:
// symbol = new CvReservedMsSymbol(pdb, reader);
// break;
// case ObjectNameStMsSymbol.PDB_ID:
// symbol = new ObjectNameStMsSymbol(pdb, reader);
// break;
// case EndArgumentsListMsSymbol.PDB_ID:
// symbol = new EndArgumentsListMsSymbol(pdb, reader);
// break;
case CobolUserDefinedType16MsSymbol.PDB_ID:
applier = new TypedefSymbolApplier(applicator, iter);
break;
// case ManyRegisterVariable16MsSymbol.PDB_ID:
// symbol = new ManyRegisterVariable16MsSymbol(pdb, reader);
// break;
// case ReturnDescriptionMsSymbol.PDB_ID:
// symbol = new ReturnDescriptionMsSymbol(pdb, reader);
// break;
// case EntryThisMsSymbol.PDB_ID:
// symbol = new EntryThisMsSymbol(pdb, reader);
// break;
//
// // 0x0100 block
// case BasePointerRelative16MsSymbol.PDB_ID:
// symbol = new BasePointerRelative16MsSymbol(pdb, reader);
// break;
case LocalData16MsSymbol.PDB_ID:
applier = new DataSymbolApplier(applicator, iter);
break;
case GlobalData16MsSymbol.PDB_ID:
applier = new DataSymbolApplier(applicator, iter);
break;
case Public16MsSymbol.PDB_ID:
applier = new PublicSymbolApplier(applicator, iter);
break;
case LocalProcedureStart16MsSymbol.PDB_ID:
applier = new FunctionSymbolApplier(applicator, iter);
break;
case GlobalProcedureStart16MsSymbol.PDB_ID:
applier = new FunctionSymbolApplier(applicator, iter);
break;
case Thunk16MsSymbol.PDB_ID:
applier = new FunctionSymbolApplier(applicator, iter);
break;
case Block16MsSymbol.PDB_ID:
applier = new BlockSymbolApplier(applicator, iter);
break;
case With16MsSymbol.PDB_ID:
applier = new WithSymbolApplier(applicator, iter);
break;
case Label16MsSymbol.PDB_ID:
applier = new LabelSymbolApplier(applicator, iter);
break;
// case ChangeExecutionModel16MsSymbol.PDB_ID:
// symbol = new ChangeExecutionModel16MsSymbol(pdb, reader);
// break;
// case VirtualFunctionTable16MsSymbol.PDB_ID:
// symbol = new VirtualFunctionTable16MsSymbol(pdb, reader);
// break;
case RegisterRelativeAddress16MsSymbol.PDB_ID:
applier = new RegisterRelativeSymbolApplier(applicator, iter);
break;
//
// // 0x0200 block
// case BasePointerRelative3216MsSymbol.PDB_ID:
// symbol = new BasePointerRelative3216MsSymbol(pdb, reader);
// break;
case LocalData3216MsSymbol.PDB_ID:
applier = new DataSymbolApplier(applicator, iter);
break;
case GlobalData3216MsSymbol.PDB_ID:
applier = new DataSymbolApplier(applicator, iter);
break;
case Public3216MsSymbol.PDB_ID:
applier = new PublicSymbolApplier(applicator, iter);
break;
case LocalProcedureStart3216MsSymbol.PDB_ID:
applier = new FunctionSymbolApplier(applicator, iter);
break;
case GlobalProcedureStart3216MsSymbol.PDB_ID:
applier = new FunctionSymbolApplier(applicator, iter);
break;
case Thunk32StMsSymbol.PDB_ID:
applier = new FunctionSymbolApplier(applicator, iter);
break;
case Block32StMsSymbol.PDB_ID:
applier = new BlockSymbolApplier(applicator, iter);
break;
case With32StMsSymbol.PDB_ID:
applier = new WithSymbolApplier(applicator, iter);
break;
case Label32StMsSymbol.PDB_ID:
applier = new LabelSymbolApplier(applicator, iter);
break;
// case ChangeExecutionModel32MsSymbol.PDB_ID:
// symbol = new ChangeExecutionModel32MsSymbol(pdb, reader);
// break;
// case VirtualFunctionTable3216MsSymbol.PDB_ID:
// symbol = new VirtualFunctionTable3216MsSymbol(pdb, reader);
// break;
case RegisterRelativeAddress3216MsSymbol.PDB_ID:
applier = new RegisterRelativeSymbolApplier(applicator, iter);
break;
// case LocalThreadStorage3216MsSymbol.PDB_ID:
// symbol = new LocalThreadStorage3216MsSymbol(pdb, reader);
// break;
// case GlobalThreadStorage3216MsSymbol.PDB_ID:
// symbol = new GlobalThreadStorage3216MsSymbol(pdb, reader);
// break;
// case StaticLinkForMipsExceptionHandlingMsSymbol.PDB_ID:
// symbol = new StaticLinkForMipsExceptionHandlingMsSymbol(pdb, reader);
// break;
//
// // 0x0300 block
// case LocalProcedureStartMips16MsSymbol.PDB_ID:
// symbol = new LocalProcedureStartMips16MsSymbol(pdb, reader);
// break;
// case GlobalProcedureStartMips16MsSymbol.PDB_ID:
// symbol = new GlobalProcedureStartMips16MsSymbol(pdb, reader);
// break;
//
// // 0x0400 block
case ProcedureReferenceStMsSymbol.PDB_ID:
applier = new ReferenceSymbolApplier(applicator, iter);
break;
case DataReferenceStMsSymbol.PDB_ID:
applier = new ReferenceSymbolApplier(applicator, iter);
break;
// case AlignMsSymbol.PDB_ID:
// symbol = new AlignMsSymbol(pdb, reader);
// break;
case LocalProcedureReferenceStMsSymbol.PDB_ID:
applier = new ReferenceSymbolApplier(applicator, iter);
break;
// case OemDefinedMsSymbol.PDB_ID:
// symbol = new OemDefinedMsSymbol(pdb, reader);
// break;
//
// // 0x1000 block
// case RegisterStMsSymbol.PDB_ID:
// symbol = new RegisterStMsSymbol(pdb, reader);
// break;
// case ConstantStMsSymbol.PDB_ID:
// symbol = new ConstantStMsSymbol(pdb, reader);
// break;
case UserDefinedTypeStMsSymbol.PDB_ID:
applier = new TypedefSymbolApplier(applicator, iter);
break;
case CobolUserDefinedTypeStMsSymbol.PDB_ID:
applier = new TypedefSymbolApplier(applicator, iter);
break;
// case ManyRegisterVariableStMsSymbol.PDB_ID:
// symbol = new ManyRegisterVariableStMsSymbol(pdb, reader);
// break;
// case BasePointerRelative32StMsSymbol.PDB_ID:
// symbol = new BasePointerRelative32StMsSymbol(pdb, reader);
// break;
case LocalData32StMsSymbol.PDB_ID:
applier = new DataSymbolApplier(applicator, iter);
break;
case GlobalData32StMsSymbol.PDB_ID:
applier = new DataSymbolApplier(applicator, iter);
break;
case Public32StMsSymbol.PDB_ID:
applier = new PublicSymbolApplier(applicator, iter);
break;
case LocalProcedureStart32StMsSymbol.PDB_ID:
applier = new FunctionSymbolApplier(applicator, iter);
break;
case GlobalProcedureStart32StMsSymbol.PDB_ID:
applier = new FunctionSymbolApplier(applicator, iter);
break;
// case VirtualFunctionTable32MsSymbol.PDB_ID:
// symbol = new VirtualFunctionTable32MsSymbol(pdb, reader);
// break;
case RegisterRelativeAddress32StMsSymbol.PDB_ID:
applier = new RegisterRelativeSymbolApplier(applicator, iter);
break;
// case LocalThreadStorage32StMsSymbol.PDB_ID:
// symbol = new LocalThreadStorage32StMsSymbol(pdb, reader);
// break;
// case GlobalThreadStorage32StMsSymbol.PDB_ID:
// symbol = new GlobalThreadStorage32StMsSymbol(pdb, reader);
// break;
// case LocalProcedureStartMipsStMsSymbol.PDB_ID:
// symbol = new LocalProcedureStartMipsStMsSymbol(pdb, reader);
// break;
// case GlobalProcedureStartMipsStMsSymbol.PDB_ID:
// symbol = new GlobalProcedureStartMipsStMsSymbol(pdb, reader);
// break;
case ExtraFrameAndProcedureInformationMsSymbol.PDB_ID:
applier = new FrameAndProcedureInformationSymbolApplier(applicator, iter);
break;
// case Compile2StMsSymbol.PDB_ID:
// symbol = new Compile2StMsSymbol(pdb, reader);
// break;
// case ManyRegisterVariable2StMsSymbol.PDB_ID:
// symbol = new ManyRegisterVariable2StMsSymbol(pdb, reader);
// break;
// case LocalProcedureStartIa64StMsSymbol.PDB_ID:
// symbol = new LocalProcedureStartIa64StMsSymbol(pdb, reader);
// break;
// case GlobalProcedureStartIa64StMsSymbol.PDB_ID:
// symbol = new GlobalProcedureStartIa64StMsSymbol(pdb, reader);
// break;
// case LocalSlotIndexFieldedLILStMsSymbol.PDB_ID:
// symbol = new LocalSlotIndexFieldedLILStMsSymbol(pdb, reader);
// break;
// case ParameterSlotIndexFieldedLILStMsSymbol.PDB_ID:
// symbol = new ParameterSlotIndexFieldedLILStMsSymbol(pdb, reader);
// break;
// case AnnotationMsSymbol.PDB_ID:
// symbol = new AnnotationMsSymbol(pdb, reader);
// break;
// case GlobalManagedProcedureStMsSymbol.PDB_ID:
// symbol = new GlobalManagedProcedureStMsSymbol(pdb, reader);
// break;
// case LocalManagedProcedureStMsSymbol.PDB_ID:
// symbol = new LocalManagedProcedureStMsSymbol(pdb, reader);
// break;
// case Reserved1MsSymbol.PDB_ID:
// symbol = new Reserved1MsSymbol(pdb, reader);
// break;
// case Reserved2MsSymbol.PDB_ID:
// symbol = new Reserved2MsSymbol(pdb, reader);
// break;
// case Reserved3MsSymbol.PDB_ID:
// symbol = new Reserved3MsSymbol(pdb, reader);
// break;
// case Reserved4MsSymbol.PDB_ID:
// symbol = new Reserved4MsSymbol(pdb, reader);
// break;
case LocalManagedDataStMsSymbol.PDB_ID:
applier = new DataSymbolApplier(applicator, iter);
break;
case GlobalManagedDataStMsSymbol.PDB_ID:
applier = new DataSymbolApplier(applicator, iter);
break;
// case ManLocOrParamReltoVFPStMsSymbol.PDB_ID:
// symbol = new ManLocOrParamReltoVFPStMsSymbol(pdb, reader);
// break;
// case ManagedLocalOrParameterSIRStMsSymbol.PDB_ID:
// symbol = new ManagedLocalOrParameterSIRStMsSymbol(pdb, reader);
// break;
// case ManagedSymbolWithSlotIndexFieldStMsSymbol.PDB_ID:
// symbol = new ManagedSymbolWithSlotIndexFieldStMsSymbol(pdb, reader);
// break;
// case ManagedLocalOrParameterSIMRStMsSymbol.PDB_ID:
// symbol = new ManagedLocalOrParameterSIMRStMsSymbol(pdb, reader);
// break;
// case ManLocOrParamReltoAMPStMsSymbol.PDB_ID:
// symbol = new ManLocOrParamReltoAMPStMsSymbol(pdb, reader);
// break;
// case ManagedLocalOrParameterSIMR2StMsSymbol.PDB_ID:
// symbol = new ManagedLocalOrParameterSIMR2StMsSymbol(pdb, reader);
// break;
// case IndexForTypeReferencedByNameFromMetadataMsSymbol.PDB_ID:
// symbol = new IndexForTypeReferencedByNameFromMetadataMsSymbol(pdb, reader);
// break;
// case UsingNamespaceStMsSymbol.PDB_ID:
// symbol = new UsingNamespaceStMsSymbol(pdb, reader);
// break;
//
// // 0x1100 block
// case ObjectNameMsSymbol.PDB_ID:
// symbol = new ObjectNameMsSymbol(pdb, reader);
// break;
case Thunk32MsSymbol.PDB_ID:
applier = new FunctionSymbolApplier(applicator, iter);
break;
case Block32MsSymbol.PDB_ID:
applier = new BlockSymbolApplier(applicator, iter);
break;
case With32MsSymbol.PDB_ID:
applier = new WithSymbolApplier(applicator, iter);
break;
case Label32MsSymbol.PDB_ID:
applier = new LabelSymbolApplier(applicator, iter);
break;
// case RegisterMsSymbol.PDB_ID:
// symbol = new RegisterMsSymbol(pdb, reader);
// break;
// case ConstantMsSymbol.PDB_ID:
// symbol = new ConstantMsSymbol(pdb, reader);
// break;
case UserDefinedTypeMsSymbol.PDB_ID:
applier = new TypedefSymbolApplier(applicator, iter);
break;
case CobolUserDefinedTypeMsSymbol.PDB_ID:
applier = new TypedefSymbolApplier(applicator, iter);
break;
// case ManyRegisterVariableMsSymbol.PDB_ID:
// symbol = new ManyRegisterVariableMsSymbol(pdb, reader);
// break;
// case BasePointerRelative32MsSymbol.PDB_ID:
// symbol = new BasePointerRelative32MsSymbol(pdb, reader);
// break;
case LocalData32MsSymbol.PDB_ID:
applier = new DataSymbolApplier(applicator, iter);
break;
case GlobalData32MsSymbol.PDB_ID:
applier = new DataSymbolApplier(applicator, iter);
break;
case Public32MsSymbol.PDB_ID:
applier = new PublicSymbolApplier(applicator, iter);
break;
case LocalProcedureStart32MsSymbol.PDB_ID:
applier = new FunctionSymbolApplier(applicator, iter);
break;
case GlobalProcedureStart32MsSymbol.PDB_ID:
applier = new FunctionSymbolApplier(applicator, iter);
break;
case RegisterRelativeAddress32MsSymbol.PDB_ID:
applier = new RegisterRelativeSymbolApplier(applicator, iter);
break;
// case LocalThreadStorage32MsSymbol.PDB_ID:
// symbol = new LocalThreadStorage32MsSymbol(pdb, reader);
// break;
// case GlobalThreadStorage32MsSymbol.PDB_ID:
// symbol = new GlobalThreadStorage32MsSymbol(pdb, reader);
// break;
// case LocalProcedureStartMipsMsSymbol.PDB_ID:
// symbol = new LocalProcedureStartMipsMsSymbol(pdb, reader);
// break;
// case GlobalProcedureStartMipsMsSymbol.PDB_ID:
// symbol = new GlobalProcedureStartMipsMsSymbol(pdb, reader);
// break;
// case Compile2MsSymbol.PDB_ID:
// symbol = new Compile2MsSymbol(pdb, reader);
// break;
// case ManyRegisterVariable2MsSymbol.PDB_ID:
// symbol = new ManyRegisterVariable2MsSymbol(pdb, reader);
// break;
// case LocalProcedureStartIa64MsSymbol.PDB_ID:
// symbol = new LocalProcedureStartIa64MsSymbol(pdb, reader);
// break;
// case GlobalProcedureStartIa64MsSymbol.PDB_ID:
// symbol = new GlobalProcedureStartIa64MsSymbol(pdb, reader);
// break;
// case LocalSlotIndexFieldedLILMsSymbol.PDB_ID:
// symbol = new LocalSlotIndexFieldedLILMsSymbol(pdb, reader);
// break;
// case ParameterSlotIndexFieldedLILMsSymbol.PDB_ID:
// symbol = new ParameterSlotIndexFieldedLILMsSymbol(pdb, reader);
// break;
case LocalManagedDataMsSymbol.PDB_ID:
applier = new DataSymbolApplier(applicator, iter);
break;
case GlobalManagedDataMsSymbol.PDB_ID:
applier = new DataSymbolApplier(applicator, iter);
break;
// case ManLocOrParamReltoVFPMsSymbol.PDB_ID:
// symbol = new ManLocOrParamReltoVFPMsSymbol(pdb, reader);
// break;
// case ManagedLocalOrParameterSIRMsSymbol.PDB_ID:
// symbol = new ManagedLocalOrParameterSIRMsSymbol(pdb, reader);
// break;
// case ManagedSymbolWithSlotIndexFieldMsSymbol.PDB_ID:
// symbol = new ManagedSymbolWithSlotIndexFieldMsSymbol(pdb, reader);
// break;
// case ManagedLocalOrParameterSIMRMsSymbol.PDB_ID:
// symbol = new ManagedLocalOrParameterSIMRMsSymbol(pdb, reader);
// break;
// case ManLocOrParamReltoAMPMsSymbol.PDB_ID:
// symbol = new ManLocOrParamReltoAMPMsSymbol(pdb, reader);
// break;
// case ManagedLocalOrParameterSIMR2MsSymbol.PDB_ID:
// symbol = new ManagedLocalOrParameterSIMR2MsSymbol(pdb, reader);
// break;
// case UsingNamespaceMsSymbol.PDB_ID:
// symbol = new UsingNamespaceMsSymbol(pdb, reader);
// break;
case ProcedureReferenceMsSymbol.PDB_ID:
applier = new ReferenceSymbolApplier(applicator, iter);
break;
case DataReferenceMsSymbol.PDB_ID:
applier = new ReferenceSymbolApplier(applicator, iter);
break;
case LocalProcedureReferenceMsSymbol.PDB_ID:
applier = new ReferenceSymbolApplier(applicator, iter);
break;
case AnnotationReferenceMsSymbol.PDB_ID:
applier = new ReferenceSymbolApplier(applicator, iter);
break;
case TokenReferenceToManagedProcedureMsSymbol.PDB_ID:
applier = new ReferenceSymbolApplier(applicator, iter);
break;
// case GlobalManagedProcedureMsSymbol.PDB_ID:
// symbol = new GlobalManagedProcedureMsSymbol(pdb, reader);
// break;
// case LocalManagedProcedureMsSymbol.PDB_ID:
// symbol = new LocalManagedProcedureMsSymbol(pdb, reader);
// break;
case TrampolineMsSymbol.PDB_ID:
applier = new TrampolineSymbolApplier(applicator, iter);
break;
// case ManagedConstantMsSymbol.PDB_ID:
// symbol = new ManagedConstantMsSymbol(pdb, reader);
// break;
// case AttribLocOrParamReltoVFPMsSymbol.PDB_ID:
// symbol = new AttribLocOrParamReltoVFPMsSymbol(pdb, reader);
// break;
// case AttributedLocalOrParameterSIRMsSymbol.PDB_ID:
// symbol = new AttributedLocalOrParameterSIRMsSymbol(pdb, reader);
// break;
// case AttribLocOrParamReltoAMPMsSymbol.PDB_ID:
// symbol = new AttribLocOrParamReltoAMPMsSymbol(pdb, reader);
// break;
// case AttributedLocalOrParameterSIMRMsSymbol.PDB_ID:
// symbol = new AttributedLocalOrParameterSIMRMsSymbol(pdb, reader);
// break;
case SeparatedCodeFromCompilerSupportMsSymbol.PDB_ID:
applier = new SeparatedCodeSymbolApplier(applicator, iter);
break;
case LocalSymbolInOptimizedCode2005MsSymbol.PDB_ID:
applier = new LocalOptimizedSymbolApplier(applicator, iter);
break;
// case DefinedSingleAddressRange2005MsSymbol.PDB_ID:
// symbol = new DefinedSingleAddressRange2005MsSymbol(pdb, reader);
// break;
// case DefinedMultipleAddressRanges2005MsSymbol.PDB_ID:
// symbol = new DefinedMultipleAddressRanges2005MsSymbol(pdb, reader);
// break;
case PeCoffSectionMsSymbol.PDB_ID:
applier = new PeCoffSectionSymbolApplier(applicator, iter);
break;
case PeCoffGroupMsSymbol.PDB_ID:
applier = new PeCoffGroupSymbolApplier(applicator, iter);
break;
// case ExportMsSymbol.PDB_ID:
// symbol = new ExportMsSymbol(pdb, reader);
// break;
// case IndirectCallSiteInfoMsSymbol.PDB_ID:
// symbol = new IndirectCallSiteInfoMsSymbol(pdb, reader);
// break;
// case FrameSecurityCookieMsSymbol.PDB_ID:
// symbol = new FrameSecurityCookieMsSymbol(pdb, reader);
// break;
// case DiscardedByLinkMsSymbol.PDB_ID:
// symbol = new DiscardedByLinkMsSymbol(pdb, reader);
// break;
// case Compile3MsSymbol.PDB_ID:
// symbol = new Compile3MsSymbol(pdb, reader);
// break;
// case EnvironmentBlockMsSymbol.PDB_ID:
// symbol = new EnvironmentBlockMsSymbol(pdb, reader);
// break;
case LocalSymbolInOptimizedCodeMsSymbol.PDB_ID:
applier = new LocalOptimizedSymbolApplier(applicator, iter);
break;
case DefinedSingleAddressRangeMsSymbol.PDB_ID:
applier = new DefinedSingleAddressRangeSymbolApplier(applicator, iter);
break;
case SubfieldDARMsSymbol.PDB_ID:
applier = new DefinedSingleAddressRangeSymbolApplier(applicator, iter);
break;
case EnregisteredSymbolDARMsSymbol.PDB_ID:
applier = new DefinedSingleAddressRangeSymbolApplier(applicator, iter);
break;
case FramePointerRelativeDARMsSymbol.PDB_ID:
applier = new DefinedSingleAddressRangeSymbolApplier(applicator, iter);
break;
case EnregisteredFieldOfSymbolDARMsSymbol.PDB_ID:
applier = new DefinedSingleAddressRangeSymbolApplier(applicator, iter);
break;
case FramePointerRelativeFullScopeDARMsSymbol.PDB_ID:
applier = new DefinedSingleAddressRangeSymbolApplier(applicator, iter);
break;
case EnregisteredSymbolRelativeDARMsSymbol.PDB_ID:
applier = new DefinedSingleAddressRangeSymbolApplier(applicator, iter);
break;
case LocalProcedure32IdMsSymbol.PDB_ID:
applier = new FunctionSymbolApplier(applicator, iter);
break;
case GlobalProcedure32IdMsSymbol.PDB_ID:
applier = new FunctionSymbolApplier(applicator, iter);
break;
// case LocalProcedureMipsIdMsSymbol.PDB_ID:
// symbol = new LocalProcedureMipsIdMsSymbol(pdb, reader);
// break;
// case GlobalProcedureMipsIdMsSymbol.PDB_ID:
// symbol = new GlobalProcedureMipsIdMsSymbol(pdb, reader);
// break;
// case LocalProcedureIa64IdMsSymbol.PDB_ID:
// symbol = new LocalProcedureIa64IdMsSymbol(pdb, reader);
// break;
// case GlobalProcedureIa64IdMsSymbol.PDB_ID:
// symbol = new GlobalProcedureIa64IdMsSymbol(pdb, reader);
// break;
// case BuildInformationMsSymbol.PDB_ID:
// symbol = new BuildInformationMsSymbol(pdb, reader);
// break;
// case InlinedFunctionCallsiteMsSymbol.PDB_ID:
// symbol = new InlinedFunctionCallsiteMsSymbol(pdb, reader);
// break;
// case InlinedFunctionEndMsSymbol.PDB_ID:
// symbol = new InlinedFunctionEndMsSymbol(pdb, reader);
// break;
// case ProcedureIdEndMsSymbol.PDB_ID:
// symbol = new ProcedureIdEndMsSymbol(pdb, reader);
// break;
case HighLevelShaderLanguageRegDimDARMsSymbol.PDB_ID:
applier = new DefinedSingleAddressRangeSymbolApplier(applicator, iter);
break;
// case GlobalDataHLSLMsSymbol.PDB_ID:
// symbol = new GlobalDataHLSLMsSymbol(pdb, reader);
// break;
// case LocalDataHLSLMsSymbol.PDB_ID:
// symbol = new LocalDataHLSLMsSymbol(pdb, reader);
// break;
// case FileStaticMsSymbol.PDB_ID:
// symbol = new FileStaticMsSymbol(pdb, reader);
// break;
// case LocalDeferredProcedureCallGroupSharedMsSymbol.PDB_ID:
// symbol = new LocalDeferredProcedureCallGroupSharedMsSymbol(pdb, reader);
// break;
case LocalProcedureStart32DeferredProcedureCallMsSymbol.PDB_ID:
applier = new FunctionSymbolApplier(applicator, iter);
break;
case LocalProcedure32DeferredProcedureCallIdMsSymbol.PDB_ID:
applier = new FunctionSymbolApplier(applicator, iter);
break;
case DeferredProcedureCallPointerTagRegDimDARMsSymbol.PDB_ID:
applier = new DefinedSingleAddressRangeSymbolApplier(applicator, iter);
break;
// case DeferredProcedureCallPointerTagToSymbolRecordMapMsSymbol.PDB_ID:
// symbol = new DeferredProcedureCallPointerTagToSymbolRecordMapMsSymbol(pdb, reader);
// break;
// case ArmSwitchTableMsSymbol.PDB_ID:
// symbol = new ArmSwitchTableMsSymbol(pdb, reader);
// break;
// case CalleesMsSymbol.PDB_ID:
// symbol = new CalleesMsSymbol(pdb, reader);
// break;
// case CallersMsSymbol.PDB_ID:
// symbol = new CallersMsSymbol(pdb, reader);
// break;
// case ProfileGuidedOptimizationDataMsSymbol.PDB_ID:
// symbol = new ProfileGuidedOptimizationDataMsSymbol(pdb, reader);
// break;
// case InlinedFunctionCallsiteExtendedMsSymbol.PDB_ID:
// symbol = new InlinedFunctionCallsiteExtendedMsSymbol(pdb, reader);
// break;
// case HeapAllocationSiteMsSymbol.PDB_ID:
// symbol = new HeapAllocationSiteMsSymbol(pdb, reader);
// break;
// case ModuleTypeReferenceMsSymbol.PDB_ID:
// symbol = new ModuleTypeReferenceMsSymbol(pdb, reader);
// break;
// case MiniPdbReferenceMsSymbol.PDB_ID:
// symbol = new MiniPdbReferenceMsSymbol(pdb, reader);
// break;
// case MapToMiniPdbMsSymbol.PDB_ID:
// symbol = new MapToMiniPdbMsSymbol(pdb, reader);
// break;
// case GlobalDataHLSL32MsSymbol.PDB_ID:
// symbol = new GlobalDataHLSL32MsSymbol(pdb, reader);
// break;
// case LocalDataHLSL32MsSymbol.PDB_ID:
// symbol = new LocalDataHLSL32MsSymbol(pdb, reader);
// break;
// case GlobalDataHLSL32ExtMsSymbol.PDB_ID:
// symbol = new GlobalDataHLSL32ExtMsSymbol(pdb, reader);
// break;
// case LocalDataHLSL32ExtMsSymbol.PDB_ID:
// symbol = new LocalDataHLSL32ExtMsSymbol(pdb, reader);
// break;
// case UnknownX1166MsSymbol.PDB_ID:
// // We have recently seen 1167 and 1168, which implies that 1166 must exist.
// symbol = new UnknownX1166MsSymbol(pdb, reader);
// break;
// case UnknownX1167MsSymbol.PDB_ID:
// // We have not investigated this type yet, but have seen it in VS2017 output.
// symbol = new UnknownX1167MsSymbol(pdb, reader);
// break;
// case UnknownX1168MsSymbol.PDB_ID:
// // We have not investigated this type yet, but have seen it in VS2017 output.
// symbol = new UnknownX1168MsSymbol(pdb, reader);
// break;
default:
// This should never happen (unless we missed something
// or MSFT has added new in a version we do not handle.
applicator.getPdbApplicatorMetrics().witnessCannotApplySymbolType(symbol);
applier = new NoSymbolApplier(applicator, iter);
break;
}
return applier;
}
}

View file

@ -0,0 +1,236 @@
/* ###
* 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.io.IOException;
import java.io.Writer;
import java.util.*;
import ghidra.app.util.bin.format.pdb2.pdbreader.symbol.AbstractMsSymbol;
/**
* This class represents a particular group of Symbols that came from the same PDB stream. This
* wraps the internal structure and offers mechanisms for accessing records. It does not map
* directly to an MSFT structure.
*/
public class SymbolGroup {
private Map<Long, AbstractMsSymbol> symbolsByOffset;
private int moduleNumber;
private List<Long> offsets;
private Map<Long, Integer> indexByOffset;
/**
* Constructor. The starting offset is set to zero.
* @param symbolsByOffset the Map used to initialize the constructor.
* @param moduleNumber The Module number corresponding to the initializing Map
* (0 for public/global Map).
*/
public SymbolGroup(Map<Long, AbstractMsSymbol> symbolsByOffset, int moduleNumber) {
this(symbolsByOffset, moduleNumber, 0);
}
/**
* Constructor.
* @param symbolsByOffset the Map used to initialize the constructor.
* @param moduleNumber The Module number corresponding to the initializing Map
* (0 for public/global Map).
* @param offset the offset location to start.
*/
public SymbolGroup(Map<Long, AbstractMsSymbol> symbolsByOffset, int moduleNumber, long offset) {
this.symbolsByOffset = symbolsByOffset;
this.moduleNumber = moduleNumber;
initOffsets();
}
/**
* Returns the list of symbols. These may not be in the order that they were seen.
* @return the list of symbols.
*/
public List<AbstractMsSymbol> getSymbols() {
return new ArrayList<>(symbolsByOffset.values());
}
/**
* Returns the module number.
* @return the module number.
*/
public int getModuleNumber() {
return moduleNumber;
}
/**
* Returns the number of symbols.
* @return the number of symbols.
*/
public int size() {
return symbolsByOffset.size();
}
/**
* Returns the list of symbol offsets in the order they were seen.
* @return the list of symbol offsets.
*/
public List<Long> getOrderedOffsets() {
return new ArrayList<>(symbolsByOffset.keySet());
}
/**
* Returns the set of symbol offsets.
* @return the set of symbol offsets.
*/
public Set<Long> getOffsets() {
return symbolsByOffset.keySet();
}
/**
* Returns the list of symbols in the order they were seen.
* @return the list of symbols.
*/
public List<AbstractMsSymbol> getOrderedSymbols() {
List<AbstractMsSymbol> symbols = new ArrayList<>();
for (long offset : offsets) {
symbols.add(symbolsByOffset.get(offset));
}
return symbols;
}
/**
* Initialized the offsets list used for requesting the symbols in sequence.
*/
private void initOffsets() {
offsets = new ArrayList<>();
indexByOffset = new HashMap<>();
int index = 0;
for (Map.Entry<Long, AbstractMsSymbol> entry : symbolsByOffset.entrySet()) {
offsets.add(index, entry.getKey());
indexByOffset.put(entry.getKey(), index++);
}
}
/**
* Debug method for dumping the symbol group
* @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("SymbolGroup-------------------------------------------------");
for (Map.Entry<Long, AbstractMsSymbol> entry : symbolsByOffset.entrySet()) {
StringBuilder builder = new StringBuilder();
builder.append("\n------------------------------------------------------------\n");
builder.append(String.format("Offset: 0X%08X\n", entry.getKey()));
builder.append(entry.getValue());
writer.write(builder.toString());
}
writer.write("\nEnd SymbolGroup---------------------------------------------\n");
}
AbstractMsSymbolIterator iterator() {
return new AbstractMsSymbolIterator();
}
//==============================================================================================
class AbstractMsSymbolIterator implements Iterator<AbstractMsSymbol> {
private int currentIndex;
private long currentOffset;
public AbstractMsSymbolIterator() {
currentIndex = 0;
currentOffset = 0L;
}
@Override
public boolean hasNext() {
if (currentIndex == offsets.size()) {
return false;
}
return true;
}
/**
* Peeks at and returns the next symbol without incrementing to the next. If none are left, then throws NoSuchElementException and
* reinitializes the state for a new iteration.
* @see #initGet()
* @return the next symbol
* @throws NoSuchElementException if there are no more elements
*/
public AbstractMsSymbol peek() throws NoSuchElementException {
if (currentIndex == offsets.size()) {
throw new NoSuchElementException("none left");
}
long temporaryOffset = offsets.get(currentIndex);
AbstractMsSymbol symbol = symbolsByOffset.get(temporaryOffset);
if (symbol == null) {
throw new NoSuchElementException("No symbol");
}
return symbol;
}
@Override
public AbstractMsSymbol next() {
if (currentIndex == offsets.size()) {
throw new NoSuchElementException("none left");
}
currentOffset = offsets.get(currentIndex++);
return symbolsByOffset.get(currentOffset);
}
/**
* Returns the next symbol. If none are left, then throws NoSuchElementException and
* reinitializes the state for a new iteration.
* @see #initGet()
* @return the next symbol
* @throws NoSuchElementException if there are no more elements
*/
public long getCurrentOffset() {
return currentOffset;
}
/**
* Initialized the mechanism for requesting the symbols in sequence.
* @see #hasNext()
*/
public void initGet() {
currentIndex = 0;
}
/**
* Initialized the mechanism for requesting the symbols in sequence.
* @param offset the offset to which to initialize the mechanism.
* @see #hasNext()
*/
public void initGetByOffset(long offset) {
int index = indexByOffset.get(offset);
if (index < 0) {
index = 0;
}
currentIndex = index;
currentOffset = offset;
}
// TODO: might not need this
/**
* Returns the module number.
* @return the module number.
*/
public int getModuleNumber() {
return moduleNumber;
}
}
}

View file

@ -0,0 +1,98 @@
/* ###
* 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.app.cmd.disassemble.DisassembleCommand;
import ghidra.app.cmd.function.CreateFunctionCmd;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.symbol.AbstractMsSymbol;
import ghidra.app.util.bin.format.pdb2.pdbreader.symbol.TrampolineMsSymbol;
import ghidra.app.util.pdb.pdbapplicator.SymbolGroup.AbstractMsSymbolIterator;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSet;
import ghidra.program.model.listing.Function;
import ghidra.util.exception.AssertException;
import ghidra.util.exception.CancelledException;
/**
* Applier for {@link TrampolineMsSymbol} symbols.
*/
public class TrampolineSymbolApplier extends AbstractMsSymbolApplier {
private TrampolineMsSymbol symbol;
public TrampolineSymbolApplier(PdbApplicator applicator, AbstractMsSymbolIterator iter) {
super(applicator, iter);
AbstractMsSymbol abstractSymbol = iter.next();
if (!(abstractSymbol instanceof TrampolineMsSymbol)) {
throw new AssertException(
"Invalid symbol type: " + abstractSymbol.getClass().getSimpleName());
}
symbol = (TrampolineMsSymbol) abstractSymbol;
}
@Override
public void applyTo(AbstractMsSymbolApplier applyToApplier) {
// Do nothing.
}
@Override
public void apply() throws CancelledException, PdbException {
// We know the size of this trampoline, so use it to restrict the disassembly.
Address symbolAddress = applicator.reladdr(symbol);
Address targetAddress =
applicator.reladdr(symbol.getSegmentTarget(), symbol.getOffsetTarget());
Function target = createNewFunction(targetAddress, 1);
Function thunk = createNewFunction(symbolAddress, symbol.getSizeOfThunk());
if (target != null && thunk != null) {
thunk.setThunkedFunction(target);
}
// int thunkModule = findModuleNumberBySectionOffsetContribution(symbol.getSectionThunk(),
// symbol.getOffsetThunk());
// int targetModule = findModuleNumberBySectionOffsetContribution(symbol.getSectionTarget(),
// symbol.getOffsetTarget());
}
// TODO? If we wanted to be able to apply this symbol to a different address, we should
// review code in FunctionSymbolApplier. Note, however, that there are two addresses
// that need to be dealt with here, and each could have a different address with a different
// delta from the specified address.
private Function createNewFunction(Address startAddress, long size) {
AddressSet addressSet = new AddressSet(startAddress, startAddress.add(size));
if (applicator.getProgram().getListing().getInstructionAt(startAddress) == null) {
DisassembleCommand cmd = new DisassembleCommand(addressSet, null, true); // TODO: false?
cmd.applyTo(applicator.getProgram(), applicator.getCancelOnlyWrappingMonitor());
}
// Only create function if it does not already exist.
Function function = applicator.getProgram().getListing().getFunctionAt(startAddress);
if (function != null) {
return function;
}
CreateFunctionCmd funCmd = new CreateFunctionCmd(startAddress);
if (!funCmd.applyTo(applicator.getProgram(), applicator.getCancelOnlyWrappingMonitor())) {
applicator.appendLogMsg("Failed to apply function at address " +
startAddress.toString() + "; attempting to use possible existing function");
}
return funCmd.getFunction();
}
}

View file

@ -0,0 +1,519 @@
/* ###
* 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.HashMap;
import java.util.Map;
import ghidra.app.util.bin.format.pdb2.pdbreader.*;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.*;
public class TypeApplierParser {
private PdbApplicator applicator;
private Map<RecordNumber, AbstractMsTypeApplier> appliersByRecordNumber;
TypeApplierParser(PdbApplicator applicator) {
this.applicator = applicator;
appliersByRecordNumber = new HashMap<>();
}
//==============================================================================================
AbstractMsTypeApplier getApplierSpec(RecordNumber recordNumber,
Class<? extends AbstractMsTypeApplier> expected) throws PdbException {
AbstractMsTypeApplier applier = getTypeApplier(recordNumber);
if (!expected.isInstance(applier)) {
throw new PdbException(applier.getClass().getSimpleName() + " seen where " +
expected.getSimpleName() + " expected for record number " + recordNumber);
}
return applier;
}
AbstractMsTypeApplier getApplierOrNoTypeSpec(RecordNumber recordNumber,
Class<? extends AbstractMsTypeApplier> expected) throws PdbException {
AbstractMsTypeApplier applier = getTypeApplier(recordNumber);
if (!expected.isInstance(applier)) {
if (!(applier instanceof PrimitiveTypeApplier &&
((PrimitiveTypeApplier) applier).isNoType())) {
throw new PdbException(applier.getClass().getSimpleName() + " seen where " +
expected.getSimpleName() + " expected for record number " + recordNumber);
}
}
return applier;
}
AbstractMsTypeApplier getTypeApplier(RecordNumber recordNumber) {
AbstractMsTypeApplier applier = appliersByRecordNumber.get(recordNumber);
if (applier == null) {
applier = getTypeApplier(applicator.getPdb().getTypeRecord(recordNumber));
appliersByRecordNumber.put(recordNumber, applier);
}
return applier;
}
//==============================================================================================
AbstractMsTypeApplier getTypeApplier(AbstractMsType type) {
if (type == null) {
applicator.appendLogMsg("PDB Warning: No AbstractMsType for getTypeApplier");
return null;
}
AbstractMsTypeApplier applier = null;
try {
switch (type.getPdbId()) {
case -1:
applier = new PrimitiveTypeApplier(applicator, type);
break;
// 0x0000 block
case Modifier16MsType.PDB_ID:
applier = new ModifierTypeApplier(applicator, type);
break;
case Pointer16MsType.PDB_ID:
applier = new PointerTypeApplier(applicator, type);
break;
case Array16MsType.PDB_ID:
applier = new ArrayTypeApplier(applicator, type);
break;
case Class16MsType.PDB_ID:
applier = new CompositeTypeApplier(applicator, type);
break;
case Structure16MsType.PDB_ID:
applier = new CompositeTypeApplier(applicator, type);
break;
case Union16MsType.PDB_ID:
applier = new CompositeTypeApplier(applicator, type);
break;
case Enum16MsType.PDB_ID:
applier = new EnumTypeApplier(applicator, type);
break;
case Procedure16MsType.PDB_ID:
applier = new ProcedureTypeApplier(applicator, type);
break;
case MemberFunction16MsType.PDB_ID:
applier = new MemberFunctionTypeApplier(applicator, type);
break;
case VtShapeMsType.PDB_ID:
applier = new VtShapeTypeApplier(applicator, type);
break;
// case Cobol016MsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case Cobol1MsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case BasicArray16MsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case LabelMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case NullMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case NotTranMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case DimensionedArray16MsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case VirtualFunctionTablePath16MsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case PrecompiledType16MsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case EndPrecompiledTypeMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case OemDefinableString16MsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case TypeServerStMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// 0x0200 block
// case Skip16MsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
case ArgumentsList16MsType.PDB_ID:
applier = new ArgumentsListTypeApplier(applicator, type);
break;
// case DefaultArguments16MsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case ListMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
case FieldList16MsType.PDB_ID:
applier = new FieldListTypeApplier(applicator, type);
break;
// case DerivedClassList16MsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
case Bitfield16MsType.PDB_ID:
applier = new BitfieldTypeApplier(applicator, type);
break;
// case MethodList16MsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case DimensionedArrayConstBoundsUpper16MsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case DimensionedArrayConstBoundsLowerUpper16MsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case DimensionedArrayVarBoundsUpper16MsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case DimensionedArrayVarBoundsLowerUpper16MsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
case ReferencedSymbolMsType.PDB_ID:
// Not evaluated/implemented yet.
break;
// 0x400 block
case BaseClass16MsType.PDB_ID:
applier = new BaseClassTypeApplier(applicator, type);
break;
case VirtualBaseClass16MsType.PDB_ID:
applier = new BaseClassTypeApplier(applicator, type);
break;
case IndirectVirtualBaseClass16MsType.PDB_ID:
applier = new BaseClassTypeApplier(applicator, type);
break;
case EnumerateStMsType.PDB_ID:
applier = new EnumerateTypeApplier(applicator, type);
break;
// case FriendFunction16MsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case Index16MsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
case Member16MsType.PDB_ID:
applier = new MemberTypeApplier(applicator, type);
break;
// case StaticMember16MsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
case OverloadedMethod16MsType.PDB_ID:
// See note in "default" case regarding NoTypeApplier
applier = new NoTypeApplier(applicator, type);
break;
case NestedType16MsType.PDB_ID:
applier = new NestedTypeApplier(applicator, type);
break;
case VirtualFunctionTablePointer16MsType.PDB_ID:
applier = new VirtualFunctionTablePointerTypeApplier(applicator, type);
break;
// case FriendClass16MsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
case OneMethod16MsType.PDB_ID:
// See note in "default" case regarding NoTypeApplier
applier = new NoTypeApplier(applicator, type);
break;
case VirtualFunctionTablePointerWithOffset16MsType.PDB_ID:
applier = new VirtualFunctionTablePointerTypeApplier(applicator, type);
break;
// 0x1000 block
case ModifierMsType.PDB_ID:
applier = new ModifierTypeApplier(applicator, type);
break;
case PointerMsType.PDB_ID:
applier = new PointerTypeApplier(applicator, type);
break;
case ArrayStMsType.PDB_ID:
applier = new ArrayTypeApplier(applicator, type);
break;
case ClassStMsType.PDB_ID:
applier = new CompositeTypeApplier(applicator, type);
break;
case StructureStMsType.PDB_ID:
applier = new CompositeTypeApplier(applicator, type);
break;
case UnionStMsType.PDB_ID:
applier = new CompositeTypeApplier(applicator, type);
break;
case EnumStMsType.PDB_ID:
applier = new EnumTypeApplier(applicator, type);
break;
case ProcedureMsType.PDB_ID:
applier = new ProcedureTypeApplier(applicator, type);
break;
case MemberFunctionMsType.PDB_ID:
applier = new MemberFunctionTypeApplier(applicator, type);
break;
// case Cobol0MsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case BasicArrayMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case DimensionedArrayStMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case VirtualFunctionTablePathMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case PrecompiledTypeStMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case OemDefinableStringMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case AliasStMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case OemDefinableString2MsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
//
// 0x1200 block
// case SkipMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
case ArgumentsListMsType.PDB_ID:
applier = new ArgumentsListTypeApplier(applicator, type);
break;
// case DefaultArgumentsStMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
case FieldListMsType.PDB_ID:
applier = new FieldListTypeApplier(applicator, type);
break;
// case DerivedClassListMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
case BitfieldMsType.PDB_ID:
applier = new BitfieldTypeApplier(applicator, type);
break;
// case MethodListMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case DimensionedArrayConstBoundsUpperMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case DimensionedArrayConstBoundsLowerUpperMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case DimensionedArrayVarBoundsUpperMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case DimensionedArrayVarBoundsLowerUpperMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// 0x1400 block
case BaseClassMsType.PDB_ID:
applier = new BaseClassTypeApplier(applicator, type);
break;
case VirtualBaseClassMsType.PDB_ID:
applier = new BaseClassTypeApplier(applicator, type);
break;
case IndirectVirtualBaseClassMsType.PDB_ID:
applier = new BaseClassTypeApplier(applicator, type);
break;
// case FriendFunctionStMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case IndexMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
case MemberStMsType.PDB_ID:
applier = new MemberTypeApplier(applicator, type);
break;
// case StaticMemberStMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
case OverloadedMethodStMsType.PDB_ID:
// See note in "default" case regarding NoTypeApplier
applier = new NoTypeApplier(applicator, type);
break;
case NestedTypeStMsType.PDB_ID:
applier = new NestedTypeApplier(applicator, type);
break;
case VirtualFunctionTablePointerMsType.PDB_ID:
applier = new VirtualFunctionTablePointerTypeApplier(applicator, type);
break;
// case FriendClassMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
case OneMethodStMsType.PDB_ID:
// See note in "default" case regarding NoTypeApplier
applier = new NoTypeApplier(applicator, type);
break;
case VirtualFunctionTablePointerWithOffsetMsType.PDB_ID:
applier = new VirtualFunctionTablePointerTypeApplier(applicator, type);
break;
case NestedTypeExtStMsType.PDB_ID:
applier = new NestedTypeApplier(applicator, type);
break;
// case MemberModifyStMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case ManagedStMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// 0x1500 block
// case TypeServerMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
case EnumerateMsType.PDB_ID:
applier = new EnumerateTypeApplier(applicator, type);
break;
case ArrayMsType.PDB_ID:
applier = new ArrayTypeApplier(applicator, type);
break;
case ClassMsType.PDB_ID:
applier = new CompositeTypeApplier(applicator, type);
break;
case StructureMsType.PDB_ID:
applier = new CompositeTypeApplier(applicator, type);
break;
case UnionMsType.PDB_ID:
applier = new CompositeTypeApplier(applicator, type);
break;
case EnumMsType.PDB_ID:
applier = new EnumTypeApplier(applicator, type);
break;
// case DimensionedArrayMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case PrecompiledTypeMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case AliasMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case DefaultArgumentsMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case FriendFunctionMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
case MemberMsType.PDB_ID:
applier = new MemberTypeApplier(applicator, type);
break;
// case StaticMemberMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
case OverloadedMethodMsType.PDB_ID:
// See note in "default" case regarding NoTypeApplier
applier = new NoTypeApplier(applicator, type);
break;
case NestedTypeMsType.PDB_ID:
applier = new NestedTypeApplier(applicator, type);
break;
case OneMethodMsType.PDB_ID:
// See note in "default" case regarding NoTypeApplier
applier = new NoTypeApplier(applicator, type);
break;
case NestedTypeExtMsType.PDB_ID:
applier = new NestedTypeApplier(applicator, type);
break;
// case MemberModifyMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case ManagedMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case TypeServer2MsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case StridedArrayMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case HighLevelShaderLanguageMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case ModifierExMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
case InterfaceMsType.PDB_ID:
applier = new CompositeTypeApplier(applicator, type);
break;
// case BaseInterfaceMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case VectorMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case MatrixMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case VirtualFunctionTableMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// 0x1600 block
// case FunctionIdMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case MemberFunctionIdMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case BuildInfoMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case SubstringListMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case StringIdMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
case UserDefinedTypeSourceAndLineMsType.PDB_ID:
applier = new UdtSourceLineTypeApplier(applicator, type);
break;
case UserDefinedTypeModuleSourceAndLineMsType.PDB_ID:
applier = new UdtSourceLineTypeApplier(applicator, type);
break;
// If all of the above are enabled, this should never happen (unless we missed
// something or MSFT has added new in a version we do not handle.
default:
applier = new NoTypeApplier(applicator, type);
// Only adding to this cannotApplyTypes list here, and not in other
// places (above) where we might currently be using a NoTypeApplier.
// Using a NoTypeApplier in other places (above) might just be a placeholder
// until we craft the specific ways in which we would like to "apply" the
// data type information.
applicator.getPdbApplicatorMetrics().witnessCannotApplyDataType(type);
break;
}
}
catch (IllegalArgumentException e) {
try {
applier = new NoTypeApplier(applicator, type);
}
catch (IllegalArgumentException e2) {
// We did a null check above on type, so this state should not happen.
}
RecordNumber recNum = type.getRecordNumber();
String msg = (recNum == null) ? "record" : recNum.toString();
String message = "GhidraException on " + msg + " with PdbId " + type.getPdbId() + ": " +
e.getMessage();
applicator.appendLogMsg(message);
PdbLog.message(message);
}
return applier;
}
}

View file

@ -0,0 +1,125 @@
/* ###
* 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.app.util.SymbolPath;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.RecordNumber;
import ghidra.app.util.bin.format.pdb2.pdbreader.symbol.AbstractMsSymbol;
import ghidra.app.util.bin.format.pdb2.pdbreader.symbol.AbstractUserDefinedTypeMsSymbol;
import ghidra.app.util.pdb.pdbapplicator.SymbolGroup.AbstractMsSymbolIterator;
import ghidra.program.model.data.*;
import ghidra.util.exception.AssertException;
import ghidra.util.exception.CancelledException;
/**
* Applier for {@link AbstractUserDefinedTypeMsSymbol} symbols.
*/
public class TypedefSymbolApplier extends AbstractMsSymbolApplier {
DataType resolvedDataType = null;
AbstractUserDefinedTypeMsSymbol udtSymbol;
public TypedefSymbolApplier(PdbApplicator applicator, AbstractMsSymbolIterator iter) {
super(applicator, iter);
AbstractMsSymbol abstractSymbol = iter.next();
if (!(abstractSymbol instanceof AbstractUserDefinedTypeMsSymbol)) {
throw new AssertException(
"Invalid symbol type: " + abstractSymbol.getClass().getSimpleName());
}
udtSymbol = (AbstractUserDefinedTypeMsSymbol) abstractSymbol;
}
@Override
public void applyTo(AbstractMsSymbolApplier applyToApplier) {
// Do nothing
}
@Override
public void apply() throws PdbException, CancelledException {
resolvedDataType = applyUserDefinedTypeMsSymbol(udtSymbol);
}
/**
* Returns the name.
* @return Name.
*/
public String getName() {
return udtSymbol.getName();
}
/**
* Returns the type record number.
* @return Type record number.
*/
public RecordNumber getTypeRecordNumber() {
return udtSymbol.getTypeRecordNumber();
}
public DataType getResolvedDataType() throws PdbException {
if (resolvedDataType == null) {
throw new PdbException("Data type not resolved");
}
return resolvedDataType;
}
// Typedefs
private DataType applyUserDefinedTypeMsSymbol(AbstractUserDefinedTypeMsSymbol symbol) {
String name = symbol.getName();
AbstractMsTypeApplier applier = applicator.getTypeApplier(symbol.getTypeRecordNumber());
// TODO:... NOT SURE IF WILL ALWAYS BE A DATATYPE OR WILL BE A VARIABLE OR ????
if (applier == null) {
return null;
}
DataType dataType = applier.getDataType();
if (dataType == null) {
return null;
}
// This code (for Composites and Enums) circumvents a collision on the names with the
// compromise that we do not store the TypeDefDataType into the DataTypeManager.
// Another issue is that we likely already have the DataType in the DataTypeManager,
// but the TypeDefDataType also wants to create it... we would need a mechanism to
// create a TypeDefDataType which uses an existing underlying DataType.
// Note, too, that we do not compare name with dataType.getName() as the latter does not
// contain namespace information.
if (applier instanceof CompositeTypeApplier) {
CompositeTypeApplier compositeApplier = (CompositeTypeApplier) applier;
String compositeName = compositeApplier.getName();
if (name.equals(compositeName)) {
return dataType;
}
}
else if (applier instanceof EnumTypeApplier) {
EnumTypeApplier enumApplier = (EnumTypeApplier) applier;
String enumName = enumApplier.getMsType().getName();
if (name.equals(enumName)) {
return dataType;
}
}
SymbolPath symbolPath = new SymbolPath(name);
CategoryPath categoryPath =
applicator.getTypedefsCategory(iter.getModuleNumber(), symbolPath);
DataType typedef = new TypedefDataType(categoryPath.getParent(), categoryPath.getName(),
dataType, applicator.getDataTypeManager());
return applicator.resolve(typedef);
}
}

View file

@ -0,0 +1,112 @@
/* ###
* 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.math.BigInteger;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.RecordNumber;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.*;
import ghidra.util.exception.CancelledException;
/**
* Applier for {@link AbstractBaseClassMsType}, {@link AbstractVirtualBaseClassMsType}, and
* {@link AbstractIndirectVirtualBaseClassMsType} types.
*/
public class UdtSourceLineTypeApplier extends AbstractMsTypeApplier {
private static AbstractMsType validateType(AbstractMsType type)
throws IllegalArgumentException {
if (!(type instanceof UserDefinedTypeSourceAndLineMsType) &&
!(type instanceof UserDefinedTypeModuleSourceAndLineMsType)) {
throw new IllegalArgumentException(
"PDB Incorrectly applying " + type.getClass().getSimpleName() + " to " +
UdtSourceLineTypeApplier.class.getSimpleName());
}
return type;
}
/**
* Constructor for base class applier.
* @param applicator {@link PdbApplicator} for which this class is working.
* @param msType {@link AbstractBaseClassMsType}, {@link AbstractVirtualBaseClassMsType}, or
* {@link AbstractIndirectVirtualBaseClassMsType} to processes.
* @throws IllegalArgumentException Upon invalid arguments.
*/
public UdtSourceLineTypeApplier(PdbApplicator applicator, AbstractMsType msType)
throws IllegalArgumentException {
super(applicator, validateType(msType));
}
// The MsTypes for which we are working do not have a size in and of themselves, but the
// classes/structures to which they refer have a size, even if zero.
// For here, we are only reporting what "we" have, not what the underlying sizes are.
// ...and a value of zero is our "don't know" and "not represented" value.
@Override
public BigInteger getSize() {
return BigInteger.ZERO;
}
/**
* Returns the offset of the Base Class within the inheriting class.
* @return the offset.
*/
public int getLineNumber() {
if (msType instanceof UserDefinedTypeSourceAndLineMsType) {
return ((UserDefinedTypeSourceAndLineMsType) msType).getLineNumber();
}
return ((UserDefinedTypeModuleSourceAndLineMsType) msType).getLineNumber();
}
/**
* Returns the source file name.
* @return the source file name. null if problem recovering name.
*/
public String getSourceFileName() {
if (msType instanceof UserDefinedTypeSourceAndLineMsType) {
return ((UserDefinedTypeSourceAndLineMsType) msType).getSourceFileName();
}
return ((UserDefinedTypeModuleSourceAndLineMsType) msType).getSourceFileName();
}
/**
* Returns the record number of the UDT.
* @return the record number of the UDT.
*/
public RecordNumber getUdtRecordNumber() {
if (msType instanceof UserDefinedTypeSourceAndLineMsType) {
return ((UserDefinedTypeSourceAndLineMsType) msType).getUdtRecordNumber();
}
return ((UserDefinedTypeModuleSourceAndLineMsType) msType).getUdtRecordNumber();
}
@Override
public void apply() throws PdbException, CancelledException {
String filename = getSourceFileName();
int lineNumber = getLineNumber();
RecordNumber udtRecordNumber = getUdtRecordNumber();
AbstractMsTypeApplier typeApplier = applicator.getTypeApplier(udtRecordNumber);
// do nothing at the moment.
applicator.putRecordNumberByFileName(udtRecordNumber, filename);
if (msType instanceof UserDefinedTypeModuleSourceAndLineMsType) {
int moduleNumber =
((UserDefinedTypeModuleSourceAndLineMsType) msType).getModuleNumber();
applicator.putRecordNumberByModuleNumber(udtRecordNumber, moduleNumber);
}
}
}

View file

@ -0,0 +1,129 @@
/* ###
* 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.HashMap;
import java.util.Map;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbLog;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.*;
import ghidra.util.Msg;
/**
* Manages virtual base table lookup for PDB classes.
*/
public class VbtManager {
DataTypeManager dtm;
PointerDataType fallbackVbptr;
Map<Address, VirtualBaseTable> vbtByAddress;
/**
* Virtual Base Table Lookup Manager
* @param dtm TODO
*/
VbtManager(DataTypeManager dtm) {
this.dtm = dtm;
vbtByAddress = new HashMap<>();
fallbackVbptr = new PointerDataType(new IntegerDataType(dtm));
}
PointerDataType getFallbackVbptr() {
return fallbackVbptr;
}
/**
* Returns the offset from the virtual base table entry
* @param address Address of virtual base table
* @param ordinal index into table
* @return the offset
* @throws PdbException if no table exists for address or no entry exists for ordinal
*/
long getOffset(Address address, int ordinal) throws PdbException {
VirtualBaseTable table = vbtByAddress.get(address);
if (table == null) {
table = createVirtualBaseTable(address);
}
VirtualBaseTableEntry entry = table.getEntry(ordinal);
if (entry == null) {
throw new PdbException(
"Virtual Base Table Entry does not exist for ordinal: " + ordinal);
}
return entry.getOffset();
}
VirtualBaseTable createVirtualBaseTable(Address address) {
VirtualBaseTable vbt = vbtByAddress.get(address);
if (vbt != null) {
String message =
"PDB: warning virtual base table already exists for address: " + address;
PdbLog.message(message);
Msg.info(this, message);
}
else {
vbt = new VirtualBaseTable();
vbtByAddress.put(address, vbt);
}
return vbt;
}
static class VirtualBaseTableEntry {
long offset;
VirtualBaseTableEntry(long offset) {
this.offset = offset;
}
long getOffset() {
return offset;
}
}
static class VirtualBaseTable {
int maxSeen = -1;
Map<Integer, VirtualBaseTableEntry> entryByOrdinal = new HashMap<>();
/**
* Returns the entry from the table for the ordinal
* @param ordinal the ordinal into the table for the entry to retrieve
* @return the table entry
* @throws PdbException upon issue retrieving the entry
*/
VirtualBaseTableEntry getEntry(int ordinal) throws PdbException {
return entryByOrdinal.get(ordinal);
}
long getOffset(int ordinal) throws PdbException {
VirtualBaseTableEntry entry = getEntry(ordinal);
if (entry == null) {
throw new PdbException("No entry in Virtual Base Table for ordinal: " + ordinal);
}
return entry.getOffset();
}
void addEntry(int ordinal, VirtualBaseTableEntry entry) {
entryByOrdinal.put(ordinal, entry);
maxSeen = Integer.max(maxSeen, ordinal);
}
int getMaxOrdinal() {
return maxSeen;
}
}
}

View file

@ -0,0 +1,104 @@
/* ###
* 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.math.BigInteger;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.RecordNumber;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.*;
import ghidra.program.model.data.DataType;
import ghidra.util.exception.CancelledException;
/**
* Applier for {@link AbstractVirtualFunctionTablePointerMsType} and
* {@link AbstractVirtualFunctionTablePointerWithOffsetMsType} types.
*/
public class VirtualFunctionTablePointerTypeApplier extends AbstractMsTypeApplier {
DataType vtShape = null;
private static AbstractMsType validateType(AbstractMsType type)
throws IllegalArgumentException {
if (!(type instanceof AbstractVirtualFunctionTablePointerMsType) &&
!(type instanceof AbstractVirtualFunctionTablePointerWithOffsetMsType)) {
throw new IllegalArgumentException(
"PDB Incorrectly applying " + type.getClass().getSimpleName() + " to " +
VirtualFunctionTablePointerTypeApplier.class.getSimpleName());
}
return type;
}
/**
* Constructor for enum type applier, for transforming a enum into a
* Ghidra DataType.
* @param applicator {@link PdbApplicator} for which this class is working.
* @param msType {@link AbstractVirtualFunctionTablePointerMsType} or
* {@link AbstractVirtualFunctionTablePointerWithOffsetMsType} to process.
* @throws IllegalArgumentException Upon invalid arguments.
*/
public VirtualFunctionTablePointerTypeApplier(PdbApplicator applicator, AbstractMsType msType) throws IllegalArgumentException {
super(applicator, validateType(msType));
}
@Override
public BigInteger getSize() {
return BigInteger.valueOf(applicator.getDataOrganization().getPointerSize());
}
/**
* Returns the offset of the Virtual Function Table Pointer.
* @return Name of the nested type.
*/
public int getOffset() {
if (msType instanceof AbstractVirtualFunctionTablePointerWithOffsetMsType) {
return ((AbstractVirtualFunctionTablePointerWithOffsetMsType) msType).getOffset();
}
return 0;
}
/**
* Returns the name to use.
* @return Name of the pointer type.
*/
public String getMemberName() {
return "VFTablePtr" + getOffset();
}
@Override
public void apply() throws PdbException, CancelledException {
if (msType instanceof AbstractVirtualFunctionTablePointerMsType) {
dataType = applyPointer(
((AbstractVirtualFunctionTablePointerMsType) msType).getPointerTypeRecordNumber());
}
else {
dataType = applyPointer(
((AbstractVirtualFunctionTablePointerWithOffsetMsType) msType).getPointerTypeRecordNumber());
}
}
// private DataType applyPointer(int pointerTypeIndex) throws PdbException {
private DataType applyPointer(RecordNumber pointerTypeRecordNumber) {
AbstractMsTypeApplier rawApplier = applicator.getTypeApplier(pointerTypeRecordNumber);
if (rawApplier instanceof PointerTypeApplier) {
return rawApplier.getDataType();
}
applicator.appendLogMsg("cannot process " + rawApplier.getClass().getSimpleName() + "for " +
getClass().getSimpleName());
return null;
}
}

View file

@ -0,0 +1,110 @@
/* ###
* 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.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import ghidra.app.util.bin.format.pdb.DefaultCompositeMember;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.*;
import ghidra.program.model.data.*;
import ghidra.util.Msg;
import ghidra.util.exception.CancelledException;
/**
* Applier for {@link VtShapeMsType} types.
*/
public class VtShapeTypeApplier extends AbstractMsTypeApplier {
private static AbstractMsType validateType(AbstractMsType type)
throws IllegalArgumentException {
if (!(type instanceof VtShapeMsType)) {
throw new IllegalArgumentException(
"PDB Incorrectly applying " + type.getClass().getSimpleName() + " to " +
VtShapeTypeApplier.class.getSimpleName());
}
return type;
}
/**
* Constructor for vtshape type applier.
* @param applicator {@link PdbApplicator} for which this class is working.
* @param msType {@link VtShapeMsType} to process.
* @throws IllegalArgumentException Upon invalid arguments.
*/
public VtShapeTypeApplier(PdbApplicator applicator, AbstractMsType msType)
throws IllegalArgumentException {
super(applicator, validateType(msType));
}
@Override
public BigInteger getSize() {
return BigInteger.valueOf(applicator.getDataOrganization().getPointerSize() *
((VtShapeMsType) msType).getCount());
}
/**
* Returns the name.
* @return the name.
*/
public String getName() {
return "vtshape_" + index;
}
@Override
public void apply() throws PdbException, CancelledException {
dataType = createVtShape((VtShapeMsType) msType);
}
// TODO: We are creating a structure for the vtshape. Is there anything different we would
// like to do instead?
private DataType createVtShape(VtShapeMsType msShape) throws CancelledException {
List<VtShapeDescriptorMsProperty> list = msShape.getDescriptorList();
// TODO: what are correct/appropriate CategoryPath and name
StructureDataType shape = new StructureDataType(applicator.getAnonymousTypesCategory(),
"vtshape" + index, 0, applicator.getDataTypeManager());
List<Default2PdbMember> members = new ArrayList<>();
int offset = 0;
for (VtShapeDescriptorMsProperty descriptor : list) {
switch (descriptor) {
case NEAR:
case FAR:
case THIN:
case OUTER:
case META:
case NEAR32:
case FAR32:
Pointer pointer = new PointerDataType(applicator.getDataTypeManager());
Default2PdbMember member =
new Default2PdbMember(applicator, "", pointer, offset);
offset += pointer.getLength();
members.add(member);
break;
case UNUSED:
offset += applicator.getDataOrganization().getPointerSize();
break;
}
}
int size = applicator.getDataOrganization().getPointerSize() * msShape.getCount();
if (!DefaultCompositeMember.applyDataTypeMembers(shape, false, size, members,
msg -> Msg.warn(this, msg), applicator.getCancelOnlyWrappingMonitor())) {
CompositeTypeApplier.clearComponents(shape);
}
return shape; // not resolved
}
}

View file

@ -0,0 +1,69 @@
/* ###
* 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.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbLog;
import ghidra.app.util.bin.format.pdb2.pdbreader.symbol.AbstractMsSymbol;
import ghidra.app.util.bin.format.pdb2.pdbreader.symbol.AbstractWithMsSymbol;
import ghidra.app.util.pdb.pdbapplicator.SymbolGroup.AbstractMsSymbolIterator;
import ghidra.program.model.address.Address;
import ghidra.util.Msg;
import ghidra.util.exception.AssertException;
import ghidra.util.exception.CancelledException;
/**
* Applier for {@link AbstractWithMsSymbol} symbols. This is not fully implemented
* because we do not know its usage or have examples, but we have implemented
* the block management portion.
*/
public class WithSymbolApplier extends AbstractMsSymbolApplier {
private AbstractWithMsSymbol symbol;
public WithSymbolApplier(PdbApplicator applicator, AbstractMsSymbolIterator iter) {
super(applicator, iter);
AbstractMsSymbol abstractSymbol = iter.next();
if (!(abstractSymbol instanceof AbstractWithMsSymbol)) {
throw new AssertException(
"Invalid symbol type: " + abstractSymbol.getClass().getSimpleName());
}
symbol = (AbstractWithMsSymbol) abstractSymbol;
}
@Override
public void apply() throws PdbException, CancelledException {
// TODO: We do not know if this can be applied to a program or not. We have no examples.
String message = "Cannot apply " + this.getClass().getSimpleName() + " directly to program";
Msg.info(this, message);
PdbLog.message(message);
}
@Override
public void applyTo(AbstractMsSymbolApplier applyToApplier) {
// Do nothing
}
@Override
public void manageBlockNesting(AbstractMsSymbolApplier applierParam) {
if (applierParam instanceof FunctionSymbolApplier) {
FunctionSymbolApplier functionSymbolApplier = (FunctionSymbolApplier) applierParam;
Address address = applicator.reladdr(symbol);
// TODO: not sure if getExpression() is correct, but there is no "name."
functionSymbolApplier.beginBlock(address, symbol.getExpression(), symbol.getLength());
}
}
}

View file

@ -33,6 +33,7 @@ import ghidra.app.plugin.PluginCategoryNames;
import ghidra.app.services.DataTypeManagerService; import ghidra.app.services.DataTypeManagerService;
import ghidra.app.util.bin.format.pdb.*; import ghidra.app.util.bin.format.pdb.*;
import ghidra.app.util.bin.format.pdb.PdbParser.PdbFileType; import ghidra.app.util.bin.format.pdb.PdbParser.PdbFileType;
import ghidra.app.util.pdb.PdbProgramAttributes;
import ghidra.framework.Application; import ghidra.framework.Application;
import ghidra.framework.plugintool.*; import ghidra.framework.plugintool.*;
import ghidra.framework.plugintool.util.PluginStatus; import ghidra.framework.plugintool.util.PluginStatus;

View file

@ -0,0 +1,180 @@
/* ###
* 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.ArrayList;
import java.util.List;
import org.junit.*;
import ghidra.app.util.bin.format.pdb.*;
import ghidra.program.database.ProgramBuilder;
import ghidra.program.database.ProgramDB;
import ghidra.program.database.data.DataTypeManagerDB;
import ghidra.program.model.data.*;
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
import ghidra.util.Msg;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
/**
* Tests for the {@link DataTypeConflictHandler conflict handler} stuff.
*
*
*/
public class ConflictHandlerTest2 extends AbstractGhidraHeadedIntegrationTest {
private ProgramDB program;
private DataTypeManagerDB dtm;
private int transactionID;
public ConflictHandlerTest2() {
super();
}
private void startTransaction() {
transactionID = program.startTransaction("Test");
}
private void endTransaction() {
program.endTransaction(transactionID, true);
}
@Before
public void setUp() throws Exception {
program = createDefaultProgram(testName.getMethodName(), ProgramBuilder._X64, this);
dtm = program.getDataTypeManager();
startTransaction();
}
@After
public void tearDown() throws Exception {
endTransaction();
program.release(this);
}
@Test
public void testDataTypeConflicts() {
DataTypeConflictHandler handler =
DataTypeConflictHandler.REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER;
// First set
Composite testStruct1 = createComposite(dtm, "outer");
Pointer pointer1 = new PointerDataType(testStruct1, -1, dtm);
FunctionDefinitionDataType fn1 =
new FunctionDefinitionDataType(CategoryPath.ROOT, "fn1", dtm);
fn1.setReturnType(pointer1);
fn1.setGenericCallingConvention(GenericCallingConvention.cdecl);
fn1.setArguments(new ParameterDefinition[0]);
Composite internalStruct1 = createComposite(dtm, "inner");
Pointer internalPointer1 = new PointerDataType(internalStruct1, -1, dtm);
fillComposite(testStruct1, TaskMonitor.DUMMY, internalPointer1);
fillComposite(internalStruct1, TaskMonitor.DUMMY, null);
// Second set
Composite testStruct2 = createComposite(dtm, "outer");
Pointer pointer2 = new PointerDataType(testStruct2, -1, dtm);
FunctionDefinitionDataType fn2 =
new FunctionDefinitionDataType(CategoryPath.ROOT, "fn2", dtm);
fn2.setReturnType(pointer2);
fn2.setGenericCallingConvention(GenericCallingConvention.cdecl);
fn2.setArguments(new ParameterDefinition[0]);
Composite internalStruct2 = createComposite(dtm, "inner");
Pointer internalPointer2 = new PointerDataType(internalStruct2, -1, dtm);
fillComposite(testStruct2, TaskMonitor.DUMMY, internalPointer2);
// fillComposite(internalStruct2, monitor, null); // Without this line, we get a conflict
// Resolve
DataType t1 = dtm.resolve(testStruct1, handler);
DataType f1 = dtm.resolve(fn1, handler);
DataType t2 = dtm.resolve(testStruct2, handler);
DataType f2 = dtm.resolve(fn2, handler);
System.out.println(t1.toString());
System.out.println(f1.toString());
System.out.println(t2.toString());
System.out.println(f2.toString());
}
private static Composite createComposite(DataTypeManager dtm, String name) {
Composite composite = new StructureDataType(CategoryPath.ROOT, name, 0, dtm);
return composite;
}
private static void fillComposite(Composite composite, TaskMonitor monitor, DataType extra) {
List<DefaultTestPdbMember> members = new ArrayList<>();
DefaultTestPdbMember member;
int size = 8;
DataType intxy = IntegerDataType.dataType;
member = new DefaultTestPdbMember("x", intxy, 0);
members.add(member);
member = new DefaultTestPdbMember("y", intxy, 4);
members.add(member);
if (extra != null) {
member = new DefaultTestPdbMember("z", extra, 8);
members.add(member);
size += extra.getLength();
}
try {
if (!DefaultCompositeMember.applyDataTypeMembers(composite, false, size, members,
msg -> Msg.warn(ConflictHandlerTest2.class, msg), monitor)) {
((Structure) composite).deleteAll();
}
}
catch (Exception e) {
Msg.info(null, "Research exception thrown");
}
}
private static class DefaultTestPdbMember extends PdbMember {
private DataType dataType;
/**
* Default PDB member construction
* @param name member field name.
* @param dataType for the field.
* @param offset member's byte offset within the root composite.
*/
DefaultTestPdbMember(String name, DataType dataType, int offset) {
super(name, dataType.getName(), offset, null);
this.dataType = dataType;
}
@Override
public String getDataTypeName() {
return dataType.getName();
}
@Override
protected WrappedDataType getDataType() throws CancelledException {
if (dataType instanceof ArrayDataType) {
int size = 1; // mocking for now
if (size == 0) {
return new WrappedDataType(dataType, true, false);
}
}
return new WrappedDataType(dataType, false, false);
}
}
}

View file

@ -45,7 +45,9 @@ public class SymbolPathParser {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Symbol list must contain at least one symbol name!"); "Symbol list must contain at least one symbol name!");
} }
if (name.indexOf(Namespace.DELIMITER) == -1) { // if (name.indexOf(Namespace.DELIMITER) == -1) {
// following is temporary kludge due to struct (blah). TODO: figure/fix
if (name.startsWith("(") || name.indexOf(Namespace.DELIMITER) == -1) {
List<String> list = new ArrayList<>(); List<String> list = new ArrayList<>();
list.add(name); list.add(name);
return list; return list;