diff --git a/substratevm/DEBUGINFO_WINDOWS.md b/substratevm/DEBUGINFO_WINDOWS.md new file mode 100644 index 000000000000..80a3ce0b680a --- /dev/null +++ b/substratevm/DEBUGINFO_WINDOWS.md @@ -0,0 +1,35 @@ +Using the prototype debug info feature on windows. +-------------------------------------- + +To add debug info to a generated native image add flag +-H:GenerateDebugInfo= to the native image command line (where N is +a positive integer value -- the default value 0 means generate no +debug info). For example, + + $ javac Hello.java + $ mx native-image -H:GenerateDebugInfo=1 Hello + +The resulting image should contain CodeView4 debug records in a +format Visual Studio understands. + +Please read the standard DEBUGINFO file; this file only documents differences. + +Because of limitations in the linker, function names are mangled into the equivalent of + _package.class.Function_999_, where '999' is a hash of the function arguments. The exception is the first main() function encountered, + which is mangled to _package.class.main_, with no argument hash. + +As an experimental feature, the Windows debug info ignores inlined Graal code. +This is currently a Graal compile-time flag in CVConstants.java: _skipGraalIntrinsics_. + +To enable Visual Studio to access the cached sources, right click on the solution +in the "Solution Explorer", and select "Properties", then "Debug Source Files". +in the Debug Source Files plane, add the cache directories to the source directory list. + +___Unimplemented functionality___ + +- Currently stack frames are not properly expressed in the debug info. +The stack frame display may have extraneous information in it, and "Step Out", may work incorrectly. + +- Currently there is no type information in the debug info. + +- Currently variables and members do not appear in the type information. diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/ObjectFile.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/ObjectFile.java index c9dabe238e12..f0f8c6981d68 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/ObjectFile.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/ObjectFile.java @@ -252,6 +252,16 @@ public enum RelocationKind { * The relocation's symbol provides high fixup bytes. */ DIRECT_HI, + /** + * The index of the object file section containing the relocation's symbol supplies the + * fixup bytes. (used in CodeView debug information) + */ + SECTION, + /** + * The address of the object file section containing the relocation's symbol (+addend( + * supplies the fixup bytes. (used in CodeView debug information) + */ + SECREL, /** * The relocation's symbol provides low fixup bytes. */ @@ -1092,7 +1102,7 @@ protected boolean elementsCanSharePage(Element s1, Element s2, int offset1, int /** * API method provided to allow a native image generator to provide details of types, code and * heap data inserted into a native image. - * + * * @param debugInfoProvider an implementation of the provider interface that communicates * details of the relevant types, code and heap data. */ @@ -1750,7 +1760,7 @@ public final SymbolTable getOrCreateSymbolTable() { * Allows a task to be executed with a debug context in a named subscope bound to the object * file and accessible to code executed during the lifetime of the task. Invoked code may obtain * access to the debug context using method {@link #debugContext}. - * + * * @param context a context to be bound to the object file for the duration of the task * execution. * @param scopeName a name to be used to define a subscope current while the task is being @@ -1772,7 +1782,7 @@ public void withDebugContext(DebugContext context, String scopeName, Runnable ta /** * Allows a consumer to retrieve the debug context currently bound to this object file. This * method must only called underneath an invocation of method {@link #withDebugContext}. - * + * * @param scopeName a name to be used to define a subscope current while the consumer is active. * @param action an action parameterised by the debug context. */ diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/SectionName.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/SectionName.java index ea04271c3deb..e821bb0235ab 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/SectionName.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/SectionName.java @@ -94,11 +94,14 @@ public String getFormatDependentName(Format f) { public static final SectionName APPLE_NAMESPACE = new ProgbitsSectionName("apple_namespac"); public static final SectionName APPLE_OBJC = new ProgbitsSectionName("apple_objc"); public static final SectionName LLVM_STACKMAPS = new ProgbitsSectionName("llvm_stackmaps"); + // Windows PECOFF CodeView 4 debug sections + public static final SectionName CV4_DEBUG_SYMBOLS = new ProgbitsSectionName("debug$S"); + public static final SectionName CV4_DEBUG_TYPES = new ProgbitsSectionName("debug$T"); private static final SectionName[] myValues; static { - myValues = new SectionName[]{DATA, RODATA, TEXT, BSS, APPLE_NAMES, APPLE_TYPES, APPLE_NAMESPACE, APPLE_OBJC, LLVM_STACKMAPS}; + myValues = new SectionName[]{DATA, RODATA, TEXT, BSS, APPLE_NAMES, APPLE_TYPES, APPLE_NAMESPACE, APPLE_OBJC, LLVM_STACKMAPS, CV4_DEBUG_SYMBOLS, CV4_DEBUG_TYPES}; } private static String getFormatPrefix(ObjectFile.Format f) { 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 50764f2aa564..4cad616912d7 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 @@ -59,4 +59,14 @@ public String getFullName() { public DirEntry getDirEntry() { return dirEntry; } + + @Override + public String toString() { + if (getDirEntry() == null) { + return getFileName() == null ? "-" : getFileName(); + } else if (getFileName() == null) { + return "--"; + } + return String.format("FileEntry(%s)", getFullName()); + } } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java index 1b65c315e8a7..4f88f5fd9308 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java @@ -36,6 +36,10 @@ */ public class Range { + + /* Use '.' for PECOFF files */ + private static final String CLASS_DELIMITER = System.getProperty("os.name").toLowerCase().contains("windows") ? "." : "::"; + private String fileName; private Path filePath; private String className; @@ -79,6 +83,25 @@ public Range(String fileName, Path filePath, String className, String methodName this.primary = primary; } + /* + * Create a slightly different copy of a previously constructed range. Because the previous + * range was constructed by one of the other constructors, a valid assumption is that all the + * strings have previously been inserted int he stringTable, and we can avoid doing that again. + */ + public Range(Range other, int lo, int hi) { + this.fileName = other.fileName; + this.filePath = other.filePath; + this.className = other.className; + this.methodName = other.methodName; + this.paramNames = other.paramNames; + this.returnTypeName = other.returnTypeName; + this.fullMethodName = other.fullMethodName; + this.lo = lo; + this.hi = hi; + this.line = other.line; + this.primary = other.primary; + } + public boolean contains(Range other) { return (lo <= other.lo && hi >= other.hi); } @@ -133,6 +156,19 @@ public String getFullMethodName() { return fullMethodName; } + public String getParamNames() { + return paramNames; + } + + public String getClassAndMethodName() { + StringBuilder builder = new StringBuilder(); + if (className != null) { + builder.append(className).append(CLASS_DELIMITER); + } + builder.append(methodName); + return builder.toString(); + } + private String getExtendedMethodName(boolean includeParams, boolean includeReturnType) { StringBuilder builder = new StringBuilder(); if (includeReturnType && returnTypeName.length() > 0) { @@ -141,7 +177,7 @@ private String getExtendedMethodName(boolean includeParams, boolean includeRetur } if (className != null) { builder.append(className); - builder.append("::"); + builder.append(CLASS_DELIMITER); } builder.append(methodName); if (includeParams && !paramNames.isEmpty()) { @@ -155,4 +191,9 @@ private String getExtendedMethodName(boolean includeParams, boolean includeRetur private String constructClassAndMethodNameWithParams() { return getExtendedMethodName(true, false); } + + @Override + public String toString() { + return String.format("Range(lo=0x%05x hi=0x%05x %s %s:%d)", lo, hi, constructClassAndMethodNameWithParams(), getFileAsPath(), line); + } } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/StringTable.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/StringTable.java index 4b8537228aec..ea5975b4cd5f 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/StringTable.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/StringTable.java @@ -46,7 +46,7 @@ public StringTable() { * Ensures a unique instance of a string exists in the table, inserting the supplied String if * no equivalent String is already present. This should only be called before the string section * has been written. - * + * * @param string the string to be included in the table * @return the unique instance of the String */ @@ -58,7 +58,7 @@ public String uniqueString(String string) { * Ensures a unique instance of a string exists in the table and is marked for inclusion in the * debug_str section, inserting the supplied String if no equivalent String is already present. * This should only be called before the string section has been written. - * + * * @param string the string to be included in the table and marked for inclusion in the * debug_str section * @return the unique instance of the String @@ -82,7 +82,7 @@ private String ensureString(String string, boolean addToStrSection) { /** * Retrieves the offset at which a given string was written into the debug_str section. This * should only be called after the string section has been written. - * + * * @param string the strng whose offset is to be retrieved * @return the offset or -1 if the string does not define an entry or the entry has not been * written to the debug_str section diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/PECoff.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/PECoff.java index 6925ab456f69..f057384794d8 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/PECoff.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/PECoff.java @@ -124,6 +124,7 @@ enum IMAGE_SECTION_HEADER { static final int IMAGE_SCN_LNK_NRELOC_OVFL = 0x01000000; + static final int IMAGE_SCN_MEM_DISCARDABLE = 0x02000000; static final int IMAGE_SCN_MEM_SHARED = 0x10000000; static final int IMAGE_SCN_MEM_EXECUTE = 0x20000000; static final int IMAGE_SCN_MEM_READ = 0x40000000; @@ -202,7 +203,8 @@ enum IMAGE_RELOCATION { static final int IMAGE_REL_AMD64_REL32_3 = 0x7; static final int IMAGE_REL_AMD64_REL32_4 = 0x8; static final int IMAGE_REL_AMD64_REL32_5 = 0x9; - + static final int IMAGE_REL_AMD64_SECTION = 0xa; + static final int IMAGE_REL_AMD64_SECREL = 0xb; } //@formatter:on // Checkstyle: resume diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/PECoffMachine.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/PECoffMachine.java index 7ec064e04c54..239e0637a020 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/PECoffMachine.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/PECoffMachine.java @@ -56,6 +56,8 @@ public static PECoffRelocationMethod getRelocation(PECoffMachine m, RelocationKi switch (k) { case DIRECT: switch (sizeInBytes) { + case 4: + return PECoffX86_64Relocation.ADDR32; case 8: return PECoffX86_64Relocation.ADDR64; default: @@ -68,6 +70,20 @@ public static PECoffRelocationMethod getRelocation(PECoffMachine m, RelocationKi default: throw new IllegalArgumentException("unsupported relocation type: " + k + " size: " + sizeInBytes); } + case SECTION: + switch (sizeInBytes) { + case 2: + return PECoffX86_64Relocation.SECTION; + default: + throw new IllegalArgumentException("unsupported relocation type: " + k + " size: " + sizeInBytes); + } + case SECREL: + switch (sizeInBytes) { + case 4: + return PECoffX86_64Relocation.SECREL; + default: + throw new IllegalArgumentException("unsupported relocation type: " + k + " size: " + sizeInBytes); + } default: case UNKNOWN: throw new IllegalArgumentException("cannot map unknown relocation kind to an PECoff x86-64 relocation type"); @@ -176,6 +192,54 @@ public long toLong() { return IMAGE_RELOCATION.IMAGE_REL_AMD64_ADDR64; } }, + ADDR32 { + @Override + public RelocationKind getKind() { + return RelocationKind.DIRECT; + } + + @Override + public int getRelocatedByteSize() { + return 4; + } + + @Override + public long toLong() { + return IMAGE_RELOCATION.IMAGE_REL_AMD64_ADDR32; + } + }, + SECREL { + @Override + public RelocationKind getKind() { + return RelocationKind.DIRECT; + } + + @Override + public int getRelocatedByteSize() { + return 4; + } + + @Override + public long toLong() { + return IMAGE_RELOCATION.IMAGE_REL_AMD64_SECREL; + } + }, + SECTION { + @Override + public RelocationKind getKind() { + return RelocationKind.DIRECT; + } + + @Override + public int getRelocatedByteSize() { + return 2; + } + + @Override + public long toLong() { + return IMAGE_RELOCATION.IMAGE_REL_AMD64_SECTION; + } + }, REL32 { @Override public RelocationKind getKind() { diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/PECoffObjectFile.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/PECoffObjectFile.java index 83ce337ae3d6..a633c89e351e 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/PECoffObjectFile.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/PECoffObjectFile.java @@ -39,10 +39,14 @@ import com.oracle.objectfile.LayoutDecisionMap; import com.oracle.objectfile.ObjectFile; import com.oracle.objectfile.SymbolTable; +import com.oracle.objectfile.debuginfo.DebugInfoProvider; import com.oracle.objectfile.io.AssemblyBuffer; import com.oracle.objectfile.io.OutputAssembler; import com.oracle.objectfile.pecoff.PECoff.IMAGE_FILE_HEADER; import com.oracle.objectfile.pecoff.PECoff.IMAGE_SECTION_HEADER; +import com.oracle.objectfile.pecoff.cv.CVDebugInfo; +import com.oracle.objectfile.pecoff.cv.CVSymbolSectionImpl; +import com.oracle.objectfile.pecoff.cv.CVTypeSectionImpl; /** * Represents a PECoff object file. @@ -379,6 +383,7 @@ public enum PECoffSectionFlag implements ValueEnum { READ(IMAGE_SECTION_HEADER.IMAGE_SCN_MEM_READ), WRITE(IMAGE_SECTION_HEADER.IMAGE_SCN_MEM_WRITE), EXECUTE(IMAGE_SECTION_HEADER.IMAGE_SCN_MEM_EXECUTE), + DISCARDABLE(IMAGE_SECTION_HEADER.IMAGE_SCN_MEM_DISCARDABLE), LINKER(IMAGE_SECTION_HEADER.IMAGE_SCN_LNK_INFO | IMAGE_SECTION_HEADER.IMAGE_SCN_LNK_REMOVE); private final int value; @@ -683,4 +688,42 @@ public PECoffRelocationTable getRelocationTable() { protected int getMinimumFileSize() { return 0; } + + @Override + public Section newDebugSection(String name, ElementImpl impl) { + PECoffSection coffSection = (PECoffSection) super.newDebugSection(name, impl); + coffSection.getFlags().add(PECoffSectionFlag.DISCARDABLE); + coffSection.getFlags().add(PECoffSectionFlag.READ); + coffSection.getFlags().add(PECoffSectionFlag.INITIALIZED_DATA); + impl.setElement(coffSection); + return coffSection; + } + + @Override + public void installDebugInfo(DebugInfoProvider debugInfoProvider) { + CVDebugInfo cvDebugInfo = new CVDebugInfo(getMachine(), getByteOrder()); + + // we need an implementation for each section + CVSymbolSectionImpl cvSymbolSectionImpl = cvDebugInfo.getCVSymbolSection(); + CVTypeSectionImpl cvTypeSectionImpl = cvDebugInfo.getCVTypeSection(); + + // now we can create the section elements with empty content + newDebugSection(cvSymbolSectionImpl.getSectionName(), cvSymbolSectionImpl); + newDebugSection(cvTypeSectionImpl.getSectionName(), cvTypeSectionImpl); + + // the byte[] for each implementation's content are created and + // written under getOrDecideContent. doing that ensures that all + // dependent sections are filled in and then sized according to the + // declared dependencies. however, if we leave it at that then + // associated reloc sections only get created when the first reloc + // is inserted during content write that's too late for them to have + // layout constraints included in the layout decision set and causes + // an NPE during reloc section write. so we need to create the relevant + // reloc sections here in advance + cvSymbolSectionImpl.getOrCreateRelocationElement(false); + cvTypeSectionImpl.getOrCreateRelocationElement(false); + + // ok now we can populate the implementations + cvDebugInfo.installDebugInfo(debugInfoProvider); + } } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVConstants.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVConstants.java new file mode 100644 index 000000000000..5e1cd4d571a5 --- /dev/null +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVConstants.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, Red Hat Inc. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.objectfile.pecoff.cv; + +public abstract class CVConstants { + + /* names of relevant CodeView sections */ + static final String CV_SYMBOL_SECTION_NAME = ".debug$S"; + static final String CV_TYPE_SECTION_NAME = ".debug$T"; + // static final String CV_RDATA_SECTION_NAME = ".rdata"; + // static final String CV_PDATA_SECTION_NAME = ".pdata"; + // static final String CV_XDATA_SECTION_NAME = ".xdata"; + // static final String TEXT_SECTION_NAME = ".text"; + // static final String DATA_SECTION_NAME = ".data"; + + /* CodeView section header signature */ + static final int CV_SIGNATURE_C13 = 4; + + /* + * Knobs + * + * (some may become Graal options in the future) + */ + + /* + * path to JDK source code (for example unzipped src.zip) If set, source paths for JDK classes + * in the object file will be $JDK_SOURCE_BASE/java/package/someclass.java instead of (cache + * directory)/sources/jdk/java/package/someclass.java or (if source cache is disabled) + * java/package/someclass.java + * + * example JDK_SOURCE_BASE = C:\\tmp\\graal-8\\jdk8_jvmci\\src\\"; + */ + static final String JDK_SOURCE_BASE = ""; + + /* + * path to Graal source code base (for examplke checked out Graal source repository) if set + * source paths will be inferred from appropriate Graal package directories (behaves similarly + * to JDK_SOURCE_BASE) + * + * Example: GRAAL_SOURCE_BASE = "C:\\tmp\\graal-8\\graal8\\"; + */ + static final String GRAAL_SOURCE_BASE = ""; + + /* + * if true, don't emit debug code for Graal classes. + */ + static final boolean skipGraalInternals = false; + + /* + * (unimplemented) if true, don't emit debug code for JDK classes. + */ + static final boolean skipJDKInternals = false; + + /* + * if true, Graal inlined code treated as user generated code. (less complicated for user-level + * debugging) + */ + static final boolean skipGraalIntrinsics = false; + + /* + * if a line record is the same line in the same file as the previous record, merge them. + */ + static final boolean mergeAdjacentLineRecords = true; + + /* + * if true, first main() does not have args in the debug name. + */ + static final boolean emitUnadornedMain = true; + + /* + * if true, first main() becomes this name (with no class name or arg list at all) (set null to + * disable). + */ + static final String replaceMainFunctionName = null; + + /* + * The standard link.exe can't handle odd characters (parentheses or commas, for example) in + * external names. Setting functionNamesHashArgs true replaces those names, so that + * "Foo.function(String[] args)" becomes "Foo.function_617849326". If functionNamesHashArgs is + * false, currently the linker will fail. + * + * if true, arg lists become obscure integers (and link.exe will work properly) TODO: strip + * illegal characters from arg lists instead + */ + static final boolean functionNamesHashArgs = true; +} diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVDebugConstants.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVDebugConstants.java new file mode 100644 index 000000000000..ebf3f51a49ec --- /dev/null +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVDebugConstants.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, Red Hat Inc. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.objectfile.pecoff.cv; + +public abstract class CVDebugConstants { + + // static final int DEBUG_S_IGNORE = 0x00; + static final int DEBUG_S_SYMBOLS = 0xf1; + static final int DEBUG_S_LINES = 0xf2; + static final int DEBUG_S_STRINGTABLE = 0xf3; + static final int DEBUG_S_FILECHKSMS = 0xf4; + + /* subcommands in DEBUG_S_SYMBOLS section */ + // static final short S_COMPILE = 0x0001; + static final short S_SSEARCH = 0x0005; + static final short S_END = 0x0006; + static final short S_OBJNAME = 0x1101; + static final short S_LDATA32_ST = 0x1007; + static final short S_FRAMEPROC = 0x1012; + static final short S_CONSTANT = 0x1107; + static final short S_UDT = 0x1108; + static final short S_LDATA32 = 0x110c; + static final short S_GDATA32 = 0x110d; + static final short S_GPROC32 = 0x1110; + static final short S_REGREL32 = 0x1111; + static final short S_COMPILE3 = 0x113c; + static final short S_ENVBLOCK = 0x113d; + static final short S_GPROC32_ID = 0x1147; + static final short S_PROC_ID_END = 0x114f; + // static final short S_BUILDINFO = 0x114c; + + /* enums are more typesafe but the IDE no longer knows which enum constant is unused */ + @SuppressWarnings("unused") + enum CV_RECORD { + CV_SIGNATURE_C13(4), + S_COMPILE(0x0001), + S_SSEARCH(0x0005), + S_END(0x0006), + S_OBJNAME(0x1101), + S_LDATA32_ST(0x1007), + S_FRAMEPROC(0x1012), + S_CONSTANT(0x1107), + S_UDT(0x1108), + S_LDATA32(0x110c), + S_GDATA32(0x110d), + S_GPROC32(0x1110), + S_REGREL32(0x1111), + S_COMPILE3(0x113c), + S_ENVBLOCK(0x113d); + + final int cmd; + + CV_RECORD(int cmd) { + this.cmd = cmd; + } + + public int command() { + return cmd; + } + } + + @SuppressWarnings("unused") + enum DEBUG_S { + DEBUG_S_IGNORE(0x00), + DEBUG_S_SYMBOLS(0xf1), + DEBUG_S_LINES(0xf2), + DEBUG_S_STRINGTABLE(0xf3), + DEBUG_S_FILECHKSMS(0xf4); + + final short cmd; + + DEBUG_S(int cmd) { + this.cmd = (short) cmd; + } + + public short command() { + return cmd; + } + } +} diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVDebugInfo.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVDebugInfo.java new file mode 100644 index 000000000000..0bf60cab5103 --- /dev/null +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVDebugInfo.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, Red Hat Inc. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.objectfile.pecoff.cv; + +import com.oracle.objectfile.debugentry.DebugInfoBase; +import com.oracle.objectfile.pecoff.PECoffMachine; + +import java.nio.ByteOrder; + +/** + * CVDebugInfo is a container class for all the CodeView sections to be emitted in the object file. + * Currently, that is .debug$S (CVSymbolSectionImpl) and .debug$T (CVTypeSectionImpl) Common data + * (useful to more than one CodeView section) goes here + */ +public final class CVDebugInfo extends DebugInfoBase { + + @SuppressWarnings("unused") private PECoffMachine machine; + private CVSymbolSectionImpl cvSymbolSection; + private CVTypeSectionImpl cvTypeSection; + + public CVDebugInfo(PECoffMachine targetMachine, ByteOrder byteOrder) { + super(byteOrder); + machine = targetMachine; + cvSymbolSection = new CVSymbolSectionImpl(this); + cvTypeSection = new CVTypeSectionImpl(); + } + + public CVSymbolSectionImpl getCVSymbolSection() { + return cvSymbolSection; + } + + public CVTypeSectionImpl getCVTypeSection() { + return cvTypeSection; + } + +} diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVFileRecord.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVFileRecord.java new file mode 100644 index 000000000000..7d0fb971702a --- /dev/null +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVFileRecord.java @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, Red Hat Inc. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.objectfile.pecoff.cv; + +import com.oracle.objectfile.debugentry.FileEntry; + +import java.io.PrintStream; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Paths; +import java.security.MessageDigest; +import java.util.LinkedHashMap; +import java.util.Map; + +final class CVFileRecord extends CVSymbolRecord { + + private static final byte CHECKSUM_NONE = 0x00; + private static final byte CHECKSUM_MD5 = 0x01; + private static final byte CB_VALUE = 0x10; + + private static final int FILE_RECORD_LENGTH = 24; + + private static final int CHECKSUM_LENGTH = 16; + private static final byte[] EMPTY_CHECKSUM = new byte[CHECKSUM_LENGTH]; + + private static final int FILE_TABLE_INITIAL_SIZE = 200; + + private final CVSymbolSectionImpl.CVStringTable strings; + + private int currentOffset = 0; + private Map fileEntryToOffsetMap = new LinkedHashMap<>(FILE_TABLE_INITIAL_SIZE); + + CVFileRecord(CVDebugInfo cvDebugInfo, CVSymbolSectionImpl.CVStringTable strings) { + super(cvDebugInfo, CVDebugConstants.DEBUG_S_FILECHKSMS); + this.strings = strings; + } + + /* + * Convert a simple path into an absolute path by determining if it's part of Graal, the JDK, or + * use code. + * + * Currently, don't even try; use the SourceCache system + */ + private static String fixPath(FileEntry fileEntry) { + final String fn; + if (fileEntry.getDirEntry() == null) { + fn = fileEntry.getFileName(); + } else { + fn = fileEntry.getFullName(); + } + return fn; + } + + int addFile(FileEntry entry) { + if (fileEntryToOffsetMap.containsKey(entry)) { + return fileEntryToOffsetMap.get(entry); + } else { + fileEntryToOffsetMap.put(entry, currentOffset); + /* create required stringtable entry */ + strings.add(fixPath(entry)); + currentOffset += FILE_RECORD_LENGTH; + return currentOffset - FILE_RECORD_LENGTH; + } + } + + @Override + public int computeSize(int initialPos) { + /* add all fileEntries; duplicates are ignored */ + /* + * probably don't need to do this because if it isn't already here it's probably referenced + * by the debug info + */ + /* consider moving this to CVSymbolSectionImpl */ + for (FileEntry entry : cvDebugInfo.getFiles()) { + addFile(entry); + } + return initialPos + (fileEntryToOffsetMap.size() * FILE_RECORD_LENGTH); + } + + @Override + public int computeContents(byte[] buffer, int initialPos) { + int pos = initialPos; + for (FileEntry entry : fileEntryToOffsetMap.keySet()) { + pos = put(entry, buffer, pos); + } + return pos; + } + + private int put(FileEntry entry, byte[] buffer, int initialPos) { + String fn = fixPath(entry); + int stringId = strings.add(fn); + int pos = CVUtil.putInt(stringId, buffer, initialPos); /* stringtable index */ + pos = CVUtil.putByte(CB_VALUE, buffer, pos); /* Cb (unknown what this is) */ + byte[] checksum = calculateMD5Sum(fn); + if (checksum != null) { + pos = CVUtil.putByte(CHECKSUM_MD5, buffer, pos); /* checksum type (0x01 == MD5) */ + pos = CVUtil.putBytes(checksum, buffer, pos); + } else { + pos = CVUtil.putByte(CHECKSUM_NONE, buffer, pos); + pos = CVUtil.putBytes(EMPTY_CHECKSUM, buffer, pos); + } + pos = CVUtil.align4(pos); + return pos; + } + + private static byte[] calculateMD5Sum(String fn) { + try { + MessageDigest md = MessageDigest.getInstance("MD5"); + md.update(Files.readAllBytes(Paths.get(fn))); + return md.digest(); + } catch (NoSuchFileException e) { + return null; + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + @Override + public String toString() { + return "CVFileRecord(type=" + type + ",pos=" + recordStartPosition + ", size=" + 999 + ")"; + } + + @Override + public void dump(PrintStream out) { + int idx = 0; + int offset = 0; + out.format("%s:\n", this); + for (FileEntry entry : fileEntryToOffsetMap.keySet()) { + out.format("%4d 0x%08x %2d %2d %s\n", idx, offset, 0x10, 1, entry.getFileName()); + idx += 1; + offset += 24; + } + } +} diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVLineRecord.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVLineRecord.java new file mode 100644 index 000000000000..d15468cf48b2 --- /dev/null +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVLineRecord.java @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, Red Hat Inc. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.objectfile.pecoff.cv; + +import com.oracle.objectfile.debugentry.FileEntry; +import com.oracle.objectfile.debugentry.PrimaryEntry; +import com.oracle.objectfile.ObjectFile; + +import java.util.ArrayList; + +/* + * a line record (DEBUG_S_LINES) consists of a list of (file block record + subrecords) + * Graal will generate one CVLineRecord per function. + */ +final class CVLineRecord extends CVSymbolRecord { + + private static final boolean HAS_COLUMNS = false; + + private static final int DEFAULT_LINE_BLOCK_COUNT = 100; + private static final int DEFAULT_LINE_ENTRY_COUNT = 100; + + private static final short CB_HAS_COLUMNS_FLAG = 0x01; + private static final short CB_HAS_NO_COLUMNS_FLAG = 0x00; + + private String symbolName; + @SuppressWarnings("unused") private PrimaryEntry primaryEntry; + private ArrayList fileBlocks = new ArrayList<>(DEFAULT_LINE_BLOCK_COUNT); + + /* + * FileBlock is a section of contiguous code in a compilation unit, associated with a single + * source file. if a function includes inlined code, that code needs its own FileBlock, + * surrounded by FileBlocks describing the enclosing source file A FileBlock consists of a list + * of LineEntries + */ + private static class FileBlock { + + ArrayList lineEntries = new ArrayList<>(DEFAULT_LINE_ENTRY_COUNT); + int highAddr = 0; + @SuppressWarnings("unused") FileEntry file; + int fileId; + + FileBlock(FileEntry file, int fileId) { + this.file = file; + this.fileId = fileId; + } + + void addEntry(LineEntry le) { + highAddr = Math.max(highAddr, le.addr); + lineEntries.add(le); + } + + int computeContents(byte[] buffer, int initialPos) { + int pos = initialPos; + pos = CVUtil.putInt(fileId, buffer, pos); + pos = CVUtil.putInt(lineEntries.size(), buffer, pos); + /* if HAS_COLUMNS is true, this formula is incorrect */ + assert !HAS_COLUMNS; + pos = CVUtil.putInt(Integer.BYTES * 3 + lineEntries.size() * LineEntry.LINE_ENTRY_SIZE, buffer, pos); + for (LineEntry lineEntry : lineEntries) { + pos = lineEntry.computeContents(buffer, pos); + } + return pos; + } + + int computeSize(int initialPos) { + /* if HAS_COLUMNS is true, this formula is incorrect */ + assert !HAS_COLUMNS; + return initialPos + Integer.BYTES * 3 + LineEntry.LINE_ENTRY_SIZE * lineEntries.size(); + } + + int getHighAddr() { + return highAddr; + } + } + + /* + * LineEntry associates some object code (at 'addr', relative to the start of this DEBUG_S_LINES + * record) with a source line in the current FileBlock file + */ + static class LineEntry { + + static final int LINE_ENTRY_SIZE = 2 * Integer.BYTES; + + int addr; + int lineAndFLags; + + LineEntry(int addr, int line, int deltaEnd, boolean isStatement) { + this.addr = addr; + assert line <= 0xffffff; + assert line >= 0; + assert deltaEnd <= 0x7f; + assert deltaEnd >= 0; + lineAndFLags = line | (deltaEnd << 24) | (isStatement ? 0x80000000 : 0); + } + + LineEntry(int addr, int line) { + this.addr = addr; + this.lineAndFLags = line; + } + + int computeContents(byte[] buffer, int initialPos) { + int pos = initialPos; + pos = CVUtil.putInt(addr, buffer, pos); + pos = CVUtil.putInt(lineAndFLags, buffer, pos); + return pos; + } + } + + CVLineRecord(CVDebugInfo cvDebugInfo, String symbolName, PrimaryEntry entry) { + super(cvDebugInfo, CVDebugConstants.DEBUG_S_LINES); + this.primaryEntry = entry; + this.symbolName = symbolName; + } + + void addNewFile(FileEntry file) { + CVFileRecord fr = cvDebugInfo.getCVSymbolSection().getFileRecord(); + int fileId = fr.addFile(file); + fileBlocks.add(new FileBlock(file, fileId)); + } + + void addNewLine(int addr, int line) { + fileBlocks.get(fileBlocks.size() - 1).addEntry(new LineEntry(addr, line)); + } + + @Override + protected int computeSize(int initialPos) { + /* header */ + int pos = initialPos + Integer.BYTES + Short.BYTES * 2 + Integer.BYTES; + /* all blocks */ + for (FileBlock fileBlock : fileBlocks) { + pos = fileBlock.computeSize(pos); + } + return pos; + } + + @Override + protected int computeContents(byte[] buffer, int initialPos) { + int pos = initialPos; + + assert symbolName != null; + /* can't handle columns yet */ + assert !HAS_COLUMNS; + + if (buffer != null) { + cvDebugInfo.getCVSymbolSection().markRelocationSite(pos, 4, ObjectFile.RelocationKind.SECREL, symbolName, false, 1L); + } + pos = CVUtil.putInt(0, buffer, pos); + + if (buffer != null) { + cvDebugInfo.getCVSymbolSection().markRelocationSite(pos, 2, ObjectFile.RelocationKind.SECTION, symbolName, false, 1L); + } + pos = CVUtil.putShort((short) 0, buffer, pos); + + final short flags = HAS_COLUMNS ? CB_HAS_COLUMNS_FLAG : CB_HAS_NO_COLUMNS_FLAG; + pos = CVUtil.putShort(flags, buffer, pos); /* flags */ + + /* + * highAddr = length of this chunk in object file (fill in correctly later) + */ + final int cbConPos = pos; /* save position of length int32 */ + int highAddr = 0; + pos = CVUtil.putInt(highAddr, buffer, pos); + for (FileBlock fileBlock : fileBlocks) { + highAddr = Math.max(highAddr, fileBlock.getHighAddr()); + pos = fileBlock.computeContents(buffer, pos); + } + CVUtil.putInt(highAddr, buffer, cbConPos); + return pos; + } + + @Override + public String toString() { + return String.format("CVLineRecord(type=0x%04x pos=0x%05x size=0x%d)", type, recordStartPosition, fileBlocks.size()); + } +} diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVLineRecordBuilder.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVLineRecordBuilder.java new file mode 100644 index 000000000000..c9b3b40c9abc --- /dev/null +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVLineRecordBuilder.java @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, Red Hat Inc. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.objectfile.pecoff.cv; + +import com.oracle.objectfile.debugentry.FileEntry; +import com.oracle.objectfile.debugentry.PrimaryEntry; +import com.oracle.objectfile.debugentry.Range; + +import static com.oracle.objectfile.pecoff.cv.CVConstants.mergeAdjacentLineRecords; +import static com.oracle.objectfile.pecoff.cv.CVConstants.skipGraalInternals; +import static com.oracle.objectfile.pecoff.cv.CVConstants.skipGraalIntrinsics; + +public class CVLineRecordBuilder { + + private static final boolean HAS_COLUMNS = false; + + private CVDebugInfo cvDebugInfo; + private CVLineRecord lineRecord; + private PrimaryEntry primaryEntry; + + CVLineRecordBuilder(CVDebugInfo cvDebugInfo) { + this.cvDebugInfo = cvDebugInfo; + } + + public static void debug(@SuppressWarnings("unused") String format, @SuppressWarnings("unused") Object... args) { + // System.out.format(format, args); + } + + /* + * In CV4, the line table consists of a series of file headers followed by line number entries + * to handle this, first we decide if we want to merge this with the previous range (only if + * same file and start of this range is end of previous range) if we are emitting a new range to + * the same file, write the range, save it as the previous range and go on If this is a + * different file, then update the length of the previous file header, write the new file header + * and write the new range At the very end, make sure we update the last file header. + * + * In addition, optionally ignore Ranges that point into Graal innards, just adding them to the + * current enclosing range + */ + + /** + * Build line number records for a function. + * + * @param entry function to build line number table for + * @return CVLineRecord containing any entries generated, or null if no entries generated + */ + @SuppressWarnings("unused") + CVLineRecord build(PrimaryEntry entry, String methodName) { + // long lowAddr = Long.MAX_VALUE; + // long highAddr = 0; + this.primaryEntry = entry; + + assert (!HAS_COLUMNS); /* can't handle columns yet */ + + Range primaryRange = primaryEntry.getPrimary(); + Range previousRange = null; + + /* option to not even bother with debug code for Graal */ + if (skipGraalInternals && CVRootPackages.isGraalClass(primaryRange.getClassName())) { + debug("skipping Graal internal class %s\n", primaryRange); + return null; + } + debug("DEBUG_S_LINES linerecord for 0x%05x file: %s:%d\n", primaryRange.getLo(), primaryRange.getFileName(), primaryRange.getLine()); + this.lineRecord = new CVLineRecord(cvDebugInfo, methodName, primaryEntry); + debug("CVLineRecord.computeContents: processing primary range %s\n", primaryRange); + previousRange = processRange(primaryRange, previousRange); + // lowAddr = Math.min(lowAddr, primaryRange.getLo()); + // highAddr = Math.max(highAddr, primaryRange.getHi()); + + for (Range subRange : primaryEntry.getSubranges()) { + debug("CVLineRecord.computeContents: processing range %s\n", subRange); + FileEntry subFileEntry = primaryEntry.getSubrangeFileEntry(subRange); + if (subFileEntry == null) { + continue; + } + previousRange = processRange(subRange, previousRange); + // lowAddr = Math.min(lowAddr, subRange.getLo()); + // highAddr = Math.max(highAddr, subRange.getHi()); + } + return lineRecord; + } + + /** + * Merge input Range structures into line number table. The Range structures are assumed to be + * ordered by ascending address merge with previous line entry if: - if a Range has a negative + * linenumber - if a range is part of Graal or the JDK, and skipGraalOption is true - if a range + * has the same line number, source file and function + * + * @param range to be merged or added to line number record + * @param oldPreviousRange the previously processed Range + * @return new value for previousRange in caller + */ + private Range processRange(Range range, Range oldPreviousRange) { + + Range previousRange = oldPreviousRange; + + /* should we merge this range with the previous entry? */ + /* i.e. same line in same file, same class and function */ + if (shouldMerge(range, previousRange)) { + debug("processRange: merging with previous\n"); + return previousRange; + // range = new Range(previousRange, range.getLo(), range.getHi()); + } /* + * else if (range.getLine() == -1) { CVUtil.debug( + * " processRange: ignoring: bad line number\n"); return previousRange; } + */ + + /* is this a new file? if so we emit a new file record */ + boolean wantNewFile = previousRange == null || !previousRange.getFileAsPath().equals(range.getFileAsPath()); + if (wantNewFile) { + FileEntry file = cvDebugInfo.findFile(range.getFileAsPath()); + if (file != null && file.getFileName() != null) { + previousRange = null; + debug("processRange: addNewFile: %s\n", file); + lineRecord.addNewFile(file); + } else { + debug("processRange: range has no file: %s\n", range); + return previousRange; + } + } + + if (wantNewRange(range, previousRange)) { + previousRange = range; + int lineLoAddr = range.getLo() - primaryEntry.getPrimary().getLo(); + int line = Math.max(range.getLine(), 1); + debug("processRange: addNewLine: 0x%05x %s\n", lineLoAddr, line); + lineRecord.addNewLine(lineLoAddr, line); + } + return previousRange; + } + + /** + * Test to see if two ranges are adjacent, and can be combined into one. + * + * @param previousRange the first range (lower address) + * @param range the second range (higher address) + * @return true if the two ranges can be combined + */ + @SuppressWarnings("unused") + private boolean shouldMerge(Range range, Range previousRange) { + if (!mergeAdjacentLineRecords) { + return false; + } + if (previousRange == null) { + return false; + } + /* if we're in a different class that the primary Class, this is inlined code */ + final boolean isInlinedCode = !range.getClassName().equals(primaryEntry.getClassEntry().getClassName()); + // if (isInlinedCode && skipInlinedCode) { return true; } + if (isInlinedCode && skipGraalIntrinsics && CVRootPackages.isGraalIntrinsic(range.getClassName())) { + return true; + } + return previousRange.getFileAsPath().equals(range.getFileAsPath()) && (range.getLine() == -1 || previousRange.getLine() == range.getLine()); + } + + /** + * Test to see if a new line record should be emitted. + * + * @param previousRange previous range + * @param range current range + * @return true if the current range is on a different line or file from the previous one + */ + private static boolean wantNewRange(@SuppressWarnings("unused") Range range, @SuppressWarnings("unused") Range previousRange) { + /* return true for now; this will be a further optimization (see unused_wantNewRange) */ + return true; + } + + /** + * Test to see if a new line record should be emitted. + * + * @param previousRange previous range + * @param range current range + * @return true if the current range is on a different line or file from the previous one + */ + @SuppressWarnings("unused") + private static boolean unusedWantNewRange(@SuppressWarnings("unused") Range range, @SuppressWarnings("unused") Range previousRange) { + if (previousRange == null) { + return true; + } + if (previousRange.getLine() != range.getLine()) { + return true; + } + if (previousRange.getFilePath() != range.getFilePath()) { + return true; + } + /* it might actually be fine to merge if there's a gap between ranges */ + // if (previousRange.getHi() < range.getLo()) { + // return true; + // } + return false; + } +} diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVRootPackages.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVRootPackages.java new file mode 100644 index 000000000000..98368acb6e29 --- /dev/null +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVRootPackages.java @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, Red Hat Inc. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.objectfile.pecoff.cv; + +import java.util.Collections; +import java.util.HashSet; + +abstract class CVRootPackages { + private static final String[] rootPackageNames = { + /* substrate root packages */ + "com.oracle.graal.pointsto", + "com.oracle.objectfile", + "com.oracle.svm.agent", + "com.oracle.svm.configure", + "com.oracle.svm.core", + "com.oracle.svm.core.genscavenge", + "com.oracle.svm.core.graal", + "com.oracle.svm.core.graal.aarch64", + "com.oracle.svm.core.graal.amd64", + "com.oracle.svm.core.graal.llvm", + "com.oracle.svm.core.jdk11", + "com.oracle.svm.core.jdk8", + "com.oracle.svm.core.posix", + "com.oracle.svm.core.posix.jdk11", + "com.oracle.svm.core.windows", + "com.oracle.svm.driver", + "com.oracle.svm.graal", + "com.oracle.svm.graal.hotspot.libgraal", + "com.oracle.svm.hosted", + "com.oracle.svm.jline", + "com.oracle.svm.jni", + "com.oracle.svm.junit", + "com.oracle.svm.libffi", + "com.oracle.svm.native.jvm.posix", + "com.oracle.svm.native.jvm.windows", + "com.oracle.svm.native.libchelper", + "com.oracle.svm.native.strictmath", + "com.oracle.svm.polyglot", + "com.oracle.svm.reflect", + "com.oracle.svm.test", + "com.oracle.svm.test.jdk11", + "com.oracle.svm.thirdparty", + "com.oracle.svm.truffle", + "com.oracle.svm.truffle.nfi", + "com.oracle.svm.truffle.nfi.posix", + "com.oracle.svm.truffle.nfi.windows", + "com.oracle.svm.tutorial", + "com.oracle.svm.util", + "com.oracle.svm.util.jdk11", + "org.graalvm.polyglot.nativeapi", + /* compiler root packages */ + "jdk.tools.jaotc", + "jdk.tools.jaotc.binformat", + "jdk.tools.jaotc", + "org.graalvm.compiler.api.directives", + "org.graalvm.compiler.api.replacements", + "org.graalvm.compiler.api.runtime", + "org.graalvm.compiler.asm", + "org.graalvm.compiler.asm.aarch64", + "org.graalvm.compiler.asm.amd64", + "org.graalvm.compiler.asm.sparc", + "org.graalvm.compiler.bytecode", + "org.graalvm.compiler.code", + "org.graalvm.compiler.core", + "org.graalvm.compiler.core.aarch64", + "org.graalvm.compiler.core.amd64", + "org.graalvm.compiler.core.common", + "org.graalvm.compiler.core.llvm", + "org.graalvm.compiler.core.match.processor", + "org.graalvm.compiler.core.sparc", + "org.graalvm.compiler.debug", + "org.graalvm.compiler.graph", + "org.graalvm.compiler.hotspot", + "org.graalvm.compiler.hotspot.aarch64", + "org.graalvm.compiler.hotspot.amd64", + "org.graalvm.compiler.hotspot.jdk8", + "org.graalvm.compiler.hotspot.management", + "org.graalvm.compiler.hotspot.sparc", + "org.graalvm.compiler.java", + "org.graalvm.compiler.jtt", + "org.graalvm.compiler.lir", + "org.graalvm.compiler.lir.aarch64", + "org.graalvm.compiler.lir.amd64", + "org.graalvm.compiler.lir.jtt", + "org.graalvm.compiler.lir.sparc", + "org.graalvm.compiler.loop", + "org.graalvm.compiler.loop.phases", + "org.graalvm.compiler.microbenchmarks", + "org.graalvm.compiler.nodeinfo", + "org.graalvm.compiler.nodeinfo.processor", + "org.graalvm.compiler.nodes", + "org.graalvm.compiler.options", + "org.graalvm.compiler.options.processor", + "org.graalvm.compiler.phases", + "org.graalvm.compiler.phases.common", + "org.graalvm.compiler.printer", + "org.graalvm.compiler.processor", + "org.graalvm.compiler.replacements", + "org.graalvm.compiler.replacements.aarch64", + "org.graalvm.compiler.replacements.amd64", + "org.graalvm.compiler.replacements.processor", + "org.graalvm.compiler.replacements.sparc", + "org.graalvm.compiler.runtime", + "org.graalvm.compiler.serviceprovider", + "org.graalvm.compiler.serviceprovider.jdk8", + "org.graalvm.compiler.serviceprovider.processor", + "org.graalvm.compiler.truffle.common", + "org.graalvm.compiler.truffle.common.hotspot", + "org.graalvm.compiler.truffle.common.hotspot.libgraal", + "org.graalvm.compiler.truffle.common.processor", + "org.graalvm.compiler.truffle.compiler", + "org.graalvm.compiler.truffle.compiler.amd64", + "org.graalvm.compiler.truffle.compiler.hotspot", + "org.graalvm.compiler.truffle.compiler.hotspot.aarch64", + "org.graalvm.compiler.truffle.compiler.hotspot.amd64", + "org.graalvm.compiler.truffle.compiler.hotspot.libgraal", + "org.graalvm.compiler.truffle.compiler.hotspot.libgraal.processor", + "org.graalvm.compiler.truffle.compiler.hotspot.sparc", + "org.graalvm.compiler.truffle.runtime", + "org.graalvm.compiler.truffle.runtime.hotspot", + "org.graalvm.compiler.truffle.runtime.hotspot.java", + "org.graalvm.compiler.truffle.runtime.hotspot.jdk8+13", + "org.graalvm.compiler.truffle.runtime.hotspot.libgraal", + "org.graalvm.compiler.truffle.runtime.serviceprovider", + "org.graalvm.compiler.truffle.runtime.serviceprovider.jdk8", + "org.graalvm.compiler.virtual", + "org.graalvm.compiler.virtual.bench", + "org.graalvm.compiler.word", + "org.graalvm.graphio", + "org.graalvm.libgraal", + "org.graalvm.libgraal.jdk8", + "org.graalvm.micro.benchmarks", + "org.graalvm.util", + }; + + private static final String[] intrinsicClassNames = { + "com.oracle.svm.core.genscavenge.AlignedHeapChunk", + "com.oracle.svm.core.genscavenge.CardTable", + "com.oracle.svm.core.genscavenge.ObjectHeaderImpl", + "com.oracle.svm.core.genscavenge.graal.GenScavengeAllocationSnippets", + "com.oracle.svm.core.genscavenge.graal.BarrierSnippets", + "com.oracle.svm.core.snippets.KnownIntrinsics", + "com.oracle.svm.core.graal.snippets.SubstrateAllocationSnippets", + "com.oracle.svm.core.threadlocal.FastThreadLocalBytes", + "org.graalvm.compiler.replacements.AllocationSnippets", + "org.graalvm.compiler.nodes.PrefetchAllocateNode", + "com.oracle.svm.core.os.CopyingImageHeapProvider" + }; + + private static final HashSet rootPackageSet; + private static final HashSet intrinsicClassNameSet; + + static { + rootPackageSet = new HashSet<>(rootPackageNames.length); + Collections.addAll(rootPackageSet, rootPackageNames); + intrinsicClassNameSet = new HashSet<>(intrinsicClassNames.length); + Collections.addAll(intrinsicClassNameSet, intrinsicClassNames); + } + + static boolean isGraalPackage(String pn) { + return rootPackageSet.contains(pn); + } + + private static String getPackagename(String className) { + return className.contains(".") ? className.substring(0, className.lastIndexOf('.')) : className; + } + + static boolean isGraalClass(String cn) { + final String pn = getPackagename(cn); + return isGraalPackage(pn); + } + + /** + * is class a Graal intrinsic class? + * + * @param cn class name of code + * @return true if this is Graal intrinsic code + */ + static boolean isGraalIntrinsic(String cn) { + return intrinsicClassNameSet.contains(cn); + } + + static boolean isJavaPackage(String pn) { + /* TODO : make more accurate */ + return pn.startsWith("java.") || pn.startsWith("javax.") || pn.startsWith("sun."); + } + + @SuppressWarnings("unused") + private static boolean isJavaFile(String pn) { + /* TODO : make more accurate */ + return pn.startsWith("java/") || pn.startsWith("javax/") || pn.startsWith("sun/"); + } +} diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVSectionImpl.java new file mode 100644 index 000000000000..7ad66b796dd8 --- /dev/null +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVSectionImpl.java @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, Red Hat Inc. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.objectfile.pecoff.cv; + +import com.oracle.objectfile.BasicProgbitsSectionImpl; +import com.oracle.objectfile.BuildDependency; +import com.oracle.objectfile.LayoutDecision; +import com.oracle.objectfile.LayoutDecisionMap; +import com.oracle.objectfile.ObjectFile; +import com.oracle.objectfile.pecoff.PECoffObjectFile; +import org.graalvm.compiler.debug.DebugContext; + +import java.util.Map; +import java.util.Set; + +abstract class CVSectionImpl extends BasicProgbitsSectionImpl { + + boolean debug = false; + long debugTextBase = 0; + long debugAddress = 0; + int debugBase = 0; + + CVSectionImpl() { + } + + @Override + public void setElement(ObjectFile.Element e) { + super.setElement(e); + /* define the section as a COFF symbol */ + getOwner().createDefinedSymbol(getSectionName(), getElement(), 0, 0, false, false); + } + + private String debugSectionLogName() { + /* + * Use prefix cv4 plus the section name (which already includes a dot separator) for the + * context key. For example messages for type section will be keyed using "cv4.debug$T". + * Other info formats use their own format-specific prefix. + */ + assert getSectionName().startsWith(".debug$"); + return "cv4" + getSectionName(); + } + + protected void enableLog(DebugContext context, int pos) { + /* + * Unlike in the Dwarf debug code, debug output is enabled in both the sizing and writing + * phases. At this time, debugBase and debugAddress aren't used but are there for the + * future. + */ + if (context.areScopesEnabled()) { + debug = true; + debugBase = pos; + debugAddress = debugTextBase; + } + } + + @Override + public int getAlignment() { + return 1; + } + + protected void log(DebugContext context, String format, Object... args) { + if (debug) { + context.logv(DebugContext.INFO_LEVEL, format, args); + } + } + + protected void verboseLog(DebugContext context, String format, Object... args) { + if (debug) { + context.logv(DebugContext.VERBOSE_LEVEL, format, args); + } + } + + @Override + public byte[] getOrDecideContent(Map alreadyDecided, byte[] contentHint) { + + /* ensure content byte[] has been created before calling super method */ + getOwner().debugContext(debugSectionLogName(), this::createContent); + + /* ensure content byte[] has been written before calling super method */ + getOwner().debugContext(debugSectionLogName(), this::writeContent); + + return super.getOrDecideContent(alreadyDecided, contentHint); + } + + @Override + public Set getDependencies(Map decisions) { + Set deps = super.getDependencies(decisions); + String targetName = getSectionName(); + @SuppressWarnings("unused") + PECoffObjectFile.PECoffSection targetSection = (PECoffObjectFile.PECoffSection) getElement().getOwner().elementForName(targetName); + LayoutDecision ourContent = decisions.get(getElement()).getDecision(LayoutDecision.Kind.CONTENT); + LayoutDecision ourSize = decisions.get(getElement()).getDecision(LayoutDecision.Kind.SIZE); + // LayoutDecision.Kind[] targetKinds = targetSectionKinds(); + /* make our content depend on the size and content of the target */ + // for (LayoutDecision.Kind targetKind : targetKinds) { + // LayoutDecision targetDecision = decisions.get(targetSection).getDecision(targetKind); + // deps.add(BuildDependency.createOrGet(ourContent, targetDecision)); + // } + /* make our size depend on our content */ + deps.add(BuildDependency.createOrGet(ourSize, ourContent)); + return deps; + } + + // public abstract LayoutDecision.Kind[] targetSectionKinds(); + public abstract void createContent(DebugContext debugContext); + + public abstract void writeContent(DebugContext debugContext); + + public abstract String getSectionName(); +} diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVStringTableRecord.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVStringTableRecord.java new file mode 100644 index 000000000000..5e2924e69c26 --- /dev/null +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVStringTableRecord.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, Red Hat Inc. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.objectfile.pecoff.cv; + +import java.io.PrintStream; + +final class CVStringTableRecord extends CVSymbolRecord { + + private final CVSymbolSectionImpl.CVStringTable stringTable; + + CVStringTableRecord(CVDebugInfo cvDebugInfo, CVSymbolSectionImpl.CVStringTable stringTable) { + super(cvDebugInfo, CVDebugConstants.DEBUG_S_STRINGTABLE); + this.stringTable = stringTable; + } + + int add(String string) { + return stringTable.add(string); + } + + @Override + public int computeSize(int pos) { + return computeContents(null, pos); + } + + @Override + public int computeContents(byte[] buffer, int initialPos) { + int pos = initialPos; + for (CVSymbolSectionImpl.CVStringTable.StringTableEntry entry : stringTable.values()) { + pos = CVUtil.putUTF8StringBytes(entry.text, buffer, pos); + } + return pos; + } + + @Override + public String toString() { + return String.format("CVStringTableRecord(type=0x%04x pos=0x%06x size=%d)", type, recordStartPosition, stringTable.size()); + } + + @Override + public void dump(PrintStream out) { + int idx = 0; + out.format("%s:\n", this); + for (CVSymbolSectionImpl.CVStringTable.StringTableEntry entry : stringTable.values()) { + out.format("%4d 0x%08x %s\n", idx, entry.offset, entry.text); + idx += 1; + } + } +} diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVSymbolRecord.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVSymbolRecord.java new file mode 100644 index 000000000000..ad84627d40fa --- /dev/null +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVSymbolRecord.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, Red Hat Inc. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.objectfile.pecoff.cv; + +import java.io.PrintStream; + +/* + * A Symbol record is a top-level record in the CodeView .debug$S section + */ +abstract class CVSymbolRecord { + + protected CVDebugInfo cvDebugInfo; + protected int recordStartPosition; + protected final int type; + + CVSymbolRecord(CVDebugInfo cvDebugInfo, int type) { + this.cvDebugInfo = cvDebugInfo; + this.type = type; + } + + int computeFullSize(int initialPos) { + this.recordStartPosition = initialPos; + int pos = initialPos + Integer.BYTES * 2; + return computeSize(pos); + } + + int computeFullContents(byte[] buffer, int initialPos) { + int pos = CVUtil.putInt(type, buffer, initialPos); + int lenPos = pos; + pos = computeContents(buffer, pos + Integer.BYTES); + /* length does not include debug record header (4 bytes record id + 4 bytes length) */ + CVUtil.putInt(pos - lenPos - Integer.BYTES, buffer, lenPos); + return pos; + } + + protected abstract int computeSize(int pos); + + protected abstract int computeContents(byte[] buffer, int pos); + + @Override + public String toString() { + return "CVSymbolRecord(type=" + type + ",pos=" + recordStartPosition + ")"; + } + + public void dump(PrintStream out) { + out.format("%s\n", this); + } + +} diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVSymbolRecordBuilder.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVSymbolRecordBuilder.java new file mode 100644 index 000000000000..955a2d9fb59b --- /dev/null +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVSymbolRecordBuilder.java @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, Red Hat Inc. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.objectfile.pecoff.cv; + +import com.oracle.objectfile.debugentry.ClassEntry; +import com.oracle.objectfile.debugentry.PrimaryEntry; +import com.oracle.objectfile.debugentry.Range; +import org.graalvm.compiler.debug.DebugContext; + +import static com.oracle.objectfile.pecoff.cv.CVConstants.functionNamesHashArgs; +import static com.oracle.objectfile.pecoff.cv.CVConstants.replaceMainFunctionName; +import static com.oracle.objectfile.pecoff.cv.CVConstants.emitUnadornedMain; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.T_NOTYPE; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.T_VOID; + +final class CVSymbolRecordBuilder { + + private final CVDebugInfo cvDebugInfo; + private final CVSymbolSubsection symbolRecord; + private DebugContext debugContext = null; + + CVSymbolRecordBuilder(CVDebugInfo cvDebugInfo) { + this.symbolRecord = new CVSymbolSubsection(cvDebugInfo); + this.cvDebugInfo = cvDebugInfo; + } + + /** + * build DEBUG_S_SYMBOLS record from all classEntries. (could probably build one per class or + * one per function) + */ + void build(DebugContext theDebugContext) { + this.debugContext = theDebugContext; + for (ClassEntry classEntry : cvDebugInfo.getPrimaryClasses()) { + build(classEntry); + } + cvDebugInfo.getCVSymbolSection().addRecord(symbolRecord); + } + + /** + * build all debug info for a classEntry. (does not yet handle member variables) + * + * @param classEntry current class + */ + private void build(ClassEntry classEntry) { + String previousMethodName = ""; + for (PrimaryEntry primaryEntry : classEntry.getPrimaryEntries()) { + Range primaryRange = primaryEntry.getPrimary(); + if (primaryRange.getFileName() != null) { + // for each function + String newMethodName = fixMethodName(primaryRange); + if (!newMethodName.equals(previousMethodName)) { + previousMethodName = newMethodName; + build(primaryEntry, newMethodName); + } + } + } + } + + /** + * emit records for each function: PROC32 S_FRAMEPROC S_END and line number records. (later: + * type records as required) + * + * @param primaryEntry primary entry for this function + * @param methodName method name alias as it will be seen by the user + */ + private void build(PrimaryEntry primaryEntry, String methodName) { + final Range primaryRange = primaryEntry.getPrimary(); + // debug("addfunc(" + methodName + ") numtypes = %d\n", + // cvDebugInfo.getCVTypeSection().getRecords().size()); + + /* S_PROC32 add function definition */ + int functionTypeIndex = addTypeRecords(primaryEntry); + byte funcFlags = 0; + CVSymbolSubrecord.CVSymbolGProc32Record proc32 = new CVSymbolSubrecord.CVSymbolGProc32Record(cvDebugInfo, methodName, 0, 0, 0, primaryRange.getHi() - primaryRange.getLo(), 0, 0, + functionTypeIndex, primaryRange.getLo(), (short) 0, funcFlags); + addToSymbolRecord(proc32); + + /* S_FRAMEPROC add frame definitions */ + int asynceh = 1 << 9; /* aync eh (msc uses 1, clang uses 0) */ + int localBP = 1 << 14; /* local base pointer = SP (0=none, 1=sp, 2=bp 3=r13) */ + int paramBP = 1 << 16; /* param base pointer = SP */ + int frameFlags = asynceh + localBP + paramBP; /* LLVM uses 0x14000; */ + addToSymbolRecord(new CVSymbolSubrecord.CVSymbolFrameProcRecord(cvDebugInfo, primaryRange.getHi() - primaryRange.getLo(), frameFlags)); + + /* TODO: add local variables, and their types */ + /* TODO: add block definitions */ + + /* S_END add end record */ + addToSymbolRecord(new CVSymbolSubrecord.CVSymbolEndRecord(cvDebugInfo)); + addLineNumberRecords(primaryEntry, methodName); + } + + private boolean noMainFound = true; + + /** + * renames a method name ot something user friendly in the debugger. (does not affect external + * symbols used by linker) + * + * first main function becomes class.main (unless replaceMainFunctionName is non-null) if + * functionNamesHashArgs is true (which it must be for the linker to work properly) all other + * functions become class.function.999 (where 999 is a hash of the arglist) + * + * @param range Range contained in the method of interest + * @return user debugger friendly method name + */ + private String fixMethodName(Range range) { + final String methodName; + if (replaceMainFunctionName != null && noMainFound && range.getMethodName().equals("main")) { + noMainFound = false; + methodName = replaceMainFunctionName; + } else if (emitUnadornedMain && noMainFound && range.getMethodName().equals("main")) { + // TODO: check for static void main(String args[]) + noMainFound = false; + methodName = range.getClassAndMethodName(); + } else if (functionNamesHashArgs) { + long hash = range.getParamNames().hashCode() & 0xffffffffL; + methodName = range.getClassAndMethodName() + "." + hash; + } else { + methodName = range.getFullMethodName(); + } + // debug("replacing %s with %s\n", range.getFullMethodName(), methodName); + return methodName; + } + + private void addLineNumberRecords(PrimaryEntry primaryEntry, String methodName) { + CVLineRecord record = new CVLineRecordBuilder(cvDebugInfo).build(primaryEntry, methodName); + /* + * if the builder decides this entry is uninteresting, we don't build a record. for example, + * Graal intrinsics may be uninteresting. + */ + if (record != null) { + cvDebugInfo.getCVSymbolSection().addRecord(record); + } + } + + private void addToSymbolRecord(CVSymbolSubrecord record) { + // debug("adding symbol subrecord: %s\n", record); + symbolRecord.addRecord(record); + } + + /** + * add type records for function. (later add arglist, and return type and local types) + * + * @param entry primaryEntry containing entities whoses type records must be added + * + * @return type index of function type + */ + private int addTypeRecords(@SuppressWarnings("unused") PrimaryEntry entry) { + CVTypeRecord.CVTypeArglistRecord argListType = addTypeRecord(new CVTypeRecord.CVTypeArglistRecord().add(T_NOTYPE)); + CVTypeRecord funcType = addTypeRecord(new CVTypeRecord.CVTypeProcedureRecord().returnType(T_VOID).argList(argListType)); + return funcType.getSequenceNumber(); + } + + private T addTypeRecord(T record) { + cvDebugInfo.getCVSymbolSection().verboseLog(debugContext, "added type record: %s hash=%d\n", record, record.hashCode()); + return cvDebugInfo.getCVTypeSection().addRecord(record); + } +} diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVSymbolSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVSymbolSectionImpl.java new file mode 100644 index 000000000000..25526ecd21ca --- /dev/null +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVSymbolSectionImpl.java @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, Red Hat Inc. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.objectfile.pecoff.cv; + +import org.graalvm.compiler.debug.DebugContext; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedHashMap; + +import static java.nio.charset.StandardCharsets.UTF_8; + +import static com.oracle.objectfile.pecoff.cv.CVConstants.CV_SIGNATURE_C13; +import static com.oracle.objectfile.pecoff.cv.CVConstants.CV_SYMBOL_SECTION_NAME; + +public final class CVSymbolSectionImpl extends CVSectionImpl { + + private static final int CV_VECTOR_DEFAULT_SIZE = 200; + private static final int CV_STRINGTABLE_DEFAULT_SIZE = 200; + + private CVDebugInfo cvDebugInfo; + private CVFileRecord fileRecord; + + private ArrayList cvRecords = new ArrayList<>(CV_VECTOR_DEFAULT_SIZE); + private CVStringTable stringTable = new CVStringTable(CV_STRINGTABLE_DEFAULT_SIZE); + + CVSymbolSectionImpl(CVDebugInfo cvDebugInfo) { + this.cvDebugInfo = cvDebugInfo; + } + + @Override + public String getSectionName() { + return CV_SYMBOL_SECTION_NAME; + } + + /* + * the CodeView symbol section ("debug$S") is actually a list of records containing sub-records + */ + @Override + public void createContent(DebugContext debugContext) { + int pos = 0; + enableLog(debugContext, pos); + log(debugContext, "CVSymbolSectionImpl.createContent() adding records"); + addRecords(debugContext); + log(debugContext, "CVSymbolSectionImpl.createContent() start"); + /* add header size */ + pos += Integer.BYTES; + /* add sum of all record sizes */ + for (CVSymbolRecord record : cvRecords) { + log(debugContext, "CVSymbolSectionImpl.createContent() computeFullSize %s", record); + pos = CVUtil.align4(pos); + pos = record.computeFullSize(pos); + } + /* create a buffer that holds it all */ + byte[] buffer = new byte[pos]; + super.setContent(buffer); + log(debugContext, "CVSymbolSectionImpl.createContent() end"); + } + + @Override + public void writeContent(DebugContext debugContext) { + int pos = 0; + enableLog(debugContext, pos); + log(debugContext, "CVSymbolSectionImpl.writeContent() start"); + byte[] buffer = getContent(); + /* write section header */ + pos = CVUtil.putInt(CV_SIGNATURE_C13, buffer, pos); + /* write all records */ + for (CVSymbolRecord record : cvRecords) { + log(debugContext, "CVSymbolSectionImpl.createContent() computeFullContentt %s", record); + pos = CVUtil.align4(pos); + pos = record.computeFullContents(buffer, pos); + } + log(debugContext, "CVSymbolSectionImpl.writeContent() end"); + } + + private void addRecords(DebugContext debugContext) { + addPrologueRecords(); + addFunctionRecords(debugContext); + addTypeRecords(); + addFileRecords(); + addStringTableRecord(); + } + + private void addPrologueRecords() { + CVSymbolRecord prologue = new CVSymbolSubsection(cvDebugInfo) { + @Override + void addSubrecords() { + CVSymbolSubrecord.CVObjectNameRecord objectNameRecord = new CVSymbolSubrecord.CVObjectNameRecord(cvDebugInfo); + if (objectNameRecord.isValid()) { + addRecord(objectNameRecord); + } + addRecord(new CVSymbolSubrecord.CVCompile3Record(cvDebugInfo)); + addRecord(new CVSymbolSubrecord.CVEnvBlockRecord(cvDebugInfo)); + } + }; + addRecord(prologue); + } + + private void addFunctionRecords(DebugContext debugContext) { + new CVSymbolRecordBuilder(cvDebugInfo).build(debugContext); + } + + private void addTypeRecords() { + /* not yet implemented. S_UDT, etc */ + // CVSymbolRecord externs = new CVSymbolSubsection.CVExternalSymbolRecord(cvDebugInfo); + // addRecord(externs); + } + + private void addFileRecords() { + addRecord(getFileRecord()); + } + + CVFileRecord getFileRecord() { + if (fileRecord == null) { + this.fileRecord = new CVFileRecord(cvDebugInfo, stringTable); + } + return fileRecord; + } + + private void addStringTableRecord() { + CVSymbolRecord stringTableRecord = new CVStringTableRecord(cvDebugInfo, stringTable); + addRecord(stringTableRecord); + } + + /* TODO: use ...objectfile.debugentry.StringTable instead */ + static final class CVStringTable { + static final class StringTableEntry { + public int offset; + public String text; + + StringTableEntry(int offset, String text) { + this.offset = offset; + this.text = text; + } + } + + /* using LinkedHashMap so order is maintained when writing string table */ + private final HashMap strings; + private int currentOffset = 0; + + CVStringTable(int startSize) { + strings = new LinkedHashMap<>(startSize); + /* ensure that the empty string has index 0 */ + add(""); + } + + int add(String s) { + StringTableEntry newEntry = new StringTableEntry(currentOffset, s); + StringTableEntry entry = strings.putIfAbsent(s, newEntry); + if (entry == null) { + /* + * TODO: getting the enecoded size should be made more efficient + */ + int utf8Length = s.getBytes(UTF_8).length; + currentOffset += utf8Length + 1; + } + return entry == null ? newEntry.offset : entry.offset; + } + + Collection values() { + return strings.values(); + } + + int size() { + return strings.size(); + } + } + + void addRecord(CVSymbolRecord record) { + cvRecords.add(record); + } +} diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVSymbolSubrecord.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVSymbolSubrecord.java new file mode 100644 index 000000000000..3d10a0ef3f6e --- /dev/null +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVSymbolSubrecord.java @@ -0,0 +1,628 @@ +/* + * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, Red Hat Inc. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.objectfile.pecoff.cv; + +import com.oracle.objectfile.ObjectFile; +import com.oracle.objectfile.SectionName; +import com.oracle.objectfile.debugentry.ClassEntry; + +import java.util.HashMap; +import java.util.Map; + +import static java.nio.charset.StandardCharsets.UTF_8; + +/* + * a CVSymbolSubrecord is a record in a DEBUG_S_SYMBOL record within a .debug$S section within a PECOFF file + */ +abstract class CVSymbolSubrecord { + + private int subrecordStartPosition; + private final short cmd; + CVDebugInfo cvDebugInfo; + + CVSymbolSubrecord(CVDebugInfo cvDebugInfo, short cmd) { + this.cvDebugInfo = cvDebugInfo; + this.cmd = cmd; + } + + int computeFullSize(int initialPos) { + this.subrecordStartPosition = initialPos; + int prologueLength = Short.BYTES * 2; /* room for length and subcommand */ + return computeSize(initialPos + prologueLength); + } + + int computeFullContents(byte[] buffer, int initialPos) { + int pos = initialPos; + pos += Short.BYTES; /* save room for length (no including length bytes) */ + pos = CVUtil.putShort(cmd, buffer, pos); + pos = computeContents(buffer, pos); + short length = (short) (pos - initialPos - Short.BYTES); + CVUtil.putShort(length, buffer, initialPos); + return pos; + } + + @Override + public String toString() { + return String.format("CVSymbolSubrecord(cmd=0x%04x pos=0x%06x)", cmd, subrecordStartPosition); + } + + protected abstract int computeSize(int pos); + + protected abstract int computeContents(byte[] buffer, int pos); + + public static final class CVObjectNameRecord extends CVSymbolSubrecord { + + String objName; /* TODO: how to find the full path to object file we will produce */ + + CVObjectNameRecord(CVDebugInfo cvDebugInfo, String objName) { + super(cvDebugInfo, CVDebugConstants.S_OBJNAME); + this.objName = objName; + } + + CVObjectNameRecord(CVDebugInfo cvDebugInfo) { + this(cvDebugInfo, findObjectName(cvDebugInfo)); + } + + private static String findObjectName(CVDebugInfo cvDebugInfo) { + /* Get file from first class object */ + String fn = null; + for (ClassEntry classEntry : cvDebugInfo.getPrimaryClasses()) { + if (classEntry.getFileName() != null) { + fn = classEntry.getFileEntry().getFileName(); + if (fn.endsWith(".java")) { + fn = fn.substring(0, fn.lastIndexOf(".java")) + ".obj"; + } + break; + } + } + return fn; + } + + boolean isValid() { + return objName != null; + } + + @Override + protected int computeSize(int initialPos) { + int pos = initialPos + Integer.BYTES; /* signature = 0; */ + pos += objName.getBytes(UTF_8).length + 1; /* inline null terminated */ + return pos; + } + + @Override + protected int computeContents(byte[] buffer, int initialPos) { + int pos = CVUtil.putInt(0, buffer, initialPos); /* signature = 0 */ + pos = CVUtil.putUTF8StringBytes(objName, buffer, pos); /* inline null terminated */ + return pos; + } + } + + public static final class CVCompile3Record extends CVSymbolSubrecord { + + private static final byte HAS_DEBUG_FLAG = 0; + // private static final byte HAS_NO_DEBUG_FLAG = (byte)0x80; + + private byte language; + private byte cf1; + private byte cf2; + private byte padding; + private short machine; + private short feMajor; + private short feMinor; + private short feBuild; + private short feQFE; + private short beMajor; + private short beMinor; + private short beBuild; + private short beQFE; + private String compiler; + + CVCompile3Record(CVDebugInfo cvDebugInfo) { + super(cvDebugInfo, CVDebugConstants.S_COMPILE3); + language = 0; + cf1 = HAS_DEBUG_FLAG; + cf2 = (byte) 0; + padding = (byte) 0; + machine = (short) 208; + feMajor = (short) 2; + feMinor = (short) 3; + feBuild = (short) 4; + feQFE = (short) 5; + beMajor = (short) 6; + beMinor = (short) 7; + beBuild = (short) 8; + beQFE = (short) 9; + compiler = "graal"; + } + + @Override + protected int computeSize(int initialPos) { + return computeContents(null, initialPos); + } + + @Override + protected int computeContents(byte[] buffer, int initialPos) { + int pos = CVUtil.putByte(language, buffer, initialPos); + pos = CVUtil.putByte(cf1, buffer, pos); + pos = CVUtil.putByte(cf2, buffer, pos); + pos = CVUtil.putByte(padding, buffer, pos); + pos = CVUtil.putShort(machine, buffer, pos); + pos = CVUtil.putShort(feMajor, buffer, pos); + pos = CVUtil.putShort(feMinor, buffer, pos); + pos = CVUtil.putShort(feBuild, buffer, pos); + pos = CVUtil.putShort(feQFE, buffer, pos); + pos = CVUtil.putShort(beMajor, buffer, pos); + pos = CVUtil.putShort(beMinor, buffer, pos); + pos = CVUtil.putShort(beBuild, buffer, pos); + pos = CVUtil.putShort(beQFE, buffer, pos); + pos = CVUtil.putUTF8StringBytes(compiler, buffer, pos); // inline null terminated + return pos; + } + } + + public static final class CVEnvBlockRecord extends CVSymbolSubrecord { + + private static final int ENVMAP_INITIAL_CAPACITY = 10; + + private Map map = new HashMap<>(ENVMAP_INITIAL_CAPACITY); + + /*- + * Example contents of the environment block: + * cwd = C:\tmp\graal-8 + * cl = C:\tmp\graal-8\ojdkbuild\tools\toolchain\vs2010e\VC\Bin\x86_amd64\cl.exe + * cmd = -Zi -MT -IC:\tmp\graal-8\tools\toolchain\vs2010e\VC\INCLUDE -IC:\tmp\graal-8\tools\toolchain\sdk71\INCLUDE -IC:\tmp\graal-8\tools\toolchain\sdk71\INCLUDE\gl -TC -X + * src = helloworld.c + * pdb = C:\tmp\graal-8\vc100.pdb + */ + CVEnvBlockRecord(CVDebugInfo cvDebugInfo) { + super(cvDebugInfo, CVDebugConstants.S_ENVBLOCK); + + /* current directory */ + map.put("cwd", System.getProperty("user.dir")); + + /* compiler executable */ + // map.put("cl", "cl.exe"); + + /* argument list */ + // map.put("cmd", "-Zi -MT -wishfulthinking"); + + /* + * find first source file - which, for Graal would be a class file on the command line + */ + String fn = findFirstFile(cvDebugInfo); + if (fn != null) { + map.put("src", fn); + } + + /* Graal doesn't yet create PDB files; all type info is stored in object file */ + // map.put("pdb", System.getProperty("user.dir") + File.separator + "vc100.pdb"); + } + + private static String findFirstFile(CVDebugInfo cvDebugInfo) { + String fn = null; + for (ClassEntry classEntry : cvDebugInfo.getPrimaryClasses()) { + if (classEntry.getFileName() != null) { + fn = classEntry.getFileEntry().getFileName(); + break; + } + } + return fn; + } + + @Override + protected int computeSize(int initialPos) { + return computeContents(null, initialPos); + } + + @Override + protected int computeContents(byte[] buffer, int initialPos) { + /* flags */ + int pos = CVUtil.putByte((byte) 0, buffer, initialPos); + + /* key/value pairs */ + for (Map.Entry entry : map.entrySet()) { + pos = CVUtil.putUTF8StringBytes(entry.getKey(), buffer, pos); + pos = CVUtil.putUTF8StringBytes(entry.getValue(), buffer, pos); + } + + /* end marker */ + pos = CVUtil.putUTF8StringBytes("", buffer, pos); + return pos; + } + } + + /* + * creating a proc32 record has side effects. - a global symbol is added to the COFF symbol + * section - two relocation entries are added to the section relocation table, they refer to the + * global symbol + */ + public static class CVSymbolGProc32Record extends CVSymbolSubrecord { + + private static ObjectFile.Element textSection; + + int pparent; + int pend; + int pnext; + int proclen; + int debugStart; + int debugEnd; + int typeIndex; + int offset; + short segment; + byte flags; + String name; + + CVSymbolGProc32Record(CVDebugInfo cvDebugInfo, short cmd, String name, int pparent, int pend, int pnext, int proclen, int debugStart, int debugEnd, int typeIndex, int offset, short segment, + byte flags) { + super(cvDebugInfo, cmd); + this.name = name; + this.pparent = pparent; + this.pend = pend; + this.pnext = pnext; + this.proclen = proclen; + this.debugStart = debugStart; + this.debugEnd = debugEnd; + this.typeIndex = typeIndex; + this.offset = offset; + this.segment = segment; + this.flags = flags; + } + + CVSymbolGProc32Record(CVDebugInfo cvDebugInfo, String name, int pparent, int pend, int pnext, int proclen, int debugStart, int debugEnd, int typeIndex, int offset, short segment, byte flags) { + this(cvDebugInfo, CVDebugConstants.S_GPROC32, name, pparent, pend, pnext, proclen, debugStart, debugEnd, typeIndex, offset, segment, flags); + } + + @Override + protected int computeSize(int initialPos) { + return computeContents(null, initialPos); + } + + @Override + protected int computeContents(byte[] buffer, int initialPos) { + int pos = CVUtil.putInt(pparent, buffer, initialPos); + pos = CVUtil.putInt(pend, buffer, pos); + pos = CVUtil.putInt(pnext, buffer, pos); + pos = CVUtil.putInt(proclen, buffer, pos); + pos = CVUtil.putInt(debugStart, buffer, pos); + pos = CVUtil.putInt(debugEnd, buffer, pos); + pos = CVUtil.putInt(typeIndex, buffer, pos); + if (buffer == null) { + cvDebugInfo.getCVSymbolSection().getOwner().createDefinedSymbol(name, getTextSection(), offset, proclen, true, true); + } + if (buffer != null) { + // CVUtil.debug("CVSymbolGProc32Record() adding SECREL reloc at pos=0x%x for func=%s + // addr=0x%x\n", pos, name, offset); + cvDebugInfo.getCVSymbolSection().markRelocationSite(pos, 4, ObjectFile.RelocationKind.SECREL, name, false, 1L); + } + pos = CVUtil.putInt(0, buffer, pos); + if (buffer != null) { + // CVUtil.debug("CVSymbolGProc32Record() adding SECTION reloc at pos=0x%x for + // func=%s addr=0x%x\n", pos, name, offset); + cvDebugInfo.getCVSymbolSection().markRelocationSite(pos, 2, ObjectFile.RelocationKind.SECTION, name, false, 1L); + } + pos = CVUtil.putShort((short) 0, buffer, pos); + pos = CVUtil.putByte(flags, buffer, pos); + pos = CVUtil.putUTF8StringBytes(name, buffer, pos); + return pos; + } + + private ObjectFile.Element getTextSection() { + if (textSection == null) { + textSection = cvDebugInfo.getCVSymbolSection().getOwner().elementForName(SectionName.TEXT.getFormatDependentName(ObjectFile.Format.PECOFF)); + } + return textSection; + } + + @Override + public String toString() { + return String.format("S_GPROC32(name=%s parent=%d startaddr=0x%x end=0x%x len=0x%x offset=0x%x type=0x%x flags=0x%x)", name, pparent, debugStart, debugEnd, proclen, offset, typeIndex, + flags); + } + } + + /*- + public static final class CVSymbolGProc32IDRecord extends CVSymbolGProc32Record { + + CVSymbolGProc32IDRecord(CVDebugInfo cvDebugInfo, String name, int pparent, int pend, int pnext, int proclen, int debugStart, int debugEnd, int typeIndex, int offset, short segment, byte flags) { + super(cvDebugInfo, CVDebugConstants.S_GPROC32_ID, name, pparent, pend, pnext, proclen, debugStart, debugEnd, typeIndex, offset, segment, flags); + } + + /* this is almost certainly bad (not enough information); use only for debugging * + CVSymbolGProc32IDRecord(CVDebugInfo cvDebugInfo, String name, int offset, int proclen) { + super(cvDebugInfo, CVDebugConstants.S_GPROC32_ID, name, 0, 0, 0, proclen, 0, 0, 0, offset, (short)0, (byte)0); + } + + @Override + public String toString() { + return String.format("S_GPROC32_ID(name=%s parent=%d startaddr=0x%x end=0x%x len=0x%x offset=0x%x type=0x%x flags=0x%x)", name, pparent, debugStart, debugEnd, proclen, offset, typeIndex, flags); + } + } + */ + + public static final class CVSymbolFrameProcRecord extends CVSymbolSubrecord { + + int framelen; + int padLen; + int padOffset; + int saveRegsCount; + int ehOffset; + short ehSection; + int flags; + + CVSymbolFrameProcRecord(CVDebugInfo cvDebugInfo, int framelen, int padLen, int padOffset, int saveRegsCount, int ehOffset, short ehSection, int flags) { + super(cvDebugInfo, CVDebugConstants.S_FRAMEPROC); + this.framelen = framelen; + this.padLen = padLen; + this.padOffset = padOffset; + this.saveRegsCount = saveRegsCount; + this.ehOffset = ehOffset; + this.ehSection = ehSection; + this.flags = flags; + } + + CVSymbolFrameProcRecord(CVDebugInfo cvDebugInfo, int framelen, int flags) { + this(cvDebugInfo, framelen, 0, 0, 0, 0, (short) 0, flags); + } + + @Override + protected int computeSize(int initialPos) { + return computeContents(null, initialPos); + } + + @Override + protected int computeContents(byte[] buffer, int initialPos) { + int pos = CVUtil.putInt(framelen, buffer, initialPos); + pos = CVUtil.putInt(padLen, buffer, pos); + pos = CVUtil.putInt(padOffset, buffer, pos); + pos = CVUtil.putInt(saveRegsCount, buffer, pos); + pos = CVUtil.putInt(ehOffset, buffer, pos); + pos = CVUtil.putShort(ehSection, buffer, pos); + pos = CVUtil.putInt(flags, buffer, pos); + return pos; + } + + @Override + public String toString() { + return String.format("S_FRAMEPROC(len=0x%x padlen=0x%x paddOffset=0x%x regCount=%d flags=0x%x)", framelen, padLen, padOffset, saveRegsCount, flags); + } + } + + public static class CVSymbolEndRecord extends CVSymbolSubrecord { + + CVSymbolEndRecord(CVDebugInfo cvDebugInfo, short cmd) { + super(cvDebugInfo, cmd); + } + + CVSymbolEndRecord(CVDebugInfo cvDebugInfo) { + this(cvDebugInfo, CVDebugConstants.S_END); + } + + @Override + protected int computeSize(int initialPos) { + return computeContents(null, initialPos); + } + + @Override + protected int computeContents(byte[] buffer, int initialPos) { + // nothing + return initialPos; + } + + @Override + public String toString() { + return "S_END"; + } + } + + /*- + public static final class CVSymbolRegRel32Record extends CVSymbolSubrecord { + + int typeIndex; + short reg; + int offset; + String name; + + public CVSymbolRegRel32Record(CVDebugInfo cvDebugInfo, String name, int typeIndex, int offset, short reg) { + super(cvDebugInfo, CVDebugConstants.S_REGREL32); + this.name = name; + this.typeIndex = typeIndex; + this.offset = offset; + } + + @Override + protected int computeSize(int initialPos) { + return computeContents(null, initialPos); + } + + @Override + protected int computeContents(byte[] buffer, int initialPos) { + int pos = CVUtil.putInt(offset, buffer, initialPos); + pos = CVUtil.putInt(typeIndex, buffer, pos); + pos = CVUtil.putShort(reg, buffer, pos); + pos = CVUtil.putUTF8StringBytes(name, buffer, pos); + return pos; + } + + @Override + public String toString() { + return "S_REGREL32"; + } + } + + public static class CVSymbolProcIdEndRecord extends CVSymbolEndRecord { + CVSymbolProcIdEndRecord(CVDebugInfo cvDebugInfo) { + super(cvDebugInfo, CVDebugConstants.S_PROC_ID_END); + } + } + + public static final class CVSymbolConstantRecord extends CVSymbolSubrecord { + + int typeIndex; + short leaf; + String name; + + public CVSymbolConstantRecord(CVDebugInfo cvDebugInfo, String name, int typeIndex, short leaf) { + super(cvDebugInfo, CVDebugConstants.S_CONSTANT); + this.name = name; + this.typeIndex = typeIndex; + this.leaf = leaf; + } + + @Override + protected int computeSize(int initialPos) { + return computeContents(null, initialPos); + } + + @Override + protected int computeContents(byte[] buffer, int initialPos) { + int pos = CVUtil.putInt(typeIndex, buffer, initialPos); + pos = CVUtil.putShort(leaf, buffer, pos); + pos = CVUtil.putUTF8StringBytes(name, buffer, pos); + return pos; + } + + @Override + public String toString() { + return "S_CONSTANT"; + } + } + + private static abstract class CVSymbolDataRecord extends CVSymbolSubrecord { + + int typeIndex; + int offset; + short segment; + String name; + + CVSymbolDataRecord(CVDebugInfo cvDebugInfo, short recordType, String name, int typeIndex, int offset, short segment) { + super(cvDebugInfo, recordType); + this.name = name; + this.typeIndex = typeIndex; + this.offset = offset; + this.segment = segment; + } + + @Override + protected int computeSize(int initialPos) { + return computeContents(null, initialPos); + } + + @Override + protected int computeContents(byte[] buffer, int initialPos) { + int pos = CVUtil.putInt(typeIndex, buffer, initialPos); + pos = CVUtil.putInt(offset, buffer, pos); + pos = CVUtil.putShort(segment, buffer, pos); + pos = CVUtil.putUTF8StringBytes(name, buffer, pos); + return pos; + } + } + + public static final class CVSymbolLData32Record extends CVSymbolDataRecord { + + public CVSymbolLData32Record(CVDebugInfo cvDebugInfo, String name, int typeIndex, int offset, short segment) { + super(cvDebugInfo, CVDebugConstants.S_LDATA32, name, typeIndex, offset, segment); + } + + @Override + public String toString() { + return "S_LDATA32_ST"; + } + } + + public static final class CVSymbolLData32STRecord extends CVSymbolDataRecord { + + public CVSymbolLData32STRecord(CVDebugInfo cvDebugInfo, String name, int typeIndex, int offset, short segment) { + super(cvDebugInfo, CVDebugConstants.S_LDATA32_ST, name, typeIndex, offset, segment); + } + + @Override + public String toString() { + return "S_LDATA32_ST"; + } + } + + public static final class CVSymbolGData32Record extends CVSymbolDataRecord { + + public CVSymbolGData32Record(CVDebugInfo cvDebugInfo, String name, int typeIndex, int offset, short segment) { + super(cvDebugInfo, CVDebugConstants.S_GDATA32, name, typeIndex, offset, segment); + } + + @Override + public String toString() { + return "S_GDATA32"; + } + } + + public static final class CVSymbolSSearchRecord extends CVSymbolDataRecord { + + public CVSymbolSSearchRecord(CVDebugInfo cvDebugInfo, int offset, short segment) { + super(cvDebugInfo, CVDebugConstants.S_SSEARCH, null, 0, offset, segment); + } + + @Override + protected int computeContents(byte[] buffer, int initialPos) { + int pos = CVUtil.putInt(offset, buffer, initialPos); + pos = CVUtil.putShort(segment, buffer, pos); + return pos; + } + + @Override + public String toString() { + return "S_SSEARCH"; + } + } + + public static final class CVSymbolUDTRecord extends CVSymbolSubrecord { + + int typeIndex; + String name; + + public CVSymbolUDTRecord(CVDebugInfo cvDebugInfo, String name, int typeIndex) { + super(cvDebugInfo, CVDebugConstants.S_UDT); + this.name = name; + this.typeIndex = typeIndex; + } + + @Override + protected int computeSize(int initialPos) { + return computeContents(null, initialPos); + } + + @Override + protected int computeContents(byte[] buffer, int initialPos) { + int pos = CVUtil.putInt(typeIndex, buffer, initialPos); + pos = CVUtil.putUTF8StringBytes(name, buffer, pos); + return pos; + } + + @Override + public String toString() { + return String.format("S_UDT(name=%s typeindex=0x%x)", name, typeIndex); + } + } + */ +} diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVSymbolSubsection.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVSymbolSubsection.java new file mode 100644 index 000000000000..7bdaa9e54ff7 --- /dev/null +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVSymbolSubsection.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, Red Hat Inc. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.objectfile.pecoff.cv; + +import java.util.ArrayList; + +/* + * A CVSymbolSubsection is s special record in debug$S containing nested symbol records + * (the nested records inherit from CVSymbolSubrecord + */ +class CVSymbolSubsection extends CVSymbolRecord { + + private static final int SUBCMD_INITIAL_CAPACITY = 100; + + private ArrayList subcmds = new ArrayList<>(SUBCMD_INITIAL_CAPACITY); + + CVSymbolSubsection(CVDebugInfo cvDebugInfo) { + super(cvDebugInfo, CVDebugConstants.DEBUG_S_SYMBOLS); + } + + void addRecord(CVSymbolSubrecord subcmd) { + subcmds.add(subcmd); + } + + void addSubrecords() { + } + + @Override + protected int computeSize(int initialPos) { + int pos = initialPos; + addSubrecords(); + for (CVSymbolSubrecord subcmd : subcmds) { + pos = subcmd.computeFullSize(pos); + } + return pos; + } + + @Override + protected int computeContents(byte[] buffer, int initialPos) { + int pos = initialPos; + for (CVSymbolSubrecord subcmd : subcmds) { + pos = subcmd.computeFullContents(buffer, pos); + } + return pos; + } + + @Override + public String toString() { + return String.format("CVSymbolSubsection(type=0x%04x pos=0x%05x count=%d)", type, recordStartPosition, subcmds.size()); + } +} diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVTypeConstants.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVTypeConstants.java new file mode 100644 index 000000000000..a0b5159abffa --- /dev/null +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVTypeConstants.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, Red Hat Inc. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.objectfile.pecoff.cv; + +abstract class CVTypeConstants { + + /* type table */ + static final short T_NOTYPE = 0x0000; + static final short T_VOID = 0x0003; + // static final short T_CHAR = 0x0010; /* 8 bit signed (java type) */ + // static final short T_WCHAR = 0x0071; + // static final short T_CHAR16 = 0x007a; /* 16 bit unicode (Java type) */ + // static final short T_SHORT = 0x0011; /* 16 bit signed short (Java type) */ + // static final short T_LONG = 0x0014; /* 32 bit signed (java type? maybe T_short4?) */ + static final short T_QUAD = 0x0013; /* 64 bit signed long long (Java type) */ + // static final short T_REAL32 = 0x0040; /* 32 bit float (Java type) */ + // static final short T_REAL64 = 0x0041; /* 64 but double (Java type) */ + // static final short T_RCHAR = 0x0070; /* ?? "really a char" */ + + // static final short T_INT4 = T_LONG; /* ?? is tis right */ + static final short T_UQUAD = T_QUAD; /* ?? */ + + // static final short T_POINTER_BITS = 0x0700; + // static final short T_POINTER32 = 0x0400; /* 32 bit pointer */ + // static final short T_POINTER64 = 0x0600; /* 64 bit pointer */ + + static final short LF_MODIFIER = 0x1001; + static final short LF_POINTER = 0x1002; + static final short LF_PROCEDURE = 0x1008; + static final short LF_ARGLIST = 0x1201; + // static final short LF_FIELDLIST = 0x1203; + static final short LF_BITFIELD = 0x1205; + static final short LF_BCLASS = 0x1400; + static final short LF_ARRAY = 0x1503; + static final short LF_CLASS = 0x1504; + static final short LF_STRUCTURE = 0x1505; + // static final short LF_UNION = 0x1506; + // static final short LF_ENUM = 0x1507; + static final short LF_MEMBER = 0x150d; + static final short LF_TYPESERVER2 = 0x1515; + static final short LF_INTERFACE = 0x1519; + static final short LF_BINTERFACE = 0x151a; + + /*- + static final short LF_CHAR = (short) 0x8000; + static final short LF_SHORT = (short) 0x8001; + static final short LF_USHORT = (short) 0x8002; + static final short LF_LONG = (short) 0x8003; + static final short LF_ULONG = (short) 0x8004; + static final short LF_REAL32 = (short) 0x8005; + static final short LF_REAL64 = (short) 0x8006; + static final short LF_QUADWORD = (short) 0x8009; + static final short LF_UQUADWORD = (short) 0x800a; + static final short LF_OCTWORD = (short) 0x8017; + static final short LF_UOCTWORD = (short) 0x8018; + */ + + // static final byte LF_PAD0 = (byte) 0xf0; + static final byte LF_PAD1 = (byte) 0xf1; + static final byte LF_PAD2 = (byte) 0xf2; + static final byte LF_PAD3 = (byte) 0xf3; + /*- + static final byte LF_PAD4 = (byte) 0xf4; + static final byte LF_PAD5 = (byte) 0xf5; + static final byte LF_PAD6 = (byte) 0xf6; + static final byte LF_PAD7 = (byte) 0xf7; + static final byte LF_PAD8 = (byte) 0xf8; + static final byte LF_PAD9 = (byte) 0xf9; + static final byte LF_PAD10 = (byte) 0xfa; + static final byte LF_PAD11 = (byte) 0xfb; + static final byte LF_PAD12 = (byte) 0xfc; + static final byte LF_PAD13 = (byte) 0xfd; + static final byte LF_PAD14 = (byte) 0xfe; + static final byte LF_PAD15 = (byte) 0xff; + */ +} diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVTypeRecord.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVTypeRecord.java new file mode 100644 index 000000000000..19a2d30ef81b --- /dev/null +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVTypeRecord.java @@ -0,0 +1,705 @@ +/* + * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, Red Hat Inc. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.objectfile.pecoff.cv; + +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.Arrays; + +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.LF_ARGLIST; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.LF_ARRAY; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.LF_BCLASS; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.LF_BINTERFACE; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.LF_BITFIELD; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.LF_CLASS; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.LF_INTERFACE; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.LF_MEMBER; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.LF_MODIFIER; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.LF_PAD1; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.LF_PAD2; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.LF_PAD3; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.LF_POINTER; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.LF_PROCEDURE; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.LF_STRUCTURE; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.LF_TYPESERVER2; +import static com.oracle.objectfile.pecoff.cv.CVTypeConstants.T_UQUAD; + +/* + * CV Type Record format (little-endian): + * uint16 length + * uint16 leaf (a.k.a. record type) + * (contents) + */ +abstract class CVTypeRecord { + + protected final short type; + private int startPosition; + private int sequenceNumber; /* CodeView type records are numbered 1000 on up */ + + CVTypeRecord(short type) { + this.type = type; + this.startPosition = -1; + this.sequenceNumber = -1; + } + + int getSequenceNumber() { + return sequenceNumber; + } + + void setSequenceNumber(int sequenceNumber) { + this.sequenceNumber = sequenceNumber; + } + + int computeFullSize(int initialPos) { + this.startPosition = initialPos; + int pos = initialPos + Short.BYTES * 2; /* save room for length and leaf type */ + pos = computeSize(pos); + pos = alignPadded4(null, pos); + return pos; + } + + int computeFullContents(byte[] buffer, int initialPos) { + int lenPos = initialPos; /* save position of length short */ + int pos = initialPos + Short.BYTES; /* save room for length short */ + pos = CVUtil.putShort(type, buffer, pos); + pos = computeContents(buffer, pos); + /* length does not include record length (2 bytes)) but does include end padding */ + pos = alignPadded4(buffer, pos); + int length = (short) (pos - lenPos - Short.BYTES); + CVUtil.putShort((short) length, buffer, lenPos); + return pos; + } + + protected abstract int computeSize(int initialPos); + + protected abstract int computeContents(byte[] buffer, int initialPos); + + @Override + public String toString() { + return String.format("CVTypeRecord(type=0x%04x seq=0x%04x pos=0x%04x)", type, sequenceNumber, startPosition); + } + + public void dump(PrintStream out) { + out.format("%s\n", this); + } + + private static int alignPadded4(byte[] buffer, int originalpos) { + int pos = originalpos; + int align = pos & 3; + if (align == 1) { + byte[] p3 = {LF_PAD3, LF_PAD2, LF_PAD1}; + pos = CVUtil.putBytes(p3, buffer, pos); + } else if (align == 2) { + pos = CVUtil.putByte(LF_PAD2, buffer, pos); + pos = CVUtil.putByte(LF_PAD1, buffer, pos); + } else if (align == 3) { + pos = CVUtil.putByte(LF_PAD1, buffer, pos); + } + return pos; + } + + static final class CVTypeModifierRecord extends CVTypeRecord { + + int originalLeaf = -1; + boolean addConst = false; + boolean addVolatile = false; + boolean addUnaligned = false; + + CVTypeModifierRecord(int originalLeaf) { + super(LF_MODIFIER); + this.originalLeaf = originalLeaf; + } + + CVTypeModifierRecord(CVTypeRecord originalLeaf) { + super(LF_MODIFIER); + this.originalLeaf = originalLeaf.getSequenceNumber(); + } + + CVTypeModifierRecord addConst() { + this.addConst = true; + return this; + } + + CVTypeModifierRecord addVolatile() { + this.addVolatile = true; + return this; + } + + CVTypeModifierRecord addUnaligned() { + this.addUnaligned = true; + return this; + } + + private short computeFlags() { + return (short) ((addConst ? 0x01 : 0x00) | (addVolatile ? 0x02 : 0x00) | (addUnaligned ? 0x04 : 0)); + } + + @Override + public int computeSize(int initialPos) { + return initialPos + Integer.BYTES + Short.BYTES; + } + + @Override + public int computeContents(byte[] buffer, int initialPos) { + int pos = CVUtil.putInt(originalLeaf, buffer, initialPos); + pos = CVUtil.putShort(computeFlags(), buffer, pos); + return pos; + } + + @Override + public String toString() { + String s = String.format("LF_MODIFIER(0x%04x", getSequenceNumber()); + if (addConst) { + s += " const"; + } + if (addVolatile) { + s += " volatile"; + } + if (addUnaligned) { + s += "unaligned"; + } + s += String.format(" 0x%04x)", originalLeaf); + return s; + } + + @Override + public int hashCode() { + int h = type; + h = 31 * h + originalLeaf; + h = 31 * h + computeFlags(); + return h; + } + } + + static final class CVTypePointerRecord extends CVTypeRecord { + + int originalLeaf = -1; + int kind = 0; + int mode = 0; + int modifiers = 0; + int size = 4; + int flags = 0; + + /*- + int kind = attributes & 0x00001f; + int mode = (attributes & 0x0000e0) >> 5; + int modifiers = (attributes & 0x001f00) >> 8; + int size = (attributes & 0x07e000) >> 13; + int flags = (attributes & 0x380000) >> 19; + out.printf("LF_POINTER len=%d leaf=0x%04x refType=0x%06x attrib=0x%06x\n", len, leaf, referentType, attributes); + out.printf(" kind=%d mode=%d modifiers=%d size=%d flags=%d\n", kind, mode, modifiers, size, flags); + */ + + CVTypePointerRecord(int originalLeaf) { + super(LF_POINTER); + this.originalLeaf = originalLeaf; + } + + CVTypePointerRecord(CVTypeRecord originalLeaf) { + super(LF_POINTER); + this.originalLeaf = originalLeaf.getSequenceNumber(); + } + + private int computeAttributes() { + return kind | (mode << 5) | (modifiers << 8) | (size << 13) | (flags << 19); + } + + @Override + public int computeSize(int initialPos) { + return initialPos + Integer.BYTES * 2; + } + + @Override + public int computeContents(byte[] buffer, int initialPos) { + int pos = CVUtil.putInt(originalLeaf, buffer, initialPos); + pos = CVUtil.putInt(computeAttributes(), buffer, pos); + return pos; + } + + @Override + public String toString() { + return String.format("LF_POINTER(0x%04x * 0x%04x)", getSequenceNumber(), originalLeaf); + } + + @Override + public int hashCode() { + int h = type; + h = 31 * h + originalLeaf; + h = 31 * h + computeAttributes(); + return h; + } + } + + static final class CVTypeProcedureRecord extends CVTypeRecord { + + int returnType = -1; + CVTypeArglistRecord argList = null; + + CVTypeProcedureRecord() { + super(LF_PROCEDURE); + } + + public CVTypeProcedureRecord returnType(int leaf) { + this.returnType = leaf; + return this; + } + + public CVTypeProcedureRecord returnType(CVTypeRecord leaf) { + this.returnType = leaf.getSequenceNumber(); + return this; + } + + CVTypeProcedureRecord argList(CVTypeArglistRecord leaf) { + this.argList = leaf; + return this; + } + + @Override + public int computeSize(int initialPos) { + return initialPos + Integer.BYTES + Byte.BYTES + Byte.BYTES + Short.BYTES + Integer.BYTES; + } + + @Override + public int computeContents(byte[] buffer, int initialPos) { + int pos = CVUtil.putInt(returnType, buffer, initialPos); + pos = CVUtil.putByte((byte) 0, buffer, pos); // callType + pos = CVUtil.putByte((byte) 0, buffer, pos); // funcAttr + pos = CVUtil.putShort((short) argList.getSize(), buffer, pos); + pos = CVUtil.putInt(argList.getSequenceNumber(), buffer, pos); + return pos; + } + + @Override + public String toString() { + return String.format("LF_PROCEDURE(0x%04x ret=0x%04x arg=0x%04x)", getSequenceNumber(), returnType, argList.getSequenceNumber()); + } + + @Override + public int hashCode() { + int h = type; + h = 31 * h + returnType; + h = 31 * h + argList.hashCode(); + // h = 31 * h + // callType + funcAttr + return h; + } + } + + static final class CVTypeArglistRecord extends CVTypeRecord { + + ArrayList args = new ArrayList<>(); + + CVTypeArglistRecord() { + super(LF_ARGLIST); + } + + CVTypeArglistRecord add(int argType) { + args.add(argType); + return this; + } + + CVTypeArglistRecord add(CVTypeRecord argType) { + args.add(argType.getSequenceNumber()); + return this; + } + + @Override + public int computeSize(int initialPos) { + return initialPos + Integer.BYTES + Integer.BYTES * args.size(); + } + + @Override + public int computeContents(byte[] buffer, int initialPos) { + int pos = CVUtil.putInt(args.size(), buffer, initialPos); + for (Integer at : args) { + pos = CVUtil.putInt(at, buffer, pos); + } + return pos; + } + + int getSize() { + return args.size(); + } + + @Override + public String toString() { + StringBuilder s = new StringBuilder(String.format("LF_ARGLIST(0x%04x [", getSequenceNumber())); + for (Integer at : args) { + s.append(' ').append(at); + } + s.append("])"); + return s.toString(); + } + + @Override + public int hashCode() { + int h = type; + h = h * 31 + args.size(); + for (Integer r : args) { + h = 31 * h + r.hashCode(); + } + return h; + } + } + + static final class CVMemberRecord extends CVTypeRecord { + + short propertyAttributes; /* property attribute field (prop_t) */ + int fieldIndex; /* type index of member type */ + /* TODO data */ + + CVMemberRecord(short attrs, int fieldIndex) { + super(LF_MEMBER); + this.propertyAttributes = attrs; + this.fieldIndex = fieldIndex; + } + + @Override + public int computeSize(int initialPos) { + return initialPos + Short.BYTES + Integer.BYTES; /* + TODO */ + } + + @Override + public int computeContents(byte[] buffer, int initialPos) { + int pos = CVUtil.putShort(propertyAttributes, buffer, initialPos); + pos = CVUtil.putInt(fieldIndex, buffer, pos); + /* TODO */ + return pos; + } + + @Override + public String toString() { + return String.format("LF_MEMBER(0x%04x attr=0x%04x fld=0x%x)", getSequenceNumber(), propertyAttributes, fieldIndex); + } + + @Override + public int hashCode() { + int h = type; + h = 31 * h + propertyAttributes; + h = 31 * h + fieldIndex; + return h; + } + } + + static class CVBaseClassRecord extends CVTypeRecord { + + short propertyAttributes; /* property attribute field (prop_t) */ + int fieldIndex; /* type index of member type */ + /* TODO data */ + + CVBaseClassRecord(short ltype, short attrs, int fieldIndex) { + super(ltype); + this.propertyAttributes = attrs; + this.fieldIndex = fieldIndex; + } + + CVBaseClassRecord(short attrs, int fieldIndex) { + this(LF_BCLASS, attrs, fieldIndex); + } + + @Override + public int computeSize(int initialPos) { + return initialPos + Short.BYTES + Integer.BYTES; // + TODO + } + + @Override + public int computeContents(byte[] buffer, int initialPos) { + int pos = CVUtil.putShort(propertyAttributes, buffer, initialPos); + pos = CVUtil.putInt(fieldIndex, buffer, pos); + // TODO + return pos; + } + + protected String toString(String leafStr) { + return String.format("%s(0x%04x attr=0x%04x fld=0x%x)", leafStr, getSequenceNumber(), propertyAttributes, fieldIndex); + } + + @Override + public String toString() { + return toString("LF_BASECLASS"); + } + + @Override + public int hashCode() { + int h = type; + h = 31 * h + propertyAttributes; + h = 31 * h + fieldIndex; + return h; + } + } + + static class CVBaseIntefaceRecord extends CVBaseClassRecord { + + CVBaseIntefaceRecord(short attrs, int fieldIndex) { + super(LF_BINTERFACE, attrs, fieldIndex); + } + + @Override + public String toString() { + return toString("LF_BINTERFACE"); + } + + } + + static class CVClassRecord extends CVTypeRecord { + + short count; /* count of number of elements in class */ + short propertyAttributes; /* property attribute field (prop_t) */ + int fieldIndex; /* type index of LF_FIELDLIST descriptor list */ + int derivedFromIndex; /* type index of derived from list if not zero */ + int vshapeIndex; /* type index of vshape table for this class */ + /* TODO data */ + + CVClassRecord(short recType, short count, short attrs, int fieldIndex, int derivedFromIndex, int vshapeIndex) { + super(recType); + this.count = count; + this.propertyAttributes = attrs; + this.fieldIndex = fieldIndex; + this.derivedFromIndex = derivedFromIndex; + this.vshapeIndex = vshapeIndex; + } + + CVClassRecord(short count, short attrs, int fieldIndex, int derivedFromIndex, int vshapeIndex) { + this(LF_CLASS, count, attrs, fieldIndex, derivedFromIndex, vshapeIndex); + } + + @Override + public int computeSize(int initialPos) { + return initialPos + Short.BYTES + Short.BYTES + Integer.BYTES + Integer.BYTES + Integer.BYTES; // + + // TODO + } + + @Override + public int computeContents(byte[] buffer, int initialPos) { + int pos = CVUtil.putShort(count, buffer, initialPos); + pos = CVUtil.putShort(propertyAttributes, buffer, pos); + pos = CVUtil.putInt(fieldIndex, buffer, pos); + pos = CVUtil.putInt(derivedFromIndex, buffer, pos); + pos = CVUtil.putInt(vshapeIndex, buffer, pos); + // TODO + return pos; + } + + protected String toString(String lfTypeStr) { + return String.format("%s(0x%04x count=%d attr=0x%04x fld=0x%x super=0x%x vshape=0x%x)", lfTypeStr, getSequenceNumber(), count, propertyAttributes, fieldIndex, derivedFromIndex, + vshapeIndex); + } + + @Override + public String toString() { + return toString("LF_CLASS"); + } + + @Override + public int hashCode() { + int h = type; + h = 31 * h + count; + h = 31 * h + propertyAttributes; + h = 31 * h + fieldIndex; + h = 31 * h + derivedFromIndex; + h = 31 * h + vshapeIndex; + return h; + } + } + + static final class CVStructRecord extends CVClassRecord { + CVStructRecord(short count, short attrs, int fieldIndex, int derivedFromIndex, int vshape) { + super(LF_STRUCTURE, count, attrs, fieldIndex, derivedFromIndex, vshape); + } + + @Override + public String toString() { + return toString("LF_STRUCT"); + } + } + + static final class CVInterfaceRecord extends CVClassRecord { + CVInterfaceRecord(short count, short attrs, int fieldIndex, int derivedFromIndex, int vshape) { + super(LF_INTERFACE, count, attrs, fieldIndex, derivedFromIndex, vshape); + } + + @Override + public String toString() { + return toString("LF_INTERFACE"); + } + } + + static final class CVTypeBitfieldRecord extends CVTypeRecord { + + byte length; + byte position; + int typeIndex; + + CVTypeBitfieldRecord(int length, int position, int typeIndex) { + super(LF_BITFIELD); + this.length = (byte) length; + this.position = (byte) position; + this.typeIndex = typeIndex; + } + + @Override + public int computeSize(int initialPos) { + return initialPos + Integer.BYTES + Byte.BYTES + Byte.BYTES; + } + + @Override + public int computeContents(byte[] buffer, int initialPos) { + int pos = CVUtil.putInt(typeIndex, buffer, initialPos); + pos = CVUtil.putByte(length, buffer, pos); + pos = CVUtil.putByte(position, buffer, pos); + return pos; + } + + @Override + public String toString() { + return String.format("LF_BITFIELD(0x%04x, type=0x%04x len=%d pos=%d)", getSequenceNumber(), typeIndex, length, position); + } + + @Override + public int hashCode() { + int h = type; + h = 31 * h + position; + h = 31 * h + length; + h = 31 * h + typeIndex; + return h; + } + } + + static final class CVTypeArrayRecord extends CVTypeRecord { + + int elementType = -1; + int indexType = -1; + int length = -1; + + CVTypeArrayRecord(int elementType, int indexType, int length) { + super(LF_ARRAY); + this.elementType = elementType; + this.indexType = indexType; + this.length = length; + } + + CVTypeArrayRecord(int elementType, int length) { + super(LF_ARRAY); + this.elementType = elementType; + this.indexType = T_UQUAD; + this.length = length; + } + + CVTypeArrayRecord(CVTypeRecord elementType, int length) { + super(LF_ARRAY); + this.elementType = elementType.getSequenceNumber(); + this.indexType = T_UQUAD; + this.length = length; + } + + @Override + public int computeSize(int initialPos) { + return initialPos + Integer.BYTES * 3; + } + + @Override + public int computeContents(byte[] buffer, int initialPos) { + int pos = CVUtil.putInt(elementType, buffer, initialPos); + pos = CVUtil.putInt(indexType, buffer, pos); + pos = CVUtil.putInt(length, buffer, pos); + return pos; + } + + @Override + public String toString() { + return String.format("LF_ARRAY(0x%04x type=0x%04x len=%d indexType=0x%04x)", getSequenceNumber(), elementType, length, indexType); + } + + @Override + public int hashCode() { + int h = type; + h = 31 * h + elementType; + h = 31 * h + indexType; + h = 31 * h + length; + return h; + } + } + + static final class CVTypeServer2Record extends CVTypeRecord { + + byte[] guid; + int age; + String fileName; + + CVTypeServer2Record(byte[] guid, int age, String fileName) { + super(LF_TYPESERVER2); + assert (guid.length == 16); + this.guid = guid; + this.age = age; + this.fileName = fileName; + + /*- + for some very odd reason GUID is stored like this: + int guid1 = in.getInt(); + int guid2 = in.getShort(); + int guid3 = in.getShort(); + byte[] guid5[10] + */ + swap(this.guid, 0, 3); + swap(this.guid, 1, 2); + swap(this.guid, 4, 5); + swap(this.guid, 6, 7); + } + + @Override + public int computeSize(int initialPos) { + return computeContents(null, initialPos); + } + + @Override + public int computeContents(byte[] buffer, int initialPos) { + int pos = CVUtil.putBytes(guid, buffer, initialPos); + pos = CVUtil.putInt(age, buffer, pos); + pos = CVUtil.putUTF8StringBytes(fileName, buffer, pos); + return pos; + } + + @Override + public String toString() { + return String.format("LF_TYPESERVER2(0x%04x '%s' age=%d)", getSequenceNumber(), fileName, age); + } + + @Override + public int hashCode() { + int h = type; + h = 31 * h + Arrays.hashCode(guid); + h = 31 * h + age; + h = 31 * h + fileName.hashCode(); + return h; + } + + private static void swap(byte[] b, int idx1, int idx2) { + byte tmp = b[idx1]; + b[idx1] = b[idx2]; + b[idx2] = tmp; + } + } +} diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVTypeRecordBuilder.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVTypeRecordBuilder.java new file mode 100644 index 000000000000..9244c11c0cf8 --- /dev/null +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVTypeRecordBuilder.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, Red Hat Inc. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.objectfile.pecoff.cv; + +import java.util.HashMap; +import java.util.Map; + +class CVTypeRecordBuilder { + + private static int sequenceCounter = 0x1000; + private CVTypeSectionImpl typeSection; + private Map typeMap = new HashMap<>(); + + CVTypeRecordBuilder(CVTypeSectionImpl typeSection) { + this.typeSection = typeSection; + } + + /* + * convenience method - return either the caller-created instance or a matching existing + * instance. + */ + /* + * we know every entry in typeMap is a T, because it is ONLY this function which inserts entries + * (of type T) + */ + @SuppressWarnings("unchecked") + T buildFrom(T newRecord) { + final T record; + final int hashCode = newRecord.hashCode(); + if (typeMap.containsKey(hashCode)) { + record = (T) typeMap.get(hashCode); + } else { + newRecord.setSequenceNumber(sequenceCounter++); + typeMap.put(hashCode, newRecord); + typeSection.addUniqueRecord(newRecord); + record = newRecord; + } + return record; + } +} diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVTypeSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVTypeSectionImpl.java new file mode 100644 index 000000000000..c811b374d3bf --- /dev/null +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVTypeSectionImpl.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, Red Hat Inc. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.objectfile.pecoff.cv; + +import com.oracle.objectfile.BuildDependency; +import com.oracle.objectfile.LayoutDecision; +import com.oracle.objectfile.LayoutDecisionMap; +import com.oracle.objectfile.ObjectFile; +import com.oracle.objectfile.pecoff.PECoffObjectFile; +import org.graalvm.compiler.debug.DebugContext; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static com.oracle.objectfile.pecoff.cv.CVConstants.CV_SIGNATURE_C13; +import static com.oracle.objectfile.pecoff.cv.CVConstants.CV_SYMBOL_SECTION_NAME; +import static com.oracle.objectfile.pecoff.cv.CVConstants.CV_TYPE_SECTION_NAME; + +public final class CVTypeSectionImpl extends CVSectionImpl { + + private static final int CV_RECORD_INITIAL_CAPACITY = 200; + private ArrayList cvRecords = new ArrayList<>(CV_RECORD_INITIAL_CAPACITY); + private CVTypeRecordBuilder builder = new CVTypeRecordBuilder(this); + + CVTypeSectionImpl() { + } + + @Override + public String getSectionName() { + return CV_TYPE_SECTION_NAME; + } + + @Override + public void createContent(DebugContext debugContext) { + int pos = 0; + log(debugContext, "CVTypeSectionImpl.createContent() start"); + addRecords(); + pos += computeHeaderSize(); + for (CVTypeRecord record : cvRecords) { + pos = record.computeFullSize(pos); + } + byte[] buffer = new byte[pos]; + super.setContent(buffer); + log(debugContext, "CVTypeSectionImpl.createContent() end"); + } + + @Override + public void writeContent(DebugContext debugContext) { + int pos = 0; + log(debugContext, "CVTypeSectionImpl.writeContent() start"); + byte[] buffer = getContent(); + pos = CVUtil.putInt(CV_SIGNATURE_C13, buffer, pos); + for (CVTypeRecord record : cvRecords) { + pos = record.computeFullContents(buffer, pos); + } + log(debugContext, "CVTypeSectionImpl.writeContent() end"); + } + + public List getRecords() { + return Collections.unmodifiableList(cvRecords); + } + + void addUniqueRecord(CVTypeRecord r) { + cvRecords.add(r); + } + + T addRecord(T newRecord) { + // CVUtil.debug("adding type record: %s hash=%d\n", newRecord, newRecord.hashCode()); + T actual = builder.buildFrom(newRecord); + return actual; + } + + private void addClassRecords() { + /* we may have done this already when emiting globals in debug$S section */ + // for (DebugInfoBase.ClassEntry classEntry : cvDebugInfo.getPrimaryClasses()) { + // TODO - emit all members, all types, etc + // } + } + + private void addRecords() { + // final CVTypeRecord r0 = addRecord(new + // CVTypeRecord.CVTypeServer2Record("0123456789abcdef".getBytes(UTF_8), 1, + // "c:\\tmp\\graal-8\\vc100.pdb")); + addClassRecords(); + } + + private static int computeHeaderSize() { + return Integer.BYTES; /* CV_SIGNATURE_C13 = 4; */ + } + + @Override + public Set getDependencies(Map decisions) { + Set deps = super.getDependencies(decisions); + PECoffObjectFile.PECoffSection targetSection = (PECoffObjectFile.PECoffSection) getElement().getOwner().elementForName(CV_SYMBOL_SECTION_NAME); + LayoutDecision ourContent = decisions.get(getElement()).getDecision(LayoutDecision.Kind.CONTENT); + LayoutDecision ourSize = decisions.get(getElement()).getDecision(LayoutDecision.Kind.SIZE); + /* make our content depend on the codeview symbol section */ + deps.add(BuildDependency.createOrGet(ourContent, decisions.get(targetSection).getDecision(LayoutDecision.Kind.CONTENT))); + /* make our size depend on our content */ + deps.add(BuildDependency.createOrGet(ourSize, ourContent)); + + return deps; + } +} diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVUtil.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVUtil.java new file mode 100644 index 000000000000..49266b354e16 --- /dev/null +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVUtil.java @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, Red Hat Inc. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.objectfile.pecoff.cv; + +import static java.nio.charset.StandardCharsets.UTF_8; + +abstract class CVUtil { + + /* base level put methods that assume a non-null buffer */ + static int putByte(byte b, byte[] buffer, int initialPos) { + if (buffer == null) { + return initialPos + Byte.BYTES; + } + int pos = initialPos; + buffer[pos++] = b; + return pos; + } + + static int putShort(short s, byte[] buffer, int initialPos) { + if (buffer == null) { + return initialPos + Short.BYTES; + } + int pos = initialPos; + buffer[pos++] = (byte) (s & 0xff); + buffer[pos++] = (byte) ((s >> 8) & 0xff); + return pos; + } + + static int putInt(int i, byte[] buffer, int initialPos) { + if (buffer == null) { + return initialPos + Integer.BYTES; + } + int pos = initialPos; + buffer[pos++] = (byte) (i & 0xff); + buffer[pos++] = (byte) ((i >> 8) & 0xff); + buffer[pos++] = (byte) ((i >> 16) & 0xff); + buffer[pos++] = (byte) ((i >> 24) & 0xff); + return pos; + } + + static int putLong(long l, byte[] buffer, int initialPos) { + if (buffer == null) { + return initialPos + Long.BYTES; + } + int pos = initialPos; + buffer[pos++] = (byte) (l & 0xff); + buffer[pos++] = (byte) ((l >> 8) & 0xff); + buffer[pos++] = (byte) ((l >> 16) & 0xff); + buffer[pos++] = (byte) ((l >> 24) & 0xff); + buffer[pos++] = (byte) ((l >> 32) & 0xff); + buffer[pos++] = (byte) ((l >> 40) & 0xff); + buffer[pos++] = (byte) ((l >> 48) & 0xff); + buffer[pos++] = (byte) ((l >> 56) & 0xff); + return pos; + } + + static int putBytes(byte[] inbuff, byte[] buffer, int initialPos) { + if (buffer == null) { + return initialPos + inbuff.length; + } + int pos = initialPos; + for (byte b : inbuff) { + buffer[pos++] = b; + } + return pos; + } + + static int putUTF8StringBytes(String s, byte[] buffer, int initialPos) { + return putUTF8StringBytes(s, 0, buffer, initialPos); + } + + private static int putUTF8StringBytes(String s, int startChar, byte[] buffer, int initialPos) { + byte[] buff = s.substring(startChar).getBytes(UTF_8); + if (buffer == null) { + return initialPos + buff.length + 1; + } + int pos = initialPos; + for (byte b : buff) { + if (b == 0) { + throw new RuntimeException("oops : string has internal NULL character! " + s); + } + buffer[pos++] = b; + } + buffer[pos++] = '\0'; + return pos; + } + + static int getInt(byte[] buffer, int initialPos) { + int pos = initialPos; + int i = buffer[pos++] & 0xff; + i += (buffer[pos++] & 0xff) << 8; + i += (buffer[pos++] & 0xff) << 16; + i += (buffer[pos] & 0xff) << 24; + return i; + } + + static short getShort(byte[] buffer, int initialPos) { + int pos = initialPos; + short i = (short) (buffer[pos++] & 0xff); + i = (short) (i + ((buffer[pos] & 0xff) << 8)); + return i; + } + + /*- + static void dump(String msg, byte[] buffer, int initialPos, int len) { + if (buffer == null) { + return; + } + System.out.format("%s0x%06x", msg, initialPos); + for (int i = 0; i < len; i++) { + System.out.format(" %02x", buffer[initialPos + i]); + } + System.out.format("\n"); + } + + static void dump(byte[] buffer, int len) { + if (buffer == null) { + return; + } + for (int i = 0; i < len; i++) { + System.out.format("%02x", buffer[i]); + } + } + */ + + /** + * align on 4 byte boundary. + * + * @param initialPos initial unaligned position + * @return pos aligned on 4 byte boundary + */ + static int align4(int initialPos) { + int pos = initialPos; + while ((pos & 0x3) != 0) { + pos++; + } + return pos; + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java index 61fbd491316c..bfa37a5242b3 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java @@ -457,6 +457,9 @@ protected void onValueUpdate(EconomicMap, Object> values, Integer o if (newValue > 0 && !Boolean.TRUE.equals(values.get(TrackNodeSourcePosition))) { TrackNodeSourcePosition.update(values, true); } + if (newValue > 0 && !Boolean.FALSE.equals(values.get(DeleteLocalSymbols))) { + DeleteLocalSymbols.update(values, false); + } } }; @Option(help = "Search path for source files for Application or GraalVM classes (list of comma-separated directories or jar files)")// diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageOptions.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageOptions.java index 54e08c0310a7..c3e22932cfcf 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageOptions.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageOptions.java @@ -60,7 +60,7 @@ public class NativeImageOptions { public static final HostedOptionKey NativeArchitecture = new HostedOptionKey<>(false); @Option(help = "Define PageSize of a machine that runs the image. The default = 0 (== same as host machine page size)")// - protected static final HostedOptionKey PageSize = new HostedOptionKey<>(0); + public static final HostedOptionKey PageSize = new HostedOptionKey<>(0); @Option(help = "Print information about classes, methods, and fields that are present in the native image")// public static final HostedOptionKey PrintUniverse = new HostedOptionKey<>(false); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/GraalVMSourceCache.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/GraalVMSourceCache.java index a3092937e412..3d958a2627a4 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/GraalVMSourceCache.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/sources/GraalVMSourceCache.java @@ -128,7 +128,7 @@ private static boolean filterSrcRoot(Path root) { /* if any of the graal paths exist accept this root */ for (String prefix : GRAALVM_SRC_PACKAGE_PREFIXES) { - String subDir = prefix.replaceAll("\\.", separator); + String subDir = prefix.replace(".", separator); if (Files.isDirectory(root.resolve(subDir))) { return true; }