use anyhow::{anyhow, Result};
use std::collections::HashMap;
use super::{
mmap::{MappedSection, MmapOptions},
module::{CodegenModule, CodegenModuleEmit, CodegenModuleJit, CodegenModuleLink},
relocations::{
handle_jump_entry, handle_relocation, is_jump_table_entry, relocation_target_section,
symbol_offset, JumpTableEntry,
},
};
use object::{Object, ObjectSection, ObjectSymbol, SectionKind};
pub struct ObjectModule {
object_buffer: Vec<u8>,
sections: HashMap<String, MappedSection>,
symbols: HashMap<String, isize>,
code_section_name: String,
}
impl CodegenModule for ObjectModule {}
impl CodegenModuleLink for ObjectModule {
fn from_object(buffer: &[u8]) -> Result<Self> {
let mut mapped_sections = HashMap::new();
let file = object::File::parse(buffer)?;
let mut text_sec = None;
for section in file.sections() {
if let SectionKind::Text = section.kind() {
if text_sec.is_some() {
return Err(anyhow!("Multiple .text sections found"));
}
text_sec = Some(section);
}
}
let text_sec = text_sec.ok_or_else(|| anyhow!("Could not find .text section"))?;
let min_page_size = MmapOptions::page_size();
let mut sections_to_map = HashMap::new();
sections_to_map.insert(
text_sec.name().expect("Could not get section name"),
text_sec.index(),
);
let jumptable_entry_size = std::mem::size_of::<JumpTableEntry>();
let mut jumptable_num_entries: usize = 0;
let sections_to_map = text_sec
.relocations()
.fold(sections_to_map, |mut acc, (_, rela)| {
if let Some(section) = relocation_target_section(&file, &rela) {
if section.index() == text_sec.index() {
return acc;
}
let section_name = section.name().expect("Could not get section name");
acc.insert(section_name, section.index());
}
if is_jump_table_entry(&file, &rela) {
jumptable_num_entries += 1;
}
acc
});
let jumptable_size =
(jumptable_num_entries * jumptable_entry_size).div_ceil(min_page_size) * min_page_size;
let mapped_sizes = sections_to_map
.values()
.map(|section| {
let section = file
.section_by_index(*section)
.expect("Could not find section");
let section_size = section.size() as usize;
section_size.div_ceil(min_page_size) * min_page_size
})
.collect::<Vec<_>>();
let mut map =
MmapOptions::new(mapped_sizes.iter().sum::<usize>() + jumptable_size + min_page_size)
.unwrap()
.map()
.unwrap();
for ((name, section_id), size) in sections_to_map.iter().zip(mapped_sizes.iter()) {
let section = file
.section_by_index(*section_id)
.expect("Could not find section");
let mut section_map = map.split_to(*size).unwrap().make_mut().unwrap();
let section_data = section.data().expect("Could not get section data");
section_map.as_mut_slice()[..section_data.len()].copy_from_slice(section_data);
match section.kind() {
SectionKind::Data
| SectionKind::Text
| SectionKind::Common
| SectionKind::UninitializedData
| SectionKind::UninitializedTls => {
mapped_sections.insert(name.to_string(), MappedSection::Mutable(section_map));
}
_ => {
mapped_sections.insert(
name.to_string(),
MappedSection::Immutable(section_map.make_read_only().unwrap()),
);
}
}
}
let mut jumptable_map = if jumptable_size > 0 {
Some(map.split_to(jumptable_size).unwrap().make_mut().unwrap())
} else {
None
};
let mut jumptable_idx = 0;
let mut jumptable = jumptable_map
.as_mut()
.map(|jumptable_map| JumpTableEntry::from_bytes(jumptable_map.as_mut_slice()));
for (offset, rela) in text_sec.relocations() {
let text_ptr = mapped_sections
.get_mut(text_sec.name().unwrap())
.unwrap()
.as_mut_ptr()
.unwrap();
let patch_ptr = unsafe { text_ptr.offset(offset as isize) };
if is_jump_table_entry(&file, &rela) {
let jumptable_entry = &mut jumptable.as_mut().unwrap()[jumptable_idx];
handle_jump_entry(&file, &rela, patch_ptr, jumptable_entry)?;
jumptable_idx += 1;
} else {
handle_relocation(&file, &rela, patch_ptr, &mapped_sections)?;
}
}
if let Some(jumptable_map) = jumptable_map {
mapped_sections.insert(
"jumptable".to_string(),
MappedSection::Immutable(
jumptable_map.make_read_only().unwrap().make_exec().unwrap(),
),
);
}
let mut text_map = mapped_sections.remove(text_sec.name().unwrap()).unwrap();
text_map = text_map.make_read_only().unwrap().make_exec().unwrap();
mapped_sections.insert(text_sec.name().unwrap().to_string(), text_map);
let text_sec_name = text_sec.name().unwrap();
let mut symbol_map = HashMap::new();
for symbol in file.symbols() {
if let Ok(name) = symbol.name() {
if let Some(section_index) = symbol.section_index() {
let section = file
.section_by_index(section_index)
.expect("Could not find section");
if let SectionKind::Text = section.kind() {
let offset = symbol_offset(&file, &symbol, §ion)?;
let name = name.strip_prefix("_").unwrap_or(name);
symbol_map.insert(name.to_string(), offset);
}
}
}
}
Ok(Self {
object_buffer: buffer.to_vec(),
sections: mapped_sections,
symbols: symbol_map,
code_section_name: text_sec_name.to_string(),
})
}
}
impl CodegenModuleEmit for ObjectModule {
fn to_object(&self) -> Result<Vec<u8>> {
Ok(self.object_buffer.clone())
}
}
impl CodegenModuleJit for ObjectModule {
fn jit(&mut self) -> Result<HashMap<String, *const u8>> {
let mut symbol_map = HashMap::new();
for (name, offset) in &self.symbols {
let section = self.sections.get(self.code_section_name.as_str()).unwrap();
let func_ptr = unsafe { section.as_ptr().offset(*offset) };
symbol_map.insert(name.clone(), func_ptr);
}
Ok(symbol_map)
}
}