File: frame.go

package info (click to toggle)
golang-golang-x-debug 0.0~git20160621.0.fb50892-1
  • links: PTS, VCS
  • area: main
  • in suites: experimental
  • size: 844 kB
  • sloc: ansic: 82; asm: 46; makefile: 4
file content (299 lines) | stat: -rw-r--r-- 11,958 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
// Copyright 2014 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Mapping from PC to SP offset (called CFA - Canonical Frame Address - in DWARF).
// This value is the offset from the stack pointer to the virtual frame pointer
// (address of zeroth argument) at each PC value in the program.

package dwarf

import "fmt"

// http://www.dwarfstd.org/doc/DWARF4.pdf Section 6.4 page 126
// We implement only the CFA column of the table, not the location
// information about other registers. In other words, we implement
// only what we need to understand Go programs compiled by gc.

// PCToSPOffset returns the offset, at the specified PC, to add to the
// SP to reach the virtual frame pointer, which corresponds to the
// address of the zeroth argument of the function, the word on the
// stack immediately above the return PC.
func (d *Data) PCToSPOffset(pc uint64) (offset int64, err error) {
	if len(d.frame) == 0 {
		return 0, fmt.Errorf("PCToSPOffset: no frame table")
	}
	var m frameMachine
	// Assume the first info unit is the same as us. Extremely likely. TODO?
	if len(d.unit) == 0 {
		return 0, fmt.Errorf("PCToSPOffset: no info section")
	}
	buf := makeBuf(d, &d.unit[0], "frame", 0, d.frame)
	for len(buf.data) > 0 {
		offset, err := m.evalCompilationUnit(&buf, pc)
		if err != nil {
			return 0, err
		}
		return offset, nil
	}
	return 0, fmt.Errorf("PCToSPOffset: no frame defined for PC %#x", pc)
}

// Call Frame instructions. Figure 40, page 181.
// Structure is high two bits plus low 6 bits specified by + in comment.
// Some take one or two operands.
const (
	frameNop              = 0<<6 + 0x00
	frameAdvanceLoc       = 1<<6 + 0x00 // + delta
	frameOffset           = 2<<6 + 0x00 // + register op: ULEB128 offset
	frameRestore          = 3<<6 + 0x00 // + register
	frameSetLoc           = 0<<6 + 0x01 // op: address
	frameAdvanceLoc1      = 0<<6 + 0x02 // op: 1-byte delta
	frameAdvanceLoc2      = 0<<6 + 0x03 // op: 2-byte delta
	frameAdvanceLoc4      = 0<<6 + 0x04 // op: 4-byte delta
	frameOffsetExtended   = 0<<6 + 0x05 // ops: ULEB128 register ULEB128 offset
	frameRestoreExtended  = 0<<6 + 0x06 // op: ULEB128 register
	frameUndefined        = 0<<6 + 0x07 // op: ULEB128 register
	frameSameValue        = 0<<6 + 0x08 // op: ULEB128 register
	frameRegister         = 0<<6 + 0x09 // op: ULEB128 register ULEB128 register
	frameRememberState    = 0<<6 + 0x0a
	frameRestoreState     = 0<<6 + 0x0b
	frameDefCFA           = 0<<6 + 0x0c // op: ULEB128 register ULEB128 offset
	frameDefCFARegister   = 0<<6 + 0x0d // op: ULEB128 register
	frameDefCFAOffset     = 0<<6 + 0x0e // op: ULEB128 offset
	frameDefCFAExpression = 0<<6 + 0x0f // op: BLOCK
	frameExpression       = 0<<6 + 0x10 // op: ULEB128 register BLOCK
	frameOffsetExtendedSf = 0<<6 + 0x11 // op: ULEB128 register SLEB128 offset
	frameDefCFASf         = 0<<6 + 0x12 // op: ULEB128 register SLEB128 offset
	frameDefCFAOffsetSf   = 0<<6 + 0x13 // op: SLEB128 offset
	frameValOffset        = 0<<6 + 0x14 // op: ULEB128 ULEB128
	frameValOffsetSf      = 0<<6 + 0x15 // op: ULEB128 SLEB128
	frameValExpression    = 0<<6 + 0x16 // op: ULEB128 BLOCK
	frameLoUser           = 0<<6 + 0x1c
	frameHiUser           = 0<<6 + 0x3f
)

// frameMachine represents the PC/SP engine.
// Section 6.4, page 129.
type frameMachine struct {
	// Initial values from CIE.
	version               uint8  // Version number, "independent of DWARF version"
	augmentation          string // Augmentation; treated as unexpected for now. TODO.
	addressSize           uint8  // In DWARF v4 and above. Size of a target address.
	segmentSize           uint8  // In DWARF v4 and above. Size of a segment selector.
	codeAlignmentFactor   uint64 // Unit of code size in advance instructions.
	dataAlignmentFactor   int64  // Unit of data size in certain offset instructions.
	returnAddressRegister int    // Pseudo-register (actually data column) representing return address.
	returnRegisterOffset  int64  // Offset to saved PC from CFA in bytes.
	// CFA definition.
	cfaRegister int   // Which register represents the SP.
	cfaOffset   int64 // CFA offset value.
	// Running machine.
	location uint64
}

// evalCompilationUnit scans the frame data for one compilation unit to retrieve
// the offset information for the specified pc.
func (m *frameMachine) evalCompilationUnit(b *buf, pc uint64) (int64, error) {
	err := m.parseCIE(b)
	if err != nil {
		return 0, err
	}
	for {
		offset, found, err := m.scanFDE(b, pc)
		if err != nil {
			return 0, err
		}
		if found {
			return offset, nil
		}
	}
}

// parseCIE assumes the incoming buffer starts with a CIE block and parses it
// to initialize a frameMachine.
func (m *frameMachine) parseCIE(allBuf *buf) error {
	length := int(allBuf.uint32())
	if len(allBuf.data) < length {
		return fmt.Errorf("CIE parse error: too short")
	}
	// Create buffer for just this section.
	b := allBuf.slice(length)
	cie := b.uint32()
	if cie != 0xFFFFFFFF {
		return fmt.Errorf("CIE parse error: not CIE: %x", cie)
	}
	m.version = b.uint8()
	if m.version != 3 && m.version != 4 {
		return fmt.Errorf("CIE parse error: unsupported version %d", m.version)
	}
	m.augmentation = b.string()
	if len(m.augmentation) > 0 {
		return fmt.Errorf("CIE: can't handled augmentation string %q", m.augmentation)
	}
	if m.version >= 4 {
		m.addressSize = b.uint8()
		m.segmentSize = b.uint8()
	} else {
		// Unused. Gc generates version 3, so these values will not be
		// set, but they are also not used so it's OK.
	}
	m.codeAlignmentFactor = b.uint()
	m.dataAlignmentFactor = b.int()
	m.returnAddressRegister = int(b.uint())

	// Initial instructions. At least for Go, establishes SP register number
	// and initial value of CFA offset at start of function.
	_, err := m.run(&b, ^uint64(0))
	if err != nil {
		return err
	}

	// There's padding, but we can ignore it.
	return nil
}

// scanFDE assumes the incoming buffer starts with a FDE block and parses it
// to run a frameMachine and, if the PC is represented in its range, return
// the CFA offset for that PC. The boolean returned reports whether the
// PC is in range for this FDE.
func (m *frameMachine) scanFDE(allBuf *buf, pc uint64) (int64, bool, error) {
	length := int(allBuf.uint32())
	if len(allBuf.data) < length {
		return 0, false, fmt.Errorf("FDE parse error: too short")
	}
	if length <= 0 {
		if length == 0 {
			// EOF.
			return 0, false, fmt.Errorf("PC %#x not found in PC/SP table", pc)
		}
		return 0, false, fmt.Errorf("bad FDE length %d", length)
	}
	// Create buffer for just this section.
	b := allBuf.slice(length)
	cieOffset := b.uint32() // TODO assumes 32 bits.
	// Expect 0: first CIE in this segment. TODO.
	if cieOffset != 0 {
		return 0, false, fmt.Errorf("FDE parse error: bad CIE offset: %.2x", cieOffset)
	}
	// Initial location.
	m.location = b.addr()
	addressRange := b.addr()
	// If the PC is not in this function, there's no point in executing the instructions.
	if pc < m.location || m.location+addressRange <= pc {
		return 0, false, nil
	}
	// The PC appears in this FDE. Scan to find the location.
	offset, err := m.run(&b, pc)
	if err != nil {
		return 0, false, err
	}

	// There's padding, but we can ignore it.
	return offset, true, nil
}

// run executes the instructions in the buffer, which has been sliced to contain
// only the data for this block. When we run out of data, we return.
// Since we are only called when we know the PC is in this block, reaching
// EOF is not an error, it just means the final CFA definition matches the
// tail of the block that holds the PC.
// The return value is the CFA at the end of the block or the PC, whichever
// comes first.
func (m *frameMachine) run(b *buf, pc uint64) (int64, error) {
	// We run the machine at location == PC because if the PC is at the first
	// instruction of a block, the definition of its offset arrives as an
	// offset-defining operand after the PC is set to that location.
	for m.location <= pc && len(b.data) > 0 {
		op := b.uint8()
		// Ops with embedded operands
		switch op & 0xC0 {
		case frameAdvanceLoc: // (6.4.2.1)
			// delta in low bits
			m.location += uint64(op & 0x3F)
			continue
		case frameOffset: // (6.4.2.3)
			// Register in low bits; ULEB128 offset.
			// For Go binaries we only see this in the CIE for the return address register.
			if int(op&0x3F) != m.returnAddressRegister {
				return 0, fmt.Errorf("invalid frameOffset register R%d should be R%d", op&0x3f, m.returnAddressRegister)
			}
			m.returnRegisterOffset = int64(b.uint()) * m.dataAlignmentFactor
			continue
		case frameRestore: // (6.4.2.3)
			// register in low bits
			return 0, fmt.Errorf("unimplemented frameRestore(R%d)\n", op&0x3F)
		}

		// The remaining ops do not have embedded operands.

		switch op {
		// Row creation instructions (6.4.2.1)
		case frameNop:
		case frameSetLoc: // op: address
			return 0, fmt.Errorf("unimplemented setloc") // what size is operand?
		case frameAdvanceLoc1: // op: 1-byte delta
			m.location += uint64(b.uint8())
		case frameAdvanceLoc2: // op: 2-byte delta
			m.location += uint64(b.uint16())
		case frameAdvanceLoc4: // op: 4-byte delta
			m.location += uint64(b.uint32())

		// CFA definition instructions (6.4.2.2)
		case frameDefCFA: // op: ULEB128 register ULEB128 offset
			m.cfaRegister = int(b.int())
			m.cfaOffset = int64(b.uint())
		case frameDefCFASf: // op: ULEB128 register SLEB128 offset
			return 0, fmt.Errorf("unimplemented frameDefCFASf")
		case frameDefCFARegister: // op: ULEB128 register
			return 0, fmt.Errorf("unimplemented frameDefCFARegister")
		case frameDefCFAOffset: // op: ULEB128 offset
			return 0, fmt.Errorf("unimplemented frameDefCFAOffset")
		case frameDefCFAOffsetSf: // op: SLEB128 offset
			offset := b.int()
			m.cfaOffset = offset * m.dataAlignmentFactor
			// TODO: Verify we are using a factored offset.
		case frameDefCFAExpression: // op: BLOCK
			return 0, fmt.Errorf("unimplemented frameDefCFAExpression")

		// Register Rule instructions (6.4.2.3)
		case frameOffsetExtended: // ops: ULEB128 register ULEB128 offset
			// The same as frameOffset, but with the register specified in an operand.
			reg := b.uint()
			// For Go binaries we only see this in the CIE for the return address register.
			if reg != uint64(m.returnAddressRegister) {
				return 0, fmt.Errorf("invalid frameOffsetExtended: register R%d should be R%d", reg, m.returnAddressRegister)
			}
			m.returnRegisterOffset = int64(b.uint()) * m.dataAlignmentFactor
		case frameRestoreExtended: // op: ULEB128 register
			return 0, fmt.Errorf("unimplemented frameRestoreExtended")
		case frameUndefined: // op: ULEB128 register; unimplemented
			return 0, fmt.Errorf("unimplemented frameUndefined")
		case frameSameValue: // op: ULEB128 register
			return 0, fmt.Errorf("unimplemented frameSameValue")
		case frameRegister: // op: ULEB128 register ULEB128 register
			return 0, fmt.Errorf("unimplemented frameRegister")
		case frameRememberState:
			return 0, fmt.Errorf("unimplemented frameRememberState")
		case frameRestoreState:
			return 0, fmt.Errorf("unimplemented frameRestoreState")
		case frameExpression: // op: ULEB128 register BLOCK
			return 0, fmt.Errorf("unimplemented frameExpression")
		case frameOffsetExtendedSf: // op: ULEB128 register SLEB128 offset
			return 0, fmt.Errorf("unimplemented frameOffsetExtended_sf")
		case frameValOffset: // op: ULEB128 ULEB128
			return 0, fmt.Errorf("unimplemented frameValOffset")
		case frameValOffsetSf: // op: ULEB128 SLEB128
			return 0, fmt.Errorf("unimplemented frameValOffsetSf")
		case frameValExpression: // op: ULEB128 BLOCK
			return 0, fmt.Errorf("unimplemented frameValExpression")

		default:
			if frameLoUser <= op && op <= frameHiUser {
				return 0, fmt.Errorf("unknown user-defined frame op %#x", op)
			}
			return 0, fmt.Errorf("unknown frame op %#x", op)
		}
	}
	return m.cfaOffset, nil
}