Skip to content

Preserve the .debug_gdb_scripts section #143679

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion compiler/rustc_codegen_gcc/src/debuginfo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,8 @@ impl<'gcc, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
// TODO(antoyo): implement.
}

fn debuginfo_finalize(&self) {
fn debuginfo_finalize(&mut self) {
// TODO: emit section `.debug_gdb_scripts`.
self.context.set_debug_info(true)
}

Expand Down
12 changes: 6 additions & 6 deletions compiler/rustc_codegen_llvm/src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,11 +109,16 @@ pub(crate) fn compile_codegen_unit(
}

// Finalize code coverage by injecting the coverage map. Note, the coverage map will
// also be added to the `llvm.compiler.used` variable, created next.
// also be added to the `llvm.compiler.used` variable, created below.
if cx.sess().instrument_coverage() {
cx.coverageinfo_finalize();
}

// Finalize debuginfo. This adds to `llvm.used`, created below.
if cx.sess().opts.debuginfo != DebugInfo::None {
cx.debuginfo_finalize();
}

// Create the llvm.used and llvm.compiler.used variables.
if !cx.used_statics.is_empty() {
cx.create_used_variable_impl(c"llvm.used", &cx.used_statics);
Expand All @@ -130,11 +135,6 @@ pub(crate) fn compile_codegen_unit(
llvm::LLVMDeleteGlobal(old_g);
}
}

// Finalize debuginfo
if cx.sess().opts.debuginfo != DebugInfo::None {
cx.debuginfo_finalize();
}
}

ModuleCodegen::new_regular(cgu_name.to_string(), llvm_module)
Expand Down
57 changes: 19 additions & 38 deletions compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
// .debug_gdb_scripts binary section.

use rustc_codegen_ssa::base::collect_debugger_visualizers_transitive;
use std::collections::BTreeSet;
use std::ffi::CString;

use rustc_codegen_ssa::traits::*;
use rustc_hir::def_id::LOCAL_CRATE;
use rustc_middle::bug;
use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerType;
use rustc_session::config::{CrateType, DebugInfo};
use rustc_session::config::DebugInfo;

use crate::builder::Builder;
use crate::common::CodegenCx;
Expand All @@ -31,7 +33,12 @@ pub(crate) fn insert_reference_to_gdb_debug_scripts_section_global(bx: &mut Buil
pub(crate) fn get_or_insert_gdb_debug_scripts_section_global<'ll>(
cx: &CodegenCx<'ll, '_>,
) -> &'ll Value {
let c_section_var_name = c"__rustc_debug_gdb_scripts_section__";
let c_section_var_name = CString::new(format!(
"__rustc_debug_gdb_scripts_section_{}_{:08x}",
cx.tcx.crate_name(LOCAL_CRATE),
cx.tcx.stable_crate_id(LOCAL_CRATE),
))
.unwrap();
let section_var_name = c_section_var_name.to_str().unwrap();

let section_var = unsafe { llvm::LLVMGetNamedGlobal(cx.llmod, c_section_var_name.as_ptr()) };
Expand All @@ -44,10 +51,14 @@ pub(crate) fn get_or_insert_gdb_debug_scripts_section_global<'ll>(

// Next, add the pretty printers that were specified via the `#[debugger_visualizer]`
// attribute.
let visualizers = collect_debugger_visualizers_transitive(
cx.tcx,
DebuggerVisualizerType::GdbPrettyPrinter,
);
let visualizers = cx
.tcx
.debugger_visualizers(LOCAL_CRATE)
.iter()
.filter(|visualizer| {
visualizer.visualizer_type == DebuggerVisualizerType::GdbPrettyPrinter
})
.collect::<BTreeSet<_>>();
let crate_name = cx.tcx.crate_name(LOCAL_CRATE);
for (index, visualizer) in visualizers.iter().enumerate() {
// The initial byte `4` instructs GDB that the following pretty printer
Expand Down Expand Up @@ -84,35 +95,5 @@ pub(crate) fn get_or_insert_gdb_debug_scripts_section_global<'ll>(
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just noticed that the section is created with LinkOnceODR linkage. This was fine when all global visualizers were merged together and a single section was created across all crates, but now that we create different sections for each crate, that would cause one of the sections to be chosen non-deterministically. I think it would be better to use regular linkage, but instead enable string merging. This is also what the GDB manual page for .debug_gdb_scripts suggests. I think the way to do that in LLVM is to put the \x01gdb_load_rust_pretty_printers.py\0 as well as every visualizer in a separate constant global variables with unnamed_addr but still in the same .debug_gdb_scripts section.


pub(crate) fn needs_gdb_debug_scripts_section(cx: &CodegenCx<'_, '_>) -> bool {
// To ensure the section `__rustc_debug_gdb_scripts_section__` will not create
// ODR violations at link time, this section will not be emitted for rlibs since
// each rlib could produce a different set of visualizers that would be embedded
// in the `.debug_gdb_scripts` section. For that reason, we make sure that the
// section is only emitted for leaf crates.
let embed_visualizers = cx.tcx.crate_types().iter().any(|&crate_type| match crate_type {
CrateType::Executable
| CrateType::Dylib
| CrateType::Cdylib
| CrateType::Staticlib
| CrateType::Sdylib => {
// These are crate types for which we will embed pretty printers since they
// are treated as leaf crates.
true
}
CrateType::ProcMacro => {
// We could embed pretty printers for proc macro crates too but it does not
// seem like a good default, since this is a rare use case and we don't
// want to slow down the common case.
false
}
CrateType::Rlib => {
// As per the above description, embedding pretty printers for rlibs could
// lead to ODR violations so we skip this crate type as well.
false
}
});

cx.sess().opts.debuginfo != DebugInfo::None
&& cx.sess().target.emit_debug_gdb_scripts
&& embed_visualizers
cx.sess().opts.debuginfo != DebugInfo::None && cx.sess().target.emit_debug_gdb_scripts
}
36 changes: 22 additions & 14 deletions compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ use tracing::debug;

use self::metadata::{UNKNOWN_COLUMN_NUMBER, UNKNOWN_LINE_NUMBER, file_metadata, type_di_node};
use self::namespace::mangled_name_of_instance;
use self::utils::{DIB, create_DIArray, is_node_local_to_unit};
use self::utils::{DIB, create_DIArray, debug_context, is_node_local_to_unit};
use crate::builder::Builder;
use crate::common::{AsCCharPtr, CodegenCx};
use crate::llvm;
Expand Down Expand Up @@ -131,20 +131,28 @@ impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> {
}

/// Creates any deferred debug metadata nodes
pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
if let Some(dbg_cx) = &cx.dbg_cx {
debug!("finalize");

if gdb::needs_gdb_debug_scripts_section(cx) {
// Add a .debug_gdb_scripts section to this compile-unit. This will
// cause GDB to try and load the gdb_load_rust_pretty_printers.py file,
// which activates the Rust pretty printers for binary this section is
// contained in.
gdb::get_or_insert_gdb_debug_scripts_section_global(cx);
}
pub(crate) fn finalize(cx: &mut CodegenCx<'_, '_>) {
if cx.dbg_cx.is_none() {
return;
}

debug!("finalize");

dbg_cx.finalize(cx.sess());
if gdb::needs_gdb_debug_scripts_section(cx) {
// Add a .debug_gdb_scripts section to this compile-unit. This will
// cause GDB to try and load the gdb_load_rust_pretty_printers.py file,
// which activates the Rust pretty printers for binary this section is
// contained in.
let section_var = gdb::get_or_insert_gdb_debug_scripts_section_global(cx);

// Make sure that the linker doesn't optimize the global away. Adding
// it to `llvm.used` has the advantage that it works even in no_std
// binaries, where we don't have a main shim and thus don't emit a
// volatile load to preserve the global.
cx.add_used_global(section_var);
}

debug_context(cx).finalize(cx.sess());
}

impl<'ll> Builder<'_, 'll, '_> {
Expand Down Expand Up @@ -614,7 +622,7 @@ impl<'ll, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
metadata::extend_scope_to_file(self, scope_metadata, file)
}

fn debuginfo_finalize(&self) {
fn debuginfo_finalize(&mut self) {
finalize(self)
}

Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_ssa/src/traits/debuginfo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ pub trait DebugInfoCodegenMethods<'tcx>: BackendTypes {
scope_metadata: Self::DIScope,
file: &SourceFile,
) -> Self::DIScope;
fn debuginfo_finalize(&self);
fn debuginfo_finalize(&mut self);

// FIXME(eddyb) find a common convention for all of the debuginfo-related
// names (choose between `dbg`, `debug`, `debuginfo`, `debug_info` etc.).
Expand Down
4 changes: 3 additions & 1 deletion tests/codegen-llvm/gdb_debug_script_load.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
#![feature(lang_items)]
#![no_std]

// CHECK: @llvm.used = {{.+}} @__rustc_debug_gdb_scripts_section

#[panic_handler]
fn panic_handler(_: &core::panic::PanicInfo) -> ! {
loop {}
Expand All @@ -22,7 +24,7 @@ extern "C" fn rust_eh_personality() {
// Needs rustc to generate `main` as that's where the magic load is inserted.
// IOW, we cannot write this test with `#![no_main]`.
// CHECK-LABEL: @main
// CHECK: load volatile i8, {{.+}} @__rustc_debug_gdb_scripts_section__
// CHECK: load volatile i8, {{.+}} @__rustc_debug_gdb_scripts_section

#[lang = "start"]
fn lang_start<T: 'static>(
Expand Down
35 changes: 21 additions & 14 deletions tests/run-make/symbols-all-mangled/rmake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,7 @@ fn symbols_check_archive(path: &str) {
continue; // All compiler-builtins symbols must remain unmangled
}

if name.contains("rust_eh_personality") {
continue; // Unfortunately LLVM doesn't allow us to mangle this symbol
}

if name.contains(".llvm.") {
// Starting in LLVM 21 we get various implementation-detail functions which
// contain .llvm. that are not a problem.
if symbol_ok_everywhere(name) {
continue;
}

Expand Down Expand Up @@ -71,13 +65,7 @@ fn symbols_check(path: &str) {
continue;
}

if name.contains("rust_eh_personality") {
continue; // Unfortunately LLVM doesn't allow us to mangle this symbol
}

if name.contains(".llvm.") {
// Starting in LLVM 21 we get various implementation-detail functions which
// contain .llvm. that are not a problem.
if symbol_ok_everywhere(name) {
continue;
}

Expand All @@ -88,3 +76,22 @@ fn symbols_check(path: &str) {
fn strip_underscore_if_apple(symbol: &str) -> &str {
if cfg!(target_vendor = "apple") { symbol.strip_prefix("_").unwrap() } else { symbol }
}

fn symbol_ok_everywhere(name: &str) -> bool {
if name.contains("rust_eh_personality") {
return true; // Unfortunately LLVM doesn't allow us to mangle this symbol
}

if name.contains(".llvm.") {
// Starting in LLVM 21 we get various implementation-detail functions which
// contain .llvm. that are not a problem.
return true;
}

if name.starts_with("__rustc_debug_gdb_scripts_section") {
// These symbols are fine; they're made unique by the crate ID.
return true;
}

return false;
}
Loading