{"record":{"id":"056f4b9339a7f789","repo":"go-delve/delve","slug":"could-not-restore-lr-v","errorCode":null,"errorMessage":"could not restore LR: %v","messagePattern":"could not restore LR: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/proc/fncall.go","lineNumber":979,"sourceCode":"\t\t// possible is to ignore it and hope it didn't matter.\n\t\tstack.callInjectionContinue = true\n\t\tfncallLog(\"unknown value of protocol register %#x\", regval)\n\t}\n\n\treturn false\n}\n\nfunc callInjectionComplete2(callScope *EvalScope, bi *BinaryInfo, fncall *functionCallState, regs Registers, thread Thread) {\n\t// Store the stack span of the currently running goroutine (which in Go >=\n\t// 1.15 might be different from the original injection goroutine) so that\n\t// later on we can use it to perform the escapeCheck\n\tif threadg, _ := GetG(thread); threadg != nil {\n\t\tcallScope.callCtx.stacks = append(callScope.callCtx.stacks, threadg.stack)\n\t}\n\tif bi.Arch.Name == \"arm64\" || bi.Arch.Name == \"ppc64le\" || bi.Arch.Name == \"loong64\" {\n\t\toldlr, err := readUintRaw(thread.ProcessMemory(), regs.SP(), int64(bi.Arch.PtrSize()))\n\t\tif err != nil {\n\t\t\tfncall.err = fmt.Errorf(\"could not restore LR: %v\", err)\n\t\t\treturn\n\t\t}\n\t\tif err = setLR(thread, oldlr); err != nil {\n\t\t\tfncall.err = fmt.Errorf(\"could not restore LR: %v\", err)\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc (scope *EvalScope) evalCallInjectionSetTarget(op *evalop.CallInjectionSetTarget, stack *evalStack, thread Thread) {\n\tfncall := stack.fncallPeek()\n\tif !fncall.hasDebugPinner && (fncall.fn == nil || fncall.receiver != nil || fncall.closureAddr != 0) {\n\t\tstack.err = funcCallEvalFuncExpr(scope, stack, fncall)\n\t\tif stack.err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\tstack.pop() // target function, consumed by funcCallEvalFuncExpr either above or in evalop.CallInjectionStart","sourceCodeStart":961,"sourceCodeEnd":997,"githubUrl":"https://github.com/go-delve/delve/blob/a23773e6c31361e43246bc43a424ee009679b174/pkg/proc/fncall.go#L961-L997","documentation":"On arm64/ppc64le/loong64, Delve's call injection protocol stashes the original Link Register value on the stack and must restore it once the injected call completes. callInjectionComplete2 reads the saved LR from the thread's stack via readUintRaw at regs.SP(). If that memory read fails, fncall.err is set to \"could not restore LR: %v\" and the call injection aborts, leaving the target's register/stack state at risk of corruption.","triggerScenarios":"Completing a function call injection on arm64, ppc64le, or loong64 when readUintRaw(thread.ProcessMemory(), regs.SP(), ptrSize) fails: the thread exited, SP points to unmapped/corrupt memory, or the process died between the call finishing and the completion step.","commonSituations":"Target process crashes or exits during the injected call; debugging a core dump or remote session where memory at SP is not readable; Go runtime moved the goroutine to a different stack mid-call (stack growth) so the saved LR is no longer at the recorded SP.","solutions":["Check the wrapped cause: if it is a memory-read error, verify the target process is still alive (threads command).","Restart or re-attach to the target process; this error usually indicates lost protocol state that cannot be repaired in-session.","Avoid injecting calls into functions that panic or exit the process; guard expressions before calling.","Update Delve to the latest version to get protocol fixes for your architecture and Go version.","Retry on a stable breakpoint where SP matches the goroutine's current stack."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// before injecting, confirm thread is alive and stack is mapped\nthr, err := proc.FindThread(dbg, tid)\nif err != nil { return err } // thread gone -> do not inject\n_, err = thr.Registers()\nif err != nil { return err } // cannot even read regs -> skip injection","typeGuard":null,"tryCatchPattern":"res, err := dbg.StepInstruction / CallFunction(...)\nif err != nil && strings.Contains(err.Error(), \"could not restore LR:\") {\n    // saved LR unreadable or register write failed: session state is lost\n    // only recovery is re-attach/restart; surface to user, do not resume\n}","preventionTips":["On arm64/ppc64le/loong64 treat call-injection failures as session-fatal; plan re-attach.","Don't inject calls that can os.Exit or crash the process mid-protocol.","Inject at stable breakpoints, not during signals or mid-stepping.","Ensure debugger has register-write privileges (ptrace_scope, sudo on Linux/macOS).","Update Delve; arch-specific LR save/restore bugs get fixed regularly."],"tags":["go","debugger","arm64","ppc64le","loong64","function-call-injection"],"backgroundTag":"function-call-injection-failed","analyzedSha":"a23773e6c31361e43246bc43a424ee009679b174","analyzedAt":"2026-08-31T15:12:45.221Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}