use object::write::{Object as Writer, SymbolId};
use object::{SectionFlags, SectionKind, SymbolFlags, elf};
use crate::section::{Array, Binding, Property, Reference, Visibility};
pub(crate) fn r_type(reference: Reference) -> Option<elf::RelocationType> {
Some(match reference {
Reference::Call => elf::R_X86_64_PLT32,
Reference::Data => elf::R_X86_64_PC32,
Reference::Got => elf::R_X86_64_REX_GOTPCRELX,
Reference::Thread => elf::R_X86_64_GOTTPOFF,
Reference::Address { bytes: 8 } => elf::R_X86_64_64,
Reference::Address { bytes: 4 } => elf::R_X86_64_32,
Reference::Address { .. } | Reference::Image => return None,
})
}
pub(crate) fn see(obj: &mut Writer<'_>, id: SymbolId, binding: Binding, visibility: Visibility) {
if binding == Binding::Local {
return;
}
let wanted = match visibility {
Visibility::Default => elf::STV_DEFAULT,
Visibility::Hidden => elf::STV_HIDDEN,
Visibility::Protected => elf::STV_PROTECTED,
};
if let SymbolFlags::Elf { st_other, .. } = obj.symbol_flags_mut(id) {
*st_other = st_other.with_visibility(wanted);
}
}
pub(crate) fn gathered(array: Array) -> SectionFlags {
SectionFlags::Elf {
sh_type: match array {
Array::Init => elf::SHT_INIT_ARRAY,
Array::Fini => elf::SHT_FINI_ARRAY,
Array::Preinit => elf::SHT_PREINIT_ARRAY,
},
sh_flags: elf::SHF_ALLOC | elf::SHF_WRITE,
}
}
pub(crate) fn ordered() -> SectionFlags {
SectionFlags::Elf {
sh_type: elf::SHT_PROGBITS,
sh_flags: elf::SHF_ALLOC | elf::SHF_WRITE | elf::SHF_LINK_ORDER,
}
}
pub(crate) const PATCHABLE: &str = "__patchable_function_entries";
pub(crate) const FRAMES: (&str, u64) = (".eh_frame", 8);
pub(crate) const REL_RO_LOCAL: Option<&str> = Some(".data.rel.ro.local");
pub(crate) fn marker(obj: &mut Writer<'_>) {
obj.add_section(Vec::new(), b".note.GNU-stack".to_vec(), SectionKind::Metadata);
}
pub(crate) fn link(bytes: &mut [u8], ordered: &[String]) {
if ordered.is_empty() {
return;
}
let word = |bytes: &[u8], at: usize| u64::from_le_bytes(bytes[at..at + 8].try_into().unwrap());
let short = |bytes: &[u8], at: usize| u16::from_le_bytes(bytes[at..at + 2].try_into().unwrap());
let long = |bytes: &[u8], at: usize| u32::from_le_bytes(bytes[at..at + 4].try_into().unwrap());
let headers = word(bytes, 0x28) as usize;
let step = short(bytes, 0x3a) as usize;
let count = short(bytes, 0x3c) as usize;
let strings = word(bytes, headers + short(bytes, 0x3e) as usize * step + 24) as usize;
let name = |bytes: &[u8], header: usize| {
let at = strings + long(bytes, header) as usize;
let end = bytes[at..].iter().position(|byte| *byte == 0).map_or(at, |len| at + len);
String::from_utf8_lossy(&bytes[at..end]).into_owned()
};
let names: Vec<String> = (0..count).map(|i| name(bytes, headers + i * step)).collect();
let mut wanted = ordered.iter();
for (i, section) in names.iter().enumerate() {
if section != PATCHABLE {
continue;
}
let Some(target) = wanted.next() else { break };
let Some(at) = names.iter().position(|name| name == target) else { continue };
let at = u32::try_from(at).expect("a file with this many sections in it");
let sh_link = headers + i * step + 40;
bytes[sh_link..sh_link + 4].copy_from_slice(&at.to_le_bytes());
}
debug_assert!(wanted.next().is_none(), "a record whose header nothing found");
}
pub(crate) fn record(property: Property) -> Vec<u8> {
let head = [4, 16, elf::NT_GNU_PROPERTY_TYPE_0.0];
let desc = [Property::X86_FEATURES, 4, property.features, 0];
let mut out = Vec::with_capacity(32);
for word in head {
out.extend_from_slice(&word.to_le_bytes());
}
out.extend_from_slice(b"GNU\0");
for word in desc {
out.extend_from_slice(&word.to_le_bytes());
}
out
}