go-delve/delve · error

ErrStackUnderflow

ErrStackUnderflow

Error message

DWARF stack underflow

What it means

ErrStackUnderflow is a sentinel error indicating a DWARF location-expression stack operation ran out of operands. Operations like dup/drop/swap/rot (stack manipulation), unaryop and binaryop pop one or two values off the DWARF value stack; if the stack has too few values, this error is returned by ExecuteStackProgram.

Source

Thrown at pkg/dwarf/op/op.go:53

// Piece is a piece of memory stored either at an address or in a register.
type Piece struct {
	Size  int
	Kind  PieceKind
	Val   uint64
	Bytes []byte
}

// PieceKind describes the kind of a piece.
type PieceKind uint8

const (
	AddrPiece PieceKind = iota // The piece is stored in memory, Val is the address
	RegPiece                   // The piece is stored in a register, Val is the register number
	ImmPiece                   // The piece is an immediate value, Val or Bytes is the value
)

var (
	ErrStackUnderflow        = errors.New("DWARF stack underflow")
	ErrStackIndexOutOfBounds = errors.New("DWARF stack index out of bounds")
	ErrMemoryReadUnavailable = errors.New("memory read unavailable")
)

const arbitraryExecutionLimitFactor = 10

// ExecuteStackProgram executes a DWARF location expression and returns
// either an address (int64), or a slice of Pieces for location expressions
// that don't evaluate to an address (such as register and composite expressions).
func ExecuteStackProgram(regs DwarfRegisters, instructions []byte, ptrSize int, readMemory ReadMemoryFunc) (int64, []Piece, error) {
	ctxt := &context{
		buf:            bytes.NewBuffer(instructions),
		prog:           instructions,
		stack:          make([]int64, 0, 3),
		DwarfRegisters: regs,
		ptrSize:        ptrSize,
		readMemory:     readMemory,
	}

View on GitHub (pinned to a23773e6c3)

Solutions

  1. Verify the binary's DWARF data is intact and produced by a standard toolchain (recompile with debug info)
  2. Check that the byte slice passed as instructions is the raw DWARF expression, not a padded or offset slice
  3. If you control the expression generation, validate that every producer opcode precedes its consumers
  4. Catch errors.Is(err, op.ErrStackUnderflow) and fall back to reporting the variable as unavailable

Example fix

// before
val, pieces, err := op.ExecuteStackProgram(regs, instr, ptrSize, nil)
// after
val, pieces, err := op.ExecuteStackProgram(regs, instr, ptrSize, nil)
if errors.Is(err, op.ErrStackUnderflow) {
    return 0, nil, fmt.Errorf("malformed DWARF expression: %w", err)
}
Defensive patterns

Strategy: try-catch

Try / catch

val, pieces, err := op.ExecuteStackProgram(regs, instr, ptrSize, mem)
if errors.Is(err, op.ErrStackUnderflow) {
    // malformed expression: report variable as unavailable
}

Prevention

When it happens

Trigger: Executing a malformed or truncated DWARF location expression via op.ExecuteStackProgram where a dup, drop, swap, rot, unaryop, or binaryop opcode needs more stack operands than are present.

Common situations: Corrupted or hand-crafted DWARF debug info, compiler bugs emitting bad expression streams, or a mis-parsed byte slice passed as 'instructions' so the opcode stream is misaligned.

Related errors


AI-assisted analysis of go-delve/delve@a23773e6c3 (2026-08-31). Data as JSON: /api/errors/bd9bf8c3836e495e. Report an issue: GitHub.