1// Copyright 2014 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 Plan 9 a.out executables.
6
7package objfile
8
9import (
10	"debug/dwarf"
11	"debug/plan9obj"
12	"errors"
13	"fmt"
14	"io"
15	"sort"
16)
17
18var validSymType = map[rune]bool{
19	'T': true,
20	't': true,
21	'D': true,
22	'd': true,
23	'B': true,
24	'b': true,
25}
26
27type plan9File struct {
28	plan9 *plan9obj.File
29}
30
31func openPlan9(r io.ReaderAt) (rawFile, error) {
32	f, err := plan9obj.NewFile(r)
33	if err != nil {
34		return nil, err
35	}
36	return &plan9File{f}, nil
37}
38
39func (f *plan9File) symbols() ([]Sym, error) {
40	plan9Syms, err := f.plan9.Symbols()
41	if err != nil {
42		return nil, err
43	}
44
45	// Build sorted list of addresses of all symbols.
46	// We infer the size of a symbol by looking at where the next symbol begins.
47	var addrs []uint64
48	for _, s := range plan9Syms {
49		if !validSymType[s.Type] {
50			continue
51		}
52		addrs = append(addrs, s.Value)
53	}
54	sort.Sort(uint64s(addrs))
55
56	var syms []Sym
57
58	for _, s := range plan9Syms {
59		if !validSymType[s.Type] {
60			continue
61		}
62		sym := Sym{Addr: s.Value, Name: s.Name, Code: s.Type}
63		i := sort.Search(len(addrs), func(x int) bool { return addrs[x] > s.Value })
64		if i < len(addrs) {
65			sym.Size = int64(addrs[i] - s.Value)
66		}
67		syms = append(syms, sym)
68	}
69
70	return syms, nil
71}
72
73func (f *plan9File) pcln() (textStart uint64, symtab, pclntab []byte, err error) {
74	textStart = f.plan9.LoadAddress + f.plan9.HdrSize
75	if pclntab, err = loadPlan9Table(f.plan9, "runtime.pclntab", "runtime.epclntab"); err != nil {
76		// We didn't find the symbols, so look for the names used in 1.3 and earlier.
77		// TODO: Remove code looking for the old symbols when we no longer care about 1.3.
78		var err2 error
79		if pclntab, err2 = loadPlan9Table(f.plan9, "pclntab", "epclntab"); err2 != nil {
80			return 0, nil, nil, err
81		}
82	}
83	if symtab, err = loadPlan9Table(f.plan9, "runtime.symtab", "runtime.esymtab"); err != nil {
84		// Same as above.
85		var err2 error
86		if symtab, err2 = loadPlan9Table(f.plan9, "symtab", "esymtab"); err2 != nil {
87			return 0, nil, nil, err
88		}
89	}
90	return textStart, symtab, pclntab, nil
91}
92
93func (f *plan9File) text() (textStart uint64, text []byte, err error) {
94	sect := f.plan9.Section("text")
95	if sect == nil {
96		return 0, nil, fmt.Errorf("text section not found")
97	}
98	textStart = f.plan9.LoadAddress + f.plan9.HdrSize
99	text, err = sect.Data()
100	return
101}
102
103func findPlan9Symbol(f *plan9obj.File, name string) (*plan9obj.Sym, error) {
104	syms, err := f.Symbols()
105	if err != nil {
106		return nil, err
107	}
108	for _, s := range syms {
109		if s.Name != name {
110			continue
111		}
112		return &s, nil
113	}
114	return nil, fmt.Errorf("no %s symbol found", name)
115}
116
117func loadPlan9Table(f *plan9obj.File, sname, ename string) ([]byte, error) {
118	ssym, err := findPlan9Symbol(f, sname)
119	if err != nil {
120		return nil, err
121	}
122	esym, err := findPlan9Symbol(f, ename)
123	if err != nil {
124		return nil, err
125	}
126	sect := f.Section("text")
127	if sect == nil {
128		return nil, err
129	}
130	data, err := sect.Data()
131	if err != nil {
132		return nil, err
133	}
134	textStart := f.LoadAddress + f.HdrSize
135	return data[ssym.Value-textStart : esym.Value-textStart], nil
136}
137
138func (f *plan9File) goarch() string {
139	switch f.plan9.Magic {
140	case plan9obj.Magic386:
141		return "386"
142	case plan9obj.MagicAMD64:
143		return "amd64"
144	case plan9obj.MagicARM:
145		return "arm"
146	}
147	return ""
148}
149
150func (f *plan9File) loadAddress() (uint64, error) {
151	return 0, fmt.Errorf("unknown load address")
152}
153
154func (f *plan9File) dwarf() (*dwarf.Data, error) {
155	return nil, errors.New("no DWARF data in Plan 9 file")
156}
157