From b23527170d2953ae6982944c0ff3dfa3f0f13cc6 Mon Sep 17 00:00:00 2001 From: Irene Siboni Date: Thu, 24 Sep 2026 12:02:12 +0200 Subject: [PATCH] Add .debug_sup support Signed-off-by: Irene Siboni --- dev/build.rs | 36 +++++ src/dwarf/debug_sup.rs | 324 +++++++++++++++++++++++++++++++++++++++++ src/dwarf/mod.rs | 1 + src/dwarf/resolver.rs | 170 ++++++++++++++++++--- tests/suite/inspect.rs | 29 +++- 5 files changed, 528 insertions(+), 32 deletions(-) create mode 100644 src/dwarf/debug_sup.rs diff --git a/dev/build.rs b/dev/build.rs index aaf73bad3..a115a984d 100644 --- a/dev/build.rs +++ b/dev/build.rs @@ -856,6 +856,42 @@ fn prepare_test_files() { ], ); + dwarf(&src, "test-stable-addrs-dwarf-only-sup.dbg"); + dwarf(&src, "test-stable-addrs-dwarf-only-broken-sup.dbg"); + let dbg = data_dir.join("test-stable-addrs-dwarf-only-sup.dbg"); + let broken_dbg = data_dir.join("test-stable-addrs-dwarf-only-broken-sup.dbg"); + dwz( + &data_dir.join("test-stable-addrs-dwarf-5.dwz"), + &[ + "--relative", + "--dwarf-5", + dbg.to_str().unwrap(), + broken_dbg.to_str().unwrap(), + ], + ); + let () = adjust_mtime(&dbg).unwrap(); + objcopy( + &src, + "test-stable-addrs-stripped-with-sup.bin", + &[ + "--strip-all", + &format!("--add-gnu-debuglink={}", dbg.display()), + ], + ); + objcopy( + &broken_dbg, + broken_dbg.as_os_str(), + &["--remove-section=.debug_sup"], + ); + objcopy( + &src, + "test-stable-addrs-stripped-with-broken-sup.bin", + &[ + "--strip-all", + &format!("--add-gnu-debuglink={}", broken_dbg.display()), + ], + ); + gnu_debugdata(&src, "test-stable-addrs-debugdata.bin"); let elf = data_dir.join("test-stable-addrs-no-dwarf.bin"); diff --git a/src/dwarf/debug_sup.rs b/src/dwarf/debug_sup.rs new file mode 100644 index 000000000..9b1b8e951 --- /dev/null +++ b/src/dwarf/debug_sup.rs @@ -0,0 +1,324 @@ +//! Support for reading of GNU .`debug_sup` data as prescribed in DWARF v5; +//! .`debug_sup` replaces .`gnu_debugaltlink` in the current standard and +//! plays the same role: it functions as a pointer to a +//! supplementary object file. +//! +//! .`debug_sup` is a special section of executable files that should contain: +//! - `version`: u16 representing the version of the DWARF information for the +//! compilation unit +//! - `is_supplementary`: u8 which is set to 1 if the file in which the +//! section is stored is a supplementary file, 0 o.w. +//! - `sup_filename`: null-terminated supplementary file name or path (is +//! !`is_supplementary`) +//! - `sup_checksum_len`: uleb128 indicating the length of the following +//! checksum field +//! - `sup_checksum`: [u8: `sup_checksum_len`] unique supplementary file id + +use std::borrow::Cow; +use std::path::Path; + +use crate::elf::ElfParser; +use crate::error::Error; +use crate::error::IntoError as _; +use crate::util::bytes_to_path; +use crate::util::ReadRaw as _; +use crate::Result; + + +#[derive(Debug)] +pub(crate) struct DebugSupInfo<'a> { + pub _version: u16, + pub is_supplementary: bool, + pub path: Option<&'a Path>, + pub checksum: Cow<'a, [u8]>, +} + + +/// Read the debug sup section. +pub(crate) fn read_debug_sup(parser: &ElfParser) -> Result>> { + let debug_sup_section = ".debug_sup"; + let idx = if let Ok(Some(idx)) = parser.find_section(debug_sup_section) { + idx + } else { + return Ok(None) + }; + + // SANITY: We just found the index so the section should always be + // found. + let data = parser.section_data(idx).unwrap(); + parse_debug_sup_section_data(data) +} + + +fn parse_debug_sup_section_data(mut data: &[u8]) -> Result>> { + let version = data + .read_u16() + .ok_or_invalid_data(|| "failed to read .debug_sup version")?; + + let is_supplementary = match data + .read_u8() + .ok_or_invalid_data(|| "failed to read .debug_sup is_supplementary")? + { + 0 => false, + 1 => true, + v => { + return Err(Error::with_invalid_data(format!( + "invalid is_supplementary field value: {v}" + ))) + } + }; + + let path_string = data + .read_cstr() + .ok_or_invalid_data(|| "failed to read .debug_sup file field")?; + let path = if !is_supplementary { + bytes_to_path(path_string.to_bytes()).ok() + } else { + None + }; + + // TODO: Use `Path::is_empty` once our MSRV is >= 1.98. + if !is_supplementary && path_string.as_ref().is_empty() { + return Err(Error::with_invalid_data("debug sup target is empty")) + } else if is_supplementary && !path_string.as_ref().is_empty() { + return Err(Error::with_invalid_data( + "debug sup target is not empty in a supplementary file", + )) + } + + let sup_checksum_len = data + .read_u64_leb128() + .ok_or_invalid_data(|| "failed to read .debug_sup sup_checksum_len")?; + + let checksum = data + .read_slice(sup_checksum_len.try_into().unwrap()) + .ok_or_invalid_data(|| "failed to read .debug_sup checksum")?; + let checksum = Cow::Borrowed(checksum); + Ok(Some(DebugSupInfo { + _version: version, + is_supplementary, + path, + checksum, + })) +} + + +#[cfg(test)] +mod tests { + use super::*; + + use std::mem::size_of_val; + use std::slice; + + use test_tag::tag; + + + /// Check that we can correctly read the checksum from a `.debug_sup` + /// section data. + #[tag(miri)] + #[test] + fn unaligned_debug_sup_parsing() { + let section_data = [ + 0x5, 0x0, 0x0, b'.', b'.', b'/', b'.', b'.', b'/', b'.', b'd', b'w', b'z', b'/', b'p', + b'r', b'o', b'g', b'r', b'a', b'm', 0x0, 0x14, 0x7f, 0xd3, 0x76, 0x0a, 0xf3, 0x98, + 0xa1, 0xcc, 0x3f, 0x90, 0x45, 0x69, 0x9a, 0xda, 0x29, 0xe0, 0xb6, 0x6b, 0x45, 0xc8, + ]; + + let mut buffer = [0u64; 8]; + let buffer = unsafe { + slice::from_raw_parts_mut( + buffer.as_mut_ptr().cast::(), + buffer.len() * size_of_val(&buffer[0]), + ) + }; + + // Make the buffer unaligned. + let buffer = &mut buffer[3..3 + section_data.len()]; + // Now write the section data into it. + let () = buffer.copy_from_slice(§ion_data); + + let DebugSupInfo { + _version: version, + is_supplementary, + path, + checksum, + } = parse_debug_sup_section_data(buffer).unwrap().unwrap(); + + assert_eq!(version, 5); + assert!(!is_supplementary); + assert!(path.is_some()); + assert_eq!(path.unwrap(), Path::new("../../.dwz/program")); + assert_eq!( + checksum.as_ref(), + [ + 0x7f, 0xd3, 0x76, 0x0a, 0xf3, 0x98, 0xa1, 0xcc, 0x3f, 0x90, 0x45, 0x69, 0x9a, 0xda, + 0x29, 0xe0, 0xb6, 0x6b, 0x45, 0xc8 + ] + ); + } + + /// Check that we handle empty version in `.debug_sup` section. + #[tag(miri)] + #[test] + fn empty_version_debug_sup_parsing() { + let section_data = &[]; + + let result = parse_debug_sup_section_data(section_data); + assert!(result.is_err()); + let error = result.unwrap_err(); + assert!(error.to_string().contains("version")); + } + + /// Check that we handle no `is_supplementary` in debug sup section. + #[tag(miri)] + #[test] + fn no_is_supplementary_debug_sup_parsing() { + let section_data = &[0x5, 0x0]; + + let result = parse_debug_sup_section_data(section_data); + assert!(result.is_err()); + let error = result.unwrap_err(); + assert!(error + .to_string() + .contains("failed to read .debug_sup is_supplementary")); + } + + /// Check that we handle invalid `is_supplementary` in debug sup section. + #[tag(miri)] + #[test] + fn invalid_is_supplementary_debug_sup_parsing() { + let section_data = &[0x5, 0x0, 0x2]; + + let result = parse_debug_sup_section_data(section_data); + assert!(result.is_err()); + let error = result.unwrap_err(); + assert!(error.to_string().contains("invalid is_supplementary")); + } + + /// Check that we handle no path in debug sup section. + #[tag(miri)] + #[test] + fn no_path_debug_sup_parsing() { + let section_data = &[0x5, 0x0, 0x0]; + + let result = parse_debug_sup_section_data(section_data); + assert!(result.is_err()); + let error = result.unwrap_err(); + assert!(error.to_string().contains("file field")); + } + + /// Check that we handle empty path in debug sup section. + #[tag(miri)] + #[test] + fn empty_path_debug_sup_parsing() { + let section_data = &[ + 0x5, 0x0, 0x0, 0x0, 0x14, 0x7f, 0xd3, 0x76, 0x0a, 0xf3, 0x98, 0xa1, 0xcc, 0x3f, 0x90, + 0x45, 0x69, 0x9a, 0xda, 0x29, 0xe0, 0xb6, 0x6b, 0x45, 0xc8, + ]; + + let result = parse_debug_sup_section_data(section_data); + assert!(result.is_err()); + let error = result.unwrap_err(); + assert!(error.to_string().contains("debug sup target is empty")); + } + + /// Check that we handle not empty path in debug sup section of + /// supplementary file. + #[tag(miri)] + #[test] + fn supplementary_not_empty_path_debug_sup_parsing() { + let section_data = &[ + 0x5, 0x0, 0x1, b'.', b'.', b'/', b'.', b'.', b'/', b'.', b'd', b'w', b'z', b'/', b'p', + b'r', b'o', b'g', b'r', b'a', b'm', 0x0, 0x14, 0x7f, 0xd3, 0x76, 0x0a, 0xf3, 0x98, + 0xa1, 0xcc, 0x3f, 0x90, 0x45, 0x69, 0x9a, 0xda, 0x29, 0xe0, 0xb6, 0x6b, 0x45, 0xc8, + ]; + + let result = parse_debug_sup_section_data(section_data); + assert!(result.is_err()); + let error = result.unwrap_err(); + assert!(error + .to_string() + .contains("debug sup target is not empty in a supplementary file")); + } + + /// Check that we handle empty checksum lenght in debug sup section. + #[tag(miri)] + #[test] + fn empty_checksum_len_debug_sup_parsing() { + let section_data = &[ + 0x5, 0x0, 0x0, b'.', b'.', b'/', b'.', b'.', b'/', b'.', b'd', b'w', b'z', b'/', b'p', + b'r', b'o', b'g', b'r', b'a', b'm', 0x0, + ]; + + let result = parse_debug_sup_section_data(section_data); + assert!(result.is_err()); + let error = result.unwrap_err(); + assert!(error.to_string().contains("sup_checksum_len")); + } + + /// Check that we handle empty checksum in debug sup section. + #[tag(miri)] + #[test] + fn empty_checksum_debug_sup_parsing() { + let section_data = &[ + 0x5, 0x0, 0x0, b'.', b'.', b'/', b'.', b'.', b'/', b'.', b'd', b'w', b'z', b'/', b'p', + b'r', b'o', b'g', b'r', b'a', b'm', 0x0, 0x14, + ]; + + let result = parse_debug_sup_section_data(section_data); + assert!(result.is_err()); + let error = result.unwrap_err(); + assert!(error.to_string().contains("checksum")); + } + + /// Check that we handle wrong checksum length in debug sup section. + #[tag(miri)] + #[test] + fn wrong_checksum_len_debug_sup_parsing() { + let section_data = &[ + 0x5, 0x0, 0x0, b'.', b'.', b'/', b'.', b'.', b'/', b'.', b'd', b'w', b'z', b'/', b'p', + b'r', b'o', b'g', b'r', b'a', b'm', 0x0, 0x20, 0x7f, 0xd3, 0x76, 0x0a, 0xf3, 0x98, + 0xa1, 0xcc, 0x3f, 0x90, 0x45, 0x69, 0x9a, 0xda, 0x29, 0xe0, 0xb6, 0x6b, 0x45, 0xc8, + ]; + + let result = parse_debug_sup_section_data(section_data); + assert!(result.is_err()); + let error = result.unwrap_err(); + assert!(error.to_string().contains("checksum")); + } + + /// Check that we can successfully read an ELF file's debug sup section. + #[test] + fn debug_link_reading() { + let elf = Path::new(&env!("CARGO_MANIFEST_DIR")) + .join("data") + .join("test-stable-addrs-dwarf-only-sup.dbg"); + + let parser = ElfParser::open(elf.as_path()).unwrap(); + let DebugSupInfo { + _version: version, + is_supplementary, + path, + checksum, + } = read_debug_sup(&parser).unwrap().unwrap(); + assert_eq!(version, 5); + assert!(!is_supplementary); + assert!(path.is_some()); + assert_eq!(path.unwrap(), Path::new("test-stable-addrs-dwarf-5.dwz")); + + let dbg = Path::new(&env!("CARGO_MANIFEST_DIR")) + .join("data") + .join("test-stable-addrs-dwarf-5.dwz"); + let parser = ElfParser::open(&dbg).unwrap(); + let DebugSupInfo { + _version: version, + is_supplementary, + path, + checksum: checksum_dwz, + } = read_debug_sup(&parser).unwrap().unwrap(); + assert_eq!(version, 5); + assert!(is_supplementary); + assert!(path.is_none()); + assert_eq!(checksum, checksum_dwz); + } +} diff --git a/src/dwarf/mod.rs b/src/dwarf/mod.rs index 4d26f7db4..db6592f03 100644 --- a/src/dwarf/mod.rs +++ b/src/dwarf/mod.rs @@ -1,5 +1,6 @@ mod debug_altlink; mod debug_link; +mod debug_sup; mod function; mod lines; mod location; diff --git a/src/dwarf/resolver.rs b/src/dwarf/resolver.rs index 251e915d6..34dc3da96 100644 --- a/src/dwarf/resolver.rs +++ b/src/dwarf/resolver.rs @@ -53,6 +53,8 @@ use super::debug_altlink::read_debug_altlink; use super::debug_link::debug_link_crc32; use super::debug_link::read_debug_link; use super::debug_link::DebugFileIter; +use super::debug_sup::read_debug_sup; +use super::debug_sup::DebugSupInfo; use super::function::Function; use super::location::Location; use super::reader; @@ -190,13 +192,13 @@ fn try_deref_debug_link( /// Find a debug file in a list of directories. /// -/// `linker` is the path to the file containing the debug altlink and +/// `linker` is the path to the file containing the debug altlink/sup and /// `build_id` the build ID of the destination, as recorded in the -/// `.gnu_debugaltlink` section. +/// `.gnu_debugaltlink`/`.debug_sup` section. /// /// # Notes /// This function ignores any errors encountered. -fn find_altdebug_file( +fn find_supplementary_file( path: &Path, linker: Option<&Path>, debug_dirs: &[PathBuf], @@ -211,12 +213,12 @@ fn find_altdebug_file( ); for path in it { if path.exists() { - debug!("found altdebug info at `{}`", path.display()); + debug!("found supplementary object file at `{}`", path.display()); return Some(path); } } warn!( - "debug altlink references destination `{}` which was not found in any known location", + "link to supplementary file references destination `{}` which was not found in any known location", path.display(), ); None @@ -233,7 +235,7 @@ fn try_deref_debug_altlink( // actual path is not necessarily correct. Consider if the // `ElfParser` references a map_files file. let linker = parser.module().map(OsStr::as_ref); - match find_altdebug_file(path, linker, debug_dirs, &build_id) { + match find_supplementary_file(path, linker, debug_dirs, &build_id) { Some(path) => { let tmp_parser; let dst_parser = if let Some(elf_cache) = elf_cache { @@ -282,6 +284,75 @@ fn try_deref_debug_altlink( } +fn try_deref_debug_sup( + parser: &ElfParser, + debug_dirs: &[PathBuf], + elf_cache: Option<&FileCache>, +) -> Result>> { + if let Some(DebugSupInfo { + _version: _, + is_supplementary: _, + path, + checksum, + }) = read_debug_sup(parser)? + { + // TODO: Usage of the module here is fishy, as it may not + // represent an actual path. However, even using the + // actual path is not necessarily correct. Consider if the + // `ElfParser` references a map_files file. + let linker = parser.module().map(OsStr::as_ref); + if let Some(path) = path { + match find_supplementary_file(path, linker, debug_dirs, &[]) { + Some(path) => { + let tmp_parser; + let dst_parser = if let Some(elf_cache) = elf_cache { + // TODO: Unclear whether we should provide `debug_dirs` + // here instead of `None`? + elf_cache + .elf_resolver(&path, None) + .with_context(|| { + format!("failed to open debug sup destination `{}`", path.display()) + })? + .parser() + } else { + let parser = ElfParser::open(&path).with_context(|| { + format!("failed to open debug sup destination `{}`", path.display()) + })?; + tmp_parser = Rc::new(parser); + &tmp_parser + }; + + let DebugSupInfo { + _version: _, + is_supplementary, + path: _, + checksum: dst_checksum, + } = read_debug_sup(dst_parser)? + .ok_or_invalid_data(|| "empty debug_sup section")?; + if !is_supplementary { + return Err(Error::with_invalid_data( + "debug sup destination marked as non-supplementary", + )) + } + if dst_checksum != checksum { + return Err(Error::with_invalid_data(format!( + "debug sup destination `{}` checksum does not match \ + expected one: {dst_checksum:?} (actual) != {checksum:?} (expected)", + path.display() + ))) + } + Ok(Some(Rc::clone(dst_parser))) + } + None => Ok(None), + } + } else { + Ok(None) + } + } else { + Ok(None) + } +} + /// Try to find a DWARF package (`.dwp`) "belonging" to the file /// referenced by the given [`ElfParser`]. fn try_find_dwp( @@ -328,9 +399,9 @@ pub(crate) struct DwarfResolver { /// If the source file contains a valid debug link, this parser /// represents it. linkee_parser: Option>, - /// If the source file contains a valid debug altlink, this parser - /// represents it. - _altlinkee_parser: Option>, + /// If the source file contains a valid debug altlink or debug sup, this + /// parser represents it. + _sup_parser: Option>, /// If there exist an associated DWARF Package (*.dwp), this parser /// represents it. _dwp_parser: Option>, @@ -352,6 +423,7 @@ impl DwarfResolver { let debug_parser = linkee_parser.as_ref().unwrap_or(&parser); let altlinkee_parser = try_deref_debug_altlink(debug_parser, debug_dirs, elf_cache)?; + let supee_parser = try_deref_debug_sup(debug_parser, debug_dirs, elf_cache)?; // SAFETY: We own the `ElfParser` and make sure that it stays // around while the `Units` object uses it. As such, it // is fine to conjure a 'static lifetime here. @@ -362,7 +434,7 @@ impl DwarfResolver { |section| reader::load_section(static_linkee_parser, section, static_relocs); let mut dwarf = Dwarf::load(&mut load_section)?; - if let Some(ref altlinkee_parser) = altlinkee_parser { + let _sup_parser = if let Some(ref altlinkee_parser) = altlinkee_parser { let static_altlinkee_parser = unsafe { mem::transmute::<&ElfParser, &'static ElfParser>(altlinkee_parser.deref()) }; @@ -370,7 +442,18 @@ impl DwarfResolver { let mut load_altsection = |section| reader::load_section(static_altlinkee_parser, section, static_altrelocs); Dwarf::load_sup(&mut dwarf, &mut load_altsection)?; - } + Some(altlinkee_parser.to_owned()) + } else if let Some(ref supee_parser) = supee_parser { + let static_supee_parser = + unsafe { mem::transmute::<&ElfParser, &'static ElfParser>(supee_parser.deref()) }; + let static_suprelocs = static_supee_parser.section_relocations()?; + let mut load_supsection = + |section| reader::load_section(static_supee_parser, section, static_suprelocs); + Dwarf::load_sup(&mut dwarf, &mut load_supsection)?; + Some(supee_parser.to_owned()) + } else { + None + }; // Cache abbreviations (which will cause them to be // automatically reused across compilation units), which can @@ -404,7 +487,7 @@ impl DwarfResolver { units, parser, linkee_parser, - _altlinkee_parser: altlinkee_parser, + _sup_parser, _dwp_parser: dwp_parser, }; Ok(slf) @@ -798,11 +881,11 @@ mod tests { let path = Path::new(&env!("CARGO_MANIFEST_DIR")) .join("data") .join("nonexistent_file"); - let debug_altlink_file = find_altdebug_file(&path, linker, &debug_dirs, &[]); + let debug_altlink_file = find_supplementary_file(&path, linker, &debug_dirs, &[]); assert!(debug_altlink_file.is_none()); let linker = parser.module().map(OsStr::as_ref); - let debug_altlink_file = find_altdebug_file(linker.unwrap(), linker, &debug_dirs, &[]); + let debug_altlink_file = find_supplementary_file(linker.unwrap(), linker, &debug_dirs, &[]); assert!(debug_altlink_file.is_none()); } @@ -823,21 +906,60 @@ mod tests { ); } - /// Check that we resolve debug altlinks correctly. + /// Check that we can discover debug sup as expected. #[test] - fn debug_altlink_resolution() { + fn debug_sup_discovery() { + let debug_dirs = Vec::new(); + let path = Path::new(&env!("CARGO_MANIFEST_DIR")) .join("data") - .join("test-stable-addrs-stripped-with-altlink.bin"); - let resolver = DwarfResolver::open(&path).unwrap(); - assert!(resolver._altlinkee_parser.is_some()); + .join("test-stable-addrs-dwarf-only-sup.dbg"); + let parser = ElfParser::open(&path).unwrap(); + let debug_sup_parser = try_deref_debug_sup(&parser, &debug_dirs, None).unwrap(); + assert!(debug_sup_parser.is_some()); - let linkee_path = Path::new(&env!("CARGO_MANIFEST_DIR")) + let path = Path::new(&env!("CARGO_MANIFEST_DIR")) .join("data") - .join("test-stable-addrs.dwz"); - assert_eq!( - resolver._altlinkee_parser.as_ref().unwrap().module(), - Some(linkee_path.as_os_str()) + .join("test-stable-addrs.bin"); + let parser = ElfParser::open(&path).unwrap(); + let debug_sup_parser = try_deref_debug_sup(&parser, &debug_dirs, None).unwrap(); + assert!(debug_sup_parser.is_none()); + + let path = Path::new(&env!("CARGO_MANIFEST_DIR")) + .join("data") + .join("test-stable-addrs-dwarf-only-broken-sup.dbg"); + let parser = ElfParser::open(&path).unwrap(); + let debug_sup_parser = try_deref_debug_sup(&parser, &debug_dirs, None).unwrap(); + assert!(debug_sup_parser.is_none()); + } + + /// Check that we resolve debug altlink and debug sup correctly. + #[test] + fn resolve_sup_file() { + fn test(debug_file_name: &str, dwz_file_name: &str) { + let path = Path::new(&env!("CARGO_MANIFEST_DIR")) + .join("data") + .join(debug_file_name); + let resolver = DwarfResolver::open(&path).unwrap(); + assert!(resolver._sup_parser.is_some()); + + let linkee_path = Path::new(&env!("CARGO_MANIFEST_DIR")) + .join("data") + .join(dwz_file_name); + assert_eq!( + resolver._sup_parser.as_ref().unwrap().module(), + Some(linkee_path.as_os_str()), + ); + } + + test( + "test-stable-addrs-stripped-with-altlink.bin", + "test-stable-addrs.dwz", + ); + + test( + "test-stable-addrs-stripped-with-sup.bin", + "test-stable-addrs-dwarf-5.dwz", ); } diff --git a/tests/suite/inspect.rs b/tests/suite/inspect.rs index 2297bf2c3..0a900d722 100644 --- a/tests/suite/inspect.rs +++ b/tests/suite/inspect.rs @@ -383,10 +383,11 @@ fn inspect_elf_all_symbols_without_duplicates() { assert_eq!(syms.iter().filter(|name| *name == "the_answer").count(), 1); } -/// Check that we correctly incorporate debug altlink files in the + +/// Check that we correctly incorporate debug altlink and debug sup files in the /// inspection process. #[test] -fn inspect_debug_altlink_honoring() { +fn inspect_sup_file_honoring() { fn symbol_names(file: &str) -> Vec { let path = Path::new(&env!("CARGO_MANIFEST_DIR")) .join("data") @@ -403,11 +404,23 @@ fn inspect_debug_altlink_honoring() { names } - // The symbol's name lives in the `dwz` multifile and, hence, is only - // reported if we followed the altlink. - let names = symbol_names("test-stable-addrs-stripped-with-altlink.bin"); - assert!(names.iter().any(|name| name == "factorial"), "{names:?}"); + fn test(debug_ok_name: &str, debug_broken_name: &str) { + // The symbol's name lives in the `dwz` multifile and, hence, is only + // reported if we follow the altlink / sup. + let names = symbol_names(debug_ok_name); + assert!(names.iter().any(|name| name == "factorial"), "{names:?}"); + + let names = symbol_names(debug_broken_name); + assert!(!names.iter().any(|name| name == "factorial"), "{names:?}"); + } + + test( + "test-stable-addrs-stripped-with-altlink.bin", + "test-stable-addrs-stripped-with-broken-altlink.bin", + ); - let names = symbol_names("test-stable-addrs-stripped-with-broken-altlink.bin"); - assert!(!names.iter().any(|name| name == "factorial"), "{names:?}"); + test( + "test-stable-addrs-stripped-with-sup.bin", + "test-stable-addrs-stripped-with-broken-sup.bin", + ); }