Source file src/pkg/debug/macho/file.go
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 "debug/dwarf"
13 "encoding/binary"
14 "errors"
15 "fmt"
16 "io"
17 "os"
18 )
19
20 // A File represents an open Mach-O file.
21 type File struct {
22 FileHeader
23 ByteOrder binary.ByteOrder
24 Loads []Load
25 Sections []*Section
26
27 Symtab *Symtab
28 Dysymtab *Dysymtab
29
30 closer io.Closer
31 }
32
33 // A Load represents any Mach-O load command.
34 type Load interface {
35 Raw() []byte
36 }
37
38 // A LoadBytes is the uninterpreted bytes of a Mach-O load command.
39 type LoadBytes []byte
40
41 func (b LoadBytes) Raw() []byte { return b }
42
43 // A SegmentHeader is the header for a Mach-O 32-bit or 64-bit load segment command.
44 type SegmentHeader struct {
45 Cmd LoadCmd
46 Len uint32
47 Name string
48 Addr uint64
49 Memsz uint64
50 Offset uint64
51 Filesz uint64
52 Maxprot uint32
53 Prot uint32
54 Nsect uint32
55 Flag uint32
56 }
57
58 // A Segment represents a Mach-O 32-bit or 64-bit load segment command.
59 type Segment struct {
60 LoadBytes
61 SegmentHeader
62
63 // Embed ReaderAt for ReadAt method.
64 // Do not embed SectionReader directly
65 // to avoid having Read and Seek.
66 // If a client wants Read and Seek it must use
67 // Open() to avoid fighting over the seek offset
68 // with other clients.
69 io.ReaderAt
70 sr *io.SectionReader
71 }
72
73 // Data reads and returns the contents of the segment.
74 func (s *Segment) Data() ([]byte, error) {
75 dat := make([]byte, s.sr.Size())
76 n, err := s.sr.ReadAt(dat, 0)
77 return dat[0:n], err
78 }
79
80 // Open returns a new ReadSeeker reading the segment.
81 func (s *Segment) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) }
82
83 type SectionHeader struct {
84 Name string
85 Seg string
86 Addr uint64
87 Size uint64
88 Offset uint32
89 Align uint32
90 Reloff uint32
91 Nreloc uint32
92 Flags uint32
93 }
94
95 type Section struct {
96 SectionHeader
97
98 // Embed ReaderAt for ReadAt method.
99 // Do not embed SectionReader directly
100 // to avoid having Read and Seek.
101 // If a client wants Read and Seek it must use
102 // Open() to avoid fighting over the seek offset
103 // with other clients.
104 io.ReaderAt
105 sr *io.SectionReader
106 }
107
108 // Data reads and returns the contents of the Mach-O section.
109 func (s *Section) Data() ([]byte, error) {
110 dat := make([]byte, s.sr.Size())
111 n, err := s.sr.ReadAt(dat, 0)
112 return dat[0:n], err
113 }
114
115 // Open returns a new ReadSeeker reading the Mach-O section.
116 func (s *Section) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) }
117
118 // A Dylib represents a Mach-O load dynamic library command.
119 type Dylib struct {
120 LoadBytes
121 Name string
122 Time uint32
123 CurrentVersion uint32
124 CompatVersion uint32
125 }
126
127 // A Symtab represents a Mach-O symbol table command.
128 type Symtab struct {
129 LoadBytes
130 SymtabCmd
131 Syms []Symbol
132 }
133
134 // A Dysymtab represents a Mach-O dynamic symbol table command.
135 type Dysymtab struct {
136 LoadBytes
137 DysymtabCmd
138 IndirectSyms []uint32 // indices into Symtab.Syms
139 }
140
141 /*
142 * Mach-O reader
143 */
144
145 type FormatError struct {
146 off int64
147 msg string
148 val interface{}
149 }
150
151 func (e *FormatError) Error() string {
152 msg := e.msg
153 if e.val != nil {
154 msg += fmt.Sprintf(" '%v'", e.val)
155 }
156 msg += fmt.Sprintf(" in record at byte %#x", e.off)
157 return msg
158 }
159
160 // Open opens the named file using os.Open and prepares it for use as a Mach-O binary.
161 func Open(name string) (*File, error) {
162 f, err := os.Open(name)
163 if err != nil {
164 return nil, err
165 }
166 ff, err := NewFile(f)
167 if err != nil {
168 f.Close()
169 return nil, err
170 }
171 ff.closer = f
172 return ff, nil
173 }
174
175 // Close closes the File.
176 // If the File was created using NewFile directly instead of Open,
177 // Close has no effect.
178 func (f *File) Close() error {
179 var err error
180 if f.closer != nil {
181 err = f.closer.Close()
182 f.closer = nil
183 }
184 return err
185 }
186
187 // NewFile creates a new File for accessing a Mach-O binary in an underlying reader.
188 // The Mach-O binary is expected to start at position 0 in the ReaderAt.
189 func NewFile(r io.ReaderAt) (*File, error) {
190 f := new(File)
191 sr := io.NewSectionReader(r, 0, 1<<63-1)
192
193 // Read and decode Mach magic to determine byte order, size.
194 // Magic32 and Magic64 differ only in the bottom bit.
195 var ident [4]byte
196 if _, err := r.ReadAt(ident[0:], 0); err != nil {
197 return nil, err
198 }
199 be := binary.BigEndian.Uint32(ident[0:])
200 le := binary.LittleEndian.Uint32(ident[0:])
201 switch Magic32 &^ 1 {
202 case be &^ 1:
203 f.ByteOrder = binary.BigEndian
204 f.Magic = be
205 case le &^ 1:
206 f.ByteOrder = binary.LittleEndian
207 f.Magic = le
208 default:
209 return nil, &FormatError{0, "invalid magic number", nil}
210 }
211
212 // Read entire file header.
213 if err := binary.Read(sr, f.ByteOrder, &f.FileHeader); err != nil {
214 return nil, err
215 }
216
217 // Then load commands.
218 offset := int64(fileHeaderSize32)
219 if f.Magic == Magic64 {
220 offset = fileHeaderSize64
221 }
222 dat := make([]byte, f.Cmdsz)
223 if _, err := r.ReadAt(dat, offset); err != nil {
224 return nil, err
225 }
226 f.Loads = make([]Load, f.Ncmd)
227 bo := f.ByteOrder
228 for i := range f.Loads {
229 // Each load command begins with uint32 command and length.
230 if len(dat) < 8 {
231 return nil, &FormatError{offset, "command block too small", nil}
232 }
233 cmd, siz := LoadCmd(bo.Uint32(dat[0:4])), bo.Uint32(dat[4:8])
234 if siz < 8 || siz > uint32(len(dat)) {
235 return nil, &FormatError{offset, "invalid command block size", nil}
236 }
237 var cmddat []byte
238 cmddat, dat = dat[0:siz], dat[siz:]
239 offset += int64(siz)
240 var s *Segment
241 switch cmd {
242 default:
243 f.Loads[i] = LoadBytes(cmddat)
244
245 case LoadCmdDylib:
246 var hdr DylibCmd
247 b := bytes.NewBuffer(cmddat)
248 if err := binary.Read(b, bo, &hdr); err != nil {
249 return nil, err
250 }
251 l := new(Dylib)
252 if hdr.Name >= uint32(len(cmddat)) {
253 return nil, &FormatError{offset, "invalid name in dynamic library command", hdr.Name}
254 }
255 l.Name = cstring(cmddat[hdr.Name:])
256 l.Time = hdr.Time
257 l.CurrentVersion = hdr.CurrentVersion
258 l.CompatVersion = hdr.CompatVersion
259 l.LoadBytes = LoadBytes(cmddat)
260 f.Loads[i] = l
261
262 case LoadCmdSymtab:
263 var hdr SymtabCmd
264 b := bytes.NewBuffer(cmddat)
265 if err := binary.Read(b, bo, &hdr); err != nil {
266 return nil, err
267 }
268 strtab := make([]byte, hdr.Strsize)
269 if _, err := r.ReadAt(strtab, int64(hdr.Stroff)); err != nil {
270 return nil, err
271 }
272 var symsz int
273 if f.Magic == Magic64 {
274 symsz = 16
275 } else {
276 symsz = 12
277 }
278 symdat := make([]byte, int(hdr.Nsyms)*symsz)
279 if _, err := r.ReadAt(symdat, int64(hdr.Symoff)); err != nil {
280 return nil, err
281 }
282 st, err := f.parseSymtab(symdat, strtab, cmddat, &hdr, offset)
283 if err != nil {
284 return nil, err
285 }
286 f.Loads[i] = st
287 f.Symtab = st
288
289 case LoadCmdDysymtab:
290 var hdr DysymtabCmd
291 b := bytes.NewBuffer(cmddat)
292 if err := binary.Read(b, bo, &hdr); err != nil {
293 return nil, err
294 }
295 dat := make([]byte, hdr.Nindirectsyms*4)
296 if _, err := r.ReadAt(dat, int64(hdr.Indirectsymoff)); err != nil {
297 return nil, err
298 }
299 x := make([]uint32, hdr.Nindirectsyms)
300 if err := binary.Read(bytes.NewBuffer(dat), bo, x); err != nil {
301 return nil, err
302 }
303 st := new(Dysymtab)
304 st.LoadBytes = LoadBytes(cmddat)
305 st.DysymtabCmd = hdr
306 st.IndirectSyms = x
307 f.Loads[i] = st
308 f.Dysymtab = st
309
310 case LoadCmdSegment:
311 var seg32 Segment32
312 b := bytes.NewBuffer(cmddat)
313 if err := binary.Read(b, bo, &seg32); err != nil {
314 return nil, err
315 }
316 s = new(Segment)
317 s.LoadBytes = cmddat
318 s.Cmd = cmd
319 s.Len = siz
320 s.Name = cstring(seg32.Name[0:])
321 s.Addr = uint64(seg32.Addr)
322 s.Memsz = uint64(seg32.Memsz)
323 s.Offset = uint64(seg32.Offset)
324 s.Filesz = uint64(seg32.Filesz)
325 s.Maxprot = seg32.Maxprot
326 s.Prot = seg32.Prot
327 s.Nsect = seg32.Nsect
328 s.Flag = seg32.Flag
329 f.Loads[i] = s
330 for i := 0; i < int(s.Nsect); i++ {
331 var sh32 Section32
332 if err := binary.Read(b, bo, &sh32); err != nil {
333 return nil, err
334 }
335 sh := new(Section)
336 sh.Name = cstring(sh32.Name[0:])
337 sh.Seg = cstring(sh32.Seg[0:])
338 sh.Addr = uint64(sh32.Addr)
339 sh.Size = uint64(sh32.Size)
340 sh.Offset = sh32.Offset
341 sh.Align = sh32.Align
342 sh.Reloff = sh32.Reloff
343 sh.Nreloc = sh32.Nreloc
344 sh.Flags = sh32.Flags
345 f.pushSection(sh, r)
346 }
347
348 case LoadCmdSegment64:
349 var seg64 Segment64
350 b := bytes.NewBuffer(cmddat)
351 if err := binary.Read(b, bo, &seg64); err != nil {
352 return nil, err
353 }
354 s = new(Segment)
355 s.LoadBytes = cmddat
356 s.Cmd = cmd
357 s.Len = siz
358 s.Name = cstring(seg64.Name[0:])
359 s.Addr = seg64.Addr
360 s.Memsz = seg64.Memsz
361 s.Offset = seg64.Offset
362 s.Filesz = seg64.Filesz
363 s.Maxprot = seg64.Maxprot
364 s.Prot = seg64.Prot
365 s.Nsect = seg64.Nsect
366 s.Flag = seg64.Flag
367 f.Loads[i] = s
368 for i := 0; i < int(s.Nsect); i++ {
369 var sh64 Section64
370 if err := binary.Read(b, bo, &sh64); err != nil {
371 return nil, err
372 }
373 sh := new(Section)
374 sh.Name = cstring(sh64.Name[0:])
375 sh.Seg = cstring(sh64.Seg[0:])
376 sh.Addr = sh64.Addr
377 sh.Size = sh64.Size
378 sh.Offset = sh64.Offset
379 sh.Align = sh64.Align
380 sh.Reloff = sh64.Reloff
381 sh.Nreloc = sh64.Nreloc
382 sh.Flags = sh64.Flags
383 f.pushSection(sh, r)
384 }
385 }
386 if s != nil {
387 s.sr = io.NewSectionReader(r, int64(s.Offset), int64(s.Filesz))
388 s.ReaderAt = s.sr
389 }
390 }
391 return f, nil
392 }
393
394 func (f *File) parseSymtab(symdat, strtab, cmddat []byte, hdr *SymtabCmd, offset int64) (*Symtab, error) {
395 bo := f.ByteOrder
396 symtab := make([]Symbol, hdr.Nsyms)
397 b := bytes.NewBuffer(symdat)
398 for i := range symtab {
399 var n Nlist64
400 if f.Magic == Magic64 {
401 if err := binary.Read(b, bo, &n); err != nil {
402 return nil, err
403 }
404 } else {
405 var n32 Nlist32
406 if err := binary.Read(b, bo, &n32); err != nil {
407 return nil, err
408 }
409 n.Name = n32.Name
410 n.Type = n32.Type
411 n.Sect = n32.Sect
412 n.Desc = n32.Desc
413 n.Value = uint64(n32.Value)
414 }
415 sym := &symtab[i]
416 if n.Name >= uint32(len(strtab)) {
417 return nil, &FormatError{offset, "invalid name in symbol table", n.Name}
418 }
419 sym.Name = cstring(strtab[n.Name:])
420 sym.Type = n.Type
421 sym.Sect = n.Sect
422 sym.Desc = n.Desc
423 sym.Value = n.Value
424 }
425 st := new(Symtab)
426 st.LoadBytes = LoadBytes(cmddat)
427 st.Syms = symtab
428 return st, nil
429 }
430
431 func (f *File) pushSection(sh *Section, r io.ReaderAt) {
432 f.Sections = append(f.Sections, sh)
433 sh.sr = io.NewSectionReader(r, int64(sh.Offset), int64(sh.Size))
434 sh.ReaderAt = sh.sr
435 }
436
437 func cstring(b []byte) string {
438 var i int
439 for i = 0; i < len(b) && b[i] != 0; i++ {
440 }
441 return string(b[0:i])
442 }
443
444 // Segment returns the first Segment with the given name, or nil if no such segment exists.
445 func (f *File) Segment(name string) *Segment {
446 for _, l := range f.Loads {
447 if s, ok := l.(*Segment); ok && s.Name == name {
448 return s
449 }
450 }
451 return nil
452 }
453
454 // Section returns the first section with the given name, or nil if no such
455 // section exists.
456 func (f *File) Section(name string) *Section {
457 for _, s := range f.Sections {
458 if s.Name == name {
459 return s
460 }
461 }
462 return nil
463 }
464
465 // DWARF returns the DWARF debug information for the Mach-O file.
466 func (f *File) DWARF() (*dwarf.Data, error) {
467 // There are many other DWARF sections, but these
468 // are the required ones, and the debug/dwarf package
469 // does not use the others, so don't bother loading them.
470 var names = [...]string{"abbrev", "info", "str"}
471 var dat [len(names)][]byte
472 for i, name := range names {
473 name = "__debug_" + name
474 s := f.Section(name)
475 if s == nil {
476 return nil, errors.New("missing Mach-O section " + name)
477 }
478 b, err := s.Data()
479 if err != nil && uint64(len(b)) < s.Size {
480 return nil, err
481 }
482 dat[i] = b
483 }
484
485 abbrev, info, str := dat[0], dat[1], dat[2]
486 return dwarf.New(abbrev, nil, nil, info, nil, nil, nil, str)
487 }
488
489 // ImportedSymbols returns the names of all symbols
490 // referred to by the binary f that are expected to be
491 // satisfied by other libraries at dynamic load time.
492 func (f *File) ImportedSymbols() ([]string, error) {
493 if f.Dysymtab == nil || f.Symtab == nil {
494 return nil, &FormatError{0, "missing symbol table", nil}
495 }
496
497 st := f.Symtab
498 dt := f.Dysymtab
499 var all []string
500 for _, s := range st.Syms[dt.Iundefsym : dt.Iundefsym+dt.Nundefsym] {
501 all = append(all, s.Name)
502 }
503 return all, nil
504 }
505
506 // ImportedLibraries returns the paths of all libraries
507 // referred to by the binary f that are expected to be
508 // linked with the binary at dynamic link time.
509 func (f *File) ImportedLibraries() ([]string, error) {
510 var all []string
511 for _, l := range f.Loads {
512 if lib, ok := l.(*Dylib); ok {
513 all = append(all, lib.Name)
514 }
515 }
516 return all, nil
517 }