diff --git a/examples/android_rel.rs b/examples/android_rel.rs new file mode 100644 index 0000000..1fb0f98 --- /dev/null +++ b/examples/android_rel.rs @@ -0,0 +1,37 @@ +use elf::{ abi, endian, ElfBytes, relocation::aps2}; + +fn main() { + let path = std::path::Path::new("sample-objects/android_cpp.arm.so"); + let raw_file = std::fs::read(path).unwrap(); + let file = + ElfBytes::::minimal_parse(raw_file.as_slice()).expect("parse elf file"); + + let rel = file.dynamic().unwrap().unwrap().iter().find(|d| d.d_tag == abi::DT_ANDROID_REL).unwrap(); + let rel_sz = file.dynamic().unwrap().unwrap().iter().find(|d| d.d_tag == abi::DT_ANDROID_RELSZ).unwrap(); + let rel_offset = addr_to_offset(&file, rel.d_ptr()).unwrap(); + + let rel_data = raw_file.as_slice().get(rel_offset..rel_offset+rel_sz.d_val() as usize).unwrap(); + + let android_rel = aps2::AndroidRelIterator::new(file.ehdr.class, rel_data).unwrap(); + + for rel in android_rel{ + match rel{ + Ok(rel) => { + println!("type: {}, sym: {}, offset: {}", rel.r_type, rel.r_sym, rel.r_offset); + }, + Err(e) => { + println!("error: {:?}", e); + break; + } + } + } +} +fn addr_to_offset(elf: &elf::ElfBytes, addr: u64) -> Option { + elf.segments().and_then(|segs| { + segs.iter().find(|item| { + item.p_type == abi::PT_LOAD && item.p_vaddr <= addr && addr < item.p_vaddr + item.p_memsz + }) + }).map(|seg| { + (addr - seg.p_vaddr + seg.p_offset) as usize + }) +} \ No newline at end of file diff --git a/examples/android_rela.rs b/examples/android_rela.rs new file mode 100644 index 0000000..8439c3d --- /dev/null +++ b/examples/android_rela.rs @@ -0,0 +1,24 @@ +use elf::{ abi, endian, ElfBytes}; + +fn main() { + let path = std::path::Path::new("sample-objects/android_cpp.aarch64.so"); + let raw_file = std::fs::read(path).unwrap(); + let file = + ElfBytes::::minimal_parse(raw_file.as_slice()).expect("parse elf file"); + + let rel_sh = file.section_headers().unwrap().iter().find(|seg|{seg.sh_type == abi::SHT_ANDROID_RELA}).unwrap(); + + let android_rela = file.section_date_as_android_relas(&rel_sh).unwrap(); + + for rela in android_rela{ + match rela{ + Ok(rela) => { + println!("type: {}, sym: {}, offset: {}, addend: {}", rela.r_type, rela.r_sym, rela.r_offset, rela.r_addend); + }, + Err(e) => { + println!("error: {:?}", e); + break; + } + } + } +} \ No newline at end of file diff --git a/sample-objects/android_cpp.aarch64.so b/sample-objects/android_cpp.aarch64.so new file mode 100644 index 0000000..53726fc Binary files /dev/null and b/sample-objects/android_cpp.aarch64.so differ diff --git a/sample-objects/android_cpp.arm.so b/sample-objects/android_cpp.arm.so new file mode 100644 index 0000000..c6c08ae Binary files /dev/null and b/sample-objects/android_cpp.arm.so differ diff --git a/src/abi.rs b/src/abi.rs index ae610d2..654fdef 100644 --- a/src/abi.rs +++ b/src/abi.rs @@ -502,6 +502,12 @@ pub const EM_AMDGPU: u16 = 224; pub const EM_RISCV: u16 = 243; /// Linux BPF pub const EM_BPF: u16 = 247; +// NEC SX-Aurora VE +pub const EM_VE: u16 = 251; +/// C-SKY 32-bit processor +pub const EM_CSKY: u16 = 252; +/// LoongArch +pub const EM_LOONGARCH: u16 = 258; // EV_* define constants for the ELF File Header's e_version field. // Represented as Elf32_Word in Elf32_Ehdr and Elf64_Word in Elf64_Ehdr which @@ -614,6 +620,10 @@ pub const SHT_PREINIT_ARRAY: u32 = 16; pub const SHT_GROUP: u32 = 17; /// Extended symbol table section index pub const SHT_SYMTAB_SHNDX: u32 = 18; +/// Relocation entries; only offsets. +/// Experimental support for SHT_RELR sections. For details, see proposal +/// at https://groups.google.com/forum/#!topic/generic-abi/bX460iggiKg +pub const SHT_RELR: u32 = 19; /// Values in [SHT_LOOS, SHT_HIOS] are reserved for operating system-specific semantics. pub const SHT_LOOS: u32 = 0x60000000; /// Object attributes @@ -814,6 +824,8 @@ pub const STV_HIDDEN: u8 = 2; /// would preempt by the default rules. pub const STV_PROTECTED: u8 = 3; +/// DT_* define constants for the ELF Dynamic Table's d_tag field. + /// An entry with a DT_NULL tag marks the end of the _DYNAMIC array. pub const DT_NULL: i64 = 0; /// This element holds the string table offset of a null-terminated string, @@ -946,6 +958,16 @@ pub const DT_PREINIT_ARRAYSZ: i64 = 33; /// This element holds the address of the SHT_SYMTAB_SHNDX section associated /// with the dynamic symbol table referenced by the DT_SYMTAB element. pub const DT_SYMTAB_SHNDX: i64 = 34; + +/// Experimental support for SHT_RELR sections. For details, see proposal +/// at https://groups.google.com/forum/#!topic/generic-abi/bX460iggiKg +/// Size of Relr relocation table. +pub const DT_RELRSZ: i64 = 35; +/// Address of relocation table (Relr entries) +pub const DT_RELR: i64 = 36; +/// Size of a Relr relocation entry. +pub const DT_RELRENT: i64 = 37; + /// Guile offset of GC roots pub const DT_GUILE_GC_ROOT: i64 = 0x37146000; /// Guile size in machine words of GC roots @@ -2658,3 +2680,108 @@ pub const R_X86_64_RELATIVE64: u32 = 38; pub const R_X86_64_GOTPCRELX: u32 = 41; /// `G + GOT + A - P` pub const R_X86_64_REX_GOTPCRELX: u32 = 42; + +// _ _ _ ____ ____ ___ ___ ____ +// / \ | \ | | _ \| _ \ / _ \_ _| _ \ +// / _ \ | \| | | | | |_) | | | | || | | | +// / ___ \| |\ | |_| | _ <| |_| | || |_| | +// /_/ \_\_| \_|____/|_| \_\\___/___|____/ +// + +/// Android compressed rel/rela sections. +/// from bionic/libc/include/elf.h +/// This was replaced by SHT_ANDROID_RELR in API level 28 (but is supported in all API levels >= 23). +pub const DT_ANDROID_REL: i64 = 0x6000000f; // DT_LOOS + 2 +pub const DT_ANDROID_RELSZ: i64 = 0x60000010; // DT_LOOS + 3 +pub const DT_ANDROID_RELA: i64 = 0x60000011; // DT_LOOS + 4 +pub const DT_ANDROID_RELASZ: i64 = 0x60000012; // DT_LOOS + 5 + +/// Experimental support for SHT_RELR sections. +/// This was eventually replaced by SHT_RELR and DT_RELR (which are identical other than their different constants) +/// but those constants are only supported by the OS in API levels >= 30. +pub const DT_ANDROID_RELR: i64 = 0x6fffe000; +pub const DT_ANDROID_RELRSZ: i64 = 0x6fffe001; +pub const DT_ANDROID_RELRENT: i64 = 0x6fffe003; +pub const DT_ANDROID_RELRCOUNT: i64 = 0x6fffe005; + +/// Experimental support for SHT_RELR sections. +/// For details, see proposal at https://groups.google.com/forum/#!topic/generic-abi/bX460iggiKg. +/// +/// This was eventually replaced by SHT_RELR and DT_RELR (which are identical other than their different constants), +/// but those constants are only supported by the OS in API levels >= 30. +pub const SHT_ANDROID_RELR: u32 = 0x6fffff00; + +/// Android compressed REL/RELA sections. +/// These were generated by the relocation packer in old versions of Android, and can be generated directly by lld with https://reviews.llvm.org/D39152. +/// +/// This was replaced by SHT_ANDROID_RELR in API level 28 (but is supported in all API levels >= 23). +pub const SHT_ANDROID_REL: u32 = 0x60000001; +pub const SHT_ANDROID_RELA: u32 = 0x60000002; + +// ___ _____ ___ __ +// |_ _|___ / ( _ ) / /_ +// | | |_ \ / _ \| '_ \ +// | | ___) | (_) | (_) | +// |___|____/ \___/ \___/ +// +pub const R_386_RELATIVE: u32 = 8; + +// _ ____ ____ +// / \ | _ \ / ___| +// / _ \ | |_) | | +// / ___ \| _ <| |___ +// /_/ \_\_| \_\\____| +// +pub const R_ARC_RELATIVE: u32 = 56; + +// _ _ _______ __ _ ____ ___ _ _ +// | | | | ____\ \/ / / \ / ___|/ _ \| \ | | +// | |_| | _| \ / / _ \| | _| | | | \| | +// | _ | |___ / \ / ___ \ |_| | |_| | |\ | +// |_| |_|_____/_/\_\/_/ \_\____|\___/|_| \_| +// +pub const R_HEX_RELATIVE: u32 = 35; + + +// _ ___ ___ _ _ ____ _ ____ ____ _ _ +// | | / _ \ / _ \| \ | |/ ___| / \ | _ \ / ___| | | | +// | | | | | | | | | \| | | _ / _ \ | |_) | | | |_| | +// | |__| |_| | |_| | |\ | |_| |/ ___ \| _ <| |___| _ | +// |_____\___/ \___/|_| \_|\____/_/ \_\_| \_\\____|_| |_| +// +pub const R_LARCH_RELATIVE: u32 = 3; + +// ____ _ _____ +// / ___| _ _ ___| |_ ___ _ __ ___ |__ / +// \___ \| | | / __| __/ _ \ '_ ` _ \ / / +// ___) | |_| \__ \ || __/ | | | | |/ /_ +// |____/ \__, |___/\__\___|_| |_| |_/____| +// |___/ +pub const R_390_RELATIVE: u32 = 12; + +// ____ +// / ___| _ __ __ _ _ __ ___ +// \___ \| '_ \ / _` | '__/ __| +// ___) | |_) | (_| | | | (__ +// |____/| .__/ \__,_|_| \___| +// |_| + +pub const R_SPARC_RELATIVE: u32 = 22; + +// ____ ____ _ ____ __ +// / ___/ ___|| |/ /\ \ / / +// | | \___ \| ' / \ V / +// | |___ ___) | . \ | | +// \____|____/|_|\_\ |_| +// + +pub const R_CKCORE_RELATIVE: u32 = 9; + +// __ _______ +// \ \ / / ____| +// \ \ / /| _| +// \ V / | |___ +// \_/ |_____| +// + +pub const R_VE_RELATIVE: u32 = 17; \ No newline at end of file diff --git a/src/elf_bytes.rs b/src/elf_bytes.rs index 6444e76..10a0172 100644 --- a/src/elf_bytes.rs +++ b/src/elf_bytes.rs @@ -9,7 +9,7 @@ use crate::gnu_symver::{ use crate::hash::{GnuHashTable, SysVHashTable}; use crate::note::NoteIterator; use crate::parse::{ParseAt, ParseError, ReadBytesExt}; -use crate::relocation::{RelIterator, RelaIterator}; +use crate::relocation::{RelIterator, RelaIterator, aps2, relr}; use crate::section::{SectionHeader, SectionHeaderTable}; use crate::segment::{ProgramHeader, SegmentTable}; use crate::string_table::StringTable; @@ -525,6 +525,71 @@ impl<'data, E: EndianParse> ElfBytes<'data, E> { )) } + /// Get the section data for a given [SectionHeader], and interpret it as an + /// iterator over no-addend relocations [Rel](crate::relocation::Rel) + /// + /// Returns a ParseError if the section is not of type [abi::SHT_ANDROID_REL] + pub fn section_date_as_android_rels( + &self, + shdr: &SectionHeader, + ) -> Result, ParseError> { + if shdr.sh_type != abi::SHT_ANDROID_REL{ + return Err(ParseError::UnexpectedSectionType(( + shdr.sh_type, + abi::SHT_ANDROID_REL, + ))); + } + let (buf, _) = self.section_data(shdr)?; + aps2::AndroidRelIterator::new( + self.ehdr.class, + buf, + ) + } + + /// Get the section data for a given [SectionHeader], and interpret it as an + /// iterator over relocations with addends [Rela](crate::relocation::Rela) + /// + /// Returns a ParseError if the section is not of type [abi::SHT_ANDROID_RELA] + pub fn section_date_as_android_relas( + &self, + shdr: &SectionHeader, + ) -> Result, ParseError> { + if shdr.sh_type != abi::SHT_ANDROID_RELA{ + return Err(ParseError::UnexpectedSectionType(( + shdr.sh_type, + abi::SHT_ANDROID_RELA, + ))); + } + let (buf, _) = self.section_data(shdr)?; + aps2::AndroidRelaIterator::new( + self.ehdr.class, + buf, + ) + } + + /// Get the section data for a given [SectionHeader], and interpret it as an + /// iterator over offset-only relocations [Rela](crate::relocation::Rel) + /// + /// Returns a ParseError if the section is not of type [abi::SHT_ANDROID_RELR] or [abi::SHT_RELR] + pub fn section_date_as_relrs( + &self, + shdr: &SectionHeader, + ) -> Result, ParseError> { + if shdr.sh_type != abi::SHT_ANDROID_RELR && shdr.sh_type != abi::SHT_RELR{ + return Err(ParseError::UnexpectedSectionType(( + shdr.sh_type, + abi::SHT_RELR, + ))); + } + let (buf, _) = self.section_data(shdr)?; + Ok(relr::RelativeRelocationIterator::new( + self.ehdr.e_machine, + self.ehdr.class, + self.ehdr.endianness, + buf, + )) + } + /// Get the section data for a given [SectionHeader], and interpret it as an /// iterator over [Note](crate::note::Note)s /// diff --git a/src/parse.rs b/src/parse.rs index d8ced7f..5faa3bd 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -6,7 +6,7 @@ use crate::file::Class; #[derive(Debug)] pub enum ParseError { - /// Returned when the ELF File Header's magic bytes weren't ELF's defined + /// Returned when magic bytes weren't as defined /// magic bytes BadMagic([u8; 4]), /// Returned when the ELF File Header's `e_ident[EI_CLASS]` wasn't one of the @@ -57,6 +57,8 @@ pub enum ParseError { /// Returned when parsing an ELF structure out of an io stream encountered /// an io error. IOError(std::io::Error), + /// Returned when parsing an android.rel section and the r_addend field is not zero + UnexpectedRelocationAddend, } #[cfg(feature = "std")] @@ -79,6 +81,7 @@ impl std::error::Error for ParseError { ParseError::TryFromSliceError(ref err) => Some(err), ParseError::TryFromIntError(ref err) => Some(err), ParseError::IOError(ref err) => Some(err), + ParseError::UnexpectedRelocationAddend => None, } } } @@ -102,6 +105,7 @@ impl core::error::Error for ParseError { ParseError::Utf8Error(ref err) => Some(err), ParseError::TryFromSliceError(ref err) => Some(err), ParseError::TryFromIntError(ref err) => Some(err), + ParseError::UnexpectedRelocationAddend => None, } } } @@ -168,6 +172,9 @@ impl core::fmt::Display for ParseError { ParseError::TryFromIntError(ref err) => err.fmt(f), #[cfg(feature = "std")] ParseError::IOError(ref err) => err.fmt(f), + ParseError::UnexpectedRelocationAddend => { + write!(f, "Unexpected r_addend in android.rel section") + } } } } @@ -327,6 +334,228 @@ impl<'data, E: EndianParse, P: ParseAt> IntoIterator for ParsingTable<'data, E, } } +/// https://en.wikipedia.org/wiki/LEB128 +pub mod leb128{ + use super::ParseError; + + const CONTINUATION_BIT: u8 = 1 << 7; // 0x80 + const VALUE_BITS: u8 = !CONTINUATION_BIT; // 0x7f + const SIGN_BIT: u8 = 1 << 6; // 0x40 + + macro_rules! parse_leb128 { + (i32) => { + parse_leb128!(1, int32, 32, i32); + }; + (i64) => { + parse_leb128!(1, int64, 64, i64); + }; + + (1, $name:tt, $bits:literal, $rett: ty) => { + pub fn $name(buf: &[u8]) -> Result<($rett, usize), ParseError>{ + let mut result = 0; + let (mut shift, mut offset) = (0, 0); + + let mut b ; + loop { + b = buf.get(offset).ok_or(ParseError::SliceReadError((offset, 1)))?; + offset += 1; + + result |= ((b&VALUE_BITS) as $rett) << shift; + shift += 7; + + if b & CONTINUATION_BIT == 0{ + break; + } + } + + if shift < $bits && b & SIGN_BIT != 0{ + result |= (!0 << shift); + } + Ok((result, offset)) + } + }; + + (u32) => { + parse_leb128!(0, uint32, 32, u32); + }; + (u64) => { + parse_leb128!(0, uint64, 64, u64); + }; + + (0, $name:tt, $bits:literal, $rett: ty) => { + pub fn $name(buf: &[u8]) -> Result<($rett, usize), ParseError>{ + let mut result = 0; + let (mut shift, mut offset) = (0, 0); + + loop{ + let b = buf.get(offset).ok_or(ParseError::SliceReadError((offset, 1)))?; + offset += 1; + + result |= ((b & VALUE_BITS) as $rett) << shift; + if b & CONTINUATION_BIT == 0{ + break; + } + shift += 7; + } + Ok((result, offset)) + } + }; + } + + parse_leb128!{i32} + parse_leb128!{i64} + parse_leb128!{u32} + parse_leb128!{u64} +} + +/// from https://android.googlesource.com/toolchain/mclinker/+/7cbf23dfd1f6021e76a4be5fd131d204c9b0c333/unittests/LEB128Test.cpp +#[cfg(test)] +mod leb128_tests { + use super::leb128::*; + + #[test] + fn test_leb128_uint64(){ + let data = [0, 0]; + let (result, offset) = uint64(&data).expect("Failed to parse"); + assert_eq!(result, 0); + assert_eq!(offset, 1); + + let data = [2, 0]; + let (result, offset) = uint64(&data).expect("Failed to parse"); + assert_eq!(result, 2); + assert_eq!(offset, 1); + + let data = [127, 0]; + let (result, offset) = uint64(&data).expect("Failed to parse"); + assert_eq!(result, 127); + assert_eq!(offset, 1); + + let data = [0x80, 1]; + let (result, offset) = uint64(&data).expect("Failed to parse"); + assert_eq!(result, 128); + assert_eq!(offset, 2); + + let data = [0x80+1, 1]; + let (result, offset) = uint64(&data).expect("Failed to parse"); + assert_eq!(result, 129); + assert_eq!(offset, 2); + + let data = [0x80+2, 1]; + let (result, offset) = uint64(&data).expect("Failed to parse"); + assert_eq!(result, 130); + assert_eq!(offset, 2); + + let data = [0x80+57, 100]; + let (result, offset) = uint64(&data).expect("Failed to parse"); + assert_eq!(result, 12857); + assert_eq!(offset, 2); + + let data = [0x80, 0x7f]; + let (result, offset) = uint64(&data).expect("Failed to parse"); + assert_eq!(result, 16256); + assert_eq!(offset, 2); + } + + #[test] + fn test_leb128_int64(){ + let data = [0, 0]; + let (result, offset) = int64(&data).expect("Failed to parse"); + assert_eq!(result, 0); + assert_eq!(offset, 1); + + let data = [2, 0]; + let (result, offset) = int64(&data).expect("Failed to parse"); + assert_eq!(result, 2); + assert_eq!(offset, 1); + + let data = [0x7e, 0]; + let (result, offset) = int64(&data).expect("Failed to parse"); + assert_eq!(result, -2); + assert_eq!(offset, 1); + + let data = [0x80+127, 0]; + let (result, offset) = int64(&data).expect("Failed to parse"); + assert_eq!(result, 127); + assert_eq!(offset, 2); + + let data = [0x80+1, 0x7f]; + let (result, offset) = int64(&data).expect("Failed to parse"); + assert_eq!(result, -127); + assert_eq!(offset, 2); + + let data = [0x80, 1]; + let (result, offset) = int64(&data).expect("Failed to parse"); + assert_eq!(result, 128); + assert_eq!(offset, 2); + + let data = [0x80, 0x7f]; + let (result, offset) = int64(&data).expect("Failed to parse"); + assert_eq!(result, -128); + assert_eq!(offset, 2); + + let data = [0x80+1, 1]; + let (result, offset) = int64(&data).expect("Failed to parse"); + assert_eq!(result, 129); + assert_eq!(offset, 2); + + let data = [0x80+0x7f, 0x7e]; + let (result, offset) = int64(&data).expect("Failed to parse"); + assert_eq!(result, -129); + assert_eq!(offset, 2); + } + + #[test] + fn test_leb128_uint32(){ + let data = [0, 0]; + let (result, offset) = uint32(&data).expect("Failed to parse"); + assert_eq!(result, 0); + assert_eq!(offset, 1); + + let data = [1, 0]; + let (result, offset) = uint32(&data).expect("Failed to parse"); + assert_eq!(result, 1); + assert_eq!(offset, 1); + + let data = [0x80, 0x7f]; + let (result, offset) = uint32(&data).expect("Failed to parse"); + assert_eq!(result, 16256); + assert_eq!(offset, 2); + + let data = [0xb4, 0x07]; + let (result, offset) = uint32(&data).expect("Failed to parse"); + assert_eq!(result, 0x3b4); + assert_eq!(offset, 2); + + let data = [0x8c, 0x08]; + let (result, offset) = uint32(&data).expect("Failed to parse"); + assert_eq!(result, 0x40c); + assert_eq!(offset, 2); + + let data = [0xff, 0xff, 0xff, 0xff, 0xf]; + let (result, offset) = uint32(&data).expect("Failed to parse"); + assert_eq!(result, 0xffffffff); + assert_eq!(offset, 5); + } + + #[test] + fn test_leb128_int32(){ + let data = [0, 0]; + let (result, offset) = int32(&data).expect("Failed to parse"); + assert_eq!(result, 0); + assert_eq!(offset, 1); + + let data = [1, 0]; + let (result, offset) = int32(&data).expect("Failed to parse"); + assert_eq!(result, 1); + assert_eq!(offset, 1); + + let data = [0x7f, 0]; + let (result, offset) = int32(&data).expect("Failed to parse"); + assert_eq!(result, -1); + assert_eq!(offset, 1); + } +} + // Simple convenience extension trait to wrap get() with .ok_or(SliceReadError) pub(crate) trait ReadBytesExt<'data> { fn get_bytes(self, range: Range) -> Result<&'data [u8], ParseError>; diff --git a/src/relocation.rs b/src/relocation.rs index bbec4a0..218d4e8 100644 --- a/src/relocation.rs +++ b/src/relocation.rs @@ -11,6 +11,7 @@ pub type RelaIterator<'data, E> = ParsingIterator<'data, E, Rela>; /// These C-style definitions are for users who want to implement their own ELF manipulation logic. #[derive(Debug)] #[repr(C)] +#[allow(non_camel_case_types)] pub struct Elf32_Rel { pub r_offset: u32, pub r_info: u32, @@ -21,6 +22,7 @@ pub struct Elf32_Rel { /// These C-style definitions are for users who want to implement their own ELF manipulation logic. #[derive(Debug)] #[repr(C)] +#[allow(non_camel_case_types)] pub struct Elf64_Rel { pub r_offset: u64, pub r_info: u64, @@ -76,6 +78,7 @@ impl ParseAt for Rel { /// These C-style definitions are for users who want to implement their own ELF manipulation logic. #[derive(Debug)] #[repr(C)] +#[allow(non_camel_case_types)] pub struct Elf32_Rela { pub r_offset: u32, pub r_info: u32, @@ -87,6 +90,7 @@ pub struct Elf32_Rela { /// These C-style definitions are for users who want to implement their own ELF manipulation logic. #[derive(Debug)] #[repr(C)] +#[allow(non_camel_case_types)] pub struct Elf64_Rela { pub r_offset: u64, pub r_info: u64, @@ -143,6 +147,475 @@ impl ParseAt for Rela { } } +/// APS2 is what Chrome for Android uses. It stores the same fields as REL/RELA`, but uses variable length ints (LEB128) and run-length encoding. +/// +/// format: https://android.googlesource.com/platform/bionic/+/52a7e7e1bcb7513ddf798eff4c0b713c26861cb5/tools/relocation_packer/src/delta_encoder.h +/// llvm implementation: https://github.com/llvm/llvm-project/blob/ca53611c905f82628ab2e40185307995b552e14d/llvm/lib/Object/ELF.cpp#L450 +pub mod aps2 { + use crate::file::Class; + use crate::parse::{leb128, ParseError}; + use crate::relocation::{Rel, Rela}; + + const MAGIC_PREFIX: [u8; 4] = [b'A', b'P', b'S', b'2']; + + pub type AndroidRelIterator<'data> = ParsingIterator<'data, false>; + pub type AndroidRelaIterator<'data> = ParsingIterator<'data, true>; + + #[derive(Debug, Clone, Copy)] + struct GroupFlag(u8); + impl GroupFlag { + const GROUP_FLAG_BY_INFO: u8 = 0x1; + const GROUP_FLAG_BY_OFFSET_DELTA: u8 = 0x2; + const GROUP_FLAG_BY_ADDEND: u8 = 0x4; + const GROUP_FLAG_HAS_ADDEND: u8 = 0x8; + + pub fn is_relocation_grouped_by_offset_delta(&self) -> bool { + self.0 & Self::GROUP_FLAG_BY_OFFSET_DELTA != 0 + } + pub fn is_relocation_grouped_by_info(&self) -> bool { + self.0 & Self::GROUP_FLAG_BY_INFO != 0 + } + + pub fn is_relocation_grouped_by_addend(&self) -> bool { + self.0 & Self::GROUP_FLAG_BY_ADDEND != 0 + } + + pub fn is_relocation_group_has_addend(&self) -> bool { + self.0 & Self::GROUP_FLAG_HAS_ADDEND != 0 + } + } + + impl From for GroupFlag { + fn from(flag: i64) -> Self { + Self(flag as u8) + } + } + + #[derive(Debug, Clone)] + struct GroupedRelocation { + class: Class, + r_offset: u64, + r_info: u64, + r_addend: i64, + } + + impl From for Rela { + fn from(value: GroupedRelocation) -> Self { + let r_info = value.r_info; + match value.class { + Class::ELF32 => Rela { + r_offset: value.r_offset, + r_sym: (r_info >> 8) as u32, + r_type: (r_info & 0xFF) as u32, + r_addend: value.r_addend, + }, + Class::ELF64 => Rela { + r_offset: value.r_offset, + r_sym: (r_info >> 32) as u32, + r_type: (r_info & 0xFFFFFFFF) as u32, + r_addend: value.r_addend, + }, + } + } + } + + impl From for Rel { + fn from(value: GroupedRelocation) -> Self { + let r_info = value.r_info; + match value.class { + Class::ELF32 => Rel { + r_offset: value.r_offset, + r_sym: (r_info >> 8) as u32, + r_type: (r_info & 0xFF) as u32, + }, + Class::ELF64 => Rel { + r_offset: value.r_offset, + r_sym: (r_info >> 32) as u32, + r_type: (r_info & 0xFFFFFFFF) as u32, + }, + } + } + } + + /// | relocation_count | r_offset | + /// | group | + /// | group | + /// | ... | + #[derive(Debug)] + pub struct ParsingIterator<'data, const RELA: bool> { + class: Class, + + data: &'data [u8], + offset: usize, + + relocation_index: usize, + relocation_count: usize, + + group_relocation_index: usize, + group_relocation_count: usize, + + group_flags: GroupFlag, + group_r_offset_delta: u64, + + relocation: GroupedRelocation, + } + + impl<'data, const RELA: bool> ParsingIterator<'data, RELA> { + pub fn new(class: Class, data: &'data [u8]) -> Result { + if data.len() < 4 { + return Err(ParseError::SliceReadError((0, 4))); + } + if data[..4] != MAGIC_PREFIX { + return Err(ParseError::BadMagic([data[0], data[1], data[2], data[3]])); + } + + let mut iter = Self { + class, + data: &data[4..], + offset: 0, + + relocation_index: 0, + relocation_count: 0, + + group_relocation_index: 0, + group_relocation_count: 0, + group_flags: GroupFlag(0), + group_r_offset_delta: 0, + + relocation: GroupedRelocation { + class, + r_offset: 0, + r_info: 0, + r_addend: 0, + }, + }; + iter.init()?; + + Ok(iter) + } + + fn init(&mut self) -> Result<(), ParseError> { + self.relocation_count = self.read_signed_leb128()? as usize; + + // initial r_offset + self.relocation.r_offset = self.read_signed_leb128()? as u64; + Ok(()) + } + + fn read_signed_leb128(&mut self) -> Result { + match self.class { + Class::ELF32 => { + let (data, offset) = leb128::int32(&self.data[self.offset..])?; + self.offset += offset; + Ok(data as i64) + } + Class::ELF64 => { + let (data, offset) = leb128::int64(&self.data[self.offset..])?; + self.offset += offset; + Ok(data) + } + } + } + + fn read_group_fields(&mut self) -> Result<(), ParseError> { + self.group_relocation_count = self.read_signed_leb128()? as usize; + self.group_flags = self.read_signed_leb128()?.into(); + + if self.group_flags.is_relocation_grouped_by_offset_delta() { + self.group_r_offset_delta = self.read_signed_leb128()? as u64; + } + + if self.group_flags.is_relocation_grouped_by_info() { + self.relocation.r_info = self.read_signed_leb128()? as u64; + } + + if self.group_flags.is_relocation_grouped_by_addend() + && self.group_flags.is_relocation_group_has_addend() + { + if !RELA { + return Err(ParseError::UnexpectedRelocationAddend); + } + self.relocation.r_addend = self + .relocation + .r_addend + .wrapping_add(self.read_signed_leb128()?); + } else if !self.group_flags.is_relocation_group_has_addend() && RELA { + self.relocation.r_addend = 0; + } + + self.group_relocation_index = 0; + Ok(()) + } + + fn read_group_relocation(&mut self) -> Result<(), ParseError> { + if self.group_flags.is_relocation_grouped_by_offset_delta() { + self.relocation.r_offset = self + .relocation + .r_offset + .wrapping_add(self.group_r_offset_delta); + } else { + // may overflow + self.relocation.r_offset = self + .relocation + .r_offset + .wrapping_add(self.read_signed_leb128()? as u64); + }; + + if !self.group_flags.is_relocation_grouped_by_info() { + self.relocation.r_info = self.read_signed_leb128()? as u64; + } + + if self.group_flags.is_relocation_group_has_addend() + && !self.group_flags.is_relocation_grouped_by_addend() + { + if !RELA { + return Err(ParseError::UnexpectedRelocationAddend); + } + self.relocation.r_addend = self + .relocation + .r_addend + .wrapping_add(self.read_signed_leb128()?); + } + + self.relocation_index += 1; + self.group_relocation_index += 1; + + Ok(()) + } + } + + impl<'data> Iterator for ParsingIterator<'data, true> { + type Item = Result; + + fn next(&mut self) -> Option { + if self.relocation_index >= self.relocation_count { + return None; + } + + if self.group_relocation_index >= self.group_relocation_count { + match self.read_group_fields() { + Ok(_) => {} + Err(e) => return Some(Err(e)), + } + } + + match self.read_group_relocation() { + Ok(_) => Some(Ok(self.relocation.clone().into())), + Err(e) => Some(Err(e)), + } + } + } + + impl<'data> Iterator for ParsingIterator<'data, false> { + type Item = Result; + + fn next(&mut self) -> Option { + if self.relocation_index >= self.relocation_count { + return None; + } + + if self.group_relocation_index >= self.group_relocation_count { + match self.read_group_fields() { + Ok(_) => {} + Err(e) => return Some(Err(e)), + } + } + + match self.read_group_relocation() { + Ok(_) => Some(Ok(self.relocation.clone().into())), + Err(e) => Some(Err(e)), + } + } + } +} + +/// RELR is what Chrome OS uses, and is supported in Android P+ (tracking bug for enabling). +/// It encodes only relative relocations and uses a bitmask to do so (which works well since all symbols that require relocations live in .data.rel.ro). +/// format: https://maskray.me/blog/2021-10-31-relative-relocations-and-relr +/// llvm implementation: https://github.com/llvm/llvm-project/blob/3ef64f7ab5b8651eab500cd944984379fce5f639/llvm/lib/Object/ELF.cpp#L334 +pub mod relr { + use crate::abi; + use crate::endian::EndianParse; + use crate::file::Class; + use crate::parse::ParseError; + use crate::relocation::Rel; + + #[cfg(feature = "std")] + pub fn decode_relocations(machine: u16, class: Class, endian: E, data: &[u8]) -> Vec + where E: EndianParse + { + let typ = get_relocation_type(machine); + let entry_sz = match class{ + Class::ELF32 => 4, + Class::ELF64 => 8, + }; + + let mut relocations = Vec::new(); + + let mut offset = 0; + let mut base = 0; + + while offset < data.len(){ + let entry = match class{ + Class::ELF32 => endian.parse_u32_at(&mut offset, data).unwrap() as u64, + Class::ELF64 => endian.parse_u64_at(&mut offset, data).unwrap(), + }; + + if entry & 1 == 0{ + relocations.push(Rel{ + r_offset: entry, + r_sym: 0, + r_type: typ, + }); + base = entry + entry_sz; + } else { + let mut offset = base; + let mut entry = entry; + entry >>= 1; + while entry != 0{ + if entry & 1 != 0{ + relocations.push(Rel{ + r_offset: offset, + r_sym: 0, + r_type: typ, + }); + } + offset += entry_sz; + entry >>= 1; + } + base += (8 * entry_sz - 1) * entry_sz; + } + } + relocations + } + + #[derive(Debug)] + pub struct RelativeRelocationIterator<'data, E: EndianParse> { + class: Class, + endian: E, + + data: &'data [u8], + offset: usize, + + typ: u32, + state: IterState, + } + + #[derive(Debug, Clone, Copy)] + struct IterState { + bitmap: bool, + base: u64, + entry: u64, + offset: u64, + } + + fn get_relocation_type(machine: u16) -> u32 { + match machine { + abi::EM_X86_64 => abi::R_X86_64_RELATIVE, + abi::EM_386 | abi::EM_IAMCU => abi::R_386_RELATIVE, + abi::EM_AARCH64 => abi::R_AARCH64_RELATIVE, + abi::EM_ARM => abi::R_ARM_RELATIVE, + abi::EM_ARC_COMPACT | abi::EM_ARC_COMPACT2 => abi::R_ARC_RELATIVE, + abi::EM_QDSP6 => abi::R_HEX_RELATIVE, + abi::EM_PPC64 => abi::R_PPC64_RELATIVE, + abi::EM_RISCV => abi::R_RISCV_RELATIVE, + abi::EM_S390 => abi::R_390_RELATIVE, + abi::EM_SPARC | abi::EM_SPARC32PLUS | abi::EM_SPARCV9 => abi::R_SPARC_RELATIVE, + abi::EM_CSKY => abi::R_CKCORE_RELATIVE, + abi::EM_VE => abi::R_VE_RELATIVE, + abi::EM_LOONGARCH => abi::R_LARCH_RELATIVE, + _ => 0, + } + } + + impl<'data, E: EndianParse> RelativeRelocationIterator<'data, E> { + const BYTE_BITS: u64 = 8; + + pub fn new(machine: u16, class: Class, endian: E, data: &'data [u8]) -> Self { + Self { + class, + endian, + + data, + offset: 0, + + typ: get_relocation_type(machine), + state: IterState { + bitmap: false, + base: 0, + entry: 0, + offset: 0, + }, + } + } + + fn read_entry(&mut self) -> Result { + match self.class { + Class::ELF32 => self + .endian + .parse_u32_at(&mut self.offset, self.data) + .map(|v| v as u64), + Class::ELF64 => self.endian.parse_u64_at(&mut self.offset, self.data), + } + } + + #[inline(always)] + fn entry_size(&self) -> u64 { + match self.class { + Class::ELF32 => 4, + Class::ELF64 => 8, + } + } + + fn read_r_offset(&mut self) -> Result { + if !self.state.bitmap{ + let entry = self.read_entry()?; + if entry & 1 == 0 { + self.state.base = entry + self.entry_size(); + return Ok(entry); + } + + self.state.bitmap = true; + self.state.entry = entry; + self.state.offset = self.state.base; + } + + self.state.entry >>= 1; + if self.state.entry == 0 { + self.state.base += (Self::BYTE_BITS * self.entry_size() - 1) * self.entry_size(); + self.state.bitmap = false; + return self.read_r_offset(); + } + + let offset = self.state.offset; + self.state.offset += self.entry_size(); + + if self.state.entry & 1 != 0 { + return Ok(offset); + } + self.read_r_offset() + } + } + + impl<'data, E: EndianParse> Iterator for RelativeRelocationIterator<'data, E> { + type Item = Rel; + + fn next(&mut self) -> Option { + if !self.state.bitmap && self.offset >= self.data.len(){ + return None; + } + + match self.read_r_offset() { + Ok(rel) => Some(Rel{ + r_offset: rel, + r_sym: 0, + r_type: self.typ, + }), + Err(_) => None, + } + } + } +} #[cfg(test)] mod parse_tests { use super::*;