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,12 @@ 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" ;
188
202
@ Override
189
203
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 ();
191
206
Segment64Command nonNullSegment = (Segment64Command ) findSegmentByName (segmentName );
192
207
if (nonNullSegment != null ) {
193
208
// we've found it; make sure it has the permission we need
@@ -201,9 +216,14 @@ protected Segment64Command getOrCreateSegment(String segmentNameOrNull, String s
201
216
}
202
217
} else {
203
218
// 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
+ }
205
225
nonNullSegment .initprot = EnumSet .of (VMProt .READ ); // always give read permission
206
- if (writable ) {
226
+ if (writable || DWARF_SEGMENT_NAME . equals ( segmentNameOrNull ) ) {
207
227
nonNullSegment .initprot .add (VMProt .WRITE );
208
228
}
209
229
if (executable ) {
@@ -1580,15 +1600,11 @@ public MachOSection(String name, int alignment, Segment64Command segment, Sectio
1580
1600
if (name .contains ("debug" )) {
1581
1601
destinationSegmentName = "__DWARF" ;
1582
1602
/*
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.
1590
1606
*/
1591
- // flags.add(SectionFlag.DEBUG);
1607
+ flags .add (SectionFlag .DEBUG );
1592
1608
} else if (flags .contains (
1593
1609
SectionFlag .SOME_INSTRUCTIONS ) /* || name.equals("__rodata") */ ) {
1594
1610
/*
@@ -1822,6 +1838,42 @@ public int bake(List<Element> sortedObjectFileElements) {
1822
1838
return super .bake (sortedObjectFileElements );
1823
1839
}
1824
1840
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
+
1825
1877
int segmentVaddrGivenFirstSectionVaddr (int sectionVaddr ) {
1826
1878
/*
1827
1879
* 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 {
1847
1899
// protections of this segment
1848
1900
// int nsects; // number of sections
1849
1901
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
+ }
1850
1912
1851
1913
@ Override
1852
1914
public String getName () {
@@ -1872,10 +1934,12 @@ public boolean isWritable() {
1872
1934
1873
1935
List <SectionInfoStruct > readStructs = new ArrayList <>();
1874
1936
1937
+ @ SuppressWarnings ("this-escape" )
1875
1938
public Segment64Command (String name , String segmentName ) {
1876
1939
super (name , LoadCommandKind .SEGMENT_64 );
1877
1940
// creates a new empty segment
1878
1941
this .segname = segmentName ;
1942
+ setLoadable (false );
1879
1943
}
1880
1944
1881
1945
@ Override
@@ -1995,8 +2059,7 @@ protected void writePayload(OutputAssembler db, final Map<Element, LayoutDecisio
1995
2059
ourRelocs = (MachORelocationElement ) e ;
1996
2060
continue ;
1997
2061
}
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 );
2000
2063
}
2001
2064
}
2002
2065
}
@@ -2006,6 +2069,7 @@ protected void writePayload(OutputAssembler db, final Map<Element, LayoutDecisio
2006
2069
* initial value is guessed in the MachOSection constructor.
2007
2070
*/
2008
2071
assert s .destinationSegmentName != null ;
2072
+ boolean noRelocs = ourRelocs == null ;
2009
2073
2010
2074
SectionInfoStruct si = new SectionInfoStruct (
2011
2075
s .getName (),
@@ -2014,8 +2078,8 @@ protected void writePayload(OutputAssembler db, final Map<Element, LayoutDecisio
2014
2078
(int ) alreadyDecided .get (s ).getDecidedValue (LayoutDecision .Kind .SIZE ),
2015
2079
(int ) alreadyDecided .get (s ).getDecidedValue (LayoutDecision .Kind .OFFSET ),
2016
2080
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 ),
2019
2083
(int ) ObjectFile .flagSetAsLong (s .flags ) | s .type .getValue (),
2020
2084
/* reserved1 */ 0 ,
2021
2085
/* reserved2 */ 0 );
@@ -2219,11 +2283,13 @@ public class LinkEditSegment64Command extends Segment64Command {
2219
2283
private MachOSymtab symtab ;
2220
2284
private MachOStrtab strtab ;
2221
2285
2286
+ @ SuppressWarnings ("this-escape" )
2222
2287
public LinkEditSegment64Command () {
2223
2288
super ("LinkEditSegment" , "__LINKEDIT" );
2224
2289
initprot = EnumSet .of (VMProt .READ ); // always give read permission
2225
2290
// native tools give maximum maxprot
2226
2291
maxprot = EnumSet .of (VMProt .READ , VMProt .WRITE , VMProt .EXECUTE );
2292
+ setLoadable (true );
2227
2293
}
2228
2294
2229
2295
public MachOSymtab getSymtab () {
@@ -2377,4 +2443,30 @@ protected void writePayload(OutputAssembler out, Map<Element, LayoutDecisionMap>
2377
2443
}
2378
2444
};
2379
2445
}
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
+ }
2380
2472
}
0 commit comments