mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-09-20 17:41:48 +00:00
Merge remote-tracking branch 'origin/GP-177_ghidra1_StickyAnalyzerEnablement'
This commit is contained in:
commit
ef1a39fe23
27
Ghidra/Features/Base/.launch/Ghidra Code Coverage.launch
Normal file
27
Ghidra/Features/Base/.launch/Ghidra Code Coverage.launch
Normal 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="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <sourceLookupDirector> <sourceContainers duplicates="false"> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;classpathContainer path=&quot;org.eclipse.jdt.launching.JRE_CONTAINER&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.classpathContainer"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;default/&gt;&#10;" typeId="org.eclipse.debug.core.containerType.default"/> </sourceContainers> </sourceLookupDirector> "/>
|
||||
<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="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry path="5" projectName="Framework Utility" type="1"/> "/>
|
||||
</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="Ghidra" -Dvisualvm.display.name=Ghidra"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.WORKING_DIRECTORY" value="${workspace_loc:Framework Utility}"/>
|
||||
</launchConfiguration>
|
|
@ -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|
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
||||
}
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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 {
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
/**
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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++;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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()]));
|
||||
// }
|
||||
//
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
|
||||
}
|
|
@ -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);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
// }
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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.
|
||||
}
|
||||
|
||||
}
|
|
@ -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("");
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -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 "";
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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++) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user