GP-5074 - CPP PDB vxtable datatype composition

This commit is contained in:
ghizard
2025-03-24 06:51:40 -04:00
parent ce6bef1e12
commit edb277177d
27 changed files with 1545 additions and 533 deletions
@@ -58,6 +58,8 @@ public class ClassFieldAttributes {
};
Property myProperty = switch (msAtts.getProperty()) {
case VIRTUAL -> Property.VIRTUAL;
case INTRO -> Property.VIRTUAL; // VIRTUAL for now; consider change, consider ELF
case INTRO_PURE -> Property.VIRTUAL; // VIRTUAL for now; consider change, consider ELF
case STATIC -> Property.STATIC;
case FRIEND -> Property.FRIEND;
case BLANK -> Property.BLANK;
@@ -17,6 +17,7 @@ package ghidra.app.util.pdb.classtype;
import ghidra.app.util.SymbolPath;
import ghidra.program.model.data.*;
import ghidra.program.model.gclass.ClassID;
/**
* Class Type Manager
@@ -44,10 +45,7 @@ public class ClassTypeManager {
}
public SymbolPath getSymbolPath(ClassID classId) {
if (classId instanceof ProgramClassID gId) {
return gId.getSymbolPath();
}
return null;
return classId.getSymbolPath();
}
/**
@@ -25,6 +25,9 @@ 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.data.DataTypeManager;
import ghidra.program.model.gclass.ClassID;
import ghidra.program.model.gclass.ClassUtils;
import ghidra.program.model.listing.Program;
import ghidra.util.Msg;
import ghidra.util.exception.AssertException;
@@ -121,8 +124,8 @@ public class MsftVxtManager extends VxtManager {
// 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;
private Map<ClassID, TreeMap<Address, VirtualBaseTable>> vbtsByOwner;
private Map<ClassID, TreeMap<Address, VirtualFunctionTable>> vftsByOwner;
// Used for locating vft and vbt
// These are explicitly used for storing/retrieving the "program" versions which result from
@@ -131,6 +134,9 @@ public class MsftVxtManager extends VxtManager {
private ParentageNode vbtRoot;
private ParentageNode vftRoot;
private Map<OwnerParentage, VirtualBaseTable> vbtsByOwnerParentage;
private Map<OwnerParentage, VirtualFunctionTable> vftsByOwnerParentage;
/**
* Constructor for this class
* @param ctm the class type manager
@@ -145,15 +151,32 @@ public class MsftVxtManager extends VxtManager {
vftsByOwner = new HashMap<>();
vbtRoot = new ParentageNode(null);
vftRoot = new ParentageNode(null);
vbtsByOwnerParentage = new HashMap<>();
vftsByOwnerParentage = new HashMap<>();
}
/**
* Finds the putative {@link VBTable} in memory requested for the owning class
* Create the vxtable structures
* @param dtm the data type manager
*/
public void doTableLayouts(DataTypeManager dtm) {
for (VirtualBaseTable vbt : vbtsByOwnerParentage.values()) {
ClassID id = vbt.getOwner();
vbt.getLayout(dtm, ClassUtils.getClassInternalsPath(id));
}
for (VirtualFunctionTable vft : vftsByOwnerParentage.values()) {
ClassID id = vft.getOwner();
vft.getLayout(dtm, ClassUtils.getClassInternalsPath(id));
}
}
/**
* Finds the putative {@link VirtualBaseTable} 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);
public VirtualBaseTable findPrimaryVbt(ClassID owner) {
TreeMap<Address, VirtualBaseTable> map = vbtsByOwner.get(owner);
if (map == null) {
return null;
}
@@ -161,12 +184,12 @@ public class MsftVxtManager extends VxtManager {
}
/**
* Finds the putative {@link VFTable} in memory requested for the owning class
* Finds the putative {@link VirtualFunctionTable} 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);
public VirtualFunctionTable findPrimaryVft(ClassID owner) {
TreeMap<Address, VirtualFunctionTable> map = vftsByOwner.get(owner);
if (map == null) {
return null;
}
@@ -194,35 +217,35 @@ public class MsftVxtManager extends VxtManager {
}
/**
* Returns the ordered in-memory {@link VBTable}s for the owning class
* Returns the ordered in-memory {@link VirtualBaseTable}s for the owning class
* @param owner the owning class of the table
* @return the tables
*/
public VBTable[] getVbts(ClassID owner) {
TreeMap<Address, VBTable> map = vbtsByOwner.get(owner);
public VirtualBaseTable[] getVbts(ClassID owner) {
TreeMap<Address, VirtualBaseTable> map = vbtsByOwner.get(owner);
if (map == null) {
return null;
}
Collection<VBTable> values = map.values();
return values.toArray(new VBTable[values.size()]);
Collection<VirtualBaseTable> values = map.values();
return values.toArray(new VirtualBaseTable[values.size()]);
}
/**
* Returns the ordered in-memory {@link VFTable}s for the owning class
* Returns the ordered in-memory {@link VirtualFunctionTable}s for the owning class
* @param owner the owning class of the table
* @return the tables
*/
public VFTable[] getVfts(ClassID owner) {
TreeMap<Address, VFTable> map = vftsByOwner.get(owner);
public VirtualFunctionTable[] getVfts(ClassID owner) {
TreeMap<Address, VirtualFunctionTable> map = vftsByOwner.get(owner);
if (map == null) {
return null;
}
Collection<VFTable> values = map.values();
return values.toArray(new VFTable[values.size()]);
Collection<VirtualFunctionTable> values = map.values();
return values.toArray(new VirtualFunctionTable[values.size()]);
}
/**
* Finds the putative {@link VBTable} in memory requested for the owning class and the
* Finds the putative {@link VirtualBaseTable} 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
@@ -230,12 +253,26 @@ public class MsftVxtManager extends VxtManager {
* that class through all of its decendents to the owner, excluding the owner
* @return the table
*/
public VBTable findVbt(ClassID owner, List<ClassID> parentage) {
public VirtualBaseTable findVbt(ClassID owner, List<ClassID> parentage) {
OwnerParentage op = new OwnerParentage(owner, parentage);
VirtualBaseTable vbt = vbtsByOwnerParentage.get(op);
if (vbt != null) {
return vbt;
}
vbt = searchVbtTree(owner, parentage);
if (vbt == null) {
vbt = new PlaceholderVirtualBaseTable(owner, parentage);
}
vbtsByOwnerParentage.put(op, vbt);
return vbt;
}
private VirtualBaseTable searchVbtTree(ClassID owner, List<ClassID> parentage) {
ParentageNode node = findNode(owner, parentage, vbtRoot);
if (node == null) {
return null;
}
VBTable vbTable = node.getVBTable();
VirtualBaseTable vbTable = node.getVBTable();
if (vbTable != null || !parentage.isEmpty()) { // see note below
return vbTable;
}
@@ -253,20 +290,35 @@ public class MsftVxtManager extends VxtManager {
}
/**
* Finds the putative {@link VFTable} in memory requested for the owning class and the
* specified parentage
* Finds the putative {@link VirtualFunctionTable} 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) {
public VirtualFunctionTable findVft(ClassID owner, List<ClassID> parentage) {
OwnerParentage op = new OwnerParentage(owner, parentage);
VirtualFunctionTable vft = vftsByOwnerParentage.get(op);
if (vft != null) {
return vft;
}
vft = searchVftTree(owner, parentage);
if (vft == null) {
vft = new PlaceholderVirtualFunctionTable(owner, parentage);
}
vftsByOwnerParentage.put(op, vft);
return vft;
}
private VirtualFunctionTable searchVftTree(ClassID owner, List<ClassID> parentage) {
ParentageNode node = findNode(owner, parentage, vftRoot);
if (node == null) {
return null;
}
VFTable vfTable = node.getVFTable();
VirtualFunctionTable vfTable = node.getVFTable();
if (vfTable != null || !parentage.isEmpty()) { // see note below
return vfTable;
}
@@ -357,7 +409,8 @@ public class MsftVxtManager extends VxtManager {
switch (demanglerResults.vtType()) {
case VBT:
ProgramVirtualBaseTable prvbt = new ProgramVirtualBaseTable(owner, parentage,
program, address, ctm.getDefaultVbtTableElementSize(), ctm, mangled);
program, address, ClassUtils.getVbtEntrySize(ctm.getDataTypeManager()),
mangled);
if (node.getVBTable() != null) {
Msg.warn(this, "VBT already exists at node for " + mangled);
return false;
@@ -385,8 +438,8 @@ public class MsftVxtManager extends VxtManager {
return true;
}
private void storeVbt(ClassID owner, Address address, VBTable vbt) {
TreeMap<Address, VBTable> map = vbtsByOwner.get(owner);
private void storeVbt(ClassID owner, Address address, VirtualBaseTable vbt) {
TreeMap<Address, VirtualBaseTable> map = vbtsByOwner.get(owner);
if (map == null) {
map = new TreeMap<>();
vbtsByOwner.put(owner, map);
@@ -394,8 +447,8 @@ public class MsftVxtManager extends VxtManager {
map.put(address, vbt);
}
private void storeVft(ClassID owner, Address address, VFTable vft) {
TreeMap<Address, VFTable> map = vftsByOwner.get(owner);
private void storeVft(ClassID owner, Address address, VirtualFunctionTable vft) {
TreeMap<Address, VirtualFunctionTable> map = vftsByOwner.get(owner);
if (map == null) {
map = new TreeMap<>();
vftsByOwner.put(owner, map);
@@ -404,10 +457,7 @@ public class MsftVxtManager extends VxtManager {
}
private ParentageNode findNode(ClassID owner, List<ClassID> parentage, ParentageNode root) {
if (!(owner instanceof ProgramClassID ownerGId)) {
return null;
}
SymbolPath ownerSp = ownerGId.getSymbolPath();
SymbolPath ownerSp = owner.getSymbolPath();
ParentageNode ownerNode = root.getBranch(ownerSp.toString());
if (ownerNode == null) {
return null;
@@ -415,10 +465,7 @@ public class MsftVxtManager extends VxtManager {
ParentageNode resultNode = null;
ParentageNode node = ownerNode;
for (ClassID id : parentage) {
if (!(id instanceof ProgramClassID gId)) {
return null;
}
SymbolPath sp = gId.getSymbolPath();
SymbolPath sp = id.getSymbolPath();
ParentageNode next = node.getBranch(sp.toString());
if (next != null) {
node = next;
@@ -445,19 +492,9 @@ public class MsftVxtManager extends VxtManager {
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());
node = node.getOrAddBranch(owner.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());
node = node.getOrAddBranch(id.getSymbolPath().toString());
}
return node;
}
@@ -511,9 +548,9 @@ public class MsftVxtManager extends VxtManager {
List<SymbolPath> parentageSps = getParentageSymbolPaths(vxTable.getNestedQualifications());
List<ClassID> parentage = new ArrayList<>();
ClassID owner = new ProgramClassID(categoryPath, ownerSp);
ClassID owner = new ClassID(categoryPath, ownerSp);
for (SymbolPath sp : parentageSps) {
ClassID user = new ProgramClassID(categoryPath, sp);
ClassID user = new ClassID(categoryPath, sp);
parentage.add(user); // owner is the parentage if parentageSps was empty
}
@@ -560,8 +597,8 @@ public class MsftVxtManager extends VxtManager {
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 VirtualFunctionTable vft;
private VirtualBaseTable vbt;
private ParentageNode(String name) {
this.name = name;
@@ -582,19 +619,19 @@ public class MsftVxtManager extends VxtManager {
return branches.get(branchName);
}
private void setVFTable(VFTable vftArg) {
private void setVFTable(VirtualFunctionTable vftArg) {
vft = vftArg;
}
private void setVBTable(VBTable vbtArg) {
private void setVBTable(VirtualBaseTable vbtArg) {
vbt = vbtArg;
}
private VFTable getVFTable() {
private VirtualFunctionTable getVFTable() {
return vft;
}
private VBTable getVBTable() {
private VirtualBaseTable getVBTable() {
return vbt;
}
@@ -15,17 +15,13 @@
*/
package ghidra.app.util.pdb.classtype;
import java.util.List;
import ghidra.program.model.gclass.ClassID;
/**
* 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
* Owner-Parentage combination for identifying a vxtable, its pointer, or a base class
* @param owner the owning class
* @param parentage the parentage of the base class or vxtable or vxtable pointer
*/
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();
}
public record OwnerParentage(ClassID owner, List<ClassID> parentage) {}
@@ -15,110 +15,76 @@
*/
package ghidra.app.util.pdb.classtype;
import java.util.*;
import java.util.List;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.program.model.gclass.ClassID;
/**
* Virtual Base Table without a program
*/
public class PlaceholderVirtualBaseTable extends VirtualBaseTable {
private int entrySize;
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 entrySize the size of the index field for each table entry as it would be in memory
*/
public PlaceholderVirtualBaseTable(ClassID owner, List<ClassID> parentage, int entrySize) {
public PlaceholderVirtualBaseTable(ClassID owner, List<ClassID> parentage) {
super(owner, parentage);
if (entrySize != 4 && entrySize != 8) {
throw new IllegalArgumentException("Invalid size (" + entrySize + "): must be 4 or 8.");
}
this.entrySize = entrySize;
}
/*
* 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 createTableDataType(int numEntries) {
}
int getMaxIndex() {
return maxIndexSeen;
}
public void setBaseClassOffsetAndId(int index, Long offset, ClassID baseId) {
VBTableEntry entry = entriesByIndex.get(index);
public void setBaseClassOffsetAndId(int tableIndex, Long offset, ClassID baseId) {
PlaceholderVirtualBaseTableEntry entry = entry(tableIndex);
if (entry == null) {
entry = new VirtualBaseTableEntry(offset, baseId);
entriesByIndex.put(index, entry);
entry = new PlaceholderVirtualBaseTableEntry(offset, baseId);
entryByTableIndex.put(tableIndex, entry);
maxTableIndexSeen = Integer.max(maxTableIndexSeen, tableIndex);
}
else {
entry.setOffset(offset);
entry.setClassId(baseId);
}
maxIndexSeen = Integer.max(maxIndexSeen, index);
}
public void setBaseClassId(int index, ClassID baseId) {
VBTableEntry entry = entriesByIndex.get(index);
public void setBaseOffset(int tableIndex, Long offset) {
PlaceholderVirtualBaseTableEntry entry = entry(tableIndex);
if (entry == null) {
entry = new VirtualBaseTableEntry(baseId);
entriesByIndex.put(index, entry);
}
else {
entry.setClassId(baseId);
}
maxIndexSeen = Integer.max(maxIndexSeen, index);
}
public void setBaseOffset(int index, Long offset) {
VBTableEntry entry = entriesByIndex.get(index);
if (entry == null) {
entry = new VirtualBaseTableEntry(offset);
entriesByIndex.put(index, entry);
entry = new PlaceholderVirtualBaseTableEntry(offset);
entryByTableIndex.put(tableIndex, entry);
maxTableIndexSeen = Integer.max(maxTableIndexSeen, tableIndex);
}
else {
entry.setOffset(offset);
}
maxIndexSeen = Integer.max(maxIndexSeen, index);
}
@Override
public Long getBaseOffset(int index) throws PdbException {
VBTableEntry entry = entriesByIndex.get(index);
public PlaceholderVirtualBaseTableEntry getEntry(int tableIndex) {
return (PlaceholderVirtualBaseTableEntry) entryByTableIndex.get(tableIndex);
}
@Override
public Long getBaseOffset(int tableIndex) throws PdbException {
PlaceholderVirtualBaseTableEntry entry = entry(tableIndex);
Long offset = (entry == null) ? null : entry.getOffset();
maxIndexSeen = Integer.max(maxIndexSeen, index);
return offset;
}
@Override
public ClassID getBaseClassId(int index) {
VBTableEntry entry = entriesByIndex.get(index);
ClassID id = (entry == null) ? null : entry.getClassId();
maxIndexSeen = Integer.max(maxIndexSeen, index);
return id;
protected PlaceholderVirtualBaseTableEntry getNewEntry(ClassID baseId) {
return new PlaceholderVirtualBaseTableEntry(baseId);
}
@Override
public VBTableEntry getBase(int index) throws PdbException {
VBTableEntry entry = entriesByIndex.get(index);
if (entry != null) {
maxIndexSeen = Integer.max(maxIndexSeen, index);
private PlaceholderVirtualBaseTableEntry entry(int tableIndex) {
return (PlaceholderVirtualBaseTableEntry) entryByTableIndex.get(tableIndex);
}
private PlaceholderVirtualBaseTableEntry existing(int tableIndex) throws PdbException {
PlaceholderVirtualBaseTableEntry entry = entry(tableIndex);
if (entry == null) {
throw new PdbException(
"No entry in Virtual Base Table for table index: " + tableIndex);
}
return entry;
}
@@ -0,0 +1,68 @@
/* ###
* 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.program.model.gclass.ClassID;
/**
* Represents the Entry for a Virtual Base Table
*/
public class PlaceholderVirtualBaseTableEntry extends VirtualBaseTableEntry {
private Long offset;
// Re-evaluate which constructors and setters we need
public PlaceholderVirtualBaseTableEntry(Long offset) {
this(offset, null);
}
public PlaceholderVirtualBaseTableEntry(ClassID baseId) {
this(null, baseId);
}
public PlaceholderVirtualBaseTableEntry(Long offset, ClassID baseId) {
super(baseId);
this.offset = offset;
}
public void setOffset(Long offset) {
this.offset = offset;
}
public Long getOffset() {
return offset;
}
//==============================================================================================
// Info from longer-running branch
void emit(StringBuilder builder, long vbtPtrOffset) {
// emitLine(builder, offset, vbtPtrOffset,
// getClassId(compiledBaseClass, baseClassComponent).toString());
emitLine(builder, getOffset(), vbtPtrOffset, getClassId().toString());
}
static void emitHeader(StringBuilder builder, long ownerOffset, long vbtPtrOffset) {
builder.append(String.format("%-10s %-10s %-10s\n", "OffInOwner", "OffFromPtr", "Base"));
emitLine(builder, ownerOffset, vbtPtrOffset, "'ForClass'");
}
static void emitLine(StringBuilder builder, long classOffset, long vbtPtrOffset, String owner) {
builder.append(
String.format("%-10d %-10d %-10s\n", classOffset, classOffset - vbtPtrOffset, owner));
}
}
@@ -0,0 +1,93 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.pdb.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.data.Pointer;
import ghidra.program.model.gclass.ClassID;
public class PlaceholderVirtualFunctionTable extends VirtualFunctionTable {
/**
* Constructor.
* 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 {@code null}
* @param parentage parentage for which this VFT is used; can be {@code null}
*/
public PlaceholderVirtualFunctionTable(ClassID owner, List<ClassID> parentage) {
super(owner, parentage);
}
public void setAddress(int tableIndex, Address address) throws PdbException {
PlaceholderVirtualFunctionTableEntry entry = existing(tableIndex);
entry.setAddress(address);
}
@Override
public Address getAddress(int tableIndex) throws PdbException {
PlaceholderVirtualFunctionTableEntry entry = existing(tableIndex);
return entry.getAddress();
}
@Override
protected VirtualFunctionTableEntry getNewEntry(SymbolPath originalMethodPath,
SymbolPath overrideMethodPath, Pointer functionPointer) {
return new PlaceholderVirtualFunctionTableEntry(originalMethodPath, overrideMethodPath,
functionPointer);
}
@Override
public void emit(StringBuilder builder) {
builder.append(
"Placeholder VFT for the following classes within owner:\n " + owner + "\n");
builder.append(String.format("For Classes:\n"));
for (ClassID id : parentage) {
builder.append(String.format(" %-10s\n", id.toString()));
}
builder.append("VftPtrOffset within Owner" + ptrOffsetInClass + "\n");
PlaceholderVirtualFunctionTableEntry.emitHeader(builder);
for (int tableIndex : entriesByTableIndex.keySet()) {
entry(tableIndex).emit(builder, ptrOffsetInClass);
}
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
emit(builder);
return builder.toString();
}
private PlaceholderVirtualFunctionTableEntry entry(int tableIndex) {
return (PlaceholderVirtualFunctionTableEntry) entriesByTableIndex.get(tableIndex);
}
private PlaceholderVirtualFunctionTableEntry existing(int tableIndex) throws PdbException {
PlaceholderVirtualFunctionTableEntry entry = entry(tableIndex);
if (entry == null) {
throw new PdbException(
"No entry in Virtual Function Table for table offset: " + tableIndex);
}
return entry;
}
}
@@ -0,0 +1,84 @@
/* ###
* 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.address.Address;
import ghidra.program.model.data.Pointer;
class PlaceholderVirtualFunctionTableEntry extends VirtualFunctionTableEntry {
private Address address;
/**
* Constructor for placeholder virtual function table entry
* @param originalMethodPath the path of the original function
* @param overrideMethodPath the path of the overriding function
* @param functionPointer the pointer to the function definition
*/
PlaceholderVirtualFunctionTableEntry(SymbolPath originalMethodPath,
SymbolPath overrideMethodPath, Pointer functionPointer) {
this(originalMethodPath, overrideMethodPath, functionPointer, null);
}
/**
* Constructor for placeholder virtual function table entry
* @param originalMethodPath the path of the original function
* @param overrideMethodPath the path of the overriding function
* @param functionPointer the pointer to the function definition
* @param address address of the function in memory; can be {@code null}
*/
PlaceholderVirtualFunctionTableEntry(SymbolPath originalMethodPath,
SymbolPath overrideMethodPath, Pointer functionPointer, Address address) {
super(originalMethodPath, overrideMethodPath, functionPointer);
this.address = address;
}
/**
* Method to set the address of the function in memory
* @param address the address
*/
void setAddress(Address address) {
this.address = address;
}
/**
* Returns the address of the function in memory, if set
* @return the address; can be {@code null}
*/
Address getAddress() {
return address;
}
//==============================================================================================
// Info from longer-running branch
void emit(StringBuilder builder, long vbtPtrOffset) {
emitLine(builder, null, overrideMethodPath);
}
// static void emitHeader(StringBuilder builder, long ownerOffset, long vbtPtrOffset) {
static void emitHeader(StringBuilder builder) {
builder.append(String.format("%16s %s\n", "Address", "Path"));
// TODO: see if we need something like this for PlaceholderVirtualFunctionTable
// emitLine(builder, ownerOffset, vbtPtrOffset );
}
static void emitLine(StringBuilder builder, Address address, SymbolPath path) {
builder.append(String.format("%16s %10s\n",
address == null ? "<unk_addr>" : address.toString(), path.toString()));
}
}
@@ -15,10 +15,11 @@
*/
package ghidra.app.util.pdb.classtype;
import java.util.*;
import java.util.List;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.program.model.address.Address;
import ghidra.program.model.gclass.ClassID;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryAccessException;
@@ -33,12 +34,6 @@ public class ProgramVirtualBaseTable extends VirtualBaseTable {
private int entrySize;
private String mangledName; // remove?
private Boolean createdFromMemory = null;
private Boolean createdFromCompiled = null;
private int maxIndexSeen = -1;
private Map<Integer, VBTableEntry> entriesByIndex = new HashMap<>();
/**
* Constructor
* @param owner the class that owns the table
@@ -46,20 +41,15 @@ public class ProgramVirtualBaseTable extends VirtualBaseTable {
* @param program the program
* @param address the address of the table
* @param entrySize the size of the index field for each table entry in memory
* @param ctm the class type manager
* @param mangledName the mangled name of the table
*/
public ProgramVirtualBaseTable(ClassID owner, List<ClassID> parentage, Program program,
Address address, int entrySize, ClassTypeManager ctm, String mangledName) {
Address address, int entrySize, String mangledName) {
super(owner, parentage);
if (entrySize != 4 && entrySize != 8) {
throw new IllegalArgumentException("Invalid size (" + entrySize + "): must be 4 or 8.");
}
this.program = program;
this.address = address;
this.entrySize = entrySize;
this.mangledName = mangledName;
createdFromMemory = true;
}
/**
@@ -78,27 +68,25 @@ public class ProgramVirtualBaseTable extends VirtualBaseTable {
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 {
public Long getBaseOffset(int tableIndex) throws PdbException {
Long offset = baseOffsetByTableIndex.get(tableIndex);
if (offset != null) {
return offset;
}
offset = getOffsetFromMemory(tableIndex);
if (offset != null) {
baseOffsetByTableIndex.put(tableIndex, offset);
}
return offset;
}
private Long getOffsetFromMemory(int tableIndex) throws PdbException {
if (program == null || address == null) {
return null;
}
Memory memory = program.getMemory();
Address entryAddress = address.add(index * entrySize);
Address entryAddress = address.add(tableIndex * entrySize);
try {
Long offset = (entrySize == 4) ? (long) memory.getInt(entryAddress)
: memory.getLong(entryAddress);
@@ -110,40 +98,31 @@ public class ProgramVirtualBaseTable extends VirtualBaseTable {
entryAddress);
}
finally {
maxIndexSeen = Integer.max(maxIndexSeen, index);
maxTableIndexSeen = Integer.max(maxTableIndexSeen, tableIndex);
}
}
@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();
protected VirtualBaseTableEntry getNewEntry(ClassID baseId) {
return new VirtualBaseTableEntry(baseId);
}
@Override
public VBTableEntry getBase(int index) throws PdbException {
VBTableEntry entry = entriesByIndex.get(index);
/**
* Returns the entry for the table index; the table index is based at 1
* @param tableIndex the index location in the table
* @return the entry
*/
private VirtualBaseTableEntry entry(int tableIndex) {
return entryByTableIndex.get(tableIndex);
}
private VirtualBaseTableEntry existing(int tableIndex) throws PdbException {
VirtualBaseTableEntry entry = entry(tableIndex);
if (entry == null) {
throw new PdbException("No entry in Virtual Base Table for index: " + index);
throw new PdbException(
"No entry in Virtual Base Table for table offset: " + tableIndex);
}
maxIndexSeen = Integer.max(maxIndexSeen, index);
return entry;
}
public void setBaseClassId(int index, ClassID baseId) {
VBTableEntry entry = entriesByIndex.get(index);
if (entry == null) {
entry = new VirtualBaseTableEntry(baseId);
entriesByIndex.put(index, entry);
}
else {
entry.setClassId(baseId);
}
maxIndexSeen = Integer.max(maxIndexSeen, index);
}
}
@@ -16,10 +16,13 @@
package ghidra.app.util.pdb.classtype;
import java.util.List;
import java.util.TreeMap;
import ghidra.app.util.SymbolPath;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.Pointer;
import ghidra.program.model.gclass.ClassID;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryAccessException;
@@ -54,6 +57,7 @@ public class ProgramVirtualFunctionTable extends VirtualFunctionTable {
this.address = address;
this.defaultEntrySize = defaultEntrySize;
this.mangledName = mangledName;
entriesByTableIndex = new TreeMap<>();
}
/**
@@ -72,6 +76,13 @@ public class ProgramVirtualFunctionTable extends VirtualFunctionTable {
return mangledName;
}
@Override
protected VirtualFunctionTableEntry getNewEntry(SymbolPath originalMethodPath,
SymbolPath overrideMethodPath, Pointer functionPointer) {
return new VirtualFunctionTableEntry(originalMethodPath, overrideMethodPath,
functionPointer);
}
@Override
public Address getAddress(int ordinal) throws PdbException {
Memory memory = program.getMemory();
@@ -91,11 +102,19 @@ public class ProgramVirtualFunctionTable extends VirtualFunctionTable {
"MemoryAccessException while trying to parse virtual function table entry at address: " +
entryAddress);
}
//throw new UnsupportedOperationException();
}
@Override
public SymbolPath getPath(int index) throws PdbException {
throw new UnsupportedOperationException();
private VirtualFunctionTableEntry entry(int tableIndex) {
return (VirtualFunctionTableEntry) entriesByTableIndex.get(tableIndex);
}
private VirtualFunctionTableEntry existing(int tableIndex) throws PdbException {
VirtualFunctionTableEntry entry = entry(tableIndex);
if (entry == null) {
throw new PdbException(
"No entry in Virtual Function Table for table offset: " + tableIndex);
}
return entry;
}
}
@@ -18,6 +18,6 @@ package ghidra.app.util.pdb.classtype;
/**
* Compiler-generated virtual base table
*/
public interface VBTable {
public interface VBTable extends VXT {
// empty for now
}
@@ -15,22 +15,12 @@
*/
package ghidra.app.util.pdb.classtype;
import ghidra.program.model.gclass.ClassID;
/**
* 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();
public interface VBTableEntry extends VXTEntry {
/**
* Sets the entry class ID
@@ -18,6 +18,6 @@ package ghidra.app.util.pdb.classtype;
/**
* Compiler-generated virtual function table
*/
public interface VFTable {
public interface VFTable extends VXT {
// empty for now
}
@@ -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 ghidra.app.util.SymbolPath;
import ghidra.program.model.data.Pointer;
/**
* Represents an entry within a virtual function table
*/
public interface VFTableEntry extends VXTEntry {
/**
* Sets the original path of the function
* @param path the symbol path
*/
public void setOriginalPath(SymbolPath path);
/**
* Gets the original path of the function
* @return the symbol path
*/
public SymbolPath getOriginalPath();
/**
* Sets the override path of the function
* @param path the symbol path
*/
public void setOverridePath(SymbolPath path);
/**
* Gets the override path of the function
* @return the symbol path
*/
public SymbolPath getOverridePath();
/**
* Sets the pointer to the function definition type
* @param pointer the pointer to the funciton definition type
*/
public void setFunctionPointer(Pointer pointer);
/**
* Returns the pointer to the function definition type
* @return the pointer to the function definition type
*/
public Pointer getFunctionPointer();
}
@@ -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 "something" table -- generic v-anything-table from any toolchain
*/
public interface VXT {
// empty for now
}
@@ -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 "something" table entry -- generic v-anything-table entry
*/
public interface VXTEntry {
// empty for now
}
@@ -15,10 +15,12 @@
*/
package ghidra.app.util.pdb.classtype;
import java.util.ArrayList;
import java.util.List;
import java.util.*;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.program.model.data.*;
import ghidra.program.model.gclass.ClassID;
import ghidra.program.model.gclass.ClassUtils;
/**
* Abstract class for virtual base tables
@@ -28,15 +30,23 @@ 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
/**
* The number of entries in the table
* The number of entries in the table, as specified by the user
*/
protected Integer numEntries;
protected Integer userSpecifiedNumEntries;
/**
* This is the offset within the class where we expect to find the pointer that can point to
* this table
*/
protected Long ptrOffsetInClass;
protected int maxTableIndexSeen;
protected Map<Integer, VirtualBaseTableEntry> entryByTableIndex;
protected Map<Integer, Long> baseOffsetByTableIndex;
// result of compile/build
private Structure tableStructure;
private boolean isBuilt;
/**
* 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
@@ -47,35 +57,82 @@ public abstract class VirtualBaseTable implements VBTable {
public VirtualBaseTable(ClassID owner, List<ClassID> parentage) {
this.owner = owner;
this.parentage = new ArrayList<>(parentage);
maxTableIndexSeen = -1;
entryByTableIndex = new HashMap<>();
baseOffsetByTableIndex = new HashMap<>();
}
protected abstract VirtualBaseTableEntry getNewEntry(ClassID baseId);
/**
* Method to add an entry to the virtual base table
* @param tableIndex the index location in the virtual base table for the entry
* @param baseId class id of the base
*/
public void addEntry(int tableIndex, ClassID baseId) {
VirtualBaseTableEntry entry = getNewEntry(baseId);
entryByTableIndex.put(tableIndex, entry);
maxTableIndexSeen = Integer.max(maxTableIndexSeen, tableIndex);
}
int getMaxTableIndex() {
return maxTableIndexSeen;
}
public Map<Integer, VirtualBaseTableEntry> getEntriesByTableIndex() {
return entryByTableIndex;
}
/**
* Returns the base class entry for the table tableIndex
* @param tableIndex the index location in the virtual base table for the entry
* @return the entry for the base class
*/
public VBTableEntry getEntry(int tableIndex) {
return entryByTableIndex.get(tableIndex);
}
/**
* 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
* VBTable is at the tableIndex location
* @param tableIndex the index location in the virtual base table for the entry
* @return the offset in the layout class
* @throws PdbException if problem retrieving the offset value
*/
public abstract Long getBaseOffset(int index) throws PdbException;
public abstract Long getBaseOffset(int tableIndex) throws PdbException;
/**
* Sets the base class id for the table index; the table index is based at 1
* @param tableIndex the index location in the table
* @param baseId the base class id
*/
public void setBaseClassId(int tableIndex, ClassID baseId) {
VirtualBaseTableEntry entry = entryByTableIndex.get(tableIndex);
if (entry == null) {
entry = new VirtualBaseTableEntry(baseId);
entryByTableIndex.put(tableIndex, entry);
}
else {
entry.setClassId(baseId);
}
maxTableIndexSeen = Integer.max(maxTableIndexSeen, tableIndex);
}
/**
* 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
* VBTable is at the tableIndex location; the table index is based at 1
* @param tableIndex the index location in the virtual base table for the entry
* @return the ClassID of the base class
* @throws PdbException if an entry does not exist for the index
* @throws PdbException if an entry does not exist for the tableIndex
*/
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;
public ClassID getBaseClassId(int tableIndex) throws PdbException {
VBTableEntry entry = entryByTableIndex.get(tableIndex);
if (entry == null) {
return null;
}
maxTableIndexSeen = Integer.max(maxTableIndexSeen, tableIndex);
return entry.getClassId();
}
/**
* Returns the owning class
@@ -95,10 +152,18 @@ public abstract class VirtualBaseTable implements VBTable {
/**
* Returns the number of entries in the table
* @return the number of entries; {@code null} if not initialized
* @return the number of entries
*/
public Integer getNumEntries() {
return numEntries;
public int getNumEntries() {
return entryByTableIndex.size();
}
/**
* Returns the number of entries in the table, as specified by the user
* @return the number of entries
*/
public int getUserSpecifiedNumEntries() {
return userSpecifiedNumEntries;
}
/**
@@ -130,7 +195,7 @@ public abstract class VirtualBaseTable implements VBTable {
* @param numEntriesArg the number of entries
*/
public void setNumEntries(Integer numEntriesArg) {
numEntries = numEntriesArg;
userSpecifiedNumEntries = numEntriesArg;
}
/**
@@ -150,4 +215,56 @@ public abstract class VirtualBaseTable implements VBTable {
}
}
/**
* Returns the built data type for this vftable for the current entries
* @param dtm the data type manager
* @param categoryPath category path for the table
* @return the structure of the vftable
*/
public Structure getLayout(DataTypeManager dtm, CategoryPath categoryPath) {
if (!isBuilt) { // what if we want to rebuild... what should we do?
build(dtm, categoryPath);
}
return tableStructure;
}
private void build(DataTypeManager dtm, CategoryPath categoryPath) {
if (ptrOffsetInClass == null || maxTableIndexSeen == -1) {
tableStructure = null;
isBuilt = true;
return;
}
String name = ClassUtils.getSpecialVxTableName(ptrOffsetInClass);
DataType defaultEntry = ClassUtils.getVbtDefaultEntry(dtm);
// Holding onto next line for now
//int entrySize = defaultEntry.getLength();
// Note that maxTableIndexSeen comes from addEntry() and those have what seems to be
// a base 1 "index" (vs. base 0 vs. offset)
int tableNumEntries =
(userSpecifiedNumEntries != null) ? userSpecifiedNumEntries : maxTableIndexSeen;
// Holding onto next line for now
//int tableSize = tableNumEntries * entrySize;
StructureDataType dt = new StructureDataType(categoryPath, name, 0, dtm);
int masterOrdinal = 0;
for (Map.Entry<Integer, VirtualBaseTableEntry> mapEntry : entryByTableIndex.entrySet()) {
// Note that entrie's tableIndex is based at 1 instead of 0
int ordinal = mapEntry.getKey() - 1;
VBTableEntry entry = mapEntry.getValue();
while (masterOrdinal < ordinal) {
dt.add(defaultEntry, "", "");
masterOrdinal++;
}
String comment = entry.getClassId().getSymbolPath().toString();
dt.add(defaultEntry, "", comment); // we could add a comment here
masterOrdinal++;
}
while (masterOrdinal < tableNumEntries) {
dt.add(defaultEntry, "", "");
masterOrdinal++;
}
tableStructure = (Structure) dtm.resolve(dt, null);
//System.out.println(tableStructure.toString());
isBuilt = true;
}
}
@@ -15,38 +15,20 @@
*/
package ghidra.app.util.pdb.classtype;
import ghidra.program.model.gclass.ClassID;
/**
* 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
public VirtualBaseTableEntry(Long offset) {
this(offset, null);
}
public VirtualBaseTableEntry(ClassID baseId) {
this(null, baseId);
}
public 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;
@@ -15,55 +15,59 @@
*/
package ghidra.app.util.pdb.classtype;
import java.util.ArrayList;
import java.util.List;
import java.util.*;
import ghidra.app.util.SymbolPath;
import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.*;
import ghidra.program.model.gclass.ClassID;
import ghidra.program.model.gclass.ClassUtils;
public abstract class VirtualFunctionTable implements VFTable {
protected ClassID owner;
protected List<ClassID> parentage;
/**
* The number of entries in the table
* The number of entries in the table, as specified by the user
*/
protected int numEntries;
protected int userSpecifiedNumEntries;
/**
* This is the offset within the class where we expect to find the pointer that can point to
* this table
*/
protected int ptrOffsetInClass;
protected Long ptrOffsetInClass;
protected int maxTableIndexSeen;
protected Map<Integer, VirtualFunctionTableEntry> entriesByTableIndex;
// result of compile/build
private Structure tableStructure;
private boolean isBuilt;
/**
* Constructor.
* 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
* @param owner class that owns this VBT (can own more than one); can be {@code null}
* @param parentage parentage for which this VFT is used; can be {@code null}
*/
VirtualFunctionTable(ClassID owner, List<ClassID> parentage) {
this.owner = owner;
this.parentage = new ArrayList<>(parentage);
numEntries = 0;
userSpecifiedNumEntries = 0;
maxTableIndexSeen = -1;
entriesByTableIndex = new TreeMap<>();
}
/**
* Returns the address value at the index in the table
* @param index the index
* Returns the address value at the table offset
* @param tableIndex the index location in the virtual function table for the entry; based at 1
* @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;
public abstract Address getAddress(int tableIndex) throws PdbException;
/**
* Returns the owning class
@@ -81,20 +85,55 @@ public abstract class VirtualFunctionTable implements VFTable {
return parentage;
}
/**
* Gets the offset within the class for the pointer that can point to this table
* @return the offset
*/
public Long getPtrOffsetInClass() {
return ptrOffsetInClass;
}
protected abstract VirtualFunctionTableEntry getNewEntry(SymbolPath originalMethodPath,
SymbolPath overrideMethodPath, Pointer functionPointer);
/**
* Method to add an entry to the virtual function table
* @param tableIndex the index location in the virtual function table for the entry; based at 1
* @param originalMethodPath the symbol path of the method
* @param overrideMethodPath the symbol path of the override method
* @param functionPointer pointer to the function definition of the method
*/
public void addEntry(int tableIndex, SymbolPath originalMethodPath,
SymbolPath overrideMethodPath, Pointer functionPointer) {
VirtualFunctionTableEntry entry =
getNewEntry(originalMethodPath, overrideMethodPath, functionPointer);
entriesByTableIndex.put(tableIndex, entry);
maxTableIndexSeen = Integer.max(maxTableIndexSeen, tableIndex);
}
/**
* Returns the entry for the table offset
* @param tableIndex the index location in the virtual function table for the entry; based at 1
* @return the entry
*/
public VirtualFunctionTableEntry getEntry(int tableIndex) {
return entriesByTableIndex.get(tableIndex);
}
/**
* Returns the number of entries in the table
* @return the number of entries
*/
public int getNumEntries() {
return numEntries;
return entriesByTableIndex.size();
}
/**
* Gets the offset within the class for the pointer that can point to this table
* @return the offset
* Returns the number of entries in the table, as specified by the user
* @return the number of entries
*/
public int getPtrOffsetInClass() {
return ptrOffsetInClass;
public int getUserSpecifiedNumEntries() {
return userSpecifiedNumEntries;
}
/**
@@ -113,22 +152,22 @@ public abstract class VirtualFunctionTable implements VFTable {
this.parentage = parentage;
}
/**
* Sets the number of entries for the table
* @param numEntriesArg the number of entries
*/
public void setNumEntries(int numEntriesArg) {
numEntries = numEntriesArg;
}
/**
* Sets the offset within the class for the pointer that can point to this table
* @param offset the offset
*/
public void setPtrOffsetInClass(int offset) {
public void setPtrOffsetInClass(Long offset) {
ptrOffsetInClass = offset;
}
/**
* Sets the "expected" number of entries for the table
* @param numEntriesArg the number of entries
*/
public void setNumEntries(int numEntriesArg) {
userSpecifiedNumEntries = numEntriesArg;
}
void emit(StringBuilder builder) {
builder.append("VBT for the following classes within: " + owner);
builder.append("\n");
@@ -137,4 +176,57 @@ public abstract class VirtualFunctionTable implements VFTable {
builder.append("\n");
}
}
public int size() {
return entriesByTableIndex.size();
}
public int getMaxTableIndex() {
return maxTableIndexSeen;
}
public Map<Integer, VirtualFunctionTableEntry> getEntriesByTableIndex() {
return entriesByTableIndex;
}
/**
* Returns the built data type for this vftable for the current entries
* @param dtm the data type manager
* @param categoryPath category path for the table
* @return the structure of the vftable
*/
public Structure getLayout(DataTypeManager dtm, CategoryPath categoryPath) {
if (!isBuilt) { // what if we want to rebuild... what should we do?
build(dtm, categoryPath);
}
return tableStructure;
}
private void build(DataTypeManager dtm, CategoryPath categoryPath) {
if (ptrOffsetInClass == null || maxTableIndexSeen == -1) {
tableStructure = null;
isBuilt = true;
return;
}
String name = ClassUtils.getSpecialVxTableName(ptrOffsetInClass);
DataType defaultEntry = ClassUtils.getVftDefaultEntry(dtm);
int entrySize = defaultEntry.getLength();
StructureDataType dt = new StructureDataType(categoryPath, name, 0, dtm);
int masterOffset = 0;
for (Map.Entry<Integer, VirtualFunctionTableEntry> mapEntry : entriesByTableIndex
.entrySet()) {
int tableIndex = mapEntry.getKey();
VFTableEntry tableEntry = mapEntry.getValue();
while (masterOffset < tableIndex) {
dt.add(defaultEntry, "", "");
masterOffset += entrySize;
}
dt.add(tableEntry.getFunctionPointer(), tableEntry.getOverridePath().toString(), "");
masterOffset += entrySize;
}
tableStructure = (Structure) dtm.resolve(dt, null);
//System.out.println(tableStructure.toString());
isBuilt = true;
}
}
@@ -0,0 +1,63 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.pdb.classtype;
import ghidra.app.util.SymbolPath;
import ghidra.program.model.data.Pointer;
public class VirtualFunctionTableEntry implements VFTableEntry {
SymbolPath originalMethodPath;
SymbolPath overrideMethodPath;
Pointer functionPointer;
public VirtualFunctionTableEntry(SymbolPath originalMethodPath, SymbolPath overrideMethodPath,
Pointer functionPointer) {
this.originalMethodPath = originalMethodPath;
this.overrideMethodPath = overrideMethodPath;
this.functionPointer = functionPointer;
}
@Override
public void setOriginalPath(SymbolPath path) {
originalMethodPath = path;
}
@Override
public SymbolPath getOriginalPath() {
return originalMethodPath;
}
@Override
public void setOverridePath(SymbolPath path) {
overrideMethodPath = path;
}
@Override
public SymbolPath getOverridePath() {
return overrideMethodPath;
}
@Override
public void setFunctionPointer(Pointer pointer) {
functionPointer = pointer;
}
@Override
public Pointer getFunctionPointer() {
return functionPointer;
}
}
@@ -23,8 +23,7 @@ import ghidra.app.util.SymbolPath;
import ghidra.app.util.bin.format.pdb.DefaultCompositeMember;
import ghidra.app.util.bin.format.pdb2.pdbreader.*;
import ghidra.app.util.bin.format.pdb2.pdbreader.type.*;
import ghidra.app.util.pdb.classtype.Access;
import ghidra.app.util.pdb.classtype.ClassFieldAttributes;
import ghidra.app.util.pdb.classtype.*;
import ghidra.program.model.data.*;
import ghidra.util.Msg;
import ghidra.util.exception.AssertException;
@@ -195,6 +194,8 @@ public class CompositeTypeApplier extends AbstractComplexTypeApplier {
addVftPtrs(composite, classType, lists.vftPtrs(), type, myMembers);
addMembers(composite, classType, lists.nonstaticMembers(), type, myMembers);
addMethods(combo, lists.methods());
if (!classType.validate()) {
// TODO: Investigate. We should do this check for some classes somewhere. Should
// we do it here. Set breakpoint here to investigate.
@@ -241,8 +242,7 @@ public class CompositeTypeApplier extends AbstractComplexTypeApplier {
throws PdbException, CancelledException {
AbstractCompositeMsType cType = (AbstractCompositeMsType) type;
Access defaultAccess = (type instanceof AbstractClassMsType) ? Access.PRIVATE
: Access.PUBLIC;
Access defaultAccess = myClassType.getDefaultAccess();
for (AbstractMsType baseType : msBases) {
applicator.checkCancelled();
@@ -364,6 +364,90 @@ public class CompositeTypeApplier extends AbstractComplexTypeApplier {
return dataType;
}
private void addMethods(ComboType combo, List<AbstractMsType> methods)
throws CancelledException {
if (methods.isEmpty()) {
return;
}
CppCompositeType cppCompositeType = combo.ct();
SymbolPath classSymbolPath = cppCompositeType.getSymbolPath();
Access defaultAccess = cppCompositeType.getDefaultAccess();
for (AbstractMsType methodType : methods) {
applicator.checkCancelled();
if (methodType instanceof AbstractOneMethodMsType oneMethodType) {
String name = oneMethodType.getName();
ClassFieldMsAttributes attributes = oneMethodType.getAttributes();
AbstractMsType t =
applicator.getPdb().getTypeRecord(oneMethodType.getProcedureTypeRecordNumber());
int adjuster = 0;
if (t instanceof AbstractMemberFunctionMsType memberFunc) {
adjuster = memberFunc.getThisAdjuster();
}
Long offset = oneMethodType.getOffsetInVFTableIfIntroVirtual();
RecordNumber procedureTypeRn = oneMethodType.getProcedureTypeRecordNumber();
DataType dt = applicator.getDataType(procedureTypeRn);
if (!(dt instanceof FunctionDefinition def)) {
Msg.warn(this,
"MemberFunction expected, but found: " + dt.getClass().getSimpleName());
// Put something in its place? Or abort the whole class?
continue;
}
SymbolPath methodSymbolPath =
classSymbolPath.append(new SymbolPath(name)).replaceInvalidChars();
ClassFieldAttributes atts = ClassFieldAttributes.convert(attributes, defaultAccess);
if (atts.getProperty() == Property.VIRTUAL) {
cppCompositeType.addVirtualMethod(adjuster, offset.intValue(), methodSymbolPath,
def);
}
}
else if (methodType instanceof AbstractOverloadedMethodMsType overloadedMethodType) {
String name = overloadedMethodType.getName();
RecordNumber methodsListRn = overloadedMethodType.getTypeMethodListRecordNumber();
AbstractMsType methodsListTry = applicator.getTypeRecord(methodsListRn);
if (methodsListTry instanceof AbstractMethodListMsType methodsListType) {
SymbolPath methodSymbolPath =
classSymbolPath.append(new SymbolPath(name)).replaceInvalidChars();
List<AbstractMethodRecordMs> recordList = methodsListType.getList();
for (AbstractMethodRecordMs methodRecord : recordList) {
applicator.checkCancelled();
Long offset = methodRecord.getOptionalOffset();
RecordNumber procedureTypeRn = methodRecord.getProcedureTypeRecordNumber();
ClassFieldMsAttributes attributes = methodRecord.getAttributes();
AbstractMsType t = applicator.getPdb().getTypeRecord(procedureTypeRn);
int adjuster = 0;
if (t instanceof AbstractMemberFunctionMsType memberFunc) {
adjuster = memberFunc.getThisAdjuster();
}
DataType dt = applicator.getDataType(procedureTypeRn);
if (!(dt instanceof FunctionDefinition def)) {
Msg.warn(this,
"MemberFunction expected, but found: " +
dt.getClass().getSimpleName());
// Put something in its place? Or abort the whole class?
continue;
}
ClassFieldAttributes atts =
ClassFieldAttributes.convert(attributes, defaultAccess);
if (atts.getProperty() == Property.VIRTUAL) {
cppCompositeType.addVirtualMethod(adjuster, offset.intValue(),
methodSymbolPath, def);
}
}
}
else {
Msg.warn(this, "Unexexpected method list type: " +
methodsListTry.getClass().getSimpleName());
}
}
else {
Msg.warn(this,
"Unexexpected method type: " + methodType.getClass().getSimpleName());
}
}
}
private void addMembers(Composite composite, CppCompositeType myClassType,
List<AbstractMemberMsType> msMembers, AbstractCompositeMsType type,
List<DefaultPdbUniversalMember> myMembers) throws CancelledException, PdbException {
@@ -36,7 +36,8 @@ import ghidra.app.util.bin.format.pdb2.pdbreader.type.PrimitiveMsType;
import ghidra.app.util.bin.format.pe.cli.tables.CliAbstractTableRow;
import ghidra.app.util.importer.MessageLog;
import ghidra.app.util.pdb.PdbCategories;
import ghidra.app.util.pdb.classtype.*;
import ghidra.app.util.pdb.classtype.ClassTypeManager;
import ghidra.app.util.pdb.classtype.MsftVxtManager;
import ghidra.framework.options.Options;
import ghidra.program.database.data.DataTypeUtilities;
import ghidra.program.disassemble.DisassemblerContextImpl;
@@ -206,7 +207,7 @@ public class DefaultPdbApplicator implements PdbApplicator {
//==============================================================================================
// If we have symbols and memory with VBTs in them, then a better VbtManager is created.
private VxtManager vxtManager;
private MsftVxtManager vxtManager;
private PdbRegisterNameToProgramRegisterMapper registerNameToRegisterMapper;
//==============================================================================================
@@ -370,6 +371,7 @@ public class DefaultPdbApplicator implements PdbApplicator {
case ALL:
processTypes();
processSymbols();
vxtManager.doTableLayouts(dataTypeManager);
break;
default:
throw new PdbException("PDB: Invalid Application Control: " +
@@ -643,16 +645,16 @@ public class DefaultPdbApplicator implements PdbApplicator {
if (program != null) {
// Currently, this must happen after symbolGroups are created.
MsftVxtManager msftVxtManager =
new MsftVxtManager(getClassTypeManager(), program);
new MsftVxtManager(getClassTypeManager(), program); // TODO: need to fix MsftVxtManager to work with or without a program!!!!!!!!!!
msftVxtManager.createVirtualTables(getRootPdbCategory(), findVirtualTableSymbols(), log,
monitor);
vxtManager = msftVxtManager;
registerNameToRegisterMapper = new PdbRegisterNameToProgramRegisterMapper(program);
}
else {
vxtManager = new VxtManager(getClassTypeManager());
}
// else {
// vxtManager = new VxtManager(getClassTypeManager());
// }
preWorkDone = true;
}
@@ -1582,7 +1584,7 @@ public class DefaultPdbApplicator implements PdbApplicator {
//==============================================================================================
// Virtual-Base/Function-Table-related methods.
//==============================================================================================
VxtManager getVxtManager() {
MsftVxtManager getVxtManager() {
return vxtManager;
}
@@ -30,6 +30,7 @@ import ghidra.program.model.*;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressIterator;
import ghidra.program.model.data.*;
import ghidra.program.model.gclass.ClassID;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryAccessException;
@@ -57,30 +58,30 @@ public class MsftVxtManagerTest extends AbstractGenericTest {
private static int[] dummyVftMeta = new int[] { 0 };
private static ClassID A1_ID = new ProgramClassID(CategoryPath.ROOT, sp("A1NS::A1"));
private static ClassID A2_ID = new ProgramClassID(CategoryPath.ROOT, sp("A2NS::A2"));
private static ClassID A_ID = new ProgramClassID(CategoryPath.ROOT, sp("ANS::A"));
private static ClassID B1_ID = new ProgramClassID(CategoryPath.ROOT, sp("B1NS::B1"));
private static ClassID B2_ID = new ProgramClassID(CategoryPath.ROOT, sp("B2NS::B2"));
private static ClassID B_ID = new ProgramClassID(CategoryPath.ROOT, sp("BNS::B"));
private static ClassID C_ID = new ProgramClassID(CategoryPath.ROOT, sp("CNS::C"));
private static ClassID D_ID = new ProgramClassID(CategoryPath.ROOT, sp("DNS::D"));
private static ClassID E_ID = new ProgramClassID(CategoryPath.ROOT, sp("ENS::E"));
private static ClassID F_ID = new ProgramClassID(CategoryPath.ROOT, sp("FNS::F"));
private static ClassID G_ID = new ProgramClassID(CategoryPath.ROOT, sp("GNS::G"));
private static ClassID H_ID = new ProgramClassID(CategoryPath.ROOT, sp("HNS::H"));
private static ClassID I_ID = new ProgramClassID(CategoryPath.ROOT, sp("INS::I"));
private static ClassID J_ID = new ProgramClassID(CategoryPath.ROOT, sp("JNS::J"));
private static ClassID K_ID = new ProgramClassID(CategoryPath.ROOT, sp("KNS::K"));
private static ClassID L_ID = new ProgramClassID(CategoryPath.ROOT, sp("LNS::L"));
private static ClassID N1_ID = new ProgramClassID(CategoryPath.ROOT, sp("N1NS::N1"));
private static ClassID N2_ID = new ProgramClassID(CategoryPath.ROOT, sp("N2NS::N2"));
private static ClassID M_ID = new ProgramClassID(CategoryPath.ROOT, sp("MNS::M"));
private static ClassID O1_ID = new ProgramClassID(CategoryPath.ROOT, sp("O1NS::O1"));
private static ClassID O2_ID = new ProgramClassID(CategoryPath.ROOT, sp("O2NS::O2"));
private static ClassID O3_ID = new ProgramClassID(CategoryPath.ROOT, sp("O3NS::O3"));
private static ClassID O4_ID = new ProgramClassID(CategoryPath.ROOT, sp("O4NS::O4"));
private static ClassID O_ID = new ProgramClassID(CategoryPath.ROOT, sp("ONS::O"));
private static ClassID A1_ID = new ClassID(CategoryPath.ROOT, sp("A1NS::A1"));
private static ClassID A2_ID = new ClassID(CategoryPath.ROOT, sp("A2NS::A2"));
private static ClassID A_ID = new ClassID(CategoryPath.ROOT, sp("ANS::A"));
private static ClassID B1_ID = new ClassID(CategoryPath.ROOT, sp("B1NS::B1"));
private static ClassID B2_ID = new ClassID(CategoryPath.ROOT, sp("B2NS::B2"));
private static ClassID B_ID = new ClassID(CategoryPath.ROOT, sp("BNS::B"));
private static ClassID C_ID = new ClassID(CategoryPath.ROOT, sp("CNS::C"));
private static ClassID D_ID = new ClassID(CategoryPath.ROOT, sp("DNS::D"));
private static ClassID E_ID = new ClassID(CategoryPath.ROOT, sp("ENS::E"));
private static ClassID F_ID = new ClassID(CategoryPath.ROOT, sp("FNS::F"));
private static ClassID G_ID = new ClassID(CategoryPath.ROOT, sp("GNS::G"));
private static ClassID H_ID = new ClassID(CategoryPath.ROOT, sp("HNS::H"));
private static ClassID I_ID = new ClassID(CategoryPath.ROOT, sp("INS::I"));
private static ClassID J_ID = new ClassID(CategoryPath.ROOT, sp("JNS::J"));
private static ClassID K_ID = new ClassID(CategoryPath.ROOT, sp("KNS::K"));
private static ClassID L_ID = new ClassID(CategoryPath.ROOT, sp("LNS::L"));
private static ClassID N1_ID = new ClassID(CategoryPath.ROOT, sp("N1NS::N1"));
private static ClassID N2_ID = new ClassID(CategoryPath.ROOT, sp("N2NS::N2"));
private static ClassID M_ID = new ClassID(CategoryPath.ROOT, sp("MNS::M"));
private static ClassID O1_ID = new ClassID(CategoryPath.ROOT, sp("O1NS::O1"));
private static ClassID O2_ID = new ClassID(CategoryPath.ROOT, sp("O2NS::O2"));
private static ClassID O3_ID = new ClassID(CategoryPath.ROOT, sp("O3NS::O3"));
private static ClassID O4_ID = new ClassID(CategoryPath.ROOT, sp("O4NS::O4"));
private static ClassID O_ID = new ClassID(CategoryPath.ROOT, sp("ONS::O"));
private static Function A1NS_A1_fa1_1 = new FunctionTestDouble("A1NS::A1::fa1_1");
private static Function A1NS_A1_fa1_2 = new FunctionTestDouble("A1NS::A1::fa1_2");
@@ -65,10 +65,10 @@ public class CppCompositeTypeTest extends AbstractGenericTest {
private static DataType vftptr64;
private static DataType vbtptr32;
private static DataType vbtptr64;
private static MsftVxtManager msftVxtManager32;
private static MsftVxtManager msftVxtManager64;
private static VxtManager vxtManager32;
private static VxtManager vxtManager64;
private static MsftVxtManager vxtManager32;
private static MsftVxtManager vxtManager64;
private static MsftVxtManager vxtManagerNoProgram32;
private static MsftVxtManager vxtManagerNoProgram64;
// Note: Currently all test have expected results based on up the CLASS_HIERARCHY layout.
private static ObjectOrientedClassLayout classLayoutChoice =
ObjectOrientedClassLayout.CLASS_HIERARCHY;
@@ -116,19 +116,19 @@ public class CppCompositeTypeTest extends AbstractGenericTest {
createVbTables();
msftVxtManager32 = new MsftVxtManager(ctm32, program32);
msftVxtManager64 = new MsftVxtManager(ctm64, program64);
vxtManager32 = new MsftVxtManager(ctm32, program32);
vxtManager64 = new MsftVxtManager(ctm64, program64);
try {
msftVxtManager32.createVirtualTables(CategoryPath.ROOT, addressByMangledName32, log,
vxtManager32.createVirtualTables(CategoryPath.ROOT, addressByMangledName32, log,
monitor);
msftVxtManager64.createVirtualTables(CategoryPath.ROOT, addressByMangledName64, log,
vxtManager64.createVirtualTables(CategoryPath.ROOT, addressByMangledName64, log,
monitor);
}
catch (CancelledException e) {
// do nothing
}
vxtManager32 = new VxtManager(ctm32);
vxtManager64 = new VxtManager(ctm64);
vxtManagerNoProgram32 = new MsftVxtManager(ctm32, null);
vxtManagerNoProgram64 = new MsftVxtManager(ctm64, null);
}
@@ -227,8 +227,8 @@ public class CppCompositeTypeTest extends AbstractGenericTest {
private FunctionManager myFunctionManager;
private MyStubProgram(Memory mem, FunctionManager fm) {
this.myMemory = mem;
this.myFunctionManager = fm;
myMemory = mem;
myFunctionManager = fm;
}
@Override
@@ -6994,7 +6994,7 @@ public class CppCompositeTypeTest extends AbstractGenericTest {
public void test_32bit_vbt() throws Exception {
boolean is64Bit = false;
MyTestDummyDataTypeManager dtm = dtm32;
VxtManager vxtManager = msftVxtManager32;
MsftVxtManager vxtManager = vxtManager32;
List<String> expectedResults = new ArrayList<>();
expectedResults.add(getExpectedA_32());
expectedResults.add(getExpectedC_32());
@@ -7037,7 +7037,7 @@ public class CppCompositeTypeTest extends AbstractGenericTest {
public void test_32bit_speculative() throws Exception {
boolean is64Bit = false;
MyTestDummyDataTypeManager dtm = dtm32;
VxtManager vxtManager = vxtManager32;
MsftVxtManager vxtManager = vxtManagerNoProgram32;
List<String> expectedResults = new ArrayList<>();
expectedResults.add(getSpeculatedA_32());
expectedResults.add(getSpeculatedC_32());
@@ -7080,7 +7080,7 @@ public class CppCompositeTypeTest extends AbstractGenericTest {
public void test_64bit_vbt() throws Exception {
boolean is64Bit = true;
MyTestDummyDataTypeManager dtm = dtm64;
VxtManager vxtManager = msftVxtManager64;
MsftVxtManager vxtManager = vxtManager64;
List<String> expectedResults = new ArrayList<>();
expectedResults.add(getExpectedA_64());
expectedResults.add(getExpectedC_64());
@@ -7123,7 +7123,7 @@ public class CppCompositeTypeTest extends AbstractGenericTest {
public void test_64bit_speculative() throws Exception {
boolean is64Bit = true;
MyTestDummyDataTypeManager dtm = dtm64;
VxtManager vxtManager = vxtManager64;
MsftVxtManager vxtManager = vxtManagerNoProgram64;
List<String> expectedResults = new ArrayList<>();
expectedResults.add(getSpeculatedA_64());
expectedResults.add(getSpeculatedC_64());
@@ -7159,7 +7159,7 @@ public class CppCompositeTypeTest extends AbstractGenericTest {
}
private void createAndTestStructures(boolean is64Bit, DataTypeManager dtm,
VxtManager vxtManager, List<String> expectedResults) throws Exception {
MsftVxtManager vxtManager, List<String> expectedResults) throws Exception {
Iterator<String> iter = expectedResults.iterator();
String expected;
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.pdb.classtype;
package ghidra.program.model.gclass;
import java.util.Objects;
@@ -21,10 +21,10 @@ 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.
* Unique ID of a Program Class Type. Not sure if there will be different implementation for
* definition vs. compiled vs. program vs. debug.
*/
public class ProgramClassID implements ClassID {
public class ClassID implements Comparable<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
@@ -32,14 +32,14 @@ public class ProgramClassID implements ClassID {
// DataTypePath changed out from underneath us).
private final SymbolPath symbolPath;
private final CategoryPath categoryPath;
static final int classNameHash = Objects.hash(ProgramClassID.class.getName());
static final int classNameHash = Objects.hash(ClassID.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) {
public ClassID(CategoryPath categoryPath, SymbolPath symbolPath) {
this.categoryPath = categoryPath;
this.symbolPath = symbolPath;
}
@@ -69,11 +69,6 @@ public class ProgramClassID implements ClassID {
// return dataTypeID;
// }
@Override
public int getClassNameHash() {
return classNameHash;
}
@Override
public String toString() {
return String.format("%s --- %s", categoryPath, symbolPath);
@@ -82,18 +77,11 @@ public class ProgramClassID implements ClassID {
@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);
ret = symbolPath.compareTo(o.symbolPath);
if (ret != 0) {
return ret;
}
return categoryPath.compareTo(oID.categoryPath);
return categoryPath.compareTo(o.categoryPath);
}
@Override
@@ -112,7 +100,7 @@ public class ProgramClassID implements ClassID {
if (getClass() != obj.getClass()) {
return false;
}
ProgramClassID other = (ProgramClassID) obj;
ClassID other = (ClassID) obj;
return Objects.equals(categoryPath, other.categoryPath) &&
Objects.equals(symbolPath, other.symbolPath);
}
@@ -15,6 +15,7 @@
*/
package ghidra.program.model.gclass;
import ghidra.app.util.SymbolPath;
import ghidra.program.model.data.*;
/**
@@ -40,6 +41,11 @@ public class ClassUtils {
*/
public static final PointerDataType VXPTR_TYPE = new PointerDataType();
/**
* The standard prefix used for the special symbol. Private for now.
*/
private static final String VTABLE_PREFIX = "VTABLE_";
/**
* private constructor -- no instances
*/
@@ -54,8 +60,27 @@ public class ClassUtils {
*/
public static CategoryPath getClassInternalsPath(Composite composite) {
DataTypePath dtp = composite.getDataTypePath();
return new CategoryPath(new CategoryPath(dtp.getCategoryPath(), dtp.getDataTypeName()),
"!internal");
return getClassInternalsPath(dtp.getCategoryPath(), dtp.getDataTypeName());
}
/**
* Returns the category for class internals for the ClassID
* @param id the class ID
* @return the category path
*/
public static CategoryPath getClassInternalsPath(ClassID id) {
CategoryPath cp = recurseGetCategoryPath(id.getCategoryPath(), id.getSymbolPath());
return cp.extend("!internal");
}
/**
* Returns the category for class internals
* @param path the category path of the class composite
* @param className the name of the class
* @return the category path
*/
public static CategoryPath getClassInternalsPath(CategoryPath path, String className) {
return new CategoryPath(new CategoryPath(path, className), "!internal");
}
/**
@@ -92,4 +117,46 @@ public class ClassUtils {
return composite;
}
/**
* Provides the standard special name for a virtual table (e.g., vbtable, vftable) that is
* keyed off of by the Decompiler during flattening and replacing of types within a class
* structure. More details to come
* @param ptrOffsetInClass the offset of the special field within the class
* @return the special name
*/
public static String getSpecialVxTableName(long ptrOffsetInClass) {
return String.format("%s%08x", VTABLE_PREFIX, ptrOffsetInClass);
}
public static DataType getVftDefaultEntry(DataTypeManager dtm) {
return new PointerDataType(dtm);
}
public static DataType getVbtDefaultEntry(DataTypeManager dtm) {
return new IntegerDataType(dtm);
}
public static int getVftEntrySize(DataTypeManager dtm) {
return dtm.getDataOrganization().getPointerSize();
}
public static int getVbtEntrySize(DataTypeManager dtm) {
return dtm.getDataOrganization().getIntegerSize();
}
/**
* Method to get a category path from a base category path and symbol path
* @param category the {@ink CategoryPath} on which to build
* @param symbolPath the current {@link SymbolPath} from which the current name is pulled.
* @return the new {@link CategoryPath} for the recursion level
*/
private static CategoryPath recurseGetCategoryPath(CategoryPath category,
SymbolPath symbolPath) {
SymbolPath parent = symbolPath.getParent();
if (parent != null) {
category = recurseGetCategoryPath(category, parent);
}
return new CategoryPath(category, symbolPath.getName());
}
}