src/pkg/debug/macho/file.go - The Go Programming Language

Golang

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	}