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

elf: add ELFCOMPRESS_ZSTD #532

Merged
merged 1 commit into from
Apr 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
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
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ indexmap = { version = "1.6", optional = true }
wasmparser = { version = "0.102.0", optional = true }
memchr = { version = "2.4.1", default-features = false }
hashbrown = { version = "0.13.1", features = ["ahash"], default-features = false, optional = true }
ruzstd = { version = "0.3.1", optional = true }

# Internal feature, only used when building as part of libstd, not part of the
# stable interface of this crate.
Expand Down Expand Up @@ -48,7 +49,7 @@ write = ["write_std", "coff", "elf", "macho", "pe", "xcoff"]
std = ["memchr/std"]
# Enable decompression of compressed sections.
# This feature is not required if you want to do your own decompression.
compression = ["flate2", "std"]
compression = ["flate2", "ruzstd", "std"]
# Treat all types as unaligned.
# Normally types use the alignment required by the specifications, but
# sometimes files do not strictly follow the specifications.
Expand Down
10 changes: 10 additions & 0 deletions crates/examples/src/readobj/elf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,15 @@ fn print_section_headers<Elf: FileHeader>(
p.field_hex("AddressAlign", section.sh_addralign(endian).into());
p.field_hex("EntrySize", section.sh_entsize(endian).into());

if let Some(Some((compression, _, _))) = section.compression(endian, data).print_err(p)
{
p.group("CompressionHeader", |p| {
p.field_enum("Type", compression.ch_type(endian), FLAGS_ELFCOMPRESS);
p.field_hex("Size", compression.ch_size(endian).into());
p.field_hex("AddressAlign", compression.ch_addralign(endian).into());
});
}

match section.sh_type(endian) {
SHT_SYMTAB | SHT_DYNSYM => {
print_section_symbols(p, endian, data, elf, sections, index, section)
Expand Down Expand Up @@ -1315,6 +1324,7 @@ static FLAGS_SHF_PARISC: &[Flag<u32>] = &flags!(SHF_PARISC_SHORT, SHF_PARISC_HUG
static FLAGS_SHF_ALPHA: &[Flag<u32>] = &flags!(SHF_ALPHA_GPREL);
static FLAGS_SHF_ARM: &[Flag<u32>] = &flags!(SHF_ARM_ENTRYSECT, SHF_ARM_COMDEF);
static FLAGS_SHF_IA_64: &[Flag<u32>] = &flags!(SHF_IA_64_SHORT, SHF_IA_64_NORECOV);
static FLAGS_ELFCOMPRESS: &[Flag<u32>] = &flags!(ELFCOMPRESS_ZLIB, ELFCOMPRESS_ZSTD);
static FLAGS_STT: &[Flag<u8>] = &flags!(
STT_NOTYPE,
STT_OBJECT,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
SectionHeader {
Index: 6
Name: ".debug_info" (0x3E)
Type: SHT_PROGBITS (0x1)
Flags: 0x800
SHF_COMPRESSED (0x800)
Address: 0x0
Offset: 0x70
Size: 0x74
Link: 0
Info: 0
AddressAlign: 0x8
EntrySize: 0x0
CompressionHeader {
Type: ELFCOMPRESS_ZLIB (0x1)
Size: 0xAF
AddressAlign: 0x1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
SectionHeader {
Index: 6
Name: ".debug_info" (0x3E)
Type: SHT_PROGBITS (0x1)
Flags: 0x800
SHF_COMPRESSED (0x800)
Address: 0x0
Offset: 0x70
Size: 0x84
Link: 0
Info: 0
AddressAlign: 0x8
EntrySize: 0x0
CompressionHeader {
Type: ELFCOMPRESS_ZSTD (0x2)
Size: 0xAF
AddressAlign: 0x1
}
}
2 changes: 2 additions & 0 deletions src/elf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -819,6 +819,8 @@ pub struct CompressionHeader64<E: Endian> {

/// ZLIB/DEFLATE algorithm.
pub const ELFCOMPRESS_ZLIB: u32 = 1;
/// Zstandard algorithm.
pub const ELFCOMPRESS_ZSTD: u32 = 2;
/// Start of OS-specific compression types.
pub const ELFCOMPRESS_LOOS: u32 = 0x6000_0000;
/// End of OS-specific compression types.
Expand Down
76 changes: 51 additions & 25 deletions src/read/elf/section.rs
Original file line number Diff line number Diff line change
Expand Up @@ -381,32 +381,24 @@ impl<'data, 'file, Elf: FileHeader, R: ReadRef<'data>> ElfSection<'data, 'file,

fn maybe_compressed(&self) -> read::Result<Option<CompressedFileRange>> {
let endian = self.file.endian;
if (self.section.sh_flags(endian).into() & u64::from(elf::SHF_COMPRESSED)) == 0 {
return Ok(None);
}
let (section_offset, section_size) = self
.section
.file_range(endian)
.read_error("Invalid ELF compressed section type")?;
let mut offset = section_offset;
let header = self
.file
.data
.read::<Elf::CompressionHeader>(&mut offset)
.read_error("Invalid ELF compressed section offset")?;
if header.ch_type(endian) != elf::ELFCOMPRESS_ZLIB {
return Err(Error("Unsupported ELF compression type"));
if let Some((header, offset, compressed_size)) =
self.section.compression(endian, self.file.data)?
{
let format = match header.ch_type(endian) {
elf::ELFCOMPRESS_ZLIB => CompressionFormat::Zlib,
elf::ELFCOMPRESS_ZSTD => CompressionFormat::Zstandard,
_ => return Err(Error("Unsupported ELF compression type")),
};
let uncompressed_size = header.ch_size(endian).into();
Ok(Some(CompressedFileRange {
format,
offset,
compressed_size,
uncompressed_size,
}))
} else {
Ok(None)
}
let uncompressed_size = header.ch_size(endian).into();
let compressed_size = section_size
.checked_sub(offset - section_offset)
.read_error("Invalid ELF compressed section size")?;
Ok(Some(CompressedFileRange {
format: CompressionFormat::Zlib,
offset,
compressed_size,
uncompressed_size,
}))
}

/// Try GNU-style "ZLIB" header decompression.
Expand Down Expand Up @@ -1006,6 +998,40 @@ pub trait SectionHeader: Debug + Pod {
let data = self.data(endian, data)?;
AttributesSection::new(endian, data)
}

/// Parse the compression header if present.
///
/// Returns the header, and the offset and size of the compressed section data
/// in the file.
///
/// Returns `Ok(None)` if the section flags do not have `SHF_COMPRESSED`.
/// Returns `Err` for invalid values.
fn compression<'data, R: ReadRef<'data>>(
&self,
endian: Self::Endian,
data: R,
) -> read::Result<
Option<(
&'data <Self::Elf as FileHeader>::CompressionHeader,
u64,
u64,
)>,
> {
if (self.sh_flags(endian).into() & u64::from(elf::SHF_COMPRESSED)) == 0 {
return Ok(None);
}
let (section_offset, section_size) = self
.file_range(endian)
.read_error("Invalid ELF compressed section type")?;
let mut offset = section_offset;
let header = data
.read::<<Self::Elf as FileHeader>::CompressionHeader>(&mut offset)
.read_error("Invalid ELF compressed section offset")?;
let compressed_size = section_size
.checked_sub(offset - section_offset)
.read_error("Invalid ELF compressed section size")?;
Ok(Some((header, offset, compressed_size)))
}
}

impl<Endian: endian::Endian> SectionHeader for elf::SectionHeader32<Endian> {
Expand Down
23 changes: 23 additions & 0 deletions src/read/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -632,6 +632,10 @@ pub enum CompressionFormat {
///
/// Used for ELF compression and GNU compressed debug information.
Zlib,
/// Zstandard.
///
/// Used for ELF compression.
Zstandard,
}

/// A range in a file that may be compressed.
Expand Down Expand Up @@ -731,6 +735,25 @@ impl<'data> CompressedData<'data> {
.read_error("Invalid zlib compressed data")?;
Ok(Cow::Owned(decompressed))
}
#[cfg(feature = "compression")]
CompressionFormat::Zstandard => {
use core::convert::TryInto;
use std::io::Read;
let size = self
.uncompressed_size
.try_into()
.ok()
.read_error("Uncompressed data size is too large.")?;
let mut decompressed = Vec::with_capacity(size);
let mut decoder = ruzstd::StreamingDecoder::new(self.data)
.ok()
.read_error("Invalid zstd compressed data")?;
decoder
.read_to_end(&mut decompressed)
.ok()
.read_error("Invalid zstd compressed data")?;
Ok(Cow::Owned(decompressed))
}
_ => Err(Error("Unsupported compressed data.")),
}
}
Expand Down