symbols = symbolTable.getSymbols(namespaceName, namespace);
Symbol namespaceSymbol =
- symbols.stream()
- .filter(s -> (s.getSymbolType() == SymbolType.NAMESPACE ||
- s.getSymbolType() == SymbolType.CLASS))
- .findFirst()
- .orElse(null);
+ symbols.stream().filter(s -> (s.getSymbolType() == SymbolType.NAMESPACE ||
+ s.getSymbolType() == SymbolType.CLASS)).findFirst().orElse(null);
if (namespaceSymbol == null) {
try {
namespace =
diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/ConflictHandlerTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/ConflictHandlerTest.java
index cc7669e4ba..0e94570a97 100644
--- a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/ConflictHandlerTest.java
+++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/ConflictHandlerTest.java
@@ -395,6 +395,122 @@ public class ConflictHandlerTest extends AbstractGhidraHeadedIntegrationTest {
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.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * Success is the aligned version is chosen over the unaligned version.
+ */
@Test
public void testResolveDataTypeNonStructConflict() throws Exception {
DataTypeManager dtm = new StandAloneDataTypeManager("Test");
diff --git a/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/checksums/MyTestMemory.java b/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/checksums/MyTestMemory.java
index 17d6ec8604..440ad3b166 100644
--- a/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/checksums/MyTestMemory.java
+++ b/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/checksums/MyTestMemory.java
@@ -27,11 +27,11 @@ import ghidra.program.model.mem.*;
import ghidra.util.exception.*;
import ghidra.util.task.TaskMonitor;
-class MyTestMemory extends AddressSet implements Memory {
+public class MyTestMemory extends AddressSet implements Memory {
byte[] myMemoryBytes;
MemoryBlock myMemoryBlock;
- MyTestMemory(byte[] bytes) {
+ public MyTestMemory(byte[] bytes) {
super();
this.myMemoryBytes = bytes;
AddressSpace space = new GenericAddressSpace("Mem", 32, AddressSpace.TYPE_RAM, 0);
@@ -108,8 +108,7 @@ class MyTestMemory extends AddressSet implements Memory {
@Override
public MemoryBlock createByteMappedBlock(String name, Address start, Address mappedAddress,
- long length, ByteMappingScheme byteMappingScheme, boolean overlay)
- throws LockException,
+ long length, ByteMappingScheme byteMappingScheme, boolean overlay) throws LockException,
MemoryConflictException, AddressOverflowException, IllegalArgumentException {
throw new UnsupportedOperationException();
}
diff --git a/Ghidra/Features/PDB/developer_scripts/DeveloperDumpAllTypesScript.java b/Ghidra/Features/PDB/developer_scripts/DeveloperDumpAllTypesScript.java
new file mode 100644
index 0000000000..a38798ef98
--- /dev/null
+++ b/Ghidra/Features/PDB/developer_scripts/DeveloperDumpAllTypesScript.java
@@ -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 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.
+ }
+
+}
diff --git a/Ghidra/Features/PDB/developer_scripts/DumpAllSymbolsDemangledScript.java b/Ghidra/Features/PDB/developer_scripts/DumpAllSymbolsDemangledScript.java
new file mode 100644
index 0000000000..2645ab3a3d
--- /dev/null
+++ b/Ghidra/Features/PDB/developer_scripts/DumpAllSymbolsDemangledScript.java
@@ -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;
+ }
+ }
+
+}
diff --git a/Ghidra/Features/PDB/developer_scripts/PdbDeveloperDumpScript.java b/Ghidra/Features/PDB/developer_scripts/PdbDeveloperDumpScript.java
new file mode 100644
index 0000000000..614c218d4a
--- /dev/null
+++ b/Ghidra/Features/PDB/developer_scripts/PdbDeveloperDumpScript.java
@@ -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");
+ }
+
+}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/plugin/core/analysis/Pdb2Analyzer.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/plugin/core/analysis/Pdb2Analyzer.java
new file mode 100644
index 0000000000..aa633d7f5d
--- /dev/null
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/plugin/core/analysis/Pdb2Analyzer.java
@@ -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();
+ }
+
+}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/plugin/core/analysis/PdbAnalyzer.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/plugin/core/analysis/PdbAnalyzer.java
index 5f6eb0c558..c16df3987d 100644
--- a/Ghidra/Features/PDB/src/main/java/ghidra/app/plugin/core/analysis/PdbAnalyzer.java
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/plugin/core/analysis/PdbAnalyzer.java
@@ -75,6 +75,7 @@ public class PdbAnalyzer extends AbstractAnalyzer {
if (pdb == null) {
return false;
}
+ Msg.info(this, "Using PDB: " + pdb.getAbsolutePath());
AutoAnalysisManager mgr = AutoAnalysisManager.getAnalysisManager(program);
return parsePdb(pdb, program, mgr, monitor, log);
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/DefaultCompositeMember.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/DefaultCompositeMember.java
index d140f989e9..138bbe65e1 100644
--- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/DefaultCompositeMember.java
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/DefaultCompositeMember.java
@@ -35,7 +35,7 @@ import ghidra.util.task.TaskMonitor;
* Container members are characterized by a null data-type name, zero length, and will be
* identified as either a structure or union.
*/
-class DefaultCompositeMember extends CompositeMember {
+public class DefaultCompositeMember extends CompositeMember {
private static int MAX_CONSTRUCTION_DEPTH = 20;
@@ -1150,7 +1150,7 @@ class DefaultCompositeMember extends CompositeMember {
* @return true if members successfully added to composite
* @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,
Consumer errorConsumer, TaskMonitor monitor) throws CancelledException {
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/PdbBitField.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/PdbBitField.java
index 4ef5a5a559..96e4283250 100644
--- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/PdbBitField.java
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/PdbBitField.java
@@ -22,7 +22,7 @@ import ghidra.util.exception.AssertException;
* PdbBitField provides ability to hang onto bitfield as a datatype.
* This will be transformed to a normal BitFieldDataType when cloned.
*/
-class PdbBitField extends BitFieldDataType {
+public class PdbBitField extends BitFieldDataType {
private int bitOffsetWithinBaseType;
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/PdbMember.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/PdbMember.java
index 30463ad076..3d5a8d4386 100644
--- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/PdbMember.java
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/PdbMember.java
@@ -21,7 +21,7 @@ import ghidra.util.exception.CancelledException;
* PdbMember convey PDB member information used for datatype
* reconstruction.
*/
-abstract class PdbMember {
+public abstract class PdbMember {
final String memberName;
final String memberDataTypeName;
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/PdbParser.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/PdbParser.java
index 495c603dcf..4253c005e8 100644
--- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/PdbParser.java
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/PdbParser.java
@@ -28,6 +28,7 @@ import ghidra.app.util.NamespaceUtils;
import ghidra.app.util.SymbolPath;
import ghidra.app.util.importer.LibrarySearchPathManager;
import ghidra.app.util.importer.MessageLog;
+import ghidra.app.util.pdb.PdbProgramAttributes;
import ghidra.framework.*;
import ghidra.framework.options.Options;
import ghidra.program.model.address.Address;
@@ -739,6 +740,7 @@ public class PdbParser {
EnumDataType createEnum(String name, int length) {
SymbolPath path = new SymbolPath(name);
+ // Ghidra does not like size of zero.
length = Integer.max(length, 1);
return new EnumDataType(getCategory(path.getParent(), true), path.getName(), length,
dataMgr);
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/WrappedDataType.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/WrappedDataType.java
index 31ce7ffcda..db5fd63789 100644
--- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/WrappedDataType.java
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/WrappedDataType.java
@@ -40,7 +40,7 @@ public class WrappedDataType {
* @param isNoType if true wrapped type corresponds to NoType as
* 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.isZeroLengthArray = isZeroLengthArray;
this.isNoType = isNoType;
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/PdbReaderOptions.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/PdbReaderOptions.java
index 564bc87d92..62ce050892 100644
--- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/PdbReaderOptions.java
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/PdbReaderOptions.java
@@ -41,8 +41,6 @@ public class PdbReaderOptions extends Exception {
private Charset oneByteCharset;
private Charset twoByteCharset;
- private boolean debug;
-
/**
* Constructor.
*/
@@ -113,6 +111,14 @@ public class PdbReaderOptions extends Exception {
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.
* @return the Charset.
@@ -130,21 +136,11 @@ public class PdbReaderOptions extends Exception {
}
/**
- * Enable/disable developmental debug.
- * @param debug {@code true} to turn debug on; default is {@code false}.
- * @return this, so options can be daisy-chained.
+ * Returns the Wchar Charset in use for PDB processing.
+ * @return the Wchar Charset.
*/
- public PdbReaderOptions setDebug(boolean debug) {
- this.debug = debug;
- return this;
- }
-
- /**
- * Returns true if debug is "on."
- * @return {@code true} if debug is "on."
- */
- public boolean isDebug() {
- return debug;
+ public Charset getWideCharCharset() {
+ return twoByteCharset;
}
}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/TypeParser.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/TypeParser.java
index 9587b509b7..2464139f43 100644
--- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/TypeParser.java
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/TypeParser.java
@@ -18,7 +18,6 @@ package ghidra.app.util.bin.format.pdb2.pdbreader;
import java.util.Objects;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.*;
-import ghidra.util.exception.AssertException;
import ghidra.util.exception.CancelledException;
/**
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/symbol/SeparatedCodeFromCompilerSupportMsSymbol.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/symbol/SeparatedCodeFromCompilerSupportMsSymbol.java
index 19e8e602b3..f2f4109d65 100644
--- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/symbol/SeparatedCodeFromCompilerSupportMsSymbol.java
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb2/pdbreader/symbol/SeparatedCodeFromCompilerSupportMsSymbol.java
@@ -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
* base class for more information.
*/
-public class SeparatedCodeFromCompilerSupportMsSymbol extends AbstractMsSymbol {
+public class SeparatedCodeFromCompilerSupportMsSymbol extends AbstractMsSymbol
+ implements AddressMsSymbol {
public static final int PDB_ID = 0x1132;
@@ -78,6 +79,72 @@ public class SeparatedCodeFromCompilerSupportMsSymbol extends AbstractMsSymbol {
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.
* @param flagsIn {@code long} containing unsigned int value.
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/PdbCategoryUtils.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/PdbCategoryUtils.java
new file mode 100644
index 0000000000..346d3717dd
--- /dev/null
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/PdbCategoryUtils.java
@@ -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 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 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 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 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++;
+ }
+
+}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/PdbLocatorSelector.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/PdbLocatorSelector.java
new file mode 100644
index 0000000000..45f50682bb
--- /dev/null
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/PdbLocatorSelector.java
@@ -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
+ *
+ * Symbol Paths.
+ *
+ * Gist is: TODO.
+ *
+ * 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).
+ *
+ *
+ */
+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 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
+ *
+ * Symbol Paths.
+ *
+ * Says order:
+ *
C:\MySymbols
+ * C:\MySymbols\ext, where ext is extension of binary (e.g., dll, exe, sys)
+ * C:\MySymbols\symbols\ext
+ *
+ */
+ // 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 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 of potential filenames
+ */
+ private List lookForPdb(Program program, boolean includePeSpecifiedPdbPath,
+ MessageLog log, String messagePrefix) {
+ String message = "";
+ try {
+
+ List 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 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 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 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 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 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 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 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 checkPathsForPdb(String symbolsRepositoryPath,
+ Set guidSubdirPaths, List potentialPdbNames,
+ PdbProgramAttributes pdbAttributes, boolean includePeSpecifiedPdbPath) {
+
+ Set symbolsRepoPaths =
+ getSymbolsRepositoryPaths(symbolsRepositoryPath, guidSubdirPaths);
+ Set predefinedPaths =
+ getPredefinedPaths(guidSubdirPaths, pdbAttributes, includePeSpecifiedPdbPath);
+
+ // Start by searching in symbolsRepositoryPath, if available.
+
+ List orderedListOfExistingFileNames = new ArrayList<>();
+ if (symbolsRepositoryPath != null) {
+ orderedListOfExistingFileNames.addAll(
+ checkSpecificPathsForPdb(symbolsRepoPaths, potentialPdbNames));
+ }
+
+ orderedListOfExistingFileNames.addAll(
+ checkSpecificPathsForPdb(predefinedPaths, potentialPdbNames));
+
+ return orderedListOfExistingFileNames;
+
+ }
+
+ //==============================================================================================
+ private static List checkSpecificPathsForPdb(Set paths,
+ List potentialPdbNames) {
+
+ List orderedListOfExistingFileNames = checkForPDBorXML(paths, potentialPdbNames);
+
+ return orderedListOfExistingFileNames;
+ }
+
+ //==============================================================================================
+ private static Set getSymbolsRepositoryPaths(String symbolsRepositoryPath,
+ Set guidSubdirPaths) {
+
+ Set 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 getPredefinedPaths(Set guidSubdirPaths,
+ PdbProgramAttributes pdbAttributes, boolean includePeSpecifiedPdbPath) {
+
+ Set predefinedPaths = new LinkedHashSet<>();
+
+ getPathsFromAttributes(pdbAttributes, includePeSpecifiedPdbPath, predefinedPaths);
+ getWindowsPaths(guidSubdirPaths, predefinedPaths);
+ getLibraryPaths(guidSubdirPaths, predefinedPaths);
+
+ return predefinedPaths;
+ }
+
+ //==============================================================================================
+ private static void getLibraryPaths(Set guidSubdirPaths, Set 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
+ *
+ * Symbol Paths.
+ *
+ * Says order:
+ *
C:\MySymbols
+ * C:\MySymbols\ext, where ext is extension of binary (e.g., dll, exe, sys)
+ * C:\MySymbols\symbols\ext
+ *
+ */
+ private static void getWindowsPaths(Set guidSubdirPaths, Set 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 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 of paths
+ * @param potentialPdbNames List of file names
+ * @return Ordered List of potential filenames
+ */
+ private static List checkForPDBorXML(Set potentialPdbDirs,
+ List potentialPdbNames) {
+
+ List 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;
+ }
+
+}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/PdbNamespaceUtils.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/PdbNamespaceUtils.java
new file mode 100644
index 0000000000..e195d4f77e
--- /dev/null
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/PdbNamespaceUtils.java
@@ -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 } and {@code }
+ * are fixed up. Example:
+ *
+ * {@code _SYSTEM_INFO::}
+ *
+ * @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 } and {@code } components of a namespace.
+ *
+ * NOTE: This could be an issue when there are multiple unnamed items, such as in, for example:
+ *
+ * {@code _SYSTEM_INFO::::}
+ *
+ * @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 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 } or {@code } 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 ("".equals(name)) {
+ return String.format("", index);
+ }
+ if ("".equals(name)) {
+ return String.format("", index);
+ }
+ if ("".equals(name)) {
+ return String.format("", index);
+ }
+ return name;
+ }
+
+}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/PdbProgramAttributes.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/PdbProgramAttributes.java
similarity index 87%
rename from Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/PdbProgramAttributes.java
rename to Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/PdbProgramAttributes.java
index 25d1de26c6..97c75c4778 100644
--- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/PdbProgramAttributes.java
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/PdbProgramAttributes.java
@@ -13,11 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package ghidra.app.util.bin.format.pdb;
+package ghidra.app.util.pdb;
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.program.model.listing.Program;
@@ -37,6 +37,7 @@ public class PdbProgramAttributes {
private String guidAgeCombo;
private String pdbFile;
+ private String pdbVersion;
private boolean pdbLoaded;
private boolean programAnalyzed;
private String executablePath;
@@ -53,6 +54,7 @@ public class PdbProgramAttributes {
programAnalyzed = propList.getBoolean(Program.ANALYZED, false);
pdbSignature = propList.getString(PdbParserConstants.PDB_SIGNATURE, (String) null);
pdbFile = propList.getString(PdbParserConstants.PDB_FILE, (String) null);
+ pdbVersion = propList.getString(PdbParserConstants.PDB_VERSION, (String) null);
executablePath = program.getExecutablePath();
@@ -68,6 +70,7 @@ public class PdbProgramAttributes {
programAnalyzed = analyzed;
pdbSignature = signature;
pdbFile = file;
+ pdbVersion = "RSDS"; // TODO: possibly receive this as argument.
executablePath = execPath;
createGuidAgeString();
@@ -89,6 +92,10 @@ public class PdbProgramAttributes {
return pdbFile;
}
+ public String getPdbVersion() {
+ return pdbVersion;
+ }
+
public boolean isPdbLoaded() {
return pdbLoaded;
}
@@ -110,21 +117,22 @@ public class PdbProgramAttributes {
if (potentialPdbFilenames == null) {
// Want to preserve add order while only keeping unique entries
- Set setOfPotentialFilenames = new LinkedHashSet();
+ Set setOfPotentialFilenames = new LinkedHashSet<>();
if (pdbFile != null) {
setOfPotentialFilenames.add(getFilename(pdbFile).toLowerCase());
+ setOfPotentialFilenames.add(getFilename(pdbFile));
setOfPotentialFilenames.add(pdbFile);
}
// getExecutablePath can return "unknown"
if (!executablePath.equals("unknown")) {
String executableFilename = getFilename(executablePath);
- setOfPotentialFilenames.add(getBinaryBasename(executableFilename).toLowerCase() +
- PdbFileType.PDB.toString());
+ setOfPotentialFilenames.add(
+ getBinaryBasename(executableFilename).toLowerCase() + ".pdb");
}
- potentialPdbFilenames = new ArrayList(setOfPotentialFilenames);
+ potentialPdbFilenames = new ArrayList<>(setOfPotentialFilenames);
}
return potentialPdbFilenames;
@@ -179,7 +187,7 @@ public class PdbProgramAttributes {
* @param fullPath from which to extract the filename
* @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
String editedPath = fullPath;
editedPath = editedPath.replaceAll("[\\/]$", "");
@@ -191,9 +199,8 @@ public class PdbProgramAttributes {
return editedPath;
}
- int indexToUse =
- (lastIndexForwardSlash > lastIndexBackSlash) ? lastIndexForwardSlash
- : lastIndexBackSlash;
+ int indexToUse = (lastIndexForwardSlash > lastIndexBackSlash) ? lastIndexForwardSlash
+ : lastIndexBackSlash;
return editedPath.substring(indexToUse + 1);
}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/AbstractComplexTypeApplier.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/AbstractComplexTypeApplier.java
new file mode 100644
index 0000000000..3f73c1f4e4
--- /dev/null
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/AbstractComplexTypeApplier.java
@@ -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 getFwdRefApplier(Class typeClass) {
+ if (!typeClass.isInstance(fwdRefApplier)) {
+ return null;
+ }
+ return typeClass.cast(fwdRefApplier);
+ }
+
+ public void setDefinitionApplier(AbstractComplexTypeApplier definitionApplier) {
+ this.definitionApplier = definitionApplier;
+ }
+
+ T getDefinitionApplier(Class 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;
+ }
+
+}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/AbstractFunctionTypeApplier.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/AbstractFunctionTypeApplier.java
new file mode 100644
index 0000000000..25ffaa4ff1
--- /dev/null
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/AbstractFunctionTypeApplier.java
@@ -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);
+ }
+
+}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/AbstractMsSymbolApplier.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/AbstractMsSymbolApplier.java
new file mode 100644
index 0000000000..22040424ac
--- /dev/null
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/AbstractMsSymbolApplier.java
@@ -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.
+ }
+
+}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/AbstractMsTypeApplier.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/AbstractMsTypeApplier.java
new file mode 100644
index 0000000000..4af118ed9a
--- /dev/null
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/AbstractMsTypeApplier.java
@@ -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 {
+
+ 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 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 list = new ArrayList<>(waitSet);
+ return list.get(0);
+ }
+
+}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/ArgumentsListTypeApplier.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/ArgumentsListTypeApplier.java
new file mode 100644
index 0000000000..6286837ca6
--- /dev/null
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/ArgumentsListTypeApplier.java
@@ -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 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 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 args = argsList.getArgRecordNumbers();
+ List 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 list = argsList.getArgTypeIndexList();
+// List 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()]));
+// }
+//
+}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/ArrayTypeApplier.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/ArrayTypeApplier.java
new file mode 100644
index 0000000000..9755a28393
--- /dev/null
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/ArrayTypeApplier.java
@@ -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;
+ }
+
+}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/BaseClassTypeApplier.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/BaseClassTypeApplier.java
new file mode 100644
index 0000000000..236a1e48b1
--- /dev/null
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/BaseClassTypeApplier.java
@@ -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.
+ }
+
+}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/BitfieldTypeApplier.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/BitfieldTypeApplier.java
new file mode 100644
index 0000000000..895257c2b4
--- /dev/null
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/BitfieldTypeApplier.java
@@ -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!!!
+ }
+
+ /**
+ * Pdb2BitField 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);
+ }
+ }
+
+}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/BlockCommentsManager.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/BlockCommentsManager.java
new file mode 100644
index 0000000000..e6a6f01aef
--- /dev/null
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/BlockCommentsManager.java
@@ -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 blockPreComments;
+ private Map 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 entry : blockPreComments.entrySet()) {
+ appendBlockComment(program, entry.getKey().add(addressDelta), entry.getValue(),
+ CodeUnit.PRE_COMMENT);
+ }
+ for (Map.Entry 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);
+ }
+
+}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/BlockSymbolApplier.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/BlockSymbolApplier.java
new file mode 100644
index 0000000000..7ad4e8900b
--- /dev/null
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/BlockSymbolApplier.java
@@ -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());
+ }
+ }
+}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/ComplexTypeApplierMapper.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/ComplexTypeApplierMapper.java
new file mode 100644
index 0000000000..bf28bbf429
--- /dev/null
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/ComplexTypeApplierMapper.java
@@ -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 complexTypeAppliersBySymbolPath;
+ private Map> compositeAppliersQueueBySymbolPath;
+ private Map> 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> applierQueueBySymbolPath,
+ AbstractComplexTypeApplier complexApplier) {
+ SymbolPath symbolPath = complexApplier.getSymbolPath();
+ Objects.requireNonNull(symbolPath, "SymbolPath may not be null");
+
+ LinkedList 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 getComplexTypeApplierBySymbolPath(
+// SymbolPath symbolPath, Class 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> applierQueueBySymbolPath,
+// AbstractComplexTypeApplier complexApplier) {
+// SymbolPath symbolPath = complexApplier.getSymbolPath();
+// Objects.requireNonNull(symbolPath, "SymbolPath may not be null");
+//
+// LinkedList 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);
+// }
+// }
+//
+
+}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/CompositeLayoutMode.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/CompositeLayoutMode.java
new file mode 100644
index 0000000000..13f15adbe1
--- /dev/null
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/CompositeLayoutMode.java
@@ -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 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;
+ }
+}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/CompositeTypeApplier.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/CompositeTypeApplier.java
new file mode 100644
index 0000000000..16acd28ffc
--- /dev/null
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/CompositeTypeApplier.java
@@ -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("", Undefined1DataType.dataType);
+//
+ private Map componentComments;
+
+ private List 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 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 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());
+ }
+ }
+ }
+
+// /**
+// * NoType 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);
+// }
+// }
+//
+}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/CppCompositeType.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/CppCompositeType.java
new file mode 100644
index 0000000000..e3d286a6db
--- /dev/null
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/CppCompositeType.java
@@ -0,0 +1,1915 @@
+/* ###
+ * 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 java.util.Map.Entry;
+
+import org.apache.commons.lang3.StringUtils;
+
+import ghidra.app.util.bin.format.pdb.*;
+import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
+import ghidra.app.util.bin.format.pdb2.pdbreader.PdbLog;
+import ghidra.app.util.pdb.pdbapplicator.PdbVbtManager.PdbVirtualBaseTable;
+import ghidra.program.model.data.*;
+import ghidra.util.Msg;
+import ghidra.util.exception.AssertException;
+import ghidra.util.exception.CancelledException;
+import ghidra.util.task.TaskMonitor;
+
+/**
+ * Notional C++ Class Type.
+ */
+public class CppCompositeType {
+
+ // Order matters for both base classes and members for class layout. Members get offsets,
+ // which helps for those, but layout algorithms usually utilize order.
+ private List syntacticBaseClasses;
+ private List layoutBaseClasses;
+ private List myMembers;
+ private List layoutMembers;
+ private List layoutVftPtrMembers;
+ private boolean isFinal;
+ private Type type;
+ private String className; // String for now.
+ private String mangledName;
+ private int size;
+ private Composite composite;
+ private CategoryPath categoryPath;
+
+ private OoComponentLayoutMode layoutMode = OoComponentLayoutMode.UNKNOWN;
+
+ private List memberData;
+
+ private boolean hasDirect;
+
+ static String createDirectClassName(Composite composite) {
+ return composite.getName() + "_direct";
+ }
+
+ static CategoryPath createDirectCategoryPath(CppCompositeType cppType) {
+ return cppType.getBaseCategoryName(
+ CppCompositeType.createDirectClassName(cppType.getComposite()));
+ }
+
+ /*
+ * Not certain, but think there should only be one Virtual Base Table for a given
+ * class (not counting those for its parents). However, since VirtualBaseClass and
+ * IndirectVirtualBase class records both have an "offset" for (seemingly) where the
+ * virtual base table point can be located, then there is a chance that different
+ * records for a class could have different values. This HashMap will is keyed by this
+ * offset, in case we see more than one. Want to log the fact if more than one value is seen
+ * for a particular hierarchy level.
+ */
+ private Map placeholderVirtualBaseTables;
+
+ //----------------------------------------------------------------------------------------------
+ public CppCompositeType(Composite composite, String mangledName) {
+ Objects.requireNonNull(composite, "composite may not be null");
+ syntacticBaseClasses = new ArrayList<>();
+ layoutBaseClasses = new ArrayList<>();
+ myMembers = new ArrayList<>();
+ layoutMembers = new ArrayList<>();
+
+ memberData = new ArrayList<>();
+ layoutVftPtrMembers = new ArrayList<>();
+
+ isFinal = false;
+ type = Type.UNKNOWN;
+ this.composite = composite;
+ placeholderVirtualBaseTables = new HashMap<>();
+ categoryPath = new CategoryPath(composite.getCategoryPath(), composite.getName());
+ this.mangledName = mangledName;
+ }
+
+ public static CppClassType createCppClassType(Composite composite, String mangledName) {
+ return new CppClassType(composite, mangledName);
+ }
+
+ public static CppClassType createCppClassType(Composite composite, String name,
+ String mangledName, int size) {
+ CppClassType cppType = new CppClassType(composite, mangledName);
+ cppType.setName(name);
+ cppType.setSize(size);
+ return cppType;
+ }
+
+ public static CppStructType createCppStructType(Composite composite, String mangledName) {
+ return new CppStructType(composite, mangledName);
+ }
+
+ public static CppStructType createCppStructType(Composite composite, String name,
+ String mangledName, int size) {
+ CppStructType cppType = new CppStructType(composite, mangledName);
+ cppType.setName(name);
+ cppType.setSize(size);
+ return cppType;
+ }
+
+ private static class CppClassType extends CppCompositeType {
+ private CppClassType(Composite composite, String mangledName) {
+ super(composite, mangledName);
+ setClass();
+ }
+ }
+
+ private static class CppStructType extends CppCompositeType {
+ private CppStructType(Composite composite, String mangledName) {
+ super(composite, mangledName);
+ setStruct();
+ }
+ }
+
+ static boolean validateMangledCompositeName(String mangledCompositeTypeName, Type type) {
+ if (mangledCompositeTypeName == null) {
+ return false;
+ }
+ if (!mangledCompositeTypeName.startsWith(".?")) {
+ return false;
+ }
+ if (mangledCompositeTypeName.length() < 7) {
+ return false;
+ }
+ if (mangledCompositeTypeName.charAt(2) != 'A') {
+ PdbLog.message("Mangled composite type name not plain 'A'");
+ }
+ switch (mangledCompositeTypeName.charAt(3)) {
+ case 'T':
+ if ((type.compareTo(Type.UNION) != 0) && (type.compareTo(Type.UNKNOWN) != 0)) {
+ PdbLog.message("Warning: Mismatched complex type 'T' for " + type);
+ }
+ break;
+ case 'U':
+ if ((type.compareTo(Type.STRUCT) != 0) && (type.compareTo(Type.UNKNOWN) != 0)) {
+ PdbLog.message("Warning: Mismatched complex type 'U' for " + type);
+ }
+ break;
+ case 'V':
+ if ((type.compareTo(Type.CLASS) != 0) && (type.compareTo(Type.UNKNOWN) != 0)) {
+ PdbLog.message("Warning: Mismatched complex type 'V' for " + type);
+ }
+ break;
+ default:
+ PdbLog.message("Not composite");
+ return false;
+ }
+ return true;
+
+ }
+
+ public boolean validate() {
+ // C++ rules:
+ // If final, can have an empty name. I believe this means "name" can be empty, but
+ // can still have parent namespace. TODO: check this if we change to something more
+ // than String.
+ if (StringUtils.isEmpty(className) && !isFinal) {
+ return false;
+ }
+ return true;
+ }
+
+ private List getLayoutBaseClasses() {
+ return layoutBaseClasses;
+ }
+
+ Composite getComposite() {
+ return composite;
+ }
+
+ private CategoryPath getCategoryPath() {
+ return categoryPath;
+ }
+
+ public void setFinal(boolean isFinal) {
+ this.isFinal = isFinal;
+ }
+
+ public boolean isFinal() {
+ return isFinal;
+ }
+
+ public void setClass() {
+ type = Type.CLASS;
+ }
+
+ public void setStruct() {
+ type = Type.STRUCT;
+ }
+
+ public void setUnion() {
+ type = Type.UNION;
+ }
+
+ // not sure if user can see Type when returned.
+ public Type getType() {
+ return type;
+ }
+
+ public void setName(String className) {
+ this.className = className;
+ }
+
+ public String getName() {
+ return className;
+ }
+
+ public void setMangledName(String mangledName) {
+ this.mangledName = mangledName;
+ }
+
+ public String getMangledName() {
+ return mangledName;
+ }
+
+ public void setSize(int size) {
+ this.size = size;
+ }
+
+ public int getSize() {
+ return size;
+ }
+
+ public int getNumMembers() {
+ return myMembers.size();
+ }
+
+ public int getNumLayoutMembers() {
+ return layoutMembers.size();
+ }
+
+ public void addVirtualFunctionTablePointer(String name, DataType dataType, int offset) {
+ Member newMember = new Member(name, dataType, false,
+ new ClassFieldAttributes(Access.UNKNOWN, Property.UNKNOWN), offset);
+ layoutVftPtrMembers.add(newMember);
+ }
+
+ private void insertVirtualFunctionTablePointers(List pdbMembers) {
+ for (Member vftPtrMember : layoutVftPtrMembers) {
+ ClassPdbMember vftPtrPdbMember = new ClassPdbMember(vftPtrMember.getName(),
+ vftPtrMember.getDataType(), vftPtrMember.isFlexibleArray(),
+ vftPtrMember.getOffset(), vftPtrMember.getComment());
+ int index = 0;
+ for (ClassPdbMember member : pdbMembers) {
+ if (member.getOffset() > vftPtrMember.getOffset()) {
+ break;
+ }
+ index++;
+ }
+ pdbMembers.add(index, vftPtrPdbMember);
+ }
+ }
+
+ public void addMember(String memberName, DataType dataType, boolean isFlexibleArray, int offset,
+ String comment) {
+ addMember(memberName, dataType, isFlexibleArray,
+ new ClassFieldAttributes(Access.UNKNOWN, Property.UNKNOWN), offset, comment);
+ }
+
+ public void addMember(String memberName, DataType dataType, boolean isFlexibleArray,
+ int offset) {
+ addMember(memberName, dataType, isFlexibleArray,
+ new ClassFieldAttributes(Access.UNKNOWN, Property.UNKNOWN), offset, null);
+ }
+
+ public void addMember(String memberName, DataType dataType, boolean isFlexibleArray,
+ ClassFieldAttributes attributes, int offset) {
+ Member newMember = new Member(memberName, dataType, isFlexibleArray, attributes, offset);
+ myMembers.add(newMember);
+ addMember(layoutMembers, newMember);
+ }
+
+ public void addMember(String memberName, DataType dataType, boolean isFlexibleArray,
+ ClassFieldAttributes attributes, int offset, String comment) {
+ Member newMember =
+ new Member(memberName, dataType, isFlexibleArray, attributes, offset, comment);
+ myMembers.add(newMember);
+ addMember(layoutMembers, newMember);
+ }
+
+ private void addMember(List members, Member newMember) {
+ members.add(newMember);
+ }
+
+ //==============================================================================================
+ /*
+ * These "insert" methods should be used judiciously. You need to know what/why you are doing
+ * this. Changing the order of "normal" members can mess up the layout algorithms from
+ * {@link DefaultCompositeMember}. The only place we currently think we can use these is
+ * when trying to place vbptr members. Not all of these methods are used too.
+ * @param isFlexibleArray TODO
+ */
+ public void insertMember(String memberName, DataType dataType, boolean isFlexibleArray,
+ int offset, String comment) {
+ insertMember(memberName, dataType, isFlexibleArray,
+ new ClassFieldAttributes(Access.UNKNOWN, Property.UNKNOWN), offset, comment);
+ }
+
+ public void insertMember(String memberName, DataType dataType, boolean isFlexibleArray,
+ int offset) {
+ insertMember(memberName, dataType, isFlexibleArray,
+ new ClassFieldAttributes(Access.UNKNOWN, Property.UNKNOWN), offset, null);
+ }
+
+ public void insertMember(String memberName, DataType dataType, boolean isFlexibleArray,
+ ClassFieldAttributes attributes, int offset) {
+ Member newMember = new Member(memberName, dataType, isFlexibleArray, attributes, offset);
+ myMembers.add(newMember);
+ insertMember(layoutMembers, newMember);
+ }
+
+ public void insertMember(String memberName, DataType dataType, boolean isFlexibleArray,
+ ClassFieldAttributes attributes, int offset, String comment) {
+ Member newMember =
+ new Member(memberName, dataType, isFlexibleArray, attributes, offset, comment);
+ myMembers.add(newMember);
+ insertMember(layoutMembers, newMember);
+ }
+
+ private void insertMember(List members, Member newMember) {
+ int index = 0;
+ for (Member member : members) {
+ if (member.getOffset() > newMember.getOffset()) {
+ break;
+ }
+ index++;
+ }
+ members.add(index, newMember);
+ }
+
+ public void addStaticMember(String memberName, DataType dataType) {
+ addStaticMember(memberName, dataType,
+ new ClassFieldAttributes(Access.UNKNOWN, Property.UNKNOWN));
+ }
+
+ public void addStaticMember(String memberName, DataType dataType,
+ ClassFieldAttributes attributes) {
+ myMembers.add(new StaticMember(memberName, dataType, attributes));
+ }
+
+ public int getNumLayoutBaseClasses() {
+ return layoutBaseClasses.size();
+ }
+
+ public int getNumLayoutVirtualBaseClasses() {
+ int num = 0;
+ for (LayoutBaseClass base : layoutBaseClasses) {
+ if (base instanceof DirectLayoutBaseClass) {
+ num++;
+ }
+ }
+ return layoutBaseClasses.size() - num;
+ }
+
+ public int getNumSyntacticBaseClasses() {
+ return syntacticBaseClasses.size();
+ }
+
+ public void addSyntacticBaseClass(CppCompositeType baseClassType) throws PdbException {
+ addSyntacticBaseClass(baseClassType,
+ new ClassFieldAttributes(Access.UNKNOWN, Property.UNKNOWN));
+ }
+
+ public void addSyntacticBaseClass(CppCompositeType baseClassType,
+ ClassFieldAttributes attributes) throws PdbException {
+ validateBaseClass(baseClassType);
+ syntacticBaseClasses.add(new SyntacticBaseClass(baseClassType, attributes));
+ }
+
+ public void addDirectSyntacticBaseClass(CppCompositeType baseClassType) throws PdbException {
+ addDirectSyntacticBaseClass(baseClassType,
+ new ClassFieldAttributes(Access.UNKNOWN, Property.UNKNOWN));
+ }
+
+ public void addDirectSyntacticBaseClass(CppCompositeType baseClassType,
+ ClassFieldAttributes attributes) throws PdbException {
+ validateBaseClass(baseClassType);
+ syntacticBaseClasses.add(new DirectSyntacticBaseClass(baseClassType, attributes));
+ }
+
+ public void addVirtualSyntacticBaseClass(CppCompositeType baseClassType) throws PdbException {
+ addVirtualSyntacticBaseClass(baseClassType,
+ new ClassFieldAttributes(Access.UNKNOWN, Property.UNKNOWN));
+ }
+
+ public void addVirtualSyntacticBaseClass(CppCompositeType baseClassType,
+ ClassFieldAttributes attributes) throws PdbException {
+ validateBaseClass(baseClassType);
+ syntacticBaseClasses.add(new VirtualSyntacticBaseClass(baseClassType, attributes));
+ }
+
+ public void insertSyntacticBaseClass(CppCompositeType baseClassType, int ordinal)
+ throws PdbException {
+ insertSyntacticBaseClass(baseClassType,
+ new ClassFieldAttributes(Access.UNKNOWN, Property.UNKNOWN), ordinal);
+ }
+
+ public void insertSyntacticBaseClass(CppCompositeType baseClassType,
+ ClassFieldAttributes attributes, int ordinal) throws PdbException {
+ validateBaseClass(baseClassType);
+ if (ordinal < 0 || ordinal > getNumSyntacticBaseClasses()) {
+ // TODO: Change this to some new Exception type; e.g., ClassTypeException.
+ throw new PdbException("Invalid base class insertion index.");
+ }
+ syntacticBaseClasses.add(ordinal, new SyntacticBaseClass(baseClassType, attributes));
+ }
+
+ public void insertDirectSyntacticBaseClass(CppCompositeType baseClassType, int ordinal)
+ throws PdbException {
+ insertDirectSyntacticBaseClass(baseClassType,
+ new ClassFieldAttributes(Access.UNKNOWN, Property.UNKNOWN), ordinal);
+ }
+
+ public void insertDirectSyntacticBaseClass(CppCompositeType baseClassType,
+ ClassFieldAttributes attributes, int ordinal) throws PdbException {
+ validateBaseClass(baseClassType);
+ if (ordinal < 0 || ordinal > getNumSyntacticBaseClasses()) {
+ // TODO: Change this to some new Exception type; e.g., ClassTypeException.
+ throw new PdbException("Invalid base class insertion index.");
+ }
+ syntacticBaseClasses.add(ordinal, new DirectSyntacticBaseClass(baseClassType, attributes));
+ }
+
+ public void insertVirtualSyntacticBaseClass(CppCompositeType baseClassType, int ordinal)
+ throws PdbException {
+ insertVirtualSyntacticBaseClass(baseClassType,
+ new ClassFieldAttributes(Access.UNKNOWN, Property.UNKNOWN), ordinal);
+ }
+
+ public void insertVirtualSyntacticBaseClass(CppCompositeType baseClassType,
+ ClassFieldAttributes attributes, int ordinal) throws PdbException {
+ validateBaseClass(baseClassType);
+ if (ordinal < 0 || ordinal > getNumSyntacticBaseClasses()) {
+ // TODO: Change this to some new Exception type; e.g., ClassTypeException.
+ throw new PdbException("Invalid base class insertion index.");
+ }
+ syntacticBaseClasses.add(ordinal, new VirtualSyntacticBaseClass(baseClassType, attributes));
+ }
+
+ //==============================================================================================
+ public void addDirectBaseClass(CppCompositeType baseClassType, int offset) throws PdbException {
+ addDirectBaseClass(baseClassType,
+ new ClassFieldAttributes(Access.UNKNOWN, Property.UNKNOWN), offset);
+ }
+
+ public void addDirectBaseClass(CppCompositeType baseClassType, ClassFieldAttributes attributes,
+ int offset) throws PdbException {
+ validateBaseClass(baseClassType);
+ layoutBaseClasses.add(new DirectLayoutBaseClass(baseClassType, attributes, offset));
+ }
+
+// // Index is order in list, not physical offset of class elements.
+// public void insertDirectBaseClass(CppCompositeType baseClassType, int index)
+// throws PdbException {
+// insertDirectBaseClass(baseClassType,
+// new ClassFieldAttributes(Access.UNKNOWN, Property.UNKNOWN), index);
+// }
+//
+// // Index is order in list, not physical offset of class elements.
+// public void insertDirectBaseClass(CppCompositeType baseClassType,
+// ClassFieldAttributes attributes, int index) throws PdbException {
+// validateBaseClass(baseClassType);
+// if (index < 0 || index > getNumBaseClasses()) {
+// // TODO: Change this to some new Exception type; e.g., ClassTypeException.
+// throw new PdbException("Invalid base class insertion index.");
+// }
+// layoutBaseClasses.add(index, new DirectBaseClass(baseClassType, attributes));
+// }
+//
+ public void addDirectVirtualBaseClass(CppCompositeType baseClassType, int basePointerOffset,
+ DataType vbptr, int offsetFromVbt) throws PdbException {
+ addDirectVirtualBaseClass(baseClassType,
+ new ClassFieldAttributes(Access.UNKNOWN, Property.UNKNOWN), basePointerOffset, vbptr,
+ offsetFromVbt);
+ }
+
+ public void addDirectVirtualBaseClass(CppCompositeType baseClassType,
+ ClassFieldAttributes attributes, int basePointerOffset, DataType vbptr,
+ int offsetFromVbt) throws PdbException {
+ validateBaseClass(baseClassType);
+ layoutBaseClasses.add(new DirectVirtualLayoutBaseClass(baseClassType, attributes,
+ basePointerOffset, vbptr, offsetFromVbt));
+ }
+
+// // Index is order in list, not physical offset of class elements.
+// public void insertDirectVirtualBaseClass(CppCompositeType baseClassType, int index)
+// throws PdbException {
+// insertDirectVirtualBaseClass(baseClassType,
+// new ClassFieldAttributes(Access.UNKNOWN, Property.UNKNOWN), index);
+// }
+//
+// // Index is order in list, not physical offset of class elements.
+// public void insertDirectVirtualBaseClass(CppCompositeType baseClassType,
+// ClassFieldAttributes attributes, int index) throws PdbException {
+// validateBaseClass(baseClassType);
+// if (index < 0 || index > getNumBaseClasses()) {
+// // TODO: Change this to some new Exception type; e.g., ClassTypeException.
+// throw new PdbException("Invalid base class insertion index.");
+// }
+// layoutBaseClasses.add(index, new DirectVirtualBaseClass(baseClassType, attributes));
+// }
+//
+ public void addIndirectVirtualBaseClass(CppCompositeType baseClassType, int basePointerOffset,
+ DataType vbptr, int offsetFromVbt) throws PdbException {
+ addIndirectVirtualBaseClass(baseClassType,
+ new ClassFieldAttributes(Access.UNKNOWN, Property.UNKNOWN), basePointerOffset, vbptr,
+ offsetFromVbt);
+ }
+
+ public void addIndirectVirtualBaseClass(CppCompositeType baseClassType,
+ ClassFieldAttributes attributes, int basePointerOffset, DataType vbptr,
+ int offsetFromVbt) throws PdbException {
+ validateBaseClass(baseClassType);
+ layoutBaseClasses.add(new IndirectVirtualLayoutBaseClass(baseClassType, attributes,
+ basePointerOffset, vbptr, offsetFromVbt));
+ }
+
+// // Index is order in list, not physical offset of class elements.
+// public void insertIndirectVirtualBaseClass(CppCompositeType baseClassType, int index)
+// throws PdbException {
+// insertIndirectVirtualBaseClass(baseClassType,
+// new ClassFieldAttributes(Access.UNKNOWN, Property.UNKNOWN), index);
+// }
+//
+// // Index is order in list, not physical offset of class elements.
+// public void insertIndirectVirtualBaseClass(CppCompositeType baseClassType,
+// ClassFieldAttributes attributes, int index) throws PdbException {
+// validateBaseClass(baseClassType);
+// if (index < 0 || index > getNumBaseClasses()) {
+// // TODO: Change this to some new Exception type; e.g., ClassTypeException.
+// throw new PdbException("Invalid base class insertion index.");
+// }
+// layoutBaseClasses.add(index, new IndirectVirtualBaseClass(baseClassType, attributes));
+// }
+//
+ private static void validateBaseClass(CppCompositeType baseClassType) throws PdbException {
+ if (baseClassType.isFinal) {
+ throw new PdbException("Cannot inherit base class marked final.");
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append(type);
+ builder.append(className);
+ if (isFinal) {
+ builder.append(" final");
+ }
+ StringBuilder baseBuilder = new StringBuilder();
+ for (BaseClass base : syntacticBaseClasses) {
+ if (baseBuilder.length() == 0) {
+ baseBuilder.append(" : ");
+ }
+ else {
+ baseBuilder.append(", ");
+ }
+ baseBuilder.append(base);
+ }
+ builder.append(baseBuilder);
+ return builder.toString();
+ }
+
+ public OoComponentLayoutMode getLayoutMode(CompositeLayoutMode layoutOptions) {
+ if (layoutMode == OoComponentLayoutMode.UNKNOWN) {
+ layoutMode = determineLayoutMode(layoutOptions);
+ }
+ return layoutMode;
+ }
+
+ private OoComponentLayoutMode determineLayoutMode(CompositeLayoutMode layoutOptions) {
+ OoComponentLayoutMode initialModeDetermination;
+ if (layoutOptions.getLayoutMode() == OoComponentLayoutMode.MEMBERS_ONLY) {
+ return OoComponentLayoutMode.MEMBERS_ONLY;
+ }
+ else if (getNumLayoutBaseClasses() == 0) {
+ initialModeDetermination = OoComponentLayoutMode.BASIC;
+ }
+ else if (getNumLayoutVirtualBaseClasses() == 0) {
+ initialModeDetermination = OoComponentLayoutMode.SIMPLE;
+ }
+ else {
+ initialModeDetermination = OoComponentLayoutMode.COMPLEX;
+ }
+ OoComponentLayoutMode optionsMode = layoutOptions.getLayoutMode();
+ return optionsMode.compareTo(initialModeDetermination) >= 0 ? optionsMode
+ : initialModeDetermination;
+ }
+
+ boolean isZeroSize() {
+ return memberData.size() == 0;
+ }
+
+ //----------------------------------------------------------------------------------------------
+ //----------------------------------------------------------------------------------------------
+ public void createLayoutFromSyntacticDescription(VbtManager vbtManager, TaskMonitor monitor) {
+ for (SyntacticBaseClass base : syntacticBaseClasses) {
+ if (base instanceof DirectSyntacticBaseClass) {
+
+ }
+ else { // VirtualSyntacticBaseClass
+
+ }
+ }
+ }
+
+ //----------------------------------------------------------------------------------------------
+ //----------------------------------------------------------------------------------------------
+ public void createLayout(CompositeLayoutMode layoutOptions, VbtManager vbtManager,
+ TaskMonitor monitor) throws PdbException, CancelledException {
+ if (vbtManager instanceof PdbVbtManager) { // Information from PDB/program symbols
+ // TODO: both same for now
+ //doSpeculativeLayout(vbtManager, monitor);
+ createVbtBasedLayout(layoutOptions, vbtManager, monitor);
+ }
+ else {
+ createSpeculativeLayout(layoutOptions, vbtManager, monitor);
+ }
+ }
+
+ //----------------------------------------------------------------------------------------------
+ //----------------------------------------------------------------------------------------------
+ public void createVbtBasedLayout(CompositeLayoutMode layoutOptions, VbtManager vbtManager,
+ TaskMonitor monitor) throws PdbException, CancelledException {
+ CategoryPath cn;
+ hasDirect = false;
+ switch (getLayoutMode(layoutOptions)) {
+ case MEMBERS_ONLY:
+ addLayoutPdbMembers(memberData, layoutMembers);
+ break;
+ case BASIC:
+ addLayoutPdbMembers(memberData, layoutMembers);
+ insertVirtualFunctionTablePointers(memberData);
+ break;
+ // TODO: evaluate... not really getting difference I thought we could get... so far
+ // BASIC and SIMPLE seem to yield the same results. I might be doing something wrong.
+ case SIMPLE:
+ case COMPLEX:
+ cn = createDirectCategoryPath(this);
+ Composite directDataType = new StructureDataType(cn.getParent(), cn.getName(), 0,
+ composite.getDataTypeManager());
+
+ List directClassPdbMembers = getDirectBaseClassMembers(monitor);
+ List myVirtualLayoutBases = preprocessVirtualBases(monitor);
+
+ // TODO: consider moving down below next line.
+ boolean allVbtFound =
+ reconcileVirtualBaseTables(composite.getDataTypeManager(), vbtManager);
+
+ addLayoutPdbMembers(directClassPdbMembers, layoutMembers);
+ insertVirtualFunctionTablePointers(directClassPdbMembers);
+
+ if (!DefaultCompositeMember.applyDataTypeMembers(directDataType, false, 0,
+ directClassPdbMembers, msg -> Msg.warn(this, msg), monitor)) {
+ clearComponents(directDataType);
+ }
+ int directClassLength = getCompositeLength(directDataType);
+
+ if (directClassLength == 0) {
+ // Not using the direct type (only used it to get the directClassLength), so
+ // remove it and add the members to the main type instead.
+ directDataType.getDataTypeManager().remove(directDataType, monitor);
+ }
+ else {
+ // this does not deal with the case where more members from memberData get
+ // added below and must still fit in "size."
+ if (directClassLength > size) {
+ // Redo it with the size of the overall structure/class
+ directDataType.getDataTypeManager().remove(directDataType, monitor);
+ directDataType = new StructureDataType(cn.getParent(), cn.getName(), 0,
+ composite.getDataTypeManager());
+ if (!DefaultCompositeMember.applyDataTypeMembers(directDataType, false,
+ size, directClassPdbMembers, msg -> Msg.warn(this, msg), monitor)) {
+ clearComponents(directDataType);
+ }
+ directClassLength = getCompositeLength(directDataType);
+ }
+ if (getLayoutMode(layoutOptions) == OoComponentLayoutMode.SIMPLE) {
+ // Not using the dummy/direct type (only used it to get the
+ // directClassLength), so remove it and add the members to the main
+ // type instead.
+ directDataType.getDataTypeManager().remove(directDataType, monitor);
+ memberData.addAll(directClassPdbMembers);
+ //addLayoutPdbMembers(memberData, layoutMembers, monitor);
+ }
+ else {
+ ClassPdbMember directClassPdbMember =
+ new ClassPdbMember("", directDataType, false, 0, null);
+ memberData.add(directClassPdbMember);
+ hasDirect = true;
+ }
+ }
+
+ addVirtualBases(directClassLength, memberData, myVirtualLayoutBases, allVbtFound,
+ monitor);
+
+ break;
+ default:
+ throw new PdbException("Unhandled layout mode");
+ }
+
+ if (!DefaultCompositeMember.applyDataTypeMembers(composite, false, size, memberData,
+ msg -> Msg.warn(this, msg), monitor)) {
+ clearComponents(composite);
+ }
+ }
+
+ //----------------------------------------------------------------------------------------------
+ //----------------------------------------------------------------------------------------------
+ private List getDirectBaseClassMembers(TaskMonitor monitor)
+ throws CancelledException {
+ List myDirectClassPdbMembers = new ArrayList<>();
+ for (LayoutBaseClass base : getLayoutBaseClasses()) {
+ monitor.checkCanceled();
+ CppCompositeType baseComposite = base.getBaseClassType();
+ if (base instanceof DirectLayoutBaseClass) {
+ if (!baseComposite.isZeroSize()) {
+ Composite baseDataType = base.getDirectDataType();
+ int offset = ((DirectLayoutBaseClass) base).getOffset();
+ CategoryPath cn =
+ getBaseCategoryName("BaseClass_" + base.getBaseClassType().getName());
+ Member baseMember =
+ new Member("", baseDataType, false, null, offset, cn.toString());
+ addPdbMember(myDirectClassPdbMembers, baseMember);
+ }
+ }
+ }
+ return myDirectClassPdbMembers;
+ }
+
+ //----------------------------------------------------------------------------------------------
+ //----------------------------------------------------------------------------------------------
+ private List preprocessVirtualBases(TaskMonitor monitor)
+ throws CancelledException, PdbException {
+ List myVirtualLayoutBases = new ArrayList<>();
+ for (LayoutBaseClass base : getLayoutBaseClasses()) {
+ monitor.checkCanceled();
+ if (base instanceof VirtualLayoutBaseClass) {
+ addPlaceholderVirtualBaseTableEntry(((VirtualLayoutBaseClass) base));
+ myVirtualLayoutBases.add((VirtualLayoutBaseClass) base);
+ }
+ }
+ return myVirtualLayoutBases;
+ }
+
+ //----------------------------------------------------------------------------------------------
+ //----------------------------------------------------------------------------------------------
+ public void createSpeculativeLayout(CompositeLayoutMode layoutOptions, VbtManager vbtManager,
+ TaskMonitor monitor) throws PdbException, CancelledException {
+ // Speculative Layout uses recursion to try to know the order of members. However, MSFT
+ // rearranges the order of the Base Class records such that they are not necessarily in
+ // the order that the class was declared, and it seems that the member order follows the
+ // order of the class hierarchy declaration.
+ // We use recursion and also also reordering so Base Classes always follow their children,
+ // so with multiple virtual inheritance, a parent from multiple family lines will likely
+ // get moved.
+ CategoryPath cn;
+ hasDirect = false;
+ switch (getLayoutMode(layoutOptions)) {
+ case MEMBERS_ONLY:
+ addLayoutPdbMembers(memberData, layoutMembers);
+ break;
+ case BASIC:
+ cn = composite.getCategoryPath();
+ addLayoutPdbMembers(memberData, layoutMembers);
+ insertVirtualFunctionTablePointers(memberData);
+ break;
+ // TODO: evaluate... not really getting difference I thought we could get... so far
+ // BASIC and SIMPLE seem to yield the same results. I might be doing something wrong.
+ case SIMPLE:
+ case COMPLEX:
+ cn = createDirectCategoryPath(this);
+ Composite directDataType = new StructureDataType(cn.getParent(), cn.getName(), 0,
+ composite.getDataTypeManager());
+
+ List myAccumulatedDirectBases = new ArrayList<>();
+ List myAccumulatedVirtualBases = new ArrayList<>();
+ List directClassPdbMembers = new ArrayList<>();
+ processBaseClassesRecursive(this, true, directClassPdbMembers,
+ myAccumulatedDirectBases, myAccumulatedVirtualBases, 0, monitor);
+
+ // TODO: consider moving down below next line.
+ boolean allVbtFound =
+ reconcileVirtualBaseTables(composite.getDataTypeManager(), vbtManager);
+
+ addLayoutPdbMembers(directClassPdbMembers, layoutMembers);
+ insertVirtualFunctionTablePointers(directClassPdbMembers);
+
+ if (!DefaultCompositeMember.applyDataTypeMembers(directDataType, false, 0,
+ directClassPdbMembers, msg -> Msg.warn(this, msg), monitor)) {
+ clearComponents(directDataType);
+ }
+ int directClassLength = getCompositeLength(directDataType);
+
+ if (directClassLength == 0) {
+ // Not using the direct type (only used it to get the directClassLength), so
+ // remove it and add the members to the main type instead.
+ directDataType.getDataTypeManager().remove(directDataType, monitor);
+ }
+ else {
+ // this does not deal with the case where more members from memberData get
+ // added below and must still fit in "size."
+ if (directClassLength > size) {
+ // Redo it with the size of the overall structure/class
+ directDataType.getDataTypeManager().remove(directDataType, monitor);
+ directDataType = new StructureDataType(cn.getParent(), cn.getName(), 0,
+ composite.getDataTypeManager());
+ if (!DefaultCompositeMember.applyDataTypeMembers(directDataType, false,
+ size, directClassPdbMembers, msg -> Msg.warn(this, msg), monitor)) {
+ clearComponents(directDataType);
+ }
+ directClassLength = getCompositeLength(directDataType);
+ }
+ if (getLayoutMode(layoutOptions) == OoComponentLayoutMode.SIMPLE) {
+ // Not using the dummy/direct type (only used it to get the
+ // directClassLength), so remove it and add the members to the main
+ // type instead.
+ directDataType.getDataTypeManager().remove(directDataType, monitor);
+ memberData.addAll(directClassPdbMembers);
+ //addLayoutPdbMembers(memberData, layoutMembers, monitor);
+ }
+ else {
+ ClassPdbMember directClassPdbMember =
+ new ClassPdbMember("", directDataType, false, 0, null);
+ memberData.add(directClassPdbMember);
+ hasDirect = true;
+ }
+ }
+
+ addVirtualBasesSpeculatively(directClassLength, memberData,
+ myAccumulatedVirtualBases, monitor);
+
+ break;
+ default:
+ throw new PdbException("Unhandled layout mode");
+ }
+
+ if (!DefaultCompositeMember.applyDataTypeMembers(composite, false, size, memberData,
+ msg -> Msg.warn(this, msg), monitor)) {
+ clearComponents(composite);
+ }
+ }
+
+ //----------------------------------------------------------------------------------------------
+ //----------------------------------------------------------------------------------------------
+ private void processBaseClassesRecursive(CppCompositeType cppType, boolean isDirect,
+ List myPdbMembers, List myAccumulatedDirectBases,
+ List myAccumulatedVirtualBases, int depth, TaskMonitor monitor)
+ throws PdbException, CancelledException {
+ depth++;
+ for (LayoutBaseClass base : cppType.getLayoutBaseClasses()) {
+ monitor.checkCanceled();
+ CppCompositeType baseComposite = base.getBaseClassType();
+ if (base instanceof DirectLayoutBaseClass) {
+ if (isDirect) {
+ if (alreadyAccumulatedByName(myAccumulatedDirectBases, base)) {
+ throw new PdbException(
+ "Direct base already seen: " + base.getBaseClassType().getName());
+ }
+ if (!baseComposite.isZeroSize()) {
+ Composite baseDataType = base.getDirectDataType();
+ int offset = ((DirectLayoutBaseClass) base).getOffset();
+ CategoryPath cn =
+ getBaseCategoryName("BaseClass_" + base.getBaseClassType().getName());
+ Member baseMember =
+ new Member("", baseDataType, false, null, offset, cn.toString());
+ addPdbMember(myPdbMembers, baseMember);
+ }
+ myAccumulatedDirectBases.add(base);
+ }
+ processBaseClassesRecursive(baseComposite, false, myPdbMembers,
+ myAccumulatedDirectBases, myAccumulatedVirtualBases, depth, monitor);
+ }
+ else if (base instanceof VirtualLayoutBaseClass) {
+ if (depth == 1) {
+ addPlaceholderVirtualBaseTableEntry(((VirtualLayoutBaseClass) base));
+ }
+ if (alreadyAccumulatedByName(myAccumulatedVirtualBases, base)) {
+ continue;
+ }
+ if (!baseComposite.isZeroSize()) {
+ processBaseClassesRecursive(baseComposite, false, myPdbMembers,
+ myAccumulatedDirectBases, myAccumulatedVirtualBases, depth, monitor);
+ }
+ myAccumulatedVirtualBases.add((VirtualLayoutBaseClass) base);
+ }
+ else {
+ throw new PdbException("Unknown base class type");
+ }
+ }
+ }
+
+ //----------------------------------------------------------------------------------------------
+ //----------------------------------------------------------------------------------------------
+ void addPlaceholderVirtualBaseTableEntry(VirtualLayoutBaseClass base) throws PdbException {
+ PlaceholderVirtualBaseTable table =
+ placeholderVirtualBaseTables.get(base.getBasePointerOffset());
+ if (table == null) {
+ table = new PlaceholderVirtualBaseTable();
+ placeholderVirtualBaseTables.put(base.getBasePointerOffset(), table);
+ }
+ PlaceholderVirtualBaseTableEntry entry =
+ table.getEntryByIndexInTable(base.getOffetFromVbt());
+ if (entry != null) {
+ throw new PdbException(
+ "Entry already exists at offset (" + base.getOffetFromVbt() + "): " + entry);
+ }
+ entry = new PlaceholderVirtualBaseTableEntry(base);
+ table.addEntry(base.getOffetFromVbt(), entry);
+ }
+
+ PlaceholderVirtualBaseTable getPlaceholderVirtualBaseTable(int basePointerOffset) {
+ return placeholderVirtualBaseTables.get(basePointerOffset);
+ }
+
+ Map getPlaceholderVirtualBaseTables() {
+ return placeholderVirtualBaseTables;
+ }
+
+ private boolean reconcileVirtualBaseTables(DataTypeManager dtm, VbtManager vbtManager)
+ throws PdbException {
+ if (placeholderVirtualBaseTables.size() > 1) {
+ // study this.
+ int a = 1;
+ a = a + 1;
+ }
+
+ boolean allVbtFound = true;
+ for (Entry tableEntry : placeholderVirtualBaseTables.entrySet()) {
+ int vbtptrOffset = tableEntry.getKey();
+ PlaceholderVirtualBaseTable table = tableEntry.getValue();
+ if (!table.validateOffset()) {
+ // TODO study this.
+ int a = 1;
+ a = a + 1;
+ }
+ DataType vbptr = getVbptrDataType(dtm, vbtManager, table);
+ allVbtFound &=
+ addOrUpdateVbtAndVbtptrMember(vbtManager, table, vbptr, vbtptrOffset, getName());
+ }
+ return allVbtFound;
+ }
+
+ private DataType getVbptrDataType(DataTypeManager dtm, VbtManager vbtManager,
+ PlaceholderVirtualBaseTable table) {
+ DataType vbptr = null;
+ for (int index = 1; index < table.getMaxOffset(); index++) {
+ PlaceholderVirtualBaseTableEntry entry = table.getEntryByIndexInTable(index);
+ vbptr = entry.getVirtualBaseClass().getVbptr();
+ if (vbptr != null) { // take first type... assuming all are the same
+ break;
+ }
+ }
+ if (vbptr == null) {
+ vbptr = vbtManager.getFallbackVbptr();
+ }
+ return vbptr;
+ }
+
+ private class CppCompositeAndMember {
+ private CppCompositeType cppType;
+ private Member member;
+
+ private CppCompositeAndMember(CppCompositeType cppType, Member member) {
+ this.cppType = cppType;
+ this.member = member;
+ }
+
+ private CppCompositeType getComposite() {
+ return cppType;
+ }
+
+ private Member getMember() {
+ return member;
+ }
+ }
+
+ private boolean addOrUpdateVbtAndVbtptrMember(VbtManager vbtManager,
+ PlaceholderVirtualBaseTable table, DataType vbptr, int vbtptrOffset, String myClass)
+ throws PdbException {
+
+ List subMangled = new ArrayList<>();
+ //subMangled.add(getMangledName());
+ CppCompositeAndMember cAndM = findDirectBaseCompositeAndMember(this, 0, vbtptrOffset);
+ if (cAndM == null) {
+ insertMember("{vbptr}", vbptr, false, vbtptrOffset, "{vbptr} for " + myClass);
+ }
+ else if (!"{vbptr}".equals(cAndM.getMember().getName())) {
+ String message = "PDB: Collision of non-{vbptr}.";
+ PdbLog.message(message);
+ Msg.info(this, message);
+ return false;
+ }
+ else {
+ CppCompositeType compositeThatContainsMember = cAndM.getComposite();
+ String mangled = compositeThatContainsMember.getMangledName();
+ subMangled.add(mangled);
+ }
+ if (!(vbtManager instanceof PdbVbtManager)) {
+ return false;
+ }
+ int entrySize = 4; // Default to something (could be wrong)
+ if (vbptr instanceof PointerDataType) {
+ entrySize = ((PointerDataType) vbptr).getDataType().getLength();
+ }
+
+ return findVbtBySymbolConstruction(table, (PdbVbtManager) vbtManager, entrySize,
+ getMangledName(), type, subMangled);
+ }
+
+ private boolean findVbtBySymbolConstruction(PlaceholderVirtualBaseTable table,
+ PdbVbtManager vbtm, int entrySize, String mangledCompositeTypeName, Type mainType,
+ List subMangledCompositeTypeNames) {
+ if (!validateMangledCompositeName(mangledCompositeTypeName, mainType)) {
+ return false;
+ }
+ for (String mangled : subMangledCompositeTypeNames) {
+ if (!validateMangledCompositeName(mangled, Type.UNKNOWN)) {
+ return false;
+ }
+ }
+ StringBuilder builder = new StringBuilder();
+ builder.append("??_8");
+ builder.append(mangledCompositeTypeName.substring(4));
+ builder.append("7B"); // Hope will always be 'B' ("const")
+ builder.append("@");
+ String possibleName = builder.toString();
+ if (findAndUpdate(table, vbtm, entrySize, possibleName)) {
+ return true;
+ }
+ for (String mangled : subMangledCompositeTypeNames) {
+ builder.deleteCharAt(builder.length() - 1);
+ builder.append(mangled.substring(4));
+ builder.append("@");
+ possibleName = builder.toString();
+ if (findAndUpdate(table, vbtm, entrySize, possibleName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ boolean findAndUpdate(PlaceholderVirtualBaseTable table, PdbVbtManager vbtm, int entrySize,
+ String mangledTableName) {
+ PdbVirtualBaseTable vbt = vbtm.createVirtualBaseTableByName(mangledTableName, entrySize);
+ if (vbt == null) {
+ return false;
+ }
+ table.setName(mangledTableName);
+ table.setVirtualBaseTable(vbt);
+ return true;
+ }
+
+ private CppCompositeAndMember findDirectBaseCompositeAndMember(CppCompositeType cppType,
+ int offsetCppType, int vbtptrOffset) throws PdbException {
+ for (LayoutBaseClass base : cppType.layoutBaseClasses) {
+ if (!(base instanceof DirectLayoutBaseClass)) {
+ continue;
+ }
+ DirectLayoutBaseClass directBase = (DirectLayoutBaseClass) base;
+ int directBaseOffset = directBase.getOffset() + offsetCppType;
+ int directBaseLength = directBase.getDirectDataType().getLength();
+ if (vbtptrOffset >= directBaseOffset &&
+ vbtptrOffset < directBaseOffset + directBaseLength) {
+ CppCompositeType childCppType = directBase.getBaseClassType();
+ CppCompositeAndMember cAndM =
+ findDirectBaseCompositeAndMember(childCppType, directBaseOffset, vbtptrOffset);
+ if (cAndM == null) {
+ Member member = childCppType.findLayoutMemberOrVftPtrMember(vbtptrOffset);
+ if (member == null) {
+ return null;
+ }
+ cAndM = new CppCompositeAndMember(childCppType, member);
+ }
+ return cAndM;
+ }
+ }
+ return null;
+ }
+
+ private Member findLayoutMemberOrVftPtrMember(int offset) {
+ for (Member member : layoutMembers) {
+ if (member.getOffset() == offset) {
+ return member;
+ }
+ }
+ for (Member member : layoutVftPtrMembers) {
+ if (member.getOffset() == offset) {
+ return member;
+ }
+ }
+ return null;
+ }
+
+ //----------------------------------------------------------------------------------------------
+ //----------------------------------------------------------------------------------------------
+// private void addVirtualBases(int startOffset, List pdbMembers,
+// List virtualBases, boolean allVbtFound, TaskMonitor monitor)
+// throws PdbException, CancelledException {
+// String accumulatedComment = "";
+// int memberOffset = startOffset;
+// for (VirtualLayoutBaseClass virtualBase : virtualBases) {
+// monitor.checkCanceled();
+// Composite baseDataType = virtualBase.getDirectDataType();
+// int virtualBaseLength = getCompositeLength(baseDataType);
+// PlaceholderVirtualBaseTable pvbt =
+// getPlaceholderVirtualBaseTable(virtualBase.getBasePointerOffset());
+// if (pvbt != null && pvbt.canLookupOffset()) {
+// long offset = pvbt.getOffset(virtualBase.getOffetFromVbt());
+// memberOffset = (int) (offset & 0xffffffffL);
+// }
+// if (virtualBaseLength != 0) {
+// String comment =
+// "(Virtual Base " + virtualBase.getDataTypePath().getDataTypeName() + ")";
+// accumulatedComment += comment;
+// ClassPdbMember virtualClassPdbMember =
+// new ClassPdbMember("", baseDataType, false, memberOffset, accumulatedComment);
+// pdbMembers.add(virtualClassPdbMember);
+// memberOffset += virtualBaseLength;
+// accumulatedComment = "";
+// }
+// else {
+// String comment = "((empty) Virtual Base " +
+// virtualBase.getDataTypePath().getDataTypeName() + ")";
+// accumulatedComment += comment;
+// }
+// // If last base is empty, then its comment and any accumulated to this point
+// // will not be seen (not applied to a PdbMember). TODO: Consider options,
+// // though we know we have left it in this state and are OK with it for now.
+// // We have not considered fall-out from this.
+// }
+// }
+
+ private void addVirtualBases(int startOffset, List pdbMembers,
+ List virtualBases, boolean allVbtFound, TaskMonitor monitor)
+ throws PdbException, CancelledException {
+ String accumulatedComment = "";
+ int memberOffset = startOffset;
+ List orderedBases = new ArrayList<>();
+ List offsets = new ArrayList<>();
+ if (!orderVirtualBases(orderedBases, offsets, virtualBases, monitor)) {
+ addVirtualBasesSpeculatively(startOffset, pdbMembers, virtualBases, monitor);
+ return;
+ }
+
+ for (int index = 0; index < offsets.size(); index++) {
+ monitor.checkCanceled();
+ VirtualLayoutBaseClass virtualBase = orderedBases.get(index);
+ memberOffset = offsets.get(index);
+ Composite baseDataType = virtualBase.getDirectDataType();
+ int virtualBaseLength = getCompositeLength(baseDataType);
+ int basePointerOffset = virtualBase.getBasePointerOffset();
+// PlaceholderVirtualBaseTable pvbt =
+// getPlaceholderVirtualBaseTable(virtualBase.getBasePointerOffset());
+// if (pvbt != null && pvbt.canLookupOffset()) {
+// long offset = pvbt.getOffset(virtualBase.getOffetFromVbt());
+// memberOffset = (int) (offset & 0xffffffffL);
+// }
+ memberOffset += basePointerOffset;
+ if (virtualBaseLength != 0) {
+ String comment =
+ "(Virtual Base " + virtualBase.getDataTypePath().getDataTypeName() + ")";
+ accumulatedComment += comment;
+ ClassPdbMember virtualClassPdbMember =
+ new ClassPdbMember("", baseDataType, false, memberOffset, accumulatedComment);
+ pdbMembers.add(virtualClassPdbMember);
+ memberOffset += virtualBaseLength;
+ accumulatedComment = "";
+ }
+ else {
+ String comment = "(Virtual Base (empty) " +
+ virtualBase.getDataTypePath().getDataTypeName() + ")";
+ accumulatedComment += comment;
+ }
+ // If last base is empty, then its comment and any accumulated to this point
+ // will not be seen (not applied to a PdbMember). TODO: Consider options,
+ // though we know we have left it in this state and are OK with it for now.
+ // We have not considered fall-out from this.
+ }
+ }
+
+ private boolean orderVirtualBases(List ordered, List offsets,
+ List unordered, TaskMonitor monitor)
+ throws PdbException, CancelledException {
+ for (VirtualLayoutBaseClass insertBase : unordered) {
+ monitor.checkCanceled();
+ PlaceholderVirtualBaseTable pvbt =
+ getPlaceholderVirtualBaseTable(insertBase.getBasePointerOffset());
+ if (pvbt == null || !pvbt.canLookupOffset()) {
+ return false;
+ }
+ long offset = pvbt.getOffset(insertBase.getOffetFromVbt());
+ int memberOffset = (int) (offset & 0xffffffffL);
+ int index;
+ for (index = 0; index < offsets.size(); index++) {
+ int existingOffset = offsets.get(index);
+ if (existingOffset > memberOffset) {
+ break;
+ }
+ }
+ ordered.add(index, insertBase);
+ offsets.add(index, memberOffset);
+ }
+ return true;
+ }
+
+ //----------------------------------------------------------------------------------------------
+ //----------------------------------------------------------------------------------------------
+ private void addVirtualBasesSpeculatively(int startOffset, List pdbMembers,
+ List virtualBases, TaskMonitor monitor)
+ throws CancelledException {
+ String accumulatedComment = "";
+ int memberOffset = startOffset;
+ for (VirtualLayoutBaseClass virtualBase : virtualBases) {
+ monitor.checkCanceled();
+ Composite baseDataType = virtualBase.getDirectDataType();
+ int virtualBaseLength = getCompositeLength(baseDataType);
+
+ if (virtualBaseLength != 0) {
+ String comment = "((Speculative Placement) Virtual Base " +
+ virtualBase.getDataTypePath().getDataTypeName() + ")";
+ accumulatedComment += comment;
+ ClassPdbMember virtualClassPdbMember =
+ new ClassPdbMember("", baseDataType, false, memberOffset, accumulatedComment);
+ pdbMembers.add(virtualClassPdbMember);
+ memberOffset += virtualBaseLength;
+ accumulatedComment = "";
+ }
+ else {
+ String comment = "((empty) (Speculative Placement) Virtual Base " +
+ virtualBase.getDataTypePath().getDataTypeName() + ")";
+ accumulatedComment += comment;
+ }
+ // If last base is empty, then its comment and any accumulated to this point
+ // will not be seen (not applied to a PdbMember). TODO: Consider options,
+ // though we know we have left it in this state and are OK with it for now.
+ // We have not considered fall-out from this.
+ }
+ }
+
+ //----------------------------------------------------------------------------------------------
+ //----------------------------------------------------------------------------------------------
+ private void addLayoutPdbMembers(List pdbMembers, List members) {
+ for (Member member : members) {
+ addPdbMember(pdbMembers, member);
+ }
+ }
+
+ void addPdbMember(List pdbMembers, Member member) {
+ ClassPdbMember classPdbMember = new ClassPdbMember(member.getName(), member.getDataType(),
+ member.isFlexibleArray(), member.getOffset(), null);
+ pdbMembers.add(classPdbMember);
+ }
+
+ void insertPdbMember(List pdbMembers, Member member) {
+ ClassPdbMember classPdbMember = new ClassPdbMember(member.getName(), member.getDataType(),
+ member.isFlexibleArray(), member.getOffset(), null);
+ int index = 0;
+ for (ClassPdbMember existingMember : pdbMembers) {
+ if (existingMember.getOffset() > member.getOffset()) {
+ break;
+ }
+ index++;
+ }
+ pdbMembers.add(index, classPdbMember);
+ }
+
+ private int getCompositeLength(Composite myComposite) {
+ if (!myComposite.isNotYetDefined()) {
+ return myComposite.getLength();
+ }
+ return 0;
+ }
+
+ private static boolean alreadyAccumulatedByName(List extends LayoutBaseClass> list,
+ LayoutBaseClass item) {
+ DataTypePath dtp = item.getDataTypePath();
+ for (LayoutBaseClass iterated : list) {
+ if (dtp.equals(iterated.getDataTypePath())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ CategoryPath getBaseCategoryName(String baseName) {
+ CategoryPath cn = getCategoryPath();
+ return new CategoryPath(cn, baseName);
+ }
+
+ // 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 abstract class BaseClass {
+ // In the future, if CppClassType is a formal DataType, then we want to be able to get
+ // an already-formed unique class from the DTM. There is no reason to have a base
+ // class duplicated as individually created components of DirectBaseClasses.
+ private CppCompositeType baseClassType;
+ private ClassFieldAttributes attributes;
+
+ private BaseClass(CppCompositeType baseClassType, ClassFieldAttributes attributes) {
+ this.baseClassType = baseClassType;
+ this.attributes = attributes;
+ }
+
+ CppCompositeType getBaseClassType() {
+ return baseClassType;
+ }
+
+ ClassFieldAttributes getAttributes() {
+ return attributes;
+ }
+
+ OoComponentLayoutMode getLayoutMode(CompositeLayoutMode layoutOptions) {
+ return baseClassType.getLayoutMode(layoutOptions);
+ }
+
+ DataTypePath getDataTypePath() {
+ return new DataTypePath(baseClassType.getCategoryPath().getParent(),
+ baseClassType.getCategoryPath().getName());
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append(attributes);
+ builder.append(baseClassType.getName());
+ return builder.toString();
+ }
+
+ Composite getDirectDataType() {
+ Composite c = getBaseClassType().getComposite();
+ if (c.getNumComponents() == 0) {
+ return c;
+ }
+ if (!baseClassType.hasDirect) {
+ return c;
+ }
+ DataTypeComponent dtc = c.getComponent(0); // by construction this should be "Direct"
+ DataType dt = dtc.getDataType();
+ Structure bdt;
+ if (!(dt instanceof Structure)) {
+ throw new AssertException("Not Structure for Direct");
+ }
+ bdt = (Structure) dt;
+ return bdt;
+ }
+
+ }
+
+ //----------------------------------------------------------------------------------------------
+ // Syntactic description of base classes.
+ //----------------------------------------------------------------------------------------------
+ private class SyntacticBaseClass extends BaseClass {
+ private SyntacticBaseClass(CppCompositeType baseClassType,
+ ClassFieldAttributes attributes) {
+ super(baseClassType, attributes);
+ }
+ }
+
+ private class DirectSyntacticBaseClass extends SyntacticBaseClass {
+ private DirectSyntacticBaseClass(CppCompositeType baseClassType,
+ ClassFieldAttributes attributes) {
+ super(baseClassType, attributes);
+ }
+ }
+
+ private class VirtualSyntacticBaseClass extends SyntacticBaseClass {
+ private VirtualSyntacticBaseClass(CppCompositeType baseClassType,
+ ClassFieldAttributes attributes) {
+ super(baseClassType, attributes);
+ }
+ }
+
+ //----------------------------------------------------------------------------------------------
+ // Layout description of base classes follow
+ //----------------------------------------------------------------------------------------------
+ private abstract class LayoutBaseClass extends BaseClass {
+ Structure layout = null;
+
+ LayoutBaseClass(CppCompositeType baseClassType, ClassFieldAttributes attributes) {
+ super(baseClassType, attributes);
+ }
+
+ void setLayout(Structure layout) {
+ this.layout = layout;
+ }
+
+ Structure getLayout() {
+ if (layout == null) {
+ int a = 1;
+ a = a + 1;
+ }
+ return layout;
+ }
+ }
+
+ private class DirectLayoutBaseClass extends LayoutBaseClass {
+ private int offset;
+
+ private DirectLayoutBaseClass(CppCompositeType baseClassType,
+ ClassFieldAttributes attributes, int offset) {
+ super(baseClassType, attributes);
+ this.offset = offset;
+ }
+
+ int getOffset() {
+ return offset;
+ }
+ }
+
+ private abstract class VirtualLayoutBaseClass extends LayoutBaseClass {
+ private int basePointerOffset;
+ private DataType vbptr;
+ private int offsetFromVbt;
+
+ private VirtualLayoutBaseClass(CppCompositeType baseClass, ClassFieldAttributes attributes,
+ int basePointerOffset, DataType vbptr, int offsetFromVbt) {
+ super(baseClass, attributes);
+ this.basePointerOffset = basePointerOffset;
+ this.vbptr = vbptr;
+ this.offsetFromVbt = offsetFromVbt;
+ }
+
+ DataType getVbptr() {
+ return vbptr;
+ }
+
+ int getOffetFromVbt() {
+ return offsetFromVbt;
+ }
+
+ int getBasePointerOffset() {
+ return basePointerOffset;
+ }
+ }
+
+ private class DirectVirtualLayoutBaseClass extends VirtualLayoutBaseClass {
+ private DirectVirtualLayoutBaseClass(CppCompositeType baseClass,
+ ClassFieldAttributes attributes, int basePointerOffset, DataType vbptr,
+ int offsetFromVbt) {
+ super(baseClass, attributes, basePointerOffset, vbptr, offsetFromVbt);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append(super.toString());
+ if (builder.length() > 0) {
+ builder.append(">");
+ builder.insert(0, "<");
+ }
+ return builder.toString();
+ }
+ }
+
+ private class IndirectVirtualLayoutBaseClass extends VirtualLayoutBaseClass {
+ private IndirectVirtualLayoutBaseClass(CppCompositeType baseClass,
+ ClassFieldAttributes attributes, int basePointerOffset, DataType vbptr,
+ int offsetFromVbt) {
+ super(baseClass, attributes, basePointerOffset, vbptr, offsetFromVbt);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append(super.toString());
+ if (builder.length() > 0) {
+ builder.append(">");
+ builder.insert(0, " 0 && memberName.length() > 0) {
+ builder.append(' ');
+ }
+ builder.append(memberName);
+ return builder.toString();
+ }
+
+ String getName() {
+ return memberName;
+ }
+
+ DataType getDataType() {
+ return dataType;
+ }
+
+ boolean isFlexibleArray() {
+ return isFlexibleArray;
+ }
+
+ ClassFieldAttributes getAttributes() {
+ return attributes;
+ }
+
+ void setComment(String comment) {
+ this.comment = comment;
+ }
+
+ String getComment() {
+ return comment;
+ }
+ }
+
+ private class StaticMember extends AbstractMember {
+ private StaticMember(String name, DataType dataType, ClassFieldAttributes attributes) {
+ super(name, dataType, false, attributes);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append(super.toString());
+ if (builder.length() > 0) {
+ builder.insert(0, "static ");
+ }
+ return builder.toString();
+ }
+ }
+
+ private class Member extends AbstractMember {
+ private int offset;
+
+ private Member(String name, DataType dataType, boolean isFlexibleArray,
+ ClassFieldAttributes attributes, int offset) {
+ this(name, dataType, isFlexibleArray, attributes, offset, null);
+ }
+
+ private Member(String name, DataType dataType, boolean isFlexibleArray,
+ ClassFieldAttributes attributes, int offset, String comment) {
+ super(name, dataType, isFlexibleArray, attributes, comment);
+ this.offset = offset;
+ }
+
+ int getOffset() {
+ return offset;
+ }
+
+ }
+
+ //----------------------------------------------------------------------------------------------
+
+ // Specific to PDB in name (Extends PdbMember... maybe should have more generic name)
+ private static class ClassPdbMember extends PdbMember {
+
+ private DataType dataType;
+ private boolean isFlexibleArray;
+
+ /**
+ * Class PDB member construction
+ * @param name member field name.
+ * @param dataType for the field.
+ * @param isFlexibleArray TODO
+ * @param offset member's byte offset within the root composite.
+ * @param comment comment for structure editor comment field; can be null.
+ */
+ ClassPdbMember(String name, DataType dataType, boolean isFlexibleArray, int offset,
+ String comment) {
+ super(name, dataType.getName(), offset, comment);
+ this.dataType = dataType;
+ this.isFlexibleArray = isFlexibleArray;
+ }
+
+ @Override
+ public String getDataTypeName() {
+ return dataType.getName();
+ }
+
+ @Override
+ protected WrappedDataType getDataType() throws CancelledException {
+ return new WrappedDataType(dataType, isFlexibleArray, false);
+ }
+ }
+
+ //----------------------------------------------------------------------------------------------
+ //----------------------------------------------------------------------------------------------
+ //----------------------------------------------------------------------------------------------
+ static class PlaceholderVirtualBaseTableEntry {
+ VirtualLayoutBaseClass virtualBaseClass;
+ int offsetInClass;
+
+ PlaceholderVirtualBaseTableEntry(VirtualLayoutBaseClass virtualBaseClass) {
+ this.virtualBaseClass = virtualBaseClass;
+ }
+
+ void setOffsetInClass(int offsetInClass) {
+ this.offsetInClass = offsetInClass;
+ }
+
+ int getOffsetInClass() {
+ return offsetInClass;
+ }
+
+ String getName() {
+ return virtualBaseClass.getBaseClassType().getName();
+ }
+
+ VirtualLayoutBaseClass getVirtualBaseClass() {
+ return virtualBaseClass;
+ }
+ }
+
+ //----------------------------------------------------------------------------------------------
+ static class PlaceholderVirtualBaseTable {
+ private String name;
+ private PdbVirtualBaseTable pdbVirtualBaseTable = null;
+
+ // We do not know if every index will be given. We can check after the fact, and once
+ // the set of sequential integers is assured, we could create a list.
+ private Map entriesByIndex;
+
+ PlaceholderVirtualBaseTable() {
+ this("");
+ }
+
+ PlaceholderVirtualBaseTable(String name) {
+ this.name = name;
+ entriesByIndex = new HashMap<>();
+ }
+
+ String getName() {
+ return name;
+ }
+
+ void setName(String name) {
+ this.name = name;
+ }
+
+ void setVirtualBaseTable(PdbVirtualBaseTable pdbVirtualBaseTable) {
+ this.pdbVirtualBaseTable = pdbVirtualBaseTable;
+ }
+
+ boolean canLookupOffset() {
+ return pdbVirtualBaseTable != null;
+ }
+
+ long getOffset(int ordinal) throws PdbException {
+ if (pdbVirtualBaseTable == null) {
+ throw new PdbException("pdbVirtualBaseTable not initialized");
+ }
+ return pdbVirtualBaseTable.getOffset(ordinal);
+ }
+
+ Map getEntries() {
+ return entriesByIndex;
+ }
+
+ int getMaxOffset() {
+ return entriesByIndex.size() + 1;
+ }
+
+ // TODO: maybe this should be called validateTableIndicies()
+ boolean validateOffset() {
+ int num = entriesByIndex.size() + 1; // assuming 0, plus 1-N)
+ for (int index : entriesByIndex.keySet()) {
+ if (index > num || index < 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ void addEntry(int indexInTable, PlaceholderVirtualBaseTableEntry entry) {
+ entriesByIndex.put(indexInTable, entry);
+ }
+
+ PlaceholderVirtualBaseTableEntry getEntryByIndexInTable(int indexInTable) {
+ return entriesByIndex.get(indexInTable);
+ }
+
+ PlaceholderVirtualBaseTableEntry getEntryByName(String nameParam) {
+ for (Entry entry : entriesByIndex.entrySet()) {
+ if (nameParam.equals(
+ entry.getValue().getVirtualBaseClass().getBaseClassType().getName())) {
+ return entry.getValue();
+ }
+ }
+ return null;
+ }
+ }
+
+ //----------------------------------------------------------------------------------------------
+ //----------------------------------------------------------------------------------------------
+ //----------------------------------------------------------------------------------------------
+ static class ClassFieldAttributes {
+ Access access;
+ Property property;
+
+ ClassFieldAttributes(Access access, Property property) {
+ this.access = access;
+ this.property = property;
+ }
+
+ private Access getAccess() {
+ return access;
+ }
+
+ private Property getProperty() {
+ return property;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ if (access.getValue() > Access.BLANK.getValue()) {
+ builder.append(access);
+ }
+ if (property.equals(Property.VIRTUAL)) {
+ builder.append(property);
+ }
+ return builder.toString();
+ }
+ }
+
+ //----------------------------------------------------------------------------------------------
+ static enum Type {
+ UNKNOWN("UNKNOWN_TYPE", -1),
+ BLANK("", 1),
+ CLASS("class", 2),
+ STRUCT("struct", 3),
+ UNION("union", 4);
+
+ private static final Map BY_VALUE = new HashMap<>();
+ static {
+ for (Type val : values()) {
+ BY_VALUE.put(val.value, val);
+ }
+ }
+ private final String label;
+ private final int value;
+
+ public String getString() {
+ return label;
+ }
+
+ @Override
+ public String toString() {
+ if (label.length() != 0) {
+ return label + " ";
+ }
+ return label;
+ }
+
+ public int getValue() {
+ return value;
+ }
+
+ public static Type fromValue(int val) {
+ return BY_VALUE.getOrDefault(val, UNKNOWN);
+ }
+
+ private Type(String label, int value) {
+ this.label = label;
+ this.value = value;
+ }
+ }
+
+ //----------------------------------------------------------------------------------------------
+ static enum Access {
+ UNKNOWN("UNKNOWN_ACCESS ", -1),
+ BLANK("", 0),
+ PUBLIC("public", 1),
+ PROTECTED("protected", 2),
+ PRIVATE("private", 3);
+
+ private static final Map BY_VALUE = new HashMap<>();
+ static {
+ for (Access val : values()) {
+ BY_VALUE.put(val.value, val);
+ }
+ }
+ private final String label;
+ private final int value;
+
+ public String getString() {
+ return label;
+ }
+
+ @Override
+ public String toString() {
+ if (label.length() != 0) {
+ return label + " ";
+ }
+ return label;
+ }
+
+ public int getValue() {
+ return value;
+ }
+
+ public static Access fromValue(int val) {
+ return BY_VALUE.getOrDefault(val, UNKNOWN);
+ }
+
+ private Access(String label, int value) {
+ this.label = label;
+ this.value = value;
+ }
+ }
+
+ //----------------------------------------------------------------------------------------------
+ static enum Property {
+ UNKNOWN("INVALID_PROPERTY", -1),
+ BLANK("", 0),
+ VIRTUAL("virtual ", 1),
+ STATIC("static ", 2),
+ FRIEND("friend ", 3);
+ // Also consider , , . See MSFT.
+
+ private static final Map BY_VALUE = new HashMap<>();
+ static {
+ for (Property val : values()) {
+ BY_VALUE.put(val.value, val);
+ }
+ }
+ private final String label;
+ private final int value;
+
+ public String getString() {
+ return label;
+ }
+
+ @Override
+ public String toString() {
+ if (label.length() != 0) {
+ return label + " ";
+ }
+ return label;
+ }
+
+ public int getValue() {
+ return value;
+ }
+
+ public static Property fromValue(int val) {
+ return BY_VALUE.getOrDefault(val, UNKNOWN);
+ }
+
+ private Property(String label, int value) {
+ this.label = label;
+ this.value = value;
+ }
+ }
+
+}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/DataSymbolApplier.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/DataSymbolApplier.java
new file mode 100644
index 0000000000..11523eb3ee
--- /dev/null
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/DataSymbolApplier.java
@@ -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);
+ }
+
+}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/Default2PdbMember.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/Default2PdbMember.java
new file mode 100644
index 0000000000..2172b51983
--- /dev/null
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/Default2PdbMember.java
@@ -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;
+
+/**
+ * PdbMember 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);
+ }
+
+}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/DefinedSingleAddressRangeSymbolApplier.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/DefinedSingleAddressRangeSymbolApplier.java
new file mode 100644
index 0000000000..981f05fdc8
--- /dev/null
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/DefinedSingleAddressRangeSymbolApplier.java
@@ -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();
+ }
+ }
+ }
+
+}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/EndSymbolApplier.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/EndSymbolApplier.java
new file mode 100644
index 0000000000..52516ff904
--- /dev/null
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/EndSymbolApplier.java
@@ -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();
+ }
+ }
+}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/EnumTypeApplier.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/EnumTypeApplier.java
new file mode 100644
index 0000000000..15702ff5b1
--- /dev/null
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/EnumTypeApplier.java
@@ -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 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());
+ }
+
+}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/EnumerateTypeApplier.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/EnumerateTypeApplier.java
new file mode 100644
index 0000000000..a6889993da
--- /dev/null
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/EnumerateTypeApplier.java
@@ -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;
+ }
+
+}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/FieldListTypeApplier.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/FieldListTypeApplier.java
new file mode 100644
index 0000000000..5089fbca3a
--- /dev/null
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/FieldListTypeApplier.java
@@ -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 baseClassList = new ArrayList<>();
+ private List memberList = new ArrayList<>();
+ private List 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 getBaseClassList() {
+ return baseClassList;
+ }
+
+ public List getMemberList() {
+ return memberList;
+ }
+
+ public List 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 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 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 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);
+ }
+ }
+
+}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/FrameAndProcedureInformationSymbolApplier.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/FrameAndProcedureInformationSymbolApplier.java
new file mode 100644
index 0000000000..c8edec85f8
--- /dev/null
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/FrameAndProcedureInformationSymbolApplier.java
@@ -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
+ }
+}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/FunctionSymbolApplier.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/FunctionSymbolApplier.java
new file mode 100644
index 0000000000..5963f89ad8
--- /dev/null
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/FunctionSymbolApplier.java
@@ -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 stackVariableAppliers = new ArrayList<>();
+
+ private List 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 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;
+ }
+
+ }
+}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/LabelSymbolApplier.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/LabelSymbolApplier.java
new file mode 100644
index 0000000000..8482dc6e55
--- /dev/null
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/LabelSymbolApplier.java
@@ -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
+ }
+}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/LocalOptimizedSymbolApplier.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/LocalOptimizedSymbolApplier.java
new file mode 100644
index 0000000000..d72c00b5ee
--- /dev/null
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/LocalOptimizedSymbolApplier.java
@@ -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);
+ }
+ }
+
+}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/MemberFunctionTypeApplier.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/MemberFunctionTypeApplier.java
new file mode 100644
index 0000000000..9c67bf3316
--- /dev/null
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/MemberFunctionTypeApplier.java
@@ -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);
+// }
+}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/MemberTypeApplier.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/MemberTypeApplier.java
new file mode 100644
index 0000000000..029a81ab2d
--- /dev/null
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/MemberTypeApplier.java
@@ -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;
+
+ }
+
+}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/ModifierTypeApplier.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/ModifierTypeApplier.java
new file mode 100644
index 0000000000..3d63599f1c
--- /dev/null
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/ModifierTypeApplier.java
@@ -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;
+ }
+}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/NestedTypeApplier.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/NestedTypeApplier.java
new file mode 100644
index 0000000000..6e57214090
--- /dev/null
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/NestedTypeApplier.java
@@ -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;
+ }
+}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/NoSymbolApplier.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/NoSymbolApplier.java
new file mode 100644
index 0000000000..1a4c0aee6b
--- /dev/null
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/NoSymbolApplier.java
@@ -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.
+ }
+
+}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/NoTypeApplier.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/NoTypeApplier.java
new file mode 100644
index 0000000000..0aea6bc6d9
--- /dev/null
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/NoTypeApplier.java
@@ -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("");
+ }
+
+}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/OoComponentLayoutMode.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/OoComponentLayoutMode.java
new file mode 100644
index 0000000000..0137e99b14
--- /dev/null
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/OoComponentLayoutMode.java
@@ -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
+}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbAddressManager.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbAddressManager.java
new file mode 100644
index 0000000000..e23bab1615
--- /dev/null
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbAddressManager.java
@@ -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 realAddressesBySection;
+ private List memoryGroupRefinement;
+ private List memorySectionRefinement;
+ private List allSegmentsInfo;
+ private Map 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 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 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.
+ *
+ * We have seen cases where blocks of the program are combined into a single block representing
+ * one segment in the PDB.
+ *
+ * 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 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 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 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;
+ }
+
+}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbApplicator.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbApplicator.java
new file mode 100644
index 0000000000..81151949fc
--- /dev/null
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbApplicator.java
@@ -0,0 +1,1328 @@
+/* ###
+ * 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.cmd.label.SetLabelPrimaryCmd;
+import ghidra.app.util.NamespaceUtils;
+import ghidra.app.util.SymbolPath;
+import ghidra.app.util.bin.format.pdb2.pdbreader.*;
+import ghidra.app.util.bin.format.pdb2.pdbreader.symbol.*;
+import ghidra.app.util.bin.format.pdb2.pdbreader.type.AbstractMsType;
+import ghidra.app.util.importer.MessageLog;
+import ghidra.app.util.pdb.PdbCategoryUtils;
+import ghidra.app.util.pdb.pdbapplicator.SymbolGroup.AbstractMsSymbolIterator;
+import ghidra.graph.*;
+import ghidra.graph.algo.GraphNavigator;
+import ghidra.graph.jung.JungDirectedGraph;
+import ghidra.program.model.address.Address;
+import ghidra.program.model.data.*;
+import ghidra.program.model.lang.Register;
+import ghidra.program.model.listing.*;
+import ghidra.program.model.symbol.*;
+import ghidra.util.Msg;
+import ghidra.util.exception.CancelledException;
+import ghidra.util.exception.InvalidInputException;
+import ghidra.util.task.CancelOnlyWrappingTaskMonitor;
+import ghidra.util.task.TaskMonitor;
+
+public class PdbApplicator {
+
+ /**
+ * Returns integer value of BigInteger or Integer.MAX_VALUE if does not fit.
+ * @param myApplicator PdbApplicator for which we are working.
+ * @param big BigInteger value to convert.
+ * @return the integer value.
+ */
+ public static long bigIntegerToLong(PdbApplicator myApplicator, BigInteger big) {
+ try {
+ return big.longValueExact();
+ }
+ catch (ArithmeticException e) {
+ String msg = "BigInteger value greater than max Long: " + big;
+ PdbLog.message(msg);
+ myApplicator.appendLogMsg(msg);
+ return Long.MAX_VALUE;
+ }
+ }
+
+ /**
+ * Returns integer value of BigInteger or Integer.MAX_VALUE if does not fit.
+ * @param myApplicator PdbApplicator for which we are working.
+ * @param big BigInteger value to convert.
+ * @return the integer value.
+ */
+ public static int bigIntegerToInt(PdbApplicator myApplicator, BigInteger big) {
+ try {
+ return big.intValueExact();
+ }
+ catch (ArithmeticException e) {
+ String msg = "BigInteger value greater than max Integer: " + big;
+ PdbLog.message(msg);
+ myApplicator.appendLogMsg(msg);
+ return Integer.MAX_VALUE;
+ }
+ }
+
+ //==============================================================================================
+ private String pdbFilename;
+ private AbstractPdb pdb;
+
+ private PdbApplicatorMetrics pdbApplicatorMetrics;
+
+ //==============================================================================================
+ private Program program;
+
+ private PdbApplicatorOptions applicatorOptions;
+ private MessageLog log;
+ private TaskMonitor monitor;
+ private CancelOnlyWrappingTaskMonitor cancelOnlyWrappingMonitor;
+
+ //==============================================================================================
+ private Address imageBase;
+ private DataTypeManager dataTypeManager;
+ private PdbAddressManager pdbAddressManager;
+ private List symbolGroups;
+
+ //==============================================================================================
+ // If we have symbols and memory with VBTs in them, then a better VbtManager is created.
+ VbtManager vbtManager;
+ PdbRegisterNameToProgramRegisterMapper registerNameToRegisterMapper;
+
+ //==============================================================================================
+ private int resolveCount;
+ private PdbCategoryUtils categoryUtils;
+ private PdbPrimitiveTypeApplicator pdbPrimitiveTypeApplicator;
+ private TypeApplierParser typeApplierParser;
+ private ComplexTypeApplierMapper complexApplierMapper;
+ private JungDirectedGraph> applierDependencyGraph;
+ /**
+ * This namespace map documents as follows:
+ *
+ * false = simple namespace
+ * true = class namespace
+ *
+ */
+ private Map isClassByNamespace;
+
+ //==============================================================================================
+ private SymbolApplierParser symbolApplierParser;
+
+ // Investigating... might change from String to AbstractSymbolApplier.
+ private Map> labelsByAddress;
+ // Investigations into source/line info
+ private Map> recordNumbersByFileName;
+ private Map> recordNumbersByModuleNumber;
+
+ //==============================================================================================
+ // TODO: eventually put access methods on AbstractPdb to get filename from it (deep down).
+ public PdbApplicator(String pdbFilename, AbstractPdb pdb) {
+ Objects.requireNonNull(pdbFilename, "pdbFilename cannot be null");
+ Objects.requireNonNull(pdb, "pdb cannot be null");
+ this.pdbFilename = pdbFilename;
+ this.pdb = pdb;
+ }
+
+ //==============================================================================================
+ //==============================================================================================
+ /**
+ * Applies the PDB to the {@link Program} or {@link DataTypeManager}. Either, but not both,
+ * can be null.
+ * @param programParam The {@link Program} to which to apply the PDB. Can be null in certain
+ * circumstances.
+ * @param dataTypeManagerParam The {@link DataTypeManager} to which to apply data types. Can be
+ * null in certain circumstances.
+ * @param imageBaseParam Address bases from which symbol addresses are based. If null, uses
+ * the image base of the program (both cannot be null).
+ * @param applicatorOptionsParam {@link PdbApplicatorOptions} used for applying the PDB.
+ * @param monitorParam TaskMonitor uses for watching progress and cancellation notices.
+ * @param logParam The MessageLog to which to output messages.
+ * @throws PdbException if there was a problem processing the data.
+ * @throws CancelledException Upon user cancellation
+ */
+ public void applyTo(Program programParam, DataTypeManager dataTypeManagerParam,
+ Address imageBaseParam, PdbApplicatorOptions applicatorOptionsParam,
+ TaskMonitor monitorParam, MessageLog logParam) throws PdbException, CancelledException {
+
+ initializeApplyTo(programParam, dataTypeManagerParam, imageBaseParam,
+ applicatorOptionsParam, monitorParam, logParam);
+
+ if (!applicatorOptions.applyPublicSymbolsOnly()) {
+ processTypes();
+ }
+
+ if (program != null && !applicatorOptions.applyDataTypesOnly()) {
+ if (applicatorOptions.applyPublicSymbolsOnly()) {
+ processPublicSymbols();
+ }
+ else {
+ processSymbols();
+ }
+ }
+
+ pdbAddressManager.logReport();
+
+ String applicatorMetrics = pdbApplicatorMetrics.getPostProcessingReport();
+ Msg.info(this, applicatorMetrics);
+ PdbLog.message(applicatorMetrics);
+ Msg.info(this, "PDB Terminated Normally");
+ }
+
+ //==============================================================================================
+ private void processTypes() throws CancelledException, PdbException {
+ setMonitorMessage("PDB: Applying to DTM " + dataTypeManager.getName() + "...");
+
+ PdbResearch.initCheckBreak(); // for developmental debug
+
+ resolveCount = 0;
+
+// PdbResearch.childWalk(this, monitor);
+
+// PdbResearch.studyDataTypeConflicts(this, monitor);
+// PdbResearch.studyCompositeFwdRefDef(pdb, monitor);
+// PdbResearch.study1(pdb, monitor);
+
+ complexApplierMapper.mapAppliers(monitor);
+
+ processSequentially();
+
+// dumpSourceFileRecordNumbers();
+
+// PdbResearch.developerDebugOrder(this, monitor);
+
+ processDeferred();
+
+ resolveSequentially();
+
+ Msg.info(this, "resolveCount: " + resolveCount);
+
+ // Currently, defining classes needs to have a program. When this is no longer true,
+ // then this call can be performed with the data types only work.
+ if (program != null) {
+ defineClasses();
+ }
+
+ // Process typedefs, which are in the symbols.
+ processGlobalTypdefSymbols();
+ }
+
+ //==============================================================================================
+ private void processSymbols() throws CancelledException, PdbException {
+// PdbResearch.studyAggregateSymbols(this, monitor);
+
+ // TODO: not sure if the following will be relevant here, elsewhere, or nowhere.
+// if (!pdbAddressManager.garnerSectionSegmentInformation()) {
+// return;
+// }
+
+ // Doing globals before publics, as publics are those that can have mangled names. By
+ // applying the non-mangled symbols first, we can get full type information from the
+ // underlying type. Then we can apply the mangled symbols and demangle them without
+ // affecting our ability to lay down PDB type information--any type information from
+ // the mangled symbols can happen afterward.
+ processGlobalSymbolsNoTypedefs();
+ processPublicSymbols();
+
+ // Seems that we shouldn't do the following, as it could be a buffer of invalid symbols
+ // that hadn't been gone through for garbage collection of sorts.
+ //processNonPublicOrGlobalSymbols();
+
+ // Seems that we shouldn't do the following, as the ones that are needed seem to be
+ // referenced from a global symbol using a ReferencedSymbol. If we process the module
+ // symbols, as below, then collisions can start to appear at addresses. This has
+ // happened to me when I forgot to clear the results from a previous build, in which
+ // case I could find symbols referring to a location within the old build and within
+ // the latest build at the particular address corresponding to its location within that
+ // build. So a module might have symbols that had not been garbage-collected.
+ processModuleSymbols();
+
+ // These are good to process (one particular module for the linker).
+// processLinkerSymbols();
+
+ // Get additional thunks (that old pdb analyzer got).
+ processThunkSymbolsFromNonLinkerModules();
+
+ //processAllSymbols();
+
+// dumpLabels();
+ }
+
+ //==============================================================================================
+ //==============================================================================================
+ private void initializeApplyTo(Program programParam, DataTypeManager dataTypeManagerParam,
+ Address imageBaseParam, PdbApplicatorOptions applicatorOptionsParam,
+ TaskMonitor monitorParam, MessageLog logParam) throws PdbException, CancelledException {
+
+ validateAndSetParameters(programParam, dataTypeManagerParam, imageBaseParam,
+ applicatorOptionsParam, monitorParam, logParam);
+
+ cancelOnlyWrappingMonitor = new CancelOnlyWrappingTaskMonitor(monitor);
+ pdbApplicatorMetrics = new PdbApplicatorMetrics();
+
+ pdbAddressManager = new PdbAddressManager(this, imageBase);
+ symbolGroups = createSymbolGroups();
+
+ categoryUtils = setPdbCatogoryUtils(pdbFilename);
+ pdbPrimitiveTypeApplicator = new PdbPrimitiveTypeApplicator(dataTypeManager);
+ typeApplierParser = new TypeApplierParser(this);
+ complexApplierMapper = new ComplexTypeApplierMapper(this);
+ applierDependencyGraph = new JungDirectedGraph<>();
+ isClassByNamespace = new TreeMap<>();
+ if (program != null) {
+ // Currently, this must happen after symbolGroups are created.
+ PdbVbtManager pdbVbtManager = new PdbVbtManager(this);
+ //pdbVbtManager.CreateVirtualBaseTables(); // Depends on symbolGroups
+ vbtManager = pdbVbtManager;
+ registerNameToRegisterMapper = new PdbRegisterNameToProgramRegisterMapper(program);
+ }
+ else {
+ vbtManager = new VbtManager(getDataTypeManager());
+ }
+
+ symbolApplierParser = new SymbolApplierParser(this);
+
+ // Investigations
+ labelsByAddress = new HashMap<>();
+ // Investigations into source/line info
+ recordNumbersByFileName = new HashMap<>();
+ recordNumbersByModuleNumber = new HashMap<>();
+ }
+
+ private void validateAndSetParameters(Program programParam,
+ DataTypeManager dataTypeManagerParam, Address imageBaseParam,
+ PdbApplicatorOptions applicatorOptionsParam, TaskMonitor monitorParam,
+ MessageLog logParam) throws PdbException {
+ if (programParam == null) {
+ if (dataTypeManagerParam == null) {
+ throw new PdbException(
+ "programParam and dataTypeManagerParam may not both be null.");
+ }
+ if (imageBaseParam == null) {
+ throw new PdbException("programParam and imageBaseParam may not both be null.");
+ }
+ }
+ applicatorOptions =
+ (applicatorOptionsParam != null) ? applicatorOptionsParam : new PdbApplicatorOptions();
+ if (applicatorOptions.applyDataTypesOnly() && applicatorOptions.applyPublicSymbolsOnly()) {
+ throw new PdbException("Cannot have both: applyDataTypesOnly, applyPublicSymbolOnly.");
+ }
+ monitor = (monitorParam != null) ? monitorParam : TaskMonitor.DUMMY;
+ log = (logParam != null) ? logParam : new MessageLog();
+ program = programParam;
+ dataTypeManager =
+ (dataTypeManagerParam != null) ? dataTypeManagerParam : program.getDataTypeManager();
+ imageBase = (imageBaseParam != null) ? imageBaseParam : program.getImageBase();
+ }
+
+ private List createSymbolGroups() throws CancelledException, PdbException {
+ List mySymbolGroups = new ArrayList<>();
+ int num = pdb.getDatabaseInterface().getNumModules();
+ // moduleNumber zero is our global/public group.
+ for (int moduleNumber = 0; moduleNumber <= num; moduleNumber++) {
+ monitor.checkCanceled();
+ Map symbols =
+ pdb.getDatabaseInterface().getModuleSymbolsByOffset(moduleNumber);
+ SymbolGroup symbolGroup = new SymbolGroup(symbols, moduleNumber);
+ mySymbolGroups.add(symbolGroup);
+ }
+ return mySymbolGroups;
+ }
+
+ //==============================================================================================
+ // Basic utility methods.
+ //==============================================================================================
+ /**
+ * Returns the {@link PdbApplicatorOptions} for this PdbApplicator.
+ * @return the {@link PdbApplicatorOptions} for this PdbApplicator.
+ */
+ PdbApplicatorOptions getPdbApplicatorOptions() {
+ return applicatorOptions;
+ }
+
+ /**
+ * Check to see if this monitor has been canceled
+ * @throws CancelledException if monitor has been cancelled
+ */
+ void checkCanceled() throws CancelledException {
+ monitor.checkCanceled();
+ }
+
+ /**
+ * Sets the message displayed on the task monitor
+ * @param message the message to display
+ */
+ void setMonitorMessage(String message) {
+ monitor.setMessage(message);
+ }
+
+ /**
+ * Sets the message displayed on the task monitor
+ * @param message the message to display
+ */
+ void appendLogMsg(String message) {
+ log.appendMsg(message);
+ }
+
+ /**
+ * Returns the {@link TaskMonitor} to available for this analyzer.
+ * @return the monitor.
+ */
+ TaskMonitor getMonitor() {
+ return monitor;
+ }
+
+ /**
+ * Returns the {@link CancelOnlyWrappingTaskMonitor} to available for this analyzer.
+ * @return the monitor.
+ */
+ TaskMonitor getCancelOnlyWrappingMonitor() {
+ return cancelOnlyWrappingMonitor;
+ }
+
+ /**
+ * Returns the {@link PdbApplicatorMetrics} being used for this applicator.
+ * @return the {@link PdbApplicatorMetrics}.
+ */
+ PdbApplicatorMetrics getPdbApplicatorMetrics() {
+ return pdbApplicatorMetrics;
+ }
+
+ /**
+ * Returns the {@link AbstractPdb} being analyzed.
+ * @return {@link AbstractPdb} being analyzed.
+ */
+ AbstractPdb getPdb() {
+ return pdb;
+ }
+
+ /**
+ * Returns the {@link Program} for which this analyzer is working.
+ * @return {@link Program} for which this analyzer is working.
+ */
+ Program getProgram() {
+ return program;
+ }
+
+ //==============================================================================================
+ // Information for a putative PdbTypeApplicator:
+
+ /**
+ * Returns the {@link DataTypeManager} associated with this analyzer.
+ * @return DataTypeManager which this analyzer is using.
+ */
+ DataTypeManager getDataTypeManager() {
+ return dataTypeManager;
+ }
+
+ // for PdbTypeApplicator (new)
+ DataOrganization getDataOrganization() {
+ return dataTypeManager.getDataOrganization();
+ }
+
+ PdbPrimitiveTypeApplicator getPdbPrimitiveTypeApplicator() {
+ return pdbPrimitiveTypeApplicator;
+ }
+
+ //==============================================================================================
+ // CategoryPath-related methods.
+ //==============================================================================================
+ /**
+ * 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.
+ */
+ CategoryPath getCategory(SymbolPath symbolPath) {
+ return categoryUtils.getCategory(symbolPath);
+ }
+
+ /**
+ * Returns the {@link CategoryPath} for a typedef with 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
+ */
+ CategoryPath getTypedefsCategory(int moduleNumber, SymbolPath symbolPath) {
+ return categoryUtils.getTypedefsCategory(moduleNumber, symbolPath);
+ }
+
+ /**
+ * Returns the {@link CategoryPath} for Anonymous Functions Category for the PDB.
+ * @return the {@link CategoryPath}
+ */
+ CategoryPath getAnonymousFunctionsCategory() {
+ return categoryUtils.getAnonymousFunctionsCategory();
+ }
+
+ /**
+ * Returns the {@link CategoryPath} for Anonymous Types Category for the PDB.
+ * @return the {@link CategoryPath}
+ */
+ public CategoryPath getAnonymousTypesCategory() {
+ return categoryUtils.getAnonymousTypesCategory();
+ }
+
+ /**
+ * 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.
+ */
+ String getNextAnonymousFunctionName() {
+ return categoryUtils.getNextAnonymousFunctionName();
+ }
+
+ /**
+ * 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.
+ */
+ void incrementNextAnonymousFunctionName() {
+ categoryUtils.incrementNextAnonymousFunctionName();
+ }
+
+ private PdbCategoryUtils setPdbCatogoryUtils(String pdbFilename)
+ throws CancelledException, PdbException {
+
+ List categoryNames = new ArrayList<>();
+ int num = pdb.getDatabaseInterface().getNumModules();
+ for (int index = 1; index <= num; index++) {
+ monitor.checkCanceled();
+ String moduleName =
+ pdb.getDatabaseInterface().getModuleInformation(index).getModuleName();
+ categoryNames.add(moduleName);
+ }
+
+ int index = pdbFilename.lastIndexOf("\\");
+ if (index == -1) {
+ index = pdbFilename.lastIndexOf("/");
+ }
+ return new PdbCategoryUtils(pdbFilename.substring(index + 1), categoryNames);
+ }
+
+ //==============================================================================================
+ // Applier-based-DataType-dependency-related methods.
+ //==============================================================================================
+ void addApplierDependency(AbstractMsTypeApplier depender) {
+ Objects.requireNonNull(depender);
+ applierDependencyGraph.addVertex(depender.getDependencyApplier());
+ }
+
+ void addApplierDependency(AbstractMsTypeApplier depender, AbstractMsTypeApplier dependee) {
+ Objects.requireNonNull(depender);
+ Objects.requireNonNull(dependee);
+ // TODO: Possibly do checks on dependee and depender types for actual creation
+ // of dependency--making this the one-stop-shop of this logic. Then make calls to
+ // this method from all possibly places. (Perhaps, for example, if depender is a
+ // pointer, then the logic would say "no.")
+ //
+ // Examples of where dependency should possibly be created (by not doing it, we are
+ // getting .conflict data types) include:
+ // structure or enum as a function return type or argument type.
+ //
+ applierDependencyGraph.addEdge(
+ new DefaultGEdge<>(depender.getDependencyApplier(), dependee.getDependencyApplier()));
+ }
+
+ List getVerticesInPostOrder() {
+ setMonitorMessage("PDB: Determining data type dependency order...");
+ return GraphAlgorithms.getVerticesInPostOrder(applierDependencyGraph,
+ GraphNavigator.topDownNavigator());
+ }
+
+ //==============================================================================================
+ //==============================================================================================
+ //==============================================================================================
+ AbstractMsTypeApplier getApplierSpec(RecordNumber recordNumber,
+ Class extends AbstractMsTypeApplier> expected) throws PdbException {
+ return typeApplierParser.getApplierSpec(recordNumber, expected);
+ }
+
+ AbstractMsTypeApplier getApplierOrNoTypeSpec(RecordNumber recordNumber,
+ Class extends AbstractMsTypeApplier> expected) throws PdbException {
+ return typeApplierParser.getApplierOrNoTypeSpec(recordNumber, expected);
+ }
+
+ AbstractMsTypeApplier getTypeApplier(RecordNumber recordNumber) {
+ return typeApplierParser.getTypeApplier(recordNumber);
+ }
+
+ AbstractMsTypeApplier getTypeApplier(AbstractMsType type) {
+ return typeApplierParser.getTypeApplier(type);
+ }
+
+ //==============================================================================================
+ //==============================================================================================
+ //==============================================================================================
+ public int findModuleNumberBySectionOffsetContribution(int section, long offset)
+ throws PdbException {
+ for (AbstractSectionContribution sectionContribution : pdb.getDatabaseInterface().getSectionContributionList()) {
+ int sectionContributionOffset = sectionContribution.getOffset();
+ int maxSectionContributionOffset =
+ sectionContributionOffset + sectionContribution.getLength();
+ if (offset >= sectionContributionOffset && offset < maxSectionContributionOffset) {
+ return sectionContribution.getModule();
+ }
+ }
+ throw new PdbException("Module not found for section/offset");
+ }
+
+ //==============================================================================================
+ private void processDataTypesSequentially() throws CancelledException, PdbException {
+ AbstractTypeProgramInterface tpi = pdb.getTypeProgramInterface();
+ int num = tpi.getTypeIndexMaxExclusive() - tpi.getTypeIndexMin();
+ monitor.initialize(num);
+ setMonitorMessage("PDB: Processing " + num + " data type components...");
+ for (int indexNumber =
+ tpi.getTypeIndexMin(); indexNumber < tpi.getTypeIndexMaxExclusive(); indexNumber++) {
+ monitor.checkCanceled();
+ PdbResearch.checkBreak(indexNumber);
+ AbstractMsTypeApplier applier =
+ getTypeApplier(RecordNumber.typeRecordNumber(indexNumber));
+ //PdbResearch.checkBreak(indexNumber, applier);
+ applier.apply();
+ monitor.incrementProgress(1);
+ }
+ }
+
+ //==============================================================================================
+ //==============================================================================================
+ //==============================================================================================
+ // Investigations into source/line info
+ void putRecordNumberByFileName(RecordNumber recordNumber, String filename) {
+ Set recordNumbers = recordNumbersByFileName.get(filename);
+ if (recordNumbers == null) {
+ recordNumbers = new HashSet<>();
+ recordNumbersByFileName.put(filename, recordNumbers);
+ }
+ recordNumbers.add(recordNumber);
+ }
+
+ //==============================================================================================
+ void putRecordNumberByModuleNumber(RecordNumber recordNumber, int moduleNumber) {
+ Set recordNumbers = recordNumbersByModuleNumber.get(moduleNumber);
+ if (recordNumbers == null) {
+ recordNumbers = new HashSet<>();
+ recordNumbersByModuleNumber.put(moduleNumber, recordNumbers);
+ }
+ recordNumbers.add(recordNumber);
+ }
+
+ //==============================================================================================
+ void dumpSourceFileRecordNumbers() {
+ PdbLog.message("RecordNumbersByFileName");
+ for (Map.Entry> entry : recordNumbersByFileName.entrySet()) {
+ String filename = entry.getKey();
+ PdbLog.message("FileName: " + filename);
+ for (RecordNumber recordNumber : entry.getValue()) {
+ AbstractMsType msType = pdb.getTypeRecord(recordNumber);
+ PdbLog.message(recordNumber.toString() + "\n" + msType);
+ }
+ }
+ PdbLog.message("RecordNumbersByModuleNumber");
+ for (Map.Entry> entry : recordNumbersByModuleNumber.entrySet()) {
+ int moduleNumber = entry.getKey();
+ PdbLog.message("ModuleNumber: " + moduleNumber);
+ for (RecordNumber recordNumber : entry.getValue()) {
+ AbstractMsType msType = pdb.getTypeRecord(recordNumber);
+ PdbLog.message(recordNumber.toString() + "\n" + msType);
+ }
+ }
+ }
+
+ //==============================================================================================
+ //==============================================================================================
+ //==============================================================================================
+ private void processItemTypesSequentially() throws CancelledException, PdbException {
+ AbstractTypeProgramInterface ipi = pdb.getItemProgramInterface();
+ int num = ipi.getTypeIndexMaxExclusive() - ipi.getTypeIndexMin();
+ monitor.initialize(num);
+ setMonitorMessage("PDB: Processing " + num + " item type components...");
+ for (int indexNumber = ipi.getTypeIndexMin(); indexNumber < num; indexNumber++) {
+ monitor.checkCanceled();
+ AbstractMsTypeApplier applier =
+ getTypeApplier(RecordNumber.itemRecordNumber(indexNumber));
+ applier.apply();
+ monitor.incrementProgress(1);
+ }
+ }
+
+ //==============================================================================================
+ private void processSequentially() throws CancelledException, PdbException {
+ processDataTypesSequentially();
+ processItemTypesSequentially();
+ }
+
+ //==============================================================================================
+ private void processDeferred() throws CancelledException, PdbException {
+ List verticesInPostOrder = getVerticesInPostOrder();
+ monitor.initialize(verticesInPostOrder.size());
+ setMonitorMessage("PDB: Processing " + verticesInPostOrder.size() +
+ " deferred data type dependencies...");
+ for (AbstractMsTypeApplier applier : verticesInPostOrder) {
+ monitor.checkCanceled();
+ PdbResearch.checkBreak(applier.index);
+ //checkBreak(applier.index, applier);
+ if (applier.isDeferred()) {
+ applier.deferredApply();
+ }
+ monitor.incrementProgress(1);
+ }
+ }
+
+ //==============================================================================================
+ private void resolveSequentially() throws CancelledException {
+ AbstractTypeProgramInterface tpi = pdb.getTypeProgramInterface();
+ int num = tpi.getTypeIndexMaxExclusive() - tpi.getTypeIndexMin();
+ monitor.initialize(num);
+ setMonitorMessage("PDB: Resolving " + num + " data type components...");
+ Date start = new Date();
+ long longStart = start.getTime();
+ for (int indexNumber =
+ tpi.getTypeIndexMin(); indexNumber < tpi.getTypeIndexMaxExclusive(); indexNumber++) {
+ monitor.checkCanceled();
+ PdbResearch.checkBreak(indexNumber);
+ AbstractMsTypeApplier applier =
+ getTypeApplier(RecordNumber.typeRecordNumber(indexNumber));
+ PdbResearch.checkBreak(indexNumber, applier);
+ applier.resolve();
+ monitor.incrementProgress(1);
+ }
+ Date stop = new Date();
+ long longStop = stop.getTime();
+ long timeDiff = longStop - longStart;
+ Msg.info(this, "Resolve time: " + timeDiff + " mS");
+ }
+
+ //==============================================================================================
+ //==============================================================================================
+ //==============================================================================================
+ DataType resolve(DataType dataType) {
+ if ("Cr_z_internal_state".equals(dataType.getName())) {
+ int a = 1;
+ a = a + 1;
+ }
+ if ("z_stream_s".equals(dataType.getName())) {
+ int a = 1;
+ a = a + 1;
+ }
+
+ DataType resolved = getDataTypeManager().resolve(dataType,
+ DataTypeConflictHandler.REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER);
+ resolveCount++;
+ return resolved;
+ }
+
+ //==============================================================================================
+ //==============================================================================================
+ //==============================================================================================
+
+ //==============================================================================================
+ // SymbolGroup-related methods.
+ //==============================================================================================
+ SymbolGroup getSymbolGroup() {
+ return getSymbolGroupForModule(0);
+ }
+
+ SymbolGroup getSymbolGroupForModule(int moduleNumber) {
+ return symbolGroups.get(moduleNumber);
+ }
+
+// public AbstractMsSymbol getSymbolForModuleAndOffset(int moduleNumber, long offset)
+// throws PdbException {
+// return pdb.getDatabaseInterface().getSymbolForModuleAndOffsetOfRecord(moduleNumber, offset);
+// }
+
+ //==============================================================================================
+ // Address-related methods.
+ //==============================================================================================
+ /**
+ * Returns the Address for the given section and offset.
+ * @param symbol The {@link AddressMsSymbol}
+ * @return The Address
+ */
+ Address reladdr(AddressMsSymbol symbol) {
+ return pdbAddressManager.reladdr(symbol);
+ }
+
+ /**
+ * Returns the Address for the given section and offset.
+ * @param segment The segment
+ * @param offset The offset
+ * @return The Address
+ */
+ Address reladdr(int segment, long offset) {
+ return pdbAddressManager.reladdr(segment, 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) {
+ pdbAddressManager.putRemapAddressByAddress(address, remapAddress);
+ }
+
+ /**
+ * 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) {
+ return pdbAddressManager.getRemapAddressByAddress(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) {
+ pdbAddressManager.putRealAddressesBySection(sectionNum, realAddress);
+ }
+
+ /**
+ * Method for callee to add a Memory Group symbol to the Memory Group list.
+ * @param symbol the symbol.
+ */
+ void addMemoryGroupRefinement(PeCoffGroupMsSymbol symbol) {
+ pdbAddressManager.addMemoryGroupRefinement(symbol);
+ }
+
+ /**
+ * Method for callee to add a Memory Section symbol to the Memory Section list.
+ * @param symbol the symbol.
+ */
+ void addMemorySectionRefinement(PeCoffSectionMsSymbol symbol) {
+ pdbAddressManager.addMemorySectionRefinement(symbol);
+ }
+
+ //==============================================================================================
+ // Virtual-Base-Table-related methods.
+ //==============================================================================================
+ VbtManager getVbtManager() {
+ return vbtManager;
+ }
+
+ //==============================================================================================
+ //
+ //==============================================================================================
+ Register getRegister(String pdbRegisterName) {
+ return registerNameToRegisterMapper.getRegister(pdbRegisterName);
+ }
+
+ //==============================================================================================
+ //==============================================================================================
+ @SuppressWarnings("unused") // for method not being called.
+ /**
+ * Process all symbols. User should not then call other methods:
+ * {@link #processGlobalSymbols()}, (@link #processPublicSymbols()}, and
+ * {@link #processNonPublicOrGlobalSymbols()}.
+ * @throws CancelledException upon user cancellation
+ * @throws PdbException upon issue processing the request
+ */
+ private void processAllSymbols() throws CancelledException, PdbException {
+ processMainSymbols();
+ processModuleSymbols();
+ }
+
+ //==============================================================================================
+ @SuppressWarnings("unused") // for method not being called.
+ private void processMainSymbols() throws CancelledException, PdbException {
+ // Get a count
+ SymbolGroup symbolGroup = getSymbolGroup();
+ int totalCount = symbolGroup.size();
+ setMonitorMessage("PDB: Applying " + totalCount + " main symbol components...");
+ monitor.initialize(totalCount);
+ AbstractMsSymbolIterator iter = symbolGroup.iterator();
+ processSymbolGroup(0, iter);
+ }
+
+ //==============================================================================================
+ private void processModuleSymbols() throws CancelledException {
+ int totalCount = 0;
+ int num = pdb.getDatabaseInterface().getNumModules();
+ for (int moduleNumber = 1; moduleNumber <= num; moduleNumber++) {
+ monitor.checkCanceled();
+ SymbolGroup symbolGroup = getSymbolGroupForModule(moduleNumber);
+ totalCount += symbolGroup.size();
+ }
+ setMonitorMessage("PDB: Applying " + totalCount + " module symbol components...");
+ monitor.initialize(totalCount);
+
+ // Process symbols list for each module
+ for (int moduleNumber = 1; moduleNumber <= num; moduleNumber++) {
+ monitor.checkCanceled();
+ // Process module symbols list
+ SymbolGroup symbolGroup = getSymbolGroupForModule(moduleNumber);
+ AbstractMsSymbolIterator iter = symbolGroup.iterator();
+ processSymbolGroup(moduleNumber, iter);
+// catelogSymbols(index, symbolGroup);
+ // do not call monitor.incrementProgress(1) here, as it is updated inside of
+ // processSymbolGroup.
+ }
+ }
+
+// private Set> moduleSymbols = new HashSet<>();
+//
+// private void catelogSymbols(int moduleNumber, SymbolGroup symbolGroup)
+// throws CancelledException {
+// symbolGroup.initGet();
+// while (symbolGroup.hasNext()) {
+// monitor.checkCanceled();
+// AbstractMsSymbol symbol = symbolGroup.peek();
+// moduleSymbols.add(symbol.getClass());
+// symbolGroup.next();
+// }
+// }
+//
+ //==============================================================================================
+ private void processSymbolGroup(int moduleNumber, AbstractMsSymbolIterator iter)
+ throws CancelledException {
+ iter.initGet();
+ while (iter.hasNext()) {
+ monitor.checkCanceled();
+ procSym(iter);
+ monitor.incrementProgress(1);
+ }
+ }
+
+ //==============================================================================================
+ /**
+ * Process public symbols. User should not then call {@link #processAllSymbols()}; but
+ * has these other methods available to supplement this one: {@link #processGlobalSymbolsNoTypedefs()}
+ * and {@link #processNonPublicOrGlobalSymbols()}.
+ * @throws CancelledException upon user cancellation
+ * @throws PdbException upon issue processing the request
+ */
+ private void processPublicSymbols() throws CancelledException, PdbException {
+
+ SymbolGroup symbolGroup = getSymbolGroup();
+
+ PublicSymbolInformation publicSymbolInformation =
+ pdb.getDatabaseInterface().getPublicSymbolInformation();
+ List offsets = publicSymbolInformation.getModifiedHashRecordSymbolOffsets();
+ setMonitorMessage("PDB: Applying " + offsets.size() + " public symbol components...");
+ monitor.initialize(offsets.size());
+
+ AbstractMsSymbolIterator iter = symbolGroup.iterator();
+ for (long offset : offsets) {
+ monitor.checkCanceled();
+ iter.initGetByOffset(offset);
+ if (!iter.hasNext()) {
+ break;
+ }
+ pdbApplicatorMetrics.witnessPublicSymbolType(iter.peek());
+ procSym(iter);
+ monitor.incrementProgress(1);
+ }
+ }
+
+ /**
+ * Process global symbols--no typedef. User should not then call {@link #processAllSymbols()};
+ * but has these other methods available to supplement this one: (@link #processPublicSymbols()}
+ * and {@link #processNonPublicOrGlobalSymbols()}.
+ * @throws CancelledException upon user cancellation
+ * @throws PdbException upon issue processing the request
+ */
+ private void processGlobalSymbolsNoTypedefs() throws CancelledException, PdbException {
+
+ SymbolGroup symbolGroup = getSymbolGroup();
+
+ GlobalSymbolInformation globalSymbolInformation =
+ pdb.getDatabaseInterface().getGlobalSymbolInformation();
+ List offsets = globalSymbolInformation.getModifiedHashRecordSymbolOffsets();
+ setMonitorMessage("PDB: Applying global 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();
+ pdbApplicatorMetrics.witnessGlobalSymbolType(symbol);
+ if (!(symbol instanceof AbstractUserDefinedTypeMsSymbol)) { // Not doing typedefs here
+ procSym(iter);
+ }
+ monitor.incrementProgress(1);
+ }
+ }
+
+ /**
+ * Process global typdef symbols.
+ * @throws CancelledException upon user cancellation
+ * @throws PdbException upon issue processing the request
+ */
+ private void processGlobalTypdefSymbols() throws CancelledException, PdbException {
+
+ SymbolGroup symbolGroup = getSymbolGroup();
+
+ GlobalSymbolInformation globalSymbolInformation =
+ pdb.getDatabaseInterface().getGlobalSymbolInformation();
+ List offsets = globalSymbolInformation.getModifiedHashRecordSymbolOffsets();
+ setMonitorMessage("PDB: Applying typedefs...");
+ 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 AbstractUserDefinedTypeMsSymbol) { // Doing typedefs here
+ procSym(iter);
+ }
+ monitor.incrementProgress(1);
+ }
+ }
+
+ /**
+ * Processing non-public, non-global symbols. User should not then call
+ * {@link #processAllSymbols()}; but has these other methods available to supplement this one:
+ * {@link #processGlobalSymbolsNoTypedefs()} and (@link #processPublicSymbols()}.
+ * @throws CancelledException upon user cancellation
+ * @throws PdbException upon issue processing the request
+ */
+ @SuppressWarnings("unused") // for method not being called.
+ private void processNonPublicOrGlobalSymbols() throws CancelledException, PdbException {
+ Set offsetsRemaining = getSymbolGroup().getOffsets();
+ for (long off : pdb.getDatabaseInterface().getPublicSymbolInformation().getModifiedHashRecordSymbolOffsets()) {
+ monitor.checkCanceled();
+ offsetsRemaining.remove(off);
+ }
+ for (long off : pdb.getDatabaseInterface().getGlobalSymbolInformation().getModifiedHashRecordSymbolOffsets()) {
+ monitor.checkCanceled();
+ offsetsRemaining.remove(off);
+ }
+
+ setMonitorMessage(
+ "PDB: Applying " + offsetsRemaining.size() + " other symbol components...");
+ monitor.initialize(offsetsRemaining.size());
+ //getCategoryUtils().setModuleTypedefsCategory(null);
+
+ SymbolGroup symbolGroup = getSymbolGroup();
+ AbstractMsSymbolIterator iter = symbolGroup.iterator();
+ for (long offset : offsetsRemaining) {
+ monitor.checkCanceled();
+ iter.initGetByOffset(offset);
+ AbstractMsSymbol symbol = iter.peek();
+ procSym(iter);
+ monitor.incrementProgress(1);
+ }
+ }
+
+ //==============================================================================================
+ private int findLinkerModuleNumber() {
+ if (pdb.getDatabaseInterface() != null) {
+ int num = 1;
+ for (AbstractModuleInformation module : pdb.getDatabaseInterface().getModuleInformationList()) {
+ if (isLinkerModule(module.getModuleName())) {
+ return num;
+ }
+ num++;
+ }
+ }
+ appendLogMsg("Not processing linker symbols because linker module not found");
+ return -1;
+ }
+
+ private boolean isLinkerModule(String name) {
+ return "* Linker *".equals(name);
+ }
+
+ //==============================================================================================
+ @SuppressWarnings("unused") // for method not being called.
+ private boolean processLinkerSymbols() throws CancelledException {
+
+ int linkerModuleNumber = findLinkerModuleNumber();
+ if (linkerModuleNumber == -1) {
+ return false;
+ }
+
+ SymbolGroup symbolGroup = getSymbolGroupForModule(linkerModuleNumber);
+ if (symbolGroup == null) {
+ Msg.info(this, "No symbols to process from linker module.");
+ return false;
+ }
+
+ setMonitorMessage("PDB: Applying " + symbolGroup.size() + " linker symbol components...");
+ monitor.initialize(symbolGroup.size());
+
+ AbstractMsSymbolIterator iter = symbolGroup.iterator();
+ while (iter.hasNext()) {
+ checkCanceled();
+ pdbApplicatorMetrics.witnessLinkerSymbolType(iter.peek());
+ procSym(iter);
+ monitor.incrementProgress(1);
+ }
+ return true;
+ }
+
+ //==============================================================================================
+ private void processThunkSymbolsFromNonLinkerModules() throws CancelledException {
+
+ int linkerModuleNumber = findLinkerModuleNumber();
+
+ int totalCount = 0;
+ int num = pdb.getDatabaseInterface().getNumModules();
+ for (int index = 1; index <= num; index++) {
+ monitor.checkCanceled();
+ if (index == linkerModuleNumber) {
+ continue;
+ }
+ SymbolGroup symbolGroup = getSymbolGroupForModule(index);
+ totalCount += symbolGroup.size();
+ }
+ setMonitorMessage("PDB: Processing module thunks...");
+ monitor.initialize(totalCount);
+
+ // Process symbols list for each module
+ for (int index = 1; index <= num; index++) {
+ monitor.checkCanceled();
+ if (index == linkerModuleNumber) {
+ continue;
+ }
+ SymbolGroup symbolGroup = getSymbolGroupForModule(index);
+ AbstractMsSymbolIterator iter = symbolGroup.iterator();
+ while (iter.hasNext()) {
+ monitor.checkCanceled();
+ AbstractMsSymbol symbol = iter.peek();
+ if (symbol instanceof AbstractThunkMsSymbol) {
+ procSym(iter);
+ }
+ else {
+ iter.next();
+ }
+ monitor.incrementProgress(1);
+ }
+ }
+
+ }
+
+ //==============================================================================================
+ //==============================================================================================
+ //==============================================================================================
+ AbstractMsSymbolApplier getSymbolApplier(AbstractMsSymbolIterator iter)
+ throws CancelledException, NoSuchElementException {
+ return symbolApplierParser.getSymbolApplier(iter);
+ }
+
+ //==============================================================================================
+ void procSym(AbstractMsSymbolIterator iter) throws CancelledException {
+ try {
+ AbstractMsSymbolApplier applier = getSymbolApplier(iter);
+ applier.apply();
+ }
+ catch (PdbException e) {
+ // skipping symbol
+ Msg.info(this, "Error applying symbol to program: " + e.toString());
+ }
+ }
+
+ //==============================================================================================
+ //==============================================================================================
+ //==============================================================================================
+ boolean isClass(SymbolPath path) {
+ return isClassByNamespace.get(path);
+ }
+
+ //==============================================================================================
+ void predefineClass(SymbolPath classPath) {
+ isClassByNamespace.put(classPath, true);
+ for (SymbolPath path = classPath.getParent(); path != null; path = path.getParent()) {
+ if (!isClassByNamespace.containsKey(path)) {
+ isClassByNamespace.put(path, false); // path is simple namespace
+ }
+ }
+ }
+
+ //==============================================================================================
+ private void defineClasses() throws CancelledException {
+ // create namespace and classes in an ordered fashion use tree map
+ monitor.initialize(isClassByNamespace.size());
+ setMonitorMessage("PDB: Defining classes...");
+ for (Map.Entry entry : isClassByNamespace.entrySet()) {
+ monitor.checkCanceled();
+ SymbolPath path = entry.getKey();
+ boolean isClass = entry.getValue();
+ Namespace parentNamespace =
+ NamespaceUtils.getNonFunctionNamespace(program, path.getParent());
+ if (parentNamespace == null) {
+ String type = isClass ? "class" : "namespace";
+ log.appendMsg("Error: failed to define " + type + ": " + path);
+ continue;
+ }
+ defineNamespace(parentNamespace, path.getName(), isClass);
+ monitor.incrementProgress(1);
+
+ }
+ }
+
+ //==============================================================================================
+ private void defineNamespace(Namespace parentNamespace, String name, boolean isClass) {
+
+ try {
+ SymbolTable symbolTable = program.getSymbolTable();
+ Namespace namespace = symbolTable.getNamespace(name, parentNamespace);
+ if (namespace != null) {
+ if (isClass) {
+ if (namespace instanceof GhidraClass) {
+ return;
+ }
+ if (isSimpleNamespaceSymbol(namespace)) {
+ NamespaceUtils.convertNamespaceToClass(namespace);
+ return;
+ }
+ }
+ else if (namespace.getSymbol().getSymbolType() == SymbolType.NAMESPACE) {
+ return;
+ }
+ log.appendMsg("Unable to create class namespace due to conflicting symbol: " +
+ namespace.getName(true));
+ }
+ else if (isClass) {
+ symbolTable.createClass(parentNamespace, name, SourceType.IMPORTED);
+ }
+ else {
+ symbolTable.createNameSpace(parentNamespace, name, SourceType.IMPORTED);
+ }
+ }
+ catch (Exception e) {
+ log.appendMsg("Unable to create class namespace: " + parentNamespace.getName(true) +
+ Namespace.DELIMITER + name + " due to exception: " + e.toString());
+ }
+ }
+
+ //==============================================================================================
+ private boolean isSimpleNamespaceSymbol(Namespace namespace) {
+ Symbol s = namespace.getSymbol();
+ if (s.getSymbolType() != SymbolType.NAMESPACE) {
+ return false;
+ }
+ Namespace n = namespace;
+ while (n != null) {
+ if (n instanceof Function) {
+ return false;
+ }
+ n = n.getParentNamespace();
+ }
+ return true;
+ }
+
+ //==============================================================================================
+ //==============================================================================================
+ //==============================================================================================
+ @SuppressWarnings("unused") // for method not being called.
+ private void storeLabelByAddress(Address address, String label) {
+ Set labels = labelsByAddress.get(address);
+ if (labels == null) {
+ labels = new TreeSet<>();
+ labelsByAddress.put(address, labels);
+ }
+ if (labels.contains(label)) {
+ // TODO investigate why we would see it again.
+ }
+ labels.add(label);
+ }
+
+ @SuppressWarnings("unused") // for method not being called.
+ private void dumpLabels() {
+ for (Map.Entry> entry : labelsByAddress.entrySet()) {
+ Address address = entry.getKey();
+ Set labels = entry.getValue();
+ System.out.println("\nAddress: " + address);
+ for (String label : labels) {
+ System.out.println(label);
+ }
+ }
+ }
+
+ //==============================================================================================
+ boolean shouldForcePrimarySymbol(Address address, boolean forceIfMangled) {
+ Symbol primarySymbol = program.getSymbolTable().getPrimarySymbol(address);
+ if (primarySymbol != null) {
+
+ if (primarySymbol.getName().startsWith("?") && forceIfMangled &&
+ applicatorOptions.allowDemotePrimaryMangledSymbol()) {
+ return true;
+ }
+
+ SourceType primarySymbolSource = primarySymbol.getSource();
+
+ if (!SourceType.ANALYSIS.isHigherPriorityThan(primarySymbolSource)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ //==============================================================================================
+ private static final String THUNK_NAME_PREFIX = "[thunk]:";
+
+ boolean createSymbol(Address address, String symbolPathString, boolean forcePrimary) {
+
+// storeLabelByAddress(address, symbolPathString);
+
+ try {
+ Namespace namespace = program.getGlobalNamespace();
+ if (symbolPathString.startsWith(THUNK_NAME_PREFIX)) {
+ symbolPathString = symbolPathString.substring(THUNK_NAME_PREFIX.length(),
+ symbolPathString.length());
+ }
+ SymbolPath symbolPath = new SymbolPath(symbolPathString);
+ symbolPath = symbolPath.replaceInvalidChars();
+ String name = symbolPath.getName();
+ String namespacePath = symbolPath.getParentPath();
+ if (namespacePath != null) {
+ namespace = NamespaceUtils.createNamespaceHierarchy(namespacePath, namespace,
+ program, address, SourceType.IMPORTED);
+ }
+
+ Symbol s = SymbolUtilities.createPreferredLabelOrFunctionSymbol(program, address,
+ namespace, name, SourceType.IMPORTED);
+ if (s != null && forcePrimary) {
+ // PDB contains both mangled, namespace names, and global names
+ // If mangled name does not remain primary it will not get demamgled
+ // and we may not get signature information applied
+ SetLabelPrimaryCmd cmd =
+ new SetLabelPrimaryCmd(address, s.getName(), s.getParentNamespace());
+ cmd.applyTo(program);
+ }
+ return true;
+ }
+ catch (InvalidInputException e) {
+ log.appendMsg("Unable to create symbol: " + e.getMessage());
+ }
+ return false;
+ }
+
+}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbApplicatorMetrics.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbApplicatorMetrics.java
new file mode 100644
index 0000000000..0296958d90
--- /dev/null
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbApplicatorMetrics.java
@@ -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 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 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> cannotApplyTypes = new HashSet<>();
+ private Set> unexpectedMemberFunctionThisPointerTypes =
+ new HashSet<>();
+ private Set> unexpectedMemberFunctionThisPointerUnderlyingTypes =
+ new HashSet<>();
+ private Set> unexpectedMemberFunctionContainerTypes =
+ new HashSet<>();
+ private Set> cannotApplySymbols = new HashSet<>();
+ private Set> unexpectedGlobalSymbols = new HashSet<>();
+ private Set> 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 "";
+ }
+
+}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbApplicatorOptions.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbApplicatorOptions.java
new file mode 100644
index 0000000000..102532a674
--- /dev/null
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbApplicatorOptions.java
@@ -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;
+ }
+
+}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbPrimitiveTypeApplicator.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbPrimitiveTypeApplicator.java
new file mode 100644
index 0000000000..b02ce78f43
--- /dev/null
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbPrimitiveTypeApplicator.java
@@ -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("", Undefined1DataType.dataType);
+
+ //==============================================================================================
+ private DataTypeManager dataTypeManager;
+
+ //==============================================================================================
+ private DataType voidGhidraPrimitive = null;
+ private DataType charGhidraPrimitive = null;
+ private DataType signedCharGhidraPrimitive = null;
+ private DataType unsignedCharGhidraPrimitive = null;
+
+ //private Map booleanGhidraPrimitives = new HashMap<>();
+ private Map integralGhidraPrimitives = new HashMap<>();
+ private Map unsignedIntegralGhidraPrimitives = new HashMap<>();
+ private Map floatGhidraPrimitives = new HashMap<>();
+ private Map complexGhidraPrimitives = new HashMap<>();
+
+ private Map 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);
+ }
+}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbRegisterNameToProgramRegisterMapper.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbRegisterNameToProgramRegisterMapper.java
new file mode 100644
index 0000000000..0e91814602
--- /dev/null
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbRegisterNameToProgramRegisterMapper.java
@@ -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 registerNameMap = new HashMap<>();
+ static {
+ //registerNameMap.put("rsp", "RSP");
+ registerNameMap.put("fbp", "RBP");
+ }
+
+ private Program program;
+ private Map 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;
+ }
+
+}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbResearch.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbResearch.java
new file mode 100644
index 0000000000..961db15a91
--- /dev/null
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbResearch.java
@@ -0,0 +1,1001 @@
+/* ###
+ * 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.pdb.*;
+import ghidra.app.util.bin.format.pdb2.pdbreader.*;
+import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
+import ghidra.app.util.bin.format.pdb2.pdbreader.symbol.*;
+import ghidra.app.util.bin.format.pdb2.pdbreader.type.*;
+import ghidra.app.util.pdb.pdbapplicator.SymbolGroup.AbstractMsSymbolIterator;
+import ghidra.program.model.address.Address;
+import ghidra.program.model.data.*;
+import ghidra.util.Msg;
+import ghidra.util.exception.CancelledException;
+import ghidra.util.task.TaskMonitor;
+import mdemangler.*;
+import mdemangler.object.MDObjectCPP;
+
+public class PdbResearch {
+
+ //==============================================================================================
+ private static Set debugIndexNumbers;
+
+ //==============================================================================================
+ static void initCheckBreak() {
+ debugIndexNumbers = new TreeSet<>();
+
+ debugIndexNumbers.add(12527);
+
+// debugIndexNumbers.add(180462);
+
+// debugIndexNumbers.add(4673); //flex array
+
+// debugIndexNumbers.add(4469);
+// debugIndexNumbers.add(4470);
+
+// debugIndexNumbers.add(11843); //chrome
+// debugIndexNumbers.add(12720); //chrome
+
+// debugIndexNumbers.add(4385);
+
+// debugIndexNumbers.add(4825);
+
+// debugIndexNumbers.add(4246);
+
+// debugIndexNumbers.add(4389);
+
+// debugIndexNumbers.add(4380);
+
+// debugIndexNumbers.add(4332);
+
+// debugIndexNumbers.add(11843);
+
+// debugIndexNumbers.add(2972763);
+// debugIndexNumbers.add(2972764);
+// debugIndexNumbers.add(2972766);
+// debugIndexNumbers.add(2972769);
+
+// debugIndexNumbers.add(87810);
+// debugIndexNumbers.add(218584);
+
+// debugIndexNumbers.add(4126);
+
+// debugIndexNumbers.add(2960288);
+
+// // nested classes.
+// debugIndexNumbers.add(7895);
+// debugIndexNumbers.add(7895);
+//
+// debugIndexNumbers.add(4549);
+
+ // chrome
+// debugIndexNumbers.add(9696);
+// debugIndexNumbers.add(9697);
+// debugIndexNumbers.add(9699);
+// debugIndexNumbers.add(9700);
+// debugIndexNumbers.add(9701);
+// debugIndexNumbers.add(9708);
+// debugIndexNumbers.add(9709);
+// debugIndexNumbers.add(216056);
+// debugIndexNumbers.add(216012);
+// debugIndexNumbers.add(216013);
+// debugIndexNumbers.add(216016);
+// debugIndexNumbers.add(216058);
+
+// // chrome: enum fwdref def
+// debugIndexNumbers.add(5809); //field list with enumerate
+
+// // chrome: enum fwdref def
+// debugIndexNumbers.add(9631);
+// debugIndexNumbers.add(30695);
+
+// // chrome: last fwdref ummatched
+// debugIndexNumbers.add(216055);
+
+// // chrome: enum namespace broken
+// debugIndexNumbers.add(17034);
+
+// // chrome: anon func and function pointer
+// debugIndexNumbers.add(197736);
+// debugIndexNumbers.add(197737);
+
+// // nt: composite
+// debugIndexNumbers.add(4499);
+// debugIndexNumbers.add(4760);
+
+// // chrome: anon func
+// debugIndexNumbers.add(6577);
+
+// // chrome: argument
+// debugIndexNumbers.add(42433);
+
+// // gray: tagLOCALETAB array
+// debugIndexNumbers.add(4887);
+
+// // gray: tagPARAMDESCEX and tagVARIANT
+// debugIndexNumbers.add(6033);
+// debugIndexNumbers.add(6037);
+// debugIndexNumbers.add(6039);
+// debugIndexNumbers.add(6075);
+
+// // gray: _IMAGE_OPTIONAL_HEADER64 and _IMAGE_DATA_DIRECTORY (array)
+// debugIndexNumbers.add(4974);
+// debugIndexNumbers.add(4975);
+// debugIndexNumbers.add(4977);
+// debugIndexNumbers.add(4982);
+// debugIndexNumbers.add(4992);
+
+// // gray: def before fwdref
+// debugIndexNumbers.add(4859);
+// debugIndexNumbers.add(4876);
+
+// debugIndexNumbers.add(5367);
+// // ********* NEXT SET **********
+// debugIndexNumbers.add(6660);
+// debugIndexNumbers.add(6835);
+// debugIndexNumbers.add(129037);
+
+// // ********* NEXT SET **********
+// debugIndexNumbers.add(236516);
+
+// // ********* NEXT SET **********
+// debugIndexNumbers.add(117107);
+// debugIndexNumbers.add(117125);
+
+ // ********* NEXT SET **********
+ //GRAY
+// debugIndexNumbers.add(4176);
+// debugIndexNumbers.add(4188);
+// debugIndexNumbers.add(4199);
+// debugIndexNumbers.add(4257);
+
+// // ********* NEXT SET **********
+// debugIndexNumbers.add(111583);
+// debugIndexNumbers.add(111585);
+
+// // ********* NEXT SET **********
+// debugIndexNumbers.add(195850);
+// debugIndexNumbers.add(195851);
+// debugIndexNumbers.add(195853);
+
+// // ********* NEXT SET **********
+// debugIndexNumbers.add(11180);
+// debugIndexNumbers.add(192618);
+// debugIndexNumbers.add(192619);
+// debugIndexNumbers.add(192620);
+
+// // ********* NEXT SET **********
+// debugIndexNumbers.add(4111);
+// debugIndexNumbers.add(42432);
+// debugIndexNumbers.add(42433);
+// debugIndexNumbers.add(29156);
+// debugIndexNumbers.add(29157);
+// debugIndexNumbers.add(29194);
+// debugIndexNumbers.add(214780);
+// debugIndexNumbers.add(237308);
+//
+// debugIndexNumbers.add(232902);
+
+// // ********* NEXT SET **********
+// debugIndexNumbers.add(5980);
+// debugIndexNumbers.add(6224);
+
+// // ********* NEXT SET **********
+// debugIndexNumbers.add(6331);
+
+// // ********* NEXT SET **********
+// debugIndexNumbers.add(81379);
+
+// // ********* NEXT SET **********
+// debugIndexNumbers.add(251910);
+
+// // ********* NEXT SET **********
+// debugIndexNumbers.add(33617);
+// debugIndexNumbers.add(33621);
+
+// // ********* NEXT SET **********
+// debugIndexNumbers.add(183821); //fwd ref
+// debugIndexNumbers.add(183828); //def 1
+// debugIndexNumbers.add(214748); //def 2
+// debugIndexNumbers.add(228395); //def 3
+// debugIndexNumbers.add(235802); //def 4
+
+// // ********* NEXT SET **********
+// debugIndexNumbers.add(4193);
+// debugIndexNumbers.add(4203);
+// debugIndexNumbers.add(4280);
+
+ // ********* NEXT SET **********
+// // big class
+// debugIndexNumbers.add(108379);
+//
+// debugIndexNumbers.add(105594); // fwdref is 105430
+//
+// debugIndexNumbers.add(105444);
+
+ // ********* NEXT SET **********
+// // big class
+// debugIndexNumbers.add(105212);
+//
+// // member 2 (def) of big class (fwdref 105103)
+// debugIndexNumbers.add(105444);
+//
+// // member 3 (def) of big class (fwdref 105104)
+// debugIndexNumbers.add(105594);
+//
+// // base classes of 105444 (field list 105443)
+// debugIndexNumbers.add(5251); // fwdref is 5072
+// debugIndexNumbers.add(105594); // fwdref is 105430
+//
+// // first and only member of 5251
+// debugIndexNumbers.add(5051); // fwdref is 4505
+
+ // ********* NEXT SET **********
+// // big class
+// debugIndexNumbers.add(7624); // fwdref is 7619
+//
+// debugIndexNumbers.add(6116); // fwdref is 5956
+//
+// debugIndexNumbers.add(6189); // fwdref is 6086
+//
+// // base class of 6189
+// debugIndexNumbers.add(6236); // fwdref is 6168
+//
+// // member of 6236
+// debugIndexNumbers.add(6251); // fwdref is 6220
+//
+// // member of 6251
+// debugIndexNumbers.add(6263); // fwdref is 6237
+//
+// // base class / member of 6263
+// debugIndexNumbers.add(6284); // fwdref is 6252
+//
+// // base class / member of 6284
+// debugIndexNumbers.add(6328); // fwdref is 6264
+//
+// // member of 6328
+// debugIndexNumbers.add(6335); // fwdref is 6285
+//
+// // base class of 6335
+// debugIndexNumbers.add(6341); // fwdref is 6329
+ }
+
+ /**
+ * Developmental method for breakpoints. TODO: will delete this from production.
+ * @param recordNumber tmp (set negative to ignore)
+ */
+ static void checkBreak(int recordNumber) {
+ if (debugIndexNumbers.contains(recordNumber)) {
+ int a = 1;
+ a = a + 1;
+ }
+ }
+
+ /**
+ * Developmental method for breakpoints. TODO: will delete this from production.
+ * @param recordNumber tmp (set negative to ignore)
+ * @param applier tmp
+ */
+ static void checkBreak(int recordNumber, AbstractMsTypeApplier applier) {
+
+ String nn = applier.getMsType().getName();
+ if ("std::__1::__map_value_compare,std::__1::__value_type,std::__1::basic_string >,std::__1::less,1>".equals(
+ nn)) {
+ int a = 1;
+ a = a + 1;
+ }
+ if ("class std::__1::__iostream_category".equals(nn)) {
+ int a = 1;
+ a = a + 1;
+ }
+ if ("std::__1::__do_message".equals(nn)) {
+ int a = 1;
+ a = a + 1;
+ }
+
+ //checkBreak(recordNumber);
+ }
+
+ //==============================================================================================
+ //==============================================================================================
+ private Set developerDebugOrderIndexNumbers;
+
+ private void initDeveloperOrderRecordNumbers() {
+ developerDebugOrderIndexNumbers = new TreeSet<>();
+
+ // big class
+ developerDebugOrderIndexNumbers.add(105212);
+
+ // member 2 (def) of big class (fwdref 105103)
+ developerDebugOrderIndexNumbers.add(105444);
+
+ // member 3 (def) of big class (fwdref 105104)
+ developerDebugOrderIndexNumbers.add(105594);
+
+ // base classes of 105444 (field list 105443)
+ developerDebugOrderIndexNumbers.add(5251); // fwdref is 5072
+ developerDebugOrderIndexNumbers.add(105594); // fwdref is 105430
+
+ // first and only member of 5251
+ developerDebugOrderIndexNumbers.add(5051); // fwdref is 4505
+ }
+
+ void developerDebugOrder(PdbApplicator applicator, TaskMonitor monitor)
+ throws CancelledException, PdbException {
+ initDeveloperOrderRecordNumbers();
+ for (int indexNumber : developerDebugOrderIndexNumbers) {
+ monitor.checkCanceled();
+ AbstractMsTypeApplier applier =
+ applicator.getTypeApplier(RecordNumber.typeRecordNumber(indexNumber));
+ applier.apply();
+ }
+
+ }
+
+ //==============================================================================================
+ //==============================================================================================
+ static void childWalk(PdbApplicator applicator, TaskMonitor monitor)
+ throws CancelledException, PdbException {
+ SymbolGroup symbolGroup = applicator.getSymbolGroup();
+ GlobalSymbolInformation globalSymbolInformation =
+ applicator.getPdb().getDatabaseInterface().getGlobalSymbolInformation();
+ List offsets = globalSymbolInformation.getModifiedHashRecordSymbolOffsets();
+ applicator.setMonitorMessage("PDB: Applying typedefs...");
+ monitor.initialize(offsets.size());
+
+ AbstractMsSymbolIterator iter = symbolGroup.iterator();
+ for (long offset : offsets) {
+ monitor.checkCanceled();
+ iter.initGetByOffset(offset);
+ if (!childWalkSym(applicator, symbolGroup.getModuleNumber(), iter)) {
+ break;
+ }
+ monitor.incrementProgress(1);
+ }
+ }
+
+ static private boolean childWalkSym(PdbApplicator applicator, int moduleNumber,
+ AbstractMsSymbolIterator iter) throws PdbException, CancelledException {
+ if (!iter.hasNext()) {
+ return false;
+ }
+ AbstractMsSymbol symbol = iter.peek(); //temporary during development
+ AbstractMsSymbolApplier applier = applicator.getSymbolApplier(iter);
+ if (applier instanceof TypedefSymbolApplier) {
+ TypedefSymbolApplier typedefApplier = (TypedefSymbolApplier) applier;
+ AbstractMsTypeApplier typeApplier =
+ applicator.getTypeApplier(typedefApplier.getTypeRecordNumber());
+ System.out.println("UDT " + typedefApplier.getName() + " depends on " +
+ typeApplier.getMsType().toString());
+// applier.apply();
+// procSym(symbolGroup);
+ }
+ else if (applier instanceof ReferenceSymbolApplier) {
+ ReferenceSymbolApplier refSymbolApplier = (ReferenceSymbolApplier) applier;
+ SymbolGroup refSymbolGroup = refSymbolApplier.getInitializedReferencedSymbolGroup();
+ // recursion
+ childWalkSym(applicator, refSymbolGroup.getModuleNumber(), refSymbolGroup.iterator());
+ }
+ else if (applier instanceof DataSymbolApplier) {
+ DataSymbolApplier dataSymbolApplier = (DataSymbolApplier) applier;
+ AbstractMsTypeApplier typeApplier = dataSymbolApplier.getTypeApplier();
+ childWalkType(moduleNumber, typeApplier);
+ }
+ else if (applier instanceof FunctionSymbolApplier) {
+ FunctionSymbolApplier functionSymbolApplier = (FunctionSymbolApplier) applier;
+ functionSymbolApplier.getFunction();
+// AbstractMsTypeApplier typeApplier = functionSymbolApplier.getTypeApplier();
+// childWalkType(symbolGroup.getModuleNumber(), typeApplier);
+ }
+// else if (applier instanceof ConstanctSymbolApplier) {
+// ConstantSymbolApplier constantSymbolApplier = (ConstantSymbolApplier) applier;
+// }
+ else {
+ int a = 1;
+ a = a + 1;
+ }
+ return true;
+ }
+
+ //==============================================================================================
+ static private boolean childWalkType(int moduleNumber, AbstractMsTypeApplier applier) {
+ int b = 1;
+ b = b + 1;
+ if (applier instanceof AbstractFunctionTypeApplier) {
+ int a = 1;
+ a = a + 1;
+ }
+ return true;
+ }
+
+ //==============================================================================================
+ //==============================================================================================
+ //==============================================================================================
+ static void studyDataTypeConflicts(PdbApplicator applicator, TaskMonitor monitor) {
+ DataTypeConflictHandler handler =
+ DataTypeConflictHandler.REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER;
+ DataTypeManager dtm = applicator.getDataTypeManager();
+
+ // First set
+ Composite testStruct1 = createComposite(dtm, "____blah____");
+ 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, "____internal____");
+ Pointer internalPointer1 = new PointerDataType(internalStruct1, -1, dtm);
+
+ fillComposite(testStruct1, monitor, internalPointer1);
+ fillComposite(internalStruct1, monitor, null);
+
+ // Second set
+ Composite testStruct2 = createComposite(dtm, "____blah____");
+ 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, "____internal____");
+ Pointer internalPointer2 = new PointerDataType(internalStruct2, -1, dtm);
+
+ fillComposite(testStruct2, monitor, internalPointer2);
+// fillComposite(internalStruct2, monitor, null);
+
+ // 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);
+
+ PdbLog.message(t1.toString());
+ PdbLog.message(f1.toString());
+ PdbLog.message(t2.toString());
+ PdbLog.message(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 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 -> reconstructionWarn(msg), monitor)) {
+ ((Structure) composite).deleteAll();
+ }
+ }
+ catch (Exception e) {
+ Msg.info(null, "Research exception thrown");
+ }
+ }
+
+ private static void reconstructionWarn(String msg) {
+ Msg.warn(null, msg);
+ }
+
+ 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);
+ }
+
+ }
+
+ //==============================================================================================
+ //==============================================================================================
+ /*
+ * Studying names of functions where they might involve function definition
+ * reuse caused by Templates and/or Identical Code Folding.
+ */
+ static void studyAggregateSymbols(PdbApplicator applicator, TaskMonitor monitor)
+ throws CancelledException {
+ Map> mapByAddress = new HashMap<>();
+ processPublicSymbols(applicator, mapByAddress, monitor);
+ processGlobalSymbols(applicator, mapByAddress, monitor);
+ processModuleSymbols(applicator, mapByAddress, monitor);
+ dumpMap(mapByAddress);
+ }
+
+ private static void processPublicSymbols(PdbApplicator applicator,
+ Map> map, TaskMonitor monitor) throws CancelledException {
+ AbstractPdb pdb = applicator.getPdb();
+ SymbolGroup symbolGroup = applicator.getSymbolGroup();
+
+ PublicSymbolInformation publicSymbolInformation =
+ pdb.getDatabaseInterface().getPublicSymbolInformation();
+ List offsets = publicSymbolInformation.getModifiedHashRecordSymbolOffsets();
+ applicator.setMonitorMessage(
+ "PDB: Applying " + offsets.size() + " public symbol components...");
+ 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();
+ //applicator.getPdbApplicatorMetrics().witnessPublicSymbolType(symbol);
+ processPublicSymbol(applicator, map, symbol);
+ processProcedureSymbol(applicator, map, symbol);
+ monitor.incrementProgress(1);
+ }
+ }
+
+ private static void processGlobalSymbols(PdbApplicator applicator,
+ Map> map, TaskMonitor monitor) throws CancelledException {
+ AbstractPdb pdb = applicator.getPdb();
+ SymbolGroup symbolGroup = applicator.getSymbolGroup();
+
+ GlobalSymbolInformation globalSymbolInformation =
+ pdb.getDatabaseInterface().getGlobalSymbolInformation();
+ List offsets = globalSymbolInformation.getModifiedHashRecordSymbolOffsets();
+ applicator.setMonitorMessage("PDB: Applying global 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();
+ //applicator.getPdbApplicatorMetrics().witnessGlobalSymbolType(symbol);
+ processPublicSymbol(applicator, map, symbol);
+ processProcedureSymbol(applicator, map, symbol);
+ monitor.incrementProgress(1);
+ }
+ }
+
+ private static void processModuleSymbols(PdbApplicator applicator,
+ Map> map, TaskMonitor monitor) throws CancelledException {
+ AbstractPdb pdb = applicator.getPdb();
+ int totalCount = 0;
+ int num = pdb.getDatabaseInterface().getNumModules();
+ for (int moduleNumber = 1; moduleNumber <= num; moduleNumber++) {
+ monitor.checkCanceled();
+ SymbolGroup symbolGroup = applicator.getSymbolGroupForModule(moduleNumber);
+ if (symbolGroup == null) {
+ continue;
+ }
+ totalCount += symbolGroup.size();
+ }
+ applicator.setMonitorMessage(
+ "PDB: Applying " + totalCount + " module symbol components...");
+ monitor.initialize(totalCount);
+
+ // Process symbols list for each module
+ for (int moduleNumber = 1; moduleNumber <= num; moduleNumber++) {
+ monitor.checkCanceled();
+
+// String moduleName =
+// pdb.getDatabaseInterface().getModuleInformation(index).getModuleName();
+
+ // Process module symbols list
+ SymbolGroup symbolGroup = applicator.getSymbolGroupForModule(moduleNumber);
+ if (symbolGroup == null) {
+ continue;
+ }
+ AbstractMsSymbolIterator iter = symbolGroup.iterator();
+ processSymbolGroup(applicator, map, moduleNumber, iter, monitor);
+ // do not call monitor.incrementProgress(1) here, as it is updated inside of
+ // processSymbolGroup.
+ }
+ }
+
+ private static void processSymbolGroup(PdbApplicator applicator, Map> map,
+ int moduleNumber, AbstractMsSymbolIterator iter, TaskMonitor monitor)
+ throws CancelledException {
+ iter.initGet();
+ while (iter.hasNext()) {
+ monitor.checkCanceled();
+ AbstractMsSymbol symbol = iter.next();
+ if (symbol != null) {
+ processPublicSymbol(applicator, map, symbol);
+ processProcedureSymbol(applicator, map, symbol);
+ }
+ monitor.incrementProgress(1);
+ }
+ }
+
+ private static void processPublicSymbol(PdbApplicator applicator, Map> map,
+ AbstractMsSymbol symbol) {
+ Stuff stuff;
+ if (!(symbol instanceof AbstractPublicMsSymbol)) {
+ return;
+ }
+ // If it can be known and determined to not be a function, then return.
+ if (symbol instanceof AbstractPublic32MsSymbol &&
+ !((AbstractPublic32MsSymbol) symbol).isFunction()) {
+ return;
+ }
+ Address address = applicator.reladdr((AbstractPublicMsSymbol) symbol);
+ String name = ((AbstractPublicMsSymbol) symbol).getName();
+ String demangledName = getDemangledQualifiedName(name);
+ if (demangledName != null) {
+ stuff = new Stuff(symbol, demangledName, What.PUBLIC_QUAL_FROM_MANGLED);
+ }
+ else {
+ stuff = new Stuff(symbol, name, What.PUBLIC_NOT_MANGLED);
+ }
+ addStuff(map, address, stuff);
+ }
+
+ private static void processProcedureSymbol(PdbApplicator applicator,
+ Map> map, AbstractMsSymbol symbol) {
+ if (symbol instanceof AbstractProcedureMsSymbol) {
+ Address address = applicator.reladdr((AbstractProcedureMsSymbol) symbol);
+ String name = ((AbstractProcedureMsSymbol) symbol).getName();
+ Stuff stuff = new Stuff(symbol, name, What.GLOBAL);
+ addStuff(map, address, stuff);
+ }
+ }
+
+ private static void addStuff(Map> map, Address address, Stuff stuff) {
+ List list = map.get(address);
+ if (list == null) {
+ list = new ArrayList<>();
+ map.put(address, list);
+ }
+ list.add(stuff);
+ }
+
+ private static void dumpMap(Map> map) {
+ for (Map.Entry> entry : map.entrySet()) {
+ Address address = entry.getKey();
+ List list = entry.getValue();
+ for (Stuff stuff : list) {
+ PdbLog.message(address + " " + stuff.getWhat() + " " + stuff.getName());
+ }
+ }
+ }
+
+ /**
+ * Gets a demangles the name and returns the qualified name. Returns null if not mangled
+ * or if error in demangling.
+ * @param mangledString the mangled string to be decoded
+ * @return the qualified name of the demangled string or null if not mangled or error.
+ */
+ private static String getDemangledQualifiedName(String mangledString) {
+ if (mangledString.charAt(0) != '?') {
+ return null;
+ }
+ MDMangGhidra demangler = new MDMangGhidra();
+ try {
+ MDParsableItem parsableItem = demangler.demangle(mangledString, true);
+ if (parsableItem instanceof MDObjectCPP) {
+ MDObjectCPP mdObject = (MDObjectCPP) parsableItem;
+ return mdObject.getQualifiedName().toString();
+ }
+ return parsableItem.toString();
+ }
+ catch (MDException e) {
+ // Couldn't demangle.
+ Msg.info(null, e.getMessage());
+ return null;
+ }
+ }
+
+ //----------------------------------------------------------------------------------------------
+ private static enum What {
+ PUBLIC_NOT_MANGLED, PUBLIC_QUAL_FROM_MANGLED, GLOBAL
+ }
+
+ private static class Stuff {
+ AbstractMsSymbol symbol;
+ String name;
+ What what;
+
+ Stuff(AbstractMsSymbol symbol, String name, What what) {
+ this.symbol = symbol;
+ this.name = name;
+ this.what = what;
+ }
+
+ AbstractMsSymbol getSymbol() {
+ return symbol;
+ }
+
+ String getName() {
+ return name;
+ }
+
+ What getWhat() {
+ return what;
+ }
+ }
+
+ //==============================================================================================
+ //==============================================================================================
+ static void studyCompositeFwdRefDef(AbstractPdb pdb, TaskMonitor monitor)
+ throws CancelledException {
+ int indexNumber = pdb.getTypeProgramInterface().getTypeIndexMin();
+ int indexLimit = pdb.getTypeProgramInterface().getTypeIndexMaxExclusive();
+ int num = indexLimit - indexNumber;
+ boolean covered[] = new boolean[indexLimit];
+
+ monitor.initialize(num);
+ monitor.setMessage("Study: NAMES_START " + (indexLimit - indexNumber));
+ PdbLog.message("STUDY_START: studyCompositeFwdRefDef");
+ //System.out.println("STUDY_START");
+ while (indexNumber < indexLimit) {
+ monitor.checkCanceled();
+ if (!covered[indexNumber]) {
+ AbstractMsType type = pdb.getTypeRecord(RecordNumber.typeRecordNumber(indexNumber));
+ covered[indexNumber] = true;
+ String name = getSpecialRecordStart(type);
+ if (name != null) {
+ MsProperty p;
+ String ps;
+ int c;
+ boolean isFwdRef = false;
+ Map> map = new HashMap<>();
+ if (type instanceof AbstractCompositeMsType) {
+ AbstractCompositeMsType compType = (AbstractCompositeMsType) type;
+ c = compType.getNumElements();
+ p = compType.getMsProperty();
+ isFwdRef = p.isForwardReference();
+ if (c == 0 && !isFwdRef) {
+ int a = 1;
+ a = a + 1;
+ // For PDBs that we have looked at, if count is zero
+ // for a fwdref, then the field list record number is zero;
+ // if count is zero for a def, then, the field list record
+ // number refers to an actual field list.
+ // So... seems we can trust fwdref and ignore count.
+ if (compType.getFieldDescriptorListRecordNumber() == RecordNumber.NO_TYPE) {
+ int b = 1;
+ b = b + 1;
+ }
+ }
+ else if (c != 0 && isFwdRef) {
+ int a = 1;
+ a = a + 1;
+ }
+// ps = c + (p.isForwardReference() ? "fwdref" : "");
+ }
+ else { //type instanceof AbstractEnumMsType
+ AbstractEnumMsType enumType = (AbstractEnumMsType) type;
+ c = 0; // made up.
+// p = enumType.getMsProperty();
+// ps = p.toString();
+ }
+ ps = type.toString();
+ Set set = new HashSet<>();
+ set.add(ps);
+ map.put(c, set);
+ PdbLog.message("----------\n" + name + "\n" + c + (isFwdRef ? " fwdref" : ""));
+ //System.out.println("----------\n" + name + "\n" + c);
+ int innerIndexNumber = indexNumber + 1;
+ while (innerIndexNumber < indexLimit) {
+ monitor.checkCanceled();
+ if (!covered[innerIndexNumber]) {
+ AbstractMsType innerType =
+ pdb.getTypeRecord(RecordNumber.typeRecordNumber(innerIndexNumber));
+ String innerName = getSpecialRecordStart(innerType);
+ if (name.equals(innerName)) {
+ covered[innerIndexNumber] = true;
+ isFwdRef = false;
+ if (type instanceof AbstractCompositeMsType) {
+ AbstractCompositeMsType compType =
+ (AbstractCompositeMsType) innerType;
+ c = compType.getNumElements();
+ p = compType.getMsProperty();
+ isFwdRef = p.isForwardReference();
+ if (c == 0 && !isFwdRef) {
+ // For PDBs that we have looked at, if count is zero
+ // for a fwdref, then the field list record number is zero;
+ // if count is zero for a def, then, the field list record
+ // number refers to an actual field list.
+ // So... seems we can trust fwdref and ignore count.
+ if (compType.getFieldDescriptorListRecordNumber() == RecordNumber.NO_TYPE) {
+ int a = 1;
+ a = a + 1;
+ }
+ }
+ else if (c != 0 && isFwdRef) {
+ int a = 1;
+ a = a + 1;
+ }
+// ps = c + (p.isForwardReference() ? "fwdref" : "");
+ }
+ else { //type instanceof AbstractEnumMsType
+ AbstractEnumMsType enumType = (AbstractEnumMsType) type;
+ c = 0; // made up.
+// p = enumType.getMsProperty();
+// ps = "" + c;
+ }
+ String psi = innerType.toString();
+ set = map.get(c);
+ String message = "";
+ if (set == null) {
+ set = new HashSet<>();
+ map.put(c, set);
+ set.add(psi);
+ }
+ else if (!set.contains(psi)) { // conflict
+ message = " <-- conflict";
+ set.add(psi);
+ }
+ else {
+ message = " <-- repeat";
+ }
+ PdbLog.message(c + (isFwdRef ? " fwdref" : "") + message);
+ if (c == 0 && isFwdRef) {
+ PdbLog.message("Orig: " + ps + "\nNew: " + psi);
+ }
+ //System.out.println(c + message);
+ }
+ }
+ innerIndexNumber++;
+ }
+ }
+
+ }
+ indexNumber++;
+ monitor.incrementProgress(1);
+ }
+ PdbLog.message("STUDY_END: studyCompositeFwdRefDef");
+ //System.out.println("STUDY_END");
+ }
+
+ private static String getSpecialRecordStart(AbstractMsType type) {
+ if (!((type instanceof AbstractCompositeMsType || type instanceof AbstractEnumMsType))) {
+ return null;
+ }
+
+ String name;
+ if (type instanceof AbstractCompositeMsType) {
+ AbstractCompositeMsType compType = (AbstractCompositeMsType) type;
+ if (compType instanceof AbstractClassMsType) {
+ name = "class " + compType.getName();
+ }
+ else if (compType instanceof AbstractStructureMsType) {
+ name = "struct " + compType.getName();
+ }
+ else if (compType instanceof AbstractUnionMsType) {
+ name = "union " + compType.getName();
+ }
+ else {
+ name = "interface " + compType.getName();
+ }
+ }
+ else { //type instanceof AbstractEnumMsType
+ AbstractEnumMsType enumType = (AbstractEnumMsType) type;
+ name = "enum " + enumType.getName();
+ }
+ return name;
+ }
+
+ //==============================================================================================
+ //==============================================================================================
+ static void study1(AbstractPdb pdb, TaskMonitor monitor) throws CancelledException {
+ int indexNumber = pdb.getTypeProgramInterface().getTypeIndexMin();
+ int indexLimit = pdb.getTypeProgramInterface().getTypeIndexMaxExclusive();
+
+ Set uniqueNames = new HashSet<>();
+ List orderedNames = new ArrayList<>();
+ List orderedRecords = new ArrayList<>();
+
+ monitor.initialize(indexLimit - indexNumber);
+ monitor.setMessage("Study: NAMES_START");
+ while (indexNumber < indexLimit) {
+ monitor.checkCanceled();
+ String name;
+ String record;
+ AbstractMsType type = pdb.getTypeRecord(RecordNumber.typeRecordNumber(indexNumber));
+ if (type instanceof AbstractCompositeMsType || type instanceof AbstractEnumMsType) {
+ if (type instanceof AbstractCompositeMsType) {
+ AbstractCompositeMsType compType = (AbstractCompositeMsType) type;
+ if (compType instanceof AbstractClassMsType) {
+ name = "class " + compType.getName();
+ }
+ else if (compType instanceof AbstractStructureMsType) {
+ name = "struct " + compType.getName();
+ }
+ else if (compType instanceof AbstractUnionMsType) {
+ name = "union " + compType.getName();
+ }
+ else {
+ name = "interface " + compType.getName();
+ }
+ record = compType.toString();
+ }
+ else { //type instanceof AbstractEnumMsType
+ AbstractEnumMsType enumType = (AbstractEnumMsType) type;
+ name = "enum " + enumType.getName();
+ record = enumType.toString();
+ }
+ if (!uniqueNames.contains(name)) {
+ uniqueNames.add(name);
+ orderedNames.add(name);
+ }
+ orderedRecords.add(record);
+ }
+ indexNumber++;
+ monitor.incrementProgress(1);
+ }
+
+ System.out.println("DUMP_START");
+ monitor.setMessage("Study: DUMP_START");
+ monitor.initialize(orderedNames.size());
+ for (String name : orderedNames) {
+ for (String record : orderedRecords) {
+ monitor.checkCanceled();
+ if (record.startsWith(name)) {
+ System.out.println(record);
+ }
+ }
+ System.out.println("----------");
+ monitor.incrementProgress(1);
+ }
+ System.out.println("DUMP_END");
+
+ }
+
+}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbVbtManager.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbVbtManager.java
new file mode 100644
index 0000000000..ced7569115
--- /dev/null
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PdbVbtManager.java
@@ -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 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 findVirtualBaseTableSymbols(PdbApplicator applicator)
+ throws CancelledException {
+
+ TaskMonitor monitor = applicator.getMonitor();
+ SymbolGroup symbolGroup = applicator.getSymbolGroup();
+ Map myAddressByMangledName = new HashMap<>();
+
+ PublicSymbolInformation publicSymbolInformation =
+ applicator.getPdb().getDatabaseInterface().getPublicSymbolInformation();
+ List 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 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 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;
+ }
+ }
+}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PeCoffGroupSymbolApplier.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PeCoffGroupSymbolApplier.java
new file mode 100644
index 0000000000..f0ec178c95
--- /dev/null
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PeCoffGroupSymbolApplier.java
@@ -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
+ }
+
+}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PeCoffSectionSymbolApplier.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PeCoffSectionSymbolApplier.java
new file mode 100644
index 0000000000..1f7f011ce9
--- /dev/null
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PeCoffSectionSymbolApplier.java
@@ -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
+ }
+}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PointerTypeApplier.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PointerTypeApplier.java
new file mode 100644
index 0000000000..a5944bb518
--- /dev/null
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PointerTypeApplier.java
@@ -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;
+ }
+
+}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PrimitiveTypeApplier.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PrimitiveTypeApplier.java
new file mode 100644
index 0000000000..9ea7c26dc9
--- /dev/null
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PrimitiveTypeApplier.java
@@ -0,0 +1,2079 @@
+/* ###
+ * 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;
+import ghidra.app.util.bin.format.pdb2.pdbreader.type.PrimitiveMsType;
+import ghidra.program.model.data.DataType;
+
+/**
+ * Applier for {@link PrimitiveMsType} types.
+ */
+public class PrimitiveTypeApplier extends AbstractMsTypeApplier {
+
+ private static AbstractMsType validateType(AbstractMsType type)
+ throws IllegalArgumentException {
+ if (!(type instanceof PrimitiveMsType)) {
+ throw new IllegalArgumentException(
+ "PDB Incorrectly applying " + type.getClass().getSimpleName() + " to " +
+ PrimitiveTypeApplier.class.getSimpleName());
+ }
+ return type;
+ }
+
+ /**
+ * Constructor for primitive type applier, for transforming a primitive into a
+ * Ghidra DataType.
+ * @param applicator {@link PdbApplicator} for which this class is working.
+ * @param msType {@link PrimitiveMsType} to process.
+ * @throws IllegalArgumentException Upon invalid arguments.
+ */
+ public PrimitiveTypeApplier(PdbApplicator applicator, AbstractMsType msType)
+ throws IllegalArgumentException {
+ super(applicator, validateType(msType));
+ apply(); // Only apply in constructor for primitives
+ }
+
+ @Override
+ public BigInteger getSize() {
+ return BigInteger.valueOf(((PrimitiveMsType) msType).getTypeSize());
+ }
+
+ @Override
+ public void apply() {
+ dataType = applyPrimitiveMsType((PrimitiveMsType) msType);
+ }
+
+ public boolean isNoType() {
+ return (((PrimitiveMsType) msType).getNumber() == 0);
+ }
+
+ private DataType applyPrimitiveMsType(PrimitiveMsType type) {
+ DataType primitiveDataType = null;
+// String name = type.getName();
+//
+// DataTypeManager applicatorDataTypeManager = applicator.getDataTypeManager();
+// DataTypeManager builtInDataTypeManager = BuiltInDataTypeManager.getDataTypeManager();
+// DataType dataType =
+// builtInDataTypeManager.getDataType(new DataTypePath(CategoryPath.ROOT, name));
+// if (dataType != null) {
+// return dataType.clone(applicatorDataTypeManager);
+// }
+
+ PdbPrimitiveTypeApplicator primitiveApplicator = applicator.getPdbPrimitiveTypeApplicator();
+
+ switch (type.getNumber()) {
+ //=======================================
+ // Special types
+ //=======================================
+ // No Type (uncharacterized type)
+ case 0x0000:
+ primitiveDataType = primitiveApplicator.getNoType(type);
+ break;
+ // Absolute symbol
+ case 0x0001:
+ primitiveDataType = primitiveApplicator.getNoType(type);
+ break;
+ // Segment type
+ case 0x0002:
+ primitiveDataType = primitiveApplicator.getNoType(type);
+ break;
+ // Void type
+ case 0x0003:
+ primitiveDataType = primitiveApplicator.getVoidType();
+ break;
+ // Near pointer to void
+ case 0x0103:
+ primitiveDataType = primitiveApplicator.get16NearPointerType(type,
+ primitiveApplicator.getVoidType());
+ break;
+ // Far pointer to void
+ case 0x0203:
+ primitiveDataType = primitiveApplicator.get1616FarPointerType(type,
+ primitiveApplicator.getVoidType());
+ break;
+ // Huge pointer to void
+ case 0x0303:
+ primitiveDataType = primitiveApplicator.get1616HugePointerType(type,
+ primitiveApplicator.getVoidType());
+ break;
+ // 32-bit pointer to void
+ case 0x0403:
+ primitiveDataType =
+ primitiveApplicator.get32PointerType(type, primitiveApplicator.getVoidType());
+ break;
+ // 16:32 pointer to void
+ case 0x0503:
+ primitiveDataType =
+ primitiveApplicator.get1632PointerType(type, primitiveApplicator.getVoidType());
+ break;
+ // 64-bit pointer to void
+ case 0x0603:
+ primitiveDataType =
+ primitiveApplicator.get64PointerType(type, primitiveApplicator.getVoidType());
+ break;
+ // 128-bit near pointer to void (LLVM doc on 0x0700)
+ case 0x0703:
+ primitiveDataType =
+ primitiveApplicator.get128PointerType(type, primitiveApplicator.getVoidType());
+ break;
+ // BASIC 8 byte currency value
+ case 0x0004:
+ primitiveDataType = primitiveApplicator.createTypedefNamedSizedType(type);
+ break;
+ // Near BASIC string
+ case 0x0005:
+ primitiveDataType = primitiveApplicator.createUnmappedPdbType(type);
+ break;
+ // Far BASIC string
+ case 0x0006:
+ primitiveDataType = primitiveApplicator.createUnmappedPdbType(type);
+ break;
+ // Type not translated by cvpack
+ case 0x0007:
+ primitiveDataType = primitiveApplicator.createUnmappedPdbType(type);
+ break;
+ // OLE/COM HRESULT
+ case 0x0008:
+ primitiveDataType = primitiveApplicator.createTypedefNamedSizedType(type);
+ break;
+ // OLE/COM HRESULT __ptr32 *
+ case 0x0408: // TODO: Make true pointer version
+ primitiveDataType = primitiveApplicator.createTypedefNamedSizedType(type);
+ break;
+ // OLE/COM HRESULT __ptr64 *
+ case 0x0608: // TODO: Make true pointer version
+ primitiveDataType = primitiveApplicator.createTypedefNamedSizedType(type);
+ break;
+ // OLE/COM HRESULT __ptr128 *
+ case 0x0708: // TODO: Make true pointer version
+ primitiveDataType = primitiveApplicator.createTypedefNamedSizedType(type);
+ break;
+ // bit
+ case 0x0060:
+ primitiveDataType = primitiveApplicator.createUnmappedPdbType(type);
+ break;
+ // Pascal CHAR
+ case 0x0061:
+ primitiveDataType = primitiveApplicator.createUnmappedPdbType(type);
+ break;
+ // 32-bit BOOL where true is 0xffffffff
+ case 0x0062:
+ primitiveDataType =
+ primitiveApplicator.createTypedefNamedSizedType("T_BOOL32FF", 4);
+ break;
+
+ //=======================================
+ // Signed Character types
+ //=======================================
+ // 8-bit signed
+ case 0x0010:
+ primitiveDataType = primitiveApplicator.getSignedCharType();
+ break;
+ // 16-bit pointer to an 8-bit signed
+ case 0x0110:
+ primitiveDataType = primitiveApplicator.get16NearPointerType(type,
+ primitiveApplicator.getSignedCharType());
+ break;
+ // 16:16 far pointer to an 8-bit signed
+ case 0x0210:
+ primitiveDataType = primitiveApplicator.get1616FarPointerType(type,
+ primitiveApplicator.getSignedCharType());
+ break;
+ // 16:16 huge pointer to an 8-bit signed
+ case 0x0310:
+ primitiveDataType = primitiveApplicator.get1616HugePointerType(type,
+ primitiveApplicator.getSignedCharType());
+ break;
+ // 32-bit pointer to an 8-bit signed
+ case 0x0410:
+ primitiveDataType = primitiveApplicator.get32PointerType(type,
+ primitiveApplicator.getSignedCharType());
+ break;
+ // 16:32 pointer to an 8-bit signed
+ case 0x0510:
+ primitiveDataType = primitiveApplicator.get1632PointerType(type,
+ primitiveApplicator.getSignedCharType());
+ break;
+ // 64-bit pointer to an 8-bit signed
+ case 0x0610:
+ primitiveDataType = primitiveApplicator.get64PointerType(type,
+ primitiveApplicator.getSignedCharType());
+ break;
+ // 128-bit near pointer to an 8-bit signed (LLVM doc on 0x0700)
+ case 0x0710:
+ primitiveDataType = primitiveApplicator.get128PointerType(type,
+ primitiveApplicator.getSignedCharType());
+ break;
+
+ //=======================================
+ // Unsigned Character types
+ //=======================================
+ // 8-bit unsigned
+ case 0x0020:
+ primitiveDataType = primitiveApplicator.getUnsignedCharType();
+ break;
+ // 16-bit pointer to an 8-bit unsigned
+ case 0x0120:
+ primitiveDataType = primitiveApplicator.get16NearPointerType(type,
+ primitiveApplicator.getUnsignedCharType());
+ break;
+ // 16:16 far pointer to an 8-bit unsigned
+ case 0x0220:
+ primitiveDataType = primitiveApplicator.get1616FarPointerType(type,
+ primitiveApplicator.getUnsignedCharType());
+ break;
+ // 16:16 huge pointer to an 8-bit unsigned
+ case 0x0320:
+ primitiveDataType = primitiveApplicator.get1616HugePointerType(type,
+ primitiveApplicator.getUnsignedCharType());
+ break;
+ // 32-bit pointer to an 8-bit unsigned
+ case 0x0420:
+ primitiveDataType = primitiveApplicator.get32PointerType(type,
+ primitiveApplicator.getUnsignedCharType());
+ break;
+ // 16:32 pointer to an 8-bit unsigned
+ case 0x0520:
+ primitiveDataType = primitiveApplicator.get1632PointerType(type,
+ primitiveApplicator.getUnsignedCharType());
+ break;
+ // 64-bit pointer to an 8-bit unsigned
+ case 0x0620:
+ primitiveDataType = primitiveApplicator.get64PointerType(type,
+ primitiveApplicator.getUnsignedCharType());
+ break;
+ // 128-bit near pointer to an 8-bit unsigned (LLVM doc on 0x0700)
+ case 0x0720:
+ primitiveDataType = primitiveApplicator.get128PointerType(type,
+ primitiveApplicator.getUnsignedCharType());
+ break;
+
+ //=======================================
+ // Real character types
+ //=======================================
+ // a real char
+ case 0x0070:
+ primitiveDataType = primitiveApplicator.getCharType();
+ break;
+ // 16-bit pointer to a real char
+ case 0x0170:
+ primitiveDataType = primitiveApplicator.get16NearPointerType(type,
+ primitiveApplicator.getCharType());
+ break;
+ // 16:16 far pointer to a real char
+ case 0x0270:
+ primitiveDataType = primitiveApplicator.get1616FarPointerType(type,
+ primitiveApplicator.getCharType());
+ break;
+ // 16:16 huge pointer to a real char
+ case 0x0370:
+ primitiveDataType = primitiveApplicator.get1616HugePointerType(type,
+ primitiveApplicator.getCharType());
+ break;
+ // 32-bit pointer to a real char
+ case 0x0470:
+ primitiveDataType =
+ primitiveApplicator.get32PointerType(type, primitiveApplicator.getCharType());
+ break;
+ // 16:32 pointer to a real char
+ case 0x0570:
+ primitiveDataType =
+ primitiveApplicator.get1632PointerType(type, primitiveApplicator.getCharType());
+ break;
+ // 64-bit pointer to a real char
+ case 0x0670:
+ primitiveDataType =
+ primitiveApplicator.get64PointerType(type, primitiveApplicator.getCharType());
+ break;
+ // 128-bit near pointer to a real char (LLVM doc on 0x0700)
+ case 0x0770:
+ primitiveDataType =
+ primitiveApplicator.get128PointerType(type, primitiveApplicator.getCharType());
+ break;
+
+ //=======================================
+ // Really a wide character types
+ //=======================================
+ // wide char
+ case 0x0071:
+ primitiveDataType = primitiveApplicator.getWideCharType();
+ break;
+ // 16-bit pointer to a wide char
+ case 0x0171:
+ primitiveDataType = primitiveApplicator.get16NearPointerType(type,
+ primitiveApplicator.getWideCharType());
+ break;
+ // 16:16 far pointer to a wide char
+ case 0x0271:
+ primitiveDataType = primitiveApplicator.get1616FarPointerType(type,
+ primitiveApplicator.getWideCharType());
+ break;
+ // 16:16 huge pointer to a wide char
+ case 0x0371:
+ primitiveDataType = primitiveApplicator.get1616HugePointerType(type,
+ primitiveApplicator.getWideCharType());
+ break;
+ // 32-bit pointer to a wide char
+ case 0x0471:
+ primitiveDataType = primitiveApplicator.get32PointerType(type,
+ primitiveApplicator.getWideCharType());
+ break;
+ // 16:32 pointer to a wide char
+ case 0x0571:
+ primitiveDataType = primitiveApplicator.get1632PointerType(type,
+ primitiveApplicator.getWideCharType());
+ break;
+ // 64-bit pointer to a wide char
+ case 0x0671:
+ primitiveDataType = primitiveApplicator.get64PointerType(type,
+ primitiveApplicator.getWideCharType());
+ break;
+ // 128-bit near pointer to a wide char (LLVM doc on 0x0700)
+ case 0x0771:
+ primitiveDataType = primitiveApplicator.get128PointerType(type,
+ primitiveApplicator.getWideCharType());
+ break;
+
+ //=======================================
+ // 16-bit char types
+ //=======================================
+ // 16-bit unicode char
+ case 0x007a:
+ primitiveDataType = primitiveApplicator.getUnicode16Type();
+ break;
+ // 16-bit pointer to a 16-bit unicode char
+ case 0x017a:
+ primitiveDataType = primitiveApplicator.get16NearPointerType(type,
+ primitiveApplicator.getUnicode16Type());
+ break;
+ // 16:16 far pointer to a 16-bit unicode char
+ case 0x027a:
+ primitiveDataType = primitiveApplicator.get1616HugePointerType(type,
+ primitiveApplicator.getUnicode16Type());
+ break;
+ // 16:16 huge pointer to a 16-bit unicode char
+ case 0x037a:
+ primitiveDataType = primitiveApplicator.get1616HugePointerType(type,
+ primitiveApplicator.getUnicode16Type());
+ break;
+ // 32-bit pointer to a 16-bit unicode char
+ case 0x047a:
+ primitiveDataType = primitiveApplicator.get32PointerType(type,
+ primitiveApplicator.getUnicode16Type());
+ break;
+ // 16:32 pointer to a 16-bit unicode char
+ case 0x057a:
+ primitiveDataType = primitiveApplicator.get1632PointerType(type,
+ primitiveApplicator.getUnicode16Type());
+ break;
+ // 64-bit pointer to a 16-bit unicode char
+ case 0x067a:
+ primitiveDataType = primitiveApplicator.get64PointerType(type,
+ primitiveApplicator.getUnicode16Type());
+ break;
+ // 128-bit near pointer to a 16-bit unicode char (LLVM doc on 0x0700)
+ case 0x077a:
+ primitiveDataType = primitiveApplicator.get128PointerType(type,
+ primitiveApplicator.getUnicode16Type());
+ break;
+
+ //=======================================
+ // 32-bit unicode char types
+ //=======================================
+ // 32-bit unicode char
+ case 0x007b:
+ primitiveDataType = primitiveApplicator.getUnicode32Type();
+ break;
+ // 16-bit pointer to a 32-bit unicode char
+ case 0x017b:
+ primitiveDataType = primitiveApplicator.get16NearPointerType(type,
+ primitiveApplicator.getUnicode32Type());
+ break;
+ // 16:16 far pointer to a 32-bit unicode char
+ case 0x027b:
+ primitiveDataType = primitiveApplicator.get1616FarPointerType(type,
+ primitiveApplicator.getUnicode32Type());
+ break;
+ // 16:16 huge pointer to a 32-bit unicode char
+ case 0x037b:
+ primitiveDataType = primitiveApplicator.get1616HugePointerType(type,
+ primitiveApplicator.getUnicode32Type());
+ break;
+ // 32-bit pointer to a 32-bit unicode char
+ case 0x047b:
+ primitiveDataType = primitiveApplicator.get32PointerType(type,
+ primitiveApplicator.getUnicode32Type());
+ break;
+ // 16:32 pointer to a 32-bit unicode char
+ case 0x057b:
+ primitiveDataType = primitiveApplicator.get1632PointerType(type,
+ primitiveApplicator.getUnicode32Type());
+ break;
+ // 64-bit pointer to a 32-bit unicode char
+ case 0x067b:
+ primitiveDataType = primitiveApplicator.get64PointerType(type,
+ primitiveApplicator.getUnicode32Type());
+ break;
+ // 128-bit near pointer to a 32-bit unicode char (LLVM doc on 0x0700)
+ case 0x077b:
+ primitiveDataType = primitiveApplicator.get128PointerType(type,
+ primitiveApplicator.getUnicode32Type());
+ break;
+
+ //=======================================
+ // 8-bit int types
+ //=======================================
+ // 8-bit int
+ case 0x0068:
+ primitiveDataType = primitiveApplicator.get8BitIntegerType();
+ break;
+ // 16-bit pointer to an 8-bit int
+ case 0x0168:
+ primitiveDataType = primitiveApplicator.get16NearPointerType(type,
+ primitiveApplicator.get8BitIntegerType());
+ break;
+ // 16:16 far pointer to an 8-bit int
+ case 0x0268:
+ primitiveDataType = primitiveApplicator.get1616FarPointerType(type,
+ primitiveApplicator.get8BitIntegerType());
+ break;
+ // 16:16 huge pointer to an 8-bit int
+ case 0x0368:
+ primitiveDataType = primitiveApplicator.get1616HugePointerType(type,
+ primitiveApplicator.get8BitIntegerType());
+ break;
+ // 32-bit pointer to an 8-bit int
+ case 0x0468:
+ primitiveDataType = primitiveApplicator.get32PointerType(type,
+ primitiveApplicator.get8BitIntegerType());
+ break;
+ // 16:32 pointer to an 8-bit int
+ case 0x0568:
+ primitiveDataType = primitiveApplicator.get1632PointerType(type,
+ primitiveApplicator.get8BitIntegerType());
+ break;
+ // 64-bit pointer to an 8-bit int
+ case 0x0668:
+ primitiveDataType = primitiveApplicator.get64PointerType(type,
+ primitiveApplicator.get8BitIntegerType());
+ break;
+ // 128-bit near pointer to an 8-bit int (LLVM doc on 0x0700)
+ case 0x0768:
+ primitiveDataType = primitiveApplicator.get128PointerType(type,
+ primitiveApplicator.get8BitIntegerType());
+ break;
+
+ //=======================================
+ // 8-bit unsigned int types
+ //=======================================
+ // 8-bit unsigned int
+ case 0x0069:
+ primitiveDataType = primitiveApplicator.get8BitUnsignedIntegerType();
+ break;
+ // 16-bit pointer to an 8-bit unsigned int
+ case 0x0169:
+ primitiveDataType = primitiveApplicator.get16NearPointerType(type,
+ primitiveApplicator.get8BitUnsignedIntegerType());
+ break;
+ // 16:16 far pointer to an 8-bit unsigned int
+ case 0x0269:
+ primitiveDataType = primitiveApplicator.get1616FarPointerType(type,
+ primitiveApplicator.get8BitUnsignedIntegerType());
+ break;
+ // 16:16 huge pointer to an 8-bit unsigned int
+ case 0x0369:
+ primitiveDataType = primitiveApplicator.get1616HugePointerType(type,
+ primitiveApplicator.get8BitUnsignedIntegerType());
+ break;
+ // 32-bit pointer to an 8-bit unsigned int
+ case 0x0469:
+ primitiveDataType = primitiveApplicator.get32PointerType(type,
+ primitiveApplicator.get8BitUnsignedIntegerType());
+ break;
+ // 16:32 pointer to an 8-bit unsigned int
+ case 0x0569:
+ primitiveDataType = primitiveApplicator.get1632PointerType(type,
+ primitiveApplicator.get8BitUnsignedIntegerType());
+ break;
+ // 64-bit pointer to an 8-bit unsigned int
+ case 0x0669:
+ primitiveDataType = primitiveApplicator.get64PointerType(type,
+ primitiveApplicator.get8BitUnsignedIntegerType());
+ break;
+ // 128-bit near pointer to an 8-bit unsigned int (LLVM doc on 0x0700)
+ case 0x0769:
+ primitiveDataType = primitiveApplicator.get128PointerType(type,
+ primitiveApplicator.get8BitUnsignedIntegerType());
+ break;
+
+ //=======================================
+ // 16-bit short types
+ //=======================================
+ // 16-bit signed short
+ case 0x0011:
+ primitiveDataType = primitiveApplicator.get16BitShortType();
+ break;
+ // 16-bit pointer to a 16-bit signed short
+ case 0x0111:
+ primitiveDataType = primitiveApplicator.get16NearPointerType(type,
+ primitiveApplicator.get16BitShortType());
+ break;
+ // 16:16 far pointer to a 16-bit signed short
+ case 0x0211:
+ primitiveDataType = primitiveApplicator.get1616FarPointerType(type,
+ primitiveApplicator.get16BitShortType());
+ break;
+ // 16:16 huge pointer to a 16-bit signed short
+ case 0x0311:
+ primitiveDataType = primitiveApplicator.get1616HugePointerType(type,
+ primitiveApplicator.get16BitShortType());
+ break;
+ // 32-bit pointer to a 16-bit signed short
+ case 0x0411:
+ primitiveDataType = primitiveApplicator.get32PointerType(type,
+ primitiveApplicator.get16BitShortType());
+ break;
+ // 16:32 pointer to a 16-bit signed short
+ case 0x0511:
+ primitiveDataType = primitiveApplicator.get1632PointerType(type,
+ primitiveApplicator.get16BitShortType());
+ break;
+ // 64-bit pointer to a 16-bit signed short
+ case 0x0611:
+ primitiveDataType = primitiveApplicator.get64PointerType(type,
+ primitiveApplicator.get16BitShortType());
+ break;
+ // 128-bit near pointer to a 16-bit signed short (LLVM doc on 0x0700)
+ case 0x0711:
+ primitiveDataType = primitiveApplicator.get128PointerType(type,
+ primitiveApplicator.get16BitShortType());
+ break;
+
+ //=======================================
+ // 16-bit unsigned short types
+ //=======================================
+ // 16-bit unsigned signed short
+ case 0x0021:
+ primitiveDataType = primitiveApplicator.get16BitUnsignedShortType();
+ break;
+ // 16-bit pointer to a 16-bit unsigned signed short
+ case 0x0121:
+ primitiveDataType = primitiveApplicator.get16NearPointerType(type,
+ primitiveApplicator.get16BitUnsignedShortType());
+ break;
+ // 16:16 far pointer to a 16-bit unsigned signed short
+ case 0x0221:
+ primitiveDataType = primitiveApplicator.get1616FarPointerType(type,
+ primitiveApplicator.get16BitUnsignedShortType());
+ break;
+ // 16:16 huge pointer to a 16-bit unsigned signed short
+ case 0x0321:
+ primitiveDataType = primitiveApplicator.get1616HugePointerType(type,
+ primitiveApplicator.get16BitUnsignedShortType());
+ break;
+ // 32-bit pointer to a 16-bit unsigned signed short
+ case 0x0421:
+ primitiveDataType = primitiveApplicator.get32PointerType(type,
+ primitiveApplicator.get16BitUnsignedShortType());
+ break;
+ // 16:32 pointer to a 16-bit unsigned signed short
+ case 0x0521:
+ primitiveDataType = primitiveApplicator.get1632PointerType(type,
+ primitiveApplicator.get16BitUnsignedShortType());
+ break;
+ // 64-bit pointer to a 16-bit unsigned signed short
+ case 0x0621:
+ primitiveDataType = primitiveApplicator.get64PointerType(type,
+ primitiveApplicator.get16BitUnsignedShortType());
+ break;
+ // 128-bit near pointer to a 16-bit unsigned signed short (LLVM doc on 0x0700)
+ case 0x0721:
+ primitiveDataType = primitiveApplicator.get128PointerType(type,
+ primitiveApplicator.get16BitUnsignedShortType());
+ break;
+
+ //=======================================
+ // 16-bit signed int types
+ //=======================================
+ // 16-bit signed int
+ case 0x0072:
+ primitiveDataType = primitiveApplicator.get16BitIntegerType();
+ break;
+ // 16-bit pointer to a 16-bit signed int
+ case 0x0172:
+ primitiveDataType = primitiveApplicator.get16NearPointerType(type,
+ primitiveApplicator.get16BitIntegerType());
+ break;
+ // 16:16 far pointer to a 16-bit signed int
+ case 0x0272:
+ primitiveDataType = primitiveApplicator.get1616FarPointerType(type,
+ primitiveApplicator.get16BitIntegerType());
+ break;
+ // 16:16 huge pointer to a 16-bit signed int
+ case 0x0372:
+ primitiveDataType = primitiveApplicator.get1616HugePointerType(type,
+ primitiveApplicator.get16BitIntegerType());
+ break;
+ // 32-bit pointer to a 16-bit signed int
+ case 0x0472:
+ primitiveDataType = primitiveApplicator.get32PointerType(type,
+ primitiveApplicator.get16BitIntegerType());
+ break;
+ // 16:32 pointer to a 16-bit signed int
+ case 0x0572:
+ primitiveDataType = primitiveApplicator.get1632PointerType(type,
+ primitiveApplicator.get16BitIntegerType());
+ break;
+ // 64-bit pointer to a 16-bit signed int
+ case 0x0672:
+ primitiveDataType = primitiveApplicator.get64PointerType(type,
+ primitiveApplicator.get16BitIntegerType());
+ break;
+ // 128-bit near pointer to a 16-bit signed int (LLVM doc on 0x0700)
+ case 0x0772:
+ primitiveDataType = primitiveApplicator.get128PointerType(type,
+ primitiveApplicator.get16BitIntegerType());
+ break;
+
+ //=======================================
+ // 16-bit unsigned int types
+ //=======================================
+ // 16-bit unsigned int
+ case 0x0073:
+ primitiveDataType = primitiveApplicator.get16BitUnsignedIntegerType();
+ break;
+ // 16-bit pointer to a 16-bit unsigned int
+ case 0x0173:
+ primitiveDataType = primitiveApplicator.get16NearPointerType(type,
+ primitiveApplicator.get16BitUnsignedIntegerType());
+ break;
+ // 16:16 far pointer to a 16-bit unsigned int
+ case 0x0273:
+ primitiveDataType = primitiveApplicator.get1616FarPointerType(type,
+ primitiveApplicator.get16BitUnsignedIntegerType());
+ break;
+ // 16:16 huge pointer to a 16-bit unsigned int
+ case 0x0373:
+ primitiveDataType = primitiveApplicator.get1616HugePointerType(type,
+ primitiveApplicator.get16BitUnsignedIntegerType());
+ break;
+ // 32-bit pointer to a 16-bit unsigned int
+ case 0x0473:
+ primitiveDataType = primitiveApplicator.get32PointerType(type,
+ primitiveApplicator.get16BitUnsignedIntegerType());
+ break;
+ // 16:32 pointer to a 16-bit unsigned int
+ case 0x0573:
+ primitiveDataType = primitiveApplicator.get1632PointerType(type,
+ primitiveApplicator.get16BitUnsignedIntegerType());
+ break;
+ // 64-bit pointer to a 16-bit unsigned int
+ case 0x0673:
+ primitiveDataType = primitiveApplicator.get64PointerType(type,
+ primitiveApplicator.get16BitUnsignedIntegerType());
+ break;
+ // 128-bit near pointer to a 16-bit unsigned int (LLVM doc on 0x0700)
+ case 0x0773:
+ primitiveDataType = primitiveApplicator.get128PointerType(type,
+ primitiveApplicator.get16BitUnsignedIntegerType());
+ break;
+
+ //=======================================
+ // 32-bit long types
+ //=======================================
+ // 32-bit signed long
+ case 0x0012:
+ primitiveDataType = primitiveApplicator.get32BitLongType();
+ break;
+ // 16-bit pointer to a 32-bit signed long
+ case 0x0112:
+ primitiveDataType = primitiveApplicator.get16NearPointerType(type,
+ primitiveApplicator.get32BitLongType());
+ break;
+ // 16:16 far pointer to a 32-bit signed long
+ case 0x0212:
+ primitiveDataType = primitiveApplicator.get1616FarPointerType(type,
+ primitiveApplicator.get32BitLongType());
+ break;
+ // 16:16 huge pointer to a 32-bit signed long
+ case 0x0312:
+ primitiveDataType = primitiveApplicator.get1616HugePointerType(type,
+ primitiveApplicator.get32BitLongType());
+ break;
+ // 32-bit pointer to a 32-bit signed long
+ case 0x0412:
+ primitiveDataType = primitiveApplicator.get32PointerType(type,
+ primitiveApplicator.get32BitLongType());
+ break;
+ // 16:32 pointer to a 32-bit signed long
+ case 0x0512:
+ primitiveDataType = primitiveApplicator.get1632PointerType(type,
+ primitiveApplicator.get32BitLongType());
+ break;
+ // 64-bit pointer to a 32-bit signed long
+ case 0x0612:
+ primitiveDataType = primitiveApplicator.get64PointerType(type,
+ primitiveApplicator.get32BitLongType());
+ break;
+ // 128-bit near pointer to a 32-bit signed long (LLVM doc on 0x0700)
+ case 0x0712:
+ primitiveDataType = primitiveApplicator.get128PointerType(type,
+ primitiveApplicator.get32BitLongType());
+ break;
+
+ //=======================================
+ // 32-bit unsigned long types
+ //=======================================
+ // 32-bit unsigned signed long
+ case 0x0022:
+ primitiveDataType = primitiveApplicator.get32BitUnsignedLongType();
+ break;
+ // 16-bit pointer to a 32-bit unsigned signed long
+ case 0x0122:
+ primitiveDataType = primitiveApplicator.get16NearPointerType(type,
+ primitiveApplicator.get32BitUnsignedLongType());
+ break;
+ // 16:16 far pointer to a 32-bit unsigned signed long
+ case 0x0222:
+ primitiveDataType = primitiveApplicator.get1616FarPointerType(type,
+ primitiveApplicator.get32BitUnsignedLongType());
+ break;
+ // 16:16 huge pointer to a 32-bit unsigned signed long
+ case 0x0322:
+ primitiveDataType = primitiveApplicator.get1616HugePointerType(type,
+ primitiveApplicator.get32BitUnsignedLongType());
+ break;
+ // 32-bit pointer to a 32-bit unsigned signed long
+ case 0x0422:
+ primitiveDataType = primitiveApplicator.get32PointerType(type,
+ primitiveApplicator.get32BitUnsignedLongType());
+ break;
+ // 16:32 pointer to a 32-bit unsigned signed long
+ case 0x0522:
+ primitiveDataType = primitiveApplicator.get1632PointerType(type,
+ primitiveApplicator.get32BitUnsignedLongType());
+ break;
+ // 64-bit pointer to a 32-bit unsigned signed long
+ case 0x0622:
+ primitiveDataType = primitiveApplicator.get64PointerType(type,
+ primitiveApplicator.get32BitUnsignedLongType());
+ break;
+ // 128-bit near pointer to a 32-bit unsigned signed long (LLVM doc on 0x0700)
+ case 0x0722:
+ primitiveDataType = primitiveApplicator.get128PointerType(type,
+ primitiveApplicator.get32BitUnsignedLongType());
+ break;
+
+ //=======================================
+ // 32-bit signed int types
+ //=======================================
+ // 32-bit signed int
+ case 0x0074:
+ primitiveDataType = primitiveApplicator.get32BitIntegerType();
+ break;
+ // 16-bit pointer to a 32-bit signed int
+ case 0x0174:
+ primitiveDataType = primitiveApplicator.get16NearPointerType(type,
+ primitiveApplicator.get32BitIntegerType());
+ break;
+ // 16:16 far pointer to a 32-bit signed int
+ case 0x0274:
+ primitiveDataType = primitiveApplicator.get1616FarPointerType(type,
+ primitiveApplicator.get32BitIntegerType());
+ break;
+ // 16:16 huge pointer to a 32-bit signed int
+ case 0x0374:
+ primitiveDataType = primitiveApplicator.get1616HugePointerType(type,
+ primitiveApplicator.get32BitIntegerType());
+ break;
+ // 32-bit pointer to a 32-bit signed int
+ case 0x0474:
+ primitiveDataType = primitiveApplicator.get32PointerType(type,
+ primitiveApplicator.get32BitIntegerType());
+ break;
+ // 16:32 pointer to a 32-bit signed int
+ case 0x0574:
+ primitiveDataType = primitiveApplicator.get1632PointerType(type,
+ primitiveApplicator.get32BitIntegerType());
+ break;
+ // 64-bit pointer to a 32-bit signed int
+ case 0x0674:
+ primitiveDataType = primitiveApplicator.get64PointerType(type,
+ primitiveApplicator.get32BitIntegerType());
+ break;
+ // 128-bit near pointer to a 32-bit signed int (LLVM doc on 0x0700)
+ case 0x0774:
+ primitiveDataType = primitiveApplicator.get128PointerType(type,
+ primitiveApplicator.get32BitIntegerType());
+ break;
+
+ //=======================================
+ // 32-bit unsigned int types
+ //=======================================
+ // 32-bit unsigned int
+ case 0x0075:
+ primitiveDataType = primitiveApplicator.get32BitUnsignedIntegerType();
+ break;
+ // 16-bit pointer to a 32-bit unsigned int
+ case 0x0175:
+ primitiveDataType = primitiveApplicator.get16NearPointerType(type,
+ primitiveApplicator.get32BitUnsignedIntegerType());
+ break;
+ // 16:16 far pointer to a 32-bit unsigned int
+ case 0x0275:
+ primitiveDataType = primitiveApplicator.get1616FarPointerType(type,
+ primitiveApplicator.get32BitUnsignedIntegerType());
+ break;
+ // 16:16 huge pointer to a 32-bit unsigned int
+ case 0x0375:
+ primitiveDataType = primitiveApplicator.get1616HugePointerType(type,
+ primitiveApplicator.get32BitUnsignedIntegerType());
+ break;
+ // 32-bit pointer to a 32-bit unsigned int
+ case 0x0475:
+ primitiveDataType = primitiveApplicator.get32PointerType(type,
+ primitiveApplicator.get32BitUnsignedIntegerType());
+ break;
+ // 16:32 pointer to a 32-bit unsigned int
+ case 0x0575:
+ primitiveDataType = primitiveApplicator.get1632PointerType(type,
+ primitiveApplicator.get32BitUnsignedIntegerType());
+ break;
+ // 64-bit pointer to a 32-bit unsigned int
+ case 0x0675:
+ primitiveDataType = primitiveApplicator.get64PointerType(type,
+ primitiveApplicator.get32BitUnsignedIntegerType());
+ break;
+ // 128-bit near pointer to a 32-bit unsigned int (LLVM doc on 0x0700)
+ case 0x0775:
+ primitiveDataType = primitiveApplicator.get128PointerType(type,
+ primitiveApplicator.get32BitUnsignedIntegerType());
+ break;
+
+ //=======================================
+ // 64-bit quad types
+ //=======================================
+ // 64-bit signed long
+ case 0x0013:
+ primitiveDataType = primitiveApplicator.get64BitLongType();
+ break;
+ // 16-bit pointer to a 64-bit signed long
+ case 0x0113:
+ primitiveDataType = primitiveApplicator.get16NearPointerType(type,
+ primitiveApplicator.get64BitLongType());
+ break;
+ // 16:16 far pointer to a 64-bit signed long
+ case 0x0213:
+ primitiveDataType = primitiveApplicator.get1616FarPointerType(type,
+ primitiveApplicator.get64BitLongType());
+ break;
+ // 16:16 huge pointer to a 64-bit signed long
+ case 0x0313:
+ primitiveDataType = primitiveApplicator.get1616HugePointerType(type,
+ primitiveApplicator.get64BitLongType());
+ break;
+ // 32-bit pointer to a 64-bit signed long
+ case 0x0413:
+ primitiveDataType = primitiveApplicator.get32PointerType(type,
+ primitiveApplicator.get64BitLongType());
+ break;
+ // 16:32 pointer to a 64-bit signed long
+ case 0x0513:
+ primitiveDataType = primitiveApplicator.get1632PointerType(type,
+ primitiveApplicator.get64BitLongType());
+ break;
+ // 64-bit pointer to a 64-bit signed long
+ case 0x0613:
+ primitiveDataType = primitiveApplicator.get64PointerType(type,
+ primitiveApplicator.get64BitLongType());
+ break;
+ // 128-bit near pointer to a 64-bit signed long (LLVM doc on 0x0700)
+ case 0x0713:
+ primitiveDataType = primitiveApplicator.get128PointerType(type,
+ primitiveApplicator.get64BitLongType());
+ break;
+
+ //=======================================
+ // 64-bit unsigned quad types
+ //=======================================
+ // 64-bit unsigned signed long
+ case 0x0023:
+ primitiveDataType = primitiveApplicator.get64BitUnsignedLongType();
+ break;
+ // 16-bit pointer to a 64-bit unsigned signed long
+ case 0x0123:
+ primitiveDataType = primitiveApplicator.get16NearPointerType(type,
+ primitiveApplicator.get64BitUnsignedLongType());
+ break;
+ // 16:16 far pointer to a 64-bit unsigned signed long
+ case 0x0223:
+ primitiveDataType = primitiveApplicator.get1616FarPointerType(type,
+ primitiveApplicator.get64BitUnsignedLongType());
+ break;
+ // 16:16 huge pointer to a 64-bit unsigned signed long
+ case 0x0323:
+ primitiveDataType = primitiveApplicator.get1616HugePointerType(type,
+ primitiveApplicator.get64BitUnsignedLongType());
+ break;
+ // 32-bit pointer to a 64-bit unsigned signed long
+ case 0x0423:
+ primitiveDataType = primitiveApplicator.get32PointerType(type,
+ primitiveApplicator.get64BitUnsignedLongType());
+ break;
+ // 16:32 pointer to a 64-bit unsigned signed long
+ case 0x0523:
+ primitiveDataType = primitiveApplicator.get1632PointerType(type,
+ primitiveApplicator.get64BitUnsignedLongType());
+ break;
+ // 64-bit pointer to a 64-bit unsigned signed long
+ case 0x0623:
+ primitiveDataType = primitiveApplicator.get64PointerType(type,
+ primitiveApplicator.get64BitUnsignedLongType());
+ break;
+ // 128-bit near pointer to a 64-bit unsigned signed long (LLVM doc on 0x0700)
+ case 0x0723:
+ primitiveDataType = primitiveApplicator.get128PointerType(type,
+ primitiveApplicator.get64BitUnsignedLongType());
+ break;
+
+ //=======================================
+ // 64-bit signed int types
+ //=======================================
+ // 64-bit signed int
+ case 0x0076:
+ primitiveDataType = primitiveApplicator.get64BitIntegerType();
+ break;
+ // 16-bit pointer to a 64-bit signed int
+ case 0x0176:
+ primitiveDataType = primitiveApplicator.get16NearPointerType(type,
+ primitiveApplicator.get64BitIntegerType());
+ break;
+ // 16:16 far pointer to a 64-bit signed int
+ case 0x0276:
+ primitiveDataType = primitiveApplicator.get1616FarPointerType(type,
+ primitiveApplicator.get64BitIntegerType());
+ break;
+ // 16:16 huge pointer to a 64-bit signed int
+ case 0x0376:
+ primitiveDataType = primitiveApplicator.get1616HugePointerType(type,
+ primitiveApplicator.get64BitIntegerType());
+ break;
+ // 32-bit pointer to a 64-bit signed int
+ case 0x0476:
+ primitiveDataType = primitiveApplicator.get32PointerType(type,
+ primitiveApplicator.get64BitIntegerType());
+ break;
+ // 16:32 pointer to a 64-bit signed int
+ case 0x0576:
+ primitiveDataType = primitiveApplicator.get1632PointerType(type,
+ primitiveApplicator.get64BitIntegerType());
+ break;
+ // 64-bit pointer to a 64-bit signed int
+ case 0x0676:
+ primitiveDataType = primitiveApplicator.get64PointerType(type,
+ primitiveApplicator.get64BitIntegerType());
+ break;
+ // 128-bit near pointer to a 64-bit signed int (LLVM doc on 0x0700)
+ case 0x0776:
+ primitiveDataType = primitiveApplicator.get128PointerType(type,
+ primitiveApplicator.get64BitIntegerType());
+ break;
+
+ //=======================================
+ // 64-bit unsigned int types
+ //=======================================
+ // 64-bit unsigned int
+ case 0x0077:
+ primitiveDataType = primitiveApplicator.get64BitUnsignedIntegerType();
+ break;
+ // 16-bit pointer to a 64-bit unsigned int
+ case 0x0177:
+ primitiveDataType = primitiveApplicator.get16NearPointerType(type,
+ primitiveApplicator.get64BitUnsignedIntegerType());
+ break;
+ // 16:16 far pointer to a 64-bit unsigned int
+ case 0x0277:
+ primitiveDataType = primitiveApplicator.get1616FarPointerType(type,
+ primitiveApplicator.get64BitUnsignedIntegerType());
+ break;
+ // 16:16 huge pointer to a 64-bit unsigned int
+ case 0x0377:
+ primitiveDataType = primitiveApplicator.get1616HugePointerType(type,
+ primitiveApplicator.get64BitUnsignedIntegerType());
+ break;
+ // 32-bit pointer to a 64-bit unsigned int
+ case 0x0477:
+ primitiveDataType = primitiveApplicator.get32PointerType(type,
+ primitiveApplicator.get64BitUnsignedIntegerType());
+ break;
+ // 16:32 pointer to a 64-bit unsigned int
+ case 0x0577:
+ primitiveDataType = primitiveApplicator.get1632PointerType(type,
+ primitiveApplicator.get64BitUnsignedIntegerType());
+ break;
+ // 64-bit pointer to a 64-bit unsigned int
+ case 0x0677:
+ primitiveDataType = primitiveApplicator.get64PointerType(type,
+ primitiveApplicator.get64BitUnsignedIntegerType());
+ break;
+ // 128-bit near pointer to a 64-bit unsigned int (LLVM doc on 0x0700)
+ case 0x0777:
+ primitiveDataType = primitiveApplicator.get128PointerType(type,
+ primitiveApplicator.get64BitUnsignedIntegerType());
+ break;
+
+ //=======================================
+ // 128-bit octet types
+ //=======================================
+ // 128-bit signed long
+ case 0x0014:
+ primitiveDataType = primitiveApplicator.get128BitLongType();
+ break;
+ // 16-bit pointer to a 128-bit signed long
+ case 0x0114:
+ primitiveDataType = primitiveApplicator.get16NearPointerType(type,
+ primitiveApplicator.get128BitLongType());
+ break;
+ // 16:16 far pointer to a 128-bit signed long
+ case 0x0214:
+ primitiveDataType = primitiveApplicator.get1616FarPointerType(type,
+ primitiveApplicator.get128BitLongType());
+ break;
+ // 16:16 huge pointer to a 128-bit signed long
+ case 0x0314:
+ primitiveDataType = primitiveApplicator.get1616HugePointerType(type,
+ primitiveApplicator.get128BitLongType());
+ break;
+ // 32-bit pointer to a 128-bit signed long
+ case 0x0414:
+ primitiveDataType = primitiveApplicator.get32PointerType(type,
+ primitiveApplicator.get128BitLongType());
+ break;
+ // 16:32 pointer to a 128-bit signed long
+ case 0x0514:
+ primitiveDataType = primitiveApplicator.get1632PointerType(type,
+ primitiveApplicator.get128BitLongType());
+ break;
+ // 64-bit pointer to a 128-bit signed long
+ case 0x0614:
+ primitiveDataType = primitiveApplicator.get64PointerType(type,
+ primitiveApplicator.get128BitLongType());
+ break;
+ // 128-bit near pointer to a 128-bit signed long (LLVM doc on 0x0700)
+ case 0x0714:
+ primitiveDataType = primitiveApplicator.get128PointerType(type,
+ primitiveApplicator.get128BitLongType());
+ break;
+
+ //=======================================
+ // 128-bit unsigned octet types
+ //=======================================
+ // 128-bit unsigned signed long
+ case 0x0024:
+ primitiveDataType = primitiveApplicator.get128BitUnsignedLongType();
+ break;
+ // 16-bit pointer to a 128-bit unsigned signed long
+ case 0x0124:
+ primitiveDataType = primitiveApplicator.get16NearPointerType(type,
+ primitiveApplicator.get128BitUnsignedLongType());
+ break;
+ // 16:16 far pointer to a 128-bit unsigned signed long
+ case 0x0224:
+ primitiveDataType = primitiveApplicator.get1616FarPointerType(type,
+ primitiveApplicator.get128BitUnsignedLongType());
+ break;
+ // 16:16 huge pointer to a 128-bit unsigned signed long
+ case 0x0324:
+ primitiveDataType = primitiveApplicator.get1616HugePointerType(type,
+ primitiveApplicator.get128BitUnsignedLongType());
+ break;
+ // 32-bit pointer to a 128-bit unsigned signed long
+ case 0x0424:
+ primitiveDataType = primitiveApplicator.get32PointerType(type,
+ primitiveApplicator.get128BitUnsignedLongType());
+ break;
+ // 16:32 pointer to a 128-bit unsigned signed long
+ case 0x0524:
+ primitiveDataType = primitiveApplicator.get1632PointerType(type,
+ primitiveApplicator.get128BitUnsignedLongType());
+ break;
+ // 64-bit pointer to a 128-bit unsigned signed long
+ case 0x0624:
+ primitiveDataType = primitiveApplicator.get64PointerType(type,
+ primitiveApplicator.get128BitUnsignedLongType());
+ break;
+ // 128-bit near pointer to a 128-bit unsigned signed long (LLVM doc on 0x0700)
+ case 0x0724:
+ primitiveDataType = primitiveApplicator.get128PointerType(type,
+ primitiveApplicator.get128BitUnsignedLongType());
+ break;
+
+ //=======================================
+ // 128-bit signed int types
+ //=======================================
+ // 128-bit signed int
+ case 0x0078:
+ primitiveDataType = primitiveApplicator.get128BitIntegerType();
+ break;
+ // 16-bit pointer to a 128-bit signed int
+ case 0x0178:
+ primitiveDataType = primitiveApplicator.get16NearPointerType(type,
+ primitiveApplicator.get128BitIntegerType());
+ break;
+ // 16:16 far pointer to a 128-bit signed int
+ case 0x0278:
+ primitiveDataType = primitiveApplicator.get1616FarPointerType(type,
+ primitiveApplicator.get128BitIntegerType());
+ break;
+ // 16:16 huge pointer to a 128-bit signed int
+ case 0x0378:
+ primitiveDataType = primitiveApplicator.get1616HugePointerType(type,
+ primitiveApplicator.get128BitIntegerType());
+ break;
+ // 32-bit pointer to a 128-bit signed int
+ case 0x0478:
+ primitiveDataType = primitiveApplicator.get32PointerType(type,
+ primitiveApplicator.get128BitIntegerType());
+ break;
+ // 16:32 pointer to a 128-bit signed int
+ case 0x0578:
+ primitiveDataType = primitiveApplicator.get1632PointerType(type,
+ primitiveApplicator.get128BitIntegerType());
+ break;
+ // 64-bit pointer to a 128-bit signed int
+ case 0x0678:
+ primitiveDataType = primitiveApplicator.get64PointerType(type,
+ primitiveApplicator.get128BitIntegerType());
+ break;
+ // 128-bit near pointer to a 128-bit signed int (LLVM doc on 0x0700)
+ case 0x0778:
+ primitiveDataType = primitiveApplicator.get128PointerType(type,
+ primitiveApplicator.get128BitIntegerType());
+ break;
+
+ //=======================================
+ // 128-bit unsigned int types
+ //=======================================
+ // 128-bit unsigned int
+ case 0x0079:
+ primitiveDataType = primitiveApplicator.get128BitUnsignedIntegerType();
+ break;
+ // 16-bit pointer to a 128-bit unsigned int
+ case 0x0179:
+ primitiveDataType = primitiveApplicator.get16NearPointerType(type,
+ primitiveApplicator.get128BitUnsignedIntegerType());
+ break;
+ // 16:16 far pointer to a 128-bit unsigned int
+ case 0x0279:
+ primitiveDataType = primitiveApplicator.get1616FarPointerType(type,
+ primitiveApplicator.get128BitUnsignedIntegerType());
+ break;
+ // 16:16 huge pointer to a 128-bit unsigned int
+ case 0x0379:
+ primitiveDataType = primitiveApplicator.get1616HugePointerType(type,
+ primitiveApplicator.get128BitUnsignedIntegerType());
+ break;
+ // 32-bit pointer to a 128-bit unsigned int
+ case 0x0479:
+ primitiveDataType = primitiveApplicator.get32PointerType(type,
+ primitiveApplicator.get128BitUnsignedIntegerType());
+ break;
+ // 16:32 pointer to a 128-bit unsigned int
+ case 0x0579:
+ primitiveDataType = primitiveApplicator.get1632PointerType(type,
+ primitiveApplicator.get128BitUnsignedIntegerType());
+ break;
+ // 64-bit pointer to a 128-bit unsigned int
+ case 0x0679:
+ primitiveDataType = primitiveApplicator.get64PointerType(type,
+ primitiveApplicator.get128BitUnsignedIntegerType());
+ break;
+ // 128-bit near pointer to a 128-bit unsigned int (LLVM doc on 0x0700)
+ case 0x0779:
+ primitiveDataType = primitiveApplicator.get128PointerType(type,
+ primitiveApplicator.get128BitUnsignedIntegerType());
+ break;
+
+ //=======================================
+ // 16-bit real types
+ //=======================================
+ // 16-bit real
+ case 0x0046:
+ primitiveDataType = primitiveApplicator.get16BitRealType();
+ break;
+ // 16-bit pointer to a 16-bit real
+ case 0x0146:
+ primitiveDataType = primitiveApplicator.get16NearPointerType(type,
+ primitiveApplicator.get16BitRealType());
+ break;
+ // 16:16 far pointer to a 16-bit real
+ case 0x0246:
+ primitiveDataType = primitiveApplicator.get1616FarPointerType(type,
+ primitiveApplicator.get16BitRealType());
+ break;
+ // 16:16 huge pointer to a 16-bit real
+ case 0x0346:
+ primitiveDataType = primitiveApplicator.get1616HugePointerType(type,
+ primitiveApplicator.get16BitRealType());
+ break;
+ // 32-bit pointer to a 16-bit real
+ case 0x0446:
+ primitiveDataType = primitiveApplicator.get32PointerType(type,
+ primitiveApplicator.get16BitRealType());
+ break;
+ // 16:32 pointer to a 16-bit real
+ case 0x0546:
+ primitiveDataType = primitiveApplicator.get1632PointerType(type,
+ primitiveApplicator.get16BitRealType());
+ break;
+ // 64-bit pointer to a 16-bit real
+ case 0x0646:
+ primitiveDataType = primitiveApplicator.get64PointerType(type,
+ primitiveApplicator.get16BitRealType());
+ break;
+ // 128-bit near pointer to a 16-bit real (LLVM doc on 0x0700)
+ case 0x0746:
+ primitiveDataType = primitiveApplicator.get128PointerType(type,
+ primitiveApplicator.get16BitRealType());
+ break;
+
+ //=======================================
+ // 32-bit real types
+ //=======================================
+ // 32-bit real
+ case 0x0040:
+ primitiveDataType = primitiveApplicator.get32BitRealType();
+ break;
+ // 16-bit pointer to a 32-bit real
+ case 0x0140:
+ primitiveDataType = primitiveApplicator.get16NearPointerType(type,
+ primitiveApplicator.get32BitRealType());
+ break;
+ // 16:16 far pointer to a 32-bit real
+ case 0x0240:
+ primitiveDataType = primitiveApplicator.get1616FarPointerType(type,
+ primitiveApplicator.get32BitRealType());
+ break;
+ // 16:16 huge pointer to a 32-bit real
+ case 0x0340:
+ primitiveDataType = primitiveApplicator.get1616HugePointerType(type,
+ primitiveApplicator.get32BitRealType());
+ break;
+ // 32-bit pointer to a 32-bit real
+ case 0x0440:
+ primitiveDataType = primitiveApplicator.get32PointerType(type,
+ primitiveApplicator.get32BitRealType());
+ break;
+ // 16:32 pointer to a 32-bit real
+ case 0x0540:
+ primitiveDataType = primitiveApplicator.get1632PointerType(type,
+ primitiveApplicator.get32BitRealType());
+ break;
+ // 64-bit pointer to a 32-bit real
+ case 0x0640:
+ primitiveDataType = primitiveApplicator.get64PointerType(type,
+ primitiveApplicator.get32BitRealType());
+ break;
+ // 128-bit near pointer to a 32-bit real (LLVM doc on 0x0700)
+ case 0x0740:
+ primitiveDataType = primitiveApplicator.get128PointerType(type,
+ primitiveApplicator.get32BitRealType());
+ break;
+
+ //=======================================
+ // 32-bit partial-precision real types
+ //=======================================
+ // 32-bit partial-precision real
+ case 0x0045:
+ primitiveDataType = primitiveApplicator.get32BitPartialPrecisionRealType();
+ break;
+ // 16-bit pointer to a 32-bit partial-precision real
+ case 0x0145:
+ primitiveDataType = primitiveApplicator.get16NearPointerType(type,
+ primitiveApplicator.get32BitPartialPrecisionRealType());
+ break;
+ // 16:16 far pointer to a 32-bit partial-precision real
+ case 0x0245:
+ primitiveDataType = primitiveApplicator.get1616FarPointerType(type,
+ primitiveApplicator.get32BitPartialPrecisionRealType());
+ break;
+ // 16:16 huge pointer to a 32-bit partial-precision real
+ case 0x0345:
+ primitiveDataType = primitiveApplicator.get1616HugePointerType(type,
+ primitiveApplicator.get32BitPartialPrecisionRealType());
+ break;
+ // 32-bit pointer to a 32-bit partial-precision real
+ case 0x0445:
+ primitiveDataType = primitiveApplicator.get32PointerType(type,
+ primitiveApplicator.get32BitPartialPrecisionRealType());
+ break;
+ // 16:32 pointer to a 32-bit partial-precision real
+ case 0x0545:
+ primitiveDataType = primitiveApplicator.get1632PointerType(type,
+ primitiveApplicator.get32BitPartialPrecisionRealType());
+ break;
+ // 64-bit pointer to a 32-bit partial-precision real
+ case 0x0645:
+ primitiveDataType = primitiveApplicator.get64PointerType(type,
+ primitiveApplicator.get32BitPartialPrecisionRealType());
+ break;
+ // 128-bit near pointer to a 32-bit partial-precision real (LLVM doc on 0x0700)
+ case 0x0745:
+ primitiveDataType = primitiveApplicator.get128PointerType(type,
+ primitiveApplicator.get32BitPartialPrecisionRealType());
+ break;
+
+ //=======================================
+ // 48-bit real types
+ //=======================================
+ // 48-bit real
+ case 0x0044:
+ primitiveDataType = primitiveApplicator.get48BitRealType();
+ break;
+ // 16-bit pointer to a 48-bit real
+ case 0x0144:
+ primitiveDataType = primitiveApplicator.get16NearPointerType(type,
+ primitiveApplicator.get48BitRealType());
+ break;
+ // 16:16 far pointer to a 48-bit real
+ case 0x0244:
+ primitiveDataType = primitiveApplicator.get1616FarPointerType(type,
+ primitiveApplicator.get48BitRealType());
+ break;
+ // 16:16 huge pointer to a 48-bit real
+ case 0x0344:
+ primitiveDataType = primitiveApplicator.get1616HugePointerType(type,
+ primitiveApplicator.get48BitRealType());
+ break;
+ // 32-bit pointer to a 48-bit real
+ case 0x0444:
+ primitiveDataType = primitiveApplicator.get32PointerType(type,
+ primitiveApplicator.get48BitRealType());
+ break;
+ // 16:32 pointer to a 48-bit real
+ case 0x0544:
+ primitiveDataType = primitiveApplicator.get1632PointerType(type,
+ primitiveApplicator.get48BitRealType());
+ break;
+ // 64-bit pointer to a 48-bit real
+ case 0x0644:
+ primitiveDataType = primitiveApplicator.get64PointerType(type,
+ primitiveApplicator.get48BitRealType());
+ break;
+ // 128-bit near pointer to a 48-bit real (LLVM doc on 0x0700)
+ case 0x0744:
+ primitiveDataType = primitiveApplicator.get128PointerType(type,
+ primitiveApplicator.get48BitRealType());
+ break;
+
+ //=======================================
+ // 64-bit real types
+ //=======================================
+ // 64-bit real
+ case 0x0041:
+ primitiveDataType = primitiveApplicator.get64BitRealType();
+ break;
+ // 16-bit pointer to a 64-bit real
+ case 0x0141:
+ primitiveDataType = primitiveApplicator.get16NearPointerType(type,
+ primitiveApplicator.get64BitRealType());
+ break;
+ // 16:16 far pointer to a 64-bit real
+ case 0x0241:
+ primitiveDataType = primitiveApplicator.get1616FarPointerType(type,
+ primitiveApplicator.get64BitRealType());
+ break;
+ // 16:16 huge pointer to a 64-bit real
+ case 0x0341:
+ primitiveDataType = primitiveApplicator.get1616HugePointerType(type,
+ primitiveApplicator.get64BitRealType());
+ break;
+ // 32-bit pointer to a 64-bit real
+ case 0x0441:
+ primitiveDataType = primitiveApplicator.get32PointerType(type,
+ primitiveApplicator.get64BitRealType());
+ break;
+ // 16:32 pointer to a 64-bit real
+ case 0x0541:
+ primitiveDataType = primitiveApplicator.get1632PointerType(type,
+ primitiveApplicator.get64BitRealType());
+ break;
+ // 64-bit pointer to a 64-bit real
+ case 0x0641:
+ primitiveDataType = primitiveApplicator.get64PointerType(type,
+ primitiveApplicator.get64BitRealType());
+ break;
+ // 128-bit near pointer to a 64-bit real (LLVM doc on 0x0700)
+ case 0x0741:
+ primitiveDataType = primitiveApplicator.get128PointerType(type,
+ primitiveApplicator.get64BitRealType());
+ break;
+
+ //=======================================
+ // 80-bit real types
+ //=======================================
+ // 80-bit real
+ case 0x0042:
+ primitiveDataType = primitiveApplicator.get80BitRealType();
+ break;
+ // 16-bit pointer to an 80-bit real
+ case 0x0142:
+ primitiveDataType = primitiveApplicator.get16NearPointerType(type,
+ primitiveApplicator.get80BitRealType());
+ break;
+ // 16:16 far pointer to an 80-bit real
+ case 0x0242:
+ primitiveDataType = primitiveApplicator.get1616FarPointerType(type,
+ primitiveApplicator.get80BitRealType());
+ break;
+ // 16:16 huge pointer to an 80-bit real
+ case 0x0342:
+ primitiveDataType = primitiveApplicator.get1616HugePointerType(type,
+ primitiveApplicator.get80BitRealType());
+ break;
+ // 32-bit pointer to an 80-bit real
+ case 0x0442:
+ primitiveDataType = primitiveApplicator.get32PointerType(type,
+ primitiveApplicator.get80BitRealType());
+ break;
+ // 16:32 pointer to an 80-bit real
+ case 0x0542:
+ primitiveDataType = primitiveApplicator.get1632PointerType(type,
+ primitiveApplicator.get80BitRealType());
+ break;
+ // 64-bit pointer to an 80-bit real
+ case 0x0642:
+ primitiveDataType = primitiveApplicator.get64PointerType(type,
+ primitiveApplicator.get80BitRealType());
+ break;
+ // 128-bit near pointer to an 80-bit real (LLVM doc on 0x0700)
+ case 0x0742:
+ primitiveDataType = primitiveApplicator.get128PointerType(type,
+ primitiveApplicator.get80BitRealType());
+ break;
+
+ //=======================================
+ // 128-bit real types
+ //=======================================
+ // 128-bit real
+ case 0x0043:
+ primitiveDataType = primitiveApplicator.get128BitRealType();
+ break;
+ // 16-bit pointer to a 128-bit real
+ case 0x0143:
+ primitiveDataType = primitiveApplicator.get16NearPointerType(type,
+ primitiveApplicator.get128BitRealType());
+ break;
+ // 16:16 far pointer to a 128-bit real
+ case 0x0243:
+ primitiveDataType = primitiveApplicator.get1616FarPointerType(type,
+ primitiveApplicator.get128BitRealType());
+ break;
+ // 16:16 huge pointer to a 128-bit real
+ case 0x0343:
+ primitiveDataType = primitiveApplicator.get1616HugePointerType(type,
+ primitiveApplicator.get128BitRealType());
+ break;
+ // 32-bit pointer to a 128-bit real
+ case 0x0443:
+ primitiveDataType = primitiveApplicator.get32PointerType(type,
+ primitiveApplicator.get128BitRealType());
+ break;
+ // 16:32 pointer to a 128-bit real
+ case 0x0543:
+ primitiveDataType = primitiveApplicator.get1632PointerType(type,
+ primitiveApplicator.get128BitRealType());
+ break;
+ // 64-bit pointer to a 128-bit real
+ case 0x0643:
+ primitiveDataType = primitiveApplicator.get64PointerType(type,
+ primitiveApplicator.get128BitRealType());
+ break;
+ // 128-bit near pointer to a 128-bit real (LLVM doc on 0x0700)
+ case 0x0743:
+ primitiveDataType = primitiveApplicator.get128PointerType(type,
+ primitiveApplicator.get128BitRealType());
+ break;
+
+ //=======================================
+ // 32-bit complex types
+ //=======================================
+ // 32-bit complex
+ case 0x0050:
+ primitiveDataType = primitiveApplicator.get32BitComplexType();
+ break;
+ // 16-bit pointer to a 32-bit complex
+ case 0x0150:
+ primitiveDataType = primitiveApplicator.get16NearPointerType(type,
+ primitiveApplicator.get32BitComplexType());
+ break;
+ // 16:16 far pointer to a 32-bit complex
+ case 0x0250:
+ primitiveDataType = primitiveApplicator.get1616FarPointerType(type,
+ primitiveApplicator.get32BitComplexType());
+ break;
+ // 16:16 huge pointer to a 32-bit complex
+ case 0x0350:
+ primitiveDataType = primitiveApplicator.get1616HugePointerType(type,
+ primitiveApplicator.get32BitComplexType());
+ break;
+ // 32-bit pointer to an 32-bit complex
+ case 0x0450:
+ primitiveDataType = primitiveApplicator.get32PointerType(type,
+ primitiveApplicator.get32BitComplexType());
+ break;
+ // 16:32 pointer to an 32-bit complex
+ case 0x0550:
+ primitiveDataType = primitiveApplicator.get1632PointerType(type,
+ primitiveApplicator.get32BitComplexType());
+ break;
+ // 64-bit pointer to an 32-bit complex
+ case 0x0650:
+ primitiveDataType = primitiveApplicator.get64PointerType(type,
+ primitiveApplicator.get32BitComplexType());
+ break;
+ // 128-bit near pointer to an 32-bit complex (LLVM doc on 0x0700)
+ case 0x0750:
+ primitiveDataType = primitiveApplicator.get128PointerType(type,
+ primitiveApplicator.get32BitComplexType());
+ break;
+
+ //=======================================
+ // 64-bit complex types
+ //=======================================
+ // 64-bit complex
+ case 0x0051:
+ primitiveDataType = primitiveApplicator.get64BitComplexType();
+ break;
+ // 16-bit pointer to a 64-bit complex
+ case 0x0151:
+ primitiveDataType = primitiveApplicator.get16NearPointerType(type,
+ primitiveApplicator.get64BitComplexType());
+ break;
+ // 16:16 far pointer to a 64-bit complex
+ case 0x0251:
+ primitiveDataType = primitiveApplicator.get1616FarPointerType(type,
+ primitiveApplicator.get64BitComplexType());
+ break;
+ // 16:16 huge pointer to a 64-bit complex
+ case 0x0351:
+ primitiveDataType = primitiveApplicator.get1616HugePointerType(type,
+ primitiveApplicator.get64BitComplexType());
+ break;
+ // 32-bit pointer to a 64-bit complex
+ case 0x0451:
+ primitiveDataType = primitiveApplicator.get32PointerType(type,
+ primitiveApplicator.get64BitComplexType());
+ break;
+ // 16:32 pointer to a 64-bit complex
+ case 0x0551:
+ primitiveDataType = primitiveApplicator.get1632PointerType(type,
+ primitiveApplicator.get64BitComplexType());
+ break;
+ // 64-bit pointer to a 64-bit complex
+ case 0x0651:
+ primitiveDataType = primitiveApplicator.get64PointerType(type,
+ primitiveApplicator.get64BitComplexType());
+ break;
+ // 128-bit near pointer to a 64-bit complex (LLVM doc on 0x0700)
+ case 0x0751:
+ primitiveDataType = primitiveApplicator.get128PointerType(type,
+ primitiveApplicator.get64BitComplexType());
+ break;
+
+ //=======================================
+ // 80-bit complex types
+ //=======================================
+ // 80-bit complex
+ case 0x0052:
+ primitiveDataType = primitiveApplicator.get80BitComplexType();
+ break;
+ // 16-bit pointer to an 80-bit complex
+ case 0x0152:
+ primitiveDataType = primitiveApplicator.get16NearPointerType(type,
+ primitiveApplicator.get80BitComplexType());
+ break;
+ // 16:16 far pointer to an 80-bit complex
+ case 0x0252:
+ primitiveDataType = primitiveApplicator.get1616FarPointerType(type,
+ primitiveApplicator.get80BitComplexType());
+ break;
+ // 16:16 huge pointer to an 80-bit complex
+ case 0x0352:
+ primitiveDataType = primitiveApplicator.get1616HugePointerType(type,
+ primitiveApplicator.get80BitComplexType());
+ break;
+ // 32-bit pointer to an 80-bit complex
+ case 0x0452:
+ primitiveDataType = primitiveApplicator.get32PointerType(type,
+ primitiveApplicator.get80BitComplexType());
+ break;
+ // 16:32 pointer to an 80-bit complex
+ case 0x0552:
+ primitiveDataType = primitiveApplicator.get1632PointerType(type,
+ primitiveApplicator.get80BitComplexType());
+ break;
+ // 64-bit pointer to an 80-bit complex
+ case 0x0652:
+ primitiveDataType = primitiveApplicator.get64PointerType(type,
+ primitiveApplicator.get80BitComplexType());
+ break;
+ // 128-bit near pointer to an 80-bit complex (LLVM doc on 0x0700)
+ case 0x0752:
+ primitiveDataType = primitiveApplicator.get128PointerType(type,
+ primitiveApplicator.get80BitComplexType());
+ break;
+
+ //=======================================
+ // 128-bit complex types
+ //=======================================
+ // 128-bit complex
+ case 0x0053:
+ primitiveDataType = primitiveApplicator.get128BitComplexType();
+ break;
+ // 16-bit pointer to a 128-bit complex
+ case 0x0153:
+ primitiveDataType = primitiveApplicator.get16NearPointerType(type,
+ primitiveApplicator.get128BitComplexType());
+ break;
+ // 16:16 far pointer to a 128-bit complex
+ case 0x0253:
+ primitiveDataType = primitiveApplicator.get1616FarPointerType(type,
+ primitiveApplicator.get128BitComplexType());
+ break;
+ // 16:16 huge pointer to a 128-bit complex
+ case 0x0353:
+ primitiveDataType = primitiveApplicator.get1616HugePointerType(type,
+ primitiveApplicator.get128BitComplexType());
+ break;
+ // 32-bit pointer to a 128-bit complex
+ case 0x0453:
+ primitiveDataType = primitiveApplicator.get32PointerType(type,
+ primitiveApplicator.get128BitComplexType());
+ break;
+ // 16:32 pointer to a 128-bit complex
+ case 0x0553:
+ primitiveDataType = primitiveApplicator.get1632PointerType(type,
+ primitiveApplicator.get128BitComplexType());
+ break;
+ // 64-bit pointer to a 128-bit complex
+ case 0x0653:
+ primitiveDataType = primitiveApplicator.get64PointerType(type,
+ primitiveApplicator.get128BitComplexType());
+ break;
+ // 128-bit near pointer to a 128-bit complex (LLVM doc on 0x0700)
+ case 0x0753:
+ primitiveDataType = primitiveApplicator.get128PointerType(type,
+ primitiveApplicator.get128BitComplexType());
+ break;
+
+ //=======================================
+ // 48-bit complex types
+ //=======================================
+ // 48-bit complex
+ case 0x0054:
+ primitiveDataType = primitiveApplicator.get48BitComplexType();
+ break;
+ // 16-bit pointer to a 48-bit complex
+ case 0x0154:
+ primitiveDataType = primitiveApplicator.get16NearPointerType(type,
+ primitiveApplicator.get48BitComplexType());
+ break;
+ // 16:16 far pointer to a 48-bit complex
+ case 0x0254:
+ primitiveDataType = primitiveApplicator.get1616FarPointerType(type,
+ primitiveApplicator.get48BitComplexType());
+ break;
+ // 16:16 huge pointer to a 48-bit complex
+ case 0x0354:
+ primitiveDataType = primitiveApplicator.get1616HugePointerType(type,
+ primitiveApplicator.get48BitComplexType());
+ break;
+ // 32-bit pointer to a 48-bit complex
+ case 0x0454:
+ primitiveDataType = primitiveApplicator.get32PointerType(type,
+ primitiveApplicator.get48BitComplexType());
+ break;
+ // 16:32 pointer to a 48-bit complex
+ case 0x0554:
+ primitiveDataType = primitiveApplicator.get1632PointerType(type,
+ primitiveApplicator.get48BitComplexType());
+ break;
+ // 64-bit pointer to a 48-bit complex
+ case 0x0654:
+ primitiveDataType = primitiveApplicator.get64PointerType(type,
+ primitiveApplicator.get48BitComplexType());
+ break;
+ // 128-bit near pointer to a 48-bit complex (LLVM doc on 0x0700)
+ case 0x0754:
+ primitiveDataType = primitiveApplicator.get128PointerType(type,
+ primitiveApplicator.get48BitComplexType());
+ break;
+
+ //=======================================
+ // 32-bit partial-precision complex types
+ //=======================================
+ // 32-bit partial-precision complex
+ case 0x0055:
+ primitiveDataType = primitiveApplicator.get32BitPartialPrecisionComplexType();
+ break;
+ // 16-bit pointer to a 32-bit partial-precision complex
+ case 0x0155:
+ primitiveDataType = primitiveApplicator.get16NearPointerType(type,
+ primitiveApplicator.get32BitPartialPrecisionComplexType());
+ break;
+ // 16:16 far pointer to a 32-bit partial-precision complex
+ case 0x0255:
+ primitiveDataType = primitiveApplicator.get1616FarPointerType(type,
+ primitiveApplicator.get32BitPartialPrecisionComplexType());
+ break;
+ // 16:16 huge pointer to a 32-bit partial-precision complex
+ case 0x0355:
+ primitiveDataType = primitiveApplicator.get1616HugePointerType(type,
+ primitiveApplicator.get32BitPartialPrecisionComplexType());
+ break;
+ // 32-bit pointer to a 32-bit partial-precision complex
+ case 0x0455:
+ primitiveDataType = primitiveApplicator.get32PointerType(type,
+ primitiveApplicator.get32BitPartialPrecisionComplexType());
+ break;
+ // 16:32 pointer to a 32-bit partial-precision complex
+ case 0x0555:
+ primitiveDataType = primitiveApplicator.get1632PointerType(type,
+ primitiveApplicator.get32BitPartialPrecisionComplexType());
+ break;
+ // 64-bit pointer to a 32-bit partial-precision complex
+ case 0x0655:
+ primitiveDataType = primitiveApplicator.get64PointerType(type,
+ primitiveApplicator.get32BitPartialPrecisionComplexType());
+ break;
+ // 128-bit near pointer to a 32-bit partial-precision complex (LLVM doc on 0x0700)
+ case 0x0755:
+ primitiveDataType = primitiveApplicator.get128PointerType(type,
+ primitiveApplicator.get32BitPartialPrecisionComplexType());
+ break;
+
+ //=======================================
+ // 16-bit complex types
+ //=======================================
+ // 16-bit complex
+ case 0x0056:
+ primitiveDataType = primitiveApplicator.get16BitComplexType();
+ break;
+ // 16-bit pointer to a 16-bit complex
+ case 0x0156:
+ primitiveDataType = primitiveApplicator.get16NearPointerType(type,
+ primitiveApplicator.get16BitComplexType());
+ break;
+ // 16:16 far pointer to a 16-bit complex
+ case 0x0256:
+ primitiveDataType = primitiveApplicator.get1616FarPointerType(type,
+ primitiveApplicator.get16BitComplexType());
+ break;
+ // 16:16 huge pointer to a 16-bit complex
+ case 0x0356:
+ primitiveDataType = primitiveApplicator.get1616HugePointerType(type,
+ primitiveApplicator.get16BitComplexType());
+ break;
+ // 32-bit pointer to a 16-bit complex
+ case 0x0456:
+ primitiveDataType = primitiveApplicator.get32PointerType(type,
+ primitiveApplicator.get16BitComplexType());
+ break;
+ // 16:32 pointer to a 16-bit complex
+ case 0x0556:
+ primitiveDataType = primitiveApplicator.get1632PointerType(type,
+ primitiveApplicator.get16BitComplexType());
+ break;
+ // 64-bit pointer to a 16-bit complex
+ case 0x0656:
+ primitiveDataType = primitiveApplicator.get64PointerType(type,
+ primitiveApplicator.get16BitComplexType());
+ break;
+ // 128-bit near pointer to a 16-bit complex (LLVM doc on 0x0700)
+ case 0x0756:
+ primitiveDataType = primitiveApplicator.get128PointerType(type,
+ primitiveApplicator.get16BitComplexType());
+ break;
+
+ //=======================================
+ // 8-bit boolean types
+ //=======================================
+ // 8-bit boolean
+ case 0x0030:
+ primitiveDataType = primitiveApplicator.get8BitBooleanType();
+ break;
+ // 16-bit pointer to an 8-bit boolean
+ case 0x0130:
+ primitiveDataType = primitiveApplicator.get16NearPointerType(type,
+ primitiveApplicator.get8BitBooleanType());
+ break;
+ // 16:16 far pointer to an 8-bit boolean
+ case 0x0230:
+ primitiveDataType = primitiveApplicator.get1616FarPointerType(type,
+ primitiveApplicator.get8BitBooleanType());
+ break;
+ // 16:16 huge pointer to an 8-bit boolean
+ case 0x0330:
+ primitiveDataType = primitiveApplicator.get1616HugePointerType(type,
+ primitiveApplicator.get8BitBooleanType());
+ break;
+ // 32-bit pointer to an 8-bit boolean
+ case 0x0430:
+ primitiveDataType = primitiveApplicator.get32PointerType(type,
+ primitiveApplicator.get8BitBooleanType());
+ break;
+ // 16:32 pointer to an 8-bit boolean
+ case 0x0530:
+ primitiveDataType = primitiveApplicator.get1632PointerType(type,
+ primitiveApplicator.get8BitBooleanType());
+ break;
+ // 64-bit pointer to an 8-bit boolean
+ case 0x0630:
+ primitiveDataType = primitiveApplicator.get64PointerType(type,
+ primitiveApplicator.get8BitBooleanType());
+ break;
+ // 128-bit near pointer to an 8-bit boolean (LLVM doc on 0x0700)
+ case 0x0730:
+ primitiveDataType = primitiveApplicator.get128PointerType(type,
+ primitiveApplicator.get8BitBooleanType());
+ break;
+
+ //=======================================
+ // 16-bit boolean types
+ //=======================================
+ // 16-bit boolean
+ case 0x0031:
+ primitiveDataType = primitiveApplicator.get16BitBooleanType();
+ break;
+ // 16-bit pointer to a 16-bit boolean
+ case 0x0131:
+ primitiveDataType = primitiveApplicator.get16NearPointerType(type,
+ primitiveApplicator.get16BitBooleanType());
+ break;
+ // 16:16 far pointer to a 16-bit boolean
+ case 0x0231:
+ primitiveDataType = primitiveApplicator.get1616FarPointerType(type,
+ primitiveApplicator.get16BitBooleanType());
+ break;
+ // 16:16 huge pointer to a 16-bit boolean
+ case 0x0331:
+ primitiveDataType = primitiveApplicator.get1616HugePointerType(type,
+ primitiveApplicator.get16BitBooleanType());
+ break;
+ // 32-bit pointer to a 16-bit boolean
+ case 0x0431:
+ primitiveDataType = primitiveApplicator.get32PointerType(type,
+ primitiveApplicator.get16BitBooleanType());
+ break;
+ // 16:32 pointer to a 16-bit boolean
+ case 0x0531:
+ primitiveDataType = primitiveApplicator.get1632PointerType(type,
+ primitiveApplicator.get16BitBooleanType());
+ break;
+ // 64-bit pointer to a 16-bit boolean
+ case 0x0631:
+ primitiveDataType = primitiveApplicator.get64PointerType(type,
+ primitiveApplicator.get16BitBooleanType());
+ break;
+ // 128-bit near pointer to a 16-bit boolean (LLVM doc on 0x0700)
+ case 0x0731:
+ primitiveDataType = primitiveApplicator.get128PointerType(type,
+ primitiveApplicator.get16BitBooleanType());
+ break;
+
+ //=======================================
+ // 32-bit boolean types
+ //=======================================
+ // 32-bit boolean
+ case 0x0032:
+ primitiveDataType = primitiveApplicator.get32BitBooleanType();
+ break;
+ // 16-bit pointer to a 32-bit boolean
+ case 0x0132:
+ primitiveDataType = primitiveApplicator.get16NearPointerType(type,
+ primitiveApplicator.get32BitBooleanType());
+ break;
+ // 16:16 far pointer to a 32-bit boolean
+ case 0x0232:
+ primitiveDataType = primitiveApplicator.get1616FarPointerType(type,
+ primitiveApplicator.get32BitBooleanType());
+ break;
+ // 16:16 huge pointer to a 32-bit boolean
+ case 0x0332:
+ primitiveDataType = primitiveApplicator.get1616HugePointerType(type,
+ primitiveApplicator.get32BitBooleanType());
+ break;
+ // 32-bit pointer to a 32-bit boolean
+ case 0x0432:
+ primitiveDataType = primitiveApplicator.get32PointerType(type,
+ primitiveApplicator.get32BitBooleanType());
+ break;
+ // 16:32 pointer to a 32-bit boolean
+ case 0x0532:
+ primitiveDataType = primitiveApplicator.get1632PointerType(type,
+ primitiveApplicator.get32BitBooleanType());
+ break;
+ // 64-bit pointer to a 32-bit boolean
+ case 0x0632:
+ primitiveDataType = primitiveApplicator.get64PointerType(type,
+ primitiveApplicator.get32BitBooleanType());
+ break;
+ // 128-bit near pointer to a 32-bit boolean (LLVM doc on 0x0700)
+ case 0x0732:
+ primitiveDataType = primitiveApplicator.get128PointerType(type,
+ primitiveApplicator.get32BitBooleanType());
+ break;
+
+ //=======================================
+ // 64-bit boolean types
+ //=======================================
+ // 64-bit boolean
+ case 0x0033:
+ primitiveDataType = primitiveApplicator.get64BitBooleanType();
+ break;
+ // 16-bit pointer to a 64-bit boolean
+ case 0x0133:
+ primitiveDataType = primitiveApplicator.get16NearPointerType(type,
+ primitiveApplicator.get64BitBooleanType());
+ break;
+ // 16:16 far pointer to a 64-bit boolean
+ case 0x0233:
+ primitiveDataType = primitiveApplicator.get1616FarPointerType(type,
+ primitiveApplicator.get64BitBooleanType());
+ break;
+ // 16:16 huge pointer to a 64-bit boolean
+ case 0x0333:
+ primitiveDataType = primitiveApplicator.get1616HugePointerType(type,
+ primitiveApplicator.get64BitBooleanType());
+ break;
+ // 32-bit pointer to a 64-bit boolean
+ case 0x0433:
+ primitiveDataType = primitiveApplicator.get32PointerType(type,
+ primitiveApplicator.get64BitBooleanType());
+ break;
+ // 16:32 pointer to a 64-bit boolean
+ case 0x0533:
+ primitiveDataType = primitiveApplicator.get1632PointerType(type,
+ primitiveApplicator.get64BitBooleanType());
+ break;
+ // 64-bit pointer to a 64-bit boolean
+ case 0x0633:
+ primitiveDataType = primitiveApplicator.get64PointerType(type,
+ primitiveApplicator.get64BitBooleanType());
+ break;
+ // 128-bit near pointer to a 64-bit boolean (LLVM doc on 0x0700)
+ case 0x0733:
+ primitiveDataType = primitiveApplicator.get128PointerType(type,
+ primitiveApplicator.get64BitBooleanType());
+ break;
+
+ //=======================================
+ // 128-bit boolean types
+ //=======================================
+ // 128-bit boolean
+ case 0x0034:
+ primitiveDataType = primitiveApplicator.get128BitBooleanType();
+ break;
+ // 16-bit pointer to a 128-bit boolean
+ case 0x0134:
+ primitiveDataType = primitiveApplicator.get16NearPointerType(type,
+ primitiveApplicator.get128BitBooleanType());
+ break;
+ // 16:16 far pointer to a 128-bit boolean
+ case 0x0234:
+ primitiveDataType = primitiveApplicator.get1616FarPointerType(type,
+ primitiveApplicator.get128BitBooleanType());
+ break;
+ // 16:16 huge pointer to a 128-bit boolean
+ case 0x0334:
+ primitiveDataType = primitiveApplicator.get1616HugePointerType(type,
+ primitiveApplicator.get128BitBooleanType());
+ break;
+ // 32-bit pointer to a 128-bit boolean
+ case 0x0434:
+ primitiveDataType = primitiveApplicator.get32PointerType(type,
+ primitiveApplicator.get128BitBooleanType());
+ break;
+ // 16:32 pointer to a 128-bit boolean
+ case 0x0534:
+ primitiveDataType = primitiveApplicator.get1632PointerType(type,
+ primitiveApplicator.get128BitBooleanType());
+ break;
+ // 64-bit pointer to a 128-bit boolean
+ case 0x0634:
+ primitiveDataType = primitiveApplicator.get64PointerType(type,
+ primitiveApplicator.get128BitBooleanType());
+ break;
+ // 128-bit near pointer to a 128-bit boolean (LLVM doc on 0x0700)
+ case 0x0734:
+ primitiveDataType = primitiveApplicator.get128PointerType(type,
+ primitiveApplicator.get128BitBooleanType());
+ break;
+
+ //=======================================
+ // Internal type with pointers
+ //=======================================
+ // CV Internal type for created near pointers
+ case 0x01f0:
+ primitiveDataType = primitiveApplicator.get16NearPointerType(type,
+ primitiveApplicator.createTypedefNamedSizedType("CVInternal", 1));
+ break;
+ // CV Internal type for created far pointers
+ case 0x02f0:
+ primitiveDataType = primitiveApplicator.get1616FarPointerType(type,
+ primitiveApplicator.createTypedefNamedSizedType("CVInternal", 1));
+ break;
+ // CV Internal type for created huge pointers
+ case 0x03f0:
+ primitiveDataType = primitiveApplicator.get1616HugePointerType(type,
+ primitiveApplicator.createTypedefNamedSizedType("CVInternal", 1));
+ break;
+ // CV Internal type for created near 32-bit pointers
+ case 0x04f0:
+ primitiveDataType = primitiveApplicator.get32PointerType(type,
+ primitiveApplicator.createTypedefNamedSizedType("CVInternal", 1));
+ break;
+ // CV Internal type for created far 32-bit pointers
+ case 0x05f0:
+ primitiveDataType = primitiveApplicator.get1632PointerType(type,
+ primitiveApplicator.createTypedefNamedSizedType("CVInternal", 1));
+ break;
+ // CV Internal type for created near 64-bit pointers
+ case 0x06f0:
+ primitiveDataType = primitiveApplicator.get64PointerType(type,
+ primitiveApplicator.createTypedefNamedSizedType("CVInternal", 1));
+ break;
+ // CV Internal type for created near 128-bit near pointers (LLVM doc on 0x0700)
+ case 0x07f0:
+ primitiveDataType = primitiveApplicator.get128PointerType(type,
+ primitiveApplicator.createTypedefNamedSizedType("CVInternal", 1));
+ break;
+
+ default:
+ // Note: 0x0400 seems to have been used as a this pointer type.
+ primitiveDataType = primitiveApplicator.createUnmappedPdbType(type);
+ break;
+
+ }
+
+ return primitiveDataType;
+ }
+
+}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/ProcedureTypeApplier.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/ProcedureTypeApplier.java
new file mode 100644
index 0000000000..5d59900bb0
--- /dev/null
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/ProcedureTypeApplier.java
@@ -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);
+ }
+
+}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PublicSymbolApplier.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PublicSymbolApplier.java
new file mode 100644
index 0000000000..4805f4f0f3
--- /dev/null
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/PublicSymbolApplier.java
@@ -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 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();
+ }
+}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/ReferenceSymbolApplier.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/ReferenceSymbolApplier.java
new file mode 100644
index 0000000000..038b6b090c
--- /dev/null
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/ReferenceSymbolApplier.java
@@ -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;
+ }
+
+}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/RegisterRelativeSymbolApplier.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/RegisterRelativeSymbolApplier.java
new file mode 100644
index 0000000000..a11e642686
--- /dev/null
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/RegisterRelativeSymbolApplier.java
@@ -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;
+ }
+
+}
diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/SeparatedCodeSymbolApplier.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/SeparatedCodeSymbolApplier.java
new file mode 100644
index 0000000000..7c873bb350
--- /dev/null
+++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/SeparatedCodeSymbolApplier.java
@@ -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