Skip to content

Commit

Permalink
write/object: derive addend size from relocation flags
Browse files Browse the repository at this point in the history
This is preparation for removing the size field when the user
specifies the flags, and also lets us do the flag calculation
earlier.
  • Loading branch information
philipc committed Dec 5, 2023
1 parent 124c401 commit 7e4bb81
Show file tree
Hide file tree
Showing 5 changed files with 142 additions and 7 deletions.
57 changes: 57 additions & 0 deletions src/write/coff/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,63 @@ impl<'a> Object<'a> {
constant
}

pub(crate) fn coff_relocation_size(&self, reloc: &crate::write::RawRelocation) -> Result<u8> {
let typ = if let RelocationFlags::Coff { typ } = reloc.flags {
typ
} else {
return Err(Error(format!("unexpected relocation for size {:?}", reloc)));
};
let size = match self.architecture {
Architecture::I386 => match typ {
coff::IMAGE_REL_I386_DIR16
| coff::IMAGE_REL_I386_REL16
| coff::IMAGE_REL_I386_SECTION => Some(16),
coff::IMAGE_REL_I386_DIR32
| coff::IMAGE_REL_I386_DIR32NB
| coff::IMAGE_REL_I386_SECREL
| coff::IMAGE_REL_I386_TOKEN
| coff::IMAGE_REL_I386_REL32 => Some(32),
_ => None,
},
Architecture::X86_64 => match typ {
coff::IMAGE_REL_AMD64_SECTION => Some(16),
coff::IMAGE_REL_AMD64_ADDR32
| coff::IMAGE_REL_AMD64_ADDR32NB
| coff::IMAGE_REL_AMD64_REL32
| coff::IMAGE_REL_AMD64_REL32_1
| coff::IMAGE_REL_AMD64_REL32_2
| coff::IMAGE_REL_AMD64_REL32_3
| coff::IMAGE_REL_AMD64_REL32_4
| coff::IMAGE_REL_AMD64_REL32_5
| coff::IMAGE_REL_AMD64_SECREL
| coff::IMAGE_REL_AMD64_TOKEN => Some(32),
coff::IMAGE_REL_AMD64_ADDR64 => Some(64),
_ => None,
},
Architecture::Arm => match typ {
coff::IMAGE_REL_ARM_SECTION => Some(16),
coff::IMAGE_REL_ARM_ADDR32
| coff::IMAGE_REL_ARM_ADDR32NB
| coff::IMAGE_REL_ARM_TOKEN
| coff::IMAGE_REL_ARM_REL32
| coff::IMAGE_REL_ARM_SECREL => Some(32),
_ => None,
},
Architecture::Aarch64 => match typ {
coff::IMAGE_REL_ARM64_SECTION => Some(16),
coff::IMAGE_REL_ARM64_ADDR32
| coff::IMAGE_REL_ARM64_ADDR32NB
| coff::IMAGE_REL_ARM64_SECREL
| coff::IMAGE_REL_ARM64_TOKEN
| coff::IMAGE_REL_ARM64_REL32 => Some(32),
coff::IMAGE_REL_ARM64_ADDR64 => Some(64),
_ => None,
},
_ => None,
};
size.ok_or_else(|| Error(format!("unsupported relocation for size {:?}", reloc)))
}

fn coff_add_stub_symbol(&mut self, symbol_id: SymbolId) -> Result<SymbolId> {
if let Some(stub_id) = self.stub_symbols.get(&symbol_id) {
return Ok(*stub_id);
Expand Down
50 changes: 50 additions & 0 deletions src/write/elf/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,56 @@ impl<'a> Object<'a> {
}
}

pub(crate) fn elf_relocation_size(&self, reloc: &crate::write::RawRelocation) -> Result<u8> {
let r_type = if let RelocationFlags::Elf { r_type } = reloc.flags {
r_type
} else {
return Err(Error("invalid relocation flags".into()));
};
// This only needs to support architectures that use implicit addends.
let size = match self.architecture {
Architecture::Arm => match r_type {
elf::R_ARM_ABS16 => Some(16),
elf::R_ARM_ABS32 | elf::R_ARM_REL32 => Some(32),
_ => None,
},
Architecture::Bpf => match r_type {
elf::R_BPF_64_32 => Some(32),
elf::R_BPF_64_64 => Some(64),
_ => None,
},
Architecture::I386 => match r_type {
elf::R_386_8 | elf::R_386_PC8 => Some(8),
elf::R_386_16 | elf::R_386_PC16 => Some(16),
elf::R_386_32
| elf::R_386_PC32
| elf::R_386_GOT32
| elf::R_386_PLT32
| elf::R_386_GOTOFF
| elf::R_386_GOTPC => Some(32),
_ => None,
},
Architecture::Mips => match r_type {
elf::R_MIPS_16 => Some(16),
elf::R_MIPS_32 => Some(32),
elf::R_MIPS_64 => Some(64),
_ => None,
},
Architecture::Sbf => match r_type {
elf::R_SBF_64_32 => Some(32),
elf::R_SBF_64_64 => Some(64),
_ => None,
},
_ => {
return Err(Error(format!(
"unimplemented architecture {:?}",
self.architecture
)));
}
};
size.ok_or_else(|| Error(format!("unsupported relocation for size {:?}", reloc)))
}

pub(crate) fn elf_is_64(&self) -> bool {
match self.architecture.address_size().unwrap() {
AddressSize::U8 | AddressSize::U16 | AddressSize::U32 => false,
Expand Down
8 changes: 8 additions & 0 deletions src/write/macho.rs
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,14 @@ impl<'a> Object<'a> {
constant
}

pub(crate) fn macho_relocation_size(&self, reloc: &crate::write::RawRelocation) -> Result<u8> {
if let RelocationFlags::MachO { r_length, .. } = reloc.flags {
Ok(8 << r_length)
} else {
return Err(Error("invalid relocation flags".into()));
}
}

pub(crate) fn macho_write(&self, buffer: &mut dyn WritableBuffer) -> Result<()> {
let address_size = self.architecture.address_size().unwrap();
let endian = self.endian;
Expand Down
25 changes: 18 additions & 7 deletions src/write/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -541,9 +541,6 @@ impl<'a> Object<'a> {
BinaryFormat::Xcoff => self.xcoff_adjust_addend(&mut relocation),
_ => unimplemented!(),
};
if addend != 0 {
self.write_relocation_addend(section, &relocation, addend)?;
}
let flags = match self.format {
#[cfg(feature = "coff")]
BinaryFormat::Coff => self.coff_relocation_flags(&relocation)?,
Expand All @@ -561,19 +558,33 @@ impl<'a> Object<'a> {
addend: relocation.addend,
flags,
};
if addend != 0 {
self.write_relocation_addend(section, &relocation, addend)?;
}
self.sections[section.0].relocations.push(relocation);
Ok(())
}

fn write_relocation_addend(
&mut self,
section: SectionId,
relocation: &Relocation,
relocation: &RawRelocation,
addend: i64,
) -> Result<()> {
let size = match self.format {
#[cfg(feature = "coff")]
BinaryFormat::Coff => self.coff_relocation_size(relocation)?,
#[cfg(feature = "elf")]
BinaryFormat::Elf => self.elf_relocation_size(relocation)?,
#[cfg(feature = "macho")]
BinaryFormat::MachO => self.macho_relocation_size(relocation)?,
#[cfg(feature = "xcoff")]
BinaryFormat::Xcoff => self.xcoff_relocation_size(relocation)?,
_ => unimplemented!(),
};
let data = self.sections[section.0].data_mut();
let offset = relocation.offset as usize;
match relocation.size {
match size {
32 => data.write_at(offset, &U32::new(self.endian, addend as u32)),
64 => data.write_at(offset, &U64::new(self.endian, addend as u64)),
_ => {
Expand All @@ -587,7 +598,7 @@ impl<'a> Object<'a> {
Error(format!(
"invalid relocation offset {}+{} (max {})",
relocation.offset,
relocation.size,
size,
data.len()
))
})
Expand Down Expand Up @@ -916,7 +927,7 @@ pub struct Relocation {

/// Temporary relocation structure introduced during refactor.
#[derive(Debug)]
struct RawRelocation {
pub(crate) struct RawRelocation {
/// The section offset of the place of the relocation.
pub offset: u64,
/// The symbol referred to by the relocation.
Expand Down
9 changes: 9 additions & 0 deletions src/write/xcoff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,15 @@ impl<'a> Object<'a> {
constant
}

pub(crate) fn xcoff_relocation_size(&self, reloc: &crate::write::RawRelocation) -> Result<u8> {
let r_rsize = if let RelocationFlags::Xcoff { r_rsize, .. } = reloc.flags {
r_rsize
} else {
return Err(Error(format!("unexpected relocation {:?}", reloc)));
};
Ok(r_rsize + 1)
}

pub(crate) fn xcoff_write(&self, buffer: &mut dyn WritableBuffer) -> Result<()> {
let is_64 = match self.architecture.address_size().unwrap() {
AddressSize::U8 | AddressSize::U16 | AddressSize::U32 => false,
Expand Down

0 comments on commit 7e4bb81

Please sign in to comment.