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