Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

build/elf: various fixes #650

Merged
merged 7 commits into from
Mar 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading