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