mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-11-21 11:31:43 +00:00
GP-5039 - PDB CPP - Move redesigned but neutered VxtManager to master
This commit is contained in:
parent
07d7358970
commit
a8623b7202
@ -0,0 +1,107 @@
|
||||
/* ###
|
||||
* 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.classtype;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
//----------------------------------------------------------------------------------------------
|
||||
// TODO: Consider expanding these beyond C++.
|
||||
// See https://en.wikipedia.org/wiki/Access_modifiers
|
||||
// These could then be:
|
||||
// UNKNOWN("UNKNOWN_ACCESS ", -1),
|
||||
// OPEN("open", 0),
|
||||
// PUBLIC("internal", 1),
|
||||
// INTERNAL("internal", 2),
|
||||
// PACKAGE("package", 3),
|
||||
// PROTECTED("protected", 4),
|
||||
// PROTECTED_INTERNAL("protected internal", 5),
|
||||
// PRIVATE_PROTECTED("private protected", 6),
|
||||
// FILE("file", 7),
|
||||
// FILE_PRIVATE("fileprivate", 8),
|
||||
// PRIVATE("private", 9);
|
||||
public enum Access {
|
||||
UNKNOWN("UNKNOWN_ACCESS", -1),
|
||||
BLANK("", 0), // eliminated 20230524... using defaultAccess on some methods. Could renumber
|
||||
PUBLIC("public", 1),
|
||||
PROTECTED("protected", 2),
|
||||
PRIVATE("private", 3);
|
||||
|
||||
private static final Map<Integer, Access> BY_VALUE = new HashMap<>();
|
||||
static {
|
||||
for (Access val : values()) {
|
||||
BY_VALUE.put(val.value, val);
|
||||
}
|
||||
}
|
||||
private final String label;
|
||||
private final int value;
|
||||
|
||||
public String getString() {
|
||||
return label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public static Access fromValue(int val) {
|
||||
return BY_VALUE.getOrDefault(val, UNKNOWN);
|
||||
}
|
||||
|
||||
private Access(String label, int value) {
|
||||
this.label = label;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge two Access values, leaning toward more restrictive. UNKNOWN is only returned
|
||||
* if both are UNKNOWN.
|
||||
* @param other value to merge
|
||||
* @return the merged value
|
||||
*/
|
||||
public Access mergeRestrictive(Access other) {
|
||||
// No need to test for UNKNOWN as its value is on the permissive end.
|
||||
if (this.value > other.value) {
|
||||
return this;
|
||||
}
|
||||
return other;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge two Access values, leaning toward more permissive. UNKNOWN is only returned
|
||||
* if both are UNKNOWN.
|
||||
* @param other value to merge
|
||||
* @return the merged value
|
||||
*/
|
||||
public Access mergePermissive(Access other) {
|
||||
if (this.value < other.value) {
|
||||
// Only need special test for UNKNOWN here, as its value is on the permissive end.
|
||||
if (this == UNKNOWN) {
|
||||
return other;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
return other;
|
||||
}
|
||||
}
|
@ -0,0 +1,114 @@
|
||||
/* ###
|
||||
* 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.classtype;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.app.util.bin.format.pdb2.pdbreader.type.ClassFieldMsAttributes;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class ClassFieldAttributes {
|
||||
|
||||
private static final Map<ClassFieldAttributes, ClassFieldAttributes> map = new HashMap<>();
|
||||
|
||||
// These initializations use the map above, so it must be initialized first
|
||||
public static final ClassFieldAttributes UNKNOWN = get(Access.UNKNOWN, Property.UNKNOWN);
|
||||
public static final ClassFieldAttributes BLANK = get(Access.BLANK, Property.BLANK);
|
||||
|
||||
private final Access access;
|
||||
private final Property property;
|
||||
|
||||
public synchronized static ClassFieldAttributes get(Access access, Property property) {
|
||||
ClassFieldAttributes key = new ClassFieldAttributes(access, property);
|
||||
ClassFieldAttributes cfa = map.putIfAbsent(key, key);
|
||||
return (cfa != null) ? cfa : key;
|
||||
}
|
||||
|
||||
public static ClassFieldAttributes convert(ClassFieldMsAttributes msAtts,
|
||||
Access defaultAccess) {
|
||||
Access myAccess = switch (msAtts.getAccess()) {
|
||||
case PUBLIC -> Access.PUBLIC;
|
||||
case PROTECTED -> Access.PROTECTED;
|
||||
case PRIVATE -> Access.PRIVATE;
|
||||
case BLANK -> defaultAccess;
|
||||
default -> Access.UNKNOWN;
|
||||
};
|
||||
Property myProperty = switch (msAtts.getProperty()) {
|
||||
case VIRTUAL -> Property.VIRTUAL;
|
||||
case STATIC -> Property.STATIC;
|
||||
case FRIEND -> Property.FRIEND;
|
||||
case BLANK -> Property.BLANK;
|
||||
default -> Property.UNKNOWN;
|
||||
};
|
||||
return get(myAccess, myProperty);
|
||||
}
|
||||
|
||||
private ClassFieldAttributes(Access access, Property property) {
|
||||
this.access = access;
|
||||
this.property = property;
|
||||
}
|
||||
|
||||
public Access getAccess() {
|
||||
return access;
|
||||
}
|
||||
|
||||
public Property getProperty() {
|
||||
return property;
|
||||
}
|
||||
|
||||
public void emit(StringBuilder builder) {
|
||||
StringBuilder myBuilder = new StringBuilder();
|
||||
if (access.getValue() > Access.BLANK.getValue()) {
|
||||
myBuilder.append(access);
|
||||
myBuilder.append(' ');
|
||||
}
|
||||
if (property.getValue() > Property.BLANK.getValue()) {
|
||||
myBuilder.append(property);
|
||||
myBuilder.append(' ');
|
||||
}
|
||||
builder.append(myBuilder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
emit(builder);
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(access, property);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
ClassFieldAttributes other = (ClassFieldAttributes) obj;
|
||||
return access == other.access && property == other.property;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
/* ###
|
||||
* 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.classtype;
|
||||
|
||||
/**
|
||||
* Unique ID of a ClassType. Not sure if there will be different implementation for definition
|
||||
* vs. compiled vs. program vs. debug. Need to come to grips with this
|
||||
*/
|
||||
public interface ClassID extends Comparable<ClassID> {
|
||||
|
||||
// For compareTo() method of classes in this hierarchy (for Comparable<ClassID>)
|
||||
/**
|
||||
* For internal use
|
||||
* @return hash of java class in ClassID hierarchy
|
||||
*/
|
||||
public int getClassNameHash();
|
||||
|
||||
}
|
@ -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.classtype;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Class keys from perspective of C++ language
|
||||
*/
|
||||
public enum ClassKey {
|
||||
UNKNOWN("UNKNOWN_TYPE", -1),
|
||||
BLANK("", 1),
|
||||
CLASS("class", 2),
|
||||
STRUCT("struct", 3),
|
||||
UNION("union", 4);
|
||||
|
||||
private static final Map<Integer, ClassKey> BY_VALUE = new HashMap<>();
|
||||
static {
|
||||
for (ClassKey val : values()) {
|
||||
BY_VALUE.put(val.value, val);
|
||||
}
|
||||
}
|
||||
private final String label;
|
||||
private final int value;
|
||||
|
||||
public String getString() {
|
||||
return label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (label.length() != 0) {
|
||||
return label + " ";
|
||||
}
|
||||
return label;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public static ClassKey fromValue(int val) {
|
||||
return BY_VALUE.getOrDefault(val, UNKNOWN);
|
||||
}
|
||||
|
||||
private ClassKey(String label, int value) {
|
||||
this.label = label;
|
||||
this.value = value;
|
||||
}
|
||||
}
|
@ -0,0 +1,101 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.util.pdb.classtype;
|
||||
|
||||
import ghidra.app.util.SymbolPath;
|
||||
import ghidra.program.model.data.*;
|
||||
|
||||
/**
|
||||
* Class Type Manager
|
||||
*/
|
||||
public class ClassTypeManager {
|
||||
|
||||
private static final String CLASS_TYPE_MANAGER_PROTOTYPE2 = "CLASS_TYPE_MANAGER_PROTOTYPE2";
|
||||
|
||||
private DataTypeManager dtm;
|
||||
|
||||
private PointerDataType defaultPtrType;
|
||||
private PointerDataType defaultVbtPtr;
|
||||
private PointerDataType defaultVftPtr;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param dtm the data type manager
|
||||
*/
|
||||
public ClassTypeManager(DataTypeManager dtm) {
|
||||
this.dtm = dtm;
|
||||
|
||||
defaultPtrType = new PointerDataType(dtm);
|
||||
defaultVbtPtr = new PointerDataType(new IntegerDataType(dtm));
|
||||
defaultVftPtr = new PointerDataType(new PointerDataType(dtm));
|
||||
}
|
||||
|
||||
public SymbolPath getSymbolPath(ClassID classId) {
|
||||
if (classId instanceof ProgramClassID gId) {
|
||||
return gId.getSymbolPath();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the underlying data type manager
|
||||
* @return the data type manager
|
||||
*/
|
||||
public DataTypeManager getDataTypeManager() {
|
||||
return dtm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default pointer type
|
||||
* @return the pointer type
|
||||
*/
|
||||
public PointerDataType getDefaultPointerType() {
|
||||
return defaultPtrType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default virtual base table pointer type
|
||||
* @return the pointer type
|
||||
*/
|
||||
public PointerDataType getDefaultVbtPtr() {
|
||||
return defaultVbtPtr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default virtual function table pointer type
|
||||
* @return the pointer type
|
||||
*/
|
||||
public PointerDataType getDefaultVftPtr() {
|
||||
return defaultVftPtr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default size of a virtual base table entry
|
||||
* @return the size of the entry
|
||||
*/
|
||||
public int getDefaultVbtTableElementSize() {
|
||||
return dtm.getDataOrganization().getIntegerSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default size of a virtual function table entry
|
||||
* @return the size of the entry
|
||||
*/
|
||||
public int getDefaultVftTableElementSize() {
|
||||
return dtm.getDataOrganization().getPointerSize();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,501 @@
|
||||
/* ###
|
||||
* 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.classtype;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.app.util.SymbolPath;
|
||||
import ghidra.app.util.demangler.DemangledException;
|
||||
import ghidra.app.util.demangler.DemangledObject;
|
||||
import ghidra.app.util.demangler.microsoft.MicrosoftDemangler;
|
||||
import ghidra.app.util.demangler.microsoft.MicrosoftMangledContext;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.data.CategoryPath;
|
||||
import ghidra.program.model.mem.Memory;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.AssertException;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import mdemangler.MDParsableItem;
|
||||
import mdemangler.naming.MDQualification;
|
||||
import mdemangler.naming.MDQualifier;
|
||||
import mdemangler.object.MDObjectCPP;
|
||||
import mdemangler.typeinfo.*;
|
||||
|
||||
/**
|
||||
* This class manages MSFT-compliant virtual function tables and virtual base tables for our
|
||||
* program.
|
||||
* <p>
|
||||
* This class also provide lookup mechanisms for locating the appropriate tables desired for
|
||||
* for particular MSFT-compliant classes of the program. This is particularly useful for
|
||||
* determining how classes are laid down in memory and for determining which virtual method
|
||||
* gets called for a class.
|
||||
* <p>
|
||||
* The initial design has been shelved for now due to issues. For now, the design relies on
|
||||
* a stinky solution that assumes the order of the tables in memory is that same as the order of
|
||||
* the pointers to these tables in the owner class. This seems true based on limited experience.
|
||||
* Hoping to revisit this soon.
|
||||
*/
|
||||
public class MsftVxtManager extends VxtManager {
|
||||
|
||||
private Memory memory;
|
||||
private Map<String, Address> vxtAddressByMangled;
|
||||
private Map<String, ParentageNode> parentageNodeByMangled;
|
||||
|
||||
// These two are for an interim solution where we believe that the order of these tables in
|
||||
// memory are the same order that their pointers appear in the classes... this is based solely
|
||||
// on limited experience. The solution stinks and would benefit from the original direction
|
||||
// of using the parentage. We will try to use the parentage as a litmus test on retrieval
|
||||
private Map<ClassID, TreeMap<Address, VBTable>> vbtsByOwner;
|
||||
private Map<ClassID, TreeMap<Address, VFTable>> vftsByOwner;
|
||||
|
||||
// Used for locating vft and vbt
|
||||
// These are explicitly used for storing/retrieving the "program" versions which result from
|
||||
// locating and parsing the mangled strings for these tables. It is possible that these
|
||||
// could be used for placeholder or other versions... TBD
|
||||
private ParentageNode mixedRoot; // originally had separate roots for VFT and VBT
|
||||
|
||||
/**
|
||||
* Constructor for this class
|
||||
* @param ctm the class type manager
|
||||
* @param memory the memory of the program
|
||||
*/
|
||||
public MsftVxtManager(ClassTypeManager ctm, Memory memory) {
|
||||
super(ctm);
|
||||
this.memory = memory;
|
||||
vxtAddressByMangled = new HashMap<>();
|
||||
parentageNodeByMangled = new HashMap<>();
|
||||
vbtsByOwner = new HashMap<>();
|
||||
vftsByOwner = new HashMap<>();
|
||||
mixedRoot = new ParentageNode(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the putative {@link VBTable} in memory requested for the owning class
|
||||
* @param owner the owning class of the table
|
||||
* @return the table
|
||||
*/
|
||||
public VBTable findPrimaryVbt(ClassID owner) {
|
||||
TreeMap<Address, VBTable> map = vbtsByOwner.get(owner);
|
||||
if (map == null) {
|
||||
return null;
|
||||
}
|
||||
return map.firstEntry().getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the putative {@link VFTable} in memory requested for the owning class
|
||||
* @param owner the owning class of the table
|
||||
* @return the table
|
||||
*/
|
||||
public VFTable findPrimaryVft(ClassID owner) {
|
||||
TreeMap<Address, VFTable> map = vftsByOwner.get(owner);
|
||||
if (map == null) {
|
||||
return null;
|
||||
}
|
||||
return map.firstEntry().getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the putative {@link VBTable} in memory requested for the owning class and the
|
||||
* specified parentage
|
||||
* @param owner the owning class of the table
|
||||
* @param parentage the parentage for the desired table. The parentage must start with the
|
||||
* parent that contains the pointer to the table and should include the ordered lineage from
|
||||
* that class through all of its decendents to the owner, excluding the owner
|
||||
* @return the table
|
||||
*/
|
||||
public VBTable findVbt(ClassID owner, List<ClassID> parentage) {
|
||||
ParentageNode node = findNode(owner, parentage);
|
||||
if (node == null) {
|
||||
return null;
|
||||
}
|
||||
VBTable vbTable = node.getVBTable();
|
||||
if (vbTable != null || !parentage.isEmpty()) { // see note below
|
||||
return vbTable;
|
||||
}
|
||||
// Not 100% sure on this... needs more investigation as to why there are mangled strings
|
||||
// that reference the owner in the parentage. Could there be a situation where there
|
||||
// is one with the parentage and one without? We are treating them as the same for now
|
||||
// unless we find counter-examples or difficulties with this.
|
||||
// Above, we test of parentage.isEmpty, because this special case comes into play only
|
||||
// if it was empty
|
||||
node = findNode(owner, List.of(owner));
|
||||
if (node == null) {
|
||||
return null;
|
||||
}
|
||||
return node.getVBTable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the putative {@link VFTable} in memory requested for the owning class and the
|
||||
* specified parentage
|
||||
* @param owner the owning class of the table
|
||||
* @param parentage the parentage for the desired table. The parentage must start with the
|
||||
* parent that contains the pointer to the table and should include the ordered lineage from
|
||||
* that class through all of its decendents to the owner, excluding the owner
|
||||
* @return the table
|
||||
*/
|
||||
public VFTable findVft(ClassID owner, List<ClassID> parentage) {
|
||||
ParentageNode node = findNode(owner, parentage);
|
||||
if (node == null) {
|
||||
return null;
|
||||
}
|
||||
VFTable vfTable = node.getVFTable();
|
||||
if (vfTable != null || !parentage.isEmpty()) { // see note below
|
||||
return vfTable;
|
||||
}
|
||||
// Not 100% sure on this... needs more investigation as to why there are mangled strings
|
||||
// that reference the owner in the parentage. Could there be a situation where there
|
||||
// is one with the parentage and one without? We are treating them as the same for now
|
||||
// unless we find counter-examples or difficulties with this.
|
||||
// Above, we test of parentage.isEmpty, because this special case comes into play only
|
||||
// if it was empty
|
||||
node = findNode(owner, List.of(owner));
|
||||
if (node == null) {
|
||||
return null;
|
||||
}
|
||||
return node.getVFTable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a virtual function and base tables for the {@code Map<String, Address>} of
|
||||
* addresses-by-mangled names. Any failures are logged
|
||||
*
|
||||
* @param categoryPath the base category path used for the collection of class-related
|
||||
* types
|
||||
* @param addressByMangledName the map of addresses-by-mangled-names
|
||||
* @param log the message log
|
||||
* @param monitor the task monitor
|
||||
* @throws CancelledException upon user cancellation
|
||||
*/
|
||||
public void createVirtualTables(CategoryPath categoryPath,
|
||||
Map<String, Address> addressByMangledName, MessageLog log, TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
for (Map.Entry<String, Address> entry : addressByMangledName.entrySet()) {
|
||||
monitor.checkCancelled();
|
||||
String mangled = entry.getKey();
|
||||
Address address = entry.getValue();
|
||||
if (!createVirtualTable(categoryPath, mangled, address, monitor)) {
|
||||
log.appendMsg("Could not create VxTable for " + mangled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a virtual function or base table for the mangled symbol and its associated
|
||||
* memory address
|
||||
*
|
||||
* @param categoryPath the base category path used for the collection of class-related
|
||||
* types
|
||||
* @param mangled the mangled name of the type
|
||||
* @param address the address associated with the mangled name
|
||||
* @param monitor the task monitor
|
||||
* @return {@code true} if successful
|
||||
*/
|
||||
public boolean createVirtualTable(CategoryPath categoryPath, String mangled, Address address,
|
||||
TaskMonitor monitor) {
|
||||
|
||||
Address a = vxtAddressByMangled.get(mangled);
|
||||
if (a != null) {
|
||||
if (!a.equals(address)) {
|
||||
Msg.warn(this, String.format("New address (%s) does not match existing %s for: %s",
|
||||
a, address, mangled));
|
||||
}
|
||||
else {
|
||||
Msg.warn(this,
|
||||
String.format("Entry already exists: address (%s), %s", address, mangled));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
vxtAddressByMangled.put(mangled, address);
|
||||
|
||||
DemanglerResults demanglerResults = getOwnerAndUsersDtp(categoryPath, mangled);
|
||||
if (demanglerResults == null) {
|
||||
Msg.warn(this, "Problem obtaining path information from mangled symbol: " + mangled);
|
||||
return false;
|
||||
}
|
||||
|
||||
OwnerAndParentage ownerAndParentage = demanglerResults.ownerAndParentage();
|
||||
ClassID owner = ownerAndParentage.owner();
|
||||
List<ClassID> parentage = ownerAndParentage.parentage();
|
||||
ParentageNode node = parentageNodeByMangled.get(mangled);
|
||||
if (node == null) {
|
||||
node = getOrAddParentageNode(categoryPath, mixedRoot, demanglerResults);
|
||||
if (node == null) {
|
||||
return false;
|
||||
}
|
||||
parentageNodeByMangled.put(mangled, node);
|
||||
}
|
||||
|
||||
switch (demanglerResults.vtType()) {
|
||||
case VBT:
|
||||
ProgramVirtualBaseTable prvbt = new ProgramVirtualBaseTable(owner, parentage,
|
||||
memory, address, ctm.getDefaultVbtTableElementSize(), ctm, mangled);
|
||||
if (node.getVBTable() != null) {
|
||||
Msg.warn(this, "VBT already exists at node for " + mangled);
|
||||
return false;
|
||||
}
|
||||
node.setVBTable(prvbt);
|
||||
vbtByAddress.put(address, prvbt);
|
||||
storeVbt(owner, address, prvbt); // temp solution?
|
||||
break;
|
||||
|
||||
case VFT:
|
||||
ProgramVirtualFunctionTable vft = new ProgramVirtualFunctionTable(owner, parentage,
|
||||
memory, address, ctm.getDefaultVftTableElementSize(), mangled);
|
||||
if (node.getVFTable() != null) {
|
||||
Msg.warn(this, "VFT already exists at node for " + mangled);
|
||||
return false;
|
||||
}
|
||||
node.setVFTable(vft);
|
||||
vftByAddress.put(address, vft);
|
||||
storeVft(owner, address, vft); // temp solution?
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new AssertException("Unhandled VtType: " + demanglerResults.vtType());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void storeVbt(ClassID owner, Address address, VBTable vbt) {
|
||||
TreeMap<Address, VBTable> map = vbtsByOwner.get(owner);
|
||||
if (map == null) {
|
||||
map = new TreeMap<>();
|
||||
vbtsByOwner.put(owner, map);
|
||||
}
|
||||
map.put(address, vbt);
|
||||
}
|
||||
|
||||
private void storeVft(ClassID owner, Address address, VFTable vft) {
|
||||
TreeMap<Address, VFTable> map = vftsByOwner.get(owner);
|
||||
if (map == null) {
|
||||
map = new TreeMap<>();
|
||||
vftsByOwner.put(owner, map);
|
||||
}
|
||||
map.put(address, vft);
|
||||
}
|
||||
|
||||
private ParentageNode findNode(ClassID owner, List<ClassID> parentage) {
|
||||
if (!(owner instanceof ProgramClassID ownerGId)) {
|
||||
return null;
|
||||
}
|
||||
SymbolPath ownerSp = ownerGId.getSymbolPath();
|
||||
ParentageNode ownerNode = mixedRoot.getBranch(ownerSp.toString());
|
||||
if (ownerNode == null) {
|
||||
return null;
|
||||
}
|
||||
ParentageNode resultNode = null;
|
||||
ParentageNode node = ownerNode;
|
||||
for (ClassID id : parentage) {
|
||||
if (!(id instanceof ProgramClassID gId)) {
|
||||
return null;
|
||||
}
|
||||
SymbolPath sp = gId.getSymbolPath();
|
||||
ParentageNode next = node.getBranch(sp.toString());
|
||||
if (next != null) {
|
||||
node = next;
|
||||
resultNode = node;
|
||||
}
|
||||
// Regardless of found or not, go to next in parentage.
|
||||
// Skips unnecessary peer-through parents
|
||||
}
|
||||
if (resultNode == null) {
|
||||
return ownerNode;
|
||||
}
|
||||
// Need to have found at least one in parentage, but since the owner is part of our
|
||||
// parentage tree, we cannot use the fact that we are still on owner (which can also
|
||||
// be in our parentage list) as the flag for knowing if we found something in the
|
||||
// parentage, so we have a separate found flag
|
||||
return resultNode;
|
||||
}
|
||||
|
||||
private ParentageNode getOrAddParentageNode(CategoryPath categoryPath, ParentageNode root,
|
||||
DemanglerResults demanglerResults) {
|
||||
|
||||
ParentageNode node = root;
|
||||
OwnerAndParentage ownerAndParentage = demanglerResults.ownerAndParentage();
|
||||
|
||||
ClassID owner = ownerAndParentage.owner(); // owner should be same as first on list
|
||||
List<ClassID> parentage = ownerAndParentage.parentage();
|
||||
if (!(owner instanceof ProgramClassID)) {
|
||||
Msg.error(this, "ClassID error for " + ownerAndParentage);
|
||||
return null;
|
||||
}
|
||||
ProgramClassID gId = (ProgramClassID) owner;
|
||||
node = node.getOrAddBranch(gId.getSymbolPath().toString());
|
||||
for (ClassID id : parentage) {
|
||||
if (!(id instanceof ProgramClassID)) {
|
||||
Msg.error(this, "ClassID error for " + ownerAndParentage);
|
||||
return null;
|
||||
}
|
||||
gId = (ProgramClassID) id;
|
||||
node = node.getOrAddBranch(gId.getSymbolPath().toString());
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
private static MDParsableItem doDemangle(String mangledString) {
|
||||
MicrosoftDemangler demangler = new MicrosoftDemangler();
|
||||
// Options, Program, and Address will have no bearing on what we are looking for
|
||||
MicrosoftMangledContext context =
|
||||
demangler.createMangledContext(mangledString, null, null, null);
|
||||
try {
|
||||
DemangledObject demangledObject = demangler.demangle(context);
|
||||
if (demangledObject == null) {
|
||||
// Couldn't demangle.
|
||||
return null;
|
||||
}
|
||||
return demangler.getMdItem();
|
||||
}
|
||||
catch (DemangledException e) {
|
||||
// Couldn't demangle.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private enum VtType {
|
||||
VFT, VBT
|
||||
}
|
||||
|
||||
private record OwnerAndParentage(ClassID owner, List<ClassID> parentage) {}
|
||||
|
||||
private record DemanglerResults(VtType vtType, OwnerAndParentage ownerAndParentage) {}
|
||||
|
||||
/**
|
||||
* Gets the owner and users of the VxT from the mangled name
|
||||
* @param categoryPath the base CategoryPath for types tree being used
|
||||
* @param mangledString the mangled string to be decoded
|
||||
* @return the associated complex type or null if the string couldn't be demangled
|
||||
*/
|
||||
private static DemanglerResults getOwnerAndUsersDtp(CategoryPath categoryPath,
|
||||
String mangledString) {
|
||||
MDParsableItem parsableItem = doDemangle(mangledString);
|
||||
|
||||
if (!(parsableItem instanceof MDObjectCPP cppItem)) {
|
||||
return null;
|
||||
}
|
||||
MDTypeInfo typeInfo = cppItem.getTypeInfo();
|
||||
if (!(typeInfo instanceof MDVxTable vxTable)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
SymbolPath ownerSp = getOwnerSymbolPath(cppItem.getQualification());
|
||||
List<SymbolPath> parentageSps = getParentageSymbolPaths(vxTable.getNestedQualifications());
|
||||
|
||||
List<ClassID> parentage = new ArrayList<>();
|
||||
ClassID owner = new ProgramClassID(categoryPath, ownerSp);
|
||||
for (SymbolPath sp : parentageSps) {
|
||||
ClassID user = new ProgramClassID(categoryPath, sp);
|
||||
parentage.add(user); // owner is the parentage if parentageSps was empty
|
||||
}
|
||||
|
||||
OwnerAndParentage ownerAndParentage = new OwnerAndParentage(owner, parentage);
|
||||
|
||||
return switch (typeInfo) {
|
||||
case MDVFTable f -> new DemanglerResults(VtType.VFT, ownerAndParentage);
|
||||
case MDVBTable b -> new DemanglerResults(VtType.VBT, ownerAndParentage);
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
|
||||
private static List<SymbolPath> getParentageSymbolPaths(List<MDQualification> qualifications) {
|
||||
if (qualifications == null) {
|
||||
return null;
|
||||
}
|
||||
List<SymbolPath> paths = new ArrayList<>();
|
||||
for (MDQualification qualification : qualifications) {
|
||||
SymbolPath symbolPath = getOwnerSymbolPath(qualification);
|
||||
paths.add(symbolPath);
|
||||
}
|
||||
return paths;
|
||||
}
|
||||
|
||||
private static SymbolPath getOwnerSymbolPath(MDQualification qualification) {
|
||||
Iterator<MDQualifier> it = qualification.iterator();
|
||||
if (!it.hasNext()) {
|
||||
return null;
|
||||
}
|
||||
List<String> parts = new ArrayList<>();
|
||||
do {
|
||||
MDQualifier qual = it.next();
|
||||
parts.add(0, qual.toString());
|
||||
}
|
||||
while (it.hasNext());
|
||||
return new SymbolPath(parts);
|
||||
}
|
||||
|
||||
//==============================================================================================
|
||||
|
||||
private static class ParentageNode {
|
||||
private ParentageNode parent = null;
|
||||
private Map<String, ParentageNode> branches;
|
||||
private String name;
|
||||
// Might want to store more than just one VXT... could store generic, pdb, program
|
||||
// versions... could mix function and base too (one tree instead of two)?
|
||||
private VFTable vft;
|
||||
private VBTable vbt;
|
||||
|
||||
private ParentageNode(String name) {
|
||||
this.name = name;
|
||||
branches = new HashMap<>();
|
||||
}
|
||||
|
||||
private ParentageNode getOrAddBranch(String branchName) {
|
||||
ParentageNode branch = branches.get(branchName);
|
||||
if (branch == null) {
|
||||
branch = new ParentageNode(branchName);
|
||||
branch.parent = this;
|
||||
branches.put(branchName, branch);
|
||||
}
|
||||
return branch;
|
||||
}
|
||||
|
||||
private ParentageNode getBranch(String branchName) {
|
||||
return branches.get(branchName);
|
||||
}
|
||||
|
||||
private void setVFTable(VFTable vftArg) {
|
||||
vft = vftArg;
|
||||
}
|
||||
|
||||
private void setVBTable(VBTable vbtArg) {
|
||||
vbt = vbtArg;
|
||||
}
|
||||
|
||||
private VFTable getVFTable() {
|
||||
return vft;
|
||||
}
|
||||
|
||||
private VBTable getVBTable() {
|
||||
return vbt;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private ParentageNode getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,120 @@
|
||||
/* ###
|
||||
* 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.classtype;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import ghidra.app.util.SymbolPath;
|
||||
import ghidra.program.model.data.CategoryPath;
|
||||
|
||||
/**
|
||||
* Unique ID of a ProgramClassType. Not sure if there will be different implementation for
|
||||
* definition vs. compiled vs. program vs. debug. See ClassID.
|
||||
*/
|
||||
public class ProgramClassID implements ClassID {
|
||||
// All of the internals of this might change, but we need something to work with for now.
|
||||
// It might end up being a hash/guid/long value.
|
||||
// We were trying to use DataTypePath, but that doesn't work in light of conflicts, as we
|
||||
// started with a DataTypePath for the type, which later got resolved to a .conflict (so
|
||||
// DataTypePath changed out from underneath us).
|
||||
private final SymbolPath symbolPath;
|
||||
private final CategoryPath categoryPath;
|
||||
static final int classNameHash = Objects.hash(ProgramClassID.class.getName());
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param categoryPath the category path for the claass
|
||||
* @param symbolPath the symbol path for the class
|
||||
*/
|
||||
public ProgramClassID(CategoryPath categoryPath, SymbolPath symbolPath) {
|
||||
this.categoryPath = categoryPath;
|
||||
this.symbolPath = symbolPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the category path
|
||||
* @return the category path
|
||||
*/
|
||||
public CategoryPath getCategoryPath() {
|
||||
return categoryPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the symbol path
|
||||
* @return the symbol path
|
||||
*/
|
||||
public SymbolPath getSymbolPath() {
|
||||
return symbolPath;
|
||||
}
|
||||
|
||||
// Might want to do something with data type ID if resolved
|
||||
// long doIt(DataTypeManager dtm, DataType dt) {
|
||||
// int x = DataTypeUtilities.getConflictValue(dt);
|
||||
// long dataTypeID;
|
||||
// dataTypeID = dtm.getID(dt);
|
||||
// UniversalID uid = dt.getUniversalID();
|
||||
// return dataTypeID;
|
||||
// }
|
||||
|
||||
@Override
|
||||
public int getClassNameHash() {
|
||||
return classNameHash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s --- %s", categoryPath, symbolPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(ClassID o) {
|
||||
int ret;
|
||||
if (!(o instanceof ProgramClassID oID)) {
|
||||
ret = getClassNameHash() - o.getClassNameHash();
|
||||
if (ret != 0) {
|
||||
throw new AssertionError("Logic problem with compareTo");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
ret = symbolPath.compareTo(oID.symbolPath);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
return categoryPath.compareTo(oID.categoryPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(categoryPath, symbolPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
ProgramClassID other = (ProgramClassID) obj;
|
||||
return Objects.equals(categoryPath, other.categoryPath) &&
|
||||
Objects.equals(symbolPath, other.symbolPath);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,149 @@
|
||||
/* ###
|
||||
* 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.classtype;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.mem.Memory;
|
||||
import ghidra.program.model.mem.MemoryAccessException;
|
||||
|
||||
/**
|
||||
* Virtual Base Table from perspective of program with memory. Values are read from memory
|
||||
*/
|
||||
public class ProgramVirtualBaseTable extends VirtualBaseTable {
|
||||
|
||||
private Memory memory;
|
||||
private Address address;
|
||||
private int entrySize;
|
||||
private String mangledName; // remove?
|
||||
|
||||
private Boolean createdFromMemory = null;
|
||||
private Boolean createdFromCompiled = null;
|
||||
|
||||
private int numEntries = 0;
|
||||
|
||||
private int maxIndexSeen = -1;
|
||||
private Map<Integer, VBTableEntry> entriesByIndex = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param owner the class that owns the table
|
||||
* @param parentage the parentage of the base class(es) of the table
|
||||
* @param memory the program memory
|
||||
* @param address the address of the table
|
||||
* @param entrySize the size for each table entry
|
||||
* @param ctm the class type manager
|
||||
* @param mangledName the mangled name of the table
|
||||
*/
|
||||
public ProgramVirtualBaseTable(ClassID owner, List<ClassID> parentage, Memory memory,
|
||||
Address address, int entrySize, ClassTypeManager ctm, String mangledName) {
|
||||
super(owner, parentage);
|
||||
if (entrySize != 4 && entrySize != 8) {
|
||||
throw new IllegalArgumentException("Invalid size (" + entrySize + "): must be 4 or 8.");
|
||||
}
|
||||
this.memory = memory;
|
||||
this.address = address;
|
||||
this.entrySize = entrySize;
|
||||
this.mangledName = mangledName;
|
||||
createdFromMemory = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the address of the table in program memory
|
||||
* @return the address
|
||||
*/
|
||||
public Address getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the mangled name
|
||||
* @return the mangled name
|
||||
*/
|
||||
String getMangledName() {
|
||||
return mangledName;
|
||||
}
|
||||
|
||||
/*
|
||||
* For the next method below... once we determine the number of virtual bases (virtual and
|
||||
* indirect virtual) for each class (from PDB or other), we can determine the number of
|
||||
* entries in each VBT. For a VBT for the main class, the number is equal... if for some
|
||||
* parentage, then the number can reflect the number of the parent. TODO: can VBT overlay/extend one from parent????????????????????????????????????????????
|
||||
*/
|
||||
/**
|
||||
* TBD: need to determine table size to do this. Might want to place a symbol (diff method?).
|
||||
*/
|
||||
void placeTableDataType(int numEntries) {
|
||||
|
||||
}
|
||||
|
||||
int getMaxIndex() {
|
||||
return maxIndexSeen;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getBaseOffset(int index) throws PdbException {
|
||||
Address entryAddress = address.add(index * entrySize);
|
||||
try {
|
||||
Long offset = (entrySize == 4) ? (long) memory.getInt(entryAddress)
|
||||
: memory.getLong(entryAddress);
|
||||
return offset;
|
||||
}
|
||||
catch (MemoryAccessException e) {
|
||||
throw new PdbException(
|
||||
"MemoryAccessException while trying to parse virtual base table entry at address: " +
|
||||
entryAddress);
|
||||
}
|
||||
finally {
|
||||
maxIndexSeen = Integer.max(maxIndexSeen, index);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClassID getBaseClassId(int index) throws PdbException {
|
||||
VBTableEntry entry = entriesByIndex.get(index);
|
||||
if (entry == null) {
|
||||
throw new PdbException("No entry in Virtual Base Table for index: " + index);
|
||||
}
|
||||
maxIndexSeen = Integer.max(maxIndexSeen, index);
|
||||
return entry.getClassId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public VBTableEntry getBase(int index) throws PdbException {
|
||||
VBTableEntry entry = entriesByIndex.get(index);
|
||||
if (entry == null) {
|
||||
throw new PdbException("No entry in Virtual Base Table for index: " + index);
|
||||
}
|
||||
maxIndexSeen = Integer.max(maxIndexSeen, index);
|
||||
return entry;
|
||||
}
|
||||
|
||||
// Need to decide if we want to allow this to overwrite existing entry.
|
||||
public void setBaseClassId(int index, ClassID baseId) throws PdbException {
|
||||
VBTableEntry entry = entriesByIndex.get(index);
|
||||
if (entry != null) {
|
||||
throw new PdbException(
|
||||
"Entry already exists in Virtual Base Table for index: " + index);
|
||||
}
|
||||
entry = new VirtualBaseTableEntry(baseId);
|
||||
entriesByIndex.put(index, entry);
|
||||
maxIndexSeen = Integer.max(maxIndexSeen, index); // do we want this here with a "set" method?
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
/* ###
|
||||
* 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.classtype;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.app.util.SymbolPath;
|
||||
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.mem.Memory;
|
||||
|
||||
/**
|
||||
* Manages virtual function table lookups.
|
||||
*/
|
||||
public class ProgramVirtualFunctionTable extends VirtualFunctionTable {
|
||||
|
||||
private Memory memory;
|
||||
private Address address;
|
||||
private int defaultEntrySize; // Might go away, as would constructor param
|
||||
private String mangledName;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param owner the owner class
|
||||
* @param parentage the parentage for the table
|
||||
* @param memory the program memory
|
||||
* @param address the address of the table in memory
|
||||
* @param defaultEntrySize the default entry size
|
||||
* @param mangledName the mangled name for the table
|
||||
*/
|
||||
public ProgramVirtualFunctionTable(ClassID owner, List<ClassID> parentage, Memory memory,
|
||||
Address address, int defaultEntrySize, String mangledName) {
|
||||
super(owner, parentage);
|
||||
if (defaultEntrySize != 4 && defaultEntrySize != 8) {
|
||||
throw new IllegalArgumentException(
|
||||
"Invalid size (" + defaultEntrySize + "): must be 4 or 8.");
|
||||
}
|
||||
this.memory = memory;
|
||||
this.address = address;
|
||||
this.defaultEntrySize = defaultEntrySize;
|
||||
this.mangledName = mangledName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the address of the table in program memory
|
||||
* @return the address
|
||||
*/
|
||||
public Address getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the mangled name
|
||||
* @return the mangled name
|
||||
*/
|
||||
String getMangledName() {
|
||||
return mangledName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address getAddress(int ordinal) throws PdbException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SymbolPath getPath(int index) throws PdbException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
/* ###
|
||||
* 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.classtype;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public enum Property {
|
||||
UNKNOWN("INVALID_PROPERTY", -1),
|
||||
BLANK("", 0), // means non-virtual, non-static, non-friend
|
||||
VIRTUAL("virtual", 1),
|
||||
STATIC("static", 2),
|
||||
FRIEND("friend", 3);
|
||||
// Also consider <intro>, <pure>, <intro,pure>. See MSFT.
|
||||
|
||||
private static final Map<Integer, Property> BY_VALUE = new HashMap<>();
|
||||
static {
|
||||
for (Property val : values()) {
|
||||
BY_VALUE.put(val.value, val);
|
||||
}
|
||||
}
|
||||
private final String label;
|
||||
private final int value;
|
||||
|
||||
public String getString() {
|
||||
return label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public static Property fromValue(int val) {
|
||||
return BY_VALUE.getOrDefault(val, UNKNOWN);
|
||||
}
|
||||
|
||||
private Property(String label, int value) {
|
||||
this.label = label;
|
||||
this.value = value;
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
/* ###
|
||||
* 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.classtype;
|
||||
|
||||
/**
|
||||
* Compiler-generated virtual base table
|
||||
*/
|
||||
public interface VBTable {
|
||||
// empty for now
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
/* ###
|
||||
* 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.classtype;
|
||||
|
||||
/**
|
||||
* Represents an entry within a virtual base table
|
||||
*/
|
||||
public interface VBTableEntry {
|
||||
|
||||
/**
|
||||
* Sets the entry offset value
|
||||
* @param offset the offset
|
||||
*/
|
||||
public void setOffset(long offset);
|
||||
|
||||
/**
|
||||
* Gets the entry offset value
|
||||
* @return the offset value
|
||||
*/
|
||||
public Long getOffset();
|
||||
|
||||
/**
|
||||
* Sets the entry class ID
|
||||
* @param baseId the ID
|
||||
*/
|
||||
public void setClassId(ClassID baseId);
|
||||
|
||||
/**
|
||||
* Gets the entry class ID
|
||||
* @return the ID
|
||||
*/
|
||||
public ClassID getClassId();
|
||||
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
/* ###
|
||||
* 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.classtype;
|
||||
|
||||
/**
|
||||
* Compiler-generated virtual function table
|
||||
*/
|
||||
public interface VFTable {
|
||||
// empty for now
|
||||
}
|
@ -0,0 +1,111 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.util.pdb.classtype;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
|
||||
|
||||
/**
|
||||
* Abstract class for virtual base tables
|
||||
*/
|
||||
public abstract class VirtualBaseTable implements VBTable {
|
||||
protected ClassID owner; // Does this belong here in this abstract class?
|
||||
protected List<ClassID> parentage; // Not sure this belongs in this abstract class
|
||||
|
||||
/**
|
||||
* Virtual Base Table for a base (parent) class within an owner class. The owner and parent
|
||||
* class can be null if not known, but methods are offered to fill them in if/when this
|
||||
* information becomes available
|
||||
* @param owner class that owns this VBT (can own more than one). Can be null
|
||||
* @param parentage class of parents for which this VBT is used (when "this" cast to parent).
|
||||
*/
|
||||
public VirtualBaseTable(ClassID owner, List<ClassID> parentage) {
|
||||
this.owner = owner;
|
||||
this.parentage = new ArrayList<>(parentage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the offset of the base class in the layout class pertaining whose entry in the
|
||||
* VBTable is at the index location
|
||||
* VBTable
|
||||
* @param index the index in the table
|
||||
* @return the offset in the layout class
|
||||
* @throws PdbException if problem retrieving the offset value
|
||||
*/
|
||||
public abstract Long getBaseOffset(int index) throws PdbException;
|
||||
|
||||
/**
|
||||
* Returns the ClassID of the base class in the layout class pertaining whose entry in the
|
||||
* VBTable is at the index location
|
||||
* @param index the index in the table
|
||||
* @return the ClassID of the base class
|
||||
* @throws PdbException if an entry does not exist for the index
|
||||
*/
|
||||
public abstract ClassID getBaseClassId(int index) throws PdbException;
|
||||
|
||||
/**
|
||||
* Returns a {@link VBTableEntry} for the base class in the layout class pertaining whose
|
||||
* entry in the VBTable is at the index location
|
||||
* @param index the index in the table
|
||||
* @return the ClassID of the base class
|
||||
* @throws PdbException if an entry does not exist for the index
|
||||
*/
|
||||
public abstract VBTableEntry getBase(int index) throws PdbException;
|
||||
|
||||
/**
|
||||
* Returns the owning class
|
||||
* @return the owner
|
||||
*/
|
||||
public ClassID getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parentage of the table
|
||||
* @return the parentage
|
||||
*/
|
||||
public List<ClassID> getParentage() {
|
||||
return parentage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the owner of the table
|
||||
* @param ownerArg the class to set as owner
|
||||
*/
|
||||
public void setOwner(ClassID ownerArg) {
|
||||
owner = ownerArg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the parentage of the parentage for the table
|
||||
* @param parentage the parentage
|
||||
*/
|
||||
public void setParentage(List<ClassID> parentage) {
|
||||
this.parentage = parentage;
|
||||
}
|
||||
|
||||
void emit(StringBuilder builder) {
|
||||
builder.append("VBT for the following parentage within: " + owner);
|
||||
builder.append("\n");
|
||||
for (ClassID id : parentage) {
|
||||
builder.append(" " + id);
|
||||
builder.append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
/* ###
|
||||
* 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.classtype;
|
||||
|
||||
/**
|
||||
* Represents the Entry for a Virtual Base Table
|
||||
*/
|
||||
public class VirtualBaseTableEntry implements VBTableEntry {
|
||||
private Long offset;
|
||||
private ClassID baseId;
|
||||
|
||||
// Re-evaluate which constructors and setters we need
|
||||
|
||||
VirtualBaseTableEntry(long offset) {
|
||||
this(offset, null);
|
||||
}
|
||||
|
||||
VirtualBaseTableEntry(ClassID baseId) {
|
||||
this(null, baseId);
|
||||
}
|
||||
|
||||
VirtualBaseTableEntry(Long offset, ClassID baseId) {
|
||||
this.offset = offset;
|
||||
this.baseId = baseId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOffset(long offset) {
|
||||
this.offset = offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getOffset() {
|
||||
return offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setClassId(ClassID baseId) {
|
||||
this.baseId = baseId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClassID getClassId() {
|
||||
return baseId;
|
||||
}
|
||||
}
|
@ -0,0 +1,98 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.util.pdb.classtype;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.app.util.SymbolPath;
|
||||
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
|
||||
import ghidra.program.model.address.Address;
|
||||
|
||||
public abstract class VirtualFunctionTable implements VFTable {
|
||||
|
||||
protected ClassID owner;
|
||||
protected List<ClassID> parentage;
|
||||
|
||||
/**
|
||||
* Virtual Function Table for a base (parent) class within an owner class. The owner and parent
|
||||
* class can be null if not known, but methods are offered to fill them in if/when this
|
||||
* information becomes available
|
||||
* @param owner class that owns this VBT (can own more than one). Can be null
|
||||
* @param parentage parentage for which this VBT is used. Can be null
|
||||
*/
|
||||
VirtualFunctionTable(ClassID owner, List<ClassID> parentage) {
|
||||
this.owner = owner;
|
||||
this.parentage = new ArrayList<>(parentage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the address value at the index in the table
|
||||
* @param index the index
|
||||
* @return the address
|
||||
* @throws PdbException upon error retrieving the value
|
||||
*/
|
||||
public abstract Address getAddress(int index) throws PdbException;
|
||||
|
||||
/**
|
||||
* Returns the symbol path of the function at the index in the table
|
||||
* @param index the index
|
||||
* @return the symbol path
|
||||
* @throws PdbException upon error retrieving the value
|
||||
*/
|
||||
public abstract SymbolPath getPath(int index) throws PdbException;
|
||||
|
||||
/**
|
||||
* Returns the owning class
|
||||
* @return the owner
|
||||
*/
|
||||
public ClassID getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parentage of the table
|
||||
* @return the parentage
|
||||
*/
|
||||
public List<ClassID> getParentage() {
|
||||
return parentage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the owner of the table
|
||||
* @param ownerArg the class to set as owner
|
||||
*/
|
||||
public void setOwner(ClassID ownerArg) {
|
||||
owner = ownerArg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the parentage of the parentage for the table
|
||||
* @param parentage the parentage
|
||||
*/
|
||||
public void setParentage(List<ClassID> parentage) {
|
||||
this.parentage = parentage;
|
||||
}
|
||||
|
||||
void emit(StringBuilder builder) {
|
||||
builder.append("VBT for the following classes within: " + owner);
|
||||
builder.append("\n");
|
||||
for (ClassID id : parentage) {
|
||||
builder.append(" " + id);
|
||||
builder.append("\n");
|
||||
}
|
||||
}
|
||||
}
|
@ -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.classtype;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.data.PointerDataType;
|
||||
|
||||
/**
|
||||
* Manages virtual base table lookup for PDB classes.
|
||||
*/
|
||||
public class VxtManager {
|
||||
|
||||
protected ClassTypeManager ctm;
|
||||
|
||||
protected Map<Address, VirtualBaseTable> vbtByAddress;
|
||||
protected Map<Address, VirtualFunctionTable> vftByAddress;
|
||||
|
||||
/**
|
||||
* Virtual Base Table Lookup Manager
|
||||
* @param ctm class type manager
|
||||
*/
|
||||
public VxtManager(ClassTypeManager ctm) {
|
||||
this.ctm = ctm;
|
||||
vbtByAddress = new HashMap<>();
|
||||
vftByAddress = new HashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default VBT pointer type for the program
|
||||
* @return the pointer type
|
||||
*/
|
||||
public PointerDataType getDefaultVbtPtr() {
|
||||
return ctm.getDefaultVbtPtr();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default VFT pointer type for the program
|
||||
* @return the pointer type
|
||||
*/
|
||||
public PointerDataType getDefaultVftPtr() {
|
||||
return ctm.getDefaultVftPtr();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the VBT located at the address
|
||||
* @param address the address
|
||||
* @return the VBT or null if a table is not found
|
||||
*/
|
||||
public VirtualBaseTable getVbt(Address address) {
|
||||
return vbtByAddress.get(address);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the VFT located at the address
|
||||
* @param address the address
|
||||
* @return the VFT or null if a table is not found
|
||||
*/
|
||||
public VirtualFunctionTable getVft(Address address) {
|
||||
return vftByAddress.get(address);
|
||||
}
|
||||
|
||||
}
|
@ -1,238 +0,0 @@
|
||||
/* ###
|
||||
* 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.type.ClassFieldMsAttributes;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class ClassFieldAttributes {
|
||||
|
||||
private static final Map<ClassFieldAttributes, ClassFieldAttributes> map = new HashMap<>();
|
||||
|
||||
// These initializations use the map above, so it must be initialized first
|
||||
public static final ClassFieldAttributes UNKNOWN = get(Access.UNKNOWN, Property.UNKNOWN);
|
||||
public static final ClassFieldAttributes BLANK = get(Access.BLANK, Property.BLANK);
|
||||
|
||||
private final Access access;
|
||||
private final Property property;
|
||||
|
||||
public static ClassFieldAttributes get(Access access, Property property) {
|
||||
ClassFieldAttributes key = new ClassFieldAttributes(access, property);
|
||||
ClassFieldAttributes cfa = map.putIfAbsent(key, key);
|
||||
return (cfa != null) ? cfa : key;
|
||||
}
|
||||
|
||||
static ClassFieldAttributes convert(ClassFieldMsAttributes msAtts, Access defaultAccess) {
|
||||
Access myAccess = switch (msAtts.getAccess()) {
|
||||
case PUBLIC -> Access.PUBLIC;
|
||||
case PROTECTED -> Access.PROTECTED;
|
||||
case PRIVATE -> Access.PRIVATE;
|
||||
case BLANK -> defaultAccess;
|
||||
default -> Access.UNKNOWN;
|
||||
};
|
||||
Property myProperty = switch (msAtts.getProperty()) {
|
||||
case VIRTUAL -> Property.VIRTUAL;
|
||||
case STATIC -> Property.STATIC;
|
||||
case FRIEND -> Property.FRIEND;
|
||||
case BLANK -> Property.BLANK;
|
||||
default -> Property.UNKNOWN;
|
||||
};
|
||||
return get(myAccess, myProperty);
|
||||
}
|
||||
|
||||
private ClassFieldAttributes(Access access, Property property) {
|
||||
this.access = access;
|
||||
this.property = property;
|
||||
}
|
||||
|
||||
Access getAccess() {
|
||||
return access;
|
||||
}
|
||||
|
||||
Property getProperty() {
|
||||
return property;
|
||||
}
|
||||
|
||||
void emit(StringBuilder builder) {
|
||||
StringBuilder myBuilder = new StringBuilder();
|
||||
if (access.getValue() > Access.BLANK.getValue()) {
|
||||
myBuilder.append(access);
|
||||
myBuilder.append(' ');
|
||||
}
|
||||
if (property.getValue() > Property.BLANK.getValue()) {
|
||||
myBuilder.append(property);
|
||||
myBuilder.append(' ');
|
||||
}
|
||||
builder.append(myBuilder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
emit(builder);
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(access, property);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
ClassFieldAttributes other = (ClassFieldAttributes) obj;
|
||||
return access == other.access && property == other.property;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------
|
||||
// TODO: Consider expanding these beyond C++.
|
||||
// See https://en.wikipedia.org/wiki/Access_modifiers
|
||||
// These could then be:
|
||||
// UNKNOWN("UNKNOWN_ACCESS ", -1),
|
||||
// OPEN("open", 0),
|
||||
// PUBLIC("internal", 1),
|
||||
// INTERNAL("internal", 2),
|
||||
// PACKAGE("package", 3),
|
||||
// PROTECTED("protected", 4),
|
||||
// PROTECTED_INTERNAL("protected internal", 5),
|
||||
// PRIVATE_PROTECTED("private protected", 6),
|
||||
// FILE("file", 7),
|
||||
// FILE_PRIVATE("fileprivate", 8),
|
||||
// PRIVATE("private", 9);
|
||||
static enum Access {
|
||||
UNKNOWN("UNKNOWN_ACCESS", -1),
|
||||
BLANK("", 0), // eliminated 20230524... using defaultAccess on some methods. Could renumber
|
||||
PUBLIC("public", 1),
|
||||
PROTECTED("protected", 2),
|
||||
PRIVATE("private", 3);
|
||||
|
||||
private static final Map<Integer, Access> BY_VALUE = new HashMap<>();
|
||||
static {
|
||||
for (Access val : values()) {
|
||||
BY_VALUE.put(val.value, val);
|
||||
}
|
||||
}
|
||||
private final String label;
|
||||
private final int value;
|
||||
|
||||
public String getString() {
|
||||
return label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public static Access fromValue(int val) {
|
||||
return BY_VALUE.getOrDefault(val, UNKNOWN);
|
||||
}
|
||||
|
||||
private Access(String label, int value) {
|
||||
this.label = label;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge two Access values, leaning toward more restrictive. UNKNOWN is only returned
|
||||
* if both are UNKNOWN.
|
||||
* @param other value to merge
|
||||
* @return the merged value
|
||||
*/
|
||||
public Access mergeRestrictive(Access other) {
|
||||
// No need to test for UNKNOWN as its value is on the permissive end.
|
||||
if (this.value > other.value) {
|
||||
return this;
|
||||
}
|
||||
return other;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge two Access values, leaning toward more permissive. UNKNOWN is only returned
|
||||
* if both are UNKNOWN.
|
||||
* @param other value to merge
|
||||
* @return the merged value
|
||||
*/
|
||||
public Access mergePermissive(Access other) {
|
||||
if (this.value < other.value) {
|
||||
// Only need special test for UNKNOWN here, as its value is on the permissive end.
|
||||
if (this == UNKNOWN) {
|
||||
return other;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
return other;
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------
|
||||
static enum Property {
|
||||
UNKNOWN("INVALID_PROPERTY", -1),
|
||||
BLANK("", 0), // means non-virtual, non-static, non-friend
|
||||
VIRTUAL("virtual", 1),
|
||||
STATIC("static", 2),
|
||||
FRIEND("friend", 3);
|
||||
// Also consider <intro>, <pure>, <intro,pure>. See MSFT.
|
||||
|
||||
private static final Map<Integer, Property> BY_VALUE = new HashMap<>();
|
||||
static {
|
||||
for (Property val : values()) {
|
||||
BY_VALUE.put(val.value, val);
|
||||
}
|
||||
}
|
||||
private final String label;
|
||||
private final int value;
|
||||
|
||||
public String getString() {
|
||||
return label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public static Property fromValue(int val) {
|
||||
return BY_VALUE.getOrDefault(val, UNKNOWN);
|
||||
}
|
||||
|
||||
private Property(String label, int value) {
|
||||
this.label = label;
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
}
|
@ -23,7 +23,8 @@ import ghidra.app.util.SymbolPath;
|
||||
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.app.util.pdb.pdbapplicator.ClassFieldAttributes.Access;
|
||||
import ghidra.app.util.pdb.classtype.Access;
|
||||
import ghidra.app.util.pdb.classtype.ClassFieldAttributes;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.AssertException;
|
||||
@ -82,19 +83,25 @@ public class CompositeTypeApplier extends AbstractComplexTypeApplier {
|
||||
myApplicator.predefineClass(fixedSymbolPath);
|
||||
myComposite = new StructureDataType(categoryPath, fixedSymbolPath.getName(), size,
|
||||
myApplicator.getDataTypeManager());
|
||||
myClassType = new CppCompositeType(fixedSymbolPath, myComposite, mangledName);
|
||||
myClassType =
|
||||
new CppCompositeType(myApplicator.getRootPdbCategory(), fixedSymbolPath,
|
||||
myComposite, mangledName);
|
||||
myClassType.setClass();
|
||||
}
|
||||
else if (compositeMsType instanceof AbstractStructureMsType) {
|
||||
myComposite = new StructureDataType(categoryPath, fixedSymbolPath.getName(), size,
|
||||
myApplicator.getDataTypeManager());
|
||||
myClassType = new CppCompositeType(fixedSymbolPath, myComposite, mangledName);
|
||||
myClassType =
|
||||
new CppCompositeType(myApplicator.getRootPdbCategory(), fixedSymbolPath,
|
||||
myComposite, mangledName);
|
||||
myClassType.setStruct();
|
||||
}
|
||||
else if (compositeMsType instanceof AbstractUnionMsType) {
|
||||
myComposite = new UnionDataType(categoryPath, fixedSymbolPath.getName(),
|
||||
myApplicator.getDataTypeManager());
|
||||
myClassType = new CppCompositeType(fixedSymbolPath, myComposite, mangledName);
|
||||
myClassType =
|
||||
new CppCompositeType(myApplicator.getRootPdbCategory(), fixedSymbolPath,
|
||||
myComposite, mangledName);
|
||||
myClassType.setUnion();
|
||||
}
|
||||
else { // InterfaceMsType
|
||||
@ -183,7 +190,7 @@ public class CompositeTypeApplier extends AbstractComplexTypeApplier {
|
||||
// we do it here. Set breakpoint here to investigate.
|
||||
}
|
||||
classType.createLayout(applicator.getPdbApplicatorOptions().getCompositeLayout(),
|
||||
applicator.getVbtManager(), applicator.getCancelOnlyWrappingMonitor());
|
||||
applicator.getVxtManager(), applicator.getCancelOnlyWrappingMonitor());
|
||||
}
|
||||
|
||||
//==============================================================================================
|
||||
@ -224,9 +231,8 @@ public class CompositeTypeApplier extends AbstractComplexTypeApplier {
|
||||
throws PdbException, CancelledException {
|
||||
|
||||
AbstractCompositeMsType cType = (AbstractCompositeMsType) type;
|
||||
ClassFieldAttributes.Access defaultAccess =
|
||||
(type instanceof AbstractClassMsType) ? ClassFieldAttributes.Access.PRIVATE
|
||||
: ClassFieldAttributes.Access.PUBLIC;
|
||||
Access defaultAccess = (type instanceof AbstractClassMsType) ? Access.PRIVATE
|
||||
: Access.PUBLIC;
|
||||
|
||||
for (AbstractMsType baseType : msBases) {
|
||||
applicator.checkCancelled();
|
||||
@ -334,9 +340,8 @@ public class CompositeTypeApplier extends AbstractComplexTypeApplier {
|
||||
private void addMembers(Composite composite, CppCompositeType myClassType,
|
||||
List<AbstractMemberMsType> msMembers, AbstractCompositeMsType type,
|
||||
List<DefaultPdbUniversalMember> myMembers) throws CancelledException, PdbException {
|
||||
ClassFieldAttributes.Access defaultAccess =
|
||||
(type instanceof AbstractClassMsType) ? ClassFieldAttributes.Access.PRIVATE
|
||||
: ClassFieldAttributes.Access.PUBLIC;
|
||||
Access defaultAccess =
|
||||
(type instanceof AbstractClassMsType) ? Access.PRIVATE : Access.PUBLIC;
|
||||
for (int index = 0; index < msMembers.size(); index++) {
|
||||
applicator.checkCancelled();
|
||||
AbstractMemberMsType memberType = msMembers.get(index);
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -24,7 +24,7 @@ import ghidra.app.util.SymbolPath;
|
||||
import ghidra.app.util.bin.format.pdb.*;
|
||||
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
|
||||
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbLog;
|
||||
import ghidra.app.util.pdb.pdbapplicator.PdbVbtManager.PdbVirtualBaseTable;
|
||||
import ghidra.app.util.pdb.classtype.*;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.AssertException;
|
||||
@ -45,10 +45,11 @@ public class CppCompositeType {
|
||||
private List<Member> layoutMembers;
|
||||
private List<Member> layoutVftPtrMembers;
|
||||
private boolean isFinal;
|
||||
private Type type;
|
||||
private ClassKey classKey;
|
||||
private String className; // String for now.
|
||||
private String mangledName;
|
||||
private int size;
|
||||
private CategoryPath baseCategoryPath;
|
||||
private SymbolPath symbolPath;
|
||||
private Composite composite;
|
||||
private CategoryPath categoryPath;
|
||||
@ -80,7 +81,8 @@ public class CppCompositeType {
|
||||
private Map<Integer, PlaceholderVirtualBaseTable> placeholderVirtualBaseTables;
|
||||
|
||||
//----------------------------------------------------------------------------------------------
|
||||
public CppCompositeType(SymbolPath symbolPath, Composite composite, String mangledName) {
|
||||
public CppCompositeType(CategoryPath baseCategoryPath, SymbolPath symbolPath,
|
||||
Composite composite, String mangledName) {
|
||||
Objects.requireNonNull(symbolPath, "symbolPath may not be null");
|
||||
Objects.requireNonNull(composite, "composite may not be null");
|
||||
syntacticBaseClasses = new ArrayList<>();
|
||||
@ -92,7 +94,8 @@ public class CppCompositeType {
|
||||
layoutVftPtrMembers = new ArrayList<>();
|
||||
|
||||
isFinal = false;
|
||||
type = Type.UNKNOWN;
|
||||
classKey = ClassKey.UNKNOWN;
|
||||
this.baseCategoryPath = baseCategoryPath;
|
||||
this.symbolPath = symbolPath;
|
||||
this.composite = composite;
|
||||
placeholderVirtualBaseTables = new HashMap<>();
|
||||
@ -100,47 +103,51 @@ public class CppCompositeType {
|
||||
this.mangledName = mangledName;
|
||||
}
|
||||
|
||||
public static CppClassType createCppClassType(SymbolPath symbolPath, Composite composite,
|
||||
String mangledName) {
|
||||
return new CppClassType(symbolPath, composite, mangledName);
|
||||
public static CppClassType createCppClassType(CategoryPath baseCategoryPath,
|
||||
SymbolPath symbolPath, Composite composite, String mangledName) {
|
||||
return new CppClassType(baseCategoryPath, symbolPath, composite, mangledName);
|
||||
}
|
||||
|
||||
public static CppClassType createCppClassType(SymbolPath symbolPath, Composite composite,
|
||||
String name, String mangledName, int size) {
|
||||
CppClassType cppType = new CppClassType(symbolPath, composite, mangledName);
|
||||
public static CppClassType createCppClassType(CategoryPath baseCategoryPath,
|
||||
SymbolPath symbolPath, Composite composite, String name, String mangledName, int size) {
|
||||
CppClassType cppType =
|
||||
new CppClassType(baseCategoryPath, symbolPath, composite, mangledName);
|
||||
cppType.setName(name);
|
||||
cppType.setSize(size);
|
||||
return cppType;
|
||||
}
|
||||
|
||||
public static CppStructType createCppStructType(SymbolPath symbolPath, Composite composite,
|
||||
String mangledName) {
|
||||
return new CppStructType(symbolPath, composite, mangledName);
|
||||
public static CppStructType createCppStructType(CategoryPath baseCategoryPath,
|
||||
SymbolPath symbolPath, Composite composite, String mangledName) {
|
||||
return new CppStructType(baseCategoryPath, symbolPath, composite, mangledName);
|
||||
}
|
||||
|
||||
public static CppStructType createCppStructType(SymbolPath symbolPath, Composite composite,
|
||||
String name, String mangledName, int size) {
|
||||
CppStructType cppType = new CppStructType(symbolPath, composite, mangledName);
|
||||
public static CppStructType createCppStructType(CategoryPath baseCategoryPath,
|
||||
SymbolPath symbolPath, Composite composite, String name, String mangledName, int size) {
|
||||
CppStructType cppType =
|
||||
new CppStructType(baseCategoryPath, symbolPath, composite, mangledName);
|
||||
cppType.setName(name);
|
||||
cppType.setSize(size);
|
||||
return cppType;
|
||||
}
|
||||
|
||||
private static class CppClassType extends CppCompositeType {
|
||||
private CppClassType(SymbolPath symbolPath, Composite composite, String mangledName) {
|
||||
super(symbolPath, composite, mangledName);
|
||||
private CppClassType(CategoryPath baseCategoryPath, SymbolPath symbolPath,
|
||||
Composite composite, String mangledName) {
|
||||
super(baseCategoryPath, symbolPath, composite, mangledName);
|
||||
setClass();
|
||||
}
|
||||
}
|
||||
|
||||
private static class CppStructType extends CppCompositeType {
|
||||
private CppStructType(SymbolPath symbolPath, Composite composite, String mangledName) {
|
||||
super(symbolPath, composite, mangledName);
|
||||
private CppStructType(CategoryPath baseCategoryPath, SymbolPath symbolPath,
|
||||
Composite composite, String mangledName) {
|
||||
super(composite.getCategoryPath(), symbolPath, composite, mangledName);
|
||||
setStruct();
|
||||
}
|
||||
}
|
||||
|
||||
static boolean validateMangledCompositeName(String mangledCompositeTypeName, Type type) {
|
||||
static boolean validateMangledCompositeName(String mangledCompositeTypeName, ClassKey type) {
|
||||
if (mangledCompositeTypeName == null) {
|
||||
return false;
|
||||
}
|
||||
@ -155,17 +162,20 @@ public class CppCompositeType {
|
||||
}
|
||||
switch (mangledCompositeTypeName.charAt(3)) {
|
||||
case 'T':
|
||||
if ((type.compareTo(Type.UNION) != 0) && (type.compareTo(Type.UNKNOWN) != 0)) {
|
||||
if ((type.compareTo(ClassKey.UNION) != 0) &&
|
||||
(type.compareTo(ClassKey.UNKNOWN) != 0)) {
|
||||
PdbLog.message("Warning: Mismatched complex type 'T' for " + type);
|
||||
}
|
||||
break;
|
||||
case 'U':
|
||||
if ((type.compareTo(Type.STRUCT) != 0) && (type.compareTo(Type.UNKNOWN) != 0)) {
|
||||
if ((type.compareTo(ClassKey.STRUCT) != 0) &&
|
||||
(type.compareTo(ClassKey.UNKNOWN) != 0)) {
|
||||
PdbLog.message("Warning: Mismatched complex type 'U' for " + type);
|
||||
}
|
||||
break;
|
||||
case 'V':
|
||||
if ((type.compareTo(Type.CLASS) != 0) && (type.compareTo(Type.UNKNOWN) != 0)) {
|
||||
if ((type.compareTo(ClassKey.CLASS) != 0) &&
|
||||
(type.compareTo(ClassKey.UNKNOWN) != 0)) {
|
||||
PdbLog.message("Warning: Mismatched complex type 'V' for " + type);
|
||||
}
|
||||
break;
|
||||
@ -200,7 +210,7 @@ public class CppCompositeType {
|
||||
return composite;
|
||||
}
|
||||
|
||||
private CategoryPath getCategoryPath() {
|
||||
public CategoryPath getCategoryPath() {
|
||||
return categoryPath;
|
||||
}
|
||||
|
||||
@ -213,20 +223,20 @@ public class CppCompositeType {
|
||||
}
|
||||
|
||||
public void setClass() {
|
||||
type = Type.CLASS;
|
||||
classKey = ClassKey.CLASS;
|
||||
}
|
||||
|
||||
public void setStruct() {
|
||||
type = Type.STRUCT;
|
||||
classKey = ClassKey.STRUCT;
|
||||
}
|
||||
|
||||
public void setUnion() {
|
||||
type = Type.UNION;
|
||||
classKey = ClassKey.UNION;
|
||||
}
|
||||
|
||||
// not sure if user can see Type when returned.
|
||||
public Type getType() {
|
||||
return type;
|
||||
public ClassKey getType() {
|
||||
return classKey;
|
||||
}
|
||||
|
||||
public void setName(String className) {
|
||||
@ -237,6 +247,10 @@ public class CppCompositeType {
|
||||
return className;
|
||||
}
|
||||
|
||||
public DataTypePath getDataTypePath() {
|
||||
return composite.getDataTypePath();
|
||||
}
|
||||
|
||||
public void setMangledName(String mangledName) {
|
||||
this.mangledName = mangledName;
|
||||
}
|
||||
@ -563,7 +577,7 @@ public class CppCompositeType {
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append(type);
|
||||
builder.append(classKey);
|
||||
builder.append(className);
|
||||
if (isFinal) {
|
||||
builder.append(" final");
|
||||
@ -615,7 +629,7 @@ public class CppCompositeType {
|
||||
|
||||
//----------------------------------------------------------------------------------------------
|
||||
//----------------------------------------------------------------------------------------------
|
||||
public void createLayoutFromSyntacticDescription(VbtManager vbtManager, TaskMonitor monitor) {
|
||||
public void createLayoutFromSyntacticDescription(VxtManager vxtManager, TaskMonitor monitor) {
|
||||
for (SyntacticBaseClass base : syntacticBaseClasses) {
|
||||
if (base instanceof DirectSyntacticBaseClass) {
|
||||
|
||||
@ -628,21 +642,21 @@ public class CppCompositeType {
|
||||
|
||||
//----------------------------------------------------------------------------------------------
|
||||
//----------------------------------------------------------------------------------------------
|
||||
public void createLayout(ObjectOrientedClassLayout layoutOptions, VbtManager vbtManager,
|
||||
public void createLayout(ObjectOrientedClassLayout layoutOptions, VxtManager vxtManager,
|
||||
TaskMonitor monitor) throws PdbException, CancelledException {
|
||||
if (vbtManager instanceof PdbVbtManager) { // Information from PDB/program symbols
|
||||
if (vxtManager instanceof MsftVxtManager) { // Information from PDB/program symbols
|
||||
// TODO: both same for now
|
||||
//doSpeculativeLayout(vbtManager, monitor);
|
||||
createVbtBasedLayout(layoutOptions, vbtManager, monitor);
|
||||
//doSpeculativeLayout(vxtManager, monitor);
|
||||
createVbtBasedLayout(layoutOptions, vxtManager, monitor);
|
||||
}
|
||||
else {
|
||||
createSpeculativeLayout(layoutOptions, vbtManager, monitor);
|
||||
createSpeculativeLayout(layoutOptions, vxtManager, monitor);
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------
|
||||
//----------------------------------------------------------------------------------------------
|
||||
public void createVbtBasedLayout(ObjectOrientedClassLayout layoutOptions, VbtManager vbtManager,
|
||||
public void createVbtBasedLayout(ObjectOrientedClassLayout layoutOptions, VxtManager vxtManager,
|
||||
TaskMonitor monitor) throws PdbException, CancelledException {
|
||||
CategoryPath cn;
|
||||
hasDirect = false;
|
||||
@ -667,7 +681,7 @@ public class CppCompositeType {
|
||||
|
||||
// TODO: consider moving down below next line.
|
||||
boolean allVbtFound =
|
||||
reconcileVirtualBaseTables(composite.getDataTypeManager(), vbtManager);
|
||||
reconcileVirtualBaseTables(composite.getDataTypeManager(), vxtManager);
|
||||
|
||||
addLayoutPdbMembers(directClassPdbMembers, layoutMembers);
|
||||
insertVirtualFunctionTablePointers(directClassPdbMembers);
|
||||
@ -772,7 +786,7 @@ public class CppCompositeType {
|
||||
//----------------------------------------------------------------------------------------------
|
||||
//----------------------------------------------------------------------------------------------
|
||||
public void createSpeculativeLayout(ObjectOrientedClassLayout layoutOptions,
|
||||
VbtManager vbtManager, TaskMonitor monitor) throws PdbException, CancelledException {
|
||||
VxtManager vxtManager, TaskMonitor monitor) throws PdbException, CancelledException {
|
||||
// Speculative Layout uses recursion to try to know the order of members. However, MSFT
|
||||
// rearranges the order of the Base Class records such that they are not necessarily in
|
||||
// the order that the class was declared, and it seems that the member order follows the
|
||||
@ -807,7 +821,7 @@ public class CppCompositeType {
|
||||
|
||||
// TODO: consider moving down below next line.
|
||||
boolean allVbtFound =
|
||||
reconcileVirtualBaseTables(composite.getDataTypeManager(), vbtManager);
|
||||
reconcileVirtualBaseTables(composite.getDataTypeManager(), vxtManager);
|
||||
|
||||
addLayoutPdbMembers(directClassPdbMembers, layoutMembers);
|
||||
insertVirtualFunctionTablePointers(directClassPdbMembers);
|
||||
@ -943,7 +957,7 @@ public class CppCompositeType {
|
||||
return placeholderVirtualBaseTables;
|
||||
}
|
||||
|
||||
private boolean reconcileVirtualBaseTables(DataTypeManager dtm, VbtManager vbtManager)
|
||||
private boolean reconcileVirtualBaseTables(DataTypeManager dtm, VxtManager vxtManager)
|
||||
throws PdbException {
|
||||
if (placeholderVirtualBaseTables.size() > 1) {
|
||||
// study this.
|
||||
@ -957,14 +971,14 @@ public class CppCompositeType {
|
||||
if (!table.validateOffset()) {
|
||||
// TODO study this.
|
||||
}
|
||||
DataType vbptr = getVbptrDataType(dtm, vbtManager, table);
|
||||
DataType vbptr = getVbptrDataType(dtm, vxtManager, table);
|
||||
allVbtFound &=
|
||||
addOrUpdateVbtAndVbtptrMember(vbtManager, table, vbptr, vbtptrOffset, getName());
|
||||
addOrUpdateVbtAndVbtptrMember(vxtManager, table, vbptr, vbtptrOffset, getName());
|
||||
}
|
||||
return allVbtFound;
|
||||
}
|
||||
|
||||
private DataType getVbptrDataType(DataTypeManager dtm, VbtManager vbtManager,
|
||||
private DataType getVbptrDataType(DataTypeManager dtm, VxtManager vxtManager,
|
||||
PlaceholderVirtualBaseTable table) {
|
||||
DataType vbptr = null;
|
||||
for (int index = 1; index < table.getMaxOffset(); index++) {
|
||||
@ -975,106 +989,67 @@ public class CppCompositeType {
|
||||
}
|
||||
}
|
||||
if (vbptr == null) {
|
||||
vbptr = vbtManager.getFallbackVbptr();
|
||||
vbptr = vxtManager.getDefaultVbtPtr();
|
||||
}
|
||||
return vbptr;
|
||||
}
|
||||
|
||||
private class CppCompositeAndMember {
|
||||
private CppCompositeType cppType;
|
||||
private Member member;
|
||||
private record CppParentageAndMember(List<CppCompositeType> parentage, Member member) {}
|
||||
|
||||
private CppCompositeAndMember(CppCompositeType cppType, Member member) {
|
||||
this.cppType = cppType;
|
||||
this.member = member;
|
||||
}
|
||||
|
||||
private CppCompositeType getComposite() {
|
||||
return cppType;
|
||||
}
|
||||
|
||||
private Member getMember() {
|
||||
return member;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean addOrUpdateVbtAndVbtptrMember(VbtManager vbtManager,
|
||||
private boolean addOrUpdateVbtAndVbtptrMember(VxtManager vxtManager,
|
||||
PlaceholderVirtualBaseTable table, DataType vbptr, int vbtptrOffset, String myClass)
|
||||
throws PdbException {
|
||||
|
||||
List<String> subMangled = new ArrayList<>();
|
||||
//subMangled.add(getMangledName());
|
||||
CppCompositeAndMember cAndM = findDirectBaseCompositeAndMember(this, 0, vbtptrOffset);
|
||||
if (cAndM == null) {
|
||||
// TODO: if we want to match vbtables with the pointers in *virtual* base classes, we are
|
||||
// not currently doing the work... we are only looking for direct bases, as they are
|
||||
// what dictate the placement for our current class (though cross-checks could be done
|
||||
// with all vbtables (at some point))
|
||||
|
||||
List<CppCompositeType> parentage = new ArrayList<>();
|
||||
|
||||
CppParentageAndMember cAndP = findDirectBaseParentageAndMember(this, 0, vbtptrOffset);
|
||||
|
||||
if (cAndP == null) {
|
||||
insertMember("{vbptr}", vbptr, false, vbtptrOffset, "{vbptr} for " + myClass);
|
||||
}
|
||||
else if (!"{vbptr}".equals(cAndM.getMember().getName())) {
|
||||
else if (!"{vbptr}".equals(cAndP.member().getName())) {
|
||||
String message = "PDB: Collision of non-{vbptr}.";
|
||||
PdbLog.message(message);
|
||||
Msg.info(this, message);
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
CppCompositeType compositeThatContainsMember = cAndM.getComposite();
|
||||
String mangled = compositeThatContainsMember.getMangledName();
|
||||
subMangled.add(mangled);
|
||||
parentage = cAndP.parentage();
|
||||
}
|
||||
if (!(vbtManager instanceof PdbVbtManager)) {
|
||||
if (!(vxtManager instanceof MsftVxtManager mvxtManager)) {
|
||||
return false;
|
||||
}
|
||||
int entrySize = 4; // Default to something (could be wrong)
|
||||
if (vbptr instanceof PointerDataType) {
|
||||
entrySize = ((PointerDataType) vbptr).getDataType().getLength();
|
||||
}
|
||||
|
||||
return findVbtBySymbolConstruction(table, (PdbVbtManager) vbtManager, entrySize,
|
||||
getMangledName(), type, subMangled);
|
||||
boolean x = findVbt(table, mvxtManager, entrySize, symbolPath, parentage);
|
||||
return x;
|
||||
}
|
||||
|
||||
private boolean findVbtBySymbolConstruction(PlaceholderVirtualBaseTable table,
|
||||
PdbVbtManager vbtm, int entrySize, String mangledCompositeTypeName, Type mainType,
|
||||
List<String> subMangledCompositeTypeNames) {
|
||||
if (!validateMangledCompositeName(mangledCompositeTypeName, mainType)) {
|
||||
return false;
|
||||
}
|
||||
for (String mangled : subMangledCompositeTypeNames) {
|
||||
if (!validateMangledCompositeName(mangled, Type.UNKNOWN)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("??_8");
|
||||
builder.append(mangledCompositeTypeName.substring(4));
|
||||
builder.append("7B"); // Hope will always be 'B' ("const")
|
||||
builder.append("@");
|
||||
String possibleName = builder.toString();
|
||||
if (findAndUpdate(table, vbtm, entrySize, possibleName)) {
|
||||
return true;
|
||||
}
|
||||
for (String mangled : subMangledCompositeTypeNames) {
|
||||
builder.deleteCharAt(builder.length() - 1);
|
||||
builder.append(mangled.substring(4));
|
||||
builder.append("@");
|
||||
possibleName = builder.toString();
|
||||
if (findAndUpdate(table, vbtm, entrySize, possibleName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
private boolean findVbt(PlaceholderVirtualBaseTable table, MsftVxtManager mvbtm, int entrySize,
|
||||
SymbolPath ownerSp, List<CppCompositeType> parentage) {
|
||||
|
||||
boolean findAndUpdate(PlaceholderVirtualBaseTable table, PdbVbtManager vbtm, int entrySize,
|
||||
String mangledTableName) {
|
||||
PdbVirtualBaseTable vbt = vbtm.createVirtualBaseTableByName(mangledTableName, entrySize);
|
||||
if (vbt == null) {
|
||||
return false;
|
||||
ClassID mId = new ProgramClassID(baseCategoryPath, ownerSp);
|
||||
List<ClassID> cIdParentage = new ArrayList<>();
|
||||
for (CppCompositeType t : parentage) {
|
||||
ClassID id = new ProgramClassID(t.baseCategoryPath, t.getSymbolPath());
|
||||
cIdParentage.add(id);
|
||||
}
|
||||
table.setName(mangledTableName);
|
||||
ProgramVirtualBaseTable vbt = (ProgramVirtualBaseTable) mvbtm.findPrimaryVbt(mId);
|
||||
//ProgramVirtualBaseTable vbt = (ProgramVirtualBaseTable) mvbtm.findVbt(mId, cIdParentage);
|
||||
|
||||
table.setVirtualBaseTable(vbt);
|
||||
return true;
|
||||
|
||||
return vbt != null;
|
||||
}
|
||||
|
||||
private CppCompositeAndMember findDirectBaseCompositeAndMember(CppCompositeType cppType,
|
||||
private CppParentageAndMember findDirectBaseParentageAndMember(CppCompositeType cppType,
|
||||
int offsetCppType, int vbtptrOffset) throws PdbException {
|
||||
for (LayoutBaseClass base : cppType.layoutBaseClasses) {
|
||||
if (!(base instanceof DirectLayoutBaseClass)) {
|
||||
@ -1086,16 +1061,18 @@ public class CppCompositeType {
|
||||
if (vbtptrOffset >= directBaseOffset &&
|
||||
vbtptrOffset < directBaseOffset + directBaseLength) {
|
||||
CppCompositeType childCppType = directBase.getBaseClassType();
|
||||
CppCompositeAndMember cAndM =
|
||||
findDirectBaseCompositeAndMember(childCppType, directBaseOffset, vbtptrOffset);
|
||||
if (cAndM == null) {
|
||||
CppParentageAndMember cAndP =
|
||||
findDirectBaseParentageAndMember(childCppType, directBaseOffset, vbtptrOffset);
|
||||
if (cAndP == null) {
|
||||
Member member = childCppType.findLayoutMemberOrVftPtrMember(vbtptrOffset);
|
||||
if (member == null) {
|
||||
return null;
|
||||
}
|
||||
cAndM = new CppCompositeAndMember(childCppType, member);
|
||||
cAndP = new CppParentageAndMember(new ArrayList<>(), member);
|
||||
}
|
||||
return cAndM;
|
||||
List<CppCompositeType> parentage = cAndP.parentage();
|
||||
parentage.add(childCppType);
|
||||
return cAndP;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@ -1674,7 +1651,7 @@ public class CppCompositeType {
|
||||
//----------------------------------------------------------------------------------------------
|
||||
static class PlaceholderVirtualBaseTable {
|
||||
private String name;
|
||||
private PdbVirtualBaseTable pdbVirtualBaseTable = null;
|
||||
private ProgramVirtualBaseTable vbt = null;
|
||||
|
||||
// We do not know if every index will be given. We can check after the fact, and once
|
||||
// the set of sequential integers is assured, we could create a list.
|
||||
@ -1697,19 +1674,19 @@ public class CppCompositeType {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
void setVirtualBaseTable(PdbVirtualBaseTable pdbVirtualBaseTable) {
|
||||
this.pdbVirtualBaseTable = pdbVirtualBaseTable;
|
||||
void setVirtualBaseTable(ProgramVirtualBaseTable vbt) {
|
||||
this.vbt = vbt;
|
||||
}
|
||||
|
||||
boolean canLookupOffset() {
|
||||
return pdbVirtualBaseTable != null;
|
||||
return vbt != null;
|
||||
}
|
||||
|
||||
long getOffset(int ordinal) throws PdbException {
|
||||
if (pdbVirtualBaseTable == null) {
|
||||
throw new PdbException("pdbVirtualBaseTable not initialized");
|
||||
if (vbt != null) {
|
||||
return vbt.getBaseOffset(ordinal);
|
||||
}
|
||||
return pdbVirtualBaseTable.getOffset(ordinal);
|
||||
throw new PdbException("pdbVirtualBaseTable not initialized");
|
||||
}
|
||||
|
||||
Map<Integer, PlaceholderVirtualBaseTableEntry> getEntries() {
|
||||
@ -1751,50 +1728,4 @@ public class CppCompositeType {
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------
|
||||
//----------------------------------------------------------------------------------------------
|
||||
//----------------------------------------------------------------------------------------------
|
||||
//----------------------------------------------------------------------------------------------
|
||||
static enum Type {
|
||||
UNKNOWN("UNKNOWN_TYPE", -1),
|
||||
BLANK("", 1),
|
||||
CLASS("class", 2),
|
||||
STRUCT("struct", 3),
|
||||
UNION("union", 4);
|
||||
|
||||
private static final Map<Integer, Type> BY_VALUE = new HashMap<>();
|
||||
static {
|
||||
for (Type val : values()) {
|
||||
BY_VALUE.put(val.value, val);
|
||||
}
|
||||
}
|
||||
private final String label;
|
||||
private final int value;
|
||||
|
||||
public String getString() {
|
||||
return label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (label.length() != 0) {
|
||||
return label + " ";
|
||||
}
|
||||
return label;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public static Type fromValue(int val) {
|
||||
return BY_VALUE.getOrDefault(val, UNKNOWN);
|
||||
}
|
||||
|
||||
private Type(String label, int value) {
|
||||
this.label = label;
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ import ghidra.app.util.bin.format.pdb2.pdbreader.type.PrimitiveMsType;
|
||||
import ghidra.app.util.bin.format.pe.cli.tables.CliAbstractTableRow;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.app.util.pdb.PdbCategories;
|
||||
import ghidra.app.util.pdb.classtype.*;
|
||||
import ghidra.framework.options.Options;
|
||||
import ghidra.program.database.data.DataTypeUtilities;
|
||||
import ghidra.program.disassemble.DisassemblerContextImpl;
|
||||
@ -190,6 +191,7 @@ public class DefaultPdbApplicator implements PdbApplicator {
|
||||
private Address imageBase;
|
||||
private int linkerModuleNumber = -1;
|
||||
private DataTypeManager dataTypeManager;
|
||||
private ClassTypeManager classTypeManager;
|
||||
private PdbAddressManager pdbAddressManager;
|
||||
private List<SymbolGroup> symbolGroups;
|
||||
|
||||
@ -204,14 +206,14 @@ public class DefaultPdbApplicator implements PdbApplicator {
|
||||
|
||||
//==============================================================================================
|
||||
// If we have symbols and memory with VBTs in them, then a better VbtManager is created.
|
||||
VbtManager vbtManager;
|
||||
PdbRegisterNameToProgramRegisterMapper registerNameToRegisterMapper;
|
||||
private VxtManager vxtManager;
|
||||
private PdbRegisterNameToProgramRegisterMapper registerNameToRegisterMapper;
|
||||
|
||||
//==============================================================================================
|
||||
private MultiphaseDataTypeResolver multiphaseResolver;
|
||||
private int resolveCount;
|
||||
private int conflictCount;
|
||||
private PdbCategories categoryUtils;
|
||||
private PdbCategories pdbCategories;
|
||||
private PdbPrimitiveTypeApplicator pdbPrimitiveTypeApplicator;
|
||||
private TypeApplierFactory typeApplierParser;
|
||||
// We may need to put the following map into the "analysis state" for access by
|
||||
@ -589,6 +591,9 @@ public class DefaultPdbApplicator implements PdbApplicator {
|
||||
pdbPeHeaderInfoManager = new PdbPeHeaderInfoManager(this);
|
||||
|
||||
multiphaseResolver = new MultiphaseDataTypeResolver(this);
|
||||
|
||||
classTypeManager = new ClassTypeManager(dataTypeManager);
|
||||
|
||||
pdbPrimitiveTypeApplicator = new PdbPrimitiveTypeApplicator(dataTypeManager);
|
||||
|
||||
typeApplierParser = new TypeApplierFactory(this);
|
||||
@ -627,17 +632,21 @@ public class DefaultPdbApplicator implements PdbApplicator {
|
||||
if (!pdbAddressManager.isInitialized()) {
|
||||
pdbAddressManager.initialize(this, imageBase);
|
||||
}
|
||||
categoryUtils = setPdbCatogoryUtils(pdb.getFilename());
|
||||
pdbCategories = setPdbCatogoryUtils(pdb.getFilename());
|
||||
symbolGroups = createSymbolGroups();
|
||||
linkerModuleNumber = findLinkerModuleNumber();
|
||||
if (program != null) {
|
||||
// Currently, this must happen after symbolGroups are created.
|
||||
PdbVbtManager pdbVbtManager = new PdbVbtManager(this);
|
||||
vbtManager = pdbVbtManager;
|
||||
MsftVxtManager msftVxtManager =
|
||||
new MsftVxtManager(getClassTypeManager(), program.getMemory());
|
||||
msftVxtManager.createVirtualTables(getRootPdbCategory(), findVirtualTableSymbols(), log,
|
||||
monitor);
|
||||
vxtManager = msftVxtManager;
|
||||
|
||||
registerNameToRegisterMapper = new PdbRegisterNameToProgramRegisterMapper(program);
|
||||
}
|
||||
else {
|
||||
vbtManager = new VbtManager(getDataTypeManager());
|
||||
vxtManager = new VxtManager(getClassTypeManager());
|
||||
}
|
||||
preWorkDone = true;
|
||||
}
|
||||
@ -689,6 +698,47 @@ public class DefaultPdbApplicator implements PdbApplicator {
|
||||
return mySymbolGroups;
|
||||
}
|
||||
|
||||
private Map<String, Address> findVirtualTableSymbols() throws CancelledException, PdbException {
|
||||
|
||||
Map<String, Address> myAddressByVxtMangledName = new HashMap<>();
|
||||
|
||||
PdbDebugInfo debugInfo = pdb.getDebugInfo();
|
||||
if (debugInfo == null) {
|
||||
return myAddressByVxtMangledName;
|
||||
}
|
||||
|
||||
SymbolGroup symbolGroup = getSymbolGroup();
|
||||
if (symbolGroup == null) {
|
||||
return myAddressByVxtMangledName;
|
||||
}
|
||||
|
||||
PublicSymbolInformation publicSymbolInformation = debugInfo.getPublicSymbolInformation();
|
||||
List<Long> offsets = publicSymbolInformation.getModifiedHashRecordSymbolOffsets();
|
||||
monitor.setMessage("PDB: Searching for VxT symbols...");
|
||||
monitor.initialize(offsets.size());
|
||||
|
||||
MsSymbolIterator iter = symbolGroup.getSymbolIterator();
|
||||
for (long offset : offsets) {
|
||||
monitor.checkCancelled();
|
||||
iter.initGetByOffset(offset);
|
||||
if (!iter.hasNext()) {
|
||||
break;
|
||||
}
|
||||
AbstractMsSymbol symbol = iter.peek();
|
||||
if (symbol instanceof AbstractPublicMsSymbol pubSymbol) {
|
||||
String name = pubSymbol.getName();
|
||||
if (name.startsWith("??_7") || name.startsWith("??_8")) {
|
||||
Address address = getAddress(pubSymbol);
|
||||
if (!isInvalidAddress(address, name)) {
|
||||
myAddressByVxtMangledName.put(name, address);
|
||||
}
|
||||
}
|
||||
}
|
||||
monitor.incrementProgress(1);
|
||||
}
|
||||
return myAddressByVxtMangledName;
|
||||
}
|
||||
|
||||
//==============================================================================================
|
||||
// Basic utility methods.
|
||||
//==============================================================================================
|
||||
@ -807,6 +857,10 @@ public class DefaultPdbApplicator implements PdbApplicator {
|
||||
return dataTypeManager;
|
||||
}
|
||||
|
||||
ClassTypeManager getClassTypeManager() {
|
||||
return classTypeManager;
|
||||
}
|
||||
|
||||
// for PdbTypeApplicator (new)
|
||||
DataOrganization getDataOrganization() {
|
||||
return dataTypeManager.getDataOrganization();
|
||||
@ -819,6 +873,14 @@ public class DefaultPdbApplicator implements PdbApplicator {
|
||||
//==============================================================================================
|
||||
// CategoryPath-related methods.
|
||||
//==============================================================================================
|
||||
/**
|
||||
* Get root CategoryPath for the PDB
|
||||
* @return the root CategoryPath
|
||||
*/
|
||||
CategoryPath getRootPdbCategory() {
|
||||
return pdbCategories.getRootCategoryPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link CategoryPath} associated with the {@link SymbolPath} specified, rooting
|
||||
* it either at the PDB Category
|
||||
@ -827,7 +889,7 @@ public class DefaultPdbApplicator implements PdbApplicator {
|
||||
* @return {@link CategoryPath} created for the input
|
||||
*/
|
||||
CategoryPath getCategory(SymbolPath symbolPath) {
|
||||
return categoryUtils.getCategory(symbolPath);
|
||||
return pdbCategories.getCategory(symbolPath);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -839,7 +901,7 @@ public class DefaultPdbApplicator implements PdbApplicator {
|
||||
* @return the CategoryPath
|
||||
*/
|
||||
CategoryPath getTypedefsCategory(int moduleNumber, SymbolPath symbolPath) {
|
||||
return categoryUtils.getTypedefsCategory(moduleNumber, symbolPath);
|
||||
return pdbCategories.getTypedefsCategory(moduleNumber, symbolPath);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -847,7 +909,7 @@ public class DefaultPdbApplicator implements PdbApplicator {
|
||||
* @return the {@link CategoryPath}
|
||||
*/
|
||||
CategoryPath getAnonymousFunctionsCategory() {
|
||||
return categoryUtils.getAnonymousFunctionsCategory();
|
||||
return pdbCategories.getAnonymousFunctionsCategory();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -855,7 +917,7 @@ public class DefaultPdbApplicator implements PdbApplicator {
|
||||
* @return the {@link CategoryPath}
|
||||
*/
|
||||
CategoryPath getAnonymousTypesCategory() {
|
||||
return categoryUtils.getAnonymousTypesCategory();
|
||||
return pdbCategories.getAnonymousTypesCategory();
|
||||
}
|
||||
|
||||
// /**
|
||||
@ -1482,10 +1544,10 @@ public class DefaultPdbApplicator implements PdbApplicator {
|
||||
}
|
||||
|
||||
//==============================================================================================
|
||||
// Virtual-Base-Table-related methods.
|
||||
// Virtual-Base/Function-Table-related methods.
|
||||
//==============================================================================================
|
||||
VbtManager getVbtManager() {
|
||||
return vbtManager;
|
||||
VxtManager getVxtManager() {
|
||||
return vxtManager;
|
||||
}
|
||||
|
||||
//==============================================================================================
|
||||
@ -2299,8 +2361,7 @@ public class DefaultPdbApplicator implements PdbApplicator {
|
||||
}
|
||||
|
||||
private Symbol createSymbolInternal(Address address, SymbolPath symbolPath,
|
||||
boolean isNewFunctionSignature,
|
||||
String plateAddition) {
|
||||
boolean isNewFunctionSignature, String plateAddition) {
|
||||
|
||||
Symbol existingSymbol = program.getSymbolTable().getPrimarySymbol(address);
|
||||
if (existingSymbol == null || isNewFunctionSignature) {
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -16,6 +16,7 @@
|
||||
package ghidra.app.util.pdb.pdbapplicator;
|
||||
|
||||
import ghidra.app.util.bin.format.pdb.*;
|
||||
import ghidra.app.util.pdb.classtype.ClassFieldAttributes;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
|
||||
|
@ -0,0 +1,623 @@
|
||||
/* ###
|
||||
* 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.classtype;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import generic.test.AbstractGenericTest;
|
||||
import ghidra.app.plugin.core.checksums.MyTestMemory;
|
||||
import ghidra.app.util.SymbolPath;
|
||||
import ghidra.app.util.SymbolPathParser;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressIterator;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.mem.Memory;
|
||||
import ghidra.program.model.mem.MemoryAccessException;
|
||||
import ghidra.util.LittleEndianDataConverter;
|
||||
import ghidra.util.exception.AssertException;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* Unit tests for the {@link MsftVxtManager}.
|
||||
* <p>
|
||||
* See {@link MsftVxtManager} for a description of what tests need to work
|
||||
*/
|
||||
public class MsftVxtManagerTest extends AbstractGenericTest {
|
||||
|
||||
private static MessageLog log = new MessageLog();
|
||||
private static TaskMonitor monitor = TaskMonitor.DUMMY;
|
||||
|
||||
private static DataTypeManager dtm32;
|
||||
private static DataTypeManager dtm64;
|
||||
// Didn't intend to modify this class to need these, but need them while modifying PdbVxtManager
|
||||
// to use them
|
||||
private static ClassTypeManager ctm32;
|
||||
private static ClassTypeManager ctm64;
|
||||
|
||||
private ClassID a1Id = new ProgramClassID(CategoryPath.ROOT, sp("A1NS::A1"));
|
||||
private ClassID a2Id = new ProgramClassID(CategoryPath.ROOT, sp("A2NS::A2"));
|
||||
private ClassID aId = new ProgramClassID(CategoryPath.ROOT, sp("ANS::A"));
|
||||
private ClassID b1Id = new ProgramClassID(CategoryPath.ROOT, sp("B1NS::B1"));
|
||||
private ClassID b2Id = new ProgramClassID(CategoryPath.ROOT, sp("B2NS::B2"));
|
||||
private ClassID bId = new ProgramClassID(CategoryPath.ROOT, sp("BNS::B"));
|
||||
private ClassID cId = new ProgramClassID(CategoryPath.ROOT, sp("CNS::C"));
|
||||
private ClassID dId = new ProgramClassID(CategoryPath.ROOT, sp("DNS::D"));
|
||||
private ClassID eId = new ProgramClassID(CategoryPath.ROOT, sp("ENS::E"));
|
||||
private ClassID fId = new ProgramClassID(CategoryPath.ROOT, sp("FNS::F"));
|
||||
private ClassID gId = new ProgramClassID(CategoryPath.ROOT, sp("GNS::G"));
|
||||
private ClassID hId = new ProgramClassID(CategoryPath.ROOT, sp("HNS::H"));
|
||||
private ClassID iId = new ProgramClassID(CategoryPath.ROOT, sp("INS::I"));
|
||||
private ClassID jId = new ProgramClassID(CategoryPath.ROOT, sp("JNS::J"));
|
||||
private ClassID kId = new ProgramClassID(CategoryPath.ROOT, sp("KNS::K"));
|
||||
private ClassID lId = new ProgramClassID(CategoryPath.ROOT, sp("LNS::L"));
|
||||
private ClassID mId = new ProgramClassID(CategoryPath.ROOT, sp("MNS::M"));
|
||||
|
||||
private static Memory memory32;
|
||||
private static Memory memory64;
|
||||
|
||||
private static List<String> vbtSymbols = new ArrayList<>();
|
||||
private static List<String> vftSymbols = new ArrayList<>();
|
||||
private static List<Address> vxtAddresses32;
|
||||
private static List<Address> vxtAddresses64;
|
||||
|
||||
private static Map<String, Address> addressByVxtMangledName32;
|
||||
private static Map<String, Address> addressByVxtMangledName64;
|
||||
|
||||
// private static PointerDataType vftptr32;
|
||||
// private static PointerDataType vftptr64;
|
||||
// private static PointerDataType vbtptr32;
|
||||
// private static PointerDataType vbtptr64;
|
||||
|
||||
private static MsftVxtManager mVxtManager32;
|
||||
private static MsftVxtManager mVxtManager64;
|
||||
|
||||
static {
|
||||
BitFieldPackingImpl bitFieldPacking = new BitFieldPackingImpl();
|
||||
bitFieldPacking.setUseMSConvention(true);
|
||||
|
||||
// DataOrganization based on x86win.cspec
|
||||
// The DataOrganizationImpl currently has defaults of a 32-bit windows cspec, but could
|
||||
// change in the future.
|
||||
DataOrganizationImpl dataOrg32 = DataOrganizationImpl.getDefaultOrganization(null);
|
||||
|
||||
dtm32 = new TestDummyDataTypeManager() {
|
||||
HashMap<String, DataType> dataTypeMap = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public DataOrganization getDataOrganization() {
|
||||
return dataOrg32;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType addDataType(DataType dataType, DataTypeConflictHandler handler) {
|
||||
// handler ignored - tests should not induce conflicts
|
||||
String pathname = dataType.getPathName();
|
||||
DataType myDt = dataTypeMap.get(pathname);
|
||||
if (myDt != null) {
|
||||
return myDt;
|
||||
}
|
||||
DataType dt = dataType.clone(this);
|
||||
dataTypeMap.put(pathname, dt);
|
||||
return dt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType findDataType(String dataTypePath) {
|
||||
return dataTypeMap.get(dataTypePath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType getDataType(CategoryPath path, String name) {
|
||||
return super.getDataType(new DataTypePath(path, name).getPath());
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType getDataType(String dataTypePath) {
|
||||
return dataTypeMap.get(dataTypePath);
|
||||
}
|
||||
};
|
||||
|
||||
// DataOrganization based on x86-64-win.cspec
|
||||
DataOrganizationImpl dataOrg64 = DataOrganizationImpl.getDefaultOrganization(null);
|
||||
DataOrganizationTestUtils.initDataOrganizationWindows64BitX86(dataOrg64);
|
||||
|
||||
dtm64 = new TestDummyDataTypeManager() {
|
||||
HashMap<String, DataType> dataTypeMap = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public DataOrganization getDataOrganization() {
|
||||
return dataOrg64;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType addDataType(DataType dataType, DataTypeConflictHandler handler) {
|
||||
// handler ignored - tests should not induce conflicts
|
||||
String pathname = dataType.getPathName();
|
||||
DataType myDt = dataTypeMap.get(pathname);
|
||||
if (myDt != null) {
|
||||
return myDt;
|
||||
}
|
||||
DataType dt = dataType.clone(this);
|
||||
dataTypeMap.put(pathname, dt);
|
||||
return dt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType findDataType(String dataTypePath) {
|
||||
return dataTypeMap.get(dataTypePath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType getDataType(CategoryPath path, String name) {
|
||||
return super.getDataType(new DataTypePath(path, name).getPath());
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType getDataType(String dataTypePath) {
|
||||
return dataTypeMap.get(dataTypePath);
|
||||
}
|
||||
};
|
||||
|
||||
// Didn't intend to modify this class to need these, but need them while modifying
|
||||
// PdbVxtManager to use them
|
||||
ctm32 = new ClassTypeManager(dtm32);
|
||||
ctm64 = new ClassTypeManager(dtm64);
|
||||
// vftptr32 = new PointerDataType(new PointerDataType(dtm32));
|
||||
// vftptr64 = new PointerDataType(new PointerDataType(dtm64));
|
||||
// vbtptr32 = new PointerDataType(new IntegerDataType(dtm32));
|
||||
// vbtptr64 = new PointerDataType(new IntegerDataType(dtm64));
|
||||
|
||||
createVxTables();
|
||||
|
||||
mVxtManager32 = new MsftVxtManager(ctm32, memory32);
|
||||
mVxtManager64 = new MsftVxtManager(ctm64, memory64);
|
||||
|
||||
try {
|
||||
mVxtManager32.createVirtualTables(CategoryPath.ROOT, addressByVxtMangledName32, log,
|
||||
monitor);
|
||||
mVxtManager64.createVirtualTables(CategoryPath.ROOT, addressByVxtMangledName64, log,
|
||||
monitor);
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//==============================================================================================
|
||||
private static SymbolPath sp(String s) {
|
||||
return new SymbolPath(SymbolPathParser.parse(s));
|
||||
}
|
||||
|
||||
//==============================================================================================
|
||||
|
||||
private static void addBytesForIntegers(int[] ints, byte[] bytes, int startOffset) {
|
||||
int maxOffset = startOffset + 4 * ints.length;
|
||||
int index = 0;
|
||||
for (int offset = startOffset; offset < maxOffset; offset += 4) {
|
||||
LittleEndianDataConverter.INSTANCE.getBytes(ints[index++], bytes, offset);
|
||||
}
|
||||
}
|
||||
|
||||
static class MemoryPreparer {
|
||||
private int nextOffset = 0;
|
||||
private List<int[]> intArrays = new ArrayList<>();
|
||||
private List<Integer> offsets = new ArrayList<>();
|
||||
private List<Address> addresses = new ArrayList<>();
|
||||
private MyTestMemory memory = null;
|
||||
private int mockAddressCounter = 0;
|
||||
|
||||
void addAddresses(int numAddresses, boolean is64bit) {
|
||||
int[] integers;
|
||||
if (is64bit) {
|
||||
integers = new int[numAddresses * 2];
|
||||
for (int i = 0; i < numAddresses; i++) {
|
||||
integers[i * 2] = mockAddressCounter;
|
||||
integers[i * 2 + 1] = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
integers = new int[numAddresses * 2];
|
||||
for (int i = 0; i < numAddresses; i++) {
|
||||
integers[i] = mockAddressCounter;
|
||||
}
|
||||
}
|
||||
addIntegers(integers);
|
||||
}
|
||||
|
||||
void addIntegers(int[] integers) {
|
||||
offsets.add(nextOffset);
|
||||
intArrays.add(integers);
|
||||
nextOffset += 4 * integers.length;
|
||||
}
|
||||
|
||||
List<Integer> getOffsets() {
|
||||
return offsets;
|
||||
}
|
||||
|
||||
void finalizeMemory() {
|
||||
byte[] bytes = new byte[nextOffset];
|
||||
for (int index = 0; index < offsets.size(); index++) {
|
||||
addBytesForIntegers(intArrays.get(index), bytes, offsets.get(index));
|
||||
}
|
||||
memory = new CppCompositeTestMemory(bytes);
|
||||
AddressIterator iter = memory.getAddresses(true);
|
||||
if (!iter.hasNext()) {
|
||||
return;
|
||||
}
|
||||
Address address = iter.next();
|
||||
for (Integer offset : offsets) {
|
||||
addresses.add(address.add(offset));
|
||||
}
|
||||
}
|
||||
|
||||
Memory getMemory() {
|
||||
return memory;
|
||||
}
|
||||
|
||||
List<Address> getAddresses() {
|
||||
return addresses;
|
||||
}
|
||||
|
||||
private static class CppCompositeTestMemory extends MyTestMemory {
|
||||
public CppCompositeTestMemory(byte[] bytes) {
|
||||
super(bytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInt(Address addr) throws MemoryAccessException {
|
||||
byte bytes[] = new byte[4];
|
||||
int num = getBytes(addr, bytes, 0, 4);
|
||||
assertEquals(num, 4);
|
||||
return LittleEndianDataConverter.INSTANCE.getInt(bytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLong(Address addr) throws MemoryAccessException {
|
||||
byte bytes[] = new byte[8];
|
||||
int num = getBytes(addr, bytes, 0, 8);
|
||||
assertEquals(num, 8);
|
||||
return LittleEndianDataConverter.INSTANCE.getLong(bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void createVxTables() {
|
||||
MemoryPreparer preparer32 = new MemoryPreparer();
|
||||
MemoryPreparer preparer64 = new MemoryPreparer();
|
||||
|
||||
vbtSymbols = new ArrayList<>();
|
||||
vftSymbols = new ArrayList<>();
|
||||
|
||||
//==========================================================================================
|
||||
|
||||
vbtSymbols.add("??_8A@ANS@@7B@");
|
||||
preparer32.addIntegers(new int[] { -4, 8, 16 });
|
||||
preparer64.addIntegers(new int[] { -8, 16, 32 });
|
||||
|
||||
vbtSymbols.add("??_8B@BNS@@7B@");
|
||||
preparer32.addIntegers(new int[] { -4, 8, 16 });
|
||||
preparer64.addIntegers(new int[] { -8, 16, 32 });
|
||||
|
||||
vbtSymbols.add("??_8C@CNS@@7B@");
|
||||
preparer32.addIntegers(new int[] { -4, 8, 16, 24, 32 });
|
||||
preparer64.addIntegers(new int[] { -8, 16, 32, 48, 64 });
|
||||
|
||||
vbtSymbols.add("??_8D@DNS@@7BC@CNS@@@");
|
||||
preparer32.addIntegers(new int[] { -4, 36, 44, 52, 60 });
|
||||
preparer64.addIntegers(new int[] { -8, 72, 88, 104, 120 });
|
||||
|
||||
vbtSymbols.add("??_8D@DNS@@7BA@ANS@@@");
|
||||
preparer32.addIntegers(new int[] { -4, 24, 32 });
|
||||
preparer64.addIntegers(new int[] { -8, 48, 64 });
|
||||
|
||||
vbtSymbols.add("??_8D@DNS@@7BB@BNS@@@");
|
||||
preparer32.addIntegers(new int[] { -4, 28, 36 });
|
||||
preparer64.addIntegers(new int[] { -8, 56, 72 });
|
||||
|
||||
vbtSymbols.add("??_8E@ENS@@7BA@ANS@@@");
|
||||
preparer32.addIntegers(new int[] { -4, 12, 20, 28, 36, 44 });
|
||||
preparer64.addIntegers(new int[] { -8, 24, 40, 56, 72, 88 });
|
||||
|
||||
vbtSymbols.add("??_8E@ENS@@7BB@BNS@@@");
|
||||
preparer32.addIntegers(new int[] { -4, -20, -12 });
|
||||
preparer64.addIntegers(new int[] { -8, -40, -24 });
|
||||
|
||||
vbtSymbols.add("??_8F@FNS@@7B@");
|
||||
preparer32.addIntegers(new int[] { 0, 8 });
|
||||
preparer64.addIntegers(new int[] { 0, 16 });
|
||||
|
||||
vbtSymbols.add("??_8G@GNS@@7B@");
|
||||
preparer32.addIntegers(new int[] { 0, 12 });
|
||||
preparer64.addIntegers(new int[] { 0, 24 });
|
||||
|
||||
vbtSymbols.add("??_8H@HNS@@7B@");
|
||||
preparer32.addIntegers(new int[] { 0, 12 });
|
||||
preparer64.addIntegers(new int[] { 0, 24 });
|
||||
|
||||
vbtSymbols.add("??_8I@INS@@7BG@GNS@@@");
|
||||
preparer32.addIntegers(new int[] { 0, 28 });
|
||||
preparer64.addIntegers(new int[] { 0, 56 });
|
||||
|
||||
vbtSymbols.add("??_8I@INS@@7BH@HNS@@@");
|
||||
preparer32.addIntegers(new int[] { 0, 16 });
|
||||
preparer64.addIntegers(new int[] { 0, 32 });
|
||||
|
||||
vbtSymbols.add("??_8J@JNS@@7B@");
|
||||
preparer32.addIntegers(new int[] { 0, 8 });
|
||||
preparer64.addIntegers(new int[] { 0, 16 });
|
||||
|
||||
vbtSymbols.add("??_8K@KNS@@7B@");
|
||||
preparer32.addIntegers(new int[] { 0, 12 });
|
||||
preparer64.addIntegers(new int[] { 0, 24 });
|
||||
|
||||
vbtSymbols.add("??_8L@LNS@@7B@");
|
||||
preparer32.addIntegers(new int[] { 0, 16 });
|
||||
preparer64.addIntegers(new int[] { 0, 32 });
|
||||
|
||||
vbtSymbols.add("??_8M@MNS@@7BA@ANS@@E@ENS@@@");
|
||||
preparer32.addIntegers(new int[] { -4, 100, 108, 116, 124, 132 });
|
||||
preparer64.addIntegers(new int[] { -8, 200, 216, 232, 248, 264 });
|
||||
|
||||
vbtSymbols.add("??_8M@MNS@@7BC@CNS@@@");
|
||||
preparer32.addIntegers(new int[] { -4, 84, 92, 100, 108 });
|
||||
preparer64.addIntegers(new int[] { -8, 168, 184, 200, 216 });
|
||||
|
||||
vbtSymbols.add("??_8M@MNS@@7BA@ANS@@D@DNS@@@");
|
||||
preparer32.addIntegers(new int[] { -4, 72, 80 });
|
||||
preparer64.addIntegers(new int[] { -8, 144, 160 });
|
||||
|
||||
vbtSymbols.add("??_8M@MNS@@7BB@BNS@@D@DNS@@@");
|
||||
preparer32.addIntegers(new int[] { -4, 76, 84 });
|
||||
preparer64.addIntegers(new int[] { -8, 152, 168 });
|
||||
|
||||
vbtSymbols.add("??_8M@MNS@@7BG@GNS@@@");
|
||||
preparer32.addIntegers(new int[] { 0, 48 });
|
||||
preparer64.addIntegers(new int[] { 0, 96 });
|
||||
|
||||
vbtSymbols.add("??_8M@MNS@@7BH@HNS@@@");
|
||||
preparer32.addIntegers(new int[] { 0, 36 });
|
||||
preparer64.addIntegers(new int[] { 0, 72 });
|
||||
|
||||
vbtSymbols.add("??_8M@MNS@@7B@");
|
||||
preparer32.addIntegers(new int[] { 0, 20 });
|
||||
preparer64.addIntegers(new int[] { 0, 40 });
|
||||
|
||||
vbtSymbols.add("??_8M@MNS@@7BB@BNS@@E@ENS@@@");
|
||||
preparer32.addIntegers(new int[] { -4, -20, -12 });
|
||||
preparer64.addIntegers(new int[] { -8, -40, -24 });
|
||||
|
||||
//==========================================================================================
|
||||
// Below: writing one int to simulate one address for 32-bit and tow ints for 64-bit (lsb)
|
||||
// Later... mock up even better
|
||||
|
||||
vftSymbols.add("??_7A1@A1NS@@6B@");
|
||||
preparer32.addAddresses(3, false);
|
||||
preparer64.addAddresses(3, true);
|
||||
|
||||
vftSymbols.add("??_7A2@A2NS@@6B@");
|
||||
preparer32.addAddresses(3, false);
|
||||
preparer64.addAddresses(3, true);
|
||||
|
||||
vftSymbols.add("??_7A@ANS@@6B01@@");
|
||||
preparer32.addAddresses(1, false);
|
||||
preparer64.addAddresses(1, true);
|
||||
|
||||
vftSymbols.add("??_7A@ANS@@6BA1@A1NS@@@");
|
||||
preparer32.addAddresses(3, false);
|
||||
preparer64.addAddresses(3, true);
|
||||
|
||||
vftSymbols.add("??_7A@ANS@@6BA2@A2NS@@@");
|
||||
preparer32.addAddresses(3, false);
|
||||
preparer64.addAddresses(3, true);
|
||||
|
||||
vftSymbols.add("??_7B1@B1NS@@6B@");
|
||||
preparer32.addAddresses(3, false);
|
||||
preparer64.addAddresses(3, true);
|
||||
|
||||
vftSymbols.add("??_7B2@B2NS@@6B@");
|
||||
preparer32.addAddresses(3, false);
|
||||
preparer64.addAddresses(3, true);
|
||||
|
||||
vftSymbols.add("??_7B@BNS@@6B01@@");
|
||||
preparer32.addAddresses(1, false);
|
||||
preparer64.addAddresses(1, true);
|
||||
|
||||
vftSymbols.add("??_7B@BNS@@6BB1@B1NS@@@");
|
||||
preparer32.addAddresses(3, false);
|
||||
preparer64.addAddresses(3, true);
|
||||
|
||||
vftSymbols.add("??_7B@BNS@@6BB2@B2NS@@@");
|
||||
preparer32.addAddresses(3, false);
|
||||
preparer64.addAddresses(3, true);
|
||||
|
||||
vftSymbols.add("??_7C@CNS@@6B01@@");
|
||||
preparer32.addAddresses(1, false);
|
||||
preparer64.addAddresses(1, true);
|
||||
|
||||
vftSymbols.add("??_7C@CNS@@6BA1@A1NS@@@");
|
||||
preparer32.addAddresses(3, false);
|
||||
preparer64.addAddresses(3, true);
|
||||
|
||||
vftSymbols.add("??_7C@CNS@@6BA2@A2NS@@@");
|
||||
preparer32.addAddresses(3, false);
|
||||
preparer64.addAddresses(3, true);
|
||||
|
||||
vftSymbols.add("??_7C@CNS@@6BB1@B1NS@@@");
|
||||
preparer32.addAddresses(3, false);
|
||||
preparer64.addAddresses(3, true);
|
||||
|
||||
vftSymbols.add("??_7C@CNS@@6BB2@B2NS@@@");
|
||||
preparer32.addAddresses(3, false);
|
||||
preparer64.addAddresses(3, true);
|
||||
|
||||
vftSymbols.add("??_7D@DNS@@6BC@CNS@@@");
|
||||
preparer32.addAddresses(1, false);
|
||||
preparer64.addAddresses(1, true);
|
||||
|
||||
vftSymbols.add("??_7D@DNS@@6BA@ANS@@@");
|
||||
preparer32.addAddresses(1, false);
|
||||
preparer64.addAddresses(1, true);
|
||||
|
||||
vftSymbols.add("??_7D@DNS@@6BB@BNS@@@");
|
||||
preparer32.addAddresses(1, false);
|
||||
preparer64.addAddresses(1, true);
|
||||
|
||||
vftSymbols.add("??_7D@DNS@@6BA1@A1NS@@@");
|
||||
preparer32.addAddresses(3, false);
|
||||
preparer64.addAddresses(3, true);
|
||||
|
||||
vftSymbols.add("??_7D@DNS@@6BA2@A2NS@@@");
|
||||
preparer32.addAddresses(3, false);
|
||||
preparer64.addAddresses(3, true);
|
||||
|
||||
vftSymbols.add("??_7D@DNS@@6BB1@B1NS@@@");
|
||||
preparer32.addAddresses(3, false);
|
||||
preparer64.addAddresses(3, true);
|
||||
|
||||
vftSymbols.add("??_7D@DNS@@6BB2@B2NS@@@");
|
||||
preparer32.addAddresses(3, false);
|
||||
preparer64.addAddresses(3, true);
|
||||
|
||||
vftSymbols.add("??_7E@ENS@@6BA@ANS@@@");
|
||||
preparer32.addAddresses(1, false);
|
||||
preparer64.addAddresses(1, true);
|
||||
|
||||
vftSymbols.add("??_7E@ENS@@6BA1@A1NS@@@");
|
||||
preparer32.addAddresses(3, false);
|
||||
preparer64.addAddresses(3, true);
|
||||
|
||||
vftSymbols.add("??_7E@ENS@@6BA2@A2NS@@@");
|
||||
preparer32.addAddresses(3, false);
|
||||
preparer64.addAddresses(3, true);
|
||||
|
||||
vftSymbols.add("??_7E@ENS@@6BB1@B1NS@@@");
|
||||
preparer32.addAddresses(3, false);
|
||||
preparer64.addAddresses(3, true);
|
||||
|
||||
vftSymbols.add("??_7E@ENS@@6BB2@B2NS@@@");
|
||||
preparer32.addAddresses(3, false);
|
||||
preparer64.addAddresses(3, true);
|
||||
|
||||
vftSymbols.add("??_7E@ENS@@6BB@BNS@@@");
|
||||
preparer32.addAddresses(1, false);
|
||||
preparer64.addAddresses(1, true);
|
||||
|
||||
vftSymbols.add("??_7F@FNS@@6B@");
|
||||
preparer32.addAddresses(3, false);
|
||||
preparer64.addAddresses(3, true);
|
||||
|
||||
vftSymbols.add("??_7G@GNS@@6B@");
|
||||
preparer32.addAddresses(3, false);
|
||||
preparer64.addAddresses(3, true);
|
||||
|
||||
vftSymbols.add("??_7H@HNS@@6B@");
|
||||
preparer32.addAddresses(3, false);
|
||||
preparer64.addAddresses(3, true);
|
||||
|
||||
vftSymbols.add("??_7I@INS@@6B@");
|
||||
preparer32.addAddresses(3, false);
|
||||
preparer64.addAddresses(3, true);
|
||||
|
||||
vftSymbols.add("??_7J@JNS@@6B@");
|
||||
preparer32.addAddresses(3, false);
|
||||
preparer64.addAddresses(3, true);
|
||||
|
||||
vftSymbols.add("??_7K@KNS@@6B@");
|
||||
preparer32.addAddresses(3, false);
|
||||
preparer64.addAddresses(3, true);
|
||||
|
||||
vftSymbols.add("??_7L@LNS@@6B@");
|
||||
preparer32.addAddresses(3, false);
|
||||
preparer64.addAddresses(3, true);
|
||||
|
||||
vftSymbols.add("??_7M@MNS@@6BA@ANS@@E@ENS@@@");
|
||||
preparer32.addAddresses(1, false);
|
||||
preparer64.addAddresses(1, true);
|
||||
|
||||
vftSymbols.add("??_7M@MNS@@6BC@CNS@@@");
|
||||
preparer32.addAddresses(1, false);
|
||||
preparer64.addAddresses(1, true);
|
||||
|
||||
vftSymbols.add("??_7M@MNS@@6BA@ANS@@D@DNS@@@");
|
||||
preparer32.addAddresses(1, false);
|
||||
preparer64.addAddresses(1, true);
|
||||
|
||||
vftSymbols.add("??_7M@MNS@@6BB@BNS@@D@DNS@@@");
|
||||
preparer32.addAddresses(1, false);
|
||||
preparer64.addAddresses(1, true);
|
||||
|
||||
vftSymbols.add("??_7M@MNS@@6BA1@A1NS@@@");
|
||||
preparer32.addAddresses(3, false);
|
||||
preparer64.addAddresses(3, true);
|
||||
|
||||
vftSymbols.add("??_7M@MNS@@6BA2@A2NS@@@");
|
||||
preparer32.addAddresses(3, false);
|
||||
preparer64.addAddresses(3, true);
|
||||
|
||||
vftSymbols.add("??_7M@MNS@@6BB1@B1NS@@@");
|
||||
preparer32.addAddresses(3, false);
|
||||
preparer64.addAddresses(3, true);
|
||||
|
||||
vftSymbols.add("??_7M@MNS@@6BB2@B2NS@@@");
|
||||
preparer32.addAddresses(3, false);
|
||||
preparer64.addAddresses(3, true);
|
||||
|
||||
vftSymbols.add("??_7M@MNS@@6BB@BNS@@E@ENS@@@");
|
||||
preparer32.addAddresses(1, false);
|
||||
preparer64.addAddresses(1, true);
|
||||
|
||||
//==========================================================================================
|
||||
|
||||
preparer32.finalizeMemory();
|
||||
preparer64.finalizeMemory();
|
||||
|
||||
memory32 = preparer32.getMemory();
|
||||
memory64 = preparer64.getMemory();
|
||||
|
||||
vxtAddresses32 = preparer32.getAddresses();
|
||||
vxtAddresses64 = preparer64.getAddresses();
|
||||
|
||||
addressByVxtMangledName32 = new HashMap<>();
|
||||
addressByVxtMangledName64 = new HashMap<>();
|
||||
|
||||
if (vbtSymbols.size() + vftSymbols.size() != vxtAddresses32.size() ||
|
||||
vbtSymbols.size() + vftSymbols.size() != vxtAddresses64.size()) {
|
||||
throw new AssertException("Fatal: list sizes do not match");
|
||||
}
|
||||
int aCount = 0;
|
||||
for (String vbtSymbol : vbtSymbols) {
|
||||
addressByVxtMangledName32.put(vbtSymbol, vxtAddresses32.get(aCount));
|
||||
addressByVxtMangledName64.put(vbtSymbol, vxtAddresses64.get(aCount));
|
||||
aCount++;
|
||||
}
|
||||
for (String vftSymbol : vftSymbols) {
|
||||
addressByVxtMangledName32.put(vftSymbol, vxtAddresses32.get(aCount));
|
||||
addressByVxtMangledName64.put(vftSymbol, vxtAddresses64.get(aCount));
|
||||
aCount++;
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================================
|
||||
//==============================================================================================
|
||||
|
||||
// No tests at this point because of need to rework the design
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user