rust-lang / rust-lang/rust

PDB debugging information has bad variable valid ranges

Open
#151,075 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

A-debuginfo C-bug needs-triage O-windows
Dominant language
Rust
Stars
119k
Forks
16.2k
PR merge metrics
PR metrics pending

Description

On windows, with this repro case, compiled with cargo rustc (--emit=asm,llvm-ir)

fn main() {
    foobar(5);
}

fn foobar(n: usize) -> () {
    let foo = if n == 5 {
        0
    } else {
        1
    };

    let bar = if n == 6 {
        1
    } else {
        2
    };
}

I expected to see this happen: When stepping through code source line-by-line (in either WinDbg, or the LLDB-based RustRover debugger), I'd expect the value of foo to be available and shown in the locals window once we reach line 12 (let bar = ...).

Instead, this happened: Instead, the value is only available when stepping past that location, either to the next line, or even to the next assembly instruction.

The ultimate reason for this is that the emitted debuginfo is inaccurate: (from the asm output)

	.def	_ZN20rust_debuginfo_repro6foobar17h5720b41e41166300E;
	.scl	3;
	.type	32;
	.endef
	.section	.text,"xr",one_only,_ZN20rust_debuginfo_repro6foobar17h5720b41e41166300E,unique,1
	.p2align	4
_ZN20rust_debuginfo_repro6foobar17h5720b41e41166300E:
.Lfunc_begin1:
	.cv_func_id 1
	.cv_loc	1 1 5 0
.seh_proc _ZN20rust_debuginfo_repro6foobar17h5720b41e41166300E
	subq	$24, %rsp
	.seh_stackalloc 24
	.seh_endprologue
	movq	%rcx, (%rsp)
	movq	%rcx, 16(%rsp)
.Ltmp2:
	.cv_loc	1 1 6 0
	cmpq	$5, %rcx
	jne	.LBB1_2
	.cv_loc	1 1 7 0
	movl	$0, 8(%rsp)
	.cv_loc	1 1 6 0
	jmp	.LBB1_3
.LBB1_2:
	.cv_loc	1 1 9 0
	movl	$1, 8(%rsp)
.LBB1_3:
	.cv_loc	1 1 12 0
	movq	(%rsp), %rax  # <- Line 12 breakpoint/stepping stops here
.Ltmp3:  # <- "foo" valid starting from here (see debug record at the end)
	cmpq	$6, %rax
	jne	.LBB1_5
	.cv_loc	1 1 13 0
	movl	$1, 12(%rsp)
	.cv_loc	1 1 12 0
	jmp	.LBB1_6
.LBB1_5:
	.cv_loc	1 1 15 0
	movl	$2, 12(%rsp)
.Ltmp4:
.LBB1_6:
	.cv_loc	1 1 17 0
	.seh_startepilogue
	addq	$24, %rsp
	.seh_endepilogue
	retq
.Ltmp5:
.Lfunc_end1:
	.seh_endproc

# later...
# Debug record for "foo" variable
	.asciz	"foo"
	.p2align	2, 0x0
.Ltmp49:
	.cv_def_range	 .Ltmp3 .Ltmp4, frame_ptr_rel, 8  # Valid from .Ltmp3 to .Ltmp4

Interestingly, I compared the debug output generated for DWARF (https://godbolt.org/z/4b9T8fvvs) and while it appears to have the same validity range as the CodeView version, it rather assigns the source locations differently:

.LBB0_2:
        .loc    1 9 9
        mov     dword ptr [rsp - 16], 1
.LBB0_3:
        .loc    1 0 9 is_stmt 0  # <- Marked as line 12 in PDB, but "no line" here
        mov     rax, qword ptr [rsp - 24]
.Ltmp1:  # <- "foo" validity starts here
        .loc    1 12 18 is_stmt 1
        cmp     rax, 6
        jne     .LBB0_5

This means that a breakpoint on that line ends up stopping at the cmp instead, and "foo" is visible as expected. (Even though, in reality, the value on the stack was already available on the previous instruction, but this is much less of an issue.)

Meta

rustc --version --verbose:

rustc 1.93.0-beta.6 (7bea8c6cf 2026-01-09)
binary: rustc
commit-hash: 7bea8c6cf1ccce6b06c26d9a435e85cd0b9113bb
commit-date: 2026-01-09
host: x86_64-pc-windows-msvc
release: 1.93.0-beta.6
LLVM version: 21.1.8

I found similar reports in the RustRover bug tracker going back several years, so this is unlikely to be a recent regression: RUST-15126 RUST-6821

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Reproduce the Windows case with cargo rustc --emit=asm,llvm-ir using the foobar example, then inspect the emitted CodeView/PDB records around .cv_loc and .cv_def_range. Compare the PDB and DWARF source locations and variable ranges; done means foo is visible when execution reaches line 12 in the debugger without stepping past it.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.