Index: debug/dwarf/frame.go |
=================================================================== |
new file mode 100644 |
--- /dev/null |
+++ b/debug/dwarf/frame.go |
@@ -0,0 +1,293 @@ |
+// 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 not found in PC/SP table") |
+ } |
+ 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 |
+ return 0, fmt.Errorf("unimplemented frameOffsetExtended") |
+ 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 |
+} |