From 887053414612c89b8d2741feed6f810a09e18356 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Mon, 6 Nov 2023 21:26:32 -0500 Subject: [PATCH 1/3] test(trim-paths): verify `SO` symbols didn't get remapped When `--remap-path-scope=object` is specified, user expect that there is no local path embedded in final executables. Under `object` scope, the current implementation only remap debug symbols if debug info is splitted into its own file. In other words, when `split-debuginfo=packed|unpacked` is set, rustc assumes there is no embedded path in the final executable needing to be remapped. However, this doesn't work on macOS. On macOS, `SO` symbols are embedded in binary executables and libraries regardless a split-debuginfo file is built. Each `SO` symbol contains a path to the root source file of a debug info compile unit. This commit demonstrates the case, and hope there is a fix soon. --- tests/run-make/split-debuginfo/Makefile | 78 +++++++++++++++++++++---- 1 file changed, 67 insertions(+), 11 deletions(-) diff --git a/tests/run-make/split-debuginfo/Makefile b/tests/run-make/split-debuginfo/Makefile index 9e05c8dc179bd..455f3fc513ca5 100644 --- a/tests/run-make/split-debuginfo/Makefile +++ b/tests/run-make/split-debuginfo/Makefile @@ -1,6 +1,8 @@ # ignore-cross-compile include ../tools.mk +export HERE := $(shell pwd) + all: off packed unpacked ifeq ($(UNAME),Darwin) @@ -11,23 +13,77 @@ off: [ ! -d $(TMPDIR)/foo.dSYM ] # Packed by default, but only if debuginfo is requested -packed: - rm -rf $(TMPDIR)/*.dSYM - $(RUSTC) foo.rs - [ ! -d $(TMPDIR)/foo.dSYM ] - rm -rf $(TMPDIR)/*.dSYM - $(RUSTC) foo.rs -g +packed: packed-remapped-scope packed-remapped-wrong-scope + +# - Debuginfo in binary file +# - `.o` deleted +# - `.dSYM` present +# - in binary, paths from `N_SO` (source files) and `N_OSO` (object files) shouldn be remapped +packed-remapped-scope: + $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=packed -C debuginfo=2 \ + --remap-path-prefix $(TMPDIR)=/a \ + --remap-path-prefix $(HERE)=/b \ + -Z remap-path-scope=object foo.rs -g + ls $(TMPDIR)/*.o && exit 1 || exit 0 [ -d $(TMPDIR)/foo.dSYM ] - rm -rf $(TMPDIR)/*.dSYM - $(RUSTC) foo.rs -g -C split-debuginfo=packed + # As of 2023-12, `OSO` should be the only thing that cannot be trimmed. See rust-lang/rust#116948 + dsymutil -s $(TMPDIR)/foo | grep $(TMPDIR) || exit 1 # expected: `grep $(TMPDIR)` to exit 1 + dsymutil -s $(TMPDIR)/foo | grep $(HERE) || exit 1 # expected: `grep $(HERE)` to exit 1 + rm -rf $(TMPDIR)/foo.dSYM + rm $(TMPDIR)/$(call BIN,foo) + +# - Debuginfo in binary file +# - `.o` deleted +# - `.dSYM` present +# - in binary, paths from `N_SO` (source files) and `N_OSO` (object files) shouldn't be remapped +packed-remapped-wrong-scope: + $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=packed -C debuginfo=2 \ + --remap-path-prefix $(TMPDIR)=/a \ + --remap-path-prefix $(HERE)=/b \ + -Z remap-path-scope=macro foo.rs -g + ls $(TMPDIR)/*.o && exit 1 || exit 0 [ -d $(TMPDIR)/foo.dSYM ] - rm -rf $(TMPDIR)/*.dSYM + dsymutil -s $(TMPDIR)/foo | grep 'N_OSO' | grep $(TMPDIR) || exit 1 + dsymutil -s $(TMPDIR)/foo | grep 'N_SO' | grep $(HERE) || exit 1 + rm -rf $(TMPDIR)/foo.dSYM + rm $(TMPDIR)/$(call BIN,foo) # Object files are preserved with unpacked and `dsymutil` isn't run -unpacked: - $(RUSTC) foo.rs -g -C split-debuginfo=unpacked +unpacked: unpacked-remapped-scope unpacked-remapped-wrong-scope + +# - Debuginfo in object files +# - `.o` present +# - `.dSYM` never created +# - in binary, paths from `N_SO` (source files) and `N_OSO` (object files) should be remapped +unpacked-remapped-scope: + $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=unpacked -C debuginfo=2 \ + --remap-path-prefix $(TMPDIR)=/a \ + --remap-path-prefix $(HERE)=/b \ + -Z remap-path-scope=split-debuginfo-path foo.rs -g ls $(TMPDIR)/*.o [ ! -d $(TMPDIR)/foo.dSYM ] + # As of 2023-12, `OSO` should be the only thing that cannot be trimmed. See rust-lang/rust#116948 + dsymutil -s $(TMPDIR)/foo | grep $(TMPDIR) || exit 1 # expected: `grep $(TMPDIR)` to exit 1 + dsymutil -s $(TMPDIR)/foo | grep $(HERE) || exit 1 # expected: `grep $(HERE)` to exit 1 + rm $(TMPDIR)/*.o + rm $(TMPDIR)/$(call BIN,foo) + +# - Debuginfo in object files +# - `.o` present, +# - `.dSYM` never created +# - in binary, paths from `N_SO` (source files) and `N_OSO` (object files) shouldn't be remapped +unpacked-remapped-wrong-scope: + $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=unpacked -C debuginfo=2 \ + --remap-path-prefix $(TMPDIR)=/a \ + --remap-path-prefix $(HERE)=/b \ + -Z remap-path-scope=macro foo.rs -g + ls $(TMPDIR)/*.o + [ ! -d $(TMPDIR)/foo.dSYM ] + dsymutil -s $(TMPDIR)/foo | grep 'N_OSO' | (grep $(TMPDIR)) || exit 1 + dsymutil -s $(TMPDIR)/foo | grep 'N_SO' | (grep $(HERE)) || exit 1 + rm $(TMPDIR)/*.o + rm $(TMPDIR)/$(call BIN,foo) + else ifdef IS_WINDOWS # Windows only supports packed debuginfo - nothing to test. From f84ef00a0688d7e604a27e72d2e0b74893134a37 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Thu, 7 Dec 2023 22:39:27 +0000 Subject: [PATCH 2/3] test(trim-paths): verify root `DW_AT_comp_dir` didn't get remapped When `--remap-path-scope=object` is specified, user expect that there is no local path embedded in final executables. Under `object` scope, the current implementation only remap debug symbols if debug info is splitted into its own file. In other words, when `split-debuginfo=packed|unpacked` is set, rustc assumes there is no embedded path in the final executable needing to be remapped. However, this doesn't work on Linux. On Linux, the root `DW_AT_comp_dir` of a compile unit seems to go into the binary binary executables. This commit demonstrates the case, and hope there is a fix soon. --- tests/run-make/split-debuginfo/Makefile | 66 ++++++++++++++++++------- 1 file changed, 47 insertions(+), 19 deletions(-) diff --git a/tests/run-make/split-debuginfo/Makefile b/tests/run-make/split-debuginfo/Makefile index 455f3fc513ca5..d417fd147bff0 100644 --- a/tests/run-make/split-debuginfo/Makefile +++ b/tests/run-make/split-debuginfo/Makefile @@ -59,7 +59,7 @@ unpacked-remapped-scope: $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=unpacked -C debuginfo=2 \ --remap-path-prefix $(TMPDIR)=/a \ --remap-path-prefix $(HERE)=/b \ - -Z remap-path-scope=split-debuginfo-path foo.rs -g + -Z remap-path-scope=object foo.rs -g ls $(TMPDIR)/*.o [ ! -d $(TMPDIR)/foo.dSYM ] # As of 2023-12, `OSO` should be the only thing that cannot be trimmed. See rust-lang/rust#116948 @@ -169,8 +169,12 @@ packed-remapped: packed-remapped-split packed-remapped-single packed-remapped-sc # - `.dwp` present packed-remapped-split: $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=packed -C debuginfo=2 \ - -Z split-dwarf-kind=split --remap-path-prefix $(TMPDIR)=/a foo.rs -g - objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (! grep $(TMPDIR)) || exit 1 + -Z split-dwarf-kind=split \ + --remap-path-prefix $(TMPDIR)=/a \ + --remap-path-prefix $(HERE)=/b \ + foo.rs -g + readelf -wi $(TMPDIR)/foo | (! grep $(TMPDIR)) || exit 1 + readelf -wi $(TMPDIR)/foo | (! grep $(HERE)) || exit 1 ls $(TMPDIR)/*.o && exit 1 || exit 0 ls $(TMPDIR)/*.dwo && exit 1 || exit 0 rm $(TMPDIR)/foo.dwp @@ -183,8 +187,12 @@ packed-remapped-split: # - `.dwp` present packed-remapped-single: $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=packed -C debuginfo=2 \ - -Z split-dwarf-kind=single --remap-path-prefix $(TMPDIR)=/a foo.rs -g - objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (! grep $(TMPDIR)) || exit 1 + -Z split-dwarf-kind=single \ + --remap-path-prefix $(TMPDIR)=/a \ + --remap-path-prefix $(HERE)=/b \ + foo.rs -g + readelf -wi $(TMPDIR)/foo | (! grep $(TMPDIR)) || exit 1 + readelf -wi $(TMPDIR)/foo | (! grep $(HERE)) || exit 1 ls $(TMPDIR)/*.o && exit 1 || exit 0 ls $(TMPDIR)/*.dwo && exit 1 || exit 0 rm $(TMPDIR)/foo.dwp @@ -197,9 +205,12 @@ packed-remapped-single: # - `.dwp` present packed-remapped-scope: $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=packed -C debuginfo=2 \ - -Z split-dwarf-kind=single --remap-path-prefix $(TMPDIR)=/a \ - -Z remap-path-scope=split-debuginfo-path foo.rs -g - objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (! grep $(TMPDIR)) || exit 1 + -Z split-dwarf-kind=single \ + --remap-path-prefix $(TMPDIR)=/a \ + --remap-path-prefix $(HERE)=/b \ + -Z remap-path-scope=object foo.rs -g + readelf -wi $(TMPDIR)/foo | (! grep $(TMPDIR)) || exit 1 + readelf -wi $(TMPDIR)/foo | grep $(HERE) || exit 1 # expected: `grep $(HERE)` to exit 1 ls $(TMPDIR)/*.o && exit 1 || exit 0 ls $(TMPDIR)/*.dwo && exit 1 || exit 0 rm $(TMPDIR)/foo.dwp @@ -212,9 +223,12 @@ packed-remapped-scope: # - `.dwp` present packed-remapped-wrong-scope: $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=packed -C debuginfo=2 \ - -Z split-dwarf-kind=single --remap-path-prefix $(TMPDIR)=/a \ + -Z split-dwarf-kind=single \ + --remap-path-prefix $(TMPDIR)=/a \ + --remap-path-prefix $(TMPDIR)=/b \ -Z remap-path-scope=macro foo.rs -g - objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (grep $(TMPDIR)) || exit 1 + readelf -wi $(TMPDIR)/foo | (grep $(TMPDIR)) || exit 1 + readelf -wi $(TMPDIR)/foo | grep $(HERE) || exit 1 ls $(TMPDIR)/*.o && exit 1 || exit 0 ls $(TMPDIR)/*.dwo && exit 1 || exit 0 rm $(TMPDIR)/foo.dwp @@ -325,8 +339,12 @@ unpacked-remapped: unpacked-remapped-split unpacked-remapped-single unpacked-rem # - `.dwp` never created unpacked-remapped-split: $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=unpacked -C debuginfo=2 \ - -Z split-dwarf-kind=split --remap-path-prefix $(TMPDIR)=/a foo.rs -g - objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (! grep $(TMPDIR)) || exit 1 + -Z split-dwarf-kind=split \ + --remap-path-prefix $(TMPDIR)=/a \ + --remap-path-prefix $(HERE)=/b \ + foo.rs -g + readelf -wi $(TMPDIR)/foo | (! grep $(TMPDIR)) || exit 1 + readelf -wi $(TMPDIR)/foo | (! grep $(HERE)) || exit 1 ls $(TMPDIR)/*.o && exit 1 || exit 0 rm $(TMPDIR)/*.dwo ls $(TMPDIR)/*.dwp && exit 1 || exit 0 @@ -339,8 +357,12 @@ unpacked-remapped-split: # - `.dwp` never created unpacked-remapped-single: $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=unpacked -C debuginfo=2 \ - -Z split-dwarf-kind=single --remap-path-prefix $(TMPDIR)=/a foo.rs -g - objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (! grep $(TMPDIR)) || exit 1 + -Z split-dwarf-kind=single \ + --remap-path-prefix $(TMPDIR)=/a \ + --remap-path-prefix $(HERE)=/b \ + foo.rs -g + readelf -wi $(TMPDIR)/foo | (! grep $(TMPDIR)) || exit 1 + readelf -wi $(TMPDIR)/foo | (! grep $(HERE)) || exit 1 rm $(TMPDIR)/*.o ls $(TMPDIR)/*.dwo && exit 1 || exit 0 ls $(TMPDIR)/*.dwp && exit 1 || exit 0 @@ -353,9 +375,12 @@ unpacked-remapped-single: # - `.dwp` never created unpacked-remapped-scope: $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=unpacked -C debuginfo=2 \ - -Z split-dwarf-kind=single --remap-path-prefix $(TMPDIR)=/a \ - -Z remap-path-scope=split-debuginfo-path foo.rs -g - objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (! grep $(TMPDIR)) || exit 1 + -Z split-dwarf-kind=single \ + --remap-path-prefix $(TMPDIR)=/a \ + --remap-path-prefix $(HERE)=/b \ + -Z remap-path-scope=object foo.rs -g + readelf -wi $(TMPDIR)/foo | (! grep $(TMPDIR)) || exit 1 + readelf -wi $(TMPDIR)/foo | grep $(HERE) || exit 1 # expected: `grep $(HERE)` to exit 1 rm $(TMPDIR)/*.o ls $(TMPDIR)/*.dwo && exit 1 || exit 0 ls $(TMPDIR)/*.dwp && exit 1 || exit 0 @@ -368,9 +393,12 @@ unpacked-remapped-scope: # - `.dwp` never created unpacked-remapped-wrong-scope: $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=unpacked -C debuginfo=2 \ - -Z split-dwarf-kind=single --remap-path-prefix $(TMPDIR)=/a \ + -Z split-dwarf-kind=single \ + --remap-path-prefix $(TMPDIR)=/a \ + --remap-path-prefix $(HERE)=/b \ -Z remap-path-scope=macro foo.rs -g - objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (grep $(TMPDIR)) || exit 1 + readelf -wi $(TMPDIR)/foo | grep $(TMPDIR) || exit 1 + readelf -wi $(TMPDIR)/foo | grep $(HERE) || exit 1 rm $(TMPDIR)/*.o ls $(TMPDIR)/*.dwo && exit 1 || exit 0 ls $(TMPDIR)/*.dwp && exit 1 || exit 0 From 4fa22f1ba3ab299ba45184fdc166d4f95cd6d720 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Thu, 7 Dec 2023 22:41:27 -0500 Subject: [PATCH 3/3] fix(trim-paths): trim working diretory from root DI node Path of working directory in the root DI node seems to be embedded in executables. Hence, we trim them when the scope of `unsplit-debuginfo` is present, as if it is kinda a "unsplit" debuginfo. --- .../src/debuginfo/metadata.rs | 17 +++++++++++++++-- tests/run-make/split-debuginfo/Makefile | 8 ++++---- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index cf78fc56b498c..99907dbc41a7c 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -851,9 +851,22 @@ pub fn build_compile_unit_di_node<'ll, 'tcx>( // FIXME(#41252) Remove "clang LLVM" if we can get GDB and LLVM to play nice. let producer = format!("clang LLVM ({rustc_producer})"); - use rustc_session::RemapFileNameExt; let name_in_debuginfo = name_in_debuginfo.to_string_lossy(); - let work_dir = tcx.sess.opts.working_dir.for_codegen(tcx.sess).to_string_lossy(); + debug!(?name_in_debuginfo, "build_compile_unit_di_node"); + + // Path of working directory in the root DI node seems to be embedded in + // executables. Hence, we trim them when the scope of `unsplit-debuginfo` + // is present, as if it is kinda a "unsplit" debuginfo. + use rustc_session::config::RemapPathScopeComponents; + use rustc_session::RemapFileNameExt; + let work_dir = tcx + .sess + .opts + .working_dir + .for_scope(tcx.sess, RemapPathScopeComponents::UNSPLIT_DEBUGINFO) + .to_string_lossy(); + debug!(?work_dir, "build_compile_unit_di_node"); + let flags = "\0"; let output_filenames = tcx.output_filenames(()); let split_name = if tcx.sess.target_can_use_split_dwarf() { diff --git a/tests/run-make/split-debuginfo/Makefile b/tests/run-make/split-debuginfo/Makefile index d417fd147bff0..9338d658c5084 100644 --- a/tests/run-make/split-debuginfo/Makefile +++ b/tests/run-make/split-debuginfo/Makefile @@ -28,7 +28,7 @@ packed-remapped-scope: [ -d $(TMPDIR)/foo.dSYM ] # As of 2023-12, `OSO` should be the only thing that cannot be trimmed. See rust-lang/rust#116948 dsymutil -s $(TMPDIR)/foo | grep $(TMPDIR) || exit 1 # expected: `grep $(TMPDIR)` to exit 1 - dsymutil -s $(TMPDIR)/foo | grep $(HERE) || exit 1 # expected: `grep $(HERE)` to exit 1 + dsymutil -s $(TMPDIR)/foo | (! grep $(HERE)) || exit 1 rm -rf $(TMPDIR)/foo.dSYM rm $(TMPDIR)/$(call BIN,foo) @@ -64,7 +64,7 @@ unpacked-remapped-scope: [ ! -d $(TMPDIR)/foo.dSYM ] # As of 2023-12, `OSO` should be the only thing that cannot be trimmed. See rust-lang/rust#116948 dsymutil -s $(TMPDIR)/foo | grep $(TMPDIR) || exit 1 # expected: `grep $(TMPDIR)` to exit 1 - dsymutil -s $(TMPDIR)/foo | grep $(HERE) || exit 1 # expected: `grep $(HERE)` to exit 1 + dsymutil -s $(TMPDIR)/foo | (! grep $(HERE)) || exit 1 rm $(TMPDIR)/*.o rm $(TMPDIR)/$(call BIN,foo) @@ -210,7 +210,7 @@ packed-remapped-scope: --remap-path-prefix $(HERE)=/b \ -Z remap-path-scope=object foo.rs -g readelf -wi $(TMPDIR)/foo | (! grep $(TMPDIR)) || exit 1 - readelf -wi $(TMPDIR)/foo | grep $(HERE) || exit 1 # expected: `grep $(HERE)` to exit 1 + readelf -wi $(TMPDIR)/foo | (! grep $(HERE)) || exit 1 ls $(TMPDIR)/*.o && exit 1 || exit 0 ls $(TMPDIR)/*.dwo && exit 1 || exit 0 rm $(TMPDIR)/foo.dwp @@ -380,7 +380,7 @@ unpacked-remapped-scope: --remap-path-prefix $(HERE)=/b \ -Z remap-path-scope=object foo.rs -g readelf -wi $(TMPDIR)/foo | (! grep $(TMPDIR)) || exit 1 - readelf -wi $(TMPDIR)/foo | grep $(HERE) || exit 1 # expected: `grep $(HERE)` to exit 1 + readelf -wi $(TMPDIR)/foo | (! grep $(HERE)) || exit 1 rm $(TMPDIR)/*.o ls $(TMPDIR)/*.dwo && exit 1 || exit 0 ls $(TMPDIR)/*.dwp && exit 1 || exit 0