Skip to content

Commit

Permalink
Handle COPY relocations.
Browse files Browse the repository at this point in the history
We now have a `.copyrel` section where at runtime the loader will copy
dynamic "objects" into.
  • Loading branch information
rbartlensky committed Jul 27, 2022
1 parent eacff37 commit d235911
Show file tree
Hide file tree
Showing 6 changed files with 190 additions and 72 deletions.
76 changes: 52 additions & 24 deletions src/elf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ use std::{
sync::Arc,
};

mod copy_rel;
pub use copy_rel::CopyRel;

mod options;
pub use options::*;

Expand Down Expand Up @@ -162,6 +165,8 @@ pub struct Writer<'d> {
plt_rel_section: SectionPtr<'d>,
hash_section: Option<SectionPtr<'d>>,
gnu_hash_section: Option<SectionPtr<'d>>,
// holds the space required by COPY relocations
copy_rel_section: SectionPtr<'d>,
}

impl<'d> Writer<'d> {
Expand Down Expand Up @@ -299,6 +304,11 @@ impl<'d> Writer<'d> {
);
}

let copy_rel_section =
Section::builder(section_with_name(section_names.get_or_create(".copyrel").offset))
.synthetic(CopyRel::default())
.build();

let null_section = Section::new(Default::default());
let shstr_section = Section::builder(Default::default()).synthetic(section_names).build();
let got_section = Section::new(got_sh);
Expand All @@ -325,6 +335,7 @@ impl<'d> Writer<'d> {
Arc::clone(&dynamic_section),
Arc::clone(&dyn_rel_section),
Arc::clone(&plt_rel_section),
Arc::clone(&copy_rel_section),
];

if let Some(s) = hash_section.clone() {
Expand Down Expand Up @@ -361,6 +372,7 @@ impl<'d> Writer<'d> {
plt_rel_section,
hash_section,
gnu_hash_section,
copy_rel_section,
})
}

Expand Down Expand Up @@ -467,7 +479,6 @@ impl<'d> Writer<'d> {
reference: &'d Path,
) -> Result<Option<SymbolRef>, ErrorType> {
elf_sym.st_shndx = SHN_UNDEF as u16;
elf_sym.st_size = 0;
elf_sym.st_value = 0;
let r = Self::add_symbol_inner(
elf_sym,
Expand Down Expand Up @@ -693,6 +704,11 @@ impl<'d> Writer<'d> {
}
}
}

// handle dynamic object symbols
let copy_rel = self.copy_rel_section.read();
as_mut_sym_tab(&self.dyn_sym_section)
.update_object_symbols(copy_rel.sh_addr, copy_rel.index());
}

fn compute_tables(&mut self) {
Expand All @@ -711,6 +727,7 @@ impl<'d> Writer<'d> {
// populate .rela.{dyn,plt} sections
let got_addr = self.got_address();
let got_plt_addr = self.got_plt_address();
let copy_rel_addr = self.copy_rel_section.read().sh_addr;
for (section, rels) in [
(&self.dyn_rel_section, &mut self.dyn_rels),
(&self.plt_rel_section, &mut self.plt_rels),
Expand All @@ -721,6 +738,7 @@ impl<'d> Writer<'d> {
match r_type(rel.r_info) {
R_X86_64_GLOB_DAT => rel.r_offset += got_addr,
R_X86_64_JUMP_SLOT => rel.r_offset += got_plt_addr,
R_X86_64_COPY => rel.r_offset += copy_rel_addr,
_ => {}
}
rel.serialize(&mut data);
Expand Down Expand Up @@ -811,47 +829,54 @@ impl<'d> Writer<'d> {
}

{
// mappings from dyn_sym to their index in the dyn sym section
let dyn_sym_indices = sorted
.iter()
.enumerate()
.map(|(i, dyn_sym)| (dyn_sym.st_name, i))
.collect::<HashMap<_, _>>();
drop(dyn_syms);

let mut symbols = as_mut_sym_tab(&self.sym_section);
let mut dyn_syms = as_mut_sym_tab(&self.dyn_sym_section);
let section_names = as_str_tab(&self.shstr_section);

for (sym, dyn_sym) in
symbols.named_mut().values_mut().filter_map(|s| s.dynamic().map(|d| (s, d)))
{
let mut calculated_index = None;
let mut index = || {
if let Some(index) = calculated_index {
index
} else {
let index = sorted
.iter()
.enumerate()
.find(|(_, s)| s.st_name == dyn_sym.st_name())
.map(|(i, _)| i)
.unwrap();
calculated_index = Some(index);
index
}
};
let index = dyn_sym_indices[&dyn_sym.st_name()];
if let Some(offset) = sym.got_offset() {
self.dyn_rels.push(rela(offset, index(), R_X86_64_GLOB_DAT));
self.dyn_rels.push(rela(offset, index, R_X86_64_GLOB_DAT));
}
let dyn_symbol = &mut dyn_syms.get_mut(dyn_sym).unwrap();
if dyn_symbol.is_object() {
let size = dyn_symbol.st_size;
let offset = self
.copy_rel_section
.write()
.inner_mut::<CopyRel>()
.unwrap()
.insert(dyn_sym, size);
self.dyn_rels.push(rela(offset as usize, index, R_X86_64_COPY));
dyn_symbol.st_value = offset;
}
if let Some(offset) = sym.got_plt_offset() {
sym.set_relocation_offset(self.plt_rels.len());
self.plt_rels.push(rela(offset, index(), R_X86_64_JUMP_SLOT));
self.plt_rels.push(rela(offset, index, R_X86_64_JUMP_SLOT));
}
}
self.dyn_rel_section.write().sh_size = (self.dyn_rels.len() * size_of::<Rela>()) as u64;

self.plt_rel_section.write().sh_size = (self.plt_rels.len() * size_of::<Rela>()) as u64;

drop(symbols);
drop(dyn_syms);
// calculate how many dyntags we have, so that we know how big our dynamic section is
// + 1 for the null entry
let extra_dyn_entries =
self.sections.iter().map(|sh| dyn_entries(&section_names, sh)).sum::<usize>() + 1;
self.dynamic_section.write().sh_size =
((self.dyn_entries.len() + extra_dyn_entries) * 2 * size_of::<u64>()) as u64;
}
drop(dyn_syms);

self.sort_sections();

Expand All @@ -865,19 +890,22 @@ impl<'d> Writer<'d> {
fn handle_relocations(&mut self) {
let got_address = self.got_address();
let plt_address = self.plt_address();
let sh_name = self.sym_section.read().sh_name;
let sh_names = [self.sym_section.read().sh_name, self.dyn_sym_section.read().sh_name];

// first patch symbol values
for section in &mut self.sections.iter_mut().skip(1).filter(|s| s.read().sh_name != sh_name)
for section in
&mut self.sections.iter_mut().skip(1).filter(|s| !sh_names.contains(&s.read().sh_name))
{
let mut symbols = as_mut_sym_tab(&self.sym_section);
section.write().patch_symbol_values(&mut symbols);
}
let symbols = as_sym_tab(&self.sym_section);
for section in &mut self.sections.iter_mut().skip(1).filter(|s| s.read().sh_name != sh_name)
let dyn_symbols = as_sym_tab(&self.dyn_sym_section);
for section in
&mut self.sections.iter_mut().skip(1).filter(|s| !sh_names.contains(&s.read().sh_name))
{
for chunk in section.write().chunks_mut() {
chunk.apply_relocations(got_address, plt_address, &symbols);
chunk.apply_relocations(got_address, plt_address, &symbols, &dyn_symbols);
}
}
}
Expand Down
20 changes: 18 additions & 2 deletions src/elf/chunk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,20 @@ impl Chunk {
&mut self.relocations
}

pub fn apply_relocations(&mut self, got_address: u64, plt_address: u64, table: &SymbolTable) {
pub fn apply_relocations(
&mut self,
got_address: u64,
plt_address: u64,
table: &SymbolTable,
dyn_table: &SymbolTable,
) {
for (rela, symbol_ref) in &self.relocations {
apply_relocation(
*self.address.as_ref().unwrap(),
&mut self.data[..],
*rela,
table.get(*symbol_ref).unwrap(),
dyn_table,
got_address,
plt_address,
);
Expand Down Expand Up @@ -87,10 +94,19 @@ fn apply_relocation(
data: &mut [u8],
rel: Rela,
symbol: &crate::elf::Symbol<'_>,
dyn_table: &SymbolTable,
got_address: u64,
plt_address: u64,
) {
let s: i64 = symbol.st_value.try_into().unwrap();
// e.g. `stderr` will be undefined, but we might reference it. Since
// `stderr` is an object, we will know its address at runtime because
// we will issue a COPY relocation to make a copy of `stderr` for our binary.
let s: i64 = match symbol.dynamic() {
Some(dyn_sym) if symbol.is_undefined() => {
dyn_table.get(dyn_sym).unwrap().st_value.try_into().unwrap()
}
_ => symbol.st_value.try_into().unwrap(),
};
let a = rel.r_addend;
let p: i64 = (chunk_address + rel.r_offset).try_into().unwrap();
let _z = symbol.st_size;
Expand Down
56 changes: 56 additions & 0 deletions src/elf/copy_rel.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
use std::collections::HashMap;

use goblin::elf64::section_header::{SectionHeader, SHF_ALLOC, SHF_WRITE, SHT_NOBITS};

use super::{
section::{Synthesized, SynthesizedKind},
Section, SymbolRef,
};

#[derive(Default)]
pub struct CopyRel {
// the total size of the section
size: u64,
// a mappings from a symbol to a "slot" in the section
slots: HashMap<SymbolRef, u64>,
}

impl CopyRel {
pub fn insert(&mut self, sym: SymbolRef, size: u64) -> u64 {
let offset = self.size;
self.slots.insert(sym, offset);
self.size += size;
offset
}
}

impl<'p> Synthesized<'p> for CopyRel {
fn fill_header(&self, sh: &mut SectionHeader) {
*sh = SectionHeader {
sh_type: SHT_NOBITS,
sh_flags: (SHF_ALLOC | SHF_WRITE) as u64,
sh_addralign: 64,
..*sh
}
}

fn expand(&self, sh: &mut Section) {
sh.sh_size = self.size as u64;
}

fn as_ref<'k>(kind: &'k SynthesizedKind<'p>) -> Option<&'k Self> {
if let SynthesizedKind::CopyRel(s) = kind {
Some(s)
} else {
None
}
}

fn as_ref_mut<'k>(kind: &'k mut SynthesizedKind<'p>) -> Option<&'k mut Self> {
if let SynthesizedKind::CopyRel(s) = kind {
Some(s)
} else {
None
}
}
}
56 changes: 26 additions & 30 deletions src/elf/section.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use goblin::elf64::section_header::{SectionHeader, SHT_NOBITS};
use parking_lot::RwLock;
use std::sync::Arc;

use super::SymbolTable;
use super::{CopyRel, SymbolTable};

pub type SectionPtr<'p> = Arc<RwLock<Section<'p>>>;

Expand Down Expand Up @@ -65,34 +65,27 @@ impl<'p> SectionBuilder<'p> {
pub enum SynthesizedKind<'p> {
Plt(Plt),
StringTable(StringTable),
Hash(HashTable),
GnuHash(GnuHashTable),
HashTable(HashTable),
GnuHashTable(GnuHashTable),
SymbolTable(SymbolTable<'p>),
CopyRel(CopyRel),
}

impl From<Plt> for SynthesizedKind<'_> {
fn from(p: Plt) -> Self {
Self::Plt(p)
}
}

impl From<StringTable> for SynthesizedKind<'_> {
fn from(p: StringTable) -> Self {
Self::StringTable(p)
}
}

impl From<HashTable> for SynthesizedKind<'_> {
fn from(p: HashTable) -> Self {
Self::Hash(p)
}
macro_rules! impl_from {
($kind: tt) => {
impl From<$kind> for SynthesizedKind<'_> {
fn from(p: $kind) -> Self {
Self::$kind(p)
}
}
};
}

impl From<GnuHashTable> for SynthesizedKind<'_> {
fn from(p: GnuHashTable) -> Self {
Self::GnuHash(p)
}
}
impl_from!(Plt);
impl_from!(StringTable);
impl_from!(HashTable);
impl_from!(GnuHashTable);
impl_from!(CopyRel);

impl<'p> From<SymbolTable<'p>> for SynthesizedKind<'p> {
fn from(p: SymbolTable<'p>) -> Self {
Expand All @@ -107,9 +100,10 @@ impl<'p> Synthesized<'p> for SynthesizedKind<'p> {
match self {
Plt(p) => p.fill_header(sh),
StringTable(s) => s.fill_header(sh),
Hash(h) => h.fill_header(sh),
GnuHash(h) => h.fill_header(sh),
HashTable(h) => h.fill_header(sh),
GnuHashTable(h) => h.fill_header(sh),
SymbolTable(s) => s.fill_header(sh),
CopyRel(s) => s.fill_header(sh),
}
}

Expand All @@ -119,9 +113,10 @@ impl<'p> Synthesized<'p> for SynthesizedKind<'p> {
match self {
Plt(p) => p.expand(sh),
StringTable(s) => s.expand(sh),
Hash(h) => h.expand(sh),
GnuHash(h) => h.expand(sh),
HashTable(h) => h.expand(sh),
GnuHashTable(h) => h.expand(sh),
SymbolTable(s) => s.expand(sh),
CopyRel(s) => s.expand(sh),
}
}

Expand All @@ -131,9 +126,10 @@ impl<'p> Synthesized<'p> for SynthesizedKind<'p> {
match self {
Plt(p) => p.finalize(sh),
StringTable(s) => s.finalize(sh),
Hash(h) => h.finalize(sh),
GnuHash(h) => h.finalize(sh),
HashTable(h) => h.finalize(sh),
GnuHashTable(h) => h.finalize(sh),
SymbolTable(s) => s.finalize(sh),
CopyRel(s) => s.expand(sh),
}
}

Expand Down
Loading

0 comments on commit d235911

Please sign in to comment.