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
}
|