@@ -6,6 +6,7 @@ package ld
6
6
7
7
import (
8
8
"bytes"
9
+ "compress/zlib"
9
10
"debug/macho"
10
11
"encoding/binary"
11
12
"fmt"
@@ -93,7 +94,7 @@ func (r loadCmdReader) WriteAt(offset int64, data interface{}) error {
93
94
// header to add the DWARF sections. (Use ld's -headerpad option)
94
95
// dsym is the path to the macho file containing DWARF from dsymutil.
95
96
// outexe is the path where the combined executable should be saved.
96
- func machoCombineDwarf (inexe , dsym , outexe string , buildmode BuildMode ) (bool , error ) {
97
+ func machoCombineDwarf (ctxt * Link , inexe , dsym , outexe string ) (bool , error ) {
97
98
exef , err := os .Open (inexe )
98
99
if err != nil {
99
100
return false , err
@@ -156,6 +157,13 @@ func machoCombineDwarf(inexe, dsym, outexe string, buildmode BuildMode) (bool, e
156
157
return false , fmt .Errorf ("missing __DWARF segment" )
157
158
}
158
159
160
+ // Try to compress the DWARF sections. This includes some Apple
161
+ // proprietary sections like __apple_types.
162
+ compressedSects , compressedBytes , err := machoCompressSections (ctxt , dwarfm )
163
+ if err != nil {
164
+ return false , err
165
+ }
166
+
159
167
// Now copy the dwarf data into the output.
160
168
// Kernel requires all loaded segments to be page-aligned in the file,
161
169
// even though we mark this one as being 0 bytes of virtual address space.
@@ -168,15 +176,27 @@ func machoCombineDwarf(inexe, dsym, outexe string, buildmode BuildMode) (bool, e
168
176
if _ , err = dwarff .Seek (int64 (realdwarf .Offset ), 0 ); err != nil {
169
177
return false , err
170
178
}
171
- if _ , err := io .CopyN (outf , dwarff , int64 (realdwarf .Filesz )); err != nil {
172
- return false , err
179
+
180
+ // Write out the compressed sections, or the originals if we gave up
181
+ // on compressing them.
182
+ var dwarfsize uint64
183
+ if compressedBytes != nil {
184
+ dwarfsize = uint64 (len (compressedBytes ))
185
+ if _ , err := outf .Write (compressedBytes ); err != nil {
186
+ return false , err
187
+ }
188
+ } else {
189
+ if _ , err := io .CopyN (outf , dwarff , int64 (realdwarf .Filesz )); err != nil {
190
+ return false , err
191
+ }
192
+ dwarfsize = realdwarf .Filesz
173
193
}
174
194
175
195
// And finally the linkedit section.
176
196
if _ , err = exef .Seek (int64 (linkseg .Offset ), 0 ); err != nil {
177
197
return false , err
178
198
}
179
- linkstart = machoCalcStart (linkseg .Offset , uint64 (dwarfstart )+ realdwarf . Filesz , pageAlign )
199
+ linkstart = machoCalcStart (linkseg .Offset , uint64 (dwarfstart )+ dwarfsize , pageAlign )
180
200
linkoffset = uint32 (linkstart - int64 (linkseg .Offset ))
181
201
if _ , err = outf .Seek (linkstart , 0 ); err != nil {
182
202
return false , err
@@ -196,14 +216,14 @@ func machoCombineDwarf(inexe, dsym, outexe string, buildmode BuildMode) (bool, e
196
216
if availablePadding < int64 (realdwarf .Len ) {
197
217
return false , fmt .Errorf ("No room to add dwarf info. Need at least %d padding bytes, found %d" , realdwarf .Len , availablePadding )
198
218
}
199
- // First, copy the dwarf load command into the header
219
+ // First, copy the dwarf load command into the header. It will be
220
+ // updated later with new offsets and lengths as necessary.
200
221
if _ , err = outf .Seek (dwarfCmdOffset , 0 ); err != nil {
201
222
return false , err
202
223
}
203
224
if _ , err := io .CopyN (outf , bytes .NewReader (realdwarf .Raw ()), int64 (realdwarf .Len )); err != nil {
204
225
return false , err
205
226
}
206
-
207
227
if _ , err = outf .Seek (int64 (unsafe .Offsetof (exem .FileHeader .Ncmd )), 0 ); err != nil {
208
228
return false , err
209
229
}
@@ -244,7 +264,76 @@ func machoCombineDwarf(inexe, dsym, outexe string, buildmode BuildMode) (bool, e
244
264
return false , err
245
265
}
246
266
}
247
- return false , machoUpdateDwarfHeader (& reader , buildmode )
267
+ // Do the final update of the DWARF segment's load command.
268
+ return false , machoUpdateDwarfHeader (& reader , ctxt .BuildMode , compressedSects )
269
+ }
270
+
271
+ // machoCompressSections tries to compress the DWARF segments in dwarfm,
272
+ // returning the updated sections and segment contents, nils if the sections
273
+ // weren't compressed, or an error if there was a problem reading dwarfm.
274
+ func machoCompressSections (ctxt * Link , dwarfm * macho.File ) ([]* macho.Section , []byte , error ) {
275
+ if ! ctxt .compressDWARF {
276
+ return nil , nil , nil
277
+ }
278
+
279
+ dwarfseg := dwarfm .Segment ("__DWARF" )
280
+ var sects []* macho.Section
281
+ var bytes []byte
282
+
283
+ for _ , sect := range dwarfm .Sections {
284
+ if sect .Seg != "__DWARF" {
285
+ continue
286
+ }
287
+
288
+ // As of writing, there are no relocations in dsymutil's output
289
+ // so there's no point in worrying about them. Bail out if that
290
+ // changes.
291
+ if sect .Nreloc != 0 {
292
+ return nil , nil , nil
293
+ }
294
+
295
+ data , err := sect .Data ()
296
+ if err != nil {
297
+ return nil , nil , err
298
+ }
299
+
300
+ compressed , contents , err := machoCompressSection (data )
301
+ if err != nil {
302
+ return nil , nil , err
303
+ }
304
+
305
+ newSec := * sect
306
+ newSec .Offset = uint32 (dwarfseg .Offset ) + uint32 (len (bytes ))
307
+ newSec .Addr = dwarfseg .Addr + uint64 (len (bytes ))
308
+ if compressed {
309
+ newSec .Name = "__z" + sect .Name [2 :]
310
+ newSec .Size = uint64 (len (contents ))
311
+ }
312
+ sects = append (sects , & newSec )
313
+ bytes = append (bytes , contents ... )
314
+ }
315
+ return sects , bytes , nil
316
+ }
317
+
318
+ // machoCompressSection compresses secBytes if it results in less data.
319
+ func machoCompressSection (sectBytes []byte ) (compressed bool , contents []byte , err error ) {
320
+ var buf bytes.Buffer
321
+ buf .Write ([]byte ("ZLIB" ))
322
+ var sizeBytes [8 ]byte
323
+ binary .BigEndian .PutUint64 (sizeBytes [:], uint64 (len (sectBytes )))
324
+ buf .Write (sizeBytes [:])
325
+
326
+ z := zlib .NewWriter (& buf )
327
+ if _ , err := z .Write (sectBytes ); err != nil {
328
+ return false , nil , err
329
+ }
330
+ if err := z .Close (); err != nil {
331
+ return false , nil , err
332
+ }
333
+ if len (buf .Bytes ()) >= len (sectBytes ) {
334
+ return false , sectBytes , nil
335
+ }
336
+ return true , buf .Bytes (), nil
248
337
}
249
338
250
339
// machoUpdateSegment updates the load command for a moved segment.
@@ -267,10 +356,10 @@ func machoUpdateSegment(r loadCmdReader, seg, sect interface{}) error {
267
356
return err
268
357
}
269
358
// There shouldn't be any sections, but just to make sure...
270
- return machoUpdateSections (r , segValue , reflect .ValueOf (sect ), uint64 (linkoffset ), 0 )
359
+ return machoUpdateSections (r , segValue , reflect .ValueOf (sect ), uint64 (linkoffset ), 0 , nil )
271
360
}
272
361
273
- func machoUpdateSections (r loadCmdReader , seg , sect reflect.Value , deltaOffset , deltaAddr uint64 ) error {
362
+ func machoUpdateSections (r loadCmdReader , seg , sect reflect.Value , deltaOffset , deltaAddr uint64 , compressedSects [] * macho. Section ) error {
274
363
iseg := reflect .Indirect (seg )
275
364
nsect := iseg .FieldByName ("Nsect" ).Uint ()
276
365
if nsect == 0 {
@@ -282,19 +371,35 @@ func machoUpdateSections(r loadCmdReader, seg, sect reflect.Value, deltaOffset,
282
371
offsetField := isect .FieldByName ("Offset" )
283
372
reloffField := isect .FieldByName ("Reloff" )
284
373
addrField := isect .FieldByName ("Addr" )
374
+ nameField := isect .FieldByName ("Name" )
375
+ sizeField := isect .FieldByName ("Size" )
285
376
sectSize := int64 (isect .Type ().Size ())
286
377
for i := uint64 (0 ); i < nsect ; i ++ {
287
378
if err := r .ReadAt (sectOffset , sect .Interface ()); err != nil {
288
379
return err
289
380
}
290
- if offsetField .Uint () != 0 {
291
- offsetField .SetUint (offsetField .Uint () + deltaOffset )
292
- }
293
- if reloffField .Uint () != 0 {
294
- reloffField .SetUint (reloffField .Uint () + deltaOffset )
295
- }
296
- if addrField .Uint () != 0 {
297
- addrField .SetUint (addrField .Uint () + deltaAddr )
381
+ if compressedSects != nil {
382
+ cSect := compressedSects [i ]
383
+ var name [16 ]byte
384
+ copy (name [:], []byte (cSect .Name ))
385
+ nameField .Set (reflect .ValueOf (name ))
386
+ sizeField .SetUint (cSect .Size )
387
+ if cSect .Offset != 0 {
388
+ offsetField .SetUint (uint64 (cSect .Offset ) + deltaOffset )
389
+ }
390
+ if cSect .Addr != 0 {
391
+ addrField .SetUint (cSect .Addr + deltaAddr )
392
+ }
393
+ } else {
394
+ if offsetField .Uint () != 0 {
395
+ offsetField .SetUint (offsetField .Uint () + deltaOffset )
396
+ }
397
+ if reloffField .Uint () != 0 {
398
+ reloffField .SetUint (reloffField .Uint () + deltaOffset )
399
+ }
400
+ if addrField .Uint () != 0 {
401
+ addrField .SetUint (addrField .Uint () + deltaAddr )
402
+ }
298
403
}
299
404
if err := r .WriteAt (sectOffset , sect .Interface ()); err != nil {
300
405
return err
@@ -305,7 +410,7 @@ func machoUpdateSections(r loadCmdReader, seg, sect reflect.Value, deltaOffset,
305
410
}
306
411
307
412
// machoUpdateDwarfHeader updates the DWARF segment load command.
308
- func machoUpdateDwarfHeader (r * loadCmdReader , buildmode BuildMode ) error {
413
+ func machoUpdateDwarfHeader (r * loadCmdReader , buildmode BuildMode , compressedSects [] * macho. Section ) error {
309
414
var seg , sect interface {}
310
415
cmd , err := r .Next ()
311
416
if err != nil {
@@ -322,10 +427,18 @@ func machoUpdateDwarfHeader(r *loadCmdReader, buildmode BuildMode) error {
322
427
return err
323
428
}
324
429
segv := reflect .ValueOf (seg ).Elem ()
325
-
326
430
segv .FieldByName ("Offset" ).SetUint (uint64 (dwarfstart ))
327
431
segv .FieldByName ("Addr" ).SetUint (uint64 (dwarfaddr ))
328
432
433
+ if compressedSects != nil {
434
+ var segSize uint64
435
+ for _ , newSect := range compressedSects {
436
+ segSize += newSect .Size
437
+ }
438
+ segv .FieldByName ("Filesz" ).SetUint (segSize )
439
+ segv .FieldByName ("Memsz" ).SetUint (uint64 (Rnd (int64 (segSize ), 1 << pageAlign )))
440
+ }
441
+
329
442
deltaOffset := uint64 (dwarfstart ) - realdwarf .Offset
330
443
deltaAddr := uint64 (dwarfaddr ) - realdwarf .Addr
331
444
@@ -344,7 +457,7 @@ func machoUpdateDwarfHeader(r *loadCmdReader, buildmode BuildMode) error {
344
457
if err := r .WriteAt (0 , seg ); err != nil {
345
458
return err
346
459
}
347
- return machoUpdateSections (* r , segv , reflect .ValueOf (sect ), deltaOffset , deltaAddr )
460
+ return machoUpdateSections (* r , segv , reflect .ValueOf (sect ), deltaOffset , deltaAddr , compressedSects )
348
461
}
349
462
350
463
func machoUpdateLoadCommand (r loadCmdReader , cmd interface {}, fields ... string ) error {
0 commit comments