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 Go intermediate object files and archives.
6
7package objfile
8
9import (
10	"cmd/internal/archive"
11	"cmd/internal/goobj"
12	"cmd/internal/objabi"
13	"cmd/internal/sys"
14	"debug/dwarf"
15	"debug/gosym"
16	"errors"
17	"fmt"
18	"io"
19	"os"
20)
21
22type goobjFile struct {
23	goobj *archive.GoObj
24	r     *goobj.Reader
25	f     *os.File
26	arch  *sys.Arch
27}
28
29func openGoFile(f *os.File) (*File, error) {
30	a, err := archive.Parse(f, false)
31	if err != nil {
32		return nil, err
33	}
34	entries := make([]*Entry, 0, len(a.Entries))
35L:
36	for _, e := range a.Entries {
37		switch e.Type {
38		case archive.EntryPkgDef:
39			continue
40		case archive.EntryGoObj:
41			o := e.Obj
42			b := make([]byte, o.Size)
43			_, err := f.ReadAt(b, o.Offset)
44			if err != nil {
45				return nil, err
46			}
47			r := goobj.NewReaderFromBytes(b, false)
48			var arch *sys.Arch
49			for _, a := range sys.Archs {
50				if a.Name == e.Obj.Arch {
51					arch = a
52					break
53				}
54			}
55			entries = append(entries, &Entry{
56				name: e.Name,
57				raw:  &goobjFile{e.Obj, r, f, arch},
58			})
59			continue
60		case archive.EntryNativeObj:
61			nr := io.NewSectionReader(f, e.Offset, e.Size)
62			for _, try := range openers {
63				if raw, err := try(nr); err == nil {
64					entries = append(entries, &Entry{
65						name: e.Name,
66						raw:  raw,
67					})
68					continue L
69				}
70			}
71		}
72		return nil, fmt.Errorf("open %s: unrecognized archive member %s", f.Name(), e.Name)
73	}
74	return &File{f, entries}, nil
75}
76
77func goobjName(name string, ver int) string {
78	if ver == 0 {
79		return name
80	}
81	return fmt.Sprintf("%s<%d>", name, ver)
82}
83
84type goobjReloc struct {
85	Off  int32
86	Size uint8
87	Type objabi.RelocType
88	Add  int64
89	Sym  string
90}
91
92func (r goobjReloc) String(insnOffset uint64) string {
93	delta := int64(r.Off) - int64(insnOffset)
94	s := fmt.Sprintf("[%d:%d]%s", delta, delta+int64(r.Size), r.Type)
95	if r.Sym != "" {
96		if r.Add != 0 {
97			return fmt.Sprintf("%s:%s+%d", s, r.Sym, r.Add)
98		}
99		return fmt.Sprintf("%s:%s", s, r.Sym)
100	}
101	if r.Add != 0 {
102		return fmt.Sprintf("%s:%d", s, r.Add)
103	}
104	return s
105}
106
107func (f *goobjFile) symbols() ([]Sym, error) {
108	r := f.r
109	var syms []Sym
110
111	// Name of referenced indexed symbols.
112	nrefName := r.NRefName()
113	refNames := make(map[goobj.SymRef]string, nrefName)
114	for i := 0; i < nrefName; i++ {
115		rn := r.RefName(i)
116		refNames[rn.Sym()] = rn.Name(r)
117	}
118
119	abiToVer := func(abi uint16) int {
120		var ver int
121		if abi == goobj.SymABIstatic {
122			// Static symbol
123			ver = 1
124		}
125		return ver
126	}
127
128	resolveSymRef := func(s goobj.SymRef) string {
129		var i uint32
130		switch p := s.PkgIdx; p {
131		case goobj.PkgIdxInvalid:
132			if s.SymIdx != 0 {
133				panic("bad sym ref")
134			}
135			return ""
136		case goobj.PkgIdxHashed64:
137			i = s.SymIdx + uint32(r.NSym())
138		case goobj.PkgIdxHashed:
139			i = s.SymIdx + uint32(r.NSym()+r.NHashed64def())
140		case goobj.PkgIdxNone:
141			i = s.SymIdx + uint32(r.NSym()+r.NHashed64def()+r.NHasheddef())
142		case goobj.PkgIdxBuiltin:
143			name, abi := goobj.BuiltinName(int(s.SymIdx))
144			return goobjName(name, abi)
145		case goobj.PkgIdxSelf:
146			i = s.SymIdx
147		default:
148			return refNames[s]
149		}
150		sym := r.Sym(i)
151		return goobjName(sym.Name(r), abiToVer(sym.ABI()))
152	}
153
154	// Defined symbols
155	ndef := uint32(r.NSym() + r.NHashed64def() + r.NHasheddef() + r.NNonpkgdef())
156	for i := uint32(0); i < ndef; i++ {
157		osym := r.Sym(i)
158		if osym.Name(r) == "" {
159			continue // not a real symbol
160		}
161		name := osym.Name(r)
162		ver := osym.ABI()
163		name = goobjName(name, abiToVer(ver))
164		typ := objabi.SymKind(osym.Type())
165		var code rune = '?'
166		switch typ {
167		case objabi.STEXT:
168			code = 'T'
169		case objabi.SRODATA:
170			code = 'R'
171		case objabi.SNOPTRDATA, objabi.SDATA:
172			code = 'D'
173		case objabi.SBSS, objabi.SNOPTRBSS, objabi.STLSBSS:
174			code = 'B'
175		}
176		if ver >= goobj.SymABIstatic {
177			code += 'a' - 'A'
178		}
179
180		sym := Sym{
181			Name: name,
182			Addr: uint64(r.DataOff(i)),
183			Size: int64(osym.Siz()),
184			Code: code,
185		}
186
187		relocs := r.Relocs(i)
188		sym.Relocs = make([]Reloc, len(relocs))
189		for j := range relocs {
190			rel := &relocs[j]
191			sym.Relocs[j] = Reloc{
192				Addr: uint64(r.DataOff(i)) + uint64(rel.Off()),
193				Size: uint64(rel.Siz()),
194				Stringer: goobjReloc{
195					Off:  rel.Off(),
196					Size: rel.Siz(),
197					Type: objabi.RelocType(rel.Type()),
198					Add:  rel.Add(),
199					Sym:  resolveSymRef(rel.Sym()),
200				},
201			}
202		}
203
204		syms = append(syms, sym)
205	}
206
207	// Referenced symbols
208	n := ndef + uint32(r.NNonpkgref())
209	for i := ndef; i < n; i++ {
210		osym := r.Sym(i)
211		sym := Sym{Name: osym.Name(r), Code: 'U'}
212		syms = append(syms, sym)
213	}
214	for i := 0; i < nrefName; i++ {
215		rn := r.RefName(i)
216		sym := Sym{Name: rn.Name(r), Code: 'U'}
217		syms = append(syms, sym)
218	}
219
220	return syms, nil
221}
222
223func (f *goobjFile) pcln() (textStart uint64, symtab, pclntab []byte, err error) {
224	// Should never be called. We implement Liner below, callers
225	// should use that instead.
226	return 0, nil, nil, fmt.Errorf("pcln not available in go object file")
227}
228
229// Find returns the file name, line, and function data for the given pc.
230// Returns "",0,nil if unknown.
231// This function implements the Liner interface in preference to pcln() above.
232func (f *goobjFile) PCToLine(pc uint64) (string, int, *gosym.Func) {
233	r := f.r
234	if f.arch == nil {
235		return "", 0, nil
236	}
237	getSymData := func(s goobj.SymRef) []byte {
238		if s.PkgIdx != goobj.PkgIdxHashed {
239			// We don't need the data for non-hashed symbols, yet.
240			panic("not supported")
241		}
242		i := uint32(s.SymIdx + uint32(r.NSym()+r.NHashed64def()))
243		return r.BytesAt(r.DataOff(i), r.DataSize(i))
244	}
245
246	ndef := uint32(r.NSym() + r.NHashed64def() + r.NHasheddef() + r.NNonpkgdef())
247	for i := uint32(0); i < ndef; i++ {
248		osym := r.Sym(i)
249		addr := uint64(r.DataOff(i))
250		if pc < addr || pc >= addr+uint64(osym.Siz()) {
251			continue
252		}
253		var pcfileSym, pclineSym goobj.SymRef
254		for _, a := range r.Auxs(i) {
255			switch a.Type() {
256			case goobj.AuxPcfile:
257				pcfileSym = a.Sym()
258			case goobj.AuxPcline:
259				pclineSym = a.Sym()
260			}
261		}
262		if pcfileSym.IsZero() || pclineSym.IsZero() {
263			continue
264		}
265		pcline := getSymData(pclineSym)
266		line := int(pcValue(pcline, pc-addr, f.arch))
267		pcfile := getSymData(pcfileSym)
268		fileID := pcValue(pcfile, pc-addr, f.arch)
269		fileName := r.File(int(fileID))
270		// Note: we provide only the name in the Func structure.
271		// We could provide more if needed.
272		return fileName, line, &gosym.Func{Sym: &gosym.Sym{Name: osym.Name(r)}}
273	}
274	return "", 0, nil
275}
276
277// pcValue looks up the given PC in a pc value table. target is the
278// offset of the pc from the entry point.
279func pcValue(tab []byte, target uint64, arch *sys.Arch) int32 {
280	val := int32(-1)
281	var pc uint64
282	for step(&tab, &pc, &val, pc == 0, arch) {
283		if target < pc {
284			return val
285		}
286	}
287	return -1
288}
289
290// step advances to the next pc, value pair in the encoded table.
291func step(p *[]byte, pc *uint64, val *int32, first bool, arch *sys.Arch) bool {
292	uvdelta := readvarint(p)
293	if uvdelta == 0 && !first {
294		return false
295	}
296	if uvdelta&1 != 0 {
297		uvdelta = ^(uvdelta >> 1)
298	} else {
299		uvdelta >>= 1
300	}
301	vdelta := int32(uvdelta)
302	pcdelta := readvarint(p) * uint32(arch.MinLC)
303	*pc += uint64(pcdelta)
304	*val += vdelta
305	return true
306}
307
308// readvarint reads, removes, and returns a varint from *p.
309func readvarint(p *[]byte) uint32 {
310	var v, shift uint32
311	s := *p
312	for shift = 0; ; shift += 7 {
313		b := s[0]
314		s = s[1:]
315		v |= (uint32(b) & 0x7F) << shift
316		if b&0x80 == 0 {
317			break
318		}
319	}
320	*p = s
321	return v
322}
323
324// We treat the whole object file as the text section.
325func (f *goobjFile) text() (textStart uint64, text []byte, err error) {
326	text = make([]byte, f.goobj.Size)
327	_, err = f.f.ReadAt(text, int64(f.goobj.Offset))
328	return
329}
330
331func (f *goobjFile) goarch() string {
332	return f.goobj.Arch
333}
334
335func (f *goobjFile) loadAddress() (uint64, error) {
336	return 0, fmt.Errorf("unknown load address")
337}
338
339func (f *goobjFile) dwarf() (*dwarf.Data, error) {
340	return nil, errors.New("no DWARF data in go object file")
341}
342