Resolve libcall relocations for older CPUs (#5567)

* Resolve libcall relocations for older CPUs

Long ago Wasmtime used to have logic for resolving relocations
post-compilation for libcalls which I ended up removing during
refactorings last year. As #5563 points out, however, it's possible to
get Wasmtime to panic by disabling SSE features which forces Cranelift
to use libcalls for some floating-point operations instead. Note that
this also requires disabling SIMD because SIMD support has a baseline of
SSE 4.2.

This commit pulls back the old implementations of various libcalls and
reimplements logic necessary to have them work on CPUs without SSE 4.2

Closes #5563

* Fix log message in `wast` support

* Fix offset listed in relocations

Be sure to factor in the offset of the function itself

* Review comments
This commit is contained in:
Alex Crichton
2023-01-18 09:04:10 -06:00
committed by GitHub
parent 94b51cdb17
commit 9b896d2a70
7 changed files with 298 additions and 14 deletions

View File

@@ -15,6 +15,8 @@
use crate::{CompiledFunction, RelocationTarget};
use anyhow::Result;
use cranelift_codegen::binemit::Reloc;
use cranelift_codegen::ir::LibCall;
use cranelift_codegen::isa::{
unwind::{systemv, UnwindInfo},
TargetIsa,
@@ -24,6 +26,7 @@ use gimli::write::{Address, EhFrame, EndianVec, FrameTable, Writer};
use gimli::RunTimeEndian;
use object::write::{Object, SectionId, StandardSegment, Symbol, SymbolId, SymbolSection};
use object::{Architecture, SectionKind, SymbolFlags, SymbolKind, SymbolScope};
use std::collections::HashMap;
use std::convert::TryFrom;
use std::ops::Range;
use wasmtime_environ::FuncIndex;
@@ -52,6 +55,13 @@ pub struct ModuleTextBuilder<'a> {
/// In-progress text section that we're using cranelift's `MachBuffer` to
/// build to resolve relocations (calls) between functions.
text: Box<dyn TextSectionBuilder>,
/// Symbols defined in the object for libcalls that relocations are applied
/// against.
///
/// Note that this isn't typically used. It's only used for SSE-disabled
/// builds without SIMD on x86_64 right now.
libcall_symbols: HashMap<LibCall, SymbolId>,
}
impl<'a> ModuleTextBuilder<'a> {
@@ -76,6 +86,7 @@ impl<'a> ModuleTextBuilder<'a> {
text_section,
unwind_info: Default::default(),
text: isa.text_section_builder(num_funcs),
libcall_symbols: HashMap::default(),
}
}
@@ -146,13 +157,49 @@ impl<'a> ModuleTextBuilder<'a> {
);
}
// At this time it's not expected that any libcall relocations
// are generated. Ideally we don't want relocations against
// libcalls anyway as libcalls should go through indirect
// `VMContext` tables to avoid needing to apply relocations at
// module-load time as well.
// Relocations against libcalls are not common at this time and
// are only used in non-default configurations that disable wasm
// SIMD, disable SSE features, and for wasm modules that still
// use floating point operations.
//
// Currently these relocations are all expected to be absolute
// 8-byte relocations so that's asserted here and then encoded
// directly into the object as a normal object relocation. This
// is processed at module load time to resolve the relocations.
RelocationTarget::LibCall(call) => {
unimplemented!("cannot generate relocation against libcall {call:?}");
let symbol = *self.libcall_symbols.entry(call).or_insert_with(|| {
self.obj.add_symbol(Symbol {
name: libcall_name(call).as_bytes().to_vec(),
value: 0,
size: 0,
kind: SymbolKind::Text,
scope: SymbolScope::Linkage,
weak: false,
section: SymbolSection::Undefined,
flags: SymbolFlags::None,
})
});
let (encoding, kind, size) = match r.reloc {
Reloc::Abs8 => (
object::RelocationEncoding::Generic,
object::RelocationKind::Absolute,
8,
),
other => unimplemented!("unimplemented relocation kind {other:?}"),
};
self.obj
.add_relocation(
self.text_section,
object::write::Relocation {
symbol,
size,
kind,
encoding,
offset: off + u64::from(r.offset),
addend: r.addend,
},
)
.unwrap();
}
};
}
@@ -486,3 +533,19 @@ impl<'a> UnwindInfoBuilder<'a> {
}
}
}
fn libcall_name(call: LibCall) -> &'static str {
use wasmtime_environ::obj::LibCall as LC;
let other = match call {
LibCall::FloorF32 => LC::FloorF32,
LibCall::FloorF64 => LC::FloorF64,
LibCall::NearestF32 => LC::NearestF32,
LibCall::NearestF64 => LC::NearestF64,
LibCall::CeilF32 => LC::CeilF32,
LibCall::CeilF64 => LC::CeilF64,
LibCall::TruncF32 => LC::TruncF32,
LibCall::TruncF64 => LC::TruncF64,
_ => panic!("unknown libcall to give a name to: {call:?}"),
};
other.symbol()
}