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