diff --git a/src/link.rs b/src/link.rs index 85a449826b99b25083bd89c8fa987f1b12d82162..c048b6f566ed644d221aab2f7647fa30fbf5a3a9 100644 --- a/src/link.rs +++ b/src/link.rs @@ -21,8 +21,9 @@ use std::path::PathBuf; // use pip_mpu_relocate::reloc::RelIter; // use pip_mpu_relocate::relocate::relocate; // use pip_mpu_relocate::symbols::SymbolsQuery; -use std::io::Write; +use std::io::{BufWriter, Write}; +use crate::rel_iter::{RelError, RelIter}; use crate::symbols::SymbolsIter; const PADDING_SIZE: usize = 32; @@ -97,7 +98,7 @@ fn align(value: usize, align: usize) -> usize { /// `symbols_name` parameter. /// /// An error is returned if *any* requested symbol is missing -pub fn export_user_info<'data, T: Write, E: EndianParse>( +fn export_user_info<'data, T: Write, E: EndianParse>( elf: &'data ElfBytes<'data, E>, symbols_name: &[&'static str], output_file: &mut T, @@ -115,6 +116,40 @@ pub fn export_user_info<'data, T: Write, E: EndianParse>( Ok(()) } +/// Get the relocation values included in a given relocation section +/// +/// It returns only relocation of type R_ARM_ABS32 +fn get_relocations_from_section<'data, E: EndianParse>( + elf: &'data ElfBytes<'data, E>, + section_name: &str, +) -> Result<Option<Vec<u32>>> { + match RelIter::new(&elf, §ion_name) { + Ok(reliter) => Ok(Some( + reliter + .filter(|rel| rel.r_type == elf::abi::R_ARM_ABS32) + .map(|rel| rel.r_offset as u32) + .collect(), + )), + Err(RelError::SectionNotFound) => Ok(None), + Err(e) => Err(anyhow!( + "Error while extracting relocation information for {section_name}: {e}" + )), + } +} + +/// Output a slice of u32 values to a file +fn write_as_bytes<T: Write>(values: &[u32], output: &mut T) -> Result<()> { + values + .iter() + .map(|rel_offset| rel_offset.to_le_bytes()) + .try_for_each(|bytes| output.write_all(&bytes))?; + Ok(()) +} + +/// Perform *pip* linking of the elf files and generate a binary image +/// of the firmware. +/// +/// Parameters are given using an [`Opts`] object pub fn link(opts: Opts) -> Result<()> { // First, open and parse both crt0 and user_code elf files @@ -124,7 +159,7 @@ pub fn link(opts: Opts) -> Result<()> { let user_code_elf_file = std::fs::read(&opts.user_code_elf)?; let user_code_elf_data = user_code_elf_file.as_slice(); - let _user_code_elf = elf::ElfBytes::<AnyEndian>::minimal_parse(user_code_elf_data)?; + let user_code_elf = elf::ElfBytes::<AnyEndian>::minimal_parse(user_code_elf_data)?; // Then open Pip elf file if necessary let pip_elf_data = match opts.pip_elf { @@ -138,7 +173,7 @@ pub fn link(opts: Opts) -> Result<()> { }; // When reaching here, all files were opened and parsed // we can safely open the output file - let mut output = std::fs::File::create(opts.output)?; + let mut output = BufWriter::new(std::fs::File::create(opts.output)?); // First, if needed, output pip binary, padded to 32 bytes if let Some(pip_elf) = pip_elf { @@ -154,9 +189,63 @@ pub fn link(opts: Opts) -> Result<()> { dump_sections(&crt0_elf, CRT0_EXPORT_SECTIONS, &mut output)?; // Now, export the needed user code information - export_user_info(&_user_code_elf, USER_CODE_INFO_SYMBOLS, &mut output)?; + export_user_info(&user_code_elf, USER_CODE_INFO_SYMBOLS, &mut output)?; + + // Dump .rom section in a vec + let mut rom_bytes: Vec<u8> = vec![]; + dump_sections(&user_code_elf, &[".rom"], &mut rom_bytes)?; + + // Now the patch table for the .rom section (included in the .rel.rom section) + match get_relocations_from_section(&user_code_elf, ".rel.rom")? { + Some(rom_relocation_offsets) => { + let size = (rom_relocation_offsets.len() as u32).to_le_bytes(); + output.write_all(&size)?; + write_as_bytes(&rom_relocation_offsets, &mut output)?; + + // Use offsets to replace bytes that needs relocation by 0xff, and + // backup them in a values vec + let mut rom_relocation_values: Vec<u8> = vec![]; + rom_relocation_offsets.iter().enumerate().try_for_each(|(offset_no, offset)| { + let idx: usize = *offset as usize; + if idx > rom_bytes.len() - 4 { + return Err(anyhow!("Rom relocation offset {offset_no}({offset:0x}) points outside rom section ({} bytes long)", rom_bytes.len())); + } + rom_relocation_values.push(rom_bytes[idx]); + rom_relocation_values.push(rom_bytes[idx + 1]); + rom_relocation_values.push(rom_bytes[idx + 2]); + rom_relocation_values.push(rom_bytes[idx + 3]); + rom_bytes[idx] = 0xff; + rom_bytes[idx + 1] = 0xff; + rom_bytes[idx + 2] = 0xff; + rom_bytes[idx + 3] = 0xff; + Ok(()) + })?; + + // Now writes relocation values + output.write_all(&rom_relocation_values)?; + } + None => { + let size = 0u32.to_le_bytes(); + output.write_all(&size)?; + } + }; + + // Now the .rel.ram relocation offsets + match get_relocations_from_section(&user_code_elf, ".rel.ram")? { + Some(ram_relocation_offsets) => { + let size = (ram_relocation_offsets.len() as u32).to_le_bytes(); + output.write_all(&size)?; + write_as_bytes(&ram_relocation_offsets, &mut output)?; + } + None => { + let size = 0u32.to_le_bytes(); + output.write_all(&size)?; + } + }; + + // Finaly dump the patched .rom bytes + output.write_all(&rom_bytes)?; - Ok(()) }