Merge remote-tracking branch 'origin/GP-177_ghidra1_StickyAnalyzerEnablement'

This commit is contained in:
ghidra1 2020-09-18 16:28:37 -04:00
commit ef1a39fe23
99 changed files with 27948 additions and 123 deletions

View File

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

View File

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

View File

@ -15,13 +15,18 @@
*/
package ghidra.app.plugin.core.analysis;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import ghidra.app.services.Analyzer;
import ghidra.app.util.importer.MessageLog;
import ghidra.framework.options.Options;
import ghidra.framework.preferences.Preferences;
import ghidra.program.model.address.*;
import ghidra.program.model.lang.Language;
import ghidra.program.model.listing.Program;
import ghidra.util.Msg;
import ghidra.util.SystemUtilities;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
@ -42,11 +47,44 @@ public class AnalysisScheduler {
throw new IllegalArgumentException("Analyzer name may not contain a period: " +
analyzer.getName());
}
this.enabled = analyzer.getDefaultEnablement(analysisMgr.getProgram());
boolean defaultEnable = analyzer.getDefaultEnablement(analysisMgr.getProgram());
enabled = getEnableOverride(defaultEnable);
if (rememberEnablementChangeAsUserPreference()) {
String val =
Preferences.getProperty(getAnalyzerPreferenceName(), Boolean.toString(enabled));
enabled = Boolean.valueOf(val);
}
else if (defaultEnable != enabled) {
Msg.warn(this,
"Analyzer \'" + analyzer.getName() + "\' for " +
analysisMgr.getProgram().getName() + " " + (enabled ? "enabled" : "disabled") +
" by PSPEC file override");
}
removeSet = new AddressSet();
addSet = new AddressSet();
}
private boolean rememberEnablementChangeAsUserPreference() {
if (!analyzer.rememberEnablementChangeAsUserPreference()) {
return false;
}
if (SystemUtilities.isInTestingMode() || SystemUtilities.isInHeadlessMode()) {
return false;
}
return true;
}
private String getAnalyzerPreferenceName() {
String str = analyzer.getName();
try {
str = URLEncoder.encode(str, "UTF8");
}
catch (UnsupportedEncodingException e) {
// ignore
}
return "Analyzers." + str;
}
synchronized void schedule() {
// if not scheduled right now, schedule it
if (!scheduled && (!addSet.isEmpty() || !removeSet.isEmpty())) {
@ -116,29 +154,20 @@ public class AnalysisScheduler {
boolean defaultEnable = analyzer.getDefaultEnablement(analysisMgr.getProgram());
defaultEnable = getEnableOverride(defaultEnable);
enabled = options.getBoolean(analyzer.getName(), defaultEnable);
boolean state = options.getBoolean(analyzer.getName(), defaultEnable);
if (state != enabled && rememberEnablementChangeAsUserPreference()) {
Preferences.setProperty(getAnalyzerPreferenceName(), Boolean.toString(state));
}
enabled = state;
analyzer.optionsChanged(options.getOptions(analyzer.getName()), analysisMgr.getProgram());
}
public void registerOptions(Options options) {
Options analyzerOptions = options.getOptions(analyzer.getName());
boolean defaultEnable = analyzer.getDefaultEnablement(analysisMgr.getProgram());
boolean overrideEnable = getEnableOverride(defaultEnable);
// only warn when option registered
if (defaultEnable != overrideEnable) {
Msg.warn(this,
"Analyzer \'" + analyzer.getName() + "\' for " +
analysisMgr.getProgram().getName() + " " +
(overrideEnable ? "enabled" : "disabled") + " by PSPEC file override");
}
options.registerOption(analyzer.getName(),
overrideEnable, null,
analyzer.getDescription());
enabled, null, analyzer.getDescription());
analyzer.registerOptions(analyzerOptions, analysisMgr.getProgram());
}

View File

@ -36,6 +36,7 @@ import ghidra.framework.cmd.BackgroundCommand;
import ghidra.framework.model.*;
import ghidra.framework.options.Options;
import ghidra.framework.plugintool.PluginTool;
import ghidra.framework.preferences.Preferences;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.*;
import ghidra.program.util.*;
@ -394,6 +395,7 @@ public class AutoAnalysisManager implements DomainObjectListener, DomainObjectCl
case DomainObject.DO_PROPERTY_CHANGED:
if (!optionsChanged) {
initializeOptions();
Preferences.store();
optionsChanged = true;
}
break;
@ -1054,6 +1056,7 @@ public class AutoAnalysisManager implements DomainObjectListener, DomainObjectCl
initializeOptions(options);
}
catch (OptionsVetoException e) {
// FIXME!! Not good to popup for all use cases
// This will only happen if an Analyzer author makes a mistake
Msg.showError(this, null, "Invalid Analysis Option",
"Invalid Analysis option set during initialization", e);

View File

@ -41,6 +41,12 @@ public class StackVariableAnalyzer extends AbstractAnalyzer {
setSupportsOneTimeAnalysis();
}
@Override
public boolean rememberEnablementChangeAsUserPreference() {
return true;
}
@Override
public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log) {
BackgroundCommand cmd;
@ -79,6 +85,7 @@ public class StackVariableAnalyzer extends AbstractAnalyzer {
"Create Function Parameter stack variables and references");
}
@Override
public void optionsChanged(Options options, Program program) {
doNewStackAnalysis =
options.getBoolean(GhidraLanguagePropertyKeys.USE_NEW_FUNCTION_STACK_ANALYSIS,

View File

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -73,6 +72,11 @@ public abstract class AbstractAnalyzer implements Analyzer {
return defaultEnablement;
}
@Override
public boolean rememberEnablementChangeAsUserPreference() {
return false;
}
@Override
public final boolean supportsOneTimeAnalysis() {
return supportsOneTimeAnalysis;

View File

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -103,10 +102,18 @@ public interface Analyzer extends ExtensionPoint {
public boolean removed(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log)
throws CancelledException;
/**
* Analyzers should register their options with associated default value, help content and
* description
* @param options the program options/property list that contains the options
* @param program program to be analyzed
*/
public void registerOptions(Options options, Program program);
/**
* Analyzers should initialize their options from the values in the given Options,
* providing appropriate default values.
* @param options the property list that contains the options
* @param options the program options/property list that contains the options
* @param program program to be analyzed
*/
public void optionsChanged(Options options, Program program);
@ -124,6 +131,13 @@ public interface Analyzer extends ExtensionPoint {
*/
public boolean isPrototype();
public void registerOptions(Options options, Program program);
/**
* Returns true if a change to the analyzer enablement should be saved as a user preference
* so that it may be establish the default enablement for analysis in other programs.
* NOTE: This feature is ignored while operating in test mode or headless mode where
* it is expected that explicit enablement/disablement will be utilized.
* @return true if an enablement change should be remembered as the new default
*/
public boolean rememberEnablementChangeAsUserPreference();
}

View File

@ -306,6 +306,15 @@ public abstract class DemangledObject implements Demangled {
public boolean applyTo(Program program, Address address, DemanglerOptions options,
TaskMonitor monitor) throws Exception {
return applyPlateCommentOnly(program, address);
}
/**
* @param program The program for which to apply the comment
* @param address The address for the comment
* @return {@code true} if a comment was applied
*/
public boolean applyPlateCommentOnly(Program program, Address address) {
if (mangled.equals(name)) {
return false;
}
@ -443,11 +452,8 @@ public abstract class DemangledObject implements Demangled {
List<Symbol> 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 =

View File

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

View File

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

View File

@ -228,4 +228,9 @@ public abstract class FileFormatAnalyzer implements Analyzer {
protected Address find(Program program, Address start, byte[] values, TaskMonitor monitor) {
return program.getMemory().findBytes(start, values, null, true, monitor);
}
@Override
public boolean rememberEnablementChangeAsUserPreference() {
return false;
}
}

View File

@ -0,0 +1,108 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Text-dump all data types from the user-specified DataTypeManager to the user-specified file.
//
//@category Data Types
import java.io.File;
import java.io.FileWriter;
import java.util.Iterator;
import docking.widgets.OptionDialog;
import ghidra.app.script.GhidraScript;
import ghidra.app.services.DataTypeManagerService;
import ghidra.app.util.ToolTipUtils;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.data.*;
import ghidra.util.HTMLUtilities;
import ghidra.util.Msg;
import ghidra.util.exception.CancelledException;
public class DeveloperDumpAllTypesScript extends GhidraScript {
@Override
protected void run() throws Exception, CancelledException {
DataTypeManager manager = userChooseDataTypeManager();
if (manager == null) {
return;
}
File dumpFile = askFile("Choose an output file", "OK");
if (dumpFile == null) {
Msg.info(this, "Canceled execution due to no output file");
return;
}
if (dumpFile.exists()) {
if (!askYesNo("Confirm Overwrite", "Overwrite file: " + dumpFile.getName())) {
Msg.info(this, "Operation canceled");
return;
}
}
FileWriter fileWriter = new FileWriter(dumpFile);
String message = "Outputting DataTypes from: " + manager.getName();
Iterator<DataType> allDataTypes = manager.getAllDataTypes();
while (allDataTypes.hasNext()) {
monitor.checkCanceled();
DataType dataType = allDataTypes.next();
DataTypePath dataTypePath = dataType.getDataTypePath();
String pathString = dataTypePath.toString();
String htmlString = ToolTipUtils.getToolTipText(dataType);
String plainString = HTMLUtilities.fromHTML(htmlString);
fileWriter.append(pathString);
fileWriter.append("\n");
fileWriter.append(plainString);
fileWriter.append("\n");
fileWriter.append("------------------------------------------------------------\n");
}
fileWriter.close();
message = "Results located in: " + dumpFile.getAbsoluteFile();
monitor.setMessage(message);
Msg.info(this, message);
}
private DataTypeManager userChooseDataTypeManager() {
PluginTool tool = state.getTool();
DataTypeManagerService service = tool.getService(DataTypeManagerService.class);
DataTypeManager[] dataTypeManagers = service.getDataTypeManagers();
String names[] = new String[dataTypeManagers.length];
String initialDtmChoice = names[0];
try {
initialDtmChoice = currentProgram.getDataTypeManager().getName();
}
catch (Exception e) {
// Ignore... assuming no program or dtm.
}
for (int i = 0; i < dataTypeManagers.length; i++) {
names[i] = dataTypeManagers[i].getName();
}
String userChoice =
OptionDialog.showInputChoiceDialog(null, "Choose a Data Type Manager or Cancel",
"Choose", names, initialDtmChoice, OptionDialog.CANCEL_OPTION);
if (userChoice == null) {
return null;
}
for (int i = 0; i < dataTypeManagers.length; i++) {
if (names[i].contentEquals(userChoice)) {
return dataTypeManagers[i];
}
}
return null; // should not reach this line.
}
}

View File

@ -0,0 +1,101 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Script for ghizard developer investigating template and ICF squashing.
//
//@category PDB
import java.io.*;
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.symbol.*;
import ghidra.util.Msg;
import mdemangler.*;
import mdemangler.object.MDObjectCPP;
public class DumpAllSymbolsDemangledScript extends GhidraScript {
@Override
public void run() throws Exception {
File dumpFile = askFile("Choose an output file", "OK");
if (dumpFile == null) {
Msg.info(this, "Canceled execution due to no output file");
return;
}
if (dumpFile.exists()) {
if (!askYesNo("Confirm Overwrite", "Overwrite file: " + dumpFile.getName())) {
Msg.info(this, "Operation canceled");
return;
}
}
FileWriter fileWriter = new FileWriter(dumpFile);
BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
SymbolTable st = currentProgram.getSymbolTable();
SymbolIterator it = st.getDefinedSymbols();
while (it.hasNext() && !monitor.isCancelled()) {
Symbol s = it.next();
if (s.getSource() == SourceType.DEFAULT) {
continue;
}
Address addr = s.getAddress();
if (addr.isExternalAddress() || Address.NO_ADDRESS.equals(addr)) {
continue;
}
String name = s.getName();
String demangled = getDemangledString(name);
if (demangled != null && !demangled.isBlank()) {
name = demangled;
}
bufferedWriter.append(addr + " " + name + "\n");
}
bufferedWriter.close();
}
/**
* Gets a demangled string for the indicated mangled string.
* @param mangledString the mangled string to be decoded
* @return the associated demangled string
*/
private static String getDemangledString(String mangledString) {
MDMangGhidra demangler = new MDMangGhidra();
try {
MDParsableItem parsableItem = demangler.demangle(mangledString, true);
if (parsableItem instanceof MDObjectCPP) {
MDObjectCPP mdObject = (MDObjectCPP) parsableItem;
return mdObject.getQualifiedName().toString();
}
// if (parsableItem instanceof MDFunctionType) {
// MDFunctionType functionType = (MDFunctionType) parsableItem;
// return functionType.getName();
// }
// if (parsableItem instanceof MDDataType) {
// MDDataType dataType = (MDDataType) parsableItem;
// return dataType.getName();
// }
return parsableItem.toString();
}
catch (MDException e) {
// Couldn't demangle.
Msg.info(null, e.getMessage());
return null;
}
}
}

View File

@ -0,0 +1,88 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Dump PDB information from AbstractPdb for PDB developer use.
//
//@category PDB
import java.io.*;
import ghidra.app.script.GhidraScript;
import ghidra.app.util.bin.format.pdb2.pdbreader.*;
import ghidra.util.Msg;
public class PdbDeveloperDumpScript extends GhidraScript {
@Override
protected void run() throws Exception {
File pdbFile = askFile("Choose a PDB file", "OK");
if (pdbFile == null) {
Msg.info(this, "Canceled execution due to no input file");
return;
}
if (!pdbFile.exists()) {
String message = pdbFile.getAbsolutePath() + " is not a valid file.";
Msg.info(this, message);
popup(message);
return;
}
String pdbFileName = pdbFile.getAbsolutePath();
if (!pdbFileName.endsWith(".pdb") && !pdbFileName.endsWith(".PDB")) {
String message = "Aborting: Expected input file to have extension of type .pdb (got '" +
pdbFileName + "').";
Msg.info(this, message);
popup(message);
return;
}
File dumpFile = askFile("Choose an output file", "OK");
if (dumpFile == null) {
Msg.info(this, "Canceled execution due to no output file");
return;
}
if (dumpFile.exists()) {
if (!askYesNo("Confirm Overwrite", "Overwrite file: " + dumpFile.getName())) {
Msg.info(this, "Operation canceled");
return;
}
}
String message = "Processing PDB Dump of: " + pdbFileName;
monitor.setMessage(message);
Msg.info(this, message);
try (AbstractPdb pdb = PdbParser.parse(pdbFileName, new PdbReaderOptions(), monitor)) {
pdb.deserialize(monitor);
FileWriter fileWriter = new FileWriter(dumpFile);
BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
outputHeaderMessage(bufferedWriter, pdbFileName);
pdb.dumpDirectory(bufferedWriter);
pdb.dumpSubStreams(bufferedWriter);
bufferedWriter.close();
}
catch (IOException ioe) {
Msg.info(this, ioe.getMessage());
popup(ioe.getMessage());
}
message = "Results located in: " + dumpFile.getAbsoluteFile();
monitor.setMessage(message);
Msg.info(this, message);
}
private void outputHeaderMessage(BufferedWriter bufferedWriter, String name) throws Exception {
bufferedWriter.append(getClass().getSimpleName() + " dump of: " + name +
"\nWARNING: FORMAT SUBJECT TO CHANGE WITHOUT NOTICE--DO NOT PARSE\n\n");
}
}

View File

@ -63,6 +63,11 @@ public class PdbAnalyzer extends AbstractAnalyzer {
setSupportsOneTimeAnalysis();
}
@Override
public boolean rememberEnablementChangeAsUserPreference() {
return true;
}
@Override
public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log) {
@ -75,6 +80,7 @@ public class PdbAnalyzer extends AbstractAnalyzer {
if (pdb == null) {
return false;
}
Msg.info(this, getClass().getSimpleName() + " configured to use: " + pdb.getAbsolutePath());
AutoAnalysisManager mgr = AutoAnalysisManager.getAnalysisManager(program);
return parsePdb(pdb, program, mgr, monitor, log);

View File

@ -0,0 +1,588 @@
/* ###
* 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.PdbLocator;
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.AssertException;
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. It has Universal in the name to describe the fact that
* it written in java, making it platform independent, unlike a previous PDB analyzer.
*/
public class PdbUniversalAnalyzer 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
//==============================================================================================
// Applicator Restrictions.
private static final String OPTION_NAME_PROCESSING_RESTRICTIONS = "Processing Restrictions";
private static final String OPTION_DESCRIPTION_PROCESSING_RESTRICTIONS =
"Restrictions on applicator processing.";
private static PdbApplicatorRestrictions restrictions;
// 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.
// Legacy
// - similar to existing DIA-based PDB Analyzer, only placing current composite direct
// members (none from parent classes.
// Warning: the remaining experimental layout choices 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 = "Composite Layout Choice";
private static final String OPTION_DESCRIPTION_COMPOSITE_LAYOUT =
"Legacy layout like original PDB Analyzer. Warning: other choices have no compatibility" +
" guarantee with future Ghidra releases or minor PDB Analyzer changes";
private ObjectOrientedClassLayout compositeLayout;
//==============================================================================================
// Additional instance data
//==============================================================================================
private PdbReaderOptions pdbReaderOptions;
private PdbApplicatorOptions pdbApplicatorOptions;
//==============================================================================================
//==============================================================================================
public PdbUniversalAnalyzer() {
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 rememberEnablementChangeAsUserPreference() {
return true;
}
@Override
public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log)
throws CancelledException {
// 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 DIA-based PDB Analyzer enabled");
return false;
}
PdbProgramAttributes programAttributes = new PdbProgramAttributes(program);
if (failMissingFilename(programAttributes, log) ||
failMissingAttributes(programAttributes, log)) {
return true;
}
setPdbLogging(log);
String pdbFilename;
if (doForceLoad) {
pdbFilename = forceLoadFile.getAbsolutePath();
}
else {
PdbLocator locator = new PdbLocator(symbolsRepositoryPath);
pdbFilename =
locator.findPdb(program, programAttributes, !SystemUtilities.isInHeadlessMode(),
includePeSpecifiedPdbPath, monitor, log, getName());
if (pdbFilename == null) {
return false;
}
}
Msg.info(this, getClass().getSimpleName() + " configured to use: " + 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)) {
monitor.setMessage("PDB: Parsing " + pdbFilename + "...");
pdb.deserialize(monitor);
PdbApplicator applicator = new PdbApplicator(pdbFilename, pdb);
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) {
// 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);
if (developerMode) {
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_PROCESSING_RESTRICTIONS, restrictions, null,
OPTION_DESCRIPTION_PROCESSING_RESTRICTIONS);
if (developerMode) {
options.registerOption(OPTION_NAME_APPLY_CODE_SCOPE_BLOCK_COMMENTS,
applyCodeScopeBlockComments, null,
OPTION_DESCRIPTION_APPLY_CODE_SCOPE_BLOCK_COMMENTS);
// 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);
// The remap capability is not completely implemented... do not turn on.
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);
// Function params and local implementation is not complete... do not turn on.
options.registerOption(OPTION_NAME_APPLY_FUNCTION_VARIABLES, applyFunctionVariables,
null, OPTION_DESCRIPTION_APPLY_FUNCTION_VARIABLES);
// Object-oriented composite layout is fairly far along, but its use will likely not
// be forward compatible with future Ghidra work in this area; i.e., it might leave
// the data type manager in a bad state for future revisions. While the current
// layout mechanism might work, I will likely change it to, instead, create a
// syntactic intermediate representation before creating the final layout. This will
// aid portability between tool chains and versions and yield a standard way of
// data-basing and presenting the information to a user.
options.registerOption(OPTION_NAME_COMPOSITE_LAYOUT, compositeLayout, null,
OPTION_DESCRIPTION_COMPOSITE_LAYOUT);
}
}
@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);
// PdbReaderOptions
pdbLogging = options.getBoolean(OPTION_NAME_PDB_READER_ANALYZER_LOGGING, pdbLogging);
if (developerMode) {
oneByteCharsetName =
options.getString(OPTION_NAME_ONE_BYTE_CHARSET_NAME, oneByteCharsetName);
wideCharCharsetName =
options.getString(OPTION_NAME_WCHAR_CHARSET_NAME, wideCharCharsetName);
}
setPdbReaderOptions();
// PdbApplicatorOptions
restrictions = options.getEnum(OPTION_NAME_PROCESSING_RESTRICTIONS, restrictions);
if (developerMode) {
applyCodeScopeBlockComments = options.getBoolean(
OPTION_NAME_APPLY_CODE_SCOPE_BLOCK_COMMENTS, applyCodeScopeBlockComments);
// 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);
compositeLayout = options.getEnum(OPTION_NAME_COMPOSITE_LAYOUT, compositeLayout);
}
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.
if (NAME.contentEquals(oldPdbNAME)) {
throw new AssertException(
"Developer error: old and new PDB analyzers were not renamed correctly");
}
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(MessageLog log) {
try {
PdbLog.setEnabled(pdbLogging);
}
catch (IOException e) {
// Probably could not open the file.
if (log != null) {
log.appendMsg(getClass().getSimpleName(),
"IOException when trying to open PdbLog file: ");
log.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.DEFAULT_APPLY_CODE_SCOPE_BLOCK_COMMENTS;
restrictions = PdbApplicatorOptions.DEFAULT_RESTRICTIONS;
applyInstructionLabels = PdbApplicatorOptions.DEFAULT_APPLY_INSTRUCTION_LABELS;
remapAddressUsingExistingPublicMangledSymbols =
PdbApplicatorOptions.DEFAULT_REMAP_ADDRESSES_USING_EXISTING_SYMBOLS;
allowDemotePrimaryMangledSymbol =
PdbApplicatorOptions.DEFAULT_ALLOW_DEMOTE_PRIMARY_MANGLED_SYMBOLS;
applyFunctionVariables = PdbApplicatorOptions.DEFAULT_APPLY_FUNCTION_VARIABLES;
compositeLayout = PdbApplicatorOptions.DEFAULT_CLASS_LAYOUT;
}
private void setPdbApplicatorOptions() {
pdbApplicatorOptions.setRestrictions(restrictions);
pdbApplicatorOptions.setApplyCodeScopeBlockComments(applyCodeScopeBlockComments);
pdbApplicatorOptions.setApplyInstructionLabels(applyInstructionLabels);
pdbApplicatorOptions.setRemapAddressUsingExistingPublicSymbols(
remapAddressUsingExistingPublicMangledSymbols);
pdbApplicatorOptions.setAllowDemotePrimaryMangledSymbols(allowDemotePrimaryMangledSymbol);
pdbApplicatorOptions.setApplyFunctionVariables(applyFunctionVariables);
pdbApplicatorOptions.setClassLayout(compositeLayout);
}
private boolean confirmDirectory(File path) {
return path.isDirectory();
}
private boolean confirmFile(File path) {
return path.isFile();
}
}

View File

@ -35,7 +35,7 @@ import ghidra.util.task.TaskMonitor;
* Container members are characterized by a null data-type name, zero length, and will be
* 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<String> errorConsumer, TaskMonitor monitor) throws CancelledException {

View File

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

View File

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

View File

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

View File

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

View File

@ -619,7 +619,7 @@ public abstract class AbstractPdb implements AutoCloseable {
builder.append("\nversionNumber: ");
builder.append(versionNumber);
builder.append("\nsignature: ");
builder.append(signature);
builder.append(Integer.toHexString(signature));
builder.append("\nage: ");
builder.append(age);
return builder.toString();

View File

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

View File

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

View File

@ -23,7 +23,8 @@ import ghidra.app.util.bin.format.pdb2.pdbreader.*;
* Note: we do not necessarily understand each of these symbol type classes. Refer to the
* 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.

View File

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

View File

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

View File

@ -0,0 +1,105 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.pdb;
import java.util.ArrayList;
import java.util.List;
import ghidra.app.util.SymbolPath;
import ghidra.program.model.symbol.SymbolUtilities;
public class PdbNamespaceUtils {
/**
* Fixes {@link SymbolPath} name, eliminating invalid characters and making the terminal
* name of the namespace unique by the index number when necessary. For example,
* anonymous and unnamed components such as {@code <unnamed-tag>} and {@code <unnamed-type>}
* are fixed up. Example:
* <pre>
* {@code _SYSTEM_INFO::<unnamed-tag>}
* </pre>
* @param symbolPath the source {@link SymbolPath}
* @param index the index number used be used as part of a unique tag name.
* @return the resulting {@link SymbolPath}
*/
public static SymbolPath convertToGhidraPathName(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 convertToGhidraPathName(SymbolPath symbolPath) {
symbolPath = symbolPath.replaceInvalidChars();
return symbolPath;
}
/**
* Fixes {@code <unnamed-tag>} and {@code <unnamed-type>} components of a namespace.
* <P>
* NOTE: This could be an issue when there are multiple unnamed items, such as in, for example:
* <pre>
* {@code _SYSTEM_INFO::<unnamed-tag>::<unnamed-tag>}
* </pre>
* @param symbolPath the source {@link SymbolPath}
* @param index the index number used be used as part of a unique tag name.
* @return the resulting {@link SymbolPath}
*/
public static SymbolPath convertToGhidraPath(SymbolPath symbolPath, int index) {
symbolPath = symbolPath.replaceInvalidChars();
List<String> modList = new ArrayList<>();
for (String str : symbolPath.asList()) {
modList.add(SymbolUtilities.replaceInvalidChars(fixUnnamed(str, index), true));
}
return new SymbolPath(modList);
// return getFixUpSymbolPathRecurse(symbolPath, index);
}
// private static SymbolPath getFixUpSymbolPathRecurse(SymbolPath symbolPath, int index) {
// SymbolPath parent = symbolPath.getParent();
// if (parent != null) {
// parent = getFixUpSymbolPathRecurse(parent, index);
// }
// return new SymbolPath(parent, fixName(symbolPath.getName(), index));
// }
//
/**
* Fixes-up {@code <unnamed-tag>} or {@code <unnamed-type>} component of a name.
* @param name original name.
* @param index the index number used be used as part of a unique tag name.
* @return the resulting name.
*/
// TODO: investigate if we can work a solution that no longer needs/has the index attached.
// I don't believe we are handling all of these correctly... someone else suggests regex, but
// that makes work in this area less clear until we know what we are doing...
// so, for now... DO NOT DO REGEX.
public static String fixUnnamed(String name, int index) {
if ("<unnamed-tag>".equals(name)) {
return String.format("<unnamed-tag_%08X>", index);
}
if ("<anonymous-tag>".equals(name)) {
return String.format("<anonymous-tag_%08X>", index);
}
if ("<unnamed-type>".equals(name)) {
return String.format("<unnamed-type_%08X>", index);
}
return name;
}
}

View File

@ -13,11 +13,11 @@
* See the License for the specific language governing permissions and
* 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<String> setOfPotentialFilenames = new LinkedHashSet<String>();
Set<String> set = new LinkedHashSet<>();
if (pdbFile != null) {
setOfPotentialFilenames.add(getFilename(pdbFile).toLowerCase());
setOfPotentialFilenames.add(pdbFile);
set.add(getFilename(pdbFile).toLowerCase());
set.add(getFilename(pdbFile));
set.add(pdbFile);
}
// getExecutablePath can return "unknown"
if (!executablePath.equals("unknown")) {
String executableFilename = getFilename(executablePath);
setOfPotentialFilenames.add(getBinaryBasename(executableFilename).toLowerCase() +
PdbFileType.PDB.toString());
set.add(
getBinaryBasename(executableFilename).toLowerCase() + ".pdb");
}
potentialPdbFilenames = new ArrayList<String>(setOfPotentialFilenames);
potentialPdbFilenames = new ArrayList<>(set);
}
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);
}

View File

@ -0,0 +1,112 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.pdb.pdbapplicator;
import 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.AbstractComplexMsType;
import ghidra.app.util.pdb.PdbNamespaceUtils;
import ghidra.program.model.data.DataType;
/**
* Applier for {@link AbstractComplexMsType} types.
*/
public abstract class AbstractComplexTypeApplier extends MsTypeApplier {
protected SymbolPath symbolPath;
protected SymbolPath fixedSymbolPath;
protected AbstractComplexTypeApplier definitionApplier = null;
protected AbstractComplexTypeApplier forwardReferenceApplier = 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 AbstractComplexMsType} to process.
*/
public AbstractComplexTypeApplier(PdbApplicator applicator, AbstractComplexMsType msType) {
super(applicator, msType);
String fullPathName = msType.getName();
symbolPath = new SymbolPath(SymbolPathParser.parse(fullPathName));
}
SymbolPath getSymbolPath() {
return symbolPath;
}
boolean isForwardReference() {
return ((AbstractComplexMsType) msType).getMsProperty().isForwardReference();
}
boolean isFinal() {
return ((AbstractComplexMsType) msType).getMsProperty().isSealed();
}
void setForwardReferenceApplier(AbstractComplexTypeApplier forwardReferenceApplier) {
this.forwardReferenceApplier = forwardReferenceApplier;
}
void setDefinitionApplier(AbstractComplexTypeApplier definitionApplier) {
this.definitionApplier = definitionApplier;
}
<T extends AbstractComplexTypeApplier> T getDefinitionApplier(Class<T> typeClass) {
if (!typeClass.isInstance(definitionApplier)) {
return null;
}
return typeClass.cast(definitionApplier);
}
protected AbstractComplexTypeApplier getAlternativeTypeApplier() {
if (isForwardReference()) {
return definitionApplier;
}
return forwardReferenceApplier;
}
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.convertToGhidraPathName(symbolPath, index);
if (symbolPath.equals(fixed)) {
fixedSymbolPath = symbolPath;
}
else {
fixedSymbolPath = fixed;
}
return fixedSymbolPath;
}
DataType getDataTypeInternal() {
return dataType;
}
}

View File

@ -0,0 +1,240 @@
/* ###
* 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 MsTypeApplier {
private FunctionDefinitionDataType functionDefinition;
private MsTypeApplier returnApplier;
private ArgumentsListTypeApplier argsListApplier;
private CallingConvention callingConvention;
private 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
*/
public AbstractFunctionTypeApplier(PdbApplicator applicator, AbstractMsType msType) {
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;
}
//==============================================================================================
@Override
void deferredApply() throws PdbException, CancelledException {
if (isDeferred()) {
applyInternal();
}
}
//==============================================================================================
/**
* Returns the function definition being created by this applier.
* @return the function definition.
*/
FunctionDefinitionDataType getFunctionDefinition() {
return functionDefinition;
}
@Override
DataType getCycleBreakType() {
if (dataType != null) {
return dataType;
}
return functionDefinition;
}
/**
* Returns the type applier of the return type
* @return the type applier
*/
MsTypeApplier getReturnTypeApplier() {
return applicator.getTypeApplier(getReturnRecordNumber());
}
/**
* Returns the {@link ArgumentsListTypeApplier}
* @return the type applier
*/
ArgumentsListTypeApplier getArgsListApplier() {
MsTypeApplier argsApplier = applicator.getTypeApplier(getArgListRecordNumber());
if (argsApplier instanceof ArgumentsListTypeApplier) {
return (ArgumentsListTypeApplier) applicator.getTypeApplier(getArgListRecordNumber());
}
return null;
}
/**
* Returns the {@link CallingConvention}
* @return the calling convention
*/
protected abstract CallingConvention getCallingConvention();
/**
* Returns whether the function has a "this" pointer
* @return {@code true} if it has a "this" pointer
*/
protected abstract boolean hasThisPointer();
/**
* Returns the {@link RecordNumber} of the function return type
* @return the record number
*/
protected abstract RecordNumber getReturnRecordNumber();
/**
* Returns the {@link RecordNumber} of the function arguments list
* @return the record number
*/
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();
}
}
private void applyInternal() throws CancelledException {
if (isApplied()) {
return;
}
if (!setReturnType()) {
return;
}
if (argsListApplier != null) {
argsListApplier.applyTo(this);
}
setCallingConvention(applicator, callingConvention, hasThisPointer);
setApplied();
}
private boolean setReturnType() {
DataType returnDataType = returnApplier.getDataType();
if (returnDataType == null) {
applicator.appendLogMsg("Return type is null in " + functionDefinition.getName());
return false;
}
functionDefinition.setReturnType(returnDataType);
return true;
}
private void setCallingConvention(PdbApplicator applicator, CallingConvention callingConvention,
boolean hasThisPointer) {
GenericCallingConvention convention;
if (hasThisPointer) {
convention = GenericCallingConvention.thiscall;
}
else {
// Since we are a member function, we will always assume a _thiscall...
// but how do we know it is not a atatic member function (no "this")?
switch (callingConvention) {
// TODO: figure all of these out.
case THISCALL: // "this" passed in register (we have not yet seen this)
convention = GenericCallingConvention.thiscall; // Is this correct if in reg?
break;
case NEAR_C: // we have seen this one
convention = GenericCallingConvention.cdecl;
break;
case NEAR_VECTOR: // we have seen this one
convention = GenericCallingConvention.vectorcall;
break;
default:
// applicator.getLog().appendMsg(
// "TODO: calling convention not implemented for value " + callingConventionVal +
// " in " + funcName);
//convention = GenericCallingConvention.cdecl;
convention = GenericCallingConvention.cdecl;
break;
}
}
functionDefinition.setGenericCallingConvention(convention);
}
}

View File

@ -0,0 +1,205 @@
/* ###
* 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.program.model.data.*;
import ghidra.util.exception.CancelledException;
/**
* Applier for {@link AbstractArgumentsListMsType} types.
*/
public class ArgumentsListTypeApplier extends MsTypeApplier {
/**
* 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, AbstractArgumentsListMsType msType)
throws IllegalArgumentException {
super(applicator, msType);
}
//==============================================================================================
@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
void apply() throws PdbException, CancelledException {
// addMyDependenciesOnly();
// // Silently do nothing.
}
@Override
BigInteger getSize() {
return BigInteger.ZERO;
}
// private void addMyDependenciesOnly() throws CancelledException, PdbException {
// AbstractArgumentsListMsType argsList = (AbstractArgumentsListMsType) msType;
// List<AbstractTypeIndex> list = argsList.getArgTypeIndexList();
// for (AbstractTypeIndex element : list) {
// applicator.checkCanceled();
// AbstractMsTypeApplier argApplier = applicator.getTypeApplier(element.get());
//
// if (argApplier instanceof PrimitiveTypeApplier &&
// ((PrimitiveTypeApplier) argApplier).isNoType()) {
// // Arguments list is empty. (There better not have been any arguments up until
// // now.)
// break;
// }
//
// if (argApplier instanceof AbstractDeferrableMsTypeApplier &&
// ((AbstractDeferrableMsTypeApplier) argApplier).isDeferred()) {
// applicator.addApplierDependency(this, argApplier);
// setDeferred();
// }
// }
// }
//
void checkForDependencies(AbstractFunctionTypeApplier functionApplier)
throws CancelledException {
AbstractArgumentsListMsType argsList = (AbstractArgumentsListMsType) msType;
List<RecordNumber> args = argsList.getArgRecordNumbers();
for (RecordNumber arg : args) {
applicator.checkCanceled();
MsTypeApplier 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
*/
void applyTo(AbstractFunctionTypeApplier functionApplier) throws CancelledException {
FunctionDefinitionDataType functionDefinition = functionApplier.getFunctionDefinition();
AbstractArgumentsListMsType argsList = (AbstractArgumentsListMsType) msType;
List<RecordNumber> args = argsList.getArgRecordNumbers();
List<ParameterDefinition> parameterDefinitionList = new ArrayList<>();
int parameterCount = 0;
for (RecordNumber arg : args) {
applicator.checkCanceled();
MsTypeApplier argApplier = applicator.getTypeApplier(arg);
if (argApplier instanceof PrimitiveTypeApplier &&
((PrimitiveTypeApplier) argApplier).isNoType()) {
// Arguments list is empty. (There better not have been any arguments up until
// now.)
break;
}
DataType argDataType = argApplier.getDataType();
if (argDataType == null) {
String message =
"PDB Warning: No type conversion for " + argApplier.getMsType().toString() +
" for parameter " + parameterCount + " of " + functionDefinition.getName();
applicator.appendLogMsg(message);
}
else {
ParameterDefinition parameterDefinition =
new ParameterDefinitionImpl(null, argDataType, "");
parameterDefinitionList.add(parameterDefinition);
parameterCount++;
}
}
functionDefinition.setArguments(parameterDefinitionList.toArray(
new ParameterDefinition[parameterDefinitionList.size()]));
}
// /**
// * Apply this to function ({@link AbstractFunctionTypeApplier}).
// * @param functionApplier the {@link AbstractFunctionTypeApplier} to which to apply the
// * arguments.
// * @throws PdbException when unexpected function internals are found.
// * @throws CancelledException Upon user cancellation
// */
// public void applyTo(AbstractFunctionTypeApplier functionApplier)
// throws CancelledException, PdbException {
// FunctionDefinitionDataType functionDefinition = functionApplier.getFunctionDefinition();
//
// AbstractArgumentsListMsType argsList = (AbstractArgumentsListMsType) msType;
// List<AbstractTypeIndex> list = argsList.getArgTypeIndexList();
// List<ParameterDefinition> parameterDefinitionList = new ArrayList<>();
// int parameterCount = 0;
// for (AbstractTypeIndex element : list) {
// applicator.getMonitor().checkCanceled();
// AbstractMsTypeApplier argApplier = applicator.getTypeApplier(element.get());
//
// if (argApplier instanceof PrimitiveTypeApplier &&
// ((PrimitiveTypeApplier) argApplier).isNoType()) {
// // Arguments list is empty. (There better not have been any arguments up until
// // now.)
// break;
// }
//
// if (argApplier instanceof AbstractDeferrableMsTypeApplier &&
// ((AbstractDeferrableMsTypeApplier) argApplier).isDeferred()) {
// applicator.addApplierDependency(functionApplier, argApplier);
// functionApplier.setDeferred();
// }
//
//// applicator.addApplierDependency(functionApplier, argApplier);
// DataType argDataType = argApplier.getDataType();
// if (argDataType == null) {
// String message =
// "PDB Warning: No type conversion for " + argApplier.getMsType().toString() +
// " for parameter " + parameterCount + " of " + functionDefinition.getName();
// applicator.getLog().appendMsg(message);
// }
// else {
// ParameterDefinition parameterDefinition =
// new ParameterDefinitionImpl(null, argDataType, "");
// parameterDefinitionList.add(parameterDefinition);
// parameterCount++;
// }
// }
// functionDefinition.setArguments(parameterDefinitionList.toArray(
// new ParameterDefinition[parameterDefinitionList.size()]));
// }
//
}

View File

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

View File

@ -0,0 +1,147 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.pdb.pdbapplicator;
import java.math.BigInteger;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.RecordNumber;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.*;
import ghidra.util.exception.CancelledException;
/**
* Applier for {@link AbstractBaseClassMsType}, {@link AbstractVirtualBaseClassMsType}, and
* {@link AbstractIndirectVirtualBaseClassMsType} types.
*/
public class BaseClassTypeApplier extends MsTypeApplier {
/**
* 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
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.
*/
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.
*/
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;
*/
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;
*/
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.
*/
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.
*/
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
void apply() throws PdbException, CancelledException {
// do nothing at the moment.
}
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;
}
}

View File

@ -0,0 +1,99 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.pdb.pdbapplicator;
import java.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.program.model.data.DataType;
import ghidra.program.model.data.InvalidDataTypeException;
import ghidra.util.exception.CancelledException;
/**
* Applier for {@link AbstractBitfieldMsType} types.
*/
public class BitfieldTypeApplier extends MsTypeApplier {
private MsTypeApplier elementTypeApplier = null;
/**
* Constructor for bitfield applier.
* @param applicator {@link PdbApplicator} for which this class is working.
* @param msType {@link AbstractBitfieldMsType} to processes
*/
public BitfieldTypeApplier(PdbApplicator applicator, AbstractBitfieldMsType msType) {
super(applicator, msType);
}
@Override
BigInteger getSize() {
if (elementTypeApplier == null) {
return BigInteger.ZERO;
}
return elementTypeApplier.getSize();
}
@Override
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
void resolve() {
// Do not resolve Bitfield Types... will be resolved with composite!!!
}
/**
* <code>Pdb2BitField</code> provides ability to hang onto bitfield as a datatype.
* This will be transformed to a normal BitFieldDataType when cloned.
*/
private class Pdb2BitField extends PdbBitField {
private Pdb2BitField(DataType baseDataType, int bitSize, int bitOffsetWithinBaseType)
throws InvalidDataTypeException {
super(baseDataType, bitSize, bitOffsetWithinBaseType);
}
}
}

View File

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

View File

@ -0,0 +1,67 @@
/* ###
* 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.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.exception.AssertException;
import ghidra.util.exception.CancelledException;
/**
* Applier for {@link AbstractBlockMsSymbol} symbols.
*/
public class BlockSymbolApplier extends MsSymbolApplier {
private AbstractBlockMsSymbol symbol;
/**
* Constructor
* @param applicator the {@link PdbApplicator} for which we are working.
* @param iter the Iterator containing the symbol sequence being processed
*/
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
void apply() throws PdbException, CancelledException {
pdbLogAndInfoMessage(this,
"Cannot apply " + this.getClass().getSimpleName() + " directly to program");
}
@Override
void applyTo(MsSymbolApplier applyToApplier) {
// Do nothing
}
@Override
void manageBlockNesting(MsSymbolApplier applierParam) {
if (applierParam instanceof FunctionSymbolApplier) {
FunctionSymbolApplier functionSymbolApplier = (FunctionSymbolApplier) applierParam;
Address address = applicator.getAddress(symbol);
functionSymbolApplier.beginBlock(address, symbol.getName(), symbol.getLength());
}
}
}

View File

@ -0,0 +1,232 @@
/* ###
* 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 forward reference and definition members of the AbstractComplexTypeApplier.
*/
// We have probably tried 5 or more ways of doing this, all with mixed results. The current
// implementation seems to yield the best results at the moment. Keeping some of the old code
// around until we are solid on our algorithm and until we document some of the various algorithms
// tried.
public class ComplexTypeApplierMapper {
private PdbApplicator applicator;
// private Map<SymbolPath, AbstractComplexTypeApplier> complexTypeAppliersBySymbolPath;
private Map<SymbolPath, LinkedList<AbstractComplexTypeApplier>> compositeAppliersQueueBySymbolPath;
private Map<SymbolPath, LinkedList<AbstractComplexTypeApplier>> enumAppliersQueueBySymbolPath;
//==============================================================================================
public ComplexTypeApplierMapper(PdbApplicator applicator) {
Objects.requireNonNull(applicator, "applicator cannot be null");
this.applicator = applicator;
// complexTypeAppliersBySymbolPath = new HashMap<>();
compositeAppliersQueueBySymbolPath = new HashMap<>();
enumAppliersQueueBySymbolPath = new HashMap<>();
}
//==============================================================================================
//==============================================================================================
void mapAppliers(TaskMonitor monitor) throws CancelledException {
int indexLimit = applicator.getPdb().getTypeProgramInterface().getTypeIndexMaxExclusive();
int indexNumber = applicator.getPdb().getTypeProgramInterface().getTypeIndexMin();
monitor.initialize(indexLimit - indexNumber);
applicator.setMonitorMessage("PDB: Mapping Composites...");
while (indexNumber < indexLimit) {
monitor.checkCanceled();
//PdbResearch.checkBreak(indexNumber);
MsTypeApplier applier =
applicator.getTypeApplier(RecordNumber.typeRecordNumber(indexNumber++));
// From real data, we know that an enum and a composite both had the same SymbolPath,
// so enums and composites must be maintained separately so they do not get matched
// with each other.
if (applier instanceof CompositeTypeApplier) {
// mapComplexApplierBySymbolPath(compositeAppliersFwdRefQueueBySymbolPath,
// (AbstractComplexTypeApplier) applier);
mapComplexApplierTwoWayBySymbolPath(compositeAppliersQueueBySymbolPath,
(AbstractComplexTypeApplier) applier);
}
else if (applier instanceof EnumTypeApplier) {
// mapComplexApplierBySymbolPath(enumAppliersFwdRefQueueBySymbolPath,
// (AbstractComplexTypeApplier) applier);
mapComplexApplierTwoWayBySymbolPath(enumAppliersQueueBySymbolPath,
(AbstractComplexTypeApplier) applier);
}
// if (applier instanceof AbstractComplexTypeApplier) {
// mapComplexApplierByQueue((AbstractComplexTypeApplier) applier);
// //mapComplexApplierForwardOnly((AbstractComplexTypeApplier) applier);
// //mapComplexApplier((AbstractComplexTypeApplier) applier);
// }
monitor.incrementProgress(1);
}
}
private void mapComplexApplierTwoWayBySymbolPath(
Map<SymbolPath, LinkedList<AbstractComplexTypeApplier>> applierQueueBySymbolPath,
AbstractComplexTypeApplier complexApplier) {
SymbolPath symbolPath = complexApplier.getSymbolPath();
Objects.requireNonNull(symbolPath, "SymbolPath may not be null");
LinkedList<AbstractComplexTypeApplier> appliers = applierQueueBySymbolPath.get(symbolPath);
if (appliers == null) {
appliers = new LinkedList<>();
applierQueueBySymbolPath.put(symbolPath, appliers);
// Putting forward reference or definition (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 forward reference
// of definition.
// If same as what is on list, add to the list.
if (!appliers.add(complexApplier)) {
// Error
}
}
else {
if (complexApplier.isForwardReference()) {
AbstractComplexTypeApplier definitionApplier = appliers.removeFirst();
definitionApplier.setForwardReferenceApplier(complexApplier);
complexApplier.setDefinitionApplier(definitionApplier);
}
else {
AbstractComplexTypeApplier forwardReferenceApplier = appliers.removeFirst();
forwardReferenceApplier.setDefinitionApplier(complexApplier);
complexApplier.setForwardReferenceApplier(forwardReferenceApplier);
}
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 forward reference.
// // Clearing cache after that def so next def does not map.
// private void mapComplexApplierForwardOnly(AbstractComplexTypeApplier complexApplier) {
// SymbolPath symbolPath = complexApplier.getSymbolPath();
//
// if (complexApplier.isForwardReference()) {
// putComplexTypeApplierBySymbolPath(symbolPath, complexApplier);
// }
// else {
// AbstractComplexTypeApplier cachedComplexApplier =
// getComplexTypeApplierBySymbolPath(symbolPath, complexApplier.getClass());
// if (cachedComplexApplier != null) {
// cachedComplexApplier.setDefinitionApplier(complexApplier);
// complexApplier.setFwdRefApplier(cachedComplexApplier);
// // set cache back to null
// complexTypeAppliersBySymbolPath.remove(symbolPath);
// }
// }
// }
//
// private void putComplexTypeApplierBySymbolPath(SymbolPath symbolPath,
// AbstractComplexTypeApplier applier) {
// Objects.requireNonNull(symbolPath, "SymbolPath may not be null");
// Objects.requireNonNull(applier, "CompositeTypeApplier may not be null");
// complexTypeAppliersBySymbolPath.put(symbolPath, applier);
// }
//
// private <T extends AbstractComplexTypeApplier> T getComplexTypeApplierBySymbolPath(
// SymbolPath symbolPath, Class<T> typeClass) {
// Objects.requireNonNull(symbolPath, "SymbolPath may not be null");
// Objects.requireNonNull(typeClass, "typeClass may not be null");
// AbstractComplexTypeApplier applier = complexTypeAppliersBySymbolPath.get(symbolPath);
// if (!typeClass.isInstance(applier)) {
// return null;
// }
// return typeClass.cast(applier);
// }
//
// //==============================================================================================
// private void mapComplexApplierBySymbolPath(
// Map<SymbolPath, LinkedList<AbstractComplexTypeApplier>> applierQueueBySymbolPath,
// AbstractComplexTypeApplier complexApplier) {
// SymbolPath symbolPath = complexApplier.getSymbolPath();
// Objects.requireNonNull(symbolPath, "SymbolPath may not be null");
//
// LinkedList<AbstractComplexTypeApplier> fwdList = applierQueueBySymbolPath.get(symbolPath);
// if (fwdList == null) {
// fwdList = new LinkedList<>();
// applierQueueBySymbolPath.put(symbolPath, fwdList);
// }
//
// if (complexApplier.isForwardReference()) {
// if (!fwdList.add(complexApplier)) {
// // Error
// }
// }
// else if (!fwdList.isEmpty()) {
// AbstractComplexTypeApplier fwdApplier = fwdList.removeFirst();
// fwdApplier.setDefinitionApplier(complexApplier);
// complexApplier.setFwdRefApplier(fwdApplier);
// }
// }
//
}

View File

@ -0,0 +1,793 @@
/* ###
* 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 forward
* reference (we have seen example of count==0 and not forward reference, 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 forward reference 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 CppCompositeType classType;
// private final static DataType NO_TYPE_DATATYPE =
// new TypedefDataType("<NoType>", Undefined1DataType.dataType);
//
private Map<Integer, String> componentComments;
private List<DefaultPdbUniversalMember> members;
/**
* 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.
*/
public CompositeTypeApplier(PdbApplicator applicator, AbstractCompositeMsType msType) {
super(applicator, msType);
String fullPathName = msType.getName();
symbolPath = new SymbolPath(SymbolPathParser.parse(fullPathName));
}
CppCompositeType getClassType() {
if (definitionApplier != null) {
return ((CompositeTypeApplier) definitionApplier).getClassTypeInternal();
}
return classType;
}
CppCompositeType getClassTypeInternal() {
return classType;
}
List<DefaultPdbUniversalMember> getMembers() {
return members;
}
// Mapping of forwardReference/definition must be done prior to this call.
private void getOrCreateComposite() {
if (dataType != null) {
return;
}
AbstractComplexTypeApplier alternativeApplier = getAlternativeTypeApplier();
if (alternativeApplier != null) {
dataType = alternativeApplier.getDataTypeInternal();
classType = ((CompositeTypeApplier) alternativeApplier).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
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
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 (MsTypeApplier baseApplierIterated : fieldListApplier.getBaseClassList()) {
if (baseApplierIterated instanceof BaseClassTypeApplier) {
BaseClassTypeApplier baseTypeApplier = (BaseClassTypeApplier) baseApplierIterated;
MsTypeApplier 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 (MsTypeApplier memberTypeApplierIterated : fieldListApplier.getMemberList()) {
applicator.checkCanceled();
if (memberTypeApplierIterated instanceof MemberTypeApplier) {
MemberTypeApplier memberTypeApplier = (MemberTypeApplier) memberTypeApplierIterated;
MsTypeApplier 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(MsTypeApplier dependee)
throws CancelledException, PdbException {
// TODO: evaluate this and make changes... this work might be being taken care of in
// ModifierTypeApplier
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();
}
// TODO: evaluate this and make changes... this work might be being taken care of in
// ArrayTypeApplier
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();
MsTypeApplier underlyingApplier = applicator.getTypeApplier(recNum);
if (underlyingApplier instanceof EnumTypeApplier) {
applicator.addApplierDependency(this, underlyingApplier);
setDeferred();
}
}
//We are assuming that bitfields on typedefs will not be defined.
}
private 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()) {
pdbLogAndInfoMessage(this,
"Unexpected base classes for union type: " + type.getName());
}
}
if (applyCpp) {
applyCpp(composite, type);
}
else {
applyBasic(composite, type);
}
setApplied();
}
//==============================================================================================
private void applyBasic(Composite composite, AbstractCompositeMsType type)
throws CancelledException, PdbException {
//boolean isClass = (type instanceof AbstractClassMsType || actsLikeClass(applicator, type));
boolean isClass = (type instanceof AbstractClassMsType);
int size = getSizeInt();
// Fill in composite definition details.
FieldListTypeApplier fieldListApplier = FieldListTypeApplier.getFieldListApplierSpecial(
applicator, type.getFieldDescriptorListRecordNumber());
clearComponents(composite);
members = new ArrayList<>();
componentComments = new HashMap<>();
addMembers(composite, fieldListApplier);
if (!DefaultCompositeMember.applyDataTypeMembers(composite, isClass, size, members,
msg -> reconstructionWarn(msg), applicator.getCancelOnlyWrappingMonitor())) {
clearComponents(composite);
}
setComponentComments(composite);
}
private void setComponentComments(Composite composite) {
if (composite instanceof Structure) {
Structure structure = (Structure) composite;
for (Map.Entry<Integer, String> entry : componentComments.entrySet()) {
DataTypeComponent component = structure.getComponentAt(entry.getKey());
if (component == null) {
pdbLogAndInfoMessage(this, "Could not set comment for 'missing' componenent " +
entry.getKey() + " for: " + structure.getName());
return;
}
component.setComment(entry.getValue());
}
}
}
//==============================================================================================
private 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().getClassLayout(),
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);
}
//==============================================================================================
@Override
void deferredApply() throws PdbException, CancelledException {
if (isDeferred()) {
applyInternal();
}
}
//==============================================================================================
//==============================================================================================
@Override
CompositeTypeApplier getDependencyApplier() {
if (definitionApplier != null && definitionApplier instanceof CompositeTypeApplier) {
return (CompositeTypeApplier) definitionApplier;
}
return this;
}
String getName() {
return getMsType().getName();
}
@Override
DataType getDataType() {
if (resolved) {
return resolvedDataType;
}
getOrCreateComposite();
return dataType;
}
@Override
DataType getCycleBreakType() {
if (isForwardReference() && definitionApplier != null && definitionApplier.isApplied()) {
return definitionApplier.getDataType();
}
return dataType;
}
boolean hasUniqueName() {
return ((AbstractCompositeMsType) msType).getMsProperty().hasUniqueName();
}
@Override
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();
}
MsTypeApplier 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.
MsTypeApplier 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 (MsTypeApplier baseApplierIterated : fieldListApplier.getBaseClassList()) {
if (!(baseApplierIterated instanceof BaseClassTypeApplier)) {
applicator.appendLogMsg(baseApplierIterated.getClass().getSimpleName() +
" seen where BaseClassTypeApplier expected for " + type.getName());
continue;
}
BaseClassTypeApplier baseTypeApplier = (BaseClassTypeApplier) baseApplierIterated;
MsTypeApplier 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) {
MsTypeApplier 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) {
MsTypeApplier 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 (MsTypeApplier 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
MsTypeApplier 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()) {
DefaultPdbUniversalMember member = new DefaultPdbUniversalMember(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 {
DefaultPdbUniversalMember member =
new DefaultPdbUniversalMember(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
pdbLogAndInfoMessage(this,
"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();
DefaultPdbUniversalMember member = new DefaultPdbUniversalMember(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.
// pdbLogAndInfoMessage(this, "Skipping Composite Nested type member: " +
// memberName + " within " + type.getName());
// TODO: Investigate. What does it mean when the internally defined type
// conficts with the name of the outer type.
continue;
}
// TODO: believe the thing to do is to show that these are types that are
// defined within the namespace of this containing type. This might be
// the place to do it... that is if we don't identify them separately
// falling under the namespace of this composite.
// AbstractMsTypeApplier nestedDefinitionApplier =
// nestedTypeApplier.getNestedTypeDefinitionApplier().getDependencyApplier();
//
// DataType ndt = nestedDefinitionApplier.getDataType(); //use this
// int ndtl = ndt.getLength(); //use this
//
// AbstractMsType ndms = nestedTypeApplier.getMsType();
//
// BigInteger val = nestedTypeApplier.getSize();
// int offset = 0; // ???? TODO..,
// DataType nt = nestedTypeApplier.getDataType();
// ClassFieldMsAttributes a = nestedTypeApplier.getAttributes();
//
// // TODO: hoping this is right... 20200521... how/where do we get offset?
// Default2PdbMember member =
// new Default2PdbMember(applicator, memberName, nestedDefinitionApplier, offset);
// members.add(member);
}
else if (memberTypeApplierIterated instanceof NoTypeApplier) {
AbstractMsType msNoType = memberTypeApplierIterated.getMsType();
if (msNoType instanceof AbstractStaticMemberMsType) {
// TODO: Investigate anything that hits here (set break point), see if we
// see dot apply the information. If so, probably should create an applier
// for the contained MS type.
}
else {
handled = false;
}
}
else {
handled = false;
}
if (!handled) {
applicator.appendLogMsg(
memberTypeApplierIterated.getClass().getSimpleName() + " with contained " +
memberTypeApplierIterated.getMsType().getClass().getSimpleName() +
" unexpected for " + msType.getName());
}
}
}
// /**
// * <code>NoType</code> provides ability to hang NoType into a composite type by faking
// * it with a zero-length bitfield. This is a bit of a kludge
// * This will be transformed to a normal BitFieldDataType when cloned.
// */
// private class NoType extends PdbBitField {
// private NoType(PdbApplicator applicator) throws InvalidDataTypeException {
// super(new CharDataType(applicator.getDataTypeManager()), 0, 0);
// }
// }
//
}

View File

@ -0,0 +1,273 @@
/* ###
* 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.program.model.util.CodeUnitInsertionException;
import ghidra.util.exception.AssertException;
import ghidra.util.exception.CancelledException;
/**
* Applier for {@link AbstractDataMsSymbol} symbols.
*/
public class DataSymbolApplier extends MsSymbolApplier {
private AbstractDataMsSymbol symbol;
/**
* Constructor
* @param applicator the {@link PdbApplicator} for which we are working.
* @param iter the Iterator containing the symbol sequence being processed
*/
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
void applyTo(MsSymbolApplier applyToApplier) throws PdbException, CancelledException {
if (applyToApplier instanceof FunctionSymbolApplier) {
FunctionSymbolApplier functionSymbolApplier = (FunctionSymbolApplier) applyToApplier;
MsTypeApplier 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.getAddress(symbol);
if (applicator.isInvalidAddress(address, symbol.getName())) {
return;
}
functionSymbolApplier.setLocalVariable(address, name, dataType);
}
}
@Override
void apply() throws CancelledException, PdbException {
Address symbolAddress = applicator.getAddress(symbol);
if (applicator.isInvalidAddress(symbolAddress, symbol.getName())) {
return;
}
RecordNumber typeRecordNumber = symbol.getTypeRecordNumber();
applicator.createSymbol(symbolAddress, symbol.getName(), true);
createData(symbolAddress, typeRecordNumber);
}
MsTypeApplier getTypeApplier() {
return applicator.getTypeApplier(symbol.getTypeRecordNumber());
}
void createData(Address address, RecordNumber typeRecordNumber) {
MsTypeApplier 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 (applicator.getImageBase().equals(address) &&
!"_IMAGE_DOS_HEADER".equals(dataType.getName())) {
return; // Squash some noise
}
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 (CodeUnitInsertionException | DataTypeConflictException 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 (CodeUnitInsertionException | DataTypeConflictException e) {
applicator.appendLogMsg("Unable to replace " + dataType.getDisplayName() +
" at 0x" + address + ": " + e.getMessage());
}
}
else {
DataType existingDataType = existingData.getDataType();
String existingDataTypeString =
existingDataType == null ? "null" : existingDataType.getDisplayName();
applicator.appendLogMsg("Warning: Did not create data type \"" +
dataType.getDisplayName() + "\" at address " + address +
". Preferring existing datatype \"" + existingDataTypeString + "\"");
}
}
private boolean isDataReplaceable(Data data) {
DataType dataType = data.getDataType();
if (dataType instanceof Pointer) {
Pointer pointer = (Pointer) dataType;
DataType pointerDataType = pointer.getDataType();
if (pointerDataType == null || pointerDataType.isEquivalent(DataType.DEFAULT)) {
return true;
}
}
else if (dataType instanceof Array) {
Array array = (Array) dataType;
DataType arrayDataType = array.getDataType();
if (arrayDataType == null || arrayDataType.isEquivalent(DataType.DEFAULT)) {
return true;
}
}
// All forms of Undefined data are replaceable
// TODO: maybe it should check the length of the data type before putting it down.
if (Undefined.isUndefined(dataType)) {
return true;
}
return false;
}
private boolean isEquivalent(Data existingData, int existingDataTypeLength,
DataType newDataType) {
if (existingData.hasStringValue()) {
if (newDataType instanceof ArrayDataType) {
Array array = (Array) newDataType;
DataType arrayDataType = array.getDataType();
if (arrayDataType instanceof ArrayStringable) {
if (array.getLength() == existingDataTypeLength) {
return true;
}
}
}
}
return false;
}
/**
* "char[12] *" "char * *"
*
* "ioinfo * *" "ioinfo[64] *"
*/
private boolean isEquivalent2(DataType datatype1, DataType datatype2) {
if (datatype1 == datatype2) {
return true;
}
if (datatype1 == null || datatype2 == null) {
return false;
}
if (datatype1 instanceof Array) {
Array array1 = (Array) datatype1;
if (datatype2 instanceof Array) {
Array array2 = (Array) datatype2;
return isEquivalent2(array1.getDataType(), array2.getDataType());
}
}
else if (datatype1 instanceof Pointer) {
Pointer pointer1 = (Pointer) datatype1;
if (datatype2 instanceof Array) {
Array array2 = (Array) datatype2;
return isEquivalent2(pointer1.getDataType(), array2.getDataType());
}
}
return datatype1.isEquivalent(datatype2);
}
}

View File

@ -0,0 +1,118 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.pdb.pdbapplicator;
import java.math.BigInteger;
import ghidra.app.util.bin.format.pdb.*;
import ghidra.program.model.data.DataType;
import ghidra.util.exception.CancelledException;
/**
* <code>PdbMember</code> convey PDB member information used for datatype
* reconstruction.
*/
public class DefaultPdbUniversalMember extends PdbMember {
private MsTypeApplier 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.
*/
DefaultPdbUniversalMember(PdbApplicator applicator, String name, MsTypeApplier 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.
*/
DefaultPdbUniversalMember(PdbApplicator applicator, String name, DataType dataType,
int offset) {
super(name, dataType.getName(), offset, null);
this.applier = null;
this.dataType = dataType;
}
MsTypeApplier 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() {
StringBuilder builder = new StringBuilder();
builder.append("name=");
builder.append(getName());
DataType dt = getDataTypeInternal();
if (dt instanceof PdbBitField) {
PdbBitField bfDt = (PdbBitField) dataType;
builder.append(", type=");
builder.append(bfDt.getBaseDataType().getName());
builder.append(", offset=");
builder.append(getOffset());
builder.append(", bitSize=");
builder.append(bfDt.getDeclaredBitSize());
builder.append(", bitOffset=");
builder.append(bfDt.getBitOffsetWithinBase());
}
else {
builder.append(", type=");
builder.append(dataType.getName());
builder.append(", offset=");
builder.append(getOffset());
}
return builder.toString();
}
@Override
protected WrappedDataType getDataType() throws CancelledException {
DataType dt = getDataTypeInternal();
if (applier != null && applier instanceof ArrayTypeApplier) {
if (BigInteger.ZERO.compareTo(applier.getSize()) == 0) {
return new WrappedDataType(dt, true, false);
}
}
return new WrappedDataType(dt, false, false);
}
}

View File

@ -0,0 +1,76 @@
/* ###
* 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.*;
import ghidra.app.util.pdb.pdbapplicator.SymbolGroup.AbstractMsSymbolIterator;
import ghidra.util.exception.AssertException;
import ghidra.util.exception.CancelledException;
/**
* Applier for {@link AbstractDefinedSingleAddressRangeMsSymbol} symbols.
*/
public class DefinedSingleAddressRangeSymbolApplier extends MsSymbolApplier {
private AbstractDefinedSingleAddressRangeMsSymbol symbol;
/**
* Constructor
* @param applicator the {@link PdbApplicator} for which we are working.
* @param iter the Iterator containing the symbol sequence being processed
*/
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
void apply() throws PdbException, CancelledException {
pdbLogAndInfoMessage(this,
"Cannot apply " + this.getClass().getSimpleName() + " directly to program");
}
@Override
void applyTo(MsSymbolApplier applyToApplier) throws PdbException, CancelledException {
if (applyToApplier instanceof LocalOptimizedSymbolApplier) {
// TODO: eventually apply.
// LocalOptimizedSymbolApplier localSymbolApplier =
// (LocalOptimizedSymbolApplier) applyToApplier;
symbol.getAddressRange();
symbol.getAddressGapList();
if (symbol instanceof EnregisteredSymbolDARMsSymbol) {
EnregisteredSymbolDARMsSymbol sym1 = (EnregisteredSymbolDARMsSymbol) symbol;
sym1.getRangeAttribute();
sym1.getRegister();
}
else if (symbol instanceof EnregisteredFieldOfSymbolDARMsSymbol) {
EnregisteredFieldOfSymbolDARMsSymbol sym1 =
(EnregisteredFieldOfSymbolDARMsSymbol) symbol;
sym1.getOffsetInParent();
sym1.getRangeAttribute();
sym1.getRegister();
}
}
}
}

View File

@ -0,0 +1,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.bin.format.pdb2.pdbreader.PdbException;
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.exception.AssertException;
import ghidra.util.exception.CancelledException;
/**
* Applier for {@link EndMsSymbol} symbols.
*/
public class EndSymbolApplier extends MsSymbolApplier {
/**
* Constructor
* @param applicator the {@link PdbApplicator} for which we are working.
* @param iter the Iterator containing the symbol sequence being processed
* @throws CancelledException upon user cancellation
*/
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
void apply() throws PdbException {
pdbLogAndInfoMessage(this,
String.format("Cannot apply %s directly to program (module:0X%04X, offset:0X%08X)",
this.getClass().getSimpleName(), iter.getModuleNumber(), iter.getCurrentOffset()));
}
@Override
void applyTo(MsSymbolApplier applyToApplier) {
if (!(applyToApplier instanceof FunctionSymbolApplier)) {
return;
}
// FunctionSymbolApplier functionSymbolApplier = (FunctionSymbolApplier) applyToApplier;
// functionSymbolApplier.endBlock();
}
@Override
void manageBlockNesting(MsSymbolApplier applierParam) {
if (applierParam instanceof FunctionSymbolApplier) {
FunctionSymbolApplier functionSymbolApplier = (FunctionSymbolApplier) applierParam;
functionSymbolApplier.endBlock();
}
else if (applierParam instanceof SeparatedCodeSymbolApplier) {
SeparatedCodeSymbolApplier separatedCodeSymbolApplier =
(SeparatedCodeSymbolApplier) applierParam;
separatedCodeSymbolApplier.endBlock();
}
}
}

View File

@ -0,0 +1,308 @@
/* ###
* 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.AbstractEnumMsType;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.MsProperty;
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;
//
/**
* 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.
*/
public EnumTypeApplier(PdbApplicator applicator, AbstractEnumMsType msType) {
super(applicator, msType);
}
@Override
BigInteger getSize() {
MsTypeApplier 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.
MsTypeApplier underlyingApplier = getUnderlyingTypeApplier();
if (underlyingApplier == null) {
return 1;
}
DataType underlyingType = underlyingApplier.getDataType();
if (underlyingType == null) {
return 1;
}
return Integer.max(underlyingType.getLength(), 1);
}
boolean isSigned() {
MsTypeApplier 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
EnumTypeApplier getDependencyApplier() {
if (definitionApplier != null && definitionApplier instanceof EnumTypeApplier) {
return (EnumTypeApplier) definitionApplier;
}
return this;
}
String getName() {
return getMsType().getName();
}
private MsTypeApplier getUnderlyingTypeApplier() {
MsTypeApplier under = null;
MsTypeApplier 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) {
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)) {
// pdbLogAndInfoMessage(this, "Cannot processes enum with underlying type: " +
// underlyingType.getClass().getSimpleName());
// 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
void apply() throws PdbException, CancelledException {
getOrCreateEnum();
AbstractEnumMsType type = (AbstractEnumMsType) msType;
MsProperty property = type.getMsProperty();
if (property.isForwardReference()) {
return;
}
applyEnumMsType((AbstractEnumMsType) msType);
}
@Override
void resolve() {
if (!isForwardReference()) {
super.resolve();
}
}
// Mapping of fwdRef/def must be done prior to this call.
private void getOrCreateEnum() {
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 (forwardReferenceApplier != null) {
dataType = forwardReferenceApplier.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<MsTypeApplier> memberList = fieldListApplier.getMemberList();
int numElements = type.getNumElements();
if (memberList.size() != numElements) {
pdbLogAndInfoMessage(this, "Enum expecting " + numElements + " elements, but only " +
memberList.size() + " available for " + fullPathName);
}
EnumDataType enumDataType = (EnumDataType) dataType;
int length = getLength();
boolean isSigned = isSigned();
for (MsTypeApplier 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.
pdbLogAndInfoMessage(this, 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()) {
pdbLogAndInfoMessage(this, "Using zero in place of non-integral enumerate: " + numeric);
return 0L; //
}
return numeric.getIntegral().longValue() & getMask();
// return NarrowingConverter.narrowBigToLong(outputSize, outputSigned, numeric.getSize(),
// numeric.isSigned(), numeric.getIntegral());
}
}

View File

@ -0,0 +1,80 @@
/* ###
* 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.pdb.PdbNamespaceUtils;
import ghidra.program.model.data.DataType;
/**
* Applier for {@link AbstractEnumerateMsType} types.
*/
public class EnumerateTypeApplier extends MsTypeApplier {
private String fieldName;
private Numeric numeric;
/**
* 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.
*/
public EnumerateTypeApplier(PdbApplicator applicator, AbstractEnumerateMsType msType) {
super(applicator, msType);
}
@Override
BigInteger getSize() {
return BigInteger.ZERO;
}
@Override
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);
}
String getName() {
return fieldName;
}
Numeric getNumeric() {
return numeric;
}
private DataType applyEnumerateMsType(AbstractEnumerateMsType type) {
//TODO: currently dropping these on the floor. The access methods above do the same work.
fieldName = PdbNamespaceUtils.fixUnnamed(type.getName(), index);
// TODO: Need to build test sample with these.
// TODO: Need to see if can do real number; need to modify Numeric for both
// integral and real numbers. Possibly need to make Numeric a "type" instead
// of just something to read using ByteReader.
numeric = type.getNumeric();
return null;
}
}

View File

@ -0,0 +1,180 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.pdb.pdbapplicator;
import java.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.exception.CancelledException;
/**
* Applier for {@link AbstractFieldListMsType} types and {@code NO_TYPE} when in place of the
* former type.
*/
public class FieldListTypeApplier extends MsTypeApplier {
private List<MsTypeApplier> baseClassList = new ArrayList<>();
private List<MsTypeApplier> memberList = new ArrayList<>();
private List<MsTypeApplier> methodList = new ArrayList<>();
private boolean isEmpty;
// return can be null
static FieldListTypeApplier getFieldListApplierSpecial(PdbApplicator applicator,
RecordNumber recordNumber) throws PdbException {
MsTypeApplier 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.
*/
boolean isEmpty() {
return isEmpty;
}
@Override
BigInteger getSize() {
return BigInteger.ZERO;
}
@Override
void apply() throws PdbException, CancelledException {
dataType = applyFieldListMsType((AbstractFieldListMsType) msType);
}
List<MsTypeApplier> getBaseClassList() {
return baseClassList;
}
List<MsTypeApplier> getMemberList() {
return memberList;
}
List<MsTypeApplier> 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()) {
MsTypeApplier 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 {
pdbLogAndInfoMessage(this, "referenceTypeApplier is not FieldListTypeApplier");
}
}
return null;
}
private void applyBaseClasses(List<MsTypeField> baseClasses)
throws CancelledException, PdbException {
for (MsTypeField typeIterated : baseClasses) {
// Use dummy index of zero.
MsTypeApplier applier =
applicator.getTypeApplier((AbstractMsType) typeIterated);
applier.apply(); // Need to apply here, as these are embedded records
baseClassList.add(applier);
}
}
private void applyMembers(List<MsTypeField> members) throws CancelledException, PdbException {
for (MsTypeField typeIterated : members) {
// Use dummy index of zero.
MsTypeApplier applier =
applicator.getTypeApplier((AbstractMsType) typeIterated);
applier.apply(); // Need to apply here, as these are embedded records
memberList.add(applier);
}
}
private void applyMethods(List<MsTypeField> methods) throws CancelledException, PdbException {
for (MsTypeField typeIterated : methods) {
// Use dummy index of zero.
MsTypeApplier applier =
applicator.getTypeApplier((AbstractMsType) typeIterated);
// TODO: note that these are likely NoTypeAppliers at the moment, as we had not
// yet implemented appliers for AbstractOneMethodMsType and
// AbstractOverloadedMethodMsType
applier.apply(); // Need to apply here, as these are embedded records
methodList.add(applier);
}
}
}

View File

@ -0,0 +1,60 @@
/* ###
* 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 MsSymbolApplier {
private ExtraFrameAndProcedureInformationMsSymbol symbol;
/**
* Constructor
* @param applicator the {@link PdbApplicator} for which we are working.
* @param iter the Iterator containing the symbol sequence being processed
*/
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
void applyTo(MsSymbolApplier applyToApplier) throws PdbException, CancelledException {
if (applyToApplier instanceof FunctionSymbolApplier) {
FunctionSymbolApplier functionSymbolApplier = (FunctionSymbolApplier) applyToApplier;
functionSymbolApplier.setSpecifiedFrameSize(symbol.getProcedureFrameTotalLength());
}
}
@Override
void apply() {
// Quietly do nothing
}
}

View File

@ -0,0 +1,462 @@
/* ###
* 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 MsSymbolApplier {
private static final String BLOCK_INDENT = " ";
private AbstractProcedureMsSymbol procedureSymbol;
private AbstractThunkMsSymbol thunkSymbol;
private Address specifiedAddress;
private Address address;
private Function function = null;
private long specifiedFrameSize = 0;
private long currentFrameSize = 0;
private BlockCommentsManager comments;
private int symbolBlockNestingLevel;
private Address currentBlockAddress;
// might not need this, but investigating whether it will help us. TODO remove?
private int baseParamOffset = 0;
// private List<RegisterRelativeSymbolApplier> stackVariableAppliers = new ArrayList<>();
private List<MsSymbolApplier> allAppliers = new ArrayList<>();
private RegisterChangeCalculator registerChangeCalculator;
/**
* Constructor
* @param applicator the {@link PdbApplicator} for which we are working.
* @param iter the Iterator containing the symbol sequence being processed
* @throws CancelledException upon user cancellation
*/
public FunctionSymbolApplier(PdbApplicator applicator, AbstractMsSymbolIterator iter)
throws CancelledException {
super(applicator, iter);
AbstractMsSymbol abstractSymbol = iter.next();
symbolBlockNestingLevel = 0;
comments = new BlockCommentsManager();
currentBlockAddress = null;
if (abstractSymbol instanceof AbstractProcedureMsSymbol) {
procedureSymbol = (AbstractProcedureMsSymbol) abstractSymbol;
specifiedAddress = applicator.getRawAddress(procedureSymbol);
address = applicator.getAddress(procedureSymbol);
}
else if (abstractSymbol instanceof AbstractThunkMsSymbol) {
thunkSymbol = (AbstractThunkMsSymbol) abstractSymbol;
specifiedAddress = applicator.getRawAddress(thunkSymbol);
address = applicator.getAddress(thunkSymbol);
}
else {
throw new AssertException(
"Invalid symbol type: " + abstractSymbol.getClass().getSimpleName());
}
manageBlockNesting(this);
while (notDone()) {
applicator.checkCanceled();
MsSymbolApplier applier = applicator.getSymbolApplier(iter);
allAppliers.add(applier);
applier.manageBlockNesting(this);
}
}
@Override
void manageBlockNesting(MsSymbolApplier applierParam) {
if (applierParam instanceof FunctionSymbolApplier) {
FunctionSymbolApplier functionSymbolApplier = (FunctionSymbolApplier) applierParam;
if (procedureSymbol != null) {
long start = procedureSymbol.getDebugStartOffset();
long end = procedureSymbol.getDebugEndOffset();
Address blockAddress = address.add(start);
long length = end - start;
functionSymbolApplier.beginBlock(blockAddress, procedureSymbol.getName(), length);
}
else if (thunkSymbol != null) {
functionSymbolApplier.beginBlock(address, thunkSymbol.getName(),
thunkSymbol.getLength());
}
}
}
/**
* Returns the {@link Function} for this applier.
* @return the Function
*/
Function getFunction() {
return function;
}
/**
* Returns the current frame size.
* @return the current frame size.
*/
long getCurrentFrameSize() {
return currentFrameSize;
}
/**
* Returns the frame size as specified by the PDB
* @return the frame size.
*/
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
*/
String getName() {
if (procedureSymbol != null) {
return procedureSymbol.getName();
}
else if (thunkSymbol != null) {
return thunkSymbol.getName();
}
return "";
}
@Override
void applyTo(MsSymbolApplier applyToApplier) {
// Do nothing.
}
@Override
void apply() throws PdbException, CancelledException {
boolean result = applyTo(applicator.getCancelOnlyWrappingMonitor());
if (result == false) {
throw new PdbException(this.getClass().getSimpleName() + ": failure at " + address +
" applying " + getName());
}
}
boolean applyTo(TaskMonitor monitor) throws PdbException, CancelledException {
if (applicator.isInvalidAddress(address, getName())) {
return false;
}
boolean functionSuccess = applyFunction(monitor);
if (functionSuccess == false) {
return false;
}
registerChangeCalculator = new RegisterChangeCalculator(procedureSymbol, function, monitor);
baseParamOffset = VariableUtilities.getBaseStackParamOffset(function);
for (MsSymbolApplier applier : allAppliers) {
applier.applyTo(this);
}
// comments
long addressDelta = address.subtract(specifiedAddress);
comments.applyTo(applicator.getProgram(), addressDelta);
// line numbers
// TODO: not done yet
// ApplyLineNumbers applyLineNumbers = new ApplyLineNumbers(pdbParser, xmlParser, program);
// applyLineNumbers.applyTo(monitor, log);
return true;
}
Integer getRegisterPrologChange(Register register) {
return registerChangeCalculator.getRegChange(applicator, register);
}
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(TaskMonitor monitor) {
Listing listing = applicator.getProgram().getListing();
applicator.createSymbol(address, getName(), true);
function = listing.getFunctionAt(address);
if (function == null) {
function = createFunction(monitor);
}
if (function != null && !function.isThunk() &&
(function.getSignatureSource() == SourceType.DEFAULT ||
function.getSignatureSource() == SourceType.ANALYSIS)) {
// Set the function definition
setFunctionDefinition(monitor);
}
if (function == null) {
return false;
}
currentFrameSize = 0;
return true;
}
private Function createFunction(TaskMonitor monitor) {
// Does function already exist?
Function myFunction = applicator.getProgram().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 = applicator.getProgram().getListing().getInstructionAt(address);
if (instr == null) {
DisassembleCommand cmd = new DisassembleCommand(address, null, true);
cmd.applyTo(applicator.getProgram(), monitor);
}
myFunction = createFunctionCommand(monitor);
return myFunction;
}
private boolean setFunctionDefinition(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();
MsTypeApplier 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(applicator.getProgram(), monitor)) {
applicator.appendLogMsg(
"failed to apply signature to function at address " + address.toString());
return false;
}
}
return true;
}
private Function createFunctionCommand(TaskMonitor monitor) {
CreateFunctionCmd funCmd = new CreateFunctionCmd(address);
if (!funCmd.applyTo(applicator.getProgram(), monitor)) {
applicator.appendLogMsg("Failed to apply function at address " + address.toString() +
"; attempting to use possible existing function");
return applicator.getProgram().getListing().getFunctionAt(address);
}
return funCmd.getFunction();
}
private boolean notDone() {
return (symbolBlockNestingLevel > 0) && iter.hasNext();
}
int endBlock() {
if (--symbolBlockNestingLevel < 0) {
applicator.appendLogMsg(
"Block Nesting went negative for " + getName() + " at " + address);
}
if (symbolBlockNestingLevel == 0) {
//currentFunctionSymbolApplier = null;
}
return symbolBlockNestingLevel;
}
void beginBlock(Address startAddress, String name, long length) {
int nestingLevel = beginBlock(startAddress);
if (!applicator.getPdbApplicatorOptions().applyCodeScopeBlockComments()) {
return;
}
if (applicator.isInvalidAddress(startAddress, name)) {
return;
}
String indent = getIndent(nestingLevel);
String baseComment = "level " + nestingLevel + ", length " + length;
String preComment = indent + "PDB: Block Beg, " + baseComment;
if (!name.isEmpty()) {
preComment += " (" + name + ")";
}
comments.addPreComment(startAddress, preComment);
String postComment = indent + "PDB: Block End, " + baseComment;
Address endAddress = startAddress.add(((length <= 0) ? 0 : length - 1));
comments.addPostComment(endAddress, postComment);
}
private int beginBlock(Address startAddress) {
currentBlockAddress = startAddress;
++symbolBlockNestingLevel;
return symbolBlockNestingLevel;
}
private String getIndent(int indentLevel) {
String indent = "";
for (int i = 1; i < indentLevel; i++) {
indent += BLOCK_INDENT;
}
return indent;
}
// Method copied from ApplyStackVariables (ghidra.app.util.bin.format.pdb package)
// on 20191119. TODO: Do we need something like this?
/**
* Get the stack offset after it settles down.
* @param monitor TaskMonitor
* @return stack offset that stack variables will be relative to.
* @throws CancelledException upon user cancellation.
*/
private int getFrameBaseOffset(TaskMonitor monitor) throws CancelledException {
int retAddrSize = function.getProgram().getDefaultPointerSize();
if (retAddrSize != 8) {
// don't do this for 32 bit.
return -retAddrSize; // 32 bit has a -4 byte offset
}
Register frameReg = function.getProgram().getCompilerSpec().getStackPointer();
Address entryAddr = function.getEntryPoint();
AddressSet scopeSet = new AddressSet();
scopeSet.addRange(entryAddr, entryAddr.add(64));
CallDepthChangeInfo valueChange =
new CallDepthChangeInfo(function, scopeSet, frameReg, monitor);
InstructionIterator instructions =
function.getProgram().getListing().getInstructions(scopeSet, true);
int max = 0;
while (instructions.hasNext()) {
monitor.checkCanceled();
Instruction next = instructions.next();
int newValue = valueChange.getDepth(next.getMinAddress());
if (newValue < -(20 * 1024) || newValue > (20 * 1024)) {
continue;
}
if (Math.abs(newValue) > Math.abs(max)) {
max = newValue;
}
}
return max;
}
private static class RegisterChangeCalculator {
private Map<Register, Integer> registerChangeByRegisterName = new HashMap<>();
private CallDepthChangeInfo callDepthChangeInfo;
private Address debugStart;
private RegisterChangeCalculator(AbstractProcedureMsSymbol procedureSymbol,
Function function, TaskMonitor monitor) throws CancelledException {
callDepthChangeInfo = createCallDepthChangInfo(procedureSymbol, function, monitor);
}
private CallDepthChangeInfo createCallDepthChangInfo(
AbstractProcedureMsSymbol procedureSymbol, Function function, TaskMonitor monitor)
throws CancelledException {
if (procedureSymbol == null) {
return null;
}
Register frameReg = function.getProgram().getCompilerSpec().getStackPointer();
Address entryAddr = function.getEntryPoint();
debugStart = entryAddr.add(procedureSymbol.getDebugStartOffset());
AddressSet scopeSet = new AddressSet();
scopeSet.addRange(entryAddr, debugStart);
return new CallDepthChangeInfo(function, scopeSet, frameReg, monitor);
}
Integer getRegChange(PdbApplicator applicator, Register register) {
if (callDepthChangeInfo == null || register == null) {
return null;
}
Integer change = registerChangeByRegisterName.get(register);
if (change != null) {
return change;
}
change = callDepthChangeInfo.getRegDepth(debugStart, register);
registerChangeByRegisterName.put(register, change);
return change;
}
}
}

View File

@ -0,0 +1,79 @@
/* ###
* 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 MsSymbolApplier {
private AbstractLabelMsSymbol symbol;
/**
* Constructor
* @param applicator the {@link PdbApplicator} for which we are working.
* @param iter the Iterator containing the symbol sequence being processed
* @throws CancelledException upon user cancellation
*/
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
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.getAddress(symbol);
if (applicator.isInvalidAddress(symbolAddress, name)) {
return;
}
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);
}
}
applicator.createSymbol(symbolAddress, symbol.getName(), false);
}
@Override
void applyTo(MsSymbolApplier applyToApplier) {
// Do nothing
}
}

View File

@ -0,0 +1,78 @@
/* ###
* 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.*;
import ghidra.app.util.pdb.pdbapplicator.SymbolGroup.AbstractMsSymbolIterator;
import ghidra.util.exception.AssertException;
import ghidra.util.exception.CancelledException;
/**
* Applier for {@link AbstractLocalSymbolInOptimizedCodeMsSymbol} symbols.
*/
public class LocalOptimizedSymbolApplier extends MsSymbolApplier {
private AbstractLocalSymbolInOptimizedCodeMsSymbol symbol;
/**
* Constructor
* @param applicator the {@link PdbApplicator} for which we are working.
* @param iter the Iterator containing the symbol sequence being processed
*/
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
void apply() throws PdbException, CancelledException {
pdbLogAndInfoMessage(this,
"Cannot apply " + this.getClass().getSimpleName() + " directly to program");
}
@Override
void applyTo(MsSymbolApplier applyToApplier) throws PdbException, CancelledException {
if (!applicator.getPdbApplicatorOptions().applyFunctionVariables()) {
return;
}
if (applyToApplier instanceof FunctionSymbolApplier) {
FunctionSymbolApplier functionSymbolApplier = (FunctionSymbolApplier) applyToApplier;
doWork(functionSymbolApplier);
}
}
private void doWork(FunctionSymbolApplier functionSymbolApplier)
throws CancelledException, PdbException {
// TODO: Not doing anything with the information yet.
symbol.getLocalVariableFlags();
symbol.getName();
symbol.getTypeRecordNumber();
while (iter.hasNext() &&
(iter.peek() instanceof AbstractDefinedSingleAddressRangeMsSymbol)) {
applicator.checkCanceled();
DefinedSingleAddressRangeSymbolApplier rangeApplier =
new DefinedSingleAddressRangeSymbolApplier(applicator, iter);
rangeApplier.applyTo(this);
}
}
}

View File

@ -0,0 +1,263 @@
/* ###
* 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.AbstractMemberFunctionMsType;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.CallingConvention;
import ghidra.program.model.data.DataType;
import ghidra.util.exception.CancelledException;
/**
* Applier for {@link AbstractMemberFunctionMsType} types.
*/
public class MemberFunctionTypeApplier extends AbstractFunctionTypeApplier {
private MsTypeApplier thisPointerApplier = null;
/**
* 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, AbstractMemberFunctionMsType msType)
throws IllegalArgumentException {
super(applicator, msType);
}
@Override
BigInteger getSize() {
return BigInteger.ZERO;
}
@Override
protected CallingConvention getCallingConvention() {
return ((AbstractMemberFunctionMsType) msType).getCallingConvention();
}
@Override
protected boolean hasThisPointer() {
MsTypeApplier 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
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) {
MsTypeApplier 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 MsTypeApplier getThisPointerApplier(AbstractMemberFunctionMsType procType) {
MsTypeApplier 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 MsTypeApplier getThisUnderlyingApplier(PointerTypeApplier thisApplier) {
return thisApplier.getUnmodifiedUnderlyingTypeApplier();
}
private AbstractComplexTypeApplier getContainingComplexApplier(
AbstractMemberFunctionMsType procType) throws PdbException {
return AbstractComplexTypeApplier.getComplexApplier(applicator,
procType.getContainingClassRecordNumber());
}
// private void processPointerUnderlyingType()
// throws CancelledException, PdbException {
//
// AbstractMsTypeApplier thisUnderlyingTypeApplier = thisPointerApplier.getUnmodifiedUnderlyingTypeApplier();
//
// if (!(thisUnderlyingTypeApplier instanceof CompositeTypeApplier)) {
// applicator.getLog().appendMsg(
// "thisUnderlyingTypeApplier is invalid type for " + msType.getName());
// return;
// }
// CompositeTypeApplier thisUnderlyingCompositeApplier =
// (CompositeTypeApplier) thisUnderlyingTypeApplier;
// SymbolPath thisUnderlyingClassSymbolPath =
// thisUnderlyingCompositeApplier.getFixedSymbolPath();
// applicator.predefineClass(thisUnderlyingClassSymbolPath);
// }
// private AbstractPointerMsType processThisPointer(AbstractMemberFunctionMsType procType)
// throws CancelledException, PdbException {
// int thisPointerTypeIndex = procType.getThisPointerTypeIndex();
// AbstractMsTypeApplier thisPointerApplier = applicator.getTypeApplier(thisPointerTypeIndex);
//
// if ((thisPointerApplier instanceof PrimitiveTypeApplier &&
// ((PrimitiveTypeApplier) thisPointerApplier).isNoType())) {
// return null; // such as for static member functions
// }
// if (!(thisPointerApplier instanceof PointerTypeApplier)) {
// applicator.getLog().appendMsg("thisApplier is invalid type for " + msType.getName());
// return null;
// }
// return (AbstractPointerMsType) thisPointerApplier.getMsType();
// }
//
// private void processPointerUnderlyingType(AbstractPointerMsType thisPointerMsType)
// throws CancelledException, PdbException {
//
// int thisUnderlyingTypeIndex = thisPointerMsType.getUnderlyingTypeIndex();
// AbstractMsTypeApplier thisUnderlyingTypeApplier =
// applicator.getTypeApplier(thisUnderlyingTypeIndex);
//
// // TODO: does not recurse below one level of modifiers... consider doing a recursion.
// if (thisUnderlyingTypeApplier instanceof ModifierTypeApplier) {
// ModifierTypeApplier x = (ModifierTypeApplier) thisUnderlyingTypeApplier;
// int y = ((AbstractModifierMsType) (x.getMsType())).getModifiedTypeIndex();
// thisUnderlyingTypeApplier = applicator.getTypeApplier(y);
// }
// if (!(thisUnderlyingTypeApplier instanceof CompositeTypeApplier)) {
// applicator.getLog().appendMsg(
// "thisUnderlyingTypeApplier is invalid type for " + msType.getName());
// return;
// }
// CompositeTypeApplier thisUnderlyingCompositeApplier =
// (CompositeTypeApplier) thisUnderlyingTypeApplier;
// SymbolPath thisUnderlyingClassSymbolPath =
// thisUnderlyingCompositeApplier.getFixedSymbolPath();
// applicator.predefineClass(thisUnderlyingClassSymbolPath);
// }
}

View File

@ -0,0 +1,87 @@
/* ###
* 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.AbstractMemberMsType;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.ClassFieldMsAttributes;
import ghidra.program.model.data.DataType;
import ghidra.util.exception.CancelledException;
/**
* Applier for {@link AbstractMemberMsType} types.
*/
public class MemberTypeApplier extends MsTypeApplier {
/**
* Constructor for member type applier.
* @param applicator {@link PdbApplicator} for which this class is working.
* @param msType {@link AbstractMemberMsType} to process.
*/
public MemberTypeApplier(PdbApplicator applicator, AbstractMemberMsType msType) {
super(applicator, msType);
}
@Override
BigInteger getSize() {
return BigInteger.ZERO;
}
@Override
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);
}
String getName() {
return ((AbstractMemberMsType) msType).getName();
}
BigInteger getOffset() {
return ((AbstractMemberMsType) msType).getOffset();
}
ClassFieldMsAttributes getAttribute() {
return ((AbstractMemberMsType) msType).getAttribute();
}
MsTypeApplier getFieldTypeApplier() {
return applicator.getTypeApplier(
((AbstractMemberMsType) msType).getFieldTypeRecordNumber());
}
private DataType applyMemberMsType(AbstractMemberMsType type) {
// String memberName = type.getName();
// BigInteger memberOffset = type.getOffset();
// ClassFieldMsAttributes memberAttributes = type.getAttribute();
// int fieldTypeIndex = type.getFieldTypeRecordIndex();
//
// AbstractMsTypeApplier fieldTypeApplier = applicator.getTypeApplier(fieldTypeIndex);
//
// DataType fieldDataType = fieldTypeApplier.getDataType();
//
//// DataType fieldDataType = getConvertedDataType(applicator, fieldTypeIndex);
return null;
}
}

View File

@ -0,0 +1,110 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.pdb.pdbapplicator;
import java.math.BigInteger;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.AbstractModifierMsType;
import ghidra.program.model.data.DataType;
import ghidra.util.exception.CancelledException;
/**
* Applier for {@link AbstractModifierMsType} types.
*/
public class ModifierTypeApplier extends MsTypeApplier {
private MsTypeApplier modifiedTypeApplier = null;
/**
* Constructor for modifier type applier.
* @param applicator {@link PdbApplicator} for which this class is working.
* @param msType {@link AbstractModifierMsType} to processes.
*/
public ModifierTypeApplier(PdbApplicator applicator, AbstractModifierMsType msType) {
super(applicator, msType);
}
//==============================================================================================
@Override
void deferredApply() throws PdbException, CancelledException {
// Do nothing. Already applied. Just needs late resolve
}
//==============================================================================================
@Override
BigInteger getSize() {
if (modifiedTypeApplier == null) {
return BigInteger.ZERO;
}
return modifiedTypeApplier.getSize();
}
@Override
void apply() throws PdbException, CancelledException {
// dataType = applyModifierMsType((AbstractModifierMsType) msType);
applyOrDeferForDependencies();
}
private void applyOrDeferForDependencies() {
AbstractModifierMsType type = (AbstractModifierMsType) msType;
applyModifierMsType(type);
MsTypeApplier modifiedApplier = applicator.getTypeApplier(type.getModifiedRecordNumber());
if (modifiedApplier.isDeferred()) {
applicator.addApplierDependency(this, modifiedApplier);
setDeferred();
}
else {
// applyModifierMsType(type);
// defer(false);
}
}
@Override
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();
}
MsTypeApplier getModifiedTypeApplier() {
return modifiedTypeApplier;
}
}

View File

@ -0,0 +1,97 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.pdb.pdbapplicator;
import java.util.Objects;
import ghidra.app.plugin.processors.sleigh.symbol.Symbol;
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.pdb.pdbapplicator.SymbolGroup.AbstractMsSymbolIterator;
import ghidra.util.exception.CancelledException;
/**
* Abstract class representing the applier for a specific {@link AbstractMsSymbol}. The
* {@link #apply()} method creates an associated {@link Symbol}, if applicable, or might
* apply information to other {@link MsSymbolApplier AbstractMsSymbolAppliers}.
* Methods associated with the {@link MsSymbolApplier} or derived class will
* make fields available to the user from the {@link AbstractMsSymbol}.
*/
public abstract class MsSymbolApplier {
protected PdbApplicator applicator;
protected AbstractMsSymbolIterator iter;
protected long currentOffset;
/**
* Constructor
* @param applicator the {@link PdbApplicator} for which we are working.
* @param iter the Iterator containing the symbol sequence being processed
*/
public MsSymbolApplier(PdbApplicator applicator, AbstractMsSymbolIterator iter) {
Objects.requireNonNull(applicator, "applicator cannot be null");
Objects.requireNonNull(iter, "iter cannot be null");
this.applicator = applicator;
this.iter = iter;
currentOffset = iter.getCurrentOffset();
}
/**
* Puts message to {@link PdbLog} and to Msg.info()
* @param originator a Logger instance, "this", or YourClass.class
* @param message the message to display
*/
protected void pdbLogAndInfoMessage(Object originator, String message) {
applicator.pdbLogAndInfoMessage(originator, message);
}
/**
* 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
*/
abstract void apply() throws PdbException, CancelledException;
/**
* Applies logic of this class to another {@link MsSymbolApplier} 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.
*/
abstract void applyTo(MsSymbolApplier 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}.
*/
void manageBlockNesting(MsSymbolApplier applierParam) {
// Do nothing by default.
}
}

View File

@ -0,0 +1,253 @@
/* ###
* 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.*;
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 for a specific {@link AbstractMsType}. The
* {@link #apply()} method creates an associated {@link DataType}, if applicable.
* Methods associated with the {@link MsTypeApplier} 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 MsTypeApplier {
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;
private boolean isDeferred = false;
protected Set<MsTypeApplier> waitSet = new HashSet<>();
/**
* Constructor.
* @param applicator {@link PdbApplicator} for which this class is working.
* @param msType {@link AbstractMsType} to apply.
*/
public MsTypeApplier(PdbApplicator applicator, AbstractMsType msType) {
this.applicator = applicator;
this.msType = msType;
RecordNumber recordNumber = msType.getRecordNumber();
if (recordNumber != null) {
index = recordNumber.getNumber();
}
else {
index = -1;
}
dataType = null;
}
/**
* Puts message to {@link PdbLog} and to Msg.info()
* @param originator a Logger instance, "this", or YourClass.class
* @param message the message to display
*/
protected void pdbLogAndInfoMessage(Object originator, String message) {
applicator.pdbLogAndInfoMessage(originator, message);
}
/**
* Returns {@code true} if the type has been applied
* @return {@code true} if applied.
*/
boolean isApplied() {
return applied;
}
/**
* Sets the {@code applied} flag to {@code true}
*/
void setApplied() {
applied = true;
}
/**
* Sets the isDeferred flag to indicate that the application of the information should be
* done when the {@link @deferredApply()} method is called
*/
void setDeferred() {
isDeferred = true;
}
/**
* Returns {@code true} if the application as been deferred (during the {@link #apply()}
* method. The {@link #deferredApply()} method will need to be applied at the appropriate
* place in the processing sequence (depending on data dependency ordering) as determined
* and driven by the {@link PdbApplicator}.
* @return {@code true} if application was deferred
*/
boolean isDeferred() {
return isDeferred;
}
/**
* Performs the work required in a deferred application of the data type. This method
* is used by the {@link PdbApplicator} in the correct data dependency sequence.
* @throws PdbException on error applying the data type
* @throws CancelledException on user cancellation
*/
void deferredApply() throws PdbException, CancelledException {
// default is to do nothing, as most appliers are not deferrable (or should not be).
}
/**
* Returns the applier for this type that needs to be called when the data type is processed
* in dependency order. This will usually return "this," except in cases where there can be
* forward references and definition appliers for the same type.
* @return the applier to be used for doing the real applier work when dependency order
* matters.
*/
MsTypeApplier getDependencyApplier() {
return this;
}
/**
* Resolves the type through the DataTypeManager and makes the resolved type primary.
*/
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.
*/
AbstractMsType getMsType() {
return msType;
}
/**
* Returns the {@link DataType} associated with this applier/wrapper.
* @return {@link DataType} associated with this applier/wrapper.
*/
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
*/
abstract void apply() throws PdbException, CancelledException;
/**
* Returns the size of the type or 0 if unknown.
* @return the size; zero if unknown.
*/
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.
*/
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.
*/
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;
}
MsTypeApplier other = (MsTypeApplier) obj;
if (index != other.index) {
return false;
}
if (!msType.getClass().getSimpleName().equals(other.msType.getClass().getSimpleName())) {
return false;
}
return true;
}
protected void waitSetPut(MsTypeApplier applier) {
waitSet.add(applier);
}
protected boolean waitSetRemove(MsTypeApplier applier) {
return waitSet.remove(applier);
}
protected boolean waitSetIsEmpty() {
return waitSet.isEmpty();
}
protected MsTypeApplier waitSetGetNext() {
List<MsTypeApplier> list = new ArrayList<>(waitSet);
return list.get(0);
}
}

View File

@ -0,0 +1,173 @@
/* ###
* 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 MsTypeApplier {
private MsTypeApplier nestedTypeDefinitionApplier = null;
/**
* 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
BigInteger getSize() {
if (nestedTypeDefinitionApplier == null) {
return BigInteger.ZERO;
}
return nestedTypeDefinitionApplier.getSize();
}
/**
* Returns the name of this nested type.
* @return Name of the nested type.
*/
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.
*/
String getMemberName() {
if (nestedTypeDefinitionApplier == null) {
return "";
}
if (msType instanceof AbstractNestedTypeMsType) {
return ((AbstractNestedTypeMsType) msType).getName();
}
return ((AbstractNestedTypeExtMsType) msType).getName();
}
MsTypeApplier 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.
*/
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.
*/
ClassFieldMsAttributes getAttributes() {
AbstractMsType type = nestedTypeDefinitionApplier.getMsType();
if (type instanceof AbstractNestedTypeExtMsType) {
return ((AbstractNestedTypeExtMsType) type).getClassFieldAttributes();
}
return null;
}
@Override
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);
}
MsTypeApplier getNestedTypeApplier() {
return nestedTypeDefinitionApplier;
}
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;
}
}

View File

@ -0,0 +1,50 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.pdb.pdbapplicator;
import ghidra.app.util.bin.format.pdb2.pdbreader.symbol.AbstractMsSymbol;
import ghidra.app.util.pdb.pdbapplicator.SymbolGroup.AbstractMsSymbolIterator;
/**
* A dummy {@link MsSymbolApplier}, 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 MsSymbolApplier {
private AbstractMsSymbol symbol;
/**
* Constructor
* @param applicator the {@link PdbApplicator} for which we are working.
* @param iter the Iterator containing the symbol sequence being processed
*/
public NoSymbolApplier(PdbApplicator applicator, AbstractMsSymbolIterator iter) {
super(applicator, iter);
symbol = iter.next();
}
@Override
void applyTo(MsSymbolApplier applyToApplier) {
// Do nothing.
}
@Override
void apply() {
// Do nothing.
}
}

View File

@ -0,0 +1,50 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.pdb.pdbapplicator;
import java.math.BigInteger;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.AbstractMsType;
/**
* Used for creating a wrapper for when there is not associated type to the PDB type (or if we
* have not yet created the association).
*/
public class NoTypeApplier extends MsTypeApplier {
/**
* 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
BigInteger getSize() {
return BigInteger.ZERO;
}
@Override
void apply() {
// Do nothing (maybe should log something... not sure)
// applicator.getLog().appendMsg("");
}
}

View File

@ -0,0 +1,45 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.pdb.pdbapplicator;
/**
* PDB Analyzer user algorithmic choice for performing object oriented class layout.
* <p>
* Actual algorithms determination is as follows:
* {@link #MEMBERS_ONLY} is a fixed setting. The others weigh the setting against the data
* available in the class records. They all start out as {@link #COMPLEX} but fall back to a
* more simplistic layout when the data permits:
* <p> {@link #SIMPLE_COMPLEX} can fall back to "simple" and
* <p> {@link #BASIC_SIMPLE_COMPLEX} can fall back to "simple" or "basic"
*/
public enum ObjectOrientedClassLayout {
MEMBERS_ONLY("Legacy"),
BASIC_SIMPLE_COMPLEX("Complex with Basic Fallback"),
SIMPLE_COMPLEX("Complex with Simple Fallback"),
COMPLEX("Complex Always");
private final String label;
@Override
public String toString() {
return label;
}
private ObjectOrientedClassLayout(String label) {
this.label = label;
}
}

View File

@ -0,0 +1,552 @@
/* ###
* 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.address.AddressSpace;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.model.symbol.Symbol;
import ghidra.program.model.symbol.SymbolIterator;
import ghidra.util.exception.CancelledException;
/**
* Manages Address/Section/Segment-related PDB items.
* Has method for providing real addresses.
*/
public class PdbAddressManager {
// This could be a valid address for the program, but we are using it as a flag. We return
// it to designate that an address is an external address, and we use it outside of this class
// to test for it being an external address.
static final Address EXTERNAL_ADDRESS = AddressSpace.EXTERNAL_SPACE.getAddress(0);
static final Address BAD_ADDRESS = Address.NO_ADDRESS; // using NO_ADDRESS as a marker
//==============================================================================================
private Map<Integer, Long> realAddressesBySection;
private List<PeCoffGroupMsSymbol> memoryGroupRefinement;
private List<PeCoffSectionMsSymbol> memorySectionRefinement;
private List<SegmentInfo> allSegmentsInfo;
// Map of Address by symbol name... if a name has appeared more than once, then the Address
// is written with Address.NO_ADDRESS to indicate that the name is found at more than one
// address (not unique from the perspective we need of being able to map PDB addresses to
// possibly different addresses (possibly from an application of another PDB with accurate
// public or "unique" symbols). Originally, we were only going to use symbols with mangled
// names, but opened this up to a wider field of symbol names.
private Map<String, Address> addressByPreExistingSymbolName;
// Since we are already visiting all existing symbols, and since it will be quicker to do in
// one pass than to continually request the primary symbol at a particular address (as we
// are also adding more symbols), we will get an initial snapshot of what symbol is primary
// at any particular address before we start adding more.
private Map<Address, Symbol> primarySymbolByAddress;
private Map<Address, Address> remapAddressByAddress;
private PdbApplicator applicator;
private Address imageBase;
//==============================================================================================
// API
//==============================================================================================
/**
* Manager
* @param applicator {@link PdbApplicator} for which this class is working.
* @param imageBase Address from which all other addresses are based.
* @throws PdbException If Program is null;
*/
PdbAddressManager(PdbApplicator applicator, Address imageBase) throws PdbException {
Objects.requireNonNull(applicator, "applicator may not be null");
Objects.requireNonNull(imageBase, "imageBase may not be null");
this.applicator = applicator;
this.imageBase = imageBase;
realAddressesBySection = new HashMap<>();
memoryGroupRefinement = new ArrayList<>();
memorySectionRefinement = new ArrayList<>();
allSegmentsInfo = new ArrayList<>();
addressByPreExistingSymbolName = new HashMap<>();
primarySymbolByAddress = new HashMap<>();
determineMemoryBlocks();
mapPreExistingSymbols();
createAddressRemap();
}
/**
* Returns the Address for the given symbol. If the {@link PdbApplicatorOptions}
* Address Remap option is turned on is turned on, it will attempt to map the address to a
* new address in the current program.
* @param symbol The {@link AddressMsSymbol}
* @return The Address, which can be {@code Address.NO_ADDRESS} if invalid or
* {@code Address.EXTERNAL_ADDRESS} if the address is external to the program.
*/
Address getAddress(AddressMsSymbol symbol) {
return getAddress(symbol.getSegment(), symbol.getOffset());
}
/**
* Returns the Address for the given section and offset. If the {@link PdbApplicatorOptions}
* Address Remap option is turned on is turned on, it will attempt to map the address to a
* new address in the current program.
* @param segment The segment
* @param offset The offset
* @return The Address, which can be {@code Address.NO_ADDRESS} if invalid or
* {@code Address.EXTERNAL_ADDRESS} if the address is external to the program.
*/
Address getAddress(int segment, long offset) {
Address address = getRawAddress(segment, offset);
if (applicator.getPdbApplicatorOptions().remapAddressUsingExistingPublicSymbols()) {
return getRemapAddressByAddress(address);
}
return address;
}
/**
* Returns the Address for the given section and offset. If the {@link PdbApplicatorOptions}
* Address Remap option is turned on is turned on, it will attempt to map the address to a
* new address in the current program.
* @param symbol The {@link AddressMsSymbol}
* @return The Address, which can be {@code Address.NO_ADDRESS} if invalid or
* {@code Address.EXTERNAL_ADDRESS} if the address is external to the program.
*/
Address getRawAddress(AddressMsSymbol symbol) {
return getRawAddress(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 invalid or
* {@code Address.EXTERNAL_ADDRESS} if the address is external to the program.
*/
Address getRawAddress(int segment, long offset) {
if (segment < 0 || segment > allSegmentsInfo.size()) {
return BAD_ADDRESS;
}
// We are lumping 0 and size as EXTERNAL... one or other could be image base... but not
// necessarily consistent... but that's OK... it is still "EXTERNAL" from the program.
else if (segment == 0 || segment == allSegmentsInfo.size()) {
// External address.
// Was getting issues of _IMAGE_DOSHEADER showing up with a segment index one
// beyond the end.
return EXTERNAL_ADDRESS;
}
SegmentInfo segmentInfo = allSegmentsInfo.get(segment);
if (offset >= segmentInfo.getLength()) {
return BAD_ADDRESS;
}
return segmentInfo.getStartAddress().add(offset);
}
/**
* 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 remapAddressByAddress.getOrDefault(address, address);
}
/**
* Returns the primary symbol for the address, as determined at the start of PDB processing
* before apply any PDB symbols.
* @param address the {@link Address}
* @return the primary symbol
*/
Symbol getPrimarySymbol(Address address) {
return primarySymbolByAddress.get(address);
}
/**
* Indicate to the {@link PdbAddressManager} that a new symbol with the given name has the
* associated address. This allows the PdbAddressManager to create and organize the
* re-mapped address and supply them. Also returns the address of the pre-existing symbol
* of the same name if the name was unique, otherwise null if it didn't exist or wasn't
* unique.
* @param name the symbol name
* @param address its associated address
* @return the {@link Address} of existing symbol or null
*/
Address witnessSymbolNameAtAddress(String name, Address address) {
Address existingAddress = getAddressByPreExistingSymbolName(name);
putRemapAddressByAddress(address, existingAddress);
return existingAddress;
}
/**
* 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 put null instead.
// For now, the method that reads this information might report EXTERNAL instead of
// trying to use this.
long segmentZeroLength = 0x7fffffff;
allSegmentsInfo.add(new SegmentInfo(imageBase, segmentZeroLength));
AbstractDatabaseInterface dbi = applicator.getPdb().getDatabaseInterface();
if (dbi instanceof DatabaseInterfaceNew) {
DebugData debugData = ((DatabaseInterfaceNew) dbi).getDebugData();
List<ImageSectionHeader> imageSectionHeaders = debugData.getImageSectionHeaders();
for (ImageSectionHeader imageSectionHeader : imageSectionHeaders) {
long virtualAddress = imageSectionHeader.getVirtualAddress();
// TODO: not sure when unionPAVS is physical address vs. virtual size. Perhaps
// it keys off whether virtualAddress is not some special value such as
// 0x00000000 or 0xffffffff.
long size = imageSectionHeader.getUnionPAVS();
allSegmentsInfo.add(new SegmentInfo(imageBase.add(virtualAddress), size));
}
}
// else instance of DatabaseInterface; TODO: what can we do here?
// Maybe get information from the program itself.
// TODO: what should we do with these? Not doing anything at the moment
AbstractPdb pdb = applicator.getPdb();
List<SegmentMapDescription> segmentMapList = pdb.getDatabaseInterface().getSegmentMapList();
for (SegmentMapDescription segmentMapDescription : segmentMapList) {
segmentMapDescription.getSegmentOffset();
segmentMapDescription.getLength();
}
}
//==============================================================================================
//==============================================================================================
/**
* Filling in the maps as indicated by their descriptions.
* @throws PdbException If Program is null;
*/
private void mapPreExistingSymbols() throws PdbException {
// Cannot do this commented-out code here... as we are relying on the primary symbol
// map, regardless of the remap... so might need to separate the two. TODO: later thoughts
// if (!applicator.getPdbApplicatorOptions().remapAddressUsingExistingPublicSymbols()) {
// return;
// }
Program program = applicator.getProgram();
if (program == null) {
throw new PdbException("Program may not be null");
}
SymbolIterator iter = program.getSymbolTable().getAllSymbols(false);
while (iter.hasNext()) {
Symbol symbol = iter.next();
String name = symbol.getPath().toString();
Address address = symbol.getAddress();
Address existingAddress = addressByPreExistingSymbolName.get(name);
if (existingAddress == null) {
addressByPreExistingSymbolName.put(name, address);
}
else if (!existingAddress.equals(address)) {
addressByPreExistingSymbolName.put(name, Address.NO_ADDRESS);
}
if (primarySymbolByAddress.get(address) == null && symbol.isPrimary()) {
primarySymbolByAddress.put(address, symbol);
}
}
}
/**
* Returns the address for the symbol name. If the symbol name did not exist within the
* program when the list was being populated or if the name was seen at more than one address,
* then null is returned
* @param name the name of the symbol
* @return the address for that name or null
*/
private Address getAddressByPreExistingSymbolName(String name) {
Address address = addressByPreExistingSymbolName.get(name);
// Thin the list of map values we no longer need. This method should only be
// used after the list has been completely populated, and the NO_ADDRESS marker
// was only being used during the method that populated the list to indicate that a
// name was not unique for our needs.
if (address != null && address.equals(Address.NO_ADDRESS)) {
addressByPreExistingSymbolName.remove(name);
return null;
}
return address;
}
private void createAddressRemap() {
remapAddressByAddress = new HashMap<>();
// Put in two basic entries so we do not have to do conditional tests before looking
// up values in the table.
remapAddressByAddress.put(BAD_ADDRESS, BAD_ADDRESS);
remapAddressByAddress.put(EXTERNAL_ADDRESS, EXTERNAL_ADDRESS);
}
/**
* 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
*/
private void putRemapAddressByAddress(Address address, Address remapAddress) {
Address lookup = remapAddressByAddress.get(address);
if (lookup == null) {
remapAddressByAddress.put(address, remapAddress);
}
else if (!lookup.equals(remapAddress) && lookup != BAD_ADDRESS) {
applicator.appendLogMsg("Trying to map a mapped address to a new address... key: " +
address + ", currentMap: " + lookup + ", newMap: " + remapAddress);
remapAddressByAddress.put(address, BAD_ADDRESS);
}
}
//==============================================================================================
//==============================================================================================
/**
* 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 = getAddress(sym);
PdbLog.message(String.format("%s: [%04X:%08X](%s) Len:%08X, Characteristics:%08X", name,
segment, offset, address.toString(), length, characteristics));
}
}
//==============================================================================================
//==============================================================================================
// TODO: This is not complete... It was a research thought, which might get looked at in the
// future.
/**
* Tries to align section/segment information of the PDB in {@link SegmentMapDescription} from
* the {@link AbstractDatabaseInterface} header substream with the memory blocks of the
* {@link Program}. Initializes the lookup table to be used for processing the PDB.
* <P>
* We have seen cases where blocks of the program are combined into a single block representing
* one segment in the PDB.
* <P>
* The PDB's {@link SegmentMapDescription} is not always fully populated, though the length
* field seems to be consistently available.
* @throws PdbException if there was a problem processing the data.
*/
@SuppressWarnings("unused") // for method not being called and local variables ununsed.
private void reconcileMemoryBlocks() throws PdbException {
// ImageSectionHeader imageSectionHeader =
// pdb.getDatabaseInterface().getDebugData().getImageSectionHeader();
AbstractPdb pdb = applicator.getPdb();
Program program = applicator.getProgram();
if (program == null) {
return;
}
Memory mem = program.getMemory();
MemoryBlock[] blocks = mem.getBlocks();
List<SegmentMapDescription> segmentMapList = pdb.getDatabaseInterface().getSegmentMapList();
/**
* Program has additional "Headers" block set up by the {@link PeLoader}.
*/
int progIndexLimit = blocks.length;
int pdbIndexLimit = segmentMapList.size();
int progIndex = 1;
int pdbIndex = 0;
// Set section/segment 0 to image base. (should be what is header)
List<SegmentInfo> myAllSegmentsInfo = new ArrayList<>(); // Contender set of segment info (vs what is in class)
myAllSegmentsInfo.add(new SegmentInfo(blocks[0].getStart(), blocks[0].getSize()));
// Try to match memory in order, grouping as needed.
long blockAccum = 0;
while (progIndex < progIndexLimit && pdbIndex < pdbIndexLimit) {
SegmentMapDescription segmentDescription = segmentMapList.get(pdbIndex);
Address blockStart = blocks[progIndex].getStart();
while (blockAccum < segmentDescription.getLength() && progIndex < progIndexLimit) {
// progIndex++;
Address addr1 = blocks[progIndex].getStart();
Address addr2 = blockStart.add(blockAccum);
if (!blocks[progIndex].getStart().equals(blockStart.add(blockAccum))) {
// Problem... blocks are not adjacent... TODO: how do we reconcile?
throw new PdbException("Memory block reconciliation failure");
}
blockAccum += blocks[progIndex].getSize();
if (blockAccum == segmentDescription.getLength()) {
myAllSegmentsInfo.add(new SegmentInfo(blockStart, blockAccum));
progIndex++;
pdbIndex++;
blockAccum = 0;
break;
}
else if (blockAccum > segmentDescription.getLength()) {
// Problem... TODO: how do we reconcile?
throw new PdbException(
"Memory block reconciliation failure--needs reverse aggregation");
}
progIndex++;
}
}
if (pdbIndex == pdbIndexLimit - 1 &&
segmentMapList.get(pdbIndex).getLength() == 0xffffffffL) {
pdbIndex++;
}
if (progIndex != progIndexLimit || pdbIndex != pdbIndexLimit) {
// Problem... TODO: didn't both end iterations together
throw new PdbException("Memory block reconciliation failure--remaining data");
}
}
//==============================================================================================
// TODO: This is not complete... It was a research thought, which might get looked at in the
// future.
@SuppressWarnings("unused") // for method not being called.
private boolean garnerSectionSegmentInformation() throws PdbException {
AbstractPdb pdb = applicator.getPdb();
if (pdb.getDatabaseInterface() == null) {
return false;
}
// ImageSectionHeader imageSectionHeader =
// pdb.getDatabaseInterface().getDebugData().getImageSectionHeader();
int num = 1;
for (AbstractModuleInformation module : pdb.getDatabaseInterface().getModuleInformationList()) {
if ("* Linker *".equals(module.getModuleName())) {
List<AbstractMsSymbol> linkerSymbolList =
applicator.getSymbolGroupForModule(num).getSymbols();
for (AbstractMsSymbol symbol : linkerSymbolList) {
if (symbol instanceof PeCoffSectionMsSymbol) {
PeCoffSectionMsSymbol section = (PeCoffSectionMsSymbol) symbol;
int sectionNum = section.getSectionNumber();
long realAddress = section.getRva();
section.getLength();
section.getCharacteristics();
section.getAlign();
section.getName();
realAddressesBySection.put(sectionNum, realAddress);
}
if (symbol instanceof PeCoffGroupMsSymbol) {
PeCoffGroupMsSymbol group = (PeCoffGroupMsSymbol) symbol;
group.getName();
group.getSegment();
group.getLength();
group.getOffset();
group.getCharacteristics();
}
}
return true;
}
num++;
}
return false;
}
}

View File

@ -0,0 +1,290 @@
/* ###
* 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.
*/
//@formatter:off
private static final Set<Integer> EXPECTED_GLOBAL_SYMBOLS = Set.of(
// AbstractReferenceMsSymbol
DataReferenceStMsSymbol.PDB_ID,
DataReferenceMsSymbol.PDB_ID,
LocalProcedureReferenceMsSymbol.PDB_ID,
LocalProcedureReferenceStMsSymbol.PDB_ID,
ProcedureReferenceMsSymbol.PDB_ID,
ProcedureReferenceStMsSymbol.PDB_ID,
AnnotationReferenceMsSymbol.PDB_ID,
TokenReferenceToManagedProcedureMsSymbol.PDB_ID,
// AbstractDataMsSymbol
GlobalData16MsSymbol.PDB_ID,
GlobalData3216MsSymbol.PDB_ID,
GlobalData32MsSymbol.PDB_ID,
GlobalData32StMsSymbol.PDB_ID,
LocalData16MsSymbol.PDB_ID,
LocalData3216MsSymbol.PDB_ID,
LocalData32MsSymbol.PDB_ID,
LocalData32StMsSymbol.PDB_ID,
GlobalManagedDataMsSymbol.PDB_ID,
GlobalManagedDataStMsSymbol.PDB_ID,
LocalManagedDataMsSymbol.PDB_ID,
LocalManagedDataStMsSymbol.PDB_ID,
// AbstractThreadStorageMsSymbol
GlobalThreadStorage3216MsSymbol.PDB_ID,
GlobalThreadStorage32MsSymbol.PDB_ID,
GlobalThreadStorage32StMsSymbol.PDB_ID,
LocalThreadStorage3216MsSymbol.PDB_ID,
LocalThreadStorage32MsSymbol.PDB_ID,
LocalThreadStorage32StMsSymbol.PDB_ID,
// AbstractUserDefinedTypeMsSymbol
CobolUserDefinedType16MsSymbol.PDB_ID,
CobolUserDefinedTypeMsSymbol.PDB_ID,
CobolUserDefinedTypeStMsSymbol.PDB_ID,
UserDefinedType16MsSymbol.PDB_ID,
UserDefinedTypeMsSymbol.PDB_ID,
UserDefinedTypeStMsSymbol.PDB_ID,
// AbstractConstantMsSymbol
Constant16MsSymbol.PDB_ID,
ConstantMsSymbol.PDB_ID,
ConstantStMsSymbol.PDB_ID,
ManagedConstantMsSymbol.PDB_ID
);
//@formatter:on
/**
* List of symbols seen (by their ID) as Public symbols.
*/
//@formatter:off
private static final Set<Integer> EXPECTED_LINKER_SYMBOLS = Set.of(
PeCoffSectionMsSymbol.PDB_ID,
TrampolineMsSymbol.PDB_ID,
ObjectNameMsSymbol.PDB_ID,
Compile3MsSymbol.PDB_ID,
Compile2MsSymbol.PDB_ID,
Compile2StMsSymbol.PDB_ID,
EnvironmentBlockMsSymbol.PDB_ID
);
//@formatter:on
private Set<Class<? extends AbstractMsType>> cannotApplyTypes = new HashSet<>();
private Set<Class<? extends AbstractMsType>> unexpectedMemberFunctionThisPointerTypes =
new HashSet<>();
private Set<Class<? extends AbstractMsType>> unexpectedMemberFunctionThisPointerUnderlyingTypes =
new HashSet<>();
private Set<Class<? extends AbstractMsType>> unexpectedMemberFunctionContainerTypes =
new HashSet<>();
private Set<Class<? extends AbstractMsSymbol>> cannotApplySymbols = new HashSet<>();
private Set<Class<? extends AbstractMsSymbol>> unexpectedGlobalSymbols = new HashSet<>();
private Set<Class<? extends AbstractMsSymbol>> unexpectedPublicSymbols = new HashSet<>();
private boolean witnessEnumerateNarrowing = false;
/**
* Method to capture data/item type that cannot be applied.
* @param type The data/item type witnessed.
*/
void witnessCannotApplyDataType(AbstractMsType type) {
cannotApplyTypes.add(type.getClass());
}
/**
* Method to capture symbol type that cannot be applied.
* @param symbol The symbol type witnessed.
*/
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.
*/
void witnessGlobalSymbolType(AbstractMsSymbol symbol) {
if (!EXPECTED_GLOBAL_SYMBOLS.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.
*/
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.
*/
void witnessLinkerSymbolType(AbstractMsSymbol symbol) {
if (!EXPECTED_LINKER_SYMBOLS.contains(symbol.getPdbId())) {
// do nothing for now
}
}
/**
* Method to capture witnessing of Enumerate narrowing.
*/
void witnessEnumerateNarrowing() {
witnessEnumerateNarrowing = true;
}
/**
* Method to capture unusual this pointer types.
* @param applier The {@AbstractMsTypeApplier} for the supposed this pointer.
*/
void witnessMemberFunctionThisPointer(MsTypeApplier 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.
*/
void witnessMemberFunctionThisPointerUnderlyingType(MsTypeApplier 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.
*/
void witnessMemberFunctionContainingType(MsTypeApplier 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.
*/
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 an unsupported 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 an unsupported PDB symbol type: " +
clazz.getSimpleName() + "\n");
}
return builder.toString();
}
private String reportUnexpectedPublicSymbols() {
StringBuilder builder = new StringBuilder();
for (Class<? extends AbstractMsSymbol> clazz : unexpectedPublicSymbols) {
builder.append("Unexpected one or more instances of PDB public symbol type: " +
clazz.getSimpleName() + "\n");
}
return builder.toString();
}
private String reportUnexpectedGlobalSymbols() {
StringBuilder builder = new StringBuilder();
for (Class<? extends AbstractMsSymbol> clazz : unexpectedGlobalSymbols) {
builder.append("Unexpected one or more instances of PDB global symbol type: " +
clazz.getSimpleName() + "\n");
}
return builder.toString();
}
private String reportEnumerateNarrowing() {
if (witnessEnumerateNarrowing) {
return "Enumerate narrowing was witnessed\n";
}
return "";
}
}

View File

@ -0,0 +1,194 @@
/* ###
* 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 {
public static final boolean DEFAULT_APPLY_CODE_SCOPE_BLOCK_COMMENTS = false;
public static final boolean DEFAULT_APPLY_INSTRUCTION_LABELS = false;
public static final PdbApplicatorRestrictions DEFAULT_RESTRICTIONS =
PdbApplicatorRestrictions.NONE;
public static final boolean DEFAULT_REMAP_ADDRESSES_USING_EXISTING_SYMBOLS = false;
public static final boolean DEFAULT_ALLOW_DEMOTE_PRIMARY_MANGLED_SYMBOLS = true;
// TODO: set the following to true if we come up with a reasonably good solution
public static final boolean DEFAULT_APPLY_FUNCTION_VARIABLES = false;
public static final ObjectOrientedClassLayout DEFAULT_CLASS_LAYOUT =
ObjectOrientedClassLayout.MEMBERS_ONLY;
// public static final ObjectOrientedClassLayout DEFAULT_CLASS_LAYOUT =
// ObjectOrientedClassLayout.BASIC_SIMPLE_COMPLEX;
// public static final ObjectOrientedClassLayout DEFAULT_CLASS_LAYOUT =
// ObjectOrientedClassLayout.SIMPLE_COMPLEX;
// public static final ObjectOrientedClassLayout DEFAULT_CLASS_LAYOUT =
// ObjectOrientedClassLayout.COMPLEX;
//==============================================================================================
private boolean applyCodeScopeBlockComments;
private boolean applyInstructionLabels;
private PdbApplicatorRestrictions restrictions;
private boolean remapAddressesUsingExistingPublicSymbols;
private boolean allowDemotePrimaryMangledSymbols;
private boolean applyFunctionVariables; // investigation. might produce bad results.
private ObjectOrientedClassLayout classLayout;
/**
* Constructor
*/
public PdbApplicatorOptions() {
restoreDefaults();
}
/**
* Set the options back to their default values
*/
public void restoreDefaults() {
applyCodeScopeBlockComments = DEFAULT_APPLY_CODE_SCOPE_BLOCK_COMMENTS;
applyInstructionLabels = DEFAULT_APPLY_INSTRUCTION_LABELS;
restrictions = DEFAULT_RESTRICTIONS;
remapAddressesUsingExistingPublicSymbols = DEFAULT_REMAP_ADDRESSES_USING_EXISTING_SYMBOLS;
allowDemotePrimaryMangledSymbols = DEFAULT_ALLOW_DEMOTE_PRIMARY_MANGLED_SYMBOLS;
applyFunctionVariables = DEFAULT_APPLY_FUNCTION_VARIABLES;
classLayout = DEFAULT_CLASS_LAYOUT;
}
/**
* 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;
}
/**
* Set processing restrictions for PdbApplicator
* @param restrictions the restrictions
*/
public void setRestrictions(PdbApplicatorRestrictions restrictions) {
this.restrictions = restrictions;
}
/**
* Returns the current restrictions on PdbApplicator processing
* @return the restrictions
*/
public PdbApplicatorRestrictions getRestrictions() {
return restrictions;
}
/**
* 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 setRemapAddressUsingExistingPublicSymbols(boolean enable) {
this.remapAddressesUsingExistingPublicSymbols = enable;
}
/**
* Returns {@code true} if remapAddressesUsingExistingPublicSymbols is "on."
* @return {@code true} if remapAddressesUsingExistingPublicSymbols is "on."
*/
public boolean remapAddressUsingExistingPublicSymbols() {
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 allowDemotePrimaryMangledSymbols on
*/
public void setAllowDemotePrimaryMangledSymbols(boolean enable) {
this.allowDemotePrimaryMangledSymbols = enable;
}
/**
* Returns {@code true} if allowDemotePrimaryMangledSymbols is "on."
* @return {@code true} if allowDemotePrimaryMangledSymbols is "on."
*/
public boolean allowDemotePrimaryMangledSymbols() {
return allowDemotePrimaryMangledSymbols;
}
/**
* 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 class layout.
* @param classLayout composite layout
*/
public void setClassLayout(ObjectOrientedClassLayout classLayout) {
this.classLayout = classLayout;
}
/**
* Returns the physical layout out classes.
* @return the class layout.
*/
public ObjectOrientedClassLayout getClassLayout() {
return classLayout;
}
}

View File

@ -0,0 +1,35 @@
/* ###
* 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;
/**
* PDB Applicator restrictions of actions performed. Used by {@link PdbApplicatorOptions}
*/
public enum PdbApplicatorRestrictions {
NONE("None"), DATA_TYPES_ONLY("Data Types Only"), PUBLIC_SYMBOLS_ONLY("Public Symbols Only");
private final String label;
@Override
public String toString() {
return label;
}
private PdbApplicatorRestrictions(String label) {
this.label = label;
}
}

View File

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

View File

@ -0,0 +1,64 @@
/* ###
* 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.
//@formatter:off
private static final Map<String, String> REGISTER_NAME_MAP = Map.of(
//"rsp", "RSP",
"fbp", "RBP"
);
//@formatter:on
private Program program;
private Map<String, Register> pdbRegisterNameToRegisterMap;
public PdbRegisterNameToProgramRegisterMapper(Program program) {
this.program = program;
pdbRegisterNameToRegisterMap = new HashMap<>();
}
Register getRegister(String pdbRegisterName) {
Register register = pdbRegisterNameToRegisterMap.get(pdbRegisterName);
if (register != null) {
return register;
}
String registerName = REGISTER_NAME_MAP.get(pdbRegisterName);
if (registerName == null) {
registerName = pdbRegisterName;
}
register = program.getRegister(registerName);
pdbRegisterNameToRegisterMap.put(pdbRegisterName, register);
if (register == null) {
Msg.info(this, "Program register not found for " + registerName);
}
return register;
}
}

View File

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

View File

@ -0,0 +1,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 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 MsSymbolApplier {
private PeCoffGroupMsSymbol symbol;
/**
* Constructor
* @param applicator the {@link PdbApplicator} for which we are working.
* @param iter the Iterator containing the symbol sequence being processed
*/
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
void apply() throws PdbException, CancelledException {
applicator.addMemoryGroupRefinement(symbol);
Address symbolAddress = applicator.getAddress(symbol);
if (applicator.isInvalidAddress(symbolAddress, symbol.getName())) {
return;
}
applicator.createSymbol(symbolAddress, symbol.getName(), false);
}
@Override
void applyTo(MsSymbolApplier applyToApplier) {
// Do nothing
}
}

View File

@ -0,0 +1,63 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.pdb.pdbapplicator;
import 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 MsSymbolApplier {
private PeCoffSectionMsSymbol symbol;
/**
* Constructor
* @param applicator the {@link PdbApplicator} for which we are working.
* @param iter the Iterator containing the symbol sequence being processed
*/
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
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
void applyTo(MsSymbolApplier applyToApplier) {
// Do nothing
}
}

View File

@ -0,0 +1,109 @@
/* ###
* 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 MsTypeApplier {
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, AbstractPointerMsType msType)
throws IllegalArgumentException {
super(applicator, msType);
}
boolean isFunctionPointer() {
return isFunctionPointer;
}
@Override
BigInteger getSize() {
return ((AbstractPointerMsType) msType).getSize();
}
@Override
void apply() throws PdbException, CancelledException {
if (msType instanceof DummyMsType) {
dataType = new PointerDataType(applicator.getDataTypeManager());
}
else {
dataType = applyAbstractPointerMsType((AbstractPointerMsType) msType);
}
}
@Override
void resolve() {
// Do not resolve pointer types... will be resolved naturally, as needed
}
MsTypeApplier getUnmodifiedUnderlyingTypeApplier() {
MsTypeApplier 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) {
MsTypeApplier underlyingApplier =
applicator.getTypeApplier(type.getUnderlyingRecordNumber());
if (underlyingApplier instanceof ProcedureTypeApplier) {
isFunctionPointer = true;
}
//DataType underlyingType = underlyingApplier.getCycleBreakType(); // out 20191211
DataType underlyingType = underlyingApplier.getCycleBreakType();
if (underlyingType == null) {
// TODO: we have seen underlyingTypeApplier is for NoTypeApplier for VtShapeMsType
// Figure it out, and perhaps create an applier that creates a structure or something?
return new VoidDataType(applicator.getDataTypeManager());
// throw new PdbException("Underlying type missing for pointer.");
}
int size = getSizeInt();
if (size == applicator.getDataOrganization().getPointerSize()) {
size = -1; // Use default
}
Pointer pointer =
new PointerDataType(underlyingType, size, applicator.getDataTypeManager());
return pointer;
}
}

View File

@ -0,0 +1,81 @@
/* ###
* 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.AbstractProcedureMsType;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.CallingConvention;
import ghidra.program.model.data.DataType;
import ghidra.util.exception.CancelledException;
/**
* Applier for {@link AbstractProcedureMsType} types.
*/
public class ProcedureTypeApplier extends AbstractFunctionTypeApplier {
/**
* 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, AbstractProcedureMsType msType)
throws IllegalArgumentException {
super(applicator, msType);
}
@Override
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
void apply() throws PdbException, CancelledException {
applyFunction(getCallingConvention(), hasThisPointer());
// AbstractProcedureMsType procType = (AbstractProcedureMsType) msType;
// applyFunction(procType.getCallingConvention(), false, procType.getReturnTypeIndex(),
// procType.getArgListTypeIndex());
// DataType definition = applyFunction(procType.getCallingConvention(), false,
// procType.getReturnTypeIndex(), procType.getArgListTypeIndex());
// ghDataTypeDB = applicator.resolve(definition);
}
}

View File

@ -0,0 +1,85 @@
/* ###
* 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.AbstractPublicMsSymbol;
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 AbstractPublicMsSymbol} symbols.
*/
public class PublicSymbolApplier extends MsSymbolApplier {
private AbstractPublicMsSymbol symbol;
private Address symbolAddress = null;
private Address existingSymbolAddress = null;
/**
* Constructor
* @param applicator the {@link PdbApplicator} for which we are working.
* @param iter the Iterator containing the symbol sequence being processed
*/
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
void applyTo(MsSymbolApplier applyToApplier) {
// Do nothing.
}
@Override
void apply() throws CancelledException, PdbException {
symbolAddress = applicator.getAddress(symbol);
if (applicator.isInvalidAddress(symbolAddress, symbol.getName())) {
return;
}
existingSymbolAddress = applicator.witnessSymbolNameAtAddress(getName(), symbolAddress);
// TODO: Consider... could add restriction of not putting down symbol if it is mangled,
// as this would violate the uniqueness of the symbol... but we would also want to
// know that this situation was being presented.
if (!symbolAddress.equals(existingSymbolAddress)) {
// Note: there might be issues of thunk functions getting the same mangled name
// as thunked functions, which violates the thesis of their being unique.
// TODO: investigate this.
applicator.createSymbol(symbolAddress, symbol.getName(), true);
}
}
Address getAddress() {
return symbolAddress;
}
Address getAddressRemappedThroughPublicSymbol() {
return (existingSymbolAddress != null) ? existingSymbolAddress : symbolAddress;
}
String getName() {
return symbol.getName();
}
}

View File

@ -0,0 +1,76 @@
/* ###
* 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 MsSymbolApplier {
private AbstractReferenceMsSymbol symbol;
/**
* Constructor
* @param applicator the {@link PdbApplicator} for which we are working.
* @param iter the Iterator containing the symbol sequence being processed
*/
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
void applyTo(MsSymbolApplier applyToApplier) {
// Do nothing.
}
@Override
void apply() throws CancelledException, PdbException {
// Potential recursive call via applicator.procSym().
AbstractMsSymbolIterator refIter = getInitializedReferencedSymbolGroup().iterator();
applicator.procSym(refIter);
}
SymbolGroup getInitializedReferencedSymbolGroup() {
SymbolGroup refSymbolGroup = getReferencedSymbolGroup();
AbstractMsSymbolIterator refIter = refSymbolGroup.iterator();
refIter.initGetByOffset(getOffsetInReferencedSymbolGroup());
return refSymbolGroup;
}
SymbolGroup getReferencedSymbolGroup() {
int refModuleNumber = symbol.getModuleIndex();
return applicator.getSymbolGroupForModule(refModuleNumber);
}
long getOffsetInReferencedSymbolGroup() {
// Adjusting offset to the offset we use for parsing the complete record.
return symbol.getOffsetActualSymbolInDollarDollarSymbols() - 4;
}
}

View File

@ -0,0 +1,157 @@
/* ###
* 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.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.exception.*;
/**
* Applier for {@link AbstractRegisterRelativeAddressMsSymbol} symbols.
*/
public class RegisterRelativeSymbolApplier extends MsSymbolApplier {
private AbstractRegisterRelativeAddressMsSymbol symbol;
/**
* Constructor
* @param applicator the {@link PdbApplicator} for which we are working.
* @param iter the Iterator containing the symbol sequence being processed
*/
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
void apply() throws PdbException, CancelledException {
pdbLogAndInfoMessage(this,
"Cannot apply " + this.getClass().getSimpleName() + " directly to program");
}
@Override
void applyTo(MsSymbolApplier 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);
MsTypeApplier 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 (InvalidInputException | DuplicateNameException e) {
applicator.appendLogMsg("Unable to create stack variable " + symbol.getName() +
" at offset " + offset + " in " + function.getName());
return false;
}
}
return true;
}
}

View File

@ -0,0 +1,145 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.pdb.pdbapplicator;
import java.util.ArrayList;
import java.util.List;
import 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 MsSymbolApplier {
private SeparatedCodeFromCompilerSupportMsSymbol symbol;
private String craftedName;
private Address specifiedAddress;
private BlockCommentsManager comments;
private int symbolBlockNestingLevel;
private Address currentBlockAddress;
private List<MsSymbolApplier> allAppliers = new ArrayList<>();
private static AbstractMsSymbolIterator validateSymbol(AbstractMsSymbolIterator iter) {
if (!(iter.peek() instanceof SeparatedCodeFromCompilerSupportMsSymbol)) {
throw new IllegalArgumentException("Not a SeparatedCodeFromCompilerSupportMsSymbol");
}
return iter;
}
/**
* Constructor
* @param applicator the {@link PdbApplicator} for which we are working.
* @param iter the Iterator containing the symbol sequence being processed
* @throws CancelledException upon user cancellation
*/
public SeparatedCodeSymbolApplier(PdbApplicator applicator, AbstractMsSymbolIterator iter)
throws CancelledException {
super(applicator, validateSymbol(iter));
symbol = (SeparatedCodeFromCompilerSupportMsSymbol) iter.next();
specifiedAddress = applicator.getAddress(symbol);
// Make up name. TODO: decide if need better anonymous name
craftedName = String.format("CompilerSeparatedCode%s", specifiedAddress);
symbolBlockNestingLevel = 0;
comments = new BlockCommentsManager();
currentBlockAddress = null;
manageBlockNesting(this);
while (notDone()) {
applicator.checkCanceled();
MsSymbolApplier applier = applicator.getSymbolApplier(iter);
if (!(applier instanceof EndSymbolApplier)) {
Msg.info(this, "Unexpected applier in " + getClass().getSimpleName() + ": " +
applier.getClass().getSimpleName());
}
allAppliers.add(applier);
applier.manageBlockNesting(this);
}
}
@Override
void manageBlockNesting(MsSymbolApplier applierParam) {
beginBlock(specifiedAddress);
}
@Override
void applyTo(MsSymbolApplier applyToApplier) {
// Do nothing.
}
@Override
void apply() throws PdbException, CancelledException {
if (applicator.isInvalidAddress(currentBlockAddress, craftedName)) {
return;
}
// DO NOTHING FOR NOW. TODO: should we have a configuration option?
// Note: these comments can be noise in the decompiler an code browser
setComments(false);
}
private void setComments(boolean enabled) {
if (enabled) {
String existingComment = applicator.getProgram().getListing().getComment(
CodeUnit.PRE_COMMENT, specifiedAddress);
String p = "*************************************************************\n";
String newComment =
String.format(p + "* Separated code (from the compiler): %s - %s *\n" + p,
specifiedAddress.toString(),
specifiedAddress.add(symbol.getBlockLength() - 1).toString());
String comment =
(existingComment == null) ? newComment : existingComment + "\n" + newComment;
SetCommentCmd.createComment(applicator.getProgram(), specifiedAddress, comment,
CodeUnit.PRE_COMMENT);
}
}
private boolean notDone() {
return (symbolBlockNestingLevel > 0) && iter.hasNext();
}
int endBlock() {
if (--symbolBlockNestingLevel < 0) {
applicator.appendLogMsg("Block Nesting went negative at " + specifiedAddress);
}
return symbolBlockNestingLevel;
}
private int beginBlock(Address startAddress) {
currentBlockAddress = startAddress;
++symbolBlockNestingLevel;
return symbolBlockNestingLevel;
}
}

View File

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

View File

@ -0,0 +1,241 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.pdb.pdbapplicator;
import java.io.IOException;
import java.io.Writer;
import java.util.*;
import ghidra.app.util.bin.format.pdb2.pdbreader.symbol.AbstractMsSymbol;
/**
* This class represents a particular group of Symbols that came from the same PDB stream. This
* wraps the internal structure and offers mechanisms for accessing records. It does not map
* directly to an MSFT structure.
*/
public class SymbolGroup {
private Map<Long, AbstractMsSymbol> symbolsByOffset;
private int moduleNumber;
private List<Long> offsets;
private Map<Long, Integer> indexByOffset;
/**
* Constructor. The starting offset is set to zero.
* @param symbolsByOffset the Map used to initialize the constructor.
* @param moduleNumber The Module number corresponding to the initializing Map
* (0 for public/global Map).
*/
public SymbolGroup(Map<Long, AbstractMsSymbol> symbolsByOffset, int moduleNumber) {
this(symbolsByOffset, moduleNumber, 0);
}
/**
* Constructor.
* @param symbolsByOffset the Map used to initialize the constructor.
* @param moduleNumber The Module number corresponding to the initializing Map
* (0 for public/global Map).
* @param offset the offset location to start.
*/
public SymbolGroup(Map<Long, AbstractMsSymbol> symbolsByOffset, int moduleNumber, long offset) {
this.symbolsByOffset = symbolsByOffset;
this.moduleNumber = moduleNumber;
initOffsets();
}
/**
* Returns the list of symbols. These may not be in the order that they were seen.
* @return the list of symbols.
*/
List<AbstractMsSymbol> getSymbols() {
return new ArrayList<>(symbolsByOffset.values());
}
/**
* Returns the module number.
* @return the module number.
*/
int getModuleNumber() {
return moduleNumber;
}
/**
* Returns the number of symbols.
* @return the number of symbols.
*/
int size() {
return symbolsByOffset.size();
}
/**
* Returns the list of symbol offsets in the order they were seen.
* @return the list of symbol offsets.
*/
List<Long> getOrderedOffsets() {
return new ArrayList<>(symbolsByOffset.keySet());
}
/**
* Returns the set of symbol offsets.
* @return the set of symbol offsets.
*/
Set<Long> getOffsets() {
return symbolsByOffset.keySet();
}
/**
* Returns the list of symbols in the order they were seen.
* @return the list of symbols.
*/
List<AbstractMsSymbol> getOrderedSymbols() {
List<AbstractMsSymbol> symbols = new ArrayList<>();
for (long offset : offsets) {
symbols.add(symbolsByOffset.get(offset));
}
return symbols;
}
/**
* Initialized the offsets list used for requesting the symbols in sequence.
*/
private void initOffsets() {
offsets = new ArrayList<>();
indexByOffset = new HashMap<>();
int index = 0;
for (Map.Entry<Long, AbstractMsSymbol> entry : symbolsByOffset.entrySet()) {
offsets.add(index, entry.getKey());
indexByOffset.put(entry.getKey(), index++);
}
}
/**
* Debug method for dumping the symbol group
* @param writer {@link Writer} to which to dump the information.
* @throws IOException Upon IOException writing to the {@link Writer}.
*/
protected void dump(Writer writer) throws IOException {
writer.write("SymbolGroup-------------------------------------------------");
for (Map.Entry<Long, AbstractMsSymbol> entry : symbolsByOffset.entrySet()) {
StringBuilder builder = new StringBuilder();
builder.append("\n------------------------------------------------------------\n");
builder.append(String.format("Offset: 0X%08X\n", entry.getKey()));
builder.append(entry.getValue());
writer.write(builder.toString());
}
writer.write("\nEnd SymbolGroup---------------------------------------------\n");
}
AbstractMsSymbolIterator iterator() {
return new AbstractMsSymbolIterator();
}
//==============================================================================================
/**
* Iterator for {@link SymbolGroup} that iterates through {@link AbstractMsSymbol
* AbstractMsSymbols}
*/
class AbstractMsSymbolIterator implements Iterator<AbstractMsSymbol> {
private int currentIndex;
private long currentOffset;
public AbstractMsSymbolIterator() {
currentIndex = 0;
currentOffset = 0L;
}
@Override
public boolean hasNext() {
if (currentIndex == offsets.size()) {
return false;
}
return true;
}
/**
* Peeks at and returns the next symbol without incrementing to the next. If none are
* left, then throws NoSuchElementException and reinitializes the state for a new
* iteration.
* @see #initGet()
* @return the next symbol
* @throws NoSuchElementException if there are no more elements
*/
public AbstractMsSymbol peek() throws NoSuchElementException {
if (currentIndex == offsets.size()) {
throw new NoSuchElementException("none left");
}
long temporaryOffset = offsets.get(currentIndex);
AbstractMsSymbol symbol = symbolsByOffset.get(temporaryOffset);
if (symbol == null) {
throw new NoSuchElementException("No symbol");
}
return symbol;
}
@Override
public AbstractMsSymbol next() {
if (currentIndex == offsets.size()) {
throw new NoSuchElementException("none left");
}
currentOffset = offsets.get(currentIndex++);
return symbolsByOffset.get(currentOffset);
}
/**
* Returns the next symbol. If none are left, then throws NoSuchElementException and
* reinitializes the state for a new iteration.
* @see #initGet()
* @return the next symbol
* @throws NoSuchElementException if there are no more elements
*/
long getCurrentOffset() {
return currentOffset;
}
/**
* Initialized the mechanism for requesting the symbols in sequence.
* @see #hasNext()
*/
void initGet() {
currentIndex = 0;
}
/**
* Initialized the mechanism for requesting the symbols in sequence.
* @param offset the offset to which to initialize the mechanism.
* @see #hasNext()
*/
void initGetByOffset(long offset) {
int index = indexByOffset.get(offset);
if (index < 0) {
index = 0;
}
currentIndex = index;
currentOffset = offset;
}
// TODO: might not need this
/**
* Returns the module number.
* @return the module number.
*/
int getModuleNumber() {
return moduleNumber;
}
}
}

View File

@ -0,0 +1,109 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.pdb.pdbapplicator;
import ghidra.app.cmd.disassemble.DisassembleCommand;
import ghidra.app.cmd.function.CreateFunctionCmd;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.symbol.AbstractMsSymbol;
import ghidra.app.util.bin.format.pdb2.pdbreader.symbol.TrampolineMsSymbol;
import ghidra.app.util.pdb.pdbapplicator.SymbolGroup.AbstractMsSymbolIterator;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSet;
import ghidra.program.model.listing.Function;
import ghidra.util.exception.AssertException;
import ghidra.util.exception.CancelledException;
/**
* Applier for {@link TrampolineMsSymbol} symbols.
*/
public class TrampolineSymbolApplier extends MsSymbolApplier {
private TrampolineMsSymbol symbol;
/**
* Constructor
* @param applicator the {@link PdbApplicator} for which we are working.
* @param iter the Iterator containing the symbol sequence being processed
*/
public TrampolineSymbolApplier(PdbApplicator applicator, AbstractMsSymbolIterator iter) {
super(applicator, iter);
AbstractMsSymbol abstractSymbol = iter.next();
if (!(abstractSymbol instanceof TrampolineMsSymbol)) {
throw new AssertException(
"Invalid symbol type: " + abstractSymbol.getClass().getSimpleName());
}
symbol = (TrampolineMsSymbol) abstractSymbol;
}
@Override
void applyTo(MsSymbolApplier applyToApplier) {
// Do nothing.
}
@Override
void apply() throws CancelledException, PdbException {
// We know the size of this trampoline, so use it to restrict the disassembly.
Address symbolAddress = applicator.getAddress(symbol);
Address targetAddress =
applicator.getAddress(symbol.getSegmentTarget(), symbol.getOffsetTarget());
Function target = null;
Function thunk = null;
if (!applicator.isInvalidAddress(targetAddress, "thunk target")) {
target = createNewFunction(targetAddress, 1);
}
if (!applicator.isInvalidAddress(symbolAddress, "thunk symbol")) {
thunk = createNewFunction(symbolAddress, symbol.getSizeOfThunk());
}
if (target != null && thunk != null) {
thunk.setThunkedFunction(target);
}
// int thunkModule = findModuleNumberBySectionOffsetContribution(symbol.getSectionThunk(),
// symbol.getOffsetThunk());
// int targetModule = findModuleNumberBySectionOffsetContribution(symbol.getSectionTarget(),
// symbol.getOffsetTarget());
}
// TODO? If we wanted to be able to apply this symbol to a different address, we should
// review code in FunctionSymbolApplier. Note, however, that there are two addresses
// that need to be dealt with here, and each could have a different address with a different
// delta from the specified address.
private Function createNewFunction(Address startAddress, long size) {
AddressSet addressSet = new AddressSet(startAddress, startAddress.add(size));
if (applicator.getProgram().getListing().getInstructionAt(startAddress) == null) {
DisassembleCommand cmd = new DisassembleCommand(addressSet, null, true); // TODO: false?
cmd.applyTo(applicator.getProgram(), applicator.getCancelOnlyWrappingMonitor());
}
// Only create function if it does not already exist.
Function function = applicator.getProgram().getListing().getFunctionAt(startAddress);
if (function != null) {
return function;
}
CreateFunctionCmd funCmd = new CreateFunctionCmd(startAddress);
if (!funCmd.applyTo(applicator.getProgram(), applicator.getCancelOnlyWrappingMonitor())) {
applicator.appendLogMsg("Failed to apply function at address " +
startAddress.toString() + "; attempting to use possible existing function");
}
return funCmd.getFunction();
}
}

View File

@ -0,0 +1,528 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.pdb.pdbapplicator;
import java.util.HashMap;
import java.util.Map;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.RecordNumber;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.*;
/**
* Pseudo-factory for creating the {@link MsTypeApplier} for the {@link AbstractMsType}
* indicated by the AbstractMsType and also its {@link RecordNumber}. This class also performs
* caching of the applier by its RecordNumber.
*/
public class TypeApplierFactory {
private PdbApplicator applicator;
private Map<RecordNumber, MsTypeApplier> appliersByRecordNumber;
TypeApplierFactory(PdbApplicator applicator) {
this.applicator = applicator;
appliersByRecordNumber = new HashMap<>();
}
//==============================================================================================
MsTypeApplier getApplierSpec(RecordNumber recordNumber, Class<? extends MsTypeApplier> expected)
throws PdbException {
MsTypeApplier applier = getTypeApplier(recordNumber);
if (!expected.isInstance(applier)) {
throw new PdbException(applier.getClass().getSimpleName() + " seen where " +
expected.getSimpleName() + " expected for record number " + recordNumber);
}
return applier;
}
MsTypeApplier getApplierOrNoTypeSpec(RecordNumber recordNumber,
Class<? extends MsTypeApplier> expected) throws PdbException {
MsTypeApplier applier = getTypeApplier(recordNumber);
if (!expected.isInstance(applier)) {
if (!(applier instanceof PrimitiveTypeApplier &&
((PrimitiveTypeApplier) applier).isNoType())) {
throw new PdbException(applier.getClass().getSimpleName() + " seen where " +
expected.getSimpleName() + " expected for record number " + recordNumber);
}
}
return applier;
}
MsTypeApplier getTypeApplier(RecordNumber recordNumber) {
MsTypeApplier applier = appliersByRecordNumber.get(recordNumber);
if (applier == null) {
applier = getTypeApplier(applicator.getPdb().getTypeRecord(recordNumber));
appliersByRecordNumber.put(recordNumber, applier);
}
return applier;
}
//==============================================================================================
MsTypeApplier getTypeApplier(AbstractMsType type) {
if (type == null) {
applicator.appendLogMsg("PDB Warning: No AbstractMsType for getTypeApplier");
return null;
}
MsTypeApplier applier = null;
try {
switch (type.getPdbId()) {
case -1: // must be careful, as we chose the -1 for PrimitiveMsType
applier = new PrimitiveTypeApplier(applicator, (PrimitiveMsType) type);
break;
// 0x0000 block
case Modifier16MsType.PDB_ID:
applier = new ModifierTypeApplier(applicator, (Modifier16MsType) type);
break;
case Pointer16MsType.PDB_ID:
applier = new PointerTypeApplier(applicator, (Pointer16MsType) type);
break;
case Array16MsType.PDB_ID:
applier = new ArrayTypeApplier(applicator, (Array16MsType) type);
break;
case Class16MsType.PDB_ID:
applier = new CompositeTypeApplier(applicator, (Class16MsType) type);
break;
case Structure16MsType.PDB_ID:
applier = new CompositeTypeApplier(applicator, (Structure16MsType) type);
break;
case Union16MsType.PDB_ID:
applier = new CompositeTypeApplier(applicator, (Union16MsType) type);
break;
case Enum16MsType.PDB_ID:
applier = new EnumTypeApplier(applicator, (Enum16MsType) type);
break;
case Procedure16MsType.PDB_ID:
applier = new ProcedureTypeApplier(applicator, (Procedure16MsType) type);
break;
case MemberFunction16MsType.PDB_ID:
applier =
new MemberFunctionTypeApplier(applicator, (MemberFunction16MsType) type);
break;
case VtShapeMsType.PDB_ID:
applier = new VtShapeTypeApplier(applicator, (VtShapeMsType) type);
break;
// case Cobol016MsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case Cobol1MsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case BasicArray16MsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case LabelMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case NullMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case NotTranMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case DimensionedArray16MsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case VirtualFunctionTablePath16MsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case PrecompiledType16MsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case EndPrecompiledTypeMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case OemDefinableString16MsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case TypeServerStMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// 0x0200 block
// case Skip16MsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
case ArgumentsList16MsType.PDB_ID:
applier =
new ArgumentsListTypeApplier(applicator, (ArgumentsList16MsType) type);
break;
// case DefaultArguments16MsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case ListMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
case FieldList16MsType.PDB_ID:
applier = new FieldListTypeApplier(applicator, type);
break;
// case DerivedClassList16MsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
case Bitfield16MsType.PDB_ID:
applier = new BitfieldTypeApplier(applicator, (Bitfield16MsType) type);
break;
// case MethodList16MsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case DimensionedArrayConstBoundsUpper16MsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case DimensionedArrayConstBoundsLowerUpper16MsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case DimensionedArrayVarBoundsUpper16MsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case DimensionedArrayVarBoundsLowerUpper16MsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case ReferencedSymbolMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// 0x400 block
case BaseClass16MsType.PDB_ID:
applier = new BaseClassTypeApplier(applicator, type);
break;
case VirtualBaseClass16MsType.PDB_ID:
applier = new BaseClassTypeApplier(applicator, type);
break;
case IndirectVirtualBaseClass16MsType.PDB_ID:
applier = new BaseClassTypeApplier(applicator, type);
break;
case EnumerateStMsType.PDB_ID:
applier = new EnumerateTypeApplier(applicator, (EnumerateStMsType) type);
break;
// case FriendFunction16MsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case Index16MsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
case Member16MsType.PDB_ID:
applier = new MemberTypeApplier(applicator, (Member16MsType) type);
break;
// case StaticMember16MsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
case OverloadedMethod16MsType.PDB_ID:
// See note in "default" case regarding NoTypeApplier
applier = new NoTypeApplier(applicator, type);
break;
case NestedType16MsType.PDB_ID:
applier = new NestedTypeApplier(applicator, type);
break;
case VirtualFunctionTablePointer16MsType.PDB_ID:
applier = new VirtualFunctionTablePointerTypeApplier(applicator, type);
break;
// case FriendClass16MsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
case OneMethod16MsType.PDB_ID:
// See note in "default" case regarding NoTypeApplier
applier = new NoTypeApplier(applicator, type);
break;
case VirtualFunctionTablePointerWithOffset16MsType.PDB_ID:
applier = new VirtualFunctionTablePointerTypeApplier(applicator, type);
break;
// 0x1000 block
case ModifierMsType.PDB_ID:
applier = new ModifierTypeApplier(applicator, (ModifierMsType) type);
break;
case PointerMsType.PDB_ID:
applier = new PointerTypeApplier(applicator, (PointerMsType) type);
break;
case ArrayStMsType.PDB_ID:
applier = new ArrayTypeApplier(applicator, (ArrayStMsType) type);
break;
case ClassStMsType.PDB_ID:
applier = new CompositeTypeApplier(applicator, (ClassStMsType) type);
break;
case StructureStMsType.PDB_ID:
applier = new CompositeTypeApplier(applicator, (StructureStMsType) type);
break;
case UnionStMsType.PDB_ID:
applier = new CompositeTypeApplier(applicator, (UnionStMsType) type);
break;
case EnumStMsType.PDB_ID:
applier = new EnumTypeApplier(applicator, (EnumStMsType) type);
break;
case ProcedureMsType.PDB_ID:
applier = new ProcedureTypeApplier(applicator, (ProcedureMsType) type);
break;
case MemberFunctionMsType.PDB_ID:
applier =
new MemberFunctionTypeApplier(applicator, (MemberFunctionMsType) type);
break;
// case Cobol0MsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case BasicArrayMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case DimensionedArrayStMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case VirtualFunctionTablePathMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case PrecompiledTypeStMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case OemDefinableStringMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case AliasStMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case OemDefinableString2MsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
//
// 0x1200 block
// case SkipMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
case ArgumentsListMsType.PDB_ID:
applier = new ArgumentsListTypeApplier(applicator, (ArgumentsListMsType) type);
break;
// case DefaultArgumentsStMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
case FieldListMsType.PDB_ID:
applier = new FieldListTypeApplier(applicator, type);
break;
// case DerivedClassListMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
case BitfieldMsType.PDB_ID:
applier = new BitfieldTypeApplier(applicator, (BitfieldMsType) type);
break;
// case MethodListMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case DimensionedArrayConstBoundsUpperMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case DimensionedArrayConstBoundsLowerUpperMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case DimensionedArrayVarBoundsUpperMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case DimensionedArrayVarBoundsLowerUpperMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// 0x1400 block
case BaseClassMsType.PDB_ID:
applier = new BaseClassTypeApplier(applicator, type);
break;
case VirtualBaseClassMsType.PDB_ID:
applier = new BaseClassTypeApplier(applicator, type);
break;
case IndirectVirtualBaseClassMsType.PDB_ID:
applier = new BaseClassTypeApplier(applicator, type);
break;
// case FriendFunctionStMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case IndexMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
case MemberStMsType.PDB_ID:
applier = new MemberTypeApplier(applicator, (MemberStMsType) type);
break;
// case StaticMemberStMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
case OverloadedMethodStMsType.PDB_ID:
// See note in "default" case regarding NoTypeApplier
applier = new NoTypeApplier(applicator, type);
break;
case NestedTypeStMsType.PDB_ID:
applier = new NestedTypeApplier(applicator, type);
break;
case VirtualFunctionTablePointerMsType.PDB_ID:
applier = new VirtualFunctionTablePointerTypeApplier(applicator, type);
break;
// case FriendClassMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
case OneMethodStMsType.PDB_ID:
// See note in "default" case regarding NoTypeApplier
applier = new NoTypeApplier(applicator, type);
break;
case VirtualFunctionTablePointerWithOffsetMsType.PDB_ID:
applier = new VirtualFunctionTablePointerTypeApplier(applicator, type);
break;
case NestedTypeExtStMsType.PDB_ID:
applier = new NestedTypeApplier(applicator, type);
break;
// case MemberModifyStMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case ManagedStMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// 0x1500 block
// case TypeServerMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
case EnumerateMsType.PDB_ID:
applier = new EnumerateTypeApplier(applicator, (EnumerateMsType) type);
break;
case ArrayMsType.PDB_ID:
applier = new ArrayTypeApplier(applicator, (ArrayMsType) type);
break;
case ClassMsType.PDB_ID:
applier = new CompositeTypeApplier(applicator, (ClassMsType) type);
break;
case StructureMsType.PDB_ID:
applier = new CompositeTypeApplier(applicator, (StructureMsType) type);
break;
case UnionMsType.PDB_ID:
applier = new CompositeTypeApplier(applicator, (UnionMsType) type);
break;
case EnumMsType.PDB_ID:
applier = new EnumTypeApplier(applicator, (EnumMsType) type);
break;
// case DimensionedArrayMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case PrecompiledTypeMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case AliasMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case DefaultArgumentsMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case FriendFunctionMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
case MemberMsType.PDB_ID:
applier = new MemberTypeApplier(applicator, (MemberMsType) type);
break;
// case StaticMemberMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
case OverloadedMethodMsType.PDB_ID:
// See note in "default" case regarding NoTypeApplier
applier = new NoTypeApplier(applicator, type);
break;
case NestedTypeMsType.PDB_ID:
applier = new NestedTypeApplier(applicator, type);
break;
case OneMethodMsType.PDB_ID:
// See note in "default" case regarding NoTypeApplier
applier = new NoTypeApplier(applicator, type);
break;
case NestedTypeExtMsType.PDB_ID:
applier = new NestedTypeApplier(applicator, type);
break;
// case MemberModifyMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case ManagedMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case TypeServer2MsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case StridedArrayMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case HighLevelShaderLanguageMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case ModifierExMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
case InterfaceMsType.PDB_ID:
applier = new CompositeTypeApplier(applicator, (InterfaceMsType) type);
break;
// case BaseInterfaceMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case VectorMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case MatrixMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case VirtualFunctionTableMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// 0x1600 block
// case FunctionIdMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case MemberFunctionIdMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case BuildInfoMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case SubstringListMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
// case StringIdMsType.PDB_ID:
// // Not evaluated/implemented yet.
// break;
case UserDefinedTypeSourceAndLineMsType.PDB_ID:
applier = new UdtSourceLineTypeApplier(applicator, type);
break;
case UserDefinedTypeModuleSourceAndLineMsType.PDB_ID:
applier = new UdtSourceLineTypeApplier(applicator, type);
break;
// If all of the above are enabled, this should never happen (unless we missed
// something or MSFT has added new in a version we do not handle.
default:
applier = new NoTypeApplier(applicator, type);
// Only adding to this cannotApplyTypes list here, and not in other
// places (above) where we might currently be using a NoTypeApplier.
// Using a NoTypeApplier in other places (above) might just be a placeholder
// until we craft the specific ways in which we would like to "apply" the
// data type information.
applicator.getPdbApplicatorMetrics().witnessCannotApplyDataType(type);
break;
}
}
catch (IllegalArgumentException e) {
try {
applier = new NoTypeApplier(applicator, type);
}
catch (IllegalArgumentException e2) {
// We did a null check above on type, so this state should not happen.
}
RecordNumber recNum = type.getRecordNumber();
String msg = (recNum == null) ? "record" : recNum.toString();
String message = "GhidraException on " + msg + " with PdbId " + type.getPdbId() + ": " +
e.getMessage();
applicator.appendLogMsg(message);
applicator.pdbLogAndInfoMessage(this, message);
}
return applier;
}
}

View File

@ -0,0 +1,130 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.pdb.pdbapplicator;
import ghidra.app.util.SymbolPath;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.RecordNumber;
import ghidra.app.util.bin.format.pdb2.pdbreader.symbol.AbstractMsSymbol;
import ghidra.app.util.bin.format.pdb2.pdbreader.symbol.AbstractUserDefinedTypeMsSymbol;
import ghidra.app.util.pdb.pdbapplicator.SymbolGroup.AbstractMsSymbolIterator;
import ghidra.program.model.data.*;
import ghidra.util.exception.AssertException;
import ghidra.util.exception.CancelledException;
/**
* Applier for {@link AbstractUserDefinedTypeMsSymbol} symbols.
*/
public class TypedefSymbolApplier extends MsSymbolApplier {
private DataType resolvedDataType = null;
private AbstractUserDefinedTypeMsSymbol udtSymbol;
/**
* Constructor
* @param applicator the {@link PdbApplicator} for which we are working.
* @param iter the Iterator containing the symbol sequence being processed
*/
public TypedefSymbolApplier(PdbApplicator applicator, AbstractMsSymbolIterator iter) {
super(applicator, iter);
AbstractMsSymbol abstractSymbol = iter.next();
if (!(abstractSymbol instanceof AbstractUserDefinedTypeMsSymbol)) {
throw new AssertException(
"Invalid symbol type: " + abstractSymbol.getClass().getSimpleName());
}
udtSymbol = (AbstractUserDefinedTypeMsSymbol) abstractSymbol;
}
@Override
void applyTo(MsSymbolApplier applyToApplier) {
// Do nothing
}
@Override
void apply() throws PdbException, CancelledException {
resolvedDataType = applyUserDefinedTypeMsSymbol(udtSymbol);
}
/**
* Returns the name.
* @return Name.
*/
String getName() {
return udtSymbol.getName();
}
/**
* Returns the type record number.
* @return Type record number.
*/
RecordNumber getTypeRecordNumber() {
return udtSymbol.getTypeRecordNumber();
}
DataType getResolvedDataType() throws PdbException {
if (resolvedDataType == null) {
throw new PdbException("Data type not resolved");
}
return resolvedDataType;
}
// Typedefs
private DataType applyUserDefinedTypeMsSymbol(AbstractUserDefinedTypeMsSymbol symbol) {
String name = symbol.getName();
MsTypeApplier applier = applicator.getTypeApplier(symbol.getTypeRecordNumber());
// TODO:... NOT SURE IF WILL ALWAYS BE A DATATYPE OR WILL BE A VARIABLE OR ????
if (applier == null) {
return null;
}
DataType dataType = applier.getDataType();
if (dataType == null) {
return null;
}
// This code (for Composites and Enums) circumvents a collision on the names with the
// compromise that we do not store the TypeDefDataType into the DataTypeManager.
// Another issue is that we likely already have the DataType in the DataTypeManager,
// but the TypeDefDataType also wants to create it... we would need a mechanism to
// create a TypeDefDataType which uses an existing underlying DataType.
// Note, too, that we do not compare name with dataType.getName() as the latter does not
// contain namespace information.
if (applier instanceof CompositeTypeApplier) {
CompositeTypeApplier compositeApplier = (CompositeTypeApplier) applier;
String compositeName = compositeApplier.getName();
if (name.equals(compositeName)) {
return dataType;
}
}
else if (applier instanceof EnumTypeApplier) {
EnumTypeApplier enumApplier = (EnumTypeApplier) applier;
String enumName = enumApplier.getMsType().getName();
if (name.equals(enumName)) {
return dataType;
}
}
SymbolPath symbolPath = new SymbolPath(name);
CategoryPath categoryPath =
applicator.getTypedefsCategory(iter.getModuleNumber(), symbolPath);
DataType typedef = new TypedefDataType(categoryPath.getParent(), categoryPath.getName(),
dataType, applicator.getDataTypeManager());
return applicator.resolve(typedef);
}
}

View File

@ -0,0 +1,112 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.pdb.pdbapplicator;
import java.math.BigInteger;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.RecordNumber;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.*;
import ghidra.util.exception.CancelledException;
/**
* Applier for {@link AbstractBaseClassMsType}, {@link AbstractVirtualBaseClassMsType}, and
* {@link AbstractIndirectVirtualBaseClassMsType} types.
*/
public class UdtSourceLineTypeApplier extends MsTypeApplier {
/**
* Constructor for base class applier.
* @param applicator {@link PdbApplicator} for which this class is working.
* @param msType {@link AbstractBaseClassMsType}, {@link AbstractVirtualBaseClassMsType}, or
* {@link AbstractIndirectVirtualBaseClassMsType} to processes.
* @throws IllegalArgumentException Upon invalid arguments.
*/
public UdtSourceLineTypeApplier(PdbApplicator applicator, AbstractMsType msType)
throws IllegalArgumentException {
super(applicator, validateType(msType));
}
// The MsTypes for which we are working do not have a size in and of themselves, but the
// classes/structures to which they refer have a size, even if zero.
// For here, we are only reporting what "we" have, not what the underlying sizes are.
// ...and a value of zero is our "don't know" and "not represented" value.
@Override
BigInteger getSize() {
return BigInteger.ZERO;
}
/**
* Returns the offset of the Base Class within the inheriting class.
* @return the offset.
*/
int getLineNumber() {
if (msType instanceof UserDefinedTypeSourceAndLineMsType) {
return ((UserDefinedTypeSourceAndLineMsType) msType).getLineNumber();
}
return ((UserDefinedTypeModuleSourceAndLineMsType) msType).getLineNumber();
}
/**
* Returns the source file name.
* @return the source file name. null if problem recovering name.
*/
String getSourceFileName() {
if (msType instanceof UserDefinedTypeSourceAndLineMsType) {
return ((UserDefinedTypeSourceAndLineMsType) msType).getSourceFileName();
}
return ((UserDefinedTypeModuleSourceAndLineMsType) msType).getSourceFileName();
}
/**
* Returns the record number of the UDT.
* @return the record number of the UDT.
*/
RecordNumber getUdtRecordNumber() {
if (msType instanceof UserDefinedTypeSourceAndLineMsType) {
return ((UserDefinedTypeSourceAndLineMsType) msType).getUdtRecordNumber();
}
return ((UserDefinedTypeModuleSourceAndLineMsType) msType).getUdtRecordNumber();
}
@Override
void apply() throws PdbException, CancelledException {
String filename = getSourceFileName();
int lineNumber = getLineNumber();
RecordNumber udtRecordNumber = getUdtRecordNumber();
MsTypeApplier typeApplier = applicator.getTypeApplier(udtRecordNumber);
// do nothing at the moment.
applicator.putRecordNumberByFileName(udtRecordNumber, filename);
if (msType instanceof UserDefinedTypeModuleSourceAndLineMsType) {
int moduleNumber =
((UserDefinedTypeModuleSourceAndLineMsType) msType).getModuleNumber();
applicator.putRecordNumberByModuleNumber(udtRecordNumber, moduleNumber);
}
}
private static AbstractMsType validateType(AbstractMsType type)
throws IllegalArgumentException {
if (!(type instanceof UserDefinedTypeSourceAndLineMsType) &&
!(type instanceof UserDefinedTypeModuleSourceAndLineMsType)) {
throw new IllegalArgumentException(
"PDB Incorrectly applying " + type.getClass().getSimpleName() + " to " +
UdtSourceLineTypeApplier.class.getSimpleName());
}
return type;
}
}

View File

@ -0,0 +1,129 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.pdb.pdbapplicator;
import java.util.HashMap;
import java.util.Map;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbLog;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.*;
import ghidra.util.Msg;
/**
* Manages virtual base table lookup for PDB classes.
*/
public class VbtManager {
private DataTypeManager dtm;
private PointerDataType fallbackVbptr;
protected Map<Address, VirtualBaseTable> vbtByAddress;
/**
* Virtual Base Table Lookup Manager
* @param dtm TODO
*/
VbtManager(DataTypeManager dtm) {
this.dtm = dtm;
vbtByAddress = new HashMap<>();
fallbackVbptr = new PointerDataType(new IntegerDataType(dtm));
}
PointerDataType getFallbackVbptr() {
return fallbackVbptr;
}
/**
* Returns the offset from the virtual base table entry
* @param address Address of virtual base table
* @param ordinal index into table
* @return the offset
* @throws PdbException if no table exists for address or no entry exists for ordinal
*/
long getOffset(Address address, int ordinal) throws PdbException {
VirtualBaseTable table = vbtByAddress.get(address);
if (table == null) {
table = createVirtualBaseTable(address);
}
VirtualBaseTableEntry entry = table.getEntry(ordinal);
if (entry == null) {
throw new PdbException(
"Virtual Base Table Entry does not exist for ordinal: " + ordinal);
}
return entry.getOffset();
}
VirtualBaseTable createVirtualBaseTable(Address address) {
VirtualBaseTable vbt = vbtByAddress.get(address);
if (vbt != null) {
String message =
"PDB: warning virtual base table already exists for address: " + address;
PdbLog.message(message);
Msg.info(this, message);
}
else {
vbt = new VirtualBaseTable();
vbtByAddress.put(address, vbt);
}
return vbt;
}
static class VirtualBaseTableEntry {
long offset;
VirtualBaseTableEntry(long offset) {
this.offset = offset;
}
long getOffset() {
return offset;
}
}
static class VirtualBaseTable {
int maxSeen = -1;
Map<Integer, VirtualBaseTableEntry> entryByOrdinal = new HashMap<>();
/**
* Returns the entry from the table for the ordinal
* @param ordinal the ordinal into the table for the entry to retrieve
* @return the table entry
* @throws PdbException upon issue retrieving the entry
*/
VirtualBaseTableEntry getEntry(int ordinal) throws PdbException {
return entryByOrdinal.get(ordinal);
}
long getOffset(int ordinal) throws PdbException {
VirtualBaseTableEntry entry = getEntry(ordinal);
if (entry == null) {
throw new PdbException("No entry in Virtual Base Table for ordinal: " + ordinal);
}
return entry.getOffset();
}
void addEntry(int ordinal, VirtualBaseTableEntry entry) {
entryByOrdinal.put(ordinal, entry);
maxSeen = Integer.max(maxSeen, ordinal);
}
int getMaxOrdinal() {
return maxSeen;
}
}
}

View File

@ -0,0 +1,102 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.pdb.pdbapplicator;
import java.math.BigInteger;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.RecordNumber;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.*;
import ghidra.program.model.data.DataType;
import ghidra.util.exception.CancelledException;
/**
* Applier for {@link AbstractVirtualFunctionTablePointerMsType} and
* {@link AbstractVirtualFunctionTablePointerWithOffsetMsType} types.
*/
public class VirtualFunctionTablePointerTypeApplier extends MsTypeApplier {
/**
* Constructor for enum type applier, for transforming a enum into a
* Ghidra DataType.
* @param applicator {@link PdbApplicator} for which this class is working.
* @param msType {@link AbstractVirtualFunctionTablePointerMsType} or
* {@link AbstractVirtualFunctionTablePointerWithOffsetMsType} to process.
* @throws IllegalArgumentException Upon invalid arguments.
*/
public VirtualFunctionTablePointerTypeApplier(PdbApplicator applicator, AbstractMsType msType)
throws IllegalArgumentException {
super(applicator, validateType(msType));
}
@Override
BigInteger getSize() {
return BigInteger.valueOf(applicator.getDataOrganization().getPointerSize());
}
/**
* Returns the offset of the Virtual Function Table Pointer.
* @return Name of the nested type.
*/
int getOffset() {
if (msType instanceof AbstractVirtualFunctionTablePointerWithOffsetMsType) {
return ((AbstractVirtualFunctionTablePointerWithOffsetMsType) msType).getOffset();
}
return 0;
}
/**
* Returns the name to use.
* @return Name of the pointer type.
*/
String getMemberName() {
return "VFTablePtr" + getOffset();
}
@Override
void apply() throws PdbException, CancelledException {
if (msType instanceof AbstractVirtualFunctionTablePointerMsType) {
dataType = applyPointer(
((AbstractVirtualFunctionTablePointerMsType) msType).getPointerTypeRecordNumber());
}
else {
dataType = applyPointer(
((AbstractVirtualFunctionTablePointerWithOffsetMsType) msType).getPointerTypeRecordNumber());
}
}
private DataType applyPointer(RecordNumber pointerTypeRecordNumber) {
MsTypeApplier rawApplier = applicator.getTypeApplier(pointerTypeRecordNumber);
if (rawApplier instanceof PointerTypeApplier) {
return rawApplier.getDataType();
}
applicator.appendLogMsg("cannot process " + rawApplier.getClass().getSimpleName() + "for " +
getClass().getSimpleName());
return null;
}
private static AbstractMsType validateType(AbstractMsType type)
throws IllegalArgumentException {
if (!(type instanceof AbstractVirtualFunctionTablePointerMsType) &&
!(type instanceof AbstractVirtualFunctionTablePointerWithOffsetMsType)) {
throw new IllegalArgumentException(
"PDB Incorrectly applying " + type.getClass().getSimpleName() + " to " +
VirtualFunctionTablePointerTypeApplier.class.getSimpleName());
}
return type;
}
}

View File

@ -0,0 +1,99 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.pdb.pdbapplicator;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import ghidra.app.util.bin.format.pdb.DefaultCompositeMember;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.VtShapeDescriptorMsProperty;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.VtShapeMsType;
import ghidra.program.model.data.*;
import ghidra.util.Msg;
import ghidra.util.exception.CancelledException;
/**
* Applier for {@link VtShapeMsType} types.
*/
public class VtShapeTypeApplier extends MsTypeApplier {
/**
* Constructor for vtshape type applier.
* @param applicator {@link PdbApplicator} for which this class is working.
* @param msType {@link VtShapeMsType} to process.
*/
public VtShapeTypeApplier(PdbApplicator applicator, VtShapeMsType msType) {
super(applicator, msType);
}
@Override
BigInteger getSize() {
return BigInteger.valueOf(applicator.getDataOrganization().getPointerSize() *
((VtShapeMsType) msType).getCount());
}
/**
* Returns the name.
* @return the name.
*/
String getName() {
return "vtshape_" + index;
}
@Override
void apply() throws PdbException, CancelledException {
dataType = createVtShape((VtShapeMsType) msType);
}
// TODO: We are creating a structure for the vtshape. Is there anything different we would
// like to do instead?
private DataType createVtShape(VtShapeMsType msShape) throws CancelledException {
List<VtShapeDescriptorMsProperty> list = msShape.getDescriptorList();
// TODO: what are correct/appropriate CategoryPath and name
StructureDataType shape = new StructureDataType(applicator.getAnonymousTypesCategory(),
"vtshape" + index, 0, applicator.getDataTypeManager());
List<DefaultPdbUniversalMember> members = new ArrayList<>();
int offset = 0;
for (VtShapeDescriptorMsProperty descriptor : list) {
switch (descriptor) {
case NEAR:
case FAR:
case THIN:
case OUTER:
case META:
case NEAR32:
case FAR32:
Pointer pointer = new PointerDataType(applicator.getDataTypeManager());
DefaultPdbUniversalMember member =
new DefaultPdbUniversalMember(applicator, "", pointer, offset);
offset += pointer.getLength();
members.add(member);
break;
case UNUSED:
offset += applicator.getDataOrganization().getPointerSize();
break;
}
}
int size = applicator.getDataOrganization().getPointerSize() * msShape.getCount();
if (!DefaultCompositeMember.applyDataTypeMembers(shape, false, size, members,
msg -> Msg.warn(this, msg), applicator.getCancelOnlyWrappingMonitor())) {
CompositeTypeApplier.clearComponents(shape);
}
return shape; // not resolved
}
}

View File

@ -0,0 +1,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.AbstractWithMsSymbol;
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 AbstractWithMsSymbol} symbols. This is not fully implemented
* because we do not know its usage or have examples, but we have implemented
* the block management portion.
*/
public class WithSymbolApplier extends MsSymbolApplier {
private AbstractWithMsSymbol symbol;
/**
* Constructor
* @param applicator the {@link PdbApplicator} for which we are working.
* @param iter the Iterator containing the symbol sequence being processed
*/
public WithSymbolApplier(PdbApplicator applicator, AbstractMsSymbolIterator iter) {
super(applicator, iter);
AbstractMsSymbol abstractSymbol = iter.next();
if (!(abstractSymbol instanceof AbstractWithMsSymbol)) {
throw new AssertException(
"Invalid symbol type: " + abstractSymbol.getClass().getSimpleName());
}
symbol = (AbstractWithMsSymbol) abstractSymbol;
}
@Override
void apply() throws PdbException, CancelledException {
// TODO: We do not know if this can be applied to a program or not. We have no examples.
pdbLogAndInfoMessage(this,
"Cannot apply " + this.getClass().getSimpleName() + " directly to program");
}
@Override
void applyTo(MsSymbolApplier applyToApplier) {
// Do nothing
}
@Override
void manageBlockNesting(MsSymbolApplier applierParam) {
if (applierParam instanceof FunctionSymbolApplier) {
FunctionSymbolApplier functionSymbolApplier = (FunctionSymbolApplier) applierParam;
Address address = applicator.getAddress(symbol);
// TODO: not sure if getExpression() is correct, but there is no "name."
functionSymbolApplier.beginBlock(address, symbol.getExpression(), symbol.getLength());
}
}
}

View File

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

View File

@ -21,6 +21,7 @@ import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import ghidra.util.LittleEndianDataConverter;
import ghidra.util.Msg;
import ghidra.util.exception.AssertException;
/**
@ -250,7 +251,11 @@ public class PdbByteWriter {
* @param data4 A byte[8], as fourth constituent piece.
*/
public void putGUID(int data1, short data2, short data3, byte[] data4) {
assert data4.length == 8;
if (data4.length != 8) {
String msg = "GUID invalid byte[] size... terminating";
Msg.error(this, msg);
throw new AssertException(msg);
}
putInt(data1);
putShort(data2);
putShort(data3);

View File

@ -16,6 +16,7 @@
package ghidra.app.util.bin.format.pdb2.pdbreader.msf;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import java.io.*;
import java.util.*;
@ -26,6 +27,7 @@ import generic.test.AbstractGenericTest;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbByteWriter;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbReaderOptions;
import ghidra.util.Msg;
import ghidra.util.exception.AssertException;
import ghidra.util.task.TaskMonitor;
public class MsfReaderUnitTest extends AbstractGenericTest {
@ -271,7 +273,9 @@ public class MsfReaderUnitTest extends AbstractGenericTest {
}
void reservePage(int pageNumber) {
assert freePage[pageNumber];
if (!freePage[pageNumber]) {
fail("Page already free... terminating");
}
freePage[pageNumber] = false;
}
@ -303,8 +307,9 @@ public class MsfReaderUnitTest extends AbstractGenericTest {
return i;
}
}
assert false;
return -1;
String msg = "Unexpected algorithm flow";
Msg.error(null, msg);
throw new AssertException(msg);
}
private byte[] serializedFreePageMap200() {
@ -521,7 +526,11 @@ public class MsfReaderUnitTest extends AbstractGenericTest {
else {
ds = new DirectoryStream(this);
}
assert ds.streamNum == 0;
if (ds.streamNum != 0) {
String msg = "Stream 0 expected... terminating";
Msg.error(null, msg);
throw new AssertException(msg);
}
header.init();
fpm.init();
st.init();
@ -549,8 +558,16 @@ public class MsfReaderUnitTest extends AbstractGenericTest {
}
void fillPages(byte[] inputBuffer, List<Integer> pageList) {
assert outputBuffer != null;
assert pageList.size() > 0;
if (outputBuffer == null) {
String msg = "Output buffer is null... terminating";
Msg.error(null, msg);
throw new AssertException(msg);
}
if (pageList.size() <= 0) {
String msg = "Invalid page list size... terminating";
Msg.error(null, msg);
throw new AssertException(msg);
}
int outputIndex;
int inputIndex = 0;
for (int i = 0; i < pageList.size() - 1; i++) {

View File

@ -16,6 +16,7 @@
package ghidra.app.util.bin.format.pdb2.pdbreader.symbol;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import java.math.BigInteger;
@ -26,7 +27,6 @@ import generic.test.AbstractGenericTest;
import ghidra.app.util.bin.format.pdb2.pdbreader.*;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.AbstractMsType;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.DummyMsType;
import ghidra.util.Msg;
public class SymbolsTest extends AbstractGenericTest {
@ -62,8 +62,7 @@ public class SymbolsTest extends AbstractGenericTest {
dummyPdb700.setItemRecord(4096, item);
}
catch (Exception e) {
Msg.error(null, "Error in initialization of test", e);
assert false;
fail("Error in static initialization of test: " + e);
}
}

View File

@ -26,6 +26,7 @@ import generic.test.AbstractGenericTest;
import ghidra.app.util.bin.format.pdb2.pdbreader.*;
import ghidra.app.util.bin.format.pdb2.pdbreader.symbol.RegisterMsSymbol;
import ghidra.util.Msg;
import ghidra.util.exception.AssertException;
//TODO: not sure if ST variety should get putPadding() or putAlign()
@ -99,8 +100,9 @@ public class TypesTest extends AbstractGenericTest {
substringListMsType1 = dummyPdb700.addItemRecord(item);
}
catch (Exception e) {
Msg.error(null, "Error in static initialization of test", e);
assert false;
String msg = "Error in static initialization of testt: " + e;
Msg.error(null, msg);
throw new AssertException(msg);
}
}

View File

@ -0,0 +1,180 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.pdb.pdbapplicator;
import java.util.ArrayList;
import java.util.List;
import org.junit.*;
import ghidra.app.util.bin.format.pdb.*;
import ghidra.program.database.ProgramBuilder;
import ghidra.program.database.ProgramDB;
import ghidra.program.database.data.DataTypeManagerDB;
import ghidra.program.model.data.*;
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
import ghidra.util.Msg;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
/**
* Tests for the {@link DataTypeConflictHandler conflict handler} stuff.
*
*
*/
public class ConflictHandlerTest2 extends AbstractGhidraHeadedIntegrationTest {
private ProgramDB program;
private DataTypeManagerDB dtm;
private int transactionID;
public ConflictHandlerTest2() {
super();
}
private void startTransaction() {
transactionID = program.startTransaction("Test");
}
private void endTransaction() {
program.endTransaction(transactionID, true);
}
@Before
public void setUp() throws Exception {
program = createDefaultProgram(testName.getMethodName(), ProgramBuilder._X64, this);
dtm = program.getDataTypeManager();
startTransaction();
}
@After
public void tearDown() throws Exception {
endTransaction();
program.release(this);
}
@Test
public void testDataTypeConflicts() {
DataTypeConflictHandler handler =
DataTypeConflictHandler.REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER;
// First set
Composite testStruct1 = createComposite(dtm, "outer");
Pointer pointer1 = new PointerDataType(testStruct1, -1, dtm);
FunctionDefinitionDataType fn1 =
new FunctionDefinitionDataType(CategoryPath.ROOT, "fn1", dtm);
fn1.setReturnType(pointer1);
fn1.setGenericCallingConvention(GenericCallingConvention.cdecl);
fn1.setArguments(new ParameterDefinition[0]);
Composite internalStruct1 = createComposite(dtm, "inner");
Pointer internalPointer1 = new PointerDataType(internalStruct1, -1, dtm);
fillComposite(testStruct1, TaskMonitor.DUMMY, internalPointer1);
fillComposite(internalStruct1, TaskMonitor.DUMMY, null);
// Second set
Composite testStruct2 = createComposite(dtm, "outer");
Pointer pointer2 = new PointerDataType(testStruct2, -1, dtm);
FunctionDefinitionDataType fn2 =
new FunctionDefinitionDataType(CategoryPath.ROOT, "fn2", dtm);
fn2.setReturnType(pointer2);
fn2.setGenericCallingConvention(GenericCallingConvention.cdecl);
fn2.setArguments(new ParameterDefinition[0]);
Composite internalStruct2 = createComposite(dtm, "inner");
Pointer internalPointer2 = new PointerDataType(internalStruct2, -1, dtm);
fillComposite(testStruct2, TaskMonitor.DUMMY, internalPointer2);
// fillComposite(internalStruct2, monitor, null); // Without this line, we get a conflict
// Resolve
DataType t1 = dtm.resolve(testStruct1, handler);
DataType f1 = dtm.resolve(fn1, handler);
DataType t2 = dtm.resolve(testStruct2, handler);
DataType f2 = dtm.resolve(fn2, handler);
System.out.println(t1.toString());
System.out.println(f1.toString());
System.out.println(t2.toString());
System.out.println(f2.toString());
}
private static Composite createComposite(DataTypeManager dtm, String name) {
Composite composite = new StructureDataType(CategoryPath.ROOT, name, 0, dtm);
return composite;
}
private static void fillComposite(Composite composite, TaskMonitor monitor, DataType extra) {
List<DefaultTestPdbMember> members = new ArrayList<>();
DefaultTestPdbMember member;
int size = 8;
DataType intxy = IntegerDataType.dataType;
member = new DefaultTestPdbMember("x", intxy, 0);
members.add(member);
member = new DefaultTestPdbMember("y", intxy, 4);
members.add(member);
if (extra != null) {
member = new DefaultTestPdbMember("z", extra, 8);
members.add(member);
size += extra.getLength();
}
try {
if (!DefaultCompositeMember.applyDataTypeMembers(composite, false, size, members,
msg -> Msg.warn(ConflictHandlerTest2.class, msg), monitor)) {
((Structure) composite).deleteAll();
}
}
catch (Exception e) {
Msg.info(null, "Research exception thrown");
}
}
private static class DefaultTestPdbMember extends PdbMember {
private DataType dataType;
/**
* Default PDB member construction
* @param name member field name.
* @param dataType for the field.
* @param offset member's byte offset within the root composite.
*/
DefaultTestPdbMember(String name, DataType dataType, int offset) {
super(name, dataType.getName(), offset, null);
this.dataType = dataType;
}
@Override
public String getDataTypeName() {
return dataType.getName();
}
@Override
protected WrappedDataType getDataType() throws CancelledException {
if (dataType instanceof ArrayDataType) {
int size = 1; // mocking for now
if (size == 0) {
return new WrappedDataType(dataType, true, false);
}
}
return new WrappedDataType(dataType, false, false);
}
}
}

View File

@ -45,7 +45,11 @@ public class SymbolPathParser {
throw new IllegalArgumentException(
"Symbol list must contain at least one symbol name!");
}
if (name.indexOf(Namespace.DELIMITER) == -1) {
// if (name.indexOf(Namespace.DELIMITER) == -1) {
// following is temporary kludge due to struct (blah). TODO: figure/fix
// This particular test for starting with the open parenthesis is to work around a type
// seen in "Rust."
if (name.startsWith("(") || name.indexOf(Namespace.DELIMITER) == -1) {
List<String> list = new ArrayList<>();
list.add(name);
return list;

View File

@ -28,6 +28,17 @@ import ghidra.util.exception.DuplicateNameException;
* be overridden so it can provide its own test implementation and return value.
*/
public class MemoryBlockStub implements MemoryBlock {
Address start;
Address end;
public MemoryBlockStub() {
this(Address.NO_ADDRESS, Address.NO_ADDRESS);
}
public MemoryBlockStub(Address start, Address end) {
this.start = start;
this.end = end;
}
@Override
public int compareTo(MemoryBlock o) {
@ -51,12 +62,12 @@ public class MemoryBlockStub implements MemoryBlock {
@Override
public Address getStart() {
throw new UnsupportedOperationException();
return start;
}
@Override
public Address getEnd() {
throw new UnsupportedOperationException();
return end;
}
@Override
@ -131,7 +142,7 @@ public class MemoryBlockStub implements MemoryBlock {
@Override
public boolean isOverlay() {
throw new UnsupportedOperationException();
return false;
}
@Override
@ -176,7 +187,7 @@ public class MemoryBlockStub implements MemoryBlock {
@Override
public MemoryBlockType getType() {
throw new UnsupportedOperationException();
return MemoryBlockType.DEFAULT;
}
@Override

View File

@ -32,21 +32,22 @@ import ghidra.util.task.TaskMonitor;
* for all methods in the Memory interface. Any method that is needed for your test can then
* be overridden so it can provide its own test implementation and return value.
*/
public class MemoryStub implements Memory {
public class MemoryStub extends AddressSet implements Memory {
byte[] myMemoryBytes;
MemoryBlock myMemoryBlock;
@Override
public boolean contains(Address addr) {
throw new UnsupportedOperationException();
public MemoryStub() {
this(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 });
}
@Override
public boolean contains(Address start, Address end) {
throw new UnsupportedOperationException();
}
@Override
public boolean contains(AddressSetView rangeSet) {
throw new UnsupportedOperationException();
public MemoryStub(byte[] bytes) {
super();
this.myMemoryBytes = bytes;
AddressSpace space = new GenericAddressSpace("Mem", 32, AddressSpace.TYPE_RAM, 0);
Address start = space.getAddress(0);
Address end = space.getAddress(bytes.length - 1);
addRange(start, end);
myMemoryBlock = new MemoryBlockStub(start, end);
}
@Override
@ -124,36 +125,11 @@ public class MemoryStub implements Memory {
throw new UnsupportedOperationException();
}
@Override
public AddressSet intersect(AddressSetView view) {
throw new UnsupportedOperationException();
}
@Override
public AddressSet intersectRange(Address start, Address end) {
throw new UnsupportedOperationException();
}
@Override
public AddressSet union(AddressSetView addrSet) {
throw new UnsupportedOperationException();
}
@Override
public AddressSet subtract(AddressSetView addrSet) {
throw new UnsupportedOperationException();
}
@Override
public AddressSet xor(AddressSetView addrSet) {
throw new UnsupportedOperationException();
}
@Override
public boolean hasSameAddresses(AddressSetView view) {
throw new UnsupportedOperationException();
}
@Override
public AddressRange getFirstRange() {
throw new UnsupportedOperationException();
@ -241,9 +217,8 @@ public class MemoryStub implements Memory {
@Override
public MemoryBlock createByteMappedBlock(String name, Address start, Address mappedAddress,
long length, ByteMappingScheme byteMappingScheme, boolean overlay)
throws LockException, MemoryConflictException, AddressOverflowException,
IllegalArgumentException {
long length, ByteMappingScheme byteMappingScheme, boolean overlay) throws LockException,
MemoryConflictException, AddressOverflowException, IllegalArgumentException {
throw new UnsupportedOperationException();
}

View File

@ -221,4 +221,9 @@ public abstract class AbstractJavaAnalyzer implements Analyzer {
protected Address find(Program program, Address start, byte[] values, TaskMonitor monitor) {
return program.getMemory().findBytes(start, values, null, true, monitor);
}
@Override
public boolean rememberEnablementChangeAsUserPreference() {
return false;
}
}