Skip to content

Commit 9a558f0

Browse files
committed
add debugInfo for MacOS
1 parent 699d359 commit 9a558f0

31 files changed

+8786
-26
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: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -223,13 +223,16 @@ public int compiledCodeMax() {
223223
return compiledCodeMax;
224224
}
225225

226+
public void installDebugInfo(DebugInfoProvider debugInfoProvider) {
227+
installDebugInfo(debugInfoProvider, 0);
228+
}
226229
/**
227230
* Entry point allowing ELFObjectFile to pass on information about types, code and heap data.
228231
*
229232
* @param debugInfoProvider provider instance passed by ObjectFile client.
230233
*/
231234
@SuppressWarnings("try")
232-
public void installDebugInfo(DebugInfoProvider debugInfoProvider) {
235+
public void installDebugInfo(DebugInfoProvider debugInfoProvider, long debugAddressOffset) {
233236
/*
234237
* This will be needed once we add support for type info:
235238
*
@@ -332,7 +335,7 @@ public void installDebugInfo(DebugInfoProvider debugInfoProvider) {
332335
/* Search for a method defining this primary range. */
333336
ClassEntry classEntry = lookupClassEntry(ownerType);
334337
MethodEntry methodEntry = classEntry.ensureMethodEntryForDebugRangeInfo(debugCodeInfo, this, debugContext);
335-
PrimaryRange primaryRange = Range.createPrimary(methodEntry, lo, hi, primaryLine);
338+
PrimaryRange primaryRange = Range.createPrimary(methodEntry, lo + debugAddressOffset, hi + debugAddressOffset, primaryLine);
336339
if (debugContext.isLogEnabled(DebugContext.INFO_LEVEL)) {
337340
debugContext.log(DebugContext.INFO_LEVEL, "PrimaryRange %s.%s %s %s:%d [0x%x, 0x%x]", ownerType.toJavaName(), methodName, filePath, fileName, primaryLine, lo, hi);
338341
}

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

Lines changed: 109 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,12 @@ 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";
188202
@Override
189203
protected Segment64Command getOrCreateSegment(String segmentNameOrNull, String sectionName, boolean writable, boolean executable) {
190-
final String segmentName = (segmentNameOrNull != null) ? segmentNameOrNull : getUnnamedSegmentName();
204+
String targetSegmentName = DWARF_SEGMENT_NAME.equals(segmentNameOrNull) ? DWARF_TARGET_SEGMENT_NAME : segmentNameOrNull;
205+
final String segmentName = (segmentNameOrNull != null) ? targetSegmentName : getUnnamedSegmentName();
191206
Segment64Command nonNullSegment = (Segment64Command) findSegmentByName(segmentName);
192207
if (nonNullSegment != null) {
193208
// we've found it; make sure it has the permission we need
@@ -201,9 +216,14 @@ protected Segment64Command getOrCreateSegment(String segmentNameOrNull, String s
201216
}
202217
} else {
203218
// create a segment
204-
nonNullSegment = new Segment64Command(sectionName, segmentName);
219+
if (DWARF_SEGMENT_NAME.equals(segmentNameOrNull)) {
220+
nonNullSegment = new Segment64Command(DWARF_TARGET_SEGMENT_NAME, targetSegmentName);
221+
nonNullSegment.setLoadable(true);
222+
} else {
223+
nonNullSegment = new Segment64Command(sectionName, segmentName);
224+
}
205225
nonNullSegment.initprot = EnumSet.of(VMProt.READ); // always give read permission
206-
if (writable) {
226+
if (writable || DWARF_SEGMENT_NAME.equals(segmentNameOrNull)) {
207227
nonNullSegment.initprot.add(VMProt.WRITE);
208228
}
209229
if (executable) {
@@ -1580,15 +1600,11 @@ public MachOSection(String name, int alignment, Segment64Command segment, Sectio
15801600
if (name.contains("debug")) {
15811601
destinationSegmentName = "__DWARF";
15821602
/*
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.
1603+
* Setting debug flag is essential so that linker won't mess with such section and won't include it
1604+
* in the final image. There is a transformer in @see NativeImageDebugInfoFeature
1605+
* that will put these sections back in the image.
15901606
*/
1591-
// flags.add(SectionFlag.DEBUG);
1607+
flags.add(SectionFlag.DEBUG);
15921608
} else if (flags.contains(
15931609
SectionFlag.SOME_INSTRUCTIONS) /* || name.equals("__rodata") */) {
15941610
/*
@@ -1822,6 +1838,42 @@ public int bake(List<Element> sortedObjectFileElements) {
18221838
return super.bake(sortedObjectFileElements);
18231839
}
18241840

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

18511913
@Override
18521914
public String getName() {
@@ -1872,10 +1934,12 @@ public boolean isWritable() {
18721934

18731935
List<SectionInfoStruct> readStructs = new ArrayList<>();
18741936

1937+
@SuppressWarnings("this-escape")
18751938
public Segment64Command(String name, String segmentName) {
18761939
super(name, LoadCommandKind.SEGMENT_64);
18771940
// creates a new empty segment
18781941
this.segname = segmentName;
1942+
setLoadable(false);
18791943
}
18801944

18811945
@Override
@@ -1995,8 +2059,7 @@ protected void writePayload(OutputAssembler db, final Map<Element, LayoutDecisio
19952059
ourRelocs = (MachORelocationElement) e;
19962060
continue;
19972061
}
1998-
assert false; // i.e. we should not find *another* RelocationElement
1999-
// also containing relevant relocs
2062+
throw new IllegalStateException("Shouldn't get here! " + e);
20002063
}
20012064
}
20022065
}
@@ -2006,6 +2069,7 @@ protected void writePayload(OutputAssembler db, final Map<Element, LayoutDecisio
20062069
* initial value is guessed in the MachOSection constructor.
20072070
*/
20082071
assert s.destinationSegmentName != null;
2072+
boolean noRelocs = ourRelocs == null;
20092073

20102074
SectionInfoStruct si = new SectionInfoStruct(
20112075
s.getName(),
@@ -2014,8 +2078,8 @@ protected void writePayload(OutputAssembler db, final Map<Element, LayoutDecisio
20142078
(int) alreadyDecided.get(s).getDecidedValue(LayoutDecision.Kind.SIZE),
20152079
(int) alreadyDecided.get(s).getDecidedValue(LayoutDecision.Kind.OFFSET),
20162080
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),
2081+
noRelocs ? 0 : (int) alreadyDecided.get(ourRelocs).getDecidedValue(LayoutDecision.Kind.OFFSET) + ourRelocs.startIndexFor(s) * ourRelocs.encodedEntrySize(),
2082+
noRelocs ? 0 : ourRelocs.countFor(s),
20192083
(int) ObjectFile.flagSetAsLong(s.flags) | s.type.getValue(),
20202084
/* reserved1 */ 0,
20212085
/* reserved2 */ 0);
@@ -2219,11 +2283,13 @@ public class LinkEditSegment64Command extends Segment64Command {
22192283
private MachOSymtab symtab;
22202284
private MachOStrtab strtab;
22212285

2286+
@SuppressWarnings("this-escape")
22222287
public LinkEditSegment64Command() {
22232288
super("LinkEditSegment", "__LINKEDIT");
22242289
initprot = EnumSet.of(VMProt.READ); // always give read permission
22252290
// native tools give maximum maxprot
22262291
maxprot = EnumSet.of(VMProt.READ, VMProt.WRITE, VMProt.EXECUTE);
2292+
setLoadable(true);
22272293
}
22282294

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

0 commit comments

Comments
 (0)