提交 38bfdaf4 编写于 作者: D Derek Parker

Implement initial support for stack unwinding.

Implement basic api for figuring out, given a current PC value, where
the function will return. Currently the API provides only a way to
determine the offset from SP (the Canonical Frame Address). It is left
up to the caller to grab the actual address from the traced program.
上级 1f171bea
无法预览此类型文件
...@@ -9,13 +9,13 @@ func sleepytime() { ...@@ -9,13 +9,13 @@ func sleepytime() {
time.Sleep(time.Millisecond) time.Sleep(time.Millisecond)
} }
func sayhi() { func helloworld() {
fmt.Println("Hello, World!") fmt.Println("Hello, World!")
} }
func main() { func main() {
for { for {
sleepytime() sleepytime()
sayhi() helloworld()
} }
} }
package frame
import "fmt"
// Represents a Common Information Entry in
// the Dwarf .debug_frame section.
type CommonInformationEntry struct {
Length uint32
CIE_id uint32
Version uint8
Augmentation string
CodeAlignmentFactor uint64
DataAlignmentFactor int64
ReturnAddressRegister byte
InitialInstructions []byte
}
type FrameDescriptionEntries []*FrameDescriptionEntry
// Represents a Frame Descriptor Entry in the
// Dwarf .debug_frame section.
type FrameDescriptionEntry struct {
Length uint32
CIE *CommonInformationEntry
AddressRange *addrange
Instructions []byte
}
type addrange struct {
begin, end uint64
}
func (r *addrange) Cover(addr uint64) bool {
if (addr - r.begin) < r.end {
return true
}
return false
}
func (fdes FrameDescriptionEntries) FindReturnAddressOffset(pc uint64) (uint64, error) {
for _, fde := range fdes {
if fde.AddressRange.Cover(pc) {
offset := unwind(fde, pc)
return offset, nil
}
}
return 0, fmt.Errorf("Could not find return address.")
}
package frame
// Operation opcodes
const (
DW_OP_addr = 0x03
DW_OP_const1s = 0x09
)
const (
DW_OP_const2u = 0x0a
DW_OP_const2s = 0x0b
DW_OP_const4u = iota
DW_OP_const4s
DW_OP_const8u
DW_OP_const8s
DW_OP_constu
DW_OP_consts
DW_OP_dup
DW_OP_drop
DW_OP_over
DW_OP_pick
DW_OP_swap
DW_OP_rot
DW_OP_xderef
DW_OP_abs
DW_OP_and
DW_OP_div
DW_OP_minus
DW_OP_mod
DW_OP_mul
DW_OP_neg
DW_OP_not
DW_OP_or
DW_OP_plus
DW_OP_plus_uconst
DW_OP_shl
DW_OP_shr
DW_OP_shra
DW_OP_xor
DW_OP_skip
DW_OP_bra
DW_OP_eq
DW_OP_ge
DW_OP_gt
DW_OP_le
DW_OP_lt
DW_OP_ne
)
const (
DW_OP_lit0 = 0x30
DW_OP_lit1 = 0x31
DW_OP_lit2 = iota
DW_OP_lit3
DW_OP_lit4
DW_OP_lit5
DW_OP_lit6
DW_OP_lit7
DW_OP_lit8
DW_OP_lit9
DW_OP_lit10
DW_OP_lit11
DW_OP_lit12
DW_OP_lit13
DW_OP_lit14
DW_OP_lit15
DW_OP_lit16
DW_OP_lit17
DW_OP_lit18
DW_OP_lit19
DW_OP_lit20
DW_OP_lit21
DW_OP_lit22
DW_OP_lit23
DW_OP_lit24
DW_OP_lit25
DW_OP_lit26
DW_OP_lit27
DW_OP_lit28
DW_OP_lit29
DW_OP_lit30
DW_OP_lit31
DW_OP_reg0
DW_OP_reg1
DW_OP_reg2
DW_OP_reg3
DW_OP_reg4
DW_OP_reg5
DW_OP_reg6
DW_OP_reg7
DW_OP_reg8
DW_OP_reg9
DW_OP_reg10
DW_OP_reg11
DW_OP_reg12
DW_OP_reg13
DW_OP_reg14
DW_OP_reg15
DW_OP_reg16
DW_OP_reg17
DW_OP_reg18
DW_OP_reg19
DW_OP_reg20
DW_OP_reg21
DW_OP_reg22
DW_OP_reg23
DW_OP_reg24
DW_OP_reg25
DW_OP_reg26
DW_OP_reg27
DW_OP_reg28
DW_OP_reg29
DW_OP_reg30
DW_OP_reg31
DW_OP_breg0
DW_OP_breg1
DW_OP_breg2
DW_OP_breg3
DW_OP_breg4
DW_OP_breg5
DW_OP_breg6
DW_OP_breg7
DW_OP_breg8
DW_OP_breg9
DW_OP_breg10
DW_OP_breg11
DW_OP_breg12
DW_OP_breg13
DW_OP_breg14
DW_OP_breg15
DW_OP_breg16
DW_OP_breg17
DW_OP_breg18
DW_OP_breg19
DW_OP_breg20
DW_OP_breg21
DW_OP_breg22
DW_OP_breg23
DW_OP_breg24
DW_OP_breg25
DW_OP_breg26
DW_OP_breg27
DW_OP_breg28
DW_OP_breg29
DW_OP_breg30
DW_OP_breg31
DW_OP_regx
DW_OP_fbreg
DW_OP_bregx
DW_OP_piece
DW_OP_deref_size
DW_OP_xderef_size
DW_OP_nop
DW_OP_push_object_address
DW_OP_call2
DW_OP_call4
DW_OP_call_ref
DW_OP_form_tls_address
DW_OP_call_frame_cfa
DW_OP_bit_piece
DW_OP_lo_user = 0xe0
DW_OP_hi_user = 0xff
)
...@@ -6,89 +6,31 @@ package frame ...@@ -6,89 +6,31 @@ package frame
import ( import (
"bytes" "bytes"
"encoding/binary" "encoding/binary"
"io"
"unicode/utf8" "unicode/utf8"
) )
type parsefunc func(*parseContext) (parsefunc, *parseContext) type parsefunc func(*parseContext) parsefunc
type parseContext struct { type parseContext struct {
Buf *bytes.Buffer Buf *bytes.Buffer
Entries CommonEntries Entries FrameDescriptionEntries
Common *CommonInformationEntry Common *CommonInformationEntry
Frame *FrameDescriptorEntry Frame *FrameDescriptionEntry
Length uint32 Length uint32
} }
type CommonEntries []*CommonInformationEntry
// Represents a Common Information Entry in
// the Dwarf .debug_frame section.
type CommonInformationEntry struct {
Length uint32
CIE_id uint32
Version uint8
Augmentation string
CodeAlignmentFactor uint64
DataAlignmentFactor int64
ReturnAddressRegister byte
InitialInstructions []byte
FrameDescriptorEntries []*FrameDescriptorEntry
}
// Represents a Frame Descriptor Entry in the
// Dwarf .debug_frame section.
type FrameDescriptorEntry struct {
Length uint32
CIE_pointer *CommonInformationEntry
InitialLocation uintptr
AddressRange uintptr
Instructions []byte
}
const (
DW_CFA_advance_loc = (0x1 << 6) // High 2 bits: 0x1, low 6: delta
DW_CFA_offset = (0x2 << 6) // High 2 bits: 0x2, low 6: register
DW_CFA_restore = (0x3 << 6) // High 2 bits: 0x3, low 6: register
DW_CFA_nop = 0x0 // No ops
DW_CFA_set_loc = 0x1 // op1: address
DW_CFA_advance_loc1 = iota // op1: 1-bytes delta
DW_CFA_advance_loc2 // op1: 2-byte delta
DW_CFA_advance_loc4 // op1: 4-byte delta
DW_CFA_offset_extended // op1: ULEB128 register, op2: ULEB128 offset
DW_CFA_restore_extended // op1: ULEB128 register
DW_CFA_undefined // op1: ULEB128 register
DW_CFA_same_value // op1: ULEB128 register
DW_CFA_register // op1: ULEB128 register, op2: ULEB128 register
DW_CFA_remember_state // No ops
DW_CFA_restore_state // No ops
DW_CFA_def_cfa // op1: ULEB128 register, op2: ULEB128 offset
DW_CFA_def_cfa_register // op1: ULEB128 register
DW_CFA_def_cfa_offset // op1: ULEB128 offset
DW_CFA_def_cfa_expression // op1: BLOCK
DW_CFA_expression // op1: ULEB128 register, op2: BLOCK
DW_CFA_offset_extended_sf // op1: ULEB128 register, op2: SLEB128 offset
DW_CFA_def_cfa_sf // op1: ULEB128 register, op2: SLEB128 offset
DW_CFA_def_cfa_offset_sf // op1: SLEB128 offset
DW_CFA_val_offset // op1: ULEB128, op2: ULEB128
DW_CFA_val_offset_sf // op1: ULEB128, op2: SLEB128
DW_CFA_val_expression // op1: ULEB128, op2: BLOCK
DW_CFA_lo_user = 0x1c // op1: BLOCK
DW_CFA_hi_user = 0x3f // op1: ULEB128 register, op2: BLOCK
)
// Parse takes in data (a byte slice) and returns a slice of // Parse takes in data (a byte slice) and returns a slice of
// CommonInformationEntry structures. Each CommonInformationEntry // CommonInformationEntry structures. Each CommonInformationEntry
// has a slice of FrameDescriptorEntry structures. // has a slice of FrameDescriptionEntry structures.
func Parse(data []byte) CommonEntries { func Parse(data []byte) FrameDescriptionEntries {
var ( var (
length uint32 buf = bytes.NewBuffer(data)
entries CommonEntries pctx = &parseContext{Buf: buf}
buf = bytes.NewBuffer(data)
pctx = &parseContext{Buf: buf, Entries: entries, Length: length}
) )
for fn := parseLength; buf.Len() != 0; { for fn := parseLength; buf.Len() != 0; {
fn, pctx = fn(pctx) fn = fn(pctx)
} }
return pctx.Entries return pctx.Entries
...@@ -159,100 +101,99 @@ func cieEntry(data []byte) bool { ...@@ -159,100 +101,99 @@ func cieEntry(data []byte) bool {
return bytes.Equal(data, []byte{0xff, 0xff, 0xff, 0xff}) return bytes.Equal(data, []byte{0xff, 0xff, 0xff, 0xff})
} }
func parseLength(ctx *parseContext) (parsefunc, *parseContext) { func parseLength(ctx *parseContext) parsefunc {
var fn parsefunc
binary.Read(ctx.Buf, binary.LittleEndian, &ctx.Length) binary.Read(ctx.Buf, binary.LittleEndian, &ctx.Length)
cieid := ctx.Buf.Next(4)
if cieEntry(ctx.Buf.Bytes()[0:4]) { if cieEntry(cieid) {
ctx.Common = &CommonInformationEntry{Length: ctx.Length} ctx.Common = &CommonInformationEntry{Length: ctx.Length}
ctx.Entries = append(ctx.Entries, ctx.Common) fn = parseVersion
return parseCIEID, ctx } else {
ctx.Frame = &FrameDescriptionEntry{Length: ctx.Length, CIE: ctx.Common, AddressRange: &addrange{}}
ctx.Entries = append(ctx.Entries, ctx.Frame)
fn = parseInitialLocation
} }
ctx.Frame = &FrameDescriptorEntry{Length: ctx.Length, CIE_pointer: ctx.Common} // Take off the length of the CIE id / CIE pointer.
ctx.Common.FrameDescriptorEntries = append(ctx.Common.FrameDescriptorEntries, ctx.Frame) ctx.Length -= 4
// We aren't reading the CIE pointer from this section so just move the cursor past it.
ctx.Buf.Next(4)
return parseInitialLocation, ctx return fn
} }
func parseInitialLocation(ctx *parseContext) (parsefunc, *parseContext) { func parseInitialLocation(ctx *parseContext) parsefunc {
binary.Read(ctx.Buf, binary.LittleEndian, &ctx.Frame.InitialLocation) binary.Read(ctx.Buf, binary.LittleEndian, &ctx.Frame.AddressRange.begin)
ctx.Length -= 4
return parseAddressRange, ctx ctx.Length -= 8
return parseAddressRange
} }
func parseAddressRange(ctx *parseContext) (parsefunc, *parseContext) { func parseAddressRange(ctx *parseContext) parsefunc {
binary.Read(ctx.Buf, binary.LittleEndian, &ctx.Frame.AddressRange) binary.Read(ctx.Buf, binary.LittleEndian, &ctx.Frame.AddressRange.end)
ctx.Length -= 4
ctx.Length -= 8
return parseFrameInstructions, ctx return parseFrameInstructions
} }
func parseFrameInstructions(ctx *parseContext) (parsefunc, *parseContext) { func parseFrameInstructions(ctx *parseContext) parsefunc {
// The rest of this entry consists of the instructions // The rest of this entry consists of the instructions
// so we can just grab all of the data from the buffer // so we can just grab all of the data from the buffer
// cursor to length. // cursor to length.
var buf = make([]byte, ctx.Length) var buf = make([]byte, ctx.Length)
binary.Read(ctx.Buf, binary.LittleEndian, &buf) io.ReadFull(ctx.Buf, buf)
ctx.Frame.Instructions = buf ctx.Frame.Instructions = buf
ctx.Length = 0 ctx.Length = 0
return parseLength, ctx return parseLength
}
func parseCIEID(ctx *parseContext) (parsefunc, *parseContext) {
binary.Read(ctx.Buf, binary.LittleEndian, &ctx.Common.CIE_id)
ctx.Length -= 4
return parseVersion, ctx
} }
func parseVersion(ctx *parseContext) (parsefunc, *parseContext) { func parseVersion(ctx *parseContext) parsefunc {
binary.Read(ctx.Buf, binary.LittleEndian, &ctx.Common.Version) binary.Read(ctx.Buf, binary.LittleEndian, &ctx.Common.Version)
ctx.Length -= 1 ctx.Length -= 1
return parseAugmentation, ctx return parseAugmentation
} }
func parseAugmentation(ctx *parseContext) (parsefunc, *parseContext) { func parseAugmentation(ctx *parseContext) parsefunc {
var str, c = parseString(ctx.Buf) var str, c = parseString(ctx.Buf)
ctx.Common.Augmentation = str ctx.Common.Augmentation = str
ctx.Length -= c ctx.Length -= c
return parseCodeAlignmentFactor, ctx return parseCodeAlignmentFactor
} }
func parseCodeAlignmentFactor(ctx *parseContext) (parsefunc, *parseContext) { func parseCodeAlignmentFactor(ctx *parseContext) parsefunc {
var caf, c = decodeULEB128(ctx.Buf) var caf, c = decodeULEB128(ctx.Buf)
ctx.Common.CodeAlignmentFactor = caf ctx.Common.CodeAlignmentFactor = caf
ctx.Length -= c ctx.Length -= c
return parseDataAlignmentFactor, ctx return parseDataAlignmentFactor
} }
func parseDataAlignmentFactor(ctx *parseContext) (parsefunc, *parseContext) { func parseDataAlignmentFactor(ctx *parseContext) parsefunc {
var daf, c = decodeSLEB128(ctx.Buf) var daf, c = decodeSLEB128(ctx.Buf)
ctx.Common.DataAlignmentFactor = daf ctx.Common.DataAlignmentFactor = daf
ctx.Length -= c ctx.Length -= c
return parseReturnAddressRegister, ctx return parseReturnAddressRegister
} }
func parseReturnAddressRegister(ctx *parseContext) (parsefunc, *parseContext) { func parseReturnAddressRegister(ctx *parseContext) parsefunc {
binary.Read(ctx.Buf, binary.LittleEndian, &ctx.Common.ReturnAddressRegister) reg, c := decodeULEB128(ctx.Buf)
ctx.Length -= 1 ctx.Common.ReturnAddressRegister = uint8(reg)
ctx.Length -= c
return parseInitialInstructions, ctx return parseInitialInstructions
} }
func parseInitialInstructions(ctx *parseContext) (parsefunc, *parseContext) { func parseInitialInstructions(ctx *parseContext) parsefunc {
// The rest of this entry consists of the instructions // The rest of this entry consists of the instructions
// so we can just grab all of the data from the buffer // so we can just grab all of the data from the buffer
// cursor to length. // cursor to length.
...@@ -262,7 +203,7 @@ func parseInitialInstructions(ctx *parseContext) (parsefunc, *parseContext) { ...@@ -262,7 +203,7 @@ func parseInitialInstructions(ctx *parseContext) (parsefunc, *parseContext) {
ctx.Common.InitialInstructions = buf ctx.Common.InitialInstructions = buf
ctx.Length = 0 ctx.Length = 0
return parseLength, ctx return parseLength
} }
func parseString(data *bytes.Buffer) (string, uint32) { func parseString(data *bytes.Buffer) (string, uint32) {
......
...@@ -2,36 +2,9 @@ package frame ...@@ -2,36 +2,9 @@ package frame
import ( import (
"bytes" "bytes"
"debug/elf"
"os"
"path/filepath"
"testing" "testing"
) )
func grabDebugFrameSection(fp string, t *testing.T) []byte {
p, err := filepath.Abs(fp)
if err != nil {
t.Fatal(err)
}
f, err := os.Open(p)
if err != nil {
t.Fatal(err)
}
ef, err := elf.NewFile(f)
if err != nil {
t.Fatal(err)
}
data, err := ef.Section(".debug_frame").Data()
if err != nil {
t.Fatal(err)
}
return data
}
func TestDecodeULEB128(t *testing.T) { func TestDecodeULEB128(t *testing.T) {
var leb128 = bytes.NewBuffer([]byte{0xE5, 0x8E, 0x26}) var leb128 = bytes.NewBuffer([]byte{0xE5, 0x8E, 0x26})
...@@ -39,6 +12,10 @@ func TestDecodeULEB128(t *testing.T) { ...@@ -39,6 +12,10 @@ func TestDecodeULEB128(t *testing.T) {
if n != 624485 { if n != 624485 {
t.Fatal("Number was not decoded properly, got: ", n, c) t.Fatal("Number was not decoded properly, got: ", n, c)
} }
if c != 3 {
t.Fatal("Count not returned correctly")
}
} }
func TestDecodeSLEB128(t *testing.T) { func TestDecodeSLEB128(t *testing.T) {
...@@ -60,19 +37,18 @@ func TestParseString(t *testing.T) { ...@@ -60,19 +37,18 @@ func TestParseString(t *testing.T) {
} }
func TestParse(t *testing.T) { func TestParse(t *testing.T) {
data := grabDebugFrameSection("../_fixtures/testprog", t) var (
ce := Parse(data)[0] data = grabDebugFrameSection("../_fixtures/testprog", t)
fe = Parse(data)[0]
ce = fe.CIE
)
if ce.Length != 16 { if ce.Length != 16 {
t.Fatal("Length was not parsed correctly") t.Error("Length was not parsed correctly, got ", ce.Length)
}
if ce.CIE_id != 0xffffffff {
t.Fatal("CIE id was not parsed correctly")
} }
if ce.Version != 0x3 { if ce.Version != 0x3 {
t.Fatalf("Version was not parsed correctly expected %#v got %#v, data was %#v", 0x3, ce.Version, data[0:40]) t.Fatalf("Version was not parsed correctly expected %#v got %#v", 0x3, ce.Version)
} }
if ce.Augmentation != "" { if ce.Augmentation != "" {
...@@ -87,17 +63,8 @@ func TestParse(t *testing.T) { ...@@ -87,17 +63,8 @@ func TestParse(t *testing.T) {
t.Fatalf("Data Alignment Factor was not parsed correctly got %#v", ce.DataAlignmentFactor) t.Fatalf("Data Alignment Factor was not parsed correctly got %#v", ce.DataAlignmentFactor)
} }
fe := ce.FrameDescriptorEntries[0]
if fe.Length != 32 { if fe.Length != 32 {
t.Fatal("Length was not parsed correctly") t.Fatal("Length was not parsed correctly, got ", fe.Length)
}
if fe.CIE_pointer != ce {
t.Fatal("Frame entry does not point to parent CIE")
} }
if fe.InitialLocation != 0x0 {
t.Fatalf("Initial location not parsed correctly, got %#v", fe.InitialLocation)
}
} }
package frame
import (
"bytes"
"encoding/binary"
"fmt"
)
type CurrentFrameAddress struct {
register uint64
offset int64
expression []byte
rule byte
}
type DWRule struct {
rule byte
offset int64
newreg uint64
expression []byte
}
type FrameContext struct {
loc uint64
address uint64
cfa CurrentFrameAddress
regs map[uint64]DWRule
initialRegs map[uint64]DWRule
prevRegs map[uint64]DWRule
buf *bytes.Buffer
cie *CommonInformationEntry
codeAlignment uint64
dataAlignment int64
}
// Instructions used to recreate the table from the .debug_frame data.
const (
DW_CFA_nop = 0x0 // No ops
DW_CFA_set_loc = 0x01 // op1: address
DW_CFA_advance_loc1 = iota // op1: 1-bytes delta
DW_CFA_advance_loc2 // op1: 2-byte delta
DW_CFA_advance_loc4 // op1: 4-byte delta
DW_CFA_offset_extended // op1: ULEB128 register, op2: ULEB128 offset
DW_CFA_restore_extended // op1: ULEB128 register
DW_CFA_undefined // op1: ULEB128 register
DW_CFA_same_value // op1: ULEB128 register
DW_CFA_register // op1: ULEB128 register, op2: ULEB128 register
DW_CFA_remember_state // No ops
DW_CFA_restore_state // No ops
DW_CFA_def_cfa // op1: ULEB128 register, op2: ULEB128 offset
DW_CFA_def_cfa_register // op1: ULEB128 register
DW_CFA_def_cfa_offset // op1: ULEB128 offset
DW_CFA_def_cfa_expression // op1: BLOCK
DW_CFA_expression // op1: ULEB128 register, op2: BLOCK
DW_CFA_offset_extended_sf // op1: ULEB128 register, op2: SLEB128 BLOCK
DW_CFA_def_cfa_sf // op1: ULEB128 register, op2: SLEB128 offset
DW_CFA_def_cfa_offset_sf // op1: SLEB128 offset
DW_CFA_val_offset // op1: ULEB128, op2: ULEB128
DW_CFA_val_offset_sf // op1: ULEB128, op2: SLEB128
DW_CFA_val_expression // op1: ULEB128, op2: BLOCK
DW_CFA_lo_user = 0x1c // op1: BLOCK
DW_CFA_hi_user = 0x3f // op1: ULEB128 register, op2: BLOCK
DW_CFA_advance_loc = (0x1 << 6) // High 2 bits: 0x1, low 6: delta
DW_CFA_offset = (0x2 << 6) // High 2 bits: 0x2, low 6: register
DW_CFA_restore = (0x3 << 6) // High 2 bits: 0x3, low 6: register
)
// Rules defined for register values.
const (
rule_undefined = iota
rule_sameval
rule_offset
rule_valoffset
rule_register
rule_expression
rule_valexpression
rule_architectural
)
const low_6_offset = 0x3f
type instruction func(frame *FrameContext)
// // Mapping from DWARF opcode to function.
var fnlookup = map[byte]instruction{
DW_CFA_advance_loc: advanceloc,
DW_CFA_offset: offset,
DW_CFA_restore: restore,
DW_CFA_set_loc: setloc,
DW_CFA_advance_loc1: advanceloc1,
DW_CFA_advance_loc2: advanceloc2,
DW_CFA_advance_loc4: advanceloc4,
DW_CFA_offset_extended: offsetextended,
DW_CFA_restore_extended: restoreextended,
DW_CFA_undefined: undefined,
DW_CFA_same_value: samevalue,
DW_CFA_register: register,
DW_CFA_remember_state: rememberstate,
DW_CFA_restore_state: restorestate,
DW_CFA_def_cfa: defcfa,
DW_CFA_def_cfa_register: defcfaregister,
DW_CFA_def_cfa_offset: defcfaoffset,
DW_CFA_def_cfa_expression: defcfaexpression,
DW_CFA_expression: expression,
DW_CFA_offset_extended_sf: offsetextendedsf,
DW_CFA_def_cfa_sf: defcfasf,
DW_CFA_def_cfa_offset_sf: defcfaoffsetsf,
DW_CFA_val_offset: valoffset,
DW_CFA_val_offset_sf: valoffsetsf,
DW_CFA_val_expression: valexpression,
DW_CFA_lo_user: louser,
DW_CFA_hi_user: hiuser,
}
// Unwind the stack to find the return address register.
func unwind(fde *FrameDescriptionEntry, pc uint64) uint64 {
frame := &FrameContext{
address: pc,
cie: fde.CIE,
loc: fde.AddressRange.begin,
regs: make(map[uint64]DWRule),
initialRegs: make(map[uint64]DWRule),
prevRegs: make(map[uint64]DWRule),
codeAlignment: fde.CIE.CodeAlignmentFactor,
dataAlignment: fde.CIE.DataAlignmentFactor,
buf: bytes.NewBuffer([]byte{}),
}
dwarfexec(frame, fde.CIE.InitialInstructions)
dwarfexec(frame, fde.Instructions)
return uint64(frame.cfa.offset)
}
// Execute dwarf instructions.
func dwarfexec(frame *FrameContext, instructions []byte) {
frame.buf.Reset()
frame.buf.Write(instructions)
// We only need to execute the instructions until
// ctx.loc > ctx.addess (which is the address we
// are currently at in the traced process).
for frame.loc <= frame.address && frame.buf.Len() > 0 {
execDwarfInstruction(frame)
}
}
func execDwarfInstruction(frame *FrameContext) {
instruction, err := frame.buf.ReadByte()
if err != nil {
panic("Could not read from instruction buffer")
}
if instruction == DW_CFA_nop {
return
}
fn := lookupFunc(instruction, frame.buf)
fn(frame)
}
func lookupFunc(instruction byte, buf *bytes.Buffer) instruction {
const high_2_bits = 0xc0
var restore bool
// Special case the 3 opcodes that have their argument encoded in the opcode itself.
switch instruction & high_2_bits {
case DW_CFA_advance_loc:
instruction = DW_CFA_advance_loc
restore = true
case DW_CFA_offset:
instruction = DW_CFA_offset
restore = true
case DW_CFA_restore:
instruction = DW_CFA_restore
restore = true
}
if restore {
// Restore the last byte as it actually contains the argument for the opcode.
err := buf.UnreadByte()
if err != nil {
panic("Could not unread byte")
}
}
fn, ok := fnlookup[instruction]
if !ok {
panic(fmt.Sprintf("Encountered an unexpected DWARF CFA opcode: %#v", instruction))
}
return fn
}
func advanceloc(frame *FrameContext) {
b, err := frame.buf.ReadByte()
if err != nil {
panic("Could not read byte")
}
delta := b & low_6_offset
frame.loc += uint64(delta) * frame.codeAlignment
}
func advanceloc1(frame *FrameContext) {
delta, err := frame.buf.ReadByte()
if err != nil {
panic("Could not read byte")
}
frame.loc += uint64(delta) * frame.codeAlignment
}
func advanceloc2(frame *FrameContext) {
var delta uint16
binary.Read(frame.buf, binary.BigEndian, &delta)
frame.loc += uint64(delta) * frame.codeAlignment
}
func advanceloc4(frame *FrameContext) {
var delta uint32
binary.Read(frame.buf, binary.BigEndian, &delta)
frame.loc += uint64(delta) * frame.codeAlignment
}
func offset(frame *FrameContext) {
b, err := frame.buf.ReadByte()
if err != nil {
panic(err)
}
var (
reg = b & low_6_offset
offset, _ = decodeULEB128(frame.buf)
)
frame.regs[uint64(reg)] = DWRule{offset: int64(offset) * frame.dataAlignment, rule: rule_offset}
}
func restore(frame *FrameContext) {
b, err := frame.buf.ReadByte()
if err != nil {
panic(err)
}
reg := uint64(b & low_6_offset)
oldrule, ok := frame.initialRegs[reg]
if ok {
frame.regs[reg] = DWRule{offset: oldrule.offset, rule: rule_offset}
} else {
frame.regs[reg] = DWRule{rule: rule_undefined}
}
}
func setloc(frame *FrameContext) {
var loc uint64
binary.Read(frame.buf, binary.BigEndian, &loc)
frame.loc = loc
}
func offsetextended(frame *FrameContext) {
var (
reg, _ = decodeULEB128(frame.buf)
offset, _ = decodeULEB128(frame.buf)
)
frame.regs[reg] = DWRule{offset: int64(offset), rule: rule_offset}
}
func undefined(frame *FrameContext) {
reg, _ := decodeULEB128(frame.buf)
frame.regs[reg] = DWRule{rule: rule_undefined}
}
func samevalue(frame *FrameContext) {
reg, _ := decodeULEB128(frame.buf)
frame.regs[reg] = DWRule{rule: rule_sameval}
}
func register(frame *FrameContext) {
reg1, _ := decodeULEB128(frame.buf)
reg2, _ := decodeULEB128(frame.buf)
frame.regs[reg1] = DWRule{newreg: reg2, rule: rule_register}
}
func rememberstate(frame *FrameContext) {
frame.prevRegs = frame.regs
}
func restorestate(frame *FrameContext) {
frame.regs = frame.prevRegs
}
func restoreextended(frame *FrameContext) {
reg, _ := decodeULEB128(frame.buf)
oldrule, ok := frame.initialRegs[reg]
if ok {
frame.regs[reg] = DWRule{offset: oldrule.offset, rule: rule_offset}
} else {
frame.regs[reg] = DWRule{rule: rule_undefined}
}
}
func defcfa(frame *FrameContext) {
reg, _ := decodeULEB128(frame.buf)
offset, _ := decodeULEB128(frame.buf)
frame.cfa.register = reg
frame.cfa.offset = int64(offset)
}
func defcfaregister(frame *FrameContext) {
reg, _ := decodeULEB128(frame.buf)
frame.cfa.register = reg
}
func defcfaoffset(frame *FrameContext) {
offset, _ := decodeULEB128(frame.buf)
frame.cfa.offset = int64(offset)
}
func defcfasf(frame *FrameContext) {
reg, _ := decodeULEB128(frame.buf)
offset, _ := decodeSLEB128(frame.buf)
frame.cfa.register = reg
frame.cfa.offset = offset * frame.dataAlignment
}
func defcfaoffsetsf(frame *FrameContext) {
offset, _ := decodeSLEB128(frame.buf)
offset *= frame.dataAlignment
frame.cfa.offset = offset
}
func defcfaexpression(frame *FrameContext) {
var (
l, _ = decodeULEB128(frame.buf)
expr = frame.buf.Next(int(l))
)
frame.cfa.expression = expr
frame.cfa.rule = rule_expression
}
func expression(frame *FrameContext) {
var (
reg, _ = decodeULEB128(frame.buf)
l, _ = decodeULEB128(frame.buf)
expr = frame.buf.Next(int(l))
)
frame.regs[reg] = DWRule{rule: rule_expression, expression: expr}
}
func offsetextendedsf(frame *FrameContext) {
var (
reg, _ = decodeULEB128(frame.buf)
offset, _ = decodeSLEB128(frame.buf)
)
frame.regs[reg] = DWRule{offset: offset * frame.dataAlignment, rule: rule_offset}
}
func valoffset(frame *FrameContext) {
var (
reg, _ = decodeULEB128(frame.buf)
offset, _ = decodeULEB128(frame.buf)
)
frame.regs[reg] = DWRule{offset: int64(offset), rule: rule_valoffset}
}
func valoffsetsf(frame *FrameContext) {
var (
reg, _ = decodeULEB128(frame.buf)
offset, _ = decodeSLEB128(frame.buf)
)
frame.regs[reg] = DWRule{offset: offset * frame.dataAlignment, rule: rule_valoffset}
}
func valexpression(frame *FrameContext) {
var (
reg, _ = decodeULEB128(frame.buf)
l, _ = decodeULEB128(frame.buf)
expr = frame.buf.Next(int(l))
)
frame.regs[reg] = DWRule{rule: rule_valexpression, expression: expr}
}
func louser(frame *FrameContext) {
frame.buf.Next(1)
}
func hiuser(frame *FrameContext) {
frame.buf.Next(1)
}
package frame
import (
"debug/elf"
"debug/gosym"
"encoding/binary"
"os"
"path/filepath"
"syscall"
"testing"
"github.com/derekparker/dbg/_helper"
"github.com/derekparker/dbg/proctl"
)
var testfile string
func init() {
testfile, _ = filepath.Abs("../_fixtures/testprog")
}
func parseGoSym(t *testing.T, exe *elf.File) *gosym.Table {
symdat, err := exe.Section(".gosymtab").Data()
if err != nil {
t.Fatal(err)
}
pclndat, err := exe.Section(".gopclntab").Data()
if err != nil {
t.Fatal(err)
}
pcln := gosym.NewLineTable(pclndat, exe.Section(".text").Addr)
tab, err := gosym.NewTable(symdat, pcln)
if err != nil {
t.Fatal(err)
}
return tab
}
func gosymData(t *testing.T) *gosym.Table {
f, err := os.Open(testfile)
if err != nil {
t.Fatal(err)
}
e, err := elf.NewFile(f)
if err != nil {
t.Fatal(err)
}
return parseGoSym(t, e)
}
func TestFindReturnAddress(t *testing.T) {
var (
dbframe = grabDebugFrameSection(testfile, t)
fdes = Parse(dbframe)
gsd = gosymData(t)
)
helper.WithTestProcess("testprog", t, func(p *proctl.DebuggedProcess) {
testsourcefile := testfile + ".go"
start, _, err := gsd.LineToPC(testsourcefile, 9)
if err != nil {
t.Fatal(err)
}
_, err = p.Break(uintptr(start))
if err != nil {
t.Fatal(err)
}
err = p.Continue()
if err != nil {
t.Fatal(err)
}
regs, err := p.Registers()
if err != nil {
t.Fatal(err)
}
end, _, err := gsd.LineToPC(testsourcefile, 19)
if err != nil {
t.Fatal(err)
}
ret, err := fdes.FindReturnAddressOffset(start)
if err != nil {
t.Fatal(err)
}
addr := regs.Rsp + ret
data := make([]byte, 8)
syscall.PtracePeekText(p.Pid, uintptr(addr), data)
addr = binary.LittleEndian.Uint64(data)
if addr != end {
t.Fatalf("return address not found correctly, expected %#v got %#v", end, ret)
}
})
}
package frame
import (
"debug/elf"
"os"
"path/filepath"
"testing"
)
func grabDebugFrameSection(fp string, t *testing.T) []byte {
p, err := filepath.Abs(fp)
if err != nil {
t.Fatal(err)
}
f, err := os.Open(p)
if err != nil {
t.Fatal(err)
}
ef, err := elf.NewFile(f)
if err != nil {
t.Fatal(err)
}
data, err := ef.Section(".debug_frame").Data()
if err != nil {
t.Fatal(err)
}
return data
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册