diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index fd7c58c8e32e2..384596dfff503 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -23,7 +23,7 @@ use rustc_errors::{FatalError, Handler, Level}; use rustc_fs_util::{link_or_copy, path_to_c_string}; use rustc_middle::bug; use rustc_middle::ty::TyCtxt; -use rustc_session::config::{self, Lto, OutputType, Passes, SwitchWithOptPath}; +use rustc_session::config::{self, Lto, OutputType, Passes, SplitDwarfKind, SwitchWithOptPath}; use rustc_session::Session; use rustc_span::symbol::sym; use rustc_span::InnerSpan; @@ -106,7 +106,11 @@ pub fn create_informational_target_machine(sess: &Session) -> &'static mut llvm: pub fn create_target_machine(tcx: TyCtxt<'_>, mod_name: &str) -> &'static mut llvm::TargetMachine { let split_dwarf_file = if tcx.sess.target_can_use_split_dwarf() { - tcx.output_filenames(()).split_dwarf_path(tcx.sess.split_debuginfo(), Some(mod_name)) + tcx.output_filenames(()).split_dwarf_path( + tcx.sess.split_debuginfo(), + tcx.sess.opts.debugging_opts.split_dwarf_kind, + Some(mod_name), + ) } else { None }; @@ -892,17 +896,18 @@ pub(crate) unsafe fn codegen( .generic_activity_with_arg("LLVM_module_codegen_emit_obj", &*module.name); let dwo_out = cgcx.output_filenames.temp_path_dwo(module_name); - let dwo_out = match cgcx.split_debuginfo { - // Don't change how DWARF is emitted in single mode (or when disabled). - SplitDebuginfo::Off | SplitDebuginfo::Packed => None, - // Emit (a subset of the) DWARF into a separate file in split mode. - SplitDebuginfo::Unpacked => { - if cgcx.target_can_use_split_dwarf { - Some(dwo_out.as_path()) - } else { - None - } - } + let dwo_out = match (cgcx.split_debuginfo, cgcx.split_dwarf_kind) { + // Don't change how DWARF is emitted when disabled. + (SplitDebuginfo::Off, _) => None, + // Don't provide a DWARF object path if split debuginfo is enabled but this is + // a platform that doesn't support Split DWARF. + _ if !cgcx.target_can_use_split_dwarf => None, + // Don't provide a DWARF object path in single mode, sections will be written + // into the object as normal but ignored by linker. + (_, SplitDwarfKind::Single) => None, + // Emit (a subset of the) DWARF into a separate dwarf object file in split + // mode. + (_, SplitDwarfKind::Split) => Some(dwo_out.as_path()), }; with_codegen(tm, llmod, config.no_builtins, |cpm| { @@ -939,7 +944,9 @@ pub(crate) unsafe fn codegen( Ok(module.into_compiled_module( config.emit_obj != EmitObj::None, - cgcx.target_can_use_split_dwarf && cgcx.split_debuginfo == SplitDebuginfo::Unpacked, + cgcx.target_can_use_split_dwarf + && cgcx.split_debuginfo != SplitDebuginfo::Off + && cgcx.split_dwarf_kind == SplitDwarfKind::Split, config.emit_bc, &cgcx.output_filenames, )) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 5f9c41891685b..e8d35cf5697f1 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -1072,7 +1072,11 @@ pub fn compile_unit_metadata<'ll, 'tcx>( let output_filenames = tcx.output_filenames(()); let split_name = if tcx.sess.target_can_use_split_dwarf() { output_filenames - .split_dwarf_path(tcx.sess.split_debuginfo(), Some(codegen_unit_name)) + .split_dwarf_path( + tcx.sess.split_debuginfo(), + tcx.sess.opts.debugging_opts.split_dwarf_kind, + Some(codegen_unit_name), + ) // We get a path relative to the working directory from split_dwarf_path .map(|f| tcx.sess.source_map().path_mapping().map_prefix(f).0) } else { diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 42a28f9429845..a59ccc63222c7 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -5,7 +5,7 @@ use rustc_fs_util::fix_windows_verbatim_for_gcc; use rustc_hir::def_id::CrateNum; use rustc_middle::middle::dependency_format::Linkage; use rustc_session::config::{self, CFGuard, CrateType, DebugInfo, LdImpl, Strip}; -use rustc_session::config::{OutputFilenames, OutputType, PrintRequest}; +use rustc_session::config::{OutputFilenames, OutputType, PrintRequest, SplitDwarfKind}; use rustc_session::cstore::DllImport; use rustc_session::output::{check_file_is_writeable, invalid_output_for_target, out_filename}; use rustc_session::search_paths::PathKind; @@ -134,31 +134,47 @@ pub fn link_binary<'a, B: ArchiveBuilder<'a>>( } } - // Remove the temporary object file and metadata if we aren't saving temps + // Remove the temporary object file and metadata if we aren't saving temps. sess.time("link_binary_remove_temps", || { - if !sess.opts.cg.save_temps { - let remove_temps_from_module = |module: &CompiledModule| { - if let Some(ref obj) = module.object { - ensure_removed(sess.diagnostic(), obj); - } - - if let Some(ref obj) = module.dwarf_object { - ensure_removed(sess.diagnostic(), obj); - } - }; + // If the user requests that temporaries are saved, don't delete any. + if sess.opts.cg.save_temps { + return; + } - if sess.opts.output_types.should_link() && !preserve_objects_for_their_debuginfo(sess) { - for module in &codegen_results.modules { - remove_temps_from_module(module); - } + let remove_temps_from_module = |module: &CompiledModule| { + if let Some(ref obj) = module.object { + ensure_removed(sess.diagnostic(), obj); } + }; - if let Some(ref metadata_module) = codegen_results.metadata_module { - remove_temps_from_module(metadata_module); + // Otherwise, always remove the metadata and allocator module temporaries. + if let Some(ref metadata_module) = codegen_results.metadata_module { + remove_temps_from_module(metadata_module); + } + + if let Some(ref allocator_module) = codegen_results.allocator_module { + remove_temps_from_module(allocator_module); + } + + // If no requested outputs require linking, then the object temporaries should + // be kept. + if !sess.opts.output_types.should_link() { + return; + } + + // Potentially keep objects for their debuginfo. + let (preserve_objects, preserve_dwarf_objects) = preserve_objects_for_their_debuginfo(sess); + debug!(?preserve_objects, ?preserve_dwarf_objects); + + for module in &codegen_results.modules { + if !preserve_objects { + remove_temps_from_module(module); } - if let Some(ref allocator_module) = codegen_results.allocator_module { - remove_temps_from_module(allocator_module); + if !preserve_dwarf_objects { + if let Some(ref obj) = module.dwarf_object { + ensure_removed(sess.diagnostic(), obj); + } } } }); @@ -1138,26 +1154,36 @@ pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) { bug!("Not enough information provided to determine how to invoke the linker"); } -/// Returns a boolean indicating whether we should preserve the object files on -/// the filesystem for their debug information. This is often useful with -/// split-dwarf like schemes. -fn preserve_objects_for_their_debuginfo(sess: &Session) -> bool { +/// Returns a pair of boolean indicating whether we should preserve the object and +/// dwarf object files on the filesystem for their debug information. This is often +/// useful with split-dwarf like schemes. +fn preserve_objects_for_their_debuginfo(sess: &Session) -> (bool, bool) { // If the objects don't have debuginfo there's nothing to preserve. if sess.opts.debuginfo == config::DebugInfo::None { - return false; + return (false, false); } // If we're only producing artifacts that are archives, no need to preserve // the objects as they're losslessly contained inside the archives. - let output_linked = - sess.crate_types().iter().any(|&x| x != CrateType::Rlib && x != CrateType::Staticlib); - if !output_linked { - return false; + if sess.crate_types().iter().all(|&x| x.is_archive()) { + return (false, false); + } + + match (sess.split_debuginfo(), sess.opts.debugging_opts.split_dwarf_kind) { + // If there is no split debuginfo then do not preserve objects. + (SplitDebuginfo::Off, _) => (false, false), + // If there is packed split debuginfo, then the debuginfo in the objects + // has been packaged and the objects can be deleted. + (SplitDebuginfo::Packed, _) => (false, false), + // If there is unpacked split debuginfo and the current target can not use + // split dwarf, then keep objects. + (SplitDebuginfo::Unpacked, _) if !sess.target_can_use_split_dwarf() => (true, false), + // If there is unpacked split debuginfo and the target can use split dwarf, then + // keep the object containing that debuginfo (whether that is an object file or + // dwarf object file depends on the split dwarf kind). + (SplitDebuginfo::Unpacked, SplitDwarfKind::Single) => (true, false), + (SplitDebuginfo::Unpacked, SplitDwarfKind::Split) => (false, true), } - - // "unpacked" split debuginfo means that we leave object files as the - // debuginfo is found in the original object files themselves - sess.split_debuginfo() == SplitDebuginfo::Unpacked } fn archive_search_paths(sess: &Session) -> Vec { diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 0281fd929c5fe..bea454458c4c0 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -286,7 +286,11 @@ impl TargetMachineFactoryConfig { module_name: &str, ) -> TargetMachineFactoryConfig { let split_dwarf_file = if cgcx.target_can_use_split_dwarf { - cgcx.output_filenames.split_dwarf_path(cgcx.split_debuginfo, Some(module_name)) + cgcx.output_filenames.split_dwarf_path( + cgcx.split_debuginfo, + cgcx.split_dwarf_kind, + Some(module_name), + ) } else { None }; @@ -329,6 +333,7 @@ pub struct CodegenContext { pub target_arch: String, pub debuginfo: config::DebugInfo, pub split_debuginfo: rustc_target::spec::SplitDebuginfo, + pub split_dwarf_kind: rustc_session::config::SplitDwarfKind, // Number of cgus excluding the allocator/metadata modules pub total_cgus: usize, @@ -1060,6 +1065,7 @@ fn start_executing_work( target_arch: tcx.sess.target.arch.clone(), debuginfo: tcx.sess.opts.debuginfo, split_debuginfo: tcx.sess.split_debuginfo(), + split_dwarf_kind: tcx.sess.opts.debugging_opts.split_dwarf_kind, }; // This is the "main loop" of parallel work happening for parallel codegen. diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index f2c7959ddb6e3..62b351f5e0279 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -231,6 +231,37 @@ pub enum DebugInfo { Full, } +/// Split debug-information is enabled by `-C split-debuginfo`, this enum is only used if split +/// debug-information is enabled (in either `Packed` or `Unpacked` modes), and the platform +/// uses DWARF for debug-information. +/// +/// Some debug-information requires link-time relocation and some does not. LLVM can partition +/// the debuginfo into sections depending on whether or not it requires link-time relocation. Split +/// DWARF provides a mechanism which allows the linker to skip the sections which don't require +/// link-time relocation - either by putting those sections in DWARF object files, or by keeping +/// them in the object file in such a way that the linker will skip them. +#[derive(Clone, Copy, Debug, PartialEq, Hash)] +pub enum SplitDwarfKind { + /// Sections which do not require relocation are written into object file but ignored by the + /// linker. + Single, + /// Sections which do not require relocation are written into a DWARF object (`.dwo`) file + /// which is ignored by the linker. + Split, +} + +impl FromStr for SplitDwarfKind { + type Err = (); + + fn from_str(s: &str) -> Result { + Ok(match s { + "single" => SplitDwarfKind::Single, + "split" => SplitDwarfKind::Split, + _ => return Err(()), + }) + } +} + #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)] #[derive(Encodable, Decodable)] pub enum OutputType { @@ -378,7 +409,7 @@ impl OutputTypes { self.0.len() } - // Returns `true` if any of the output types require codegen or linking. + /// Returns `true` if any of the output types require codegen or linking. pub fn should_codegen(&self) -> bool { self.0.keys().any(|k| match *k { OutputType::Bitcode @@ -391,7 +422,7 @@ impl OutputTypes { }) } - // Returns `true` if any of the output types require linking. + /// Returns `true` if any of the output types require linking. pub fn should_link(&self) -> bool { self.0.keys().any(|k| match *k { OutputType::Bitcode @@ -681,18 +712,23 @@ impl OutputFilenames { pub fn split_dwarf_path( &self, split_debuginfo_kind: SplitDebuginfo, + split_dwarf_kind: SplitDwarfKind, cgu_name: Option<&str>, ) -> Option { let obj_out = self.temp_path(OutputType::Object, cgu_name); let dwo_out = self.temp_path_dwo(cgu_name); - match split_debuginfo_kind { - SplitDebuginfo::Off => None, + match (split_debuginfo_kind, split_dwarf_kind) { + (SplitDebuginfo::Off, SplitDwarfKind::Single | SplitDwarfKind::Split) => None, // Single mode doesn't change how DWARF is emitted, but does add Split DWARF attributes // (pointing at the path which is being determined here). Use the path to the current // object file. - SplitDebuginfo::Packed => Some(obj_out), + (SplitDebuginfo::Packed | SplitDebuginfo::Unpacked, SplitDwarfKind::Single) => { + Some(obj_out) + } // Split mode emits the DWARF into a different file, use that path. - SplitDebuginfo::Unpacked => Some(dwo_out), + (SplitDebuginfo::Packed | SplitDebuginfo::Unpacked, SplitDwarfKind::Split) => { + Some(dwo_out) + } } } } @@ -821,6 +857,18 @@ pub enum CrateType { impl_stable_hash_via_hash!(CrateType); +impl CrateType { + /// When generated, is this crate type an archive? + pub fn is_archive(&self) -> bool { + match *self { + CrateType::Rlib | CrateType::Staticlib => true, + CrateType::Executable | CrateType::Dylib | CrateType::Cdylib | CrateType::ProcMacro => { + false + } + } + } +} + #[derive(Clone, Hash, Debug, PartialEq, Eq)] pub enum Passes { Some(Vec), diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index c8adc9f00a1b9..0b9623d1c7d6f 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -412,6 +412,8 @@ mod desc { pub const parse_wasi_exec_model: &str = "either `command` or `reactor`"; pub const parse_split_debuginfo: &str = "one of supported split-debuginfo modes (`off`, `packed`, or `unpacked`)"; + pub const parse_split_dwarf_kind: &str = + "one of supported split dwarf modes (`split` or `single`)"; pub const parse_gcc_ld: &str = "one of: no value, `lld`"; pub const parse_stack_protector: &str = "one of (`none` (default), `basic`, `strong`, or `all`)"; @@ -941,6 +943,14 @@ mod parse { true } + crate fn parse_split_dwarf_kind(slot: &mut SplitDwarfKind, v: Option<&str>) -> bool { + match v.and_then(|s| SplitDwarfKind::from_str(s).ok()) { + Some(e) => *slot = e, + _ => return false, + } + true + } + crate fn parse_gcc_ld(slot: &mut Option, v: Option<&str>) -> bool { match v { None => *slot = None, @@ -1403,6 +1413,14 @@ options! { "control stack smash protection strategy (`rustc --print stack-protector-strategies` for details)"), strip: Strip = (Strip::None, parse_strip, [UNTRACKED], "tell the linker which information to strip (`none` (default), `debuginfo` or `symbols`)"), + split_dwarf_kind: SplitDwarfKind = (SplitDwarfKind::Split, parse_split_dwarf_kind, [UNTRACKED], + "split dwarf variant (only if -Csplit-debuginfo is enabled and on relevant platform) + (default: `split`) + + `split`: sections which do not require relocation are written into a DWARF object (`.dwo`) + file which is ignored by the linker + `single`: sections which do not require relocation are written into object file but ignored + by the linker"), split_dwarf_inlining: bool = (true, parse_bool, [UNTRACKED], "provide minimal debug info in the object/executable to facilitate online \ symbolication/stack traces in the absence of .dwo/.dwp files when using Split DWARF"), diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 43913183694e0..ca1949b9f75a3 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -479,7 +479,7 @@ pub enum SplitDebuginfo { /// /// * Windows - not supported /// * macOS - supported, scattered object files - /// * ELF - supported, scattered `*.dwo` files + /// * ELF - supported, scattered `*.dwo` or `*.o` files (see `SplitDwarfKind`) Unpacked, } diff --git a/src/test/run-make-fulldeps/split-debuginfo/Makefile b/src/test/run-make-fulldeps/split-debuginfo/Makefile index e8e62efe01c14..292fe5ae55b84 100644 --- a/src/test/run-make-fulldeps/split-debuginfo/Makefile +++ b/src/test/run-make-fulldeps/split-debuginfo/Makefile @@ -44,16 +44,30 @@ off: [ ! -f $(TMPDIR)/*.dwp ] [ ! -f $(TMPDIR)/*.dwo ] -packed: - $(RUSTC) foo.rs -g -C split-debuginfo=packed -Z unstable-options +packed: packed-split packed-single + +packed-split: + $(RUSTC) foo.rs -g -C split-debuginfo=packed -Z unstable-options -Zsplit-dwarf-kind=split + ls $(TMPDIR)/*.dwp + rm -rf $(TMPDIR)/*.dwp $(TMPDIR)/*.dwo + +packed-single: + $(RUSTC) foo.rs -g -C split-debuginfo=packed -Z unstable-options -Zsplit-dwarf-kind=single ls $(TMPDIR)/*.dwp ls $(TMPDIR)/*.dwo && exit 1 || exit 0 rm -rf $(TMPDIR)/*.dwp -unpacked: - $(RUSTC) foo.rs -g -C split-debuginfo=unpacked -Z unstable-options +unpacked: unpacked-split unpacked-single + +unpacked-split: + $(RUSTC) foo.rs -g -C split-debuginfo=unpacked -Z unstable-options -Zsplit-dwarf-kind=split ls $(TMPDIR)/*.dwp && exit 1 || exit 0 ls $(TMPDIR)/*.dwo - rm -rf $(TMPDIR)/*.dwo + rm -rf $(TMPDIR)/*.dwp $(TMPDIR)/*.dwo + +unpacked-single: + $(RUSTC) foo.rs -g -C split-debuginfo=unpacked -Z unstable-options -Zsplit-dwarf-kind=single + ls $(TMPDIR)/*.dwp && exit 1 || exit 0 + ls $(TMPDIR)/*.dwo && exit 1 || exit 0 endif endif