From cc5d40cc1112775c439b9fc6838d6374dc668867 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 13 May 2020 14:18:44 -0700 Subject: [PATCH] Prepare for this crate to go into libstd This commit is a preparation of this crate to be included as a submodule into the standard library. I'm not 100% sold on this yet but I'm somewhat convinced that this is going to happen this way. This is progress on #328 and a preview of what it might look like to implement this strategy. Currently I don't plan to merge this to the `master` branch unless it's decided to move forward with this integration strategy of the gimli feature of the backtrace crate. --- .github/workflows/main.yml | 1 + Cargo.toml | 19 ++++-------- ci/run.sh | 1 + crates/as-if-std/Cargo.toml | 28 ++++++++++++++++++ crates/as-if-std/build.rs | 3 ++ crates/as-if-std/src/lib.rs | 21 ++++++++++++++ src/backtrace/dbghelp.rs | 3 +- src/backtrace/libunwind.rs | 3 +- src/dbghelp.rs | 2 +- src/lib.rs | 23 ++++++++++----- src/print.rs | 16 +++++----- src/symbolize/dbghelp.rs | 8 ++--- src/symbolize/gimli.rs | 16 ++++++---- src/symbolize/gimli/coff.rs | 4 +-- src/symbolize/gimli/elf.rs | 45 ++++++++++++++++++----------- src/symbolize/gimli/macho.rs | 2 +- src/symbolize/gimli/mmap_unix.rs | 9 +++--- src/symbolize/gimli/mmap_windows.rs | 11 ++++--- src/symbolize/gimli/stash.rs | 8 +++-- src/symbolize/mod.rs | 4 +-- src/symbolize/noop.rs | 4 +-- 21 files changed, 149 insertions(+), 82 deletions(-) create mode 100644 crates/as-if-std/Cargo.toml create mode 100644 crates/as-if-std/build.rs create mode 100644 crates/as-if-std/src/lib.rs diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index bccbbd5a0..64abfdb19 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -82,6 +82,7 @@ jobs: if: contains(matrix.os, 'ubuntu') - run: RUSTFLAGS="-C link-arg=-Wl,--compress-debug-sections=zlib-gnu" cargo test --features gimli-symbolize if: contains(matrix.os, 'ubuntu') + - run: cargo build --manifest-path crates/as-if-std/Cargo.toml windows_arm64: name: Windows AArch64 diff --git a/Cargo.toml b/Cargo.toml index b5e04bc23..abd8dd258 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ autotests = true edition = "2018" [workspace] -members = ['crates/cpp_smoke_test'] +members = ['crates/cpp_smoke_test', 'crates/as-if-std'] exclude = ['crates/without_debuginfo', 'crates/macos_frames_test', 'crates/line-tables-only'] [dependencies] @@ -32,14 +32,12 @@ rustc-serialize = { version = "0.3", optional = true } # Optionally demangle C++ frames' symbols in backtraces. cpp_demangle = { default-features = false, version = "0.2.3", optional = true } -# Internal dependencies when built as a dependency of libstd, do not use. -core = { version = "1.0.0", optional = true, package = 'rustc-std-workspace-core' } -compiler_builtins = { version = '0.1.2', optional = true } # Optional dependencies enabled through the `gimli-symbolize` feature, do not # use these features directly. addr2line = { version = "0.12.0", optional = true, default-features = false } -miniz_oxide = { version = "0.3.7", optional = true } +# miniz_oxide = { version = "0.3.7", optional = true } +miniz_oxide = { git = 'https://github.com/alexcrichton/miniz_oxide', branch = 'no-std', optional = true } [dependencies.object] version = "0.20.0" optional = true @@ -105,14 +103,6 @@ verify-winapi = [ 'winapi/winbase', 'winapi/winnt', ] -rustc-dep-of-std = [ - 'backtrace-sys/rustc-dep-of-std', - 'cfg-if/rustc-dep-of-std', - 'core', - 'compiler_builtins', - 'libc/rustc-dep-of-std', - 'rustc-demangle/rustc-dep-of-std', -] [[example]] name = "backtrace" @@ -144,3 +134,6 @@ edition = '2018' name = "concurrent-panics" required-features = ["std"] harness = false + +[patch.crates-io] +miniz_oxide = { git = 'https://github.com/alexcrichton/miniz_oxide', branch = 'no-std' } diff --git a/ci/run.sh b/ci/run.sh index 5cc151507..166b387e4 100755 --- a/ci/run.sh +++ b/ci/run.sh @@ -3,3 +3,4 @@ set -ex cargo test --target $TARGET +cargo build --target $TARGET --manifest-path crates/as-if-std/Cargo.toml diff --git a/crates/as-if-std/Cargo.toml b/crates/as-if-std/Cargo.toml new file mode 100644 index 000000000..745027c19 --- /dev/null +++ b/crates/as-if-std/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "as-if-std" +version = "0.1.0" +authors = ["Alex Crichton "] +edition = "2018" +publish = false + +[lib] +test = false +doc = false +doctest = false +bench = false + +[dependencies] +cfg-if = "0.1.10" +rustc-demangle = "0.1.4" +libc = { version = "0.2.45", default-features = false } +addr2line = { version = "0.12.0", default-features = false } +miniz_oxide = { version = "0.3.7" } + +[dependencies.object] +version = "0.20.0" +default-features = false +features = ['read_core', 'elf', 'macho', 'pe', 'unaligned'] + +[features] +default = ['gimli-symbolize'] +gimli-symbolize = [] diff --git a/crates/as-if-std/build.rs b/crates/as-if-std/build.rs new file mode 100644 index 000000000..7018b1017 --- /dev/null +++ b/crates/as-if-std/build.rs @@ -0,0 +1,3 @@ +fn main() { + println!("cargo:rustc-cfg=backtrace_in_libstd"); +} diff --git a/crates/as-if-std/src/lib.rs b/crates/as-if-std/src/lib.rs new file mode 100644 index 000000000..c0f49b77d --- /dev/null +++ b/crates/as-if-std/src/lib.rs @@ -0,0 +1,21 @@ +// A crate which builds the `backtrace` crate as-if it's included as a +// submodule into the standard library. We try to set this crate up similarly +// to the standard library itself to minimize the likelihood of issues when +// updating the `backtrace` crate. + +#![no_std] + +extern crate alloc; + +// We want to `pub use std::*` in the root but we don't want `std` available in +// the root namespace, so do this in a funky inner module. +mod __internal { + extern crate std; + pub use std::*; +} + +pub use __internal::*; + +// This is the magical part which we hope works. +#[path = "../../../src/lib.rs"] +mod the_backtrace_crate; diff --git a/src/backtrace/dbghelp.rs b/src/backtrace/dbghelp.rs index cbfa4ec12..8898e2234 100644 --- a/src/backtrace/dbghelp.rs +++ b/src/backtrace/dbghelp.rs @@ -21,8 +21,7 @@ #![allow(bad_style)] -use crate::dbghelp; -use crate::windows::*; +use super::super::{dbghelp, windows::*}; use core::ffi::c_void; use core::mem; diff --git a/src/backtrace/libunwind.rs b/src/backtrace/libunwind.rs index c5ddc76ed..b0c848a62 100644 --- a/src/backtrace/libunwind.rs +++ b/src/backtrace/libunwind.rs @@ -25,6 +25,7 @@ //! //! This is the default unwinding API for all non-Windows platforms currently. +use super::super::Bomb; use core::ffi::c_void; pub enum Frame { @@ -103,7 +104,7 @@ pub unsafe fn trace(mut cb: &mut dyn FnMut(&super::Frame) -> bool) { inner: Frame::Raw(ctx), }; - let mut bomb = crate::Bomb { enabled: true }; + let mut bomb = Bomb { enabled: true }; let keep_going = cb(&cx); bomb.enabled = false; diff --git a/src/dbghelp.rs b/src/dbghelp.rs index b86f92b77..c534ebd17 100644 --- a/src/dbghelp.rs +++ b/src/dbghelp.rs @@ -23,7 +23,7 @@ #![allow(non_snake_case)] -use crate::windows::*; +use super::windows::*; use core::mem; use core::ptr; diff --git a/src/lib.rs b/src/lib.rs index 4d82b32dc..46c2e006b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -49,32 +49,39 @@ feature(sgx_platform) )] #![warn(rust_2018_idioms)] +// When we're building as part of libstd, silence all warnings since they're +// irrelevant as this crate is developed out-of-tree. +#![cfg_attr(backtrace_in_libstd, allow(warnings))] #[cfg(feature = "std")] #[macro_use] extern crate std; -pub use crate::backtrace::{trace_unsynchronized, Frame}; +// This is only used for gimli right now, so silence warnings elsewhere. +#[cfg_attr(not(target_os = "linux"), allow(unused_extern_crates))] +extern crate alloc; + +pub use self::backtrace::{trace_unsynchronized, Frame}; mod backtrace; -pub use crate::symbolize::resolve_frame_unsynchronized; -pub use crate::symbolize::{resolve_unsynchronized, Symbol, SymbolName}; +pub use self::symbolize::resolve_frame_unsynchronized; +pub use self::symbolize::{resolve_unsynchronized, Symbol, SymbolName}; mod symbolize; -pub use crate::types::BytesOrWideString; +pub use self::types::BytesOrWideString; mod types; #[cfg(feature = "std")] -pub use crate::symbolize::clear_symbol_cache; +pub use self::symbolize::clear_symbol_cache; mod print; pub use print::{BacktraceFmt, BacktraceFrameFmt, PrintFmt}; cfg_if::cfg_if! { if #[cfg(feature = "std")] { - pub use crate::backtrace::trace; - pub use crate::symbolize::{resolve, resolve_frame}; - pub use crate::capture::{Backtrace, BacktraceFrame, BacktraceSymbol}; + pub use self::backtrace::trace; + pub use self::symbolize::{resolve, resolve_frame}; + pub use self::capture::{Backtrace, BacktraceFrame, BacktraceSymbol}; mod capture; } } diff --git a/src/print.rs b/src/print.rs index 966323585..353fc03ef 100644 --- a/src/print.rs +++ b/src/print.rs @@ -1,4 +1,6 @@ -use crate::BytesOrWideString; +#[cfg(feature = "std")] +use super::{BacktraceFrame, BacktraceSymbol}; +use super::{BytesOrWideString, Frame, SymbolName}; use core::ffi::c_void; use core::fmt; @@ -105,7 +107,7 @@ impl BacktraceFrameFmt<'_, '_, '_> { /// This function requires the `std` feature of the `backtrace` crate to be /// enabled, and the `std` feature is enabled by default. #[cfg(feature = "std")] - pub fn backtrace_frame(&mut self, frame: &crate::BacktraceFrame) -> fmt::Result { + pub fn backtrace_frame(&mut self, frame: &BacktraceFrame) -> fmt::Result { let symbols = frame.symbols(); for symbol in symbols { self.backtrace_symbol(frame, symbol)?; @@ -125,8 +127,8 @@ impl BacktraceFrameFmt<'_, '_, '_> { #[cfg(feature = "std")] pub fn backtrace_symbol( &mut self, - frame: &crate::BacktraceFrame, - symbol: &crate::BacktraceSymbol, + frame: &BacktraceFrame, + symbol: &BacktraceSymbol, ) -> fmt::Result { self.print_raw( frame.ip(), @@ -144,7 +146,7 @@ impl BacktraceFrameFmt<'_, '_, '_> { /// Prints a raw traced `Frame` and `Symbol`, typically from within the raw /// callbacks of this crate. - pub fn symbol(&mut self, frame: &crate::Frame, symbol: &crate::Symbol) -> fmt::Result { + pub fn symbol(&mut self, frame: &Frame, symbol: &super::Symbol) -> fmt::Result { self.print_raw( frame.ip(), symbol.name(), @@ -162,7 +164,7 @@ impl BacktraceFrameFmt<'_, '_, '_> { pub fn print_raw( &mut self, frame_ip: *mut c_void, - symbol_name: Option>, + symbol_name: Option>, filename: Option>, lineno: Option, ) -> fmt::Result { @@ -182,7 +184,7 @@ impl BacktraceFrameFmt<'_, '_, '_> { fn print_raw_generic( &mut self, mut frame_ip: *mut c_void, - symbol_name: Option>, + symbol_name: Option>, filename: Option>, lineno: Option, ) -> fmt::Result { diff --git a/src/symbolize/dbghelp.rs b/src/symbolize/dbghelp.rs index 4673f87aa..abb7796b6 100644 --- a/src/symbolize/dbghelp.rs +++ b/src/symbolize/dbghelp.rs @@ -27,12 +27,8 @@ #![allow(bad_style)] -use crate::backtrace::FrameImp as Frame; -use crate::dbghelp; -use crate::symbolize::ResolveWhat; -use crate::types::BytesOrWideString; -use crate::windows::*; -use crate::SymbolName; +use super::super::{backtrace::FrameImp as Frame, dbghelp, windows::*}; +use super::{BytesOrWideString, ResolveWhat, SymbolName}; use core::char; use core::ffi::c_void; use core::marker; diff --git a/src/symbolize/gimli.rs b/src/symbolize/gimli.rs index b9dcaebe8..a83622019 100644 --- a/src/symbolize/gimli.rs +++ b/src/symbolize/gimli.rs @@ -8,9 +8,9 @@ use self::gimli::read::EndianSlice; use self::gimli::LittleEndian as Endian; use self::mmap::Mmap; use self::stash::Stash; -use crate::symbolize::ResolveWhat; -use crate::types::BytesOrWideString; -use crate::SymbolName; +use super::BytesOrWideString; +use super::ResolveWhat; +use super::SymbolName; use addr2line::gimli; use core::convert::TryInto; use core::mem; @@ -21,6 +21,11 @@ use std::fs::File; use std::path::Path; use std::prelude::v1::*; +#[cfg(backtrace_in_libstd)] +mod std { + pub use crate::*; +} + #[cfg(windows)] #[path = "gimli/mmap_windows.rs"] mod mmap; @@ -70,8 +75,6 @@ fn cx<'data>(stash: &'data Stash, object: Object<'data>) -> Option {{ - use crate::symbolize::gimli::{Context, Mapping, Mmap}; - fn assert_lifetimes<'a>(_: &'a Mmap, _: &Context<'a>, _: &'a Stash) {} assert_lifetimes(&$map, &$inner, &$stash); Mapping { @@ -93,8 +96,9 @@ fn mmap(path: &Path) -> Option { cfg_if::cfg_if! { if #[cfg(windows)] { use core::mem::MaybeUninit; - use crate::windows::*; + use super::super::windows::*; use std::os::windows::prelude::*; + use alloc::vec; mod coff; use self::coff::Object; diff --git a/src/symbolize/gimli/coff.rs b/src/symbolize/gimli/coff.rs index 6442cf061..5d6c8fae9 100644 --- a/src/symbolize/gimli/coff.rs +++ b/src/symbolize/gimli/coff.rs @@ -1,4 +1,5 @@ -use super::{Mapping, Path, Stash, Vec}; +use super::{Context, Mapping, Mmap, Path, Stash, Vec}; +use core::convert::TryFrom; use object::pe::{ImageDosHeader, ImageSymbol}; use object::read::pe::{ImageNtHeaders, ImageOptionalHeader, SectionTable}; use object::read::StringTable; @@ -8,7 +9,6 @@ use object::{Bytes, LittleEndian as LE}; type Pe = object::pe::ImageNtHeaders32; #[cfg(target_pointer_width = "64")] type Pe = object::pe::ImageNtHeaders64; -use std::convert::TryFrom; impl Mapping { pub fn new(path: &Path) -> Option { diff --git a/src/symbolize/gimli/elf.rs b/src/symbolize/gimli/elf.rs index ce703328d..2ca57f88a 100644 --- a/src/symbolize/gimli/elf.rs +++ b/src/symbolize/gimli/elf.rs @@ -1,9 +1,9 @@ -use super::{Mapping, Path, Stash, Vec}; +use super::{Context, Mapping, Mmap, Path, Stash, Vec}; +use core::convert::TryFrom; use object::elf::{ELFCOMPRESS_ZLIB, SHF_COMPRESSED}; use object::read::elf::{CompressionHeader, FileHeader, SectionHeader, SectionTable, Sym}; use object::read::StringTable; use object::{BigEndian, Bytes, NativeEndian}; -use std::io::Cursor; #[cfg(target_pointer_width = "32")] type Elf = object::elf::FileHeader32; @@ -107,30 +107,40 @@ impl<'a> Object<'a> { // Zlib compression is the only known type. return None; } - let size = header.ch_size(self.endian) as usize; + let size = usize::try_from(header.ch_size(self.endian)).ok()?; let buf = stash.allocate(size); decompress_zlib(data.0, buf)?; return Some(buf); } // Check for the nonstandard GNU compression format, i.e., as generated - // by ld's `--compress-debug-sections=zlib-gnu` flag. + // by ld's `--compress-debug-sections=zlib-gnu` flag. This means that if + // we're actually asking for `.debug_info` then we need to look up a + // section named `.zdebug_info`. if !name.starts_with(".debug_") { return None; } - let zdebug_name = format!(".zdebug_{}", &name[7..]); - if let Some(section) = self.section_header(&zdebug_name) { - let mut data = section.data(self.endian, self.data).ok()?; - if data.read_bytes(8).ok()?.0 != b"ZLIB\0\0\0\0" { - return None; - } - let size = data.read::>().ok()?.get(BigEndian) as usize; - let buf = stash.allocate(size); - decompress_zlib(data.0, buf)?; - return Some(buf); + let debug_name = name[7..].as_bytes(); + let compressed_section = self + .sections + .iter() + .filter_map(|header| { + let name = self.sections.section_name(self.endian, header).ok()?; + if name.starts_with(b".zdebug_") && &name[8..] == debug_name { + Some(header) + } else { + None + } + }) + .next()?; + let mut data = compressed_section.data(self.endian, self.data).ok()?; + if data.read_bytes(8).ok()?.0 != b"ZLIB\0\0\0\0" { + return None; } - - None + let size = usize::try_from(data.read::>().ok()?.get(BigEndian)).ok()?; + let buf = stash.allocate(size); + decompress_zlib(data.0, buf)?; + Some(buf) } fn section_header(&self, name: &str) -> Option<&::SectionHeader> { @@ -164,7 +174,8 @@ fn decompress_zlib(input: &[u8], output: &mut [u8]) -> Option<()> { let (status, in_read, out_read) = decompress( &mut DecompressorOxide::new(), input, - &mut Cursor::new(output), + output, + 0, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF | TINFL_FLAG_PARSE_ZLIB_HEADER, ); if status == TINFLStatus::Done && in_read == input.len() && out_read == output.len() { diff --git a/src/symbolize/gimli/macho.rs b/src/symbolize/gimli/macho.rs index f34f1814f..5cee8fdad 100644 --- a/src/symbolize/gimli/macho.rs +++ b/src/symbolize/gimli/macho.rs @@ -1,4 +1,4 @@ -use super::{Mapping, Path, Stash, Vec}; +use super::{Context, Mapping, Mmap, Path, Stash, Vec}; use core::convert::TryInto; use object::macho; use object::read::macho::{MachHeader, Nlist, Section, Segment as _}; diff --git a/src/symbolize/gimli/mmap_unix.rs b/src/symbolize/gimli/mmap_unix.rs index 348466288..d730ef1c3 100644 --- a/src/symbolize/gimli/mmap_unix.rs +++ b/src/symbolize/gimli/mmap_unix.rs @@ -1,8 +1,7 @@ -use std::fs::File; -use std::ops::Deref; -use std::os::unix::prelude::*; -use std::ptr; -use std::slice; +use super::{AsRawFd, File}; +use core::ops::Deref; +use core::ptr; +use core::slice; pub struct Mmap { ptr: *mut libc::c_void, diff --git a/src/symbolize/gimli/mmap_windows.rs b/src/symbolize/gimli/mmap_windows.rs index 6995787ce..cda61e1b3 100644 --- a/src/symbolize/gimli/mmap_windows.rs +++ b/src/symbolize/gimli/mmap_windows.rs @@ -1,9 +1,8 @@ -use crate::windows::*; -use std::fs::File; -use std::ops::Deref; -use std::os::windows::prelude::*; -use std::ptr; -use std::slice; +use super::super::super::windows::*; +use super::{AsRawHandle, File}; +use core::ops::Deref; +use core::ptr; +use core::slice; pub struct Mmap { // keep the file alive to prevent it from ebeing deleted which would cause diff --git a/src/symbolize/gimli/stash.rs b/src/symbolize/gimli/stash.rs index 615f847c8..6375152e6 100644 --- a/src/symbolize/gimli/stash.rs +++ b/src/symbolize/gimli/stash.rs @@ -1,5 +1,9 @@ -use std::cell::UnsafeCell; -use std::vec::Vec; +// only used on Linux right now, so allow dead code elsewhere +#![cfg_attr(not(target_os = "linux"), allow(dead_code))] + +use alloc::vec; +use alloc::vec::Vec; +use core::cell::UnsafeCell; /// A simple arena allocator for byte buffers. pub struct Stash { diff --git a/src/symbolize/mod.rs b/src/symbolize/mod.rs index f6785963f..d1709a93c 100644 --- a/src/symbolize/mod.rs +++ b/src/symbolize/mod.rs @@ -7,8 +7,8 @@ cfg_if::cfg_if! { } } -use crate::backtrace::Frame; -use crate::types::BytesOrWideString; +use super::backtrace::Frame; +use super::types::BytesOrWideString; use core::ffi::c_void; use rustc_demangle::{try_demangle, Demangle}; diff --git a/src/symbolize/noop.rs b/src/symbolize/noop.rs index bb720e677..c2cedb5d3 100644 --- a/src/symbolize/noop.rs +++ b/src/symbolize/noop.rs @@ -1,9 +1,7 @@ //! Empty symbolication strategy used to compile for platforms that have no //! support. -use crate::symbolize::ResolveWhat; -use crate::types::BytesOrWideString; -use crate::SymbolName; +use super::{BytesOrWideString, ResolveWhat, SymbolName}; use core::ffi::c_void; use core::marker;