Change:
<!-- Thank you for filing a bug report! Please provide a short summary of the bug, along with any information you feel relevant to replicating the bug. --Setting up a release profile with stripped binaries and split debuginfo is made very complicated due to subtle differences in how , and interact with eachother across platforms. In particular, in the following setup: Then when targetting linux/macos, a / debug information file will be generated, while when targeting windows, no debug information file can be found. In particular, no file is created. This seems to stem from how the flags propagate to linker invocation: On windows, of rustccodegenssa only takes into account when choosing how to generate the debug information. If it's set to any value other than None, it will not generate any debuginfo whatsoever, regardless of the value of . On macos/linux, it's  that's responsible both for splitting debuginfo and stripping. A / file will be created only if is set to a value other than 0, and is set to packed. Stripping is, crucially, not a part of the equation. I believe the behavior with the windows should be adjusted to match what is done on macos/linux. In particular, if is set to and strip is set to , then a file should be generated. <!-- If you're using the stable version of the compiler, you should also check if the bug also exists in the beta or nightly versions. --:
I believe is also effected by such inconsistencies. Would be nice to have consistent behavior. And possibly a flag that produces both an unstripped and stripped binary, though that's a separate feature.

Discussion:
} } <del> fn debuginfo(&mut self, strip: Strip, natvis_debugger_visualizers: &[PathBuf]) { match strip { Strip::None => { // This will cause the Microsoft linker to generate a PDB file // from the CodeView line tables in the object files. self.cmd.arg("/DEBUG"); // Default to emitting only the file name of the PDB file into // the binary instead of the full path. Emitting the full path // may leak private information (such as user names). // See https://github.com/rust-lang/rust/issues/87825. // // This default behavior can be overridden by explicitly passing // `-Clink-arg=/PDBALTPATH:...` to rustc. self.cmd.arg("/PDBALTPATH:%_PDB%"); // This will cause the Microsoft linker to embed .natvis info into the PDB file let natvis_dir_path = self.sess.sysroot.join("librustlibetc"); if let Ok(natvis_dir) = fs::read_dir(&natvis_dir_path) { for entry in natvis_dir { match entry { Ok(entry) => { let path = entry.path(); if path.extension() == Some("natvis".as_ref()) { let mut arg = OsString::from("/NATVIS:"); arg.push(path); self.cmd.arg(arg); } } Err(error) => { self.sess.dcx().emit_warn(errors::NoNatvisDirectory { error }); } </del> <ins> fn debuginfo(&mut self, _strip: Strip, natvis_debugger_visualizers: &[PathBuf]) { // This will cause the Microsoft linker to generate a PDB file // from the CodeView line tables in the object files. self.cmd.arg("/DEBUG"); // Default to emitting only the file name of the PDB file into // the binary instead of the full path. Emitting the full path // may leak private information (such as user names). // See https://github.com/rust-lang/rust/issues/87825. // // This default behavior can be overridden by explicitly passing // `-Clink-arg=/PDBALTPATH:...` to rustc. self.cmd.arg("/PDBALTPATH:%_PDB%"); // This will cause the Microsoft linker to embed .natvis info into the PDB file let natvis_dir_path = self.sess.sysroot.join("librustlibetc"); if let Ok(natvis_dir) = fs::read_dir(&natvis_dir_path) { for entry in natvis_dir { match entry { Ok(entry) => { let path = entry.path(); if path.extension() == Some("natvis".as_ref()) { let mut arg = OsString::from("/NATVIS:"); arg.push(path); self.cmd.arg(arg); </ins> } } <del> } // This will cause the Microsoft linker to embed .natvis info for all crates into the PDB file for path in natvis_debugger_visualizers { let mut arg = OsString::from("/NATVIS:"); arg.push(path); self.cmd.arg(arg); </del> <ins> Err(error) => { self.sess.dcx().emit_warn(errors::NoNatvisDirectory { error }); } </ins> } } <del> Strip::Debuginfo | Strip::Symbols => { self.cmd.arg("/DEBUG:NONE"); } </del> <ins> } // This will cause the Microsoft linker to embed .natvis info for all crates into the PDB file for path in natvis_debugger_visualizers { let mut arg = OsString::from("/NATVIS:"); arg.push(path); self.cmd.arg(arg); </ins> } }

Supported values for this option are: <del> - `none` - debuginfo and symbols (if they exist) are copied to the produced binary or separate files depending on the target (e.g. `.pdb` files in case of MSVC). </del> <ins> - `none` - debuginfo and symbols are not modified. </ins> - `debuginfo` - debuginfo sections and debuginfo symbols from the symbol table <del> section are stripped at link time and are not copied to the produced binary or separate files. This should leave backtraces mostly-intact but may make using a debugger like gdb or lldb ineffectual. - `symbols` - same as `debuginfo`, but the rest of the symbol table section is stripped as well, depending on platform support. On platforms which depend on this symbol table for backtraces, profiling, and similar, this can affect them so negatively as to make the trace incomprehensible. Programs which may be combined with others, such as CLI pipelines and developer tooling, or even anything which wants crash-reporting, should usually avoid `-Cstrip=symbols`. Note that, at any level, removing debuginfo only necessarily impacts "friendly" introspection. `-Cstrip` cannot be relied on as a meaningful security or obfuscation measure, as disassemblers and decompilers can extract considerable information even in the absence of symbols. </del> <ins> section are stripped at link time and are not copied to the produced binary. This should leave backtraces mostly-intact but may make using a debugger like gdb or lldb ineffectual. Prior to 1.79, this unintentionally disabled the generation of `*.pdb` files on MSVC, resulting in the absence of symbols. - `symbols` - same as `debuginfo`, but the rest of the symbol table section is stripped as well, depending on platform support. On platforms which depend on this symbol table for backtraces, profiling, and similar, this can affect them so negatively as to make the trace incomprehensible. Programs which may be combined with others, such as CLI pipelines and developer tooling, or even anything which wants crash-reporting, should usually avoid `-Cstrip=symbols`. Note that, at any level, removing debuginfo only necessarily impacts "friendly" introspection. `-Cstrip` cannot be relied on as a meaningful security or obfuscation measure, as disassemblers and decompilers can extract considerable information even in the absence of symbols. </ins> ## symbol-mangling-version

<ins> //@ compile-flags: -C strip=debuginfo //@ only-msvc //@ run-pass use std::path::Path; pub fn is_related_pdb<P: AsRef<Path>>(path: &P, exe: &P) -> bool { let (exe, path) = (exe.as_ref(), path.as_ref()); path.extension() .map(|x| x.to_ascii_lowercase()) .is_some_and(|x| x == "pdb") && path.file_stem() == exe.file_stem() } pub fn main() { let curr_exe = std::env::current_exe().unwrap(); let curr_dir = curr_exe.parent().unwrap(); let entries = std::fs::read_dir(curr_dir).unwrap(); assert!(entries .map_while(|x| x.ok()) .find(|x| is_related_pdb(&x.path(), &curr_exe)) .is_some()); } </ins>

<ins> //@ compile-flags: -C strip=symbols //@ only-msvc //@ run-pass use std::path::Path; pub fn is_related_pdb<P: AsRef<Path>>(path: &P, exe: &P) -> bool { let (exe, path) = (exe.as_ref(), path.as_ref()); path.extension() .map(|x| x.to_ascii_lowercase()) .is_some_and(|x| x == "pdb") && path.file_stem() == exe.file_stem() } pub fn main() { let curr_exe = std::env::current_exe().unwrap(); let curr_dir = curr_exe.parent().unwrap(); let entries = std::fs::read_dir(curr_dir).unwrap(); assert!(entries .map_while(|x| x.ok()) .find(|x| is_related_pdb(&x.path(), &curr_exe)) .is_some()); } </ins>