github.com/varialus/godfly@v0.0.0-20130904042352-1934f9f095ab/src/pkg/debug/macho/file.go (about)

     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  // FormatError is returned by some operations if the data does
   146  // not have the correct format for an object file.
   147  type FormatError struct {
   148  	off int64
   149  	msg string
   150  	val interface{}
   151  }
   152  
   153  func (e *FormatError) Error() string {
   154  	msg := e.msg
   155  	if e.val != nil {
   156  		msg += fmt.Sprintf(" '%v'", e.val)
   157  	}
   158  	msg += fmt.Sprintf(" in record at byte %#x", e.off)
   159  	return msg
   160  }
   161  
   162  // Open opens the named file using os.Open and prepares it for use as a Mach-O binary.
   163  func Open(name string) (*File, error) {
   164  	f, err := os.Open(name)
   165  	if err != nil {
   166  		return nil, err
   167  	}
   168  	ff, err := NewFile(f)
   169  	if err != nil {
   170  		f.Close()
   171  		return nil, err
   172  	}
   173  	ff.closer = f
   174  	return ff, nil
   175  }
   176  
   177  // Close closes the File.
   178  // If the File was created using NewFile directly instead of Open,
   179  // Close has no effect.
   180  func (f *File) Close() error {
   181  	var err error
   182  	if f.closer != nil {
   183  		err = f.closer.Close()
   184  		f.closer = nil
   185  	}
   186  	return err
   187  }
   188  
   189  // NewFile creates a new File for accessing a Mach-O binary in an underlying reader.
   190  // The Mach-O binary is expected to start at position 0 in the ReaderAt.
   191  func NewFile(r io.ReaderAt) (*File, error) {
   192  	f := new(File)
   193  	sr := io.NewSectionReader(r, 0, 1<<63-1)
   194  
   195  	// Read and decode Mach magic to determine byte order, size.
   196  	// Magic32 and Magic64 differ only in the bottom bit.
   197  	var ident [4]byte
   198  	if _, err := r.ReadAt(ident[0:], 0); err != nil {
   199  		return nil, err
   200  	}
   201  	be := binary.BigEndian.Uint32(ident[0:])
   202  	le := binary.LittleEndian.Uint32(ident[0:])
   203  	switch Magic32 &^ 1 {
   204  	case be &^ 1:
   205  		f.ByteOrder = binary.BigEndian
   206  		f.Magic = be
   207  	case le &^ 1:
   208  		f.ByteOrder = binary.LittleEndian
   209  		f.Magic = le
   210  	default:
   211  		return nil, &FormatError{0, "invalid magic number", nil}
   212  	}
   213  
   214  	// Read entire file header.
   215  	if err := binary.Read(sr, f.ByteOrder, &f.FileHeader); err != nil {
   216  		return nil, err
   217  	}
   218  
   219  	// Then load commands.
   220  	offset := int64(fileHeaderSize32)
   221  	if f.Magic == Magic64 {
   222  		offset = fileHeaderSize64
   223  	}
   224  	dat := make([]byte, f.Cmdsz)
   225  	if _, err := r.ReadAt(dat, offset); err != nil {
   226  		return nil, err
   227  	}
   228  	f.Loads = make([]Load, f.Ncmd)
   229  	bo := f.ByteOrder
   230  	for i := range f.Loads {
   231  		// Each load command begins with uint32 command and length.
   232  		if len(dat) < 8 {
   233  			return nil, &FormatError{offset, "command block too small", nil}
   234  		}
   235  		cmd, siz := LoadCmd(bo.Uint32(dat[0:4])), bo.Uint32(dat[4:8])
   236  		if siz < 8 || siz > uint32(len(dat)) {
   237  			return nil, &FormatError{offset, "invalid command block size", nil}
   238  		}
   239  		var cmddat []byte
   240  		cmddat, dat = dat[0:siz], dat[siz:]
   241  		offset += int64(siz)
   242  		var s *Segment
   243  		switch cmd {
   244  		default:
   245  			f.Loads[i] = LoadBytes(cmddat)
   246  
   247  		case LoadCmdDylib:
   248  			var hdr DylibCmd
   249  			b := bytes.NewBuffer(cmddat)
   250  			if err := binary.Read(b, bo, &hdr); err != nil {
   251  				return nil, err
   252  			}
   253  			l := new(Dylib)
   254  			if hdr.Name >= uint32(len(cmddat)) {
   255  				return nil, &FormatError{offset, "invalid name in dynamic library command", hdr.Name}
   256  			}
   257  			l.Name = cstring(cmddat[hdr.Name:])
   258  			l.Time = hdr.Time
   259  			l.CurrentVersion = hdr.CurrentVersion
   260  			l.CompatVersion = hdr.CompatVersion
   261  			l.LoadBytes = LoadBytes(cmddat)
   262  			f.Loads[i] = l
   263  
   264  		case LoadCmdSymtab:
   265  			var hdr SymtabCmd
   266  			b := bytes.NewBuffer(cmddat)
   267  			if err := binary.Read(b, bo, &hdr); err != nil {
   268  				return nil, err
   269  			}
   270  			strtab := make([]byte, hdr.Strsize)
   271  			if _, err := r.ReadAt(strtab, int64(hdr.Stroff)); err != nil {
   272  				return nil, err
   273  			}
   274  			var symsz int
   275  			if f.Magic == Magic64 {
   276  				symsz = 16
   277  			} else {
   278  				symsz = 12
   279  			}
   280  			symdat := make([]byte, int(hdr.Nsyms)*symsz)
   281  			if _, err := r.ReadAt(symdat, int64(hdr.Symoff)); err != nil {
   282  				return nil, err
   283  			}
   284  			st, err := f.parseSymtab(symdat, strtab, cmddat, &hdr, offset)
   285  			if err != nil {
   286  				return nil, err
   287  			}
   288  			f.Loads[i] = st
   289  			f.Symtab = st
   290  
   291  		case LoadCmdDysymtab:
   292  			var hdr DysymtabCmd
   293  			b := bytes.NewBuffer(cmddat)
   294  			if err := binary.Read(b, bo, &hdr); err != nil {
   295  				return nil, err
   296  			}
   297  			dat := make([]byte, hdr.Nindirectsyms*4)
   298  			if _, err := r.ReadAt(dat, int64(hdr.Indirectsymoff)); err != nil {
   299  				return nil, err
   300  			}
   301  			x := make([]uint32, hdr.Nindirectsyms)
   302  			if err := binary.Read(bytes.NewBuffer(dat), bo, x); err != nil {
   303  				return nil, err
   304  			}
   305  			st := new(Dysymtab)
   306  			st.LoadBytes = LoadBytes(cmddat)
   307  			st.DysymtabCmd = hdr
   308  			st.IndirectSyms = x
   309  			f.Loads[i] = st
   310  			f.Dysymtab = st
   311  
   312  		case LoadCmdSegment:
   313  			var seg32 Segment32
   314  			b := bytes.NewBuffer(cmddat)
   315  			if err := binary.Read(b, bo, &seg32); err != nil {
   316  				return nil, err
   317  			}
   318  			s = new(Segment)
   319  			s.LoadBytes = cmddat
   320  			s.Cmd = cmd
   321  			s.Len = siz
   322  			s.Name = cstring(seg32.Name[0:])
   323  			s.Addr = uint64(seg32.Addr)
   324  			s.Memsz = uint64(seg32.Memsz)
   325  			s.Offset = uint64(seg32.Offset)
   326  			s.Filesz = uint64(seg32.Filesz)
   327  			s.Maxprot = seg32.Maxprot
   328  			s.Prot = seg32.Prot
   329  			s.Nsect = seg32.Nsect
   330  			s.Flag = seg32.Flag
   331  			f.Loads[i] = s
   332  			for i := 0; i < int(s.Nsect); i++ {
   333  				var sh32 Section32
   334  				if err := binary.Read(b, bo, &sh32); err != nil {
   335  					return nil, err
   336  				}
   337  				sh := new(Section)
   338  				sh.Name = cstring(sh32.Name[0:])
   339  				sh.Seg = cstring(sh32.Seg[0:])
   340  				sh.Addr = uint64(sh32.Addr)
   341  				sh.Size = uint64(sh32.Size)
   342  				sh.Offset = sh32.Offset
   343  				sh.Align = sh32.Align
   344  				sh.Reloff = sh32.Reloff
   345  				sh.Nreloc = sh32.Nreloc
   346  				sh.Flags = sh32.Flags
   347  				f.pushSection(sh, r)
   348  			}
   349  
   350  		case LoadCmdSegment64:
   351  			var seg64 Segment64
   352  			b := bytes.NewBuffer(cmddat)
   353  			if err := binary.Read(b, bo, &seg64); err != nil {
   354  				return nil, err
   355  			}
   356  			s = new(Segment)
   357  			s.LoadBytes = cmddat
   358  			s.Cmd = cmd
   359  			s.Len = siz
   360  			s.Name = cstring(seg64.Name[0:])
   361  			s.Addr = seg64.Addr
   362  			s.Memsz = seg64.Memsz
   363  			s.Offset = seg64.Offset
   364  			s.Filesz = seg64.Filesz
   365  			s.Maxprot = seg64.Maxprot
   366  			s.Prot = seg64.Prot
   367  			s.Nsect = seg64.Nsect
   368  			s.Flag = seg64.Flag
   369  			f.Loads[i] = s
   370  			for i := 0; i < int(s.Nsect); i++ {
   371  				var sh64 Section64
   372  				if err := binary.Read(b, bo, &sh64); err != nil {
   373  					return nil, err
   374  				}
   375  				sh := new(Section)
   376  				sh.Name = cstring(sh64.Name[0:])
   377  				sh.Seg = cstring(sh64.Seg[0:])
   378  				sh.Addr = sh64.Addr
   379  				sh.Size = sh64.Size
   380  				sh.Offset = sh64.Offset
   381  				sh.Align = sh64.Align
   382  				sh.Reloff = sh64.Reloff
   383  				sh.Nreloc = sh64.Nreloc
   384  				sh.Flags = sh64.Flags
   385  				f.pushSection(sh, r)
   386  			}
   387  		}
   388  		if s != nil {
   389  			s.sr = io.NewSectionReader(r, int64(s.Offset), int64(s.Filesz))
   390  			s.ReaderAt = s.sr
   391  		}
   392  	}
   393  	return f, nil
   394  }
   395  
   396  func (f *File) parseSymtab(symdat, strtab, cmddat []byte, hdr *SymtabCmd, offset int64) (*Symtab, error) {
   397  	bo := f.ByteOrder
   398  	symtab := make([]Symbol, hdr.Nsyms)
   399  	b := bytes.NewBuffer(symdat)
   400  	for i := range symtab {
   401  		var n Nlist64
   402  		if f.Magic == Magic64 {
   403  			if err := binary.Read(b, bo, &n); err != nil {
   404  				return nil, err
   405  			}
   406  		} else {
   407  			var n32 Nlist32
   408  			if err := binary.Read(b, bo, &n32); err != nil {
   409  				return nil, err
   410  			}
   411  			n.Name = n32.Name
   412  			n.Type = n32.Type
   413  			n.Sect = n32.Sect
   414  			n.Desc = n32.Desc
   415  			n.Value = uint64(n32.Value)
   416  		}
   417  		sym := &symtab[i]
   418  		if n.Name >= uint32(len(strtab)) {
   419  			return nil, &FormatError{offset, "invalid name in symbol table", n.Name}
   420  		}
   421  		sym.Name = cstring(strtab[n.Name:])
   422  		sym.Type = n.Type
   423  		sym.Sect = n.Sect
   424  		sym.Desc = n.Desc
   425  		sym.Value = n.Value
   426  	}
   427  	st := new(Symtab)
   428  	st.LoadBytes = LoadBytes(cmddat)
   429  	st.Syms = symtab
   430  	return st, nil
   431  }
   432  
   433  func (f *File) pushSection(sh *Section, r io.ReaderAt) {
   434  	f.Sections = append(f.Sections, sh)
   435  	sh.sr = io.NewSectionReader(r, int64(sh.Offset), int64(sh.Size))
   436  	sh.ReaderAt = sh.sr
   437  }
   438  
   439  func cstring(b []byte) string {
   440  	var i int
   441  	for i = 0; i < len(b) && b[i] != 0; i++ {
   442  	}
   443  	return string(b[0:i])
   444  }
   445  
   446  // Segment returns the first Segment with the given name, or nil if no such segment exists.
   447  func (f *File) Segment(name string) *Segment {
   448  	for _, l := range f.Loads {
   449  		if s, ok := l.(*Segment); ok && s.Name == name {
   450  			return s
   451  		}
   452  	}
   453  	return nil
   454  }
   455  
   456  // Section returns the first section with the given name, or nil if no such
   457  // section exists.
   458  func (f *File) Section(name string) *Section {
   459  	for _, s := range f.Sections {
   460  		if s.Name == name {
   461  			return s
   462  		}
   463  	}
   464  	return nil
   465  }
   466  
   467  // DWARF returns the DWARF debug information for the Mach-O file.
   468  func (f *File) DWARF() (*dwarf.Data, error) {
   469  	// There are many other DWARF sections, but these
   470  	// are the required ones, and the debug/dwarf package
   471  	// does not use the others, so don't bother loading them.
   472  	var names = [...]string{"abbrev", "info", "str"}
   473  	var dat [len(names)][]byte
   474  	for i, name := range names {
   475  		name = "__debug_" + name
   476  		s := f.Section(name)
   477  		if s == nil {
   478  			return nil, errors.New("missing Mach-O section " + name)
   479  		}
   480  		b, err := s.Data()
   481  		if err != nil && uint64(len(b)) < s.Size {
   482  			return nil, err
   483  		}
   484  		dat[i] = b
   485  	}
   486  
   487  	abbrev, info, str := dat[0], dat[1], dat[2]
   488  	return dwarf.New(abbrev, nil, nil, info, nil, nil, nil, str)
   489  }
   490  
   491  // ImportedSymbols returns the names of all symbols
   492  // referred to by the binary f that are expected to be
   493  // satisfied by other libraries at dynamic load time.
   494  func (f *File) ImportedSymbols() ([]string, error) {
   495  	if f.Dysymtab == nil || f.Symtab == nil {
   496  		return nil, &FormatError{0, "missing symbol table", nil}
   497  	}
   498  
   499  	st := f.Symtab
   500  	dt := f.Dysymtab
   501  	var all []string
   502  	for _, s := range st.Syms[dt.Iundefsym : dt.Iundefsym+dt.Nundefsym] {
   503  		all = append(all, s.Name)
   504  	}
   505  	return all, nil
   506  }
   507  
   508  // ImportedLibraries returns the paths of all libraries
   509  // referred to by the binary f that are expected to be
   510  // linked with the binary at dynamic link time.
   511  func (f *File) ImportedLibraries() ([]string, error) {
   512  	var all []string
   513  	for _, l := range f.Loads {
   514  		if lib, ok := l.(*Dylib); ok {
   515  			all = append(all, lib.Name)
   516  		}
   517  	}
   518  	return all, nil
   519  }