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