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