From 908876d6aaf1c769bc12ff293490d520bdf9ba09 Mon Sep 17 00:00:00 2001 From: Javier Honduvilla Coto Date: Wed, 23 Aug 2023 20:56:58 +0100 Subject: [PATCH] DWARF CFI: use a stack for restore/remember opcodes (#3480) 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 --- pkg/dwarf/frame/table.go | 79 +++++++++++++++++++++++++++++----------- 1 file changed, 57 insertions(+), 22 deletions(-) diff --git a/pkg/dwarf/frame/table.go b/pkg/dwarf/frame/table.go index c49d2115..628d7b36 100644 --- a/pkg/dwarf/frame/table.go +++ b/pkg/dwarf/frame/table.go @@ -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. @@ -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() @@ -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) {