mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-11-21 19:42:14 +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.pdb.DefaultCompositeMember;
|
||||||
import ghidra.app.util.bin.format.pdb2.pdbreader.*;
|
import ghidra.app.util.bin.format.pdb2.pdbreader.*;
|
||||||
import ghidra.app.util.bin.format.pdb2.pdbreader.type.*;
|
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.program.model.data.*;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
import ghidra.util.exception.AssertException;
|
import ghidra.util.exception.AssertException;
|
||||||
@ -82,19 +83,25 @@ public class CompositeTypeApplier extends AbstractComplexTypeApplier {
|
|||||||
myApplicator.predefineClass(fixedSymbolPath);
|
myApplicator.predefineClass(fixedSymbolPath);
|
||||||
myComposite = new StructureDataType(categoryPath, fixedSymbolPath.getName(), size,
|
myComposite = new StructureDataType(categoryPath, fixedSymbolPath.getName(), size,
|
||||||
myApplicator.getDataTypeManager());
|
myApplicator.getDataTypeManager());
|
||||||
myClassType = new CppCompositeType(fixedSymbolPath, myComposite, mangledName);
|
myClassType =
|
||||||
|
new CppCompositeType(myApplicator.getRootPdbCategory(), fixedSymbolPath,
|
||||||
|
myComposite, mangledName);
|
||||||
myClassType.setClass();
|
myClassType.setClass();
|
||||||
}
|
}
|
||||||
else if (compositeMsType instanceof AbstractStructureMsType) {
|
else if (compositeMsType instanceof AbstractStructureMsType) {
|
||||||
myComposite = new StructureDataType(categoryPath, fixedSymbolPath.getName(), size,
|
myComposite = new StructureDataType(categoryPath, fixedSymbolPath.getName(), size,
|
||||||
myApplicator.getDataTypeManager());
|
myApplicator.getDataTypeManager());
|
||||||
myClassType = new CppCompositeType(fixedSymbolPath, myComposite, mangledName);
|
myClassType =
|
||||||
|
new CppCompositeType(myApplicator.getRootPdbCategory(), fixedSymbolPath,
|
||||||
|
myComposite, mangledName);
|
||||||
myClassType.setStruct();
|
myClassType.setStruct();
|
||||||
}
|
}
|
||||||
else if (compositeMsType instanceof AbstractUnionMsType) {
|
else if (compositeMsType instanceof AbstractUnionMsType) {
|
||||||
myComposite = new UnionDataType(categoryPath, fixedSymbolPath.getName(),
|
myComposite = new UnionDataType(categoryPath, fixedSymbolPath.getName(),
|
||||||
myApplicator.getDataTypeManager());
|
myApplicator.getDataTypeManager());
|
||||||
myClassType = new CppCompositeType(fixedSymbolPath, myComposite, mangledName);
|
myClassType =
|
||||||
|
new CppCompositeType(myApplicator.getRootPdbCategory(), fixedSymbolPath,
|
||||||
|
myComposite, mangledName);
|
||||||
myClassType.setUnion();
|
myClassType.setUnion();
|
||||||
}
|
}
|
||||||
else { // InterfaceMsType
|
else { // InterfaceMsType
|
||||||
@ -183,7 +190,7 @@ public class CompositeTypeApplier extends AbstractComplexTypeApplier {
|
|||||||
// we do it here. Set breakpoint here to investigate.
|
// we do it here. Set breakpoint here to investigate.
|
||||||
}
|
}
|
||||||
classType.createLayout(applicator.getPdbApplicatorOptions().getCompositeLayout(),
|
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 {
|
throws PdbException, CancelledException {
|
||||||
|
|
||||||
AbstractCompositeMsType cType = (AbstractCompositeMsType) type;
|
AbstractCompositeMsType cType = (AbstractCompositeMsType) type;
|
||||||
ClassFieldAttributes.Access defaultAccess =
|
Access defaultAccess = (type instanceof AbstractClassMsType) ? Access.PRIVATE
|
||||||
(type instanceof AbstractClassMsType) ? ClassFieldAttributes.Access.PRIVATE
|
: Access.PUBLIC;
|
||||||
: ClassFieldAttributes.Access.PUBLIC;
|
|
||||||
|
|
||||||
for (AbstractMsType baseType : msBases) {
|
for (AbstractMsType baseType : msBases) {
|
||||||
applicator.checkCancelled();
|
applicator.checkCancelled();
|
||||||
@ -334,9 +340,8 @@ public class CompositeTypeApplier extends AbstractComplexTypeApplier {
|
|||||||
private void addMembers(Composite composite, CppCompositeType myClassType,
|
private void addMembers(Composite composite, CppCompositeType myClassType,
|
||||||
List<AbstractMemberMsType> msMembers, AbstractCompositeMsType type,
|
List<AbstractMemberMsType> msMembers, AbstractCompositeMsType type,
|
||||||
List<DefaultPdbUniversalMember> myMembers) throws CancelledException, PdbException {
|
List<DefaultPdbUniversalMember> myMembers) throws CancelledException, PdbException {
|
||||||
ClassFieldAttributes.Access defaultAccess =
|
Access defaultAccess =
|
||||||
(type instanceof AbstractClassMsType) ? ClassFieldAttributes.Access.PRIVATE
|
(type instanceof AbstractClassMsType) ? Access.PRIVATE : Access.PUBLIC;
|
||||||
: ClassFieldAttributes.Access.PUBLIC;
|
|
||||||
for (int index = 0; index < msMembers.size(); index++) {
|
for (int index = 0; index < msMembers.size(); index++) {
|
||||||
applicator.checkCancelled();
|
applicator.checkCancelled();
|
||||||
AbstractMemberMsType memberType = msMembers.get(index);
|
AbstractMemberMsType memberType = msMembers.get(index);
|
||||||
|
@ -4,9 +4,9 @@
|
|||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* 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.pdb.*;
|
||||||
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
|
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
|
||||||
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbLog;
|
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbLog;
|
||||||
import ghidra.app.util.pdb.pdbapplicator.PdbVbtManager.PdbVirtualBaseTable;
|
import ghidra.app.util.pdb.classtype.*;
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.*;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
import ghidra.util.exception.AssertException;
|
import ghidra.util.exception.AssertException;
|
||||||
@ -45,10 +45,11 @@ public class CppCompositeType {
|
|||||||
private List<Member> layoutMembers;
|
private List<Member> layoutMembers;
|
||||||
private List<Member> layoutVftPtrMembers;
|
private List<Member> layoutVftPtrMembers;
|
||||||
private boolean isFinal;
|
private boolean isFinal;
|
||||||
private Type type;
|
private ClassKey classKey;
|
||||||
private String className; // String for now.
|
private String className; // String for now.
|
||||||
private String mangledName;
|
private String mangledName;
|
||||||
private int size;
|
private int size;
|
||||||
|
private CategoryPath baseCategoryPath;
|
||||||
private SymbolPath symbolPath;
|
private SymbolPath symbolPath;
|
||||||
private Composite composite;
|
private Composite composite;
|
||||||
private CategoryPath categoryPath;
|
private CategoryPath categoryPath;
|
||||||
@ -80,7 +81,8 @@ public class CppCompositeType {
|
|||||||
private Map<Integer, PlaceholderVirtualBaseTable> placeholderVirtualBaseTables;
|
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(symbolPath, "symbolPath may not be null");
|
||||||
Objects.requireNonNull(composite, "composite may not be null");
|
Objects.requireNonNull(composite, "composite may not be null");
|
||||||
syntacticBaseClasses = new ArrayList<>();
|
syntacticBaseClasses = new ArrayList<>();
|
||||||
@ -92,7 +94,8 @@ public class CppCompositeType {
|
|||||||
layoutVftPtrMembers = new ArrayList<>();
|
layoutVftPtrMembers = new ArrayList<>();
|
||||||
|
|
||||||
isFinal = false;
|
isFinal = false;
|
||||||
type = Type.UNKNOWN;
|
classKey = ClassKey.UNKNOWN;
|
||||||
|
this.baseCategoryPath = baseCategoryPath;
|
||||||
this.symbolPath = symbolPath;
|
this.symbolPath = symbolPath;
|
||||||
this.composite = composite;
|
this.composite = composite;
|
||||||
placeholderVirtualBaseTables = new HashMap<>();
|
placeholderVirtualBaseTables = new HashMap<>();
|
||||||
@ -100,47 +103,51 @@ public class CppCompositeType {
|
|||||||
this.mangledName = mangledName;
|
this.mangledName = mangledName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CppClassType createCppClassType(SymbolPath symbolPath, Composite composite,
|
public static CppClassType createCppClassType(CategoryPath baseCategoryPath,
|
||||||
String mangledName) {
|
SymbolPath symbolPath, Composite composite, String mangledName) {
|
||||||
return new CppClassType(symbolPath, composite, mangledName);
|
return new CppClassType(baseCategoryPath, symbolPath, composite, mangledName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CppClassType createCppClassType(SymbolPath symbolPath, Composite composite,
|
public static CppClassType createCppClassType(CategoryPath baseCategoryPath,
|
||||||
String name, String mangledName, int size) {
|
SymbolPath symbolPath, Composite composite, String name, String mangledName, int size) {
|
||||||
CppClassType cppType = new CppClassType(symbolPath, composite, mangledName);
|
CppClassType cppType =
|
||||||
|
new CppClassType(baseCategoryPath, symbolPath, composite, mangledName);
|
||||||
cppType.setName(name);
|
cppType.setName(name);
|
||||||
cppType.setSize(size);
|
cppType.setSize(size);
|
||||||
return cppType;
|
return cppType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CppStructType createCppStructType(SymbolPath symbolPath, Composite composite,
|
public static CppStructType createCppStructType(CategoryPath baseCategoryPath,
|
||||||
String mangledName) {
|
SymbolPath symbolPath, Composite composite, String mangledName) {
|
||||||
return new CppStructType(symbolPath, composite, mangledName);
|
return new CppStructType(baseCategoryPath, symbolPath, composite, mangledName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CppStructType createCppStructType(SymbolPath symbolPath, Composite composite,
|
public static CppStructType createCppStructType(CategoryPath baseCategoryPath,
|
||||||
String name, String mangledName, int size) {
|
SymbolPath symbolPath, Composite composite, String name, String mangledName, int size) {
|
||||||
CppStructType cppType = new CppStructType(symbolPath, composite, mangledName);
|
CppStructType cppType =
|
||||||
|
new CppStructType(baseCategoryPath, symbolPath, composite, mangledName);
|
||||||
cppType.setName(name);
|
cppType.setName(name);
|
||||||
cppType.setSize(size);
|
cppType.setSize(size);
|
||||||
return cppType;
|
return cppType;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class CppClassType extends CppCompositeType {
|
private static class CppClassType extends CppCompositeType {
|
||||||
private CppClassType(SymbolPath symbolPath, Composite composite, String mangledName) {
|
private CppClassType(CategoryPath baseCategoryPath, SymbolPath symbolPath,
|
||||||
super(symbolPath, composite, mangledName);
|
Composite composite, String mangledName) {
|
||||||
|
super(baseCategoryPath, symbolPath, composite, mangledName);
|
||||||
setClass();
|
setClass();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class CppStructType extends CppCompositeType {
|
private static class CppStructType extends CppCompositeType {
|
||||||
private CppStructType(SymbolPath symbolPath, Composite composite, String mangledName) {
|
private CppStructType(CategoryPath baseCategoryPath, SymbolPath symbolPath,
|
||||||
super(symbolPath, composite, mangledName);
|
Composite composite, String mangledName) {
|
||||||
|
super(composite.getCategoryPath(), symbolPath, composite, mangledName);
|
||||||
setStruct();
|
setStruct();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static boolean validateMangledCompositeName(String mangledCompositeTypeName, Type type) {
|
static boolean validateMangledCompositeName(String mangledCompositeTypeName, ClassKey type) {
|
||||||
if (mangledCompositeTypeName == null) {
|
if (mangledCompositeTypeName == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -155,17 +162,20 @@ public class CppCompositeType {
|
|||||||
}
|
}
|
||||||
switch (mangledCompositeTypeName.charAt(3)) {
|
switch (mangledCompositeTypeName.charAt(3)) {
|
||||||
case 'T':
|
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);
|
PdbLog.message("Warning: Mismatched complex type 'T' for " + type);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'U':
|
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);
|
PdbLog.message("Warning: Mismatched complex type 'U' for " + type);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'V':
|
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);
|
PdbLog.message("Warning: Mismatched complex type 'V' for " + type);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -200,7 +210,7 @@ public class CppCompositeType {
|
|||||||
return composite;
|
return composite;
|
||||||
}
|
}
|
||||||
|
|
||||||
private CategoryPath getCategoryPath() {
|
public CategoryPath getCategoryPath() {
|
||||||
return categoryPath;
|
return categoryPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -213,20 +223,20 @@ public class CppCompositeType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setClass() {
|
public void setClass() {
|
||||||
type = Type.CLASS;
|
classKey = ClassKey.CLASS;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setStruct() {
|
public void setStruct() {
|
||||||
type = Type.STRUCT;
|
classKey = ClassKey.STRUCT;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setUnion() {
|
public void setUnion() {
|
||||||
type = Type.UNION;
|
classKey = ClassKey.UNION;
|
||||||
}
|
}
|
||||||
|
|
||||||
// not sure if user can see Type when returned.
|
// not sure if user can see Type when returned.
|
||||||
public Type getType() {
|
public ClassKey getType() {
|
||||||
return type;
|
return classKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setName(String className) {
|
public void setName(String className) {
|
||||||
@ -237,6 +247,10 @@ public class CppCompositeType {
|
|||||||
return className;
|
return className;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DataTypePath getDataTypePath() {
|
||||||
|
return composite.getDataTypePath();
|
||||||
|
}
|
||||||
|
|
||||||
public void setMangledName(String mangledName) {
|
public void setMangledName(String mangledName) {
|
||||||
this.mangledName = mangledName;
|
this.mangledName = mangledName;
|
||||||
}
|
}
|
||||||
@ -563,7 +577,7 @@ public class CppCompositeType {
|
|||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
builder.append(type);
|
builder.append(classKey);
|
||||||
builder.append(className);
|
builder.append(className);
|
||||||
if (isFinal) {
|
if (isFinal) {
|
||||||
builder.append(" final");
|
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) {
|
for (SyntacticBaseClass base : syntacticBaseClasses) {
|
||||||
if (base instanceof DirectSyntacticBaseClass) {
|
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 {
|
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
|
// TODO: both same for now
|
||||||
//doSpeculativeLayout(vbtManager, monitor);
|
//doSpeculativeLayout(vxtManager, monitor);
|
||||||
createVbtBasedLayout(layoutOptions, vbtManager, monitor);
|
createVbtBasedLayout(layoutOptions, vxtManager, monitor);
|
||||||
}
|
}
|
||||||
else {
|
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 {
|
TaskMonitor monitor) throws PdbException, CancelledException {
|
||||||
CategoryPath cn;
|
CategoryPath cn;
|
||||||
hasDirect = false;
|
hasDirect = false;
|
||||||
@ -667,7 +681,7 @@ public class CppCompositeType {
|
|||||||
|
|
||||||
// TODO: consider moving down below next line.
|
// TODO: consider moving down below next line.
|
||||||
boolean allVbtFound =
|
boolean allVbtFound =
|
||||||
reconcileVirtualBaseTables(composite.getDataTypeManager(), vbtManager);
|
reconcileVirtualBaseTables(composite.getDataTypeManager(), vxtManager);
|
||||||
|
|
||||||
addLayoutPdbMembers(directClassPdbMembers, layoutMembers);
|
addLayoutPdbMembers(directClassPdbMembers, layoutMembers);
|
||||||
insertVirtualFunctionTablePointers(directClassPdbMembers);
|
insertVirtualFunctionTablePointers(directClassPdbMembers);
|
||||||
@ -772,7 +786,7 @@ public class CppCompositeType {
|
|||||||
//----------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------
|
||||||
//----------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------
|
||||||
public void createSpeculativeLayout(ObjectOrientedClassLayout layoutOptions,
|
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
|
// 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
|
// 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
|
// 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.
|
// TODO: consider moving down below next line.
|
||||||
boolean allVbtFound =
|
boolean allVbtFound =
|
||||||
reconcileVirtualBaseTables(composite.getDataTypeManager(), vbtManager);
|
reconcileVirtualBaseTables(composite.getDataTypeManager(), vxtManager);
|
||||||
|
|
||||||
addLayoutPdbMembers(directClassPdbMembers, layoutMembers);
|
addLayoutPdbMembers(directClassPdbMembers, layoutMembers);
|
||||||
insertVirtualFunctionTablePointers(directClassPdbMembers);
|
insertVirtualFunctionTablePointers(directClassPdbMembers);
|
||||||
@ -943,7 +957,7 @@ public class CppCompositeType {
|
|||||||
return placeholderVirtualBaseTables;
|
return placeholderVirtualBaseTables;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean reconcileVirtualBaseTables(DataTypeManager dtm, VbtManager vbtManager)
|
private boolean reconcileVirtualBaseTables(DataTypeManager dtm, VxtManager vxtManager)
|
||||||
throws PdbException {
|
throws PdbException {
|
||||||
if (placeholderVirtualBaseTables.size() > 1) {
|
if (placeholderVirtualBaseTables.size() > 1) {
|
||||||
// study this.
|
// study this.
|
||||||
@ -957,14 +971,14 @@ public class CppCompositeType {
|
|||||||
if (!table.validateOffset()) {
|
if (!table.validateOffset()) {
|
||||||
// TODO study this.
|
// TODO study this.
|
||||||
}
|
}
|
||||||
DataType vbptr = getVbptrDataType(dtm, vbtManager, table);
|
DataType vbptr = getVbptrDataType(dtm, vxtManager, table);
|
||||||
allVbtFound &=
|
allVbtFound &=
|
||||||
addOrUpdateVbtAndVbtptrMember(vbtManager, table, vbptr, vbtptrOffset, getName());
|
addOrUpdateVbtAndVbtptrMember(vxtManager, table, vbptr, vbtptrOffset, getName());
|
||||||
}
|
}
|
||||||
return allVbtFound;
|
return allVbtFound;
|
||||||
}
|
}
|
||||||
|
|
||||||
private DataType getVbptrDataType(DataTypeManager dtm, VbtManager vbtManager,
|
private DataType getVbptrDataType(DataTypeManager dtm, VxtManager vxtManager,
|
||||||
PlaceholderVirtualBaseTable table) {
|
PlaceholderVirtualBaseTable table) {
|
||||||
DataType vbptr = null;
|
DataType vbptr = null;
|
||||||
for (int index = 1; index < table.getMaxOffset(); index++) {
|
for (int index = 1; index < table.getMaxOffset(); index++) {
|
||||||
@ -975,106 +989,67 @@ public class CppCompositeType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (vbptr == null) {
|
if (vbptr == null) {
|
||||||
vbptr = vbtManager.getFallbackVbptr();
|
vbptr = vxtManager.getDefaultVbtPtr();
|
||||||
}
|
}
|
||||||
return vbptr;
|
return vbptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class CppCompositeAndMember {
|
private record CppParentageAndMember(List<CppCompositeType> parentage, Member member) {}
|
||||||
private CppCompositeType cppType;
|
|
||||||
private Member member;
|
|
||||||
|
|
||||||
private CppCompositeAndMember(CppCompositeType cppType, Member member) {
|
private boolean addOrUpdateVbtAndVbtptrMember(VxtManager vxtManager,
|
||||||
this.cppType = cppType;
|
|
||||||
this.member = member;
|
|
||||||
}
|
|
||||||
|
|
||||||
private CppCompositeType getComposite() {
|
|
||||||
return cppType;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Member getMember() {
|
|
||||||
return member;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean addOrUpdateVbtAndVbtptrMember(VbtManager vbtManager,
|
|
||||||
PlaceholderVirtualBaseTable table, DataType vbptr, int vbtptrOffset, String myClass)
|
PlaceholderVirtualBaseTable table, DataType vbptr, int vbtptrOffset, String myClass)
|
||||||
throws PdbException {
|
throws PdbException {
|
||||||
|
|
||||||
List<String> subMangled = new ArrayList<>();
|
// TODO: if we want to match vbtables with the pointers in *virtual* base classes, we are
|
||||||
//subMangled.add(getMangledName());
|
// not currently doing the work... we are only looking for direct bases, as they are
|
||||||
CppCompositeAndMember cAndM = findDirectBaseCompositeAndMember(this, 0, vbtptrOffset);
|
// what dictate the placement for our current class (though cross-checks could be done
|
||||||
if (cAndM == null) {
|
// 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);
|
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}.";
|
String message = "PDB: Collision of non-{vbptr}.";
|
||||||
PdbLog.message(message);
|
PdbLog.message(message);
|
||||||
Msg.info(this, message);
|
Msg.info(this, message);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
CppCompositeType compositeThatContainsMember = cAndM.getComposite();
|
parentage = cAndP.parentage();
|
||||||
String mangled = compositeThatContainsMember.getMangledName();
|
|
||||||
subMangled.add(mangled);
|
|
||||||
}
|
}
|
||||||
if (!(vbtManager instanceof PdbVbtManager)) {
|
if (!(vxtManager instanceof MsftVxtManager mvxtManager)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
int entrySize = 4; // Default to something (could be wrong)
|
int entrySize = 4; // Default to something (could be wrong)
|
||||||
if (vbptr instanceof PointerDataType) {
|
if (vbptr instanceof PointerDataType) {
|
||||||
entrySize = ((PointerDataType) vbptr).getDataType().getLength();
|
entrySize = ((PointerDataType) vbptr).getDataType().getLength();
|
||||||
}
|
}
|
||||||
|
boolean x = findVbt(table, mvxtManager, entrySize, symbolPath, parentage);
|
||||||
return findVbtBySymbolConstruction(table, (PdbVbtManager) vbtManager, entrySize,
|
return x;
|
||||||
getMangledName(), type, subMangled);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean findVbtBySymbolConstruction(PlaceholderVirtualBaseTable table,
|
private boolean findVbt(PlaceholderVirtualBaseTable table, MsftVxtManager mvbtm, int entrySize,
|
||||||
PdbVbtManager vbtm, int entrySize, String mangledCompositeTypeName, Type mainType,
|
SymbolPath ownerSp, List<CppCompositeType> parentage) {
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean findAndUpdate(PlaceholderVirtualBaseTable table, PdbVbtManager vbtm, int entrySize,
|
ClassID mId = new ProgramClassID(baseCategoryPath, ownerSp);
|
||||||
String mangledTableName) {
|
List<ClassID> cIdParentage = new ArrayList<>();
|
||||||
PdbVirtualBaseTable vbt = vbtm.createVirtualBaseTableByName(mangledTableName, entrySize);
|
for (CppCompositeType t : parentage) {
|
||||||
if (vbt == null) {
|
ClassID id = new ProgramClassID(t.baseCategoryPath, t.getSymbolPath());
|
||||||
return false;
|
cIdParentage.add(id);
|
||||||
}
|
}
|
||||||
table.setName(mangledTableName);
|
ProgramVirtualBaseTable vbt = (ProgramVirtualBaseTable) mvbtm.findPrimaryVbt(mId);
|
||||||
|
//ProgramVirtualBaseTable vbt = (ProgramVirtualBaseTable) mvbtm.findVbt(mId, cIdParentage);
|
||||||
|
|
||||||
table.setVirtualBaseTable(vbt);
|
table.setVirtualBaseTable(vbt);
|
||||||
return true;
|
|
||||||
|
return vbt != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private CppCompositeAndMember findDirectBaseCompositeAndMember(CppCompositeType cppType,
|
private CppParentageAndMember findDirectBaseParentageAndMember(CppCompositeType cppType,
|
||||||
int offsetCppType, int vbtptrOffset) throws PdbException {
|
int offsetCppType, int vbtptrOffset) throws PdbException {
|
||||||
for (LayoutBaseClass base : cppType.layoutBaseClasses) {
|
for (LayoutBaseClass base : cppType.layoutBaseClasses) {
|
||||||
if (!(base instanceof DirectLayoutBaseClass)) {
|
if (!(base instanceof DirectLayoutBaseClass)) {
|
||||||
@ -1086,16 +1061,18 @@ public class CppCompositeType {
|
|||||||
if (vbtptrOffset >= directBaseOffset &&
|
if (vbtptrOffset >= directBaseOffset &&
|
||||||
vbtptrOffset < directBaseOffset + directBaseLength) {
|
vbtptrOffset < directBaseOffset + directBaseLength) {
|
||||||
CppCompositeType childCppType = directBase.getBaseClassType();
|
CppCompositeType childCppType = directBase.getBaseClassType();
|
||||||
CppCompositeAndMember cAndM =
|
CppParentageAndMember cAndP =
|
||||||
findDirectBaseCompositeAndMember(childCppType, directBaseOffset, vbtptrOffset);
|
findDirectBaseParentageAndMember(childCppType, directBaseOffset, vbtptrOffset);
|
||||||
if (cAndM == null) {
|
if (cAndP == null) {
|
||||||
Member member = childCppType.findLayoutMemberOrVftPtrMember(vbtptrOffset);
|
Member member = childCppType.findLayoutMemberOrVftPtrMember(vbtptrOffset);
|
||||||
if (member == null) {
|
if (member == null) {
|
||||||
return 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;
|
return null;
|
||||||
@ -1674,7 +1651,7 @@ public class CppCompositeType {
|
|||||||
//----------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------
|
||||||
static class PlaceholderVirtualBaseTable {
|
static class PlaceholderVirtualBaseTable {
|
||||||
private String name;
|
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
|
// 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.
|
// the set of sequential integers is assured, we could create a list.
|
||||||
@ -1697,19 +1674,19 @@ public class CppCompositeType {
|
|||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setVirtualBaseTable(PdbVirtualBaseTable pdbVirtualBaseTable) {
|
void setVirtualBaseTable(ProgramVirtualBaseTable vbt) {
|
||||||
this.pdbVirtualBaseTable = pdbVirtualBaseTable;
|
this.vbt = vbt;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean canLookupOffset() {
|
boolean canLookupOffset() {
|
||||||
return pdbVirtualBaseTable != null;
|
return vbt != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
long getOffset(int ordinal) throws PdbException {
|
long getOffset(int ordinal) throws PdbException {
|
||||||
if (pdbVirtualBaseTable == null) {
|
if (vbt != null) {
|
||||||
throw new PdbException("pdbVirtualBaseTable not initialized");
|
return vbt.getBaseOffset(ordinal);
|
||||||
}
|
}
|
||||||
return pdbVirtualBaseTable.getOffset(ordinal);
|
throw new PdbException("pdbVirtualBaseTable not initialized");
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<Integer, PlaceholderVirtualBaseTableEntry> getEntries() {
|
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.bin.format.pe.cli.tables.CliAbstractTableRow;
|
||||||
import ghidra.app.util.importer.MessageLog;
|
import ghidra.app.util.importer.MessageLog;
|
||||||
import ghidra.app.util.pdb.PdbCategories;
|
import ghidra.app.util.pdb.PdbCategories;
|
||||||
|
import ghidra.app.util.pdb.classtype.*;
|
||||||
import ghidra.framework.options.Options;
|
import ghidra.framework.options.Options;
|
||||||
import ghidra.program.database.data.DataTypeUtilities;
|
import ghidra.program.database.data.DataTypeUtilities;
|
||||||
import ghidra.program.disassemble.DisassemblerContextImpl;
|
import ghidra.program.disassemble.DisassemblerContextImpl;
|
||||||
@ -190,6 +191,7 @@ public class DefaultPdbApplicator implements PdbApplicator {
|
|||||||
private Address imageBase;
|
private Address imageBase;
|
||||||
private int linkerModuleNumber = -1;
|
private int linkerModuleNumber = -1;
|
||||||
private DataTypeManager dataTypeManager;
|
private DataTypeManager dataTypeManager;
|
||||||
|
private ClassTypeManager classTypeManager;
|
||||||
private PdbAddressManager pdbAddressManager;
|
private PdbAddressManager pdbAddressManager;
|
||||||
private List<SymbolGroup> symbolGroups;
|
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.
|
// If we have symbols and memory with VBTs in them, then a better VbtManager is created.
|
||||||
VbtManager vbtManager;
|
private VxtManager vxtManager;
|
||||||
PdbRegisterNameToProgramRegisterMapper registerNameToRegisterMapper;
|
private PdbRegisterNameToProgramRegisterMapper registerNameToRegisterMapper;
|
||||||
|
|
||||||
//==============================================================================================
|
//==============================================================================================
|
||||||
private MultiphaseDataTypeResolver multiphaseResolver;
|
private MultiphaseDataTypeResolver multiphaseResolver;
|
||||||
private int resolveCount;
|
private int resolveCount;
|
||||||
private int conflictCount;
|
private int conflictCount;
|
||||||
private PdbCategories categoryUtils;
|
private PdbCategories pdbCategories;
|
||||||
private PdbPrimitiveTypeApplicator pdbPrimitiveTypeApplicator;
|
private PdbPrimitiveTypeApplicator pdbPrimitiveTypeApplicator;
|
||||||
private TypeApplierFactory typeApplierParser;
|
private TypeApplierFactory typeApplierParser;
|
||||||
// We may need to put the following map into the "analysis state" for access by
|
// 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);
|
pdbPeHeaderInfoManager = new PdbPeHeaderInfoManager(this);
|
||||||
|
|
||||||
multiphaseResolver = new MultiphaseDataTypeResolver(this);
|
multiphaseResolver = new MultiphaseDataTypeResolver(this);
|
||||||
|
|
||||||
|
classTypeManager = new ClassTypeManager(dataTypeManager);
|
||||||
|
|
||||||
pdbPrimitiveTypeApplicator = new PdbPrimitiveTypeApplicator(dataTypeManager);
|
pdbPrimitiveTypeApplicator = new PdbPrimitiveTypeApplicator(dataTypeManager);
|
||||||
|
|
||||||
typeApplierParser = new TypeApplierFactory(this);
|
typeApplierParser = new TypeApplierFactory(this);
|
||||||
@ -627,17 +632,21 @@ public class DefaultPdbApplicator implements PdbApplicator {
|
|||||||
if (!pdbAddressManager.isInitialized()) {
|
if (!pdbAddressManager.isInitialized()) {
|
||||||
pdbAddressManager.initialize(this, imageBase);
|
pdbAddressManager.initialize(this, imageBase);
|
||||||
}
|
}
|
||||||
categoryUtils = setPdbCatogoryUtils(pdb.getFilename());
|
pdbCategories = setPdbCatogoryUtils(pdb.getFilename());
|
||||||
symbolGroups = createSymbolGroups();
|
symbolGroups = createSymbolGroups();
|
||||||
linkerModuleNumber = findLinkerModuleNumber();
|
linkerModuleNumber = findLinkerModuleNumber();
|
||||||
if (program != null) {
|
if (program != null) {
|
||||||
// Currently, this must happen after symbolGroups are created.
|
// Currently, this must happen after symbolGroups are created.
|
||||||
PdbVbtManager pdbVbtManager = new PdbVbtManager(this);
|
MsftVxtManager msftVxtManager =
|
||||||
vbtManager = pdbVbtManager;
|
new MsftVxtManager(getClassTypeManager(), program.getMemory());
|
||||||
|
msftVxtManager.createVirtualTables(getRootPdbCategory(), findVirtualTableSymbols(), log,
|
||||||
|
monitor);
|
||||||
|
vxtManager = msftVxtManager;
|
||||||
|
|
||||||
registerNameToRegisterMapper = new PdbRegisterNameToProgramRegisterMapper(program);
|
registerNameToRegisterMapper = new PdbRegisterNameToProgramRegisterMapper(program);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
vbtManager = new VbtManager(getDataTypeManager());
|
vxtManager = new VxtManager(getClassTypeManager());
|
||||||
}
|
}
|
||||||
preWorkDone = true;
|
preWorkDone = true;
|
||||||
}
|
}
|
||||||
@ -689,6 +698,47 @@ public class DefaultPdbApplicator implements PdbApplicator {
|
|||||||
return mySymbolGroups;
|
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.
|
// Basic utility methods.
|
||||||
//==============================================================================================
|
//==============================================================================================
|
||||||
@ -807,6 +857,10 @@ public class DefaultPdbApplicator implements PdbApplicator {
|
|||||||
return dataTypeManager;
|
return dataTypeManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ClassTypeManager getClassTypeManager() {
|
||||||
|
return classTypeManager;
|
||||||
|
}
|
||||||
|
|
||||||
// for PdbTypeApplicator (new)
|
// for PdbTypeApplicator (new)
|
||||||
DataOrganization getDataOrganization() {
|
DataOrganization getDataOrganization() {
|
||||||
return dataTypeManager.getDataOrganization();
|
return dataTypeManager.getDataOrganization();
|
||||||
@ -819,6 +873,14 @@ public class DefaultPdbApplicator implements PdbApplicator {
|
|||||||
//==============================================================================================
|
//==============================================================================================
|
||||||
// CategoryPath-related methods.
|
// 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
|
* Get the {@link CategoryPath} associated with the {@link SymbolPath} specified, rooting
|
||||||
* it either at the PDB Category
|
* it either at the PDB Category
|
||||||
@ -827,7 +889,7 @@ public class DefaultPdbApplicator implements PdbApplicator {
|
|||||||
* @return {@link CategoryPath} created for the input
|
* @return {@link CategoryPath} created for the input
|
||||||
*/
|
*/
|
||||||
CategoryPath getCategory(SymbolPath symbolPath) {
|
CategoryPath getCategory(SymbolPath symbolPath) {
|
||||||
return categoryUtils.getCategory(symbolPath);
|
return pdbCategories.getCategory(symbolPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -839,7 +901,7 @@ public class DefaultPdbApplicator implements PdbApplicator {
|
|||||||
* @return the CategoryPath
|
* @return the CategoryPath
|
||||||
*/
|
*/
|
||||||
CategoryPath getTypedefsCategory(int moduleNumber, SymbolPath symbolPath) {
|
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}
|
* @return the {@link CategoryPath}
|
||||||
*/
|
*/
|
||||||
CategoryPath getAnonymousFunctionsCategory() {
|
CategoryPath getAnonymousFunctionsCategory() {
|
||||||
return categoryUtils.getAnonymousFunctionsCategory();
|
return pdbCategories.getAnonymousFunctionsCategory();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -855,7 +917,7 @@ public class DefaultPdbApplicator implements PdbApplicator {
|
|||||||
* @return the {@link CategoryPath}
|
* @return the {@link CategoryPath}
|
||||||
*/
|
*/
|
||||||
CategoryPath getAnonymousTypesCategory() {
|
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() {
|
VxtManager getVxtManager() {
|
||||||
return vbtManager;
|
return vxtManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
//==============================================================================================
|
//==============================================================================================
|
||||||
@ -2299,8 +2361,7 @@ public class DefaultPdbApplicator implements PdbApplicator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Symbol createSymbolInternal(Address address, SymbolPath symbolPath,
|
private Symbol createSymbolInternal(Address address, SymbolPath symbolPath,
|
||||||
boolean isNewFunctionSignature,
|
boolean isNewFunctionSignature, String plateAddition) {
|
||||||
String plateAddition) {
|
|
||||||
|
|
||||||
Symbol existingSymbol = program.getSymbolTable().getPrimarySymbol(address);
|
Symbol existingSymbol = program.getSymbolTable().getPrimarySymbol(address);
|
||||||
if (existingSymbol == null || isNewFunctionSignature) {
|
if (existingSymbol == null || isNewFunctionSignature) {
|
||||||
|
@ -4,9 +4,9 @@
|
|||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@ -16,6 +16,7 @@
|
|||||||
package ghidra.app.util.pdb.pdbapplicator;
|
package ghidra.app.util.pdb.pdbapplicator;
|
||||||
|
|
||||||
import ghidra.app.util.bin.format.pdb.*;
|
import ghidra.app.util.bin.format.pdb.*;
|
||||||
|
import ghidra.app.util.pdb.classtype.ClassFieldAttributes;
|
||||||
import ghidra.program.model.data.DataType;
|
import ghidra.program.model.data.DataType;
|
||||||
import ghidra.util.exception.CancelledException;
|
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