Refactor CompiledModule to separate compile and linking stages (#1831)
* Refactor how relocs are stored and handled * refactor CompiledModule::instantiate and link_module * Refactor DWARF creation: split generation and serialization * Separate DWARF data transform from instantiation * rm LinkContext
This commit is contained in:
@@ -3,117 +3,99 @@
|
||||
#![allow(clippy::cast_ptr_alignment)]
|
||||
|
||||
use anyhow::Error;
|
||||
use faerie::{Artifact, Decl};
|
||||
use gimli::write::{Address, FrameTable};
|
||||
use faerie::{Artifact, Decl, SectionKind};
|
||||
use more_asserts::assert_gt;
|
||||
use target_lexicon::BinaryFormat;
|
||||
use wasmtime_environ::isa::{unwind::UnwindInfo, TargetIsa};
|
||||
use wasmtime_environ::{Compilation, ModuleAddressMap, ModuleVmctxInfo, ValueLabelsRanges};
|
||||
use wasmtime_environ::isa::TargetIsa;
|
||||
|
||||
pub use crate::read_debuginfo::{read_debuginfo, DebugInfoData, WasmFileInfo};
|
||||
pub use crate::transform::transform_dwarf;
|
||||
pub use crate::write_debuginfo::{emit_dwarf, ResolvedSymbol, SymbolResolver};
|
||||
pub use crate::write_debuginfo::{emit_dwarf, DwarfSection};
|
||||
|
||||
mod gc;
|
||||
mod read_debuginfo;
|
||||
mod transform;
|
||||
mod write_debuginfo;
|
||||
|
||||
struct FunctionRelocResolver {}
|
||||
impl SymbolResolver for FunctionRelocResolver {
|
||||
fn resolve_symbol(&self, symbol: usize, addend: i64) -> ResolvedSymbol {
|
||||
let name = format!("_wasm_function_{}", symbol);
|
||||
ResolvedSymbol::Reloc { name, addend }
|
||||
pub fn write_debugsections(obj: &mut Artifact, sections: Vec<DwarfSection>) -> Result<(), Error> {
|
||||
let (bodies, relocs) = sections
|
||||
.into_iter()
|
||||
.map(|s| ((s.name.clone(), s.body), (s.name, s.relocs)))
|
||||
.unzip::<_, _, Vec<_>, Vec<_>>();
|
||||
for (name, body) in bodies {
|
||||
obj.declare_with(name, Decl::section(SectionKind::Debug), body)?;
|
||||
}
|
||||
}
|
||||
|
||||
fn create_frame_table<'a>(
|
||||
isa: &dyn TargetIsa,
|
||||
infos: impl Iterator<Item = &'a Option<UnwindInfo>>,
|
||||
) -> Option<FrameTable> {
|
||||
let mut table = FrameTable::default();
|
||||
|
||||
let cie_id = table.add_cie(isa.create_systemv_cie()?);
|
||||
|
||||
for (i, info) in infos.enumerate() {
|
||||
if let Some(UnwindInfo::SystemV(info)) = info {
|
||||
table.add_fde(
|
||||
cie_id,
|
||||
info.to_fde(Address::Symbol {
|
||||
symbol: i,
|
||||
addend: 0,
|
||||
}),
|
||||
);
|
||||
for (name, relocs) in relocs {
|
||||
for reloc in relocs {
|
||||
obj.link_with(
|
||||
faerie::Link {
|
||||
from: &name,
|
||||
to: &reloc.target,
|
||||
at: reloc.offset as u64,
|
||||
},
|
||||
faerie::Reloc::Debug {
|
||||
size: reloc.size,
|
||||
addend: reloc.addend,
|
||||
},
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
Some(table)
|
||||
}
|
||||
|
||||
pub fn emit_debugsections(
|
||||
obj: &mut Artifact,
|
||||
vmctx_info: &ModuleVmctxInfo,
|
||||
isa: &dyn TargetIsa,
|
||||
debuginfo_data: &DebugInfoData,
|
||||
at: &ModuleAddressMap,
|
||||
ranges: &ValueLabelsRanges,
|
||||
compilation: &Compilation,
|
||||
) -> Result<(), Error> {
|
||||
let resolver = FunctionRelocResolver {};
|
||||
let dwarf = transform_dwarf(isa, debuginfo_data, at, vmctx_info, ranges)?;
|
||||
let frame_table = create_frame_table(isa, compilation.into_iter().map(|f| &f.unwind_info));
|
||||
|
||||
emit_dwarf(obj, dwarf, &resolver, frame_table)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
struct ImageRelocResolver<'a> {
|
||||
func_offsets: &'a Vec<u64>,
|
||||
}
|
||||
|
||||
impl<'a> SymbolResolver for ImageRelocResolver<'a> {
|
||||
fn resolve_symbol(&self, symbol: usize, addend: i64) -> ResolvedSymbol {
|
||||
let func_start = self.func_offsets[symbol];
|
||||
ResolvedSymbol::PhysicalAddress(func_start + addend as u64)
|
||||
fn patch_dwarf_sections(sections: &mut [DwarfSection], funcs: &[*const u8]) {
|
||||
for section in sections {
|
||||
const FUNC_SYMBOL_PREFIX: &str = "_wasm_function_";
|
||||
for reloc in section.relocs.iter() {
|
||||
if !reloc.target.starts_with(FUNC_SYMBOL_PREFIX) {
|
||||
// Fixing only "all" section relocs -- all functions are merged
|
||||
// into one blob.
|
||||
continue;
|
||||
}
|
||||
let func_index = reloc.target[FUNC_SYMBOL_PREFIX.len()..]
|
||||
.parse::<usize>()
|
||||
.expect("func index");
|
||||
let target = (funcs[func_index] as u64).wrapping_add(reloc.addend as i64 as u64);
|
||||
let entry_ptr = section.body
|
||||
[reloc.offset as usize..reloc.offset as usize + reloc.size as usize]
|
||||
.as_mut_ptr();
|
||||
unsafe {
|
||||
match reloc.size {
|
||||
4 => std::ptr::write(entry_ptr as *mut u32, target as u32),
|
||||
8 => std::ptr::write(entry_ptr as *mut u64, target),
|
||||
_ => panic!("unexpected reloc entry size"),
|
||||
}
|
||||
}
|
||||
}
|
||||
section
|
||||
.relocs
|
||||
.retain(|r| !r.target.starts_with(FUNC_SYMBOL_PREFIX));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn emit_debugsections_image(
|
||||
pub fn write_debugsections_image(
|
||||
isa: &dyn TargetIsa,
|
||||
debuginfo_data: &DebugInfoData,
|
||||
vmctx_info: &ModuleVmctxInfo,
|
||||
at: &ModuleAddressMap,
|
||||
ranges: &ValueLabelsRanges,
|
||||
funcs: &[(*const u8, usize)],
|
||||
compilation: &Compilation,
|
||||
mut sections: Vec<DwarfSection>,
|
||||
code_region: (*const u8, usize),
|
||||
funcs: &[*const u8],
|
||||
) -> Result<Vec<u8>, Error> {
|
||||
let func_offsets = &funcs
|
||||
.iter()
|
||||
.map(|(ptr, _)| *ptr as u64)
|
||||
.collect::<Vec<u64>>();
|
||||
let mut obj = Artifact::new(isa.triple().clone(), String::from("module"));
|
||||
let resolver = ImageRelocResolver { func_offsets };
|
||||
let dwarf = transform_dwarf(isa, debuginfo_data, at, vmctx_info, ranges)?;
|
||||
|
||||
// Assuming all functions in the same code block, looking min/max of its range.
|
||||
assert!(!code_region.0.is_null() && code_region.1 > 0);
|
||||
assert_gt!(funcs.len(), 0);
|
||||
let mut segment_body: (usize, usize) = (!0, 0);
|
||||
for (body_ptr, body_len) in funcs {
|
||||
segment_body.0 = std::cmp::min(segment_body.0, *body_ptr as usize);
|
||||
segment_body.1 = std::cmp::max(segment_body.1, *body_ptr as usize + body_len);
|
||||
}
|
||||
let segment_body = (segment_body.0 as *const u8, segment_body.1 - segment_body.0);
|
||||
|
||||
let body = unsafe { std::slice::from_raw_parts(segment_body.0, segment_body.1) };
|
||||
let body = unsafe { std::slice::from_raw_parts(code_region.0, code_region.1) };
|
||||
obj.declare_with("all", Decl::function(), body.to_vec())?;
|
||||
|
||||
let frame_table = create_frame_table(isa, compilation.into_iter().map(|f| &f.unwind_info));
|
||||
emit_dwarf(&mut obj, dwarf, &resolver, frame_table)?;
|
||||
// Get DWARF sections and patch relocs
|
||||
patch_dwarf_sections(&mut sections, funcs);
|
||||
|
||||
write_debugsections(&mut obj, sections)?;
|
||||
|
||||
// LLDB is too "magical" about mach-o, generating elf
|
||||
let mut bytes = obj.emit_as(BinaryFormat::Elf)?;
|
||||
// elf is still missing details...
|
||||
convert_faerie_elf_to_loadable_file(&mut bytes, segment_body.0);
|
||||
convert_faerie_elf_to_loadable_file(&mut bytes, code_region.0);
|
||||
|
||||
// let mut file = ::std::fs::File::create(::std::path::Path::new("test.o")).expect("file");
|
||||
// ::std::io::Write::write(&mut file, &bytes).expect("write");
|
||||
@@ -170,7 +152,7 @@ fn convert_faerie_elf_to_loadable_file(bytes: &mut Vec<u8>, code_ptr: *const u8)
|
||||
}
|
||||
|
||||
assert!(segment.is_none());
|
||||
// Functions was added at emit_debugsections_image as .text.all.
|
||||
// Functions was added at write_debugsections_image as .text.all.
|
||||
// Patch vaddr, and save file location and its size.
|
||||
unsafe {
|
||||
*(bytes.as_ptr().offset(off + 0x10) as *mut u64) = code_ptr as u64;
|
||||
|
||||
@@ -1,84 +1,63 @@
|
||||
use faerie::artifact::{Decl, SectionKind};
|
||||
use faerie::*;
|
||||
pub use crate::read_debuginfo::{read_debuginfo, DebugInfoData, WasmFileInfo};
|
||||
pub use crate::transform::transform_dwarf;
|
||||
use gimli::write::{Address, Dwarf, EndianVec, FrameTable, Result, Sections, Writer};
|
||||
use gimli::{RunTimeEndian, SectionId};
|
||||
use wasmtime_environ::isa::{unwind::UnwindInfo, TargetIsa};
|
||||
use wasmtime_environ::{Compilation, ModuleAddressMap, ModuleVmctxInfo, ValueLabelsRanges};
|
||||
|
||||
#[derive(Clone)]
|
||||
struct DebugReloc {
|
||||
offset: u32,
|
||||
size: u8,
|
||||
name: String,
|
||||
addend: i64,
|
||||
pub struct DwarfSectionReloc {
|
||||
pub target: String,
|
||||
pub offset: u32,
|
||||
pub addend: i32,
|
||||
pub size: u8,
|
||||
}
|
||||
|
||||
pub enum ResolvedSymbol {
|
||||
PhysicalAddress(u64),
|
||||
Reloc { name: String, addend: i64 },
|
||||
pub struct DwarfSection {
|
||||
pub name: String,
|
||||
pub body: Vec<u8>,
|
||||
pub relocs: Vec<DwarfSectionReloc>,
|
||||
}
|
||||
|
||||
pub trait SymbolResolver {
|
||||
fn resolve_symbol(&self, symbol: usize, addend: i64) -> ResolvedSymbol;
|
||||
}
|
||||
|
||||
pub fn emit_dwarf(
|
||||
artifact: &mut Artifact,
|
||||
fn emit_dwarf_sections(
|
||||
mut dwarf: Dwarf,
|
||||
symbol_resolver: &dyn SymbolResolver,
|
||||
frames: Option<FrameTable>,
|
||||
) -> anyhow::Result<()> {
|
||||
let endian = RunTimeEndian::Little;
|
||||
|
||||
let mut sections = Sections::new(WriterRelocate::new(endian, symbol_resolver));
|
||||
) -> anyhow::Result<Vec<DwarfSection>> {
|
||||
let mut sections = Sections::new(WriterRelocate::default());
|
||||
dwarf.write(&mut sections)?;
|
||||
if let Some(frames) = frames {
|
||||
frames.write_debug_frame(&mut sections.debug_frame)?;
|
||||
}
|
||||
|
||||
let mut result = Vec::new();
|
||||
sections.for_each_mut(|id, s| -> anyhow::Result<()> {
|
||||
artifact.declare_with(
|
||||
id.name(),
|
||||
Decl::section(SectionKind::Debug),
|
||||
s.writer.take(),
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
sections.for_each_mut(|id, s| -> anyhow::Result<()> {
|
||||
for reloc in &s.relocs {
|
||||
artifact.link_with(
|
||||
faerie::Link {
|
||||
from: id.name(),
|
||||
to: &reloc.name,
|
||||
at: u64::from(reloc.offset),
|
||||
},
|
||||
faerie::Reloc::Debug {
|
||||
size: reloc.size,
|
||||
addend: reloc.addend as i32,
|
||||
},
|
||||
)?;
|
||||
}
|
||||
let name = id.name().to_string();
|
||||
let body = s.writer.take();
|
||||
let mut relocs = vec![];
|
||||
::std::mem::swap(&mut relocs, &mut s.relocs);
|
||||
result.push(DwarfSection { name, body, relocs });
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct WriterRelocate<'a> {
|
||||
relocs: Vec<DebugReloc>,
|
||||
pub struct WriterRelocate {
|
||||
relocs: Vec<DwarfSectionReloc>,
|
||||
writer: EndianVec<RunTimeEndian>,
|
||||
symbol_resolver: &'a dyn SymbolResolver,
|
||||
}
|
||||
|
||||
impl<'a> WriterRelocate<'a> {
|
||||
pub fn new(endian: RunTimeEndian, symbol_resolver: &'a dyn SymbolResolver) -> Self {
|
||||
impl Default for WriterRelocate {
|
||||
fn default() -> Self {
|
||||
WriterRelocate {
|
||||
relocs: Vec::new(),
|
||||
writer: EndianVec::new(endian),
|
||||
symbol_resolver,
|
||||
writer: EndianVec::new(RunTimeEndian::Little),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Writer for WriterRelocate<'a> {
|
||||
impl Writer for WriterRelocate {
|
||||
type Endian = RunTimeEndian;
|
||||
|
||||
fn endian(&self) -> Self::Endian {
|
||||
@@ -101,31 +80,27 @@ impl<'a> Writer for WriterRelocate<'a> {
|
||||
match address {
|
||||
Address::Constant(val) => self.write_udata(val, size),
|
||||
Address::Symbol { symbol, addend } => {
|
||||
match self.symbol_resolver.resolve_symbol(symbol, addend as i64) {
|
||||
ResolvedSymbol::PhysicalAddress(addr) => self.write_udata(addr, size),
|
||||
ResolvedSymbol::Reloc { name, addend } => {
|
||||
let offset = self.len() as u64;
|
||||
self.relocs.push(DebugReloc {
|
||||
offset: offset as u32,
|
||||
size,
|
||||
name,
|
||||
addend,
|
||||
});
|
||||
self.write_udata(addend as u64, size)
|
||||
}
|
||||
}
|
||||
let target = format!("_wasm_function_{}", symbol);
|
||||
let offset = self.len() as u32;
|
||||
self.relocs.push(DwarfSectionReloc {
|
||||
target,
|
||||
offset,
|
||||
size,
|
||||
addend: addend as i32,
|
||||
});
|
||||
self.write_udata(addend as u64, size)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn write_offset(&mut self, val: usize, section: SectionId, size: u8) -> Result<()> {
|
||||
let offset = self.len() as u32;
|
||||
let name = section.name().to_string();
|
||||
self.relocs.push(DebugReloc {
|
||||
let target = section.name().to_string();
|
||||
self.relocs.push(DwarfSectionReloc {
|
||||
target,
|
||||
offset,
|
||||
size,
|
||||
name,
|
||||
addend: val as i64,
|
||||
addend: val as i32,
|
||||
});
|
||||
self.write_udata(val as u64, size)
|
||||
}
|
||||
@@ -137,13 +112,50 @@ impl<'a> Writer for WriterRelocate<'a> {
|
||||
section: SectionId,
|
||||
size: u8,
|
||||
) -> Result<()> {
|
||||
let name = section.name().to_string();
|
||||
self.relocs.push(DebugReloc {
|
||||
let target = section.name().to_string();
|
||||
self.relocs.push(DwarfSectionReloc {
|
||||
target,
|
||||
offset: offset as u32,
|
||||
size,
|
||||
name,
|
||||
addend: val as i64,
|
||||
addend: val as i32,
|
||||
});
|
||||
self.write_udata_at(offset, val as u64, size)
|
||||
}
|
||||
}
|
||||
|
||||
fn create_frame_table<'a>(
|
||||
isa: &dyn TargetIsa,
|
||||
infos: impl Iterator<Item = &'a Option<UnwindInfo>>,
|
||||
) -> Option<FrameTable> {
|
||||
let mut table = FrameTable::default();
|
||||
|
||||
let cie_id = table.add_cie(isa.create_systemv_cie()?);
|
||||
|
||||
for (i, info) in infos.enumerate() {
|
||||
if let Some(UnwindInfo::SystemV(info)) = info {
|
||||
table.add_fde(
|
||||
cie_id,
|
||||
info.to_fde(Address::Symbol {
|
||||
symbol: i,
|
||||
addend: 0,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Some(table)
|
||||
}
|
||||
|
||||
pub fn emit_dwarf(
|
||||
isa: &dyn TargetIsa,
|
||||
debuginfo_data: &DebugInfoData,
|
||||
at: &ModuleAddressMap,
|
||||
vmctx_info: &ModuleVmctxInfo,
|
||||
ranges: &ValueLabelsRanges,
|
||||
compilation: &Compilation,
|
||||
) -> anyhow::Result<Vec<DwarfSection>> {
|
||||
let dwarf = transform_dwarf(isa, debuginfo_data, at, vmctx_info, ranges)?;
|
||||
let frame_table = create_frame_table(isa, compilation.into_iter().map(|f| &f.unwind_info));
|
||||
let sections = emit_dwarf_sections(dwarf, frame_table)?;
|
||||
Ok(sections)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user