1 | // Copyright 2009 The Go Authors. All rights reserved. |
---|---|
2 | // Use of this source code is governed by a BSD-style |
3 | // license that can be found in the LICENSE file. |
4 | |
5 | // Package macho implements access to Mach-O object files. |
6 | package macho |
7 | |
8 | // High level access to low level data structures. |
9 | |
10 | import ( |
11 | "bytes" |
12 | "compress/zlib" |
13 | "debug/dwarf" |
14 | "encoding/binary" |
15 | "fmt" |
16 | "io" |
17 | "os" |
18 | "strings" |
19 | "unsafe" |
20 | ) |
21 | |
22 | // A File represents an open Mach-O file. |
23 | type File struct { |
24 | FileTOC |
25 | |
26 | Symtab *Symtab |
27 | Dysymtab *Dysymtab |
28 | |
29 | closer io.Closer |
30 | } |
31 | |
32 | type FileTOC struct { |
33 | FileHeader |
34 | ByteOrder binary.ByteOrder |
35 | Loads []Load |
36 | Sections []*Section |
37 | } |
38 | |
39 | func (t *FileTOC) AddLoad(l Load) { |
40 | t.Loads = append(t.Loads, l) |
41 | t.NCommands++ |
42 | t.SizeCommands += l.LoadSize(t) |
43 | } |
44 | |
45 | // AddSegment adds segment s to the file table of contents, |
46 | // and also zeroes out the segment information with the expectation |
47 | // that this will be added next. |
48 | func (t *FileTOC) AddSegment(s *Segment) { |
49 | t.AddLoad(s) |
50 | s.Nsect = 0 |
51 | s.Firstsect = 0 |
52 | } |
53 | |
54 | // Adds section to the most recently added Segment |
55 | func (t *FileTOC) AddSection(s *Section) { |
56 | g := t.Loads[len(t.Loads)-1].(*Segment) |
57 | if g.Nsect == 0 { |
58 | g.Firstsect = uint32(len(t.Sections)) |
59 | } |
60 | g.Nsect++ |
61 | t.Sections = append(t.Sections, s) |
62 | sectionsize := uint32(unsafe.Sizeof(Section32{})) |
63 | if g.Command() == LcSegment64 { |
64 | sectionsize = uint32(unsafe.Sizeof(Section64{})) |
65 | } |
66 | t.SizeCommands += sectionsize |
67 | g.Len += sectionsize |
68 | } |
69 | |
70 | // A Load represents any Mach-O load command. |
71 | type Load interface { |
72 | String() string |
73 | Command() LoadCmd |
74 | LoadSize(*FileTOC) uint32 // Need the TOC for alignment, sigh. |
75 | Put([]byte, binary.ByteOrder) int |
76 | |
77 | // command LC_DYLD_INFO_ONLY contains offsets into __LINKEDIT |
78 | // e.g., from "otool -l a.out" |
79 | // |
80 | // Load command 3 |
81 | // cmd LC_SEGMENT_64 |
82 | // cmdsize 72 |
83 | // segname __LINKEDIT |
84 | // vmaddr 0x0000000100002000 |
85 | // vmsize 0x0000000000001000 |
86 | // fileoff 8192 |
87 | // filesize 520 |
88 | // maxprot 0x00000007 |
89 | // initprot 0x00000001 |
90 | // nsects 0 |
91 | // flags 0x0 |
92 | // Load command 4 |
93 | // cmd LC_DYLD_INFO_ONLY |
94 | // cmdsize 48 |
95 | // rebase_off 8192 |
96 | // rebase_size 8 |
97 | // bind_off 8200 |
98 | // bind_size 24 |
99 | // weak_bind_off 0 |
100 | // weak_bind_size 0 |
101 | // lazy_bind_off 8224 |
102 | // lazy_bind_size 16 |
103 | // export_off 8240 |
104 | // export_size 48 |
105 | } |
106 | |
107 | // LoadBytes is the uninterpreted bytes of a Mach-O load command. |
108 | type LoadBytes []byte |
109 | |
110 | // A SegmentHeader is the header for a Mach-O 32-bit or 64-bit load segment command. |
111 | type SegmentHeader struct { |
112 | LoadCmd |
113 | Len uint32 |
114 | Name string // 16 characters or fewer |
115 | Addr uint64 // memory address |
116 | Memsz uint64 // memory size |
117 | Offset uint64 // file offset |
118 | Filesz uint64 // number of bytes starting at that file offset |
119 | Maxprot uint32 |
120 | Prot uint32 |
121 | Nsect uint32 |
122 | Flag SegFlags |
123 | Firstsect uint32 |
124 | } |
125 | |
126 | // A Segment represents a Mach-O 32-bit or 64-bit load segment command. |
127 | type Segment struct { |
128 | SegmentHeader |
129 | |
130 | // Embed ReaderAt for ReadAt method. |
131 | // Do not embed SectionReader directly |
132 | // to avoid having Read and Seek. |
133 | // If a client wants Read and Seek it must use |
134 | // Open() to avoid fighting over the seek offset |
135 | // with other clients. |
136 | io.ReaderAt |
137 | sr *io.SectionReader |
138 | } |
139 | |
140 | func (s *Segment) Put32(b []byte, o binary.ByteOrder) int { |
141 | o.PutUint32(b[0*4:], uint32(s.LoadCmd)) |
142 | o.PutUint32(b[1*4:], s.Len) |
143 | putAtMost16Bytes(b[2*4:], s.Name) |
144 | o.PutUint32(b[6*4:], uint32(s.Addr)) |
145 | o.PutUint32(b[7*4:], uint32(s.Memsz)) |
146 | o.PutUint32(b[8*4:], uint32(s.Offset)) |
147 | o.PutUint32(b[9*4:], uint32(s.Filesz)) |
148 | o.PutUint32(b[10*4:], s.Maxprot) |
149 | o.PutUint32(b[11*4:], s.Prot) |
150 | o.PutUint32(b[12*4:], s.Nsect) |
151 | o.PutUint32(b[13*4:], uint32(s.Flag)) |
152 | return 14 * 4 |
153 | } |
154 | |
155 | func (s *Segment) Put64(b []byte, o binary.ByteOrder) int { |
156 | o.PutUint32(b[0*4:], uint32(s.LoadCmd)) |
157 | o.PutUint32(b[1*4:], s.Len) |
158 | putAtMost16Bytes(b[2*4:], s.Name) |
159 | o.PutUint64(b[6*4+0*8:], s.Addr) |
160 | o.PutUint64(b[6*4+1*8:], s.Memsz) |
161 | o.PutUint64(b[6*4+2*8:], s.Offset) |
162 | o.PutUint64(b[6*4+3*8:], s.Filesz) |
163 | o.PutUint32(b[6*4+4*8:], s.Maxprot) |
164 | o.PutUint32(b[7*4+4*8:], s.Prot) |
165 | o.PutUint32(b[8*4+4*8:], s.Nsect) |
166 | o.PutUint32(b[9*4+4*8:], uint32(s.Flag)) |
167 | return 10*4 + 4*8 |
168 | } |
169 | |
170 | // LoadCmdBytes is a command-tagged sequence of bytes. |
171 | // This is used for Load Commands that are not (yet) |
172 | // interesting to us, and to common up this behavior for |
173 | // all those that are. |
174 | type LoadCmdBytes struct { |
175 | LoadCmd |
176 | LoadBytes |
177 | } |
178 | |
179 | type SectionHeader struct { |
180 | Name string |
181 | Seg string |
182 | Addr uint64 |
183 | Size uint64 |
184 | Offset uint32 |
185 | Align uint32 |
186 | Reloff uint32 |
187 | Nreloc uint32 |
188 | Flags SecFlags |
189 | Reserved1 uint32 |
190 | Reserved2 uint32 |
191 | Reserved3 uint32 // only present if original was 64-bit |
192 | } |
193 | |
194 | // A Reloc represents a Mach-O relocation. |
195 | type Reloc struct { |
196 | Addr uint32 |
197 | Value uint32 |
198 | // when Scattered == false && Extern == true, Value is the symbol number. |
199 | // when Scattered == false && Extern == false, Value is the section number. |
200 | // when Scattered == true, Value is the value that this reloc refers to. |
201 | Type uint8 |
202 | Len uint8 // 0=byte, 1=word, 2=long, 3=quad |
203 | Pcrel bool |
204 | Extern bool // valid if Scattered == false |
205 | Scattered bool |
206 | } |
207 | |
208 | type Section struct { |
209 | SectionHeader |
210 | Relocs []Reloc |
211 | |
212 | // Embed ReaderAt for ReadAt method. |
213 | // Do not embed SectionReader directly |
214 | // to avoid having Read and Seek. |
215 | // If a client wants Read and Seek it must use |
216 | // Open() to avoid fighting over the seek offset |
217 | // with other clients. |
218 | io.ReaderAt |
219 | sr *io.SectionReader |
220 | } |
221 | |
222 | func (s *Section) Put32(b []byte, o binary.ByteOrder) int { |
223 | putAtMost16Bytes(b[0:], s.Name) |
224 | putAtMost16Bytes(b[16:], s.Seg) |
225 | o.PutUint32(b[8*4:], uint32(s.Addr)) |
226 | o.PutUint32(b[9*4:], uint32(s.Size)) |
227 | o.PutUint32(b[10*4:], s.Offset) |
228 | o.PutUint32(b[11*4:], s.Align) |
229 | o.PutUint32(b[12*4:], s.Reloff) |
230 | o.PutUint32(b[13*4:], s.Nreloc) |
231 | o.PutUint32(b[14*4:], uint32(s.Flags)) |
232 | o.PutUint32(b[15*4:], s.Reserved1) |
233 | o.PutUint32(b[16*4:], s.Reserved2) |
234 | a := 17 * 4 |
235 | return a + s.PutRelocs(b[a:], o) |
236 | } |
237 | |
238 | func (s *Section) Put64(b []byte, o binary.ByteOrder) int { |
239 | putAtMost16Bytes(b[0:], s.Name) |
240 | putAtMost16Bytes(b[16:], s.Seg) |
241 | o.PutUint64(b[8*4+0*8:], s.Addr) |
242 | o.PutUint64(b[8*4+1*8:], s.Size) |
243 | o.PutUint32(b[8*4+2*8:], s.Offset) |
244 | o.PutUint32(b[9*4+2*8:], s.Align) |
245 | o.PutUint32(b[10*4+2*8:], s.Reloff) |
246 | o.PutUint32(b[11*4+2*8:], s.Nreloc) |
247 | o.PutUint32(b[12*4+2*8:], uint32(s.Flags)) |
248 | o.PutUint32(b[13*4+2*8:], s.Reserved1) |
249 | o.PutUint32(b[14*4+2*8:], s.Reserved2) |
250 | o.PutUint32(b[15*4+2*8:], s.Reserved3) |
251 | a := 16*4 + 2*8 |
252 | return a + s.PutRelocs(b[a:], o) |
253 | } |
254 | |
255 | func (s *Section) PutRelocs(b []byte, o binary.ByteOrder) int { |
256 | a := 0 |
257 | for _, r := range s.Relocs { |
258 | var ri relocInfo |
259 | typ := uint32(r.Type) & (1<<4 - 1) |
260 | len := uint32(r.Len) & (1<<2 - 1) |
261 | pcrel := uint32(0) |
262 | if r.Pcrel { |
263 | pcrel = 1 |
264 | } |
265 | ext := uint32(0) |
266 | if r.Extern { |
267 | ext = 1 |
268 | } |
269 | switch { |
270 | case r.Scattered: |
271 | ri.Addr = r.Addr&(1<<24-1) | typ<<24 | len<<28 | 1<<31 | pcrel<<30 |
272 | ri.Symnum = r.Value |
273 | case o == binary.LittleEndian: |
274 | ri.Addr = r.Addr |
275 | ri.Symnum = r.Value&(1<<24-1) | pcrel<<24 | len<<25 | ext<<27 | typ<<28 |
276 | case o == binary.BigEndian: |
277 | ri.Addr = r.Addr |
278 | ri.Symnum = r.Value<<8 | pcrel<<7 | len<<5 | ext<<4 | typ |
279 | } |
280 | o.PutUint32(b, ri.Addr) |
281 | o.PutUint32(b[4:], ri.Symnum) |
282 | a += 8 |
283 | b = b[8:] |
284 | } |
285 | return a |
286 | } |
287 | |
288 | func putAtMost16Bytes(b []byte, n string) { |
289 | for i := range n { // at most 16 bytes |
290 | if i == 16 { |
291 | break |
292 | } |
293 | b[i] = n[i] |
294 | } |
295 | } |
296 | |
297 | // A Symbol is a Mach-O 32-bit or 64-bit symbol table entry. |
298 | type Symbol struct { |
299 | Name string |
300 | Type uint8 |
301 | Sect uint8 |
302 | Desc uint16 |
303 | Value uint64 |
304 | } |
305 | |
306 | /* |
307 | * Mach-O reader |
308 | */ |
309 | |
310 | // FormatError is returned by some operations if the data does |
311 | // not have the correct format for an object file. |
312 | type FormatError struct { |
313 | off int64 |
314 | msg string |
315 | } |
316 | |
317 | func formatError(off int64, format string, data ...interface{}) *FormatError { |
318 | return &FormatError{off, fmt.Sprintf(format, data...)} |
319 | } |
320 | |
321 | func (e *FormatError) Error() string { |
322 | return e.msg + fmt.Sprintf(" in record at byte %#x", e.off) |
323 | } |
324 | |
325 | func (e *FormatError) String() string { |
326 | return e.Error() |
327 | } |
328 | |
329 | // DerivedCopy returns a modified copy of the TOC, with empty loads and sections, |
330 | // and with the specified header type and flags. |
331 | func (t *FileTOC) DerivedCopy(Type HdrType, Flags HdrFlags) *FileTOC { |
332 | h := t.FileHeader |
333 | h.NCommands, h.SizeCommands, h.Type, h.Flags = 0, 0, Type, Flags |
334 | |
335 | return &FileTOC{FileHeader: h, ByteOrder: t.ByteOrder} |
336 | } |
337 | |
338 | // TOCSize returns the size in bytes of the object file representation |
339 | // of the header and Load Commands (including Segments and Sections, but |
340 | // not their contents) at the beginning of a Mach-O file. This typically |
341 | // overlaps the text segment in the object file. |
342 | func (t *FileTOC) TOCSize() uint32 { |
343 | return t.HdrSize() + t.LoadSize() |
344 | } |
345 | |
346 | // LoadAlign returns the required alignment of Load commands in a binary. |
347 | // This is used to add padding for necessary alignment. |
348 | func (t *FileTOC) LoadAlign() uint64 { |
349 | if t.Magic == Magic64 { |
350 | return 8 |
351 | } |
352 | return 4 |
353 | } |
354 | |
355 | // SymbolSize returns the size in bytes of a Symbol (Nlist32 or Nlist64) |
356 | func (t *FileTOC) SymbolSize() uint32 { |
357 | if t.Magic == Magic64 { |
358 | return uint32(unsafe.Sizeof(Nlist64{})) |
359 | } |
360 | return uint32(unsafe.Sizeof(Nlist32{})) |
361 | } |
362 | |
363 | // HdrSize returns the size in bytes of the Macho header for a given |
364 | // magic number (where the magic number has been appropriately byte-swapped). |
365 | func (t *FileTOC) HdrSize() uint32 { |
366 | switch t.Magic { |
367 | case Magic32: |
368 | return fileHeaderSize32 |
369 | case Magic64: |
370 | return fileHeaderSize64 |
371 | case MagicFat: |
372 | panic("MagicFat not handled yet") |
373 | default: |
374 | panic(fmt.Sprintf("Unexpected magic number 0x%x, expected Mach-O object file", t.Magic)) |
375 | } |
376 | } |
377 | |
378 | // LoadSize returns the size of all the load commands in a file's table-of contents |
379 | // (but not their associated data, e.g., sections and symbol tables) |
380 | func (t *FileTOC) LoadSize() uint32 { |
381 | cmdsz := uint32(0) |
382 | for _, l := range t.Loads { |
383 | s := l.LoadSize(t) |
384 | cmdsz += s |
385 | } |
386 | return cmdsz |
387 | } |
388 | |
389 | // FileSize returns the size in bytes of the header, load commands, and the |
390 | // in-file contents of all the segments and sections included in those |
391 | // load commands, accounting for their offsets within the file. |
392 | func (t *FileTOC) FileSize() uint64 { |
393 | sz := uint64(t.LoadSize()) // ought to be contained in text segment, but just in case. |
394 | for _, l := range t.Loads { |
395 | if s, ok := l.(*Segment); ok { |
396 | if m := s.Offset + s.Filesz; m > sz { |
397 | sz = m |
398 | } |
399 | } |
400 | } |
401 | return sz |
402 | } |
403 | |
404 | // Put writes the header and all load commands to buffer, using |
405 | // the byte ordering specified in FileTOC t. For sections, this |
406 | // writes the headers that come in-line with the segment Load commands, |
407 | // but does not write the reference data for those sections. |
408 | func (t *FileTOC) Put(buffer []byte) int { |
409 | next := t.FileHeader.Put(buffer, t.ByteOrder) |
410 | for _, l := range t.Loads { |
411 | if s, ok := l.(*Segment); ok { |
412 | switch t.Magic { |
413 | case Magic64: |
414 | next += s.Put64(buffer[next:], t.ByteOrder) |
415 | for i := uint32(0); i < s.Nsect; i++ { |
416 | c := t.Sections[i+s.Firstsect] |
417 | next += c.Put64(buffer[next:], t.ByteOrder) |
418 | } |
419 | case Magic32: |
420 | next += s.Put32(buffer[next:], t.ByteOrder) |
421 | for i := uint32(0); i < s.Nsect; i++ { |
422 | c := t.Sections[i+s.Firstsect] |
423 | next += c.Put32(buffer[next:], t.ByteOrder) |
424 | } |
425 | default: |
426 | panic(fmt.Sprintf("Unexpected magic number 0x%x", t.Magic)) |
427 | } |
428 | |
429 | } else { |
430 | next += l.Put(buffer[next:], t.ByteOrder) |
431 | } |
432 | } |
433 | return next |
434 | } |
435 | |
436 | // UncompressedSize returns the size of the segment with its sections uncompressed, ignoring |
437 | // its offset within the file. The returned size is rounded up to the power of two in align. |
438 | func (s *Segment) UncompressedSize(t *FileTOC, align uint64) uint64 { |
439 | sz := uint64(0) |
440 | for j := uint32(0); j < s.Nsect; j++ { |
441 | c := t.Sections[j+s.Firstsect] |
442 | sz += c.UncompressedSize() |
443 | } |
444 | return (sz + align - 1) & uint64(-int64(align)) |
445 | } |
446 | |
447 | func (s *Section) UncompressedSize() uint64 { |
448 | if !strings.HasPrefix(s.Name, "__z") { |
449 | return s.Size |
450 | } |
451 | b := make([]byte, 12) |
452 | n, err := s.sr.ReadAt(b, 0) |
453 | if err != nil { |
454 | panic("Malformed object file") |
455 | } |
456 | if n != len(b) { |
457 | return s.Size |
458 | } |
459 | if string(b[:4]) == "ZLIB" { |
460 | return binary.BigEndian.Uint64(b[4:12]) |
461 | } |
462 | return s.Size |
463 | } |
464 | |
465 | func (s *Section) PutData(b []byte) { |
466 | bb := b[0:s.Size] |
467 | n, err := s.sr.ReadAt(bb, 0) |
468 | if err != nil || uint64(n) != s.Size { |
469 | panic("Malformed object file (ReadAt error)") |
470 | } |
471 | } |
472 | |
473 | func (s *Section) PutUncompressedData(b []byte) { |
474 | if strings.HasPrefix(s.Name, "__z") { |
475 | bb := make([]byte, 12) |
476 | n, err := s.sr.ReadAt(bb, 0) |
477 | if err != nil { |
478 | panic("Malformed object file") |
479 | } |
480 | if n == len(bb) && string(bb[:4]) == "ZLIB" { |
481 | size := binary.BigEndian.Uint64(bb[4:12]) |
482 | // Decompress starting at b[12:] |
483 | r, err := zlib.NewReader(io.NewSectionReader(s, 12, int64(size)-12)) |
484 | if err != nil { |
485 | panic("Malformed object file (zlib.NewReader error)") |
486 | } |
487 | n, err := io.ReadFull(r, b[0:size]) |
488 | if err != nil { |
489 | panic("Malformed object file (ReadFull error)") |
490 | } |
491 | if uint64(n) != size { |
492 | panic(fmt.Sprintf("PutUncompressedData, expected to read %d bytes, instead read %d", size, n)) |
493 | } |
494 | if err := r.Close(); err != nil { |
495 | panic("Malformed object file (Close error)") |
496 | } |
497 | return |
498 | } |
499 | } |
500 | // Not compressed |
501 | s.PutData(b) |
502 | } |
503 | |
504 | func (b LoadBytes) String() string { |
505 | s := "[" |
506 | for i, a := range b { |
507 | if i > 0 { |
508 | s += " " |
509 | if len(b) > 48 && i >= 16 { |
510 | s += fmt.Sprintf("... (%d bytes)", len(b)) |
511 | break |
512 | } |
513 | } |
514 | s += fmt.Sprintf("%x", a) |
515 | } |
516 | s += "]" |
517 | return s |
518 | } |
519 | |
520 | func (b LoadBytes) Raw() []byte { return b } |
521 | func (b LoadBytes) Copy() LoadBytes { return LoadBytes(append([]byte{}, b...)) } |
522 | func (b LoadBytes) LoadSize(t *FileTOC) uint32 { return uint32(len(b)) } |
523 | |
524 | func (lc LoadCmd) Put(b []byte, o binary.ByteOrder) int { |
525 | panic(fmt.Sprintf("Put not implemented for %s", lc.String())) |
526 | } |
527 | |
528 | func (s LoadCmdBytes) String() string { |
529 | return s.LoadCmd.String() + ": " + s.LoadBytes.String() |
530 | } |
531 | func (s LoadCmdBytes) Copy() LoadCmdBytes { |
532 | return LoadCmdBytes{LoadCmd: s.LoadCmd, LoadBytes: s.LoadBytes.Copy()} |
533 | } |
534 | |
535 | func (s *SegmentHeader) String() string { |
536 | return fmt.Sprintf( |
537 | "Seg %s, len=0x%x, addr=0x%x, memsz=0x%x, offset=0x%x, filesz=0x%x, maxprot=0x%x, prot=0x%x, nsect=%d, flag=0x%x, firstsect=%d", |
538 | s.Name, s.Len, s.Addr, s.Memsz, s.Offset, s.Filesz, s.Maxprot, s.Prot, s.Nsect, s.Flag, s.Firstsect) |
539 | } |
540 | |
541 | func (s *Segment) String() string { |
542 | return fmt.Sprintf( |
543 | "Seg %s, len=0x%x, addr=0x%x, memsz=0x%x, offset=0x%x, filesz=0x%x, maxprot=0x%x, prot=0x%x, nsect=%d, flag=0x%x, firstsect=%d", |
544 | s.Name, s.Len, s.Addr, s.Memsz, s.Offset, s.Filesz, s.Maxprot, s.Prot, s.Nsect, s.Flag, s.Firstsect) |
545 | } |
546 | |
547 | // Data reads and returns the contents of the segment. |
548 | func (s *Segment) Data() ([]byte, error) { |
549 | dat := make([]byte, s.sr.Size()) |
550 | n, err := s.sr.ReadAt(dat, 0) |
551 | if n == len(dat) { |
552 | err = nil |
553 | } |
554 | return dat[0:n], err |
555 | } |
556 | |
557 | func (s *Segment) Copy() *Segment { |
558 | r := &Segment{SegmentHeader: s.SegmentHeader} |
559 | return r |
560 | } |
561 | func (s *Segment) CopyZeroed() *Segment { |
562 | r := s.Copy() |
563 | r.Filesz = 0 |
564 | r.Offset = 0 |
565 | r.Nsect = 0 |
566 | r.Firstsect = 0 |
567 | if s.Command() == LcSegment64 { |
568 | r.Len = uint32(unsafe.Sizeof(Segment64{})) |
569 | } else { |
570 | r.Len = uint32(unsafe.Sizeof(Segment32{})) |
571 | } |
572 | return r |
573 | } |
574 | |
575 | func (s *Segment) LoadSize(t *FileTOC) uint32 { |
576 | if s.Command() == LcSegment64 { |
577 | return uint32(unsafe.Sizeof(Segment64{})) + uint32(s.Nsect)*uint32(unsafe.Sizeof(Section64{})) |
578 | } |
579 | return uint32(unsafe.Sizeof(Segment32{})) + uint32(s.Nsect)*uint32(unsafe.Sizeof(Section32{})) |
580 | } |
581 | |
582 | // Open returns a new ReadSeeker reading the segment. |
583 | func (s *Segment) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) } |
584 | |
585 | // Data reads and returns the contents of the Mach-O section. |
586 | func (s *Section) Data() ([]byte, error) { |
587 | dat := make([]byte, s.sr.Size()) |
588 | n, err := s.sr.ReadAt(dat, 0) |
589 | if n == len(dat) { |
590 | err = nil |
591 | } |
592 | return dat[0:n], err |
593 | } |
594 | |
595 | func (s *Section) Copy() *Section { |
596 | return &Section{SectionHeader: s.SectionHeader} |
597 | } |
598 | |
599 | // Open returns a new ReadSeeker reading the Mach-O section. |
600 | func (s *Section) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) } |
601 | |
602 | // A Dylib represents a Mach-O load dynamic library command. |
603 | type Dylib struct { |
604 | DylibCmd |
605 | Name string |
606 | Time uint32 |
607 | CurrentVersion uint32 |
608 | CompatVersion uint32 |
609 | } |
610 | |
611 | func (s *Dylib) String() string { return "Dylib " + s.Name } |
612 | func (s *Dylib) Copy() *Dylib { |
613 | r := *s |
614 | return &r |
615 | } |
616 | func (s *Dylib) LoadSize(t *FileTOC) uint32 { |
617 | return uint32(RoundUp(uint64(unsafe.Sizeof(DylibCmd{}))+uint64(len(s.Name)), t.LoadAlign())) |
618 | } |
619 | |
620 | type Dylinker struct { |
621 | DylinkerCmd // shared by 3 commands, need the LoadCmd |
622 | Name string |
623 | } |
624 | |
625 | func (s *Dylinker) String() string { return s.DylinkerCmd.LoadCmd.String() + " " + s.Name } |
626 | func (s *Dylinker) Copy() *Dylinker { |
627 | return &Dylinker{DylinkerCmd: s.DylinkerCmd, Name: s.Name} |
628 | } |
629 | func (s *Dylinker) LoadSize(t *FileTOC) uint32 { |
630 | return uint32(RoundUp(uint64(unsafe.Sizeof(DylinkerCmd{}))+uint64(len(s.Name)), t.LoadAlign())) |
631 | } |
632 | |
633 | // A Symtab represents a Mach-O symbol table command. |
634 | type Symtab struct { |
635 | SymtabCmd |
636 | Syms []Symbol |
637 | } |
638 | |
639 | func (s *Symtab) Put(b []byte, o binary.ByteOrder) int { |
640 | o.PutUint32(b[0*4:], uint32(s.LoadCmd)) |
641 | o.PutUint32(b[1*4:], s.Len) |
642 | o.PutUint32(b[2*4:], s.Symoff) |
643 | o.PutUint32(b[3*4:], s.Nsyms) |
644 | o.PutUint32(b[4*4:], s.Stroff) |
645 | o.PutUint32(b[5*4:], s.Strsize) |
646 | return 6 * 4 |
647 | } |
648 | |
649 | func (s *Symtab) String() string { return fmt.Sprintf("Symtab %#v", s.SymtabCmd) } |
650 | func (s *Symtab) Copy() *Symtab { |
651 | return &Symtab{SymtabCmd: s.SymtabCmd, Syms: append([]Symbol{}, s.Syms...)} |
652 | } |
653 | func (s *Symtab) LoadSize(t *FileTOC) uint32 { |
654 | return uint32(unsafe.Sizeof(SymtabCmd{})) |
655 | } |
656 | |
657 | type LinkEditData struct { |
658 | LinkEditDataCmd |
659 | } |
660 | |
661 | func (s *LinkEditData) String() string { return "LinkEditData " + s.LoadCmd.String() } |
662 | func (s *LinkEditData) Copy() *LinkEditData { |
663 | return &LinkEditData{LinkEditDataCmd: s.LinkEditDataCmd} |
664 | } |
665 | func (s *LinkEditData) LoadSize(t *FileTOC) uint32 { |
666 | return uint32(unsafe.Sizeof(LinkEditDataCmd{})) |
667 | } |
668 | |
669 | type Uuid struct { |
670 | UuidCmd |
671 | } |
672 | |
673 | func (s *Uuid) String() string { |
674 | return fmt.Sprintf("Uuid %X-%X-%X-%X-%X", |
675 | s.Id[0:4], s.Id[4:6], s.Id[6:8], s.Id[8:10], s.Id[10:16]) |
676 | } // 8-4-4-4-12 |
677 | func (s *Uuid) Copy() *Uuid { |
678 | return &Uuid{UuidCmd: s.UuidCmd} |
679 | } |
680 | func (s *Uuid) LoadSize(t *FileTOC) uint32 { |
681 | return uint32(unsafe.Sizeof(UuidCmd{})) |
682 | } |
683 | func (s *Uuid) Put(b []byte, o binary.ByteOrder) int { |
684 | o.PutUint32(b[0*4:], uint32(s.LoadCmd)) |
685 | o.PutUint32(b[1*4:], s.Len) |
686 | copy(b[2*4:], s.Id[0:]) |
687 | return int(s.Len) |
688 | } |
689 | |
690 | type DyldInfo struct { |
691 | DyldInfoCmd |
692 | } |
693 | |
694 | func (s *DyldInfo) String() string { return "DyldInfo " + s.LoadCmd.String() } |
695 | func (s *DyldInfo) Copy() *DyldInfo { |
696 | return &DyldInfo{DyldInfoCmd: s.DyldInfoCmd} |
697 | } |
698 | func (s *DyldInfo) LoadSize(t *FileTOC) uint32 { |
699 | return uint32(unsafe.Sizeof(DyldInfoCmd{})) |
700 | } |
701 | |
702 | type EncryptionInfo struct { |
703 | EncryptionInfoCmd |
704 | } |
705 | |
706 | func (s *EncryptionInfo) String() string { return "EncryptionInfo " + s.LoadCmd.String() } |
707 | func (s *EncryptionInfo) Copy() *EncryptionInfo { |
708 | return &EncryptionInfo{EncryptionInfoCmd: s.EncryptionInfoCmd} |
709 | } |
710 | func (s *EncryptionInfo) LoadSize(t *FileTOC) uint32 { |
711 | return uint32(unsafe.Sizeof(EncryptionInfoCmd{})) |
712 | } |
713 | |
714 | // A Dysymtab represents a Mach-O dynamic symbol table command. |
715 | type Dysymtab struct { |
716 | DysymtabCmd |
717 | IndirectSyms []uint32 // indices into Symtab.Syms |
718 | } |
719 | |
720 | func (s *Dysymtab) String() string { return fmt.Sprintf("Dysymtab %#v", s.DysymtabCmd) } |
721 | func (s *Dysymtab) Copy() *Dysymtab { |
722 | return &Dysymtab{DysymtabCmd: s.DysymtabCmd, IndirectSyms: append([]uint32{}, s.IndirectSyms...)} |
723 | } |
724 | func (s *Dysymtab) LoadSize(t *FileTOC) uint32 { |
725 | return uint32(unsafe.Sizeof(DysymtabCmd{})) |
726 | } |
727 | |
728 | // A Rpath represents a Mach-O rpath command. |
729 | type Rpath struct { |
730 | LoadCmd |
731 | Path string |
732 | } |
733 | |
734 | func (s *Rpath) String() string { return "Rpath " + s.Path } |
735 | func (s *Rpath) Command() LoadCmd { return LcRpath } |
736 | func (s *Rpath) Copy() *Rpath { |
737 | return &Rpath{Path: s.Path} |
738 | } |
739 | func (s *Rpath) LoadSize(t *FileTOC) uint32 { |
740 | return uint32(RoundUp(uint64(unsafe.Sizeof(RpathCmd{}))+uint64(len(s.Path)), t.LoadAlign())) |
741 | } |
742 | |
743 | // Open opens the named file using os.Open and prepares it for use as a Mach-O binary. |
744 | func Open(name string) (*File, error) { |
745 | f, err := os.Open(name) |
746 | if err != nil { |
747 | return nil, err |
748 | } |
749 | ff, err := NewFile(f) |
750 | if err != nil { |
751 | f.Close() |
752 | return nil, err |
753 | } |
754 | ff.closer = f |
755 | return ff, nil |
756 | } |
757 | |
758 | // Close closes the File. |
759 | // If the File was created using NewFile directly instead of Open, |
760 | // Close has no effect. |
761 | func (f *File) Close() error { |
762 | var err error |
763 | if f.closer != nil { |
764 | err = f.closer.Close() |
765 | f.closer = nil |
766 | } |
767 | return err |
768 | } |
769 | |
770 | // NewFile creates a new File for accessing a Mach-O binary in an underlying reader. |
771 | // The Mach-O binary is expected to start at position 0 in the ReaderAt. |
772 | func NewFile(r io.ReaderAt) (*File, error) { |
773 | f := new(File) |
774 | sr := io.NewSectionReader(r, 0, 1<<63-1) |
775 | |
776 | // Read and decode Mach magic to determine byte order, size. |
777 | // Magic32 and Magic64 differ only in the bottom bit. |
778 | var ident [4]byte |
779 | if _, err := r.ReadAt(ident[0:], 0); err != nil { |
780 | return nil, err |
781 | } |
782 | be := binary.BigEndian.Uint32(ident[0:]) |
783 | le := binary.LittleEndian.Uint32(ident[0:]) |
784 | switch Magic32 &^ 1 { |
785 | case be &^ 1: |
786 | f.ByteOrder = binary.BigEndian |
787 | f.Magic = be |
788 | case le &^ 1: |
789 | f.ByteOrder = binary.LittleEndian |
790 | f.Magic = le |
791 | default: |
792 | return nil, formatError(0, "invalid magic number be=0x%x, le=0x%x", be, le) |
793 | } |
794 | |
795 | // Read entire file header. |
796 | if err := binary.Read(sr, f.ByteOrder, &f.FileHeader); err != nil { |
797 | return nil, err |
798 | } |
799 | |
800 | // Then load commands. |
801 | offset := int64(fileHeaderSize32) |
802 | if f.Magic == Magic64 { |
803 | offset = fileHeaderSize64 |
804 | } |
805 | dat := make([]byte, f.SizeCommands) |
806 | if _, err := r.ReadAt(dat, offset); err != nil { |
807 | return nil, err |
808 | } |
809 | f.Loads = make([]Load, f.NCommands) |
810 | bo := f.ByteOrder |
811 | for i := range f.Loads { |
812 | // Each load command begins with uint32 command and length. |
813 | if len(dat) < 8 { |
814 | return nil, formatError(offset, "command block too small, len(dat) = %d", len(dat)) |
815 | } |
816 | cmd, siz := LoadCmd(bo.Uint32(dat[0:4])), bo.Uint32(dat[4:8]) |
817 | if siz < 8 || siz > uint32(len(dat)) { |
818 | return nil, formatError(offset, "invalid command block size, len(dat)=%d, size=%d", len(dat), siz) |
819 | } |
820 | var cmddat []byte |
821 | cmddat, dat = dat[0:siz], dat[siz:] |
822 | offset += int64(siz) |
823 | var s *Segment |
824 | switch cmd { |
825 | default: |
826 | f.Loads[i] = LoadCmdBytes{LoadCmd(cmd), LoadBytes(cmddat)} |
827 | |
828 | case LcUuid: |
829 | var hdr UuidCmd |
830 | b := bytes.NewReader(cmddat) |
831 | if err := binary.Read(b, bo, &hdr); err != nil { |
832 | return nil, err |
833 | } |
834 | l := &Uuid{UuidCmd: hdr} |
835 | |
836 | f.Loads[i] = l |
837 | |
838 | case LcRpath: |
839 | var hdr RpathCmd |
840 | b := bytes.NewReader(cmddat) |
841 | if err := binary.Read(b, bo, &hdr); err != nil { |
842 | return nil, err |
843 | } |
844 | l := &Rpath{LoadCmd: hdr.LoadCmd} |
845 | if hdr.Path >= uint32(len(cmddat)) { |
846 | return nil, formatError(offset, "invalid path in rpath command, len(cmddat)=%d, hdr.Path=%d", len(cmddat), hdr.Path) |
847 | } |
848 | l.Path = cstring(cmddat[hdr.Path:]) |
849 | f.Loads[i] = l |
850 | |
851 | case LcLoadDylinker, LcIdDylinker, LcDyldEnvironment: |
852 | var hdr DylinkerCmd |
853 | b := bytes.NewReader(cmddat) |
854 | if err := binary.Read(b, bo, &hdr); err != nil { |
855 | return nil, err |
856 | } |
857 | l := new(Dylinker) |
858 | if hdr.Name >= uint32(len(cmddat)) { |
859 | return nil, formatError(offset, "invalid name in dynamic linker command, hdr.Name=%d, len(cmddat)=%d", hdr.Name, len(cmddat)) |
860 | } |
861 | l.Name = cstring(cmddat[hdr.Name:]) |
862 | l.DylinkerCmd = hdr |
863 | f.Loads[i] = l |
864 | |
865 | case LcDylib: |
866 | var hdr DylibCmd |
867 | b := bytes.NewReader(cmddat) |
868 | if err := binary.Read(b, bo, &hdr); err != nil { |
869 | return nil, err |
870 | } |
871 | l := new(Dylib) |
872 | if hdr.Name >= uint32(len(cmddat)) { |
873 | return nil, formatError(offset, "invalid name in dynamic library command, hdr.Name=%d, len(cmddat)=%d", hdr.Name, len(cmddat)) |
874 | } |
875 | l.Name = cstring(cmddat[hdr.Name:]) |
876 | l.Time = hdr.Time |
877 | l.CurrentVersion = hdr.CurrentVersion |
878 | l.CompatVersion = hdr.CompatVersion |
879 | f.Loads[i] = l |
880 | |
881 | case LcSymtab: |
882 | var hdr SymtabCmd |
883 | b := bytes.NewReader(cmddat) |
884 | if err := binary.Read(b, bo, &hdr); err != nil { |
885 | return nil, err |
886 | } |
887 | strtab := make([]byte, hdr.Strsize) |
888 | if _, err := r.ReadAt(strtab, int64(hdr.Stroff)); err != nil { |
889 | return nil, err |
890 | } |
891 | var symsz int |
892 | if f.Magic == Magic64 { |
893 | symsz = 16 |
894 | } else { |
895 | symsz = 12 |
896 | } |
897 | symdat := make([]byte, int(hdr.Nsyms)*symsz) |
898 | if _, err := r.ReadAt(symdat, int64(hdr.Symoff)); err != nil { |
899 | return nil, err |
900 | } |
901 | st, err := f.parseSymtab(symdat, strtab, cmddat, &hdr, offset) |
902 | st.SymtabCmd = hdr |
903 | if err != nil { |
904 | return nil, err |
905 | } |
906 | f.Loads[i] = st |
907 | f.Symtab = st |
908 | |
909 | case LcDysymtab: |
910 | var hdr DysymtabCmd |
911 | b := bytes.NewReader(cmddat) |
912 | if err := binary.Read(b, bo, &hdr); err != nil { |
913 | return nil, err |
914 | } |
915 | dat := make([]byte, hdr.Nindirectsyms*4) |
916 | if _, err := r.ReadAt(dat, int64(hdr.Indirectsymoff)); err != nil { |
917 | return nil, err |
918 | } |
919 | x := make([]uint32, hdr.Nindirectsyms) |
920 | if err := binary.Read(bytes.NewReader(dat), bo, x); err != nil { |
921 | return nil, err |
922 | } |
923 | st := new(Dysymtab) |
924 | st.DysymtabCmd = hdr |
925 | st.IndirectSyms = x |
926 | f.Loads[i] = st |
927 | f.Dysymtab = st |
928 | |
929 | case LcSegment: |
930 | var seg32 Segment32 |
931 | b := bytes.NewReader(cmddat) |
932 | if err := binary.Read(b, bo, &seg32); err != nil { |
933 | return nil, err |
934 | } |
935 | s = new(Segment) |
936 | s.LoadCmd = cmd |
937 | s.Len = siz |
938 | s.Name = cstring(seg32.Name[0:]) |
939 | s.Addr = uint64(seg32.Addr) |
940 | s.Memsz = uint64(seg32.Memsz) |
941 | s.Offset = uint64(seg32.Offset) |
942 | s.Filesz = uint64(seg32.Filesz) |
943 | s.Maxprot = seg32.Maxprot |
944 | s.Prot = seg32.Prot |
945 | s.Nsect = seg32.Nsect |
946 | s.Flag = seg32.Flag |
947 | s.Firstsect = uint32(len(f.Sections)) |
948 | f.Loads[i] = s |
949 | for i := 0; i < int(s.Nsect); i++ { |
950 | var sh32 Section32 |
951 | if err := binary.Read(b, bo, &sh32); err != nil { |
952 | return nil, err |
953 | } |
954 | sh := new(Section) |
955 | sh.Name = cstring(sh32.Name[0:]) |
956 | sh.Seg = cstring(sh32.Seg[0:]) |
957 | sh.Addr = uint64(sh32.Addr) |
958 | sh.Size = uint64(sh32.Size) |
959 | sh.Offset = sh32.Offset |
960 | sh.Align = sh32.Align |
961 | sh.Reloff = sh32.Reloff |
962 | sh.Nreloc = sh32.Nreloc |
963 | sh.Flags = sh32.Flags |
964 | sh.Reserved1 = sh32.Reserve1 |
965 | sh.Reserved2 = sh32.Reserve2 |
966 | if err := f.pushSection(sh, r); err != nil { |
967 | return nil, err |
968 | } |
969 | } |
970 | |
971 | case LcSegment64: |
972 | var seg64 Segment64 |
973 | b := bytes.NewReader(cmddat) |
974 | if err := binary.Read(b, bo, &seg64); err != nil { |
975 | return nil, err |
976 | } |
977 | s = new(Segment) |
978 | s.LoadCmd = cmd |
979 | s.Len = siz |
980 | s.Name = cstring(seg64.Name[0:]) |
981 | s.Addr = seg64.Addr |
982 | s.Memsz = seg64.Memsz |
983 | s.Offset = seg64.Offset |
984 | s.Filesz = seg64.Filesz |
985 | s.Maxprot = seg64.Maxprot |
986 | s.Prot = seg64.Prot |
987 | s.Nsect = seg64.Nsect |
988 | s.Flag = seg64.Flag |
989 | s.Firstsect = uint32(len(f.Sections)) |
990 | f.Loads[i] = s |
991 | for i := 0; i < int(s.Nsect); i++ { |
992 | var sh64 Section64 |
993 | if err := binary.Read(b, bo, &sh64); err != nil { |
994 | return nil, err |
995 | } |
996 | sh := new(Section) |
997 | sh.Name = cstring(sh64.Name[0:]) |
998 | sh.Seg = cstring(sh64.Seg[0:]) |
999 | sh.Addr = sh64.Addr |
1000 | sh.Size = sh64.Size |
1001 | sh.Offset = sh64.Offset |
1002 | sh.Align = sh64.Align |
1003 | sh.Reloff = sh64.Reloff |
1004 | sh.Nreloc = sh64.Nreloc |
1005 | sh.Flags = sh64.Flags |
1006 | sh.Reserved1 = sh64.Reserve1 |
1007 | sh.Reserved2 = sh64.Reserve2 |
1008 | sh.Reserved3 = sh64.Reserve3 |
1009 | if err := f.pushSection(sh, r); err != nil { |
1010 | return nil, err |
1011 | } |
1012 | } |
1013 | |
1014 | case LcCodeSignature, LcSegmentSplitInfo, LcFunctionStarts, |
1015 | LcDataInCode, LcDylibCodeSignDrs: |
1016 | var hdr LinkEditDataCmd |
1017 | b := bytes.NewReader(cmddat) |
1018 | |
1019 | if err := binary.Read(b, bo, &hdr); err != nil { |
1020 | return nil, err |
1021 | } |
1022 | l := new(LinkEditData) |
1023 | |
1024 | l.LinkEditDataCmd = hdr |
1025 | f.Loads[i] = l |
1026 | |
1027 | case LcEncryptionInfo, LcEncryptionInfo64: |
1028 | var hdr EncryptionInfoCmd |
1029 | b := bytes.NewReader(cmddat) |
1030 | |
1031 | if err := binary.Read(b, bo, &hdr); err != nil { |
1032 | return nil, err |
1033 | } |
1034 | l := new(EncryptionInfo) |
1035 | |
1036 | l.EncryptionInfoCmd = hdr |
1037 | f.Loads[i] = l |
1038 | |
1039 | case LcDyldInfo, LcDyldInfoOnly: |
1040 | var hdr DyldInfoCmd |
1041 | b := bytes.NewReader(cmddat) |
1042 | |
1043 | if err := binary.Read(b, bo, &hdr); err != nil { |
1044 | return nil, err |
1045 | } |
1046 | l := new(DyldInfo) |
1047 | |
1048 | l.DyldInfoCmd = hdr |
1049 | f.Loads[i] = l |
1050 | } |
1051 | if s != nil { |
1052 | s.sr = io.NewSectionReader(r, int64(s.Offset), int64(s.Filesz)) |
1053 | s.ReaderAt = s.sr |
1054 | } |
1055 | if f.Loads[i].LoadSize(&f.FileTOC) != siz { |
1056 | fmt.Printf("Oops, actual size was %d, calculated was %d, load was %s\n", siz, f.Loads[i].LoadSize(&f.FileTOC), f.Loads[i].String()) |
1057 | panic("oops") |
1058 | } |
1059 | } |
1060 | return f, nil |
1061 | } |
1062 | |
1063 | func (f *File) parseSymtab(symdat, strtab, cmddat []byte, hdr *SymtabCmd, offset int64) (*Symtab, error) { |
1064 | bo := f.ByteOrder |
1065 | symtab := make([]Symbol, hdr.Nsyms) |
1066 | b := bytes.NewReader(symdat) |
1067 | for i := range symtab { |
1068 | var n Nlist64 |
1069 | if f.Magic == Magic64 { |
1070 | if err := binary.Read(b, bo, &n); err != nil { |
1071 | return nil, err |
1072 | } |
1073 | } else { |
1074 | var n32 Nlist32 |
1075 | if err := binary.Read(b, bo, &n32); err != nil { |
1076 | return nil, err |
1077 | } |
1078 | n.Name = n32.Name |
1079 | n.Type = n32.Type |
1080 | n.Sect = n32.Sect |
1081 | n.Desc = n32.Desc |
1082 | n.Value = uint64(n32.Value) |
1083 | } |
1084 | sym := &symtab[i] |
1085 | if n.Name >= uint32(len(strtab)) { |
1086 | return nil, formatError(offset, "invalid name in symbol table, n.Name=%d, len(strtab)=%d", n.Name, len(strtab)) |
1087 | } |
1088 | sym.Name = cstring(strtab[n.Name:]) |
1089 | sym.Type = n.Type |
1090 | sym.Sect = n.Sect |
1091 | sym.Desc = n.Desc |
1092 | sym.Value = n.Value |
1093 | } |
1094 | st := new(Symtab) |
1095 | st.Syms = symtab |
1096 | return st, nil |
1097 | } |
1098 | |
1099 | type relocInfo struct { |
1100 | Addr uint32 |
1101 | Symnum uint32 |
1102 | } |
1103 | |
1104 | func (f *File) pushSection(sh *Section, r io.ReaderAt) error { |
1105 | f.Sections = append(f.Sections, sh) |
1106 | sh.sr = io.NewSectionReader(r, int64(sh.Offset), int64(sh.Size)) |
1107 | sh.ReaderAt = sh.sr |
1108 | |
1109 | if sh.Nreloc > 0 { |
1110 | reldat := make([]byte, int(sh.Nreloc)*8) |
1111 | if _, err := r.ReadAt(reldat, int64(sh.Reloff)); err != nil { |
1112 | return err |
1113 | } |
1114 | b := bytes.NewReader(reldat) |
1115 | |
1116 | bo := f.ByteOrder |
1117 | |
1118 | sh.Relocs = make([]Reloc, sh.Nreloc) |
1119 | for i := range sh.Relocs { |
1120 | rel := &sh.Relocs[i] |
1121 | |
1122 | var ri relocInfo |
1123 | if err := binary.Read(b, bo, &ri); err != nil { |
1124 | return err |
1125 | } |
1126 | |
1127 | if ri.Addr&(1<<31) != 0 { // scattered |
1128 | rel.Addr = ri.Addr & (1<<24 - 1) |
1129 | rel.Type = uint8((ri.Addr >> 24) & (1<<4 - 1)) |
1130 | rel.Len = uint8((ri.Addr >> 28) & (1<<2 - 1)) |
1131 | rel.Pcrel = ri.Addr&(1<<30) != 0 |
1132 | rel.Value = ri.Symnum |
1133 | rel.Scattered = true |
1134 | } else { |
1135 | switch bo { |
1136 | case binary.LittleEndian: |
1137 | rel.Addr = ri.Addr |
1138 | rel.Value = ri.Symnum & (1<<24 - 1) |
1139 | rel.Pcrel = ri.Symnum&(1<<24) != 0 |
1140 | rel.Len = uint8((ri.Symnum >> 25) & (1<<2 - 1)) |
1141 | rel.Extern = ri.Symnum&(1<<27) != 0 |
1142 | rel.Type = uint8((ri.Symnum >> 28) & (1<<4 - 1)) |
1143 | case binary.BigEndian: |
1144 | rel.Addr = ri.Addr |
1145 | rel.Value = ri.Symnum >> 8 |
1146 | rel.Pcrel = ri.Symnum&(1<<7) != 0 |
1147 | rel.Len = uint8((ri.Symnum >> 5) & (1<<2 - 1)) |
1148 | rel.Extern = ri.Symnum&(1<<4) != 0 |
1149 | rel.Type = uint8(ri.Symnum & (1<<4 - 1)) |
1150 | default: |
1151 | panic("unreachable") |
1152 | } |
1153 | } |
1154 | } |
1155 | } |
1156 | |
1157 | return nil |
1158 | } |
1159 | |
1160 | func cstring(b []byte) string { |
1161 | i := bytes.IndexByte(b, 0) |
1162 | if i == -1 { |
1163 | i = len(b) |
1164 | } |
1165 | return string(b[0:i]) |
1166 | } |
1167 | |
1168 | // Segment returns the first Segment with the given name, or nil if no such segment exists. |
1169 | func (f *File) Segment(name string) *Segment { |
1170 | for _, l := range f.Loads { |
1171 | if s, ok := l.(*Segment); ok && s.Name == name { |
1172 | return s |
1173 | } |
1174 | } |
1175 | return nil |
1176 | } |
1177 | |
1178 | // Section returns the first section with the given name, or nil if no such |
1179 | // section exists. |
1180 | func (f *File) Section(name string) *Section { |
1181 | for _, s := range f.Sections { |
1182 | if s.Name == name { |
1183 | return s |
1184 | } |
1185 | } |
1186 | return nil |
1187 | } |
1188 | |
1189 | // DWARF returns the DWARF debug information for the Mach-O file. |
1190 | func (f *File) DWARF() (*dwarf.Data, error) { |
1191 | dwarfSuffix := func(s *Section) string { |
1192 | switch { |
1193 | case strings.HasPrefix(s.Name, "__debug_"): |
1194 | return s.Name[8:] |
1195 | case strings.HasPrefix(s.Name, "__zdebug_"): |
1196 | return s.Name[9:] |
1197 | default: |
1198 | return "" |
1199 | } |
1200 | |
1201 | } |
1202 | sectionData := func(s *Section) ([]byte, error) { |
1203 | b, err := s.Data() |
1204 | if err != nil && uint64(len(b)) < s.Size { |
1205 | return nil, err |
1206 | } |
1207 | |
1208 | if len(b) >= 12 && string(b[:4]) == "ZLIB" { |
1209 | dlen := binary.BigEndian.Uint64(b[4:12]) |
1210 | dbuf := make([]byte, dlen) |
1211 | r, err := zlib.NewReader(bytes.NewBuffer(b[12:])) |
1212 | if err != nil { |
1213 | return nil, err |
1214 | } |
1215 | if _, err := io.ReadFull(r, dbuf); err != nil { |
1216 | return nil, err |
1217 | } |
1218 | if err := r.Close(); err != nil { |
1219 | return nil, err |
1220 | } |
1221 | b = dbuf |
1222 | } |
1223 | return b, nil |
1224 | } |
1225 | |
1226 | // There are many other DWARF sections, but these |
1227 | // are the ones the debug/dwarf package uses. |
1228 | // Don't bother loading others. |
1229 | var dat = map[string][]byte{"abbrev": nil, "info": nil, "str": nil, "line": nil, "ranges": nil} |
1230 | for _, s := range f.Sections { |
1231 | suffix := dwarfSuffix(s) |
1232 | if suffix == "" { |
1233 | continue |
1234 | } |
1235 | if _, ok := dat[suffix]; !ok { |
1236 | continue |
1237 | } |
1238 | b, err := sectionData(s) |
1239 | if err != nil { |
1240 | return nil, err |
1241 | } |
1242 | dat[suffix] = b |
1243 | } |
1244 | |
1245 | d, err := dwarf.New(dat["abbrev"], nil, nil, dat["info"], dat["line"], nil, dat["ranges"], dat["str"]) |
1246 | if err != nil { |
1247 | return nil, err |
1248 | } |
1249 | |
1250 | // Look for DWARF4 .debug_types sections. |
1251 | for i, s := range f.Sections { |
1252 | suffix := dwarfSuffix(s) |
1253 | if suffix != "types" { |
1254 | continue |
1255 | } |
1256 | |
1257 | b, err := sectionData(s) |
1258 | if err != nil { |
1259 | return nil, err |
1260 | } |
1261 | |
1262 | err = d.AddTypes(fmt.Sprintf("types-%d", i), b) |
1263 | if err != nil { |
1264 | return nil, err |
1265 | } |
1266 | } |
1267 | |
1268 | return d, nil |
1269 | } |
1270 | |
1271 | // ImportedSymbols returns the names of all symbols |
1272 | // referred to by the binary f that are expected to be |
1273 | // satisfied by other libraries at dynamic load time. |
1274 | func (f *File) ImportedSymbols() ([]string, error) { |
1275 | if f.Dysymtab == nil || f.Symtab == nil { |
1276 | return nil, formatError(0, "missing symbol table, f.Dsymtab=%v, f.Symtab=%v", f.Dysymtab, f.Symtab) |
1277 | } |
1278 | |
1279 | st := f.Symtab |
1280 | dt := f.Dysymtab |
1281 | var all []string |
1282 | for _, s := range st.Syms[dt.Iundefsym : dt.Iundefsym+dt.Nundefsym] { |
1283 | all = append(all, s.Name) |
1284 | } |
1285 | return all, nil |
1286 | } |
1287 | |
1288 | // ImportedLibraries returns the paths of all libraries |
1289 | // referred to by the binary f that are expected to be |
1290 | // linked with the binary at dynamic link time. |
1291 | func (f *File) ImportedLibraries() ([]string, error) { |
1292 | var all []string |
1293 | for _, l := range f.Loads { |
1294 | if lib, ok := l.(*Dylib); ok { |
1295 | all = append(all, lib.Name) |
1296 | } |
1297 | } |
1298 | return all, nil |
1299 | } |
1300 | |
1301 | func RoundUp(x, align uint64) uint64 { |
1302 | return uint64((x + align - 1) & -align) |
1303 | } |
1304 |
Members