Skip to content

Commit

Permalink
elf: add ELFCOMPRESS_ZSTD
Browse files Browse the repository at this point in the history
Also add readobj support for compression headers.
  • Loading branch information
philipc committed Apr 4, 2023
1 parent e0f7ccd commit 135bb7a
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 25 deletions.
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
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
4 changes: 4 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

0 comments on commit 135bb7a

Please sign in to comment.