Source file src/pkg/cmd/link/internal/ld/macho_combine_dwarf.go
1
2
3
4
5 package ld
6
7 import (
8 "bytes"
9 "compress/zlib"
10 "debug/macho"
11 "encoding/binary"
12 "fmt"
13 "io"
14 "os"
15 "reflect"
16 "unsafe"
17 )
18
19 var realdwarf, linkseg *macho.Segment
20 var dwarfstart, linkstart int64
21 var dwarfaddr int64
22 var linkoffset uint32
23
24 const (
25 pageAlign = 12
26 )
27
28 type loadCmd struct {
29 Cmd macho.LoadCmd
30 Len uint32
31 }
32
33 type dyldInfoCmd struct {
34 Cmd macho.LoadCmd
35 Len uint32
36 RebaseOff, RebaseLen uint32
37 BindOff, BindLen uint32
38 WeakBindOff, WeakBindLen uint32
39 LazyBindOff, LazyBindLen uint32
40 ExportOff, ExportLen uint32
41 }
42
43 type linkEditDataCmd struct {
44 Cmd macho.LoadCmd
45 Len uint32
46 DataOff, DataLen uint32
47 }
48
49 type encryptionInfoCmd struct {
50 Cmd macho.LoadCmd
51 Len uint32
52 CryptOff, CryptLen uint32
53 CryptId uint32
54 }
55
56 type loadCmdReader struct {
57 offset, next int64
58 f *os.File
59 order binary.ByteOrder
60 }
61
62 func (r *loadCmdReader) Next() (cmd loadCmd, err error) {
63 r.offset = r.next
64 if _, err = r.f.Seek(r.offset, 0); err != nil {
65 return
66 }
67 if err = binary.Read(r.f, r.order, &cmd); err != nil {
68 return
69 }
70 r.next = r.offset + int64(cmd.Len)
71 return
72 }
73
74 func (r loadCmdReader) ReadAt(offset int64, data interface{}) error {
75 if _, err := r.f.Seek(r.offset+offset, 0); err != nil {
76 return err
77 }
78 return binary.Read(r.f, r.order, data)
79 }
80
81 func (r loadCmdReader) WriteAt(offset int64, data interface{}) error {
82 if _, err := r.f.Seek(r.offset+offset, 0); err != nil {
83 return err
84 }
85 return binary.Write(r.f, r.order, data)
86 }
87
88
89
90
91
92
93
94
95
96
97 func machoCombineDwarf(ctxt *Link, exef *os.File, exem *macho.File, dsym, outexe string) error {
98 dwarff, err := os.Open(dsym)
99 if err != nil {
100 return err
101 }
102 defer dwarff.Close()
103 outf, err := os.Create(outexe)
104 if err != nil {
105 return err
106 }
107 outf.Chmod(0755)
108
109 dwarfm, err := macho.NewFile(dwarff)
110 if err != nil {
111 return err
112 }
113
114
115
116
117 linkseg = exem.Segment("__LINKEDIT")
118 if linkseg == nil {
119 return fmt.Errorf("missing __LINKEDIT segment")
120 }
121
122 if _, err = exef.Seek(0, 0); err != nil {
123 return err
124 }
125 if _, err := io.CopyN(outf, exef, int64(linkseg.Offset)); err != nil {
126 return err
127 }
128
129 realdwarf = dwarfm.Segment("__DWARF")
130 if realdwarf == nil {
131 return fmt.Errorf("missing __DWARF segment")
132 }
133
134
135
136 compressedSects, compressedBytes, err := machoCompressSections(ctxt, dwarfm)
137 if err != nil {
138 return err
139 }
140
141
142
143
144 dwarfstart = machoCalcStart(realdwarf.Offset, linkseg.Offset, pageAlign)
145 if _, err = outf.Seek(dwarfstart, 0); err != nil {
146 return err
147 }
148 dwarfaddr = int64((linkseg.Addr + linkseg.Memsz + 1<<pageAlign - 1) &^ (1<<pageAlign - 1))
149
150 if _, err = dwarff.Seek(int64(realdwarf.Offset), 0); err != nil {
151 return err
152 }
153
154
155
156 var dwarfsize uint64
157 if compressedBytes != nil {
158 dwarfsize = uint64(len(compressedBytes))
159 if _, err := outf.Write(compressedBytes); err != nil {
160 return err
161 }
162 } else {
163 if _, err := io.CopyN(outf, dwarff, int64(realdwarf.Filesz)); err != nil {
164 return err
165 }
166 dwarfsize = realdwarf.Filesz
167 }
168
169
170 if _, err = exef.Seek(int64(linkseg.Offset), 0); err != nil {
171 return err
172 }
173 linkstart = machoCalcStart(linkseg.Offset, uint64(dwarfstart)+dwarfsize, pageAlign)
174 linkoffset = uint32(linkstart - int64(linkseg.Offset))
175 if _, err = outf.Seek(linkstart, 0); err != nil {
176 return err
177 }
178 if _, err := io.Copy(outf, exef); err != nil {
179 return err
180 }
181
182
183 textsect := exem.Section("__text")
184 if linkseg == nil {
185 return fmt.Errorf("missing __text section")
186 }
187
188 cmdOffset := unsafe.Sizeof(exem.FileHeader)
189 is64bit := exem.Magic == macho.Magic64
190 if is64bit {
191
192 cmdOffset += unsafe.Sizeof(exem.Magic)
193 }
194 dwarfCmdOffset := int64(cmdOffset) + int64(exem.FileHeader.Cmdsz)
195 availablePadding := int64(textsect.Offset) - dwarfCmdOffset
196 if availablePadding < int64(realdwarf.Len) {
197 return fmt.Errorf("No room to add dwarf info. Need at least %d padding bytes, found %d", realdwarf.Len, availablePadding)
198 }
199
200
201 if _, err = outf.Seek(dwarfCmdOffset, 0); err != nil {
202 return err
203 }
204 if _, err := io.CopyN(outf, bytes.NewReader(realdwarf.Raw()), int64(realdwarf.Len)); err != nil {
205 return err
206 }
207 if _, err = outf.Seek(int64(unsafe.Offsetof(exem.FileHeader.Ncmd)), 0); err != nil {
208 return err
209 }
210 if err = binary.Write(outf, exem.ByteOrder, exem.Ncmd+1); err != nil {
211 return err
212 }
213 if err = binary.Write(outf, exem.ByteOrder, exem.Cmdsz+realdwarf.Len); err != nil {
214 return err
215 }
216
217 reader := loadCmdReader{next: int64(cmdOffset), f: outf, order: exem.ByteOrder}
218 for i := uint32(0); i < exem.Ncmd; i++ {
219 cmd, err := reader.Next()
220 if err != nil {
221 return err
222 }
223 switch cmd.Cmd {
224 case macho.LoadCmdSegment64:
225 err = machoUpdateSegment(reader, &macho.Segment64{}, &macho.Section64{})
226 case macho.LoadCmdSegment:
227 err = machoUpdateSegment(reader, &macho.Segment32{}, &macho.Section32{})
228 case LC_DYLD_INFO, LC_DYLD_INFO_ONLY:
229 err = machoUpdateLoadCommand(reader, &dyldInfoCmd{}, "RebaseOff", "BindOff", "WeakBindOff", "LazyBindOff", "ExportOff")
230 case macho.LoadCmdSymtab:
231 err = machoUpdateLoadCommand(reader, &macho.SymtabCmd{}, "Symoff", "Stroff")
232 case macho.LoadCmdDysymtab:
233 err = machoUpdateLoadCommand(reader, &macho.DysymtabCmd{}, "Tocoffset", "Modtaboff", "Extrefsymoff", "Indirectsymoff", "Extreloff", "Locreloff")
234 case LC_CODE_SIGNATURE, LC_SEGMENT_SPLIT_INFO, LC_FUNCTION_STARTS, LC_DATA_IN_CODE, LC_DYLIB_CODE_SIGN_DRS:
235 err = machoUpdateLoadCommand(reader, &linkEditDataCmd{}, "DataOff")
236 case LC_ENCRYPTION_INFO, LC_ENCRYPTION_INFO_64:
237 err = machoUpdateLoadCommand(reader, &encryptionInfoCmd{}, "CryptOff")
238 case macho.LoadCmdDylib, macho.LoadCmdThread, macho.LoadCmdUnixThread, LC_PREBOUND_DYLIB, LC_UUID, LC_VERSION_MIN_MACOSX, LC_VERSION_MIN_IPHONEOS, LC_SOURCE_VERSION, LC_MAIN, LC_LOAD_DYLINKER, LC_LOAD_WEAK_DYLIB, LC_REEXPORT_DYLIB, LC_RPATH, LC_ID_DYLIB, LC_SYMSEG, LC_LOADFVMLIB, LC_IDFVMLIB, LC_IDENT, LC_FVMFILE, LC_PREPAGE, LC_ID_DYLINKER, LC_ROUTINES, LC_SUB_FRAMEWORK, LC_SUB_UMBRELLA, LC_SUB_CLIENT, LC_SUB_LIBRARY, LC_TWOLEVEL_HINTS, LC_PREBIND_CKSUM, LC_ROUTINES_64, LC_LAZY_LOAD_DYLIB, LC_LOAD_UPWARD_DYLIB, LC_DYLD_ENVIRONMENT, LC_LINKER_OPTION, LC_LINKER_OPTIMIZATION_HINT, LC_VERSION_MIN_TVOS, LC_VERSION_MIN_WATCHOS, LC_VERSION_NOTE, LC_BUILD_VERSION:
239
240 default:
241 err = fmt.Errorf("Unknown load command 0x%x (%s)\n", int(cmd.Cmd), cmd.Cmd)
242 }
243 if err != nil {
244 return err
245 }
246 }
247
248 return machoUpdateDwarfHeader(&reader, compressedSects, dwarfsize)
249 }
250
251
252
253
254 func machoCompressSections(ctxt *Link, dwarfm *macho.File) ([]*macho.Section, []byte, error) {
255 if !ctxt.compressDWARF {
256 return nil, nil, nil
257 }
258
259 dwarfseg := dwarfm.Segment("__DWARF")
260 var sects []*macho.Section
261 var bytes []byte
262
263 for _, sect := range dwarfm.Sections {
264 if sect.Seg != "__DWARF" {
265 continue
266 }
267
268
269
270
271 if sect.Nreloc != 0 {
272 return nil, nil, nil
273 }
274
275 data, err := sect.Data()
276 if err != nil {
277 return nil, nil, err
278 }
279
280 compressed, contents, err := machoCompressSection(data)
281 if err != nil {
282 return nil, nil, err
283 }
284
285 newSec := *sect
286 newSec.Offset = uint32(dwarfseg.Offset) + uint32(len(bytes))
287 newSec.Addr = dwarfseg.Addr + uint64(len(bytes))
288 if compressed {
289 newSec.Name = "__z" + sect.Name[2:]
290 newSec.Size = uint64(len(contents))
291 }
292 sects = append(sects, &newSec)
293 bytes = append(bytes, contents...)
294 }
295 return sects, bytes, nil
296 }
297
298
299 func machoCompressSection(sectBytes []byte) (compressed bool, contents []byte, err error) {
300 var buf bytes.Buffer
301 buf.Write([]byte("ZLIB"))
302 var sizeBytes [8]byte
303 binary.BigEndian.PutUint64(sizeBytes[:], uint64(len(sectBytes)))
304 buf.Write(sizeBytes[:])
305
306 z := zlib.NewWriter(&buf)
307 if _, err := z.Write(sectBytes); err != nil {
308 return false, nil, err
309 }
310 if err := z.Close(); err != nil {
311 return false, nil, err
312 }
313 if len(buf.Bytes()) >= len(sectBytes) {
314 return false, sectBytes, nil
315 }
316 return true, buf.Bytes(), nil
317 }
318
319
320
321
322
323 func machoUpdateSegment(r loadCmdReader, seg, sect interface{}) error {
324 if err := r.ReadAt(0, seg); err != nil {
325 return err
326 }
327 segValue := reflect.ValueOf(seg)
328 offset := reflect.Indirect(segValue).FieldByName("Offset")
329
330
331 if offset.Uint() < linkseg.Offset {
332 return nil
333 }
334 offset.SetUint(offset.Uint() + uint64(linkoffset))
335 if err := r.WriteAt(0, seg); err != nil {
336 return err
337 }
338
339 return machoUpdateSections(r, segValue, reflect.ValueOf(sect), uint64(linkoffset), 0, nil)
340 }
341
342 func machoUpdateSections(r loadCmdReader, seg, sect reflect.Value, deltaOffset, deltaAddr uint64, compressedSects []*macho.Section) error {
343 iseg := reflect.Indirect(seg)
344 nsect := iseg.FieldByName("Nsect").Uint()
345 if nsect == 0 {
346 return nil
347 }
348 sectOffset := int64(iseg.Type().Size())
349
350 isect := reflect.Indirect(sect)
351 offsetField := isect.FieldByName("Offset")
352 reloffField := isect.FieldByName("Reloff")
353 addrField := isect.FieldByName("Addr")
354 nameField := isect.FieldByName("Name")
355 sizeField := isect.FieldByName("Size")
356 sectSize := int64(isect.Type().Size())
357 for i := uint64(0); i < nsect; i++ {
358 if err := r.ReadAt(sectOffset, sect.Interface()); err != nil {
359 return err
360 }
361 if compressedSects != nil {
362 cSect := compressedSects[i]
363 var name [16]byte
364 copy(name[:], []byte(cSect.Name))
365 nameField.Set(reflect.ValueOf(name))
366 sizeField.SetUint(cSect.Size)
367 if cSect.Offset != 0 {
368 offsetField.SetUint(uint64(cSect.Offset) + deltaOffset)
369 }
370 if cSect.Addr != 0 {
371 addrField.SetUint(cSect.Addr + deltaAddr)
372 }
373 } else {
374 if offsetField.Uint() != 0 {
375 offsetField.SetUint(offsetField.Uint() + deltaOffset)
376 }
377 if reloffField.Uint() != 0 {
378 reloffField.SetUint(reloffField.Uint() + deltaOffset)
379 }
380 if addrField.Uint() != 0 {
381 addrField.SetUint(addrField.Uint() + deltaAddr)
382 }
383 }
384 if err := r.WriteAt(sectOffset, sect.Interface()); err != nil {
385 return err
386 }
387 sectOffset += sectSize
388 }
389 return nil
390 }
391
392
393 func machoUpdateDwarfHeader(r *loadCmdReader, compressedSects []*macho.Section, dwarfsize uint64) error {
394 var seg, sect interface{}
395 cmd, err := r.Next()
396 if err != nil {
397 return err
398 }
399 if cmd.Cmd == macho.LoadCmdSegment64 {
400 seg = new(macho.Segment64)
401 sect = new(macho.Section64)
402 } else {
403 seg = new(macho.Segment32)
404 sect = new(macho.Section32)
405 }
406 if err := r.ReadAt(0, seg); err != nil {
407 return err
408 }
409 segv := reflect.ValueOf(seg).Elem()
410 segv.FieldByName("Offset").SetUint(uint64(dwarfstart))
411
412 if compressedSects != nil {
413 var segSize uint64
414 for _, newSect := range compressedSects {
415 segSize += newSect.Size
416 }
417 segv.FieldByName("Filesz").SetUint(segSize)
418 } else {
419 segv.FieldByName("Filesz").SetUint(dwarfsize)
420 }
421
422 deltaOffset := uint64(dwarfstart) - realdwarf.Offset
423 deltaAddr := uint64(dwarfaddr) - realdwarf.Addr
424
425
426
427
428
429
430
431
432
433
434
435 segv.FieldByName("Addr").SetUint(0)
436 segv.FieldByName("Memsz").SetUint(0)
437 segv.FieldByName("Prot").SetUint(0)
438 deltaAddr = 0
439
440 if err := r.WriteAt(0, seg); err != nil {
441 return err
442 }
443 return machoUpdateSections(*r, segv, reflect.ValueOf(sect), deltaOffset, deltaAddr, compressedSects)
444 }
445
446 func machoUpdateLoadCommand(r loadCmdReader, cmd interface{}, fields ...string) error {
447 if err := r.ReadAt(0, cmd); err != nil {
448 return err
449 }
450 value := reflect.Indirect(reflect.ValueOf(cmd))
451
452 for _, name := range fields {
453 field := value.FieldByName(name)
454 fieldval := field.Uint()
455 if fieldval >= linkseg.Offset {
456 field.SetUint(fieldval + uint64(linkoffset))
457 }
458 }
459 if err := r.WriteAt(0, cmd); err != nil {
460 return err
461 }
462 return nil
463 }
464
465 func machoCalcStart(origAddr, newAddr uint64, alignExp uint32) int64 {
466 align := uint64(1 << alignExp)
467 if (origAddr % align) == (newAddr % align) {
468 return int64(newAddr)
469 }
470 padding := (align - (newAddr % align))
471 padding += origAddr % align
472 return int64(padding + newAddr)
473 }
474
View as plain text