Skip to content

Commit

Permalink
Merge pull request #650 from philipc/elf-fixes
Browse files Browse the repository at this point in the history
build/elf: various fixes
  • Loading branch information
philipc authored Mar 25, 2024
2 parents 9f11ebd + f6b7fb4 commit 981b874
Show file tree
Hide file tree
Showing 6 changed files with 251 additions and 42 deletions.
20 changes: 17 additions & 3 deletions crates/rewrite/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use std::path::{Path, PathBuf};

use anyhow::{anyhow, Context, Result};
use clap::{command, Arg, ArgAction, ArgGroup};
use log::info;
use object_rewrite as rewrite;

fn main() -> Result<()> {
Expand Down Expand Up @@ -123,6 +124,10 @@ fn main() -> Result<()> {
.value_name("path")
.value_parser(clap::value_parser!(String))
.help("Set the interpreter path in the PT_INTERP segment"),
Arg::new("ignore-unknown-format")
.long("ignore-unknown-format")
.action(ArgAction::SetTrue)
.help("Ignore input files with unknown formats"),
Arg::new("verbose")
.short('v')
.long("verbose")
Expand Down Expand Up @@ -171,9 +176,18 @@ fn main() -> Result<()> {
let in_data = unsafe { memmap2::Mmap::map(&in_file) }
.with_context(|| format!("Failed to map input file '{}'", in_path.display()))?;
let in_data = &*in_data;
match object::FileKind::parse(in_data) {
Ok(object::FileKind::Elf32) | Ok(object::FileKind::Elf64) => {}
_ => return Ok(()),

if matches.get_flag("ignore-unknown-format") {
match object::FileKind::parse(in_data) {
Ok(object::FileKind::Elf32) | Ok(object::FileKind::Elf64) => {}
_ => {
info!(
"Ignoring input file '{}' with unknown format",
in_path.display()
);
return Ok(());
}
}
}
let mut rewriter = rewrite::Rewriter::read(in_data)
.with_context(|| format!("Failed to parse input file '{}'", in_path.display()))?;
Expand Down
5 changes: 5 additions & 0 deletions src/build/bytes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ impl<'a> Bytes<'a> {
pub fn to_mut(&mut self) -> &mut Vec<u8> {
self.0.to_mut()
}

/// Get the bytes as a slice.
pub fn as_slice(&self) -> &[u8] {
self.0.as_ref()
}
}

impl<'a> core::ops::Deref for Bytes<'a> {
Expand Down
94 changes: 68 additions & 26 deletions src/build/elf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ impl<'data> Builder<'data> {
index,
endian,
is_mips64el,
section,
rels,
link,
&symbols,
Expand All @@ -189,6 +190,7 @@ impl<'data> Builder<'data> {
index,
endian,
is_mips64el,
section,
rels,
link,
&symbols,
Expand All @@ -207,9 +209,10 @@ impl<'data> Builder<'data> {
}
let data = match section.sh_type(endian) {
elf::SHT_NOBITS => SectionData::UninitializedData(section.sh_size(endian).into()),
elf::SHT_PROGBITS | elf::SHT_INIT_ARRAY | elf::SHT_FINI_ARRAY => {
SectionData::Data(section.data(endian, data)?.into())
}
elf::SHT_PROGBITS
| elf::SHT_INIT_ARRAY
| elf::SHT_FINI_ARRAY
| elf::SHT_PREINIT_ARRAY => SectionData::Data(section.data(endian, data)?.into()),
elf::SHT_REL | elf::SHT_RELA => relocations,
elf::SHT_SYMTAB => {
if index == symbols.section().0 {
Expand Down Expand Up @@ -272,7 +275,9 @@ impl<'data> Builder<'data> {
elf::SHT_GNU_VERNEED => SectionData::GnuVerneed,
other => match (builder.header.e_machine, other) {
(elf::EM_ARM, elf::SHT_ARM_ATTRIBUTES)
| (elf::EM_AARCH64, elf::SHT_AARCH64_ATTRIBUTES) => {
| (elf::EM_AARCH64, elf::SHT_AARCH64_ATTRIBUTES)
| (elf::EM_CSKY, elf::SHT_CSKY_ATTRIBUTES)
| (elf::EM_RISCV, elf::SHT_RISCV_ATTRIBUTES) => {
let attributes = section.attributes(endian, data)?;
Self::read_attributes(index, attributes, sections.len(), symbols.len())?
}
Expand All @@ -282,7 +287,8 @@ impl<'data> Builder<'data> {
(elf::EM_ARM, elf::SHT_ARM_EXIDX)
| (elf::EM_IA_64, elf::SHT_IA_64_UNWIND)
| (elf::EM_MIPS, elf::SHT_MIPS_REGINFO)
| (elf::EM_MIPS, elf::SHT_MIPS_DWARF) => {
| (elf::EM_MIPS, elf::SHT_MIPS_DWARF)
| (elf::EM_X86_64, elf::SHT_X86_64_UNWIND) => {
SectionData::Data(section.data(endian, data)?.into())
}
_ => return Err(Error(format!("Unsupported section type {:x}", other))),
Expand Down Expand Up @@ -362,6 +368,7 @@ impl<'data> Builder<'data> {
index: usize,
endian: Elf::Endian,
is_mips64el: bool,
section: &'data Elf::SectionHeader,
rels: &'data [Rel],
link: read::SectionIndex,
symbols: &read::elf::SymbolTable<'data, Elf, R>,
Expand All @@ -372,7 +379,27 @@ impl<'data> Builder<'data> {
Rel: Copy + Into<Elf::Rela>,
R: ReadRef<'data>,
{
if link.0 == 0 {
if link == dynamic_symbols.section() {
Self::read_relocations_impl::<Elf, Rel, true>(
index,
endian,
is_mips64el,
rels,
dynamic_symbols.len(),
)
.map(SectionData::DynamicRelocation)
} else if link.0 == 0 || section.sh_flags(endian).into() & u64::from(elf::SHF_ALLOC) != 0 {
// If there's no link, then none of the relocations may reference symbols.
// Assume that these are dynamic relocations, but don't use the dynamic
// symbol table when parsing.
//
// Additionally, sometimes there is an allocated section that links to
// the static symbol table. We don't currently support this case in general,
// but if none of the relocation entries reference a symbol then it is
// safe to treat it as a dynamic relocation section.
//
// For both of these cases, if there is a reference to a symbol then
// an error will be returned when parsing the relocations.
Self::read_relocations_impl::<Elf, Rel, true>(index, endian, is_mips64el, rels, 0)
.map(SectionData::DynamicRelocation)
} else if link == symbols.section() {
Expand All @@ -384,15 +411,6 @@ impl<'data> Builder<'data> {
symbols.len(),
)
.map(SectionData::Relocation)
} else if link == dynamic_symbols.section() {
Self::read_relocations_impl::<Elf, Rel, true>(
index,
endian,
is_mips64el,
rels,
dynamic_symbols.len(),
)
.map(SectionData::DynamicRelocation)
} else {
return Err(Error(format!(
"Invalid sh_link {} in relocation section at index {}",
Expand Down Expand Up @@ -862,8 +880,16 @@ impl<'data> Builder<'data> {

// Assign dynamic symbol indices.
let mut out_dynsyms = Vec::with_capacity(self.dynamic_symbols.len());
let mut gnu_hash_symbol_count = 0;
for symbol in &self.dynamic_symbols {
// Local symbols must come before global.
let local_symbols = self
.dynamic_symbols
.into_iter()
.filter(|symbol| symbol.st_bind() == elf::STB_LOCAL);
let global_symbols = self
.dynamic_symbols
.into_iter()
.filter(|symbol| symbol.st_bind() != elf::STB_LOCAL);
for symbol in local_symbols.chain(global_symbols) {
let mut name = None;
let mut hash = None;
let mut gnu_hash = None;
Expand All @@ -872,9 +898,8 @@ impl<'data> Builder<'data> {
if hash_id.is_some() {
hash = Some(elf::hash(&symbol.name));
}
if gnu_hash_id.is_some() && symbol.st_shndx != elf::SHN_UNDEF {
if gnu_hash_id.is_some() && symbol.section.is_some() {
gnu_hash = Some(elf::gnu_hash(&symbol.name));
gnu_hash_symbol_count += 1;
}
}
out_dynsyms.push(DynamicSymbolOut {
Expand All @@ -884,16 +909,26 @@ impl<'data> Builder<'data> {
gnu_hash,
});
}
let num_local_dynamic = out_dynsyms
.iter()
.take_while(|sym| self.dynamic_symbols.get(sym.id).st_bind() == elf::STB_LOCAL)
.count();
// We must sort for GNU hash before allocating symbol indices.
let mut gnu_hash_symbol_count = 0;
if gnu_hash_id.is_some() {
if self.gnu_hash_bucket_count == 0 {
return Err(Error::new(".gnu.hash bucket count is zero"));
}
// TODO: recalculate bucket_count?
out_dynsyms.sort_by_key(|sym| match sym.gnu_hash {
out_dynsyms[num_local_dynamic..].sort_by_key(|sym| match sym.gnu_hash {
None => (0, 0),
Some(hash) => (1, hash % self.gnu_hash_bucket_count),
});
gnu_hash_symbol_count = out_dynsyms
.iter()
.skip(num_local_dynamic)
.skip_while(|sym| sym.gnu_hash.is_none())
.count() as u32;
}
let mut out_dynsyms_index = vec![None; self.dynamic_symbols.len()];
if dynsym_id.is_some() {
Expand Down Expand Up @@ -938,10 +973,10 @@ impl<'data> Builder<'data> {
name,
});
}
let num_local = 1 + out_syms
let num_local = out_syms
.iter()
.take_while(|sym| self.symbols.get(sym.id).st_bind() == elf::STB_LOCAL)
.count() as u32;
.count();
let mut out_syms_index = vec![None; self.symbols.len()];
if symtab_id.is_some() {
writer.reserve_null_symbol_index();
Expand Down Expand Up @@ -1589,7 +1624,13 @@ impl<'data> Builder<'data> {
SectionData::Dynamic(dynamics) => {
((1 + dynamics.len()) * self.class().dyn_size()) as u64
}
_ => 0,
SectionData::Attributes(_) => out_section.attributes.len() as u64,
_ => {
return Err(Error(format!(
"Unimplemented size for section type {:x}",
section.sh_type
)))
}
};
let sh_link = if let Some(id) = section.sh_link_section {
if let Some(index) = out_sections_index[id.0] {
Expand Down Expand Up @@ -1634,7 +1675,7 @@ impl<'data> Builder<'data> {
writer.write_shstrtab_section_header();
}
SectionData::Symbol => {
writer.write_symtab_section_header(num_local);
writer.write_symtab_section_header(1 + num_local as u32);
}
SectionData::SymbolSectionIndex => {
writer.write_symtab_shndx_section_header();
Expand All @@ -1646,7 +1687,8 @@ impl<'data> Builder<'data> {
writer.write_dynstr_section_header(section.sh_addr);
}
SectionData::DynamicSymbol => {
writer.write_dynsym_section_header(section.sh_addr, 1);
writer
.write_dynsym_section_header(section.sh_addr, 1 + num_local_dynamic as u32);
}
SectionData::Hash => {
writer.write_hash_section_header(section.sh_addr);
Expand Down Expand Up @@ -3011,7 +3053,7 @@ pub struct AttributesSubsubsection<'data> {
}

/// The tag for a sub-subsection in an attributes section.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AttributeTag {
/// The attributes apply to the whole file.
///
Expand Down
4 changes: 4 additions & 0 deletions src/elf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5807,6 +5807,10 @@ pub const EF_RISCV_FLOAT_ABI_QUAD: u32 = 0x0006;
pub const EF_RISCV_RVE: u32 = 0x0008;
pub const EF_RISCV_TSO: u32 = 0x0010;

// RISC-V values for `SectionHeader*::sh_type`.
/// RISC-V attributes section.
pub const SHT_RISCV_ATTRIBUTES: u32 = SHT_LOPROC + 3;

// RISC-V values `Rel*::r_type`.
pub const R_RISCV_NONE: u32 = 0;
pub const R_RISCV_32: u32 = 1;
Expand Down
2 changes: 1 addition & 1 deletion src/write/elf/writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1524,7 +1524,7 @@ impl<'a> Writer<'a> {
sh_link: self.dynsym_index.0,
sh_info: 0,
sh_addralign: self.elf_align as u64,
sh_entsize: 0,
sh_entsize: if self.is_64 { 0 } else { 4 },
});
}

Expand Down
Loading

0 comments on commit 981b874

Please sign in to comment.