1
1
/*
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.
3
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
4
*
5
5
* This code is free software; you can redistribute it and/or modify it
24
24
*/
25
25
package com .oracle .objectfile .macho ;
26
26
27
+ import java .io .IOException ;
27
28
import java .nio .ByteBuffer ;
28
29
import java .nio .ByteOrder ;
30
+ import java .nio .channels .FileChannel ;
31
+ import java .nio .file .Path ;
32
+ import java .nio .file .StandardOpenOption ;
29
33
import java .util .ArrayList ;
30
34
import java .util .Collection ;
31
35
import java .util .Collections ;
42
46
import java .util .stream .Collectors ;
43
47
import java .util .stream .Stream ;
44
48
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 ;
45
53
import org .graalvm .nativeimage .ImageSingletons ;
46
54
import org .graalvm .nativeimage .Platform ;
47
55
56
64
import com .oracle .objectfile .io .AssemblyBuffer ;
57
65
import com .oracle .objectfile .io .OutputAssembler ;
58
66
67
+ import sun .nio .ch .DirectBuffer ;
68
+
59
69
/**
60
70
* Models a Mach-O relocatable object file.
61
71
*/
@@ -68,6 +78,8 @@ public final class MachOObjectFile extends ObjectFile {
68
78
private static final int MAGIC = 0xfeedfacf ;
69
79
private static final int CIGAM = 0xcffaedfe ;
70
80
81
+ private static final long PAGEZERO_SIZE = 0x100000000L ;
82
+
71
83
private static final ByteOrder nativeOrder = ByteOrder .nativeOrder ();
72
84
private static final ByteOrder oppositeOrder = (nativeOrder == ByteOrder .BIG_ENDIAN ) ? ByteOrder .LITTLE_ENDIAN : ByteOrder .BIG_ENDIAN ;
73
85
@@ -170,7 +182,7 @@ public int getWordSizeInBytes() {
170
182
171
183
@ Override
172
184
public boolean shouldRecordDebugRelocations () {
173
- return false ;
185
+ return true ;
174
186
}
175
187
176
188
@ Override
@@ -185,9 +197,13 @@ public Symbol createUndefinedSymbol(String name, int size, boolean isCode) {
185
197
return symtab .newUndefinedEntry (name , isCode );
186
198
}
187
199
200
+ private static final String DWARF_TARGET_SEGMENT_NAME = getUnnamedSegmentName ();
201
+ private static final String DWARF_SEGMENT_NAME = "__DWARF" ;
202
+
188
203
@ Override
189
204
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 ();
191
207
Segment64Command nonNullSegment = (Segment64Command ) findSegmentByName (segmentName );
192
208
if (nonNullSegment != null ) {
193
209
// we've found it; make sure it has the permission we need
@@ -201,9 +217,14 @@ protected Segment64Command getOrCreateSegment(String segmentNameOrNull, String s
201
217
}
202
218
} else {
203
219
// 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
+ }
205
226
nonNullSegment .initprot = EnumSet .of (VMProt .READ ); // always give read permission
206
- if (writable ) {
227
+ if (writable || DWARF_SEGMENT_NAME . equals ( segmentNameOrNull ) ) {
207
228
nonNullSegment .initprot .add (VMProt .WRITE );
208
229
}
209
230
if (executable ) {
@@ -1580,15 +1601,11 @@ public MachOSection(String name, int alignment, Segment64Command segment, Sectio
1580
1601
if (name .contains ("debug" )) {
1581
1602
destinationSegmentName = "__DWARF" ;
1582
1603
/*
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.
1590
1607
*/
1591
- // flags.add(SectionFlag.DEBUG);
1608
+ flags .add (SectionFlag .DEBUG );
1592
1609
} else if (flags .contains (
1593
1610
SectionFlag .SOME_INSTRUCTIONS ) /* || name.equals("__rodata") */ ) {
1594
1611
/*
@@ -1822,6 +1839,43 @@ public int bake(List<Element> sortedObjectFileElements) {
1822
1839
return super .bake (sortedObjectFileElements );
1823
1840
}
1824
1841
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
+
1825
1879
int segmentVaddrGivenFirstSectionVaddr (int sectionVaddr ) {
1826
1880
/*
1827
1881
* 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 {
1847
1901
// protections of this segment
1848
1902
// int nsects; // number of sections
1849
1903
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
+ }
1850
1914
1851
1915
@ Override
1852
1916
public String getName () {
@@ -1872,10 +1936,12 @@ public boolean isWritable() {
1872
1936
1873
1937
List <SectionInfoStruct > readStructs = new ArrayList <>();
1874
1938
1939
+ @ SuppressWarnings ("this-escape" )
1875
1940
public Segment64Command (String name , String segmentName ) {
1876
1941
super (name , LoadCommandKind .SEGMENT_64 );
1877
1942
// creates a new empty segment
1878
1943
this .segname = segmentName ;
1944
+ setLoadable (false );
1879
1945
}
1880
1946
1881
1947
@ Override
@@ -1995,8 +2061,7 @@ protected void writePayload(OutputAssembler db, final Map<Element, LayoutDecisio
1995
2061
ourRelocs = (MachORelocationElement ) e ;
1996
2062
continue ;
1997
2063
}
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 );
2000
2065
}
2001
2066
}
2002
2067
}
@@ -2006,6 +2071,7 @@ protected void writePayload(OutputAssembler db, final Map<Element, LayoutDecisio
2006
2071
* initial value is guessed in the MachOSection constructor.
2007
2072
*/
2008
2073
assert s .destinationSegmentName != null ;
2074
+ boolean noRelocs = ourRelocs == null ;
2009
2075
2010
2076
SectionInfoStruct si = new SectionInfoStruct (
2011
2077
s .getName (),
@@ -2014,8 +2080,8 @@ protected void writePayload(OutputAssembler db, final Map<Element, LayoutDecisio
2014
2080
(int ) alreadyDecided .get (s ).getDecidedValue (LayoutDecision .Kind .SIZE ),
2015
2081
(int ) alreadyDecided .get (s ).getDecidedValue (LayoutDecision .Kind .OFFSET ),
2016
2082
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 ),
2019
2085
(int ) ObjectFile .flagSetAsLong (s .flags ) | s .type .getValue (),
2020
2086
/* reserved1 */ 0 ,
2021
2087
/* reserved2 */ 0 );
@@ -2219,11 +2285,13 @@ public class LinkEditSegment64Command extends Segment64Command {
2219
2285
private MachOSymtab symtab ;
2220
2286
private MachOStrtab strtab ;
2221
2287
2288
+ @ SuppressWarnings ("this-escape" )
2222
2289
public LinkEditSegment64Command () {
2223
2290
super ("LinkEditSegment" , "__LINKEDIT" );
2224
2291
initprot = EnumSet .of (VMProt .READ ); // always give read permission
2225
2292
// native tools give maximum maxprot
2226
2293
maxprot = EnumSet .of (VMProt .READ , VMProt .WRITE , VMProt .EXECUTE );
2294
+ setLoadable (true );
2227
2295
}
2228
2296
2229
2297
public MachOSymtab getSymtab () {
@@ -2377,4 +2445,30 @@ protected void writePayload(OutputAssembler out, Map<Element, LayoutDecisionMap>
2377
2445
}
2378
2446
};
2379
2447
}
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
+ }
2380
2474
}
0 commit comments