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 <javierhonduco@gmail.com>
This commit is contained in:
Javier Honduvilla Coto 2023-08-23 20:56:58 +01:00 committed by GitHub
parent 789f8b4054
commit 908876d6aa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -18,18 +18,46 @@ type DWRule struct {
// FrameContext wrapper of FDE context // FrameContext wrapper of FDE context
type FrameContext struct { type FrameContext struct {
loc uint64 loc uint64
order binary.ByteOrder order binary.ByteOrder
address uint64 address uint64
CFA DWRule CFA DWRule
Regs map[uint64]DWRule Regs map[uint64]DWRule
initialRegs map[uint64]DWRule initialRegs map[uint64]DWRule
prevRegs map[uint64]DWRule buf *bytes.Buffer
buf *bytes.Buffer cie *CommonInformationEntry
cie *CommonInformationEntry RetAddrReg uint64
RetAddrReg uint64 codeAlignment uint64
codeAlignment uint64 dataAlignment int64
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. // 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)) initialInstructions := make([]byte, len(cie.InitialInstructions))
copy(initialInstructions, cie.InitialInstructions) copy(initialInstructions, cie.InitialInstructions)
frame := &FrameContext{ frame := &FrameContext{
cie: cie, cie: cie,
Regs: make(map[uint64]DWRule), Regs: make(map[uint64]DWRule),
RetAddrReg: cie.ReturnAddressRegister, RetAddrReg: cie.ReturnAddressRegister,
initialRegs: make(map[uint64]DWRule), initialRegs: make(map[uint64]DWRule),
prevRegs: make(map[uint64]DWRule), codeAlignment: cie.CodeAlignmentFactor,
codeAlignment: cie.CodeAlignmentFactor, dataAlignment: cie.DataAlignmentFactor,
dataAlignment: cie.DataAlignmentFactor, buf: bytes.NewBuffer(initialInstructions),
buf: bytes.NewBuffer(initialInstructions), rememberedState: newStateStack(),
} }
frame.executeDwarfProgram() frame.executeDwarfProgram()
@ -308,11 +336,18 @@ func register(frame *FrameContext) {
} }
func rememberstate(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) { func restorestate(frame *FrameContext) {
frame.Regs = frame.prevRegs restored := frame.rememberedState.pop()
frame.CFA = restored.cfa
frame.Regs = restored.regs
} }
func restoreextended(frame *FrameContext) { func restoreextended(frame *FrameContext) {