...

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

View as plain text