Skip to content

Commit 9410dd4

Browse files
committed
add debugInfo for MacOS
1 parent 699d359 commit 9410dd4

32 files changed

+8790
-30
lines changed

substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/ObjectFile.java

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2012, 2020, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2012, 2023, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -528,15 +528,19 @@ public Section newUserDefinedSection(String name, ElementImpl impl) {
528528
return result;
529529
}
530530

531-
public Section newDebugSection(String name, ElementImpl impl) {
532-
final Segment segment = getOrCreateSegment(null, name, false, false);
531+
public Section newDebugSection(String segmentName, String name, ElementImpl impl) {
532+
final Segment segment = getOrCreateSegment(segmentName, name, false, false);
533533
final int alignment = 1; // debugging information is mostly unaligned; padding can result in
534534
// corrupted data when the linker merges multiple debugging
535535
// sections from different inputs
536536
final Section result = newUserDefinedSection(segment, name, alignment, impl);
537537
return result;
538538
}
539539

540+
public Section newDebugSection(String name, ElementImpl impl) {
541+
return newDebugSection(null, name, impl);
542+
}
543+
540544
// Convenience that does not specify a segment name.
541545
public Section newProgbitsSection(String name, int alignment, boolean writable, boolean executable, ProgbitsSectionImpl impl) {
542546
assert impl != null;
@@ -1172,6 +1176,10 @@ public void installDebugInfo(@SuppressWarnings("unused") DebugInfoProvider debug
11721176
// do nothing by default
11731177
}
11741178

1179+
public List<String> getDebugSectionNames() {
1180+
return Collections.emptyList();
1181+
}
1182+
11751183
protected static Iterable<LayoutDecision> allDecisions(final Map<Element, LayoutDecisionMap> decisions) {
11761184
return () -> StreamSupport.stream(decisions.values().spliterator(), false)
11771185
.flatMap(layoutDecisionMap -> StreamSupport.stream(layoutDecisionMap.spliterator(), false)).iterator();
@@ -1289,7 +1297,7 @@ public Element getOffsetBootstrapElement() {
12891297

12901298
private final HashSet<LayoutDecision> allDecisions = new HashSet<>();
12911299
private final Map<Element, LayoutDecisionMap> decisionsByElement = new IdentityHashMap<>();
1292-
private final Map<Element, LayoutDecisionMap> decisionsTaken = new IdentityHashMap<>();
1300+
public final Map<Element, LayoutDecisionMap> decisionsTaken = new IdentityHashMap<>();
12931301

12941302
private final Map<Element, List<BuildDependency>> dependenciesByDependingElement = new IdentityHashMap<>();
12951303
private final Map<Element, List<BuildDependency>> dependenciesByDependedOnElement = new IdentityHashMap<>();
@@ -1303,7 +1311,7 @@ public void write(DebugContext context, Path outputFile) throws IOException {
13031311
}
13041312

13051313
@SuppressWarnings("try")
1306-
public final void write(FileChannel outputChannel) {
1314+
public List<Element> write(FileChannel outputChannel) {
13071315
List<Element> sortedObjectFileElements = new ArrayList<>();
13081316
int totalSize = bake(sortedObjectFileElements);
13091317
try {
@@ -1317,6 +1325,7 @@ public final void write(FileChannel outputChannel) {
13171325
} catch (IOException e) {
13181326
throw new RuntimeException(e);
13191327
}
1328+
return sortedObjectFileElements;
13201329
}
13211330

13221331
/*
@@ -1861,4 +1870,8 @@ public void debugContext(String scopeName, Consumer<DebugContext> action) {
18611870
throw debugContext.handle(e);
18621871
}
18631872
}
1873+
1874+
public long getCodeBaseAddress() {
1875+
return 0;
1876+
}
18641877
}

substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DebugInfoBase.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -223,13 +223,17 @@ public int compiledCodeMax() {
223223
return compiledCodeMax;
224224
}
225225

226+
public void installDebugInfo(DebugInfoProvider debugInfoProvider) {
227+
installDebugInfo(debugInfoProvider, 0);
228+
}
229+
226230
/**
227231
* Entry point allowing ELFObjectFile to pass on information about types, code and heap data.
228232
*
229233
* @param debugInfoProvider provider instance passed by ObjectFile client.
230234
*/
231235
@SuppressWarnings("try")
232-
public void installDebugInfo(DebugInfoProvider debugInfoProvider) {
236+
public void installDebugInfo(DebugInfoProvider debugInfoProvider, long debugAddressOffset) {
233237
/*
234238
* This will be needed once we add support for type info:
235239
*
@@ -332,7 +336,7 @@ public void installDebugInfo(DebugInfoProvider debugInfoProvider) {
332336
/* Search for a method defining this primary range. */
333337
ClassEntry classEntry = lookupClassEntry(ownerType);
334338
MethodEntry methodEntry = classEntry.ensureMethodEntryForDebugRangeInfo(debugCodeInfo, this, debugContext);
335-
PrimaryRange primaryRange = Range.createPrimary(methodEntry, lo, hi, primaryLine);
339+
PrimaryRange primaryRange = Range.createPrimary(methodEntry, lo + debugAddressOffset, hi + debugAddressOffset, primaryLine);
336340
if (debugContext.isLogEnabled(DebugContext.INFO_LEVEL)) {
337341
debugContext.log(DebugContext.INFO_LEVEL, "PrimaryRange %s.%s %s %s:%d [0x%x, 0x%x]", ownerType.toJavaName(), methodName, filePath, fileName, primaryLine, lo, hi);
338342
}
@@ -780,7 +784,7 @@ private static void collectFilesAndDirs(ClassEntry classEntry) {
780784
/**
781785
* Ensure the supplied file entry and associated directory entry are included, but only once, in
782786
* a class entry's file and dir list.
783-
*
787+
*
784788
* @param classEntry the class entry whose file and dir list may need to be updated
785789
* @param fileEntry a file entry which may need to be added to the class entry's file list or
786790
* whose dir may need adding to the class entry's dir list

substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/range/Range.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ public abstract class Range {
8080
/**
8181
* Create a primary range representing the root of the subrange tree for a top level compiled
8282
* method.
83-
*
83+
*
8484
* @param methodEntry the top level compiled method for this primary range.
8585
* @param lo the lowest address included in the range.
8686
* @param hi the first address above the highest address in the range.
@@ -94,7 +94,7 @@ public static PrimaryRange createPrimary(MethodEntry methodEntry, long lo, long
9494
/**
9595
* Create a subrange representing a segment of the address range for code of a top level or
9696
* inlined compiled method. The result will either be a call or a leaf range.
97-
*
97+
*
9898
* @param methodEntry the method from which code in the subrange is derived.
9999
* @param lo the lowest address included in the range.
100100
* @param hi the first address above the highest address in the range.
@@ -115,7 +115,6 @@ public static SubRange createSubrange(MethodEntry methodEntry, long lo, long hi,
115115
}
116116
}
117117

118-
119118
protected Range(MethodEntry methodEntry, long lo, long hi, int line, int depth) {
120119
assert methodEntry != null;
121120
this.methodEntry = methodEntry;

substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/macho/MachOObjectFile.java

Lines changed: 111 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2013, 2013, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -24,8 +24,12 @@
2424
*/
2525
package com.oracle.objectfile.macho;
2626

27+
import java.io.IOException;
2728
import java.nio.ByteBuffer;
2829
import java.nio.ByteOrder;
30+
import java.nio.channels.FileChannel;
31+
import java.nio.file.Path;
32+
import java.nio.file.StandardOpenOption;
2933
import java.util.ArrayList;
3034
import java.util.Collection;
3135
import java.util.Collections;
@@ -42,6 +46,10 @@
4246
import java.util.stream.Collectors;
4347
import java.util.stream.Stream;
4448

49+
import com.oracle.objectfile.debuginfo.DebugInfoProvider;
50+
import com.oracle.objectfile.macho.dsym.DSYMDebugInfo;
51+
import com.oracle.objectfile.macho.dsym.constants.DSYMSectionName;
52+
import jdk.graal.compiler.debug.DebugContext;
4553
import org.graalvm.nativeimage.ImageSingletons;
4654
import org.graalvm.nativeimage.Platform;
4755

@@ -56,6 +64,8 @@
5664
import com.oracle.objectfile.io.AssemblyBuffer;
5765
import com.oracle.objectfile.io.OutputAssembler;
5866

67+
import sun.nio.ch.DirectBuffer;
68+
5969
/**
6070
* Models a Mach-O relocatable object file.
6171
*/
@@ -68,6 +78,8 @@ public final class MachOObjectFile extends ObjectFile {
6878
private static final int MAGIC = 0xfeedfacf;
6979
private static final int CIGAM = 0xcffaedfe;
7080

81+
private static final long PAGEZERO_SIZE = 0x100000000L;
82+
7183
private static final ByteOrder nativeOrder = ByteOrder.nativeOrder();
7284
private static final ByteOrder oppositeOrder = (nativeOrder == ByteOrder.BIG_ENDIAN) ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN;
7385

@@ -170,7 +182,7 @@ public int getWordSizeInBytes() {
170182

171183
@Override
172184
public boolean shouldRecordDebugRelocations() {
173-
return false;
185+
return true;
174186
}
175187

176188
@Override
@@ -185,9 +197,13 @@ public Symbol createUndefinedSymbol(String name, int size, boolean isCode) {
185197
return symtab.newUndefinedEntry(name, isCode);
186198
}
187199

200+
private static final String DWARF_TARGET_SEGMENT_NAME = getUnnamedSegmentName();
201+
private static final String DWARF_SEGMENT_NAME = "__DWARF";
202+
188203
@Override
189204
protected Segment64Command getOrCreateSegment(String segmentNameOrNull, String sectionName, boolean writable, boolean executable) {
190-
final String segmentName = (segmentNameOrNull != null) ? segmentNameOrNull : getUnnamedSegmentName();
205+
String targetSegmentName = DWARF_SEGMENT_NAME.equals(segmentNameOrNull) ? DWARF_TARGET_SEGMENT_NAME : segmentNameOrNull;
206+
final String segmentName = (segmentNameOrNull != null) ? targetSegmentName : getUnnamedSegmentName();
191207
Segment64Command nonNullSegment = (Segment64Command) findSegmentByName(segmentName);
192208
if (nonNullSegment != null) {
193209
// we've found it; make sure it has the permission we need
@@ -201,9 +217,14 @@ protected Segment64Command getOrCreateSegment(String segmentNameOrNull, String s
201217
}
202218
} else {
203219
// create a segment
204-
nonNullSegment = new Segment64Command(sectionName, segmentName);
220+
if (DWARF_SEGMENT_NAME.equals(segmentNameOrNull)) {
221+
nonNullSegment = new Segment64Command(DWARF_TARGET_SEGMENT_NAME, targetSegmentName);
222+
nonNullSegment.setLoadable(true);
223+
} else {
224+
nonNullSegment = new Segment64Command(sectionName, segmentName);
225+
}
205226
nonNullSegment.initprot = EnumSet.of(VMProt.READ); // always give read permission
206-
if (writable) {
227+
if (writable || DWARF_SEGMENT_NAME.equals(segmentNameOrNull)) {
207228
nonNullSegment.initprot.add(VMProt.WRITE);
208229
}
209230
if (executable) {
@@ -1580,15 +1601,11 @@ public MachOSection(String name, int alignment, Segment64Command segment, Sectio
15801601
if (name.contains("debug")) {
15811602
destinationSegmentName = "__DWARF";
15821603
/*
1583-
* FIXME: set the DEBUG flag on this section. Unfortunately, this currently breaks
1584-
* debugging: on OS X, the linker intentionally strips debug sections because
1585-
* debuggers are expected to retrieve them from the original object files or from a
1586-
* debug info archive. We should conform to this by creating a debug info archive
1587-
* using dsymutil(1), which would also reduce the size of the linked binary.
1588-
* However, attempts to implement this as in an extra step after linking has failed,
1589-
* which likely means that more other stuff needs to be fixed beforehand.
1604+
* Setting debug flag is essential so that linker won't mess with such section and
1605+
* won't include it in the final image. There is a transformer in @see
1606+
* NativeImageDebugInfoFeature that will put these sections back in the image.
15901607
*/
1591-
// flags.add(SectionFlag.DEBUG);
1608+
flags.add(SectionFlag.DEBUG);
15921609
} else if (flags.contains(
15931610
SectionFlag.SOME_INSTRUCTIONS) /* || name.equals("__rodata") */) {
15941611
/*
@@ -1822,6 +1839,43 @@ public int bake(List<Element> sortedObjectFileElements) {
18221839
return super.bake(sortedObjectFileElements);
18231840
}
18241841

1842+
public void write(DebugContext context, Path outputFile) throws IOException {
1843+
try (FileChannel channel = FileChannel.open(outputFile, StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE)) {
1844+
withDebugContext(context, "ObjectFile.write", () -> {
1845+
try {
1846+
writeDebugSections(outputFile, write(channel));
1847+
} catch (IOException e) {
1848+
throw new RuntimeException(e);
1849+
}
1850+
});
1851+
}
1852+
}
1853+
1854+
public void writeDebugSections(Path outputFile, List<Element> sortedObjectFileElements) throws IOException {
1855+
/*
1856+
* Dump each debug section into temp file so that linker could add them back untouched
1857+
* during linking stage.
1858+
*
1859+
* @see NativeImageDebugInfoFeature for a corresponding LinkInovcation transformer.
1860+
*/
1861+
for (Element e : sortedObjectFileElements) {
1862+
if (DSYMSectionName.debugSections.contains(e.getName())) {
1863+
Path sectionDump = outputFile.resolveSibling(e.getName());
1864+
try (FileChannel channel = FileChannel.open(sectionDump, StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE)) {
1865+
ByteBuffer out = channel.map(FileChannel.MapMode.READ_WRITE, 0, (int) decisionsTaken.get(e).getDecidedValue(LayoutDecision.Kind.SIZE));
1866+
out.position(0);
1867+
byte[] content = (byte[]) decisionsTaken.get(e).getDecidedValue(LayoutDecision.Kind.CONTENT);
1868+
try {
1869+
out.put(content);
1870+
} finally {
1871+
// unmap immediately
1872+
((DirectBuffer) out).cleaner().clean();
1873+
}
1874+
}
1875+
}
1876+
}
1877+
}
1878+
18251879
int segmentVaddrGivenFirstSectionVaddr(int sectionVaddr) {
18261880
/*
18271881
* We round down the minVaddr to the next lower page boundary. And the same for the file
@@ -1847,6 +1901,16 @@ public class Segment64Command extends LoadCommand implements Segment {
18471901
// protections of this segment
18481902
// int nsects; // number of sections
18491903
int flags; // flags
1904+
boolean loadable;
1905+
1906+
public void setLoadable(boolean loadable) {
1907+
this.loadable = loadable;
1908+
}
1909+
1910+
@Override
1911+
public boolean isLoadable() {
1912+
return loadable;
1913+
}
18501914

18511915
@Override
18521916
public String getName() {
@@ -1872,10 +1936,12 @@ public boolean isWritable() {
18721936

18731937
List<SectionInfoStruct> readStructs = new ArrayList<>();
18741938

1939+
@SuppressWarnings("this-escape")
18751940
public Segment64Command(String name, String segmentName) {
18761941
super(name, LoadCommandKind.SEGMENT_64);
18771942
// creates a new empty segment
18781943
this.segname = segmentName;
1944+
setLoadable(false);
18791945
}
18801946

18811947
@Override
@@ -1995,8 +2061,7 @@ protected void writePayload(OutputAssembler db, final Map<Element, LayoutDecisio
19952061
ourRelocs = (MachORelocationElement) e;
19962062
continue;
19972063
}
1998-
assert false; // i.e. we should not find *another* RelocationElement
1999-
// also containing relevant relocs
2064+
throw new IllegalStateException("Shouldn't get here! " + e);
20002065
}
20012066
}
20022067
}
@@ -2006,6 +2071,7 @@ protected void writePayload(OutputAssembler db, final Map<Element, LayoutDecisio
20062071
* initial value is guessed in the MachOSection constructor.
20072072
*/
20082073
assert s.destinationSegmentName != null;
2074+
boolean noRelocs = ourRelocs == null;
20092075

20102076
SectionInfoStruct si = new SectionInfoStruct(
20112077
s.getName(),
@@ -2014,8 +2080,8 @@ protected void writePayload(OutputAssembler db, final Map<Element, LayoutDecisio
20142080
(int) alreadyDecided.get(s).getDecidedValue(LayoutDecision.Kind.SIZE),
20152081
(int) alreadyDecided.get(s).getDecidedValue(LayoutDecision.Kind.OFFSET),
20162082
logAlignment,
2017-
ourRelocs == null ? 0 : (int) alreadyDecided.get(ourRelocs).getDecidedValue(LayoutDecision.Kind.OFFSET) + ourRelocs.startIndexFor(s) * ourRelocs.encodedEntrySize(),
2018-
ourRelocs == null ? 0 : ourRelocs.countFor(s),
2083+
noRelocs ? 0 : (int) alreadyDecided.get(ourRelocs).getDecidedValue(LayoutDecision.Kind.OFFSET) + ourRelocs.startIndexFor(s) * ourRelocs.encodedEntrySize(),
2084+
noRelocs ? 0 : ourRelocs.countFor(s),
20192085
(int) ObjectFile.flagSetAsLong(s.flags) | s.type.getValue(),
20202086
/* reserved1 */ 0,
20212087
/* reserved2 */ 0);
@@ -2219,11 +2285,13 @@ public class LinkEditSegment64Command extends Segment64Command {
22192285
private MachOSymtab symtab;
22202286
private MachOStrtab strtab;
22212287

2288+
@SuppressWarnings("this-escape")
22222289
public LinkEditSegment64Command() {
22232290
super("LinkEditSegment", "__LINKEDIT");
22242291
initprot = EnumSet.of(VMProt.READ); // always give read permission
22252292
// native tools give maximum maxprot
22262293
maxprot = EnumSet.of(VMProt.READ, VMProt.WRITE, VMProt.EXECUTE);
2294+
setLoadable(true);
22272295
}
22282296

22292297
public MachOSymtab getSymtab() {
@@ -2377,4 +2445,30 @@ protected void writePayload(OutputAssembler out, Map<Element, LayoutDecisionMap>
23772445
}
23782446
};
23792447
}
2448+
2449+
@Override
2450+
public List<String> getDebugSectionNames() {
2451+
return DSYMSectionName.debugSections;
2452+
}
2453+
2454+
/*
2455+
* Used to help correctly determine addresses for debug info sections. Also @see
2456+
* NativeImageDebugInfoFeature.beforeImageWrite method where we construct corresponding linker
2457+
* option.
2458+
*/
2459+
@Override
2460+
public long getCodeBaseAddress() {
2461+
return PAGEZERO_SIZE;
2462+
}
2463+
2464+
@Override
2465+
public void installDebugInfo(DebugInfoProvider debugInfoProvider) {
2466+
new DSYMDebugInfo(
2467+
MachOCpuType.from(ImageSingletons.lookup(Platform.class).getArchitecture()),
2468+
getByteOrder(),
2469+
s -> {
2470+
newDebugSection(DWARF_SEGMENT_NAME, s.getSectionName(), s);
2471+
createDefinedSymbol(s.getSectionName(), s.getElement(), 0, 0, false, false);
2472+
}).installDebugInfo(debugInfoProvider, getCodeBaseAddress() + getPageSize());
2473+
}
23802474
}

0 commit comments

Comments
 (0)