use std::fmt;
use std::io::{Cursor, Read};
use super::strtab::StrTab;
use super::symtab::SymTab;
use crate::le::read_u32_le;
pub const SHT_SYMTAB: u32 = 2;
pub const SHT_STRTAB: u32 = 3;
pub const SHT_RELA: u32 = 4;
pub const SHT_NOBITS: u32 = 8;
pub const SHT_REL: u32 = 9;
#[derive(Debug, Default, Clone, PartialEq)]
pub struct Section {
pub sh_name: u32,
pub sh_type: u32,
pub sh_flags: u32,
pub sh_addr: u32,
pub sh_offset: u32,
pub sh_size: u32,
pub sh_link: u32,
pub sh_info: u32,
pub sh_addralign: u32,
pub sh_entsize: u32,
pub data: Vec<u8>,
pub name: String,
}
impl Section {
pub const HEADER_SIZE: usize = 40;
#[allow(clippy::too_many_arguments)]
pub fn new(
sh_name: u32,
sh_type: u32,
sh_flags: u32,
sh_addr: u32,
sh_offset: u32,
sh_size: u32,
sh_link: u32,
sh_info: u32,
sh_addralign: u32,
sh_entsize: u32,
data: Vec<u8>,
) -> Self {
Self {
sh_name,
sh_type,
sh_flags,
sh_addr,
sh_offset,
sh_size,
sh_link,
sh_info,
sh_addralign,
sh_entsize,
data,
name: String::new(),
}
}
pub fn pack_header(&self) -> Vec<u8> {
let mut buf = Vec::with_capacity(Self::HEADER_SIZE);
buf.extend_from_slice(&self.sh_name.to_le_bytes());
buf.extend_from_slice(&self.sh_type.to_le_bytes());
buf.extend_from_slice(&self.sh_flags.to_le_bytes());
buf.extend_from_slice(&self.sh_addr.to_le_bytes());
buf.extend_from_slice(&self.sh_offset.to_le_bytes());
if self.sh_type != SHT_NOBITS {
buf.extend_from_slice(&(self.data.len() as u32).to_le_bytes());
} else {
buf.extend_from_slice(&(self.sh_size).to_le_bytes());
}
buf.extend_from_slice(&self.sh_link.to_le_bytes());
buf.extend_from_slice(&self.sh_info.to_le_bytes());
buf.extend_from_slice(&self.sh_addralign.to_le_bytes());
buf.extend_from_slice(&self.sh_entsize.to_le_bytes());
buf
}
pub fn pack_data(&self) -> Vec<u8> {
self.data.clone()
}
pub fn pack(&self) -> (Vec<u8>, Vec<u8>) {
(self.pack_header(), self.pack_data())
}
pub fn unpack(rdr: &mut Cursor<&[u8]>) -> Self {
let sh_name = read_u32_le(rdr); let sh_type = read_u32_le(rdr); let sh_flags = read_u32_le(rdr); let sh_addr = read_u32_le(rdr); let sh_offset = read_u32_le(rdr); let sh_size = read_u32_le(rdr);
let sh_link = read_u32_le(rdr);
let sh_info = read_u32_le(rdr);
let sh_addralign = read_u32_le(rdr);
let sh_entsize = read_u32_le(rdr);
let mut data: Vec<u8> = vec![];
if sh_type != SHT_NOBITS {
data = vec![0u8; sh_size as usize];
rdr.set_position(sh_offset as u64);
rdr.read_exact(&mut data).unwrap();
}
Self {
sh_name,
sh_type,
sh_flags,
sh_addr,
sh_offset,
sh_size,
sh_link,
sh_info,
sh_addralign,
sh_entsize,
data,
name: String::new(), }
}
}
impl fmt::Display for Section {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let size = if self.sh_type == SHT_NOBITS {
self.sh_size
} else {
self.data.len() as u32
};
write!(
f,
"sh_name: 0x{:X} sh_type: 0x{:X} sh_flags: 0x{:X} sh_addr: 0x{:X} \
sh_offset: 0x{:X} sh_size: 0x{:X} sh_link: 0x{:X} sh_info: 0x{:X} \
sh_addralign: 0x{:X} sh_entsize: 0x{:X}",
self.sh_name,
self.sh_type,
self.sh_flags,
self.sh_addr,
self.sh_offset,
size,
self.sh_link,
self.sh_info,
self.sh_addralign,
self.sh_entsize
)
}
}
#[derive(Debug, Clone, Default)]
pub struct TextSection {
pub section: Section,
pub function_name: String,
}
impl TextSection {
pub fn from_section(section: Section) -> Self {
Self {
section,
function_name: String::new(),
}
}
pub fn pack(&self) -> (Vec<u8>, Vec<u8>) {
self.section.pack()
}
}
#[derive(Debug, Clone, Default, PartialEq)]
pub struct BssSection {
pub section: Section,
}
impl BssSection {
pub fn from_section(section: Section) -> Self {
Self { section }
}
pub fn pack_data(&self) -> Vec<u8> {
Vec::new()
}
pub fn pack(&self) -> (Vec<u8>, Vec<u8>) {
let header = self.section.pack_header();
let data = self.pack_data();
(header, data)
}
}
#[derive(Debug, Clone)]
pub enum SectionVariant {
Raw(Section),
SymTab(SymTab),
StrTab(StrTab),
Rel(RelocationRecord),
Bss(BssSection),
Text(TextSection),
}
impl SectionVariant {
pub fn as_section_mut(&mut self) -> &mut Section {
match self {
SectionVariant::Raw(s) => s,
SectionVariant::Bss(s) => &mut s.section,
SectionVariant::SymTab(s) => &mut s.section,
SectionVariant::StrTab(s) => &mut s.section,
SectionVariant::Rel(s) => &mut s.section,
SectionVariant::Text(s) => &mut s.section,
}
}
pub fn pack(&mut self) -> (Vec<u8>, Vec<u8>) {
match self {
SectionVariant::Raw(s) => s.pack(),
SectionVariant::Bss(s) => s.pack(),
SectionVariant::SymTab(s) => s.pack(),
SectionVariant::StrTab(s) => s.pack(),
SectionVariant::Rel(s) => s.pack(),
SectionVariant::Text(s) => s.pack(),
}
}
pub fn wrap(section: Section) -> Self {
match section.sh_type {
SHT_SYMTAB => Self::SymTab(SymTab::new(section)),
SHT_STRTAB => Self::StrTab(StrTab::new(section)),
_ => Self::Raw(section),
}
}
}
#[derive(Debug, Clone, Default, PartialEq)]
pub struct Relocation {
pub r_offset: u32,
pub r_info: u32,
pub symbol: String,
}
impl Relocation {
pub fn symbol_index(&self) -> usize {
(self.r_info >> 8) as usize
}
pub fn set_symbol_index(&mut self, index: u32) {
if index > 0xFFFFFF {
panic!(
"Relocation cannot reference a symbols above {}, got {index}",
0xFFFFFF
);
}
self.r_info = (index << 8) | self.type_id()
}
pub fn type_id(&self) -> u32 {
self.r_info & 0xff
}
pub fn pack(&self) -> Vec<u8> {
let mut buf = Vec::with_capacity(8);
buf.extend_from_slice(&self.r_offset.to_le_bytes());
buf.extend_from_slice(&self.r_info.to_le_bytes());
buf
}
pub fn unpack(data: &[u8]) -> Self {
let r_offset = u32::from_le_bytes(data[0..4].try_into().unwrap());
let r_info = u32::from_le_bytes(data[4..8].try_into().unwrap());
Self {
r_offset,
r_info,
symbol: String::new(),
}
}
pub fn unpack_all(data: &[u8]) -> Vec<Self> {
data.chunks(8).map(Relocation::unpack).collect()
}
}
#[derive(Debug, Clone, Default, PartialEq)]
pub struct RelocationRecord {
pub section: Section,
pub relocations: Vec<Relocation>,
}
impl RelocationRecord {
pub fn new(section: Section) -> Self {
let relocations = Relocation::unpack_all(§ion.data);
Self {
section,
relocations,
}
}
pub fn pack_data(&mut self) -> &[u8] {
let mut buf = Vec::with_capacity(self.relocations.len() * 8); for rel in &self.relocations {
buf.extend_from_slice(&rel.pack());
}
self.section.sh_size = buf.len() as u32;
self.section.data = buf;
&self.section.data
}
pub fn pack(&mut self) -> (Vec<u8>, Vec<u8>) {
let data = self.pack_data().to_vec();
let header = self.section.pack_header();
(header, data)
}
pub fn add_relocation(&mut self, rel: Relocation) {
self.relocations.push(rel);
}
}