diff --git a/substratevm/CHANGELOG.md b/substratevm/CHANGELOG.md index 98d893b3a0ac..579735f12fdb 100644 --- a/substratevm/CHANGELOG.md +++ b/substratevm/CHANGELOG.md @@ -10,6 +10,7 @@ This changelog summarizes major changes to GraalVM Native Image. * (GR-41100) Add support for `-XX:HeapDumpPath` to control where heap dumps are created. * (GR-42148) Adjust build output to report types (primitives, classes, interfaces, and arrays) instead of classes and revise the output schema of `-H:BuildOutputJSONFile`. * (GR-42375) Add `-H:±GenerateBuildArtifactsFile` option, which generates a `build-artifacts.json` file with a list of all artifacts produced by Native Image. `.build_artifacts.txt` files are now deprecated, disabled (can be re-enabled with env setting `NATIVE_IMAGE_DEPRECATED_BUILD_ARTIFACTS_TXT=true`), and will be removed in a future release. +* (GR-34179) Improved debugging support on Windows: Debug information now includes information about Java types (contributed by Red Hat). ## Version 22.3.0 * (GR-35721) Remove old build output style and the `-H:±BuildOutputUseNewStyle` option. diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ClassEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ClassEntry.java index 6633f1fd629b..278306153693 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ClassEntry.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ClassEntry.java @@ -244,7 +244,7 @@ public String getFileName() { } @SuppressWarnings("unused") - String getFullFileName() { + public String getFullFileName() { if (fileEntry != null) { return fileEntry.getFullName(); } else { diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVConstants.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVConstants.java index 5465da967165..9bea2634bbdc 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVConstants.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVConstants.java @@ -35,4 +35,13 @@ public abstract class CVConstants { /* CodeView section header signature */ static final int CV_SIGNATURE_C13 = 4; + + static final int CV_AMD64_R8 = 336; + static final int CV_AMD64_R9 = 337; + static final int CV_AMD64_R10 = 338; + static final int CV_AMD64_R11 = 339; + static final int CV_AMD64_R12 = 340; + static final int CV_AMD64_R13 = 341; + static final int CV_AMD64_R14 = 342; + static final int CV_AMD64_R15 = 343; } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVDebugConstants.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVDebugConstants.java index 26f01446bb1d..13e266c1fa1f 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVDebugConstants.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVDebugConstants.java @@ -35,9 +35,13 @@ public abstract class CVDebugConstants { /* Subcommands in DEBUG_S_SYMBOLS section. */ static final short S_END = 0x0006; - static final short S_OBJNAME = 0x1101; static final short S_FRAMEPROC = 0x1012; - static final short S_GPROC32 = 0x1110; + static final short S_OBJNAME = 0x1101; + static final short S_UDT = 0x1108; + static final short S_LDATA32 = 0x110c; /* Local static. */ + static final short S_GDATA32 = 0x110d; /* Global static. */ + static final short S_GPROC32 = 0x1110; /* Global procedure. */ + static final short S_REGREL32 = 0x1111; static final short S_COMPILE3 = 0x113c; static final short S_ENVBLOCK = 0x113d; } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVDebugInfo.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVDebugInfo.java index 67d50ceb3ba4..bcca177caca5 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVDebugInfo.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVDebugInfo.java @@ -43,12 +43,23 @@ public final class CVDebugInfo extends DebugInfoBase { private final CVTypeSectionImpl cvTypeSection; private DebugContext debugContext; + /* Register constants for Windows x86_64 */ + /* See AMD64ReservedRegisters.java. */ + public static final byte RHEAPBASE_X86 = (byte) 14; + public static final byte RTHREAD_X86 = (byte) 15; + + private final byte heapbaseRegister; + private final byte threadRegister; + public CVDebugInfo(PECoffMachine machine, ByteOrder byteOrder) { super(byteOrder); cvSymbolSection = new CVSymbolSectionImpl(this); cvTypeSection = new CVTypeSectionImpl(this); - if (machine != PECoffMachine.X86_64) { - /* room for future aarch64 port */ + if (machine == PECoffMachine.X86_64) { + this.heapbaseRegister = RHEAPBASE_X86; + this.threadRegister = RTHREAD_X86; + } else { + /* room for future aach64 port */ throw GraalError.shouldNotReachHere("Unsupported architecture on Windows"); } } @@ -61,6 +72,15 @@ public CVTypeSectionImpl getCVTypeSection() { return cvTypeSection; } + public byte getHeapbaseRegister() { + return heapbaseRegister; + } + + @SuppressWarnings("unused") + public byte getThreadRegister() { + return threadRegister; + } + public DebugContext getDebugContext() { return debugContext; } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVNames.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVNames.java new file mode 100644 index 000000000000..a1666dc08ce3 --- /dev/null +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVNames.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2020, Red Hat Inc. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.objectfile.pecoff.cv; + +import com.oracle.objectfile.debugentry.FieldEntry; +import com.oracle.objectfile.debugentry.MethodEntry; +import com.oracle.objectfile.debugentry.TypeEntry; + +final class CVNames { + + static String typeNameToCodeViewName(String typeName) { + return typeName.replace('.', '_').replace("[]", "_array"); + } + + static String typeNameToCodeViewName(TypeEntry typeEntry) { + return typeNameToCodeViewName(typeEntry.getTypeName()); + } + + static String methodNameToCodeViewName(MethodEntry memberEntry) { + return typeNameToCodeViewName(memberEntry.ownerType()) + "::" + memberEntry.methodName(); + } + + static String fieldNameToCodeViewName(FieldEntry memberEntry) { + return typeNameToCodeViewName(memberEntry.ownerType()) + "::" + memberEntry.fieldName(); + } +} diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVSymbolSubrecord.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVSymbolSubrecord.java index dd909f40af15..29d819c743f1 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVSymbolSubrecord.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVSymbolSubrecord.java @@ -245,6 +245,92 @@ public String toString() { } } + private abstract static class CVSymbolData32Record extends CVSymbolSubrecord { + + protected final int typeIndex; + protected final int offset; + protected final short segment; + protected final String displayName; + protected final String symbolName; + + protected CVSymbolData32Record(CVDebugInfo cvDebugInfo, short cmd, String symbolName, String displayName, int typeIndex, int offset, short segment) { + super(cvDebugInfo, cmd); + assert symbolName != null; + this.displayName = displayName; + this.symbolName = symbolName; + this.typeIndex = typeIndex; + this.offset = offset; + this.segment = segment; + } + + @Override + protected int computeContents(byte[] buffer, int initialPos) { + int pos = CVUtil.putInt(typeIndex, buffer, initialPos); + pos = cvDebugInfo.getCVSymbolSection().markRelocationSite(buffer, pos, symbolName, offset); + pos = CVUtil.putUTF8StringBytes(displayName, buffer, pos); + return pos; + } + } + + public static class CVSymbolGData32Record extends CVSymbolData32Record { + + CVSymbolGData32Record(CVDebugInfo cvDebugInfo, String symbolName, String displayName, int typeIndex, int offset, short segment) { + super(cvDebugInfo, CVDebugConstants.S_GDATA32, symbolName, displayName, typeIndex, offset, segment); + } + + @Override + public String toString() { + return String.format("S_GDATA32 name=%s(%s) offset=0x%x type=0x%x", symbolName, displayName, offset, typeIndex); + } + } + + @SuppressWarnings("unused") + public static class CVSymbolLData32Record extends CVSymbolData32Record { + + CVSymbolLData32Record(CVDebugInfo cvDebugInfo, String symbolName, String displayName, int typeIndex, int offset, short segment) { + super(cvDebugInfo, CVDebugConstants.S_LDATA32, symbolName, displayName, typeIndex, offset, segment); + } + + CVSymbolLData32Record(CVDebugInfo cvDebugInfo, String symbolName, int typeIndex, int offset, short segment) { + super(cvDebugInfo, CVDebugConstants.S_LDATA32, symbolName, symbolName, typeIndex, offset, segment); + } + + @Override + public String toString() { + return String.format("S_LDATA32 name=%s(%s) offset=0x%x type=0x%x", symbolName, displayName, offset, typeIndex); + } + } + + public static class CVSymbolRegRel32Record extends CVSymbolSubrecord { + + private final String name; + private final int typeIndex; + private final int offset; + private final short register; + + CVSymbolRegRel32Record(CVDebugInfo debugInfo, String name, int typeIndex, int offset, short register) { + super(debugInfo, CVDebugConstants.S_REGREL32); + this.name = name; + this.typeIndex = typeIndex; + this.offset = offset; + this.register = register; + } + + @Override + protected int computeContents(byte[] buffer, int initialPos) { + int pos = CVUtil.putInt(offset, buffer, initialPos); + pos = CVUtil.putInt(typeIndex, buffer, pos); + pos = CVUtil.putShort(register, buffer, pos); + pos = CVUtil.putUTF8StringBytes(name, buffer, pos); + return pos; + } + + @Override + public String toString() { + return String.format("S_REGREL32 name=%s offset=(r%d + 0x%x) type=0x%x)", name, register, offset, typeIndex); + } + } + /* * Creating a proc32 record has a side effect: two relocation entries are added to the section * relocation table; they refer back to the global symbol. @@ -258,15 +344,14 @@ public static class CVSymbolGProc32Record extends CVSymbolSubrecord { private final int debugStart; private final int debugEnd; private final int typeIndex; - private final int offset; private final short segment; private final byte flags; private final String symbolName; private final String displayName; - CVSymbolGProc32Record(CVDebugInfo cvDebugInfo, short cmd, String symbolName, String displayName, int pparent, int pend, int pnext, int proclen, int debugStart, int debugEnd, int typeIndex, - int offset, short segment, byte flags) { - super(cvDebugInfo, cmd); + CVSymbolGProc32Record(CVDebugInfo cvDebugInfo, String symbolName, String displayName, int pparent, int pend, int pnext, int proclen, int debugStart, int debugEnd, int typeIndex, + short segment, byte flags) { + super(cvDebugInfo, CVDebugConstants.S_GPROC32); this.symbolName = symbolName; this.displayName = displayName; this.pparent = pparent; @@ -276,16 +361,10 @@ public static class CVSymbolGProc32Record extends CVSymbolSubrecord { this.debugStart = debugStart; this.debugEnd = debugEnd; this.typeIndex = typeIndex; - this.offset = offset; this.segment = segment; this.flags = flags; } - CVSymbolGProc32Record(CVDebugInfo cvDebugInfo, String symbolName, String displayName, int pparent, int pend, int pnext, int proclen, int debugStart, int debugEnd, int typeIndex, int offset, - short segment, byte flags) { - this(cvDebugInfo, CVDebugConstants.S_GPROC32, symbolName, displayName, pparent, pend, pnext, proclen, debugStart, debugEnd, typeIndex, offset, segment, flags); - } - @Override protected int computeContents(byte[] buffer, int initialPos) { int pos = CVUtil.putInt(pparent, buffer, initialPos); @@ -303,8 +382,8 @@ protected int computeContents(byte[] buffer, int initialPos) { @Override public String toString() { - return String.format("S_GPROC32 name=%s/%s parent=%d debugstart=0x%x debugend=0x%x len=0x%x seg:offset=0x%x:0x%x type=0x%x flags=0x%x)", displayName, symbolName, pparent, debugStart, - debugEnd, proclen, segment, offset, typeIndex, flags); + return String.format("S_GPROC32 name=%s/%s parent=%d debugstart=0x%x debugend=0x%x len=0x%x seg:offset=0x%x:0 type=0x%x flags=0x%x)", displayName, symbolName, pparent, debugStart, + debugEnd, proclen, segment, typeIndex, flags); } } @@ -351,6 +430,30 @@ public String toString() { } } + public static final class CVSymbolUDTRecord extends CVSymbolSubrecord { + + private final int typeIdx; + private final String typeName; + + CVSymbolUDTRecord(CVDebugInfo cvDebugInfo, int typeIdx, String typeName) { + super(cvDebugInfo, CVDebugConstants.S_UDT); + this.typeIdx = typeIdx; + this.typeName = typeName; + } + + @Override + protected int computeContents(byte[] buffer, int initialPos) { + int pos = CVUtil.putInt(typeIdx, buffer, initialPos); + pos = CVUtil.putUTF8StringBytes(typeName, buffer, pos); + return pos; + } + + @Override + public String toString() { + return String.format("S_UDT type=0x%x typename=%s", typeIdx, typeName); + } + } + public static class CVSymbolEndRecord extends CVSymbolSubrecord { CVSymbolEndRecord(CVDebugInfo cvDebugInfo, short cmd) { diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVSymbolSubsectionBuilder.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVSymbolSubsectionBuilder.java index ec33be0b99f9..dd2b8e972f83 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVSymbolSubsectionBuilder.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVSymbolSubsectionBuilder.java @@ -26,12 +26,17 @@ package com.oracle.objectfile.pecoff.cv; +import com.oracle.objectfile.SectionName; import com.oracle.objectfile.debugentry.ClassEntry; import com.oracle.objectfile.debugentry.CompiledMethodEntry; +import com.oracle.objectfile.debugentry.FieldEntry; import com.oracle.objectfile.debugentry.Range; +import com.oracle.objectfile.debugentry.TypeEntry; -import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.T_NOTYPE; -import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.T_VOID; +import java.lang.reflect.Modifier; + +import static com.oracle.objectfile.pecoff.cv.CVConstants.CV_AMD64_R8; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.MAX_PRIMITIVE; final class CVSymbolSubsectionBuilder { @@ -39,37 +44,71 @@ final class CVSymbolSubsectionBuilder { private final CVSymbolSubsection cvSymbolSubsection; private final CVLineRecordBuilder lineRecordBuilder; - private boolean noMainFound = true; + private final String heapName; + private final short heapRegister; CVSymbolSubsectionBuilder(CVDebugInfo cvDebugInfo) { this.cvDebugInfo = cvDebugInfo; this.cvSymbolSubsection = new CVSymbolSubsection(cvDebugInfo); this.lineRecordBuilder = new CVLineRecordBuilder(cvDebugInfo); + this.heapName = SectionName.SVM_HEAP.getFormatDependentName(cvDebugInfo.getCVSymbolSection().getOwner().getFormat()); + /* For isolates, Graal currently uses r14; this code will handle r8-r15. */ + assert 8 <= cvDebugInfo.getHeapbaseRegister() && cvDebugInfo.getHeapbaseRegister() <= 15; + this.heapRegister = (short) (CV_AMD64_R8 + cvDebugInfo.getHeapbaseRegister() - 8); } /** * Build DEBUG_S_SYMBOLS record from all classEntries. (CodeView 4 format allows us to build one * per class or one per function or one big record - which is what we do here). * - * The CodeView symbol section Prolog is also a CVSymbolSubsection, but it is not build in this + * The CodeView symbol section Prolog is also a CVSymbolSubsection, but it is not built in this * class. */ void build() { - /* loop over all classes defined in this module. */ - for (ClassEntry classEntry : cvDebugInfo.getInstanceClasses()) { - build(classEntry); + /* Loop over all classes defined in this module. */ + for (TypeEntry typeEntry : cvDebugInfo.getTypes()) { + /* Add type record for this entry. */ + if (typeEntry.isClass()) { + buildClass((ClassEntry) typeEntry); + } else { + addTypeRecords(typeEntry); + } } cvDebugInfo.getCVSymbolSection().addRecord(cvSymbolSubsection); } /** - * Build all debug info for a classEntry. (does not yet handle member variables). + * Build all debug info for a classEntry. * * @param classEntry current class */ - private void build(ClassEntry classEntry) { - /* Loop over all functions defined in this class. */ - classEntry.compiledEntries().forEach(compiledEntry -> build(compiledEntry)); + private void buildClass(ClassEntry classEntry) { + + /* Define all the functions in this class all functions defined in this class. */ + classEntry.compiledEntries().forEach(this::buildFunction); + + /* Define the class itself. */ + addTypeRecords(classEntry); + + /* Add manifested static fields as S_GDATA32 records. */ + classEntry.fields().filter(CVSymbolSubsectionBuilder::isManifestedStaticField).forEach(f -> { + int typeIndex = cvDebugInfo.getCVTypeSection().getIndexForPointer(f.getValueType()); + String displayName = CVNames.fieldNameToCodeViewName(f); + if (cvDebugInfo.useHeapBase()) { + /* + * Isolates are enabled. Static member is located at REL32 offset from heap base + * register. + */ + addSymbolRecord(new CVSymbolSubrecord.CVSymbolRegRel32Record(cvDebugInfo, displayName, typeIndex, f.getOffset(), heapRegister)); + } else { + /* Isolates are disabled. Static member is located at offset from heap begin. */ + addSymbolRecord(new CVSymbolSubrecord.CVSymbolGData32Record(cvDebugInfo, heapName, displayName, typeIndex, f.getOffset(), (short) 0)); + } + }); + } + + private static boolean isManifestedStaticField(FieldEntry fieldEntry) { + return Modifier.isStatic(fieldEntry.getModifiers()) && fieldEntry.getOffset() >= 0; } /** @@ -78,11 +117,11 @@ private void build(ClassEntry classEntry) { * * @param compiledEntry compiled method for this function */ - private void build(CompiledMethodEntry compiledEntry) { + private void buildFunction(CompiledMethodEntry compiledEntry) { final Range primaryRange = compiledEntry.getPrimary(); /* The name as it will appear in the debugger. */ - final String debuggerName = getDebuggerName(primaryRange); + final String debuggerName = CVNames.methodNameToCodeViewName(primaryRange.getMethodEntry()); /* The name as exposed to the linker. */ final String externalName = primaryRange.getSymbolName(); @@ -91,54 +130,28 @@ private void build(CompiledMethodEntry compiledEntry) { int functionTypeIndex = addTypeRecords(compiledEntry); byte funcFlags = 0; CVSymbolSubrecord.CVSymbolGProc32Record proc32 = new CVSymbolSubrecord.CVSymbolGProc32Record(cvDebugInfo, externalName, debuggerName, 0, 0, 0, primaryRange.getHi() - primaryRange.getLo(), 0, - 0, functionTypeIndex, primaryRange.getLo(), (short) 0, funcFlags); - addToSymbolSubsection(proc32); + 0, functionTypeIndex, (short) 0, funcFlags); + addSymbolRecord(proc32); /* S_FRAMEPROC add frame definitions. */ int asynceh = 1 << 9; /* Async exception handling (vc++ uses 1, clang uses 0). */ + /* TODO: This may change in the presence of isolates. */ int localBP = 1 << 14; /* Local base pointer = SP (0=none, 1=sp, 2=bp 3=r13). */ int paramBP = 1 << 16; /* Param base pointer = SP. */ int frameFlags = asynceh + localBP + paramBP; /* NB: LLVM uses 0x14000. */ - addToSymbolSubsection(new CVSymbolSubrecord.CVSymbolFrameProcRecord(cvDebugInfo, compiledEntry.getFrameSize(), frameFlags)); + addSymbolRecord(new CVSymbolSubrecord.CVSymbolFrameProcRecord(cvDebugInfo, compiledEntry.getFrameSize(), frameFlags)); + /* TODO: add parameter definitions (types have been added already). */ /* TODO: add local variables, and their types. */ /* TODO: add block definitions. */ /* S_END add end record. */ - addToSymbolSubsection(new CVSymbolSubrecord.CVSymbolEndRecord(cvDebugInfo)); + addSymbolRecord(new CVSymbolSubrecord.CVSymbolEndRecord(cvDebugInfo)); /* Add line number records. */ addLineNumberRecords(compiledEntry); } - /** - * Rename function names for usability or functionality. - * - * First encountered main function becomes class.main. This is for usability. - * - * All other functions become class.function.999 (where 999 is a hash of the arglist). This is - * because The standard link.exe can't handle odd characters (parentheses or commas, for - * example) in debug information. - * - * This does not affect external symbols used by linker. - * - * TODO: strip illegal characters from arg lists instead ("link.exe" - safe names) - * - * @param range Range contained in the method of interest - * @return user debugger friendly method name - */ - private String getDebuggerName(Range range) { - final String methodName; - if (noMainFound && range.getMethodName().equals("main")) { - noMainFound = false; - methodName = range.getFullMethodName(); - } else { - /* In the future, use a more user-friendly name instead of a hash function. */ - methodName = range.getSymbolName(); - } - return methodName; - } - private void addLineNumberRecords(CompiledMethodEntry compiledEntry) { CVLineRecord record = lineRecordBuilder.build(compiledEntry); /* @@ -156,23 +169,35 @@ private void addLineNumberRecords(CompiledMethodEntry compiledEntry) { * * @param record the symbol subrecord to add. */ - private void addToSymbolSubsection(CVSymbolSubrecord record) { + private void addSymbolRecord(CVSymbolSubrecord record) { cvSymbolSubsection.addRecord(record); } /** - * Add type records for function. (later add arglist, and return type and local types). + * Add type records for a class and all its members. * - * @param entry compild method containing entities whoses type records must be added - * @return type index of function type + * @param typeEntry class to add records for. */ - private int addTypeRecords(@SuppressWarnings("unused") CompiledMethodEntry entry) { - CVTypeRecord.CVTypeArglistRecord argListType = addTypeRecord(new CVTypeRecord.CVTypeArglistRecord().add(T_NOTYPE)); - CVTypeRecord funcType = addTypeRecord(new CVTypeRecord.CVTypeProcedureRecord().returnType(T_VOID).argList(argListType)); - return funcType.getSequenceNumber(); + private void addTypeRecords(TypeEntry typeEntry) { + int typeIdx = cvDebugInfo.getCVTypeSection().addTypeRecords(typeEntry).getSequenceNumber(); + + if (typeIdx > MAX_PRIMITIVE) { + /* + * Adding an S_UDT (User Defined Type) record ensures the linker doesn't throw away the + * type definition. + */ + CVSymbolSubrecord.CVSymbolUDTRecord udtRecord = new CVSymbolSubrecord.CVSymbolUDTRecord(cvDebugInfo, typeIdx, CVNames.typeNameToCodeViewName(typeEntry.getTypeName())); + addSymbolRecord(udtRecord); + } } - private T addTypeRecord(T record) { - return cvDebugInfo.getCVTypeSection().addOrReference(record); + /** + * Add type records for a class and all its members. + * + * @param entry compiled method containing entities whos type records must be added + * @return type index of function type + */ + private int addTypeRecords(CompiledMethodEntry entry) { + return cvDebugInfo.getCVTypeSection().addTypeRecords(entry).getSequenceNumber(); } } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVTypeConstants.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVTypeConstants.java index 5db2e2e57bdf..2930ad9778e8 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVTypeConstants.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVTypeConstants.java @@ -26,8 +26,16 @@ package com.oracle.objectfile.pecoff.cv; +@SuppressWarnings("unused") abstract class CVTypeConstants { + static final int ADDRESS_BITS = 64; + + static final String JAVA_LANG_OBJECT_NAME = "java.lang.Object"; + static final String OBJ_HEADER_NAME = "_objhdr"; + + static final int FUNC_IS_CONSTRUCTOR = 2; + /* * Type table. Constants below 0x1000 are 'hardcoded', above are new type entries in the type * section. @@ -35,11 +43,143 @@ abstract class CVTypeConstants { static final short T_NOTYPE = 0x0000; static final short T_VOID = 0x0003; + static final short T_BOOL08 = 0x0030; /* 8 bit boolean */ + static final short T_64PBOOL08 = 0x0630; /* 64 bit pointer to 8 bit bool */ + + static final short T_WCHAR = 0x0071; /* 16 bite wide character (java char type) */ + static final short T_REAL32 = 0x0040; /* 32 bit float (Java float type) */ + static final short T_REAL64 = 0x0041; /* 64 bit double (Java double type) */ + + static final short T_INT1 = 0x0068; /* 8 bit int (java byte type) */ + static final short T_INT2 = 0x0072; /* 16 bit int (java short type) */ + static final short T_INT4 = 0x0074; /* 32 bit int (java int type) */ + static final short T_INT8 = 0x0076; /* 64 bit int (java long type) */ + + static final short T_64PINT1 = 0x0668; /* 64 bit pointer to 8 bit int (java byte type) */ + static final short T_64PINT2 = 0x0672; /* 64 bit pointer to 16 bit int (java short type) */ + static final short T_64PINT4 = 0x0674; /* 64 bit pointer to 32 bit int (java int type) */ + static final short T_64PINT8 = 0x0676; /* 64 bit pointer to 64 bit int (java long type) */ + + static final short T_UINT1 = 0x0069; /* 8 bit unsigned int */ + static final short T_UINT2 = 0x0073; /* 16 bit unsigned int */ + static final short T_UINT4 = 0x0075; /* 32 bit unsigned int */ + static final short T_UINT8 = 0x0077; /* 64 bit unsigned int */ + + static final short T_64PVOID = 0x0603; + static final short T_64PCHAR = 0x0610; + static final short T_64PUCHAR = 0x0620; + static final short T_64PWCHAR = 0x0671; + static final short T_64PUINT1 = 0x0669; + static final short T_64PREAL32 = 0x0640; + static final short T_64PREAL64 = 0x0641; + + static final short T_POINTER_BITS = 0x0700; + static final short T_POINTER32 = 0x0400; /* 32 bit pointer */ + static final short T_POINTER64 = 0x0600; /* 64 bit pointer */ + + static final short MAX_PRIMITIVE = 0x0fff; + + static final short LF_MODIFIER = 0x1001; + static final short LF_POINTER = 0x1002; static final short LF_PROCEDURE = 0x1008; + static final short LF_MFUNCTION = 0x1009; static final short LF_ARGLIST = 0x1201; + static final short LF_FIELDLIST = 0x1203; + static final short LF_BITFIELD = 0x1205; + static final short LF_METHODLIST = 0x1206; + static final short LF_BCLASS = 0x1400; + static final short LF_INDEX = 0x1404; + static final short LF_ENUMERATE = 0x1502; + static final short LF_ARRAY = 0x1503; + static final short LF_CLASS = 0x1504; + static final short LF_STRUCTURE = 0x1505; + static final short LF_UNION = 0x1506; + static final short LF_ENUM = 0x1507; + static final short LF_MEMBER = 0x150d; + static final short LF_STMEMBER = 0x150e; + static final short LF_METHOD = 0x150f; + static final short LF_NESTTYPE = 0x1510; + static final short LF_ONEMETHOD = 0x1511; + static final short LF_TYPESERVER2 = 0x1515; + static final short LF_INTERFACE = 0x1519; + static final short LF_BINTERFACE = 0x151a; + + static final short LF_FUNC_ID = 0x1601; + static final short LF_MFUNC_ID = 0x1602; + static final short LF_BUILDINFO = 0x1603; + static final short LF_STRING_ID = 0x1605; + static final short LF_UDT_SRC_LINE = 0x1606; + static final short LF_UDT_MOD_SRC_LINE = 0x1607; + static final short LF_ID_LAST = 0x1608; + + /* LF_NUMERIC constanta */ + static final short LF_NUMERIC = (short) 0x8000; + static final short LF_CHAR = (short) 0x8000; + static final short LF_SHORT = (short) 0x8001; + static final short LF_USHORT = (short) 0x8002; + static final short LF_LONG = (short) 0x8003; + static final short LF_ULONG = (short) 0x8004; + static final short LF_REAL32 = (short) 0x8005; + static final short LF_REAL64 = (short) 0x8006; + static final short LF_REAL80 = (short) 0x8007; + static final short LF_REAL128 = (short) 0x8008; + static final short LF_QUADWORD = (short) 0x8009; + static final short LF_UQUADWORD = (short) 0x800a; + static final short LF_REAL48 = (short) 0x800b; + static final short LF_COMPLEX32 = (short) 0x800c; + static final short LF_COMPLEX64 = (short) 0x800d; + static final short LF_COMPLEX80 = (short) 0x800e; + static final short LF_COMPLEX128 = (short) 0x800f; + static final short LF_VARSTRING = (short) 0x8010; + static final short LF_OCTWORD = (short) 0x8017; + static final short LF_UOCTWORD = (short) 0x8018; + + static final short LF_DECIMAL = (short) 0x8019; + static final short LF_DATE = (short) 0x801a; + static final short LF_UTF8STRING = (short) 0x801b; + + static final short LF_REAL16 = (short) 0x801c; /* Padding. */ + // static final byte LF_PAD0 = (byte) 0xf0; static final byte LF_PAD1 = (byte) 0xf1; static final byte LF_PAD2 = (byte) 0xf2; static final byte LF_PAD3 = (byte) 0xf3; + /*- + static final byte LF_PAD4 = (byte) 0xf4; + static final byte LF_PAD5 = (byte) 0xf5; + static final byte LF_PAD6 = (byte) 0xf6; + static final byte LF_PAD7 = (byte) 0xf7; + static final byte LF_PAD8 = (byte) 0xf8; + static final byte LF_PAD9 = (byte) 0xf9; + static final byte LF_PAD10 = (byte) 0xfa; + static final byte LF_PAD11 = (byte) 0xfb; + static final byte LF_PAD12 = (byte) 0xfc; + static final byte LF_PAD13 = (byte) 0xfd; + static final byte LF_PAD14 = (byte) 0xfe; + static final byte LF_PAD15 = (byte) 0xff; + */ + + static final short MPROP_VANILLA = 0; + static final short MPROP_PRIVATE = 1; + static final short MPROP_PROTECTED = 2; + static final short MPROP_PUBLIC = 3; + static final short MPROP_PPP_MASK = 0x03; + + static final short MPROP_VIRTUAL = (1 << 2); // redefinition + static final short MPROP_STATIC = (2 << 2); + static final short MPROP_FRIEND = (3 << 2); + static final short MPROP_IVIRTUAL = (4 << 2); + static final short MPROP_PURE_VIRTUAL = (5 << 2); + static final short MPROP_PURE_IVIRTUAL = (6 << 2); + static final short MPROP_VSF_MASK = 0x1c; + + static final short MPROP_PSEUDO = 0x20; + static final short MPROP_FINAL_CLASS = 0x40; + static final short MPROP_ABSTRACT = 0x80; + static final short MPROP_COMPGENX = 0x100; + static final short MPROP_FINAL_METHOD = 0x200; + + /* For x86_64, calling type is always CV_CALL_NEAR_C. */ + static final int CV_CALL_NEAR_C = 0; } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVTypeRecord.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVTypeRecord.java index 2b8f1de74e9d..94dfc59bd8b7 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVTypeRecord.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVTypeRecord.java @@ -1,6 +1,6 @@ /* - * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2020, 2020, Red Hat Inc. All rights reserved. + * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2021, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,13 +26,41 @@ package com.oracle.objectfile.pecoff.cv; +import org.graalvm.compiler.debug.GraalError; + import java.util.ArrayList; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.CV_CALL_NEAR_C; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.FUNC_IS_CONSTRUCTOR; import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.LF_ARGLIST; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.LF_ARRAY; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.LF_BCLASS; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.LF_CLASS; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.LF_FIELDLIST; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.LF_INDEX; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.LF_MEMBER; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.LF_METHOD; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.LF_METHODLIST; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.LF_MFUNCTION; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.LF_ONEMETHOD; import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.LF_PAD1; import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.LF_PAD2; import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.LF_PAD3; -import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.LF_PROCEDURE; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.LF_POINTER; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.LF_STMEMBER; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.LF_STRING_ID; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.LF_UDT_SRC_LINE; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.MPROP_ABSTRACT; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.MPROP_COMPGENX; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.MPROP_FINAL_CLASS; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.MPROP_FINAL_METHOD; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.MPROP_IVIRTUAL; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.MPROP_PPP_MASK; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.MPROP_PSEUDO; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.MPROP_PURE_IVIRTUAL; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.MPROP_VSF_MASK; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.T_NOTYPE; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.T_UINT8; /* * CV Type Record format (little-endian): @@ -42,6 +70,9 @@ */ abstract class CVTypeRecord { + static final int FIRST_TYPE_INDEX = 0x1000; + static final int CV_TYPE_RECORD_MAX_SIZE = 0xffff; + protected final short type; private int startPosition; private int sequenceNumber; /* CodeView type records are numbered 1000 on up. */ @@ -61,6 +92,7 @@ void setSequenceNumber(int sequenceNumber) { } int computeFullSize(int initialPos) { + assert sequenceNumber >= FIRST_TYPE_INDEX; this.startPosition = initialPos; int pos = initialPos + Short.BYTES * 2; /* Save room for length and leaf type. */ pos = computeSize(pos); @@ -69,17 +101,23 @@ int computeFullSize(int initialPos) { } int computeFullContents(byte[] buffer, int initialPos) { + assert sequenceNumber >= FIRST_TYPE_INDEX; int pos = initialPos + Short.BYTES; /* Save room for length short. */ pos = CVUtil.putShort(type, buffer, pos); pos = computeContents(buffer, pos); /* Length does not include record length (2 bytes)) but does include end padding. */ pos = alignPadded4(buffer, pos); - int length = (short) (pos - initialPos - Short.BYTES); + int length = pos - initialPos - Short.BYTES; + if (length > CV_TYPE_RECORD_MAX_SIZE) { + throw GraalError.shouldNotReachHere(String.format("Type record too large: %d (maximum %d) bytes: %s", length, CV_TYPE_RECORD_MAX_SIZE, this)); + } CVUtil.putShort((short) length, buffer, initialPos); return pos; } - protected abstract int computeSize(int initialPos); + public int computeSize(int initialPos) { + return computeContents(null, initialPos); + } protected abstract int computeContents(byte[] buffer, int initialPos); @@ -117,56 +155,195 @@ private static int alignPadded4(byte[] buffer, int originalpos) { return pos; } - static final class CVTypeProcedureRecord extends CVTypeRecord { + static final class CVTypePrimitive extends CVTypeRecord { - int returnType = -1; - CVTypeArglistRecord argList = null; + private final int length; - CVTypeProcedureRecord() { - super(LF_PROCEDURE); + CVTypePrimitive(short cvtype, int length) { + super(cvtype); + assert cvtype < FIRST_TYPE_INDEX; + this.length = length; + setSequenceNumber(cvtype); } - public CVTypeProcedureRecord returnType(int leaf) { - this.returnType = leaf; - return this; + @Override + public int computeSize(int initialPos) { + throw GraalError.shouldNotReachHere(); } - public CVTypeProcedureRecord returnType(CVTypeRecord leaf) { - this.returnType = leaf.getSequenceNumber(); - return this; + @Override + protected int computeContents(byte[] buffer, int initialPos) { + throw GraalError.shouldNotReachHere(); } - CVTypeProcedureRecord argList(CVTypeArglistRecord leaf) { - this.argList = leaf; - return this; + @Override + public int hashCode() { + throw GraalError.shouldNotReachHere(); } @Override - public int computeSize(int initialPos) { - return computeContents(null, initialPos); + public String toString() { + return String.format("PRIMITIVE 0x%04x (len=%d)", getSequenceNumber(), length); + } + } + + static final class CVTypePointerRecord extends CVTypeRecord { + + static final int KIND_64 = 0x0000c; + static final int SIZE_8 = 8 << 13; + + /* Standard 64-bit absolute pointer type. */ + static final int NORMAL_64 = KIND_64 | SIZE_8; + + private final int pointsTo; + + /*- + * int kind = attributes & 0x00001f; + * int mode = (attributes & 0x0000e0) >> 5; + * int modifiers = (attributes & 0x001f00) >> 8; + * int size = (attributes & 0x07e000) >> 13; + * int flags = (attributes & 0x380000) >> 19; + */ + private final int attrs; + + CVTypePointerRecord(int pointTo, int attrs) { + super(LF_POINTER); + this.pointsTo = pointTo; + this.attrs = attrs; + } + + @SuppressWarnings("unused") + int getPointsTo() { + return pointsTo; } @Override public int computeContents(byte[] buffer, int initialPos) { - int pos = CVUtil.putInt(returnType, buffer, initialPos); - pos = CVUtil.putByte((byte) 0, buffer, pos); /* callType */ - pos = CVUtil.putByte((byte) 0, buffer, pos); /* funcAttr */ - pos = CVUtil.putShort((short) argList.getSize(), buffer, pos); - pos = CVUtil.putInt(argList.getSequenceNumber(), buffer, pos); - return pos; + int pos = CVUtil.putInt(pointsTo, buffer, initialPos); + return CVUtil.putInt(attrs, buffer, pos); } + static String[] ptrType = {"near16", "far16", "huge", "base-seg", "base-val", "base-segval", "base-addr", "base-segaddr", "base-type", "base-self", "near32", "far32", "64"}; + static String[] modeStrs = {"normal", "lvalref", "datamem", "memfunc", "rvalref"}; + @Override public String toString() { - return String.format("LF_PROCEDURE 0x%04x ret=0x%04x arg=0x%04x ", getSequenceNumber(), returnType, argList.getSequenceNumber()); + int kind = attrs & 0x00001f; + int mode = (attrs & 0x0000e0) >> 5; + int flags1 = (attrs & 0x001f00) >> 8; + int size = (attrs & 0x07e000) >> 13; + int flags2 = (attrs & 0x380000) >> 19; + StringBuilder sb = new StringBuilder(); + sb.append((flags1 & 1) != 0 ? "flat32" : ""); + sb.append((flags1 & 2) != 0 ? " volatile" : ""); + sb.append((flags1 & 4) != 0 ? " const" : ""); + sb.append((flags1 & 8) != 0 ? " unaligned" : ""); + sb.append((flags1 & 16) != 0 ? " restricted" : ""); + return String.format("LF_POINTER 0x%04x attrs=0x%x(kind=%d(%s) mode=%d(%s) flags1=0x%x(%s) size=%d flags2=0x%x) pointTo=0x%04x", getSequenceNumber(), attrs, kind, ptrType[kind], mode, + modeStrs[mode], flags1, sb, size, flags2, pointsTo); } @Override public int hashCode() { int h = type; - h = 31 * h + returnType; - h = 31 * h + argList.hashCode(); - /* callType and funcAttr are always zero so do not add them to the hash */ + h = 31 * h + pointsTo; + h = 31 * h + attrs; + return h; + } + + @Override + public boolean equals(Object obj) { + if (!super.equals(obj)) { + return false; + } + CVTypePointerRecord other = (CVTypePointerRecord) obj; + return this.pointsTo == other.pointsTo && this.attrs == other.attrs; + } + } + + static class CVUdtTypeLineRecord extends CVTypeRecord { + + final int typeIndex; + int fileIndex; + int line; + + CVUdtTypeLineRecord(int typeIndex, int fileIndex, int line) { + this(LF_UDT_SRC_LINE, typeIndex, fileIndex, line); + } + + CVUdtTypeLineRecord(short t, int typeIndex, int fileIndex, int line) { + super(t); + this.typeIndex = typeIndex; + this.fileIndex = fileIndex; + this.line = line; + } + + @Override + public int computeContents(byte[] buffer, int initialPos) { + int pos = CVUtil.putInt(typeIndex, buffer, initialPos); + pos = CVUtil.putInt(fileIndex, buffer, pos); + return CVUtil.putInt(line, buffer, pos); + } + + @Override + public String toString() { + return String.format("LF_UDT_SRC_LINE 0x%04x typeIdx=0x%x fileIdx=0x%x line=%d", getSequenceNumber(), typeIndex, fileIndex, line); + } + + @Override + public int hashCode() { + int h = type; + h = 31 * h + typeIndex; + h = 31 * h + fileIndex; + h = 31 * h + line; + return h; + } + + @Override + public boolean equals(Object obj) { + if (!super.equals(obj)) { + return false; + } + CVUdtTypeLineRecord other = (CVUdtTypeLineRecord) obj; + /* + * NB: if the record has the same type but different file or line, it's probably an + * error. + */ + return this.typeIndex == other.typeIndex && this.fileIndex == other.fileIndex && this.line == other.line; + } + } + + static final class CVTypeStringIdRecord extends CVTypeRecord { + + String string; + int substringListIndex; + + CVTypeStringIdRecord(int substringListIndex, String string) { + super(LF_STRING_ID); + this.substringListIndex = substringListIndex; + this.string = string; + } + + CVTypeStringIdRecord(String string) { + this(T_NOTYPE, string); + } + + @Override + public int computeContents(byte[] buffer, int initialPos) { + int pos = CVUtil.putInt(substringListIndex, buffer, initialPos); + return CVUtil.putUTF8StringBytes(string, buffer, pos); + } + + @Override + public String toString() { + return String.format("LF_STRING_ID 0x%04x substringListIdx=0x%x %s", getSequenceNumber(), substringListIndex, string); + } + + @Override + public int hashCode() { + int h = type; + h = 31 * h + substringListIndex; + h = 31 * h + string.hashCode(); return h; } @@ -175,22 +352,21 @@ public boolean equals(Object obj) { if (!super.equals(obj)) { return false; } - CVTypeProcedureRecord other = (CVTypeProcedureRecord) obj; - return this.returnType == other.returnType && this.argList == other.argList; + CVTypeStringIdRecord other = (CVTypeStringIdRecord) obj; + return this.string.equals(other.string) && this.substringListIndex == other.substringListIndex; } } static final class CVTypeArglistRecord extends CVTypeRecord { - ArrayList args = new ArrayList<>(); + private final ArrayList args = new ArrayList<>(); CVTypeArglistRecord() { super(LF_ARGLIST); } - CVTypeArglistRecord add(int argType) { + void add(int argType) { args.add(argType); - return this; } @Override @@ -235,4 +411,795 @@ public boolean equals(Object obj) { return this.args.equals(other.args); } } + + static final class CVTypeMFunctionRecord extends CVTypeRecord { + + private int returnType = -1; + private int classType = -1; + private int thisType = -1; + private byte callType = CV_CALL_NEAR_C; + private byte funcAttr = 0; + private int thisAdjust = 0; + + private CVTypeArglistRecord argList = null; + + CVTypeMFunctionRecord() { + super(LF_MFUNCTION); + } + + void setReturnType(int returnType) { + this.returnType = returnType; + } + + void setClassType(int classType) { + this.classType = classType; + } + + void setThisType(int thisType) { + this.thisType = thisType; + } + + @SuppressWarnings("SameParameterValue") + void setCallType(byte callType) { + this.callType = callType; + } + + void setFuncAttr(byte funcAttr) { + this.funcAttr = funcAttr; + } + + @SuppressWarnings("unused") + void setThisAdjust(int thisAdjust) { + this.thisAdjust = thisAdjust; + } + + void setArgList(CVTypeArglistRecord argList) { + this.argList = argList; + } + + @Override + public int computeContents(byte[] buffer, int initialPos) { + int pos = CVUtil.putInt(returnType, buffer, initialPos); + pos = CVUtil.putInt(classType, buffer, pos); + pos = CVUtil.putInt(thisType, buffer, pos); + pos = CVUtil.putByte(callType, buffer, pos); + pos = CVUtil.putByte(funcAttr, buffer, pos); + pos = CVUtil.putShort((short) argList.getSize(), buffer, pos); + pos = CVUtil.putInt(argList.getSequenceNumber(), buffer, pos); + pos = CVUtil.putInt(thisAdjust, buffer, pos); + return pos; + } + + @Override + public String toString() { + String attrString = (funcAttr & FUNC_IS_CONSTRUCTOR) == FUNC_IS_CONSTRUCTOR ? "(ctor)" : ""; + return String.format("LF_MFUNCTION 0x%04x ret=0x%04x this=0x%04x *this=0x%04x+%d calltype=0x%x attr=0x%x%s, argcount=0x%04x ", getSequenceNumber(), returnType, classType, thisType, + thisAdjust, callType, funcAttr, attrString, argList.getSequenceNumber()); + } + + @Override + public int hashCode() { + int h = type; + h = 31 * h + returnType; + h = 31 * h + classType; + h = 31 * h + thisType; + h = 31 * h + callType; + h = 31 * h + funcAttr; + h = 31 * h + thisAdjust; + h = 31 * h + argList.getSequenceNumber(); + return h; + } + + @Override + public boolean equals(Object obj) { + if (!super.equals(obj)) { + return false; + } + CVTypeMFunctionRecord other = (CVTypeMFunctionRecord) obj; + return this.returnType == other.returnType && this.classType == other.classType && this.thisType == other.thisType && this.callType == other.callType && this.funcAttr == other.funcAttr && + this.thisAdjust == other.thisAdjust && this.argList.getSequenceNumber() == other.argList.getSequenceNumber(); + } + } + + static final class CVTypeMethodListRecord extends CVTypeRecord { + + static class MDef extends CVOneMethodRecord { + + MDef(short attrs, int funcIdx, int vtbleOffset, String name) { + super(attrs, funcIdx, vtbleOffset, name); + } + + @Override + public int computeContents(byte[] buffer, int initialPos) { + int pos = initialPos; + pos = CVUtil.putShort(attrs, buffer, pos); + /* Two bytes of padding. */ + pos = CVUtil.putShort((short) 0, buffer, pos); + pos = CVUtil.putInt(funcIdx, buffer, pos); + if (hasVtableOffset()) { + assert vtbleOffset >= 0; + pos = CVUtil.putInt(vtbleOffset, buffer, pos); + } + return pos; + } + } + + static final int INITIAL_CAPACITY = 10; + + private final ArrayList methods = new ArrayList<>(INITIAL_CAPACITY); + + CVTypeMethodListRecord() { + super(LF_METHODLIST); + } + + public void add(short attrs, int idx, int offset, String name) { + methods.add(new MDef(attrs, idx, offset, name)); + } + + public int count() { + return methods.size(); + } + + @Override + public int computeContents(byte[] buffer, int initialPos) { + int pos = initialPos; + for (MDef f : methods) { + pos = f.computeContents(buffer, pos); + } + return pos; + } + + @Override + public int hashCode() { + int h = type; + h = 31 * h + methods.hashCode(); + return h; + } + + @Override + public boolean equals(Object obj) { + if (!super.equals(obj)) { + return false; + } + CVTypeMethodListRecord other = (CVTypeMethodListRecord) obj; + return this.methods.equals(other.methods); + } + + @Override + public String toString() { + return String.format("LF_METHODLIST idx=0x%04x count=%d", getSequenceNumber(), methods.size()); + } + } + + static String attrString(short attrs) { + final StringBuilder sb = new StringBuilder(); + + /* Low byte. */ + if ((attrs & MPROP_PPP_MASK) != 0) { + String[] aStr = {"", "private", "protected", "public"}; + sb.append(aStr[attrs & MPROP_PPP_MASK]); + } + if ((attrs & MPROP_VSF_MASK) != 0) { + int p = (attrs & MPROP_VSF_MASK) >> 2; + String[] pStr = {"", " virtual", " static", " friend", " intro", " pure", " intro-pure", " (*7*)"}; + sb.append(pStr[p]); + } + if ((attrs & MPROP_PSEUDO) != 0) { + sb.append(" pseudo"); + } + if ((attrs & MPROP_FINAL_CLASS) != 0) { + sb.append(" final-class"); + } + if ((attrs & MPROP_ABSTRACT) != 0) { + sb.append(" abstract"); + } + if ((attrs & MPROP_COMPGENX) != 0) { + sb.append(" compgenx"); + } + if ((attrs & MPROP_FINAL_METHOD) != 0) { + sb.append(" final-method"); + } + return sb.toString(); + } + + abstract static class FieldRecord { + + protected final short type; + protected final short attrs; /* property attribute field (prop_t) */ + protected final String name; + + protected FieldRecord(short leafType, short attrs, String name) { + assert name != null; + this.type = leafType; + this.attrs = attrs; + this.name = name; + } + + protected FieldRecord(short leafType) { + this(leafType, (short) 0, ""); + } + + public int computeSize() { + return computeContents(null, 0); + } + + public abstract int computeContents(byte[] buffer, int initialPos); + + @Override + public int hashCode() { + int h = type; + h = 31 * h + attrs; + h = 31 * h + name.hashCode(); + return h; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || obj.getClass() != this.getClass()) { + return false; + } + FieldRecord other = (FieldRecord) obj; + return this.type == other.type && this.attrs == other.attrs && this.name.equals(other.name); + } + } + + static final class CVOverloadedMethodRecord extends FieldRecord { + + private final int methodListIndex; /* index of method list record */ + private final short count; + + CVOverloadedMethodRecord(short count, int methodListIndex, String methodName) { + super(LF_METHOD, (short) 0, methodName); + this.methodListIndex = methodListIndex; + this.count = count; + } + + @Override + public int computeContents(byte[] buffer, int initialPos) { + int pos = CVUtil.putShort(type, buffer, initialPos); + pos = CVUtil.putShort(count, buffer, pos); + pos = CVUtil.putInt(methodListIndex, buffer, pos); + pos = CVUtil.putUTF8StringBytes(name, buffer, pos); + return pos; + } + + @Override + public String toString() { + return String.format("LF_METHOD(0x%04x) count=0x%x listIdx=0x%04x %s", type, count, methodListIndex, name); + } + + @Override + public int hashCode() { + int h = super.hashCode(); + h = 31 * h + methodListIndex; + h = 31 + h + count; + return h; + } + + @Override + public boolean equals(Object obj) { + if (!super.equals(obj)) { + return false; + } + CVOverloadedMethodRecord other = (CVOverloadedMethodRecord) obj; + return this.methodListIndex == other.methodListIndex && this.count == other.count; + } + } + + static final class CVIndexRecord extends FieldRecord { + + private final int index; /* index of continuation record */ + + CVIndexRecord(int index) { + super(LF_INDEX); + this.index = index; + } + + @Override + public int computeContents(byte[] buffer, int initialPos) { + int pos = CVUtil.putShort(type, buffer, initialPos); + pos = CVUtil.putShort((short) 0, buffer, pos); /* padding. */ + pos = CVUtil.putInt(index, buffer, pos); + return pos; + } + + @Override + public String toString() { + return String.format("LF_INDEX(0x%04x) index=0x%04x", type, index); + } + + @Override + public int hashCode() { + int h = super.hashCode(); + h = 31 * h + index; + return h; + } + + @Override + public boolean equals(Object obj) { + if (!super.equals(obj)) { + return false; + } + CVIndexRecord other = (CVIndexRecord) obj; + return this.index == other.index; + } + } + + static final class CVMemberRecord extends FieldRecord { + + private final int underlyingTypeIndex; /* type index of member type */ + private int offset; + + CVMemberRecord(short attrs, int underlyingTypeIndex, int offset, String name) { + super(LF_MEMBER, attrs, name); + this.underlyingTypeIndex = underlyingTypeIndex; + this.offset = offset; + } + + public void setOffset(int offset) { + this.offset = offset; + } + + @Override + public int computeContents(byte[] buffer, int initialPos) { + int pos = CVUtil.putShort(type, buffer, initialPos); + pos = CVUtil.putShort(attrs, buffer, pos); + pos = CVUtil.putInt(underlyingTypeIndex, buffer, pos); + pos = CVUtil.putLfNumeric(offset, buffer, pos); + pos = CVUtil.putUTF8StringBytes(name, buffer, pos); + return pos; + } + + @Override + public String toString() { + return String.format("LF_MEMBER(0x%04x) attr=0x%x(%s) t=0x%x off=%d 0x%x %s", type, attrs, attrString(attrs), underlyingTypeIndex, offset, offset & 0xffff, name); + } + + @Override + public int hashCode() { + int h = super.hashCode(); + h = 31 * h + underlyingTypeIndex; + h = 31 * h + offset; + return h; + } + + @Override + public boolean equals(Object obj) { + if (!super.equals(obj)) { + return false; + } + CVMemberRecord other = (CVMemberRecord) obj; + return this.offset == other.offset && this.underlyingTypeIndex == other.underlyingTypeIndex; + } + } + + static final class CVStaticMemberRecord extends FieldRecord { + + /* Type index of member type. */ + private final int underlyingTypeIndex; + + CVStaticMemberRecord(short attrs, int underlyingTypeIndex, String name) { + super(LF_STMEMBER, attrs, name); + this.underlyingTypeIndex = underlyingTypeIndex; + } + + @Override + public int computeContents(byte[] buffer, int initialPos) { + int pos = CVUtil.putShort(type, buffer, initialPos); + pos = CVUtil.putShort(attrs, buffer, pos); + pos = CVUtil.putInt(underlyingTypeIndex, buffer, pos); + pos = CVUtil.putUTF8StringBytes(name, buffer, pos); + return pos; + } + + @Override + public String toString() { + return String.format("LF_STMEMBER(0x%04x) attr=0x%x(%s) t=0x%x %s", type, attrs, attrString(attrs), underlyingTypeIndex, name); + } + + @Override + public int hashCode() { + int h = super.hashCode(); + h = 31 * h + underlyingTypeIndex; + return h; + } + + @Override + public boolean equals(Object obj) { + if (!super.equals(obj)) { + return false; + } + CVStaticMemberRecord other = (CVStaticMemberRecord) obj; + return this.underlyingTypeIndex == other.underlyingTypeIndex; + } + } + + static class CVOneMethodRecord extends FieldRecord { + + protected final int funcIdx; /* type index of member type */ + protected final int vtbleOffset; + + CVOneMethodRecord(short attrs, int funcIdx, int vtbleOffset, String name) { + super(LF_ONEMETHOD, attrs, name); + this.funcIdx = funcIdx; + this.vtbleOffset = vtbleOffset; + } + + boolean hasVtableOffset() { + return (attrs & MPROP_VSF_MASK) == MPROP_IVIRTUAL || (attrs & MPROP_VSF_MASK) == MPROP_PURE_IVIRTUAL; + } + + @Override + public int computeContents(byte[] buffer, int initialPos) { + int pos = CVUtil.putShort(type, buffer, initialPos); + pos = CVUtil.putShort(attrs, buffer, pos); + pos = CVUtil.putInt(funcIdx, buffer, pos); + if (hasVtableOffset()) { + assert vtbleOffset >= 0; + pos = CVUtil.putInt(vtbleOffset, buffer, pos); + } + pos = CVUtil.putUTF8StringBytes(name, buffer, pos); + return pos; + } + + @Override + public String toString() { + return String.format("LF_ONEMETHOD(0x%04x) attr=0x%x(%s) funcIdx=0x%x vtbloffet=0x%x %s", type, attrs, attrString(attrs), funcIdx, vtbleOffset, name); + } + + @Override + public int hashCode() { + int h = super.hashCode(); + h = 31 * h + funcIdx; + h = 31 * h + vtbleOffset; + return h; + } + + @Override + public boolean equals(Object obj) { + if (!super.equals(obj)) { + return false; + } + CVOneMethodRecord other = (CVOneMethodRecord) obj; + return this.vtbleOffset == other.vtbleOffset && this.funcIdx == other.funcIdx; + } + } + + static class CVBaseMemberRecord extends FieldRecord { + + private final int basetypeIndex; /* type index of member type */ + private final int offset; /* in java, usually 0 as there is no multiple inheritance. */ + + CVBaseMemberRecord(short attrs, int basetypeIndex, int offset) { + super(LF_BCLASS, attrs, ""); + this.basetypeIndex = basetypeIndex; + this.offset = offset; + } + + @Override + public int computeContents(byte[] buffer, int initialPos) { + int pos = CVUtil.putShort(type, buffer, initialPos); + pos = CVUtil.putShort(attrs, buffer, pos); + pos = CVUtil.putInt(basetypeIndex, buffer, pos); + pos = CVUtil.putLfNumeric(offset, buffer, pos); + return pos; + } + + @Override + public String toString() { + return String.format("LF_BCLASS(0x%04x) attr=0x%04x(%s ?) baseIdx=0x%04x offset=0x%x", LF_BCLASS, attrs, attrString(attrs), basetypeIndex, offset); + } + + @Override + public int hashCode() { + int h = super.hashCode(); + h = 31 * h + basetypeIndex; + h = 31 * h + offset; + return h; + } + + @Override + public boolean equals(Object obj) { + if (!super.equals(obj)) { + return false; + } + CVBaseMemberRecord other = (CVBaseMemberRecord) obj; + return this.basetypeIndex == other.basetypeIndex && this.offset == other.offset; + } + } + + static class CVClassRecord extends CVTypeRecord { + + static final int ATTR_FORWARD_REF = 0x0080; + static final int ATTR_HAS_UNIQUENAME = 0x0200; + + /* Count of number of elements in class field list. */ + private final short count; + + /* Property attribute field (prop_t). */ + private final short propertyAttributes; + + /* Type index of LF_FIELDLIST descriptor list. */ + private final int fieldIndex; + + /* Type index of derived from list if not zero */ + /* + * For Java, there is only one class, so LF_BCLASS is in the member list and + * derivedFromIndex is 0. + */ + private final int derivedFromIndex; + + /* Type index of vshape table for this class. */ + private final int vshapeIndex; + + /* Size (in bytes) of an instance. */ + private final long size; + + /* Class name. */ + private final String className; + + /* Linker class name. */ + private final String uniqueName; + + CVClassRecord(short recType, short count, short attrs, int fieldIndex, int derivedFromIndex, int vshapeIndex, long size, String className, String uniqueName) { + super(recType); + this.count = count; + this.propertyAttributes = (short) (attrs | (short) (uniqueName != null ? ATTR_HAS_UNIQUENAME : 0)); + this.fieldIndex = fieldIndex; + this.derivedFromIndex = derivedFromIndex; + this.vshapeIndex = vshapeIndex; + this.size = size; + this.className = className; + this.uniqueName = uniqueName; + } + + @SuppressWarnings("unused") + CVClassRecord(short count, short attrs, int fieldIndex, int derivedFromIndex, int vshapeIndex, long size, String className, String uniqueName) { + this(LF_CLASS, count, attrs, fieldIndex, derivedFromIndex, vshapeIndex, size, className, uniqueName); + } + + CVClassRecord(short attrs, String className, String uniqueName) { + this(LF_CLASS, (short) 0, attrs, 0, 0, 0, 0, className, uniqueName); + } + + String getClassName() { + return className; + } + + @Override + public int computeContents(byte[] buffer, int initialPos) { + int pos = CVUtil.putShort(count, buffer, initialPos); + pos = CVUtil.putShort(propertyAttributes, buffer, pos); + pos = CVUtil.putInt(fieldIndex, buffer, pos); + pos = CVUtil.putInt(derivedFromIndex, buffer, pos); + pos = CVUtil.putInt(vshapeIndex, buffer, pos); + pos = CVUtil.putLfNumeric(size, buffer, pos); + String fixedName = CVNames.typeNameToCodeViewName(className); + pos = CVUtil.putUTF8StringBytes(fixedName, buffer, pos); + if (hasUniqueName()) { + assert uniqueName != null; + pos = CVUtil.putUTF8StringBytes(uniqueName, buffer, pos); + } + return pos; + } + + boolean isForwardRef() { + return (propertyAttributes & ATTR_FORWARD_REF) != 0; + } + + @SuppressWarnings("unused") + public boolean hasUniqueName() { + return (propertyAttributes & ATTR_HAS_UNIQUENAME) != 0; + } + + protected String toString(String lfTypeStr) { + return String.format("%s 0x%04x count=%d attr=0x%x(%s) fld=0x%x super=0x%x vshape=0x%x size=%d %s%s", lfTypeStr, getSequenceNumber(), count, propertyAttributes, + propertyString(propertyAttributes), fieldIndex, derivedFromIndex, + vshapeIndex, size, className, uniqueName != null ? " (" + uniqueName + ")" : ""); + } + + @Override + public String toString() { + return toString("LF_CLASS"); + } + + @Override + public int hashCode() { + int h = type; + h = 31 * h + count; + h = 31 * h + propertyAttributes; + h = 31 * h + fieldIndex; + h = 31 * h + derivedFromIndex; + h = 31 * h + (int) size; + h = 31 * h + className.hashCode(); + if (uniqueName != null) { + h = 31 * h + uniqueName.hashCode(); + } + h = 31 * h + vshapeIndex; + return h; + } + + @Override + public boolean equals(Object obj) { + if (!super.equals(obj)) { + return false; + } + CVClassRecord other = (CVClassRecord) obj; + return this.count == other.count && this.propertyAttributes == other.propertyAttributes && this.fieldIndex == other.fieldIndex && this.derivedFromIndex == other.derivedFromIndex && + this.size == other.size && this.className.equals(other.className) && (this.uniqueName != null ? this.uniqueName.equals(other.uniqueName) : other.uniqueName == null) && + this.vshapeIndex == other.vshapeIndex; + } + } + + static final class CVFieldListRecord extends CVTypeRecord { + + static final int INITIAL_CAPACITY = 10; + + /* Size includes type field but not record length field. */ + private int estimatedSize = CVUtil.align4(Short.BYTES); + + private final ArrayList members = new ArrayList<>(INITIAL_CAPACITY); + + CVFieldListRecord() { + super(LF_FIELDLIST); + } + + void add(FieldRecord m) { + /* Keep a running total. */ + estimatedSize += CVUtil.align4(m.computeSize()); + members.add(m); + } + + int getEstimatedSize() { + return estimatedSize; + } + + @Override + protected int computeContents(byte[] buffer, int initialPos) { + int pos = initialPos; + for (FieldRecord field : members) { + pos = field.computeContents(buffer, pos); + pos = CVTypeRecord.alignPadded4(buffer, pos); + } + return pos; + } + + @Override + public int hashCode() { + int hash = type; + hash = 31 * hash + members.hashCode(); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (!super.equals(obj)) { + return false; + } + CVFieldListRecord other = (CVFieldListRecord) obj; + return this.members.equals(other.members); + } + + @Override + public String toString() { + return String.format("LF_FIELDLIST idx=0x%x count=%d", getSequenceNumber(), members.size()); + } + } + + static final class CVTypeArrayRecord extends CVTypeRecord { + + private final int elementTypeIndex; + private final int indexType; + private final int length; + private final String name; + + CVTypeArrayRecord(int elementTypeIndex, int indexType, int length, String name) { + super(LF_ARRAY); + this.elementTypeIndex = elementTypeIndex; + this.indexType = indexType; + this.length = length; + this.name = name; + } + + CVTypeArrayRecord(int elementTypeIndex, int indexType, int length) { + this(elementTypeIndex, indexType, length, ""); + } + + @SuppressWarnings("unused") + CVTypeArrayRecord(int elementTypeIndex, int length) { + this(elementTypeIndex, T_UINT8, length); + } + + @SuppressWarnings("unused") + CVTypeArrayRecord(CVTypeRecord elementType, int length) { + this(elementType.getSequenceNumber(), T_UINT8, length); + } + + @Override + public int computeContents(byte[] buffer, int initialPos) { + int pos = CVUtil.putInt(elementTypeIndex, buffer, initialPos); + pos = CVUtil.putInt(indexType, buffer, pos); + pos = CVUtil.putLfNumeric(length, buffer, pos); + pos = CVUtil.putUTF8StringBytes(name, buffer, pos); + return pos; + } + + @Override + public String toString() { + return String.format("LF_ARRAY 0x%04x type=0x%04x len=%d indexType=0x%04x%s", getSequenceNumber(), elementTypeIndex, length, indexType, name.isEmpty() ? "" : "name=" + name); + } + + @Override + public int hashCode() { + int h = type; + h = 31 * h + elementTypeIndex; + h = 31 * h + indexType; + h = 31 * h + length; + h = 31 * h + name.hashCode(); + return h; + } + + @Override + public boolean equals(Object obj) { + if (!super.equals(obj)) { + return false; + } + CVTypeArrayRecord other = (CVTypeArrayRecord) obj; + return this.elementTypeIndex == other.elementTypeIndex && this.indexType == other.indexType && this.length == other.length && this.name.equals(other.name); + } + } + + static String propertyString(int properties) { + final StringBuilder sb = new StringBuilder(); + + /* Low byte. */ + if ((properties & 0x0001) != 0) { + sb.append(" packed"); + } + if ((properties & 0x0002) != 0) { + sb.append(" ctor"); + } + if ((properties & 0x0004) != 0) { + sb.append(" ovlops"); + } + if ((properties & 0x0008) != 0) { + sb.append(" isnested"); + } + if ((properties & 0x0010) != 0) { + sb.append(" cnested"); + } + if ((properties & 0x0020) != 0) { + sb.append(" opassign"); + } + if ((properties & 0x0040) != 0) { + sb.append(" opcast"); + } + if ((properties & 0x0080) != 0) { + sb.append(" forwardref"); + } + + /* High byte. */ + if ((properties & 0x0100) != 0) { + sb.append(" scope"); + } + if ((properties & 0x0200) != 0) { + sb.append(" hasuniquename"); + } + if ((properties & 0x0400) != 0) { + sb.append(" sealed"); + } + if ((properties & 0x1800) != 0) { + sb.append(" hfa..."); + } + if ((properties & 0x2000) != 0) { + sb.append(" intrinsic"); + } + if ((properties & 0xc000) != 0) { + sb.append(" macom..."); + } + return sb.toString(); + } } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVTypeSectionBuilder.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVTypeSectionBuilder.java new file mode 100644 index 000000000000..5e2fc1e34893 --- /dev/null +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVTypeSectionBuilder.java @@ -0,0 +1,556 @@ +/* + * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2021, Red Hat Inc. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.objectfile.pecoff.cv; + +import com.oracle.objectfile.debugentry.ArrayTypeEntry; +import com.oracle.objectfile.debugentry.ClassEntry; +import com.oracle.objectfile.debugentry.CompiledMethodEntry; +import com.oracle.objectfile.debugentry.FieldEntry; +import com.oracle.objectfile.debugentry.HeaderTypeEntry; +import com.oracle.objectfile.debugentry.MemberEntry; +import com.oracle.objectfile.debugentry.MethodEntry; +import com.oracle.objectfile.debugentry.StructureTypeEntry; +import com.oracle.objectfile.debugentry.TypeEntry; + +import org.graalvm.compiler.debug.GraalError; + +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Deque; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.ADDRESS_BITS; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.CV_CALL_NEAR_C; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.FUNC_IS_CONSTRUCTOR; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.JAVA_LANG_OBJECT_NAME; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.LF_CLASS; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.MPROP_IVIRTUAL; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.MPROP_PRIVATE; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.MPROP_PROTECTED; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.MPROP_PUBLIC; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.MPROP_PURE_IVIRTUAL; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.MPROP_PURE_VIRTUAL; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.MPROP_STATIC; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.MPROP_VANILLA; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.MPROP_VIRTUAL; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.OBJ_HEADER_NAME; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.T_64PBOOL08; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.T_64PINT1; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.T_64PINT2; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.T_64PINT4; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.T_64PINT8; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.T_64PREAL32; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.T_64PREAL64; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.T_64PVOID; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.T_64PWCHAR; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.T_BOOL08; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.T_INT1; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.T_INT2; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.T_INT4; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.T_INT8; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.T_NOTYPE; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.T_REAL32; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.T_REAL64; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.T_UINT4; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.T_VOID; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.T_WCHAR; +import static com.oracle.objectfile.pecoff.cv.CVTypeRecord.CVClassRecord.ATTR_FORWARD_REF; +import static com.oracle.objectfile.pecoff.cv.CVTypeRecord.CV_TYPE_RECORD_MAX_SIZE; + +class CVTypeSectionBuilder { + + private final int objectHeaderRecordIndex; + + private final CVTypeSectionImpl typeSection; + + private final TypeTable types; + + CVTypeSectionBuilder(CVTypeSectionImpl typeSection) { + this.typeSection = typeSection; + this.types = new TypeTable(typeSection); + objectHeaderRecordIndex = types.getIndexForForwardRef(OBJ_HEADER_NAME); + } + + void verifyAllClassesDefined() { + /* When this is called, all types should be seen already. */ + /* Just in case, check to see if we have some unresolved forward refs. */ + types.testForUndefinedClasses(); + } + + CVTypeRecord buildType(TypeEntry typeEntry) { + + CVTypeRecord typeRecord = types.getExistingType(typeEntry); + /* + * If we've never seen the class or only defined it as a forward reference, define it now. + */ + if (typeRecord != null && typeRecord.type == LF_CLASS && !((CVTypeRecord.CVClassRecord) typeRecord).isForwardRef()) { + log("buildType() type %s(%s) is known %s", typeEntry.getTypeName(), typeEntry.typeKind().name(), typeRecord); + } else { + log("buildType() %s %s size=%d - begin", typeEntry.typeKind().name(), typeEntry.getTypeName(), typeEntry.getSize()); + switch (typeEntry.typeKind()) { + case PRIMITIVE: { + typeRecord = types.getExistingType(typeEntry); + break; + } + case ARRAY: + case ENUM: + case INSTANCE: + case INTERFACE: { + typeRecord = buildStructureTypeEntry((StructureTypeEntry) typeEntry); + break; + } + case HEADER: { + /* + * The bits at the beginning of an Object: contains pointer to DynamicHub. + */ + assert typeEntry.getTypeName().equals(OBJ_HEADER_NAME); + typeRecord = buildStructureTypeEntry((HeaderTypeEntry) typeEntry); + break; + } + } + } + assert typeRecord != null; + log("buildType end: %s", typeRecord); + return typeRecord; + } + + /** + * Add type records for function. In the future add local types when they become available. + * + * @param entry primaryEntry containing entities whose type records must be added + * @return type record for this function (may return existing matching record) + */ + CVTypeRecord buildFunction(CompiledMethodEntry entry) { + return buildMemberFunction(entry.getClassEntry(), entry.getPrimary().getMethodEntry()); + } + + static class FieldListBuilder { + + static final int CV_INDEX_RECORD_SIZE = CVUtil.align4(new CVTypeRecord.CVIndexRecord(0).computeSize()); + final List fields = new ArrayList<>(); + + FieldListBuilder() { + } + + void addField(CVTypeRecord.FieldRecord field) { + fields.add(field); + } + + int getFieldCount() { + return fields.size(); + } + + CVTypeRecord.CVFieldListRecord buildFieldListRecords(CVTypeSectionBuilder builder) { + + /* + * The last FieldList must refer back to the one before it, and must contain the first + * fields in the class. + */ + + CVTypeRecord.CVFieldListRecord currentFieldList = new CVTypeRecord.CVFieldListRecord(); + Deque fl = new LinkedList<>(); + fl.add(currentFieldList); + + /* Build all Field List records in field order (FIFO). */ + for (CVTypeRecord.FieldRecord fieldRecord : fields) { + /* + * Calculate the potential size of the fieldList if the current fieldRecord and a + * (potential) index record are added to it. + */ + int sizeOfExpandedFieldList = currentFieldList.getEstimatedSize() + CVUtil.align4(fieldRecord.computeSize()) + CV_INDEX_RECORD_SIZE; + /* If there isn't enough room for the new fieldRecord, start a new CVFieldList. */ + if (sizeOfExpandedFieldList >= CV_TYPE_RECORD_MAX_SIZE) { + currentFieldList = new CVTypeRecord.CVFieldListRecord(); + fl.add(currentFieldList); + } + currentFieldList.add(fieldRecord); + } + + /* + * Emit all Field List records in reverse order (LIFO), adding Index records to all but + * the first emitted. + */ + CVTypeRecord.CVFieldListRecord fieldListRecord = null; + int idx = 0; + while (!fl.isEmpty()) { + fieldListRecord = fl.removeLast(); + fieldListRecord = builder.addTypeRecord(fieldListRecord); + /* For all fieldlist but the first, link to the previous record. */ + if (idx != 0) { + fieldListRecord.add(new CVTypeRecord.CVIndexRecord(idx)); + } + idx = fieldListRecord.getSequenceNumber(); + } + return fieldListRecord; + } + } + + private CVTypeRecord buildStructureTypeEntry(final StructureTypeEntry typeEntry) { + + log("buildStructureTypeEntry size=%d kind=%s %s", typeEntry.getSize(), typeEntry.typeKind().name(), typeEntry.getTypeName()); + + ClassEntry superType = typeEntry.isClass() ? ((ClassEntry) typeEntry).getSuperClass() : null; + int superTypeIndex = superType != null ? types.getIndexForForwardRef(superType) : 0; + + /* Arrays are implemented as classes, but the inheritance from Object() is implicit. */ + if (superTypeIndex == 0 && typeEntry.isArray()) { + superTypeIndex = types.getIndexForForwardRef(JAVA_LANG_OBJECT_NAME); + } + + /* Both java.lang.Object and __objhdr have null superclass. */ + /* Force java.lang.Object to have __objhdr as a superclass. */ + /* Force interfaces to have __objhdr as a superclass. */ + if (superTypeIndex == 0 && (typeEntry.getTypeName().equals(JAVA_LANG_OBJECT_NAME) || typeEntry.isInterface())) { + superTypeIndex = objectHeaderRecordIndex; + } + + final List methods = typeEntry.isClass() ? ((ClassEntry) typeEntry).getMethods() : Collections.emptyList(); + + /* Build fieldlist record */ + FieldListBuilder fieldListBuilder = new FieldListBuilder(); + log("building field list"); + + if (superTypeIndex != 0) { + CVTypeRecord.CVBaseMemberRecord btype = new CVTypeRecord.CVBaseMemberRecord(MPROP_PUBLIC, superTypeIndex, 0); + log("basetype %s", btype); + fieldListBuilder.addField(btype); + } + + /* Only define manifested fields. */ + typeEntry.fields().filter(CVTypeSectionBuilder::isManifestedField).forEach(f -> { + log("field %s attr=(%s) offset=%d size=%d valuetype=%s", f.fieldName(), f.getModifiersString(), f.getOffset(), f.getSize(), f.getValueType().getTypeName()); + CVTypeRecord.FieldRecord fieldRecord = buildField(f); + log("field %s", fieldRecord); + fieldListBuilder.addField(fieldRecord); + }); + + if (typeEntry.isArray()) { + /* + * Model an array as a struct with a pointer, length and then array of length 0. + * String[] becomes struct String[] : Object { int length; String*[0]; } + */ + assert typeEntry instanceof ArrayTypeEntry; + ArrayTypeEntry arrayEntry = (ArrayTypeEntry) typeEntry; + + /* Build 0 length array - this index could be cached. */ + final TypeEntry elementType = arrayEntry.getElementType(); + int elementTypeIndex = types.getIndexForPointerOrPrimitive(elementType); + CVTypeRecord array0record = addTypeRecord(new CVTypeRecord.CVTypeArrayRecord(elementTypeIndex, T_UINT4, 0)); + + /* Build a field for the 0 length array. */ + CVTypeRecord.CVMemberRecord dm = new CVTypeRecord.CVMemberRecord(MPROP_PUBLIC, array0record.getSequenceNumber(), typeEntry.getSize(), "data"); + log("field %s", dm); + fieldListBuilder.addField(dm); + } + + /* + * Functions go into the main fieldList if they are not overloaded. Overloaded functions get + * a M_FUNCTION entry in the field list, and a LF_METHODLIST record pointing to M_MFUNCTION + * records for each overload. + */ + if (methods.size() > 0) { + + log("building methods"); + + /* first build a list of all overloaded functions */ + HashSet overloaded = new HashSet<>(methods.size()); + HashSet allFunctions = new HashSet<>(methods.size()); + methods.forEach(m -> { + if (allFunctions.contains(m.methodName())) { + overloaded.add(m.methodName()); + } else { + allFunctions.add(m.methodName()); + } + }); + + /* TODO: if methodlist is too big, split it up using LF_INDEX records. */ + overloaded.forEach(mname -> { + + /* LF_METHODLIST */ + CVTypeRecord.CVTypeMethodListRecord mlist = new CVTypeRecord.CVTypeMethodListRecord(); + + /* LF_MFUNCTION records */ + methods.stream().filter(methodEntry -> methodEntry.methodName().equals(mname)).forEach(m -> { + log("overloaded method %s attr=(%s) valuetype=%s", m.methodName(), m.getModifiersString(), m.getValueType().getTypeName()); + CVTypeRecord.CVTypeMFunctionRecord mFunctionRecord = buildMemberFunction((ClassEntry) typeEntry, m); + short attr = modifiersToAttr(m); + log(" overloaded method %s", mFunctionRecord); + mlist.add(attr, mFunctionRecord.getSequenceNumber(), m.getVtableOffset(), m.methodName()); + }); + + CVTypeRecord.CVTypeMethodListRecord nmlist = addTypeRecord(mlist); + + /* LF_METHOD record */ + CVTypeRecord.CVOverloadedMethodRecord methodRecord = new CVTypeRecord.CVOverloadedMethodRecord((short) nmlist.count(), nmlist.getSequenceNumber(), mname); + fieldListBuilder.addField(methodRecord); + }); + + methods.stream().filter(methodEntry -> !overloaded.contains(methodEntry.methodName())).forEach(m -> { + log("`unique method %s %s(...)", m.methodName(), m.getModifiersString(), m.getValueType().getTypeName(), m.methodName()); + CVTypeRecord.CVOneMethodRecord method = buildMethod((ClassEntry) typeEntry, m); + log(" unique method %s", method); + fieldListBuilder.addField(method); + }); + } + /* Build fieldlist record from manifested fields. */ + CVTypeRecord.CVFieldListRecord fieldListRecord = fieldListBuilder.buildFieldListRecords(this); + int fieldListIdx = fieldListRecord.getSequenceNumber(); + int fieldCount = fieldListBuilder.getFieldCount(); + log("finished building fieldlist %s", fieldListRecord); + + /* Build final class record. */ + short attrs = 0; /* property attribute field (prop_t) */ + CVTypeRecord typeRecord = new CVTypeRecord.CVClassRecord(LF_CLASS, (short) fieldCount, attrs, fieldListIdx, 0, 0, typeEntry.getSize(), typeEntry.getTypeName(), null); + typeRecord = addTypeRecord(typeRecord); + + if (typeEntry.isClass()) { + /* Add a UDT record (if we have the information) */ + ClassEntry classEntry = (ClassEntry) typeEntry; + /* + * Try to find a line number for the first function - if none, don't bother to create + * the record. + */ + int line = classEntry.getMethods().isEmpty() ? 0 : classEntry.getMethods().get(0).getLine(); + if (line > 0) { + int idIdx = typeSection.getStringId(classEntry.getFullFileName()).getSequenceNumber(); + CVTypeRecord.CVUdtTypeLineRecord udt = new CVTypeRecord.CVUdtTypeLineRecord(typeRecord.getSequenceNumber(), idIdx, line); + addTypeRecord(udt); + } + } + + types.typeNameMap.put(typeEntry.getTypeName(), typeRecord); + + /* CVSymbolSubsectionBuilder will add associated S_UDT record to symbol table. */ + log(" finished class %s", typeRecord); + + return typeRecord; + } + + private static boolean isManifestedField(FieldEntry fieldEntry) { + return fieldEntry.getOffset() >= 0; + } + + private CVTypeRecord.FieldRecord buildField(FieldEntry fieldEntry) { + TypeEntry valueType = fieldEntry.getValueType(); + int valueTypeIndex = types.getIndexForPointerOrPrimitive(valueType); + short attr = modifiersToAttr(fieldEntry); + if (Modifier.isStatic(fieldEntry.getModifiers())) { + return new CVTypeRecord.CVStaticMemberRecord(attr, valueTypeIndex, fieldEntry.fieldName()); + } else { + return new CVTypeRecord.CVMemberRecord(attr, valueTypeIndex, fieldEntry.getOffset(), fieldEntry.fieldName()); + } + } + + private static short modifiersToAttr(MethodEntry member) { + + short attr = accessToAttr(member); + boolean isStatic = Modifier.isStatic(member.getModifiers()); + if (isStatic) { + attr += MPROP_STATIC; + } else if (!member.isVirtual()) { + // noinspection ConstantConditions + attr += MPROP_VANILLA; + } else if (Modifier.isAbstract(member.getModifiers())) { + attr += member.isOverride() ? MPROP_PURE_VIRTUAL : MPROP_PURE_IVIRTUAL; + } else { + attr += member.isOverride() ? MPROP_VIRTUAL : MPROP_IVIRTUAL; + } + return attr; + } + + private static short modifiersToAttr(FieldEntry member) { + return accessToAttr(member); + } + + private CVTypeRecord.CVOneMethodRecord buildMethod(ClassEntry classEntry, MethodEntry methodEntry) { + CVTypeRecord.CVTypeMFunctionRecord funcRecord = buildMemberFunction(classEntry, methodEntry); + short attr = modifiersToAttr(methodEntry); + return new CVTypeRecord.CVOneMethodRecord(attr, funcRecord.getSequenceNumber(), methodEntry.getVtableOffset(), methodEntry.methodName()); + } + + private static short accessToAttr(MemberEntry member) { + int modifiers = member.getModifiers(); + final short attr; + if (Modifier.isPublic(modifiers)) { + attr = MPROP_PUBLIC; + } else if (Modifier.isPrivate(modifiers)) { + attr = MPROP_PRIVATE; + } else if (Modifier.isProtected(modifiers)) { + attr = MPROP_PROTECTED; + } else { + attr = MPROP_VANILLA; + } + return attr; + } + + CVTypeRecord.CVTypeMFunctionRecord buildMemberFunction(ClassEntry classEntry, MethodEntry methodEntry) { + CVTypeRecord.CVTypeMFunctionRecord mFunctionRecord = new CVTypeRecord.CVTypeMFunctionRecord(); + mFunctionRecord.setClassType(types.getIndexForForwardRef(classEntry)); + mFunctionRecord.setCallType((byte) (CV_CALL_NEAR_C)); + mFunctionRecord.setThisType(Modifier.isStatic(methodEntry.getModifiers()) ? T_NOTYPE : types.getIndexForPointerOrPrimitive(classEntry)); + /* 'attr' is CV_funcattr_t and if set to 2 indicates a constructor function. */ + byte attr = methodEntry.isConstructor() ? (byte) FUNC_IS_CONSTRUCTOR : 0; + mFunctionRecord.setFuncAttr(attr); + mFunctionRecord.setReturnType(types.getIndexForPointerOrPrimitive(methodEntry.getValueType())); + CVTypeRecord.CVTypeArglistRecord argListType = new CVTypeRecord.CVTypeArglistRecord(); + for (int i = 0; i < methodEntry.getParamCount(); i++) { + argListType.add(types.getIndexForPointerOrPrimitive(methodEntry.getParamType(i))); + } + argListType = addTypeRecord(argListType); + mFunctionRecord.setArgList(argListType); + return addTypeRecord(mFunctionRecord); + } + + private T addTypeRecord(T record) { + return types.addTypeRecord(record); + } + + int getIndexForPointerOrPrimitive(TypeEntry entry) { + return types.getIndexForPointerOrPrimitive(entry); + } + + private void log(String fmt, Object... args) { + typeSection.log(fmt, args); + } + + static class TypeTable { + + private static final int CV_TYPENAME_INITIAL_CAPACITY = 20000; + + /* A map of typename to type records, */ + private final Map typeNameMap = new HashMap<>(CV_TYPENAME_INITIAL_CAPACITY); + + /* For convenience, quick lookup of pointer type indices given class type index */ + /* Could have saved this in typeNameMap. */ + /* maps type index to pointer to forward ref record */ + private final Map typePointerMap = new HashMap<>(CV_TYPENAME_INITIAL_CAPACITY); + + /* + * A map of type names to type records. Only forward references are stored here, and only + * until they are defined. + */ + private final Map forwardRefMap = new HashMap<>(CV_TYPENAME_INITIAL_CAPACITY); + + private final CVTypeSectionImpl typeSection; + + TypeTable(CVTypeSectionImpl typeSection) { + this.typeSection = typeSection; + addPrimitiveTypes(); + } + + void testForUndefinedClasses() { + for (CVTypeRecord record : forwardRefMap.values()) { + CVTypeRecord.CVClassRecord classRecord = (CVTypeRecord.CVClassRecord) record; + if (!typeNameMap.containsKey(classRecord.getClassName())) { + GraalError.shouldNotReachHere("no typeentry for " + classRecord.getClassName() + "; type remains incomplete"); + } + } + } + + private T addTypeRecord(T record) { + return typeSection.addOrReference(record); + } + + /** + * Return a CV type index for a pointer to a java type, or the type itself if a primitive. + * + * @param entry The java type to return a typeindex for. If the type has not been seen, a + * forward reference is generated. + * @return The index for the typeentry for a pointer to the type. If the type is a primitive + * type, the index returned is for the type, not a pointer to the type. + */ + int getIndexForPointerOrPrimitive(TypeEntry entry) { + if (entry.isPrimitive()) { + CVTypeRecord record = getExistingType(entry); + assert record != null; + return record.getSequenceNumber(); + } + CVTypeRecord forwardRefRecord = getExistingForwardReference(entry); + if (forwardRefRecord == null) { + forwardRefRecord = addTypeRecord(new CVTypeRecord.CVClassRecord((short) ATTR_FORWARD_REF, entry.getTypeName(), null)); + forwardRefMap.put(entry.getTypeName(), forwardRefRecord); + } + /* We now have a class record but must create a pointer record. */ + CVTypeRecord ptrRecord = typePointerMap.get(forwardRefRecord.getSequenceNumber()); + if (ptrRecord == null) { + ptrRecord = addTypeRecord(new CVTypeRecord.CVTypePointerRecord(forwardRefRecord.getSequenceNumber(), CVTypeRecord.CVTypePointerRecord.NORMAL_64)); + typePointerMap.put(forwardRefRecord.getSequenceNumber(), ptrRecord); + } + return ptrRecord.getSequenceNumber(); + } + + private int getIndexForForwardRef(ClassEntry entry) { + return getIndexForForwardRef(entry.getTypeName()); + } + + int getIndexForForwardRef(String className) { + CVTypeRecord clsRecord = forwardRefMap.get(className); + if (clsRecord == null) { + clsRecord = addTypeRecord(new CVTypeRecord.CVClassRecord((short) ATTR_FORWARD_REF, className, null)); + forwardRefMap.put(className, clsRecord); + } + return clsRecord.getSequenceNumber(); + } + + CVTypeRecord getExistingType(TypeEntry typeEntry) { + return typeNameMap.get(typeEntry.getTypeName()); + } + + CVTypeRecord getExistingForwardReference(TypeEntry typeEntry) { + return forwardRefMap.get(typeEntry.getTypeName()); + } + + void definePrimitiveType(String typename, short typeId, int length, short pointerTypeId) { + CVTypeRecord record = new CVTypeRecord.CVTypePrimitive(typeId, length); + typeNameMap.put(typename, record); + if (pointerTypeId != 0) { + CVTypeRecord pointerRecord = new CVTypeRecord.CVTypePrimitive(pointerTypeId, ADDRESS_BITS); + typePointerMap.put((int) typeId, pointerRecord); + } + } + + private void addPrimitiveTypes() { + /* + * Primitive types are pre-defined and do not get written out to the typeInfo section. + */ + definePrimitiveType("void", T_VOID, 0, T_64PVOID); + definePrimitiveType("byte", T_INT1, Byte.BYTES, T_64PINT1); + definePrimitiveType("boolean", T_BOOL08, 1, T_64PBOOL08); + definePrimitiveType("char", T_WCHAR, Character.BYTES, T_64PWCHAR); + definePrimitiveType("short", T_INT2, Short.BYTES, T_64PINT2); + definePrimitiveType("int", T_INT4, Integer.BYTES, T_64PINT4); + definePrimitiveType("long", T_INT8, Long.BYTES, T_64PINT8); + definePrimitiveType("float", T_REAL32, Float.BYTES, T_64PREAL32); + definePrimitiveType("double", T_REAL64, Double.BYTES, T_64PREAL64); + } + } +} diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVTypeSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVTypeSectionImpl.java index 89a552626bf0..696b9434c9aa 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVTypeSectionImpl.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVTypeSectionImpl.java @@ -1,6 +1,6 @@ /* - * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2020, 2020, Red Hat Inc. All rights reserved. + * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2021, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,6 +30,8 @@ import com.oracle.objectfile.LayoutDecision; import com.oracle.objectfile.LayoutDecisionMap; import com.oracle.objectfile.ObjectFile; +import com.oracle.objectfile.debugentry.CompiledMethodEntry; +import com.oracle.objectfile.debugentry.TypeEntry; import com.oracle.objectfile.pecoff.PECoffObjectFile; import org.graalvm.compiler.debug.DebugContext; @@ -49,10 +51,17 @@ public final class CVTypeSectionImpl extends CVSectionImpl { private int sequenceCounter = 0x1000; /* A sequential map of type records, starting at 1000 */ + /* This map is used to implement deduplication. */ private final Map typeMap = new LinkedHashMap<>(CV_RECORD_INITIAL_CAPACITY); + private final CVTypeSectionBuilder builder; + CVTypeSectionImpl(CVDebugInfo cvDebugInfo) { + /* + * At this point, there is no debugContext in debugInfo, so no logging should be attempted. + */ super(cvDebugInfo); + builder = new CVTypeSectionBuilder(this); } @Override @@ -64,8 +73,8 @@ public String getSectionName() { public void createContent(DebugContext debugContext) { int pos = 0; enableLog(debugContext); - log("CVTypeSectionImpl.createContent() adding records"); - addRecords(); + log("CVTypeSectionImpl.createContent() verifying that all types have been defined"); + builder.verifyAllClassesDefined(); log("CVTypeSectionImpl.createContent() start"); pos = CVUtil.putInt(CV_SIGNATURE_C13, null, pos); for (CVTypeRecord record : typeMap.values()) { @@ -91,14 +100,6 @@ public void writeContent(DebugContext debugContext) { verboseLog("CVTypeSectionImpl.writeContent() end"); } - /** - * Add all relevant type records to the type section. - */ - private void addRecords() { - /* if an external PDB file is generated, add CVTypeServer2Record */ - /* for each class, add all members, types, etc */ - } - /** * Return either the caller-created instance or a matching existing instance. Every entry in * typeMap is a T, because it is ONLY this function which inserts entries (of type T). @@ -108,7 +109,7 @@ private void addRecords() { * @return the record (if previously unseen) or old record */ @SuppressWarnings("unchecked") - public T addOrReference(T newRecord) { + T addOrReference(T newRecord) { final T record; if (typeMap.containsKey(newRecord)) { record = (T) typeMap.get(newRecord); @@ -130,4 +131,34 @@ public Set getDependencies(Map