go-delve/delve · error

PCToFunc returned nil

Error message

PCToFunc returned nil

What it means

Once the destination address is extracted from the instruction, Delve maps it back to a Go function with BinInfo.PCToFunc to name and trace it. This error is returned when PCToFunc finds no function covering that address. The call target is real code but outside any known Go function — typically runtime traps, cgo/C code, or text not covered by debug info.

Source

Thrown at service/debugger/debugger.go:1513

					}
					// Disassemble the instruction at the current PC to get the call destination
					pc := instr.Loc.PC
					maxInstLen := uint64(tgt.BinInfo().Arch.MaxInstructionLength())
					disasm, err := proc.Disassemble(t.Memory(), regs, t.Breakpoints(), tgt.BinInfo(), pc, pc+maxInstLen)
					if err != nil {
						return false, fmt.Errorf("failed to disassemble instruction: %w", err)
					}

					// Extract address from the decoded instruction's destination location
					var addr uint64
					if len(disasm) > 0 && disasm[0].DestLoc != nil {
						addr = disasm[0].DestLoc.PC
					} else {
						return false, fmt.Errorf("failed to extract call destination from instruction at PC %#x", pc)
					}
					fn := tgt.BinInfo().PCToFunc(addr)
					if fn == nil {
						return false, fmt.Errorf("PCToFunc returned nil")
					}
					err = createFunctionTracepoints(d, fn.Name, rootstr, followCalls)
					if err != nil {
						return false, fmt.Errorf("error creating tracepoint in function %s", fn.Name)
					}
					dynchildren, err := d.traverse(t, fn, sdepth+1, followCalls, rootstr)
					if err != nil {
						return false, fmt.Errorf("error calling traverse on dynamic children")
					}
					for _, child := range dynchildren {
						err := createFunctionTracepoints(d, child, rootstr, followCalls)
						if err != nil {
							return false, fmt.Errorf("error creating tracepoint in function %s", child)
						}
					}
					return false, nil
				}
				for _, dynBrklet := range dynbp.Breaklets {

View on GitHub (pinned to a23773e6c3)

Solutions

  1. Check whether the target address is in C/cgo code (dlv: 'disassemble <addr>' or nm/objdump); skip such targets — eBPF uprobes on Go functions cannot trace them.
  2. Rebuild the binary unstripped (no -s -w) so the function table is complete.
  3. Confirm you are tracing a Go binary built with the same toolchain Delve indexed; stale binaries cause lookup misses.
  4. Extend skip-lists (like the existing runtime.* filter) to also skip unresolved destinations instead of erroring.

Example fix

// before
fn := tgt.BinInfo().PCToFunc(addr)
if fn == nil {
	return false, fmt.Errorf("PCToFunc returned nil")
}
// after (skip non-Go destinations such as cgo trampolines)
fn := tgt.BinInfo().PCToFunc(addr)
if fn == nil {
	log.Debugf("call destination %#x is not a Go function; skipping", addr)
	return false, nil
}
Defensive patterns

Strategy: validation

Validate before calling

// before tracing, confirm the binary exposes Go symbols
f, err := elf.Open(binPath)
if err != nil { log.Fatal(err) }
syms, err := f.Symbols()
if err != nil || len(syms) == 0 {
	log.Fatal("no symbol table: PCToFunc will fail for call destinations")
}
f.Close()

Type guard

func isUnresolvedDestination(err error) bool {
	return err != nil && strings.Contains(err.Error(), "PCToFunc returned nil")
}

Try / catch

if err := runTrace(); err != nil {
	if isUnresolvedDestination(err) {
		log.Println("call target is cgo/C or runtime stub; excluding it from follow-calls")
		return runTrace(TraceOpts{Skip: unresolvedTargets})
	}
	return err
}

Prevention

When it happens

Trigger: dynCallback resolved disasm[0].DestLoc.PC to an address for which tgt.BinInfo().PCToFunc(addr) returned nil while creating follow-calls tracepoints.

Common situations: Indirect calls into cgo/C shared libraries (no Go func entries); calls into runtime assembler stubs excluded from the function map; stripped binary or missing symbol table so PCToFunc lookups fail; PLT/thunk trampolines on some platforms.

Related errors


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