...
Run Format

Source file src/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		"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	}
   525	

View as plain text