diff options
Diffstat (limited to 'library/backtrace')
23 files changed, 268 insertions, 117 deletions
diff --git a/library/backtrace/.github/workflows/main.yml b/library/backtrace/.github/workflows/main.yml index 24d814363..29fff2795 100644 --- a/library/backtrace/.github/workflows/main.yml +++ b/library/backtrace/.github/workflows/main.yml @@ -229,7 +229,7 @@ jobs: with: submodules: true - name: Install Rust - run: rustup update 1.42.0 && rustup default 1.42.0 + run: rustup update 1.55.0 && rustup default 1.55.0 - run: cargo build miri: diff --git a/library/backtrace/Cargo.toml b/library/backtrace/Cargo.toml index 477909111..cff2c9e66 100644 --- a/library/backtrace/Cargo.toml +++ b/library/backtrace/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "backtrace" -version = "0.3.67" +version = "0.3.68" authors = ["The Rust Project Developers"] build = "build.rs" license = "MIT OR Apache-2.0" @@ -27,7 +27,7 @@ exclude = [ [dependencies] cfg-if = "1.0" rustc-demangle = "0.1.4" -libc = { version = "0.2.94", default-features = false } +libc = { version = "0.2.146", default-features = false } # Optionally enable the ability to serialize a `Backtrace`, controlled through # the `serialize-*` features below. @@ -37,11 +37,11 @@ rustc-serialize = { version = "0.3", optional = true } # Optionally demangle C++ frames' symbols in backtraces. cpp_demangle = { default-features = false, version = "0.4.0", optional = true, features = ["alloc"] } -addr2line = { version = "0.19.0", default-features = false } -miniz_oxide = { version = "0.6.0", default-features = false } +addr2line = { version = "0.20.0", default-features = false } +miniz_oxide = { version = "0.7.0", default-features = false } [dependencies.object] -version = "0.30.0" +version = "0.31.1" default-features = false features = ['read_core', 'elf', 'macho', 'pe', 'unaligned', 'archive'] diff --git a/library/backtrace/ci/android-ndk.sh b/library/backtrace/ci/android-ndk.sh index b5df62b6f..38521daf1 100755 --- a/library/backtrace/ci/android-ndk.sh +++ b/library/backtrace/ci/android-ndk.sh @@ -1,23 +1,14 @@ set -ex -ANDROID_ARCH=$1 -ANDROID_SDK_VERSION=4333796 - -mkdir /tmp/android -cd /tmp/android - -curl -o android-sdk.zip \ - "https://dl.google.com/android/repository/sdk-tools-linux-${ANDROID_SDK_VERSION}.zip" -unzip -q android-sdk.zip - -yes | ./tools/bin/sdkmanager --licenses > /dev/null -./tools/bin/sdkmanager ndk-bundle > /dev/null - -./ndk-bundle/build/tools/make_standalone_toolchain.py \ - --arch $ANDROID_ARCH \ - --stl=libc++ \ - --api 21 \ - --install-dir /android-toolchain +ANDROID_NDK_URL=https://dl.google.com/android/repository +ANDROID_NDK_ARCHIVE=android-ndk-r25b-linux.zip + +mkdir /android-toolchain +cd /android-toolchain +curl -fO $ANDROID_NDK_URL/$ANDROID_NDK_ARCHIVE +unzip -q $ANDROID_NDK_ARCHIVE +rm $ANDROID_NDK_ARCHIVE +mv android-ndk-* ndk cd /tmp rm -rf android diff --git a/library/backtrace/ci/docker/aarch64-linux-android/Dockerfile b/library/backtrace/ci/docker/aarch64-linux-android/Dockerfile index c5655ed5e..906c68789 100644 --- a/library/backtrace/ci/docker/aarch64-linux-android/Dockerfile +++ b/library/backtrace/ci/docker/aarch64-linux-android/Dockerfile @@ -10,9 +10,9 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ libc6-dev COPY android-ndk.sh / -RUN /android-ndk.sh arm64 -ENV PATH=$PATH:/android-toolchain/bin +RUN /android-ndk.sh +ENV PATH=$PATH:/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/bin # TODO: run tests in an emulator eventually -ENV CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER=aarch64-linux-android-gcc \ +ENV CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER=aarch64-linux-android21-clang \ CARGO_TARGET_AARCH64_LINUX_ANDROID_RUNNER=echo diff --git a/library/backtrace/ci/docker/arm-linux-androideabi/Dockerfile b/library/backtrace/ci/docker/arm-linux-androideabi/Dockerfile index 446a64cc0..7471dcb73 100644 --- a/library/backtrace/ci/docker/arm-linux-androideabi/Dockerfile +++ b/library/backtrace/ci/docker/arm-linux-androideabi/Dockerfile @@ -10,9 +10,9 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ libc6-dev COPY android-ndk.sh / -RUN /android-ndk.sh arm -ENV PATH=$PATH:/android-toolchain/bin +RUN /android-ndk.sh +ENV PATH=$PATH:/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/bin # TODO: run tests in an emulator eventually -ENV CARGO_TARGET_ARM_LINUX_ANDROIDEABI_LINKER=arm-linux-androideabi-gcc \ +ENV CARGO_TARGET_ARM_LINUX_ANDROIDEABI_LINKER=armv7a-linux-androideabi19-clang \ CARGO_TARGET_ARM_LINUX_ANDROIDEABI_RUNNER=echo diff --git a/library/backtrace/ci/docker/armv7-linux-androideabi/Dockerfile b/library/backtrace/ci/docker/armv7-linux-androideabi/Dockerfile index 539bbc494..543fcd243 100644 --- a/library/backtrace/ci/docker/armv7-linux-androideabi/Dockerfile +++ b/library/backtrace/ci/docker/armv7-linux-androideabi/Dockerfile @@ -10,9 +10,9 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ libc6-dev COPY android-ndk.sh / -RUN /android-ndk.sh arm -ENV PATH=$PATH:/android-toolchain/bin +RUN /android-ndk.sh +ENV PATH=$PATH:/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/bin # TODO: run tests in an emulator eventually -ENV CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_LINKER=arm-linux-androideabi-gcc \ +ENV CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_LINKER=armv7a-linux-androideabi19-clang \ CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_RUNNER=echo diff --git a/library/backtrace/ci/docker/i686-linux-android/Dockerfile b/library/backtrace/ci/docker/i686-linux-android/Dockerfile index 83ccb2948..61cb9d075 100644 --- a/library/backtrace/ci/docker/i686-linux-android/Dockerfile +++ b/library/backtrace/ci/docker/i686-linux-android/Dockerfile @@ -10,9 +10,9 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ libc6-dev COPY android-ndk.sh / -RUN /android-ndk.sh x86 -ENV PATH=$PATH:/android-toolchain/bin +RUN /android-ndk.sh +ENV PATH=$PATH:/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/bin # TODO: run tests in an emulator eventually -ENV CARGO_TARGET_I686_LINUX_ANDROID_LINKER=i686-linux-android-gcc \ +ENV CARGO_TARGET_I686_LINUX_ANDROID_LINKER=i686-linux-android19-clang \ CARGO_TARGET_I686_LINUX_ANDROID_RUNNER=echo diff --git a/library/backtrace/ci/docker/x86_64-linux-android/Dockerfile b/library/backtrace/ci/docker/x86_64-linux-android/Dockerfile index 88a22ce6c..4c8922936 100644 --- a/library/backtrace/ci/docker/x86_64-linux-android/Dockerfile +++ b/library/backtrace/ci/docker/x86_64-linux-android/Dockerfile @@ -10,9 +10,9 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ libc6-dev COPY android-ndk.sh / -RUN /android-ndk.sh x86_64 -ENV PATH=$PATH:/android-toolchain/bin +RUN /android-ndk.sh +ENV PATH=$PATH:/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/bin # TODO: run tests in an emulator eventually -ENV CARGO_TARGET_X86_64_LINUX_ANDROID_LINKER=x86_64-linux-android-gcc \ +ENV CARGO_TARGET_X86_64_LINUX_ANDROID_LINKER=x86_64-linux-android21-clang \ CARGO_TARGET_X86_64_LINUX_ANDROID_RUNNER=echo diff --git a/library/backtrace/crates/as-if-std/Cargo.toml b/library/backtrace/crates/as-if-std/Cargo.toml index c763227f2..012e60f8f 100644 --- a/library/backtrace/crates/as-if-std/Cargo.toml +++ b/library/backtrace/crates/as-if-std/Cargo.toml @@ -13,13 +13,13 @@ bench = false [dependencies] cfg-if = "1.0" -rustc-demangle = "0.1.4" -libc = { version = "0.2.45", default-features = false } -addr2line = { version = "0.16.0", default-features = false, optional = true } -miniz_oxide = { version = "0.4.0", default-features = false } +rustc-demangle = "0.1.21" +libc = { version = "0.2.146", default-features = false } +addr2line = { version = "0.20.0", default-features = false, optional = true } +miniz_oxide = { version = "0.7", default-features = false } [dependencies.object] -version = "0.28" +version = "0.31.1" default-features = false optional = true features = ['read_core', 'elf', 'macho', 'pe', 'unaligned', 'archive'] diff --git a/library/backtrace/crates/as-if-std/src/lib.rs b/library/backtrace/crates/as-if-std/src/lib.rs index c0f49b77d..e1d8faaeb 100644 --- a/library/backtrace/crates/as-if-std/src/lib.rs +++ b/library/backtrace/crates/as-if-std/src/lib.rs @@ -14,7 +14,8 @@ mod __internal { pub use std::*; } -pub use __internal::*; +#[allow(unused_imports)] +use __internal::*; // This is the magical part which we hope works. #[path = "../../../src/lib.rs"] diff --git a/library/backtrace/src/backtrace/libunwind.rs b/library/backtrace/src/backtrace/libunwind.rs index c319fe45a..aefa8b094 100644 --- a/library/backtrace/src/backtrace/libunwind.rs +++ b/library/backtrace/src/backtrace/libunwind.rs @@ -66,7 +66,7 @@ impl Frame { // // Note the `skip_inner_frames.rs` test is skipped on macOS due to this // clause, and if this is fixed that test in theory can be run on macOS! - if cfg!(target_os = "macos") || cfg!(target_os = "ios") { + if cfg!(target_vendor = "apple") { self.ip() } else { unsafe { uw::_Unwind_FindEnclosingFunction(self.ip()) } diff --git a/library/backtrace/src/backtrace/mod.rs b/library/backtrace/src/backtrace/mod.rs index 93355d744..6ca1080c4 100644 --- a/library/backtrace/src/backtrace/mod.rs +++ b/library/backtrace/src/backtrace/mod.rs @@ -138,6 +138,7 @@ cfg_if::cfg_if! { unix, not(target_os = "emscripten"), not(all(target_os = "ios", target_arch = "arm")), + not(all(target_os = "nto", target_env = "nto70")), ), all( target_env = "sgx", diff --git a/library/backtrace/src/dbghelp.rs b/library/backtrace/src/dbghelp.rs index edeec653d..c81766bae 100644 --- a/library/backtrace/src/dbghelp.rs +++ b/library/backtrace/src/dbghelp.rs @@ -239,6 +239,23 @@ pub struct Init { pub fn init() -> Result<Init, ()> { use core::sync::atomic::{AtomicUsize, Ordering::SeqCst}; + // Helper function for generating a name that's unique to the process. + fn mutex_name() -> [u8; 33] { + let mut name: [u8; 33] = *b"Local\\RustBacktraceMutex00000000\0"; + let mut id = unsafe { GetCurrentProcessId() }; + // Quick and dirty no alloc u32 to hex. + let mut index = name.len() - 1; + while id > 0 { + name[index - 1] = match (id & 0xF) as u8 { + h @ 0..=9 => b'0' + h, + h => b'A' + (h - 10), + }; + id >>= 4; + index -= 1; + } + name + } + unsafe { // First thing we need to do is to synchronize this function. This can // be called concurrently from other threads or recursively within one @@ -277,11 +294,8 @@ pub fn init() -> Result<Init, ()> { static LOCK: AtomicUsize = AtomicUsize::new(0); let mut lock = LOCK.load(SeqCst); if lock == 0 { - lock = CreateMutexA( - ptr::null_mut(), - 0, - "Local\\RustBacktraceMutex\0".as_ptr() as _, - ) as usize; + let name = mutex_name(); + lock = CreateMutexA(ptr::null_mut(), 0, name.as_ptr().cast::<i8>()) as usize; if lock == 0 { return Err(()); } diff --git a/library/backtrace/src/lib.rs b/library/backtrace/src/lib.rs index e5dea3387..4615e1f96 100644 --- a/library/backtrace/src/lib.rs +++ b/library/backtrace/src/lib.rs @@ -86,7 +86,6 @@ //! time, but otherwise it's important to be aware of the limitations of //! unwinding-based backtraces! -#![doc(html_root_url = "https://docs.rs/backtrace")] #![deny(missing_docs)] #![no_std] #![cfg_attr( diff --git a/library/backtrace/src/print.rs b/library/backtrace/src/print.rs index 174d8ae5c..8d9cbe3d4 100644 --- a/library/backtrace/src/print.rs +++ b/library/backtrace/src/print.rs @@ -86,6 +86,23 @@ impl<'a, 'b> BacktraceFmt<'a, 'b> { // Currently a no-op-- including this hook to allow for future additions. Ok(()) } + + /// Inserts a message in the backtrace output. + /// + /// This allows information to be inserted between frames, + /// and won't increment the `frame_index` unlike the `frame` + /// method. + pub fn message(&mut self, msg: &str) -> fmt::Result { + self.fmt.write_str(msg) + } + + /// Return the inner formatter. + /// + /// This is used for writing custom information between frames with `write!` and `writeln!`, + /// and won't increment the `frame_index` unlike the `frame` method. + pub fn formatter(&mut self) -> &mut fmt::Formatter<'b> { + self.fmt + } } /// A formatter for just one frame of a backtrace. diff --git a/library/backtrace/src/symbolize/gimli.rs b/library/backtrace/src/symbolize/gimli.rs index cd4cec58c..7f1c6a528 100644 --- a/library/backtrace/src/symbolize/gimli.rs +++ b/library/backtrace/src/symbolize/gimli.rs @@ -58,7 +58,7 @@ struct Mapping { // 'static lifetime is a lie to hack around lack of support for self-referential structs. cx: Context<'static>, _map: Mmap, - _stash: Stash, + stash: Stash, } enum Either<A, B> { @@ -97,7 +97,7 @@ impl Mapping { // only borrow `map` and `stash` and we're preserving them below. cx: unsafe { core::mem::transmute::<Context<'_>, Context<'static>>(cx) }, _map: data, - _stash: stash, + stash: stash, }) } } @@ -105,6 +105,7 @@ impl Mapping { struct Context<'a> { dwarf: addr2line::Context<EndianSlice<'a, Endian>>, object: Object<'a>, + package: Option<gimli::DwarfPackage<EndianSlice<'a, Endian>>>, } impl<'data> Context<'data> { @@ -112,6 +113,7 @@ impl<'data> Context<'data> { stash: &'data Stash, object: Object<'data>, sup: Option<Object<'data>>, + dwp: Option<Object<'data>>, ) -> Option<Context<'data>> { let mut sections = gimli::Dwarf::load(|id| -> Result<_, ()> { let data = object.section(stash, id.name()).unwrap_or(&[]); @@ -129,7 +131,46 @@ impl<'data> Context<'data> { } let dwarf = addr2line::Context::from_dwarf(sections).ok()?; - Some(Context { dwarf, object }) + let mut package = None; + if let Some(dwp) = dwp { + package = Some( + gimli::DwarfPackage::load( + |id| -> Result<_, gimli::Error> { + let data = id + .dwo_name() + .and_then(|name| dwp.section(stash, name)) + .unwrap_or(&[]); + Ok(EndianSlice::new(data, Endian)) + }, + EndianSlice::new(&[], Endian), + ) + .ok()?, + ); + } + + Some(Context { + dwarf, + object, + package, + }) + } + + fn find_frames( + &'_ self, + stash: &'data Stash, + probe: u64, + ) -> gimli::Result<addr2line::FrameIter<'_, EndianSlice<'data, Endian>>> { + use addr2line::{LookupContinuation, LookupResult}; + + let mut l = self.dwarf.find_frames(probe); + loop { + let (load, continuation) = match l { + LookupResult::Output(output) => break output, + LookupResult::Load { load, continuation } => (load, continuation), + }; + + l = continuation.resume(handle_split_dwarf(self.package.as_ref(), stash, load)); + } } } @@ -142,7 +183,7 @@ fn mmap(path: &Path) -> Option<Mmap> { cfg_if::cfg_if! { if #[cfg(windows)] { mod coff; - use self::coff::Object; + use self::coff::{handle_split_dwarf, Object}; } else if #[cfg(any( target_os = "macos", target_os = "ios", @@ -150,10 +191,10 @@ cfg_if::cfg_if! { target_os = "watchos", ))] { mod macho; - use self::macho::Object; + use self::macho::{handle_split_dwarf, Object}; } else { mod elf; - use self::elf::Object; + use self::elf::{handle_split_dwarf, Object}; } } @@ -178,6 +219,7 @@ cfg_if::cfg_if! { target_os = "fuchsia", target_os = "freebsd", target_os = "openbsd", + target_os = "netbsd", all(target_os = "android", feature = "dl_iterate_phdr"), ), not(target_env = "uclibc"), @@ -302,7 +344,7 @@ impl Cache { .next() } - fn mapping_for_lib<'a>(&'a mut self, lib: usize) -> Option<&'a mut Context<'a>> { + fn mapping_for_lib<'a>(&'a mut self, lib: usize) -> Option<(&'a mut Context<'a>, &'a Stash)> { let idx = self.mappings.iter().position(|(idx, _)| *idx == lib); // Invariant: after this conditional completes without early returning @@ -328,10 +370,15 @@ impl Cache { self.mappings.insert(0, (lib, mapping)); } - let cx: &'a mut Context<'static> = &mut self.mappings[0].1.cx; + let mapping = &mut self.mappings[0].1; + let cx: &'a mut Context<'static> = &mut mapping.cx; + let stash: &'a Stash = &mapping.stash; // don't leak the `'static` lifetime, make sure it's scoped to just // ourselves - Some(unsafe { mem::transmute::<&'a mut Context<'static>, &'a mut Context<'a>>(cx) }) + Some(( + unsafe { mem::transmute::<&'a mut Context<'static>, &'a mut Context<'a>>(cx) }, + stash, + )) } } @@ -353,12 +400,12 @@ pub unsafe fn resolve(what: ResolveWhat<'_>, cb: &mut dyn FnMut(&super::Symbol)) // Finally, get a cached mapping or create a new mapping for this file, and // evaluate the DWARF info to find the file/line/name for this address. - let cx = match cache.mapping_for_lib(lib) { - Some(cx) => cx, + let (cx, stash) = match cache.mapping_for_lib(lib) { + Some((cx, stash)) => (cx, stash), None => return, }; let mut any_frames = false; - if let Ok(mut frames) = cx.dwarf.find_frames(addr as u64) { + if let Ok(mut frames) = cx.find_frames(stash, addr as u64) { while let Ok(Some(frame)) = frames.next() { any_frames = true; let name = match frame.function { @@ -374,7 +421,7 @@ pub unsafe fn resolve(what: ResolveWhat<'_>, cb: &mut dyn FnMut(&super::Symbol)) } if !any_frames { if let Some((object_cx, object_addr)) = cx.object.search_object_map(addr as u64) { - if let Ok(mut frames) = object_cx.dwarf.find_frames(object_addr) { + if let Ok(mut frames) = object_cx.find_frames(stash, object_addr) { while let Ok(Some(frame)) = frames.next() { any_frames = true; call(Symbol::Frame { diff --git a/library/backtrace/src/symbolize/gimli/coff.rs b/library/backtrace/src/symbolize/gimli/coff.rs index 84d334207..759c827e0 100644 --- a/library/backtrace/src/symbolize/gimli/coff.rs +++ b/library/backtrace/src/symbolize/gimli/coff.rs @@ -1,6 +1,8 @@ -use super::{Context, Mapping, Path, Stash, Vec}; +use super::{gimli, Context, Endian, EndianSlice, Mapping, Path, Stash, Vec}; +use alloc::sync::Arc; use core::convert::TryFrom; use object::pe::{ImageDosHeader, ImageSymbol}; +use object::read::coff::ImageSymbol as _; use object::read::pe::{ImageNtHeaders, ImageOptionalHeader, SectionTable}; use object::read::StringTable; use object::LittleEndian as LE; @@ -14,7 +16,7 @@ impl Mapping { pub fn new(path: &Path) -> Option<Mapping> { let map = super::mmap(path)?; Mapping::mk(map, |data, stash| { - Context::new(stash, Object::parse(data)?, None) + Context::new(stash, Object::parse(data)?, None, None) }) } } @@ -106,3 +108,11 @@ impl<'a> Object<'a> { None } } + +pub(super) fn handle_split_dwarf<'data>( + _package: Option<&gimli::DwarfPackage<EndianSlice<'data, Endian>>>, + _stash: &'data Stash, + _load: addr2line::SplitDwarfLoad<EndianSlice<'data, Endian>>, +) -> Option<Arc<gimli::Dwarf<EndianSlice<'data, Endian>>>> { + None +} diff --git a/library/backtrace/src/symbolize/gimli/elf.rs b/library/backtrace/src/symbolize/gimli/elf.rs index bc71ee2c9..b0eec0762 100644 --- a/library/backtrace/src/symbolize/gimli/elf.rs +++ b/library/backtrace/src/symbolize/gimli/elf.rs @@ -3,7 +3,8 @@ use super::mystd::fs; use super::mystd::os::unix::ffi::{OsStrExt, OsStringExt}; use super::mystd::path::{Path, PathBuf}; use super::Either; -use super::{Context, Mapping, Stash, Vec}; +use super::{gimli, Context, Endian, EndianSlice, Mapping, Stash, Vec}; +use alloc::sync::Arc; use core::convert::{TryFrom, TryInto}; use core::str; use object::elf::{ELFCOMPRESS_ZLIB, ELF_NOTE_GNU, NT_GNU_BUILD_ID, SHF_COMPRESSED}; @@ -24,24 +25,26 @@ impl Mapping { // Try to locate an external debug file using the build ID. if let Some(path_debug) = object.build_id().and_then(locate_build_id) { - if let Some(mapping) = Mapping::new_debug(path_debug, None) { + if let Some(mapping) = Mapping::new_debug(path, path_debug, None) { return Some(Either::A(mapping)); } } // Try to locate an external debug file using the GNU debug link section. if let Some((path_debug, crc)) = object.gnu_debuglink_path(path) { - if let Some(mapping) = Mapping::new_debug(path_debug, Some(crc)) { + if let Some(mapping) = Mapping::new_debug(path, path_debug, Some(crc)) { return Some(Either::A(mapping)); } } - Context::new(stash, object, None).map(Either::B) + let dwp = Mapping::load_dwarf_package(path, stash); + + Context::new(stash, object, None, dwp).map(Either::B) }) } /// Load debuginfo from an external debug file. - fn new_debug(path: PathBuf, crc: Option<u32>) -> Option<Mapping> { + fn new_debug(original_path: &Path, path: PathBuf, crc: Option<u32>) -> Option<Mapping> { let map = super::mmap(&path)?; Mapping::mk(map, |map, stash| { let object = Object::parse(&map)?; @@ -51,20 +54,45 @@ impl Mapping { } // Try to locate a supplementary object file. + let mut sup = None; if let Some((path_sup, build_id_sup)) = object.gnu_debugaltlink_path(&path) { if let Some(map_sup) = super::mmap(&path_sup) { - let map_sup = stash.set_mmap_aux(map_sup); - if let Some(sup) = Object::parse(map_sup) { - if sup.build_id() == Some(build_id_sup) { - return Context::new(stash, object, Some(sup)); + let map_sup = stash.cache_mmap(map_sup); + if let Some(sup_) = Object::parse(map_sup) { + if sup_.build_id() == Some(build_id_sup) { + sup = Some(sup_); } } } } - Context::new(stash, object, None) + let dwp = Mapping::load_dwarf_package(original_path, stash); + + Context::new(stash, object, sup, dwp) }) } + + /// Try to locate a DWARF package file. + fn load_dwarf_package<'data>(path: &Path, stash: &'data Stash) -> Option<Object<'data>> { + let mut path_dwp = path.to_path_buf(); + let dwp_extension = path + .extension() + .map(|previous_extension| { + let mut previous_extension = previous_extension.to_os_string(); + previous_extension.push(".dwp"); + previous_extension + }) + .unwrap_or_else(|| "dwp".into()); + path_dwp.set_extension(dwp_extension); + if let Some(map_dwp) = super::mmap(&path_dwp) { + let map_dwp = stash.cache_mmap(map_dwp); + if let Some(dwp_) = Object::parse(map_dwp) { + return Some(dwp_); + } + } + + None + } } struct ParsedSym { @@ -421,3 +449,47 @@ fn locate_debugaltlink(path: &Path, filename: &[u8], build_id: &[u8]) -> Option< locate_build_id(build_id) } + +fn convert_path<R: gimli::Reader>(r: &R) -> Result<PathBuf, gimli::Error> { + let bytes = r.to_slice()?; + Ok(PathBuf::from(OsStr::from_bytes(&bytes))) +} + +pub(super) fn handle_split_dwarf<'data>( + package: Option<&gimli::DwarfPackage<EndianSlice<'data, Endian>>>, + stash: &'data Stash, + load: addr2line::SplitDwarfLoad<EndianSlice<'data, Endian>>, +) -> Option<Arc<gimli::Dwarf<EndianSlice<'data, Endian>>>> { + if let Some(dwp) = package.as_ref() { + if let Ok(Some(cu)) = dwp.find_cu(load.dwo_id, &load.parent) { + return Some(Arc::new(cu)); + } + } + + let mut path = PathBuf::new(); + if let Some(p) = load.comp_dir.as_ref() { + path.push(convert_path(p).ok()?); + } + + path.push(convert_path(load.path.as_ref()?).ok()?); + + if let Some(map_dwo) = super::mmap(&path) { + let map_dwo = stash.cache_mmap(map_dwo); + if let Some(dwo) = Object::parse(map_dwo) { + return gimli::Dwarf::load(|id| -> Result<_, ()> { + let data = id + .dwo_name() + .and_then(|name| dwo.section(stash, name)) + .unwrap_or(&[]); + Ok(EndianSlice::new(data, Endian)) + }) + .ok() + .map(|mut dwo_dwarf| { + dwo_dwarf.make_dwo(&load.parent); + Arc::new(dwo_dwarf) + }); + } + } + + None +} diff --git a/library/backtrace/src/symbolize/gimli/macho.rs b/library/backtrace/src/symbolize/gimli/macho.rs index adea97a09..74ed8091a 100644 --- a/library/backtrace/src/symbolize/gimli/macho.rs +++ b/library/backtrace/src/symbolize/gimli/macho.rs @@ -1,4 +1,5 @@ -use super::{Box, Context, Mapping, Path, Stash, Vec}; +use super::{gimli, Box, Context, Endian, EndianSlice, Mapping, Path, Stash, Vec}; +use alloc::sync::Arc; use core::convert::TryInto; use object::macho; use object::read::macho::{MachHeader, Nlist, Section, Segment as _}; @@ -45,7 +46,7 @@ impl Mapping { let (macho, data) = find_header(data)?; let endian = macho.endian().ok()?; let obj = Object::parse(macho, endian, data)?; - Context::new(stash, obj, None) + Context::new(stash, obj, None, None) }) } @@ -82,7 +83,7 @@ impl Mapping { return None; } let obj = Object::parse(macho, endian, data)?; - Context::new(stash, obj, None) + Context::new(stash, obj, None, None) }); if let Some(candidate) = candidate { return Some(candidate); @@ -309,7 +310,7 @@ fn object_mapping(path: &[u8]) -> Option<Mapping> { let (macho, data) = find_header(data)?; let endian = macho.endian().ok()?; let obj = Object::parse(macho, endian, data)?; - Context::new(stash, obj, None) + Context::new(stash, obj, None, None) }) } @@ -322,3 +323,11 @@ fn split_archive_path(path: &[u8]) -> Option<(&[u8], &[u8])> { let (archive, rest) = path.split_at(index); Some((archive, &rest[1..])) } + +pub(super) fn handle_split_dwarf<'data>( + _package: Option<&gimli::DwarfPackage<EndianSlice<'data, Endian>>>, + _stash: &'data Stash, + _load: addr2line::SplitDwarfLoad<EndianSlice<'data, Endian>>, +) -> Option<Arc<gimli::Dwarf<EndianSlice<'data, Endian>>>> { + None +} diff --git a/library/backtrace/src/symbolize/gimli/mmap_unix.rs b/library/backtrace/src/symbolize/gimli/mmap_unix.rs index 5806c9f7e..261ffc1d8 100644 --- a/library/backtrace/src/symbolize/gimli/mmap_unix.rs +++ b/library/backtrace/src/symbolize/gimli/mmap_unix.rs @@ -4,6 +4,11 @@ use core::ops::Deref; use core::ptr; use core::slice; +#[cfg(not(all(target_os = "linux", target_env = "gnu")))] +use libc::mmap as mmap64; +#[cfg(all(target_os = "linux", target_env = "gnu"))] +use libc::mmap64; + pub struct Mmap { ptr: *mut libc::c_void, len: usize, @@ -11,7 +16,7 @@ pub struct Mmap { impl Mmap { pub unsafe fn map(file: &File, len: usize) -> Option<Mmap> { - let ptr = libc::mmap( + let ptr = mmap64( ptr::null_mut(), len, libc::PROT_READ, diff --git a/library/backtrace/src/symbolize/gimli/parse_running_mmaps_unix.rs b/library/backtrace/src/symbolize/gimli/parse_running_mmaps_unix.rs index a196ffcfb..deeeb2971 100644 --- a/library/backtrace/src/symbolize/gimli/parse_running_mmaps_unix.rs +++ b/library/backtrace/src/symbolize/gimli/parse_running_mmaps_unix.rs @@ -97,15 +97,10 @@ impl FromStr for MapsEntry { let pathname_str = parts.next().unwrap_or(""); // pathname may be omitted. let hex = |s| usize::from_str_radix(s, 16).map_err(|_| "Couldn't parse hex number"); - let address = { - // This could use `range_str.split_once('-')` once the MSRV passes 1.52. - if let Some(idx) = range_str.find('-') { - let (start, rest) = range_str.split_at(idx); - let (_div, limit) = rest.split_at(1); - (hex(start)?, hex(limit)?) - } else { - return Err("Couldn't parse address range"); - } + let address = if let Some((start, limit)) = range_str.split_once('-') { + (hex(start)?, hex(limit)?) + } else { + return Err("Couldn't parse address range"); }; let perms: [char; 4] = { let mut chars = perms_str.chars(); @@ -117,15 +112,10 @@ impl FromStr for MapsEntry { perms }; let offset = hex(offset_str)?; - let dev = { - // This could use `dev_str.split_once(':')` once the MSRV passes 1.52. - if let Some(idx) = dev_str.find(':') { - let (major, rest) = dev_str.split_at(idx); - let (_div, minor) = rest.split_at(1); - (hex(major)?, hex(minor)?) - } else { - return Err("Couldn't parse dev")?; - } + let dev = if let Some((major, minor)) = dev_str.split_once(':') { + (hex(major)?, hex(minor)?) + } else { + return Err("Couldn't parse dev"); }; let inode = hex(inode_str)?; let pathname = pathname_str.into(); diff --git a/library/backtrace/src/symbolize/gimli/stash.rs b/library/backtrace/src/symbolize/gimli/stash.rs index 3adfc598a..792f9a6f8 100644 --- a/library/backtrace/src/symbolize/gimli/stash.rs +++ b/library/backtrace/src/symbolize/gimli/stash.rs @@ -9,14 +9,14 @@ use core::cell::UnsafeCell; /// A simple arena allocator for byte buffers. pub struct Stash { buffers: UnsafeCell<Vec<Vec<u8>>>, - mmap_aux: UnsafeCell<Option<Mmap>>, + mmaps: UnsafeCell<Vec<Mmap>>, } impl Stash { pub fn new() -> Stash { Stash { buffers: UnsafeCell::new(Vec::new()), - mmap_aux: UnsafeCell::new(None), + mmaps: UnsafeCell::new(Vec::new()), } } @@ -35,18 +35,16 @@ impl Stash { /// Stores a `Mmap` for the lifetime of this `Stash`, returning a pointer /// which is scoped to just this lifetime. - pub fn set_mmap_aux(&self, map: Mmap) -> &[u8] { + pub fn cache_mmap(&self, map: Mmap) -> &[u8] { // SAFETY: this is the only location for a mutable pointer to - // `mmap_aux`, and this structure isn't threadsafe to shared across - // threads either. This also is careful to store at most one `mmap_aux` - // since overwriting a previous one would invalidate the previous - // pointer. Given that though we can safely return a pointer to our - // interior-owned contents. + // `mmaps`, and this structure isn't threadsafe to shared across + // threads either. We also never remove elements from `self.mmaps`, + // so a reference to the data inside the map will live as long as + // `self` does. unsafe { - let mmap_aux = &mut *self.mmap_aux.get(); - assert!(mmap_aux.is_none()); - *mmap_aux = Some(map); - mmap_aux.as_ref().unwrap() + let mmaps = &mut *self.mmaps.get(); + mmaps.push(map); + mmaps.last().unwrap() } } } diff --git a/library/backtrace/tests/current-exe-mismatch.rs b/library/backtrace/tests/current-exe-mismatch.rs index 21c67bcbf..b655827fb 100644 --- a/library/backtrace/tests/current-exe-mismatch.rs +++ b/library/backtrace/tests/current-exe-mismatch.rs @@ -118,11 +118,8 @@ fn find_interpreter(me: &Path) -> Result<PathBuf, EarlyExit> { let line = line?; let line = line.trim(); let prefix = "[Requesting program interpreter: "; - // This could use `line.split_once` and `suffix.rsplit_once` once the MSRV passes 1.52 - if let Some(idx) = line.find(prefix) { - let (_, suffix) = line.split_at(idx + prefix.len()); - if let Some(idx) = suffix.rfind("]") { - let (found_path, _ignore_remainder) = suffix.split_at(idx); + if let Some((_, suffix)) = line.split_once(prefix) { + if let Some((found_path, _)) = suffix.rsplit_once("]") { return Ok(found_path.into()); } } |