Home | History | Annotate | Download | only in objfile
      1 // Copyright 2013 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 // Parsing of Mach-O executables (OS X).
      6 
      7 package objfile
      8 
      9 import (
     10 	"debug/macho"
     11 	"fmt"
     12 	"os"
     13 	"sort"
     14 )
     15 
     16 const stabTypeMask = 0xe0
     17 
     18 type machoFile struct {
     19 	macho *macho.File
     20 }
     21 
     22 func openMacho(r *os.File) (rawFile, error) {
     23 	f, err := macho.NewFile(r)
     24 	if err != nil {
     25 		return nil, err
     26 	}
     27 	return &machoFile{f}, nil
     28 }
     29 
     30 func (f *machoFile) symbols() ([]Sym, error) {
     31 	if f.macho.Symtab == nil {
     32 		return nil, fmt.Errorf("missing symbol table")
     33 	}
     34 
     35 	// Build sorted list of addresses of all symbols.
     36 	// We infer the size of a symbol by looking at where the next symbol begins.
     37 	var addrs []uint64
     38 	for _, s := range f.macho.Symtab.Syms {
     39 		// Skip stab debug info.
     40 		if s.Type&stabTypeMask == 0 {
     41 			addrs = append(addrs, s.Value)
     42 		}
     43 	}
     44 	sort.Sort(uint64s(addrs))
     45 
     46 	var syms []Sym
     47 	for _, s := range f.macho.Symtab.Syms {
     48 		if s.Type&stabTypeMask != 0 {
     49 			// Skip stab debug info.
     50 			continue
     51 		}
     52 		sym := Sym{Name: s.Name, Addr: s.Value, Code: '?'}
     53 		i := sort.Search(len(addrs), func(x int) bool { return addrs[x] > s.Value })
     54 		if i < len(addrs) {
     55 			sym.Size = int64(addrs[i] - s.Value)
     56 		}
     57 		if s.Sect == 0 {
     58 			sym.Code = 'U'
     59 		} else if int(s.Sect) <= len(f.macho.Sections) {
     60 			sect := f.macho.Sections[s.Sect-1]
     61 			switch sect.Seg {
     62 			case "__TEXT":
     63 				sym.Code = 'R'
     64 			case "__DATA":
     65 				sym.Code = 'D'
     66 			}
     67 			switch sect.Seg + " " + sect.Name {
     68 			case "__TEXT __text":
     69 				sym.Code = 'T'
     70 			case "__DATA __bss", "__DATA __noptrbss":
     71 				sym.Code = 'B'
     72 			}
     73 		}
     74 		syms = append(syms, sym)
     75 	}
     76 
     77 	return syms, nil
     78 }
     79 
     80 func (f *machoFile) pcln() (textStart uint64, symtab, pclntab []byte, err error) {
     81 	if sect := f.macho.Section("__text"); sect != nil {
     82 		textStart = sect.Addr
     83 	}
     84 	if sect := f.macho.Section("__gosymtab"); sect != nil {
     85 		if symtab, err = sect.Data(); err != nil {
     86 			return 0, nil, nil, err
     87 		}
     88 	}
     89 	if sect := f.macho.Section("__gopclntab"); sect != nil {
     90 		if pclntab, err = sect.Data(); err != nil {
     91 			return 0, nil, nil, err
     92 		}
     93 	}
     94 	return textStart, symtab, pclntab, nil
     95 }
     96 
     97 func (f *machoFile) text() (textStart uint64, text []byte, err error) {
     98 	sect := f.macho.Section("__text")
     99 	if sect == nil {
    100 		return 0, nil, fmt.Errorf("text section not found")
    101 	}
    102 	textStart = sect.Addr
    103 	text, err = sect.Data()
    104 	return
    105 }
    106 
    107 func (f *machoFile) goarch() string {
    108 	switch f.macho.Cpu {
    109 	case macho.Cpu386:
    110 		return "386"
    111 	case macho.CpuAmd64:
    112 		return "amd64"
    113 	case macho.CpuArm:
    114 		return "arm"
    115 	case macho.CpuPpc64:
    116 		return "ppc64"
    117 	}
    118 	return ""
    119 }
    120 
    121 type uint64s []uint64
    122 
    123 func (x uint64s) Len() int           { return len(x) }
    124 func (x uint64s) Swap(i, j int)      { x[i], x[j] = x[j], x[i] }
    125 func (x uint64s) Less(i, j int) bool { return x[i] < x[j] }
    126