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
7package objfile
8
9import (
10	"debug/dwarf"
11	"debug/macho"
12	"fmt"
13	"io"
14	"sort"
15)
16
17const stabTypeMask = 0xe0
18
19type machoFile struct {
20	macho *macho.File
21}
22
23func openMacho(r io.ReaderAt) (rawFile, error) {
24	f, err := macho.NewFile(r)
25	if err != nil {
26		return nil, err
27	}
28	return &machoFile{f}, nil
29}
30
31func (f *machoFile) symbols() ([]Sym, error) {
32	if f.macho.Symtab == nil {
33		return nil, nil
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", "__DATA_CONST":
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
81func (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
98func (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
108func (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.CpuArm64:
117		return "arm64"
118	case macho.CpuPpc64:
119		return "ppc64"
120	}
121	return ""
122}
123
124type uint64s []uint64
125
126func (x uint64s) Len() int           { return len(x) }
127func (x uint64s) Swap(i, j int)      { x[i], x[j] = x[j], x[i] }
128func (x uint64s) Less(i, j int) bool { return x[i] < x[j] }
129
130func (f *machoFile) loadAddress() (uint64, error) {
131	if seg := f.macho.Segment("__TEXT"); seg != nil {
132		return seg.Addr, nil
133	}
134	return 0, fmt.Errorf("unknown load address")
135}
136
137func (f *machoFile) dwarf() (*dwarf.Data, error) {
138	return f.macho.DWARF()
139}
140