Implement SectionHeader parsing with the Parse trait

Also move SectionHeader into section.rs
Also, refactor the code for reading section data and section names from the section string table to be more idiomatic
This commit is contained in:
Christopher Cole 2022-10-06 01:06:04 -07:00
parent ae9e24420d
commit fcac7d79cc
3 changed files with 322 additions and 237 deletions

View File

@ -6,6 +6,7 @@ use std::io::{Read, Seek};
pub mod gabi;
pub mod types;
pub mod segment;
pub mod section;
pub mod parse;
use parse::Parse;
@ -159,86 +160,32 @@ impl File {
let mut sections = Vec::<Section>::default();
// Parse the section headers
let mut name_idxs: Vec<u32> = Vec::new();
io_file.seek(io::SeekFrom::Start(ehdr.e_shoff))?;
for _ in 0..ehdr.e_shnum {
let name: String = String::new();
let shtype: types::SectionType;
let flags: types::SectionFlag;
let addr: u64;
let offset: u64;
let size: u64;
let link: u32;
let info: u32;
let addralign: u64;
let entsize: u64;
name_idxs.push(utils::read_u32(ehdr.endianness, io_file)?);
shtype = types::SectionType(utils::read_u32(ehdr.endianness, io_file)?);
if ehdr.class == gabi::ELFCLASS32 {
flags = types::SectionFlag(utils::read_u32(ehdr.endianness, io_file)? as u64);
addr = utils::read_u32(ehdr.endianness, io_file)? as u64;
offset = utils::read_u32(ehdr.endianness, io_file)? as u64;
size = utils::read_u32(ehdr.endianness, io_file)? as u64;
link = utils::read_u32(ehdr.endianness, io_file)?;
info = utils::read_u32(ehdr.endianness, io_file)?;
addralign = utils::read_u32(ehdr.endianness, io_file)? as u64;
entsize = utils::read_u32(ehdr.endianness, io_file)? as u64;
} else {
flags = types::SectionFlag(utils::read_u64(ehdr.endianness, io_file)?);
addr = utils::read_u64(ehdr.endianness, io_file)?;
offset = utils::read_u64(ehdr.endianness, io_file)?;
size = utils::read_u64(ehdr.endianness, io_file)?;
link = utils::read_u32(ehdr.endianness, io_file)?;
info = utils::read_u32(ehdr.endianness, io_file)?;
addralign = utils::read_u64(ehdr.endianness, io_file)?;
entsize = utils::read_u64(ehdr.endianness, io_file)?;
}
sections.push(Section {
shdr: types::SectionHeader {
name: name,
shtype: shtype,
flags: flags,
addr: addr,
offset: offset,
size: size,
link: link,
info: info,
addralign: addralign,
entsize: entsize,
},
let shdr = section::SectionHeader::parse(ehdr.endianness, ehdr.class, io_file)?;
sections.push(
Section {
name: String::new(),
shdr: shdr,
data: Vec::new(),
});
}
// Read the section data
let mut s_i: usize = 0;
loop {
if s_i == ehdr.e_shnum as usize { break; }
let off = sections[s_i].shdr.offset;
let size = sections[s_i].shdr.size;
io_file.seek(io::SeekFrom::Start(off))?;
let mut data = vec![0; size as usize];
if sections[s_i].shdr.shtype != types::SHT_NOBITS {
io_file.read_exact(&mut data)?;
for section in sections.iter_mut() {
if section.shdr.sh_type == section::SHT_NOBITS {
continue;
}
sections[s_i].data = data;
s_i += 1;
io_file.seek(io::SeekFrom::Start(section.shdr.sh_offset))?;
section.data.resize(section.shdr.sh_size as usize, 0u8);
io_file.read_exact(&mut section.data)?;
}
// Parse the section names from the string header string table
s_i = 0;
loop {
if s_i == ehdr.e_shnum as usize { break; }
sections[s_i].shdr.name = utils::get_string(
&sections[ehdr.e_shstrndx as usize].data,
name_idxs[s_i] as usize)?;
s_i += 1;
// Parse the section names from the section header string table
for i in 0..sections.len() {
let shstr_data = &sections[ehdr.e_shstrndx as usize].data;
sections[i].name = utils::get_string(shstr_data, sections[i].shdr.sh_name as usize)?;
}
Ok(File {
@ -250,8 +197,8 @@ impl File {
pub fn get_symbols(&self, section: &Section) -> Result<Vec<types::Symbol>, ParseError> {
let mut symbols = Vec::new();
if section.shdr.shtype == types::SHT_SYMTAB || section.shdr.shtype == types::SHT_DYNSYM {
let link = &self.sections[section.shdr.link as usize].data;
if section.shdr.sh_type == section::SHT_SYMTAB || section.shdr.sh_type == section::SHT_DYNSYM {
let link = &self.sections[section.shdr.sh_link as usize].data;
let mut io_section = io::Cursor::new(&section.data);
while (io_section.position() as usize) < section.data.len() {
self.parse_symbol(&mut io_section, &mut symbols, link)?;
@ -299,13 +246,14 @@ impl File {
pub fn get_section<T: AsRef<str>>(&self, name: T) -> Option<&Section> {
self.sections
.iter()
.find(|section| section.shdr.name == name.as_ref() )
.find(|section| section.name == name.as_ref() )
}
}
#[derive(Debug)]
pub struct Section {
pub shdr: types::SectionHeader,
pub name: String,
pub shdr: section::SectionHeader,
pub data: Vec<u8>,
}

301
src/section.rs Normal file
View File

@ -0,0 +1,301 @@
use gabi;
use parse::Parse;
use types;
use utils;
/// Encapsulates the contents of an ELF Section Header
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct SectionHeader {
/// Section Name
pub sh_name: u32,
/// Section Type
pub sh_type: SectionType,
/// Section Flags
pub sh_flags: SectionFlag,
/// in-memory address where this section is loaded
pub sh_addr: u64,
/// Byte-offset into the file where this section starts
pub sh_offset: u64,
/// Section size in bytes
pub sh_size: u64,
/// Defined by section type
pub sh_link: u32,
/// Defined by section type
pub sh_info: u32,
/// address alignment
pub sh_addralign: u64,
/// size of an entry if section data is an array of entries
pub sh_entsize: u64,
}
impl<R> Parse<R> for SectionHeader
where
R: std::io::Read,
{
fn parse(
endian: types::Endian,
class: types::Class,
reader: &mut R,
) -> Result<Self, crate::ParseError> {
if class == gabi::ELFCLASS32 {
return Ok(SectionHeader {
sh_name: utils::read_u32(endian, reader)?,
sh_type: SectionType(utils::read_u32(endian, reader)?),
sh_flags: SectionFlag(utils::read_u32(endian, reader)? as u64),
sh_addr: utils::read_u32(endian, reader)? as u64,
sh_offset: utils::read_u32(endian, reader)? as u64,
sh_size: utils::read_u32(endian, reader)? as u64,
sh_link: utils::read_u32(endian, reader)?,
sh_info: utils::read_u32(endian, reader)?,
sh_addralign: utils::read_u32(endian, reader)? as u64,
sh_entsize: utils::read_u32(endian, reader)? as u64,
});
}
Ok(SectionHeader {
sh_name: utils::read_u32(endian, reader)?,
sh_type: SectionType(utils::read_u32(endian, reader)?),
sh_flags: SectionFlag(utils::read_u64(endian, reader)?),
sh_addr: utils::read_u64(endian, reader)?,
sh_offset: utils::read_u64(endian, reader)?,
sh_size: utils::read_u64(endian, reader)?,
sh_link: utils::read_u32(endian, reader)?,
sh_info: utils::read_u32(endian, reader)?,
sh_addralign: utils::read_u64(endian, reader)?,
sh_entsize: utils::read_u64(endian, reader)?,
})
}
}
impl std::fmt::Display for SectionHeader {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "Section Header: Name: {} Type: {} Flags: {} Addr: {:#010x} Offset: {:#06x} Size: {:#06x} Link: {} Info: {:#x} AddrAlign: {} EntSize: {}",
self.sh_name, self.sh_type, self.sh_flags, self.sh_addr, self.sh_offset,
self.sh_size, self.sh_link, self.sh_info, self.sh_addralign, self.sh_entsize)
}
}
/// Represens ELF Section type
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct SectionType(pub u32);
/// Inactive section with undefined values
pub const SHT_NULL: SectionType = SectionType(0);
/// Information defined by the program, includes executable code and data
pub const SHT_PROGBITS: SectionType = SectionType(1);
/// Section data contains a symbol table
pub const SHT_SYMTAB: SectionType = SectionType(2);
/// Section data contains a string table
pub const SHT_STRTAB: SectionType = SectionType(3);
/// Section data contains relocation entries with explicit addends
pub const SHT_RELA: SectionType = SectionType(4);
/// Section data contains a symbol hash table. Must be present for dynamic linking
pub const SHT_HASH: SectionType = SectionType(5);
/// Section data contains information for dynamic linking
pub const SHT_DYNAMIC: SectionType = SectionType(6);
/// Section data contains information that marks the file in some way
pub const SHT_NOTE: SectionType = SectionType(7);
/// Section data occupies no space in the file but otherwise resembles SHT_PROGBITS
pub const SHT_NOBITS: SectionType = SectionType(8);
/// Section data contains relocation entries without explicit addends
pub const SHT_REL: SectionType = SectionType(9);
/// Section is reserved but has unspecified semantics
pub const SHT_SHLIB: SectionType = SectionType(10);
/// Section data contains a minimal set of dynamic linking symbols
pub const SHT_DYNSYM: SectionType = SectionType(11);
/// Section data contains an array of constructors
pub const SHT_INIT_ARRAY: SectionType = SectionType(14);
/// Section data contains an array of destructors
pub const SHT_FINI_ARRAY: SectionType = SectionType(15);
/// Section data contains an array of pre-constructors
pub const SHT_PREINIT_ARRAY: SectionType = SectionType(16);
/// Section group
pub const SHT_GROUP: SectionType = SectionType(17);
/// Extended symbol table section index
pub const SHT_SYMTAB_SHNDX: SectionType = SectionType(18);
/// Number of reserved SHT_* values
pub const SHT_NUM: SectionType = SectionType(19);
/// Object attributes
pub const SHT_GNU_ATTRIBUTES: SectionType = SectionType(0x6ffffff5);
/// GNU-style hash section
pub const SHT_GNU_HASH: SectionType = SectionType(0x6ffffff6);
/// Pre-link library list
pub const SHT_GNU_LIBLIST: SectionType = SectionType(0x6ffffff7);
/// Version definition section
pub const SHT_GNU_VERDEF: SectionType = SectionType(0x6ffffffd);
/// Version needs section
pub const SHT_GNU_VERNEED: SectionType = SectionType(0x6ffffffe);
/// Version symbol table
pub const SHT_GNU_VERSYM: SectionType = SectionType(0x6fffffff);
impl std::fmt::Debug for SectionType {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{:#x}", self.0)
}
}
impl std::fmt::Display for SectionType {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let str = match *self {
SHT_NULL => "SHT_NULL",
SHT_PROGBITS => "SHT_PROGBITS",
SHT_SYMTAB => "SHT_SYMTAB",
SHT_STRTAB => "SHT_STRTAB",
SHT_RELA => "SHT_RELA",
SHT_HASH => "SHT_HASH",
SHT_DYNAMIC => "SHT_DYNAMIC",
SHT_NOTE => "SHT_NOTE",
SHT_NOBITS => "SHT_NOBITS",
SHT_REL => "SHT_REL",
SHT_SHLIB => "SHT_SHLIB",
SHT_DYNSYM => "SHT_DYNSYM",
SHT_INIT_ARRAY => "SHT_INIT_ARRAY",
SHT_FINI_ARRAY => "SHT_FINI_ARRAY",
SHT_PREINIT_ARRAY => "SHT_PREINIT_ARRAY",
SHT_GROUP => "SHT_GROUP",
SHT_SYMTAB_SHNDX => "SHT_SYMTAB_SHNDX",
SHT_NUM => "SHT_NUM",
SHT_GNU_ATTRIBUTES => "SHT_GNU_ATTRIBUTES",
SHT_GNU_HASH => "SHT_GNU_HASH",
SHT_GNU_LIBLIST => "SHT_GNU_LIBLIST",
SHT_GNU_VERDEF => "SHT_GNU_VERDEF",
SHT_GNU_VERNEED => "SHT_GNU_VERNEED",
SHT_GNU_VERSYM => "SHT_GNU_VERSYM",
_ => "Unknown",
};
write!(f, "{}", str)
}
}
///
/// Wrapper type for SectionFlag
///
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct SectionFlag(pub u64);
/// Empty flags
pub const SHF_NONE: SectionFlag = SectionFlag(0);
/// Writable
pub const SHF_WRITE: SectionFlag = SectionFlag(1);
/// Occupies memory during execution
pub const SHF_ALLOC: SectionFlag = SectionFlag(2);
/// Executable
pub const SHF_EXECINSTR: SectionFlag = SectionFlag(4);
/// Might be merged
pub const SHF_MERGE: SectionFlag = SectionFlag(16);
/// Contains nul-terminated strings
pub const SHF_STRINGS: SectionFlag = SectionFlag(32);
/// `sh_info' contains SHT index
pub const SHF_INFO_LINK: SectionFlag = SectionFlag(64);
/// Preserve order after combining
pub const SHF_LINK_ORDER: SectionFlag = SectionFlag(128);
/// Non-standard OS specific handling required
pub const SHF_OS_NONCONFORMING: SectionFlag = SectionFlag(256);
/// Section is member of a group
pub const SHF_GROUP: SectionFlag = SectionFlag(512);
/// Section hold thread-local data
pub const SHF_TLS: SectionFlag = SectionFlag(1024);
impl std::fmt::Debug for SectionFlag {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{:#x}", self.0)
}
}
impl std::fmt::Display for SectionFlag {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{:#x}", self.0)
}
}
#[cfg(test)]
mod tests {
use gabi;
use parse::Parse;
use section::{SectionFlag, SectionHeader, SectionType};
use types::{Class, Endian};
#[test]
fn parse_shdr32_fuzz_too_short() {
let data = [0u8; 40];
for n in 0..40 {
let slice = data.split_at(n).0;
assert!(SectionHeader::parse(
Endian(gabi::ELFDATA2LSB),
Class(gabi::ELFCLASS32),
&mut slice.as_ref()
)
.is_err());
}
}
#[test]
fn parse_shdr32_works() {
let mut data = [0u8; 40];
for n in 0u8..40 {
data[n as usize] = n;
}
assert_eq!(
SectionHeader::parse(
Endian(gabi::ELFDATA2LSB),
Class(gabi::ELFCLASS32),
&mut data.as_ref()
)
.unwrap(),
SectionHeader {
sh_name: 0x03020100,
sh_type: SectionType(0x07060504),
sh_flags: SectionFlag(0xB0A0908),
sh_addr: 0x0F0E0D0C,
sh_offset: 0x13121110,
sh_size: 0x17161514,
sh_link: 0x1B1A1918,
sh_info: 0x1F1E1D1C,
sh_addralign: 0x23222120,
sh_entsize: 0x27262524,
}
);
}
#[test]
fn parse_shdr64_fuzz_too_short() {
let data = [0u8; 64];
for n in 0..64 {
let slice = data.split_at(n).0;
assert!(SectionHeader::parse(
Endian(gabi::ELFDATA2LSB),
Class(gabi::ELFCLASS64),
&mut slice.as_ref()
)
.is_err());
}
}
#[test]
fn parse_shdr64_works() {
let mut data = [0u8; 64];
for n in 0u8..64 {
data[n as usize] = n;
}
assert_eq!(
SectionHeader::parse(
Endian(gabi::ELFDATA2MSB),
Class(gabi::ELFCLASS64),
&mut data.as_ref()
)
.unwrap(),
SectionHeader {
sh_name: 0x00010203,
sh_type: SectionType(0x04050607),
sh_flags: SectionFlag(0x08090A0B0C0D0E0F),
sh_addr: 0x1011121314151617,
sh_offset: 0x18191A1B1C1D1E1F,
sh_size: 0x2021222324252627,
sh_link: 0x28292A2B,
sh_info: 0x2C2D2E2F,
sh_addralign: 0x3031323334353637,
sh_entsize: 0x38393A3B3C3D3E3F,
}
);
}
}

View File

@ -397,170 +397,6 @@ impl fmt::Display for FileHeader {
}
}
/// Represens ELF Section type
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct SectionType(pub u32);
/// Inactive section with undefined values
pub const SHT_NULL: SectionType = SectionType(0);
/// Information defined by the program, includes executable code and data
pub const SHT_PROGBITS: SectionType = SectionType(1);
/// Section data contains a symbol table
pub const SHT_SYMTAB: SectionType = SectionType(2);
/// Section data contains a string table
pub const SHT_STRTAB: SectionType = SectionType(3);
/// Section data contains relocation entries with explicit addends
pub const SHT_RELA: SectionType = SectionType(4);
/// Section data contains a symbol hash table. Must be present for dynamic linking
pub const SHT_HASH: SectionType = SectionType(5);
/// Section data contains information for dynamic linking
pub const SHT_DYNAMIC: SectionType = SectionType(6);
/// Section data contains information that marks the file in some way
pub const SHT_NOTE: SectionType = SectionType(7);
/// Section data occupies no space in the file but otherwise resembles SHT_PROGBITS
pub const SHT_NOBITS: SectionType = SectionType(8);
/// Section data contains relocation entries without explicit addends
pub const SHT_REL: SectionType = SectionType(9);
/// Section is reserved but has unspecified semantics
pub const SHT_SHLIB: SectionType = SectionType(10);
/// Section data contains a minimal set of dynamic linking symbols
pub const SHT_DYNSYM: SectionType = SectionType(11);
/// Section data contains an array of constructors
pub const SHT_INIT_ARRAY: SectionType = SectionType(14);
/// Section data contains an array of destructors
pub const SHT_FINI_ARRAY: SectionType = SectionType(15);
/// Section data contains an array of pre-constructors
pub const SHT_PREINIT_ARRAY: SectionType = SectionType(16);
/// Section group
pub const SHT_GROUP: SectionType = SectionType(17);
/// Extended symbol table section index
pub const SHT_SYMTAB_SHNDX: SectionType = SectionType(18);
/// Number of reserved SHT_* values
pub const SHT_NUM: SectionType = SectionType(19);
/// Object attributes
pub const SHT_GNU_ATTRIBUTES: SectionType = SectionType(0x6ffffff5);
/// GNU-style hash section
pub const SHT_GNU_HASH: SectionType = SectionType(0x6ffffff6);
/// Pre-link library list
pub const SHT_GNU_LIBLIST: SectionType = SectionType(0x6ffffff7);
/// Version definition section
pub const SHT_GNU_VERDEF: SectionType = SectionType(0x6ffffffd);
/// Version needs section
pub const SHT_GNU_VERNEED: SectionType = SectionType(0x6ffffffe);
/// Version symbol table
pub const SHT_GNU_VERSYM: SectionType = SectionType(0x6fffffff);
impl fmt::Debug for SectionType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:#x}", self.0)
}
}
impl fmt::Display for SectionType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let str = match *self {
SHT_NULL => "SHT_NULL",
SHT_PROGBITS => "SHT_PROGBITS",
SHT_SYMTAB => "SHT_SYMTAB",
SHT_STRTAB => "SHT_STRTAB",
SHT_RELA => "SHT_RELA",
SHT_HASH => "SHT_HASH",
SHT_DYNAMIC => "SHT_DYNAMIC",
SHT_NOTE => "SHT_NOTE",
SHT_NOBITS => "SHT_NOBITS",
SHT_REL => "SHT_REL",
SHT_SHLIB => "SHT_SHLIB",
SHT_DYNSYM => "SHT_DYNSYM",
SHT_INIT_ARRAY => "SHT_INIT_ARRAY",
SHT_FINI_ARRAY => "SHT_FINI_ARRAY",
SHT_PREINIT_ARRAY => "SHT_PREINIT_ARRAY",
SHT_GROUP => "SHT_GROUP",
SHT_SYMTAB_SHNDX => "SHT_SYMTAB_SHNDX",
SHT_NUM => "SHT_NUM",
SHT_GNU_ATTRIBUTES => "SHT_GNU_ATTRIBUTES",
SHT_GNU_HASH => "SHT_GNU_HASH",
SHT_GNU_LIBLIST => "SHT_GNU_LIBLIST",
SHT_GNU_VERDEF => "SHT_GNU_VERDEF",
SHT_GNU_VERNEED => "SHT_GNU_VERNEED",
SHT_GNU_VERSYM => "SHT_GNU_VERSYM",
_ => "Unknown",
};
write!(f, "{}", str)
}
}
///
/// Wrapper type for SectionFlag
///
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct SectionFlag(pub u64);
/// Empty flags
pub const SHF_NONE: SectionFlag = SectionFlag(0);
/// Writable
pub const SHF_WRITE: SectionFlag = SectionFlag(1);
/// Occupies memory during execution
pub const SHF_ALLOC: SectionFlag = SectionFlag(2);
/// Executable
pub const SHF_EXECINSTR: SectionFlag = SectionFlag(4);
/// Might be merged
pub const SHF_MERGE: SectionFlag = SectionFlag(16);
/// Contains nul-terminated strings
pub const SHF_STRINGS: SectionFlag = SectionFlag(32);
/// `sh_info' contains SHT index
pub const SHF_INFO_LINK: SectionFlag = SectionFlag(64);
/// Preserve order after combining
pub const SHF_LINK_ORDER: SectionFlag = SectionFlag(128);
/// Non-standard OS specific handling required
pub const SHF_OS_NONCONFORMING: SectionFlag = SectionFlag(256);
/// Section is member of a group
pub const SHF_GROUP: SectionFlag = SectionFlag(512);
/// Section hold thread-local data
pub const SHF_TLS: SectionFlag = SectionFlag(1024);
impl fmt::Debug for SectionFlag {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:#x}", self.0)
}
}
impl fmt::Display for SectionFlag {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:#x}", self.0)
}
}
/// Encapsulates the contents of an ELF Section Header
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SectionHeader {
/// Section Name
pub name: String,
/// Section Type
pub shtype: SectionType,
/// Section Flags
pub flags: SectionFlag,
/// in-memory address where this section is loaded
pub addr: u64,
/// Byte-offset into the file where this section starts
pub offset: u64,
/// Section size in bytes
pub size: u64,
/// Defined by section type
pub link: u32,
/// Defined by section type
pub info: u32,
/// address alignment
pub addralign: u64,
/// size of an entry if section data is an array of entries
pub entsize: u64,
}
impl fmt::Display for SectionHeader {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Section Header: Name: {} Type: {} Flags: {} Addr: {:#010x} Offset: {:#06x} Size: {:#06x} Link: {} Info: {:#x} AddrAlign: {} EntSize: {}",
self.name, self.shtype, self.flags, self.addr, self.offset,
self.size, self.link, self.info, self.addralign, self.entsize)
}
}
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct SymbolType(pub u8);
/// Unspecified symbol type