Skip to content
Open
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
Binary file added sample-objects/arm64-main-r5f0-0-fw.armhf
Binary file not shown.
17 changes: 17 additions & 0 deletions src/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3146,6 +3146,23 @@ pub const DT_MIPS_DIRECT: u32 = 0x70000032;
/// The dynamic symbol entry of a callback function
pub const DT_MIPS_RLD_OBJ_UPDATE: u32 = 0x70000033;

// resource_type in FirmwareResource
/// Request for allocation of a physically contiguous memory region.
pub const RSC_CARVEOUT: u32 = 0;
/// Request to iommu_map a memory-based peripheral.
pub const RSC_DEVMEM: u32 = 1;
/// Announces the availability of a trace buffer into which the remote processor will be writing logs.
pub const RSC_TRACE: u32 = 2;
/// Declare support for a virtio device, and serve as its virtio header.
pub const RSC_VDEV: u32 = 3;
/// Start of the vendor specific resource types range
pub const RSC_VENDOR_START: u32 = 128;
/// End of the vendor specific resource types range
pub const RSC_VENDOR_END: u32 = 512;

/// Tells that the firmware does not care about the location of the resource
pub const FW_RSC_ADDR_ANY: u32 = u32::MAX;

/// None
pub const RHF_NONE: u32 = 0x00000000;
/// Use runtime loading shortcuts if possible
Expand Down
99 changes: 98 additions & 1 deletion src/elf_bytes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use crate::hash::{GnuHashTable, SysVHashTable};
use crate::note::NoteIterator;
use crate::parse::{ParseAt, ParseError, ReadBytesExt};
use crate::relocation::{RelIterator, RelaIterator};
use crate::resource_table::ResourceTable;
use crate::section::{SectionHeader, SectionHeaderTable};
use crate::segment::{ProgramHeader, SegmentTable};
use crate::string_table::StringTable;
Expand Down Expand Up @@ -549,6 +550,28 @@ impl<'data, E: EndianParse> ElfBytes<'data, E> {
))
}

/// Get the section data for a given [SectionHeader], and interpret it as a
/// [ResourceTable](crate::resource_table::ResourceTable)
///
/// Returns a [ParseError] if the
/// [sh_type](SectionHeader#structfield.sh_type) is not
/// [SHT_PROGBITS](abi::SHT_PROGBITS).
pub fn section_data_as_resource_table(
&self,
shdr: &SectionHeader,
) -> Result<ResourceTable<'data, E>, ParseError> {
if shdr.sh_type != abi::SHT_PROGBITS {
return Err(ParseError::UnexpectedSectionType((
shdr.sh_type,
abi::SHT_PROGBITS,
)));
}

let (buf, _) = self.section_data(shdr)?;
let mut offset = 0;
ResourceTable::parse_at(self.ehdr.endianness, self.ehdr.class, &mut offset, buf)
}

/// Internal helper to get the section data for an SHT_DYNAMIC section as a .dynamic section table.
/// See [ElfBytes::dynamic] or [ElfBytes::find_common_data] for the public interface
fn section_data_as_dynamic(
Expand Down Expand Up @@ -828,7 +851,10 @@ impl<'data, E: EndianParse> ElfBytes<'data, E> {
#[cfg(test)]
mod interface_tests {
use super::*;
use crate::abi::{SHT_GNU_HASH, SHT_NOBITS, SHT_NOTE, SHT_NULL, SHT_REL, SHT_RELA, SHT_STRTAB};
use crate::abi::{
FW_RSC_ADDR_ANY, SHT_GNU_HASH, SHT_NOBITS, SHT_NOTE, SHT_NULL, SHT_REL, SHT_RELA,
SHT_STRTAB,
};
use crate::endian::AnyEndian;
use crate::hash::sysv_hash;
use crate::note::{Note, NoteGnuAbiTag, NoteGnuBuildId};
Expand Down Expand Up @@ -1200,6 +1226,77 @@ mod interface_tests {
assert!(notes.next().is_none());
}

#[test]
fn section_data_as_resource_table() {
use crate::resource_table::{FirmwareResource, FirmwareResourceVdevVring};
let path = std::path::PathBuf::from("sample-objects/arm64-main-r5f0-0-fw.armhf");
let file_data = std::fs::read(path).expect("Could not read file.");
let slice = file_data.as_slice();
let file = ElfBytes::<AnyEndian>::minimal_parse(slice).expect("Open test1");

let shdr = file
.section_headers()
.expect("File should have a section table")
.get(24)
.expect("Failed to get .resource_table shdr");

let resource_table = file
.section_data_as_resource_table(&shdr)
.expect("Failed to read .resource_table section");

let mut it = (&resource_table).into_iter();
if let FirmwareResource::Vdev(vdev) = it.next().unwrap() {
assert_eq!(vdev.id, 7);
assert_eq!(vdev.notify_id, 0);
assert_eq!(vdev.dfeatures, 1);
assert_eq!(vdev.num_of_vrings, 2);
let mut it = (&vdev).into_iter();

let vdev_vring1 = it.next().unwrap();
assert_eq!(
vdev_vring1,
FirmwareResourceVdevVring {
da: FW_RSC_ADDR_ANY,
align: 0x1000,
num: 256,
notify_id: 1,
pa: 0
}
);

let vdev_vring2 = it.next().unwrap();
assert_eq!(
vdev_vring2,
FirmwareResourceVdevVring {
da: FW_RSC_ADDR_ANY,
align: 0x1000,
num: 256,
notify_id: 2,
pa: 0
}
);
assert!(it.next().is_none());
} else {
panic!(".resource_table parsed incorrectly");
}

if let FirmwareResource::Trace(trace) = it.next().unwrap() {
assert_eq!(trace.da, 0xA0106080);
assert_eq!(trace.len, 0x1000);
let null_pos = trace
.name
.iter()
.position(|&c| c == 0)
.unwrap_or(trace.name.len());
assert_eq!(
String::from_utf8_lossy(&trace.name[..null_pos]),
"trace:r5fss0_0"
);
} else {
panic!(".resource_table parsed incorrectly");
}
}

#[test]
fn segment_data_as_notes() {
let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
Expand Down
93 changes: 93 additions & 0 deletions src/elf_stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use crate::gnu_symver::{
use crate::note::NoteIterator;
use crate::parse::{ParseAt, ParseError};
use crate::relocation::{RelIterator, RelaIterator};
use crate::resource_table::ResourceTable;
use crate::section::{SectionHeader, SectionHeaderTable};
use crate::segment::ProgramHeader;
use crate::segment::SegmentTable;
Expand Down Expand Up @@ -646,6 +647,30 @@ impl<E: EndianParse, S: std::io::Read + std::io::Seek> ElfStream<E, S> {
))
}

/// Read the section data for the given
/// [SectionHeader](SectionHeader) and interpret it in-place as a
/// [ResourceTable](crate::resource_table::ResourceTable)
///
/// Returns a [ParseError] if the
/// [sh_type](SectionHeader#structfield.sh_type) is not
/// [SHT_PROGBITS](abi::SHT_PROGBITS).
pub fn section_data_as_resource_table(
&mut self,
shdr: &SectionHeader,
) -> Result<ResourceTable<'_, E>, ParseError> {
if shdr.sh_type != abi::SHT_PROGBITS {
return Err(ParseError::UnexpectedSectionType((
shdr.sh_type,
abi::SHT_PROGBITS,
)));
}

let (start, end) = shdr.get_data_range()?;
let buf = self.reader.read_bytes(start, end)?;
let mut offset = 0;
ResourceTable::parse_at(self.ehdr.endianness, self.ehdr.class, &mut offset, buf)
}

/// Read the segment data for the given [Segment](ProgramHeader).
pub fn segment_data(&mut self, phdr: &ProgramHeader) -> Result<&[u8], ParseError> {
let (start, end) = phdr.get_file_data_range()?;
Expand Down Expand Up @@ -739,6 +764,7 @@ impl<R: Read + Seek> CachingReader<R> {
#[cfg(test)]
mod interface_tests {
use super::*;
use crate::abi::FW_RSC_ADDR_ANY;
use crate::dynamic::Dyn;
use crate::endian::AnyEndian;
use crate::hash::SysVHashTable;
Expand Down Expand Up @@ -1112,6 +1138,73 @@ mod interface_tests {
assert!(notes.next().is_none());
}

#[test]
fn section_data_as_resource_table() {
use crate::resource_table::{FirmwareResource, FirmwareResourceVdevVring};
let path = std::path::PathBuf::from("sample-objects/arm64-main-r5f0-0-fw.armhf");
let io = std::fs::File::open(path).expect("Could not open file.");
let mut file = ElfStream::<AnyEndian, _>::open_stream(io).expect("Open test1");

let shdrs = file.section_headers();
let shdr = shdrs[24];

let resource_table = file
.section_data_as_resource_table(&shdr)
.expect("Failed to read .resource_table section");

let mut it = (&resource_table).into_iter();
if let FirmwareResource::Vdev(vdev) = it.next().unwrap() {
assert_eq!(vdev.id, 7);
assert_eq!(vdev.notify_id, 0);
assert_eq!(vdev.dfeatures, 1);
assert_eq!(vdev.num_of_vrings, 2);
let mut it = (&vdev).into_iter();

let vdev_vring1 = it.next().unwrap();
assert_eq!(
vdev_vring1,
FirmwareResourceVdevVring {
da: FW_RSC_ADDR_ANY,
align: 0x1000,
num: 256,
notify_id: 1,
pa: 0
}
);

let vdev_vring2 = it.next().unwrap();
assert_eq!(
vdev_vring2,
FirmwareResourceVdevVring {
da: FW_RSC_ADDR_ANY,
align: 0x1000,
num: 256,
notify_id: 2,
pa: 0
}
);
assert!(it.next().is_none());
} else {
panic!(".resource_table parsed incorrectly");
}

if let FirmwareResource::Trace(trace) = it.next().unwrap() {
assert_eq!(trace.da, 0xA0106080);
assert_eq!(trace.len, 0x1000);
let null_pos = trace
.name
.iter()
.position(|&c| c == 0)
.unwrap_or(trace.name.len());
assert_eq!(
String::from_utf8_lossy(&trace.name[..null_pos]),
"trace:r5fss0_0"
);
} else {
panic!(".resource_table parsed incorrectly");
}
}

#[test]
fn symbol_version_table() {
let path = std::path::PathBuf::from("sample-objects/symver.x86_64.so");
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ pub mod gnu_symver;
pub mod hash;
pub mod note;
pub mod relocation;
pub mod resource_table;
pub mod section;
pub mod segment;
pub mod string_table;
Expand Down
Loading