diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/ClassFieldAttributes.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/ClassFieldAttributes.java index 98007248af..7c2729e37a 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/ClassFieldAttributes.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/ClassFieldAttributes.java @@ -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; diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/ClassTypeManager.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/ClassTypeManager.java index 52190a849f..0b7c7227f5 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/ClassTypeManager.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/ClassTypeManager.java @@ -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(); } /** diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/MsftVxtManager.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/MsftVxtManager.java index 4f5a4c23c4..6ab5cb0766 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/MsftVxtManager.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/MsftVxtManager.java @@ -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> vbtsByOwner; - private Map> vftsByOwner; + private Map> vbtsByOwner; + private Map> 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 vbtsByOwnerParentage; + private Map 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 map = vbtsByOwner.get(owner); + public VirtualBaseTable findPrimaryVbt(ClassID owner) { + TreeMap 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 map = vftsByOwner.get(owner); + public VirtualFunctionTable findPrimaryVft(ClassID owner) { + TreeMap 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 map = vbtsByOwner.get(owner); + public VirtualBaseTable[] getVbts(ClassID owner) { + TreeMap map = vbtsByOwner.get(owner); if (map == null) { return null; } - Collection values = map.values(); - return values.toArray(new VBTable[values.size()]); + Collection 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 map = vftsByOwner.get(owner); + public VirtualFunctionTable[] getVfts(ClassID owner) { + TreeMap map = vftsByOwner.get(owner); if (map == null) { return null; } - Collection values = map.values(); - return values.toArray(new VFTable[values.size()]); + Collection 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 parentage) { + public VirtualBaseTable findVbt(ClassID owner, List 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 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 parentage) { + public VirtualFunctionTable findVft(ClassID owner, List 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 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 map = vbtsByOwner.get(owner); + private void storeVbt(ClassID owner, Address address, VirtualBaseTable vbt) { + TreeMap 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 map = vftsByOwner.get(owner); + private void storeVft(ClassID owner, Address address, VirtualFunctionTable vft) { + TreeMap 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 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 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 parentageSps = getParentageSymbolPaths(vxTable.getNestedQualifications()); List 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; } diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/ClassID.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/OwnerParentage.java similarity index 59% rename from Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/ClassID.java rename to Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/OwnerParentage.java index d10ee6a598..4cbba0320f 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/ClassID.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/OwnerParentage.java @@ -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 { - - // For compareTo() method of classes in this hierarchy (for Comparable) - /** - * For internal use - * @return hash of java class in ClassID hierarchy - */ - public int getClassNameHash(); - -} +public record OwnerParentage(ClassID owner, List parentage) {} diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/PlaceholderVirtualBaseTable.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/PlaceholderVirtualBaseTable.java index add13dc88c..a736c33af8 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/PlaceholderVirtualBaseTable.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/PlaceholderVirtualBaseTable.java @@ -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 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 parentage, int entrySize) { + public PlaceholderVirtualBaseTable(ClassID owner, List 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; } diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/PlaceholderVirtualBaseTableEntry.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/PlaceholderVirtualBaseTableEntry.java new file mode 100644 index 0000000000..3036b6f466 --- /dev/null +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/PlaceholderVirtualBaseTableEntry.java @@ -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)); + } + +} diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/PlaceholderVirtualFunctionTable.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/PlaceholderVirtualFunctionTable.java new file mode 100644 index 0000000000..c496b6bc91 --- /dev/null +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/PlaceholderVirtualFunctionTable.java @@ -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 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; + } + +} diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/PlaceholderVirtualFunctionTableEntry.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/PlaceholderVirtualFunctionTableEntry.java new file mode 100644 index 0000000000..259a06c701 --- /dev/null +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/PlaceholderVirtualFunctionTableEntry.java @@ -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 ? "" : address.toString(), path.toString())); + } + +} diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/ProgramVirtualBaseTable.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/ProgramVirtualBaseTable.java index 78e4741fce..df4ca5267b 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/ProgramVirtualBaseTable.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/ProgramVirtualBaseTable.java @@ -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 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 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); - } - } diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/ProgramVirtualFunctionTable.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/ProgramVirtualFunctionTable.java index ab868668ae..ed2fe6fd33 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/ProgramVirtualFunctionTable.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/ProgramVirtualFunctionTable.java @@ -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; + } + } diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/VBTable.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/VBTable.java index 1fdea1cfda..016c6fd1df 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/VBTable.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/VBTable.java @@ -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 } diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/VBTableEntry.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/VBTableEntry.java index 2dc4ad76c4..444b64a4bd 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/VBTableEntry.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/VBTableEntry.java @@ -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 diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/VFTable.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/VFTable.java index a91d09fc64..fb6d7c14a2 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/VFTable.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/VFTable.java @@ -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 } diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/VFTableEntry.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/VFTableEntry.java new file mode 100644 index 0000000000..c5e28410e4 --- /dev/null +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/VFTableEntry.java @@ -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(); + +} diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/VXT.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/VXT.java new file mode 100644 index 0000000000..1bdab9b635 --- /dev/null +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/VXT.java @@ -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 +} diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/VXTEntry.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/VXTEntry.java new file mode 100644 index 0000000000..46ac4eee72 --- /dev/null +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/VXTEntry.java @@ -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 +} diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/VirtualBaseTable.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/VirtualBaseTable.java index 8169090a09..b024dda0e3 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/VirtualBaseTable.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/VirtualBaseTable.java @@ -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 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 entryByTableIndex; + protected Map 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 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 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 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; + } + } diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/VirtualBaseTableEntry.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/VirtualBaseTableEntry.java index 0d7a0df188..e1fa51a177 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/VirtualBaseTableEntry.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/VirtualBaseTableEntry.java @@ -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; diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/VirtualFunctionTable.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/VirtualFunctionTable.java index 0da715b9d4..949ec9d2f1 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/VirtualFunctionTable.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/VirtualFunctionTable.java @@ -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 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 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 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 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 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; + } + } diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/VirtualFunctionTableEntry.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/VirtualFunctionTableEntry.java new file mode 100644 index 0000000000..7efc51ea2a --- /dev/null +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/VirtualFunctionTableEntry.java @@ -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; + } + +} diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/CompositeTypeApplier.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/CompositeTypeApplier.java index 5f569c1b87..c921cc85ae 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/CompositeTypeApplier.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/CompositeTypeApplier.java @@ -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 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 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 msMembers, AbstractCompositeMsType type, List myMembers) throws CancelledException, PdbException { diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/CppCompositeType.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/CppCompositeType.java index b7d7432645..ef1b066ee1 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/CppCompositeType.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/CppCompositeType.java @@ -25,6 +25,7 @@ import ghidra.app.util.bin.format.pdb2.pdbreader.PdbException; import ghidra.app.util.bin.format.pdb2.pdbreader.PdbLog; import ghidra.app.util.pdb.classtype.*; import ghidra.program.model.data.*; +import ghidra.program.model.gclass.ClassID; import ghidra.program.model.gclass.ClassUtils; import ghidra.util.Msg; import ghidra.util.exception.CancelledException; @@ -41,7 +42,6 @@ public class CppCompositeType { private static final String VIRTUAL_BASE_COMMENT = "Virtual Base"; private static final String VIRTUAL_BASE_SPECULATIVE_COMMENT = "Virtual Base - Speculative Placement"; - //private static final String INDIRECT_VIRTUAL_BASE_CLASS_COMMENT = "Indirect Virtual Base Class"; private boolean isFinal; private ClassKey classKey; @@ -50,8 +50,7 @@ public class CppCompositeType { private int size; private SymbolPath symbolPath; private CategoryPath categoryPath; - private DataTypePath selfBaseDataTypePath; - private ProgramClassID myId; + private ClassID myId; private CategoryPath baseCategoryPath; private CategoryPath internalsCategoryPath; @@ -71,6 +70,13 @@ public class CppCompositeType { private List myMembers; private List layoutMembers; + private static record VirtualFunctionInfo(Integer tableOffset, Integer thisAdjuster, + SymbolPath name, FunctionDefinition definition) {} + + private List virtualFunctionInfo; + + //---- + private List syntacticBaseClasses; //============================================================================================== @@ -99,6 +105,11 @@ public class CppCompositeType { */ private Long mainVbtPtrOffset; + /** + * Holds the main Vft for this class + */ + private VirtualFunctionTable mainVft; + /** * Holds the main Vbt for this class */ @@ -126,19 +137,6 @@ public class CppCompositeType { private TreeMap baseOffsetById; //============================================================================================== - // Data used for resolving main vftptr - - /* - * Not certain, but think there should only be one Virtual Base Table for a given - * class (not counting those for its parents). However, since VirtualBaseClass and - * IndirectVirtualBase class records both have an "offset" for (seemingly) where the - * virtual base table point can be located, then there is a chance that different - * records for a class could have different values. This HashMap will is keyed by this - * offset, in case we see more than one. Want to log the fact if more than one value is seen - * for a particular hierarchy level. - */ - private Map placeholderVirtualBaseTables; - //============================================================================================== // Data used for analyzing Vxts and their parentage @@ -148,8 +146,10 @@ public class CppCompositeType { private TreeSet propagatedDirectVirtualBaseVbts; private TreeSet propagatededIndirectVirtualBaseVfts; private TreeSet propagatedIndirectVirtualBaseVbts; - private TreeMap finalLayoutVfts; - private TreeMap finalLayoutVbts; + private TreeMap finalVftPtrInfoByOffset; + private TreeMap finalVbtPtrInfoByOffset; + private TreeMap finalVftByOffset; + private TreeMap finalVbtByOffset; //============================================================================================== //============================================================================================== @@ -165,13 +165,14 @@ public class CppCompositeType { this.composite = composite; this.mangledName = mangledName; myId = getClassId(this); + categoryPath = new CategoryPath(composite.getCategoryPath(), composite.getName()); internalsCategoryPath = ClassUtils.getClassInternalsPath(composite); // eliminate - selfBaseDataTypePath = ClassUtils.getBaseClassDataTypePath(composite); directLayoutBaseClasses = new ArrayList<>(); virtualLayoutBaseClasses = new ArrayList<>(); directVirtualLayoutBaseClasses = new ArrayList<>(); indirectVirtualLayoutBaseClasses = new ArrayList<>(); + virtualFunctionInfo = new ArrayList<>(); vftPtrTypeByOffset = new TreeMap<>(); myMembers = new ArrayList<>(); @@ -343,7 +344,7 @@ public class CppCompositeType { } /** - * Method for adding a member to this type, to include a attribtues and comment + * Method for adding a member to this type, to include a attributes and comment * @param memberName member name * @param dataType data type of member * @param isFlexibleArray {@code true} if member is a flexible array @@ -359,6 +360,20 @@ public class CppCompositeType { addMember(layoutMembers, newMember); } + /** + * Method for adding a virtual method to this type + * @param thisAdjuster the this-adjustor offset + * @param tableOffset virtual function table offset + * @param name function name + * @param definition function definition + */ + public void addVirtualMethod(int thisAdjuster, int tableOffset, SymbolPath name, + FunctionDefinition definition) { + VirtualFunctionInfo info = + new VirtualFunctionInfo(tableOffset, thisAdjuster, name, definition); + virtualFunctionInfo.add(info); + } + /** * Method to perform class layout from the user specified information. Note that all * dependency classes (parents, etc.) must have had their like-processing performed @@ -368,7 +383,7 @@ public class CppCompositeType { * @throws PdbException upon issue performing the layout * @throws CancelledException upon user cancellation */ - public void createLayout(ObjectOrientedClassLayout layoutOptions, VxtManager vxtManager, + public void createLayout(ObjectOrientedClassLayout layoutOptions, MsftVxtManager vxtManager, TaskMonitor monitor) throws PdbException, CancelledException { switch (layoutOptions) { case MEMBERS_ONLY: @@ -427,6 +442,14 @@ public class CppCompositeType { return classKey; } + /** + * Returns the default access of the type + * @return the default access + */ + public Access getDefaultAccess() { + return ClassKey.CLASS.equals(classKey) ? Access.PRIVATE : Access.PUBLIC; + } + /** * Method to set the name of the composite * @param className the name @@ -518,7 +541,7 @@ public class CppCompositeType { * Returns the ClassID for this class * @return the class id */ - public ProgramClassID getClassId() { + public ClassID getClassId() { return myId; } @@ -540,6 +563,14 @@ public class CppCompositeType { return selfBaseType; } + /** + * Returns the CategoryPath of this composite + * @return the CategoryPath + */ + public CategoryPath getCategoryPath() { + return categoryPath; + } + // TODO: move to ClassUtils? /** * Returns the "internals" CategoryPath of this composite @@ -672,8 +703,8 @@ public class CppCompositeType { * @param cpp the CPP type * @return the class id */ - private static ProgramClassID getClassId(CppCompositeType cpp) { - return new ProgramClassID(cpp.baseCategoryPath, cpp.getSymbolPath()); + private static ClassID getClassId(CppCompositeType cpp) { + return new ClassID(cpp.baseCategoryPath, cpp.getSymbolPath()); } //============================================================================================== @@ -769,15 +800,15 @@ public class CppCompositeType { //============================================================================================== //============================================================================================== - private void createHierarchicalClassLayout(VxtManager vxtManager, TaskMonitor monitor) + private void createHierarchicalClassLayout(MsftVxtManager vxtManager, TaskMonitor monitor) throws PdbException, CancelledException { initLayoutAlgorithmData(); - findDirectBaseVxtPtrs(); + findDirectBaseVxtPtrs(vxtManager); - findOrAllocateMainVftPtr(); - findOrAllocateMainVbtPtr(); + findOrAllocateMainVftPtr(vxtManager); + findOrAllocateMainVbtPtr(vxtManager); createClassLayout(vxtManager, monitor); @@ -803,11 +834,6 @@ public class CppCompositeType { baseOffsetById = new TreeMap<>(); - //====== - // Data used for resolving main vftptr - - placeholderVirtualBaseTables = new HashMap<>(); - //====== // Data used for analyzing Vxts and their parentage @@ -817,8 +843,10 @@ public class CppCompositeType { propagatedDirectVirtualBaseVbts = new TreeSet<>(); propagatededIndirectVirtualBaseVfts = new TreeSet<>(); propagatedIndirectVirtualBaseVbts = new TreeSet<>(); - finalLayoutVfts = new TreeMap<>(); - finalLayoutVbts = new TreeMap<>(); + finalVftPtrInfoByOffset = new TreeMap<>(); + finalVbtPtrInfoByOffset = new TreeMap<>(); + finalVftByOffset = new TreeMap<>(); + finalVbtByOffset = new TreeMap<>(); } /** @@ -838,45 +866,45 @@ public class CppCompositeType { PNode childToParentNode; PNode parentToChildNode; - for (VxtPtrInfo info : finalLayoutVfts.values()) { + for (VxtPtrInfo info : finalVftPtrInfoByOffset.values()) { List parentage = info.parentage(); childToParentNode = vftChildToParentRoot; parentToChildNode = vftParentToChildRoot; for (ClassID id : parentage) { - String name = ((ProgramClassID) id).getSymbolPath().toString(); + String name = id.getSymbolPath().toString(); childToParentNode.incrementPathCount(); childToParentNode = childToParentNode.getOrAddBranch(name); } for (ClassID id : parentage.reversed()) { - String name = ((ProgramClassID) id).getSymbolPath().toString(); + String name = id.getSymbolPath().toString(); parentToChildNode.incrementPathCount(); parentToChildNode = parentToChildNode.getOrAddBranch(name); } } - for (VxtPtrInfo info : finalLayoutVbts.values()) { + for (VxtPtrInfo info : finalVbtPtrInfoByOffset.values()) { List parentage = info.parentage(); childToParentNode = vbtChildToParentRoot; parentToChildNode = vbtParentToChildRoot; for (ClassID id : parentage) { - String name = ((ProgramClassID) id).getSymbolPath().toString(); + String name = id.getSymbolPath().toString(); childToParentNode.incrementPathCount(); childToParentNode = childToParentNode.getOrAddBranch(name); } for (ClassID id : parentage.reversed()) { - String name = ((ProgramClassID) id).getSymbolPath().toString(); + String name = id.getSymbolPath().toString(); parentToChildNode.incrementPathCount(); parentToChildNode = parentToChildNode.getOrAddBranch(name); } } StringBuilder builder = new StringBuilder(); - for (VxtPtrInfo info : finalLayoutVfts.values()) { + for (VxtPtrInfo info : finalVftPtrInfoByOffset.values()) { List altParentage = finalizeVxtPtrParentage(vftChildToParentRoot, vftParentToChildRoot, info); builder.append(dumpVxtPtrResult("vft", info, altParentage)); } - for (VxtPtrInfo info : finalLayoutVbts.values()) { + for (VxtPtrInfo info : finalVbtPtrInfoByOffset.values()) { List altParentage = finalizeVxtPtrParentage(vbtChildToParentRoot, vbtParentToChildRoot, info); builder.append(dumpVxtPtrResult("vbt", info, altParentage)); @@ -901,7 +929,7 @@ public class CppCompositeType { String startNode = null; for (ClassID id : parentage) { - String name = ((ProgramClassID) id).getSymbolPath().toString(); + String name = id.getSymbolPath().toString(); childToParentNode = childToParentNode.getBranch(name); if (childToParentNode.getPathCount() == 1) { startNode = name; @@ -913,7 +941,7 @@ public class CppCompositeType { // (use all nodes) boolean foundStart = (startNode == null); for (ClassID id : parentage.reversed()) { - String name = ((ProgramClassID) id).getSymbolPath().toString(); + String name = id.getSymbolPath().toString(); if (name.equals(startNode)) { foundStart = true; } @@ -940,12 +968,12 @@ public class CppCompositeType { private String dumpVxtPtrResult(String vxt, VxtPtrInfo info, List altParentage) { List r1 = new ArrayList<>(); for (ClassID id : altParentage.reversed()) { - String name = ((ProgramClassID) id).getSymbolPath().toString(); + String name = id.getSymbolPath().toString(); r1.add(name); } List r2 = new ArrayList<>(); for (ClassID id : info.parentage().reversed()) { - String name = ((ProgramClassID) id).getSymbolPath().toString(); + String name = id.getSymbolPath().toString(); r2.add(name); } return String.format(" %4d %s %s\t%s\n", info.finalOffset(), vxt, r1.toString(), @@ -990,9 +1018,17 @@ public class CppCompositeType { * @throws CancelledException upon user cancellation * @throws PdbException up issue with finding the vbt or assigning offsets to virtual bases */ - private void createClassLayout(VxtManager vxtManager, TaskMonitor monitor) + private void createClassLayout(MsftVxtManager vxtManager, TaskMonitor monitor) throws CancelledException, PdbException { List selfBaseMembers = getSelfBaseClassMembers(); + mainVft = getMainVft(vxtManager); + if (mainVft != null) { + updateMainVft(); + for (VXT t : finalVftByOffset.values()) { + VirtualFunctionTable vft = (VirtualFunctionTable) t; + updateVftFromSelf(vft); + } + } if (getNumLayoutVirtualBaseClasses() == 0) { if (!DefaultCompositeMember.applyDataTypeMembers(composite, false, false, size, selfBaseMembers, msg -> Msg.warn(this, msg), monitor)) { @@ -1010,13 +1046,22 @@ public class CppCompositeType { new ClassPdbMember("", selfBaseType, false, 0, SELF_BASE_COMMENT); mainVbt = getMainVbt(vxtManager); + if (mainVbt != null) { + updateMainVbt(); + // If there was any updating to do for secondary tables, we would do it here. + // Something to consider in the future +// for (VXT t : finalVbtByOffset.values()) { +// VirtualBaseTable vbt = (VirtualBaseTable) t; +// updateVbtFromSelf(vbt); +// } + } assignVirtualBaseOffsets(); String baseComment = (mainVbt instanceof ProgramVirtualBaseTable) ? VIRTUAL_BASE_COMMENT : VIRTUAL_BASE_SPECULATIVE_COMMENT; TreeMap virtualBasePdbMembers = getVirtualBaseClassMembers(baseComment); - findVirtualBaseVxtPtrs(); + findVirtualBaseVxtPtrs(vxtManager); TreeMap allMembers = new TreeMap<>(); allMembers.put(0L, directClassPdbMember); @@ -1178,22 +1223,26 @@ public class CppCompositeType { * Finds all virtual base and virtual function pointers in the hierarchy of this class's * self base. */ - private void findDirectBaseVxtPtrs() { + private void findDirectBaseVxtPtrs(VxtManager vxtManager) { for (DirectLayoutBaseClass base : directLayoutBaseClasses) { CppCompositeType cppBaseType = base.getBaseClassType(); - ProgramClassID baseId = cppBaseType.getClassId(); + ClassID baseId = cppBaseType.getClassId(); long baseOffset = base.getOffset(); // Note that if the parent has already had its layout done, it will not have // used the vxtManager that we are passing in here; it will have used whatever // was passed to the layout method for that class - for (VxtPtrInfo info : cppBaseType.getPropagatedSelfBaseVfts()) { - VxtPtrInfo newInfo = createSelfOwnedDirectVxtPtrInfo(info, baseId, baseOffset); - storeVxtInfo(propagatedSelfBaseVfts, finalLayoutVfts, vftTableIdByOffset, + for (VxtPtrInfo parentInfo : cppBaseType.getPropagatedSelfBaseVfts()) { + VxtPtrInfo newInfo = + createSelfOwnedDirectVxtPtrInfo(parentInfo, baseId, baseOffset); + updateVft(vxtManager, baseId, newInfo, parentInfo); + storeVxtInfo(propagatedSelfBaseVfts, finalVftPtrInfoByOffset, vftTableIdByOffset, vftOffsetByTableId, newInfo); } - for (VxtPtrInfo info : cppBaseType.getPropagatedSelfBaseVbts()) { - VxtPtrInfo newInfo = createSelfOwnedDirectVxtPtrInfo(info, baseId, baseOffset); - storeVxtInfo(propagatedSelfBaseVbts, finalLayoutVbts, vbtTableIdByOffset, + for (VxtPtrInfo parentInfo : cppBaseType.getPropagatedSelfBaseVbts()) { + VxtPtrInfo newInfo = + createSelfOwnedDirectVxtPtrInfo(parentInfo, baseId, baseOffset); + updateVbt(vxtManager, baseId, newInfo, parentInfo); + storeVxtInfo(propagatedSelfBaseVbts, finalVbtPtrInfoByOffset, vbtTableIdByOffset, vbtOffsetByTableId, newInfo); } } @@ -1205,32 +1254,36 @@ public class CppCompositeType { * we are not relying on the "indirect" virtual base class information from the PDB. This * is done this way so that we can collect parentage information for the pointers. */ - private void findVirtualBaseVxtPtrs() { + private void findVirtualBaseVxtPtrs(MsftVxtManager vxtManager) { // Walk direct bases to find vxts of virtual bases. TODO: also notate all rolled up // virtuals for each direct base. for (DirectLayoutBaseClass base : directLayoutBaseClasses) { CppCompositeType cppBaseType = base.getBaseClassType(); - + ClassID baseId = cppBaseType.getClassId(); for (VxtPtrInfo info : cppBaseType.getPropagatedDirectVirtualBaseVfts()) { VxtPtrInfo newInfo = createSelfOwnedVirtualVxtPtrInfo(info); - storeVxtInfo(propagatedDirectVirtualBaseVfts, finalLayoutVfts, vftTableIdByOffset, - vftOffsetByTableId, newInfo); + updateVft(vxtManager, baseId, newInfo, info); + storeVxtInfo(propagatedDirectVirtualBaseVfts, finalVftPtrInfoByOffset, + vftTableIdByOffset, vftOffsetByTableId, newInfo); } for (VxtPtrInfo info : cppBaseType.getPropagatedDirectVirtualBaseVbts()) { VxtPtrInfo newInfo = createSelfOwnedVirtualVxtPtrInfo(info); - storeVxtInfo(propagatedDirectVirtualBaseVbts, finalLayoutVbts, vbtTableIdByOffset, - vbtOffsetByTableId, newInfo); + updateVbt(vxtManager, baseId, newInfo, info); + storeVxtInfo(propagatedDirectVirtualBaseVbts, finalVbtPtrInfoByOffset, + vbtTableIdByOffset, vbtOffsetByTableId, newInfo); } for (VxtPtrInfo info : cppBaseType.getPropagatedIndirectVirtualBaseVfts()) { VxtPtrInfo newInfo = createSelfOwnedVirtualVxtPtrInfo(info); - storeVxtInfo(propagatededIndirectVirtualBaseVfts, finalLayoutVfts, + updateVft(vxtManager, baseId, newInfo, info); + storeVxtInfo(propagatededIndirectVirtualBaseVfts, finalVftPtrInfoByOffset, vftTableIdByOffset, vftOffsetByTableId, newInfo); } for (VxtPtrInfo info : cppBaseType.getPropagatedIndirectVirtualBaseVbts()) { VxtPtrInfo newInfo = createSelfOwnedVirtualVxtPtrInfo(info); - storeVxtInfo(propagatedIndirectVirtualBaseVbts, finalLayoutVbts, vbtTableIdByOffset, - vbtOffsetByTableId, newInfo); + updateVbt(vxtManager, baseId, newInfo, info); + storeVxtInfo(propagatedIndirectVirtualBaseVbts, finalVbtPtrInfoByOffset, + vbtTableIdByOffset, vbtOffsetByTableId, newInfo); } } @@ -1240,40 +1293,46 @@ public class CppCompositeType { for (DirectVirtualLayoutBaseClass base : directVirtualLayoutBaseClasses) { CppCompositeType cppBaseType = base.getBaseClassType(); - ProgramClassID baseId = cppBaseType.getClassId(); + ClassID baseId = cppBaseType.getClassId(); for (VxtPtrInfo info : cppBaseType.getPropagatedSelfBaseVfts()) { VxtPtrInfo newInfo = createVirtualOwnedSelfVxtPtrInfo(info, baseId); - storeVxtInfo(propagatedDirectVirtualBaseVfts, finalLayoutVfts, vftTableIdByOffset, - vftOffsetByTableId, newInfo); + updateVft(vxtManager, baseId, newInfo, info); + storeVxtInfo(propagatedDirectVirtualBaseVfts, finalVftPtrInfoByOffset, + vftTableIdByOffset, vftOffsetByTableId, newInfo); } for (VxtPtrInfo info : cppBaseType.getPropagatedSelfBaseVbts()) { VxtPtrInfo newInfo = createVirtualOwnedSelfVxtPtrInfo(info, baseId); - storeVxtInfo(propagatedDirectVirtualBaseVbts, finalLayoutVbts, vbtTableIdByOffset, - vbtOffsetByTableId, newInfo); + updateVbt(vxtManager, baseId, newInfo, info); + storeVxtInfo(propagatedDirectVirtualBaseVbts, finalVbtPtrInfoByOffset, + vbtTableIdByOffset, vbtOffsetByTableId, newInfo); } for (VxtPtrInfo info : cppBaseType.getPropagatedDirectVirtualBaseVfts()) { VxtPtrInfo newInfo = createVirtualOwnedVirtualVxtPtrInfo(info); - storeVxtInfo(propagatededIndirectVirtualBaseVfts, finalLayoutVfts, + updateVft(vxtManager, baseId, newInfo, info); + storeVxtInfo(propagatededIndirectVirtualBaseVfts, finalVftPtrInfoByOffset, vftTableIdByOffset, vftOffsetByTableId, newInfo); } for (VxtPtrInfo info : cppBaseType.getPropagatedDirectVirtualBaseVbts()) { VxtPtrInfo newInfo = createVirtualOwnedVirtualVxtPtrInfo(info); - storeVxtInfo(propagatedIndirectVirtualBaseVbts, finalLayoutVbts, vbtTableIdByOffset, - vbtOffsetByTableId, newInfo); + updateVbt(vxtManager, baseId, newInfo, info); + storeVxtInfo(propagatedIndirectVirtualBaseVbts, finalVbtPtrInfoByOffset, + vbtTableIdByOffset, vbtOffsetByTableId, newInfo); } for (VxtPtrInfo info : cppBaseType.getPropagatedIndirectVirtualBaseVfts()) { VxtPtrInfo newInfo = createVirtualOwnedVirtualVxtPtrInfo(info); - storeVxtInfo(propagatededIndirectVirtualBaseVfts, finalLayoutVfts, + updateVft(vxtManager, baseId, newInfo, info); + storeVxtInfo(propagatededIndirectVirtualBaseVfts, finalVftPtrInfoByOffset, vftTableIdByOffset, vftOffsetByTableId, newInfo); } for (VxtPtrInfo info : cppBaseType.getPropagatedIndirectVirtualBaseVbts()) { VxtPtrInfo newInfo = createVirtualOwnedVirtualVxtPtrInfo(info); - storeVxtInfo(propagatedIndirectVirtualBaseVbts, finalLayoutVbts, vbtTableIdByOffset, - vbtOffsetByTableId, newInfo); + updateVbt(vxtManager, baseId, newInfo, info); + storeVxtInfo(propagatedIndirectVirtualBaseVbts, finalVbtPtrInfoByOffset, + vbtTableIdByOffset, vbtOffsetByTableId, newInfo); } - } + } // Note sure what the final information will look like when we are done. For this stopping @@ -1284,7 +1343,6 @@ public class CppCompositeType { * @param finalInfo the final info tree * @param tableIdByOffset the table-id-by-offset map * @param offsetByTableId the offset-by-table-id map - * @param info the vxt ptr info */ private void storeVxtInfo(TreeSet propagate, TreeMap finalInfo, Map tableIdByOffset, Map offsetByTableId, @@ -1364,7 +1422,7 @@ public class CppCompositeType { throw new PdbException("Cannot place base class"); } baseOffset += base.getBasePointerOffset(); - ProgramClassID baseId = cppBaseType.getClassId(); + ClassID baseId = cppBaseType.getClassId(); baseOffsetById.put(baseId, baseOffset); } } @@ -1373,7 +1431,7 @@ public class CppCompositeType { * Finds or allocates (if needed) the Virtual Function Table "Pointer" within the class * structure */ - private void findOrAllocateMainVftPtr() { + private void findOrAllocateMainVftPtr(MsftVxtManager vxtManager) { if (propagatedSelfBaseVfts.isEmpty()) { if (!vftPtrTypeByOffset.isEmpty()) { if (vftPtrTypeByOffset.size() > 1) { @@ -1382,8 +1440,21 @@ public class CppCompositeType { myVftPtrOffset = vftPtrTypeByOffset.firstKey(); VxtPtrInfo info = new VxtPtrInfo(myVftPtrOffset, myVftPtrOffset, myId, List.of(myId)); - propagatedSelfBaseVfts.add(info); - finalLayoutVfts.put(info.accumOffset(), info); + VirtualFunctionTable myVft = + vxtManager.findVft(myId, List.of(myId)); + if (myVft != null) { + myVft.setPtrOffsetInClass(info.finalOffset()); + propagatedSelfBaseVfts.add(info); + finalVftByOffset.put(info.finalOffset(), myVft); + } + else { + PlaceholderVirtualFunctionTable t = new PlaceholderVirtualFunctionTable( + myId, List.of(myId)); + t.setPtrOffsetInClass(info.finalOffset()); + propagatedSelfBaseVfts.add(info); + finalVftByOffset.put(info.finalOffset(), t); + } + finalVftPtrInfoByOffset.put(info.accumOffset(), info); OwnerParentage op = new OwnerParentage(info.baseId(), info.parentage()); vftTableIdByOffset.put(info.accumOffset(), op); vftOffsetByTableId.put(op, info.accumOffset()); @@ -1391,17 +1462,17 @@ public class CppCompositeType { ClassFieldAttributes.UNKNOWN, myVftPtrOffset.intValue()); layoutVftPtrMembers.add(newMember); myMembers.add(newMember); - } } - mainVftPtrOffset = finalLayoutVfts.isEmpty() ? null : finalLayoutVfts.firstKey(); + mainVftPtrOffset = + finalVftPtrInfoByOffset.isEmpty() ? null : finalVftPtrInfoByOffset.firstKey(); } /** * Finds or allocates (if needed) the Virtual Base Table "Pointer" for within the class * structure */ - private void findOrAllocateMainVbtPtr() { + private void findOrAllocateMainVbtPtr(MsftVxtManager vxtManager) { if (propagatedSelfBaseVbts.isEmpty()) { if (!virtualLayoutBaseClasses.isEmpty()) { TreeSet vbtOffsets = new TreeSet<>(); @@ -1416,103 +1487,314 @@ public class CppCompositeType { Msg.warn(this, "Mismatch vbt location for " + myId); } VxtPtrInfo info = new VxtPtrInfo(vbtPtrOffset, vbtPtrOffset, myId, List.of(myId)); - propagatedSelfBaseVbts.add(info); - finalLayoutVbts.put(info.accumOffset(), info); + VirtualBaseTable myVbt = vxtManager.findVbt(myId, List.of(myId)); + if (myVbt != null) { + myVbt.setPtrOffsetInClass(info.finalOffset()); + propagatedSelfBaseVbts.add(info); + finalVbtByOffset.put(info.finalOffset(), myVbt); + } + finalVbtPtrInfoByOffset.put(info.accumOffset(), info); OwnerParentage op = new OwnerParentage(info.baseId(), info.parentage()); vbtTableIdByOffset.put(info.accumOffset(), op); vbtOffsetByTableId.put(op, info.accumOffset()); - myVbtPtrOffset = finalLayoutVbts.firstKey(); + myVbtPtrOffset = finalVbtPtrInfoByOffset.firstKey(); Member newMember = new Member(ClassUtils.VBPTR, ClassUtils.VXPTR_TYPE, false, ClassFieldAttributes.UNKNOWN, myVbtPtrOffset.intValue()); layoutVbtPtrMembers.add(newMember); myMembers.add(newMember); } } - mainVbtPtrOffset = finalLayoutVbts.isEmpty() ? null : finalLayoutVbts.firstKey(); + mainVbtPtrOffset = + finalVbtPtrInfoByOffset.isEmpty() ? null : finalVbtPtrInfoByOffset.firstKey(); + } + + /** + * Adds new entries to the main vftable for this class + */ + private void updateMainVft() { + for (VirtualFunctionInfo vfInfo : virtualFunctionInfo) { + int tableOffset = vfInfo.tableOffset(); + // we believe this adjuster of 0 is all we want for first direct base + // -1 signifies not intro + if (vfInfo.thisAdjuster() == 0 && vfInfo.tableOffset() != -1) { + mainVft.addEntry(tableOffset, vfInfo.name(), vfInfo.name(), + new PointerDataType(vfInfo.definition())); + } + } + } + + /** + * Updates vftable entries with values from this class that override those of parent classes + */ + private VirtualFunctionTable updateVft(VxtManager vxtManager, ClassID baseId, VxtPtrInfo info, + VxtPtrInfo parentInfo) { + if (!(vxtManager instanceof MsftVxtManager mvxtManager)) { + // error + return null; + } + ClassID parentId; + List parentParentage; + if (parentInfo == null) { + parentId = info.baseId(); + List parentage = info.parentage(); + parentParentage = parentage.subList(0, parentage.size() - 1); + } + else { + parentId = baseId; + parentParentage = parentInfo.parentage(); + } + + Long finalOffset = info.finalOffset(); + VirtualFunctionTable myVft = (VirtualFunctionTable) finalVftByOffset.get(finalOffset); + if (myVft == null) { + myVft = mvxtManager.findVft(myId, info.parentage()); + if (myVft == null) { + return null; + } + finalVftByOffset.put(finalOffset, myVft); + } + + myVft.setPtrOffsetInClass(finalOffset); + VirtualFunctionTable parentVft = + mvxtManager.findVft(parentId, parentParentage); + + if (parentVft == null) { + // this is an error + return null; + } + + for (Map.Entry mapEntry : parentVft + .getEntriesByTableIndex() + .entrySet()) { + int tableOffset = mapEntry.getKey(); + VFTableEntry e = mapEntry.getValue(); + SymbolPath parentOrigPath = e.getOriginalPath(); + SymbolPath parentPath = e.getOverridePath(); + VFTableEntry currentEntry = myVft.getEntry(tableOffset); + if (currentEntry != null) { + SymbolPath currentOrigPath = currentEntry.getOriginalPath(); + SymbolPath currentPath = currentEntry.getOverridePath(); + // Note that this check also checks the method name + if (!parentOrigPath.equals(currentOrigPath)) { + // problem + } + boolean parentOverride = !parentOrigPath.equals(parentPath); + boolean currentOverride = !currentOrigPath.equals(currentPath); + if (!currentOverride && parentOverride) { + myVft.addEntry(tableOffset, parentOrigPath, parentPath, e.getFunctionPointer()); + } + else if (currentOverride && !parentOverride) { + myVft.addEntry(tableOffset, currentOrigPath, currentPath, + e.getFunctionPointer()); + } + else { + // maybe order matters? + } + } + else { + myVft.addEntry(tableOffset, parentOrigPath, parentPath, e.getFunctionPointer()); + } + } + return myVft; + } + + private void updateVftFromSelf(VirtualFunctionTable vft) { + for (Map.Entry mapEntry : vft.getEntriesByTableIndex() + .entrySet()) { + int tableOffset = mapEntry.getKey(); + VFTableEntry e = mapEntry.getValue(); + SymbolPath origPath = e.getOriginalPath(); + SymbolPath methodPath = e.getOverridePath(); + String methodName = methodPath.getName(); + for (VirtualFunctionInfo vfInfo : virtualFunctionInfo) { + SymbolPath selfMethodPath = vfInfo.name(); + String selfMethodName = selfMethodPath.getName(); + if (selfMethodName.equals(methodName)) { + // potential overridden method; just replace path (could be the same) + methodPath = selfMethodPath; + break; + } + } + vft.addEntry(tableOffset, origPath, methodPath, e.getFunctionPointer()); + } + } + + private void updateMainVbt() { + int numEntries = virtualLayoutBaseClasses.size(); + Integer existingEntries = mainVbt.getNumEntries(); + if (numEntries < existingEntries) { + // error: silent for now... not sure how we want to deal with this + return; + } + for (VirtualLayoutBaseClass virtualLayoutBaseClass : virtualLayoutBaseClasses) { + int tableOffset = virtualLayoutBaseClass.getOffetFromVbt(); + // Value in base class is more of an index + ClassID baseId = virtualLayoutBaseClass.getBaseClassType().getClassId(); + int vbtPtrOffset = virtualLayoutBaseClass.getBasePointerOffset(); + if (vbtPtrOffset != mainVbtPtrOffset) { + // error + // ignoring for now... not sure how we want to deal with this + continue; + } + VBTableEntry e = mainVbt.getEntry(tableOffset); + if (e == null) { + mainVbt.addEntry(tableOffset, baseId); + } + // No need to update an existing entry in base table + } + } + + // TODO: Remove? Believe that only the main VBT should ever possibly get updated. The others + // will only get updated in size when they are the main VBT within those respective base + // classes. + private VirtualBaseTable updateVbt(VxtManager vxtManager, ClassID baseId, VxtPtrInfo info, + VxtPtrInfo parentInfo) { + if (!(vxtManager instanceof MsftVxtManager mvxtManager)) { + // error + return null; + } + ClassID parentId; + List parentParentage; + if (parentInfo == null) { + parentId = info.baseId(); + List parentage = info.parentage(); + parentParentage = parentage.subList(0, parentage.size() - 1); + } + else { + parentId = baseId; + parentParentage = parentInfo.parentage(); + } + + Long finalOffset = info.finalOffset(); + VirtualBaseTable myVbt = (VirtualBaseTable) finalVbtByOffset.get(finalOffset); + if (myVbt == null) { + myVbt = mvxtManager.findVbt(myId, info.parentage()); + if (myVbt == null) { + return null; + } + finalVbtByOffset.put(finalOffset, myVbt); + } + + myVbt.setPtrOffsetInClass(finalOffset); + VirtualBaseTable parentVbt = + mvxtManager.findVbt(parentId, parentParentage); + if (parentVbt == null) { + // this is an error + return null; + } + for (Map.Entry mapEntry : parentVbt.getEntriesByTableIndex() + .entrySet()) { + int tableOffset = mapEntry.getKey(); + VBTableEntry e = mapEntry.getValue(); + myVbt.addEntry(tableOffset, e.getClassId()); + } + + return myVbt; } /** * Provides the Virtual Base Table to be used for placing virtual bases of this class + * @throws PdbException upon unrecognized vft type */ - private VirtualBaseTable getMainVbt(VxtManager vxtManager) throws PdbException { - VirtualBaseTable vbt = null; - if (!finalLayoutVbts.isEmpty()) { - VxtPtrInfo firstVbtPtrInfo = finalLayoutVbts.firstEntry().getValue(); - List reorderedVirtualBases = new ArrayList<>(); - for (ClassID bId : depthFirstVirtualBases().keySet()) { - for (VirtualLayoutBaseClass base : virtualLayoutBaseClasses) { - CppCompositeType baseType = base.getBaseClassType(); - ClassID id = baseType.getClassId(); - if (id.equals(bId)) { - reorderedVirtualBases.add(base); - } - } - } - long offset = selfBaseType.getAlignedLength(); - for (VirtualLayoutBaseClass base : reorderedVirtualBases) { - CppCompositeType baseType = base.getBaseClassType(); - addPlaceholderVirtualBaseTableEntry(vxtManager, base, offset); - offset += baseType.getSelfBaseType().getAlignedLength(); - } - if (vxtManager instanceof MsftVxtManager mvxtManager) { - VBTable table = mvxtManager.findVbt(myId, firstVbtPtrInfo.parentage()); - if (table instanceof ProgramVirtualBaseTable pvbt) { - return pvbt; - } - } - vbt = placeholderVirtualBaseTables.get(mainVbtPtrOffset); + private VirtualFunctionTable getMainVft(MsftVxtManager vxtManager) throws PdbException { + if (!finalVftPtrInfoByOffset.isEmpty()) { + VxtPtrInfo firstVftPtrInfo = finalVftPtrInfoByOffset.firstEntry().getValue(); + VirtualFunctionTable vft = vxtManager.findVft(myId, firstVftPtrInfo.parentage()); + return vft; + // Following is for consideration for testing without a program: +// if (vft instanceof ProgramVirtualFunctionTable pvft) { +// return pvft; +// } +// else if (vft instanceof PlaceholderVirtualFunctionTable plvft) { +// return plvft; +// } +// else { +// throw new PdbException( +// "VFT type not expected: " + vft.getClass().getSimpleName()); +// } } - return vbt; + return null; } - //---------------------------------------------------------------------------------------------- - //---------------------------------------------------------------------------------------------- - // used by find main vbt (probably should evaluate for cleanup) - private void addPlaceholderVirtualBaseTableEntry(VxtManager vxtManager, - VirtualLayoutBaseClass base, Long baseOffset) throws PdbException { + /** + * Provides the Virtual Base Table to be used for placing virtual bases of this class + * @throws PdbException upon unrecognized vbt type + */ + private VirtualBaseTable getMainVbt(MsftVxtManager vxtManager) throws PdbException { + if (!finalVbtPtrInfoByOffset.isEmpty()) { + VxtPtrInfo firstVbtPtrInfo = finalVbtPtrInfoByOffset.firstEntry().getValue(); + VirtualBaseTable vbt = vxtManager.findVbt(myId, firstVbtPtrInfo.parentage()); + if (vbt instanceof ProgramVirtualBaseTable pvbt) { + return pvbt; + } + else if (vbt instanceof PlaceholderVirtualBaseTable plvbt) { + List reorderedVirtualBases = new ArrayList<>(); + for (ClassID bId : depthFirstVirtualBases().keySet()) { + for (VirtualLayoutBaseClass base : virtualLayoutBaseClasses) { + CppCompositeType baseType = base.getBaseClassType(); + ClassID id = baseType.getClassId(); + if (id.equals(bId)) { + reorderedVirtualBases.add(base); + } + } + } + long off = selfBaseType.getAlignedLength(); + for (VirtualLayoutBaseClass base : reorderedVirtualBases) { + CppCompositeType baseType = base.getBaseClassType(); + addPlaceholderVirtualBaseTableEntry(plvbt, vxtManager, base, off); + off += baseType.getSelfBaseType().getAlignedLength(); + } + return plvbt; + } + else { + throw new PdbException( + "VBT type not expected: " + vbt.getClass().getSimpleName()); + } + } + return null; + } - long index = base.getBasePointerOffset(); - OwnerParentage op = vbtTableIdByOffset.get(index); - if (op == null) { + private void addPlaceholderVirtualBaseTableEntry(PlaceholderVirtualBaseTable ptable, + MsftVxtManager vxtManager, VirtualLayoutBaseClass base, long baseOffset) { + long basePtrOffset = base.getBasePointerOffset(); + if (ptable.getPtrOffsetInClass() != basePtrOffset) { // error return; } - if (vxtManager instanceof MsftVxtManager mvxtManager) { - VBTable xtable = mvxtManager.findVbt(op.owner(), op.parentage()); - if (xtable == null) { - int entrySize = - composite.getDataTypeManager().getDataOrganization().getIntegerSize(); - PlaceholderVirtualBaseTable newTable = - new PlaceholderVirtualBaseTable(op.owner(), op.parentage(), entrySize); - xtable = newTable; - placeholderVirtualBaseTables.put(index, newTable); - } - if (xtable instanceof PlaceholderVirtualBaseTable ptable) { - VBTableEntry e = ptable.getBase(base.getOffetFromVbt()); - if (e != null) { - return; - } - ClassID baseId = base.getBaseClassType().getClassId(); - ptable.setBaseClassOffsetAndId(base.getOffetFromVbt(), baseOffset, baseId); - } - } - else { - PlaceholderVirtualBaseTable ptable = placeholderVirtualBaseTables.get(index); - if (ptable == null) { - int entrySize = - composite.getDataTypeManager().getDataOrganization().getIntegerSize(); - ptable = new PlaceholderVirtualBaseTable(op.owner(), op.parentage(), entrySize); - placeholderVirtualBaseTables.put(index, ptable); - } - VBTableEntry e = ptable.getBase(base.getOffetFromVbt()); - if (e != null) { - return; - } - ClassID baseId = base.getBaseClassType().getClassId(); - ptable.setBaseClassOffsetAndId(base.getOffetFromVbt(), baseOffset, baseId); + PlaceholderVirtualBaseTableEntry e = ptable.getEntry(base.getOffetFromVbt()); + if (e != null) { + e.setOffset(baseOffset); + return; } + ClassID baseId = base.getBaseClassType().getClassId(); + ptable.setBaseClassOffsetAndId(base.getOffetFromVbt(), baseOffset, baseId); } +// private void addVirtualFunctionTableEntry(MsftVxtManager vxtManager, int offsetInTable, +// SymbolPath methodPath, FunctionDefinition functionDefinition) throws PdbException { +// OwnerParentage op = vftTableIdByOffset.get(mainVftPtrOffset); +// if (op == null) { +// // error +// return; +// } +// if (vxtManager instanceof MsftVxtManager mvxtManager) { +// VFTable xtable = mvxtManager.findVft(op.owner(), op.parentage()); +// VirtualFunctionTable vft; +// if (xtable != null) { +// if (!(xtable instanceof VirtualFunctionTable myvft)) { +// // error +// return; +// } +// vft = myvft; +// } +// else { +// vft = new PlaceholderVirtualFunctionTable((ProgramClassID) op.owner(), +// op.parentage(), mainVftPtrOffset.intValue()); +// } +// vft.addEntry(offsetInTable, methodPath, new PointerDataType(functionDefinition)); +// } +// } + /** * Returns depth-first occurrences of ClassIDs along with their parentage with the assumption * that all direct (non-virtual) base classes occur before direct virtual base classes. @@ -1635,12 +1917,6 @@ public class CppCompositeType { //============================================================================================== //============================================================================================== - /** - * Record holding owner and parentage using ClassIDs. These can be used for vxtptrs and - * (possibly) also for base class info - */ - private record OwnerParentage(ClassID owner, List parentage) {} - /** * We understand the shallow immutability of records and that the contents of the List are * not used in comparison. Should we convert from record to class? diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/DefaultPdbApplicator.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/DefaultPdbApplicator.java index 8936f3b569..4e2f76589e 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/DefaultPdbApplicator.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/DefaultPdbApplicator.java @@ -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; } diff --git a/Ghidra/Features/PDB/src/test/java/ghidra/app/util/pdb/classtype/MsftVxtManagerTest.java b/Ghidra/Features/PDB/src/test/java/ghidra/app/util/pdb/classtype/MsftVxtManagerTest.java index 7fce28fb3a..94a6c9021a 100644 --- a/Ghidra/Features/PDB/src/test/java/ghidra/app/util/pdb/classtype/MsftVxtManagerTest.java +++ b/Ghidra/Features/PDB/src/test/java/ghidra/app/util/pdb/classtype/MsftVxtManagerTest.java @@ -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"); diff --git a/Ghidra/Features/PDB/src/test/java/ghidra/app/util/pdb/pdbapplicator/CppCompositeTypeTest.java b/Ghidra/Features/PDB/src/test/java/ghidra/app/util/pdb/pdbapplicator/CppCompositeTypeTest.java index f04e7fef1d..95de749a8b 100644 --- a/Ghidra/Features/PDB/src/test/java/ghidra/app/util/pdb/pdbapplicator/CppCompositeTypeTest.java +++ b/Ghidra/Features/PDB/src/test/java/ghidra/app/util/pdb/pdbapplicator/CppCompositeTypeTest.java @@ -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 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 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 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 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 expectedResults) throws Exception { + MsftVxtManager vxtManager, List expectedResults) throws Exception { Iterator iter = expectedResults.iterator(); String expected; diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/ProgramClassID.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/gclass/ClassID.java similarity index 75% rename from Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/ProgramClassID.java rename to Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/gclass/ClassID.java index c26c71afb7..deb15955e6 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/classtype/ProgramClassID.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/gclass/ClassID.java @@ -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 { // 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); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/gclass/ClassUtils.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/gclass/ClassUtils.java index af95c80ec5..979a7b5a8f 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/gclass/ClassUtils.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/gclass/ClassUtils.java @@ -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()); + } + }