2015-03-28 01:12:07 +00:00
|
|
|
package line
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"encoding/binary"
|
2017-01-09 23:21:54 +00:00
|
|
|
"errors"
|
2015-03-28 01:12:07 +00:00
|
|
|
"fmt"
|
2018-07-30 09:18:41 +00:00
|
|
|
"io"
|
2015-03-28 01:12:07 +00:00
|
|
|
|
2019-01-04 18:39:25 +00:00
|
|
|
"github.com/go-delve/delve/pkg/dwarf/util"
|
2015-03-28 01:12:07 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type Location struct {
|
|
|
|
File string
|
|
|
|
Line int
|
|
|
|
Address uint64
|
|
|
|
Delta int
|
|
|
|
}
|
|
|
|
|
|
|
|
type StateMachine struct {
|
2018-03-25 08:04:32 +00:00
|
|
|
dbl *DebugLineInfo
|
|
|
|
file string
|
|
|
|
line int
|
|
|
|
address uint64
|
|
|
|
column uint
|
|
|
|
isStmt bool
|
2019-08-27 21:27:15 +00:00
|
|
|
isa uint64 // instruction set architecture register (DWARFv4)
|
2018-03-25 08:04:32 +00:00
|
|
|
basicBlock bool
|
|
|
|
endSeq bool
|
|
|
|
lastDelta int
|
|
|
|
prologueEnd bool
|
|
|
|
epilogueBegin bool
|
2017-02-07 21:07:18 +00:00
|
|
|
// valid is true if the current value of the state machine is the address of
|
|
|
|
// an instruction (using the terminology used by DWARF spec the current
|
|
|
|
// value of the state machine should be appended to the matrix representing
|
|
|
|
// the compilation unit)
|
|
|
|
valid bool
|
2017-08-18 17:49:29 +00:00
|
|
|
|
|
|
|
started bool
|
|
|
|
|
|
|
|
buf *bytes.Buffer // remaining instructions
|
|
|
|
opcodes []opcodefn
|
|
|
|
|
2017-08-19 11:06:20 +00:00
|
|
|
definedFiles []*FileEntry // files defined with DW_LINE_define_file
|
|
|
|
|
2017-08-18 17:49:29 +00:00
|
|
|
lastAddress uint64
|
|
|
|
lastFile string
|
|
|
|
lastLine int
|
2020-03-10 16:34:40 +00:00
|
|
|
ptrSize int
|
2015-03-28 01:12:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type opcodefn func(*StateMachine, *bytes.Buffer)
|
|
|
|
|
|
|
|
// Special opcodes
|
|
|
|
const (
|
|
|
|
DW_LNS_copy = 1
|
|
|
|
DW_LNS_advance_pc = 2
|
|
|
|
DW_LNS_advance_line = 3
|
|
|
|
DW_LNS_set_file = 4
|
|
|
|
DW_LNS_set_column = 5
|
|
|
|
DW_LNS_negate_stmt = 6
|
|
|
|
DW_LNS_set_basic_block = 7
|
|
|
|
DW_LNS_const_add_pc = 8
|
|
|
|
DW_LNS_fixed_advance_pc = 9
|
2018-03-25 08:04:32 +00:00
|
|
|
DW_LNS_prologue_end = 10
|
|
|
|
DW_LNS_epilogue_begin = 11
|
2019-08-27 21:27:15 +00:00
|
|
|
DW_LNS_set_isa = 12
|
2015-03-28 01:12:07 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// Extended opcodes
|
|
|
|
const (
|
2020-04-09 20:57:44 +00:00
|
|
|
DW_LINE_end_sequence = 1
|
|
|
|
DW_LINE_set_address = 2
|
|
|
|
DW_LINE_define_file = 3
|
|
|
|
DW_LINE_set_discriminator = 4
|
2015-03-28 01:12:07 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
var standardopcodes = map[byte]opcodefn{
|
|
|
|
DW_LNS_copy: copyfn,
|
|
|
|
DW_LNS_advance_pc: advancepc,
|
|
|
|
DW_LNS_advance_line: advanceline,
|
|
|
|
DW_LNS_set_file: setfile,
|
|
|
|
DW_LNS_set_column: setcolumn,
|
|
|
|
DW_LNS_negate_stmt: negatestmt,
|
|
|
|
DW_LNS_set_basic_block: setbasicblock,
|
|
|
|
DW_LNS_const_add_pc: constaddpc,
|
|
|
|
DW_LNS_fixed_advance_pc: fixedadvancepc,
|
2018-03-25 08:04:32 +00:00
|
|
|
DW_LNS_prologue_end: prologueend,
|
|
|
|
DW_LNS_epilogue_begin: epiloguebegin,
|
2019-08-27 21:27:15 +00:00
|
|
|
DW_LNS_set_isa: setisa,
|
2015-03-28 01:12:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var extendedopcodes = map[byte]opcodefn{
|
2020-04-09 20:57:44 +00:00
|
|
|
DW_LINE_end_sequence: endsequence,
|
|
|
|
DW_LINE_set_address: setaddress,
|
|
|
|
DW_LINE_define_file: definefile,
|
|
|
|
DW_LINE_set_discriminator: setdiscriminator,
|
2015-03-28 01:12:07 +00:00
|
|
|
}
|
|
|
|
|
2020-03-10 16:34:40 +00:00
|
|
|
func newStateMachine(dbl *DebugLineInfo, instructions []byte, ptrSize int) *StateMachine {
|
2017-08-18 17:49:29 +00:00
|
|
|
opcodes := make([]opcodefn, len(standardopcodes)+1)
|
|
|
|
opcodes[0] = execExtendedOpcode
|
|
|
|
for op := range standardopcodes {
|
|
|
|
opcodes[op] = standardopcodes[op]
|
|
|
|
}
|
2021-06-22 11:37:46 +00:00
|
|
|
var file string
|
|
|
|
if len(dbl.FileNames) > 0 {
|
|
|
|
file = dbl.FileNames[0].Path
|
|
|
|
}
|
2020-03-10 16:34:40 +00:00
|
|
|
sm := &StateMachine{
|
|
|
|
dbl: dbl,
|
2021-06-22 11:37:46 +00:00
|
|
|
file: file,
|
2020-03-10 16:34:40 +00:00
|
|
|
line: 1,
|
|
|
|
buf: bytes.NewBuffer(instructions),
|
|
|
|
opcodes: opcodes,
|
|
|
|
isStmt: dbl.Prologue.InitialIsStmt == uint8(1),
|
|
|
|
address: dbl.staticBase,
|
|
|
|
lastAddress: ^uint64(0),
|
|
|
|
ptrSize: ptrSize,
|
|
|
|
}
|
2018-03-25 08:04:32 +00:00
|
|
|
return sm
|
2015-03-28 01:12:07 +00:00
|
|
|
}
|
|
|
|
|
2019-06-28 02:39:15 +00:00
|
|
|
// AllPCsForFileLines Adds all PCs for a given file and set (domain of map) of lines
|
|
|
|
// to the map value corresponding to each line.
|
|
|
|
func (lineInfo *DebugLineInfo) AllPCsForFileLines(f string, m map[int][]uint64) {
|
|
|
|
if lineInfo == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
|
|
|
lastAddr uint64
|
2020-03-10 16:34:40 +00:00
|
|
|
sm = newStateMachine(lineInfo, lineInfo.Instructions, lineInfo.ptrSize)
|
2019-06-28 02:39:15 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
for {
|
|
|
|
if err := sm.next(); err != nil {
|
|
|
|
if lineInfo.Logf != nil {
|
|
|
|
lineInfo.Logf("AllPCsForFileLine error: %v", err)
|
|
|
|
}
|
|
|
|
break
|
|
|
|
}
|
|
|
|
if sm.address != lastAddr && sm.isStmt && sm.valid && sm.file == f {
|
|
|
|
if pcs, ok := m[sm.line]; ok {
|
|
|
|
pcs = append(pcs, sm.address)
|
|
|
|
m[sm.line] = pcs
|
|
|
|
lastAddr = sm.address
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-28 19:07:42 +00:00
|
|
|
var ErrNoSource = errors.New("no source available")
|
2017-01-09 23:21:54 +00:00
|
|
|
|
2020-12-14 17:31:11 +00:00
|
|
|
// AllPCsBetween returns all PC addresses between begin and end (including both begin and end)
|
|
|
|
// that have the is_stmt flag set and do not belong to excludeFile:excludeLine.
|
2018-03-25 08:04:32 +00:00
|
|
|
func (lineInfo *DebugLineInfo) AllPCsBetween(begin, end uint64, excludeFile string, excludeLine int) ([]uint64, error) {
|
2017-01-09 23:21:54 +00:00
|
|
|
if lineInfo == nil {
|
2021-09-28 19:07:42 +00:00
|
|
|
return nil, ErrNoSource
|
2017-01-09 23:21:54 +00:00
|
|
|
}
|
2017-09-01 13:30:45 +00:00
|
|
|
|
2015-04-20 18:03:22 +00:00
|
|
|
var (
|
2015-10-22 17:07:24 +00:00
|
|
|
pcs []uint64
|
|
|
|
lastaddr uint64
|
2020-03-10 16:34:40 +00:00
|
|
|
sm = newStateMachine(lineInfo, lineInfo.Instructions, lineInfo.ptrSize)
|
2015-04-20 18:03:22 +00:00
|
|
|
)
|
|
|
|
|
2017-08-18 17:49:29 +00:00
|
|
|
for {
|
|
|
|
if err := sm.next(); err != nil {
|
2018-06-14 18:12:11 +00:00
|
|
|
if lineInfo.Logf != nil {
|
|
|
|
lineInfo.Logf("AllPCsBetween error: %v", err)
|
2018-03-25 08:04:32 +00:00
|
|
|
}
|
2017-08-18 17:49:29 +00:00
|
|
|
break
|
|
|
|
}
|
2017-02-07 21:07:18 +00:00
|
|
|
if !sm.valid {
|
|
|
|
continue
|
|
|
|
}
|
2019-10-07 16:54:32 +00:00
|
|
|
if (sm.address > end) && (end >= sm.lastAddress) {
|
2015-04-20 18:03:22 +00:00
|
|
|
break
|
|
|
|
}
|
2020-09-24 15:03:46 +00:00
|
|
|
if sm.address >= begin && sm.address <= end && sm.address > lastaddr && sm.isStmt && !sm.endSeq && ((sm.file != excludeFile) || (sm.line != excludeLine)) {
|
2015-10-22 17:07:24 +00:00
|
|
|
lastaddr = sm.address
|
2015-04-20 18:03:22 +00:00
|
|
|
pcs = append(pcs, sm.address)
|
|
|
|
}
|
|
|
|
}
|
2017-01-09 23:21:54 +00:00
|
|
|
return pcs, nil
|
2015-04-20 18:03:22 +00:00
|
|
|
}
|
|
|
|
|
2017-08-18 17:49:29 +00:00
|
|
|
// copy returns a copy of this state machine, running the returned state
|
|
|
|
// machine will not affect sm.
|
|
|
|
func (sm *StateMachine) copy() *StateMachine {
|
|
|
|
var r StateMachine
|
|
|
|
r = *sm
|
|
|
|
r.buf = bytes.NewBuffer(sm.buf.Bytes())
|
|
|
|
return &r
|
|
|
|
}
|
|
|
|
|
2018-03-25 08:04:32 +00:00
|
|
|
func (lineInfo *DebugLineInfo) stateMachineForEntry(basePC uint64) (sm *StateMachine) {
|
|
|
|
sm = lineInfo.stateMachineCache[basePC]
|
|
|
|
if sm == nil {
|
2020-03-10 16:34:40 +00:00
|
|
|
sm = newStateMachine(lineInfo, lineInfo.Instructions, lineInfo.ptrSize)
|
2018-03-25 08:04:32 +00:00
|
|
|
sm.PCToLine(basePC)
|
|
|
|
lineInfo.stateMachineCache[basePC] = sm
|
|
|
|
}
|
|
|
|
sm = sm.copy()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-09-01 13:30:45 +00:00
|
|
|
// PCToLine returns the filename and line number associated with pc.
|
|
|
|
// If pc isn't found inside lineInfo's table it will return the filename and
|
|
|
|
// line number associated with the closest PC address preceding pc.
|
2017-08-18 17:49:29 +00:00
|
|
|
// basePC will be used for caching, it's normally the entry point for the
|
|
|
|
// function containing pc.
|
|
|
|
func (lineInfo *DebugLineInfo) PCToLine(basePC, pc uint64) (string, int) {
|
2017-09-01 13:30:45 +00:00
|
|
|
if lineInfo == nil {
|
|
|
|
return "", 0
|
|
|
|
}
|
2017-08-18 17:49:29 +00:00
|
|
|
if basePC > pc {
|
|
|
|
panic(fmt.Errorf("basePC after pc %#x %#x", basePC, pc))
|
|
|
|
}
|
2017-09-01 13:30:45 +00:00
|
|
|
|
2019-11-01 19:41:06 +00:00
|
|
|
sm := lineInfo.stateMachineFor(basePC, pc)
|
|
|
|
|
|
|
|
file, line, _ := sm.PCToLine(pc)
|
|
|
|
return file, line
|
|
|
|
}
|
|
|
|
|
|
|
|
func (lineInfo *DebugLineInfo) stateMachineFor(basePC, pc uint64) *StateMachine {
|
2017-08-18 17:49:29 +00:00
|
|
|
var sm *StateMachine
|
|
|
|
if basePC == 0 {
|
2020-03-10 16:34:40 +00:00
|
|
|
sm = newStateMachine(lineInfo, lineInfo.Instructions, lineInfo.ptrSize)
|
2017-08-18 17:49:29 +00:00
|
|
|
} else {
|
|
|
|
// Try to use the last state machine that we used for this function, if
|
|
|
|
// there isn't one or it's already past pc try to clone the cached state
|
|
|
|
// machine stopped at the entry point of the function.
|
|
|
|
// As a last resort start from the start of the debug_line section.
|
|
|
|
sm = lineInfo.lastMachineCache[basePC]
|
2019-11-01 19:41:06 +00:00
|
|
|
if sm == nil || sm.lastAddress >= pc {
|
2018-03-25 08:04:32 +00:00
|
|
|
sm = lineInfo.stateMachineForEntry(basePC)
|
2017-08-18 17:49:29 +00:00
|
|
|
lineInfo.lastMachineCache[basePC] = sm
|
2017-09-01 13:30:45 +00:00
|
|
|
}
|
2017-08-18 17:49:29 +00:00
|
|
|
}
|
2019-11-01 19:41:06 +00:00
|
|
|
return sm
|
2017-08-18 17:49:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (sm *StateMachine) PCToLine(pc uint64) (string, int, bool) {
|
|
|
|
if !sm.started {
|
|
|
|
if err := sm.next(); err != nil {
|
2018-06-14 18:12:11 +00:00
|
|
|
if sm.dbl.Logf != nil {
|
|
|
|
sm.dbl.Logf("PCToLine error: %v", err)
|
2018-03-25 08:04:32 +00:00
|
|
|
}
|
2017-08-18 17:49:29 +00:00
|
|
|
return "", 0, false
|
2017-09-01 13:30:45 +00:00
|
|
|
}
|
2017-08-18 17:49:29 +00:00
|
|
|
}
|
2019-10-07 16:54:32 +00:00
|
|
|
if sm.lastAddress > pc && sm.lastAddress != ^uint64(0) {
|
2017-08-18 17:49:29 +00:00
|
|
|
return "", 0, false
|
|
|
|
}
|
|
|
|
for {
|
|
|
|
if sm.valid {
|
2019-10-07 16:54:32 +00:00
|
|
|
if (sm.address > pc) && (pc >= sm.lastAddress) {
|
2017-08-18 17:49:29 +00:00
|
|
|
return sm.lastFile, sm.lastLine, true
|
|
|
|
}
|
|
|
|
if sm.address == pc {
|
|
|
|
return sm.file, sm.line, true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if err := sm.next(); err != nil {
|
2018-06-14 18:12:11 +00:00
|
|
|
if sm.dbl.Logf != nil {
|
|
|
|
sm.dbl.Logf("PCToLine error: %v", err)
|
2018-03-25 08:04:32 +00:00
|
|
|
}
|
2017-08-18 17:49:29 +00:00
|
|
|
break
|
2017-09-01 13:30:45 +00:00
|
|
|
}
|
|
|
|
}
|
2017-11-28 14:42:50 +00:00
|
|
|
if sm.valid {
|
|
|
|
return sm.file, sm.line, true
|
|
|
|
}
|
2017-08-18 17:49:29 +00:00
|
|
|
return "", 0, false
|
2017-09-01 13:30:45 +00:00
|
|
|
}
|
|
|
|
|
2021-09-16 14:51:51 +00:00
|
|
|
// PCStmt is a PC address with its is_stmt flag
|
|
|
|
type PCStmt struct {
|
|
|
|
PC uint64
|
|
|
|
Stmt bool
|
|
|
|
}
|
|
|
|
|
|
|
|
// LineToPCs returns all PCs associated with filename:lineno
|
|
|
|
func (lineInfo *DebugLineInfo) LineToPCs(filename string, lineno int) []PCStmt {
|
2017-09-01 13:30:45 +00:00
|
|
|
if lineInfo == nil {
|
2021-09-16 14:51:51 +00:00
|
|
|
return nil
|
2017-09-01 13:30:45 +00:00
|
|
|
}
|
|
|
|
|
2020-03-10 16:34:40 +00:00
|
|
|
sm := newStateMachine(lineInfo, lineInfo.Instructions, lineInfo.ptrSize)
|
2017-09-01 13:30:45 +00:00
|
|
|
|
2021-09-16 14:51:51 +00:00
|
|
|
pcstmts := []PCStmt{}
|
2018-03-25 08:04:32 +00:00
|
|
|
|
2017-08-18 17:49:29 +00:00
|
|
|
for {
|
|
|
|
if err := sm.next(); err != nil {
|
2018-07-30 09:18:41 +00:00
|
|
|
if lineInfo.Logf != nil && err != io.EOF {
|
2021-09-16 14:51:51 +00:00
|
|
|
lineInfo.Logf("LineToPCs error: %v", err)
|
2018-03-25 08:04:32 +00:00
|
|
|
}
|
2017-08-18 17:49:29 +00:00
|
|
|
break
|
|
|
|
}
|
2018-07-23 15:54:50 +00:00
|
|
|
if sm.line == lineno && sm.file == filename && sm.valid {
|
2021-09-16 14:51:51 +00:00
|
|
|
pcstmts = append(pcstmts, PCStmt{sm.address, sm.isStmt})
|
2019-11-01 19:41:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-16 14:51:51 +00:00
|
|
|
return pcstmts
|
2019-11-01 19:41:06 +00:00
|
|
|
}
|
|
|
|
|
2018-03-25 08:04:32 +00:00
|
|
|
// PrologueEndPC returns the first PC address marked as prologue_end in the half open interval [start, end)
|
|
|
|
func (lineInfo *DebugLineInfo) PrologueEndPC(start, end uint64) (pc uint64, file string, line int, ok bool) {
|
2019-10-21 17:43:03 +00:00
|
|
|
if lineInfo == nil {
|
|
|
|
return 0, "", 0, false
|
|
|
|
}
|
|
|
|
|
2018-03-25 08:04:32 +00:00
|
|
|
sm := lineInfo.stateMachineForEntry(start)
|
|
|
|
for {
|
|
|
|
if sm.valid {
|
|
|
|
if sm.address >= end {
|
|
|
|
return 0, "", 0, false
|
|
|
|
}
|
|
|
|
if sm.prologueEnd {
|
|
|
|
return sm.address, sm.file, sm.line, true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if err := sm.next(); err != nil {
|
2018-06-14 18:12:11 +00:00
|
|
|
if lineInfo.Logf != nil {
|
|
|
|
lineInfo.Logf("PrologueEnd error: %v", err)
|
2018-03-25 08:04:32 +00:00
|
|
|
}
|
|
|
|
return 0, "", 0, false
|
|
|
|
}
|
|
|
|
}
|
2017-09-01 13:30:45 +00:00
|
|
|
}
|
|
|
|
|
2019-11-01 19:41:06 +00:00
|
|
|
// FirstStmtForLine looks in the half open interval [start, end) for the
|
|
|
|
// first PC address marked as stmt for the line at address 'start'.
|
|
|
|
func (lineInfo *DebugLineInfo) FirstStmtForLine(start, end uint64) (pc uint64, file string, line int, ok bool) {
|
|
|
|
first := true
|
|
|
|
sm := lineInfo.stateMachineForEntry(start)
|
|
|
|
for {
|
|
|
|
if sm.valid {
|
|
|
|
if sm.address >= end {
|
|
|
|
return 0, "", 0, false
|
|
|
|
}
|
|
|
|
if first {
|
|
|
|
first = false
|
|
|
|
file, line = sm.file, sm.line
|
|
|
|
}
|
|
|
|
if sm.isStmt && sm.file == file && sm.line == line {
|
|
|
|
return sm.address, sm.file, sm.line, true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if err := sm.next(); err != nil {
|
|
|
|
if lineInfo.Logf != nil {
|
2019-12-03 13:00:30 +00:00
|
|
|
lineInfo.Logf("FirstStmtForLine error: %v", err)
|
2019-11-01 19:41:06 +00:00
|
|
|
}
|
|
|
|
return 0, "", 0, false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-03 13:00:30 +00:00
|
|
|
func (lineInfo *DebugLineInfo) FirstFile() string {
|
2020-03-10 16:34:40 +00:00
|
|
|
sm := newStateMachine(lineInfo, lineInfo.Instructions, lineInfo.ptrSize)
|
2019-12-03 13:00:30 +00:00
|
|
|
for {
|
|
|
|
if sm.valid {
|
|
|
|
return sm.file
|
|
|
|
}
|
|
|
|
if err := sm.next(); err != nil {
|
|
|
|
if lineInfo.Logf != nil {
|
|
|
|
lineInfo.Logf("FirstFile error: %v", err)
|
|
|
|
}
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-18 17:49:29 +00:00
|
|
|
func (sm *StateMachine) next() error {
|
|
|
|
sm.started = true
|
|
|
|
if sm.valid {
|
|
|
|
sm.lastAddress, sm.lastFile, sm.lastLine = sm.address, sm.file, sm.line
|
2018-07-30 09:18:41 +00:00
|
|
|
|
|
|
|
// valid is set by either a special opcode or a DW_LNS_copy, in both cases
|
|
|
|
// we need to reset basic_block, prologue_end and epilogue_begin
|
|
|
|
sm.basicBlock = false
|
|
|
|
sm.prologueEnd = false
|
|
|
|
sm.epilogueBegin = false
|
2017-08-18 17:49:29 +00:00
|
|
|
}
|
2017-12-04 10:03:17 +00:00
|
|
|
if sm.endSeq {
|
|
|
|
sm.endSeq = false
|
|
|
|
sm.file = sm.dbl.FileNames[0].Path
|
|
|
|
sm.line = 1
|
|
|
|
sm.column = 0
|
2019-08-27 21:27:15 +00:00
|
|
|
sm.isa = 0
|
2018-07-30 09:18:41 +00:00
|
|
|
sm.isStmt = sm.dbl.Prologue.InitialIsStmt == uint8(1)
|
2017-12-04 10:03:17 +00:00
|
|
|
sm.basicBlock = false
|
2019-10-07 16:54:32 +00:00
|
|
|
sm.lastAddress = ^uint64(0)
|
2017-12-04 10:03:17 +00:00
|
|
|
}
|
2017-08-18 17:49:29 +00:00
|
|
|
b, err := sm.buf.ReadByte()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-07-30 09:18:41 +00:00
|
|
|
if b < sm.dbl.Prologue.OpcodeBase {
|
|
|
|
if int(b) < len(sm.opcodes) {
|
|
|
|
sm.valid = false
|
|
|
|
sm.opcodes[b](sm, sm.buf)
|
2018-03-25 08:04:32 +00:00
|
|
|
} else {
|
2018-07-30 09:18:41 +00:00
|
|
|
// unimplemented standard opcode, read the number of arguments specified
|
|
|
|
// in the prologue and do nothing with them
|
|
|
|
opnum := sm.dbl.Prologue.StdOpLengths[b-1]
|
|
|
|
for i := 0; i < int(opnum); i++ {
|
|
|
|
util.DecodeSLEB128(sm.buf)
|
|
|
|
}
|
2019-08-12 22:10:01 +00:00
|
|
|
fmt.Printf("unknown opcode %d(0x%x), %d arguments, file %s, line %d, address 0x%x\n", b, b, opnum, sm.file, sm.line, sm.address)
|
2017-08-18 17:49:29 +00:00
|
|
|
}
|
|
|
|
} else {
|
2015-03-28 01:12:07 +00:00
|
|
|
execSpecialOpcode(sm, b)
|
|
|
|
}
|
2017-08-18 17:49:29 +00:00
|
|
|
return nil
|
2015-03-28 01:12:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func execSpecialOpcode(sm *StateMachine, instr byte) {
|
|
|
|
var (
|
|
|
|
opcode = uint8(instr)
|
|
|
|
decoded = opcode - sm.dbl.Prologue.OpcodeBase
|
|
|
|
)
|
|
|
|
|
|
|
|
sm.lastDelta = int(sm.dbl.Prologue.LineBase + int8(decoded%sm.dbl.Prologue.LineRange))
|
|
|
|
sm.line += sm.lastDelta
|
2017-02-07 21:07:18 +00:00
|
|
|
sm.address += uint64(decoded/sm.dbl.Prologue.LineRange) * uint64(sm.dbl.Prologue.MinInstrLength)
|
|
|
|
sm.valid = true
|
2015-03-28 01:12:07 +00:00
|
|
|
}
|
|
|
|
|
2017-08-18 17:49:29 +00:00
|
|
|
func execExtendedOpcode(sm *StateMachine, buf *bytes.Buffer) {
|
2015-03-28 01:12:07 +00:00
|
|
|
_, _ = util.DecodeULEB128(buf)
|
|
|
|
b, _ := buf.ReadByte()
|
2017-08-18 17:49:29 +00:00
|
|
|
if fn, ok := extendedopcodes[b]; ok {
|
|
|
|
fn(sm, buf)
|
2015-03-28 01:12:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func copyfn(sm *StateMachine, buf *bytes.Buffer) {
|
2017-02-07 21:07:18 +00:00
|
|
|
sm.valid = true
|
2015-03-28 01:12:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func advancepc(sm *StateMachine, buf *bytes.Buffer) {
|
|
|
|
addr, _ := util.DecodeULEB128(buf)
|
|
|
|
sm.address += addr * uint64(sm.dbl.Prologue.MinInstrLength)
|
|
|
|
}
|
|
|
|
|
|
|
|
func advanceline(sm *StateMachine, buf *bytes.Buffer) {
|
|
|
|
line, _ := util.DecodeSLEB128(buf)
|
|
|
|
sm.line += int(line)
|
|
|
|
sm.lastDelta = int(line)
|
|
|
|
}
|
|
|
|
|
|
|
|
func setfile(sm *StateMachine, buf *bytes.Buffer) {
|
|
|
|
i, _ := util.DecodeULEB128(buf)
|
2020-07-21 20:39:09 +00:00
|
|
|
if sm.dbl.Prologue.Version < 5 {
|
|
|
|
// in DWARF v5 files are indexed starting from 0, in v4 and prior the index starts at 1
|
|
|
|
i--
|
|
|
|
}
|
|
|
|
if i < uint64(len(sm.dbl.FileNames)) {
|
|
|
|
sm.file = sm.dbl.FileNames[i].Path
|
2017-08-19 11:06:20 +00:00
|
|
|
} else {
|
2020-07-21 20:39:09 +00:00
|
|
|
j := i - uint64(len(sm.dbl.FileNames))
|
2017-08-19 11:06:20 +00:00
|
|
|
if j < uint64(len(sm.definedFiles)) {
|
|
|
|
sm.file = sm.definedFiles[j].Path
|
|
|
|
} else {
|
|
|
|
sm.file = ""
|
|
|
|
}
|
|
|
|
}
|
2015-03-28 01:12:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func setcolumn(sm *StateMachine, buf *bytes.Buffer) {
|
|
|
|
c, _ := util.DecodeULEB128(buf)
|
|
|
|
sm.column = uint(c)
|
|
|
|
}
|
|
|
|
|
|
|
|
func negatestmt(sm *StateMachine, buf *bytes.Buffer) {
|
|
|
|
sm.isStmt = !sm.isStmt
|
|
|
|
}
|
|
|
|
|
|
|
|
func setbasicblock(sm *StateMachine, buf *bytes.Buffer) {
|
|
|
|
sm.basicBlock = true
|
|
|
|
}
|
|
|
|
|
|
|
|
func constaddpc(sm *StateMachine, buf *bytes.Buffer) {
|
2017-02-07 21:07:18 +00:00
|
|
|
sm.address += uint64((255-sm.dbl.Prologue.OpcodeBase)/sm.dbl.Prologue.LineRange) * uint64(sm.dbl.Prologue.MinInstrLength)
|
2015-03-28 01:12:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func fixedadvancepc(sm *StateMachine, buf *bytes.Buffer) {
|
|
|
|
var operand uint16
|
|
|
|
binary.Read(buf, binary.LittleEndian, &operand)
|
|
|
|
|
|
|
|
sm.address += uint64(operand)
|
|
|
|
}
|
|
|
|
|
|
|
|
func endsequence(sm *StateMachine, buf *bytes.Buffer) {
|
|
|
|
sm.endSeq = true
|
2020-12-04 17:35:57 +00:00
|
|
|
sm.valid = sm.dbl.endSeqIsValid
|
2015-03-28 01:12:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func setaddress(sm *StateMachine, buf *bytes.Buffer) {
|
2020-03-10 16:34:40 +00:00
|
|
|
addr, err := util.ReadUintRaw(buf, binary.LittleEndian, sm.ptrSize)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
2018-05-29 15:01:51 +00:00
|
|
|
sm.address = addr + sm.dbl.staticBase
|
2015-03-28 01:12:07 +00:00
|
|
|
}
|
|
|
|
|
2020-04-09 20:57:44 +00:00
|
|
|
func setdiscriminator(sm *StateMachine, buf *bytes.Buffer) {
|
|
|
|
_, _ = util.DecodeULEB128(buf)
|
|
|
|
}
|
|
|
|
|
2015-03-28 01:12:07 +00:00
|
|
|
func definefile(sm *StateMachine, buf *bytes.Buffer) {
|
2021-05-04 19:36:22 +00:00
|
|
|
if entry := readFileEntry(sm.dbl, sm.buf, false); entry != nil {
|
|
|
|
sm.definedFiles = append(sm.definedFiles, entry)
|
|
|
|
}
|
2015-03-28 01:12:07 +00:00
|
|
|
}
|
2018-03-25 08:04:32 +00:00
|
|
|
|
|
|
|
func prologueend(sm *StateMachine, buf *bytes.Buffer) {
|
|
|
|
sm.prologueEnd = true
|
|
|
|
}
|
|
|
|
|
|
|
|
func epiloguebegin(sm *StateMachine, buf *bytes.Buffer) {
|
|
|
|
sm.epilogueBegin = true
|
|
|
|
}
|
2019-08-27 21:27:15 +00:00
|
|
|
|
|
|
|
func setisa(sm *StateMachine, buf *bytes.Buffer) {
|
|
|
|
c, _ := util.DecodeULEB128(buf)
|
|
|
|
sm.isa = c
|
|
|
|
}
|