Skip to content
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

Support zstd-compressed ELF sections. #625

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
16 changes: 16 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ jobs:
rust: beta
- os: ubuntu-20.04
rust: nightly
- os: ubuntu-24.04
rust: stable
- os: ubuntu-24.04
rust: beta
- os: ubuntu-24.04
rust: nightly
- os: macos-latest
rust: stable
- os: macos-latest
Expand All @@ -48,6 +54,12 @@ jobs:
- run: echo RUSTFLAGS=-Dwarnings >> $GITHUB_ENV
shell: bash

# Starting with Ubuntu 22.04 libc6-dbg is needed.
- name: Install libc debug info
run: sudo apt-get install -y libc6-dbg
shell: bash
if: contains(matrix.os, 'ubuntu-24.04')

# full fidelity of backtraces on 32-bit msvc requires frame pointers, so
# enable that for our tests
- name: Force frame pointers
Expand Down Expand Up @@ -85,6 +97,10 @@ jobs:
if: contains(matrix.os, 'ubuntu')
env:
RUSTFLAGS: "-C link-arg=-Wl,--compress-debug-sections=zlib-gnu"
- run: cargo test
if: contains(matrix.os, 'ubuntu-24.04')
env:
RUSTFLAGS: "-C link-arg=-Wl,--compress-debug-sections=zstd"

# Test that, on macOS, packed/unpacked debuginfo both work
- run: cargo clean && cargo test
Expand Down
49 changes: 49 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ miniz_oxide = { version = "0.7.0", default-features = false }
addr2line = { version = "0.22.0", default-features = false }
libc = { version = "0.2.146", default-features = false }

[target.'cfg(not(any(all(windows, target_env = "msvc", not(target_vendor = "uwp")), target_os = "illumos")))'.dependencies]
Copy link
Member

Choose a reason for hiding this comment

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

Why is this dependency added for uwp?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's not. Unless I screwed something up that part of the condition is copied from the section above. What's new here is that illumos is excluded as well.

Copy link
Member

Choose a reason for hiding this comment

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

The conditional evaluates as follows on x86_64-uwp-windows-msvc:

  • not(any(all(windows, target_env = "msvc", not(target_vendor = "uwp")), target_os = "illumos"))
    • any(all(windows, target_env = "msvc", not(target_vendor = "uwp")), target_os = "illumos")
      • all(windows, target_env = "msvc", not(target_vendor = "uwp"))
        • windows
        • target_env = "msvc"
        • not(target_vendor = "uwp")
          • target_vendor = "uwp"
      • target_os = "illumos"

So it will include zstd on x86_64-uwp-windows-msvc.

Copy link
Member

Choose a reason for hiding this comment

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

Why is Windows included at all? Surely even gnu does not use elf sections due to the binary format being different on Windows?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry, you're right. This is what I get for commenting at 6:30 AM.

The uwp part was copied from the above section with miniz_oxide, addr2line, and libc. That exclusion was added in #543 without explanation.

The correct handling here would be for addr2line/libc to be gated on not using msvc (because mingw produces DWARF, not PDBs) and for miniz_oxide/zstd to be gated on not windows (because no Windows target uses ELF and these are for ELF compression).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok I pushed a commit that cleans this up. Even if backtrace doesn't end up taking this PR it'll probably want something like that commit.

zstd = { version = "= 0.13.0", default-features = false }
Copy link
Member

@bjorn3 bjorn3 May 24, 2024

Choose a reason for hiding this comment

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

Using the C implementation of zstd makes cross-compilation harder, especially for miri which currently is able to test for any supported target without installing any toolchain for the target.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, I discussed that in the commit message. I could disable this for miri too.


[target.'cfg(not(all(windows, target_env = "msvc", not(target_vendor = "uwp"))))'.dependencies.object]
version = "0.35.0"
default-features = false
Expand Down
1 change: 1 addition & 0 deletions ci/docker/arm-linux-androideabi/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ 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=armv7a-linux-androideabi19-clang \
CC=armv7a-linux-androideabi19-clang \
CARGO_TARGET_ARM_LINUX_ANDROIDEABI_RUNNER=echo
1 change: 1 addition & 0 deletions ci/docker/armv7-linux-androideabi/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ 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=armv7a-linux-androideabi19-clang \
CC=armv7a-linux-androideabi19-clang \
CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_RUNNER=echo
1 change: 1 addition & 0 deletions ci/docker/i686-linux-android/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ 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-android19-clang \
CC=i686-linux-android19-clang \
CARGO_TARGET_I686_LINUX_ANDROID_RUNNER=echo
5 changes: 4 additions & 1 deletion crates/as-if-std/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ libc = { version = "0.2.146", default-features = false }
miniz_oxide = { version = "0.7.0", optional = true, default-features = false }
addr2line = { version = "0.22.0", optional = true, default-features = false }

[target.'cfg(not(any(all(windows, target_env = "msvc", not(target_vendor = "uwp")), target_os = "illumos")))'.dependencies]
zstd = { version = "= 0.13.0", optional = true, default-features = false }

[target.'cfg(not(all(windows, target_env = "msvc", not(target_vendor = "uwp"))))'.dependencies.object]
version = "0.35.0"
default-features = false
Expand All @@ -32,7 +35,7 @@ cc = "1.0.90"

[features]
default = ['backtrace']
backtrace = ['addr2line', 'miniz_oxide', 'object']
backtrace = ['addr2line', 'miniz_oxide', 'object', 'zstd']
std = []

[lints.rust]
Expand Down
32 changes: 24 additions & 8 deletions src/symbolize/gimli/elf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ use super::{gimli, Context, Endian, EndianSlice, Mapping, Stash, Vec};
use alloc::sync::Arc;
use core::convert::{TryFrom, TryInto};
use core::str;
#[cfg(not(target_os = "illumos"))]
use object::elf::ELFCOMPRESS_ZSTD;
use object::elf::{ELFCOMPRESS_ZLIB, ELF_NOTE_GNU, NT_GNU_BUILD_ID, SHF_COMPRESSED};
use object::read::elf::{CompressionHeader, FileHeader, SectionHeader, SectionTable, Sym};
use object::read::StringTable;
Expand Down Expand Up @@ -170,22 +172,31 @@ impl<'a> Object<'a> {
let mut data = Bytes(section.data(self.endian, self.data).ok()?);

// Check for DWARF-standard (gABI) compression, i.e., as generated
// by ld's `--compress-debug-sections=zlib-gabi` flag.
// by ld's `--compress-debug-sections=zlib-gabi` and
// `--compress-debug-sections=zstd` flags.
let flags: u64 = section.sh_flags(self.endian).into();
if (flags & u64::from(SHF_COMPRESSED)) == 0 {
// Not compressed.
return Some(data.0);
}

let header = data.read::<<Elf as FileHeader>::CompressionHeader>().ok()?;
if header.ch_type(self.endian) != ELFCOMPRESS_ZLIB {
// Zlib compression is the only known type.
return None;
match header.ch_type(self.endian) {
ELFCOMPRESS_ZLIB => {
let size = usize::try_from(header.ch_size(self.endian)).ok()?;
let buf = stash.allocate(size);
decompress_zlib(data.0, buf)?;
return Some(buf);
}
#[cfg(not(target_os = "illumos"))]
ELFCOMPRESS_ZSTD => {
let size = usize::try_from(header.ch_size(self.endian)).ok()?;
let buf = stash.allocate(size);
decompress_zstd(data.0, buf)?;
return Some(buf);
}
_ => return None, // Unknown compression type.
}
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
Expand Down Expand Up @@ -304,6 +315,11 @@ fn decompress_zlib(input: &[u8], output: &mut [u8]) -> Option<()> {
}
}

#[cfg(not(target_os = "illumos"))]
fn decompress_zstd(input: &[u8], output: &mut [u8]) -> Option<()> {
zstd::stream::copy_decode(input, output).ok()
}

const DEBUG_PATH: &[u8] = b"/usr/lib/debug";

fn debug_path_exists() -> bool {
Expand Down
Loading