Skip to content

Commit

Permalink
DWARF CFI: use a stack for restore/remember opcodes
Browse files Browse the repository at this point in the history
Implementing the `DW_CFA_remember_state` and `DW_CFA_restore_state`
according to the DWARF specification requires us to create a stack that
can store an arbitrary number of elements, that is, there could be
multiple "pushes" before "popping" them.

From the 5th revision of the spec [0]:

> 6.4.2.4 Row State Instructions
> DW_CFA_remember_state
> 	The DW_CFA_remember_state instruction takes no operands. The required
>	action is to push the set of rules for every register onto an implicit stack.
>	DW_CFA_restore_state
> DW_CFA_restore_state
>	The DW_CFA_restore_state instruction takes no operands. The required action
>	is to pop the set of rules off the implicit stack and place them in the
>	current row.

- [0]: https://dwarfstd.org/doc/DWARF5.pdf

Signed-off-by: Francisco Javier Honduvilla Coto <javierhonduco@gmail.com>
  • Loading branch information
javierhonduco committed Aug 23, 2023
1 parent 6a0423a commit 86b6964
Showing 1 changed file with 57 additions and 22 deletions.
79 changes: 57 additions & 22 deletions pkg/dwarf/frame/table.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,46 @@ type DWRule struct {

// FrameContext wrapper of FDE context
type FrameContext struct {
loc uint64
order binary.ByteOrder
address uint64
CFA DWRule
Regs map[uint64]DWRule
initialRegs map[uint64]DWRule
prevRegs map[uint64]DWRule
buf *bytes.Buffer
cie *CommonInformationEntry
RetAddrReg uint64
codeAlignment uint64
dataAlignment int64
loc uint64
order binary.ByteOrder
address uint64
CFA DWRule
Regs map[uint64]DWRule
initialRegs map[uint64]DWRule
buf *bytes.Buffer
cie *CommonInformationEntry
RetAddrReg uint64
codeAlignment uint64
dataAlignment int64
rememberedState *stateStack
}

type rowState struct {
cfa DWRule
regs map[uint64]DWRule
}

// stateStack is a stack where `DW_CFA_remember_state` pushes
// its CFA and registers state and `DW_CFA_restore_state`
// pops them.
type stateStack struct {
items []rowState
}

func newStateStack() *stateStack {
return &stateStack{
items: make([]rowState, 0),
}
}

func (stack *stateStack) push(state rowState) {
stack.items = append(stack.items, state)
}

func (stack *stateStack) pop() rowState {
restored := stack.items[len(stack.items)-1]
stack.items = stack.items[0 : len(stack.items)-1]
return restored
}

// Instructions used to recreate the table from the .debug_frame data.
Expand Down Expand Up @@ -119,14 +147,14 @@ func executeCIEInstructions(cie *CommonInformationEntry) *FrameContext {
initialInstructions := make([]byte, len(cie.InitialInstructions))
copy(initialInstructions, cie.InitialInstructions)
frame := &FrameContext{
cie: cie,
Regs: make(map[uint64]DWRule),
RetAddrReg: cie.ReturnAddressRegister,
initialRegs: make(map[uint64]DWRule),
prevRegs: make(map[uint64]DWRule),
codeAlignment: cie.CodeAlignmentFactor,
dataAlignment: cie.DataAlignmentFactor,
buf: bytes.NewBuffer(initialInstructions),
cie: cie,
Regs: make(map[uint64]DWRule),
RetAddrReg: cie.ReturnAddressRegister,
initialRegs: make(map[uint64]DWRule),
codeAlignment: cie.CodeAlignmentFactor,
dataAlignment: cie.DataAlignmentFactor,
buf: bytes.NewBuffer(initialInstructions),
rememberedState: newStateStack(),
}

frame.executeDwarfProgram()
Expand Down Expand Up @@ -308,11 +336,18 @@ func register(frame *FrameContext) {
}

func rememberstate(frame *FrameContext) {
frame.prevRegs = frame.Regs
clonedRegs := make(map[uint64]DWRule, len(frame.Regs))
for k, v := range frame.Regs {
clonedRegs[k] = v
}
frame.rememberedState.push(rowState{cfa: frame.CFA, regs: clonedRegs})
}

func restorestate(frame *FrameContext) {
frame.Regs = frame.prevRegs
restored := frame.rememberedState.pop()

frame.CFA = restored.cfa
frame.Regs = restored.regs
}

func restoreextended(frame *FrameContext) {
Expand Down

0 comments on commit 86b6964

Please sign in to comment.