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