forked from googleapis/google-cloud-go
/
frame.go
309 lines (290 loc) · 12.1 KB
/
frame.go
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
300
301
302
303
304
305
306
307
308
309
// Copyright 2018 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// 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
}