Skip to content

Commit d6ab680

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 9f98e8e commit d6ab680

File tree

5 files changed

+53
-10
lines changed

5 files changed

+53
-10
lines changed

.github/workflows/main.yml

Lines changed: 16 additions & 0 deletions
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
@@ -55,6 +61,12 @@ jobs:
5561
shell: bash
5662
if: contains(matrix.rust, 'i686')
5763

64+
# Starting with Ubuntu 22.04 libc6-dbg is needed.
65+
- name: Install libc debug info
66+
run: sudo apt-get install -y libc6-dbg
67+
shell: bash
68+
if: contains(matrix.os, 'ubuntu-24.04')
69+
5870
- name: Enable collapse_debuginfo based on version
5971
run: echo RUSTFLAGS="--cfg dbginfo=\"collapsible\" $RUSTFLAGS" >> $GITHUB_ENV
6072
shell: bash
@@ -80,6 +92,10 @@ jobs:
8092
if: contains(matrix.os, 'ubuntu')
8193
env:
8294
RUSTFLAGS: "-C link-arg=-Wl,--compress-debug-sections=zlib"
95+
- run: cargo test
96+
if: contains(matrix.os, 'ubuntu-24.04') || contains(matrix.rust, 'nightly')
97+
env:
98+
RUSTFLAGS: "-C link-arg=-Wl,--compress-debug-sections=zstd"
8399

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

Cargo.lock

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ windows-targets = "0.52.6"
4343

4444
[target.'cfg(not(all(windows, target_env = "msvc", not(target_vendor = "uwp"))))'.dependencies]
4545
miniz_oxide = { version = "0.8", default-features = false }
46+
ruzstd = { version = "0.7.2", default-features = false }
4647
addr2line = { version = "0.24.0", default-features = false }
4748
libc = { version = "0.2.156", default-features = false }
4849

crates/as-if-std/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ libc = { version = "0.2.156", default-features = false }
1818

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

2324
[target.'cfg(not(all(windows, target_env = "msvc", not(target_vendor = "uwp"))))'.dependencies.object]
@@ -31,7 +32,7 @@ windows-targets = "0.52.6"
3132

3233
[features]
3334
default = ['backtrace']
34-
backtrace = ['addr2line', 'miniz_oxide', 'object']
35+
backtrace = ['addr2line', 'miniz_oxide', 'object', 'ruzstd']
3536
std = []
3637

3738
[lints.rust]

src/symbolize/gimli/elf.rs

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ use super::{gimli, Context, Endian, EndianSlice, Mapping, Stash, Vec};
99
use alloc::sync::Arc;
1010
use core::convert::{TryFrom, TryInto};
1111
use core::str;
12-
use object::elf::{ELFCOMPRESS_ZLIB, ELF_NOTE_GNU, NT_GNU_BUILD_ID, SHF_COMPRESSED};
12+
use object::elf::{
13+
ELFCOMPRESS_ZLIB, ELFCOMPRESS_ZSTD, ELF_NOTE_GNU, NT_GNU_BUILD_ID, SHF_COMPRESSED,
14+
};
1315
use object::read::elf::{CompressionHeader, FileHeader, SectionHeader, SectionTable, Sym};
1416
use object::read::StringTable;
1517
use object::{BigEndian, Bytes, NativeEndian};
@@ -213,22 +215,30 @@ impl<'a> Object<'a> {
213215
let mut data = Bytes(section.data(self.endian, self.data).ok()?);
214216

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

223226
let header = data.read::<<Elf as FileHeader>::CompressionHeader>().ok()?;
224-
if header.ch_type(self.endian) != ELFCOMPRESS_ZLIB {
225-
// Zlib compression is the only known type.
226-
return None;
227+
match header.ch_type(self.endian) {
228+
ELFCOMPRESS_ZLIB => {
229+
let size = usize::try_from(header.ch_size(self.endian)).ok()?;
230+
let buf = stash.allocate(size);
231+
decompress_zlib(data.0, buf)?;
232+
return Some(buf);
233+
}
234+
ELFCOMPRESS_ZSTD => {
235+
let size = usize::try_from(header.ch_size(self.endian)).ok()?;
236+
let buf = stash.allocate(size);
237+
decompress_zstd(data.0, buf)?;
238+
return Some(buf);
239+
}
240+
_ => return None, // Unknown compression type.
227241
}
228-
let size = usize::try_from(header.ch_size(self.endian)).ok()?;
229-
let buf = stash.allocate(size);
230-
decompress_zlib(data.0, buf)?;
231-
return Some(buf);
232242
}
233243

234244
// Check for the nonstandard GNU compression format, i.e., as generated
@@ -347,6 +357,13 @@ fn decompress_zlib(input: &[u8], output: &mut [u8]) -> Option<()> {
347357
}
348358
}
349359

360+
fn decompress_zstd(input: &[u8], output: &mut [u8]) -> Option<()> {
361+
use ruzstd::io::Read;
362+
363+
let mut decoder = ruzstd::StreamingDecoder::new(input).ok()?;
364+
decoder.read_exact(output).ok()
365+
}
366+
350367
const DEBUG_PATH: &[u8] = b"/usr/lib/debug";
351368

352369
fn debug_path_exists() -> bool {

0 commit comments

Comments
 (0)