mirror of
https://github.com/rui314/mold.git
synced 2026-11-09 04:49:33 -10:00
Flatten output chunk modules
Give each section a sibling module containing its type and functions, and update callers to use the direct paths. Remove the misc and version grouping modules and redundant nested wrappers. Validation: - cargo check --locked --workspace - cargo test --locked --workspace: 5,488 integration tests passed, 636 skipped, no failures; all library tests passed. Four cross targets were unavailable because their compiler or QEMU was missing.
This commit is contained in:
1 parent
6fcdeb9088
commit
2eef5735f0
46 files changed
+3932
-3855
No files matched your search
+1
-1
@@ -63,7 +63,7 @@ use crate::context::Context;
|
||||
use crate::elf::*;
|
||||
use crate::input_sections::{check_tlsle, scan_absrel, scan_pcrel, InputSection};
|
||||
use crate::output_chunks::eh_frame;
|
||||
use crate::output_chunks::got::plt::SPARC_NUM_SMALL_PLT;
|
||||
use crate::output_chunks::plt::SPARC_NUM_SMALL_PLT;
|
||||
use crate::symbol::{Symbol, NEEDS_GOT, NEEDS_GOTTP, NEEDS_PLT, NEEDS_TLSGD};
|
||||
use crate::util::{bit, bits};
|
||||
use crate::{error, fatal};
|
||||
|
||||
+37
-17
@@ -15,24 +15,43 @@ use crate::input_sections::{
|
||||
FragmentRef, InputSection, InputSectionId, SectionFragment, SectionRef,
|
||||
};
|
||||
use crate::linker_script::{DynamicPattern, VersionPattern};
|
||||
use crate::output_chunks::dynamic::{DynamicSection, RelDynSection, RelrDynSection};
|
||||
use crate::output_chunks::eh_frame::{EhFrameHdrSection, EhFrameRelocSection, EhFrameSection};
|
||||
use crate::output_chunks::got::{
|
||||
GotPltSection, GotSection, PltGotSection, PltSection, RelPltSection,
|
||||
};
|
||||
use crate::output_chunks::build_id::BuildIdSection;
|
||||
use crate::output_chunks::comdat_group::ComdatGroupSection;
|
||||
use crate::output_chunks::compressed::CompressedSection;
|
||||
use crate::output_chunks::copyrel::CopyrelSection;
|
||||
use crate::output_chunks::dynamic::DynamicSection;
|
||||
use crate::output_chunks::dynstr::DynstrSection;
|
||||
use crate::output_chunks::dynsym::DynsymSection;
|
||||
use crate::output_chunks::eh_frame::EhFrameSection;
|
||||
use crate::output_chunks::eh_frame_hdr::EhFrameHdrSection;
|
||||
use crate::output_chunks::eh_frame_reloc::EhFrameRelocSection;
|
||||
use crate::output_chunks::gnu_debuglink::GnuDebuglinkSection;
|
||||
use crate::output_chunks::gnu_hash::GnuHashSection;
|
||||
use crate::output_chunks::got::GotSection;
|
||||
use crate::output_chunks::gotplt::GotPltSection;
|
||||
use crate::output_chunks::hash::HashSection;
|
||||
use crate::output_chunks::interp::InterpSection;
|
||||
use crate::output_chunks::merged::{MergedSection, MergedSectionId};
|
||||
use crate::output_chunks::misc::{
|
||||
BuildIdSection, ComdatGroupSection, CompressedSection, CopyrelSection, GnuDebuglinkSection,
|
||||
InterpSection, NotePackageSection, NotePropertySection, RelocSection, RelroPaddingSection,
|
||||
RiscvAttributesSection,
|
||||
};
|
||||
use crate::output_chunks::note_package::NotePackageSection;
|
||||
use crate::output_chunks::note_property::NotePropertySection;
|
||||
use crate::output_chunks::output_section::OutputSection;
|
||||
use crate::output_chunks::sframe::{SFrameRelocSection, SFrameSection};
|
||||
use crate::output_chunks::symtab::{
|
||||
DynstrSection, DynsymSection, GnuHashSection, HashSection, ShstrtabSection, StrtabSection,
|
||||
SymtabSection, SymtabShndxSection,
|
||||
};
|
||||
use crate::output_chunks::version::{VerdefSection, VerneedSection, VersymSection};
|
||||
use crate::output_chunks::plt::PltSection;
|
||||
use crate::output_chunks::pltgot::PltGotSection;
|
||||
use crate::output_chunks::reldyn::RelDynSection;
|
||||
use crate::output_chunks::reloc::RelocSection;
|
||||
use crate::output_chunks::relplt::RelPltSection;
|
||||
use crate::output_chunks::relrdyn::RelrDynSection;
|
||||
use crate::output_chunks::relro_padding::RelroPaddingSection;
|
||||
use crate::output_chunks::riscv_attributes::RiscvAttributesSection;
|
||||
use crate::output_chunks::sframe::SFrameSection;
|
||||
use crate::output_chunks::sframe_reloc::SFrameRelocSection;
|
||||
use crate::output_chunks::shstrtab::ShstrtabSection;
|
||||
use crate::output_chunks::strtab::StrtabSection;
|
||||
use crate::output_chunks::symtab::SymtabSection;
|
||||
use crate::output_chunks::symtab_shndx::SymtabShndxSection;
|
||||
use crate::output_chunks::verdef::VerdefSection;
|
||||
use crate::output_chunks::verneed::VerneedSection;
|
||||
use crate::output_chunks::versym::VersymSection;
|
||||
use crate::output_chunks::{
|
||||
ChunkHeader, ChunkId, GdbIndexSection, OutputEhdr, OutputPhdr, OutputSectionId, OutputShdr,
|
||||
};
|
||||
@@ -174,7 +193,8 @@ pub struct Context<E: Arch> {
|
||||
pub note_property: Option<NotePropertySection<E>>,
|
||||
pub riscv_attributes: Option<RiscvAttributesSection<E>>,
|
||||
pub arm_exidx: Option<crate::output_chunks::arm_exidx::ArmExidxSection<E>>,
|
||||
pub ppc64_save_restore: Option<crate::output_chunks::misc::Ppc64SaveRestoreSection<E>>,
|
||||
pub ppc64_save_restore:
|
||||
Option<crate::output_chunks::ppc64_save_restore::Ppc64SaveRestoreSection<E>>,
|
||||
pub ppc64_opd: Option<crate::output_chunks::opd::Ppc64OpdSection<E>>,
|
||||
/// Whether any input uses Power10 PC-relative calls, which decides
|
||||
/// how thunks address their targets.
|
||||
|
||||
+9
-11
@@ -428,7 +428,7 @@ pub fn link<E: Arch>(cmdline: &[String]) -> Result<i32, String> {
|
||||
// RELR is encoded independently for each output chunk using offsets
|
||||
// relative to that chunk.
|
||||
if ctx.args.pack_dyn_relocs_relr {
|
||||
output_chunks::dynamic::reldyn::construct_relr(&mut ctx);
|
||||
output_chunks::reldyn::construct_relr(&mut ctx);
|
||||
}
|
||||
// Reserve a space for dynamic symbol strings in .dynstr and sort
|
||||
// .dynsym contents if necessary. Beyond this point, no symbol will
|
||||
@@ -475,10 +475,10 @@ pub fn link<E: Arch>(cmdline: &[String]) -> Result<i32, String> {
|
||||
|
||||
// Fill .gnu.version_d section contents.
|
||||
if ctx.verdef.is_some() {
|
||||
output_chunks::version::verdef::construct(&mut ctx);
|
||||
output_chunks::verdef::construct(&mut ctx);
|
||||
}
|
||||
// Fill .gnu.version_r section contents.
|
||||
output_chunks::version::verneed::construct(&mut ctx);
|
||||
output_chunks::verneed::construct(&mut ctx);
|
||||
|
||||
// .eh_frame is a special section from the linker's point of view,
|
||||
// as its contents are parsed and reconstructed by the linker,
|
||||
@@ -562,7 +562,7 @@ pub fn link<E: Arch>(cmdline: &[String]) -> Result<i32, String> {
|
||||
// At this point, both memory and file layouts are fixed.
|
||||
|
||||
let t = ctx.timer("update_reldyn");
|
||||
output_chunks::dynamic::reldyn::update_shdr(&mut ctx);
|
||||
output_chunks::reldyn::update_shdr(&mut ctx);
|
||||
drop(t);
|
||||
ctx.filesize = filesize;
|
||||
t_before_copy.stop();
|
||||
@@ -596,7 +596,7 @@ pub fn link<E: Arch>(cmdline: &[String]) -> Result<i32, String> {
|
||||
if ctx.chunks.contains(&ChunkId::RelDyn) && reldyn.sh_size.get() != 0 {
|
||||
let start = reldyn.sh_offset.get() as usize;
|
||||
let end = (reldyn.sh_offset.get() + reldyn.sh_size.get()) as usize;
|
||||
output_chunks::dynamic::reldyn::sort(&ctx, &mut buf[start..end]);
|
||||
output_chunks::reldyn::sort(&ctx, &mut buf[start..end]);
|
||||
}
|
||||
|
||||
// The final stage reads address ranges, which requires relocated debug
|
||||
@@ -825,12 +825,10 @@ fn run_tasks<E: Arch>(
|
||||
ChunkId::EhFrame => output_chunks::eh_frame::copy_buf(ctx, own, bufs.next()),
|
||||
ChunkId::Symtab => {
|
||||
let strtab = bufs.next().unwrap();
|
||||
output_chunks::symtab::symtab::copy_buf(ctx, own, strtab, bufs.next());
|
||||
}
|
||||
ChunkId::Reloc(i) => output_chunks::misc::reloc::copy_buf(ctx, i, own, bufs.next()),
|
||||
ChunkId::EhFrameReloc => {
|
||||
output_chunks::eh_frame::eh_frame_reloc::copy_buf(ctx, own, bufs.next())
|
||||
output_chunks::symtab::copy_buf(ctx, own, strtab, bufs.next());
|
||||
}
|
||||
ChunkId::Reloc(i) => output_chunks::reloc::copy_buf(ctx, i, own, bufs.next()),
|
||||
ChunkId::EhFrameReloc => output_chunks::eh_frame_reloc::copy_buf(ctx, own, bufs.next()),
|
||||
id => output_chunks::copy_buf(ctx, id, own),
|
||||
}
|
||||
});
|
||||
@@ -838,7 +836,7 @@ fn run_tasks<E: Arch>(
|
||||
// .eh_frame_hdr's header, whose table .eh_frame wrote.
|
||||
if tasks.iter().any(|t| t.chunk == ChunkId::EhFrame) && ctx.eh_frame_hdr.is_some() {
|
||||
let r = file_range(ctx, ChunkId::EhFrameHdr);
|
||||
output_chunks::eh_frame::eh_frame_hdr::write_header(
|
||||
output_chunks::eh_frame_hdr::write_header(
|
||||
ctx,
|
||||
&mut buf[r.offset as usize..(r.offset + r.size) as usize],
|
||||
);
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
//! `.note.gnu.build-id`, the output file's build identifier.
|
||||
|
||||
use crate::arch::Arch;
|
||||
use crate::context::Context;
|
||||
use crate::elf::*;
|
||||
use crate::output_chunks::ChunkHeader;
|
||||
|
||||
// .note.gnu.build-id contains an identifier for an output ELF file. The
|
||||
// contents of the section is usually a cryptogrpahic hash of the output
|
||||
// file itself to guarantee uniqueness of build-id.
|
||||
#[derive(Debug)]
|
||||
pub struct BuildIdSection<E: Layout> {
|
||||
pub hdr: ChunkHeader<E>,
|
||||
pub contents: Vec<u8>,
|
||||
}
|
||||
|
||||
impl<E: Layout> BuildIdSection<E> {
|
||||
pub fn new() -> BuildIdSection<E> {
|
||||
let mut hdr = ChunkHeader::<E>::new(".note.gnu.build-id", SHT_NOTE, SHF_ALLOC as u64);
|
||||
hdr.shdr.sh_addralign.set(4);
|
||||
hdr.shdr.sh_size.set(1);
|
||||
BuildIdSection {
|
||||
hdr,
|
||||
contents: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Layout> Default for BuildIdSection<E> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_shdr<E: Arch>(ctx: &mut Context<E>) {
|
||||
let size = ctx.args.build_id.size() as u64 + 16; // +16 for the header
|
||||
ctx.buildid.as_mut().unwrap().hdr.shdr.sh_size.set(size);
|
||||
}
|
||||
|
||||
pub fn copy_buf<E: Arch>(ctx: &Context<E>, buf: &mut [u8]) {
|
||||
let sec = ctx.buildid.as_ref().unwrap();
|
||||
buf.fill(0);
|
||||
E::Endian::write_u32(buf, 4); // Name size
|
||||
E::Endian::write_u32(&mut buf[4..], ctx.args.build_id.size() as u32); // Hash size
|
||||
E::Endian::write_u32(&mut buf[8..], NT_GNU_BUILD_ID);
|
||||
buf[12..16].copy_from_slice(b"GNU\0"); // Name string
|
||||
buf[16..16 + sec.contents.len()].copy_from_slice(&sec.contents); // Build ID
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
//! COMDAT groups in relocatable outputs.
|
||||
|
||||
use crate::arch::Arch;
|
||||
use crate::context::Context;
|
||||
use crate::elf::*;
|
||||
use crate::output_chunks::{ChunkHeader, ChunkId};
|
||||
use crate::symbol::SymbolId;
|
||||
|
||||
// ComdatGroupSection represents a comdat group for an output file.
|
||||
// This is used only for the relocatable output (i.e. the `-r` output).
|
||||
#[derive(Debug)]
|
||||
pub struct ComdatGroupSection<E: Layout> {
|
||||
pub hdr: ChunkHeader<E>,
|
||||
pub sym: SymbolId,
|
||||
pub members: Vec<ChunkId>,
|
||||
}
|
||||
|
||||
impl<E: Layout> ComdatGroupSection<E> {
|
||||
pub fn new(sym: SymbolId, members: Vec<ChunkId>) -> ComdatGroupSection<E> {
|
||||
let mut hdr = ChunkHeader::<E>::new(".group", SHT_GROUP, 0);
|
||||
hdr.shdr.sh_entsize.set(4);
|
||||
hdr.shdr.sh_addralign.set(4);
|
||||
hdr.shdr.sh_size.set((members.len() * 4 + 4) as u64);
|
||||
ComdatGroupSection { hdr, sym, members }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_shdr<E: Arch>(ctx: &mut Context<E>, i: u32) {
|
||||
debug_assert!(ctx.args.relocatable);
|
||||
let sec = &ctx.comdat_group_sections[i as usize];
|
||||
let sym = &ctx.symbols[sec.sym];
|
||||
let sh_info = if sym.st_type() == STT_SECTION {
|
||||
let isec = sym.input_section_ref(ctx).unwrap();
|
||||
ctx.output_section(isec.output_section.unwrap()).hdr.shndx
|
||||
} else {
|
||||
sym.output_sym_idx(ctx)
|
||||
};
|
||||
let symtab_shndx = ctx.symtab.hdr.shndx;
|
||||
let sec = &mut ctx.comdat_group_sections[i as usize];
|
||||
sec.hdr.shdr.sh_link.set(symtab_shndx);
|
||||
sec.hdr.shdr.sh_info.set(sh_info);
|
||||
}
|
||||
|
||||
pub fn copy_buf<E: Arch>(ctx: &Context<E>, i: u32, buf: &mut [u8]) {
|
||||
let sec = &ctx.comdat_group_sections[i as usize];
|
||||
E::Endian::write_u32(buf, GRP_COMDAT);
|
||||
for (j, &member) in sec.members.iter().enumerate() {
|
||||
E::Endian::write_u32(&mut buf[4 + j * 4..], ctx.chunk_header(member).shndx);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
//! Compressed output sections.
|
||||
|
||||
use crate::arch::Arch;
|
||||
use crate::context::Context;
|
||||
use crate::elf::*;
|
||||
use crate::output_chunks::{self, ChunkHeader, ChunkId};
|
||||
use crate::util::compress::Compressor;
|
||||
|
||||
// Debug sections can be compressed with zlib or zstd to reduce the
|
||||
// overall size of an ELF file. CompressedSection represents a compressed
|
||||
// section.
|
||||
#[derive(Debug)]
|
||||
pub struct CompressedSection<E: Layout> {
|
||||
pub hdr: ChunkHeader<E>,
|
||||
pub chdr: ElfChdr<E>,
|
||||
pub compressor: Compressor,
|
||||
/// Kept for --gdb-index, which reads the uncompressed contents.
|
||||
pub uncompressed_data: Option<Vec<u8>>,
|
||||
pub original: ChunkId,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Compressor {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "Compressor({} bytes)", self.compressed_size())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new<E: Arch>(ctx: &Context<E>, original: ChunkId) -> CompressedSection<E> {
|
||||
let hdr = ctx.chunk_header(original);
|
||||
|
||||
// C++ mold uses uninitialized storage here to avoid zero-filling a
|
||||
// potentially large scratch buffer. Rust currently uses a
|
||||
// zero-initialized Vec because write_to accepts &mut [u8].
|
||||
let mut buf = vec![0u8; hdr.shdr.sh_size.get() as usize];
|
||||
|
||||
// Write uncompressed contents and then compress them
|
||||
output_chunks::write_to(ctx, original, &mut buf);
|
||||
|
||||
let level = ctx.args.compress_debug_sections_level;
|
||||
let compressor = if ctx.args.compress_debug_sections == ELFCOMPRESS_ZLIB {
|
||||
Compressor::zlib(&buf, level as u32)
|
||||
} else {
|
||||
Compressor::zstd(&buf, level as i32)
|
||||
};
|
||||
|
||||
// Compute header field values
|
||||
let mut chdr = ElfChdr::<E>::default();
|
||||
chdr.ch_type_mut().set(ctx.args.compress_debug_sections);
|
||||
chdr.ch_size_mut().set(hdr.shdr.sh_size.get());
|
||||
chdr.ch_addralign_mut().set(hdr.shdr.sh_addralign.get());
|
||||
let mut new_hdr = ChunkHeader::<E>::with_name(
|
||||
hdr.name,
|
||||
hdr.shdr.sh_type.get(),
|
||||
hdr.shdr.sh_flags.get() | SHF_COMPRESSED as u64,
|
||||
);
|
||||
new_hdr.shndx = hdr.shndx;
|
||||
new_hdr.is_compressed = true;
|
||||
new_hdr.shdr = hdr.shdr;
|
||||
new_hdr
|
||||
.shdr
|
||||
.sh_flags
|
||||
.set(new_hdr.shdr.sh_flags.get() | SHF_COMPRESSED as u64);
|
||||
new_hdr.shdr.sh_addralign.set(1);
|
||||
new_hdr
|
||||
.shdr
|
||||
.sh_size
|
||||
.set((std::mem::size_of::<ElfChdr<E>>() + compressor.compressed_size()) as u64);
|
||||
|
||||
// We can discard the uncompressed contents unless --gdb-index is given
|
||||
CompressedSection {
|
||||
hdr: new_hdr,
|
||||
chdr,
|
||||
compressor,
|
||||
uncompressed_data: ctx.args.gdb_index.then_some(buf),
|
||||
original,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn copy_buf<E: Arch>(ctx: &Context<E>, i: u32, buf: &mut [u8]) {
|
||||
let sec = &ctx.compressed_sections[i as usize];
|
||||
sec.chdr.write(buf);
|
||||
sec.compressor
|
||||
.write_to(&mut buf[std::mem::size_of::<ElfChdr<E>>()..]);
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
//! `.copyrel` and `.copyrel.rel.ro`, storage for copy relocations.
|
||||
|
||||
use crate::arch::Arch;
|
||||
use crate::context::Context;
|
||||
use crate::elf::*;
|
||||
use crate::error;
|
||||
use crate::input_files::FileId;
|
||||
use crate::output_chunks::{self, ChunkHeader};
|
||||
use crate::symbol::SymbolId;
|
||||
use crate::util::align_to;
|
||||
|
||||
// .copyrel and .copyrel.rel.ro represent memory regions to which the
|
||||
// runtime copies symbols from other ELF files for copy relocations.
|
||||
#[derive(Debug)]
|
||||
pub struct CopyrelSection<E: Layout> {
|
||||
pub hdr: ChunkHeader<E>,
|
||||
pub symbols: Vec<SymbolId>,
|
||||
}
|
||||
|
||||
impl<E: Layout> CopyrelSection<E> {
|
||||
pub fn new(is_relro: bool) -> CopyrelSection<E> {
|
||||
let name = if is_relro {
|
||||
".copyrel.rel.ro"
|
||||
} else {
|
||||
".copyrel"
|
||||
};
|
||||
let mut hdr = ChunkHeader::<E>::new(name, SHT_NOBITS, (SHF_ALLOC | SHF_WRITE) as u64);
|
||||
hdr.is_relro = is_relro;
|
||||
CopyrelSection {
|
||||
hdr,
|
||||
symbols: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_symbol<E: Arch>(ctx: &mut Context<E>, relro: bool, id: SymbolId) {
|
||||
debug_assert!(!ctx.args.shared);
|
||||
let sym = &ctx.symbols[id];
|
||||
if sym.has_copyrel() {
|
||||
return;
|
||||
}
|
||||
let Some(FileId::Dso(dso_id)) = sym.file() else {
|
||||
debug_assert!(sym.is_undef_weak());
|
||||
let file = ctx.file_display(sym.file().unwrap());
|
||||
error!("{file}: cannot create a copy relocation for {sym}; recompile with -fPIE or -fPIC");
|
||||
return;
|
||||
};
|
||||
let dso = &ctx.dsos[dso_id.index()];
|
||||
if sym.esym(ctx).st_visibility() == STV_PROTECTED {
|
||||
error!("{dso}: cannot create a copy relocation for protected symbol '{sym}'; recompile with -fPIC"
|
||||
);
|
||||
return;
|
||||
}
|
||||
if !ctx.args.z_copyreloc {
|
||||
error!("-z nocopyreloc: {dso}: cannot create a copy relocation for symbol '{sym}'; recompile with -fPIC"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let alignment = dso.alignment(sym);
|
||||
let size = sym.esym(ctx).st_size().get();
|
||||
// We need to create dynamic symbols not only for this particular symbol
|
||||
// but also for its aliases (i.e. other symbols at the same address)
|
||||
// becasue otherwise the aliases are broken apart at runtime.
|
||||
// For example, `environ`, `_environ` and `__environ` in libc.so are
|
||||
// aliases. If one of the symbols is copied by a copy relocation, other
|
||||
// symbols have to refer to the copied place as well.
|
||||
let aliases: Vec<SymbolId> = dso.symbols_at(ctx, sym, dso_id).to_vec();
|
||||
|
||||
let sec = if relro {
|
||||
&mut ctx.copyrel_relro
|
||||
} else {
|
||||
&mut ctx.copyrel
|
||||
};
|
||||
sec.symbols.push(id);
|
||||
let offset = align_to(sec.hdr.shdr.sh_size.get(), alignment);
|
||||
sec.hdr.shdr.sh_size.set(offset + size);
|
||||
sec.hdr
|
||||
.shdr
|
||||
.sh_addralign
|
||||
.set(sec.hdr.shdr.sh_addralign.get().max(alignment));
|
||||
|
||||
for alias in aliases {
|
||||
ctx.symbols.aux_mut(alias);
|
||||
let s = &mut ctx.symbols[alias];
|
||||
s.set_imported(true);
|
||||
s.set_exported(true);
|
||||
s.set_copyrel(true);
|
||||
s.set_copyrel_readonly(relro);
|
||||
s.value = offset;
|
||||
output_chunks::dynsym::add_symbol(ctx, alias);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_dynrels<E: Arch>(ctx: &Context<E>, sec: &CopyrelSection<E>, out: &mut [E::Rel]) {
|
||||
for (i, &id) in sec.symbols.iter().enumerate() {
|
||||
let sym = &ctx.symbols[id];
|
||||
out[i] = ElfRel::<E>::new(
|
||||
sym.addr(ctx),
|
||||
E::R_COPY,
|
||||
sym.dynsym_idx(&ctx.symbols).unwrap_or(0),
|
||||
0,
|
||||
);
|
||||
}
|
||||
debug_assert_eq!(sec.symbols.len(), out.len());
|
||||
}
|
||||
+301
-730
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,62 @@
|
||||
//! `.dynstr`, strings used by the dynamic linker.
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::arch::Arch;
|
||||
use crate::context::Context;
|
||||
use crate::elf::*;
|
||||
use crate::output_chunks::ChunkHeader;
|
||||
use crate::util::write_cstr;
|
||||
|
||||
// .dynstr contains strings that the runtime uses.
|
||||
#[derive(Debug)]
|
||||
pub struct DynstrSection<E: Layout> {
|
||||
pub hdr: ChunkHeader<E>,
|
||||
strings: HashMap<Vec<u8>, u64>,
|
||||
}
|
||||
|
||||
impl<E: Layout> DynstrSection<E> {
|
||||
pub fn new() -> DynstrSection<E> {
|
||||
DynstrSection {
|
||||
hdr: ChunkHeader::<E>::new(".dynstr", SHT_STRTAB, SHF_ALLOC as u64),
|
||||
strings: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_string(&mut self, s: &[u8]) -> u64 {
|
||||
if self.hdr.shdr.sh_size.get() == 0 {
|
||||
self.strings.insert(Vec::new(), 0);
|
||||
self.hdr.shdr.sh_size.set(1);
|
||||
}
|
||||
if let Some(&off) = self.strings.get(s) {
|
||||
return off;
|
||||
}
|
||||
let off = self.hdr.shdr.sh_size.get();
|
||||
self.strings.insert(s.to_vec(), off);
|
||||
self.hdr
|
||||
.shdr
|
||||
.sh_size
|
||||
.set(self.hdr.shdr.sh_size.get() + s.len() as u64 + 1);
|
||||
off
|
||||
}
|
||||
|
||||
pub fn find_string(&self, s: &[u8]) -> u64 {
|
||||
*self.strings.get(s).expect("string not in .dynstr")
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Layout> Default for DynstrSection<E> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn copy_buf<E: Arch>(ctx: &Context<E>, buf: &mut [u8]) {
|
||||
for (s, &off) in &ctx.dynstr.strings {
|
||||
write_cstr(&mut buf[off as usize..], s);
|
||||
}
|
||||
let mut off = ctx.dynsym.dynstr_offset as usize;
|
||||
for &id in ctx.dynsym.symbols.iter().flatten() {
|
||||
off += write_cstr(&mut buf[off..], ctx.symbols[id].name());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
//! `.dynsym`, the dynamic symbol table.
|
||||
|
||||
use crate::arch::Arch;
|
||||
use crate::context::Context;
|
||||
use crate::elf::*;
|
||||
use crate::error;
|
||||
use crate::output_chunks::symtab::to_output_esym;
|
||||
use crate::output_chunks::ChunkHeader;
|
||||
use crate::symbol::SymbolId;
|
||||
|
||||
// .dynsym contains symbols for dynamic linking. This is similar to
|
||||
// .symtab, but .dynsym contains data that the runtime uses.
|
||||
#[derive(Debug)]
|
||||
pub struct DynsymSection<E: Layout> {
|
||||
pub hdr: ChunkHeader<E>,
|
||||
/// Index 0 is the null symbol.
|
||||
pub symbols: Vec<Option<SymbolId>>,
|
||||
pub dynstr_offset: u64,
|
||||
}
|
||||
|
||||
impl<E: Arch> DynsymSection<E> {
|
||||
pub fn new() -> DynsymSection<E> {
|
||||
let mut hdr = ChunkHeader::<E>::new(".dynsym", SHT_DYNSYM, SHF_ALLOC as u64);
|
||||
hdr.shdr
|
||||
.sh_entsize
|
||||
.set(std::mem::size_of::<ElfSym<E>>() as u64);
|
||||
hdr.shdr.sh_addralign.set(E::WORD_SIZE as u64);
|
||||
DynsymSection {
|
||||
hdr,
|
||||
symbols: Vec::new(),
|
||||
dynstr_offset: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Arch> Default for DynsymSection<E> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_symbol<E: Arch>(ctx: &mut Context<E>, sym: SymbolId) {
|
||||
if ctx.dynsym.symbols.is_empty() {
|
||||
ctx.dynsym.symbols.push(None);
|
||||
}
|
||||
if ctx.symbols[sym].dynsym_idx(&ctx.symbols).is_none() {
|
||||
// A placeholder; the real index is assigned by sort_dynsyms.
|
||||
ctx.symbols.aux_mut(sym).dynsym_idx = Some(u32::MAX);
|
||||
ctx.dynsym.symbols.push(Some(sym));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_shdr<E: Arch>(ctx: &mut Context<E>) {
|
||||
ctx.dynsym.hdr.shdr.sh_link.set(ctx.dynstr.hdr.shndx);
|
||||
ctx.dynsym
|
||||
.hdr
|
||||
.shdr
|
||||
.sh_size
|
||||
.set(std::mem::size_of::<ElfSym<E>>() as u64 * ctx.dynsym.symbols.len() as u64);
|
||||
}
|
||||
|
||||
pub fn copy_buf<E: Arch>(ctx: &Context<E>, buf: &mut [u8]) {
|
||||
let size = std::mem::size_of::<ElfSym<E>>();
|
||||
buf[..size].fill(0);
|
||||
let mut offset = ctx.dynsym.dynstr_offset as u32;
|
||||
for &id in ctx.dynsym.symbols.iter().skip(1).flatten() {
|
||||
let sym = &ctx.symbols[id];
|
||||
let (esym, xindex) = to_output_esym(ctx, sym, offset);
|
||||
if xindex != 0 {
|
||||
let nshdrs = ctx.shdr.as_ref().map_or(0, |s| {
|
||||
s.hdr.shdr.sh_size.get() / ElfShdr::<E>::size() as u64
|
||||
});
|
||||
error!("{}: .dynsym: too many output sections: {nshdrs} requested, but ELF allows at most 65279",
|
||||
ctx.args.output
|
||||
);
|
||||
return;
|
||||
}
|
||||
esym.write(&mut buf[sym.dynsym_idx(&ctx.symbols).unwrap() as usize * size..]);
|
||||
offset += sym.name().len() as u32 + 1;
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,20 @@
|
||||
//! `.eh_frame`, `.eh_frame_hdr` and `.rela.eh_frame`.
|
||||
//! `.eh_frame`, reconstructed exception-handling records.
|
||||
//!
|
||||
//! `.eh_frame` is reconstructed rather than copied: FDEs of dead functions
|
||||
//! are dropped, identical CIEs are merged, and a sorted lookup table is
|
||||
//! emitted as `.eh_frame_hdr` so that the unwinder can find the FDE for a
|
||||
//! PC by binary search.
|
||||
|
||||
use rayon::prelude::*;
|
||||
use std::ptr::NonNull;
|
||||
|
||||
use rayon::prelude::*;
|
||||
|
||||
use crate::arch::Arch;
|
||||
use crate::context::Context;
|
||||
use crate::elf::*;
|
||||
use crate::input_files::ObjectFile;
|
||||
use crate::input_sections::CieRecord;
|
||||
use crate::output_chunks::eh_frame_hdr::EhFrameHdrSection;
|
||||
use crate::output_chunks::ChunkHeader;
|
||||
use crate::output_file::split_at_offsets;
|
||||
use crate::symbol::Symbol;
|
||||
@@ -404,192 +406,6 @@ pub fn check_range<E: Arch>(
|
||||
}
|
||||
}
|
||||
|
||||
// .eh_frame_hdr is a lookup table for .eh_frame. Entries in .eh_frame_hdr
|
||||
// are sorted by their dcorresponding function addresses, so tha the
|
||||
// runtime can quickly find an exception-handling record for the current
|
||||
// function by binary search. Without .eh_frame_hdr, the runtime would
|
||||
// have had to do linear search in .eh_frame.
|
||||
#[derive(Debug)]
|
||||
pub struct EhFrameHdrSection<E: Layout> {
|
||||
pub hdr: ChunkHeader<E>,
|
||||
pub num_fdes: u64,
|
||||
}
|
||||
|
||||
impl<E: Layout> EhFrameHdrSection<E> {
|
||||
pub const HEADER_SIZE: u64 = 12;
|
||||
|
||||
pub fn new() -> EhFrameHdrSection<E> {
|
||||
let mut hdr = ChunkHeader::<E>::new(".eh_frame_hdr", SHT_PROGBITS, SHF_ALLOC as u64);
|
||||
hdr.shdr.sh_addralign.set(4);
|
||||
hdr.shdr.sh_size.set(Self::HEADER_SIZE);
|
||||
EhFrameHdrSection { hdr, num_fdes: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Layout> Default for EhFrameHdrSection<E> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub mod eh_frame_hdr {
|
||||
use super::*;
|
||||
|
||||
pub fn update_shdr<E: Arch>(ctx: &mut Context<E>) {
|
||||
let num_fdes: u64 = ctx.objs.iter().map(|f| f.fdes.len() as u64).sum();
|
||||
let sec = ctx.eh_frame_hdr.as_mut().unwrap();
|
||||
sec.num_fdes = num_fdes;
|
||||
sec.hdr
|
||||
.shdr
|
||||
.sh_size
|
||||
.set(EhFrameHdrSection::<E>::HEADER_SIZE + num_fdes * 8);
|
||||
}
|
||||
|
||||
pub fn write_header<E: Arch>(ctx: &Context<E>, buf: &mut [u8]) {
|
||||
let sec = ctx.eh_frame_hdr.as_ref().unwrap();
|
||||
|
||||
// Write a header. The actual table is written by EhFrameSection::copy_buf.
|
||||
buf[0] = 1;
|
||||
buf[1] = (DW_EH_PE_pcrel | DW_EH_PE_sdata4) as u8;
|
||||
buf[2] = DW_EH_PE_udata4 as u8;
|
||||
buf[3] = (DW_EH_PE_datarel | DW_EH_PE_sdata4) as u8;
|
||||
E::Endian::write_u32(
|
||||
&mut buf[4..],
|
||||
ctx.eh_frame
|
||||
.hdr
|
||||
.shdr
|
||||
.sh_addr
|
||||
.get()
|
||||
.wrapping_sub(sec.hdr.shdr.sh_addr.get())
|
||||
.wrapping_sub(4) as u32,
|
||||
);
|
||||
E::Endian::write_u32(&mut buf[8..], sec.num_fdes as u32);
|
||||
}
|
||||
}
|
||||
|
||||
// EhFrameRelocSection contains relocation records for .eh_frame. It is used
|
||||
// only for relocatable outputs (an .o file rather than an executable or .so).
|
||||
#[derive(Debug)]
|
||||
pub struct EhFrameRelocSection<E: Layout> {
|
||||
pub hdr: ChunkHeader<E>,
|
||||
}
|
||||
|
||||
impl<E: Arch> EhFrameRelocSection<E> {
|
||||
pub fn new() -> EhFrameRelocSection<E> {
|
||||
let (name, ty) = if E::IS_RELA {
|
||||
(".rela.eh_frame", SHT_RELA)
|
||||
} else {
|
||||
(".rel.eh_frame", SHT_REL)
|
||||
};
|
||||
let mut hdr = ChunkHeader::<E>::new(name, ty, SHF_INFO_LINK as u64);
|
||||
hdr.shdr.sh_addralign.set(E::WORD_SIZE as u64);
|
||||
hdr.shdr
|
||||
.sh_entsize
|
||||
.set(std::mem::size_of::<ElfRel<E>>() as u64);
|
||||
EhFrameRelocSection { hdr }
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Arch> Default for EhFrameRelocSection<E> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub mod eh_frame_reloc {
|
||||
use super::*;
|
||||
|
||||
pub fn update_shdr<E: Arch>(ctx: &mut Context<E>) {
|
||||
let count: usize = ctx
|
||||
.objs
|
||||
.par_iter()
|
||||
.map(|file| {
|
||||
let cies: usize = file
|
||||
.cies
|
||||
.iter()
|
||||
.filter(|c| c.is_leader)
|
||||
.map(|c| c.rels(file).len())
|
||||
.sum();
|
||||
let fdes: usize = file.fdes.iter().map(|f| f.rels(file).len()).sum();
|
||||
cies + fdes
|
||||
})
|
||||
.sum();
|
||||
let sec = ctx.eh_frame_reloc.as_mut().unwrap();
|
||||
sec.hdr
|
||||
.shdr
|
||||
.sh_size
|
||||
.set((count * std::mem::size_of::<ElfRel<E>>()) as u64);
|
||||
sec.hdr.shdr.sh_link.set(ctx.symtab.hdr.shndx);
|
||||
sec.hdr.shdr.sh_info.set(ctx.eh_frame.hdr.shndx);
|
||||
}
|
||||
|
||||
/// Writes the relocations; with REL and `-r`, addends are written into
|
||||
/// `.eh_frame` itself, which is passed as `eh_frame_buf`.
|
||||
pub fn copy_buf<E: Arch>(ctx: &Context<E>, buf: &mut [u8], eh_frame_buf: Option<&mut [u8]>) {
|
||||
let out = rels_from_bytes_mut::<E>(buf);
|
||||
let mut eh_frame_buf = eh_frame_buf;
|
||||
let mut n = 0;
|
||||
|
||||
let mut copy = |file: &ObjectFile<E>,
|
||||
shndx: u32,
|
||||
r: &ElfRel<E>,
|
||||
offset: u64,
|
||||
eh_frame_buf: &mut Option<&mut [u8]>| {
|
||||
let isec = file.section_at(shndx);
|
||||
let sym = &ctx.symbols[file.base.symbols[r.r_sym() as usize]];
|
||||
let mut rel = ElfRel::<E>::new(
|
||||
ctx.eh_frame.hdr.shdr.sh_addr.get() + offset,
|
||||
r.r_type(),
|
||||
0,
|
||||
0,
|
||||
);
|
||||
|
||||
if sym.st_type() == STT_SECTION {
|
||||
// We discard section symbols in input files and re-create new
|
||||
// ones for each output section. So we need to adjust relocations'
|
||||
// addends if they refer a section symbol.
|
||||
let target = sym.input_section_ref(ctx).unwrap();
|
||||
rel.set_r_sym(ctx.output_section(target.output_section.unwrap()).hdr.shndx);
|
||||
let addend = isec.rel_addend(r) + target.offset() as i64;
|
||||
if E::IS_RELA {
|
||||
rel.set_r_addend(addend);
|
||||
} else if ctx.args.relocatable {
|
||||
if let Some(eh) = eh_frame_buf {
|
||||
E::write_addend(&mut eh[offset as usize..], addend, r);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
rel.set_r_sym(sym.output_sym_idx(ctx));
|
||||
if E::IS_RELA {
|
||||
rel.set_r_addend(isec.rel_addend(r));
|
||||
}
|
||||
}
|
||||
out[n] = rel;
|
||||
n += 1;
|
||||
};
|
||||
|
||||
for file in &ctx.objs {
|
||||
for cie in &file.cies {
|
||||
if cie.is_leader {
|
||||
for rel in cie.rels(file) {
|
||||
let offset =
|
||||
cie.output_offset as u64 + rel.r_offset() - cie.input_offset as u64;
|
||||
copy(file, cie.section, rel, offset, &mut eh_frame_buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
for fde in &file.fdes {
|
||||
let cie = &file.cies[fde.cie_idx as usize];
|
||||
let base = file.fde_offset + fde.output_offset as u64;
|
||||
for rel in fde.rels(file) {
|
||||
let offset = base + rel.r_offset() - fde.input_offset as u64;
|
||||
copy(file, cie.section, rel, offset, &mut eh_frame_buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Fatal error for `.eh_frame` contents that can't be handled.
|
||||
pub fn unsupported<E: Arch>(rel: &ElfRel<E>) -> ! {
|
||||
fatal!(
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
//! `.eh_frame_hdr`, the lookup table for exception-handling records.
|
||||
|
||||
use crate::arch::Arch;
|
||||
use crate::context::Context;
|
||||
use crate::elf::*;
|
||||
use crate::output_chunks::ChunkHeader;
|
||||
|
||||
// .eh_frame_hdr is a lookup table for .eh_frame. Entries in .eh_frame_hdr
|
||||
// are sorted by their dcorresponding function addresses, so tha the
|
||||
// runtime can quickly find an exception-handling record for the current
|
||||
// function by binary search. Without .eh_frame_hdr, the runtime would
|
||||
// have had to do linear search in .eh_frame.
|
||||
#[derive(Debug)]
|
||||
pub struct EhFrameHdrSection<E: Layout> {
|
||||
pub hdr: ChunkHeader<E>,
|
||||
pub num_fdes: u64,
|
||||
}
|
||||
|
||||
impl<E: Layout> EhFrameHdrSection<E> {
|
||||
pub const HEADER_SIZE: u64 = 12;
|
||||
|
||||
pub fn new() -> EhFrameHdrSection<E> {
|
||||
let mut hdr = ChunkHeader::<E>::new(".eh_frame_hdr", SHT_PROGBITS, SHF_ALLOC as u64);
|
||||
hdr.shdr.sh_addralign.set(4);
|
||||
hdr.shdr.sh_size.set(Self::HEADER_SIZE);
|
||||
EhFrameHdrSection { hdr, num_fdes: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Layout> Default for EhFrameHdrSection<E> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_shdr<E: Arch>(ctx: &mut Context<E>) {
|
||||
let num_fdes: u64 = ctx.objs.iter().map(|f| f.fdes.len() as u64).sum();
|
||||
let sec = ctx.eh_frame_hdr.as_mut().unwrap();
|
||||
sec.num_fdes = num_fdes;
|
||||
sec.hdr
|
||||
.shdr
|
||||
.sh_size
|
||||
.set(EhFrameHdrSection::<E>::HEADER_SIZE + num_fdes * 8);
|
||||
}
|
||||
|
||||
pub fn write_header<E: Arch>(ctx: &Context<E>, buf: &mut [u8]) {
|
||||
let sec = ctx.eh_frame_hdr.as_ref().unwrap();
|
||||
|
||||
// Write a header. The actual table is written by EhFrameSection::copy_buf.
|
||||
buf[0] = 1;
|
||||
buf[1] = (DW_EH_PE_pcrel | DW_EH_PE_sdata4) as u8;
|
||||
buf[2] = DW_EH_PE_udata4 as u8;
|
||||
buf[3] = (DW_EH_PE_datarel | DW_EH_PE_sdata4) as u8;
|
||||
E::Endian::write_u32(
|
||||
&mut buf[4..],
|
||||
ctx.eh_frame
|
||||
.hdr
|
||||
.shdr
|
||||
.sh_addr
|
||||
.get()
|
||||
.wrapping_sub(sec.hdr.shdr.sh_addr.get())
|
||||
.wrapping_sub(4) as u32,
|
||||
);
|
||||
E::Endian::write_u32(&mut buf[8..], sec.num_fdes as u32);
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
//! `.rel.eh_frame` and `.rela.eh_frame` for relocatable outputs.
|
||||
|
||||
use rayon::prelude::*;
|
||||
|
||||
use crate::arch::Arch;
|
||||
use crate::context::Context;
|
||||
use crate::elf::*;
|
||||
use crate::input_files::ObjectFile;
|
||||
use crate::output_chunks::ChunkHeader;
|
||||
|
||||
// EhFrameRelocSection contains relocation records for .eh_frame. It is used
|
||||
// only for relocatable outputs (an .o file rather than an executable or .so).
|
||||
#[derive(Debug)]
|
||||
pub struct EhFrameRelocSection<E: Layout> {
|
||||
pub hdr: ChunkHeader<E>,
|
||||
}
|
||||
|
||||
impl<E: Arch> EhFrameRelocSection<E> {
|
||||
pub fn new() -> EhFrameRelocSection<E> {
|
||||
let (name, ty) = if E::IS_RELA {
|
||||
(".rela.eh_frame", SHT_RELA)
|
||||
} else {
|
||||
(".rel.eh_frame", SHT_REL)
|
||||
};
|
||||
let mut hdr = ChunkHeader::<E>::new(name, ty, SHF_INFO_LINK as u64);
|
||||
hdr.shdr.sh_addralign.set(E::WORD_SIZE as u64);
|
||||
hdr.shdr
|
||||
.sh_entsize
|
||||
.set(std::mem::size_of::<ElfRel<E>>() as u64);
|
||||
EhFrameRelocSection { hdr }
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Arch> Default for EhFrameRelocSection<E> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_shdr<E: Arch>(ctx: &mut Context<E>) {
|
||||
let count: usize = ctx
|
||||
.objs
|
||||
.par_iter()
|
||||
.map(|file| {
|
||||
let cies: usize = file
|
||||
.cies
|
||||
.iter()
|
||||
.filter(|c| c.is_leader)
|
||||
.map(|c| c.rels(file).len())
|
||||
.sum();
|
||||
let fdes: usize = file.fdes.iter().map(|f| f.rels(file).len()).sum();
|
||||
cies + fdes
|
||||
})
|
||||
.sum();
|
||||
let sec = ctx.eh_frame_reloc.as_mut().unwrap();
|
||||
sec.hdr
|
||||
.shdr
|
||||
.sh_size
|
||||
.set((count * std::mem::size_of::<ElfRel<E>>()) as u64);
|
||||
sec.hdr.shdr.sh_link.set(ctx.symtab.hdr.shndx);
|
||||
sec.hdr.shdr.sh_info.set(ctx.eh_frame.hdr.shndx);
|
||||
}
|
||||
|
||||
/// Writes the relocations; with REL and `-r`, addends are written into
|
||||
/// `.eh_frame` itself, which is passed as `eh_frame_buf`.
|
||||
pub fn copy_buf<E: Arch>(ctx: &Context<E>, buf: &mut [u8], eh_frame_buf: Option<&mut [u8]>) {
|
||||
let out = rels_from_bytes_mut::<E>(buf);
|
||||
let mut eh_frame_buf = eh_frame_buf;
|
||||
let mut n = 0;
|
||||
|
||||
let mut copy = |file: &ObjectFile<E>,
|
||||
shndx: u32,
|
||||
r: &ElfRel<E>,
|
||||
offset: u64,
|
||||
eh_frame_buf: &mut Option<&mut [u8]>| {
|
||||
let isec = file.section_at(shndx);
|
||||
let sym = &ctx.symbols[file.base.symbols[r.r_sym() as usize]];
|
||||
let mut rel = ElfRel::<E>::new(
|
||||
ctx.eh_frame.hdr.shdr.sh_addr.get() + offset,
|
||||
r.r_type(),
|
||||
0,
|
||||
0,
|
||||
);
|
||||
|
||||
if sym.st_type() == STT_SECTION {
|
||||
// We discard section symbols in input files and re-create new
|
||||
// ones for each output section. So we need to adjust relocations'
|
||||
// addends if they refer a section symbol.
|
||||
let target = sym.input_section_ref(ctx).unwrap();
|
||||
rel.set_r_sym(ctx.output_section(target.output_section.unwrap()).hdr.shndx);
|
||||
let addend = isec.rel_addend(r) + target.offset() as i64;
|
||||
if E::IS_RELA {
|
||||
rel.set_r_addend(addend);
|
||||
} else if ctx.args.relocatable {
|
||||
if let Some(eh) = eh_frame_buf {
|
||||
E::write_addend(&mut eh[offset as usize..], addend, r);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
rel.set_r_sym(sym.output_sym_idx(ctx));
|
||||
if E::IS_RELA {
|
||||
rel.set_r_addend(isec.rel_addend(r));
|
||||
}
|
||||
}
|
||||
out[n] = rel;
|
||||
n += 1;
|
||||
};
|
||||
|
||||
for file in &ctx.objs {
|
||||
for cie in &file.cies {
|
||||
if cie.is_leader {
|
||||
for rel in cie.rels(file) {
|
||||
let offset =
|
||||
cie.output_offset as u64 + rel.r_offset() - cie.input_offset as u64;
|
||||
copy(file, cie.section, rel, offset, &mut eh_frame_buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
for fde in &file.fdes {
|
||||
let cie = &file.cies[fde.cie_idx as usize];
|
||||
let base = file.fde_offset + fde.output_offset as u64;
|
||||
for rel in fde.rels(file) {
|
||||
let offset = base + rel.r_offset() - fde.input_offset as u64;
|
||||
copy(file, cie.section, rel, offset, &mut eh_frame_buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
//! `.gnu_debuglink`, the pathname and checksum of separate debug information.
|
||||
|
||||
use crate::arch::Arch;
|
||||
use crate::context::Context;
|
||||
use crate::elf::*;
|
||||
use crate::output_chunks::ChunkHeader;
|
||||
use crate::util::align_to;
|
||||
use crate::util::{path_filename, write_cstr};
|
||||
|
||||
// .gnu_debuglink section contains a pathname and its CRC32 checksum for a
|
||||
// separate debug info file. gdb can read the section to read debug info
|
||||
// from an external file.
|
||||
#[derive(Debug)]
|
||||
pub struct GnuDebuglinkSection<E: Layout> {
|
||||
pub hdr: ChunkHeader<E>,
|
||||
pub filename: String,
|
||||
pub crc32: u32,
|
||||
}
|
||||
|
||||
impl<E: Layout> GnuDebuglinkSection<E> {
|
||||
pub fn new() -> GnuDebuglinkSection<E> {
|
||||
let mut hdr = ChunkHeader::<E>::new(".gnu_debuglink", SHT_PROGBITS, 0);
|
||||
hdr.shdr.sh_addralign.set(4);
|
||||
GnuDebuglinkSection {
|
||||
hdr,
|
||||
filename: String::new(),
|
||||
crc32: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Layout> Default for GnuDebuglinkSection<E> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_shdr<E: Arch>(ctx: &mut Context<E>) {
|
||||
let filename = path_filename(&ctx.args.separate_debug_file);
|
||||
let sec = ctx.gnu_debuglink.as_mut().unwrap();
|
||||
sec.hdr
|
||||
.shdr
|
||||
.sh_size
|
||||
.set(align_to(filename.len() as u64 + 1, 4) + 4);
|
||||
sec.filename = filename;
|
||||
}
|
||||
|
||||
pub fn copy_buf<E: Arch>(ctx: &Context<E>, buf: &mut [u8]) {
|
||||
let sec = ctx.gnu_debuglink.as_ref().unwrap();
|
||||
buf.fill(0);
|
||||
write_cstr(buf, sec.filename.as_bytes());
|
||||
let n = buf.len();
|
||||
E::Endian::write_u32(&mut buf[n - 4..], sec.crc32);
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
//! `.gnu.hash`, the GNU hash table for dynamic symbol lookup.
|
||||
|
||||
use crate::arch::Arch;
|
||||
use crate::context::Context;
|
||||
use crate::elf::*;
|
||||
use crate::output_chunks::ChunkHeader;
|
||||
use crate::symbol::SymbolId;
|
||||
|
||||
/// The hash function for `.gnu.hash`.
|
||||
// The hash function for .gnu.hash.
|
||||
pub fn djb_hash(name: &[u8]) -> u32 {
|
||||
let mut h: u32 = 5381;
|
||||
for &c in name {
|
||||
h = (h << 5).wrapping_add(h).wrapping_add(c as u32);
|
||||
}
|
||||
h
|
||||
}
|
||||
|
||||
// .gnu.hash is an alternative format for .hash. It contains not only an
|
||||
// on-disk hash table but also contains a bloom filter to quickly identify
|
||||
// whether or not a given symbol name exists in .dynsym.
|
||||
#[derive(Debug)]
|
||||
pub struct GnuHashSection<E: Layout> {
|
||||
pub hdr: ChunkHeader<E>,
|
||||
pub num_buckets: u32,
|
||||
pub num_bloom: u32,
|
||||
pub num_exported: u32,
|
||||
}
|
||||
|
||||
impl<E: Arch> GnuHashSection<E> {
|
||||
pub const LOAD_FACTOR: u32 = 8;
|
||||
pub const HEADER_SIZE: u64 = 16;
|
||||
pub const BLOOM_SHIFT: u32 = 26;
|
||||
|
||||
pub fn new() -> GnuHashSection<E> {
|
||||
let mut hdr = ChunkHeader::<E>::new(".gnu.hash", SHT_GNU_HASH, SHF_ALLOC as u64);
|
||||
hdr.shdr.sh_addralign.set(E::WORD_SIZE as u64);
|
||||
GnuHashSection {
|
||||
hdr,
|
||||
num_buckets: 0,
|
||||
num_bloom: 1,
|
||||
num_exported: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Arch> Default for GnuHashSection<E> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_shdr<E: Arch>(ctx: &mut Context<E>) {
|
||||
if ctx.dynsym.symbols.is_empty() {
|
||||
return;
|
||||
}
|
||||
let word = E::WORD_SIZE as u64;
|
||||
let gh = ctx.gnu_hash.as_mut().unwrap();
|
||||
// We allocate 12 bits for each symbol in the bloom filter.
|
||||
gh.num_bloom = ((gh.num_exported as u64 * 12) / (word * 8))
|
||||
.max(1)
|
||||
.next_power_of_two() as u32;
|
||||
gh.hdr.shdr.sh_size.set(
|
||||
GnuHashSection::<E>::HEADER_SIZE
|
||||
+ gh.num_bloom as u64 * word // Bloom filter
|
||||
+ gh.num_buckets as u64 * 4 // Hash buckets
|
||||
+ gh.num_exported as u64 * 4, // Hash values
|
||||
);
|
||||
gh.hdr.shdr.sh_link.set(ctx.dynsym.hdr.shndx);
|
||||
}
|
||||
|
||||
pub fn copy_buf<E: Arch>(ctx: &Context<E>, buf: &mut [u8]) {
|
||||
buf.fill(0);
|
||||
let gh = ctx.gnu_hash.as_ref().unwrap();
|
||||
let word = E::WORD_SIZE;
|
||||
let first_exported = ctx.dynsym.symbols.len() - gh.num_exported as usize;
|
||||
|
||||
E::Endian::write_u32(buf, gh.num_buckets);
|
||||
E::Endian::write_u32(&mut buf[4..], first_exported as u32);
|
||||
E::Endian::write_u32(&mut buf[8..], gh.num_bloom);
|
||||
E::Endian::write_u32(&mut buf[12..], GnuHashSection::<E>::BLOOM_SHIFT);
|
||||
|
||||
let syms: Vec<SymbolId> = ctx.dynsym.symbols[first_exported..]
|
||||
.iter()
|
||||
.flatten()
|
||||
.copied()
|
||||
.collect();
|
||||
if syms.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Write a bloom filter
|
||||
let bloom_off = GnuHashSection::<E>::HEADER_SIZE as usize;
|
||||
let word_bits = word * 8;
|
||||
let mut indices = Vec::with_capacity(syms.len());
|
||||
for &id in &syms {
|
||||
let h = ctx.symbols[id].aux(&ctx.symbols).unwrap().djb_hash;
|
||||
indices.push(h % gh.num_buckets);
|
||||
let idx = (h as usize / word_bits) % gh.num_bloom as usize;
|
||||
let bits = (1u64 << (h as usize % word_bits))
|
||||
| (1u64 << ((h >> GnuHashSection::<E>::BLOOM_SHIFT) as usize % word_bits));
|
||||
let slot = &mut buf[bloom_off + idx * word..];
|
||||
if E::IS_64 {
|
||||
E::Endian::write_u64(slot, E::Endian::read_u64(slot) | bits);
|
||||
} else {
|
||||
E::Endian::write_u32(slot, E::Endian::read_u32(slot) | bits as u32);
|
||||
}
|
||||
}
|
||||
|
||||
// Write hash bucket indices
|
||||
let buckets_off = bloom_off + gh.num_bloom as usize * word;
|
||||
for (i, &bucket) in indices.iter().enumerate().rev() {
|
||||
E::Endian::write_u32(
|
||||
&mut buf[buckets_off + bucket as usize * 4..],
|
||||
(first_exported + i) as u32,
|
||||
);
|
||||
}
|
||||
|
||||
// Write a hash table
|
||||
let table_off = buckets_off + gh.num_buckets as usize * 4;
|
||||
for (i, &id) in syms.iter().enumerate() {
|
||||
// The last entry in a chain must be terminated with an entry with
|
||||
// least-significant bit 1.
|
||||
let h = ctx.symbols[id].aux(&ctx.symbols).unwrap().djb_hash;
|
||||
let last = i + 1 == syms.len() || indices[i] != indices[i + 1];
|
||||
E::Endian::write_u32(
|
||||
&mut buf[table_off + i * 4..],
|
||||
if last { h | 1 } else { h & !1 },
|
||||
);
|
||||
}
|
||||
}
|
||||
+377
-783
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,75 @@
|
||||
//! `.got.plt`, function pointers used by the PLT.
|
||||
|
||||
use crate::arch::{Arch, Family};
|
||||
use crate::context::Context;
|
||||
use crate::elf::*;
|
||||
use crate::output_chunks::ChunkHeader;
|
||||
|
||||
// .got.plt is similar to .got in the sense that it is a table containing
|
||||
// pointers. The contents in .got.plt are function pointers used by .plt.
|
||||
#[derive(Debug)]
|
||||
pub struct GotPltSection<E: Layout> {
|
||||
pub hdr: ChunkHeader<E>,
|
||||
}
|
||||
|
||||
impl<E: Arch> GotPltSection<E> {
|
||||
pub fn new(args: &crate::cmdline::Args) -> GotPltSection<E> {
|
||||
let sh_type = if E::IS_PPC64 {
|
||||
SHT_NOBITS
|
||||
} else {
|
||||
SHT_PROGBITS
|
||||
};
|
||||
let mut hdr = ChunkHeader::<E>::new(".got.plt", sh_type, (SHF_ALLOC | SHF_WRITE) as u64);
|
||||
hdr.is_relro = args.z_now;
|
||||
hdr.shdr.sh_addralign.set(E::WORD_SIZE as u64);
|
||||
hdr.shdr.sh_size.set(header_size::<E>());
|
||||
GotPltSection { hdr }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn header_size<E: Arch>() -> u64 {
|
||||
let words = if E::FAMILY == Family::Ppc64V2 { 2 } else { 3 };
|
||||
words * E::WORD_SIZE as u64
|
||||
}
|
||||
|
||||
pub fn entry_size<E: Arch>() -> u64 {
|
||||
let words = if E::FAMILY == Family::Ppc64V1 { 3 } else { 1 };
|
||||
words * E::WORD_SIZE as u64
|
||||
}
|
||||
|
||||
pub fn update_shdr<E: Arch>(ctx: &mut Context<E>) {
|
||||
ctx.gotplt
|
||||
.hdr
|
||||
.shdr
|
||||
.sh_size
|
||||
.set(header_size::<E>() + ctx.plt.symbols.len() as u64 * entry_size::<E>());
|
||||
}
|
||||
|
||||
pub fn copy_buf<E: Arch>(ctx: &Context<E>, buf: &mut [u8]) {
|
||||
// On PPC64, it's dynamic loader responsibility to fill the .got.plt
|
||||
// section. Dynamic loader finds the address of the first PLT entry by
|
||||
// DT_PPC64_GLINK and assumes that each PLT entry is 4 bytes long.
|
||||
if E::IS_PPC64 {
|
||||
return;
|
||||
}
|
||||
let w = E::WORD_SIZE;
|
||||
let write = |buf: &mut [u8], idx: usize, val: u64| {
|
||||
if E::IS_64 {
|
||||
E::Endian::write_u64(&mut buf[idx * w..], val);
|
||||
} else {
|
||||
E::Endian::write_u32(&mut buf[idx * w..], val as u32);
|
||||
}
|
||||
};
|
||||
// The first slot of .got.plt points to _DYNAMIC, as requested by
|
||||
// the psABI. The second and the third slots are reserved by the psABI.
|
||||
write(
|
||||
buf,
|
||||
0,
|
||||
ctx.dynamic.as_ref().map_or(0, |d| d.hdr.shdr.sh_addr.get()),
|
||||
);
|
||||
write(buf, 1, 0);
|
||||
write(buf, 2, 0);
|
||||
for i in 0..ctx.plt.symbols.len() {
|
||||
write(buf, i + 3, ctx.plt.hdr.shdr.sh_addr.get());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
//! `.hash`, the ELF hash table for dynamic symbol lookup.
|
||||
|
||||
use crate::arch::{Arch, Family};
|
||||
use crate::context::Context;
|
||||
use crate::elf::*;
|
||||
use crate::output_chunks::ChunkHeader;
|
||||
|
||||
// The hash function for .hash.
|
||||
pub fn elf_hash(name: &[u8]) -> u32 {
|
||||
let mut h: u32 = 0;
|
||||
for &c in name {
|
||||
h = (h << 4).wrapping_add(c as u32);
|
||||
let g = h & 0xf000_0000;
|
||||
if g != 0 {
|
||||
h ^= g >> 24;
|
||||
}
|
||||
h &= !g;
|
||||
}
|
||||
h
|
||||
}
|
||||
|
||||
// .hash contains an on-disk hash table for .dynsym so that the runtime
|
||||
// can look up a symbol name quickly without scannin all entries in
|
||||
// .dynsym.
|
||||
//
|
||||
// Quickly identifying whether or not a .dynsym contains a given symbol is
|
||||
// especially important for ELF because of the dynamic symbol lookup rule
|
||||
// for ELF. In ELF, each dynamic symbol is not searched from a specific
|
||||
// library but from all the ELF files loaded to memory. Therefore,
|
||||
// minimizing the cost of each dynamic symbol lookup is important.
|
||||
#[derive(Debug)]
|
||||
pub struct HashSection<E: Layout> {
|
||||
pub hdr: ChunkHeader<E>,
|
||||
}
|
||||
|
||||
impl<E: Arch> HashSection<E> {
|
||||
pub fn new() -> HashSection<E> {
|
||||
let mut hdr = ChunkHeader::<E>::new(".hash", SHT_HASH, SHF_ALLOC as u64);
|
||||
// Even though u32 should suffice as an etnry size for all targets,
|
||||
// s390x uses u64. It looks like a spec bug, but we need to follow
|
||||
// suit for the sake of binary compatibility.
|
||||
let entry = entry_size::<E>() as u64;
|
||||
hdr.shdr.sh_entsize.set(entry);
|
||||
hdr.shdr.sh_addralign.set(entry);
|
||||
HashSection { hdr }
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Arch> Default for HashSection<E> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn entry_size<E: Arch>() -> usize {
|
||||
if E::FAMILY == Family::S390x {
|
||||
8
|
||||
} else {
|
||||
4
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_shdr<E: Arch>(ctx: &mut Context<E>) {
|
||||
if ctx.dynsym.symbols.is_empty() {
|
||||
return;
|
||||
}
|
||||
let entry = entry_size::<E>() as u64;
|
||||
let num_slots = ctx.dynsym.symbols.len() as u64;
|
||||
let hash = ctx.hash.as_mut().unwrap();
|
||||
hash.hdr.shdr.sh_size.set(entry * 2 + num_slots * entry * 2);
|
||||
hash.hdr.shdr.sh_link.set(ctx.dynsym.hdr.shndx);
|
||||
}
|
||||
|
||||
pub fn copy_buf<E: Arch>(ctx: &Context<E>, buf: &mut [u8]) {
|
||||
buf.fill(0);
|
||||
let entry = entry_size::<E>();
|
||||
let write = |buf: &mut [u8], i: usize, v: u32| {
|
||||
if entry == 8 {
|
||||
E::Endian::write_u64(&mut buf[i * 8..], v as u64);
|
||||
} else {
|
||||
E::Endian::write_u32(&mut buf[i * 4..], v);
|
||||
}
|
||||
};
|
||||
let read = |buf: &[u8], i: usize| -> u32 {
|
||||
if entry == 8 {
|
||||
E::Endian::read_u64(&buf[i * 8..]) as u32
|
||||
} else {
|
||||
E::Endian::read_u32(&buf[i * 4..])
|
||||
}
|
||||
};
|
||||
|
||||
let n = ctx.dynsym.symbols.len();
|
||||
write(buf, 0, n as u32);
|
||||
write(buf, 1, n as u32);
|
||||
let buckets = 2;
|
||||
let chains = 2 + n;
|
||||
|
||||
for &id in ctx.dynsym.symbols.iter().skip(1).flatten() {
|
||||
let sym = &ctx.symbols[id];
|
||||
let i = sym.dynsym_idx(&ctx.symbols).unwrap() as usize;
|
||||
let h = elf_hash(sym.name()) as usize % n;
|
||||
let head = read(buf, buckets + h);
|
||||
write(buf, chains + i, head);
|
||||
write(buf, buckets + h, i as u32);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
//! `.interp`, the dynamic linker's pathname.
|
||||
|
||||
use crate::arch::Arch;
|
||||
use crate::context::Context;
|
||||
use crate::elf::*;
|
||||
use crate::output_chunks::ChunkHeader;
|
||||
use crate::util::write_cstr;
|
||||
|
||||
// .interp contains the pathname of a dynamic linker. Dynamically-linked
|
||||
// executables have the section. If exists, the kernel runs the program at
|
||||
// the specified path with the executable pathname as an argument,
|
||||
// allowing the dynamic linker to run the program.
|
||||
#[derive(Debug)]
|
||||
pub struct InterpSection<E: Layout> {
|
||||
pub hdr: ChunkHeader<E>,
|
||||
}
|
||||
|
||||
impl<E: Layout> InterpSection<E> {
|
||||
pub fn new() -> InterpSection<E> {
|
||||
InterpSection {
|
||||
hdr: ChunkHeader::<E>::new(".interp", SHT_PROGBITS, SHF_ALLOC as u64),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Layout> Default for InterpSection<E> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_shdr<E: Arch>(ctx: &mut Context<E>) {
|
||||
let size = ctx.args.dynamic_linker.len() as u64 + 1;
|
||||
ctx.interp.as_mut().unwrap().hdr.shdr.sh_size.set(size);
|
||||
}
|
||||
|
||||
pub fn copy_buf<E: Arch>(ctx: &Context<E>, buf: &mut [u8]) {
|
||||
write_cstr(buf, ctx.args.dynamic_linker.as_bytes());
|
||||
}
|
||||
@@ -1,805 +0,0 @@
|
||||
//! Small synthesized sections.
|
||||
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
|
||||
use crate::arch::Arch;
|
||||
use crate::context::Context;
|
||||
use crate::elf::*;
|
||||
use crate::error;
|
||||
use crate::input_files::FileId;
|
||||
use crate::input_sections::{r_delta, InputSection};
|
||||
use crate::output_chunks::{self, ChunkHeader, ChunkId, OutputSectionId};
|
||||
use crate::symbol::{OriginValue, SymbolId};
|
||||
use crate::util::align_to;
|
||||
use crate::util::compress::Compressor;
|
||||
use crate::util::{path_filename, write_cstr};
|
||||
use bstr::BStr;
|
||||
|
||||
// .interp contains the pathname of a dynamic linker. Dynamically-linked
|
||||
// executables have the section. If exists, the kernel runs the program at
|
||||
// the specified path with the executable pathname as an argument,
|
||||
// allowing the dynamic linker to run the program.
|
||||
#[derive(Debug)]
|
||||
pub struct InterpSection<E: Layout> {
|
||||
pub hdr: ChunkHeader<E>,
|
||||
}
|
||||
|
||||
impl<E: Layout> InterpSection<E> {
|
||||
pub fn new() -> InterpSection<E> {
|
||||
InterpSection {
|
||||
hdr: ChunkHeader::<E>::new(".interp", SHT_PROGBITS, SHF_ALLOC as u64),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Layout> Default for InterpSection<E> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub mod interp {
|
||||
use super::*;
|
||||
|
||||
pub fn update_shdr<E: Arch>(ctx: &mut Context<E>) {
|
||||
let size = ctx.args.dynamic_linker.len() as u64 + 1;
|
||||
ctx.interp.as_mut().unwrap().hdr.shdr.sh_size.set(size);
|
||||
}
|
||||
|
||||
pub fn copy_buf<E: Arch>(ctx: &Context<E>, buf: &mut [u8]) {
|
||||
write_cstr(buf, ctx.args.dynamic_linker.as_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
// .copyrel and .copyrel.rel.ro represent memory regions to which the
|
||||
// runtime copies symbols from other ELF files for copy relocations.
|
||||
#[derive(Debug)]
|
||||
pub struct CopyrelSection<E: Layout> {
|
||||
pub hdr: ChunkHeader<E>,
|
||||
pub symbols: Vec<SymbolId>,
|
||||
}
|
||||
|
||||
impl<E: Layout> CopyrelSection<E> {
|
||||
pub fn new(is_relro: bool) -> CopyrelSection<E> {
|
||||
let name = if is_relro {
|
||||
".copyrel.rel.ro"
|
||||
} else {
|
||||
".copyrel"
|
||||
};
|
||||
let mut hdr = ChunkHeader::<E>::new(name, SHT_NOBITS, (SHF_ALLOC | SHF_WRITE) as u64);
|
||||
hdr.is_relro = is_relro;
|
||||
CopyrelSection {
|
||||
hdr,
|
||||
symbols: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod copyrel {
|
||||
use super::*;
|
||||
|
||||
pub fn add_symbol<E: Arch>(ctx: &mut Context<E>, relro: bool, id: SymbolId) {
|
||||
debug_assert!(!ctx.args.shared);
|
||||
let sym = &ctx.symbols[id];
|
||||
if sym.has_copyrel() {
|
||||
return;
|
||||
}
|
||||
let Some(FileId::Dso(dso_id)) = sym.file() else {
|
||||
debug_assert!(sym.is_undef_weak());
|
||||
let file = ctx.file_display(sym.file().unwrap());
|
||||
error!(
|
||||
"{file}: cannot create a copy relocation for {sym}; recompile with -fPIE or -fPIC"
|
||||
);
|
||||
return;
|
||||
};
|
||||
let dso = &ctx.dsos[dso_id.index()];
|
||||
if sym.esym(ctx).st_visibility() == STV_PROTECTED {
|
||||
error!("{dso}: cannot create a copy relocation for protected symbol '{sym}'; recompile with -fPIC"
|
||||
);
|
||||
return;
|
||||
}
|
||||
if !ctx.args.z_copyreloc {
|
||||
error!("-z nocopyreloc: {dso}: cannot create a copy relocation for symbol '{sym}'; recompile with -fPIC"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let alignment = dso.alignment(sym);
|
||||
let size = sym.esym(ctx).st_size().get();
|
||||
// We need to create dynamic symbols not only for this particular symbol
|
||||
// but also for its aliases (i.e. other symbols at the same address)
|
||||
// becasue otherwise the aliases are broken apart at runtime.
|
||||
// For example, `environ`, `_environ` and `__environ` in libc.so are
|
||||
// aliases. If one of the symbols is copied by a copy relocation, other
|
||||
// symbols have to refer to the copied place as well.
|
||||
let aliases: Vec<SymbolId> = dso.symbols_at(ctx, sym, dso_id).to_vec();
|
||||
|
||||
let sec = if relro {
|
||||
&mut ctx.copyrel_relro
|
||||
} else {
|
||||
&mut ctx.copyrel
|
||||
};
|
||||
sec.symbols.push(id);
|
||||
let offset = align_to(sec.hdr.shdr.sh_size.get(), alignment);
|
||||
sec.hdr.shdr.sh_size.set(offset + size);
|
||||
sec.hdr
|
||||
.shdr
|
||||
.sh_addralign
|
||||
.set(sec.hdr.shdr.sh_addralign.get().max(alignment));
|
||||
|
||||
for alias in aliases {
|
||||
ctx.symbols.aux_mut(alias);
|
||||
let s = &mut ctx.symbols[alias];
|
||||
s.set_imported(true);
|
||||
s.set_exported(true);
|
||||
s.set_copyrel(true);
|
||||
s.set_copyrel_readonly(relro);
|
||||
s.value = offset;
|
||||
output_chunks::symtab::dynsym::add_symbol(ctx, alias);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_dynrels<E: Arch>(ctx: &Context<E>, sec: &CopyrelSection<E>, out: &mut [E::Rel]) {
|
||||
for (i, &id) in sec.symbols.iter().enumerate() {
|
||||
let sym = &ctx.symbols[id];
|
||||
out[i] = ElfRel::<E>::new(
|
||||
sym.addr(ctx),
|
||||
E::R_COPY,
|
||||
sym.dynsym_idx(&ctx.symbols).unwrap_or(0),
|
||||
0,
|
||||
);
|
||||
}
|
||||
debug_assert_eq!(sec.symbols.len(), out.len());
|
||||
}
|
||||
}
|
||||
|
||||
// .note.gnu.build-id contains an identifier for an output ELF file. The
|
||||
// contents of the section is usually a cryptogrpahic hash of the output
|
||||
// file itself to guarantee uniqueness of build-id.
|
||||
#[derive(Debug)]
|
||||
pub struct BuildIdSection<E: Layout> {
|
||||
pub hdr: ChunkHeader<E>,
|
||||
pub contents: Vec<u8>,
|
||||
}
|
||||
|
||||
impl<E: Layout> BuildIdSection<E> {
|
||||
pub fn new() -> BuildIdSection<E> {
|
||||
let mut hdr = ChunkHeader::<E>::new(".note.gnu.build-id", SHT_NOTE, SHF_ALLOC as u64);
|
||||
hdr.shdr.sh_addralign.set(4);
|
||||
hdr.shdr.sh_size.set(1);
|
||||
BuildIdSection {
|
||||
hdr,
|
||||
contents: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Layout> Default for BuildIdSection<E> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub mod build_id {
|
||||
use super::*;
|
||||
|
||||
pub fn update_shdr<E: Arch>(ctx: &mut Context<E>) {
|
||||
let size = ctx.args.build_id.size() as u64 + 16; // +16 for the header
|
||||
ctx.buildid.as_mut().unwrap().hdr.shdr.sh_size.set(size);
|
||||
}
|
||||
|
||||
pub fn copy_buf<E: Arch>(ctx: &Context<E>, buf: &mut [u8]) {
|
||||
let sec = ctx.buildid.as_ref().unwrap();
|
||||
buf.fill(0);
|
||||
E::Endian::write_u32(buf, 4); // Name size
|
||||
E::Endian::write_u32(&mut buf[4..], ctx.args.build_id.size() as u32); // Hash size
|
||||
E::Endian::write_u32(&mut buf[8..], NT_GNU_BUILD_ID);
|
||||
buf[12..16].copy_from_slice(b"GNU\0"); // Name string
|
||||
buf[16..16 + sec.contents.len()].copy_from_slice(&sec.contents); // Build ID
|
||||
}
|
||||
}
|
||||
|
||||
// .note.package is an optional hint section that can contain arbitrary
|
||||
// string. Package managers, such as dpkg or rpm, uses the section to
|
||||
// embed package metadata into each ELF file so that it is easy to find
|
||||
// the origin of an ELF file without any additional information.
|
||||
#[derive(Debug)]
|
||||
pub struct NotePackageSection<E: Layout> {
|
||||
pub hdr: ChunkHeader<E>,
|
||||
}
|
||||
|
||||
impl<E: Layout> NotePackageSection<E> {
|
||||
pub fn new() -> NotePackageSection<E> {
|
||||
let mut hdr = ChunkHeader::<E>::new(".note.package", SHT_NOTE, SHF_ALLOC as u64);
|
||||
hdr.shdr.sh_addralign.set(4);
|
||||
NotePackageSection { hdr }
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Layout> Default for NotePackageSection<E> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub mod note_package {
|
||||
use super::*;
|
||||
|
||||
pub fn update_shdr<E: Arch>(ctx: &mut Context<E>) {
|
||||
if !ctx.args.package_metadata.is_empty() {
|
||||
// +17 is for the header and the NUL terminator
|
||||
ctx.note_package
|
||||
.hdr
|
||||
.shdr
|
||||
.sh_size
|
||||
.set(align_to(ctx.args.package_metadata.len() as u64 + 17, 4));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn copy_buf<E: Arch>(ctx: &Context<E>, buf: &mut [u8]) {
|
||||
buf.fill(0);
|
||||
E::Endian::write_u32(buf, 4); // Name size
|
||||
E::Endian::write_u32(
|
||||
&mut buf[4..],
|
||||
ctx.note_package.hdr.shdr.sh_size.get() as u32 - 16,
|
||||
); // Content size
|
||||
E::Endian::write_u32(&mut buf[8..], NT_FDO_PACKAGING_METADATA);
|
||||
buf[12..16].copy_from_slice(b"FDO\0");
|
||||
write_cstr(&mut buf[16..], ctx.args.package_metadata.as_bytes()); // Content
|
||||
}
|
||||
}
|
||||
|
||||
// .note.gnu.property section contains an additional runtime information
|
||||
// about ISA variant.
|
||||
#[derive(Debug)]
|
||||
pub struct NotePropertySection<E: Layout> {
|
||||
pub hdr: ChunkHeader<E>,
|
||||
pub contents: Vec<(u32, u32)>,
|
||||
}
|
||||
|
||||
impl<E: Arch> NotePropertySection<E> {
|
||||
pub fn new() -> NotePropertySection<E> {
|
||||
let mut hdr = ChunkHeader::<E>::new(".note.gnu.property", SHT_NOTE, SHF_ALLOC as u64);
|
||||
hdr.shdr.sh_addralign.set(E::WORD_SIZE as u64);
|
||||
NotePropertySection {
|
||||
hdr,
|
||||
contents: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Arch> Default for NotePropertySection<E> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub mod note_property {
|
||||
use super::*;
|
||||
|
||||
fn entry_size<E: Arch>() -> usize {
|
||||
if E::IS_64 {
|
||||
16
|
||||
} else {
|
||||
12
|
||||
}
|
||||
}
|
||||
|
||||
// Merges input files' .note.gnu.property values.
|
||||
pub fn update_shdr<E: Arch>(ctx: &mut Context<E>) {
|
||||
// Obtain the list of keys
|
||||
let files: Vec<&crate::input_files::ObjectFile<E>> = ctx
|
||||
.objs
|
||||
.iter()
|
||||
.filter(|file| !ctx.is_internal(file.id()))
|
||||
.collect();
|
||||
let keys: BTreeSet<u32> = files
|
||||
.iter()
|
||||
.flat_map(|f| f.gnu_properties.keys().copied())
|
||||
.collect();
|
||||
let value = |f: &crate::input_files::ObjectFile<E>, key: u32| {
|
||||
f.gnu_properties.get(&key).copied().unwrap_or(0)
|
||||
};
|
||||
|
||||
// Merge values for each key
|
||||
let mut map: BTreeMap<u32, u32> = BTreeMap::new();
|
||||
for key in keys {
|
||||
if (GNU_PROPERTY_X86_UINT32_AND_LO..=GNU_PROPERTY_X86_UINT32_AND_HI).contains(&key) {
|
||||
// An AND feature is set if all input objects have the property and
|
||||
// the feature.
|
||||
map.insert(
|
||||
key,
|
||||
files.iter().fold(u32::MAX, |acc, f| acc & value(f, key)),
|
||||
);
|
||||
} else if (GNU_PROPERTY_X86_UINT32_OR_LO..=GNU_PROPERTY_X86_UINT32_OR_HI).contains(&key)
|
||||
{
|
||||
// An OR feature is set if some input object has the feature.
|
||||
map.insert(key, files.iter().fold(0, |acc, f| acc | value(f, key)));
|
||||
} else if (GNU_PROPERTY_X86_UINT32_OR_AND_LO..=GNU_PROPERTY_X86_UINT32_OR_AND_HI)
|
||||
.contains(&key)
|
||||
{
|
||||
// An OR-AND feature is set if all input object files have the property
|
||||
// and some of them has the feature.
|
||||
if files.iter().all(|f| f.gnu_properties.contains_key(&key)) {
|
||||
map.insert(key, files.iter().fold(0, |acc, f| acc | value(f, key)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ctx.args.z_ibt {
|
||||
*map.entry(GNU_PROPERTY_X86_FEATURE_1_AND).or_insert(0) |=
|
||||
GNU_PROPERTY_X86_FEATURE_1_IBT;
|
||||
}
|
||||
if ctx.args.z_shstk {
|
||||
*map.entry(GNU_PROPERTY_X86_FEATURE_1_AND).or_insert(0) |=
|
||||
GNU_PROPERTY_X86_FEATURE_1_SHSTK;
|
||||
}
|
||||
*map.entry(GNU_PROPERTY_X86_ISA_1_NEEDED).or_insert(0) |= ctx.args.z_x86_64_isa_level;
|
||||
|
||||
// Serialize the map
|
||||
let contents: Vec<(u32, u32)> = map.into_iter().filter(|&(_, v)| v != 0).collect();
|
||||
let sec = ctx.note_property.as_mut().unwrap();
|
||||
sec.hdr.shdr.sh_size.set(if contents.is_empty() {
|
||||
0
|
||||
} else {
|
||||
(16 + contents.len() * entry_size::<E>()) as u64
|
||||
});
|
||||
sec.contents = contents;
|
||||
}
|
||||
|
||||
pub fn copy_buf<E: Arch>(ctx: &Context<E>, buf: &mut [u8]) {
|
||||
let sec = ctx.note_property.as_ref().unwrap();
|
||||
buf.fill(0);
|
||||
E::Endian::write_u32(buf, 4); // Name size
|
||||
E::Endian::write_u32(&mut buf[4..], sec.hdr.shdr.sh_size.get() as u32 - 16); // Content size
|
||||
E::Endian::write_u32(&mut buf[8..], NT_GNU_PROPERTY_TYPE_0);
|
||||
buf[12..16].copy_from_slice(b"GNU\0");
|
||||
for (i, &(ty, val)) in sec.contents.iter().enumerate() {
|
||||
let off = 16 + i * entry_size::<E>();
|
||||
E::Endian::write_u32(&mut buf[off..], ty);
|
||||
E::Endian::write_u32(&mut buf[off + 4..], 4);
|
||||
E::Endian::write_u32(&mut buf[off + 8..], val); // Content
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// .gnu_debuglink section contains a pathname and its CRC32 checksum for a
|
||||
// separate debug info file. gdb can read the section to read debug info
|
||||
// from an external file.
|
||||
#[derive(Debug)]
|
||||
pub struct GnuDebuglinkSection<E: Layout> {
|
||||
pub hdr: ChunkHeader<E>,
|
||||
pub filename: String,
|
||||
pub crc32: u32,
|
||||
}
|
||||
|
||||
impl<E: Layout> GnuDebuglinkSection<E> {
|
||||
pub fn new() -> GnuDebuglinkSection<E> {
|
||||
let mut hdr = ChunkHeader::<E>::new(".gnu_debuglink", SHT_PROGBITS, 0);
|
||||
hdr.shdr.sh_addralign.set(4);
|
||||
GnuDebuglinkSection {
|
||||
hdr,
|
||||
filename: String::new(),
|
||||
crc32: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Layout> Default for GnuDebuglinkSection<E> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub mod gnu_debuglink {
|
||||
use super::*;
|
||||
|
||||
pub fn update_shdr<E: Arch>(ctx: &mut Context<E>) {
|
||||
let filename = path_filename(&ctx.args.separate_debug_file);
|
||||
let sec = ctx.gnu_debuglink.as_mut().unwrap();
|
||||
sec.hdr
|
||||
.shdr
|
||||
.sh_size
|
||||
.set(align_to(filename.len() as u64 + 1, 4) + 4);
|
||||
sec.filename = filename;
|
||||
}
|
||||
|
||||
pub fn copy_buf<E: Arch>(ctx: &Context<E>, buf: &mut [u8]) {
|
||||
let sec = ctx.gnu_debuglink.as_ref().unwrap();
|
||||
buf.fill(0);
|
||||
write_cstr(buf, sec.filename.as_bytes());
|
||||
let n = buf.len();
|
||||
E::Endian::write_u32(&mut buf[n - 4..], sec.crc32);
|
||||
}
|
||||
}
|
||||
|
||||
// PT_GNU_RELRO works on page granularity. We want to align its end to
|
||||
// a page boundary. We append this section at end of a segment so that
|
||||
// the segment always ends at a page boundary.
|
||||
#[derive(Debug)]
|
||||
pub struct RelroPaddingSection<E: Layout> {
|
||||
pub hdr: ChunkHeader<E>,
|
||||
}
|
||||
|
||||
impl<E: Layout> RelroPaddingSection<E> {
|
||||
pub fn new() -> RelroPaddingSection<E> {
|
||||
let mut hdr =
|
||||
ChunkHeader::<E>::new(".relro_padding", SHT_NOBITS, (SHF_ALLOC | SHF_WRITE) as u64);
|
||||
hdr.is_relro = true;
|
||||
hdr.shdr.sh_size.set(1);
|
||||
RelroPaddingSection { hdr }
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Layout> Default for RelroPaddingSection<E> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
// Debug sections can be compressed with zlib or zstd to reduce the
|
||||
// overall size of an ELF file. CompressedSection represents a compressed
|
||||
// section.
|
||||
#[derive(Debug)]
|
||||
pub struct CompressedSection<E: Layout> {
|
||||
pub hdr: ChunkHeader<E>,
|
||||
pub chdr: ElfChdr<E>,
|
||||
pub compressor: Compressor,
|
||||
/// Kept for --gdb-index, which reads the uncompressed contents.
|
||||
pub uncompressed_data: Option<Vec<u8>>,
|
||||
pub original: ChunkId,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Compressor {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "Compressor({} bytes)", self.compressed_size())
|
||||
}
|
||||
}
|
||||
|
||||
pub mod compressed {
|
||||
use super::*;
|
||||
|
||||
pub fn new<E: Arch>(ctx: &Context<E>, original: ChunkId) -> CompressedSection<E> {
|
||||
let hdr = ctx.chunk_header(original);
|
||||
|
||||
// C++ mold uses uninitialized storage here to avoid zero-filling a
|
||||
// potentially large scratch buffer. Rust currently uses a
|
||||
// zero-initialized Vec because write_to accepts &mut [u8].
|
||||
let mut buf = vec![0u8; hdr.shdr.sh_size.get() as usize];
|
||||
|
||||
// Write uncompressed contents and then compress them
|
||||
output_chunks::write_to(ctx, original, &mut buf);
|
||||
|
||||
let level = ctx.args.compress_debug_sections_level;
|
||||
let compressor = if ctx.args.compress_debug_sections == ELFCOMPRESS_ZLIB {
|
||||
Compressor::zlib(&buf, level as u32)
|
||||
} else {
|
||||
Compressor::zstd(&buf, level as i32)
|
||||
};
|
||||
|
||||
// Compute header field values
|
||||
let mut chdr = ElfChdr::<E>::default();
|
||||
chdr.ch_type_mut().set(ctx.args.compress_debug_sections);
|
||||
chdr.ch_size_mut().set(hdr.shdr.sh_size.get());
|
||||
chdr.ch_addralign_mut().set(hdr.shdr.sh_addralign.get());
|
||||
let mut new_hdr = ChunkHeader::<E>::with_name(
|
||||
hdr.name,
|
||||
hdr.shdr.sh_type.get(),
|
||||
hdr.shdr.sh_flags.get() | SHF_COMPRESSED as u64,
|
||||
);
|
||||
new_hdr.shndx = hdr.shndx;
|
||||
new_hdr.is_compressed = true;
|
||||
new_hdr.shdr = hdr.shdr;
|
||||
new_hdr
|
||||
.shdr
|
||||
.sh_flags
|
||||
.set(new_hdr.shdr.sh_flags.get() | SHF_COMPRESSED as u64);
|
||||
new_hdr.shdr.sh_addralign.set(1);
|
||||
new_hdr
|
||||
.shdr
|
||||
.sh_size
|
||||
.set((std::mem::size_of::<ElfChdr<E>>() + compressor.compressed_size()) as u64);
|
||||
|
||||
// We can discard the uncompressed contents unless --gdb-index is given
|
||||
CompressedSection {
|
||||
hdr: new_hdr,
|
||||
chdr,
|
||||
compressor,
|
||||
uncompressed_data: ctx.args.gdb_index.then_some(buf),
|
||||
original,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn copy_buf<E: Arch>(ctx: &Context<E>, i: u32, buf: &mut [u8]) {
|
||||
let sec = &ctx.compressed_sections[i as usize];
|
||||
sec.chdr.write(buf);
|
||||
sec.compressor
|
||||
.write_to(&mut buf[std::mem::size_of::<ElfChdr<E>>()..]);
|
||||
}
|
||||
}
|
||||
|
||||
// RelocSection represents a relocation table for an output file.
|
||||
// This is used only for the relocatable output (i.e. the `-r` output).
|
||||
#[derive(Debug)]
|
||||
pub struct RelocSection<E: Layout> {
|
||||
pub hdr: ChunkHeader<E>,
|
||||
pub output_section: OutputSectionId,
|
||||
/// The index of the first relocation of each member.
|
||||
offsets: Vec<u64>,
|
||||
}
|
||||
|
||||
pub mod reloc {
|
||||
use super::*;
|
||||
|
||||
pub fn new<E: Arch>(ctx: &Context<E>, osec_id: OutputSectionId) -> RelocSection<E> {
|
||||
let osec = &ctx.output_sections[osec_id.index()];
|
||||
let name = format!(
|
||||
"{}{}",
|
||||
if E::IS_RELA { ".rela" } else { ".rel" },
|
||||
osec.hdr.name
|
||||
);
|
||||
let name = BStr::new(crate::util::leak_bytes(name.into_bytes()));
|
||||
let mut hdr = ChunkHeader::<E>::with_name(
|
||||
name,
|
||||
if E::IS_RELA { SHT_RELA } else { SHT_REL },
|
||||
SHF_INFO_LINK as u64,
|
||||
);
|
||||
hdr.shdr.sh_addralign.set(E::WORD_SIZE as u64);
|
||||
hdr.shdr
|
||||
.sh_entsize
|
||||
.set(std::mem::size_of::<ElfRel<E>>() as u64);
|
||||
|
||||
// Compute an offset for each input section
|
||||
let mut offsets = Vec::with_capacity(osec.members.len());
|
||||
let mut sum = 0u64;
|
||||
for &m in &osec.members {
|
||||
offsets.push(sum);
|
||||
let isec = ctx.input_section(m);
|
||||
let file = &ctx.objs[isec.file.index()];
|
||||
sum += isec.rels(file).len() as u64;
|
||||
}
|
||||
hdr.shdr
|
||||
.sh_size
|
||||
.set(sum * std::mem::size_of::<ElfRel<E>>() as u64);
|
||||
RelocSection {
|
||||
hdr,
|
||||
output_section: osec_id,
|
||||
offsets,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_shdr<E: Arch>(ctx: &mut Context<E>, i: u32) {
|
||||
let symtab_shndx = ctx.symtab.hdr.shndx;
|
||||
let osec = ctx.reloc_sections[i as usize].output_section;
|
||||
let osec_shndx = ctx.output_sections[osec.index()].hdr.shndx;
|
||||
let sec = &mut ctx.reloc_sections[i as usize];
|
||||
sec.hdr.shdr.sh_link.set(symtab_shndx);
|
||||
sec.hdr.shdr.sh_info.set(osec_shndx);
|
||||
}
|
||||
|
||||
// Translates an input relocation's symbol reference into the {r_sym, addend}
|
||||
// pair that is valid in the output file. The returned r_sym is either an output
|
||||
// section index (for section-relative relocs) or an output symbol table index.
|
||||
fn symidx_addend<E: Arch>(
|
||||
ctx: &Context<E>,
|
||||
isec: &InputSection<E>,
|
||||
rel: &ElfRel<E>,
|
||||
) -> (u32, i64) {
|
||||
let file = &ctx.objs[isec.file.index()];
|
||||
let sym = &ctx.symbols[file.base.symbols[rel.r_sym() as usize]];
|
||||
|
||||
if !isec.is_alloc() {
|
||||
if let Some((frag, addend)) = isec.fragment(ctx, rel) {
|
||||
let msec = &ctx.merged_sections[frag.section.index()];
|
||||
return (
|
||||
msec.hdr.shndx,
|
||||
msec.fragments.get(frag.entry).offset() as i64 + addend,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if sym.st_type() == STT_SECTION {
|
||||
match sym.origin::<E>() {
|
||||
OriginValue::Fragment(frag) => {
|
||||
let msec = &ctx.merged_sections[frag.section.index()];
|
||||
return (
|
||||
msec.hdr.shndx,
|
||||
msec.fragments.get(frag.entry).offset() as i64
|
||||
+ sym.value as i64
|
||||
+ isec.rel_addend(rel),
|
||||
);
|
||||
}
|
||||
OriginValue::InputSection(section) => {
|
||||
let target = ctx.input_section(section);
|
||||
if let Some(osec) = target.output_section {
|
||||
return (
|
||||
ctx.output_section(osec).hdr.shndx,
|
||||
isec.rel_addend(rel) + target.offset() as i64,
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
// This is usually a dead debug section referring to a
|
||||
// COMDAT-eliminated section.
|
||||
return (0, 0);
|
||||
}
|
||||
|
||||
if sym.write_to_symtab() {
|
||||
return (sym.output_sym_idx(ctx), isec.rel_addend(rel));
|
||||
}
|
||||
(0, 0)
|
||||
}
|
||||
|
||||
/// Writes the relocations. With `-r` on a REL target, the addends are
|
||||
/// written into the output section's bytes, passed as `osec_buf`.
|
||||
pub fn copy_buf<E: Arch>(
|
||||
ctx: &Context<E>,
|
||||
i: u32,
|
||||
buf: &mut [u8],
|
||||
osec_buf: Option<&mut [u8]>,
|
||||
) {
|
||||
let sec = &ctx.reloc_sections[i as usize];
|
||||
let osec = &ctx.output_sections[sec.output_section.index()];
|
||||
let out = rels_from_bytes_mut::<E>(buf);
|
||||
let mut osec_buf = osec_buf;
|
||||
|
||||
for (mi, &m) in osec.members.iter().enumerate() {
|
||||
let isec = ctx.input_section(m);
|
||||
let file = &ctx.objs[isec.file.index()];
|
||||
let base = sec.offsets[mi] as usize;
|
||||
for (j, rel) in isec.rels(file).iter().enumerate() {
|
||||
let (symidx, addend) = symidx_addend(ctx, isec, rel);
|
||||
let mut r_offset = osec.hdr.shdr.sh_addr.get() + isec.offset() + rel.r_offset();
|
||||
if E::IS_RISCV || E::IS_LOONGARCH {
|
||||
// On RISC-V and LoongArch, relaxation may have deleted instructions,
|
||||
// shifting this relocation's offset.
|
||||
r_offset -= r_delta(isec, rel.r_offset()) as u64;
|
||||
}
|
||||
|
||||
// SH4 object files store addends in the relocated places rather
|
||||
// than in r_addend, and the relocation records we emit here are
|
||||
// meant to be consumed as if they were in an object file, so we
|
||||
// follow that convention.
|
||||
let out_addend = if E::FAMILY == crate::arch::Family::Sh4 {
|
||||
0
|
||||
} else {
|
||||
addend
|
||||
};
|
||||
out[base + j] = ElfRel::<E>::new(r_offset, rel.r_type(), symidx, out_addend);
|
||||
|
||||
if ctx.args.relocatable {
|
||||
if let Some(osec_buf) = osec_buf.as_deref_mut() {
|
||||
let loc = (isec.offset() + rel.r_offset()) as usize;
|
||||
E::write_addend(&mut osec_buf[loc..], addend, rel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ComdatGroupSection represents a comdat group for an output file.
|
||||
// This is used only for the relocatable output (i.e. the `-r` output).
|
||||
#[derive(Debug)]
|
||||
pub struct ComdatGroupSection<E: Layout> {
|
||||
pub hdr: ChunkHeader<E>,
|
||||
pub sym: SymbolId,
|
||||
pub members: Vec<ChunkId>,
|
||||
}
|
||||
|
||||
impl<E: Layout> ComdatGroupSection<E> {
|
||||
pub fn new(sym: SymbolId, members: Vec<ChunkId>) -> ComdatGroupSection<E> {
|
||||
let mut hdr = ChunkHeader::<E>::new(".group", SHT_GROUP, 0);
|
||||
hdr.shdr.sh_entsize.set(4);
|
||||
hdr.shdr.sh_addralign.set(4);
|
||||
hdr.shdr.sh_size.set((members.len() * 4 + 4) as u64);
|
||||
ComdatGroupSection { hdr, sym, members }
|
||||
}
|
||||
}
|
||||
|
||||
pub mod comdat_group {
|
||||
use super::*;
|
||||
|
||||
pub fn update_shdr<E: Arch>(ctx: &mut Context<E>, i: u32) {
|
||||
debug_assert!(ctx.args.relocatable);
|
||||
let sec = &ctx.comdat_group_sections[i as usize];
|
||||
let sym = &ctx.symbols[sec.sym];
|
||||
let sh_info = if sym.st_type() == STT_SECTION {
|
||||
let isec = sym.input_section_ref(ctx).unwrap();
|
||||
ctx.output_section(isec.output_section.unwrap()).hdr.shndx
|
||||
} else {
|
||||
sym.output_sym_idx(ctx)
|
||||
};
|
||||
let symtab_shndx = ctx.symtab.hdr.shndx;
|
||||
let sec = &mut ctx.comdat_group_sections[i as usize];
|
||||
sec.hdr.shdr.sh_link.set(symtab_shndx);
|
||||
sec.hdr.shdr.sh_info.set(sh_info);
|
||||
}
|
||||
|
||||
pub fn copy_buf<E: Arch>(ctx: &Context<E>, i: u32, buf: &mut [u8]) {
|
||||
let sec = &ctx.comdat_group_sections[i as usize];
|
||||
E::Endian::write_u32(buf, GRP_COMDAT);
|
||||
for (j, &member) in sec.members.iter().enumerate() {
|
||||
E::Endian::write_u32(&mut buf[4 + j * 4..], ctx.chunk_header(member).shndx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// `.riscv.attributes` describes the ISA the output requires, merged
|
||||
/// from the input files' attributes.
|
||||
#[derive(Debug)]
|
||||
pub struct RiscvAttributesSection<E: Layout> {
|
||||
pub hdr: ChunkHeader<E>,
|
||||
pub contents: Vec<u8>,
|
||||
}
|
||||
|
||||
impl<E: Layout> RiscvAttributesSection<E> {
|
||||
pub fn new() -> RiscvAttributesSection<E> {
|
||||
RiscvAttributesSection {
|
||||
hdr: ChunkHeader::<E>::new(".riscv.attributes", SHT_RISCV_ATTRIBUTES, 0),
|
||||
contents: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Layout> Default for RiscvAttributesSection<E> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub mod riscv_attributes {
|
||||
use super::*;
|
||||
|
||||
pub fn update_shdr<E: Arch>(ctx: &mut Context<E>) {
|
||||
if !ctx.riscv_attributes.as_ref().unwrap().contents.is_empty() {
|
||||
return;
|
||||
}
|
||||
let contents = crate::arch::riscv::attributes_contents(ctx);
|
||||
let sec = ctx.riscv_attributes.as_mut().unwrap();
|
||||
sec.hdr.shdr.sh_size.set(contents.len() as u64);
|
||||
sec.contents = contents;
|
||||
}
|
||||
|
||||
pub fn copy_buf<E: Arch>(ctx: &Context<E>, buf: &mut [u8]) {
|
||||
let sec = ctx.riscv_attributes.as_ref().unwrap();
|
||||
buf[..sec.contents.len()].copy_from_slice(&sec.contents);
|
||||
}
|
||||
}
|
||||
|
||||
/// `.save_restore_regs`, the register save and restore routines that GCC
|
||||
/// expects the linker to provide on PowerPC64 ELFv2.
|
||||
#[derive(Debug)]
|
||||
pub struct Ppc64SaveRestoreSection<E: Layout> {
|
||||
pub hdr: ChunkHeader<E>,
|
||||
}
|
||||
|
||||
impl<E: Layout> Ppc64SaveRestoreSection<E> {
|
||||
pub fn new() -> Ppc64SaveRestoreSection<E> {
|
||||
let mut hdr = ChunkHeader::<E>::new(
|
||||
".save_restore_regs",
|
||||
SHT_PROGBITS,
|
||||
(SHF_ALLOC | SHF_EXECINSTR) as u64,
|
||||
);
|
||||
hdr.shdr.sh_addralign.set(16);
|
||||
hdr.shdr
|
||||
.sh_size
|
||||
.set((crate::arch::ppc64v2::SAVE_RESTORE_INSNS.len() * 4) as u64);
|
||||
Ppc64SaveRestoreSection { hdr }
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Layout> Default for Ppc64SaveRestoreSection<E> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub mod ppc64_save_restore {
|
||||
use super::*;
|
||||
|
||||
pub fn copy_buf<E: Arch>(_ctx: &Context<E>, buf: &mut [u8]) {
|
||||
buf.copy_from_slice(&crate::arch::ppc64v2::save_restore_contents());
|
||||
}
|
||||
}
|
||||
+94
-65
@@ -7,16 +7,45 @@
|
||||
//! Operations common to all chunks dispatch on the id.
|
||||
|
||||
pub mod arm_exidx;
|
||||
pub mod build_id;
|
||||
pub mod comdat_group;
|
||||
pub mod compressed;
|
||||
pub mod copyrel;
|
||||
pub mod dynamic;
|
||||
pub mod dynstr;
|
||||
pub mod dynsym;
|
||||
pub mod eh_frame;
|
||||
pub mod eh_frame_hdr;
|
||||
pub mod eh_frame_reloc;
|
||||
pub mod gnu_debuglink;
|
||||
pub mod gnu_hash;
|
||||
pub mod got;
|
||||
pub mod gotplt;
|
||||
pub mod hash;
|
||||
pub mod interp;
|
||||
pub mod merged;
|
||||
pub mod misc;
|
||||
pub mod note_package;
|
||||
pub mod note_property;
|
||||
pub mod opd;
|
||||
pub mod output_section;
|
||||
pub mod plt;
|
||||
pub mod pltgot;
|
||||
pub mod ppc64_save_restore;
|
||||
pub mod reldyn;
|
||||
pub mod reloc;
|
||||
pub mod relplt;
|
||||
pub mod relrdyn;
|
||||
pub mod relro_padding;
|
||||
pub mod riscv_attributes;
|
||||
pub mod sframe;
|
||||
pub mod sframe_reloc;
|
||||
pub mod shstrtab;
|
||||
pub mod strtab;
|
||||
pub mod symtab;
|
||||
pub mod version;
|
||||
pub mod symtab_shndx;
|
||||
pub mod verdef;
|
||||
pub mod verneed;
|
||||
pub mod versym;
|
||||
|
||||
use std::num::NonZeroU32;
|
||||
|
||||
@@ -693,32 +722,32 @@ pub fn update_phdr<E: Arch>(ctx: &mut Context<E>) {
|
||||
pub fn update_shdr<E: Arch>(ctx: &mut Context<E>, id: ChunkId) {
|
||||
match id {
|
||||
ChunkId::Phdr => update_phdr(ctx),
|
||||
ChunkId::Interp => misc::interp::update_shdr(ctx),
|
||||
ChunkId::GotPlt => got::gotplt::update_shdr(ctx),
|
||||
ChunkId::RelPlt => got::relplt::update_shdr(ctx),
|
||||
ChunkId::RelDyn => dynamic::reldyn::update_shdr(ctx),
|
||||
ChunkId::Dynamic => dynamic::dynamic::update_shdr(ctx),
|
||||
ChunkId::Strtab => symtab::strtab::update_shdr(ctx),
|
||||
ChunkId::Shstrtab => symtab::shstrtab::update_shdr(ctx),
|
||||
ChunkId::Plt => got::plt::update_shdr(ctx),
|
||||
ChunkId::Symtab => symtab::symtab::update_shdr(ctx),
|
||||
ChunkId::Dynsym => symtab::dynsym::update_shdr(ctx),
|
||||
ChunkId::Hash => symtab::hash::update_shdr(ctx),
|
||||
ChunkId::GnuHash => symtab::gnu_hash::update_shdr(ctx),
|
||||
ChunkId::EhFrameHdr => eh_frame::eh_frame_hdr::update_shdr(ctx),
|
||||
ChunkId::EhFrameReloc => eh_frame::eh_frame_reloc::update_shdr(ctx),
|
||||
ChunkId::SFrameReloc => sframe::sframe_reloc::update_shdr(ctx),
|
||||
ChunkId::Versym => version::versym::update_shdr(ctx),
|
||||
ChunkId::Verneed => version::verneed::update_shdr(ctx),
|
||||
ChunkId::Verdef => version::verdef::update_shdr(ctx),
|
||||
ChunkId::BuildId => misc::build_id::update_shdr(ctx),
|
||||
ChunkId::NotePackage => misc::note_package::update_shdr(ctx),
|
||||
ChunkId::NoteProperty => misc::note_property::update_shdr(ctx),
|
||||
ChunkId::RiscvAttributes => misc::riscv_attributes::update_shdr(ctx),
|
||||
ChunkId::Interp => interp::update_shdr(ctx),
|
||||
ChunkId::GotPlt => gotplt::update_shdr(ctx),
|
||||
ChunkId::RelPlt => relplt::update_shdr(ctx),
|
||||
ChunkId::RelDyn => reldyn::update_shdr(ctx),
|
||||
ChunkId::Dynamic => dynamic::update_shdr(ctx),
|
||||
ChunkId::Strtab => strtab::update_shdr(ctx),
|
||||
ChunkId::Shstrtab => shstrtab::update_shdr(ctx),
|
||||
ChunkId::Plt => plt::update_shdr(ctx),
|
||||
ChunkId::Symtab => symtab::update_shdr(ctx),
|
||||
ChunkId::Dynsym => dynsym::update_shdr(ctx),
|
||||
ChunkId::Hash => hash::update_shdr(ctx),
|
||||
ChunkId::GnuHash => gnu_hash::update_shdr(ctx),
|
||||
ChunkId::EhFrameHdr => eh_frame_hdr::update_shdr(ctx),
|
||||
ChunkId::EhFrameReloc => eh_frame_reloc::update_shdr(ctx),
|
||||
ChunkId::SFrameReloc => sframe_reloc::update_shdr(ctx),
|
||||
ChunkId::Versym => versym::update_shdr(ctx),
|
||||
ChunkId::Verneed => verneed::update_shdr(ctx),
|
||||
ChunkId::Verdef => verdef::update_shdr(ctx),
|
||||
ChunkId::BuildId => build_id::update_shdr(ctx),
|
||||
ChunkId::NotePackage => note_package::update_shdr(ctx),
|
||||
ChunkId::NoteProperty => note_property::update_shdr(ctx),
|
||||
ChunkId::RiscvAttributes => riscv_attributes::update_shdr(ctx),
|
||||
ChunkId::ArmExidx => arm_exidx::update_shdr(ctx),
|
||||
ChunkId::GnuDebuglink => misc::gnu_debuglink::update_shdr(ctx),
|
||||
ChunkId::Reloc(i) => misc::reloc::update_shdr(ctx, i),
|
||||
ChunkId::ComdatGroup(i) => misc::comdat_group::update_shdr(ctx, i),
|
||||
ChunkId::GnuDebuglink => gnu_debuglink::update_shdr(ctx),
|
||||
ChunkId::Reloc(i) => reloc::update_shdr(ctx, i),
|
||||
ChunkId::ComdatGroup(i) => comdat_group::update_shdr(ctx, i),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@@ -738,7 +767,7 @@ pub fn compute_section_size<E: Arch>(ctx: &mut Context<E>, id: ChunkId) {
|
||||
pub fn num_dynrels<E: Arch>(ctx: &Context<E>, id: ChunkId) -> u64 {
|
||||
match id {
|
||||
ChunkId::Output(id) => output_section::num_dynrels(ctx, id),
|
||||
ChunkId::Got => got::got::num_dynrels(ctx),
|
||||
ChunkId::Got => got::num_dynrels(ctx),
|
||||
ChunkId::Copyrel => ctx.copyrel.symbols.len() as u64,
|
||||
ChunkId::CopyrelRelro => ctx.copyrel_relro.symbols.len() as u64,
|
||||
ChunkId::Ppc64Opd => opd::num_dynrels(ctx),
|
||||
@@ -751,7 +780,7 @@ pub fn num_dynrels<E: Arch>(ctx: &Context<E>, id: ChunkId) -> u64 {
|
||||
pub fn relr_offsets<E: Arch>(ctx: &mut Context<E>, id: ChunkId) -> Vec<u64> {
|
||||
match id {
|
||||
ChunkId::Output(id) => output_section::relr_offsets(ctx, id),
|
||||
ChunkId::Got => got::got::relr_offsets(ctx),
|
||||
ChunkId::Got => got::relr_offsets(ctx),
|
||||
ChunkId::Ppc64Opd => opd::relr_offsets(ctx),
|
||||
_ => Vec::new(),
|
||||
}
|
||||
@@ -761,9 +790,9 @@ pub fn relr_offsets<E: Arch>(ctx: &mut Context<E>, id: ChunkId) -> Vec<u64> {
|
||||
pub fn write_dynrels<E: Arch>(ctx: &Context<E>, id: ChunkId, out: &mut [E::Rel]) {
|
||||
match id {
|
||||
ChunkId::Output(id) => output_section::write_dynrels(ctx, id, out),
|
||||
ChunkId::Got => got::got::write_dynrels(ctx, out),
|
||||
ChunkId::Copyrel => misc::copyrel::write_dynrels(ctx, &ctx.copyrel, out),
|
||||
ChunkId::CopyrelRelro => misc::copyrel::write_dynrels(ctx, &ctx.copyrel_relro, out),
|
||||
ChunkId::Got => got::write_dynrels(ctx, out),
|
||||
ChunkId::Copyrel => copyrel::write_dynrels(ctx, &ctx.copyrel, out),
|
||||
ChunkId::CopyrelRelro => copyrel::write_dynrels(ctx, &ctx.copyrel_relro, out),
|
||||
ChunkId::Ppc64Opd => opd::write_dynrels(ctx, out),
|
||||
_ => {}
|
||||
}
|
||||
@@ -773,9 +802,9 @@ pub fn write_dynrels<E: Arch>(ctx: &Context<E>, id: ChunkId, out: &mut [E::Rel])
|
||||
pub fn compute_symtab_size<E: Arch>(ctx: &mut Context<E>, id: ChunkId) {
|
||||
match id {
|
||||
ChunkId::Output(id) => output_section::compute_symtab_size(ctx, id),
|
||||
ChunkId::Got => got::got::compute_symtab_size(ctx),
|
||||
ChunkId::Plt => got::plt::compute_symtab_size(ctx),
|
||||
ChunkId::PltGot => got::pltgot::compute_symtab_size(ctx),
|
||||
ChunkId::Got => got::compute_symtab_size(ctx),
|
||||
ChunkId::Plt => plt::compute_symtab_size(ctx),
|
||||
ChunkId::PltGot => pltgot::compute_symtab_size(ctx),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@@ -784,9 +813,9 @@ pub fn compute_symtab_size<E: Arch>(ctx: &mut Context<E>, id: ChunkId) {
|
||||
pub fn populate_symtab<E: Arch>(ctx: &Context<E>, id: ChunkId, block: &mut SymtabBlock<'_>) {
|
||||
match id {
|
||||
ChunkId::Output(id) => output_section::populate_symtab(ctx, id, block),
|
||||
ChunkId::Got => got::got::populate_symtab(ctx, block),
|
||||
ChunkId::Plt => got::plt::populate_symtab(ctx, block),
|
||||
ChunkId::PltGot => got::pltgot::populate_symtab(ctx, block),
|
||||
ChunkId::Got => got::populate_symtab(ctx, block),
|
||||
ChunkId::Plt => plt::populate_symtab(ctx, block),
|
||||
ChunkId::PltGot => pltgot::populate_symtab(ctx, block),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@@ -804,44 +833,44 @@ pub fn copy_buf<E: Arch>(ctx: &Context<E>, id: ChunkId, buf: &mut [u8]) {
|
||||
let phdrs = &ctx.phdr.as_ref().unwrap().phdrs;
|
||||
ElfPhdr::<E>::write_all(phdrs, buf);
|
||||
}
|
||||
ChunkId::Interp => misc::interp::copy_buf(ctx, buf),
|
||||
ChunkId::Got => got::got::copy_buf(ctx, buf),
|
||||
ChunkId::GotPlt => got::gotplt::copy_buf(ctx, buf),
|
||||
ChunkId::RelPlt => got::relplt::copy_buf(ctx, buf),
|
||||
ChunkId::RelDyn => dynamic::reldyn::copy_buf(ctx, buf),
|
||||
ChunkId::RelrDyn => dynamic::relrdyn::copy_buf(ctx, buf),
|
||||
ChunkId::Dynamic => dynamic::dynamic::copy_buf(ctx, buf),
|
||||
ChunkId::Strtab => symtab::strtab::copy_buf(ctx, buf),
|
||||
ChunkId::Dynstr => symtab::dynstr::copy_buf(ctx, buf),
|
||||
ChunkId::Hash => symtab::hash::copy_buf(ctx, buf),
|
||||
ChunkId::GnuHash => symtab::gnu_hash::copy_buf(ctx, buf),
|
||||
ChunkId::GnuDebuglink => misc::gnu_debuglink::copy_buf(ctx, buf),
|
||||
ChunkId::Shstrtab => symtab::shstrtab::copy_buf(ctx, buf),
|
||||
ChunkId::Plt => got::plt::copy_buf(ctx, buf),
|
||||
ChunkId::PltGot => got::pltgot::copy_buf(ctx, buf),
|
||||
ChunkId::Interp => interp::copy_buf(ctx, buf),
|
||||
ChunkId::Got => got::copy_buf(ctx, buf),
|
||||
ChunkId::GotPlt => gotplt::copy_buf(ctx, buf),
|
||||
ChunkId::RelPlt => relplt::copy_buf(ctx, buf),
|
||||
ChunkId::RelDyn => reldyn::copy_buf(ctx, buf),
|
||||
ChunkId::RelrDyn => relrdyn::copy_buf(ctx, buf),
|
||||
ChunkId::Dynamic => dynamic::copy_buf(ctx, buf),
|
||||
ChunkId::Strtab => strtab::copy_buf(ctx, buf),
|
||||
ChunkId::Dynstr => dynstr::copy_buf(ctx, buf),
|
||||
ChunkId::Hash => hash::copy_buf(ctx, buf),
|
||||
ChunkId::GnuHash => gnu_hash::copy_buf(ctx, buf),
|
||||
ChunkId::GnuDebuglink => gnu_debuglink::copy_buf(ctx, buf),
|
||||
ChunkId::Shstrtab => shstrtab::copy_buf(ctx, buf),
|
||||
ChunkId::Plt => plt::copy_buf(ctx, buf),
|
||||
ChunkId::PltGot => pltgot::copy_buf(ctx, buf),
|
||||
ChunkId::Symtab | ChunkId::SymtabShndx => {}
|
||||
ChunkId::Dynsym => symtab::dynsym::copy_buf(ctx, buf),
|
||||
ChunkId::Dynsym => dynsym::copy_buf(ctx, buf),
|
||||
ChunkId::EhFrame | ChunkId::EhFrameHdr => {}
|
||||
ChunkId::EhFrameReloc => {}
|
||||
ChunkId::SFrame => sframe::copy_buf(ctx, buf),
|
||||
ChunkId::SFrameReloc => sframe::sframe_reloc::copy_buf(ctx, buf),
|
||||
ChunkId::SFrameReloc => sframe_reloc::copy_buf(ctx, buf),
|
||||
ChunkId::Copyrel | ChunkId::CopyrelRelro => {}
|
||||
ChunkId::Versym => version::versym::copy_buf(ctx, buf),
|
||||
ChunkId::Verneed => version::verneed::copy_buf(ctx, buf),
|
||||
ChunkId::Verdef => version::verdef::copy_buf(ctx, buf),
|
||||
ChunkId::BuildId => misc::build_id::copy_buf(ctx, buf),
|
||||
ChunkId::NotePackage => misc::note_package::copy_buf(ctx, buf),
|
||||
ChunkId::NoteProperty => misc::note_property::copy_buf(ctx, buf),
|
||||
ChunkId::RiscvAttributes => misc::riscv_attributes::copy_buf(ctx, buf),
|
||||
ChunkId::Versym => versym::copy_buf(ctx, buf),
|
||||
ChunkId::Verneed => verneed::copy_buf(ctx, buf),
|
||||
ChunkId::Verdef => verdef::copy_buf(ctx, buf),
|
||||
ChunkId::BuildId => build_id::copy_buf(ctx, buf),
|
||||
ChunkId::NotePackage => note_package::copy_buf(ctx, buf),
|
||||
ChunkId::NoteProperty => note_property::copy_buf(ctx, buf),
|
||||
ChunkId::RiscvAttributes => riscv_attributes::copy_buf(ctx, buf),
|
||||
ChunkId::ArmExidx => arm_exidx::copy_buf(ctx, buf),
|
||||
ChunkId::Ppc64SaveRestore => misc::ppc64_save_restore::copy_buf(ctx, buf),
|
||||
ChunkId::Ppc64SaveRestore => ppc64_save_restore::copy_buf(ctx, buf),
|
||||
ChunkId::Ppc64Opd => opd::copy_buf(ctx, buf),
|
||||
ChunkId::GdbIndex | ChunkId::RelroPadding | ChunkId::Placeholder(_) => {}
|
||||
ChunkId::Output(id) => output_section::copy_buf(ctx, id, buf),
|
||||
ChunkId::Merged(id) => merged::copy_buf(ctx, id, buf),
|
||||
ChunkId::Reloc(_) => {}
|
||||
ChunkId::ComdatGroup(i) => misc::comdat_group::copy_buf(ctx, i, buf),
|
||||
ChunkId::Compressed(i) => misc::compressed::copy_buf(ctx, i, buf),
|
||||
ChunkId::ComdatGroup(i) => comdat_group::copy_buf(ctx, i, buf),
|
||||
ChunkId::Compressed(i) => compressed::copy_buf(ctx, i, buf),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
//! `.note.package`, package metadata.
|
||||
|
||||
use crate::arch::Arch;
|
||||
use crate::context::Context;
|
||||
use crate::elf::*;
|
||||
use crate::output_chunks::ChunkHeader;
|
||||
use crate::util::align_to;
|
||||
use crate::util::write_cstr;
|
||||
|
||||
// .note.package is an optional hint section that can contain arbitrary
|
||||
// string. Package managers, such as dpkg or rpm, uses the section to
|
||||
// embed package metadata into each ELF file so that it is easy to find
|
||||
// the origin of an ELF file without any additional information.
|
||||
#[derive(Debug)]
|
||||
pub struct NotePackageSection<E: Layout> {
|
||||
pub hdr: ChunkHeader<E>,
|
||||
}
|
||||
|
||||
impl<E: Layout> NotePackageSection<E> {
|
||||
pub fn new() -> NotePackageSection<E> {
|
||||
let mut hdr = ChunkHeader::<E>::new(".note.package", SHT_NOTE, SHF_ALLOC as u64);
|
||||
hdr.shdr.sh_addralign.set(4);
|
||||
NotePackageSection { hdr }
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Layout> Default for NotePackageSection<E> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_shdr<E: Arch>(ctx: &mut Context<E>) {
|
||||
if !ctx.args.package_metadata.is_empty() {
|
||||
// +17 is for the header and the NUL terminator
|
||||
ctx.note_package
|
||||
.hdr
|
||||
.shdr
|
||||
.sh_size
|
||||
.set(align_to(ctx.args.package_metadata.len() as u64 + 17, 4));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn copy_buf<E: Arch>(ctx: &Context<E>, buf: &mut [u8]) {
|
||||
buf.fill(0);
|
||||
E::Endian::write_u32(buf, 4); // Name size
|
||||
E::Endian::write_u32(
|
||||
&mut buf[4..],
|
||||
ctx.note_package.hdr.shdr.sh_size.get() as u32 - 16,
|
||||
); // Content size
|
||||
E::Endian::write_u32(&mut buf[8..], NT_FDO_PACKAGING_METADATA);
|
||||
buf[12..16].copy_from_slice(b"FDO\0");
|
||||
write_cstr(&mut buf[16..], ctx.args.package_metadata.as_bytes()); // Content
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
//! `.note.gnu.property`, merged ISA properties.
|
||||
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
|
||||
use crate::arch::Arch;
|
||||
use crate::context::Context;
|
||||
use crate::elf::*;
|
||||
use crate::output_chunks::ChunkHeader;
|
||||
|
||||
// .note.gnu.property section contains an additional runtime information
|
||||
// about ISA variant.
|
||||
#[derive(Debug)]
|
||||
pub struct NotePropertySection<E: Layout> {
|
||||
pub hdr: ChunkHeader<E>,
|
||||
pub contents: Vec<(u32, u32)>,
|
||||
}
|
||||
|
||||
impl<E: Arch> NotePropertySection<E> {
|
||||
pub fn new() -> NotePropertySection<E> {
|
||||
let mut hdr = ChunkHeader::<E>::new(".note.gnu.property", SHT_NOTE, SHF_ALLOC as u64);
|
||||
hdr.shdr.sh_addralign.set(E::WORD_SIZE as u64);
|
||||
NotePropertySection {
|
||||
hdr,
|
||||
contents: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Arch> Default for NotePropertySection<E> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
fn entry_size<E: Arch>() -> usize {
|
||||
if E::IS_64 {
|
||||
16
|
||||
} else {
|
||||
12
|
||||
}
|
||||
}
|
||||
|
||||
// Merges input files' .note.gnu.property values.
|
||||
pub fn update_shdr<E: Arch>(ctx: &mut Context<E>) {
|
||||
// Obtain the list of keys
|
||||
let files: Vec<&crate::input_files::ObjectFile<E>> = ctx
|
||||
.objs
|
||||
.iter()
|
||||
.filter(|file| !ctx.is_internal(file.id()))
|
||||
.collect();
|
||||
let keys: BTreeSet<u32> = files
|
||||
.iter()
|
||||
.flat_map(|f| f.gnu_properties.keys().copied())
|
||||
.collect();
|
||||
let value = |f: &crate::input_files::ObjectFile<E>, key: u32| {
|
||||
f.gnu_properties.get(&key).copied().unwrap_or(0)
|
||||
};
|
||||
|
||||
// Merge values for each key
|
||||
let mut map: BTreeMap<u32, u32> = BTreeMap::new();
|
||||
for key in keys {
|
||||
if (GNU_PROPERTY_X86_UINT32_AND_LO..=GNU_PROPERTY_X86_UINT32_AND_HI).contains(&key) {
|
||||
// An AND feature is set if all input objects have the property and
|
||||
// the feature.
|
||||
map.insert(
|
||||
key,
|
||||
files.iter().fold(u32::MAX, |acc, f| acc & value(f, key)),
|
||||
);
|
||||
} else if (GNU_PROPERTY_X86_UINT32_OR_LO..=GNU_PROPERTY_X86_UINT32_OR_HI).contains(&key) {
|
||||
// An OR feature is set if some input object has the feature.
|
||||
map.insert(key, files.iter().fold(0, |acc, f| acc | value(f, key)));
|
||||
} else if (GNU_PROPERTY_X86_UINT32_OR_AND_LO..=GNU_PROPERTY_X86_UINT32_OR_AND_HI)
|
||||
.contains(&key)
|
||||
{
|
||||
// An OR-AND feature is set if all input object files have the property
|
||||
// and some of them has the feature.
|
||||
if files.iter().all(|f| f.gnu_properties.contains_key(&key)) {
|
||||
map.insert(key, files.iter().fold(0, |acc, f| acc | value(f, key)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ctx.args.z_ibt {
|
||||
*map.entry(GNU_PROPERTY_X86_FEATURE_1_AND).or_insert(0) |= GNU_PROPERTY_X86_FEATURE_1_IBT;
|
||||
}
|
||||
if ctx.args.z_shstk {
|
||||
*map.entry(GNU_PROPERTY_X86_FEATURE_1_AND).or_insert(0) |= GNU_PROPERTY_X86_FEATURE_1_SHSTK;
|
||||
}
|
||||
*map.entry(GNU_PROPERTY_X86_ISA_1_NEEDED).or_insert(0) |= ctx.args.z_x86_64_isa_level;
|
||||
|
||||
// Serialize the map
|
||||
let contents: Vec<(u32, u32)> = map.into_iter().filter(|&(_, v)| v != 0).collect();
|
||||
let sec = ctx.note_property.as_mut().unwrap();
|
||||
sec.hdr.shdr.sh_size.set(if contents.is_empty() {
|
||||
0
|
||||
} else {
|
||||
(16 + contents.len() * entry_size::<E>()) as u64
|
||||
});
|
||||
sec.contents = contents;
|
||||
}
|
||||
|
||||
pub fn copy_buf<E: Arch>(ctx: &Context<E>, buf: &mut [u8]) {
|
||||
let sec = ctx.note_property.as_ref().unwrap();
|
||||
buf.fill(0);
|
||||
E::Endian::write_u32(buf, 4); // Name size
|
||||
E::Endian::write_u32(&mut buf[4..], sec.hdr.shdr.sh_size.get() as u32 - 16); // Content size
|
||||
E::Endian::write_u32(&mut buf[8..], NT_GNU_PROPERTY_TYPE_0);
|
||||
buf[12..16].copy_from_slice(b"GNU\0");
|
||||
for (i, &(ty, val)) in sec.contents.iter().enumerate() {
|
||||
let off = 16 + i * entry_size::<E>();
|
||||
E::Endian::write_u32(&mut buf[off..], ty);
|
||||
E::Endian::write_u32(&mut buf[off + 4..], 4);
|
||||
E::Endian::write_u32(&mut buf[off + 8..], val); // Content
|
||||
}
|
||||
}
|
||||
@@ -601,18 +601,9 @@ pub fn populate_symtab<E: Arch>(
|
||||
block.push_synthetic::<E>(name, suffix.as_bytes(), func(addr));
|
||||
if E::FAMILY == Family::Arm32 {
|
||||
// Emit "$t", "$a" and "$d" if ARM32.
|
||||
block.push_mapping_symbol::<E>(
|
||||
crate::output_chunks::symtab::strtab::THUMB,
|
||||
func(addr),
|
||||
);
|
||||
block.push_mapping_symbol::<E>(
|
||||
crate::output_chunks::symtab::strtab::ARM,
|
||||
func(addr + 4),
|
||||
);
|
||||
block.push_mapping_symbol::<E>(
|
||||
crate::output_chunks::symtab::strtab::DATA,
|
||||
func(addr + 12),
|
||||
);
|
||||
block.push_mapping_symbol::<E>(crate::output_chunks::strtab::THUMB, func(addr));
|
||||
block.push_mapping_symbol::<E>(crate::output_chunks::strtab::ARM, func(addr + 4));
|
||||
block.push_mapping_symbol::<E>(crate::output_chunks::strtab::DATA, func(addr + 12));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
//! `.plt`, stubs for runtime lazy symbol resolution.
|
||||
|
||||
use crate::arch::{Arch, Family};
|
||||
use crate::context::Context;
|
||||
use crate::elf::*;
|
||||
use crate::input_files::SymtabBlock;
|
||||
use crate::output_chunks::ChunkHeader;
|
||||
use crate::symbol::SymbolId;
|
||||
|
||||
// .plt contains linker-synthesized stub code that acts as if they are
|
||||
// functions. They are in fact immediately branches to real function entry
|
||||
// points. .plt is used as a stub for runtime lazy symbol resolution.
|
||||
#[derive(Debug)]
|
||||
pub struct PltSection<E: Layout> {
|
||||
pub hdr: ChunkHeader<E>,
|
||||
pub symbols: Vec<SymbolId>,
|
||||
}
|
||||
|
||||
impl<E: Arch> PltSection<E> {
|
||||
pub fn new() -> PltSection<E> {
|
||||
let mut hdr =
|
||||
ChunkHeader::<E>::new(".plt", SHT_PROGBITS, (SHF_ALLOC | SHF_EXECINSTR) as u64);
|
||||
if E::IS_SPARC {
|
||||
hdr.shdr
|
||||
.sh_flags
|
||||
.set(hdr.shdr.sh_flags.get() | SHF_WRITE as u64);
|
||||
hdr.shdr.sh_addralign.set(256);
|
||||
} else {
|
||||
hdr.shdr.sh_addralign.set(16);
|
||||
}
|
||||
PltSection {
|
||||
hdr,
|
||||
symbols: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Arch> Default for PltSection<E> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
// On SPARC, .plt uses 32-byte "small" entries until it grows past 0x100000
|
||||
// bytes (the reach of a small entry's branch to the resolver), after which
|
||||
// it switches to a "large" entry format. This is how many small entries fit.
|
||||
pub const SPARC_NUM_SMALL_PLT: u64 = (0x100000 - 128) / 32;
|
||||
|
||||
/// The offset of a PLT entry within `.plt`.
|
||||
pub fn entry_offset<E: Arch>(idx: u32) -> u64 {
|
||||
let idx = idx as u64;
|
||||
match E::FAMILY {
|
||||
Family::Ppc64V1 => {
|
||||
// The PPC64 ELFv1 ABI requires PLT entries to vary in size
|
||||
// depending on their indices. For entries whose PLT index is
|
||||
// less than 32768, the entry size is 8 bytes. Other entries are
|
||||
// 12 bytes long.
|
||||
if idx < 0x8000 {
|
||||
E::PLT_HDR_SIZE + idx * 8
|
||||
} else {
|
||||
E::PLT_HDR_SIZE + 0x8000 * 8 + (idx - 0x8000) * 12
|
||||
}
|
||||
}
|
||||
Family::Sparc64 => {
|
||||
// SPARC large PLT entries are grouped into blocks of 160, each holding
|
||||
// 160 24-byte code stubs followed by 160 8-byte data pointers (so a
|
||||
// stub's `ldx` reaches its pointer within a signed 13-bit offset). This
|
||||
// returns the offset of pltidx's code stub.
|
||||
if idx < SPARC_NUM_SMALL_PLT {
|
||||
E::PLT_HDR_SIZE + idx * E::PLT_SIZE
|
||||
} else {
|
||||
let i = idx - SPARC_NUM_SMALL_PLT;
|
||||
0x100000 + (i / 160) * 5120 + (i % 160) * 24
|
||||
}
|
||||
}
|
||||
_ => E::PLT_HDR_SIZE + idx * E::PLT_SIZE,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_symbol<E: Arch>(ctx: &mut Context<E>, sym: SymbolId) {
|
||||
debug_assert!(!ctx.symbols[sym].has_plt(&ctx.symbols));
|
||||
let idx = ctx.plt.symbols.len() as u32;
|
||||
ctx.symbols.aux_mut(sym).plt_idx = Some(idx);
|
||||
ctx.plt.symbols.push(sym);
|
||||
crate::output_chunks::dynsym::add_symbol(ctx, sym);
|
||||
}
|
||||
|
||||
pub fn update_shdr<E: Arch>(ctx: &mut Context<E>) {
|
||||
let n = ctx.plt.symbols.len() as u64;
|
||||
ctx.plt.hdr.shdr.sh_size.set(if n == 0 {
|
||||
0
|
||||
} else if E::IS_SPARC {
|
||||
E::PLT_HDR_SIZE + n * E::PLT_SIZE
|
||||
} else {
|
||||
entry_offset::<E>(n as u32)
|
||||
});
|
||||
}
|
||||
|
||||
pub fn copy_buf<E: Arch>(ctx: &Context<E>, buf: &mut [u8]) {
|
||||
E::write_plt_header(ctx, buf);
|
||||
for (i, &id) in ctx.plt.symbols.iter().enumerate() {
|
||||
let off = entry_offset::<E>(i as u32) as usize;
|
||||
E::write_plt_entry(ctx, &mut buf[off..], &ctx.symbols[id]);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compute_symtab_size<E: Arch>(ctx: &mut Context<E>) {
|
||||
let n = ctx.plt.symbols.len() as u32;
|
||||
let strtab_size: u64 = ctx
|
||||
.plt
|
||||
.symbols
|
||||
.iter()
|
||||
.map(|&id| ctx.symbols[id].name().len() as u64 + "$plt".len() as u64 + 1)
|
||||
.sum();
|
||||
ctx.plt.hdr.num_local_symtab = if E::FAMILY == Family::Arm32 {
|
||||
n * 3 + 2
|
||||
} else {
|
||||
n
|
||||
};
|
||||
ctx.plt.hdr.strtab_size = strtab_size;
|
||||
}
|
||||
|
||||
pub fn populate_symtab<E: Arch>(ctx: &Context<E>, block: &mut SymtabBlock<'_>) {
|
||||
let plt = &ctx.plt;
|
||||
if plt.hdr.num_local_symtab == 0 {
|
||||
return;
|
||||
}
|
||||
let func = |addr: u64| {
|
||||
let mut sym = ElfSym::<E>::default();
|
||||
sym.st_shndx_mut().set(plt.hdr.shndx as u16);
|
||||
sym.st_value_mut().set(addr);
|
||||
sym.set_type(STT_FUNC);
|
||||
sym
|
||||
};
|
||||
use crate::output_chunks::strtab::{ARM, DATA};
|
||||
if E::FAMILY == Family::Arm32 {
|
||||
block.push_mapping_symbol::<E>(ARM, func(plt.hdr.shdr.sh_addr.get()));
|
||||
block.push_mapping_symbol::<E>(DATA, func(plt.hdr.shdr.sh_addr.get() + 16));
|
||||
}
|
||||
for &id in &plt.symbols {
|
||||
let sym = &ctx.symbols[id];
|
||||
let addr = sym.plt_addr(ctx);
|
||||
block.push_synthetic::<E>(sym.name(), b"$plt", func(addr));
|
||||
if E::FAMILY == Family::Arm32 {
|
||||
block.push_mapping_symbol::<E>(ARM, func(addr));
|
||||
block.push_mapping_symbol::<E>(DATA, func(addr + 12));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
//! `.plt.got`, PLT stubs for symbols already resolved through the GOT.
|
||||
|
||||
use crate::arch::{Arch, Family};
|
||||
use crate::context::Context;
|
||||
use crate::elf::*;
|
||||
use crate::input_files::SymtabBlock;
|
||||
use crate::output_chunks::ChunkHeader;
|
||||
use crate::symbol::SymbolId;
|
||||
|
||||
// .plt.got is similar to .plt but doesn't support lazy symbol resolution.
|
||||
// If we have the same symbol already in .got, resolving the same symbol
|
||||
// lazily for .plt is just waste of time. Therefore, in such case, we use
|
||||
// .plt.got for that symbol instead.
|
||||
#[derive(Debug)]
|
||||
pub struct PltGotSection<E: Layout> {
|
||||
pub hdr: ChunkHeader<E>,
|
||||
pub symbols: Vec<SymbolId>,
|
||||
}
|
||||
|
||||
impl<E: Layout> PltGotSection<E> {
|
||||
pub fn new() -> PltGotSection<E> {
|
||||
let mut hdr =
|
||||
ChunkHeader::<E>::new(".plt.got", SHT_PROGBITS, (SHF_ALLOC | SHF_EXECINSTR) as u64);
|
||||
hdr.shdr.sh_addralign.set(16);
|
||||
PltGotSection {
|
||||
hdr,
|
||||
symbols: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Layout> Default for PltGotSection<E> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_symbol<E: Arch>(ctx: &mut Context<E>, sym: SymbolId) {
|
||||
debug_assert!(!ctx.symbols[sym].has_plt(&ctx.symbols));
|
||||
debug_assert!(ctx.symbols[sym].has_got(&ctx.symbols));
|
||||
let idx = ctx.pltgot.symbols.len() as u32;
|
||||
ctx.symbols.aux_mut(sym).pltgot_idx = Some(idx);
|
||||
ctx.pltgot.symbols.push(sym);
|
||||
ctx.pltgot
|
||||
.hdr
|
||||
.shdr
|
||||
.sh_size
|
||||
.set(ctx.pltgot.symbols.len() as u64 * E::PLTGOT_SIZE);
|
||||
}
|
||||
|
||||
pub fn copy_buf<E: Arch>(ctx: &Context<E>, buf: &mut [u8]) {
|
||||
for (i, &id) in ctx.pltgot.symbols.iter().enumerate() {
|
||||
let off = i * E::PLTGOT_SIZE as usize;
|
||||
E::write_pltgot_entry(ctx, &mut buf[off..], &ctx.symbols[id]);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compute_symtab_size<E: Arch>(ctx: &mut Context<E>) {
|
||||
let n = ctx.pltgot.symbols.len() as u32;
|
||||
let strtab_size: u64 = ctx
|
||||
.pltgot
|
||||
.symbols
|
||||
.iter()
|
||||
.map(|&id| ctx.symbols[id].name().len() as u64 + "$pltgot".len() as u64 + 1)
|
||||
.sum();
|
||||
ctx.pltgot.hdr.num_local_symtab = if E::FAMILY == Family::Arm32 { n * 3 } else { n };
|
||||
ctx.pltgot.hdr.strtab_size = strtab_size;
|
||||
}
|
||||
|
||||
pub fn populate_symtab<E: Arch>(ctx: &Context<E>, block: &mut SymtabBlock<'_>) {
|
||||
let pltgot = &ctx.pltgot;
|
||||
if pltgot.hdr.num_local_symtab == 0 {
|
||||
return;
|
||||
}
|
||||
let func = |addr: u64| {
|
||||
let mut sym = ElfSym::<E>::default();
|
||||
sym.st_shndx_mut().set(pltgot.hdr.shndx as u16);
|
||||
sym.st_value_mut().set(addr);
|
||||
sym.set_type(STT_FUNC);
|
||||
sym
|
||||
};
|
||||
use crate::output_chunks::strtab::{ARM, DATA};
|
||||
for &id in &pltgot.symbols {
|
||||
let sym = &ctx.symbols[id];
|
||||
let addr = sym.plt_addr(ctx);
|
||||
block.push_synthetic::<E>(sym.name(), b"$pltgot", func(addr));
|
||||
if E::FAMILY == Family::Arm32 {
|
||||
block.push_mapping_symbol::<E>(ARM, func(addr));
|
||||
block.push_mapping_symbol::<E>(DATA, func(addr + 12));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
//! PowerPC64 register save and restore functions.
|
||||
|
||||
use crate::arch::Arch;
|
||||
use crate::context::Context;
|
||||
use crate::elf::*;
|
||||
use crate::output_chunks::ChunkHeader;
|
||||
|
||||
/// `.save_restore_regs`, the register save and restore routines that GCC
|
||||
/// expects the linker to provide on PowerPC64 ELFv2.
|
||||
#[derive(Debug)]
|
||||
pub struct Ppc64SaveRestoreSection<E: Layout> {
|
||||
pub hdr: ChunkHeader<E>,
|
||||
}
|
||||
|
||||
impl<E: Layout> Ppc64SaveRestoreSection<E> {
|
||||
pub fn new() -> Ppc64SaveRestoreSection<E> {
|
||||
let mut hdr = ChunkHeader::<E>::new(
|
||||
".save_restore_regs",
|
||||
SHT_PROGBITS,
|
||||
(SHF_ALLOC | SHF_EXECINSTR) as u64,
|
||||
);
|
||||
hdr.shdr.sh_addralign.set(16);
|
||||
hdr.shdr
|
||||
.sh_size
|
||||
.set((crate::arch::ppc64v2::SAVE_RESTORE_INSNS.len() * 4) as u64);
|
||||
Ppc64SaveRestoreSection { hdr }
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Layout> Default for Ppc64SaveRestoreSection<E> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn copy_buf<E: Arch>(_ctx: &Context<E>, buf: &mut [u8]) {
|
||||
buf.copy_from_slice(&crate::arch::ppc64v2::save_restore_contents());
|
||||
}
|
||||
@@ -0,0 +1,302 @@
|
||||
//! `.rel.dyn` and `.rela.dyn`, dynamic relocations.
|
||||
|
||||
use rayon::prelude::*;
|
||||
|
||||
use crate::arch::Arch;
|
||||
use crate::context::Context;
|
||||
use crate::elf::*;
|
||||
use crate::output_chunks::relrdyn::encode_relr;
|
||||
use crate::output_chunks::{self, ChunkHeader};
|
||||
use crate::util::encode_sleb;
|
||||
|
||||
// .rel.dyn contains relocation infromation for other sections.
|
||||
#[derive(Debug)]
|
||||
pub struct RelDynSection<E: Layout> {
|
||||
pub hdr: ChunkHeader<E>,
|
||||
pub android_encoded: Vec<u8>,
|
||||
pub keep_android_size: bool,
|
||||
}
|
||||
|
||||
impl<E: Arch> RelDynSection<E> {
|
||||
pub fn new(args: &crate::cmdline::Args) -> RelDynSection<E> {
|
||||
let name = if E::IS_RELA { ".rela.dyn" } else { ".rel.dyn" };
|
||||
let mut hdr = ChunkHeader::<E>::new(name, 0, SHF_ALLOC as u64);
|
||||
if args.pack_dyn_relocs_android {
|
||||
hdr.shdr.sh_type.set(if E::IS_RELA {
|
||||
SHT_ANDROID_RELA
|
||||
} else {
|
||||
SHT_ANDROID_REL
|
||||
});
|
||||
hdr.shdr.sh_entsize.set(0);
|
||||
hdr.shdr.sh_addralign.set(1);
|
||||
} else {
|
||||
hdr.shdr
|
||||
.sh_type
|
||||
.set(if E::IS_RELA { SHT_RELA } else { SHT_REL });
|
||||
hdr.shdr
|
||||
.sh_entsize
|
||||
.set(std::mem::size_of::<ElfRel<E>>() as u64);
|
||||
hdr.shdr.sh_addralign.set(E::WORD_SIZE as u64);
|
||||
}
|
||||
RelDynSection {
|
||||
hdr,
|
||||
android_encoded: Vec::new(),
|
||||
keep_android_size: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Gathers the dynamic relocations of all chunks.
|
||||
pub fn collect_relocs<E: Arch>(ctx: &Context<E>) -> Vec<ElfRel<E>> {
|
||||
let count: usize = ctx
|
||||
.chunks
|
||||
.iter()
|
||||
.map(|&id| {
|
||||
let hdr = ctx.chunk_header(id);
|
||||
(hdr.num_dynrels - hdr.num_relrs) as usize
|
||||
})
|
||||
.sum();
|
||||
let mut out = vec![ElfRel::<E>::default(); count];
|
||||
let mut rest = out.as_mut_slice();
|
||||
for &id in &ctx.chunks {
|
||||
let hdr = ctx.chunk_header(id);
|
||||
let count = (hdr.num_dynrels - hdr.num_relrs) as usize;
|
||||
if count != 0 {
|
||||
let (slots, tail) = std::mem::take(&mut rest).split_at_mut(count);
|
||||
output_chunks::write_dynrels(ctx, id, slots);
|
||||
rest = tail;
|
||||
}
|
||||
}
|
||||
debug_assert!(rest.is_empty());
|
||||
out
|
||||
}
|
||||
|
||||
/// Encodes base relocations of each chunk in RELR form, using offsets
|
||||
/// relative to the chunk.
|
||||
pub fn construct_relr<E: Arch>(ctx: &mut Context<E>) {
|
||||
debug_assert!(ctx.args.pack_dyn_relocs_relr);
|
||||
let word = E::WORD_SIZE as u64;
|
||||
let ids = ctx.chunks.clone();
|
||||
|
||||
for &id in &ids {
|
||||
let n = output_chunks::num_dynrels(ctx, id);
|
||||
let hdr = ctx.chunk_header_mut(id);
|
||||
hdr.num_dynrels = n;
|
||||
hdr.num_relrs = 0;
|
||||
hdr.relr.clear();
|
||||
|
||||
// Do not use RELR for executable chunks, as they don't usually contain
|
||||
// base relocations.
|
||||
if hdr.shdr.sh_flags.get() & SHF_EXECINSTR as u64 != 0 {
|
||||
continue;
|
||||
}
|
||||
// --section-start can override a chunk's alignment. Conservatively use
|
||||
// .rel[a].dyn if the explicitly assigned address is not word-aligned.
|
||||
let name = String::from_utf8_lossy(hdr.name).into_owned();
|
||||
if ctx
|
||||
.args
|
||||
.section_start
|
||||
.get(&name)
|
||||
.is_some_and(|&addr| addr % word != 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if n != 0 {
|
||||
let offsets = output_chunks::relr_offsets(ctx, id);
|
||||
let hdr = ctx.chunk_header_mut(id);
|
||||
hdr.num_relrs = offsets.len() as u64;
|
||||
debug_assert!(hdr.num_relrs <= hdr.num_dynrels);
|
||||
hdr.relr = encode_relr::<E>(&offsets);
|
||||
}
|
||||
}
|
||||
|
||||
let size: u64 = ids
|
||||
.iter()
|
||||
.map(|&id| ctx.chunk_header(id).relr.len() as u64 * word)
|
||||
.sum();
|
||||
if let Some(relrdyn) = &mut ctx.relrdyn {
|
||||
relrdyn.hdr.shdr.sh_size.set(size);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_shdr<E: Arch>(ctx: &mut Context<E>) {
|
||||
let ids = ctx.chunks.clone();
|
||||
if !ctx.args.pack_dyn_relocs_relr {
|
||||
for &id in &ids {
|
||||
let n = output_chunks::num_dynrels(ctx, id);
|
||||
let hdr = ctx.chunk_header_mut(id);
|
||||
hdr.num_dynrels = n;
|
||||
hdr.num_relrs = 0;
|
||||
}
|
||||
}
|
||||
|
||||
let mut num_relocs = 0;
|
||||
let mut num_relrs = 0;
|
||||
for &id in &ids {
|
||||
let hdr = ctx.chunk_header(id);
|
||||
num_relocs += hdr.num_dynrels;
|
||||
num_relrs += hdr.num_relrs;
|
||||
}
|
||||
|
||||
if ctx.args.pack_dyn_relocs_android {
|
||||
let relocs = collect_relocs(ctx);
|
||||
// APS2 uses SLEB128-encoded deltas, so .rela.dyn size may oscillate
|
||||
// as addresses move. If a shrink is followed by a growth, stop
|
||||
// shrinking and pad the encoded stream to converge.
|
||||
let encoded = encode_android::<E>(relocs);
|
||||
let old_size = ctx.reldyn.hdr.shdr.sh_size.get() as usize;
|
||||
let reldyn = &mut ctx.reldyn;
|
||||
if old_size != 0 && old_size < encoded.len() {
|
||||
reldyn.keep_android_size = true;
|
||||
}
|
||||
reldyn.android_encoded = encoded;
|
||||
if reldyn.keep_android_size && reldyn.android_encoded.len() < old_size {
|
||||
reldyn.android_encoded.resize(old_size, 0);
|
||||
}
|
||||
reldyn
|
||||
.hdr
|
||||
.shdr
|
||||
.sh_size
|
||||
.set(reldyn.android_encoded.len() as u64);
|
||||
} else {
|
||||
ctx.reldyn
|
||||
.hdr
|
||||
.shdr
|
||||
.sh_size
|
||||
.set((num_relocs - num_relrs) * std::mem::size_of::<ElfRel<E>>() as u64);
|
||||
}
|
||||
ctx.reldyn.hdr.shdr.sh_link.set(ctx.dynsym.hdr.shndx);
|
||||
}
|
||||
|
||||
pub fn copy_buf<E: Arch>(ctx: &Context<E>, buf: &mut [u8]) {
|
||||
if ctx.args.pack_dyn_relocs_android {
|
||||
buf[..ctx.reldyn.android_encoded.len()].copy_from_slice(&ctx.reldyn.android_encoded);
|
||||
} else {
|
||||
let size = std::mem::size_of::<ElfRel<E>>();
|
||||
let mut rest = buf;
|
||||
for &id in &ctx.chunks {
|
||||
let hdr = ctx.chunk_header(id);
|
||||
let count = (hdr.num_dynrels - hdr.num_relrs) as usize;
|
||||
if count != 0 {
|
||||
let (slots, tail) = std::mem::take(&mut rest).split_at_mut(count * size);
|
||||
output_chunks::write_dynrels(ctx, id, rels_from_bytes_mut::<E>(slots));
|
||||
rest = tail;
|
||||
}
|
||||
}
|
||||
debug_assert!(rest.is_empty());
|
||||
}
|
||||
}
|
||||
|
||||
// Sort dynamic relocations. This is the reason why we do it.
|
||||
// Quote from https://www.airs.com/blog/archives/186
|
||||
//
|
||||
// The dynamic linker in glibc uses a one element cache when processing
|
||||
// relocs: if a relocation refers to the same symbol as the previous
|
||||
// relocation, then the dynamic linker reuses the value rather than
|
||||
// looking up the symbol again. Thus the dynamic linker gets the best
|
||||
// results if the dynamic relocations are sorted so that all dynamic
|
||||
// relocations for a given dynamic symbol are adjacent.
|
||||
//
|
||||
// Other than that, the linker sorts together all relative relocations,
|
||||
// which don't have symbols. Two relative relocations, or two relocations
|
||||
// against the same symbol, are sorted by the address in the output
|
||||
// file. This tends to optimize paging and caching when there are two
|
||||
// references from the same page.
|
||||
pub fn sort<E: Arch>(ctx: &Context<E>, buf: &mut [u8]) {
|
||||
// .rela.dyn contains APS2-encoded bytes, not ElfRel entries.
|
||||
if ctx.args.pack_dyn_relocs_android {
|
||||
return;
|
||||
}
|
||||
|
||||
// We group IFUNC relocations at the end of .rel.dyn because we want to
|
||||
// apply all the other relocations before running user-supplied IFUNC
|
||||
// resolvers.
|
||||
let rank = |r_type: u32| -> u32 {
|
||||
if r_type == E::R_RELATIVE {
|
||||
0
|
||||
} else if Some(r_type) == E::R_IRELATIVE {
|
||||
2
|
||||
} else {
|
||||
1
|
||||
}
|
||||
};
|
||||
let relocs = rels_from_bytes_mut::<E>(buf);
|
||||
relocs.par_sort_by_key(|r| (rank(r.r_type()), r.r_sym(), r.r_offset()));
|
||||
}
|
||||
|
||||
// Encode dynamic relocations using the Android Packed Relocation format
|
||||
// (APS2). The encoded stream begins with the magic bytes "APS2" followed
|
||||
// by SLEB128-encoded fields. Relocations are emitted in groups; within a
|
||||
// group, common offset deltas, info values, and addend deltas are
|
||||
// factored out so each per-relocation entry is just the differing fields.
|
||||
//
|
||||
// See bionic's linker/linker_relocs.cpp for the decoder.
|
||||
pub fn encode_android<E: Arch>(mut rels: Vec<ElfRel<E>>) -> Vec<u8> {
|
||||
const GROUPED_BY_INFO: i64 = 1;
|
||||
const GROUPED_BY_OFFSET_DELTA: i64 = 2;
|
||||
const GROUP_HAS_ADDEND: i64 = 8;
|
||||
|
||||
let r_info = |r: &ElfRel<E>| -> i64 {
|
||||
if E::IS_64 {
|
||||
(((r.r_sym() as u64) << 32) | r.r_type() as u64) as i64
|
||||
} else {
|
||||
(((r.r_sym() as u64) << 8) | (r.r_type() & 0xff) as u64) as i64
|
||||
}
|
||||
};
|
||||
|
||||
let mut buf = b"APS2".to_vec();
|
||||
encode_sleb(&mut buf, rels.len() as i64);
|
||||
encode_sleb(&mut buf, 0); // initial offset state
|
||||
if rels.is_empty() {
|
||||
return buf;
|
||||
}
|
||||
|
||||
// APS2 offset deltas are signed, so the format doesn't require
|
||||
// relocations to be in increasing-offset order. Sort by (r_type,
|
||||
// r_sym, r_offset) so all relocs of the dominant type (typically
|
||||
// R_RELATIVE, ~90% of dynrels in a real Android binary) land in one
|
||||
// contiguous block. That collapses dozens of type-broken groups into
|
||||
// a few large info-grouped runs.
|
||||
rels.sort_by_key(|r| (r.r_type(), r.r_sym(), r.r_offset()));
|
||||
|
||||
let mut prev_offset = 0i64;
|
||||
let mut prev_addend = 0i64;
|
||||
let mut i = 0;
|
||||
while i < rels.len() {
|
||||
let offset_delta = rels[i].r_offset() as i64 - prev_offset;
|
||||
let cur_info = r_info(&rels[i]);
|
||||
|
||||
// Greedily extend the group while consecutive relocations share the
|
||||
// same offset_delta and r_info.
|
||||
let mut j = i + 1;
|
||||
while j < rels.len()
|
||||
&& r_info(&rels[j]) == cur_info
|
||||
&& rels[j].r_offset() as i64 - rels[j - 1].r_offset() as i64 == offset_delta
|
||||
{
|
||||
j += 1;
|
||||
}
|
||||
|
||||
let size = (j - i) as i64;
|
||||
let mut flags = 0;
|
||||
if size > 1 {
|
||||
flags = GROUPED_BY_INFO | GROUPED_BY_OFFSET_DELTA;
|
||||
}
|
||||
if E::IS_RELA {
|
||||
flags |= GROUP_HAS_ADDEND;
|
||||
}
|
||||
encode_sleb(&mut buf, size);
|
||||
encode_sleb(&mut buf, flags);
|
||||
encode_sleb(&mut buf, offset_delta);
|
||||
encode_sleb(&mut buf, cur_info);
|
||||
|
||||
if E::IS_RELA {
|
||||
for r in &rels[i..j] {
|
||||
encode_sleb(&mut buf, r.r_addend() - prev_addend);
|
||||
prev_addend = r.r_addend();
|
||||
}
|
||||
}
|
||||
prev_offset = rels[j - 1].r_offset() as i64;
|
||||
i = j;
|
||||
}
|
||||
buf
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
//! Relocation tables for relocatable outputs.
|
||||
|
||||
use bstr::BStr;
|
||||
|
||||
use crate::arch::Arch;
|
||||
use crate::context::Context;
|
||||
use crate::elf::*;
|
||||
use crate::input_sections::{r_delta, InputSection};
|
||||
use crate::output_chunks::{ChunkHeader, OutputSectionId};
|
||||
use crate::symbol::OriginValue;
|
||||
|
||||
// RelocSection represents a relocation table for an output file.
|
||||
// This is used only for the relocatable output (i.e. the `-r` output).
|
||||
#[derive(Debug)]
|
||||
pub struct RelocSection<E: Layout> {
|
||||
pub hdr: ChunkHeader<E>,
|
||||
pub output_section: OutputSectionId,
|
||||
/// The index of the first relocation of each member.
|
||||
offsets: Vec<u64>,
|
||||
}
|
||||
|
||||
pub fn new<E: Arch>(ctx: &Context<E>, osec_id: OutputSectionId) -> RelocSection<E> {
|
||||
let osec = &ctx.output_sections[osec_id.index()];
|
||||
let name = format!(
|
||||
"{}{}",
|
||||
if E::IS_RELA { ".rela" } else { ".rel" },
|
||||
osec.hdr.name
|
||||
);
|
||||
let name = BStr::new(crate::util::leak_bytes(name.into_bytes()));
|
||||
let mut hdr = ChunkHeader::<E>::with_name(
|
||||
name,
|
||||
if E::IS_RELA { SHT_RELA } else { SHT_REL },
|
||||
SHF_INFO_LINK as u64,
|
||||
);
|
||||
hdr.shdr.sh_addralign.set(E::WORD_SIZE as u64);
|
||||
hdr.shdr
|
||||
.sh_entsize
|
||||
.set(std::mem::size_of::<ElfRel<E>>() as u64);
|
||||
|
||||
// Compute an offset for each input section
|
||||
let mut offsets = Vec::with_capacity(osec.members.len());
|
||||
let mut sum = 0u64;
|
||||
for &m in &osec.members {
|
||||
offsets.push(sum);
|
||||
let isec = ctx.input_section(m);
|
||||
let file = &ctx.objs[isec.file.index()];
|
||||
sum += isec.rels(file).len() as u64;
|
||||
}
|
||||
hdr.shdr
|
||||
.sh_size
|
||||
.set(sum * std::mem::size_of::<ElfRel<E>>() as u64);
|
||||
RelocSection {
|
||||
hdr,
|
||||
output_section: osec_id,
|
||||
offsets,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_shdr<E: Arch>(ctx: &mut Context<E>, i: u32) {
|
||||
let symtab_shndx = ctx.symtab.hdr.shndx;
|
||||
let osec = ctx.reloc_sections[i as usize].output_section;
|
||||
let osec_shndx = ctx.output_sections[osec.index()].hdr.shndx;
|
||||
let sec = &mut ctx.reloc_sections[i as usize];
|
||||
sec.hdr.shdr.sh_link.set(symtab_shndx);
|
||||
sec.hdr.shdr.sh_info.set(osec_shndx);
|
||||
}
|
||||
|
||||
// Translates an input relocation's symbol reference into the {r_sym, addend}
|
||||
// pair that is valid in the output file. The returned r_sym is either an output
|
||||
// section index (for section-relative relocs) or an output symbol table index.
|
||||
fn symidx_addend<E: Arch>(ctx: &Context<E>, isec: &InputSection<E>, rel: &ElfRel<E>) -> (u32, i64) {
|
||||
let file = &ctx.objs[isec.file.index()];
|
||||
let sym = &ctx.symbols[file.base.symbols[rel.r_sym() as usize]];
|
||||
|
||||
if !isec.is_alloc() {
|
||||
if let Some((frag, addend)) = isec.fragment(ctx, rel) {
|
||||
let msec = &ctx.merged_sections[frag.section.index()];
|
||||
return (
|
||||
msec.hdr.shndx,
|
||||
msec.fragments.get(frag.entry).offset() as i64 + addend,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if sym.st_type() == STT_SECTION {
|
||||
match sym.origin::<E>() {
|
||||
OriginValue::Fragment(frag) => {
|
||||
let msec = &ctx.merged_sections[frag.section.index()];
|
||||
return (
|
||||
msec.hdr.shndx,
|
||||
msec.fragments.get(frag.entry).offset() as i64
|
||||
+ sym.value as i64
|
||||
+ isec.rel_addend(rel),
|
||||
);
|
||||
}
|
||||
OriginValue::InputSection(section) => {
|
||||
let target = ctx.input_section(section);
|
||||
if let Some(osec) = target.output_section {
|
||||
return (
|
||||
ctx.output_section(osec).hdr.shndx,
|
||||
isec.rel_addend(rel) + target.offset() as i64,
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
// This is usually a dead debug section referring to a
|
||||
// COMDAT-eliminated section.
|
||||
return (0, 0);
|
||||
}
|
||||
|
||||
if sym.write_to_symtab() {
|
||||
return (sym.output_sym_idx(ctx), isec.rel_addend(rel));
|
||||
}
|
||||
(0, 0)
|
||||
}
|
||||
|
||||
/// Writes the relocations. With `-r` on a REL target, the addends are
|
||||
/// written into the output section's bytes, passed as `osec_buf`.
|
||||
pub fn copy_buf<E: Arch>(ctx: &Context<E>, i: u32, buf: &mut [u8], osec_buf: Option<&mut [u8]>) {
|
||||
let sec = &ctx.reloc_sections[i as usize];
|
||||
let osec = &ctx.output_sections[sec.output_section.index()];
|
||||
let out = rels_from_bytes_mut::<E>(buf);
|
||||
let mut osec_buf = osec_buf;
|
||||
|
||||
for (mi, &m) in osec.members.iter().enumerate() {
|
||||
let isec = ctx.input_section(m);
|
||||
let file = &ctx.objs[isec.file.index()];
|
||||
let base = sec.offsets[mi] as usize;
|
||||
for (j, rel) in isec.rels(file).iter().enumerate() {
|
||||
let (symidx, addend) = symidx_addend(ctx, isec, rel);
|
||||
let mut r_offset = osec.hdr.shdr.sh_addr.get() + isec.offset() + rel.r_offset();
|
||||
if E::IS_RISCV || E::IS_LOONGARCH {
|
||||
// On RISC-V and LoongArch, relaxation may have deleted instructions,
|
||||
// shifting this relocation's offset.
|
||||
r_offset -= r_delta(isec, rel.r_offset()) as u64;
|
||||
}
|
||||
|
||||
// SH4 object files store addends in the relocated places rather
|
||||
// than in r_addend, and the relocation records we emit here are
|
||||
// meant to be consumed as if they were in an object file, so we
|
||||
// follow that convention.
|
||||
let out_addend = if E::FAMILY == crate::arch::Family::Sh4 {
|
||||
0
|
||||
} else {
|
||||
addend
|
||||
};
|
||||
out[base + j] = ElfRel::<E>::new(r_offset, rel.r_type(), symidx, out_addend);
|
||||
|
||||
if ctx.args.relocatable {
|
||||
if let Some(osec_buf) = osec_buf.as_deref_mut() {
|
||||
let loc = (isec.offset() + rel.r_offset()) as usize;
|
||||
E::write_addend(&mut osec_buf[loc..], addend, rel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
//! `.rel.plt` and `.rela.plt`, relocations for PLT entries.
|
||||
|
||||
use crate::arch::Arch;
|
||||
use crate::context::Context;
|
||||
use crate::elf::*;
|
||||
use crate::output_chunks::{plt, ChunkHeader};
|
||||
|
||||
// .rel.plt contains relocation information for .plt.
|
||||
#[derive(Debug)]
|
||||
pub struct RelPltSection<E: Layout> {
|
||||
pub hdr: ChunkHeader<E>,
|
||||
}
|
||||
|
||||
impl<E: Arch> RelPltSection<E> {
|
||||
pub fn new() -> RelPltSection<E> {
|
||||
let (name, ty) = if E::IS_RELA {
|
||||
(".rela.plt", SHT_RELA)
|
||||
} else {
|
||||
(".rel.plt", SHT_REL)
|
||||
};
|
||||
let mut hdr = ChunkHeader::<E>::new(name, ty, SHF_ALLOC as u64);
|
||||
hdr.shdr
|
||||
.sh_entsize
|
||||
.set(std::mem::size_of::<ElfRel<E>>() as u64);
|
||||
hdr.shdr.sh_addralign.set(E::WORD_SIZE as u64);
|
||||
RelPltSection { hdr }
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Arch> Default for RelPltSection<E> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_shdr<E: Arch>(ctx: &mut Context<E>) {
|
||||
ctx.relplt
|
||||
.hdr
|
||||
.shdr
|
||||
.sh_size
|
||||
.set(ctx.plt.symbols.len() as u64 * std::mem::size_of::<ElfRel<E>>() as u64);
|
||||
ctx.relplt.hdr.shdr.sh_link.set(ctx.dynsym.hdr.shndx);
|
||||
if !E::IS_SPARC {
|
||||
ctx.relplt.hdr.shdr.sh_info.set(ctx.gotplt.hdr.shndx);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn copy_buf<E: Arch>(ctx: &Context<E>, buf: &mut [u8]) {
|
||||
let out = rels_from_bytes_mut::<E>(buf);
|
||||
debug_assert_eq!(out.len(), ctx.plt.symbols.len());
|
||||
for (i, &id) in ctx.plt.symbols.iter().enumerate() {
|
||||
let sym = &ctx.symbols[id];
|
||||
let rel = if E::IS_SPARC {
|
||||
// SPARC doesn't have a .got.plt because its role is merged to .plt.
|
||||
// On SPARC, .plt is writable (!) and the dynamic linker directly
|
||||
// modifies .plt's machine instructions as it resolves dynamic symbols.
|
||||
// Therefore, it doesn't need a separate section to store the symbol
|
||||
// resolution results. That is of course horrible from the security
|
||||
// point of view, though.
|
||||
let idx = sym.plt_idx(&ctx.symbols).unwrap() as u64;
|
||||
if idx < plt::SPARC_NUM_SMALL_PLT {
|
||||
ElfRel::<E>::new(
|
||||
sym.plt_addr(ctx),
|
||||
E::R_JUMP_SLOT,
|
||||
sym.dynsym_idx(&ctx.symbols).unwrap_or(0),
|
||||
0,
|
||||
)
|
||||
} else {
|
||||
// A large PLT entry resolves through a data pointer rather than
|
||||
// self-modifying code, so its relocation targets that pointer and
|
||||
// carries -(call address) as the addend, making the loader store
|
||||
// (target - call) there (see arch/sparc64.rs).
|
||||
let call = sym.plt_addr(ctx) + 4;
|
||||
let ptr = ctx.plt.hdr.shdr.sh_addr.get()
|
||||
+ crate::arch::sparc64::plt_ptr_offset(ctx.plt.symbols.len(), idx);
|
||||
ElfRel::<E>::new(
|
||||
ptr,
|
||||
E::R_JUMP_SLOT,
|
||||
sym.dynsym_idx(&ctx.symbols).unwrap_or(0),
|
||||
-(call as i64),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
ElfRel::<E>::new(
|
||||
sym.gotplt_addr(ctx),
|
||||
E::R_JUMP_SLOT,
|
||||
sym.dynsym_idx(&ctx.symbols).unwrap_or(0),
|
||||
0,
|
||||
)
|
||||
};
|
||||
out[i] = rel;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
//! `.relr.dyn`, packed base relocations.
|
||||
|
||||
use crate::arch::Arch;
|
||||
use crate::context::Context;
|
||||
use crate::elf::*;
|
||||
use crate::output_chunks::ChunkHeader;
|
||||
|
||||
// .relr.dyn is a relatively new section to contain base relocation
|
||||
// information.
|
||||
//
|
||||
// A relocatable executable/DSO contains a lot of certain type of
|
||||
// relocation entries, called "base relocations", to specify the locations
|
||||
// of pointers in the FILE that need to be adjusted according to the
|
||||
// desired load address and the actual load address. As an example,
|
||||
// consider the following C code.
|
||||
//
|
||||
// extern int foo;
|
||||
// int *bar = &foo;
|
||||
//
|
||||
// If an executable containing the above code is built as relocatable
|
||||
// executable, meaning that the executable can be loaded not to a specific
|
||||
// address in memory but anywhere in the virtual address space, then the
|
||||
// pointer `bar`'s address is not known at link-time.
|
||||
//
|
||||
// The linker temporarily links the executable to a base address, record
|
||||
// that information to the ELF header, and emits dynamic relocations to
|
||||
// refer to the location of `bar`. At runtime, the loader adds the
|
||||
// difference of the expected load address and the actual one to the
|
||||
// pointer value to fix the pointer value.
|
||||
//
|
||||
// Relocatable executables/DSOs usually contain a fairly large number of
|
||||
// base relocations. In particular, C++ virtual function table is an array
|
||||
// of statically-initialized pointers which need base relocations.
|
||||
//
|
||||
// Notice that base relocations don't contain symbol information. They
|
||||
// need only pointer locations in the ELF file that need fixing at
|
||||
// load-time. Therefore, storing that information to the usual ELF
|
||||
// relocation table is waste of space.
|
||||
//
|
||||
// .relr.dyn is designed to store base relocations in a space-efficient way.
|
||||
#[derive(Debug)]
|
||||
pub struct RelrDynSection<E: Layout> {
|
||||
pub hdr: ChunkHeader<E>,
|
||||
}
|
||||
|
||||
impl<E: Arch> RelrDynSection<E> {
|
||||
pub fn new(args: &crate::cmdline::Args) -> RelrDynSection<E> {
|
||||
let ty = if args.use_android_relr_tags {
|
||||
SHT_ANDROID_RELR
|
||||
} else {
|
||||
SHT_RELR
|
||||
};
|
||||
let mut hdr = ChunkHeader::<E>::new(".relr.dyn", ty, SHF_ALLOC as u64);
|
||||
hdr.shdr.sh_entsize.set(E::WORD_SIZE as u64);
|
||||
hdr.shdr.sh_addralign.set(E::WORD_SIZE as u64);
|
||||
RelrDynSection { hdr }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn copy_buf<E: Arch>(ctx: &Context<E>, buf: &mut [u8]) {
|
||||
let w = E::WORD_SIZE;
|
||||
let mut i = 0;
|
||||
for &id in &ctx.chunks {
|
||||
let hdr = ctx.chunk_header(id);
|
||||
for &val in &hdr.relr {
|
||||
let v = if val & 1 != 0 {
|
||||
val
|
||||
} else {
|
||||
hdr.shdr.sh_addr.get() + val
|
||||
};
|
||||
if E::IS_64 {
|
||||
E::Endian::write_u64(&mut buf[i * w..], v);
|
||||
} else {
|
||||
E::Endian::write_u32(&mut buf[i * w..], v as u32);
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// .relr.dyn contains base relocations encoded in a space-efficient form.
|
||||
// The contents of the section is essentially just a list of addresses
|
||||
// that have to be fixed up at runtime.
|
||||
//
|
||||
// Here is the encoding scheme (we assume 64-bit ELF in this description
|
||||
// for the sake of simplicity): .relr.dyn contains zero or more address
|
||||
// groups. Each address group consists of a 64-bit start address followed
|
||||
// by zero or more 63-bit bitmaps. Let A be the address of a start
|
||||
// address. Then, the loader fixes address A. If Nth bit in the following
|
||||
// bitmap is on, the loader also fixes address A + N * 8. In this scheme,
|
||||
// one address and one bitmap can represent up to 64 base relocations in a
|
||||
// 512 bytes range.
|
||||
//
|
||||
// A start address and a bitmap is distinguished by the lowest significant
|
||||
// bit. An address must be even and thus its LSB is 0 (odd address is not
|
||||
// representable in this encoding and such relocation must be stored to
|
||||
// the .rel.dyn section). A bitmap has LSB 1.
|
||||
pub fn encode_relr<E: Arch>(offsets: &[u64]) -> Vec<u64> {
|
||||
let word = E::WORD_SIZE as u64;
|
||||
let num_bits = if E::IS_64 { 63 } else { 31 };
|
||||
let max_delta = word * num_bits;
|
||||
let mut vec = Vec::new();
|
||||
let mut i = 0;
|
||||
|
||||
while i < offsets.len() {
|
||||
let first = offsets[i];
|
||||
vec.push(first);
|
||||
let mut base = first + word;
|
||||
i += 1;
|
||||
loop {
|
||||
let mut bits = 0u64;
|
||||
while i < offsets.len() && offsets[i] - base < max_delta {
|
||||
bits |= 1 << ((offsets[i] - base) / word);
|
||||
i += 1;
|
||||
}
|
||||
if bits == 0 {
|
||||
break;
|
||||
}
|
||||
vec.push((bits << 1) | 1);
|
||||
base += max_delta;
|
||||
}
|
||||
}
|
||||
vec
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
//! Padding at the end of a RELRO segment.
|
||||
|
||||
use crate::elf::*;
|
||||
use crate::output_chunks::ChunkHeader;
|
||||
|
||||
// PT_GNU_RELRO works on page granularity. We want to align its end to
|
||||
// a page boundary. We append this section at end of a segment so that
|
||||
// the segment always ends at a page boundary.
|
||||
#[derive(Debug)]
|
||||
pub struct RelroPaddingSection<E: Layout> {
|
||||
pub hdr: ChunkHeader<E>,
|
||||
}
|
||||
|
||||
impl<E: Layout> RelroPaddingSection<E> {
|
||||
pub fn new() -> RelroPaddingSection<E> {
|
||||
let mut hdr =
|
||||
ChunkHeader::<E>::new(".relro_padding", SHT_NOBITS, (SHF_ALLOC | SHF_WRITE) as u64);
|
||||
hdr.is_relro = true;
|
||||
hdr.shdr.sh_size.set(1);
|
||||
RelroPaddingSection { hdr }
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Layout> Default for RelroPaddingSection<E> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
//! `.riscv.attributes`, merged RISC-V target attributes.
|
||||
|
||||
use crate::arch::Arch;
|
||||
use crate::context::Context;
|
||||
use crate::elf::*;
|
||||
use crate::output_chunks::ChunkHeader;
|
||||
|
||||
/// `.riscv.attributes` describes the ISA the output requires, merged
|
||||
/// from the input files' attributes.
|
||||
#[derive(Debug)]
|
||||
pub struct RiscvAttributesSection<E: Layout> {
|
||||
pub hdr: ChunkHeader<E>,
|
||||
pub contents: Vec<u8>,
|
||||
}
|
||||
|
||||
impl<E: Layout> RiscvAttributesSection<E> {
|
||||
pub fn new() -> RiscvAttributesSection<E> {
|
||||
RiscvAttributesSection {
|
||||
hdr: ChunkHeader::<E>::new(".riscv.attributes", SHT_RISCV_ATTRIBUTES, 0),
|
||||
contents: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Layout> Default for RiscvAttributesSection<E> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_shdr<E: Arch>(ctx: &mut Context<E>) {
|
||||
if !ctx.riscv_attributes.as_ref().unwrap().contents.is_empty() {
|
||||
return;
|
||||
}
|
||||
let contents = crate::arch::riscv::attributes_contents(ctx);
|
||||
let sec = ctx.riscv_attributes.as_mut().unwrap();
|
||||
sec.hdr.shdr.sh_size.set(contents.len() as u64);
|
||||
sec.contents = contents;
|
||||
}
|
||||
|
||||
pub fn copy_buf<E: Arch>(ctx: &Context<E>, buf: &mut [u8]) {
|
||||
let sec = ctx.riscv_attributes.as_ref().unwrap();
|
||||
buf[..sec.contents.len()].copy_from_slice(&sec.contents);
|
||||
}
|
||||
@@ -160,75 +160,3 @@ pub fn copy_buf<E: Arch>(ctx: &Context<E>, buf: &mut [u8]) {
|
||||
fre_off += fde.fre.len();
|
||||
}
|
||||
}
|
||||
|
||||
// SFrameRelocSection holds the relocations for .sframe. We use it only for
|
||||
// relocatable outputs, where function addresses aren't known yet and each
|
||||
// FDE's func_start has to remain a relocation for the final link to
|
||||
// resolve. It is the .sframe counterpart of EhFrameRelocSection.
|
||||
#[derive(Debug)]
|
||||
pub struct SFrameRelocSection<E: Layout> {
|
||||
pub hdr: ChunkHeader<E>,
|
||||
}
|
||||
|
||||
impl<E: Arch> SFrameRelocSection<E> {
|
||||
pub fn new() -> SFrameRelocSection<E> {
|
||||
let mut hdr = ChunkHeader::<E>::new(".rela.sframe", SHT_RELA, SHF_INFO_LINK as u64);
|
||||
hdr.shdr.sh_addralign.set(E::WORD_SIZE as u64);
|
||||
hdr.shdr
|
||||
.sh_entsize
|
||||
.set(std::mem::size_of::<ElfRel<E>>() as u64);
|
||||
SFrameRelocSection { hdr }
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Arch> Default for SFrameRelocSection<E> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub mod sframe_reloc {
|
||||
use super::*;
|
||||
|
||||
pub fn update_shdr<E: Arch>(ctx: &mut Context<E>) {
|
||||
let n = ctx.sframe.fdes.len();
|
||||
let sec = ctx.sframe_reloc.as_mut().unwrap();
|
||||
sec.hdr
|
||||
.shdr
|
||||
.sh_size
|
||||
.set((n * std::mem::size_of::<ElfRel<E>>()) as u64);
|
||||
sec.hdr.shdr.sh_link.set(ctx.symtab.hdr.shndx);
|
||||
sec.hdr.shdr.sh_info.set(ctx.sframe.hdr.shndx);
|
||||
}
|
||||
|
||||
// Emit one relocation per FDE for its func_start field. The entries are
|
||||
// written in the same order as SFrameSection::copy_buf lays out the index.
|
||||
pub fn copy_buf<E: Arch>(ctx: &Context<E>, buf: &mut [u8]) {
|
||||
let Some(r_type) = E::R_SFRAME else {
|
||||
return;
|
||||
};
|
||||
let out = rels_from_bytes_mut::<E>(buf);
|
||||
debug_assert_eq!(out.len(), ctx.sframe.fdes.len());
|
||||
for (i, &(fi, fi_idx)) in ctx.sframe.fdes.iter().enumerate() {
|
||||
let fde = &ctx.objs[fi.index()].sframe_fdes[fi_idx as usize];
|
||||
let sym = &ctx.symbols[fde.sym];
|
||||
let r_offset = ctx.sframe.hdr.shdr.sh_addr.get()
|
||||
+ SFrameHeader::<E>::size() as u64
|
||||
+ (i * SFrameFdeIdx::<E>::size()) as u64;
|
||||
|
||||
let (r_sym, r_addend) = if sym.st_type() == STT_SECTION {
|
||||
// We discard input section symbols and create a fresh one per output
|
||||
// section, so a reference to a section symbol needs its addend
|
||||
// adjusted by the input section's offset in its output section.
|
||||
let target = sym.input_section_ref(ctx).unwrap();
|
||||
(
|
||||
ctx.output_section(target.output_section.unwrap()).hdr.shndx,
|
||||
fde.addend + target.offset() as i64,
|
||||
)
|
||||
} else {
|
||||
(sym.output_sym_idx(ctx), fde.addend)
|
||||
};
|
||||
out[i] = ElfRel::<E>::new(r_offset, r_type, r_sym, r_addend);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
//! `.rela.sframe`, SFrame relocations for relocatable outputs.
|
||||
|
||||
use crate::arch::Arch;
|
||||
use crate::context::Context;
|
||||
use crate::elf::*;
|
||||
use crate::output_chunks::ChunkHeader;
|
||||
|
||||
// SFrameRelocSection holds the relocations for .sframe. We use it only for
|
||||
// relocatable outputs, where function addresses aren't known yet and each
|
||||
// FDE's func_start has to remain a relocation for the final link to
|
||||
// resolve. It is the .sframe counterpart of EhFrameRelocSection.
|
||||
#[derive(Debug)]
|
||||
pub struct SFrameRelocSection<E: Layout> {
|
||||
pub hdr: ChunkHeader<E>,
|
||||
}
|
||||
|
||||
impl<E: Arch> SFrameRelocSection<E> {
|
||||
pub fn new() -> SFrameRelocSection<E> {
|
||||
let mut hdr = ChunkHeader::<E>::new(".rela.sframe", SHT_RELA, SHF_INFO_LINK as u64);
|
||||
hdr.shdr.sh_addralign.set(E::WORD_SIZE as u64);
|
||||
hdr.shdr
|
||||
.sh_entsize
|
||||
.set(std::mem::size_of::<ElfRel<E>>() as u64);
|
||||
SFrameRelocSection { hdr }
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Arch> Default for SFrameRelocSection<E> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_shdr<E: Arch>(ctx: &mut Context<E>) {
|
||||
let n = ctx.sframe.fdes.len();
|
||||
let sec = ctx.sframe_reloc.as_mut().unwrap();
|
||||
sec.hdr
|
||||
.shdr
|
||||
.sh_size
|
||||
.set((n * std::mem::size_of::<ElfRel<E>>()) as u64);
|
||||
sec.hdr.shdr.sh_link.set(ctx.symtab.hdr.shndx);
|
||||
sec.hdr.shdr.sh_info.set(ctx.sframe.hdr.shndx);
|
||||
}
|
||||
|
||||
// Emit one relocation per FDE for its func_start field. The entries are
|
||||
// written in the same order as SFrameSection::copy_buf lays out the index.
|
||||
pub fn copy_buf<E: Arch>(ctx: &Context<E>, buf: &mut [u8]) {
|
||||
let Some(r_type) = E::R_SFRAME else {
|
||||
return;
|
||||
};
|
||||
let out = rels_from_bytes_mut::<E>(buf);
|
||||
debug_assert_eq!(out.len(), ctx.sframe.fdes.len());
|
||||
for (i, &(fi, fi_idx)) in ctx.sframe.fdes.iter().enumerate() {
|
||||
let fde = &ctx.objs[fi.index()].sframe_fdes[fi_idx as usize];
|
||||
let sym = &ctx.symbols[fde.sym];
|
||||
let r_offset = ctx.sframe.hdr.shdr.sh_addr.get()
|
||||
+ SFrameHeader::<E>::size() as u64
|
||||
+ (i * SFrameFdeIdx::<E>::size()) as u64;
|
||||
|
||||
let (r_sym, r_addend) = if sym.st_type() == STT_SECTION {
|
||||
// We discard input section symbols and create a fresh one per output
|
||||
// section, so a reference to a section symbol needs its addend
|
||||
// adjusted by the input section's offset in its output section.
|
||||
let target = sym.input_section_ref(ctx).unwrap();
|
||||
(
|
||||
ctx.output_section(target.output_section.unwrap()).hdr.shndx,
|
||||
fde.addend + target.offset() as i64,
|
||||
)
|
||||
} else {
|
||||
(sym.output_sym_idx(ctx), fde.addend)
|
||||
};
|
||||
out[i] = ElfRel::<E>::new(r_offset, r_type, r_sym, r_addend);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
//! `.shstrtab`, output section names.
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use bstr::BStr;
|
||||
|
||||
use crate::arch::Arch;
|
||||
use crate::context::Context;
|
||||
use crate::elf::*;
|
||||
use crate::output_chunks::ChunkHeader;
|
||||
use crate::util::write_cstr;
|
||||
|
||||
// .shstrtab contains section names, such as ".text" or ".data". Just like
|
||||
// .strtab, .shstrtab is not needed at runtime. One can remove .shstrtab
|
||||
// and section table from an executable without breaking it.
|
||||
#[derive(Debug)]
|
||||
pub struct ShstrtabSection<E: Layout> {
|
||||
pub hdr: ChunkHeader<E>,
|
||||
}
|
||||
|
||||
impl<E: Layout> ShstrtabSection<E> {
|
||||
pub fn new() -> ShstrtabSection<E> {
|
||||
ShstrtabSection {
|
||||
hdr: ChunkHeader::<E>::new(".shstrtab", SHT_STRTAB, 0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Layout> Default for ShstrtabSection<E> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_shdr<E: Arch>(ctx: &mut Context<E>) {
|
||||
let mut map: HashMap<&'static BStr, u64> = HashMap::new();
|
||||
let mut offset = 1u64;
|
||||
for id in ctx.chunks.clone() {
|
||||
if id.is_header() {
|
||||
continue;
|
||||
}
|
||||
let name = ctx.chunk_header(id).name;
|
||||
if name.is_empty() {
|
||||
continue;
|
||||
}
|
||||
let off = *map.entry(name).or_insert_with(|| {
|
||||
let off = offset;
|
||||
offset += name.len() as u64 + 1;
|
||||
off
|
||||
});
|
||||
ctx.chunk_header_mut(id).shdr.sh_name.set(off as u32);
|
||||
}
|
||||
ctx.shstrtab.as_mut().unwrap().hdr.shdr.sh_size.set(offset);
|
||||
}
|
||||
|
||||
pub fn copy_buf<E: Arch>(ctx: &Context<E>, buf: &mut [u8]) {
|
||||
buf[0] = 0;
|
||||
for &id in &ctx.chunks {
|
||||
let hdr = ctx.chunk_header(id);
|
||||
if hdr.shdr.sh_name.get() != 0 {
|
||||
write_cstr(&mut buf[hdr.shdr.sh_name.get() as usize..], hdr.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
//! `.strtab`, names of non-dynamic symbols.
|
||||
|
||||
use crate::arch::{Arch, Family};
|
||||
use crate::context::Context;
|
||||
use crate::elf::*;
|
||||
use crate::output_chunks::ChunkHeader;
|
||||
|
||||
// .strtab is referenced by .strtab and contains symbol names. Note that
|
||||
// .strtab is not needed at runtime; one can remove the section from an
|
||||
// ELF file without breaking it. Strings that runtime accesses are stored
|
||||
// in .dynstr.
|
||||
#[derive(Debug)]
|
||||
pub struct StrtabSection<E: Layout> {
|
||||
pub hdr: ChunkHeader<E>,
|
||||
}
|
||||
|
||||
impl<E: Layout> StrtabSection<E> {
|
||||
pub fn new() -> StrtabSection<E> {
|
||||
StrtabSection {
|
||||
hdr: ChunkHeader::<E>::new(".strtab", SHT_STRTAB, 0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Layout> Default for StrtabSection<E> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
// Offsets in .strtab for ARM32 mapping symbols
|
||||
pub const ARM: u32 = 1;
|
||||
pub const THUMB: u32 = 4;
|
||||
pub const DATA: u32 = 7;
|
||||
|
||||
pub fn update_shdr<E: Arch>(ctx: &mut Context<E>) {
|
||||
let mut offset = 1u64;
|
||||
|
||||
// ARM32 uses $a, $t and $t mapping symbols to mark the beginning of
|
||||
// ARM, Thumb and data in text, respectively. These symbols don't
|
||||
// affect correctness of the program but helps disassembler to
|
||||
// disassemble machine code appropriately.
|
||||
if E::FAMILY == Family::Arm32 && !ctx.args.strip_all {
|
||||
offset += b"$a\0$t\0$d\0".len() as u64;
|
||||
}
|
||||
|
||||
for id in ctx.chunks.clone() {
|
||||
let hdr = ctx.chunk_header_mut(id);
|
||||
hdr.strtab_offset = offset;
|
||||
offset += hdr.strtab_size;
|
||||
}
|
||||
for file in &mut ctx.objs {
|
||||
file.base.strtab_offset = offset;
|
||||
offset += file.base.strtab_size;
|
||||
}
|
||||
for file in &mut ctx.dsos {
|
||||
file.base.strtab_offset = offset;
|
||||
offset += file.base.strtab_size;
|
||||
}
|
||||
ctx.strtab
|
||||
.hdr
|
||||
.shdr
|
||||
.sh_size
|
||||
.set(if offset == 1 { 0 } else { offset });
|
||||
}
|
||||
|
||||
pub fn copy_buf<E: Arch>(ctx: &Context<E>, buf: &mut [u8]) {
|
||||
buf[0] = 0;
|
||||
if E::FAMILY == Family::Arm32 && !ctx.args.strip_all {
|
||||
buf[1..10].copy_from_slice(b"$a\0$t\0$d\0");
|
||||
}
|
||||
}
|
||||
+181
-719
@@ -1,204 +1,14 @@
|
||||
//! Symbol and string tables: `.symtab`, `.strtab`, `.shstrtab`, `.dynsym`,
|
||||
//! `.dynstr`, `.hash` and `.gnu.hash`.
|
||||
//! `.symtab`, the non-dynamic symbol table.
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use bstr::BStr;
|
||||
use rayon::prelude::*;
|
||||
|
||||
use crate::arch::{Arch, Family};
|
||||
use crate::context::Context;
|
||||
use crate::elf::*;
|
||||
use crate::error;
|
||||
use crate::input_files::{SymtabBlock, SymtabEntries};
|
||||
use crate::input_sections::{r_delta, InputSection};
|
||||
use crate::output_chunks::{self, ChunkHeader, ChunkId};
|
||||
use crate::symbol::{AddrFlags, OriginValue, Symbol, SymbolId};
|
||||
use crate::util::write_cstr;
|
||||
|
||||
// .strtab is referenced by .strtab and contains symbol names. Note that
|
||||
// .strtab is not needed at runtime; one can remove the section from an
|
||||
// ELF file without breaking it. Strings that runtime accesses are stored
|
||||
// in .dynstr.
|
||||
#[derive(Debug)]
|
||||
pub struct StrtabSection<E: Layout> {
|
||||
pub hdr: ChunkHeader<E>,
|
||||
}
|
||||
|
||||
impl<E: Layout> StrtabSection<E> {
|
||||
pub fn new() -> StrtabSection<E> {
|
||||
StrtabSection {
|
||||
hdr: ChunkHeader::<E>::new(".strtab", SHT_STRTAB, 0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Layout> Default for StrtabSection<E> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub mod strtab {
|
||||
use super::*;
|
||||
|
||||
// Offsets in .strtab for ARM32 mapping symbols
|
||||
pub const ARM: u32 = 1;
|
||||
pub const THUMB: u32 = 4;
|
||||
pub const DATA: u32 = 7;
|
||||
|
||||
pub fn update_shdr<E: Arch>(ctx: &mut Context<E>) {
|
||||
let mut offset = 1u64;
|
||||
|
||||
// ARM32 uses $a, $t and $t mapping symbols to mark the beginning of
|
||||
// ARM, Thumb and data in text, respectively. These symbols don't
|
||||
// affect correctness of the program but helps disassembler to
|
||||
// disassemble machine code appropriately.
|
||||
if E::FAMILY == Family::Arm32 && !ctx.args.strip_all {
|
||||
offset += b"$a\0$t\0$d\0".len() as u64;
|
||||
}
|
||||
|
||||
for id in ctx.chunks.clone() {
|
||||
let hdr = ctx.chunk_header_mut(id);
|
||||
hdr.strtab_offset = offset;
|
||||
offset += hdr.strtab_size;
|
||||
}
|
||||
for file in &mut ctx.objs {
|
||||
file.base.strtab_offset = offset;
|
||||
offset += file.base.strtab_size;
|
||||
}
|
||||
for file in &mut ctx.dsos {
|
||||
file.base.strtab_offset = offset;
|
||||
offset += file.base.strtab_size;
|
||||
}
|
||||
ctx.strtab
|
||||
.hdr
|
||||
.shdr
|
||||
.sh_size
|
||||
.set(if offset == 1 { 0 } else { offset });
|
||||
}
|
||||
|
||||
pub fn copy_buf<E: Arch>(ctx: &Context<E>, buf: &mut [u8]) {
|
||||
buf[0] = 0;
|
||||
if E::FAMILY == Family::Arm32 && !ctx.args.strip_all {
|
||||
buf[1..10].copy_from_slice(b"$a\0$t\0$d\0");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// .shstrtab contains section names, such as ".text" or ".data". Just like
|
||||
// .strtab, .shstrtab is not needed at runtime. One can remove .shstrtab
|
||||
// and section table from an executable without breaking it.
|
||||
#[derive(Debug)]
|
||||
pub struct ShstrtabSection<E: Layout> {
|
||||
pub hdr: ChunkHeader<E>,
|
||||
}
|
||||
|
||||
impl<E: Layout> ShstrtabSection<E> {
|
||||
pub fn new() -> ShstrtabSection<E> {
|
||||
ShstrtabSection {
|
||||
hdr: ChunkHeader::<E>::new(".shstrtab", SHT_STRTAB, 0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Layout> Default for ShstrtabSection<E> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub mod shstrtab {
|
||||
use super::*;
|
||||
|
||||
pub fn update_shdr<E: Arch>(ctx: &mut Context<E>) {
|
||||
let mut map: HashMap<&'static BStr, u64> = HashMap::new();
|
||||
let mut offset = 1u64;
|
||||
for id in ctx.chunks.clone() {
|
||||
if id.is_header() {
|
||||
continue;
|
||||
}
|
||||
let name = ctx.chunk_header(id).name;
|
||||
if name.is_empty() {
|
||||
continue;
|
||||
}
|
||||
let off = *map.entry(name).or_insert_with(|| {
|
||||
let off = offset;
|
||||
offset += name.len() as u64 + 1;
|
||||
off
|
||||
});
|
||||
ctx.chunk_header_mut(id).shdr.sh_name.set(off as u32);
|
||||
}
|
||||
ctx.shstrtab.as_mut().unwrap().hdr.shdr.sh_size.set(offset);
|
||||
}
|
||||
|
||||
pub fn copy_buf<E: Arch>(ctx: &Context<E>, buf: &mut [u8]) {
|
||||
buf[0] = 0;
|
||||
for &id in &ctx.chunks {
|
||||
let hdr = ctx.chunk_header(id);
|
||||
if hdr.shdr.sh_name.get() != 0 {
|
||||
write_cstr(&mut buf[hdr.shdr.sh_name.get() as usize..], hdr.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// .dynstr contains strings that the runtime uses.
|
||||
#[derive(Debug)]
|
||||
pub struct DynstrSection<E: Layout> {
|
||||
pub hdr: ChunkHeader<E>,
|
||||
strings: HashMap<Vec<u8>, u64>,
|
||||
}
|
||||
|
||||
impl<E: Layout> DynstrSection<E> {
|
||||
pub fn new() -> DynstrSection<E> {
|
||||
DynstrSection {
|
||||
hdr: ChunkHeader::<E>::new(".dynstr", SHT_STRTAB, SHF_ALLOC as u64),
|
||||
strings: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_string(&mut self, s: &[u8]) -> u64 {
|
||||
if self.hdr.shdr.sh_size.get() == 0 {
|
||||
self.strings.insert(Vec::new(), 0);
|
||||
self.hdr.shdr.sh_size.set(1);
|
||||
}
|
||||
if let Some(&off) = self.strings.get(s) {
|
||||
return off;
|
||||
}
|
||||
let off = self.hdr.shdr.sh_size.get();
|
||||
self.strings.insert(s.to_vec(), off);
|
||||
self.hdr
|
||||
.shdr
|
||||
.sh_size
|
||||
.set(self.hdr.shdr.sh_size.get() + s.len() as u64 + 1);
|
||||
off
|
||||
}
|
||||
|
||||
pub fn find_string(&self, s: &[u8]) -> u64 {
|
||||
*self.strings.get(s).expect("string not in .dynstr")
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Layout> Default for DynstrSection<E> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub mod dynstr {
|
||||
use super::*;
|
||||
|
||||
pub fn copy_buf<E: Arch>(ctx: &Context<E>, buf: &mut [u8]) {
|
||||
for (s, &off) in &ctx.dynstr.strings {
|
||||
write_cstr(&mut buf[off as usize..], s);
|
||||
}
|
||||
let mut off = ctx.dynsym.dynstr_offset as usize;
|
||||
for &id in ctx.dynsym.symbols.iter().flatten() {
|
||||
off += write_cstr(&mut buf[off..], ctx.symbols[id].name());
|
||||
}
|
||||
}
|
||||
}
|
||||
use crate::output_chunks::{self, strtab, ChunkHeader, ChunkId};
|
||||
use crate::symbol::{AddrFlags, OriginValue, Symbol};
|
||||
|
||||
// .symtab contains non-dynamic symbols. The section is not needed at
|
||||
// runtime and can be stripped from an ELF file without affecting the
|
||||
@@ -225,242 +35,202 @@ impl<E: Arch> Default for SymtabSection<E> {
|
||||
}
|
||||
}
|
||||
|
||||
// .symtab_shndx is a parallel table for .symtab to contain section
|
||||
// indices for symbols.
|
||||
//
|
||||
// Symbol table entry contains a field for section index, but that's only
|
||||
// 16 bit in size, so it cannot refer to a section whose section index is
|
||||
// greater than 65535. We use .symtab_shndx for ELF files containing a lot
|
||||
// of sections.
|
||||
//
|
||||
// Use of this section is exceptional. Most ELF files don't contain one.
|
||||
#[derive(Debug)]
|
||||
pub struct SymtabShndxSection<E: Layout> {
|
||||
pub hdr: ChunkHeader<E>,
|
||||
}
|
||||
pub fn update_shdr<E: Arch>(ctx: &mut Context<E>) {
|
||||
let mut nsyms = 1u32;
|
||||
|
||||
impl<E: Layout> SymtabShndxSection<E> {
|
||||
pub fn new() -> SymtabShndxSection<E> {
|
||||
let mut hdr = ChunkHeader::<E>::new(".symtab_shndx", SHT_SYMTAB_SHNDX, 0);
|
||||
hdr.shdr.sh_entsize.set(4);
|
||||
hdr.shdr.sh_addralign.set(4);
|
||||
SymtabShndxSection { hdr }
|
||||
// Section symbols
|
||||
nsyms += ctx
|
||||
.chunks
|
||||
.iter()
|
||||
.filter(|&&id| ctx.chunk_header(id).shndx != 0)
|
||||
.count() as u32;
|
||||
|
||||
// Linker-synthesized symbols
|
||||
for id in ctx.chunks.clone() {
|
||||
let hdr = ctx.chunk_header_mut(id);
|
||||
hdr.local_symtab_idx = nsyms;
|
||||
nsyms += hdr.num_local_symtab;
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Layout> Default for SymtabShndxSection<E> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
// File local symbols
|
||||
for file in &mut ctx.objs {
|
||||
file.base.local_symtab_idx = nsyms;
|
||||
nsyms += file.base.num_local_symtab;
|
||||
}
|
||||
|
||||
// File global symbols
|
||||
for file in &mut ctx.objs {
|
||||
file.base.global_symtab_idx = nsyms;
|
||||
nsyms += file.base.num_global_symtab;
|
||||
}
|
||||
for file in &mut ctx.dsos {
|
||||
file.base.global_symtab_idx = nsyms;
|
||||
nsyms += file.base.num_global_symtab;
|
||||
}
|
||||
|
||||
ctx.symtab
|
||||
.hdr
|
||||
.shdr
|
||||
.sh_info
|
||||
.set(ctx.objs.first().map_or(nsyms, |f| f.base.global_symtab_idx));
|
||||
ctx.symtab.hdr.shdr.sh_link.set(ctx.strtab.hdr.shndx);
|
||||
ctx.symtab.hdr.shdr.sh_size.set(if nsyms == 1 {
|
||||
0
|
||||
} else {
|
||||
nsyms as u64 * std::mem::size_of::<ElfSym<E>>() as u64
|
||||
});
|
||||
}
|
||||
|
||||
// Named after the chunk, like the other chunks' modules in this directory.
|
||||
#[allow(clippy::module_inception)]
|
||||
pub mod symtab {
|
||||
use super::*;
|
||||
/// Writes `.symtab`, `.strtab` and `.symtab_shndx`.
|
||||
pub fn copy_buf<E: Arch>(
|
||||
ctx: &Context<E>,
|
||||
symtab: &mut [u8],
|
||||
strtab: &mut [u8],
|
||||
mut xindex: Option<&mut [u8]>,
|
||||
) {
|
||||
let size = std::mem::size_of::<ElfSym<E>>();
|
||||
symtab[..size].fill(0);
|
||||
if let Some(xindex) = xindex.as_deref_mut() {
|
||||
xindex.fill(0);
|
||||
}
|
||||
|
||||
pub fn update_shdr<E: Arch>(ctx: &mut Context<E>) {
|
||||
let mut nsyms = 1u32;
|
||||
|
||||
// Section symbols
|
||||
nsyms += ctx
|
||||
.chunks
|
||||
.iter()
|
||||
.filter(|&&id| ctx.chunk_header(id).shndx != 0)
|
||||
.count() as u32;
|
||||
|
||||
// Linker-synthesized symbols
|
||||
for id in ctx.chunks.clone() {
|
||||
let hdr = ctx.chunk_header_mut(id);
|
||||
hdr.local_symtab_idx = nsyms;
|
||||
nsyms += hdr.num_local_symtab;
|
||||
// Create section symbols
|
||||
for &id in &ctx.chunks {
|
||||
let hdr = ctx.chunk_header(id);
|
||||
if hdr.shndx == 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
// File local symbols
|
||||
for file in &mut ctx.objs {
|
||||
file.base.local_symtab_idx = nsyms;
|
||||
nsyms += file.base.num_local_symtab;
|
||||
let mut esym = ElfSym::<E>::default();
|
||||
esym.st_value_mut().set(hdr.shdr.sh_addr.get());
|
||||
esym.set_type(STT_SECTION);
|
||||
match xindex.as_deref_mut() {
|
||||
Some(xindex) => {
|
||||
E::Endian::write_u32(&mut xindex[hdr.shndx as usize * 4..], hdr.shndx);
|
||||
esym.st_shndx_mut().set(SHN_XINDEX as u16);
|
||||
}
|
||||
None => esym.st_shndx_mut().set(hdr.shndx as u16),
|
||||
}
|
||||
esym.write(&mut symtab[hdr.shndx as usize * size..]);
|
||||
}
|
||||
|
||||
// File global symbols
|
||||
for file in &mut ctx.objs {
|
||||
file.base.global_symtab_idx = nsyms;
|
||||
nsyms += file.base.num_global_symtab;
|
||||
}
|
||||
for file in &mut ctx.dsos {
|
||||
file.base.global_symtab_idx = nsyms;
|
||||
nsyms += file.base.num_global_symtab;
|
||||
}
|
||||
strtab::copy_buf(ctx, strtab);
|
||||
|
||||
ctx.symtab
|
||||
.hdr
|
||||
.shdr
|
||||
.sh_info
|
||||
.set(ctx.objs.first().map_or(nsyms, |f| f.base.global_symtab_idx));
|
||||
ctx.symtab.hdr.shdr.sh_link.set(ctx.strtab.hdr.shndx);
|
||||
ctx.symtab.hdr.shdr.sh_size.set(if nsyms == 1 {
|
||||
0
|
||||
} else {
|
||||
nsyms as u64 * std::mem::size_of::<ElfSym<E>>() as u64
|
||||
// Symbols synthesized by chunks, then symbols from input files.
|
||||
// Each writer fills its own part of the tables: the local symbols
|
||||
// come first, the chunks' then the files', and the global symbols
|
||||
// after them, with the names laid out in the same order.
|
||||
enum Writer {
|
||||
Chunk(ChunkId),
|
||||
Obj(crate::input_files::ObjId),
|
||||
Dso(crate::input_files::DsoId),
|
||||
}
|
||||
struct Part {
|
||||
writer: Writer,
|
||||
locals: (u32, u32),
|
||||
globals: (u32, u32),
|
||||
strtab: (u64, u64),
|
||||
}
|
||||
let mut parts: Vec<Part> = Vec::new();
|
||||
|
||||
// Populate linker-synthesized symbols
|
||||
for &id in &ctx.chunks {
|
||||
let hdr = ctx.chunk_header(id);
|
||||
if hdr.num_local_symtab != 0 {
|
||||
let locals = (hdr.local_symtab_idx, hdr.num_local_symtab);
|
||||
parts.push(Part {
|
||||
writer: Writer::Chunk(id),
|
||||
locals,
|
||||
globals: (0, 0),
|
||||
strtab: (hdr.strtab_offset, hdr.strtab_size),
|
||||
});
|
||||
}
|
||||
}
|
||||
// Copy symbols from input files
|
||||
for file in &ctx.objs {
|
||||
let base = &file.base;
|
||||
let locals = (base.local_symtab_idx, base.num_local_symtab);
|
||||
let globals = (base.global_symtab_idx, base.num_global_symtab);
|
||||
parts.push(Part {
|
||||
writer: Writer::Obj(file.id()),
|
||||
locals,
|
||||
globals,
|
||||
strtab: (base.strtab_offset, base.strtab_size),
|
||||
});
|
||||
}
|
||||
for file in &ctx.dsos {
|
||||
let base = &file.base;
|
||||
let globals = (base.global_symtab_idx, base.num_global_symtab);
|
||||
parts.push(Part {
|
||||
writer: Writer::Dso(file.id()),
|
||||
locals: (0, 0),
|
||||
globals,
|
||||
strtab: (base.strtab_offset, base.strtab_size),
|
||||
});
|
||||
}
|
||||
|
||||
/// Writes `.symtab`, `.strtab` and `.symtab_shndx`.
|
||||
pub fn copy_buf<E: Arch>(
|
||||
ctx: &Context<E>,
|
||||
symtab: &mut [u8],
|
||||
strtab: &mut [u8],
|
||||
mut xindex: Option<&mut [u8]>,
|
||||
) {
|
||||
let size = std::mem::size_of::<ElfSym<E>>();
|
||||
symtab[..size].fill(0);
|
||||
if let Some(xindex) = xindex.as_deref_mut() {
|
||||
xindex.fill(0);
|
||||
}
|
||||
// Carve each part's slices off the buffers, which the parts cover
|
||||
// in order.
|
||||
let (mut rest, mut pos) = (symtab, 0);
|
||||
let (mut xrest, mut xpos) = (xindex, 0);
|
||||
let mut entries = |(start, count): (u32, u32)| {
|
||||
let syms = carve(
|
||||
&mut rest,
|
||||
&mut pos,
|
||||
start as usize * size,
|
||||
count as usize * size,
|
||||
);
|
||||
let xindex = xrest
|
||||
.as_mut()
|
||||
.map(|x| carve(x, &mut xpos, start as usize * 4, count as usize * 4));
|
||||
(syms, xindex)
|
||||
};
|
||||
let locals: Vec<_> = parts.iter().map(|p| entries(p.locals)).collect();
|
||||
let globals: Vec<_> = parts.iter().map(|p| entries(p.globals)).collect();
|
||||
let (mut srest, mut spos) = (strtab, 0);
|
||||
let strtabs: Vec<_> = parts
|
||||
.iter()
|
||||
.map(|p| {
|
||||
carve(
|
||||
&mut srest,
|
||||
&mut spos,
|
||||
p.strtab.0 as usize,
|
||||
p.strtab.1 as usize,
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Create section symbols
|
||||
for &id in &ctx.chunks {
|
||||
let hdr = ctx.chunk_header(id);
|
||||
if hdr.shndx == 0 {
|
||||
continue;
|
||||
}
|
||||
let mut esym = ElfSym::<E>::default();
|
||||
esym.st_value_mut().set(hdr.shdr.sh_addr.get());
|
||||
esym.set_type(STT_SECTION);
|
||||
match xindex.as_deref_mut() {
|
||||
Some(xindex) => {
|
||||
E::Endian::write_u32(&mut xindex[hdr.shndx as usize * 4..], hdr.shndx);
|
||||
esym.st_shndx_mut().set(SHN_XINDEX as u16);
|
||||
}
|
||||
None => esym.st_shndx_mut().set(hdr.shndx as u16),
|
||||
}
|
||||
esym.write(&mut symtab[hdr.shndx as usize * size..]);
|
||||
}
|
||||
|
||||
strtab::copy_buf(ctx, strtab);
|
||||
|
||||
// Symbols synthesized by chunks, then symbols from input files.
|
||||
// Each writer fills its own part of the tables: the local symbols
|
||||
// come first, the chunks' then the files', and the global symbols
|
||||
// after them, with the names laid out in the same order.
|
||||
enum Writer {
|
||||
Chunk(ChunkId),
|
||||
Obj(crate::input_files::ObjId),
|
||||
Dso(crate::input_files::DsoId),
|
||||
}
|
||||
struct Part {
|
||||
writer: Writer,
|
||||
locals: (u32, u32),
|
||||
globals: (u32, u32),
|
||||
strtab: (u64, u64),
|
||||
}
|
||||
let mut parts: Vec<Part> = Vec::new();
|
||||
|
||||
// Populate linker-synthesized symbols
|
||||
for &id in &ctx.chunks {
|
||||
let hdr = ctx.chunk_header(id);
|
||||
if hdr.num_local_symtab != 0 {
|
||||
let locals = (hdr.local_symtab_idx, hdr.num_local_symtab);
|
||||
parts.push(Part {
|
||||
writer: Writer::Chunk(id),
|
||||
locals,
|
||||
globals: (0, 0),
|
||||
strtab: (hdr.strtab_offset, hdr.strtab_size),
|
||||
});
|
||||
}
|
||||
}
|
||||
// Copy symbols from input files
|
||||
for file in &ctx.objs {
|
||||
let base = &file.base;
|
||||
let locals = (base.local_symtab_idx, base.num_local_symtab);
|
||||
let globals = (base.global_symtab_idx, base.num_global_symtab);
|
||||
parts.push(Part {
|
||||
writer: Writer::Obj(file.id()),
|
||||
locals,
|
||||
globals,
|
||||
strtab: (base.strtab_offset, base.strtab_size),
|
||||
});
|
||||
}
|
||||
for file in &ctx.dsos {
|
||||
let base = &file.base;
|
||||
let globals = (base.global_symtab_idx, base.num_global_symtab);
|
||||
parts.push(Part {
|
||||
writer: Writer::Dso(file.id()),
|
||||
locals: (0, 0),
|
||||
globals,
|
||||
strtab: (base.strtab_offset, base.strtab_size),
|
||||
});
|
||||
}
|
||||
|
||||
// Carve each part's slices off the buffers, which the parts cover
|
||||
// in order.
|
||||
let (mut rest, mut pos) = (symtab, 0);
|
||||
let (mut xrest, mut xpos) = (xindex, 0);
|
||||
let mut entries = |(start, count): (u32, u32)| {
|
||||
let syms = carve(
|
||||
&mut rest,
|
||||
&mut pos,
|
||||
start as usize * size,
|
||||
count as usize * size,
|
||||
parts
|
||||
.par_iter()
|
||||
.zip(locals)
|
||||
.zip(globals)
|
||||
.zip(strtabs)
|
||||
.for_each(|(((part, (lsyms, lx)), (gsyms, gx)), strtab)| {
|
||||
let mut block = SymtabBlock::new(
|
||||
SymtabEntries::new(lsyms, lx),
|
||||
SymtabEntries::new(gsyms, gx),
|
||||
strtab,
|
||||
part.strtab.0,
|
||||
);
|
||||
let xindex = xrest
|
||||
.as_mut()
|
||||
.map(|x| carve(x, &mut xpos, start as usize * 4, count as usize * 4));
|
||||
(syms, xindex)
|
||||
};
|
||||
let locals: Vec<_> = parts.iter().map(|p| entries(p.locals)).collect();
|
||||
let globals: Vec<_> = parts.iter().map(|p| entries(p.globals)).collect();
|
||||
let (mut srest, mut spos) = (strtab, 0);
|
||||
let strtabs: Vec<_> = parts
|
||||
.iter()
|
||||
.map(|p| {
|
||||
carve(
|
||||
&mut srest,
|
||||
&mut spos,
|
||||
p.strtab.0 as usize,
|
||||
p.strtab.1 as usize,
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
match part.writer {
|
||||
Writer::Chunk(id) => output_chunks::populate_symtab(ctx, id, &mut block),
|
||||
Writer::Obj(id) => ctx.objs[id.index()].populate_symtab(ctx, id, &mut block),
|
||||
Writer::Dso(id) => ctx.dsos[id.index()].populate_symtab(ctx, id, &mut block),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
parts
|
||||
.par_iter()
|
||||
.zip(locals)
|
||||
.zip(globals)
|
||||
.zip(strtabs)
|
||||
.for_each(|(((part, (lsyms, lx)), (gsyms, gx)), strtab)| {
|
||||
let mut block = SymtabBlock::new(
|
||||
SymtabEntries::new(lsyms, lx),
|
||||
SymtabEntries::new(gsyms, gx),
|
||||
strtab,
|
||||
part.strtab.0,
|
||||
);
|
||||
match part.writer {
|
||||
Writer::Chunk(id) => output_chunks::populate_symtab(ctx, id, &mut block),
|
||||
Writer::Obj(id) => ctx.objs[id.index()].populate_symtab(ctx, id, &mut block),
|
||||
Writer::Dso(id) => ctx.dsos[id.index()].populate_symtab(ctx, id, &mut block),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Splits `len` bytes starting at `start` off `rest`, which begins
|
||||
/// at `pos` in the buffer; the pieces are taken in order.
|
||||
fn carve<'a>(
|
||||
rest: &mut &'a mut [u8],
|
||||
pos: &mut usize,
|
||||
start: usize,
|
||||
len: usize,
|
||||
) -> &'a mut [u8] {
|
||||
if len == 0 {
|
||||
return &mut [];
|
||||
}
|
||||
let buf = std::mem::take(rest);
|
||||
let (_, buf) = buf.split_at_mut(start - *pos);
|
||||
let (piece, tail) = buf.split_at_mut(len);
|
||||
*rest = tail;
|
||||
*pos = start + len;
|
||||
piece
|
||||
/// Splits `len` bytes starting at `start` off `rest`, which begins
|
||||
/// at `pos` in the buffer; the pieces are taken in order.
|
||||
fn carve<'a>(rest: &mut &'a mut [u8], pos: &mut usize, start: usize, len: usize) -> &'a mut [u8] {
|
||||
if len == 0 {
|
||||
return &mut [];
|
||||
}
|
||||
let buf = std::mem::take(rest);
|
||||
let (_, buf) = buf.split_at_mut(start - *pos);
|
||||
let (piece, tail) = buf.split_at_mut(len);
|
||||
*rest = tail;
|
||||
*pos = start + len;
|
||||
piece
|
||||
}
|
||||
|
||||
// RISC-V and LoongArch have code-shrinking linker relaxation. If we
|
||||
@@ -622,311 +392,3 @@ pub fn to_output_esym<E: Arch>(ctx: &Context<E>, sym: &Symbol, st_name: u32) ->
|
||||
}
|
||||
(esym, xindex)
|
||||
}
|
||||
|
||||
// .dynsym contains symbols for dynamic linking. This is similar to
|
||||
// .symtab, but .dynsym contains data that the runtime uses.
|
||||
#[derive(Debug)]
|
||||
pub struct DynsymSection<E: Layout> {
|
||||
pub hdr: ChunkHeader<E>,
|
||||
/// Index 0 is the null symbol.
|
||||
pub symbols: Vec<Option<SymbolId>>,
|
||||
pub dynstr_offset: u64,
|
||||
}
|
||||
|
||||
impl<E: Arch> DynsymSection<E> {
|
||||
pub fn new() -> DynsymSection<E> {
|
||||
let mut hdr = ChunkHeader::<E>::new(".dynsym", SHT_DYNSYM, SHF_ALLOC as u64);
|
||||
hdr.shdr
|
||||
.sh_entsize
|
||||
.set(std::mem::size_of::<ElfSym<E>>() as u64);
|
||||
hdr.shdr.sh_addralign.set(E::WORD_SIZE as u64);
|
||||
DynsymSection {
|
||||
hdr,
|
||||
symbols: Vec::new(),
|
||||
dynstr_offset: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Arch> Default for DynsymSection<E> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub mod dynsym {
|
||||
use super::*;
|
||||
|
||||
pub fn add_symbol<E: Arch>(ctx: &mut Context<E>, sym: SymbolId) {
|
||||
if ctx.dynsym.symbols.is_empty() {
|
||||
ctx.dynsym.symbols.push(None);
|
||||
}
|
||||
if ctx.symbols[sym].dynsym_idx(&ctx.symbols).is_none() {
|
||||
// A placeholder; the real index is assigned by sort_dynsyms.
|
||||
ctx.symbols.aux_mut(sym).dynsym_idx = Some(u32::MAX);
|
||||
ctx.dynsym.symbols.push(Some(sym));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_shdr<E: Arch>(ctx: &mut Context<E>) {
|
||||
ctx.dynsym.hdr.shdr.sh_link.set(ctx.dynstr.hdr.shndx);
|
||||
ctx.dynsym
|
||||
.hdr
|
||||
.shdr
|
||||
.sh_size
|
||||
.set(std::mem::size_of::<ElfSym<E>>() as u64 * ctx.dynsym.symbols.len() as u64);
|
||||
}
|
||||
|
||||
pub fn copy_buf<E: Arch>(ctx: &Context<E>, buf: &mut [u8]) {
|
||||
let size = std::mem::size_of::<ElfSym<E>>();
|
||||
buf[..size].fill(0);
|
||||
let mut offset = ctx.dynsym.dynstr_offset as u32;
|
||||
for &id in ctx.dynsym.symbols.iter().skip(1).flatten() {
|
||||
let sym = &ctx.symbols[id];
|
||||
let (esym, xindex) = to_output_esym(ctx, sym, offset);
|
||||
if xindex != 0 {
|
||||
let nshdrs = ctx.shdr.as_ref().map_or(0, |s| {
|
||||
s.hdr.shdr.sh_size.get() / ElfShdr::<E>::size() as u64
|
||||
});
|
||||
error!("{}: .dynsym: too many output sections: {nshdrs} requested, but ELF allows at most 65279",
|
||||
ctx.args.output
|
||||
);
|
||||
return;
|
||||
}
|
||||
esym.write(&mut buf[sym.dynsym_idx(&ctx.symbols).unwrap() as usize * size..]);
|
||||
offset += sym.name().len() as u32 + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The hash function for .hash.
|
||||
pub fn elf_hash(name: &[u8]) -> u32 {
|
||||
let mut h: u32 = 0;
|
||||
for &c in name {
|
||||
h = (h << 4).wrapping_add(c as u32);
|
||||
let g = h & 0xf000_0000;
|
||||
if g != 0 {
|
||||
h ^= g >> 24;
|
||||
}
|
||||
h &= !g;
|
||||
}
|
||||
h
|
||||
}
|
||||
|
||||
/// The hash function for `.gnu.hash`.
|
||||
// The hash function for .gnu.hash.
|
||||
pub fn djb_hash(name: &[u8]) -> u32 {
|
||||
let mut h: u32 = 5381;
|
||||
for &c in name {
|
||||
h = (h << 5).wrapping_add(h).wrapping_add(c as u32);
|
||||
}
|
||||
h
|
||||
}
|
||||
|
||||
// .hash contains an on-disk hash table for .dynsym so that the runtime
|
||||
// can look up a symbol name quickly without scannin all entries in
|
||||
// .dynsym.
|
||||
//
|
||||
// Quickly identifying whether or not a .dynsym contains a given symbol is
|
||||
// especially important for ELF because of the dynamic symbol lookup rule
|
||||
// for ELF. In ELF, each dynamic symbol is not searched from a specific
|
||||
// library but from all the ELF files loaded to memory. Therefore,
|
||||
// minimizing the cost of each dynamic symbol lookup is important.
|
||||
#[derive(Debug)]
|
||||
pub struct HashSection<E: Layout> {
|
||||
pub hdr: ChunkHeader<E>,
|
||||
}
|
||||
|
||||
impl<E: Arch> HashSection<E> {
|
||||
pub fn new() -> HashSection<E> {
|
||||
let mut hdr = ChunkHeader::<E>::new(".hash", SHT_HASH, SHF_ALLOC as u64);
|
||||
// Even though u32 should suffice as an etnry size for all targets,
|
||||
// s390x uses u64. It looks like a spec bug, but we need to follow
|
||||
// suit for the sake of binary compatibility.
|
||||
let entry = hash::entry_size::<E>() as u64;
|
||||
hdr.shdr.sh_entsize.set(entry);
|
||||
hdr.shdr.sh_addralign.set(entry);
|
||||
HashSection { hdr }
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Arch> Default for HashSection<E> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub mod hash {
|
||||
use super::*;
|
||||
|
||||
pub fn entry_size<E: Arch>() -> usize {
|
||||
if E::FAMILY == Family::S390x {
|
||||
8
|
||||
} else {
|
||||
4
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_shdr<E: Arch>(ctx: &mut Context<E>) {
|
||||
if ctx.dynsym.symbols.is_empty() {
|
||||
return;
|
||||
}
|
||||
let entry = entry_size::<E>() as u64;
|
||||
let num_slots = ctx.dynsym.symbols.len() as u64;
|
||||
let hash = ctx.hash.as_mut().unwrap();
|
||||
hash.hdr.shdr.sh_size.set(entry * 2 + num_slots * entry * 2);
|
||||
hash.hdr.shdr.sh_link.set(ctx.dynsym.hdr.shndx);
|
||||
}
|
||||
|
||||
pub fn copy_buf<E: Arch>(ctx: &Context<E>, buf: &mut [u8]) {
|
||||
buf.fill(0);
|
||||
let entry = entry_size::<E>();
|
||||
let write = |buf: &mut [u8], i: usize, v: u32| {
|
||||
if entry == 8 {
|
||||
E::Endian::write_u64(&mut buf[i * 8..], v as u64);
|
||||
} else {
|
||||
E::Endian::write_u32(&mut buf[i * 4..], v);
|
||||
}
|
||||
};
|
||||
let read = |buf: &[u8], i: usize| -> u32 {
|
||||
if entry == 8 {
|
||||
E::Endian::read_u64(&buf[i * 8..]) as u32
|
||||
} else {
|
||||
E::Endian::read_u32(&buf[i * 4..])
|
||||
}
|
||||
};
|
||||
|
||||
let n = ctx.dynsym.symbols.len();
|
||||
write(buf, 0, n as u32);
|
||||
write(buf, 1, n as u32);
|
||||
let buckets = 2;
|
||||
let chains = 2 + n;
|
||||
|
||||
for &id in ctx.dynsym.symbols.iter().skip(1).flatten() {
|
||||
let sym = &ctx.symbols[id];
|
||||
let i = sym.dynsym_idx(&ctx.symbols).unwrap() as usize;
|
||||
let h = elf_hash(sym.name()) as usize % n;
|
||||
let head = read(buf, buckets + h);
|
||||
write(buf, chains + i, head);
|
||||
write(buf, buckets + h, i as u32);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// .gnu.hash is an alternative format for .hash. It contains not only an
|
||||
// on-disk hash table but also contains a bloom filter to quickly identify
|
||||
// whether or not a given symbol name exists in .dynsym.
|
||||
#[derive(Debug)]
|
||||
pub struct GnuHashSection<E: Layout> {
|
||||
pub hdr: ChunkHeader<E>,
|
||||
pub num_buckets: u32,
|
||||
pub num_bloom: u32,
|
||||
pub num_exported: u32,
|
||||
}
|
||||
|
||||
impl<E: Arch> GnuHashSection<E> {
|
||||
pub const LOAD_FACTOR: u32 = 8;
|
||||
pub const HEADER_SIZE: u64 = 16;
|
||||
pub const BLOOM_SHIFT: u32 = 26;
|
||||
|
||||
pub fn new() -> GnuHashSection<E> {
|
||||
let mut hdr = ChunkHeader::<E>::new(".gnu.hash", SHT_GNU_HASH, SHF_ALLOC as u64);
|
||||
hdr.shdr.sh_addralign.set(E::WORD_SIZE as u64);
|
||||
GnuHashSection {
|
||||
hdr,
|
||||
num_buckets: 0,
|
||||
num_bloom: 1,
|
||||
num_exported: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Arch> Default for GnuHashSection<E> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub mod gnu_hash {
|
||||
use super::*;
|
||||
|
||||
pub fn update_shdr<E: Arch>(ctx: &mut Context<E>) {
|
||||
if ctx.dynsym.symbols.is_empty() {
|
||||
return;
|
||||
}
|
||||
let word = E::WORD_SIZE as u64;
|
||||
let gh = ctx.gnu_hash.as_mut().unwrap();
|
||||
// We allocate 12 bits for each symbol in the bloom filter.
|
||||
gh.num_bloom = ((gh.num_exported as u64 * 12) / (word * 8))
|
||||
.max(1)
|
||||
.next_power_of_two() as u32;
|
||||
gh.hdr.shdr.sh_size.set(
|
||||
GnuHashSection::<E>::HEADER_SIZE
|
||||
+ gh.num_bloom as u64 * word // Bloom filter
|
||||
+ gh.num_buckets as u64 * 4 // Hash buckets
|
||||
+ gh.num_exported as u64 * 4, // Hash values
|
||||
);
|
||||
gh.hdr.shdr.sh_link.set(ctx.dynsym.hdr.shndx);
|
||||
}
|
||||
|
||||
pub fn copy_buf<E: Arch>(ctx: &Context<E>, buf: &mut [u8]) {
|
||||
buf.fill(0);
|
||||
let gh = ctx.gnu_hash.as_ref().unwrap();
|
||||
let word = E::WORD_SIZE;
|
||||
let first_exported = ctx.dynsym.symbols.len() - gh.num_exported as usize;
|
||||
|
||||
E::Endian::write_u32(buf, gh.num_buckets);
|
||||
E::Endian::write_u32(&mut buf[4..], first_exported as u32);
|
||||
E::Endian::write_u32(&mut buf[8..], gh.num_bloom);
|
||||
E::Endian::write_u32(&mut buf[12..], GnuHashSection::<E>::BLOOM_SHIFT);
|
||||
|
||||
let syms: Vec<SymbolId> = ctx.dynsym.symbols[first_exported..]
|
||||
.iter()
|
||||
.flatten()
|
||||
.copied()
|
||||
.collect();
|
||||
if syms.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Write a bloom filter
|
||||
let bloom_off = GnuHashSection::<E>::HEADER_SIZE as usize;
|
||||
let word_bits = word * 8;
|
||||
let mut indices = Vec::with_capacity(syms.len());
|
||||
for &id in &syms {
|
||||
let h = ctx.symbols[id].aux(&ctx.symbols).unwrap().djb_hash;
|
||||
indices.push(h % gh.num_buckets);
|
||||
let idx = (h as usize / word_bits) % gh.num_bloom as usize;
|
||||
let bits = (1u64 << (h as usize % word_bits))
|
||||
| (1u64 << ((h >> GnuHashSection::<E>::BLOOM_SHIFT) as usize % word_bits));
|
||||
let slot = &mut buf[bloom_off + idx * word..];
|
||||
if E::IS_64 {
|
||||
E::Endian::write_u64(slot, E::Endian::read_u64(slot) | bits);
|
||||
} else {
|
||||
E::Endian::write_u32(slot, E::Endian::read_u32(slot) | bits as u32);
|
||||
}
|
||||
}
|
||||
|
||||
// Write hash bucket indices
|
||||
let buckets_off = bloom_off + gh.num_bloom as usize * word;
|
||||
for (i, &bucket) in indices.iter().enumerate().rev() {
|
||||
E::Endian::write_u32(
|
||||
&mut buf[buckets_off + bucket as usize * 4..],
|
||||
(first_exported + i) as u32,
|
||||
);
|
||||
}
|
||||
|
||||
// Write a hash table
|
||||
let table_off = buckets_off + gh.num_buckets as usize * 4;
|
||||
for (i, &id) in syms.iter().enumerate() {
|
||||
// The last entry in a chain must be terminated with an entry with
|
||||
// least-significant bit 1.
|
||||
let h = ctx.symbols[id].aux(&ctx.symbols).unwrap().djb_hash;
|
||||
let last = i + 1 == syms.len() || indices[i] != indices[i + 1];
|
||||
E::Endian::write_u32(
|
||||
&mut buf[table_off + i * 4..],
|
||||
if last { h | 1 } else { h & !1 },
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
//! `.symtab_shndx`, extended section indices for the symbol table.
|
||||
|
||||
use crate::elf::*;
|
||||
use crate::output_chunks::ChunkHeader;
|
||||
|
||||
// .symtab_shndx is a parallel table for .symtab to contain section
|
||||
// indices for symbols.
|
||||
//
|
||||
// Symbol table entry contains a field for section index, but that's only
|
||||
// 16 bit in size, so it cannot refer to a section whose section index is
|
||||
// greater than 65535. We use .symtab_shndx for ELF files containing a lot
|
||||
// of sections.
|
||||
//
|
||||
// Use of this section is exceptional. Most ELF files don't contain one.
|
||||
#[derive(Debug)]
|
||||
pub struct SymtabShndxSection<E: Layout> {
|
||||
pub hdr: ChunkHeader<E>,
|
||||
}
|
||||
|
||||
impl<E: Layout> SymtabShndxSection<E> {
|
||||
pub fn new() -> SymtabShndxSection<E> {
|
||||
let mut hdr = ChunkHeader::<E>::new(".symtab_shndx", SHT_SYMTAB_SHNDX, 0);
|
||||
hdr.shdr.sh_entsize.set(4);
|
||||
hdr.shdr.sh_addralign.set(4);
|
||||
SymtabShndxSection { hdr }
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Layout> Default for SymtabShndxSection<E> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
//! `.gnu.version_d`, defined symbol versions.
|
||||
|
||||
use crate::arch::Arch;
|
||||
use crate::context::Context;
|
||||
use crate::elf::*;
|
||||
use crate::input_files::FileId;
|
||||
use crate::output_chunks::dynstr::DynstrSection;
|
||||
use crate::output_chunks::hash::elf_hash;
|
||||
use crate::output_chunks::ChunkHeader;
|
||||
use crate::util::path_filename;
|
||||
|
||||
// .gnu.version contains a parallel table for .dynsym to specify symbol
|
||||
// versions of defined symbols. This section appears only in .so files,
|
||||
// and it specifies the symbol version for each defined dynamic symbol.
|
||||
#[derive(Debug)]
|
||||
pub struct VerdefSection<E: Layout> {
|
||||
pub hdr: ChunkHeader<E>,
|
||||
pub contents: Vec<u8>,
|
||||
}
|
||||
|
||||
impl<E: Layout> VerdefSection<E> {
|
||||
pub fn new() -> VerdefSection<E> {
|
||||
let mut hdr = ChunkHeader::<E>::new(".gnu.version_d", SHT_GNU_VERDEF, SHF_ALLOC as u64);
|
||||
hdr.shdr.sh_addralign.set(4);
|
||||
VerdefSection {
|
||||
hdr,
|
||||
contents: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Layout> Default for VerdefSection<E> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn construct<E: Arch>(ctx: &mut Context<E>) {
|
||||
let _t = ctx.timer("fill_verdef");
|
||||
if ctx.args.version_definitions.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle --default-symver
|
||||
if ctx.args.default_symver {
|
||||
for &id in ctx.dynsym.symbols.iter().flatten() {
|
||||
let sym = &mut ctx.symbols[id];
|
||||
if matches!(sym.file(), Some(FileId::Obj(_))) && !sym.is_undef() {
|
||||
let ver = sym.ver_idx as u32;
|
||||
if ver == VER_NDX_GLOBAL || ver == VER_NDX_UNSPECIFIED {
|
||||
sym.ver_idx = VER_NDX_LAST_RESERVED as u16 + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Resize .gnu.version and write to it
|
||||
let n = ctx.dynsym.symbols.len();
|
||||
ctx.versym.contents.resize(n, VER_NDX_GLOBAL as u16);
|
||||
ctx.versym.contents[0] = VER_NDX_LOCAL as u16;
|
||||
|
||||
for &id in ctx.dynsym.symbols.iter().flatten() {
|
||||
let sym = &ctx.symbols[id];
|
||||
if !matches!(sym.file(), Some(FileId::Obj(_))) {
|
||||
continue;
|
||||
}
|
||||
let idx = sym.dynsym_idx(&ctx.symbols).unwrap() as usize;
|
||||
// An unversioned undefined symbol takes version index 0.
|
||||
if sym.ver_idx as u32 != VER_NDX_UNSPECIFIED {
|
||||
ctx.versym.contents[idx] = sym.ver_idx;
|
||||
} else if sym.is_undef() {
|
||||
ctx.versym.contents[idx] = VER_NDX_LOCAL as u16;
|
||||
}
|
||||
}
|
||||
|
||||
// Allocate a buffer for .gnu.version_d and write to it
|
||||
let verdef_size = ElfVerdef::<E>::size();
|
||||
let verdaux_size = ElfVerdaux::<E>::size();
|
||||
let mut contents: Vec<u8> =
|
||||
Vec::with_capacity((verdef_size + verdaux_size) * (ctx.args.version_definitions.len() + 1));
|
||||
let mut prev: Option<usize> = None;
|
||||
let mut count = 0u32;
|
||||
|
||||
let mut write = |contents: &mut Vec<u8>,
|
||||
dynstr: &mut DynstrSection<E>,
|
||||
verstr: &[u8],
|
||||
idx: u16,
|
||||
flags: u16| {
|
||||
count += 1;
|
||||
if let Some(p) = prev {
|
||||
let mut vd = ElfVerdef::<E>::parse(&contents[p..]);
|
||||
vd.vd_next.set((contents.len() - p) as u32);
|
||||
vd.write(&mut contents[p..]);
|
||||
}
|
||||
let pos = contents.len();
|
||||
prev = Some(pos);
|
||||
contents.resize(pos + verdef_size + verdaux_size, 0);
|
||||
ElfVerdef::<E> {
|
||||
vd_version: U16::new(1),
|
||||
vd_flags: U16::new(flags),
|
||||
vd_ndx: U16::new(idx),
|
||||
vd_cnt: U16::new(1),
|
||||
vd_hash: U32::new(elf_hash(verstr)),
|
||||
vd_aux: U32::new(verdef_size as u32),
|
||||
vd_next: U32::default(),
|
||||
}
|
||||
.write(&mut contents[pos..]);
|
||||
ElfVerdaux::<E> {
|
||||
vda_name: U32::new(dynstr.add_string(verstr) as u32),
|
||||
vda_next: U32::default(),
|
||||
}
|
||||
.write(&mut contents[pos + verdef_size..]);
|
||||
};
|
||||
|
||||
let soname = if ctx.args.soname.is_empty() {
|
||||
path_filename(&ctx.args.output)
|
||||
} else {
|
||||
ctx.args.soname.clone()
|
||||
};
|
||||
write(
|
||||
&mut contents,
|
||||
&mut ctx.dynstr,
|
||||
soname.as_bytes(),
|
||||
1,
|
||||
VER_FLG_BASE as u16,
|
||||
);
|
||||
|
||||
let defs = ctx.args.version_definitions.clone();
|
||||
for (i, verstr) in defs.iter().enumerate() {
|
||||
write(
|
||||
&mut contents,
|
||||
&mut ctx.dynstr,
|
||||
verstr.as_bytes(),
|
||||
VER_NDX_LAST_RESERVED as u16 + 1 + i as u16,
|
||||
0,
|
||||
);
|
||||
}
|
||||
|
||||
let verdef = ctx.verdef.as_mut().unwrap();
|
||||
verdef.hdr.shdr.sh_info.set(count);
|
||||
verdef.contents = contents;
|
||||
}
|
||||
|
||||
pub fn update_shdr<E: Arch>(ctx: &mut Context<E>) {
|
||||
let shndx = ctx.dynstr.hdr.shndx;
|
||||
let verdef = ctx.verdef.as_mut().unwrap();
|
||||
verdef.hdr.shdr.sh_size.set(verdef.contents.len() as u64);
|
||||
verdef.hdr.shdr.sh_link.set(shndx);
|
||||
}
|
||||
|
||||
pub fn copy_buf<E: Arch>(ctx: &Context<E>, buf: &mut [u8]) {
|
||||
let verdef = ctx.verdef.as_ref().unwrap();
|
||||
buf[..verdef.contents.len()].copy_from_slice(&verdef.contents);
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
//! `.gnu.version_r`, required symbol versions.
|
||||
|
||||
use crate::arch::Arch;
|
||||
use crate::context::Context;
|
||||
use crate::elf::*;
|
||||
use crate::input_files::{DsoId, FileId};
|
||||
use crate::output_chunks::dynstr::DynstrSection;
|
||||
use crate::output_chunks::hash::elf_hash;
|
||||
use crate::output_chunks::ChunkHeader;
|
||||
use crate::symbol::SymbolId;
|
||||
|
||||
// .gnu.version_r contains information to refer to shared libraries and
|
||||
// their symbol versions.
|
||||
#[derive(Debug)]
|
||||
pub struct VerneedSection<E: Layout> {
|
||||
pub hdr: ChunkHeader<E>,
|
||||
pub contents: Vec<u8>,
|
||||
}
|
||||
|
||||
impl<E: Layout> VerneedSection<E> {
|
||||
pub fn new() -> VerneedSection<E> {
|
||||
let mut hdr = ChunkHeader::<E>::new(".gnu.version_r", SHT_GNU_VERNEED, SHF_ALLOC as u64);
|
||||
hdr.shdr.sh_addralign.set(4);
|
||||
VerneedSection {
|
||||
hdr,
|
||||
contents: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Layout> Default for VerneedSection<E> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
// If `-z pack-relative-relocs` is specified, we'll create a .relr.dyn
|
||||
// section and store base relocation records to that section instead of
|
||||
// to the usual .rela.dyn section.
|
||||
//
|
||||
// .relr.dyn is relatively new feature and not supported by glibc until
|
||||
// 2.38 which was released in 2022. If we don't do anything, executables
|
||||
// built with `-z pack-relative-relocs` would just crash immediately on
|
||||
// startup with an older version of glibc.
|
||||
//
|
||||
// As a workaround, we'll add a dependency to a dummy version name
|
||||
// "GLIBC_ABI_DT_RELR" if `-z pack-relative-relocs` is given so that
|
||||
// executables built with the option failed with a more friendly "version
|
||||
// `GLIBC_ABI_DT_RELR' not found" error message. glibc 2.38 or later knows
|
||||
// about this dummy version name and simply ignores it.
|
||||
fn is_glibc2<E: Layout>(dso: &crate::input_files::SharedFile<E>) -> bool {
|
||||
dso.soname.starts_with("libc.so.")
|
||||
&& dso
|
||||
.version_strings
|
||||
.iter()
|
||||
.any(|v| v.starts_with(b"GLIBC_2."))
|
||||
}
|
||||
|
||||
pub fn construct<E: Arch>(ctx: &mut Context<E>) {
|
||||
let _t = ctx.timer("fill_verneed");
|
||||
|
||||
// Create a list of versioned symbols and sort by file and version.
|
||||
let mut syms: Vec<(DsoId, SymbolId)> = ctx
|
||||
.dynsym
|
||||
.symbols
|
||||
.iter()
|
||||
.skip(1)
|
||||
.flatten()
|
||||
.filter_map(|&id| {
|
||||
let sym = &ctx.symbols[id];
|
||||
match sym.file() {
|
||||
Some(FileId::Dso(dso)) if (sym.ver_idx as u32) > VER_NDX_LAST_RESERVED => {
|
||||
Some((dso, id))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
if syms.is_empty() {
|
||||
return;
|
||||
}
|
||||
syms.sort_by_key(|&(dso, id)| {
|
||||
(
|
||||
ctx.dsos[dso.index()].soname.clone(),
|
||||
ctx.symbols[id].ver_idx,
|
||||
)
|
||||
});
|
||||
|
||||
// Resize .gnu.version
|
||||
let n = ctx.dynsym.symbols.len();
|
||||
ctx.versym.contents.resize(n, VER_NDX_GLOBAL as u16);
|
||||
ctx.versym.contents[0] = VER_NDX_LOCAL as u16;
|
||||
|
||||
// Allocate a large enough buffer for .gnu.version_r.
|
||||
let capacity = (ElfVerneed::<E>::size() + ElfVernaux::<E>::size()) * (syms.len() + 1);
|
||||
let mut builder = VerneedBuilder::<E> {
|
||||
contents: Vec::with_capacity(capacity),
|
||||
veridx: VER_NDX_LAST_RESERVED as u16 + ctx.args.version_definitions.len() as u16,
|
||||
num_groups: 0,
|
||||
group_pos: None,
|
||||
aux_pos: None,
|
||||
_arch: std::marker::PhantomData,
|
||||
};
|
||||
|
||||
// Fill .gnu.version_r.
|
||||
// Create version entries.
|
||||
for i in 0..syms.len() {
|
||||
let (dso, id) = syms[i];
|
||||
let start_group = i == 0 || syms[i - 1].0 != dso;
|
||||
if start_group {
|
||||
let soname = ctx.dsos[dso.index()].soname.clone();
|
||||
let vn_file = ctx.dynstr.find_string(soname.as_bytes()) as u32;
|
||||
builder.start_group(vn_file);
|
||||
if ctx.args.pack_dyn_relocs_relr && is_glibc2(&ctx.dsos[dso.index()]) {
|
||||
builder.add_entry(&mut ctx.dynstr, b"GLIBC_ABI_DT_RELR");
|
||||
}
|
||||
let version = ctx.symbols[id].version(ctx);
|
||||
builder.add_entry(&mut ctx.dynstr, version);
|
||||
} else if ctx.symbols[syms[i - 1].1].ver_idx != ctx.symbols[id].ver_idx {
|
||||
let version = ctx.symbols[id].version(ctx);
|
||||
builder.add_entry(&mut ctx.dynstr, version);
|
||||
}
|
||||
let dynsym_idx = ctx.symbols[id].dynsym_idx(&ctx.symbols).unwrap() as usize;
|
||||
ctx.versym.contents[dynsym_idx] = builder.veridx;
|
||||
}
|
||||
|
||||
// Resize .gnu.version_r to fit to its contents.
|
||||
ctx.verneed.hdr.shdr.sh_info.set(builder.num_groups);
|
||||
ctx.verneed.contents = builder.contents;
|
||||
}
|
||||
|
||||
/// Builds `.gnu.version_r` incrementally: a verneed entry per shared
|
||||
/// library, each followed by its vernaux entries.
|
||||
struct VerneedBuilder<E: Arch> {
|
||||
contents: Vec<u8>,
|
||||
veridx: u16,
|
||||
num_groups: u32,
|
||||
/// Positions of the current group's verneed and last vernaux.
|
||||
group_pos: Option<usize>,
|
||||
aux_pos: Option<usize>,
|
||||
_arch: std::marker::PhantomData<E>,
|
||||
}
|
||||
|
||||
impl<E: Arch> VerneedBuilder<E> {
|
||||
fn start_group(&mut self, vn_file: u32) {
|
||||
self.num_groups += 1;
|
||||
if let Some(gp) = self.group_pos {
|
||||
let mut vn = ElfVerneed::<E>::parse(&self.contents[gp..]);
|
||||
vn.vn_next.set((self.contents.len() - gp) as u32);
|
||||
vn.write(&mut self.contents[gp..]);
|
||||
}
|
||||
let pos = self.contents.len();
|
||||
self.group_pos = Some(pos);
|
||||
self.aux_pos = None;
|
||||
self.contents.resize(pos + ElfVerneed::<E>::size(), 0);
|
||||
ElfVerneed::<E> {
|
||||
vn_version: U16::new(1),
|
||||
vn_cnt: U16::default(),
|
||||
vn_file: U32::new(vn_file),
|
||||
vn_aux: U32::new(ElfVerneed::<E>::size() as u32),
|
||||
vn_next: U32::default(),
|
||||
}
|
||||
.write(&mut self.contents[pos..]);
|
||||
}
|
||||
|
||||
fn add_entry(&mut self, dynstr: &mut DynstrSection<E>, verstr: &[u8]) {
|
||||
let gp = self.group_pos.unwrap();
|
||||
let mut vn = ElfVerneed::<E>::parse(&self.contents[gp..]);
|
||||
vn.vn_cnt.set(vn.vn_cnt.get() + 1);
|
||||
vn.write(&mut self.contents[gp..]);
|
||||
if let Some(ap) = self.aux_pos {
|
||||
let mut aux = ElfVernaux::<E>::parse(&self.contents[ap..]);
|
||||
aux.vna_next.set(ElfVernaux::<E>::size() as u32);
|
||||
aux.write(&mut self.contents[ap..]);
|
||||
}
|
||||
self.veridx += 1;
|
||||
let aux = ElfVernaux::<E> {
|
||||
vna_hash: U32::new(elf_hash(verstr)),
|
||||
vna_flags: U16::default(),
|
||||
vna_other: U16::new(self.veridx),
|
||||
vna_name: U32::new(dynstr.add_string(verstr) as u32),
|
||||
vna_next: U32::default(),
|
||||
};
|
||||
let pos = self.contents.len();
|
||||
self.aux_pos = Some(pos);
|
||||
self.contents.resize(pos + ElfVernaux::<E>::size(), 0);
|
||||
aux.write(&mut self.contents[pos..]);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_shdr<E: Arch>(ctx: &mut Context<E>) {
|
||||
ctx.verneed
|
||||
.hdr
|
||||
.shdr
|
||||
.sh_size
|
||||
.set(ctx.verneed.contents.len() as u64);
|
||||
ctx.verneed.hdr.shdr.sh_link.set(ctx.dynstr.hdr.shndx);
|
||||
}
|
||||
|
||||
pub fn copy_buf<E: Arch>(ctx: &Context<E>, buf: &mut [u8]) {
|
||||
buf[..ctx.verneed.contents.len()].copy_from_slice(&ctx.verneed.contents);
|
||||
}
|
||||
@@ -1,411 +0,0 @@
|
||||
//! Symbol versioning sections: `.gnu.version`, `.gnu.version_r` and
|
||||
//! `.gnu.version_d`.
|
||||
|
||||
use crate::arch::Arch;
|
||||
use crate::context::Context;
|
||||
use crate::elf::*;
|
||||
use crate::input_files::{DsoId, FileId};
|
||||
use crate::output_chunks::symtab::elf_hash;
|
||||
use crate::output_chunks::ChunkHeader;
|
||||
use crate::symbol::SymbolId;
|
||||
use crate::util::path_filename;
|
||||
|
||||
// .gnu.version section contains version indices as a parallel array for
|
||||
// .dynsym. If a dynamic symbol is a defined one, its version information
|
||||
// is in .gnu.version_d. Otherwise, it's in .gnu.version_r.
|
||||
//
|
||||
// .gnu.version contains a parallel table for .dynsym to specify symbol
|
||||
// versions of undefined symbols. A symbol having an entry in .gnu.version
|
||||
// must be resolved to a symbol with the exact same version string at
|
||||
// runtime.
|
||||
#[derive(Debug)]
|
||||
pub struct VersymSection<E: Layout> {
|
||||
pub hdr: ChunkHeader<E>,
|
||||
pub contents: Vec<u16>,
|
||||
}
|
||||
|
||||
impl<E: Layout> VersymSection<E> {
|
||||
pub fn new() -> VersymSection<E> {
|
||||
let mut hdr = ChunkHeader::<E>::new(".gnu.version", SHT_GNU_VERSYM, SHF_ALLOC as u64);
|
||||
hdr.shdr.sh_entsize.set(2);
|
||||
hdr.shdr.sh_addralign.set(2);
|
||||
VersymSection {
|
||||
hdr,
|
||||
contents: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Layout> Default for VersymSection<E> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub mod versym {
|
||||
use super::*;
|
||||
|
||||
pub fn update_shdr<E: Arch>(ctx: &mut Context<E>) {
|
||||
ctx.versym
|
||||
.hdr
|
||||
.shdr
|
||||
.sh_size
|
||||
.set(ctx.versym.contents.len() as u64 * 2);
|
||||
ctx.versym.hdr.shdr.sh_link.set(ctx.dynsym.hdr.shndx);
|
||||
}
|
||||
|
||||
pub fn copy_buf<E: Arch>(ctx: &Context<E>, buf: &mut [u8]) {
|
||||
for (i, &v) in ctx.versym.contents.iter().enumerate() {
|
||||
E::Endian::write_u16(&mut buf[i * 2..], v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// .gnu.version_r contains information to refer to shared libraries and
|
||||
// their symbol versions.
|
||||
#[derive(Debug)]
|
||||
pub struct VerneedSection<E: Layout> {
|
||||
pub hdr: ChunkHeader<E>,
|
||||
pub contents: Vec<u8>,
|
||||
}
|
||||
|
||||
impl<E: Layout> VerneedSection<E> {
|
||||
pub fn new() -> VerneedSection<E> {
|
||||
let mut hdr = ChunkHeader::<E>::new(".gnu.version_r", SHT_GNU_VERNEED, SHF_ALLOC as u64);
|
||||
hdr.shdr.sh_addralign.set(4);
|
||||
VerneedSection {
|
||||
hdr,
|
||||
contents: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Layout> Default for VerneedSection<E> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
// If `-z pack-relative-relocs` is specified, we'll create a .relr.dyn
|
||||
// section and store base relocation records to that section instead of
|
||||
// to the usual .rela.dyn section.
|
||||
//
|
||||
// .relr.dyn is relatively new feature and not supported by glibc until
|
||||
// 2.38 which was released in 2022. If we don't do anything, executables
|
||||
// built with `-z pack-relative-relocs` would just crash immediately on
|
||||
// startup with an older version of glibc.
|
||||
//
|
||||
// As a workaround, we'll add a dependency to a dummy version name
|
||||
// "GLIBC_ABI_DT_RELR" if `-z pack-relative-relocs` is given so that
|
||||
// executables built with the option failed with a more friendly "version
|
||||
// `GLIBC_ABI_DT_RELR' not found" error message. glibc 2.38 or later knows
|
||||
// about this dummy version name and simply ignores it.
|
||||
fn is_glibc2<E: Layout>(dso: &crate::input_files::SharedFile<E>) -> bool {
|
||||
dso.soname.starts_with("libc.so.")
|
||||
&& dso
|
||||
.version_strings
|
||||
.iter()
|
||||
.any(|v| v.starts_with(b"GLIBC_2."))
|
||||
}
|
||||
|
||||
pub mod verneed {
|
||||
use super::*;
|
||||
|
||||
pub fn construct<E: Arch>(ctx: &mut Context<E>) {
|
||||
let _t = ctx.timer("fill_verneed");
|
||||
|
||||
// Create a list of versioned symbols and sort by file and version.
|
||||
let mut syms: Vec<(DsoId, SymbolId)> = ctx
|
||||
.dynsym
|
||||
.symbols
|
||||
.iter()
|
||||
.skip(1)
|
||||
.flatten()
|
||||
.filter_map(|&id| {
|
||||
let sym = &ctx.symbols[id];
|
||||
match sym.file() {
|
||||
Some(FileId::Dso(dso)) if (sym.ver_idx as u32) > VER_NDX_LAST_RESERVED => {
|
||||
Some((dso, id))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
if syms.is_empty() {
|
||||
return;
|
||||
}
|
||||
syms.sort_by_key(|&(dso, id)| {
|
||||
(
|
||||
ctx.dsos[dso.index()].soname.clone(),
|
||||
ctx.symbols[id].ver_idx,
|
||||
)
|
||||
});
|
||||
|
||||
// Resize .gnu.version
|
||||
let n = ctx.dynsym.symbols.len();
|
||||
ctx.versym.contents.resize(n, VER_NDX_GLOBAL as u16);
|
||||
ctx.versym.contents[0] = VER_NDX_LOCAL as u16;
|
||||
|
||||
// Allocate a large enough buffer for .gnu.version_r.
|
||||
let capacity = (ElfVerneed::<E>::size() + ElfVernaux::<E>::size()) * (syms.len() + 1);
|
||||
let mut builder = VerneedBuilder::<E> {
|
||||
contents: Vec::with_capacity(capacity),
|
||||
veridx: VER_NDX_LAST_RESERVED as u16 + ctx.args.version_definitions.len() as u16,
|
||||
num_groups: 0,
|
||||
group_pos: None,
|
||||
aux_pos: None,
|
||||
_arch: std::marker::PhantomData,
|
||||
};
|
||||
|
||||
// Fill .gnu.version_r.
|
||||
// Create version entries.
|
||||
for i in 0..syms.len() {
|
||||
let (dso, id) = syms[i];
|
||||
let start_group = i == 0 || syms[i - 1].0 != dso;
|
||||
if start_group {
|
||||
let soname = ctx.dsos[dso.index()].soname.clone();
|
||||
let vn_file = ctx.dynstr.find_string(soname.as_bytes()) as u32;
|
||||
builder.start_group(vn_file);
|
||||
if ctx.args.pack_dyn_relocs_relr && is_glibc2(&ctx.dsos[dso.index()]) {
|
||||
builder.add_entry(&mut ctx.dynstr, b"GLIBC_ABI_DT_RELR");
|
||||
}
|
||||
let version = ctx.symbols[id].version(ctx);
|
||||
builder.add_entry(&mut ctx.dynstr, version);
|
||||
} else if ctx.symbols[syms[i - 1].1].ver_idx != ctx.symbols[id].ver_idx {
|
||||
let version = ctx.symbols[id].version(ctx);
|
||||
builder.add_entry(&mut ctx.dynstr, version);
|
||||
}
|
||||
let dynsym_idx = ctx.symbols[id].dynsym_idx(&ctx.symbols).unwrap() as usize;
|
||||
ctx.versym.contents[dynsym_idx] = builder.veridx;
|
||||
}
|
||||
|
||||
// Resize .gnu.version_r to fit to its contents.
|
||||
ctx.verneed.hdr.shdr.sh_info.set(builder.num_groups);
|
||||
ctx.verneed.contents = builder.contents;
|
||||
}
|
||||
|
||||
/// Builds `.gnu.version_r` incrementally: a verneed entry per shared
|
||||
/// library, each followed by its vernaux entries.
|
||||
struct VerneedBuilder<E: Arch> {
|
||||
contents: Vec<u8>,
|
||||
veridx: u16,
|
||||
num_groups: u32,
|
||||
/// Positions of the current group's verneed and last vernaux.
|
||||
group_pos: Option<usize>,
|
||||
aux_pos: Option<usize>,
|
||||
_arch: std::marker::PhantomData<E>,
|
||||
}
|
||||
|
||||
impl<E: Arch> VerneedBuilder<E> {
|
||||
fn start_group(&mut self, vn_file: u32) {
|
||||
self.num_groups += 1;
|
||||
if let Some(gp) = self.group_pos {
|
||||
let mut vn = ElfVerneed::<E>::parse(&self.contents[gp..]);
|
||||
vn.vn_next.set((self.contents.len() - gp) as u32);
|
||||
vn.write(&mut self.contents[gp..]);
|
||||
}
|
||||
let pos = self.contents.len();
|
||||
self.group_pos = Some(pos);
|
||||
self.aux_pos = None;
|
||||
self.contents.resize(pos + ElfVerneed::<E>::size(), 0);
|
||||
ElfVerneed::<E> {
|
||||
vn_version: U16::new(1),
|
||||
vn_cnt: U16::default(),
|
||||
vn_file: U32::new(vn_file),
|
||||
vn_aux: U32::new(ElfVerneed::<E>::size() as u32),
|
||||
vn_next: U32::default(),
|
||||
}
|
||||
.write(&mut self.contents[pos..]);
|
||||
}
|
||||
|
||||
fn add_entry(
|
||||
&mut self,
|
||||
dynstr: &mut super::super::symtab::DynstrSection<E>,
|
||||
verstr: &[u8],
|
||||
) {
|
||||
let gp = self.group_pos.unwrap();
|
||||
let mut vn = ElfVerneed::<E>::parse(&self.contents[gp..]);
|
||||
vn.vn_cnt.set(vn.vn_cnt.get() + 1);
|
||||
vn.write(&mut self.contents[gp..]);
|
||||
if let Some(ap) = self.aux_pos {
|
||||
let mut aux = ElfVernaux::<E>::parse(&self.contents[ap..]);
|
||||
aux.vna_next.set(ElfVernaux::<E>::size() as u32);
|
||||
aux.write(&mut self.contents[ap..]);
|
||||
}
|
||||
self.veridx += 1;
|
||||
let aux = ElfVernaux::<E> {
|
||||
vna_hash: U32::new(elf_hash(verstr)),
|
||||
vna_flags: U16::default(),
|
||||
vna_other: U16::new(self.veridx),
|
||||
vna_name: U32::new(dynstr.add_string(verstr) as u32),
|
||||
vna_next: U32::default(),
|
||||
};
|
||||
let pos = self.contents.len();
|
||||
self.aux_pos = Some(pos);
|
||||
self.contents.resize(pos + ElfVernaux::<E>::size(), 0);
|
||||
aux.write(&mut self.contents[pos..]);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_shdr<E: Arch>(ctx: &mut Context<E>) {
|
||||
ctx.verneed
|
||||
.hdr
|
||||
.shdr
|
||||
.sh_size
|
||||
.set(ctx.verneed.contents.len() as u64);
|
||||
ctx.verneed.hdr.shdr.sh_link.set(ctx.dynstr.hdr.shndx);
|
||||
}
|
||||
|
||||
pub fn copy_buf<E: Arch>(ctx: &Context<E>, buf: &mut [u8]) {
|
||||
buf[..ctx.verneed.contents.len()].copy_from_slice(&ctx.verneed.contents);
|
||||
}
|
||||
}
|
||||
|
||||
// .gnu.version contains a parallel table for .dynsym to specify symbol
|
||||
// versions of defined symbols. This section appears only in .so files,
|
||||
// and it specifies the symbol version for each defined dynamic symbol.
|
||||
#[derive(Debug)]
|
||||
pub struct VerdefSection<E: Layout> {
|
||||
pub hdr: ChunkHeader<E>,
|
||||
pub contents: Vec<u8>,
|
||||
}
|
||||
|
||||
impl<E: Layout> VerdefSection<E> {
|
||||
pub fn new() -> VerdefSection<E> {
|
||||
let mut hdr = ChunkHeader::<E>::new(".gnu.version_d", SHT_GNU_VERDEF, SHF_ALLOC as u64);
|
||||
hdr.shdr.sh_addralign.set(4);
|
||||
VerdefSection {
|
||||
hdr,
|
||||
contents: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Layout> Default for VerdefSection<E> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub mod verdef {
|
||||
use super::*;
|
||||
|
||||
pub fn construct<E: Arch>(ctx: &mut Context<E>) {
|
||||
let _t = ctx.timer("fill_verdef");
|
||||
if ctx.args.version_definitions.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle --default-symver
|
||||
if ctx.args.default_symver {
|
||||
for &id in ctx.dynsym.symbols.iter().flatten() {
|
||||
let sym = &mut ctx.symbols[id];
|
||||
if matches!(sym.file(), Some(FileId::Obj(_))) && !sym.is_undef() {
|
||||
let ver = sym.ver_idx as u32;
|
||||
if ver == VER_NDX_GLOBAL || ver == VER_NDX_UNSPECIFIED {
|
||||
sym.ver_idx = VER_NDX_LAST_RESERVED as u16 + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Resize .gnu.version and write to it
|
||||
let n = ctx.dynsym.symbols.len();
|
||||
ctx.versym.contents.resize(n, VER_NDX_GLOBAL as u16);
|
||||
ctx.versym.contents[0] = VER_NDX_LOCAL as u16;
|
||||
|
||||
for &id in ctx.dynsym.symbols.iter().flatten() {
|
||||
let sym = &ctx.symbols[id];
|
||||
if !matches!(sym.file(), Some(FileId::Obj(_))) {
|
||||
continue;
|
||||
}
|
||||
let idx = sym.dynsym_idx(&ctx.symbols).unwrap() as usize;
|
||||
// An unversioned undefined symbol takes version index 0.
|
||||
if sym.ver_idx as u32 != VER_NDX_UNSPECIFIED {
|
||||
ctx.versym.contents[idx] = sym.ver_idx;
|
||||
} else if sym.is_undef() {
|
||||
ctx.versym.contents[idx] = VER_NDX_LOCAL as u16;
|
||||
}
|
||||
}
|
||||
|
||||
// Allocate a buffer for .gnu.version_d and write to it
|
||||
let verdef_size = ElfVerdef::<E>::size();
|
||||
let verdaux_size = ElfVerdaux::<E>::size();
|
||||
let mut contents: Vec<u8> = Vec::with_capacity(
|
||||
(verdef_size + verdaux_size) * (ctx.args.version_definitions.len() + 1),
|
||||
);
|
||||
let mut prev: Option<usize> = None;
|
||||
let mut count = 0u32;
|
||||
|
||||
let mut write = |contents: &mut Vec<u8>,
|
||||
dynstr: &mut super::super::symtab::DynstrSection<E>,
|
||||
verstr: &[u8],
|
||||
idx: u16,
|
||||
flags: u16| {
|
||||
count += 1;
|
||||
if let Some(p) = prev {
|
||||
let mut vd = ElfVerdef::<E>::parse(&contents[p..]);
|
||||
vd.vd_next.set((contents.len() - p) as u32);
|
||||
vd.write(&mut contents[p..]);
|
||||
}
|
||||
let pos = contents.len();
|
||||
prev = Some(pos);
|
||||
contents.resize(pos + verdef_size + verdaux_size, 0);
|
||||
ElfVerdef::<E> {
|
||||
vd_version: U16::new(1),
|
||||
vd_flags: U16::new(flags),
|
||||
vd_ndx: U16::new(idx),
|
||||
vd_cnt: U16::new(1),
|
||||
vd_hash: U32::new(elf_hash(verstr)),
|
||||
vd_aux: U32::new(verdef_size as u32),
|
||||
vd_next: U32::default(),
|
||||
}
|
||||
.write(&mut contents[pos..]);
|
||||
ElfVerdaux::<E> {
|
||||
vda_name: U32::new(dynstr.add_string(verstr) as u32),
|
||||
vda_next: U32::default(),
|
||||
}
|
||||
.write(&mut contents[pos + verdef_size..]);
|
||||
};
|
||||
|
||||
let soname = if ctx.args.soname.is_empty() {
|
||||
path_filename(&ctx.args.output)
|
||||
} else {
|
||||
ctx.args.soname.clone()
|
||||
};
|
||||
write(
|
||||
&mut contents,
|
||||
&mut ctx.dynstr,
|
||||
soname.as_bytes(),
|
||||
1,
|
||||
VER_FLG_BASE as u16,
|
||||
);
|
||||
|
||||
let defs = ctx.args.version_definitions.clone();
|
||||
for (i, verstr) in defs.iter().enumerate() {
|
||||
write(
|
||||
&mut contents,
|
||||
&mut ctx.dynstr,
|
||||
verstr.as_bytes(),
|
||||
VER_NDX_LAST_RESERVED as u16 + 1 + i as u16,
|
||||
0,
|
||||
);
|
||||
}
|
||||
|
||||
let verdef = ctx.verdef.as_mut().unwrap();
|
||||
verdef.hdr.shdr.sh_info.set(count);
|
||||
verdef.contents = contents;
|
||||
}
|
||||
|
||||
pub fn update_shdr<E: Arch>(ctx: &mut Context<E>) {
|
||||
let shndx = ctx.dynstr.hdr.shndx;
|
||||
let verdef = ctx.verdef.as_mut().unwrap();
|
||||
verdef.hdr.shdr.sh_size.set(verdef.contents.len() as u64);
|
||||
verdef.hdr.shdr.sh_link.set(shndx);
|
||||
}
|
||||
|
||||
pub fn copy_buf<E: Arch>(ctx: &Context<E>, buf: &mut [u8]) {
|
||||
let verdef = ctx.verdef.as_ref().unwrap();
|
||||
buf[..verdef.contents.len()].copy_from_slice(&verdef.contents);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
//! `.gnu.version`, symbol version indices.
|
||||
|
||||
use crate::arch::Arch;
|
||||
use crate::context::Context;
|
||||
use crate::elf::*;
|
||||
use crate::output_chunks::ChunkHeader;
|
||||
|
||||
// .gnu.version section contains version indices as a parallel array for
|
||||
// .dynsym. If a dynamic symbol is a defined one, its version information
|
||||
// is in .gnu.version_d. Otherwise, it's in .gnu.version_r.
|
||||
//
|
||||
// .gnu.version contains a parallel table for .dynsym to specify symbol
|
||||
// versions of undefined symbols. A symbol having an entry in .gnu.version
|
||||
// must be resolved to a symbol with the exact same version string at
|
||||
// runtime.
|
||||
#[derive(Debug)]
|
||||
pub struct VersymSection<E: Layout> {
|
||||
pub hdr: ChunkHeader<E>,
|
||||
pub contents: Vec<u16>,
|
||||
}
|
||||
|
||||
impl<E: Layout> VersymSection<E> {
|
||||
pub fn new() -> VersymSection<E> {
|
||||
let mut hdr = ChunkHeader::<E>::new(".gnu.version", SHT_GNU_VERSYM, SHF_ALLOC as u64);
|
||||
hdr.shdr.sh_entsize.set(2);
|
||||
hdr.shdr.sh_addralign.set(2);
|
||||
VersymSection {
|
||||
hdr,
|
||||
contents: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Layout> Default for VersymSection<E> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_shdr<E: Arch>(ctx: &mut Context<E>) {
|
||||
ctx.versym
|
||||
.hdr
|
||||
.shdr
|
||||
.sh_size
|
||||
.set(ctx.versym.contents.len() as u64 * 2);
|
||||
ctx.versym.hdr.shdr.sh_link.set(ctx.dynsym.hdr.shndx);
|
||||
}
|
||||
|
||||
pub fn copy_buf<E: Arch>(ctx: &Context<E>, buf: &mut [u8]) {
|
||||
for (i, &v) in ctx.versym.contents.iter().enumerate() {
|
||||
E::Endian::write_u16(&mut buf[i * 2..], v);
|
||||
}
|
||||
}
|
||||
+38
-34
@@ -24,20 +24,24 @@ use crate::input_files::{
|
||||
};
|
||||
use crate::input_sections::{InputSection, InputSectionId, SectionRef};
|
||||
use crate::linker_script::VersionPattern;
|
||||
use crate::output_chunks::dynamic::{DynamicSection, RelrDynSection};
|
||||
use crate::output_chunks::eh_frame::{EhFrameHdrSection, EhFrameRelocSection};
|
||||
use crate::output_chunks::misc::{
|
||||
self, BuildIdSection, GnuDebuglinkSection, InterpSection, NotePropertySection,
|
||||
RelroPaddingSection,
|
||||
};
|
||||
use crate::output_chunks::build_id::{self, BuildIdSection};
|
||||
use crate::output_chunks::dynamic::DynamicSection;
|
||||
use crate::output_chunks::eh_frame_hdr::EhFrameHdrSection;
|
||||
use crate::output_chunks::eh_frame_reloc::EhFrameRelocSection;
|
||||
use crate::output_chunks::gnu_debuglink::{self, GnuDebuglinkSection};
|
||||
use crate::output_chunks::gnu_hash::{self, GnuHashSection};
|
||||
use crate::output_chunks::hash::HashSection;
|
||||
use crate::output_chunks::interp::InterpSection;
|
||||
use crate::output_chunks::note_property::NotePropertySection;
|
||||
use crate::output_chunks::output_section::OutputSection;
|
||||
use crate::output_chunks::symtab::{
|
||||
self, GnuHashSection, HashSection, ShstrtabSection, SymtabShndxSection,
|
||||
};
|
||||
use crate::output_chunks::version::VerdefSection;
|
||||
use crate::output_chunks::relrdyn::RelrDynSection;
|
||||
use crate::output_chunks::relro_padding::RelroPaddingSection;
|
||||
use crate::output_chunks::shstrtab::ShstrtabSection;
|
||||
use crate::output_chunks::symtab_shndx::SymtabShndxSection;
|
||||
use crate::output_chunks::verdef::VerdefSection;
|
||||
use crate::output_chunks::{
|
||||
self, ChunkHeader, ChunkId, GdbIndexSection, OutputEhdr, OutputPhdr, OutputSectionId,
|
||||
OutputShdr,
|
||||
self, compressed, copyrel, dynsym, reloc, ChunkHeader, ChunkId, GdbIndexSection, OutputEhdr,
|
||||
OutputPhdr, OutputSectionId, OutputShdr,
|
||||
};
|
||||
use crate::output_file::OutputFile;
|
||||
use crate::symbol::{
|
||||
@@ -198,11 +202,13 @@ pub fn create_synthetic_sections<E: Arch>(ctx: &mut Context<E>) {
|
||||
chunks.push(ChunkId::NoteProperty);
|
||||
}
|
||||
if E::IS_RISCV {
|
||||
ctx.riscv_attributes = Some(crate::output_chunks::misc::RiscvAttributesSection::new());
|
||||
ctx.riscv_attributes =
|
||||
Some(crate::output_chunks::riscv_attributes::RiscvAttributesSection::new());
|
||||
chunks.push(ChunkId::RiscvAttributes);
|
||||
}
|
||||
if E::FAMILY == Family::Ppc64V2 {
|
||||
ctx.ppc64_save_restore = Some(crate::output_chunks::misc::Ppc64SaveRestoreSection::new());
|
||||
ctx.ppc64_save_restore =
|
||||
Some(crate::output_chunks::ppc64_save_restore::Ppc64SaveRestoreSection::new());
|
||||
chunks.push(ChunkId::Ppc64SaveRestore);
|
||||
}
|
||||
if E::FAMILY == Family::Ppc64V1 {
|
||||
@@ -2789,7 +2795,7 @@ pub fn scan_relocations<E: Arch>(ctx: &mut Context<E>) {
|
||||
};
|
||||
|
||||
if ctx.needs_tlsld.load(Ordering::Relaxed) {
|
||||
crate::output_chunks::got::got::add_tlsld(ctx);
|
||||
crate::output_chunks::got::add_tlsld(ctx);
|
||||
}
|
||||
|
||||
// Every dynamic symbol gets its auxiliary record. The loop below
|
||||
@@ -2811,10 +2817,10 @@ pub fn scan_relocations<E: Arch>(ctx: &mut Context<E>) {
|
||||
};
|
||||
|
||||
if is_imported || is_exported {
|
||||
symtab::dynsym::add_symbol(ctx, id);
|
||||
dynsym::add_symbol(ctx, id);
|
||||
}
|
||||
if flags & NEEDS_GOT != 0 {
|
||||
crate::output_chunks::got::got::add_got_symbol(ctx, id);
|
||||
crate::output_chunks::got::add_got_symbol(ctx, id);
|
||||
}
|
||||
if flags & NEEDS_CANONICAL != 0 && ty == STT_FUNC {
|
||||
let sym = &mut ctx.symbols[id];
|
||||
@@ -2826,22 +2832,22 @@ pub fn scan_relocations<E: Arch>(ctx: &mut Context<E>) {
|
||||
// We can't use .plt.got for a canonical PLT because otherwise
|
||||
// .plt.got and .got would refer to each other, resulting in an
|
||||
// infinite loop at runtime.
|
||||
crate::output_chunks::got::plt::add_symbol(ctx, id);
|
||||
crate::output_chunks::plt::add_symbol(ctx, id);
|
||||
} else if flags & NEEDS_PLT != 0 {
|
||||
if flags & NEEDS_GOT != 0 {
|
||||
crate::output_chunks::got::pltgot::add_symbol(ctx, id);
|
||||
crate::output_chunks::pltgot::add_symbol(ctx, id);
|
||||
} else {
|
||||
crate::output_chunks::got::plt::add_symbol(ctx, id);
|
||||
crate::output_chunks::plt::add_symbol(ctx, id);
|
||||
}
|
||||
}
|
||||
if flags & NEEDS_GOTTP != 0 {
|
||||
crate::output_chunks::got::got::add_gottp_symbol(ctx, id);
|
||||
crate::output_chunks::got::add_gottp_symbol(ctx, id);
|
||||
}
|
||||
if flags & NEEDS_TLSGD != 0 {
|
||||
crate::output_chunks::got::got::add_tlsgd_symbol(ctx, id);
|
||||
crate::output_chunks::got::add_tlsgd_symbol(ctx, id);
|
||||
}
|
||||
if flags & NEEDS_TLSDESC != 0 {
|
||||
crate::output_chunks::got::got::add_tlsdesc_symbol(ctx, id);
|
||||
crate::output_chunks::got::add_tlsdesc_symbol(ctx, id);
|
||||
}
|
||||
if flags & NEEDS_CANONICAL != 0 && ty != STT_FUNC {
|
||||
let relro = ctx.args.z_relro
|
||||
@@ -2849,7 +2855,7 @@ pub fn scan_relocations<E: Arch>(ctx: &mut Context<E>) {
|
||||
Some(FileId::Dso(dso)) => ctx.dsos[dso.index()].is_readonly(&ctx.symbols[id]),
|
||||
_ => false,
|
||||
};
|
||||
misc::copyrel::add_symbol(ctx, relro, id);
|
||||
copyrel::add_symbol(ctx, relro, id);
|
||||
}
|
||||
if E::FAMILY == Family::Ppc64V1 && flags & NEEDS_PPC_OPD != 0 {
|
||||
crate::output_chunks::opd::add_symbol(ctx, id);
|
||||
@@ -2931,11 +2937,9 @@ pub fn create_reloc_sections<E: Arch>(ctx: &mut Context<E>) {
|
||||
.iter()
|
||||
.filter_map(|c| c.as_output_section())
|
||||
.collect();
|
||||
let secs: Vec<misc::RelocSection<E>> = {
|
||||
let secs: Vec<reloc::RelocSection<E>> = {
|
||||
let ctx_ref: &Context<E> = ctx;
|
||||
ids.par_iter()
|
||||
.map(|&id| misc::reloc::new(ctx_ref, id))
|
||||
.collect()
|
||||
ids.par_iter().map(|&id| reloc::new(ctx_ref, id)).collect()
|
||||
};
|
||||
for (id, sec) in ids.into_iter().zip(secs) {
|
||||
ctx.reloc_sections.push(sec);
|
||||
@@ -2972,7 +2976,7 @@ pub fn sort_dynsyms<E: Arch>(ctx: &mut Context<E>) {
|
||||
// subset of it. Every dynamic symbol already has an auxiliary record.
|
||||
unsafe {
|
||||
symbols.par_for_each_aux_mut(&exported, |_, sym, aux| {
|
||||
aux.djb_hash = symtab::djb_hash(sym.name());
|
||||
aux.djb_hash = gnu_hash::djb_hash(sym.name());
|
||||
});
|
||||
}
|
||||
exported.par_sort_unstable_by(|&a, &b| {
|
||||
@@ -4077,7 +4081,7 @@ pub fn set_osec_offsets<E: Arch>(ctx: &mut Context<E>) -> u64 {
|
||||
|
||||
if ctx.args.pack_dyn_relocs_android {
|
||||
let before = ctx.reldyn.hdr.shdr.sh_size.get();
|
||||
crate::output_chunks::dynamic::reldyn::update_shdr(ctx);
|
||||
crate::output_chunks::reldyn::update_shdr(ctx);
|
||||
if before != ctx.reldyn.hdr.shdr.sh_size.get() {
|
||||
continue;
|
||||
}
|
||||
@@ -4382,11 +4386,11 @@ pub fn compress_debug_sections<E: Arch>(ctx: &mut Context<E>) {
|
||||
})
|
||||
.map(|(i, &id)| (i, id))
|
||||
.collect();
|
||||
let compressed: Vec<misc::CompressedSection<E>> = {
|
||||
let compressed: Vec<compressed::CompressedSection<E>> = {
|
||||
let ctx_ref: &Context<E> = ctx;
|
||||
targets
|
||||
.par_iter()
|
||||
.map(|&(_, id)| misc::compressed::new(ctx_ref, id))
|
||||
.map(|&(_, id)| compressed::new(ctx_ref, id))
|
||||
.collect()
|
||||
};
|
||||
for ((i, _), sec) in targets.into_iter().zip(compressed) {
|
||||
@@ -4443,7 +4447,7 @@ pub fn write_build_id<E: Arch>(ctx: &mut Context<E>, buf: &mut [u8], is_mmapped:
|
||||
let hdr = ctx.buildid.as_ref().unwrap().hdr.shdr;
|
||||
let start = hdr.sh_offset.get() as usize;
|
||||
let end = (hdr.sh_offset.get() + hdr.sh_size.get()) as usize;
|
||||
misc::build_id::copy_buf(ctx, &mut buf[start..end]);
|
||||
build_id::copy_buf(ctx, &mut buf[start..end]);
|
||||
}
|
||||
|
||||
// A .gnu_debuglink section contains a filename and a CRC32 checksum of a
|
||||
@@ -4478,7 +4482,7 @@ pub fn write_gnu_debuglink<E: Arch>(ctx: &mut Context<E>, buf: &mut [u8]) {
|
||||
let hdr = ctx.gnu_debuglink.as_ref().unwrap().hdr.shdr;
|
||||
let start = hdr.sh_offset.get() as usize;
|
||||
let end = (hdr.sh_offset.get() + hdr.sh_size.get()) as usize;
|
||||
misc::gnu_debuglink::copy_buf(ctx, &mut buf[start..end]);
|
||||
gnu_debuglink::copy_buf(ctx, &mut buf[start..end]);
|
||||
}
|
||||
|
||||
/// The CRC32 of a large buffer, computed in parallel.
|
||||
|
||||
+6
-4
@@ -36,10 +36,12 @@ use crate::arch::Arch;
|
||||
use crate::context::Context;
|
||||
use crate::elf::*;
|
||||
use crate::input_files::FileId;
|
||||
use crate::output_chunks::eh_frame::EhFrameRelocSection;
|
||||
use crate::output_chunks::misc::{ComdatGroupSection, NotePropertySection, RiscvAttributesSection};
|
||||
use crate::output_chunks::sframe::SFrameRelocSection;
|
||||
use crate::output_chunks::symtab::ShstrtabSection;
|
||||
use crate::output_chunks::comdat_group::ComdatGroupSection;
|
||||
use crate::output_chunks::eh_frame_reloc::EhFrameRelocSection;
|
||||
use crate::output_chunks::note_property::NotePropertySection;
|
||||
use crate::output_chunks::riscv_attributes::RiscvAttributesSection;
|
||||
use crate::output_chunks::sframe_reloc::SFrameRelocSection;
|
||||
use crate::output_chunks::shstrtab::ShstrtabSection;
|
||||
use crate::output_chunks::{self, ChunkId, OutputEhdr, OutputShdr};
|
||||
use crate::output_file::OutputFile;
|
||||
use crate::passes;
|
||||
|
||||
+3
-3
@@ -1079,9 +1079,9 @@ impl Symbol {
|
||||
|
||||
pub fn gotplt_addr<E: Arch>(&self, ctx: &Context<E>) -> u64 {
|
||||
ctx.gotplt.hdr.shdr.sh_addr.get()
|
||||
+ crate::output_chunks::got::gotplt::header_size::<E>()
|
||||
+ crate::output_chunks::gotplt::header_size::<E>()
|
||||
+ self.plt_idx(&ctx.symbols).unwrap() as u64
|
||||
* crate::output_chunks::got::gotplt::entry_size::<E>()
|
||||
* crate::output_chunks::gotplt::entry_size::<E>()
|
||||
}
|
||||
|
||||
pub fn gottp_addr<E: Arch>(&self, ctx: &Context<E>) -> u64 {
|
||||
@@ -1103,7 +1103,7 @@ impl Symbol {
|
||||
pub fn plt_addr<E: Arch>(&self, ctx: &Context<E>) -> u64 {
|
||||
if let Some(idx) = self.plt_idx(&ctx.symbols) {
|
||||
return ctx.plt.hdr.shdr.sh_addr.get()
|
||||
+ crate::output_chunks::got::plt::entry_offset::<E>(idx);
|
||||
+ crate::output_chunks::plt::entry_offset::<E>(idx);
|
||||
}
|
||||
ctx.pltgot.hdr.shdr.sh_addr.get()
|
||||
+ self.pltgot_idx(&ctx.symbols).unwrap() as u64 * E::PLTGOT_SIZE
|
||||
|
||||
Reference in new issue
Block a user