1// Copyright 2019 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15// +build linux
16
17package server
18
19import (
20	"errors"
21	"fmt"
22
23	"cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf"
24)
25
26func (s *Server) functionStartAddress(name string) (uint64, error) {
27	entry, err := s.dwarfData.LookupFunction(name)
28	if err != nil {
29		return 0, err
30	}
31	addrAttr := entry.Val(dwarf.AttrLowpc)
32	if addrAttr == nil {
33		return 0, fmt.Errorf("symbol %q has no LowPC attribute", name)
34	}
35	addr, ok := addrAttr.(uint64)
36	if !ok {
37		return 0, fmt.Errorf("symbol %q has non-uint64 LowPC attribute", name)
38	}
39	return addr, nil
40}
41
42// evalLocation parses a DWARF location description encoded in v.  It works for
43// cases where the variable is stored at an offset from the Canonical Frame
44// Address.  The return value is this offset.
45// TODO: a more general location-description-parsing function.
46func evalLocation(v []uint8) (int64, error) {
47	// Some DWARF constants.
48	const (
49		opConsts       = 0x11
50		opPlus         = 0x22
51		opCallFrameCFA = 0x9C
52	)
53	if len(v) == 0 {
54		return 0, errors.New("empty location specifier")
55	}
56	if v[0] != opCallFrameCFA {
57		return 0, errors.New("unsupported location specifier")
58	}
59	if len(v) == 1 {
60		// The location description was just DW_OP_call_frame_cfa, so the location is exactly the CFA.
61		return 0, nil
62	}
63	if v[1] != opConsts {
64		return 0, errors.New("unsupported location specifier")
65	}
66	offset, v, err := sleb128(v[2:])
67	if err != nil {
68		return 0, err
69	}
70	if len(v) == 1 && v[0] == opPlus {
71		// The location description was DW_OP_call_frame_cfa, DW_OP_consts <offset>, DW_OP_plus.
72		// So return the offset.
73		return offset, nil
74	}
75	return 0, errors.New("unsupported location specifier")
76}
77
78func uleb128(v []uint8) (u uint64) {
79	var shift uint
80	for _, x := range v {
81		u |= (uint64(x) & 0x7F) << shift
82		shift += 7
83		if x&0x80 == 0 {
84			break
85		}
86	}
87	return u
88}
89
90// sleb128 parses a signed integer encoded with sleb128 at the start of v, and
91// returns the integer and the remainder of v.
92func sleb128(v []uint8) (s int64, rest []uint8, err error) {
93	var shift uint
94	var sign int64 = -1
95	var i int
96	var x uint8
97	for i, x = range v {
98		s |= (int64(x) & 0x7F) << shift
99		shift += 7
100		sign <<= 7
101		if x&0x80 == 0 {
102			if x&0x40 != 0 {
103				s |= sign
104			}
105			break
106		}
107	}
108	if i == len(v) {
109		return 0, nil, errors.New("truncated sleb128")
110	}
111	return s, v[i+1:], nil
112}
113