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 19f7f66fbde8..713dcb649072 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 @@ -86,10 +86,38 @@ public class ClassEntry extends StructureTypeEntry { */ private final EconomicMap compiledMethodIndex = EconomicMap.create(); + /** + * A list of all files referenced from info associated with this class, including info detailing + * inline method ranges. + */ + private final ArrayList files; + /** + * A list of all directories referenced from info associated with this class, including info + * detailing inline method ranges. + */ + private final ArrayList dirs; + /** + * An index identifying the file table position of every file referenced from info associated + * with this class, including info detailing inline method ranges. + */ + private EconomicMap fileIndex; + /** + * An index identifying the dir table position of every directory referenced from info + * associated with this class, including info detailing inline method ranges. + */ + private EconomicMap dirIndex; + public ClassEntry(String className, FileEntry fileEntry, int size) { super(className, size); this.fileEntry = fileEntry; this.loader = null; + // file and dir lists/indexes are populated after all DebugInfo API input has + // been received and are only created on demand + files = new ArrayList<>(); + dirs = new ArrayList<>(); + // create these on demand using the size of the file and dir lists + this.fileIndex = null; + this.dirIndex = null; } @Override @@ -179,7 +207,33 @@ public FileEntry getFileEntry() { } public int getFileIdx() { - return fileEntry.getIdx(); + return getFileIdx(this.getFileEntry()); + } + + public int getFileIdx(FileEntry file) { + if (file == null || fileIndex == null) { + return 0; + } + return fileIndex.get(file); + } + + public DirEntry getDirEntry(FileEntry file) { + if (file == null) { + return null; + } + return file.getDirEntry(); + } + + public int getDirIdx(FileEntry file) { + DirEntry dirEntry = getDirEntry(file); + return getDirIdx(dirEntry); + } + + public int getDirIdx(DirEntry dir) { + if (dir == null || dir.getPathString().isEmpty() || dirIndex == null) { + return 0; + } + return dirIndex.get(dir); } public String getLoaderId() { @@ -187,8 +241,7 @@ public String getLoaderId() { } /** - * Retrieve a stream of all compiled method entries for this class, including both normal and - * deopt fallback compiled methods. + * Retrieve a stream of all compiled method entries for this class. * * @return a stream of all compiled method entries for this class. */ @@ -196,16 +249,6 @@ public Stream compiledEntries() { return compiledEntries.stream(); } - /** - * Retrieve a stream of all normal compiled method entries for this class, excluding deopt - * fallback compiled methods. - * - * @return a stream of all normal compiled method entries for this class. - */ - public Stream normalCompiledEntries() { - return compiledEntries(); - } - protected void processInterface(ResolvedJavaType interfaceType, DebugInfoBase debugInfoBase, DebugContext debugContext) { String interfaceName = interfaceType.toJavaName(); debugContext.log("typename %s adding interface %s%n", typeName, interfaceName); @@ -267,8 +310,17 @@ private static String formatParams(DebugLocalInfo[] paramInfo) { return builder.toString(); } + public int compiledEntryCount() { + return compiledEntries.size(); + } + public boolean hasCompiledEntries() { - return compiledEntries.size() != 0; + return compiledEntryCount() != 0; + } + + public int compiledEntriesBase() { + assert hasCompiledEntries(); + return compiledEntries.get(0).getPrimary().getLo(); } public ClassEntry getSuperClass() { @@ -317,4 +369,80 @@ public int hipc() { assert hasCompiledEntries(); return compiledEntries.get(compiledEntries.size() - 1).getPrimary().getHi(); } + + /** + * Add a file to the list of files referenced from info associated with this class. + * + * @param file The file to be added. + */ + public void includeFile(FileEntry file) { + assert !files.contains(file) : "caller should ensure file is only included once"; + assert fileIndex == null : "cannot include files after index has been created"; + files.add(file); + } + + /** + * Add a directory to the list of firectories referenced from info associated with this class. + * + * @param dirEntry The directory to be added. + */ + public void includeDir(DirEntry dirEntry) { + assert !dirs.contains(dirEntry) : "caller should ensure dir is only included once"; + assert dirIndex == null : "cannot include dirs after index has been created"; + dirs.add(dirEntry); + } + + /** + * Populate the file and directory indexes that track positions in the file and dir tables for + * this class's line info section. + */ + public void buildFileAndDirIndexes() { + // this is a one-off operation + assert fileIndex == null && dirIndex == null : "file and indexes can only be generated once"; + if (files.isEmpty()) { + assert dirs.isEmpty() : "should not have included any dirs if we have no files"; + } + int idx = 1; + fileIndex = EconomicMap.create(files.size()); + for (FileEntry file : files) { + fileIndex.put(file, idx++); + } + dirIndex = EconomicMap.create(dirs.size()); + idx = 1; + for (DirEntry dir : dirs) { + if (!dir.getPathString().isEmpty()) { + dirIndex.put(dir, idx++); + } else { + assert idx == 1; + } + } + } + + /** + * Retrieve a stream of all files referenced from debug info for this class in line info file + * table order, starting with the file at index 1. + * + * @return a stream of all referenced files + */ + public Stream fileStream() { + if (!files.isEmpty()) { + return files.stream(); + } else { + return Stream.empty(); + } + } + + /** + * Retrieve a stream of all directories referenced from debug info for this class in line info + * directory table order, starting with the directory at index 1. + * + * @return a stream of all referenced directories + */ + public Stream dirStream() { + if (!dirs.isEmpty()) { + return dirs.stream(); + } else { + return Stream.empty(); + } + } } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DebugInfoBase.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DebugInfoBase.java index b343ec04171e..4d814307c185 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DebugInfoBase.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DebugInfoBase.java @@ -35,6 +35,7 @@ import java.util.Map; import org.graalvm.collections.EconomicMap; +import org.graalvm.collections.EconomicSet; import org.graalvm.compiler.debug.DebugContext; import com.oracle.objectfile.debugentry.range.PrimaryRange; @@ -77,31 +78,14 @@ * 3) by inlined method (sub range) within top level method, also ordered by ascending address * * Since clients may need to generate records for classes with no compiled methods, the second - * traversal order is often employed. In rare cases clients need to sort the class list by address - * before traversal to ensure the generated debug records are also sorted by address. + * traversal order is often employed. * - * n.b. the above strategy relies on the details that methods of a given class always appear in a - * single continuous address range with no intervening code from other methods or data values. This - * means we can treat each class as a Compilation Unit, allowing data common to all methods of the - * class to be referenced using CU-local offsets. - * - * Just as an aside, for full disclosure, this is not strictly the full story. Sometimes a class can - * include speculatively optimized, compiled methods plus deopt fallback compiled variants of those - * same methods. In such cases the normal and/or speculatively compiled methods occupy one - * contiguous range and deopt methods occupy a separate higher range. The current compilation - * strategy ensures that the union across all classes of the normal/speculative ranges and the union - * across all classes of the deopt ranges lie in two distinct intervals where the highest address in - * the first union is strictly less than the lowest address in the second union. The implication is - * twofold. An address order traversal requires generating details for classes, methods and - * non-deopt primary ranges before generating details for the deopt primary ranges. The former - * details need to be generated in a distinct CU from deopt method details. - * - * A third option appears to be to traverse via files, then top level class within file etc. - * Unfortunately, files cannot be treated as a compilation unit. A file F may contain multiple - * classes, say C1 and C2. There is no guarantee that methods for some other class C' in file F' - * will not be compiled into the address space interleaved between methods of C1 and C2. That is a - * shame because generating debug info records one file at a time would allow more sharing e.g. - * enabling all classes in a file to share a single copy of the file and dir tables. + * n.b. methods of a given class do not always appear in a single continuous address range. The + * compiler choose to interleave intervening code from other classes or data values in order to get + * better cache locality. It may also choose to generate deoptimized variants of methods in a + * separate range from normal, optimized compiled code. This out of (code addess) order sorting may + * make it difficult to use a class by class traversal to generate debug info in separate per-class + * units. */ public abstract class DebugInfoBase { protected ByteOrder byteOrder; @@ -364,6 +348,10 @@ public void installDebugInfo(DebugInfoProvider debugInfoProvider) { debugContext.log(DebugContext.INFO_LEVEL, "Data: address 0x%x size 0x%x type %s partition %s provenance %s ", address, size, typeName, partitionName, provenance); } })); + // populate a file and dir list and associated index for each class entry + getInstanceClasses().forEach(classEntry -> { + collectFilesAndDirs(classEntry); + }); } private TypeEntry createTypeEntry(String typeName, String fileName, Path filePath, int size, DebugTypeKind typeKind) { @@ -581,7 +569,7 @@ private FileEntry addFileEntry(String fileName, Path filePath) { /* Ensure file and cachepath are added to the debug_str section. */ uniqueDebugString(fileName); uniqueDebugString(cachePath); - fileEntry = new FileEntry(fileName, dirEntry, files.size() + 1); + fileEntry = new FileEntry(fileName, dirEntry); files.add(fileEntry); /* Index the file entry by file path. */ filesIndex.put(fileAsPath, fileEntry); @@ -619,7 +607,7 @@ private DirEntry ensureDirEntry(Path filePath) { if (dirEntry == null) { /* Ensure dir path is entered into the debug_str section. */ uniqueDebugString(filePath.toString()); - dirEntry = new DirEntry(filePath, dirs.size()); + dirEntry = new DirEntry(filePath); dirsIndex.put(filePath, dirEntry); dirs.add(dirEntry); } @@ -691,7 +679,6 @@ public String uniqueDebugString(String string) { * Indirects this call to the string table. * * @param string the string whose index is required. - * * @return the offset of the string in the .debug_str section. */ public int debugStringIndex(String string) { @@ -741,4 +728,61 @@ public boolean isHubClassEntry(ClassEntry classEntry) { public ClassEntry getHubClassEntry() { return hubClassEntry; } + + private static void collectFilesAndDirs(ClassEntry classEntry) { + // track files and dirs we have already seen so that we only add them once + EconomicSet visitedFiles = EconomicSet.create(); + EconomicSet visitedDirs = EconomicSet.create(); + // add the class's file and dir + includeOnce(classEntry, classEntry.getFileEntry(), visitedFiles, visitedDirs); + // add files for fields (may differ from class file if we have a substitution) + for (FieldEntry fieldEntry : classEntry.fields) { + includeOnce(classEntry, fieldEntry.getFileEntry(), visitedFiles, visitedDirs); + } + // add files for declared methods (may differ from class file if we have a substitution) + for (MethodEntry methodEntry : classEntry.getMethods()) { + includeOnce(classEntry, methodEntry.getFileEntry(), visitedFiles, visitedDirs); + } + // add files for top level compiled and inline methods + classEntry.compiledEntries().forEachOrdered(compiledMethodEntry -> { + includeOnce(classEntry, compiledMethodEntry.getPrimary().getFileEntry(), visitedFiles, visitedDirs); + // we need files for leaf ranges and for inline caller ranges + // + // add leaf range files first because they get searched for linearly + // during line info processing + compiledMethodEntry.leafRangeIterator().forEachRemaining(subRange -> { + includeOnce(classEntry, subRange.getFileEntry(), visitedFiles, visitedDirs); + }); + // now the non-leaf range files + compiledMethodEntry.topDownRangeIterator().forEachRemaining(subRange -> { + if (!subRange.isLeaf()) { + includeOnce(classEntry, subRange.getFileEntry(), visitedFiles, visitedDirs); + } + }); + }); + // now all files and dirs are known build an index for them + classEntry.buildFileAndDirIndexes(); + } + + /** + * Ensure the supplied file entry and associated directory entry are included, but only once, in + * a class entry's file and dir list. + * + * @param classEntry the class entry whose file and dir list may need to be updated + * @param fileEntry a file entry which may need to be added to the class entry's file list or + * whose dir may need adding to the class entry's dir list + * @param visitedFiles a set tracking current file list entries, updated if a file is added + * @param visitedDirs a set tracking current dir list entries, updated if a dir is added + */ + private static void includeOnce(ClassEntry classEntry, FileEntry fileEntry, EconomicSet visitedFiles, EconomicSet visitedDirs) { + if (fileEntry != null && !visitedFiles.contains(fileEntry)) { + visitedFiles.add(fileEntry); + classEntry.includeFile(fileEntry); + DirEntry dirEntry = fileEntry.getDirEntry(); + if (dirEntry != null && !dirEntry.getPathString().isEmpty() && !visitedDirs.contains(dirEntry)) { + visitedDirs.add(dirEntry); + classEntry.includeDir(dirEntry); + } + } + } } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DirEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DirEntry.java index ddfbf7da5bcd..619e37d26cba 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DirEntry.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DirEntry.java @@ -37,11 +37,9 @@ */ public class DirEntry { private final Path path; - private final int idx; - public DirEntry(Path path, int idx) { + public DirEntry(Path path) { this.path = path; - this.idx = idx; } public Path getPath() { @@ -51,14 +49,4 @@ public Path getPath() { public String getPathString() { return path.toString(); } - - /** - * Retrieve the index of the dir entry in the list of all known dirs. - * - * @return the index of the file entry. - */ - public int getIdx() { - return idx; - } - } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/FileEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/FileEntry.java index d14ccf92af4d..3ae8f0a4832e 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/FileEntry.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/FileEntry.java @@ -32,12 +32,10 @@ public class FileEntry { private final String fileName; private final DirEntry dirEntry; - private final int idx; - public FileEntry(String fileName, DirEntry dirEntry, int idx) { + public FileEntry(String fileName, DirEntry dirEntry) { this.fileName = fileName; this.dirEntry = dirEntry; - this.idx = idx; } /** @@ -47,15 +45,6 @@ public String getFileName() { return fileName; } - /** - * Retrieve the index of the file entry in the list of all known files. - * - * @return the index of the file entry. - */ - public int getIdx() { - return idx; - } - public String getPathName() { @SuppressWarnings("hiding") DirEntry dirEntry = getDirEntry(); diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/MemberEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/MemberEntry.java index 603e1ddee1f6..48a5bf55ffca 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/MemberEntry.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/MemberEntry.java @@ -82,7 +82,12 @@ public FileEntry getFileEntry() { } public int getFileIdx() { - return fileEntry.getIdx(); + if (ownerType instanceof ClassEntry) { + return ((ClassEntry) ownerType).getFileIdx(fileEntry); + } + // should not be asking for a file for header fields + assert false : "not expecting a file lookup for header fields"; + return 1; } public int getLine() { diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/range/Range.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/range/Range.java index 2cfe82d53193..e0a0360a6177 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/range/Range.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/range/Range.java @@ -211,10 +211,6 @@ public FileEntry getFileEntry() { return methodEntry.getFileEntry(); } - public int getFileIndex() { - return getFileEntry().getIdx(); - } - public int getModifiers() { return methodEntry.getModifiers(); } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/ELFObjectFile.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/ELFObjectFile.java index 9f6c76ae1f77..c5a4fc36f731 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/ELFObjectFile.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/ELFObjectFile.java @@ -53,6 +53,7 @@ import com.oracle.objectfile.elf.dwarf.DwarfFrameSectionImpl; import com.oracle.objectfile.elf.dwarf.DwarfInfoSectionImpl; import com.oracle.objectfile.elf.dwarf.DwarfLineSectionImpl; +import com.oracle.objectfile.elf.dwarf.DwarfRangesSectionImpl; import com.oracle.objectfile.elf.dwarf.DwarfStrSectionImpl; import com.oracle.objectfile.io.AssemblyBuffer; import com.oracle.objectfile.io.OutputAssembler; @@ -1180,6 +1181,7 @@ public void installDebugInfo(DebugInfoProvider debugInfoProvider) { DwarfLocSectionImpl elfLocSectionImpl = dwarfSections.getLocSectionImpl(); DwarfInfoSectionImpl elfInfoSectionImpl = dwarfSections.getInfoSectionImpl(); DwarfARangesSectionImpl elfARangesSectionImpl = dwarfSections.getARangesSectionImpl(); + DwarfRangesSectionImpl elfRangesSectionImpl = dwarfSections.getRangesSectionImpl(); DwarfLineSectionImpl elfLineSectionImpl = dwarfSections.getLineSectionImpl(); /* Now we can create the section elements with empty content. */ newUserDefinedSection(elfStrSectionImpl.getSectionName(), elfStrSectionImpl); @@ -1188,6 +1190,7 @@ public void installDebugInfo(DebugInfoProvider debugInfoProvider) { newUserDefinedSection(elfLocSectionImpl.getSectionName(), elfLocSectionImpl); newUserDefinedSection(elfInfoSectionImpl.getSectionName(), elfInfoSectionImpl); newUserDefinedSection(elfARangesSectionImpl.getSectionName(), elfARangesSectionImpl); + newUserDefinedSection(elfRangesSectionImpl.getSectionName(), elfRangesSectionImpl); newUserDefinedSection(elfLineSectionImpl.getSectionName(), elfLineSectionImpl); /* * Add symbols for the base of all DWARF sections whose content may need to be referenced @@ -1199,6 +1202,7 @@ public void installDebugInfo(DebugInfoProvider debugInfoProvider) { createDefinedSymbol(elfInfoSectionImpl.getSectionName(), elfInfoSectionImpl.getElement(), 0, 0, false, false); createDefinedSymbol(elfLineSectionImpl.getSectionName(), elfLineSectionImpl.getElement(), 0, 0, false, false); createDefinedSymbol(elfStrSectionImpl.getSectionName(), elfStrSectionImpl.getElement(), 0, 0, false, false); + createDefinedSymbol(elfRangesSectionImpl.getSectionName(), elfRangesSectionImpl.getElement(), 0, 0, false, false); createDefinedSymbol(elfLocSectionImpl.getSectionName(), elfLocSectionImpl.getElement(), 0, 0, false, false); /* * The byte[] for each implementation's content are created and written under @@ -1215,6 +1219,7 @@ public void installDebugInfo(DebugInfoProvider debugInfoProvider) { elfInfoSectionImpl.getOrCreateRelocationElement(0); elfLocSectionImpl.getOrCreateRelocationElement(0); elfARangesSectionImpl.getOrCreateRelocationElement(0); + elfRangesSectionImpl.getOrCreateRelocationElement(0); elfLineSectionImpl.getOrCreateRelocationElement(0); /* Ok now we can populate the debug info model. */ dwarfSections.installDebugInfo(debugInfoProvider); diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfARangesSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfARangesSectionImpl.java index f84c4083bb9f..25198d29b318 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfARangesSectionImpl.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfARangesSectionImpl.java @@ -28,6 +28,7 @@ import java.util.Map; +import com.oracle.objectfile.debugentry.ClassEntry; import org.graalvm.compiler.debug.DebugContext; import com.oracle.objectfile.LayoutDecision; @@ -56,7 +57,7 @@ public String getSectionName() { @Override public void createContent() { /* - * We need a single entry for the Java compilation unit + * We need an entry for each compilation unit that has compiled methods * *
    * @@ -92,8 +93,11 @@ public void createContent() { * Where N is the number of compiled methods. */ assert !contentByteArrayCreated(); - int methodCount = compiledMethodsCount(); - byte[] buffer = new byte[entrySize(methodCount)]; + Cursor byteCount = new Cursor(); + instanceClassStream().filter(ClassEntry::hasCompiledEntries).forEachOrdered(classEntry -> { + byteCount.add(entrySize(classEntry.compiledEntryCount())); + }); + byte[] buffer = new byte[byteCount.get()]; super.setContent(buffer); } @@ -137,15 +141,18 @@ public void writeContent(DebugContext context) { enableLog(context, cursor.get()); log(context, " [0x%08x] DEBUG_ARANGES", cursor.get()); - int lengthPos = cursor.get(); - cursor.set(writeHeader(0, buffer, cursor.get())); - compiledMethodsStream().forEach(compiledMethodEntry -> { - cursor.set(writeARange(context, compiledMethodEntry, buffer, cursor.get())); + instanceClassStream().filter(ClassEntry::hasCompiledEntries).forEachOrdered(classEntry -> { + int lengthPos = cursor.get(); + log(context, " [0x%08x] class %s CU 0x%x", lengthPos, classEntry.getTypeName(), getCUIndex(classEntry)); + cursor.set(writeHeader(getCUIndex(classEntry), buffer, cursor.get())); + classEntry.compiledEntries().forEachOrdered(compiledMethodEntry -> { + cursor.set(writeARange(context, compiledMethodEntry, buffer, cursor.get())); + }); + // write two terminating zeroes + cursor.set(writeLong(0, buffer, cursor.get())); + cursor.set(writeLong(0, buffer, cursor.get())); + patchLength(lengthPos, buffer, cursor.get()); }); - // write two terminating zeroes - cursor.set(writeLong(0, buffer, cursor.get())); - cursor.set(writeLong(0, buffer, cursor.get())); - patchLength(lengthPos, buffer, cursor.get()); assert cursor.get() == size; } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfAbbrevSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfAbbrevSectionImpl.java index 14bc1ef8246c..4a2c4c971188 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfAbbrevSectionImpl.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfAbbrevSectionImpl.java @@ -76,8 +76,19 @@ * *
* - * For the moment we only use one abbrev table and one CU. It employs the following top level and - * nested DIES + * For the moment we only use one abbrev table with two types of CU. There is one occurrence of the + * builtin_unit CU which includes definitions of Java primitive value types and the + * struct type used to model a Java object header. There are multiple occurrences of the + * class_unit CU, one for each Java class, interface or array class included in the + * generated native image binary. The latter describes the class, array or interface layout and + * defines a class, interface or array reference pointer type. It provides declarations for instance + * and static methods and static fields of a class, and methods of an interface. In the case of a + * class it may also include definitions of static fields (i.e. location info) and for a class or + * interface definitions of compiled methods (i.e. code address locations). The latter may include + * details of inlined method frames and top level or inlined parameter or local variable locations. + *

+ * + * These two CUs include the following top level and nested DIES *

* * Level 0 DIEs @@ -86,8 +97,11 @@ * *

  • code = null, tag == null - empty terminator * - *
  • code = class_unit, tag == compile_unit - CU that defines the Java object header - * struct, all Java primitive and object types and all Java compiled code. + *
  • code = builtin_unit, tag == class_unit - CU that defines the Java object header + * struct and all Java primitive types. + * + *
  • code = class_unit, tag == class_unit - CU that defines a specific Java object, + * interface or array type. * * * @@ -95,12 +109,13 @@ * *
      * - *
    • code = primitive_type, tag == base_type, parent = class_unit - Java primitive + *
    • code = primitive_type, tag == base_type, parent = builtin_unit - Java primitive * type (non-void) * - *
    • code = void_type, tag == unspecified_type, parent = class_unit - Java void type + *
    • code = void_type, tag == unspecified_type, parent = builtin_unit - Java void + * type * - *
    • code = object_header, tag == structure_type, parent = class_unit - Java object + *
    • code = object_header, tag == structure_type, parent = builtin_unit - Java object * header * *
    • code = class_layout, tag == class_type, parent = class_unit - Java instance type @@ -115,10 +130,10 @@ *
    • code = static_field_location, tag == variable, parent = class_unit - Java static * field definition (i.e. location of data) * - *
    • code = array_layout, tag == structure_type, parent = array_unit - Java array + *
    • code = array_layout, tag == structure_type, parent = class_unit - Java array * type structure definition * - *
    • code = array_pointer, tag == pointer_type, parent = array_unit - Java array ref + *
    • code = array_pointer, tag == pointer_type, parent = class_unit - Java array ref * type * *
    • code = interface_layout, tag == union_type, parent = class_unit - Java array @@ -127,25 +142,23 @@ *
    • code = interface_pointer, tag == pointer_type, parent = class_unit - Java * interface ref type * - *
    • code = indirect_layout, tag == class_type, parent = class_unit, array_unit, - * interface_unit - wrapper layout attaches address rewriting logic to the layout types that - * it wraps using a data_location attribute - * - *
    • code = indirect_pointer, tag == pointer_type, parent = class_unit, array_unit, - * interface_unit - indirect ref type used to type indirect oops that encode the address of - * an object, whether by adding tag bits or representing the address as an offset from some base - * address. these are used to type object references stored in static and instance fields. They are - * not needed when typing local vars and parameters held in registers or on the stack as they appear - * as raw addresses. - * - *
    • code = namespace, tag == namespace, parent = class_unit, array_unit, - * interface_unit - a wrap-around DIE that is used to embed all the normal level 1 DIEs of a - * class_unit or array_unit in a namespace. This is needed when the - * corresponding class/interface or array base element type have been loaded by a loader with a - * non-empty loader in order to ensure that mangled names for the class and its members can - * legitimately employ the loader id as a namespace prefix. Note that use of a namespace wrapper DIE - * causes all the embedded level 1+ DIEs documented above and all their children to be generated at - * a level one greater than documented here. + *
    • code = indirect_layout, tag == class_type, parent = class_unit - wrapper layout + * attaches address rewriting logic to the layout types that it wraps using a + * data_location attribute + * + *
    • code = indirect_pointer, tag == pointer_type, parent = class_unit - indirect ref + * type used to type indirect oops that encode the address of an object, whether by adding tag bits + * or representing the address as an offset from some base address. these are used to type object + * references stored in static and instance fields. They are not needed when typing local vars and + * parameters held in registers or on the stack as they appear as raw addresses. + * + *
    • code = namespace, tag == namespace, parent = class_unit - a wrap-around DIE that + * is used to embed all the normal level 1 DIEs of a class_unit in a namespace. This is + * needed when the corresponding class/interface or array base element type have been loaded by a + * loader with a non-empty loader in order to ensure that mangled names for the class and its + * members can legitimately employ the loader id as a namespace prefix. Note that use of a namespace + * wrapper DIE causes all the embedded level 1+ DIEs documented above and all their children to be + * generated at a level one greater than documented here. * *
    * @@ -156,7 +169,7 @@ *
  • code = header_field, tag == member, parent = object_header - object/array header * field * - *
  • code == method_declaration1/2, tag == subprogram, parent = class_layout + *
  • code == method_declaration1/2, tag == subprogram, parent = class_layout, interface_layout * *
  • code = field_declaration1/2/3/4, tag == member, parent = class_layout - instance * field declaration (i.e. specification of properties) @@ -192,12 +205,28 @@ * Detailed layouts of the DIEs listed above are as follows: *

    * - * A single instance of the level 0 class_unit compile unit provides details of the - * object header struct, all Java primitive and object types and all Java compiled code. + * A single instance of the level 0 builtin_unit compile unit provide details of all + * Java primitive types and the struct type used to model a Java object header + * + *

      + * + *
    • abbrev_code == built_unit, tag == DW_TAG_compilation_unit, + * has_children + * + *
    • DW_AT_language : ... DW_FORM_data1 + * + *
    • DW_AT_name : ....... DW_FORM_strp + * + *
    • DW_AT_use_UTF8 : ... DW_FORM_flag + * + *
    + * + * Instances of the level 0 class_unit compile unit provide details of all Java object + * types and compiled code. * *
      * - *
    • abbrev_code == class_unit, tag == DW_TAG_compilation_unit, + *
    • abbrev_code == class_unit1/2, tag == DW_TAG_compilation_unit, * has_children * *
    • DW_AT_language : ... DW_FORM_data1 @@ -206,9 +235,9 @@ * *
    • DW_AT_comp_dir : ... DW_FORM_strp * - *
    • DW_AT_low_pc : ..... DW_FORM_address + *
    • DW_AT_low_pc : ..... DW_FORM_address only for class_unit1 * - *
    • DW_AT_hi_pc : ...... DW_FORM_address + *
    • DW_AT_hi_pc : ...... DW_FORM_address only for class_unit1 * *
    • DW_AT_use_UTF8 : ... DW_FORM_flag * @@ -216,9 +245,6 @@ * *
    * - * All other Java derived DIEs are embedded within this top level CU. - *

    - * * Primitive Types: For each non-void Java primitive type there is a level 1 DIE defining a base * type * @@ -362,12 +388,12 @@ * *

  • DW_AT_declaration : ....... DW_FORM_flag * - *
  • Dw_AT_object_pointer : .... DW_FORM_ref_addr n.b. only for method_declaration1, + *
  • Dw_AT_object_pointer : .... DW_FORM_ref4 n.b. only for method_declaration1, * points to param 0 DIE * *
  • DW_AT_virtuality : ........ DW_FORM_data1 (for override methods) * - *
  • DW_AT_containing_type : ... DW_FORM_ref_addr (for override methods) + *
  • DW_AT_containing_type : ... DW_FORM_ref4 (for override methods) * * * @@ -493,7 +519,7 @@ * *
  • Dw_AT_byte_size : ... DW_FORM_data1 * - *
  • Dw_AT_type : ........ DW_FORM_ref_addr + *
  • Dw_AT_type : ........ DW_FORM_ref4 * * * @@ -503,7 +529,7 @@ * *
  • Dw_AT_byte_size : ... DW_FORM_data1 * - *
  • Dw_AT_type : ........ DW_FORM_ref_addr + *
  • Dw_AT_type : ........ DW_FORM_ref4 * * * @@ -543,7 +569,7 @@ * *
  • DW_AT_external : ........ DW_FORM_flag * - *
  • DW_AT_specification : ... DW_FORM_ref_addr + *
  • DW_AT_specification : ... DW_FORM_ref4 * * * @@ -562,13 +588,42 @@ *
  • abbrev_code == DW_ABBREV_CODE_method_local_location1/2, tag == * DW_TAG_formal_parameter, no_children * - *
  • DW_AT_specification : .......... DW_FORM_ref_addr + *
  • DW_AT_specification : .......... DW_FORM_ref4 * *
  • DW_AT_location: ................ DW_FORM_sec_offset n.b. only for * method_local_location2 * * * + * Abstract Inline Methods: For any method m' which has been inlined into a top level compiled + * method m there will be an abstract_inline_method DIE for m' at level 1 DIE in the CU to which m + * belongs. The declaration serves as an abstract_origin for any corresponding inlined method DIEs + * appearing as children of m. The abstract_inline_method DIE will inherit attributes from the + * method_definition DIE referenced as its specification attribute without the need to repeat them, + * including attributes specified in child DIEs of the method_definition. However, it is actually + * necessary to replicate the method_parameter/local_declaration DIEs of the specification as + * children of the abstract_inline_method DIE. This provides a CU-local target for references from + * the corresponding method_parameter/local_location DIEs that sit below the inlined_subroutine DIEs + * in the concrete inlined subroutine tree. This is needed because some tools require the location + * DIEs abstract_origin attribute that links the location to specification to be a CU-relative + * offset (FORM_Ref4) rather than a relcoatabel cross-CU info section offset. This has the added + * benefit that repeated reference to abstract inline methods or parameters from concrete inline + * method DIEs or parameter or local location DIES only avoids the cost of tracking separate + * relocatable reference. + * + *
      + * + *
    • abbrev_code == DW_ABBREV_CODE_abstract_inline_method, tag == DW_TAG_subprogram, + * has_children + * + *
    • DW_AT_inline : .......... DW_FORM_data1 + * + *
    • DW_AT_external : ........ DW_FORM_flag + * + *
    • DW_AT_specification : ... DW_FORM_ref_addr + * + *
    + * * Concrete Inlined Methods: Concrete inlined method DIEs are nested as a tree of children under the * method_location DIE for the method into which they have been inlined. Each inlined method DIE * defines an address range that is a subrange of its parent DIE. A method_location DIE occurs at @@ -592,7 +647,7 @@ *
  • abbrev_code == DW_ABBREV_CODE_inlined_subroutine_with_children, tag == * DW_TAG_subprogram, has_children * - *
  • DW_AT_abstract_origin : ... DW_FORM_ref_addr + *
  • DW_AT_abstract_origin : ... DW_FORM_ref4 * *
  • DW_AT_low_pc : ............ DW_FORM_addr * @@ -612,7 +667,7 @@ *
  • abbrev_code == static_field_location, tag == DW_TAG_variable, * no_children * - *
  • DW_AT_specification : ... DW_FORM_ref_addr + *
  • DW_AT_specification : ... DW_FORM_ref4 * *
  • DW_AT_linkage_name : .... DW_FORM_strp * @@ -671,7 +726,7 @@ * *
  • Dw_AT_byte_size : ... DW_FORM_data1 * - *
  • Dw_AT_type : ........ DW_FORM_ref_addr + *
  • Dw_AT_type : ........ DW_FORM_ref4 * * * @@ -739,7 +794,7 @@ * *
  • Dw_AT_byte_size : ... DW_FORM_data1 * - *
  • DW_AT_TYPE : ....... DW_FORM_ref_addr + *
  • DW_AT_TYPE : ....... DW_FORM_ref4 * * * @@ -812,7 +867,7 @@ public void writeContent(DebugContext context) { public int writeAbbrevs(DebugContext context, byte[] buffer, int p) { int pos = p; - pos = writeClassUnitAbbrev(context, buffer, pos); + pos = writeCompileUnitAbbrevs(context, buffer, pos); pos = writePrimitiveTypeAbbrev(context, buffer, pos); pos = writeVoidTypeAbbrev(context, buffer, pos); @@ -840,6 +895,7 @@ public int writeAbbrevs(DebugContext context, byte[] buffer, int p) { pos = writeArrayDataTypeAbbrevs(context, buffer, pos); pos = writeArraySubrangeTypeAbbrev(context, buffer, pos); pos = writeMethodLocationAbbrev(context, buffer, pos); + pos = writeAbstractInlineMethodAbbrev(context, buffer, pos); pos = writeStaticFieldLocationAbbrev(context, buffer, pos); pos = writeSuperReferenceAbbrev(context, buffer, pos); pos = writeInterfaceImplementorAbbrev(context, buffer, pos); @@ -880,9 +936,17 @@ private int writeAttrForm(long code, byte[] buffer, int pos) { return writeSLEB(code, buffer, pos); } - private int writeClassUnitAbbrev(@SuppressWarnings("unused") DebugContext context, byte[] buffer, int p) { + private int writeCompileUnitAbbrevs(@SuppressWarnings("unused") DebugContext context, byte[] buffer, int p) { + int pos = p; + pos = writeCompileUnitAbbrev(context, DwarfDebugInfo.DW_ABBREV_CODE_builtin_unit, buffer, pos); + pos = writeCompileUnitAbbrev(context, DwarfDebugInfo.DW_ABBREV_CODE_class_unit1, buffer, pos); + pos = writeCompileUnitAbbrev(context, DwarfDebugInfo.DW_ABBREV_CODE_class_unit2, buffer, pos); + return pos; + } + + private int writeCompileUnitAbbrev(@SuppressWarnings("unused") DebugContext context, int abbrevCode, byte[] buffer, int p) { int pos = p; - pos = writeAbbrevCode(DwarfDebugInfo.DW_ABBREV_CODE_class_unit, buffer, pos); + pos = writeAbbrevCode(abbrevCode, buffer, pos); pos = writeTag(DwarfDebugInfo.DW_TAG_compile_unit, buffer, pos); pos = writeFlag(DwarfDebugInfo.DW_CHILDREN_yes, buffer, pos); pos = writeAttrType(DwarfDebugInfo.DW_AT_language, buffer, pos); @@ -893,12 +957,14 @@ private int writeClassUnitAbbrev(@SuppressWarnings("unused") DebugContext contex pos = writeAttrForm(DwarfDebugInfo.DW_FORM_strp, buffer, pos); pos = writeAttrType(DwarfDebugInfo.DW_AT_comp_dir, buffer, pos); pos = writeAttrForm(DwarfDebugInfo.DW_FORM_strp, buffer, pos); - pos = writeAttrType(DwarfDebugInfo.DW_AT_low_pc, buffer, pos); - pos = writeAttrForm(DwarfDebugInfo.DW_FORM_addr, buffer, pos); - pos = writeAttrType(DwarfDebugInfo.DW_AT_hi_pc, buffer, pos); - pos = writeAttrForm(DwarfDebugInfo.DW_FORM_addr, buffer, pos); - pos = writeAttrType(DwarfDebugInfo.DW_AT_stmt_list, buffer, pos); - pos = writeAttrForm(DwarfDebugInfo.DW_FORM_sec_offset, buffer, pos); + if (abbrevCode == DwarfDebugInfo.DW_ABBREV_CODE_class_unit2) { + pos = writeAttrType(DwarfDebugInfo.DW_AT_ranges, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_sec_offset, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_low_pc, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_addr, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_stmt_list, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_sec_offset, buffer, pos); + } /* * Now terminate. */ @@ -1024,7 +1090,7 @@ private int writeClassReferenceAbbrev(@SuppressWarnings("unused") DebugContext c pos = writeAttrType(DwarfDebugInfo.DW_AT_byte_size, buffer, pos); pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data1, buffer, pos); pos = writeAttrType(DwarfDebugInfo.DW_AT_type, buffer, pos); - pos = writeAttrForm(DwarfDebugInfo.DW_FORM_ref_addr, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_ref4, buffer, pos); /* * Now terminate. */ @@ -1067,10 +1133,10 @@ private int writeMethodDeclarationAbbrev(@SuppressWarnings("unused") DebugContex // pos = writeAttrType(DwarfDebugInfo.DW_AT_virtuality, buffer, pos); // pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data1, buffer, pos); pos = writeAttrType(DwarfDebugInfo.DW_AT_containing_type, buffer, pos); - pos = writeAttrForm(DwarfDebugInfo.DW_FORM_ref_addr, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_ref4, buffer, pos); if (abbrevCode == DwarfDebugInfo.DW_ABBREV_CODE_method_declaration) { pos = writeAttrType(DwarfDebugInfo.DW_AT_object_pointer, buffer, pos); - pos = writeAttrForm(DwarfDebugInfo.DW_FORM_ref_addr, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_ref4, buffer, pos); } /* * Now terminate. @@ -1185,7 +1251,7 @@ private int writeArrayReferenceAbbrev(@SuppressWarnings("unused") DebugContext c pos = writeAttrType(DwarfDebugInfo.DW_AT_byte_size, buffer, pos); pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data1, buffer, pos); pos = writeAttrType(DwarfDebugInfo.DW_AT_type, buffer, pos); - pos = writeAttrForm(DwarfDebugInfo.DW_FORM_ref_addr, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_ref4, buffer, pos); /* * Now terminate. */ @@ -1219,7 +1285,7 @@ private int writeInterfaceReferenceAbbrev(@SuppressWarnings("unused") DebugConte pos = writeAttrType(DwarfDebugInfo.DW_AT_byte_size, buffer, pos); pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data1, buffer, pos); pos = writeAttrType(DwarfDebugInfo.DW_AT_type, buffer, pos); - pos = writeAttrForm(DwarfDebugInfo.DW_FORM_ref_addr, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_ref4, buffer, pos); /* * Now terminate. */ @@ -1257,6 +1323,9 @@ private int writeForeignReferenceAbbrev(@SuppressWarnings("unused") DebugContext pos = writeFlag(DwarfDebugInfo.DW_CHILDREN_no, buffer, pos); pos = writeAttrType(DwarfDebugInfo.DW_AT_byte_size, buffer, pos); pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data1, buffer, pos); + // n.b we use a (relocatable) ref_addr here rather than a (CU-relative) ref4 + // because an unknown foreign pointer type will reference void which is not + // local to the current CU. pos = writeAttrType(DwarfDebugInfo.DW_AT_type, buffer, pos); pos = writeAttrForm(DwarfDebugInfo.DW_FORM_ref_addr, buffer, pos); /* @@ -1277,7 +1346,7 @@ private int writeForeignTypedefAbbrev(@SuppressWarnings("unused") DebugContext c pos = writeAttrType(DwarfDebugInfo.DW_AT_name, buffer, pos); pos = writeAttrForm(DwarfDebugInfo.DW_FORM_strp, buffer, pos); pos = writeAttrType(DwarfDebugInfo.DW_AT_type, buffer, pos); - pos = writeAttrForm(DwarfDebugInfo.DW_FORM_ref_addr, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_ref4, buffer, pos); /* * Now terminate. */ @@ -1345,6 +1414,9 @@ private int writeArrayDataTypeAbbrev(@SuppressWarnings("unused") DebugContext co pos = writeAttrType(DwarfDebugInfo.DW_AT_byte_size, buffer, pos); pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data4, buffer, pos); } + // n.b we use a (relocatable) ref_addr here rather than a (CU-relative) ref4 + // because a foreign array type can reference another foreign type which is + // not in the current CU. pos = writeAttrType(DwarfDebugInfo.DW_AT_type, buffer, pos); pos = writeAttrForm(DwarfDebugInfo.DW_FORM_ref_addr, buffer, pos); /* @@ -1384,6 +1456,25 @@ private int writeMethodLocationAbbrev(@SuppressWarnings("unused") DebugContext c pos = writeAttrType(DwarfDebugInfo.DW_AT_external, buffer, pos); pos = writeAttrForm(DwarfDebugInfo.DW_FORM_flag, buffer, pos); pos = writeAttrType(DwarfDebugInfo.DW_AT_specification, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_ref4, buffer, pos); + /* + * Now terminate. + */ + pos = writeAttrType(DwarfDebugInfo.DW_AT_null, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_null, buffer, pos); + return pos; + } + + private int writeAbstractInlineMethodAbbrev(@SuppressWarnings("unused") DebugContext context, byte[] buffer, int p) { + int pos = p; + pos = writeAbbrevCode(DwarfDebugInfo.DW_ABBREV_CODE_abstract_inline_method, buffer, pos); + pos = writeTag(DwarfDebugInfo.DW_TAG_subprogram, buffer, pos); + pos = writeFlag(DwarfDebugInfo.DW_CHILDREN_yes, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_inline, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data1, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_external, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_flag, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_specification, buffer, pos); pos = writeAttrForm(DwarfDebugInfo.DW_FORM_ref_addr, buffer, pos); /* * Now terminate. @@ -1473,7 +1564,7 @@ private int writeIndirectReferenceAbbrev(@SuppressWarnings("unused") DebugContex pos = writeAttrType(DwarfDebugInfo.DW_AT_byte_size, buffer, pos); pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data1, buffer, pos); pos = writeAttrType(DwarfDebugInfo.DW_AT_type, buffer, pos); - pos = writeAttrForm(DwarfDebugInfo.DW_FORM_ref_addr, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_ref4, buffer, pos); /* * Now terminate. */ @@ -1634,9 +1725,9 @@ private int writeInlinedSubroutineAbbrev(byte[] buffer, int p, boolean withChild } /** - * The debug_abbrev section depends on debug_aranges section. + * The debug_abbrev section depends on debug_ranges section. */ - private static final String TARGET_SECTION_NAME = DwarfDebugInfo.DW_ARANGES_SECTION_NAME; + private static final String TARGET_SECTION_NAME = DwarfDebugInfo.DW_RANGES_SECTION_NAME; @Override public String targetSectionName() { diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfDebugInfo.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfDebugInfo.java index 8683d0328579..05afa09b251f 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfDebugInfo.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfDebugInfo.java @@ -58,6 +58,7 @@ public class DwarfDebugInfo extends DebugInfoBase { public static final String DW_INFO_SECTION_NAME = ".debug_info"; public static final String DW_LOC_SECTION_NAME = ".debug_loc"; public static final String DW_ARANGES_SECTION_NAME = ".debug_aranges"; + public static final String DW_RANGES_SECTION_NAME = ".debug_ranges"; /** * Currently generated debug info relies on DWARF spec version 4. @@ -70,54 +71,57 @@ public class DwarfDebugInfo extends DebugInfoBase { */ @SuppressWarnings("unused") public static final int DW_ABBREV_CODE_null = 0; /* Level 0 DIEs. */ - public static final int DW_ABBREV_CODE_class_unit = 1; + public static final int DW_ABBREV_CODE_builtin_unit = 1; + public static final int DW_ABBREV_CODE_class_unit1 = 2; + public static final int DW_ABBREV_CODE_class_unit2 = 3; /* Level 1 DIEs. */ - public static final int DW_ABBREV_CODE_primitive_type = 2; - public static final int DW_ABBREV_CODE_void_type = 3; - public static final int DW_ABBREV_CODE_object_header = 4; - public static final int DW_ABBREV_CODE_namespace = 5; - public static final int DW_ABBREV_CODE_class_layout1 = 6; - public static final int DW_ABBREV_CODE_class_layout2 = 7; - public static final int DW_ABBREV_CODE_class_pointer = 8; - public static final int DW_ABBREV_CODE_foreign_pointer = 9; - public static final int DW_ABBREV_CODE_foreign_typedef = 10; - public static final int DW_ABBREV_CODE_foreign_struct = 11; - public static final int DW_ABBREV_CODE_method_location = 12; - public static final int DW_ABBREV_CODE_static_field_location = 13; - public static final int DW_ABBREV_CODE_array_layout = 14; - public static final int DW_ABBREV_CODE_array_pointer = 15; - public static final int DW_ABBREV_CODE_interface_layout = 16; - public static final int DW_ABBREV_CODE_interface_pointer = 17; - public static final int DW_ABBREV_CODE_indirect_layout = 18; - public static final int DW_ABBREV_CODE_indirect_pointer = 19; + public static final int DW_ABBREV_CODE_primitive_type = 4; + public static final int DW_ABBREV_CODE_void_type = 5; + public static final int DW_ABBREV_CODE_object_header = 6; + public static final int DW_ABBREV_CODE_namespace = 7; + public static final int DW_ABBREV_CODE_class_layout1 = 8; + public static final int DW_ABBREV_CODE_class_layout2 = 9; + public static final int DW_ABBREV_CODE_class_pointer = 10; + public static final int DW_ABBREV_CODE_foreign_pointer = 11; + public static final int DW_ABBREV_CODE_foreign_typedef = 12; + public static final int DW_ABBREV_CODE_foreign_struct = 13; + public static final int DW_ABBREV_CODE_method_location = 14; + public static final int DW_ABBREV_CODE_static_field_location = 15; + public static final int DW_ABBREV_CODE_array_layout = 16; + public static final int DW_ABBREV_CODE_array_pointer = 17; + public static final int DW_ABBREV_CODE_interface_layout = 18; + public static final int DW_ABBREV_CODE_interface_pointer = 19; + public static final int DW_ABBREV_CODE_indirect_layout = 20; + public static final int DW_ABBREV_CODE_indirect_pointer = 21; /* Level 2 DIEs. */ - public static final int DW_ABBREV_CODE_method_declaration = 20; - public static final int DW_ABBREV_CODE_method_declaration_static = 21; - public static final int DW_ABBREV_CODE_field_declaration1 = 22; - public static final int DW_ABBREV_CODE_field_declaration2 = 23; - public static final int DW_ABBREV_CODE_field_declaration3 = 24; - public static final int DW_ABBREV_CODE_field_declaration4 = 25; - public static final int DW_ABBREV_CODE_class_constant = 26; - public static final int DW_ABBREV_CODE_header_field = 27; - public static final int DW_ABBREV_CODE_array_data_type1 = 28; - public static final int DW_ABBREV_CODE_array_data_type2 = 29; - public static final int DW_ABBREV_CODE_array_subrange = 30; - public static final int DW_ABBREV_CODE_super_reference = 31; - public static final int DW_ABBREV_CODE_interface_implementor = 32; + public static final int DW_ABBREV_CODE_method_declaration = 22; + public static final int DW_ABBREV_CODE_method_declaration_static = 23; + public static final int DW_ABBREV_CODE_field_declaration1 = 24; + public static final int DW_ABBREV_CODE_field_declaration2 = 25; + public static final int DW_ABBREV_CODE_field_declaration3 = 26; + public static final int DW_ABBREV_CODE_field_declaration4 = 27; + public static final int DW_ABBREV_CODE_class_constant = 28; + public static final int DW_ABBREV_CODE_header_field = 29; + public static final int DW_ABBREV_CODE_array_data_type1 = 30; + public static final int DW_ABBREV_CODE_array_data_type2 = 31; + public static final int DW_ABBREV_CODE_array_subrange = 32; + public static final int DW_ABBREV_CODE_super_reference = 33; + public static final int DW_ABBREV_CODE_interface_implementor = 34; /* Level 2+K DIEs (where inline depth K >= 0) */ - public static final int DW_ABBREV_CODE_inlined_subroutine = 33; - public static final int DW_ABBREV_CODE_inlined_subroutine_with_children = 34; + public static final int DW_ABBREV_CODE_inlined_subroutine = 35; + public static final int DW_ABBREV_CODE_inlined_subroutine_with_children = 36; + public static final int DW_ABBREV_CODE_abstract_inline_method = 37; /* Level 2 DIEs. */ - public static final int DW_ABBREV_CODE_method_parameter_declaration1 = 35; - public static final int DW_ABBREV_CODE_method_parameter_declaration2 = 36; - public static final int DW_ABBREV_CODE_method_parameter_declaration3 = 37; - public static final int DW_ABBREV_CODE_method_local_declaration1 = 38; - public static final int DW_ABBREV_CODE_method_local_declaration2 = 39; + public static final int DW_ABBREV_CODE_method_parameter_declaration1 = 38; + public static final int DW_ABBREV_CODE_method_parameter_declaration2 = 39; + public static final int DW_ABBREV_CODE_method_parameter_declaration3 = 40; + public static final int DW_ABBREV_CODE_method_local_declaration1 = 41; + public static final int DW_ABBREV_CODE_method_local_declaration2 = 42; /* Level 3 DIEs. */ - public static final int DW_ABBREV_CODE_method_parameter_location1 = 40; - public static final int DW_ABBREV_CODE_method_parameter_location2 = 41; - public static final int DW_ABBREV_CODE_method_local_location1 = 42; - public static final int DW_ABBREV_CODE_method_local_location2 = 43; + public static final int DW_ABBREV_CODE_method_parameter_location1 = 43; + public static final int DW_ABBREV_CODE_method_parameter_location2 = 44; + public static final int DW_ABBREV_CODE_method_local_location1 = 45; + public static final int DW_ABBREV_CODE_method_local_location2 = 46; /* * Define all the Dwarf tags we need for our DIEs. @@ -155,7 +159,7 @@ public class DwarfDebugInfo extends DebugInfoBase { public static final int DW_AT_language = 0x13; public static final int DW_AT_comp_dir = 0x1b; public static final int DW_AT_containing_type = 0x1d; - public static final int DW_AT_inline = 0x20; + @SuppressWarnings("unused") public static final int DW_AT_inline = 0x20; public static final int DW_AT_abstract_origin = 0x31; public static final int DW_AT_accessibility = 0x32; public static final int DW_AT_artificial = 0x34; @@ -173,6 +177,7 @@ public class DwarfDebugInfo extends DebugInfoBase { public static final int DW_AT_type = 0x49; public static final int DW_AT_data_location = 0x50; public static final int DW_AT_use_UTF8 = 0x53; + public static final int DW_AT_ranges = 0x55; public static final int DW_AT_call_file = 0x58; public static final int DW_AT_call_line = 0x59; public static final int DW_AT_object_pointer = 0x64; @@ -318,12 +323,12 @@ public class DwarfDebugInfo extends DebugInfoBase { * bits */ public static final String HUB_TYPE_NAME = "java.lang.Class"; - private final DwarfStrSectionImpl dwarfStrSection; private final DwarfAbbrevSectionImpl dwarfAbbrevSection; private final DwarfInfoSectionImpl dwarfInfoSection; private final DwarfLocSectionImpl dwarfLocSection; private final DwarfARangesSectionImpl dwarfARangesSection; + private final DwarfRangesSectionImpl dwarfRangesSection; private final DwarfLineSectionImpl dwarfLineSection; private final DwarfFrameSectionImpl dwarfFameSection; public final ELFMachine elfMachine; @@ -362,6 +367,7 @@ public DwarfDebugInfo(ELFMachine elfMachine, ByteOrder byteOrder) { dwarfInfoSection = new DwarfInfoSectionImpl(this); dwarfLocSection = new DwarfLocSectionImpl(this); dwarfARangesSection = new DwarfARangesSectionImpl(this); + dwarfRangesSection = new DwarfRangesSectionImpl(this); dwarfLineSection = new DwarfLineSectionImpl(this); if (elfMachine == ELFMachine.AArch64) { @@ -399,6 +405,10 @@ public DwarfARangesSectionImpl getARangesSectionImpl() { return dwarfARangesSection; } + public DwarfRangesSectionImpl getRangesSectionImpl() { + return dwarfRangesSection; + } + public DwarfLineSectionImpl getLineSectionImpl() { return dwarfLineSection; } @@ -470,6 +480,10 @@ public TypeEntry getTypeEntry() { */ static class DwarfClassProperties extends DwarfTypeProperties { + /** + * Index of the class entry's compile unit in the debug_info section. + */ + private int cuIndex; /** * Index of the class entry's class_layout DIE in the debug_info section. */ @@ -478,6 +492,18 @@ static class DwarfClassProperties extends DwarfTypeProperties { * Index of the class entry's indirect layout DIE in the debug_info section. */ private int indirectLayoutIndex; + /** + * Index of the class entry's code ranges data in the debug_ranges section. + */ + private int codeRangesIndex; + /** + * Index of the class entry's line data in the debug_line section. + */ + private int lineIndex; + /** + * Size of the class entry's prologue in the debug_line section. + */ + private int linePrologueSize; /** * Map from field names to info section index for the field declaration. */ @@ -485,8 +511,12 @@ static class DwarfClassProperties extends DwarfTypeProperties { DwarfClassProperties(StructureTypeEntry entry) { super(entry); + this.cuIndex = -1; this.layoutIndex = -1; this.indirectLayoutIndex = -1; + this.codeRangesIndex = -1; + this.lineIndex = -1; + this.linePrologueSize = -1; fieldDeclarationIndex = null; } } @@ -501,13 +531,20 @@ static class DwarfMethodProperties { private int methodDeclarationIndex; /** - * Index of info locations for params/locals belonging to a method. + * Per class map that identifies the info declarations for a top level method declaration or + * an abstract inline method declaration. + */ + private EconomicMap localPropertiesMap; + + /** + * Per class map that identifies the info declaration for an abstract inline method. */ - private DwarfLocalProperties localProperties; + private EconomicMap abstractInlineMethodIndex; DwarfMethodProperties() { methodDeclarationIndex = -1; - localProperties = null; + localPropertiesMap = null; + abstractInlineMethodIndex = null; } public int getMethodDeclarationIndex() { @@ -520,12 +557,31 @@ public void setMethodDeclarationIndex(int pos) { methodDeclarationIndex = pos; } - public DwarfLocalProperties getLocalProperties() { + public DwarfLocalProperties getLocalProperties(ClassEntry classEntry) { + if (localPropertiesMap == null) { + localPropertiesMap = EconomicMap.create(); + } + DwarfLocalProperties localProperties = localPropertiesMap.get(classEntry); if (localProperties == null) { localProperties = new DwarfLocalProperties(); + localPropertiesMap.put(classEntry, localProperties); } return localProperties; } + + public void setAbstractInlineMethodIndex(ClassEntry classEntry, int pos) { + if (abstractInlineMethodIndex == null) { + abstractInlineMethodIndex = EconomicMap.create(); + } + // replace but check it did not change + Integer val = abstractInlineMethodIndex.put(classEntry, pos); + assert val == null || val == pos; + } + + public int getAbstractInlineMethodIndex(ClassEntry classEntry) { + // should be set before we get here but an NPE will guard that + return abstractInlineMethodIndex.get(classEntry); + } } private DwarfTypeProperties addTypeProperties(TypeEntry typeEntry) { @@ -615,6 +671,22 @@ int getIndirectTypeIndex(DwarfTypeProperties typeProperties) { return typeProperties.getIndirectTypeInfoIndex(); } + public void setCUIndex(ClassEntry classEntry, int idx) { + assert idx >= 0; + DwarfClassProperties classProperties = lookupClassProperties(classEntry); + assert classProperties.getTypeEntry() == classEntry; + assert classProperties.cuIndex == -1 || classProperties.cuIndex == idx; + classProperties.cuIndex = idx; + } + + public int getCUIndex(ClassEntry classEntry) { + DwarfClassProperties classProperties; + classProperties = lookupClassProperties(classEntry); + assert classProperties.getTypeEntry() == classEntry; + assert classProperties.cuIndex >= 0; + return classProperties.cuIndex; + } + void setLayoutIndex(ClassEntry classEntry, int idx) { assert idx >= 0 || idx == -1; DwarfClassProperties classProperties = lookupClassProperties(classEntry); @@ -659,6 +731,54 @@ int getIndirectLayoutIndex(ClassEntry classEntry) { return classProperties.indirectLayoutIndex; } + public void setCodeRangesIndex(ClassEntry classEntry, int idx) { + assert idx >= 0; + DwarfClassProperties classProperties = lookupClassProperties(classEntry); + assert classProperties.getTypeEntry() == classEntry; + assert classProperties.codeRangesIndex == -1 || classProperties.codeRangesIndex == idx; + classProperties.codeRangesIndex = idx; + } + + public int getCodeRangesIndex(ClassEntry classEntry) { + DwarfClassProperties classProperties; + classProperties = lookupClassProperties(classEntry); + assert classProperties.getTypeEntry() == classEntry; + assert classProperties.codeRangesIndex >= 0; + return classProperties.codeRangesIndex; + } + + public void setLineIndex(ClassEntry classEntry, int idx) { + assert idx >= 0; + DwarfClassProperties classProperties = lookupClassProperties(classEntry); + assert classProperties.getTypeEntry() == classEntry; + assert classProperties.lineIndex == -1 || classProperties.lineIndex == idx; + classProperties.lineIndex = idx; + } + + public int getLineIndex(ClassEntry classEntry) { + DwarfClassProperties classProperties; + classProperties = lookupClassProperties(classEntry); + assert classProperties.getTypeEntry() == classEntry; + assert classProperties.lineIndex >= 0; + return classProperties.lineIndex; + } + + public void setLinePrologueSize(ClassEntry classEntry, int size) { + assert size >= 0; + DwarfClassProperties classProperties = lookupClassProperties(classEntry); + assert classProperties.getTypeEntry() == classEntry; + assert classProperties.linePrologueSize == -1 || classProperties.linePrologueSize == size; + classProperties.linePrologueSize = size; + } + + public int getLinePrologueSize(ClassEntry classEntry) { + DwarfClassProperties classProperties; + classProperties = lookupClassProperties(classEntry); + assert classProperties.getTypeEntry() == classEntry; + assert classProperties.linePrologueSize >= 0; + return classProperties.linePrologueSize; + } + public void setFieldDeclarationIndex(StructureTypeEntry entry, String fieldName, int pos) { DwarfClassProperties classProperties; classProperties = lookupClassProperties(entry); @@ -719,23 +839,31 @@ void setIndex(DebugLocalInfo localInfo, int index) { } } + public void setAbstractInlineMethodIndex(ClassEntry classEntry, MethodEntry methodEntry, int pos) { + lookupMethodProperties(methodEntry).setAbstractInlineMethodIndex(classEntry, pos); + } + + public int getAbstractInlineMethodIndex(ClassEntry classEntry, MethodEntry methodEntry) { + return lookupMethodProperties(methodEntry).getAbstractInlineMethodIndex(classEntry); + } + private DwarfLocalProperties addRangeLocalProperties(Range range) { DwarfLocalProperties localProperties = new DwarfLocalProperties(); rangeLocalPropertiesIndex.put(range, localProperties); return localProperties; } - public DwarfLocalProperties lookupLocalProperties(MethodEntry methodEntry) { - return lookupMethodProperties(methodEntry).getLocalProperties(); + public DwarfLocalProperties lookupLocalProperties(ClassEntry classEntry, MethodEntry methodEntry) { + return lookupMethodProperties(methodEntry).getLocalProperties(classEntry); } - public void setMethodLocalIndex(MethodEntry methodEntry, DebugLocalInfo localInfo, int index) { - DwarfLocalProperties localProperties = lookupLocalProperties(methodEntry); + public void setMethodLocalIndex(ClassEntry classEntry, MethodEntry methodEntry, DebugLocalInfo localInfo, int index) { + DwarfLocalProperties localProperties = lookupLocalProperties(classEntry, methodEntry); localProperties.setIndex(localInfo, index); } - public int getMethodLocalIndex(MethodEntry methodEntry, DebugLocalInfo localInfo) { - DwarfLocalProperties localProperties = lookupLocalProperties(methodEntry); + public int getMethodLocalIndex(ClassEntry classEntry, MethodEntry methodEntry, DebugLocalInfo localInfo) { + DwarfLocalProperties localProperties = lookupLocalProperties(classEntry, methodEntry); assert localProperties != null : "get of non-existent local index"; int index = localProperties.getIndex(localInfo); assert index >= 0 : "get of local index before it was set"; diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImpl.java index e44bd1c2b48a..fda78c22feb4 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImpl.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImpl.java @@ -57,7 +57,8 @@ public void createContent() { int pos = 0; /* - * The frame section contains one CIE at offset 0 followed by an FIE for each method. + * The frame section contains one CIE at offset 0 followed by an FIE for each compiled + * method. */ pos = writeCIE(null, pos); pos = writeMethodFrames(null, pos); diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfInfoSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfInfoSectionImpl.java index 53e2cc007716..e49ba150e213 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfInfoSectionImpl.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfInfoSectionImpl.java @@ -33,6 +33,7 @@ import java.util.List; import java.util.stream.Stream; +import org.graalvm.collections.EconomicSet; import org.graalvm.compiler.debug.DebugContext; import com.oracle.objectfile.LayoutDecision; @@ -79,11 +80,14 @@ public class DwarfInfoSectionImpl extends DwarfSectionImpl { * it up front. */ private int voidOffset; + private int cuStart; public DwarfInfoSectionImpl(DwarfDebugInfo dwarfSections) { super(dwarfSections); // initialize to an invalid value voidOffset = -1; + // initialize CU start to an invalid value + cuStart = -1; } @Override @@ -146,75 +150,45 @@ byte computeEncoding(int flags, int bitCount) { public int generateContent(DebugContext context, byte[] buffer) { int pos = 0; - // Write the single Java compile unit header - int lengthPos = pos; - log(context, " [0x%08x] <0> Java Compile Unit", pos); - pos = writeCUHeader(buffer, pos); - assert pos == lengthPos + DW_DIE_HEADER_SIZE; - int abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_class_unit; - log(context, " [0x%08x] <0> Abbrev Number %d", pos, abbrevCode); - pos = writeAbbrevCode(abbrevCode, buffer, pos); - log(context, " [0x%08x] language %s", pos, "DW_LANG_Java"); - pos = writeAttrData1(DwarfDebugInfo.LANG_ENCODING, buffer, pos); - log(context, " [0x%08x] use_UTF8", pos); - pos = writeFlag((byte) 1, buffer, pos); - String name = uniqueDebugString("JAVA"); - log(context, " [0x%08x] name 0x%x (%s)", pos, debugStringIndex(name), name); - pos = writeStrSectionOffset(name, buffer, pos); - String compilationDirectory = dwarfSections.getCachePath(); - log(context, " [0x%08x] comp_dir 0x%x (%s)", pos, debugStringIndex(compilationDirectory), compilationDirectory); - pos = writeStrSectionOffset(compilationDirectory, buffer, pos); - /* - * Code addresses start at offset 0 and range up to the limit defined by the code cache - * size. - */ - int lo = 0; - int hi = dwarfSections.compiledCodeMax(); - // there is one line section starting at offset 0 - int lineIndex = 0; - log(context, " [0x%08x] lo_pc 0x%08x", pos, lo); - pos = writeAttrAddress(lo, buffer, pos); - log(context, " [0x%08x] hi_pc 0x%08x", pos, hi); - pos = writeAttrAddress(hi, buffer, pos); - log(context, " [0x%08x] stmt_list 0x%08x", pos, lineIndex); - pos = writeLineSectionOffset(lineIndex, buffer, pos); - - /* Write DIEs for primitive types and header struct */ + /* Write CU for primitive types and header struct. */ pos = writeBuiltInTypes(context, buffer, pos); /* - * Write DIEs for all instance classes, which includes interfaces and enums + * Write CUs for all instance classes, which includes interfaces and enums. That also + * incorporates interfaces that model foreign types. */ pos = writeInstanceClasses(context, buffer, pos); - /* Write DIEs for array types. */ + /* Write CUs for array types. */ pos = writeArrays(context, buffer, pos); - /* Write all compiled code locations */ - - pos = writeMethodLocations(context, buffer, pos); - - /* Write all static field definitions -- in class order */ - - pos = writeStaticFieldLocations(context, buffer, pos); - - /* - * Write a terminating null attribute. - */ - pos = writeAttrNull(buffer, pos); - - /* Fix up the CU length. */ - - patchLength(lengthPos, buffer, pos); return pos; } private int writeBuiltInTypes(DebugContext context, byte[] buffer, int p) { int pos = p; - log(context, " [0x%08x] <1> Builtin Types:", pos); + // Write the single Java builtin unit header + int lengthPos = pos; + log(context, " [0x%08x] <0> Java Builtin Compile Unit", pos); + cuStart = p; + pos = writeCUHeader(buffer, pos); + assert pos == lengthPos + DW_DIE_HEADER_SIZE; + int abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_builtin_unit; + log(context, " [0x%08x] <0> Abbrev Number %d", pos, abbrevCode); + pos = writeAbbrevCode(abbrevCode, buffer, pos); + log(context, " [0x%08x] language %s", pos, "DW_LANG_Java"); + pos = writeAttrData1(DwarfDebugInfo.LANG_ENCODING, buffer, pos); + log(context, " [0x%08x] use_UTF8", pos); + pos = writeFlag((byte) 1, buffer, pos); + String name = uniqueDebugString("JAVA"); + log(context, " [0x%08x] name 0x%x (%s)", pos, debugStringIndex(name), name); + pos = writeStrSectionOffset(name, buffer, pos); + String compilationDirectory = dwarfSections.getCachePath(); + log(context, " [0x%08x] comp_dir 0x%x (%s)", pos, debugStringIndex(compilationDirectory), compilationDirectory); + pos = writeStrSectionOffset(compilationDirectory, buffer, pos); /* Write child entries for primitive Java types. */ pos = primitiveTypeStream().reduce(pos, @@ -237,6 +211,14 @@ private int writeBuiltInTypes(DebugContext context, byte[] buffer, int p) { (pos1, primitiveTypeEntry) -> writeClassConstantDeclaration(context, primitiveTypeEntry, buffer, pos1), (oldpos, newpos) -> newpos); + /* + * Write a terminating null attribute. + */ + pos = writeAttrNull(buffer, pos); + + /* Fix up the CU length. */ + + patchLength(lengthPos, buffer, pos); return pos; } @@ -387,6 +369,9 @@ private int writeInstanceClasses(DebugContext context, byte[] buffer, int pos) { log(context, " [0x%08x] instance classes", pos); Cursor cursor = new Cursor(pos); instanceClassStream().forEach(classEntry -> { + // save current CU start so we can write Ref4 attributes as CU offsets + cuStart = cursor.get(); + setCUIndex(classEntry, cuStart); cursor.set(writeInstanceClassInfo(context, classEntry, buffer, cursor.get())); }); return cursor.get(); @@ -394,6 +379,39 @@ private int writeInstanceClasses(DebugContext context, byte[] buffer, int pos) { private int writeInstanceClassInfo(DebugContext context, ClassEntry classEntry, byte[] buffer, int p) { int pos = p; + // Write a Java class unit header + int lengthPos = pos; + log(context, " [0x%08x] Instance class unit", pos); + pos = writeCUHeader(buffer, pos); + assert pos == lengthPos + DW_DIE_HEADER_SIZE; + int abbrevCode = (classEntry.hasCompiledEntries() ? DwarfDebugInfo.DW_ABBREV_CODE_class_unit2 : DwarfDebugInfo.DW_ABBREV_CODE_class_unit1); + log(context, " [0x%08x] <0> Abbrev Number %d", pos, abbrevCode); + pos = writeAbbrevCode(abbrevCode, buffer, pos); + log(context, " [0x%08x] language %s", pos, "DW_LANG_Java"); + pos = writeAttrData1(DwarfDebugInfo.LANG_ENCODING, buffer, pos); + log(context, " [0x%08x] use_UTF8", pos); + pos = writeFlag((byte) 1, buffer, pos); + String name = classEntry.getFullFileName(); + if (name == null) { + name = classEntry.getTypeName().replace('.', '/') + ".java"; + } + log(context, " [0x%08x] name 0x%x (%s)", pos, debugStringIndex(name), name); + pos = writeStrSectionOffset(uniqueDebugString(name), buffer, pos); + String compilationDirectory = dwarfSections.getCachePath(); + log(context, " [0x%08x] comp_dir 0x%x (%s)", pos, debugStringIndex(compilationDirectory), compilationDirectory); + pos = writeStrSectionOffset(compilationDirectory, buffer, pos); + if (abbrevCode == DwarfDebugInfo.DW_ABBREV_CODE_class_unit2) { + int codeRangesIndex = getCodeRangesIndex(classEntry); + log(context, " [0x%08x] ranges 0x%x", pos, codeRangesIndex); + pos = writeRangesSectionOffset(codeRangesIndex, buffer, pos); + // write low_pc as well as ranges so that lcoation lists can default the base address + int lo = classEntry.lowpc(); + log(context, " [0x%08x] low_pc 0x%x", pos, codeRangesIndex); + pos = writeAttrAddress(lo, buffer, pos); + int lineIndex = getLineIndex(classEntry); + log(context, " [0x%08x] stmt_list 0x%x", pos, lineIndex); + pos = writeLineSectionOffset(lineIndex, buffer, pos); + } /* if the class has a loader then embed the children in a namespace */ String loaderId = classEntry.getLoaderId(); if (!loaderId.isEmpty()) { @@ -420,10 +438,30 @@ private int writeInstanceClassInfo(DebugContext context, ClassEntry classEntry, /* if we opened a namespace then terminate its children */ + /* Write all compiled code locations */ + + pos = writeMethodLocations(context, classEntry, buffer, pos); + + /* Write abstract inline methods. */ + + pos = writeAbstractInlineMethods(context, classEntry, buffer, pos); + + /* Write all static field definitions */ + + pos = writeStaticFieldLocations(context, classEntry, buffer, pos); + if (!loaderId.isEmpty()) { pos = writeAttrNull(buffer, pos); } + /* + * Write a terminating null attribute. + */ + pos = writeAttrNull(buffer, pos); + + /* Fix up the CU length. */ + + patchLength(lengthPos, buffer, pos); return pos; } @@ -684,7 +722,7 @@ private int writeMethodDeclaration(DebugContext context, ClassEntry classEntry, fileEntry = classEntry.getFileEntry(); } assert fileEntry != null; - int fileIdx = fileEntry.getIdx(); + int fileIdx = classEntry.getFileIdx(fileEntry); log(context, " [0x%08x] file 0x%x (%s)", pos, fileIdx, fileEntry.getFullName()); pos = writeAttrData2((short) fileIdx, buffer, pos); int line = method.getLine(); @@ -704,45 +742,43 @@ private int writeMethodDeclaration(DebugContext context, ClassEntry classEntry, pos = writeFlag((byte) 1, buffer, pos); int typeIdx = getLayoutIndex(classEntry); log(context, " [0x%08x] containing_type 0x%x (%s)", pos, typeIdx, classEntry.getTypeName()); - pos = writeInfoSectionOffset(typeIdx, buffer, pos); + pos = writeAttrRef4(typeIdx, buffer, pos); if (abbrevCode == DwarfDebugInfo.DW_ABBREV_CODE_method_declaration) { /* Record the current position so we can back patch the object pointer. */ int objectPointerIndex = pos; /* * Write a dummy ref address to move pos on to where the first parameter gets written. - * - * n.b. buffer is passed as null so we don't attempt to create a reloc! */ - pos = writeInfoSectionOffset(0, null, pos); + pos = writeAttrRef4(0, null, pos); /* * Now backpatch object pointer slot with current pos, identifying the first parameter. */ log(context, " [0x%08x] object_pointer 0x%x", objectPointerIndex, pos); - writeInfoSectionOffset(pos, buffer, objectPointerIndex); + writeAttrRef4(pos, buffer, objectPointerIndex); } /* Write method parameter declarations. */ - pos = writeMethodParameterDeclarations(context, method, fileIdx, 3, buffer, pos); + pos = writeMethodParameterDeclarations(context, classEntry, method, fileIdx, 3, buffer, pos); /* write method local declarations */ - pos = writeMethodLocalDeclarations(context, method, fileIdx, 3, buffer, pos); + pos = writeMethodLocalDeclarations(context, classEntry, method, fileIdx, 3, buffer, pos); /* * Write a terminating null attribute. */ return writeAttrNull(buffer, pos); } - private int writeMethodParameterDeclarations(DebugContext context, MethodEntry method, int fileIdx, int level, byte[] buffer, int p) { + private int writeMethodParameterDeclarations(DebugContext context, ClassEntry classEntry, MethodEntry method, int fileIdx, int level, byte[] buffer, int p) { int pos = p; int refAddr; if (!Modifier.isStatic(method.getModifiers())) { refAddr = pos; DebugLocalInfo paramInfo = method.getThisParam(); - setMethodLocalIndex(method, paramInfo, refAddr); + setMethodLocalIndex(classEntry, method, paramInfo, refAddr); pos = writeMethodParameterDeclaration(context, paramInfo, fileIdx, true, level, buffer, pos); } for (int i = 0; i < method.getParamCount(); i++) { refAddr = pos; DebugLocalInfo paramInfo = method.getParam(i); - setMethodLocalIndex(method, paramInfo, refAddr); + setMethodLocalIndex(classEntry, method, paramInfo, refAddr); pos = writeMethodParameterDeclaration(context, paramInfo, fileIdx, false, level, buffer, pos); } return pos; @@ -785,13 +821,13 @@ private int writeMethodParameterDeclaration(DebugContext context, DebugLocalInfo return pos; } - private int writeMethodLocalDeclarations(DebugContext context, MethodEntry method, int fileIdx, int level, byte[] buffer, int p) { + private int writeMethodLocalDeclarations(DebugContext context, ClassEntry classEntry, MethodEntry method, int fileIdx, int level, byte[] buffer, int p) { int pos = p; int refAddr; for (int i = 0; i < method.getLocalCount(); i++) { refAddr = pos; DebugLocalInfo localInfo = method.getLocal(i); - setMethodLocalIndex(method, localInfo, refAddr); + setMethodLocalIndex(classEntry, method, localInfo, refAddr); pos = writeMethodLocalDeclaration(context, localInfo, fileIdx, level, buffer, pos); } return pos; @@ -1084,7 +1120,7 @@ private int writeClassType(DebugContext context, ClassEntry classEntry, byte[] b pos = writeAttrData1((byte) pointerSize, buffer, pos); int layoutOffset = getLayoutIndex(classEntry); log(context, " [0x%08x] type 0x%x", pos, layoutOffset); - pos = writeInfoSectionOffset(layoutOffset, buffer, pos); + pos = writeAttrRef4(layoutOffset, buffer, pos); if (dwarfSections.useHeapBase()) { /* Define an indirect pointer type referring to the indirect layout. */ @@ -1098,7 +1134,7 @@ private int writeClassType(DebugContext context, ClassEntry classEntry, byte[] b pos = writeAttrData1((byte) oopReferenceSize, buffer, pos); layoutOffset = getIndirectLayoutIndex(classEntry); log(context, " [0x%08x] type 0x%x", pos, layoutOffset); - pos = writeInfoSectionOffset(layoutOffset, buffer, pos); + pos = writeAttrRef4(layoutOffset, buffer, pos); } else { setIndirectTypeIndex(classEntry, typeIdx); } @@ -1121,7 +1157,7 @@ private int writeInterfaceType(DebugContext context, InterfaceClassEntry interfa pos = writeAttrData1((byte) pointerSize, buffer, pos); int layoutOffset = getLayoutIndex(interfaceClassEntry); log(context, " [0x%08x] type 0x%x", pos, layoutOffset); - pos = writeInfoSectionOffset(layoutOffset, buffer, pos); + pos = writeAttrRef4(layoutOffset, buffer, pos); if (dwarfSections.useHeapBase()) { /* Define an indirect pointer type referring to the indirect layout. */ @@ -1135,7 +1171,7 @@ private int writeInterfaceType(DebugContext context, InterfaceClassEntry interfa pos = writeAttrData1((byte) byteSize, buffer, pos); layoutOffset = getIndirectLayoutIndex(interfaceClassEntry); log(context, " [0x%08x] type 0x%x", pos, layoutOffset); - pos = writeInfoSectionOffset(layoutOffset, buffer, pos); + pos = writeAttrRef4(layoutOffset, buffer, pos); } else { setIndirectTypeIndex(interfaceClassEntry, typeIdx); } @@ -1160,6 +1196,8 @@ private int writeForeignType(DebugContext context, ForeignTypeEntry foreignTypeE int pointerSize = dwarfSections.pointerSize(); log(context, " [0x%08x] byte_size 0x%x", pos, pointerSize); pos = writeAttrData1((byte) pointerSize, buffer, pos); + // Note that we write a ref_addr offset here because an unknown (void *) + // foreign type can have a layout offset that is not in its CU log(context, " [0x%08x] type 0x%x", pos, layoutOffset); pos = writeInfoSectionOffset(layoutOffset, buffer, pos); @@ -1173,7 +1211,7 @@ private int writeForeignType(DebugContext context, ForeignTypeEntry foreignTypeE log(context, " [0x%08x] name %s", pos, name); pos = writeStrSectionOffset(name, buffer, pos); log(context, " [0x%08x] type 0x%x", pos, refTypeIdx); - pos = writeInfoSectionOffset(refTypeIdx, buffer, pos); + pos = writeAttrRef4(refTypeIdx, buffer, pos); setTypeIndex(foreignTypeEntry, typedefIdx); // foreign pointers are never stored compressed so don't need a separate indirect type @@ -1182,15 +1220,7 @@ private int writeForeignType(DebugContext context, ForeignTypeEntry foreignTypeE return pos; } - private int writeStaticFieldLocations(DebugContext context, byte[] buffer, int p) { - Cursor cursor = new Cursor(p); - instanceClassStream().forEach(classEntry -> { - cursor.set(writeClassStaticFieldLocations(context, classEntry, buffer, cursor.get())); - }); - return cursor.get(); - } - - private int writeClassStaticFieldLocations(DebugContext context, ClassEntry classEntry, byte[] buffer, int p) { + private int writeStaticFieldLocations(DebugContext context, ClassEntry classEntry, byte[] buffer, int p) { /* * Only write locations for static fields that have an offset greater than 0. A negative * offset indicates that the field has been folded into code as an unmaterialized constant. @@ -1229,6 +1259,7 @@ private int writeArrays(DebugContext context, byte[] buffer, int p) { log(context, " [0x%08x] array classes", p); Cursor cursor = new Cursor(p); arrayTypeStream().forEach(arrayTypeEntry -> { + cuStart = cursor.get(); cursor.set(writeArray(context, arrayTypeEntry, buffer, cursor.get())); }); return cursor.get(); @@ -1236,6 +1267,25 @@ private int writeArrays(DebugContext context, byte[] buffer, int p) { private int writeArray(DebugContext context, ArrayTypeEntry arrayTypeEntry, byte[] buffer, int p) { int pos = p; + // Write a Java class unit header + int lengthPos = pos; + log(context, " [0x%08x] Array class unit", pos); + pos = writeCUHeader(buffer, pos); + assert pos == lengthPos + DW_DIE_HEADER_SIZE; + int abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_class_unit1; + log(context, " [0x%08x] <0> Abbrev Number %d", pos, abbrevCode); + pos = writeAbbrevCode(abbrevCode, buffer, pos); + log(context, " [0x%08x] language %s", pos, "DW_LANG_Java"); + pos = writeAttrData1(DwarfDebugInfo.LANG_ENCODING, buffer, pos); + log(context, " [0x%08x] use_UTF8", pos); + pos = writeFlag((byte) 1, buffer, pos); + String name = uniqueDebugString("JAVA"); + log(context, " [0x%08x] name 0x%x (%s)", pos, debugStringIndex(name), name); + pos = writeStrSectionOffset(name, buffer, pos); + String compilationDirectory = dwarfSections.getCachePath(); + log(context, " [0x%08x] comp_dir 0x%x (%s)", pos, debugStringIndex(compilationDirectory), compilationDirectory); + pos = writeStrSectionOffset(compilationDirectory, buffer, pos); + /* if the array base type is a class with a loader then embed the children in a namespace */ String loaderId = arrayTypeEntry.getLoaderId(); if (!loaderId.isEmpty()) { @@ -1261,6 +1311,14 @@ private int writeArray(DebugContext context, ArrayTypeEntry arrayTypeEntry, byte pos = writeAttrNull(buffer, pos); } + /* + * Write a terminating null attribute. + */ + pos = writeAttrNull(buffer, pos); + + /* Fix up the CU length. */ + + patchLength(lengthPos, buffer, pos); return pos; } @@ -1433,7 +1491,7 @@ private int writeArrayTypes(DebugContext context, ArrayTypeEntry arrayTypeEntry, log(context, " [0x%08x] byte_size 0x%x", pos, pointerSize); pos = writeAttrData1((byte) pointerSize, buffer, pos); log(context, " [0x%08x] type (pointer) 0x%x (%s)", pos, layoutOffset, name); - pos = writeInfoSectionOffset(layoutOffset, buffer, pos); + pos = writeAttrRef4(layoutOffset, buffer, pos); if (dwarfSections.useHeapBase()) { setIndirectTypeIndex(arrayTypeEntry, pos); @@ -1446,7 +1504,7 @@ private int writeArrayTypes(DebugContext context, ArrayTypeEntry arrayTypeEntry, log(context, " [0x%08x] byte_size 0x%x", pos, byteSize); pos = writeAttrData1((byte) byteSize, buffer, pos); log(context, " [0x%08x] type (pointer) 0x%x (%s)", pos, indirectLayoutOffset, name); - pos = writeInfoSectionOffset(indirectLayoutOffset, buffer, pos); + pos = writeAttrRef4(indirectLayoutOffset, buffer, pos); } else { setIndirectTypeIndex(arrayTypeEntry, typeIdx); } @@ -1454,15 +1512,15 @@ private int writeArrayTypes(DebugContext context, ArrayTypeEntry arrayTypeEntry, return pos; } - private int writeMethodLocations(DebugContext context, byte[] buffer, int p) { + private int writeMethodLocations(DebugContext context, ClassEntry classEntry, byte[] buffer, int p) { Cursor cursor = new Cursor(p); - compiledMethodsStream().forEach(compiledMethodEntry -> { - cursor.set(writeMethodLocation(context, compiledMethodEntry, buffer, cursor.get())); + classEntry.compiledEntries().forEach(compiledMethodEntry -> { + cursor.set(writeMethodLocation(context, classEntry, compiledMethodEntry, buffer, cursor.get())); }); return cursor.get(); } - private int writeMethodLocation(DebugContext context, CompiledMethodEntry compiledEntry, byte[] buffer, int p) { + private int writeMethodLocation(DebugContext context, ClassEntry classEntry, CompiledMethodEntry compiledEntry, byte[] buffer, int p) { int pos = p; Range primary = compiledEntry.getPrimary(); log(context, " [0x%08x] method location", pos); @@ -1481,16 +1539,16 @@ private int writeMethodLocation(DebugContext context, CompiledMethodEntry compil String methodKey = primary.getSymbolName(); int methodSpecOffset = getMethodDeclarationIndex(primary.getMethodEntry()); log(context, " [0x%08x] specification 0x%x (%s)", pos, methodSpecOffset, methodKey); - pos = writeInfoSectionOffset(methodSpecOffset, buffer, pos); + pos = writeAttrRef4(methodSpecOffset, buffer, pos); HashMap> varRangeMap = primary.getVarRangeMap(); - pos = writeMethodParameterLocations(context, varRangeMap, primary, 2, buffer, pos); - pos = writeMethodLocalLocations(context, varRangeMap, primary, 2, buffer, pos); + pos = writeMethodParameterLocations(context, classEntry, varRangeMap, primary, 2, buffer, pos); + pos = writeMethodLocalLocations(context, classEntry, varRangeMap, primary, 2, buffer, pos); if (primary.includesInlineRanges()) { /* * the method has inlined ranges so write concrete inlined method entries as its * children */ - pos = generateConcreteInlinedMethods(context, compiledEntry, buffer, pos); + pos = generateConcreteInlinedMethods(context, classEntry, compiledEntry, buffer, pos); } /* * Write a terminating null attribute. @@ -1498,7 +1556,7 @@ private int writeMethodLocation(DebugContext context, CompiledMethodEntry compil return writeAttrNull(buffer, pos); } - private int writeMethodParameterLocations(DebugContext context, HashMap> varRangeMap, Range range, int depth, byte[] buffer, int p) { + private int writeMethodParameterLocations(DebugContext context, ClassEntry classEntry, HashMap> varRangeMap, Range range, int depth, byte[] buffer, int p) { int pos = p; MethodEntry methodEntry; if (range.isPrimary()) { @@ -1509,20 +1567,20 @@ private int writeMethodParameterLocations(DebugContext context, HashMap ranges = varRangeMap.get(thisParamInfo); pos = writeMethodLocalLocation(context, range, thisParamInfo, refAddr, ranges, depth, true, buffer, pos); } for (int i = 0; i < methodEntry.getParamCount(); i++) { DebugLocalInfo paramInfo = methodEntry.getParam(i); - int refAddr = getMethodLocalIndex(methodEntry, paramInfo); + int refAddr = getMethodLocalIndex(classEntry, methodEntry, paramInfo); List ranges = varRangeMap.get(paramInfo); pos = writeMethodLocalLocation(context, range, paramInfo, refAddr, ranges, depth, true, buffer, pos); } return pos; } - private int writeMethodLocalLocations(DebugContext context, HashMap> varRangeMap, Range range, int depth, byte[] buffer, int p) { + private int writeMethodLocalLocations(DebugContext context, ClassEntry classEntry, HashMap> varRangeMap, Range range, int depth, byte[] buffer, int p) { int pos = p; MethodEntry methodEntry; if (range.isPrimary()) { @@ -1534,7 +1592,7 @@ private int writeMethodLocalLocations(DebugContext context, HashMap ranges = varRangeMap.get(localInfo); pos = writeMethodLocalLocation(context, range, localInfo, refAddr, ranges, depth, false, buffer, pos); } @@ -1575,7 +1633,6 @@ private int writeMethodLocalLocation(DebugContext context, Range range, DebugLoc } log(context, " [0x%08x] <%d> Abbrev Number %d", pos, depth, abbrevCode); pos = writeAbbrevCode(abbrevCode, buffer, pos); - // n.b. specification offset gets written as a Ref4, relative to CU start log(context, " [0x%08x] specification 0x%x", pos, refAddr); pos = writeAttrRef4(refAddr, buffer, pos); if (abbrevCode == DwarfDebugInfo.DW_ABBREV_CODE_method_local_location2 || @@ -1590,7 +1647,7 @@ private int writeMethodLocalLocation(DebugContext context, Range range, DebugLoc /** * Go through the subranges and generate concrete debug entries for inlined methods. */ - private int generateConcreteInlinedMethods(DebugContext context, CompiledMethodEntry compiledEntry, byte[] buffer, int p) { + private int generateConcreteInlinedMethods(DebugContext context, ClassEntry classEntry, CompiledMethodEntry compiledEntry, byte[] buffer, int p) { Range primary = compiledEntry.getPrimary(); if (primary.isLeaf()) { return p; @@ -1611,12 +1668,12 @@ private int generateConcreteInlinedMethods(DebugContext context, CompiledMethodE depth--; } depth = subrange.getDepth(); - pos = writeInlineSubroutine(context, subrange, depth + 2, buffer, pos); + pos = writeInlineSubroutine(context, classEntry, subrange, depth + 2, buffer, pos); HashMap> varRangeMap = subrange.getVarRangeMap(); // increment depth to account for parameter and method locations depth++; - pos = writeMethodParameterLocations(context, varRangeMap, subrange, depth + 2, buffer, pos); - pos = writeMethodLocalLocations(context, varRangeMap, subrange, depth + 2, buffer, pos); + pos = writeMethodParameterLocations(context, classEntry, varRangeMap, subrange, depth + 2, buffer, pos); + pos = writeMethodLocalLocations(context, classEntry, varRangeMap, subrange, depth + 2, buffer, pos); } // if we just stepped out of a child range write nulls for each step up while (depth > 0) { @@ -1626,7 +1683,7 @@ private int generateConcreteInlinedMethods(DebugContext context, CompiledMethodE return pos; } - private int writeInlineSubroutine(DebugContext context, Range caller, int depth, byte[] buffer, int p) { + private int writeInlineSubroutine(DebugContext context, ClassEntry classEntry, SubRange caller, int depth, byte[] buffer, int p) { assert !caller.isLeaf(); // the supplied range covers an inline call and references the caller method entry. its // child ranges all reference the same inlined called method. leaf children cover code for @@ -1636,7 +1693,7 @@ private int writeInlineSubroutine(DebugContext context, Range caller, int depth, MethodEntry methodEntry = callee.getMethodEntry(); String methodKey = methodEntry.getSymbolName(); /* the abstract index was written in the method's class entry */ - int abstractOriginIndex = getMethodDeclarationIndex(methodEntry); + int abstractOriginIndex = (classEntry == methodEntry.ownerType() ? getMethodDeclarationIndex(methodEntry) : getAbstractInlineMethodIndex(classEntry, methodEntry)); int pos = p; log(context, " [0x%08x] concrete inline subroutine [0x%x, 0x%x] %s", pos, caller.getLo(), caller.getHi(), methodKey); @@ -1648,14 +1705,14 @@ private int writeInlineSubroutine(DebugContext context, Range caller, int depth, log(context, " Unable to retrieve call line for inlined method %s", callee.getFullMethodName()); /* continue with line 0 and fileIndex 1 as we must insert a tree node */ callLine = 0; - fileIndex = 1; + fileIndex = classEntry.getFileIdx(); } else { FileEntry subFileEntry = caller.getFileEntry(); if (subFileEntry != null) { - fileIndex = subFileEntry.getIdx(); + fileIndex = classEntry.getFileIdx(subFileEntry); } else { log(context, " Unable to retrieve caller FileEntry for inlined method %s (caller method %s)", callee.getFullMethodName(), caller.getFullMethodName()); - fileIndex = 1; + fileIndex = classEntry.getFileIdx(); } } final int code; @@ -1675,9 +1732,93 @@ private int writeInlineSubroutine(DebugContext context, Range caller, int depth, return pos; } + private int writeAbstractInlineMethods(DebugContext context, ClassEntry classEntry, byte[] buffer, int p) { + EconomicSet inlinedMethods = collectInlinedMethods(context, classEntry, p); + int pos = p; + for (MethodEntry methodEntry : inlinedMethods) { + // n.b. class entry used to index the method belongs to the inlining method + // not the inlined method + setAbstractInlineMethodIndex(classEntry, methodEntry, pos); + pos = writeAbstractInlineMethod(context, classEntry, methodEntry, buffer, pos); + } + return pos; + } + + private EconomicSet collectInlinedMethods(DebugContext context, ClassEntry classEntry, int p) { + final EconomicSet methods = EconomicSet.create(); + classEntry.compiledEntries().forEach(compiledEntry -> addInlinedMethods(context, compiledEntry, compiledEntry.getPrimary(), methods, p)); + return methods; + } + + private void addInlinedMethods(DebugContext context, CompiledMethodEntry compiledEntry, Range primary, EconomicSet hashSet, int p) { + if (primary.isLeaf()) { + return; + } + verboseLog(context, " [0x%08x] collect abstract inlined methods %s", p, primary.getFullMethodName()); + Iterator iterator = compiledEntry.topDownRangeIterator(); + while (iterator.hasNext()) { + SubRange subrange = iterator.next(); + if (subrange.isLeaf()) { + // we only generate abstract inline methods for non-leaf entries + continue; + } + // the subrange covers an inline call and references the caller method entry. its + // child ranges all reference the same inlined called method. leaf children cover code + // for + // that inlined method. non-leaf children cover code for recursively inlined methods. + // identify the inlined method by looking at the first callee + Range callee = subrange.getFirstCallee(); + MethodEntry methodEntry = callee.getMethodEntry(); + if (hashSet.add(methodEntry)) { + verboseLog(context, " [0x%08x] add abstract inlined method %s", p, methodEntry.getSymbolName()); + } + } + } + + private int writeAbstractInlineMethod(DebugContext context, ClassEntry classEntry, MethodEntry method, byte[] buffer, int p) { + int pos = p; + log(context, " [0x%08x] abstract inline method %s::%s", pos, classEntry.getTypeName(), method.methodName()); + int abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_abstract_inline_method; + log(context, " [0x%08x] <1> Abbrev Number %d", pos, abbrevCode); + pos = writeAbbrevCode(abbrevCode, buffer, pos); + log(context, " [0x%08x] inline 0x%x", pos, DwarfDebugInfo.DW_INL_inlined); + pos = writeAttrData1(DwarfDebugInfo.DW_INL_inlined, buffer, pos); + /* + * Should pass true only if method is non-private. + */ + log(context, " [0x%08x] external true", pos); + pos = writeFlag(DwarfDebugInfo.DW_FLAG_true, buffer, pos); + int methodSpecOffset = getMethodDeclarationIndex(method); + log(context, " [0x%08x] specification 0x%x", pos, methodSpecOffset); + pos = writeInfoSectionOffset(methodSpecOffset, buffer, pos); + /* + * If the inline method exists in a different CU then write locals and params otherwise we + * can just reuse the locals and params in the declaration + */ + if (classEntry != method.ownerType()) { + FileEntry fileEntry = method.getFileEntry(); + if (fileEntry == null) { + fileEntry = classEntry.getFileEntry(); + } + assert fileEntry != null; + int fileIdx = classEntry.getFileIdx(fileEntry); + int level = 3; + // n.b. class entry used to index the params belongs to the inlining method + // not the inlined method + pos = writeMethodParameterDeclarations(context, classEntry, method, fileIdx, level, buffer, pos); + pos = writeMethodLocalDeclarations(context, classEntry, method, fileIdx, level, buffer, pos); + } + /* + * Write a terminating null attribute. + */ + return writeAttrNull(buffer, pos); + } + private int writeAttrRef4(int reference, byte[] buffer, int p) { - // writes a CU-relative offset but we only have one CU which starts at offset 0 - return writeAttrData4(reference, buffer, p); + // make sure we have actually started writing a CU + assert cuStart >= 0; + // writes a CU-relative offset + return writeAttrData4(reference - cuStart, buffer, p); } private int writeCUHeader(byte[] buffer, int p) { diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLineSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLineSectionImpl.java index 5c658aedaaa5..e5b552782351 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLineSectionImpl.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLineSectionImpl.java @@ -29,13 +29,13 @@ import java.util.Iterator; import java.util.Map; +import com.oracle.objectfile.debugentry.ClassEntry; import org.graalvm.compiler.debug.DebugContext; 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.DirEntry; import com.oracle.objectfile.debugentry.FileEntry; import com.oracle.objectfile.debugentry.range.Range; import com.oracle.objectfile.debugentry.range.SubRange; @@ -139,11 +139,8 @@ public class DwarfLineSectionImpl extends DwarfSectionImpl { */ private static final byte DW_LNE_define_file = 3; - private int linePrologueSize; - DwarfLineSectionImpl(DwarfDebugInfo dwarfSections) { super(dwarfSections); - linePrologueSize = 0; } @Override @@ -157,20 +154,23 @@ public void createContent() { /* * We need to create a header, dir table, file table and line number table encoding for each - * CU. - */ - - /* - * Write line info for all compiled methods. - */ - int headerSize = headerSize(); - int dirTableSize = computeDirTableSize(); - int fileTableSize = computeFileTableSize(); - int prologueSize = headerSize + dirTableSize + fileTableSize; - setLinePrologueSize(prologueSize); - int lineNumberTableSize = computeLineNUmberTableSize(); - int totalSize = prologueSize + lineNumberTableSize; - byte[] buffer = new byte[totalSize]; + * class CU that contains compiled methods. + */ + + Cursor byteCount = new Cursor(); + instanceClassStream().filter(ClassEntry::hasCompiledEntries).forEachOrdered(classEntry -> { + setLineIndex(classEntry, byteCount.get()); + int headerSize = headerSize(); + int dirTableSize = computeDirTableSize(classEntry); + int fileTableSize = computeFileTableSize(classEntry); + int prologueSize = headerSize + dirTableSize + fileTableSize; + setLinePrologueSize(classEntry, prologueSize); + // mark the start of the line table for this entry + int lineNumberTableSize = computeLineNumberTableSize(classEntry); + int totalSize = prologueSize + lineNumberTableSize; + byteCount.add(totalSize); + }); + byte[] buffer = new byte[byteCount.get()]; super.setContent(buffer); } @@ -206,16 +206,17 @@ private static int headerSize() { return DW_LN_HEADER_SIZE; } - private int computeDirTableSize() { + private int computeDirTableSize(ClassEntry classEntry) { /* * Table contains a sequence of 'nul'-terminated UTF8 dir name bytes followed by an extra * 'nul'. */ Cursor cursor = new Cursor(); - dirStream().forEach(dirEntry -> { - if (dirEntry.getIdx() > 0) { - cursor.add(countUTF8Bytes(dirEntry.getPathString()) + 1); - } + classEntry.dirStream().forEachOrdered(dirEntry -> { + int length = countUTF8Bytes(dirEntry.getPathString()); + // We should never have a null or zero length entry in local dirs + assert length > 0; + cursor.add(length + 1); }); /* * Allow for terminator nul. @@ -224,7 +225,7 @@ private int computeDirTableSize() { return cursor.get(); } - private int computeFileTableSize() { + private int computeFileTableSize(ClassEntry classEntry) { /* * Table contains a sequence of file entries followed by an extra 'nul' * @@ -232,7 +233,7 @@ private int computeFileTableSize() { * time stamps */ Cursor cursor = new Cursor(); - fileStream().forEach(fileEntry -> { + classEntry.fileStream().forEachOrdered(fileEntry -> { // We want the file base name excluding path. String baseName = fileEntry.getFileName(); int length = countUTF8Bytes(baseName); @@ -240,7 +241,7 @@ private int computeFileTableSize() { assert length > 0; cursor.add(length + 1); // The dir index gets written as a ULEB - int dirIdx = fileEntry.getDirEntry().getIdx(); + int dirIdx = classEntry.getDirIdx(fileEntry); cursor.add(writeULEB(dirIdx, scratch, 0)); // The two zero timestamps require 1 byte each cursor.add(2); @@ -252,12 +253,12 @@ private int computeFileTableSize() { return cursor.get(); } - private int computeLineNUmberTableSize() { + private int computeLineNumberTableSize(ClassEntry classEntry) { /* * Sigh -- we have to do this by generating the content even though we cannot write it into * a byte[]. */ - return writeLineNumberTable(null, null, 0); + return writeLineNumberTable(null, classEntry, null, 0); } @Override @@ -282,32 +283,38 @@ public void writeContent(DebugContext context) { assert contentByteArrayCreated(); byte[] buffer = getContent(); + Cursor cursor = new Cursor(); - int pos = 0; - - enableLog(context, pos); - log(context, " [0x%08x] DEBUG_LINE", pos); - pos = writeHeader(buffer, pos); - log(context, " [0x%08x] headerSize = 0x%08x", pos, pos); - int dirTablePos = pos; - pos = writeDirTable(context, buffer, pos); - log(context, " [0x%08x] dirTableSize = 0x%08x", pos, pos - dirTablePos); - int fileTablePos = pos; - pos = writeFileTable(context, buffer, pos); - log(context, " [0x%08x] fileTableSize = 0x%08x", pos, pos - fileTablePos); - int lineNumberTablePos = pos; - pos = writeLineNumberTable(context, buffer, pos); - log(context, " [0x%08x] lineNumberTableSize = 0x%x", pos, pos - lineNumberTablePos); - log(context, " [0x%08x] size = 0x%x", pos, pos); - assert pos == buffer.length; - } - - private int writeHeader(byte[] buffer, int p) { + enableLog(context, cursor.get()); + log(context, " [0x%08x] DEBUG_LINE", cursor.get()); + instanceClassStream().filter(ClassEntry::hasCompiledEntries).forEachOrdered(classEntry -> { + int pos = cursor.get(); + setLineIndex(classEntry, pos); + int lengthPos = pos; + pos = writeHeader(classEntry, buffer, pos); + log(context, " [0x%08x] headerSize = 0x%08x", pos, pos); + int dirTablePos = pos; + pos = writeDirTable(context, classEntry, buffer, pos); + log(context, " [0x%08x] dirTableSize = 0x%08x", pos, pos - dirTablePos); + int fileTablePos = pos; + pos = writeFileTable(context, classEntry, buffer, pos); + log(context, " [0x%08x] fileTableSize = 0x%08x", pos, pos - fileTablePos); + int lineNumberTablePos = pos; + pos = writeLineNumberTable(context, classEntry, buffer, pos); + log(context, " [0x%08x] lineNumberTableSize = 0x%x", pos, pos - lineNumberTablePos); + log(context, " [0x%08x] size = 0x%x", pos, pos - lengthPos); + patchLength(lengthPos, buffer, pos); + cursor.set(pos); + }); + assert cursor.get() == buffer.length; + } + + private int writeHeader(ClassEntry classEntry, byte[] buffer, int p) { int pos = p; /* - * 4 ubyte length field. + * write dummy 4 ubyte length field. */ - pos = writeInt(buffer.length - 4, buffer, pos); + pos = writeInt(0, buffer, pos); /* * 2 ubyte version is always 2. */ @@ -315,7 +322,7 @@ private int writeHeader(byte[] buffer, int p) { /* * 4 ubyte prologue length includes rest of header and dir + file table section. */ - int prologueSize = getLinePrologueSize() - (4 + 2 + 4); + int prologueSize = getLinePrologueSize(classEntry) - (4 + 2 + 4); pos = writeInt(prologueSize, buffer, pos); /* * 1 ubyte min instruction length is always 1. @@ -371,19 +378,20 @@ private int writeHeader(byte[] buffer, int p) { return pos; } - private int writeDirTable(DebugContext context, byte[] buffer, int p) { + private int writeDirTable(DebugContext context, ClassEntry classEntry, byte[] buffer, int p) { verboseLog(context, " [0x%08x] Dir Name", p); /* * Write out the list of dirs */ Cursor cursor = new Cursor(p); - dirStream().forEach(dirEntry -> { - int dirIdx = dirEntry.getIdx(); - if (dirIdx > 0) { - String dirPath = dirEntry.getPathString(); - verboseLog(context, " [0x%08x] %-4d %s", cursor.get(), dirIdx, dirPath); - cursor.set(writeUTF8StringBytes(dirPath, buffer, cursor.get())); - } + Cursor idx = new Cursor(1); + classEntry.dirStream().forEach(dirEntry -> { + int dirIdx = idx.get(); + assert (classEntry.getDirIdx(dirEntry) == dirIdx); + String dirPath = dirEntry.getPathString(); + verboseLog(context, " [0x%08x] %-4d %s", cursor.get(), dirIdx, dirPath); + cursor.set(writeUTF8StringBytes(dirPath, buffer, cursor.get())); + idx.add(1); }); /* * Separate dirs from files with a nul. @@ -392,25 +400,26 @@ private int writeDirTable(DebugContext context, byte[] buffer, int p) { return cursor.get(); } - private int writeFileTable(DebugContext context, byte[] buffer, int p) { + private int writeFileTable(DebugContext context, ClassEntry classEntry, byte[] buffer, int p) { verboseLog(context, " [0x%08x] Entry Dir Name", p); /* * Write out the list of files */ Cursor cursor = new Cursor(p); - fileStream().forEach(fileEntry -> { + Cursor idx = new Cursor(1); + classEntry.fileStream().forEach(fileEntry -> { int pos = cursor.get(); + int fileIdx = idx.get(); + assert classEntry.getFileIdx(fileEntry) == fileIdx; + int dirIdx = classEntry.getDirIdx(fileEntry); String baseName = fileEntry.getFileName(); - DirEntry dirEntry = fileEntry.getDirEntry(); - int fileIdx = fileEntry.getIdx(); - int dirIdx = dirEntry.getIdx(); verboseLog(context, " [0x%08x] %-5d %-5d %s", pos, fileIdx, dirIdx, baseName); pos = writeUTF8StringBytes(baseName, buffer, pos); pos = writeULEB(dirIdx, buffer, pos); pos = writeULEB(0, buffer, pos); pos = writeULEB(0, buffer, pos); cursor.set(pos); - + idx.add(1); }); /* * Terminate files with a nul. @@ -422,7 +431,7 @@ private int writeFileTable(DebugContext context, byte[] buffer, int p) { private long debugLine = 1; private int debugCopyCount = 0; - private int writeCompiledMethodLineInfo(DebugContext context, CompiledMethodEntry compiledEntry, byte[] buffer, int p) { + private int writeCompiledMethodLineInfo(DebugContext context, ClassEntry classEntry, CompiledMethodEntry compiledEntry, byte[] buffer, int p) { int pos = p; Range primaryRange = compiledEntry.getPrimary(); // the compiled method might be a substitution and not in the file of the class entry @@ -433,7 +442,7 @@ private int writeCompiledMethodLineInfo(DebugContext context, CompiledMethodEntr return pos; } String file = fileEntry.getFileName(); - int fileIdx = fileEntry.getIdx(); + int fileIdx = classEntry.getFileIdx(fileEntry); /* * Each primary represents a method i.e. a contiguous sequence of subranges. For normal * methods we expect the first leaf range to start at offset 0 covering the method prologue. @@ -449,7 +458,7 @@ private int writeCompiledMethodLineInfo(DebugContext context, CompiledMethodEntr if (line > 0) { FileEntry firstFileEntry = prologueRange.getFileEntry(); if (firstFileEntry != null) { - fileIdx = firstFileEntry.getIdx(); + fileIdx = classEntry.getFileIdx(firstFileEntry); } } } @@ -500,7 +509,7 @@ private int writeCompiledMethodLineInfo(DebugContext context, CompiledMethodEntr continue; } String subfile = subFileEntry.getFileName(); - int subFileIdx = subFileEntry.getIdx(); + int subFileIdx = classEntry.getFileIdx(subFileEntry); assert subFileIdx > 0; long subLine = subrange.getLine(); long subAddressLo = subrange.getLo(); @@ -618,14 +627,14 @@ private int writeCompiledMethodLineInfo(DebugContext context, CompiledMethodEntr return pos; } - private int writeLineNumberTable(DebugContext context, byte[] buffer, int p) { + private int writeLineNumberTable(DebugContext context, ClassEntry classEntry, byte[] buffer, int p) { Cursor cursor = new Cursor(p); - compiledMethodsStream().forEach(compiledMethod -> { + classEntry.compiledEntries().forEachOrdered(compiledMethod -> { int pos = cursor.get(); String methodName = compiledMethod.getPrimary().getFullMethodNameWithParams(); String fileName = compiledMethod.getClassEntry().getFullFileName(); log(context, " [0x%08x] %s %s", pos, methodName, fileName); - pos = writeCompiledMethodLineInfo(context, compiledMethod, buffer, pos); + pos = writeCompiledMethodLineInfo(context, classEntry, compiledMethod, buffer, pos); cursor.set(pos); }); return cursor.get(); @@ -844,15 +853,6 @@ private static boolean isFixedAdvancePC(long addressDiff) { return addressDiff >= 0 && addressDiff < 0xffff; } - protected int getLinePrologueSize() { - return linePrologueSize; - } - - protected void setLinePrologueSize(int size) { - assert linePrologueSize == 0 : "prologue size set twice!"; - linePrologueSize = size; - } - /** * The debug_line section depends on debug_str section. */ diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLocSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLocSectionImpl.java index b358ec91baea..10420c1d51be 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLocSectionImpl.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLocSectionImpl.java @@ -36,6 +36,7 @@ import java.util.Map; import java.util.Set; +import com.oracle.objectfile.debugentry.ClassEntry; import com.oracle.objectfile.debugentry.range.SubRange; import org.graalvm.compiler.debug.DebugContext; @@ -125,24 +126,42 @@ public void writeContent(DebugContext context) { private int generateContent(DebugContext context, byte[] buffer) { Cursor cursor = new Cursor(); - compiledMethodsStream().forEach(compiledMethod -> { - cursor.set(writeCompiledMethodLocations(context, compiledMethod, buffer, cursor.get())); + // n.b. We could do this by iterating over the compiled methods sequence. the + // reason for doing it in class entry order is to because it mirrors the + // order in which entries appear in the info section. That stops objdump + // posting spurious messages about overlaps and holes in the var ranges. + instanceClassStream().filter(ClassEntry::hasCompiledEntries).forEachOrdered(classEntry -> { + classEntry.compiledEntries().forEachOrdered(compiledMethodEntry -> { + cursor.set(writeCompiledMethodLocations(context, compiledMethodEntry, buffer, cursor.get())); + }); }); return cursor.get(); } private int writeCompiledMethodLocations(DebugContext context, CompiledMethodEntry compiledEntry, byte[] buffer, int p) { int pos = p; - pos = writeTopLevelLocations(context, compiledEntry, buffer, pos); - pos = writeInlineLocations(context, compiledEntry, buffer, pos); + Range primary = compiledEntry.getPrimary(); + /* + * Note that offsets are written relative to the primary range base. This requires writing a + * base address entry before each of the location list ranges. It is possible to default the + * base address to the low_pc value of the compile unit for the compiled method's owning + * class, saving two words per location list. However, that forces the debugger to do a lot + * more up-front cross-referencing of CUs when it needs to resolve code addresses e.g. to + * set a breakpoint, leading to a very slow response for the user. + */ + int base = primary.getLo(); + log(context, " [0x%08x] top level locations [0x%x, 0x%x] method %s", pos, primary.getLo(), primary.getHi(), primary.getFullMethodNameWithParams()); + pos = writeTopLevelLocations(context, compiledEntry, base, buffer, pos); + if (!primary.isLeaf()) { + log(context, " [0x%08x] inline locations %s", pos, primary.getFullMethodNameWithParams()); + pos = writeInlineLocations(context, compiledEntry, base, buffer, pos); + } return pos; } - private int writeTopLevelLocations(DebugContext context, CompiledMethodEntry compiledEntry, byte[] buffer, int p) { + private int writeTopLevelLocations(DebugContext context, CompiledMethodEntry compiledEntry, int base, byte[] buffer, int p) { int pos = p; Range primary = compiledEntry.getPrimary(); - long base = primary.getLo(); - log(context, " [0x%08x] top level locations [0x%x,0x%x] %s", pos, primary.getLo(), primary.getHi(), primary.getFullMethodNameWithParams()); HashMap> varRangeMap = primary.getVarRangeMap(); for (DebugLocalInfo local : varRangeMap.keySet()) { List rangeList = varRangeMap.get(local); @@ -154,14 +173,10 @@ private int writeTopLevelLocations(DebugContext context, CompiledMethodEntry com return pos; } - private int writeInlineLocations(DebugContext context, CompiledMethodEntry compiledEntry, byte[] buffer, int p) { + private int writeInlineLocations(DebugContext context, CompiledMethodEntry compiledEntry, int base, byte[] buffer, int p) { int pos = p; Range primary = compiledEntry.getPrimary(); - if (primary.isLeaf()) { - return p; - } - long base = primary.getLo(); - log(context, " [0x%08x] inline locations [0x%x,0x%x] %s", pos, primary.getLo(), primary.getHi(), primary.getFullMethodNameWithParams()); + assert !primary.isLeaf(); Iterator iterator = compiledEntry.topDownRangeIterator(); while (iterator.hasNext()) { SubRange subrange = iterator.next(); @@ -180,22 +195,17 @@ private int writeInlineLocations(DebugContext context, CompiledMethodEntry compi return pos; } - private int writeVarLocations(DebugContext context, DebugLocalInfo local, long base, List rangeList, byte[] buffer, int p) { + private int writeVarLocations(DebugContext context, DebugLocalInfo local, int base, List rangeList, byte[] buffer, int p) { assert !rangeList.isEmpty(); int pos = p; // collect ranges and values, merging adjacent ranges that have equal value List extents = LocalValueExtent.coalesce(local, rangeList); - // TODO - currently we use the start address of the range's compiled method as - // a base from which to write location offsets. This means we need to explicitly - // write a (relocatable) base address as a prefix to each location list. If instead - // we rebased relative to the start of the enclosing compilation unit then we - // could omit writing the base address prefix, saving two long words per location list. - - // write the base address + // write start of primary range as base address - see comment above for reasons why + // we choose ot do this rather than use the relevant compile unit low_pc pos = writeAttrData8(-1L, buffer, pos); pos = writeAttrAddress(base, buffer, pos); - // now write ranges as offsets from base + // write ranges as offsets from base for (LocalValueExtent extent : extents) { DebugLocalValueInfo value = extent.value; assert (value != null); diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfRangesSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfRangesSectionImpl.java new file mode 100644 index 000000000000..bb85991ef746 --- /dev/null +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfRangesSectionImpl.java @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2023, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2023, 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.elf.dwarf; + +import com.oracle.objectfile.LayoutDecision; +import com.oracle.objectfile.LayoutDecisionMap; +import com.oracle.objectfile.ObjectFile; +import com.oracle.objectfile.debugentry.ClassEntry; +import org.graalvm.compiler.debug.DebugContext; + +import java.util.Map; + +public class DwarfRangesSectionImpl extends DwarfSectionImpl { + public DwarfRangesSectionImpl(DwarfDebugInfo dwarfSections) { + super(dwarfSections); + } + + @Override + public String getSectionName() { + return DwarfDebugInfo.DW_RANGES_SECTION_NAME; + } + + @Override + public void createContent() { + assert !contentByteArrayCreated(); + Cursor cursor = new Cursor(); + instanceClassStream().filter(ClassEntry::hasCompiledEntries).forEachOrdered(classEntry -> { + setCodeRangesIndex(classEntry, cursor.get()); + // base address + cursor.add(2 * 8); + // per method lo and hi offsets + cursor.add(2 * 8 * classEntry.compiledEntryCount()); + // end marker + cursor.add(2 * 8); + }); + byte[] buffer = new byte[cursor.get()]; + super.setContent(buffer); + } + + @Override + public byte[] getOrDecideContent(Map alreadyDecided, byte[] contentHint) { + ObjectFile.Element textElement = getElement().getOwner().elementForName(".text"); + LayoutDecisionMap decisionMap = alreadyDecided.get(textElement); + if (decisionMap != null) { + Object valueObj = decisionMap.getDecidedValue(LayoutDecision.Kind.VADDR); + if (valueObj != null && valueObj instanceof Number) { + /* + * This may not be the final vaddr for the text segment but it will be close enough + * to make debug easier i.e. to within a 4k page or two. + */ + debugTextBase = ((Number) valueObj).longValue(); + } + } + return super.getOrDecideContent(alreadyDecided, contentHint); + } + + @Override + public void writeContent(DebugContext context) { + assert contentByteArrayCreated(); + byte[] buffer = getContent(); + int size = buffer.length; + Cursor cursor = new Cursor(); + + enableLog(context, cursor.get()); + log(context, " [0x%08x] DEBUG_RANGES", cursor.get()); + + instanceClassStream().filter(ClassEntry::hasCompiledEntries).forEachOrdered(classEntry -> { + int pos = cursor.get(); + int start = pos; + setCodeRangesIndex(classEntry, pos); + log(context, " [0x%08x] ranges start for class %s", pos, classEntry.getTypeName()); + int base = classEntry.compiledEntriesBase(); + log(context, " [0x%08x] base 0x%x", pos, base); + pos = writeLong(-1L, buffer, pos); + pos = writeRelocatableCodeOffset(base, buffer, pos); + cursor.set(pos); + classEntry.compiledEntries().forEach(compiledMethodEntry -> { + int lo = compiledMethodEntry.getPrimary().getLo(); + int hi = compiledMethodEntry.getPrimary().getHi(); + log(context, " [0x%08x] lo 0x%x (%s)", cursor.get(), lo - base, compiledMethodEntry.getPrimary().getFullMethodNameWithParams()); + cursor.set(writeLong(lo - base, buffer, cursor.get())); + log(context, " [0x%08x] hi 0x%x", cursor.get(), hi - base); + cursor.set(writeLong(hi - base, buffer, cursor.get())); + }); + pos = cursor.get(); + // write end marker + pos = writeLong(0, buffer, pos); + pos = writeLong(0, buffer, pos); + log(context, " [0x%08x] ranges size 0x%x for class %s", pos, pos - start, classEntry.getTypeName()); + cursor.set(pos); + }); + + assert cursor.get() == size; + } + + /* + * The debug_ranges section depends on debug_aranges section. + */ + private static final String TARGET_SECTION_NAME = DwarfDebugInfo.DW_ARANGES_SECTION_NAME; + + @Override + public String targetSectionName() { + return TARGET_SECTION_NAME; + } + + private final LayoutDecision.Kind[] targetSectionKinds = { + LayoutDecision.Kind.CONTENT, + LayoutDecision.Kind.SIZE + }; + + @Override + public LayoutDecision.Kind[] targetSectionKinds() { + return targetSectionKinds; + } +} diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSectionImpl.java index 54672e011e43..6aee68102e85 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSectionImpl.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSectionImpl.java @@ -466,6 +466,10 @@ protected int writeLineSectionOffset(int offset, byte[] buffer, int pos) { return writeDwarfSectionOffset(offset, buffer, DwarfDebugInfo.DW_LINE_SECTION_NAME, pos); } + protected int writeRangesSectionOffset(int offset, byte[] buffer, int pos) { + return writeDwarfSectionOffset(offset, buffer, DwarfDebugInfo.DW_RANGES_SECTION_NAME, pos); + } + protected int writeAbbrevSectionOffset(int offset, byte[] buffer, int pos) { return writeDwarfSectionOffset(offset, buffer, DwarfDebugInfo.DW_ABBREV_SECTION_NAME, pos); } @@ -812,6 +816,21 @@ protected void setIndirectTypeIndex(TypeEntry typeEntry, int pos) { dwarfSections.setIndirectTypeIndex(typeEntry, pos); } + protected int getCUIndex(ClassEntry classEntry) { + if (!contentByteArrayCreated()) { + return 0; + } + return dwarfSections.getCUIndex(classEntry); + } + + protected void setCUIndex(ClassEntry classEntry, int idx) { + dwarfSections.setCUIndex(classEntry, idx); + } + + protected void setLayoutIndex(ClassEntry classEntry, int pos) { + dwarfSections.setLayoutIndex(classEntry, pos); + } + protected int getLayoutIndex(ClassEntry classEntry) { if (!contentByteArrayCreated()) { return 0; @@ -830,8 +849,37 @@ protected int getIndirectLayoutIndex(ClassEntry classEntry) { return dwarfSections.getIndirectLayoutIndex(classEntry); } - protected void setLayoutIndex(ClassEntry classEntry, int pos) { - dwarfSections.setLayoutIndex(classEntry, pos); + protected void setCodeRangesIndex(ClassEntry classEntry, int pos) { + dwarfSections.setCodeRangesIndex(classEntry, pos); + } + + protected int getCodeRangesIndex(ClassEntry classEntry) { + if (!contentByteArrayCreated()) { + return 0; + } + return dwarfSections.getCodeRangesIndex(classEntry); + } + + protected void setLineIndex(ClassEntry classEntry, int pos) { + dwarfSections.setLineIndex(classEntry, pos); + } + + protected int getLineIndex(ClassEntry classEntry) { + if (!contentByteArrayCreated()) { + return 0; + } + return dwarfSections.getLineIndex(classEntry); + } + + protected void setLinePrologueSize(ClassEntry classEntry, int pos) { + dwarfSections.setLinePrologueSize(classEntry, pos); + } + + protected int getLinePrologueSize(ClassEntry classEntry) { + if (!contentByteArrayCreated()) { + return 0; + } + return dwarfSections.getLinePrologueSize(classEntry); } protected void setFieldDeclarationIndex(StructureTypeEntry entry, String fieldName, int pos) { @@ -856,31 +904,44 @@ protected int getMethodDeclarationIndex(MethodEntry methodEntry) { return dwarfSections.getMethodDeclarationIndex(methodEntry); } + protected void setAbstractInlineMethodIndex(ClassEntry classEntry, MethodEntry methodEntry, int pos) { + dwarfSections.setAbstractInlineMethodIndex(classEntry, methodEntry, pos); + } + + protected int getAbstractInlineMethodIndex(ClassEntry classEntry, MethodEntry methodEntry) { + if (!contentByteArrayCreated()) { + return 0; + } + return dwarfSections.getAbstractInlineMethodIndex(classEntry, methodEntry); + } + /** * Record the info section offset of a local (or parameter) declaration DIE appearing as a child - * of a standard method declaration. - * + * of a standard method declaration or an abstract inline method declaration. + * + * @param classEntry the class of the top level method being declared or inlined into * @param methodEntry the method being declared or inlined. * @param localInfo the local or param whose index is to be recorded. * @param index the info section offset to be recorded. */ - protected void setMethodLocalIndex(MethodEntry methodEntry, DebugLocalInfo localInfo, int index) { - dwarfSections.setMethodLocalIndex(methodEntry, localInfo, index); + protected void setMethodLocalIndex(ClassEntry classEntry, MethodEntry methodEntry, DebugLocalInfo localInfo, int index) { + dwarfSections.setMethodLocalIndex(classEntry, methodEntry, localInfo, index); } /** * Retrieve the info section offset of a local (or parameter) declaration DIE appearing as a - * child of a standard method declaration. + * child of a standard method declaration or an abstract inline method declaration. * + * @param classEntry the class of the top level method being declared or inlined into * @param methodEntry the method being declared or imported * @param localInfo the local or param whose index is to be retrieved. * @return the associated info section offset. */ - protected int getMethodLocalIndex(MethodEntry methodEntry, DebugLocalInfo localInfo) { + protected int getMethodLocalIndex(ClassEntry classEntry, MethodEntry methodEntry, DebugLocalInfo localInfo) { if (!contentByteArrayCreated()) { return 0; } - return dwarfSections.getMethodLocalIndex(methodEntry, localInfo); + return dwarfSections.getMethodLocalIndex(classEntry, methodEntry, localInfo); } /**