1
0
forked from M-Labs/nac3
nac3/nac3ld/src/lib.rs

1504 lines
60 KiB
Rust

#![deny(
future_incompatible,
let_underscore,
nonstandard_style,
rust_2024_compatibility,
clippy::all
)]
#![warn(clippy::pedantic)]
#![allow(
clippy::cast_possible_truncation,
clippy::cast_possible_wrap,
clippy::cast_sign_loss,
clippy::doc_markdown,
clippy::enum_glob_use,
clippy::missing_errors_doc,
clippy::missing_panics_doc,
clippy::module_name_repetitions,
clippy::similar_names,
clippy::struct_field_names,
clippy::too_many_lines,
clippy::wildcard_imports
)]
use dwarf::*;
use elf::*;
use std::collections::HashMap;
use std::{mem, ptr, slice, str};
extern crate byteorder;
use byteorder::{ByteOrder, LittleEndian};
mod dwarf;
mod elf;
#[derive(PartialEq, Clone, Copy)]
pub enum Isa {
CortexA9,
RiscV32,
}
#[derive(Debug)]
pub enum Error {
Parsing(&'static str),
Lookup(&'static str),
}
impl From<&'static str> for Error {
fn from(desc: &'static str) -> Error {
Error::Parsing(desc)
}
}
pub trait Relocatable {
fn offset(&self) -> Elf32_Addr;
fn type_info(&self) -> u8;
fn sym_info(&self) -> Elf32_Word;
fn addend(&self, sec_image: &[u8]) -> Elf32_Sword;
}
impl Relocatable for Elf32_Rel {
fn offset(&self) -> Elf32_Addr {
self.r_offset
}
fn type_info(&self) -> u8 {
ELF32_R_TYPE(self.r_info)
}
fn sym_info(&self) -> Elf32_Word {
ELF32_R_SYM(self.r_info)
}
fn addend(&self, sec_image: &[u8]) -> Elf32_Sword {
LittleEndian::read_i32(&sec_image[self.offset() as usize..])
}
}
impl Relocatable for Elf32_Rela {
fn offset(&self) -> Elf32_Addr {
self.r_offset
}
fn type_info(&self) -> u8 {
ELF32_R_TYPE(self.r_info)
}
fn sym_info(&self) -> Elf32_Word {
ELF32_R_SYM(self.r_info)
}
fn addend(&self, _: &[u8]) -> Elf32_Sword {
self.r_addend
}
}
struct SectionRecord<'a> {
shdr: Elf32_Shdr,
name: &'a str,
data: Vec<u8>,
}
fn read_unaligned<T: Copy>(data: &[u8], offset: usize) -> Option<T> {
if data.len() < offset + mem::size_of::<T>() {
None
} else {
let ptr = data.as_ptr().wrapping_add(offset).cast();
Some(unsafe { ptr::read_unaligned(ptr) })
}
}
#[must_use]
pub fn get_ref_slice<T: Copy>(data: &[u8], offset: usize, len: usize) -> Option<&[T]> {
if data.len() < offset + mem::size_of::<T>() * len {
None
} else {
let ptr = data.as_ptr().wrapping_add(offset).cast();
Some(unsafe { slice::from_raw_parts(ptr, len) })
}
}
fn from_struct_slice<T>(struct_vec: &[T]) -> Vec<u8> {
let ptr = struct_vec.as_ptr();
unsafe { slice::from_raw_parts(ptr.cast(), mem::size_of_val(struct_vec)) }.to_vec()
}
fn to_struct_slice<T>(bytes: &[u8]) -> &[T] {
unsafe { slice::from_raw_parts(bytes.as_ptr().cast(), bytes.len() / mem::size_of::<T>()) }
}
fn to_struct_mut_slice<T>(bytes: &mut [u8]) -> &mut [T] {
unsafe {
slice::from_raw_parts_mut(bytes.as_mut_ptr().cast(), bytes.len() / mem::size_of::<T>())
}
}
fn elf_hash(name: &[u8]) -> u32 {
let mut h: u32 = 0;
for c in name {
h = (h << 4) + u32::from(*c);
let g = h & 0xf000_0000;
if g != 0 {
h ^= g >> 24;
h &= !g;
}
}
h
}
fn name_starting_at_slice(slice: &[u8], offset: usize) -> Result<&[u8], Error> {
let size = slice
.iter()
.skip(offset)
.position(|&x| x == 0)
.ok_or("symbol in symbol table not null-terminated")?;
Ok(slice.get(offset..offset + size).ok_or("cannot read symbol name")?)
}
macro_rules! get_section_by_name {
($linker: ident, $sec_name: expr) => {
$linker.elf_shdrs.iter().find(|rec| rec.name == $sec_name)
};
}
macro_rules! get_mut_section_by_name {
($linker: ident, $sec_name: expr) => {
$linker.elf_shdrs.iter_mut().find(|rec| rec.name == $sec_name)
};
}
struct SymbolTableReader<'a> {
symtab: &'a [Elf32_Sym],
strtab: &'a [u8],
}
impl<'a> SymbolTableReader<'a> {
pub fn find_index_by_name(&self, sym_name: &[u8]) -> Option<usize> {
self.symtab.iter().position(|sym| {
if let Ok(dynsym_name) = name_starting_at_slice(self.strtab, sym.st_name as usize) {
sym_name == dynsym_name
} else {
false
}
})
}
}
pub struct Linker<'a> {
isa: Isa,
symtab: &'a [Elf32_Sym],
strtab: &'a [u8],
elf_shdrs: Vec<SectionRecord<'a>>,
section_map: HashMap<usize, usize>,
image: Vec<u8>,
load_offset: u32,
rela_dyn_relas: Vec<Elf32_Rela>,
}
impl<'a> Linker<'a> {
fn get_dynamic_symbol_table(&self) -> Result<SymbolTableReader, Error> {
let dynsym_rec = get_section_by_name!(self, ".dynsym")
.ok_or("cannot make SymbolTableReader using .dynsym")?;
Ok(SymbolTableReader {
symtab: to_struct_slice::<Elf32_Sym>(dynsym_rec.data.as_slice()),
strtab: self.elf_shdrs[dynsym_rec.shdr.sh_link as usize].data.as_slice(),
})
}
fn load_section(&mut self, shdr: &Elf32_Shdr, sh_name_str: &'a str, data: Vec<u8>) -> usize {
let mut elf_shdr = *shdr;
// Maintain alignment requirement specified in sh_addralign
let align = shdr.sh_addralign;
let padding = (align - (self.load_offset % align)) % align;
self.load_offset += padding;
elf_shdr.sh_addr =
if (shdr.sh_flags as usize & SHF_ALLOC) == SHF_ALLOC { self.load_offset } else { 0 };
elf_shdr.sh_offset = self.load_offset;
self.elf_shdrs.push(SectionRecord { shdr: elf_shdr, name: sh_name_str, data });
self.load_offset += shdr.sh_size;
self.elf_shdrs.len() - 1
}
// Perform relocation according to the relocation entries
// Only symbols that support relative addressing would be resolved
// This is because the loading address is not known yet
fn resolve_relocatables<R: Relocatable>(
&mut self,
relocs: &[R],
target_section: Elf32_Word,
) -> Result<(), Error> {
type RelocateFn = dyn Fn(&mut [u8], Elf32_Word);
struct RelocInfo<'a, R> {
pub defined_val: bool,
pub indirect_reloc: Option<&'a R>,
pub pc_relative: bool,
pub relocate: Option<Box<RelocateFn>>,
}
for reloc in relocs {
let sym = match reloc.sym_info() as usize {
STN_UNDEF => None,
sym_index => {
Some(self.symtab.get(sym_index).ok_or("symbol out of bounds of symbol table")?)
}
};
let resolve_symbol_addr =
|sym_option: Option<&Elf32_Sym>| -> Result<Elf32_Word, Error> {
let Some(sym) = sym_option else { return Ok(0) };
match sym.st_shndx {
SHN_UNDEF => Err(Error::Lookup("undefined symbol")),
SHN_ABS => Ok(sym.st_value),
sec_ind => self
.section_map
.get(&(sec_ind as usize))
.map(|&elf_sec_ind: &usize| {
// Unlike the code in artiq libdyld, the image offset value is
// irrelevant in this case.
// The .elf dynamic library can be linked to an arbitrary address
// within the kernel address space
self.elf_shdrs[elf_sec_ind].shdr.sh_offset as Elf32_Word
+ sym.st_value
})
.ok_or(Error::Parsing("section not mapped to the ELF file")),
}
};
let get_target_section_index = || -> Result<usize, Error> {
self.section_map
.get(&(target_section as usize))
.copied()
.ok_or(Error::Parsing("Cannot find section with matching sh_index"))
};
let classify = |reloc: &R, sym_option: Option<&Elf32_Sym>| -> Option<RelocInfo<R>> {
let defined_val = sym_option.map_or(true, |sym| {
sym.st_shndx != SHN_UNDEF || ELF32_ST_BIND(sym.st_info) == STB_LOCAL
});
match self.isa {
Isa::CortexA9 => match reloc.type_info() {
R_ARM_REL32 | R_ARM_TARGET2 => Some(RelocInfo {
defined_val,
indirect_reloc: None,
pc_relative: true,
relocate: Some(Box::new(|target_word, value| {
LittleEndian::write_u32(target_word, value);
})),
}),
R_ARM_PREL31 => Some(RelocInfo {
defined_val,
indirect_reloc: None,
pc_relative: true,
relocate: Some(Box::new(|target_word, value| {
LittleEndian::write_u32(
target_word,
(LittleEndian::read_u32(target_word) & 0x8000_0000)
| value & 0x7FFF_FFFF,
);
})),
}),
R_ARM_ABS32 => Some(RelocInfo {
defined_val,
indirect_reloc: None,
pc_relative: false,
relocate: None,
}),
_ => None,
},
Isa::RiscV32 => match reloc.type_info() {
R_RISCV_CALL_PLT | R_RISCV_GOT_HI20 | R_RISCV_PCREL_HI20 => {
Some(RelocInfo {
defined_val,
indirect_reloc: None,
pc_relative: true,
relocate: Some(Box::new(|target_word, value| {
let auipc_raw = LittleEndian::read_u32(target_word);
let auipc_insn =
(auipc_raw & 0xFFF) | ((value + 0x800) & 0xFFFF_F000);
LittleEndian::write_u32(target_word, auipc_insn);
})),
})
}
R_RISCV_32_PCREL => Some(RelocInfo {
defined_val,
indirect_reloc: None,
pc_relative: true,
relocate: Some(Box::new(|target_word, value| {
LittleEndian::write_u32(target_word, value);
})),
}),
R_RISCV_PCREL_LO12_I => {
let expected_offset = sym_option.map_or(0, |sym| sym.st_value);
let indirect_reloc =
relocs.iter().find(|reloc| reloc.offset() == expected_offset)?;
Some(RelocInfo {
defined_val: {
let indirect_sym =
self.symtab[indirect_reloc.sym_info() as usize];
indirect_sym.st_shndx != SHN_UNDEF
|| ELF32_ST_BIND(indirect_sym.st_info) == STB_LOCAL
},
indirect_reloc: Some(indirect_reloc),
pc_relative: true,
relocate: Some(Box::new(|target_word, value| {
// Here, we convert to direct addressing
// GOT reloc (indirect) -> lw + addi
// PCREL reloc (direct) -> addi
let (lo_opcode, lo_funct3) = (0b001_0011, 0b000);
let addi_lw_raw = LittleEndian::read_u32(target_word);
let addi_insn = lo_opcode
| (addi_lw_raw & 0xF8F80)
| (lo_funct3 << 12)
| ((value & 0xFFF) << 20);
LittleEndian::write_u32(target_word, addi_insn);
})),
})
}
R_RISCV_32 => Some(RelocInfo {
defined_val,
indirect_reloc: None,
pc_relative: false,
relocate: None,
}),
R_RISCV_SET32 => Some(RelocInfo {
defined_val,
indirect_reloc: None,
pc_relative: false,
relocate: Some(Box::new(|target_word, value| {
LittleEndian::write_u32(target_word, value);
})),
}),
R_RISCV_ADD32 => Some(RelocInfo {
defined_val,
indirect_reloc: None,
pc_relative: false,
relocate: Some(Box::new(|target_word, value| {
let old_value = LittleEndian::read_u32(target_word);
LittleEndian::write_u32(target_word, old_value.wrapping_add(value));
})),
}),
R_RISCV_SUB32 => Some(RelocInfo {
defined_val,
indirect_reloc: None,
pc_relative: false,
relocate: Some(Box::new(|target_word, value| {
let old_value = LittleEndian::read_u32(target_word);
LittleEndian::write_u32(target_word, old_value.wrapping_sub(value));
})),
}),
R_RISCV_SET16 => Some(RelocInfo {
defined_val,
indirect_reloc: None,
pc_relative: false,
relocate: Some(Box::new(|target_word, value| {
LittleEndian::write_u16(target_word, value as u16);
})),
}),
R_RISCV_ADD16 => Some(RelocInfo {
defined_val,
indirect_reloc: None,
pc_relative: false,
relocate: Some(Box::new(|target_word, value| {
let old_value = LittleEndian::read_u16(target_word);
LittleEndian::write_u16(
target_word,
old_value.wrapping_add(value as u16),
);
})),
}),
R_RISCV_SUB16 => Some(RelocInfo {
defined_val,
indirect_reloc: None,
pc_relative: false,
relocate: Some(Box::new(|target_word, value| {
let old_value = LittleEndian::read_u16(target_word);
LittleEndian::write_u16(
target_word,
old_value.wrapping_sub(value as u16),
);
})),
}),
R_RISCV_SET8 => Some(RelocInfo {
defined_val,
indirect_reloc: None,
pc_relative: false,
relocate: Some(Box::new(|target_word, value| {
target_word[0] = value as u8;
})),
}),
R_RISCV_ADD8 => Some(RelocInfo {
defined_val,
indirect_reloc: None,
pc_relative: false,
relocate: Some(Box::new(|target_word, value| {
target_word[0] = target_word[0].wrapping_add(value as u8);
})),
}),
R_RISCV_SUB8 => Some(RelocInfo {
defined_val,
indirect_reloc: None,
pc_relative: false,
relocate: Some(Box::new(|target_word, value| {
target_word[0] = target_word[0].wrapping_sub(value as u8);
})),
}),
R_RISCV_SET6 => Some(RelocInfo {
defined_val,
indirect_reloc: None,
pc_relative: false,
relocate: Some(Box::new(|target_word, value| {
target_word[0] = (target_word[0] & 0xC0) | ((value & 0x3F) as u8);
})),
}),
R_RISCV_SUB6 => Some(RelocInfo {
defined_val,
indirect_reloc: None,
pc_relative: false,
relocate: Some(Box::new(|target_word, value| {
let new_value = (target_word[0].wrapping_sub(value as u8)) & 0x3F;
target_word[0] = (target_word[0] & 0xC0) | new_value;
})),
}),
_ => None,
},
}
};
let reloc_info =
classify(reloc, sym).ok_or(Error::Parsing("unsupported relocation"))?;
let target_index = get_target_section_index()?;
let target_sec_off = self.elf_shdrs[target_index].shdr.sh_offset;
if reloc_info.defined_val {
let (sym_addr, rela_off) = {
let (refed_sym, refed_reloc) =
if let Some(indirect_reloc) = reloc_info.indirect_reloc {
(Some(&self.symtab[indirect_reloc.sym_info() as usize]), indirect_reloc)
} else {
(sym, reloc)
};
(resolve_symbol_addr(refed_sym)?, target_sec_off + refed_reloc.offset())
};
let target_sec_image = &mut self.elf_shdrs[target_index].data;
let value = if reloc_info.pc_relative {
sym_addr
.wrapping_sub(rela_off)
.wrapping_add(reloc.addend(target_sec_image) as Elf32_Word)
} else {
sym_addr.wrapping_add(reloc.addend(target_sec_image) as Elf32_Word)
};
if let Some(relocate) = reloc_info.relocate {
let target_word = &mut target_sec_image[reloc.offset() as usize..];
relocate(target_word, value);
} else {
self.rela_dyn_relas.push(Elf32_Rela {
r_offset: rela_off,
r_info: ELF32_R_INFO(
0, // R_ARM_RELATIVE does not have associated symbol
match self.isa {
Isa::CortexA9 => R_ARM_RELATIVE,
Isa::RiscV32 => R_RISCV_RELATIVE,
},
),
r_addend: value as Elf32_Sword,
});
}
} else {
let target_sec_image = &self.elf_shdrs[target_index].data;
let sym_name = name_starting_at_slice(self.strtab, sym.unwrap().st_name as usize)
.map_err(|_| "cannot read symbol name from original .strtab")?;
let dynsymtab_index = self
.get_dynamic_symbol_table()?
.find_index_by_name(sym_name)
.ok_or("UNDEF relative symbol: cannot find symbol in .dynsym")?;
self.rela_dyn_relas.push(Elf32_Rela {
r_offset: target_sec_off as Elf32_Addr + reloc.offset(),
r_info: ELF32_R_INFO(dynsymtab_index as Elf32_Word, reloc.type_info()),
r_addend: reloc.addend(target_sec_image),
});
}
}
Ok(())
}
// Fill in the .eh_frame_hdr section
// Technically it can be done before relocation, but the FDE entries in the
// eh_frame_hdr section should be sorted. There are no guarantees that those in
// .eh_frame would be sorted.
fn implement_eh_frame_hdr(&mut self) -> Result<(), Error> {
// Fetch .eh_frame & .eh_frame_hdr from the custom section table
let eh_frame_rec =
get_section_by_name!(self, ".eh_frame").ok_or("cannot find .eh_frame from .elf")?;
let eh_frame_hdr_rec = get_section_by_name!(self, ".eh_frame_hdr")
.ok_or("cannot find .eh_frame_hdr from .elf")?;
let eh_frame_slice = eh_frame_rec.data.as_slice();
// Prepare a new buffer to dodge borrow check
let mut eh_frame_hdr_vec: Vec<u8> = vec![0; eh_frame_hdr_rec.shdr.sh_size as usize];
let eh_frame = EH_Frame::new(eh_frame_slice, eh_frame_rec.shdr.sh_offset);
let mut eh_frame_hdr = EH_Frame_Hdr::new(
eh_frame_hdr_vec.as_mut_slice(),
eh_frame_hdr_rec.shdr.sh_offset,
eh_frame_rec.shdr.sh_offset,
);
eh_frame.cfi_records().flat_map(|cfi| cfi.fde_records()).for_each(&mut |(
init_pos,
virt_addr,
)| {
eh_frame_hdr.add_fde(init_pos, virt_addr);
});
// Sort FDE entries in .eh_frame_hdr
eh_frame_hdr.finalize_fde();
// Replace the data buffer in the record
get_mut_section_by_name!(self, ".eh_frame_hdr")
.ok_or("cannot find .eh_frame_hdr from .elf")?
.data = eh_frame_hdr_vec;
Ok(())
}
pub fn ld(data: &'a [u8]) -> Result<Vec<u8>, Error> {
fn allocate_rela_dyn<R: Relocatable>(
linker: &Linker,
relocs: &[R],
) -> Result<(usize, Vec<u32>), Error> {
let mut alloc_size = 0;
let mut rela_dyn_sym_indices = Vec::new();
for reloc in relocs {
if reloc.sym_info() as usize == STN_UNDEF {
continue;
}
let sym: &Elf32_Sym = linker
.symtab
.get(reloc.sym_info() as usize)
.ok_or("symbol out of bounds of symbol table")?;
match (linker.isa, reloc.type_info()) {
// Absolute address relocations
// A runtime relocation is needed to find the loading address
(Isa::CortexA9, R_ARM_ABS32) | (Isa::RiscV32, R_RISCV_32) => {
alloc_size += mem::size_of::<Elf32_Rela>(); // FIXME: RELA vs REL
if ELF32_ST_BIND(sym.st_info) == STB_GLOBAL && sym.st_shndx == SHN_UNDEF {
rela_dyn_sym_indices.push(reloc.sym_info());
}
}
// Relative address relocations
// Relay the relocation to the runtime linker only if the symbol is not defined
(Isa::CortexA9, R_ARM_REL32 | R_ARM_PREL31 | R_ARM_TARGET2)
| (
Isa::RiscV32,
R_RISCV_CALL_PLT | R_RISCV_PCREL_HI20 | R_RISCV_GOT_HI20 | R_RISCV_32_PCREL
| R_RISCV_SET32 | R_RISCV_ADD32 | R_RISCV_SUB32 | R_RISCV_SET16
| R_RISCV_ADD16 | R_RISCV_SUB16 | R_RISCV_SET8 | R_RISCV_ADD8
| R_RISCV_SUB8 | R_RISCV_SET6 | R_RISCV_SUB6,
) => {
if ELF32_ST_BIND(sym.st_info) == STB_GLOBAL && sym.st_shndx == SHN_UNDEF {
alloc_size += mem::size_of::<Elf32_Rela>(); // FIXME: RELA vs REL
rela_dyn_sym_indices.push(reloc.sym_info());
}
}
// RISC-V: Lower 12-bits relocations
// If the upper 20-bits relocation cannot be resolved,
// this relocation will be relayed to the runtime linker.
(Isa::RiscV32, R_RISCV_PCREL_LO12_I) => {
// Find the HI20 relocation
let indirect_reloc = relocs
.iter()
.find(|reloc| reloc.offset() == sym.st_value)
.ok_or("malformatted LO12 relocation")?;
let indirect_sym = linker.symtab[indirect_reloc.sym_info() as usize];
if ELF32_ST_BIND(indirect_sym.st_info) == STB_GLOBAL
&& indirect_sym.st_shndx == SHN_UNDEF
{
alloc_size += mem::size_of::<Elf32_Rela>(); // FIXME: RELA vs REL
rela_dyn_sym_indices.push(reloc.sym_info());
}
}
_ => {
println!("Relocation type 0x{:X?} is not supported", reloc.type_info());
unimplemented!()
}
}
}
Ok((alloc_size, rela_dyn_sym_indices))
}
let Some(ehdr) = read_unaligned::<Elf32_Ehdr>(data, 0) else {
Err("cannot read ELF header")?
};
let isa = match ehdr.e_machine {
EM_ARM => Isa::CortexA9,
EM_RISCV => Isa::RiscV32,
_ => return Err(Error::Parsing("unsupported architecture")),
};
let Some(shdrs) =
get_ref_slice::<Elf32_Shdr>(data, ehdr.e_shoff as usize, ehdr.e_shnum as usize)
else {
Err("cannot read section header table")?
};
// Read .strtab
let strtab_shdr = shdrs[ehdr.e_shstrndx as usize];
let Some(strtab) =
get_ref_slice::<u8>(data, strtab_shdr.sh_offset as usize, strtab_shdr.sh_size as usize)
else {
Err("cannot read the string table from data")?
};
// Read .symtab
let symtab_shdr = shdrs
.iter()
.find(|shdr| shdr.sh_type as usize == SHT_SYMTAB)
.ok_or(Error::Parsing("cannot find the symbol table"))?;
let Some(symtab) = get_ref_slice::<Elf32_Sym>(
data,
symtab_shdr.sh_offset as usize,
symtab_shdr.sh_size as usize / mem::size_of::<Elf32_Sym>(),
) else {
Err("cannot read the symbol table from data")?
};
// Section table for the .elf paired with the section name
// To be formalized incrementally
// Very hashmap-like structure, but the order matters, so it is a vector
let elf_shdrs = vec![SectionRecord {
shdr: Elf32_Shdr {
sh_name: 0,
sh_type: 0,
sh_flags: 0,
sh_addr: 0,
sh_offset: 0,
sh_size: 0,
sh_link: 0,
sh_info: 0,
sh_addralign: 0,
sh_entsize: 0,
},
name: "",
data: vec![0; 0],
}];
let elf_sh_data_off = mem::size_of::<Elf32_Ehdr>() + mem::size_of::<Elf32_Phdr>() * 5;
// Image of the linked dynamic library, to be formalized incrementally
// just as the section table eventually does
let image: Vec<u8> = vec![0; elf_sh_data_off];
// Section relocation table
// A map of the original index of copied sections to the new sections
let section_map = HashMap::new();
// Vector of relocation entries in .rela.dyn
let rela_dyn_relas = Vec::new();
let mut linker = Linker {
isa,
symtab,
strtab,
elf_shdrs,
section_map,
image,
load_offset: elf_sh_data_off as u32,
rela_dyn_relas,
};
// Generate .text, keep the section index to find .rela.text
let is_text_shdr = |shdr: &Elf32_Shdr| {
shdr.sh_flags as usize & (SHF_ALLOC | SHF_EXECINSTR) == (SHF_ALLOC | SHF_EXECINSTR)
};
let is_progbits = |shdr: &Elf32_Shdr| shdr.sh_type as usize == SHT_PROGBITS;
let text_shdr_index = shdrs
.iter()
.position(|shdr| is_text_shdr(shdr) && is_progbits(shdr))
.ok_or(Error::Parsing("cannot find the .text section"))?;
let text_shdr = shdrs[text_shdr_index];
linker.load_section(
&text_shdr,
".text",
data[text_shdr.sh_offset as usize
..text_shdr.sh_offset as usize + text_shdr.sh_size as usize]
.to_vec(),
);
linker.section_map.insert(text_shdr_index, 1);
// ARM: Prioritize the transfer of EXIDX before EXTAB
// It is to ensure that EXIDX is within a LOAD program header
// Otherwise, the runtime linker will not copy the index table
if linker.isa == Isa::CortexA9 {
let arm_exidx_shdr_index = shdrs
.iter()
.position(|shdr| shdr.sh_type as usize == SHT_ARM_EXIDX)
.ok_or(Error::Parsing("cannot find the .ARM.exidx section"))?;
let arm_exidx_shdr = shdrs[arm_exidx_shdr_index];
let loaded_index = linker.load_section(
&arm_exidx_shdr,
".ARM.exidx",
data[arm_exidx_shdr.sh_offset as usize
..arm_exidx_shdr.sh_offset as usize + arm_exidx_shdr.sh_size as usize]
.to_vec(),
);
linker.section_map.insert(arm_exidx_shdr_index, loaded_index);
}
// Prepare all read-only progbits except .eh_frame
// The executable section is already loaded as .text
for (i, shdr) in shdrs.iter().enumerate() {
if shdr.sh_type as usize != SHT_PROGBITS
|| shdr.sh_flags as usize & (SHF_WRITE | SHF_ALLOC | SHF_EXECINSTR) != SHF_ALLOC
{
continue;
}
let section_name = name_starting_at_slice(strtab, shdr.sh_name as usize)
.map_err(|_| "cannot read section name")?;
let elf_shdrs_index = linker.load_section(
shdr,
str::from_utf8(section_name).unwrap(),
data[shdr.sh_offset as usize..(shdr.sh_offset + shdr.sh_size) as usize].to_vec(),
);
linker.section_map.insert(i, elf_shdrs_index);
}
// Non-ARM targets use .eh_frame with an additional .eh_frame_hdr to perform
// exception handling. ARM targets use .ARM.exidx, indicated by the ARM_EXIDX type
// But the exception handling section would have been loaded beforehand.
// Therefore, there is nothing to do for CortexA9 target.
if linker.isa == Isa::RiscV32 {
// Prepare .eh_frame and give a dummy .eh_frame_hdr
// The header will be implemented later
let eh_frame_shdr = shdrs
.iter()
.find(|shdr| {
name_starting_at_slice(strtab, shdr.sh_name as usize).unwrap() == b".eh_frame"
})
.ok_or("cannot find .eh_frame from object")?;
// For some reason ld.lld would add an zero-entry of CIE at the end of the .eh_frame,
// which obviously has no FDEs associated to it. That entry should be skippable.
let eh_frame = &data[eh_frame_shdr.sh_offset as usize
..(eh_frame_shdr.sh_offset + eh_frame_shdr.sh_size) as usize];
// Allocate memory for .eh_frame_hdr
// Calculate the size by parsing .eh_frame at coarse as possible
let eh_frame_hdr_size = EH_Frame_Hdr::size_from_eh_frame(eh_frame);
// Describe the .eh_frame_hdr with a dummy shdr.
let eh_frame_hdr_shdr = Elf32_Shdr {
sh_name: 0,
sh_type: SHT_PROGBITS as Elf32_Word,
sh_flags: SHF_ALLOC as Elf32_Word,
sh_addr: 0,
sh_offset: 0,
sh_size: eh_frame_hdr_size as Elf32_Word,
sh_link: 0,
sh_info: 0,
sh_addralign: 4,
sh_entsize: 0,
};
linker.load_section(&eh_frame_hdr_shdr, ".eh_frame_hdr", vec![0; eh_frame_hdr_size]);
}
// Allocate memory for both .rela.dyn
// The number of entries in .rela.dyn is found by counting relocations that either
// - use global undefined symbols; or
// - need the loading address
let mut rela_dyn_size = 0;
let mut rela_dyn_sym_indices = Vec::<u32>::new();
// There are 2 types of relocation entries, RELA & REL.
// There are essentially no difference in processing their fields.
macro_rules! reloc_invariant {
($shdr: expr, $stmt: expr) => {
match $shdr.sh_type as usize {
SHT_RELA => {
let Some(relocs) = get_ref_slice::<Elf32_Rela>(
data,
$shdr.sh_offset as usize,
$shdr.sh_size as usize / mem::size_of::<Elf32_Rela>(),
) else {
Err("cannot parse relocations")?
};
#[allow(clippy::redundant_closure_call)]
$stmt(relocs)
}
SHT_REL => {
let Some(relocs) = get_ref_slice::<Elf32_Rel>(
data,
$shdr.sh_offset as usize,
$shdr.sh_size as usize / mem::size_of::<Elf32_Rel>(),
) else {
Err("cannot parse relocations")?
};
#[allow(clippy::redundant_closure_call)]
$stmt(relocs)
}
_ => unreachable!(),
}
};
}
for shdr in shdrs
.iter()
.filter(|shdr| shdr.sh_type as usize == SHT_REL || shdr.sh_type as usize == SHT_RELA)
{
// If the reloction refers to a section that will not be loaded,
// do not allocate space for the resulting relocations, it will not be processed
let referred_shdr = shdrs
.get(shdr.sh_info as usize)
.ok_or("relocation is not specified to a valid section number")?;
if (referred_shdr.sh_flags as usize & SHF_ALLOC) != SHF_ALLOC {
continue;
}
reloc_invariant!(shdr, |relocs| {
match allocate_rela_dyn(&linker, relocs) {
Ok((alloc_size, additional_indices)) => {
rela_dyn_size += alloc_size;
rela_dyn_sym_indices.extend(additional_indices);
Ok(())
}
Err(e) => Err(e),
}
})?;
}
// Avoid symbol duplication
rela_dyn_sym_indices.sort_unstable();
rela_dyn_sym_indices.dedup();
if rela_dyn_size != 0 {
let rela_dyn_shdr = Elf32_Shdr {
sh_name: 0,
sh_type: SHT_RELA as Elf32_Word,
sh_flags: SHF_ALLOC as Elf32_Word,
sh_addr: 0,
sh_offset: 0,
sh_size: rela_dyn_size as Elf32_Word,
sh_link: 0,
sh_info: 0,
sh_addralign: 4,
sh_entsize: mem::size_of::<Elf32_Rela>() as Elf32_Word,
};
linker.load_section(&rela_dyn_shdr, ".rela.dyn", vec![0; rela_dyn_size]);
}
// Construct the .dynsym & .dynstr sections
// .dynsym section should only contain the symbols needed for .rela.dyn
let mut dynsym = Vec::new();
let mut dynstr = Vec::new();
let mut dynsym_names = Vec::new();
dynsym.push(Elf32_Sym {
st_name: 0,
st_value: 0,
st_size: 0,
st_info: 0,
st_other: 0,
st_shndx: 0,
});
dynstr.push(0);
dynsym_names.push((0, 0));
for rela_dyn_sym_index in rela_dyn_sym_indices {
let mut sym = linker.symtab[rela_dyn_sym_index as usize];
let sym_name = name_starting_at_slice(strtab, sym.st_name as usize)
.map_err(|_| "cannot read symbol name from the original .strtab")?;
let dynstr_start_index = dynstr.len();
sym.st_name = dynstr_start_index as Elf32_Word;
if sym.st_shndx != SHN_UNDEF {
let elf_shdr_index = linker
.section_map
.get(&(sym.st_shndx as usize))
.copied()
.ok_or(Error::Parsing("Cannot find section with matching sh_index"))?;
let elf_shdr_offset = linker.elf_shdrs[elf_shdr_index].shdr.sh_offset;
sym.st_value += elf_shdr_offset;
// Convert scope of symbols to global
// All relocation symbols must be visible to the dynamic linker
sym.st_info = ELF32_ST_INFO(STB_GLOBAL, ELF32_ST_TYPE(sym.st_info));
sym.st_shndx = elf_shdr_index as Elf32_Section;
}
dynsym.push(sym);
dynstr.extend(sym_name);
dynstr.push(0);
dynsym_names.push((dynstr_start_index, dynstr_start_index + sym_name.len()));
}
// Copy __modinit__ symbol from object file
let modinit_sym = symtab
.iter()
.find(|sym| {
let sym_name = name_starting_at_slice(strtab, sym.st_name as usize).unwrap();
sym_name == b"__modinit__"
})
.ok_or("__modinit__ symbol cannot be found")?;
let modinit_shdr_index = linker
.section_map
.get(&(modinit_sym.st_shndx as usize))
.copied()
.ok_or(Error::Parsing("Cannot find section with matching sh_index"))?;
let modinit_shdr = linker.elf_shdrs[modinit_shdr_index].shdr;
let dynstr_start_index = dynstr.len();
dynsym.push(Elf32_Sym {
st_name: dynstr_start_index as Elf32_Word,
st_value: modinit_shdr.sh_offset + modinit_sym.st_value,
st_size: modinit_sym.st_value,
st_info: modinit_sym.st_info,
st_other: modinit_sym.st_other,
st_shndx: modinit_shdr_index as Elf32_Section,
});
let sym_slice = b"__modinit__";
dynsym_names.push((dynstr.len(), dynstr.len() + sym_slice.len()));
dynstr.extend(sym_slice);
dynstr.push(0);
// Additional symbols
// st_name will be defined when synthesizing .dynstr
// st_value & st_shndx will be finalized when .bss sections are processed
let mut extra_sym_vec = vec![
Elf32_Sym {
st_name: 0,
st_value: 0,
st_size: 0,
st_info: ELF32_ST_INFO(STB_GLOBAL, STT_NOTYPE),
st_other: STV_DEFAULT as u8,
st_shndx: 0,
};
3
];
let sym_slice = b"__bss_start";
dynsym_names.push((dynstr.len(), dynstr.len() + sym_slice.len()));
extra_sym_vec[0].st_name = dynstr.len() as Elf32_Word;
dynstr.extend(b"__bss_start");
dynstr.push(0);
let sym_slice = b"_end";
dynsym_names.push((dynstr.len(), dynstr.len() + sym_slice.len()));
extra_sym_vec[1].st_name = dynstr.len() as Elf32_Word;
dynstr.extend(b"_end");
dynstr.push(0);
let sym_slice = b"_sstack_guard";
dynsym_names.push((dynstr.len(), dynstr.len() + sym_slice.len()));
extra_sym_vec[2].st_name = dynstr.len() as Elf32_Word;
dynstr.extend(b"_sstack_guard");
dynstr.push(0);
dynsym.extend(extra_sym_vec);
// There should be dynsym.len() buckets & chains
// No entries could be skipped, even symbols like __modinit__ will be looked up
let mut hash_bucket: Vec<u32> = vec![0; dynsym.len()];
let mut hash_chain: Vec<u32> = vec![0; dynsym.len()];
for (sym_index, (str_start, str_end)) in
dynsym_names.iter().enumerate().take(dynsym.len()).skip(1)
{
let hash = elf_hash(&dynstr[*str_start..*str_end]);
let mut hash_index = hash as usize % hash_bucket.len();
if hash_bucket[hash_index] == 0 {
hash_bucket[hash_index] = sym_index as u32;
} else {
hash_index = hash_bucket[hash_index] as usize;
while hash_chain[hash_index] != 0 {
hash_index = hash_chain[hash_index] as usize;
}
hash_chain[hash_index] = sym_index as u32;
}
}
let mut hash: Vec<u32> = Vec::new();
hash.push(hash_bucket.len() as u32);
hash.push(hash_chain.len() as u32);
hash.extend(hash_bucket);
hash.extend(hash_chain);
// Add .dynsym, .dynstr, .hash to the linker
let dynstr_elf_index = linker.load_section(
&Elf32_Shdr {
sh_name: 0,
sh_type: SHT_STRTAB as Elf32_Word,
sh_flags: SHF_ALLOC as Elf32_Word,
sh_addr: 0,
sh_offset: 0,
sh_size: dynstr.len() as Elf32_Word,
sh_link: 0,
sh_info: 0,
sh_addralign: 1,
sh_entsize: 0,
},
".dynstr",
dynstr,
);
let dynsym_elf_index = linker.load_section(
&Elf32_Shdr {
sh_name: 0,
sh_type: SHT_DYNSYM as Elf32_Word,
sh_flags: SHF_ALLOC as Elf32_Word,
sh_addr: 0,
sh_offset: 0,
sh_size: (dynsym.len() * mem::size_of::<Elf32_Sym>()) as Elf32_Word,
sh_link: dynstr_elf_index as Elf32_Word, // Index of the .dynstr section, to be inserted
sh_info: 1, // Last local symbol is at index 0 (NOTYPE)
sh_addralign: mem::size_of::<Elf32_Sym>() as Elf32_Word,
sh_entsize: mem::size_of::<Elf32_Sym>() as Elf32_Word,
},
".dynsym",
from_struct_slice(&dynsym),
);
let hash_elf_index = linker.load_section(
&Elf32_Shdr {
sh_name: 0,
sh_type: SHT_HASH as Elf32_Word,
sh_flags: SHF_ALLOC as Elf32_Word,
sh_addr: 0,
sh_offset: 0,
sh_size: (hash.len() * 4) as Elf32_Word,
sh_link: dynsym_elf_index as Elf32_Word, // Index of the .dynsym section
sh_info: 0,
sh_addralign: 4,
sh_entsize: 4,
},
".hash",
from_struct_slice(&hash),
);
// Link .rela.dyn header to the .dynsym header
get_mut_section_by_name!(linker, ".rela.dyn")
.ok_or(".dynsym not initialized before .dynstr")?
.shdr
.sh_link = dynsym_elf_index as Elf32_Word;
let first_writable_sec_elf_index = linker.elf_shdrs.len();
// Load writable PROGBITS sections
for (i, shdr) in shdrs.iter().enumerate() {
if shdr.sh_type as usize == SHT_PROGBITS
&& shdr.sh_flags as usize & (SHF_WRITE | SHF_ALLOC | SHF_EXECINSTR)
== (SHF_WRITE | SHF_ALLOC)
{
let section_name = name_starting_at_slice(strtab, shdr.sh_name as usize)
.map_err(|_| "failed to load section name")?;
let elf_shdrs_index = linker.load_section(
shdr,
str::from_utf8(section_name).unwrap(),
data[shdr.sh_offset as usize..(shdr.sh_offset + shdr.sh_size) as usize]
.to_vec(),
);
linker.section_map.insert(i, elf_shdrs_index);
}
}
// Load the .dynamic section
// Initialize with mandatory dyn entries
let mut dyn_entries = vec![
Elf32_Dyn {
d_tag: DT_HASH,
d_un: Elf32_Dyn__bindgen_ty_1 {
d_ptr: linker.elf_shdrs[hash_elf_index].shdr.sh_offset,
},
},
Elf32_Dyn {
d_tag: DT_STRTAB,
d_un: Elf32_Dyn__bindgen_ty_1 {
d_ptr: linker.elf_shdrs[dynstr_elf_index].shdr.sh_offset,
},
},
Elf32_Dyn {
d_tag: DT_SYMTAB,
d_un: Elf32_Dyn__bindgen_ty_1 {
d_ptr: linker.elf_shdrs[dynsym_elf_index].shdr.sh_offset,
},
},
Elf32_Dyn {
d_tag: DT_STRSZ,
d_un: Elf32_Dyn__bindgen_ty_1 {
d_val: linker.elf_shdrs[dynstr_elf_index].shdr.sh_size,
},
},
Elf32_Dyn {
d_tag: DT_SYMENT,
d_un: Elf32_Dyn__bindgen_ty_1 {
d_val: linker.elf_shdrs[dynsym_elf_index].shdr.sh_entsize,
},
},
];
if rela_dyn_size != 0 {
let rela_dyn_shdr = get_section_by_name!(linker, ".rela.dyn")
.ok_or(".rela.dyn header not properly initialised")?
.shdr;
dyn_entries.push(Elf32_Dyn {
d_tag: DT_RELA,
d_un: Elf32_Dyn__bindgen_ty_1 { d_ptr: rela_dyn_shdr.sh_offset },
});
dyn_entries.push(Elf32_Dyn {
d_tag: DT_RELASZ,
d_un: Elf32_Dyn__bindgen_ty_1 { d_ptr: rela_dyn_shdr.sh_size },
});
dyn_entries.push(Elf32_Dyn {
d_tag: DT_RELAENT,
d_un: Elf32_Dyn__bindgen_ty_1 { d_ptr: rela_dyn_shdr.sh_entsize },
});
}
// Termination entry in .dynamic
dyn_entries.push(Elf32_Dyn { d_tag: DT_NULL, d_un: Elf32_Dyn__bindgen_ty_1 { d_val: 0 } });
let dynamic_shdr = Elf32_Shdr {
sh_name: 0,
sh_type: SHT_DYNAMIC as Elf32_Word,
sh_flags: (SHF_WRITE | SHF_ALLOC) as Elf32_Word,
sh_addr: 0,
sh_offset: 0,
sh_size: (dyn_entries.len() * mem::size_of::<Elf32_Dyn>()) as Elf32_Word,
sh_link: dynstr_elf_index as Elf32_Word,
sh_info: 0,
sh_addralign: 4,
sh_entsize: mem::size_of::<Elf32_Dyn>() as Elf32_Word,
};
let dynamic_elf_index =
linker.load_section(&dynamic_shdr, ".dynamic", from_struct_slice(&dyn_entries));
let last_w_sec_elf_index = linker.elf_shdrs.len() - 1;
// Load all other A-flag non-PROGBITS sections (ARM: non-ARM_EXIDX as well)
// .bss sections (i.e. .sbss, .sbss.*, .bss & .bss.*) will be loaded later
let mut bss_index_vec = Vec::new();
for (i, shdr) in shdrs.iter().enumerate() {
if (shdr.sh_type as usize != SHT_PROGBITS)
&& (shdr.sh_type as usize != SHT_ARM_EXIDX)
&& ((shdr.sh_flags as usize & SHF_ALLOC) == SHF_ALLOC)
{
let section_name_slice = name_starting_at_slice(strtab, shdr.sh_name as usize)
.map_err(|_| "failed to load section name")?;
let section_name =
str::from_utf8(section_name_slice).map_err(|_| "cannot parse section name")?;
if section_name == ".bss"
|| section_name == ".sbss"
|| section_name.starts_with(".bss.")
|| section_name.starts_with(".sbss.")
{
bss_index_vec.push((i, section_name));
} else {
let elf_shdrs_index = linker.load_section(
shdr,
section_name,
data[shdr.sh_offset as usize..(shdr.sh_offset + shdr.sh_size) as usize]
.to_vec(),
);
linker.section_map.insert(i, elf_shdrs_index);
}
}
}
macro_rules! update_dynsym_record {
($sym_name: expr, $st_value: expr, $st_shndx: expr) => {
let symbol_table = linker.get_dynamic_symbol_table()?;
let bss_start_sym_index = symbol_table
.find_index_by_name($sym_name)
.ok_or(stringify!($sym_name symbol not initialized))?;
let dynsyms = to_struct_mut_slice::<Elf32_Sym>(
get_mut_section_by_name!(linker, ".dynsym")
.ok_or("cannot make retrieve .dynsym")?
.data
.as_mut_slice(),
);
dynsyms[bss_start_sym_index].st_value = $st_value;
dynsyms[bss_start_sym_index].st_shndx = $st_shndx;
}
}
// Load the .bss sections, finalize the .bss symbols
if bss_index_vec.is_empty() {
// Insert a zero-size .bss section if there aren't any
let bss_elf_index = linker.load_section(
&Elf32_Shdr {
sh_name: 0,
sh_type: SHT_NOBITS as Elf32_Word,
sh_flags: (SHF_ALLOC | SHF_WRITE) as Elf32_Word,
sh_addr: 0,
sh_offset: 0,
sh_size: 0,
sh_link: 0,
sh_info: 0,
sh_addralign: 4,
sh_entsize: 0,
},
".bss",
vec![0; 0],
);
let bss_offset = linker.elf_shdrs[bss_elf_index].shdr.sh_offset;
update_dynsym_record!(b"__bss_start", bss_offset, bss_elf_index as Elf32_Section);
update_dynsym_record!(b"_end", bss_offset, bss_elf_index as Elf32_Section);
} else {
for (bss_iter_index, &(bss_section_index, section_name)) in
bss_index_vec.iter().enumerate()
{
let shdr = &shdrs[bss_section_index];
let bss_elf_index = linker.load_section(
shdr,
section_name,
data[shdr.sh_offset as usize..(shdr.sh_offset + shdr.sh_size) as usize]
.to_vec(),
);
linker.section_map.insert(bss_section_index, bss_elf_index);
let loaded_shdr = linker.elf_shdrs[bss_elf_index].shdr;
if bss_iter_index == 0 {
update_dynsym_record!(
b"__bss_start",
loaded_shdr.sh_offset,
bss_elf_index as Elf32_Section
);
}
if bss_iter_index == bss_index_vec.len() - 1 {
update_dynsym_record!(
b"_end",
loaded_shdr.sh_offset + loaded_shdr.sh_size,
bss_elf_index as Elf32_Section
);
}
}
}
// All sections that should be allocated memory are loaded
// The stack guard address can be determined
let last_elf_shdr_index = linker.elf_shdrs.len() - 1;
let last_load_shdr = linker.elf_shdrs[last_elf_shdr_index].shdr;
let end_load_addr = last_load_shdr.sh_offset + last_load_shdr.sh_size;
let stack_guard_addr = end_load_addr + ((0x1000 - (end_load_addr % 0x1000)) % 0x1000);
update_dynsym_record!(
b"_sstack_guard",
stack_guard_addr,
last_elf_shdr_index as Elf32_Section
);
for shdr in shdrs
.iter()
.filter(|shdr| shdr.sh_type as usize == SHT_RELA || shdr.sh_type as usize == SHT_REL)
{
// If the reloction refers to a section that will not be loaded,
// do not process the relocations. The section will not be loaded
let referred_shdr = shdrs
.get(shdr.sh_info as usize)
.ok_or("relocation is not specified to a valid section number")?;
if (referred_shdr.sh_flags as usize & SHF_ALLOC) != SHF_ALLOC {
continue;
}
reloc_invariant!(shdr, |relocs| linker.resolve_relocatables(relocs, shdr.sh_info))?;
}
// Load .rela.dyn symbols generated during relocation
if rela_dyn_size != 0 {
let rela_dyn_rec = get_mut_section_by_name!(linker, ".rela.dyn")
.ok_or(".rela.dyn not initialized in the ELF file")?;
let rela_dyn_slice =
to_struct_mut_slice::<Elf32_Rela>(rela_dyn_rec.data.as_mut_slice());
for (i, &rela) in linker.rela_dyn_relas.iter().enumerate() {
rela_dyn_slice[i] = rela;
}
}
// Prepare a STRTAB to hold the names of section headers
// Fix the sh_name field of the section headers
let mut shstrtab = Vec::new();
for shdr_rec in &mut linker.elf_shdrs {
let shstrtab_index = shstrtab.len();
shstrtab.extend(shdr_rec.name.as_bytes());
shstrtab.push(0);
shdr_rec.shdr.sh_name = shstrtab_index as Elf32_Word;
}
// Add en entry for .shstrtab
let shstrtab_shdr_sh_name = shstrtab.len();
shstrtab.extend(b".shstrtab");
shstrtab.push(0);
let shstrtab_shdr = Elf32_Shdr {
sh_name: shstrtab_shdr_sh_name as Elf32_Word,
sh_type: SHT_STRTAB as Elf32_Word,
sh_flags: 0,
sh_addr: 0,
sh_offset: 0,
sh_size: shstrtab.len() as Elf32_Word,
sh_link: 0,
sh_info: 0,
sh_addralign: 1,
sh_entsize: 0,
};
let shstrtab_elf_index = linker.load_section(&shstrtab_shdr, ".shstrtab", shstrtab);
// Edit .eh_frame_hdr content
if linker.isa == Isa::RiscV32 {
linker.implement_eh_frame_hdr()?;
}
// Load all section data into the image
for rec in &linker.elf_shdrs[1..] {
linker.image.extend(vec![0; (rec.shdr.sh_offset as usize) - linker.image.len()]);
linker.image.extend(&rec.data);
}
// Load all section headers to the image
let alignment = (4 - (linker.image.len() % 4)) % 4;
let sec_headers_offset = linker.image.len() + alignment;
linker.image.extend(vec![0; alignment]);
for rec in &linker.elf_shdrs {
let shdr = rec.shdr;
linker.image.extend(unsafe {
slice::from_raw_parts(ptr::addr_of!(shdr).cast(), mem::size_of::<Elf32_Shdr>())
});
}
// Update the PHDRs
let phdr_offset = mem::size_of::<Elf32_Ehdr>();
unsafe {
let phdr_ptr = linker.image.as_mut_ptr().add(phdr_offset).cast();
let phdr_slice = slice::from_raw_parts_mut(phdr_ptr, 5);
// List of program headers:
// 1. ELF headers & program headers
// 2. Read-only sections
// 3. All other A-flag sections
// 4. Dynamic
// 5. EH frame & its header
let header_size = mem::size_of::<Elf32_Ehdr>() + mem::size_of::<Elf32_Phdr>() * 5;
phdr_slice[0] = Elf32_Phdr {
p_type: PT_LOAD,
p_offset: 0,
p_vaddr: 0,
p_paddr: 0,
p_filesz: header_size as Elf32_Word,
p_memsz: header_size as Elf32_Word,
p_flags: PF_R as Elf32_Word,
p_align: 0x1000,
};
let last_ro_shdr = linker.elf_shdrs[first_writable_sec_elf_index - 1].shdr;
let last_ro_addr = last_ro_shdr.sh_offset + last_ro_shdr.sh_size;
let ro_load_size = last_ro_addr - header_size as Elf32_Word;
phdr_slice[1] = Elf32_Phdr {
p_type: PT_LOAD,
p_offset: header_size as Elf32_Off,
p_vaddr: header_size as Elf32_Addr,
p_paddr: header_size as Elf32_Addr,
p_filesz: ro_load_size,
p_memsz: ro_load_size,
p_flags: (PF_R | PF_X) as Elf32_Word,
p_align: 0x1000,
};
let first_w_shdr = linker.elf_shdrs[first_writable_sec_elf_index].shdr;
let first_w_addr = first_w_shdr.sh_offset;
let last_w_shdr = linker.elf_shdrs[last_w_sec_elf_index].shdr;
let w_size = last_w_shdr.sh_offset + last_w_shdr.sh_size - first_w_addr;
phdr_slice[2] = Elf32_Phdr {
p_type: PT_LOAD,
p_offset: first_w_addr as Elf32_Off,
p_vaddr: first_w_addr as Elf32_Addr,
p_paddr: first_w_addr as Elf32_Addr,
p_filesz: w_size,
p_memsz: w_size,
p_flags: (PF_R | PF_W) as Elf32_Word,
p_align: 0x1000,
};
let dynamic_shdr = linker.elf_shdrs[dynamic_elf_index].shdr;
phdr_slice[3] = Elf32_Phdr {
p_type: PT_DYNAMIC,
p_offset: dynamic_shdr.sh_offset,
p_vaddr: dynamic_shdr.sh_offset,
p_paddr: dynamic_shdr.sh_offset,
p_filesz: dynamic_shdr.sh_size,
p_memsz: dynamic_shdr.sh_size,
p_flags: (PF_R | PF_W) as Elf32_Word,
p_align: 4,
};
let (eh_type, eh_shdr_name) = match linker.isa {
Isa::CortexA9 => (PT_ARM_EXIDX, ".ARM.exidx"),
Isa::RiscV32 => (PT_GNU_EH_FRAME, ".eh_frame_hdr"),
};
let eh_shdr = get_section_by_name!(linker, eh_shdr_name)
.ok_or("cannot read error handling section when finalizing phdrs")?
.shdr;
phdr_slice[4] = Elf32_Phdr {
p_type: eh_type,
p_offset: eh_shdr.sh_offset,
p_vaddr: eh_shdr.sh_offset,
p_paddr: eh_shdr.sh_offset,
p_filesz: eh_shdr.sh_size,
p_memsz: eh_shdr.sh_size,
p_flags: PF_R as Elf32_Word,
p_align: 4,
};
}
// Update the EHDR
let ehdr_ptr = linker.image.as_mut_ptr().cast();
unsafe {
*ehdr_ptr = Elf32_Ehdr {
e_ident: ehdr.e_ident,
e_type: ET_DYN,
e_machine: ehdr.e_machine,
e_version: ehdr.e_version,
e_entry: elf_sh_data_off as Elf32_Addr,
e_phoff: phdr_offset as Elf32_Off,
e_shoff: sec_headers_offset as Elf32_Off,
e_flags: match linker.isa {
Isa::RiscV32 => ehdr.e_flags,
Isa::CortexA9 => ehdr.e_flags | EF_ARM_ABI_FLOAT_HARD as Elf32_Word,
},
e_ehsize: mem::size_of::<Elf32_Ehdr>() as Elf32_Half,
e_phentsize: mem::size_of::<Elf32_Phdr>() as Elf32_Half,
e_phnum: 5,
e_shentsize: mem::size_of::<Elf32_Shdr>() as Elf32_Half,
e_shnum: linker.elf_shdrs.len() as Elf32_Half,
e_shstrndx: shstrtab_elf_index as Elf32_Half,
}
}
Ok(linker.image)
}
}