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