...
Run Format

Source file src/debug/macho/file.go

Documentation: debug/macho

     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  // A Reloc represents a Mach-O relocation.
    98  type Reloc struct {
    99  	Addr  uint32
   100  	Value uint32
   101  	// when Scattered == false && Extern == true, Value is the symbol number.
   102  	// when Scattered == false && Extern == false, Value is the section number.
   103  	// when Scattered == true, Value is the value that this reloc refers to.
   104  	Type      uint8
   105  	Len       uint8 // 0=byte, 1=word, 2=long, 3=quad
   106  	Pcrel     bool
   107  	Extern    bool // valid if Scattered == false
   108  	Scattered bool
   109  }
   110  
   111  type Section struct {
   112  	SectionHeader
   113  	Relocs []Reloc
   114  
   115  	// Embed ReaderAt for ReadAt method.
   116  	// Do not embed SectionReader directly
   117  	// to avoid having Read and Seek.
   118  	// If a client wants Read and Seek it must use
   119  	// Open() to avoid fighting over the seek offset
   120  	// with other clients.
   121  	io.ReaderAt
   122  	sr *io.SectionReader
   123  }
   124  
   125  // Data reads and returns the contents of the Mach-O section.
   126  func (s *Section) Data() ([]byte, error) {
   127  	dat := make([]byte, s.sr.Size())
   128  	n, err := s.sr.ReadAt(dat, 0)
   129  	if n == len(dat) {
   130  		err = nil
   131  	}
   132  	return dat[0:n], err
   133  }
   134  
   135  // Open returns a new ReadSeeker reading the Mach-O section.
   136  func (s *Section) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) }
   137  
   138  // A Dylib represents a Mach-O load dynamic library command.
   139  type Dylib struct {
   140  	LoadBytes
   141  	Name           string
   142  	Time           uint32
   143  	CurrentVersion uint32
   144  	CompatVersion  uint32
   145  }
   146  
   147  // A Symtab represents a Mach-O symbol table command.
   148  type Symtab struct {
   149  	LoadBytes
   150  	SymtabCmd
   151  	Syms []Symbol
   152  }
   153  
   154  // A Dysymtab represents a Mach-O dynamic symbol table command.
   155  type Dysymtab struct {
   156  	LoadBytes
   157  	DysymtabCmd
   158  	IndirectSyms []uint32 // indices into Symtab.Syms
   159  }
   160  
   161  // A Rpath represents a Mach-O rpath command.
   162  type Rpath struct {
   163  	LoadBytes
   164  	Path string
   165  }
   166  
   167  // A Symbol is a Mach-O 32-bit or 64-bit symbol table entry.
   168  type Symbol struct {
   169  	Name  string
   170  	Type  uint8
   171  	Sect  uint8
   172  	Desc  uint16
   173  	Value uint64
   174  }
   175  
   176  /*
   177   * Mach-O reader
   178   */
   179  
   180  // FormatError is returned by some operations if the data does
   181  // not have the correct format for an object file.
   182  type FormatError struct {
   183  	off int64
   184  	msg string
   185  	val interface{}
   186  }
   187  
   188  func (e *FormatError) Error() string {
   189  	msg := e.msg
   190  	if e.val != nil {
   191  		msg += fmt.Sprintf(" '%v'", e.val)
   192  	}
   193  	msg += fmt.Sprintf(" in record at byte %#x", e.off)
   194  	return msg
   195  }
   196  
   197  // Open opens the named file using os.Open and prepares it for use as a Mach-O binary.
   198  func Open(name string) (*File, error) {
   199  	f, err := os.Open(name)
   200  	if err != nil {
   201  		return nil, err
   202  	}
   203  	ff, err := NewFile(f)
   204  	if err != nil {
   205  		f.Close()
   206  		return nil, err
   207  	}
   208  	ff.closer = f
   209  	return ff, nil
   210  }
   211  
   212  // Close closes the File.
   213  // If the File was created using NewFile directly instead of Open,
   214  // Close has no effect.
   215  func (f *File) Close() error {
   216  	var err error
   217  	if f.closer != nil {
   218  		err = f.closer.Close()
   219  		f.closer = nil
   220  	}
   221  	return err
   222  }
   223  
   224  // NewFile creates a new File for accessing a Mach-O binary in an underlying reader.
   225  // The Mach-O binary is expected to start at position 0 in the ReaderAt.
   226  func NewFile(r io.ReaderAt) (*File, error) {
   227  	f := new(File)
   228  	sr := io.NewSectionReader(r, 0, 1<<63-1)
   229  
   230  	// Read and decode Mach magic to determine byte order, size.
   231  	// Magic32 and Magic64 differ only in the bottom bit.
   232  	var ident [4]byte
   233  	if _, err := r.ReadAt(ident[0:], 0); err != nil {
   234  		return nil, err
   235  	}
   236  	be := binary.BigEndian.Uint32(ident[0:])
   237  	le := binary.LittleEndian.Uint32(ident[0:])
   238  	switch Magic32 &^ 1 {
   239  	case be &^ 1:
   240  		f.ByteOrder = binary.BigEndian
   241  		f.Magic = be
   242  	case le &^ 1:
   243  		f.ByteOrder = binary.LittleEndian
   244  		f.Magic = le
   245  	default:
   246  		return nil, &FormatError{0, "invalid magic number", nil}
   247  	}
   248  
   249  	// Read entire file header.
   250  	if err := binary.Read(sr, f.ByteOrder, &f.FileHeader); err != nil {
   251  		return nil, err
   252  	}
   253  
   254  	// Then load commands.
   255  	offset := int64(fileHeaderSize32)
   256  	if f.Magic == Magic64 {
   257  		offset = fileHeaderSize64
   258  	}
   259  	dat := make([]byte, f.Cmdsz)
   260  	if _, err := r.ReadAt(dat, offset); err != nil {
   261  		return nil, err
   262  	}
   263  	f.Loads = make([]Load, f.Ncmd)
   264  	bo := f.ByteOrder
   265  	for i := range f.Loads {
   266  		// Each load command begins with uint32 command and length.
   267  		if len(dat) < 8 {
   268  			return nil, &FormatError{offset, "command block too small", nil}
   269  		}
   270  		cmd, siz := LoadCmd(bo.Uint32(dat[0:4])), bo.Uint32(dat[4:8])
   271  		if siz < 8 || siz > uint32(len(dat)) {
   272  			return nil, &FormatError{offset, "invalid command block size", nil}
   273  		}
   274  		var cmddat []byte
   275  		cmddat, dat = dat[0:siz], dat[siz:]
   276  		offset += int64(siz)
   277  		var s *Segment
   278  		switch cmd {
   279  		default:
   280  			f.Loads[i] = LoadBytes(cmddat)
   281  
   282  		case LoadCmdRpath:
   283  			var hdr RpathCmd
   284  			b := bytes.NewReader(cmddat)
   285  			if err := binary.Read(b, bo, &hdr); err != nil {
   286  				return nil, err
   287  			}
   288  			l := new(Rpath)
   289  			if hdr.Path >= uint32(len(cmddat)) {
   290  				return nil, &FormatError{offset, "invalid path in rpath command", hdr.Path}
   291  			}
   292  			l.Path = cstring(cmddat[hdr.Path:])
   293  			l.LoadBytes = LoadBytes(cmddat)
   294  			f.Loads[i] = l
   295  
   296  		case LoadCmdDylib:
   297  			var hdr DylibCmd
   298  			b := bytes.NewReader(cmddat)
   299  			if err := binary.Read(b, bo, &hdr); err != nil {
   300  				return nil, err
   301  			}
   302  			l := new(Dylib)
   303  			if hdr.Name >= uint32(len(cmddat)) {
   304  				return nil, &FormatError{offset, "invalid name in dynamic library command", hdr.Name}
   305  			}
   306  			l.Name = cstring(cmddat[hdr.Name:])
   307  			l.Time = hdr.Time
   308  			l.CurrentVersion = hdr.CurrentVersion
   309  			l.CompatVersion = hdr.CompatVersion
   310  			l.LoadBytes = LoadBytes(cmddat)
   311  			f.Loads[i] = l
   312  
   313  		case LoadCmdSymtab:
   314  			var hdr SymtabCmd
   315  			b := bytes.NewReader(cmddat)
   316  			if err := binary.Read(b, bo, &hdr); err != nil {
   317  				return nil, err
   318  			}
   319  			strtab := make([]byte, hdr.Strsize)
   320  			if _, err := r.ReadAt(strtab, int64(hdr.Stroff)); err != nil {
   321  				return nil, err
   322  			}
   323  			var symsz int
   324  			if f.Magic == Magic64 {
   325  				symsz = 16
   326  			} else {
   327  				symsz = 12
   328  			}
   329  			symdat := make([]byte, int(hdr.Nsyms)*symsz)
   330  			if _, err := r.ReadAt(symdat, int64(hdr.Symoff)); err != nil {
   331  				return nil, err
   332  			}
   333  			st, err := f.parseSymtab(symdat, strtab, cmddat, &hdr, offset)
   334  			if err != nil {
   335  				return nil, err
   336  			}
   337  			f.Loads[i] = st
   338  			f.Symtab = st
   339  
   340  		case LoadCmdDysymtab:
   341  			var hdr DysymtabCmd
   342  			b := bytes.NewReader(cmddat)
   343  			if err := binary.Read(b, bo, &hdr); err != nil {
   344  				return nil, err
   345  			}
   346  			dat := make([]byte, hdr.Nindirectsyms*4)
   347  			if _, err := r.ReadAt(dat, int64(hdr.Indirectsymoff)); err != nil {
   348  				return nil, err
   349  			}
   350  			x := make([]uint32, hdr.Nindirectsyms)
   351  			if err := binary.Read(bytes.NewReader(dat), bo, x); err != nil {
   352  				return nil, err
   353  			}
   354  			st := new(Dysymtab)
   355  			st.LoadBytes = LoadBytes(cmddat)
   356  			st.DysymtabCmd = hdr
   357  			st.IndirectSyms = x
   358  			f.Loads[i] = st
   359  			f.Dysymtab = st
   360  
   361  		case LoadCmdSegment:
   362  			var seg32 Segment32
   363  			b := bytes.NewReader(cmddat)
   364  			if err := binary.Read(b, bo, &seg32); err != nil {
   365  				return nil, err
   366  			}
   367  			s = new(Segment)
   368  			s.LoadBytes = cmddat
   369  			s.Cmd = cmd
   370  			s.Len = siz
   371  			s.Name = cstring(seg32.Name[0:])
   372  			s.Addr = uint64(seg32.Addr)
   373  			s.Memsz = uint64(seg32.Memsz)
   374  			s.Offset = uint64(seg32.Offset)
   375  			s.Filesz = uint64(seg32.Filesz)
   376  			s.Maxprot = seg32.Maxprot
   377  			s.Prot = seg32.Prot
   378  			s.Nsect = seg32.Nsect
   379  			s.Flag = seg32.Flag
   380  			f.Loads[i] = s
   381  			for i := 0; i < int(s.Nsect); i++ {
   382  				var sh32 Section32
   383  				if err := binary.Read(b, bo, &sh32); err != nil {
   384  					return nil, err
   385  				}
   386  				sh := new(Section)
   387  				sh.Name = cstring(sh32.Name[0:])
   388  				sh.Seg = cstring(sh32.Seg[0:])
   389  				sh.Addr = uint64(sh32.Addr)
   390  				sh.Size = uint64(sh32.Size)
   391  				sh.Offset = sh32.Offset
   392  				sh.Align = sh32.Align
   393  				sh.Reloff = sh32.Reloff
   394  				sh.Nreloc = sh32.Nreloc
   395  				sh.Flags = sh32.Flags
   396  				if err := f.pushSection(sh, r); err != nil {
   397  					return nil, err
   398  				}
   399  			}
   400  
   401  		case LoadCmdSegment64:
   402  			var seg64 Segment64
   403  			b := bytes.NewReader(cmddat)
   404  			if err := binary.Read(b, bo, &seg64); err != nil {
   405  				return nil, err
   406  			}
   407  			s = new(Segment)
   408  			s.LoadBytes = cmddat
   409  			s.Cmd = cmd
   410  			s.Len = siz
   411  			s.Name = cstring(seg64.Name[0:])
   412  			s.Addr = seg64.Addr
   413  			s.Memsz = seg64.Memsz
   414  			s.Offset = seg64.Offset
   415  			s.Filesz = seg64.Filesz
   416  			s.Maxprot = seg64.Maxprot
   417  			s.Prot = seg64.Prot
   418  			s.Nsect = seg64.Nsect
   419  			s.Flag = seg64.Flag
   420  			f.Loads[i] = s
   421  			for i := 0; i < int(s.Nsect); i++ {
   422  				var sh64 Section64
   423  				if err := binary.Read(b, bo, &sh64); err != nil {
   424  					return nil, err
   425  				}
   426  				sh := new(Section)
   427  				sh.Name = cstring(sh64.Name[0:])
   428  				sh.Seg = cstring(sh64.Seg[0:])
   429  				sh.Addr = sh64.Addr
   430  				sh.Size = sh64.Size
   431  				sh.Offset = sh64.Offset
   432  				sh.Align = sh64.Align
   433  				sh.Reloff = sh64.Reloff
   434  				sh.Nreloc = sh64.Nreloc
   435  				sh.Flags = sh64.Flags
   436  				if err := f.pushSection(sh, r); err != nil {
   437  					return nil, err
   438  				}
   439  			}
   440  		}
   441  		if s != nil {
   442  			s.sr = io.NewSectionReader(r, int64(s.Offset), int64(s.Filesz))
   443  			s.ReaderAt = s.sr
   444  		}
   445  	}
   446  	return f, nil
   447  }
   448  
   449  func (f *File) parseSymtab(symdat, strtab, cmddat []byte, hdr *SymtabCmd, offset int64) (*Symtab, error) {
   450  	bo := f.ByteOrder
   451  	symtab := make([]Symbol, hdr.Nsyms)
   452  	b := bytes.NewReader(symdat)
   453  	for i := range symtab {
   454  		var n Nlist64
   455  		if f.Magic == Magic64 {
   456  			if err := binary.Read(b, bo, &n); err != nil {
   457  				return nil, err
   458  			}
   459  		} else {
   460  			var n32 Nlist32
   461  			if err := binary.Read(b, bo, &n32); err != nil {
   462  				return nil, err
   463  			}
   464  			n.Name = n32.Name
   465  			n.Type = n32.Type
   466  			n.Sect = n32.Sect
   467  			n.Desc = n32.Desc
   468  			n.Value = uint64(n32.Value)
   469  		}
   470  		sym := &symtab[i]
   471  		if n.Name >= uint32(len(strtab)) {
   472  			return nil, &FormatError{offset, "invalid name in symbol table", n.Name}
   473  		}
   474  		sym.Name = cstring(strtab[n.Name:])
   475  		sym.Type = n.Type
   476  		sym.Sect = n.Sect
   477  		sym.Desc = n.Desc
   478  		sym.Value = n.Value
   479  	}
   480  	st := new(Symtab)
   481  	st.LoadBytes = LoadBytes(cmddat)
   482  	st.Syms = symtab
   483  	return st, nil
   484  }
   485  
   486  type relocInfo struct {
   487  	Addr   uint32
   488  	Symnum uint32
   489  }
   490  
   491  func (f *File) pushSection(sh *Section, r io.ReaderAt) error {
   492  	f.Sections = append(f.Sections, sh)
   493  	sh.sr = io.NewSectionReader(r, int64(sh.Offset), int64(sh.Size))
   494  	sh.ReaderAt = sh.sr
   495  
   496  	if sh.Nreloc > 0 {
   497  		reldat := make([]byte, int(sh.Nreloc)*8)
   498  		if _, err := r.ReadAt(reldat, int64(sh.Reloff)); err != nil {
   499  			return err
   500  		}
   501  		b := bytes.NewReader(reldat)
   502  
   503  		bo := f.ByteOrder
   504  
   505  		sh.Relocs = make([]Reloc, sh.Nreloc)
   506  		for i := range sh.Relocs {
   507  			rel := &sh.Relocs[i]
   508  
   509  			var ri relocInfo
   510  			if err := binary.Read(b, bo, &ri); err != nil {
   511  				return err
   512  			}
   513  
   514  			if ri.Addr&(1<<31) != 0 { // scattered
   515  				rel.Addr = ri.Addr & (1<<24 - 1)
   516  				rel.Type = uint8((ri.Addr >> 24) & (1<<4 - 1))
   517  				rel.Len = uint8((ri.Addr >> 28) & (1<<2 - 1))
   518  				rel.Pcrel = ri.Addr&(1<<30) != 0
   519  				rel.Value = ri.Symnum
   520  				rel.Scattered = true
   521  			} else {
   522  				switch bo {
   523  				case binary.LittleEndian:
   524  					rel.Addr = ri.Addr
   525  					rel.Value = ri.Symnum & (1<<24 - 1)
   526  					rel.Pcrel = ri.Symnum&(1<<24) != 0
   527  					rel.Len = uint8((ri.Symnum >> 25) & (1<<2 - 1))
   528  					rel.Extern = ri.Symnum&(1<<27) != 0
   529  					rel.Type = uint8((ri.Symnum >> 28) & (1<<4 - 1))
   530  				case binary.BigEndian:
   531  					rel.Addr = ri.Addr
   532  					rel.Value = ri.Symnum >> 8
   533  					rel.Pcrel = ri.Symnum&(1<<7) != 0
   534  					rel.Len = uint8((ri.Symnum >> 5) & (1<<2 - 1))
   535  					rel.Extern = ri.Symnum&(1<<4) != 0
   536  					rel.Type = uint8(ri.Symnum & (1<<4 - 1))
   537  				default:
   538  					panic("unreachable")
   539  				}
   540  			}
   541  		}
   542  	}
   543  
   544  	return nil
   545  }
   546  
   547  func cstring(b []byte) string {
   548  	var i int
   549  	for i = 0; i < len(b) && b[i] != 0; i++ {
   550  	}
   551  	return string(b[0:i])
   552  }
   553  
   554  // Segment returns the first Segment with the given name, or nil if no such segment exists.
   555  func (f *File) Segment(name string) *Segment {
   556  	for _, l := range f.Loads {
   557  		if s, ok := l.(*Segment); ok && s.Name == name {
   558  			return s
   559  		}
   560  	}
   561  	return nil
   562  }
   563  
   564  // Section returns the first section with the given name, or nil if no such
   565  // section exists.
   566  func (f *File) Section(name string) *Section {
   567  	for _, s := range f.Sections {
   568  		if s.Name == name {
   569  			return s
   570  		}
   571  	}
   572  	return nil
   573  }
   574  
   575  // DWARF returns the DWARF debug information for the Mach-O file.
   576  func (f *File) DWARF() (*dwarf.Data, error) {
   577  	// There are many other DWARF sections, but these
   578  	// are the ones the debug/dwarf package uses.
   579  	// Don't bother loading others.
   580  	var names = [...]string{"abbrev", "info", "line", "ranges", "str"}
   581  	var dat [len(names)][]byte
   582  	for i, name := range names {
   583  		name = "__debug_" + name
   584  		s := f.Section(name)
   585  		if s == nil {
   586  			continue
   587  		}
   588  		b, err := s.Data()
   589  		if err != nil && uint64(len(b)) < s.Size {
   590  			return nil, err
   591  		}
   592  		dat[i] = b
   593  	}
   594  
   595  	abbrev, info, line, ranges, str := dat[0], dat[1], dat[2], dat[3], dat[4]
   596  	return dwarf.New(abbrev, nil, nil, info, line, nil, ranges, str)
   597  }
   598  
   599  // ImportedSymbols returns the names of all symbols
   600  // referred to by the binary f that are expected to be
   601  // satisfied by other libraries at dynamic load time.
   602  func (f *File) ImportedSymbols() ([]string, error) {
   603  	if f.Dysymtab == nil || f.Symtab == nil {
   604  		return nil, &FormatError{0, "missing symbol table", nil}
   605  	}
   606  
   607  	st := f.Symtab
   608  	dt := f.Dysymtab
   609  	var all []string
   610  	for _, s := range st.Syms[dt.Iundefsym : dt.Iundefsym+dt.Nundefsym] {
   611  		all = append(all, s.Name)
   612  	}
   613  	return all, nil
   614  }
   615  
   616  // ImportedLibraries returns the paths of all libraries
   617  // referred to by the binary f that are expected to be
   618  // linked with the binary at dynamic link time.
   619  func (f *File) ImportedLibraries() ([]string, error) {
   620  	var all []string
   621  	for _, l := range f.Loads {
   622  		if lib, ok := l.(*Dylib); ok {
   623  			all = append(all, lib.Name)
   624  		}
   625  	}
   626  	return all, nil
   627  }
   628  

View as plain text