Skip to content

Commit 10db852

Browse files
committed
Support zstd-compressed ELF sections.
zstd has been introduced as an alternative to zlib for the compression of debug sections.[0] Toolchain support is widely present at this time but lack of support in backtrace is a severe limitation on using this feature in Rust programs. This uses a Rust reimplementation of zstd (the ruzstd crate). This has the benefit of simplifying the build process, but this crate is less used and admittedly slower than the zstd crate that binds to the C libzstd. [0] https://maskray.me/blog/2022-09-09-zstd-compressed-debug-sections
1 parent 0147d56 commit 10db852

File tree

5 files changed

+86
-11
lines changed

5 files changed

+86
-11
lines changed

.github/workflows/main.yml

+16
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@ jobs:
2222
rust: beta
2323
- os: ubuntu-20.04
2424
rust: nightly
25+
- os: ubuntu-24.04
26+
rust: stable
27+
- os: ubuntu-24.04
28+
rust: beta
29+
- os: ubuntu-24.04
30+
rust: nightly
2531
- os: macos-latest
2632
rust: stable
2733
- os: macos-latest
@@ -48,6 +54,12 @@ jobs:
4854
- run: echo RUSTFLAGS=-Dwarnings >> $GITHUB_ENV
4955
shell: bash
5056

57+
# Starting with Ubuntu 22.04 libc6-dbg is needed.
58+
- name: Install libc debug info
59+
run: sudo apt-get install -y libc6-dbg
60+
shell: bash
61+
if: contains(matrix.os, 'ubuntu-24.04')
62+
5163
# full fidelity of backtraces on 32-bit msvc requires frame pointers, so
5264
# enable that for our tests
5365
- name: Force frame pointers
@@ -81,6 +93,10 @@ jobs:
8193
if: contains(matrix.os, 'ubuntu')
8294
env:
8395
RUSTFLAGS: "-C link-arg=-Wl,--compress-debug-sections=zlib"
96+
- run: cargo test
97+
if: contains(matrix.os, 'ubuntu-24.04')
98+
env:
99+
RUSTFLAGS: "-C link-arg=-Wl,--compress-debug-sections=zstd"
84100

85101
# Test that, on macOS, packed/unpacked debuginfo both work
86102
- run: cargo clean && cargo test

Cargo.lock

+41-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ cpp_demangle = { default-features = false, version = "0.4.0", optional = true, f
4141

4242
[target.'cfg(not(all(windows, target_env = "msvc", not(target_vendor = "uwp"))))'.dependencies]
4343
miniz_oxide = { version = "0.7.0", default-features = false }
44+
ruzstd = { version = "0.6.0", default-features = false }
4445
addr2line = { version = "0.22.0", default-features = false }
4546
libc = { version = "0.2.146", default-features = false }
4647

crates/as-if-std/Cargo.toml

+2-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ libc = { version = "0.2.146", default-features = false }
1818

1919
[target.'cfg(not(all(windows, target_env = "msvc", not(target_vendor = "uwp"))))'.dependencies]
2020
miniz_oxide = { version = "0.7.0", optional = true, default-features = false }
21+
ruzstd = { version = "0.6.0", optional = true, default-features = false }
2122
addr2line = { version = "0.22.0", optional = true, default-features = false }
2223

2324
[target.'cfg(not(all(windows, target_env = "msvc", not(target_vendor = "uwp"))))'.dependencies.object]
@@ -32,7 +33,7 @@ cc = "1.0.97"
3233

3334
[features]
3435
default = ['backtrace']
35-
backtrace = ['addr2line', 'miniz_oxide', 'object']
36+
backtrace = ['addr2line', 'miniz_oxide', 'object', 'ruzstd']
3637
std = []
3738

3839
[lints.rust]

src/symbolize/gimli/elf.rs

+26-9
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ use super::{gimli, Context, Endian, EndianSlice, Mapping, Stash, Vec};
77
use alloc::sync::Arc;
88
use core::convert::{TryFrom, TryInto};
99
use core::str;
10-
use object::elf::{ELFCOMPRESS_ZLIB, ELF_NOTE_GNU, NT_GNU_BUILD_ID, SHF_COMPRESSED};
10+
use object::elf::{
11+
ELFCOMPRESS_ZLIB, ELFCOMPRESS_ZSTD, ELF_NOTE_GNU, NT_GNU_BUILD_ID, SHF_COMPRESSED,
12+
};
1113
use object::read::elf::{CompressionHeader, FileHeader, SectionHeader, SectionTable, Sym};
1214
use object::read::StringTable;
1315
use object::{BigEndian, Bytes, NativeEndian};
@@ -170,22 +172,30 @@ impl<'a> Object<'a> {
170172
let mut data = Bytes(section.data(self.endian, self.data).ok()?);
171173

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

180183
let header = data.read::<<Elf as FileHeader>::CompressionHeader>().ok()?;
181-
if header.ch_type(self.endian) != ELFCOMPRESS_ZLIB {
182-
// Zlib compression is the only known type.
183-
return None;
184+
match header.ch_type(self.endian) {
185+
ELFCOMPRESS_ZLIB => {
186+
let size = usize::try_from(header.ch_size(self.endian)).ok()?;
187+
let buf = stash.allocate(size);
188+
decompress_zlib(data.0, buf)?;
189+
return Some(buf);
190+
}
191+
ELFCOMPRESS_ZSTD => {
192+
let size = usize::try_from(header.ch_size(self.endian)).ok()?;
193+
let buf = stash.allocate(size);
194+
decompress_zstd(data.0, buf)?;
195+
return Some(buf);
196+
}
197+
_ => return None, // Unknown compression type.
184198
}
185-
let size = usize::try_from(header.ch_size(self.endian)).ok()?;
186-
let buf = stash.allocate(size);
187-
decompress_zlib(data.0, buf)?;
188-
return Some(buf);
189199
}
190200

191201
// Check for the nonstandard GNU compression format, i.e., as generated
@@ -304,6 +314,13 @@ fn decompress_zlib(input: &[u8], output: &mut [u8]) -> Option<()> {
304314
}
305315
}
306316

317+
fn decompress_zstd(input: &[u8], output: &mut [u8]) -> Option<()> {
318+
use ruzstd::io::Read;
319+
320+
let mut decoder = ruzstd::StreamingDecoder::new(input).ok()?;
321+
decoder.read_exact(output).ok()
322+
}
323+
307324
const DEBUG_PATH: &[u8] = b"/usr/lib/debug";
308325

309326
fn debug_path_exists() -> bool {

0 commit comments

Comments
 (0)