Skip to content

Commit 35de0c7

Browse files
committed
Add option to build and run pulley pre-compiled modules or components
Signed-off-by: Doru Blânzeanu <dblnz@pm.me>
1 parent a67625c commit 35de0c7

9 files changed

Lines changed: 145 additions & 17 deletions

File tree

Cargo.lock

Lines changed: 15 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/hyperlight_wasm/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ crashdump = ["hyperlight-host/crashdump"]
114114
gdb = ["hyperlight-host/gdb"]
115115
kvm = ["hyperlight-host/kvm"]
116116
mshv3 = ["hyperlight-host/mshv3"]
117+
pulley = []
117118
trace_guest = ["hyperlight-host/trace_guest"]
118119

119120
[[bench]]

src/hyperlight_wasm/build.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,10 @@ fn build_wasm_runtime() -> PathBuf {
128128
if std::env::var("CARGO_FEATURE_GDB").is_ok() {
129129
cmd = cmd.arg("--features").arg("gdb");
130130
}
131+
// Add --features pulley if the pulley feature is enabled
132+
if std::env::var("CARGO_FEATURE_PULLEY").is_ok() {
133+
cmd = cmd.arg("--features").arg("pulley");
134+
}
131135
// Enable the "trace_guest" feature if the corresponding Cargo feature is enabled
132136
if std::env::var("CARGO_FEATURE_TRACE_GUEST").is_ok() {
133137
cmd = cmd.arg("--features").arg("trace_guest");

src/hyperlight_wasm_aot/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,11 @@ Application to precompile WebAssembly binaries to for hyperlight-wasm.
1212
"""
1313

1414
[dependencies]
15-
wasmtime = { version = "36.0.5", default-features = false, features = ["cranelift", "runtime", "component-model" ] }
15+
wasmtime = { version = "36.0.5", default-features = false, features = ["cranelift", "pulley", "runtime", "component-model" ] }
1616
clap = { version = "4.5", features = ["derive"] }
1717
cargo_metadata = "0.23"
1818
cargo-util-schemas = "0.10.1"
19+
object = { version = "0.38.1", default-features = false, features = ["read_core", "elf"] }
1920

2021
[features]
2122
gdb = ["wasmtime/debug-builtins"]

src/hyperlight_wasm_aot/src/main.rs

Lines changed: 104 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,31 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17+
use std::fmt::Display;
1718
use std::path::Path;
1819

1920
use cargo_metadata::{MetadataCommand, Package};
2021
use cargo_util_schemas::manifest::PackageName;
2122
use clap::{Parser, Subcommand};
23+
use object::read::elf::ElfFile64;
24+
use object::{Architecture, Endianness, FileFlags, Object};
2225
use wasmtime::{Config, Engine, Module, OptLevel, Precompiled};
2326

27+
#[derive(Debug)]
28+
enum SupportedTarget {
29+
X86_64UnknownNone,
30+
WasmtimePulley64,
31+
}
32+
33+
impl Display for SupportedTarget {
34+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35+
match self {
36+
SupportedTarget::X86_64UnknownNone => write!(f, "x86_64-unknown-none"),
37+
SupportedTarget::WasmtimePulley64 => write!(f, "pulley64"),
38+
}
39+
}
40+
}
41+
2442
#[derive(Parser)]
2543
#[command(name = "hyperlight-wasm-aot")]
2644
#[command(version = env!("CARGO_PKG_VERSION"))]
@@ -51,6 +69,10 @@ enum Commands {
5169
/// Disable address map and native unwind info for smaller binaries
5270
#[arg(long)]
5371
minimal: bool,
72+
73+
/// Pre-compile for the pulley64 target
74+
#[arg(long)]
75+
pulley: bool,
5476
},
5577

5678
/// Check which Wasmtime version was used to precompile a module
@@ -74,6 +96,7 @@ fn main() {
7496
component,
7597
debug,
7698
minimal,
99+
pulley,
77100
} => {
78101
let outfile = match output {
79102
Some(s) => s,
@@ -83,15 +106,20 @@ fn main() {
83106
path.to_str().unwrap().to_string()
84107
}
85108
};
109+
let target = if pulley {
110+
SupportedTarget::WasmtimePulley64
111+
} else {
112+
SupportedTarget::X86_64UnknownNone
113+
};
86114
if debug {
87115
println!(
88-
"Aot Compiling {} to {} with debug info and optimizations off",
89-
input, outfile
116+
"Aot Compiling {} to [{}]: {} with debug info and optimizations off",
117+
input, target, outfile
90118
);
91119
} else {
92-
println!("Aot Compiling {} to {}", input, outfile);
120+
println!("Aot Compiling {} to [{}]: {}", input, target, outfile);
93121
}
94-
let config = get_config(debug, minimal);
122+
let config = get_config(debug, minimal, &target);
95123
let engine = Engine::new(&config).unwrap();
96124
let bytes = std::fs::read(&input).unwrap();
97125
let serialized = if component {
@@ -121,7 +149,17 @@ fn main() {
121149
}
122150
// load the file into wasmtime, check that it is aot compiled and extract the version of wasmtime used to compile it from its metadata
123151
let bytes = std::fs::read(&file).unwrap();
124-
let config = get_config(debug, false);
152+
let target = match get_aot_target(&bytes) {
153+
Ok(target) => target,
154+
Err(e) => {
155+
eprintln!(
156+
"Error - {} is not a valid precompiled Wasmtime module: {}",
157+
file, e
158+
);
159+
std::process::exit(1)
160+
}
161+
};
162+
let config = get_config(debug, false, &target);
125163
let engine = Engine::new(&config).unwrap();
126164
match Engine::detect_precompiled(&bytes) {
127165
Some(pre_compiled) => {
@@ -132,8 +170,8 @@ fn main() {
132170
// so we will try and load it and then catch the error and parse the version from the error message :-(
133171
match unsafe { Module::deserialize(&engine, bytes) } {
134172
Ok(_) => println!(
135-
"File {} was AOT compiled with wasmtime version: {}",
136-
file, version_number
173+
"File {} was AOT compiled to '{}' with wasmtime version: {}",
174+
file, target, version_number
137175
),
138176
Err(e) => {
139177
let error_message = e.to_string();
@@ -145,8 +183,8 @@ fn main() {
145183
}
146184
let version = error_message.trim_start_matches("Module was compiled with incompatible Wasmtime version ").trim();
147185
println!(
148-
"File {} was AOT compiled with wasmtime version: {}",
149-
file, version
186+
"File {} was AOT compiled to '{}' with wasmtime version: {}",
187+
file, target, version
150188
);
151189
}
152190
};
@@ -168,9 +206,18 @@ fn main() {
168206
}
169207

170208
/// Returns a new `Config` for the Wasmtime engine with additional settings for AOT compilation.
171-
fn get_config(debug: bool, minimal: bool) -> Config {
209+
fn get_config(debug: bool, minimal: bool, target: &SupportedTarget) -> Config {
172210
let mut config = Config::new();
173-
config.target("x86_64-unknown-none").unwrap();
211+
212+
// Compile for the pulley64 target if specified
213+
match target {
214+
SupportedTarget::X86_64UnknownNone => {
215+
config.target("x86_64-unknown-none").unwrap();
216+
}
217+
SupportedTarget::WasmtimePulley64 => {
218+
config.target("pulley64").unwrap();
219+
}
220+
}
174221

175222
// Enable the default features for the Wasmtime engine.
176223
if debug {
@@ -185,3 +232,49 @@ fn get_config(debug: bool, minimal: bool) -> Config {
185232

186233
config
187234
}
235+
236+
/// Parses the AOT compiled file as an ELF file and extracts the target triple
237+
/// NOTE: These flag bits must match Wasmtime's EF_WASMTIME_PULLEY{64,32} values
238+
/// used when emitting RISC-V ELF object files. If Wasmtime changes these values,
239+
/// this code must be updated accordingly to correctly detect pulley targets.
240+
/// Source of definitions:
241+
/// https://github.com/bytecodealliance/wasmtime/blob/release-42.0.0/crates/environ/src/obj.rs#L26
242+
/// Source of logic for detecting pulley targets:
243+
/// https://github.com/bytecodealliance/wasmtime/blob/release-42.0.0/src/commands/objdump.rs#L408
244+
fn get_aot_target(bytes: &[u8]) -> Result<SupportedTarget, String> {
245+
const EF_WASMTIME_PULLEY64: u32 = 1 << 3;
246+
const EF_WASMTIME_PULLEY32: u32 = 1 << 2;
247+
248+
if let Ok(elf) = ElfFile64::<Endianness>::parse(bytes) {
249+
match elf.architecture() {
250+
Architecture::X86_64 => Ok(SupportedTarget::X86_64UnknownNone),
251+
Architecture::Aarch64 => {
252+
Err("Unsupported architecture Aarch64 in AOT compiled file".to_string())
253+
}
254+
Architecture::S390x => {
255+
Err("Unsupported architecture S390x in AOT compiled file".to_string())
256+
}
257+
Architecture::Riscv64 => {
258+
let e_flags = match elf.flags() {
259+
FileFlags::Elf { e_flags, .. } => e_flags,
260+
_ => return Err("Unsupported file format in AOT compiled file".to_string()),
261+
};
262+
263+
if e_flags & EF_WASMTIME_PULLEY64 != 0 {
264+
Ok(SupportedTarget::WasmtimePulley64)
265+
} else if e_flags & EF_WASMTIME_PULLEY32 != 0 {
266+
Err("Unsupported Riscv64 AOT compiled file: pulley32 artifacts are not supported".to_string())
267+
} else {
268+
Err(
269+
"Unsupported Riscv64 AOT compiled file, missing expected e_flags in elf header".to_string()
270+
)
271+
}
272+
}
273+
other => Err(format!(
274+
"Unsupported architecture {other:?} in AOT compiled file"
275+
)),
276+
}
277+
} else {
278+
Err("Failed to parse AOT compiled file as ELF".to_string())
279+
}
280+
}

src/wasm_runtime/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,5 @@ reqwest = {version = "0.12", default-features = false, features = ["blocking","
3131
[features]
3232
default = []
3333
gdb = ["wasmtime/debug-builtins"]
34+
pulley = ["wasmtime/pulley"]
3435
trace_guest = ["hyperlight-common/trace_guest", "hyperlight-guest/trace_guest", "hyperlight-guest-bin/trace_guest"]

src/wasm_runtime/build.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,5 +67,6 @@ fn main() {
6767

6868
cfg_aliases::cfg_aliases! {
6969
gdb: { all(feature = "gdb", debug_assertions) },
70+
pulley: { feature = "pulley" },
7071
}
7172
}

src/wasm_runtime/src/component.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,16 @@ pub extern "C" fn hyperlight_main() {
111111
config.with_custom_code_memory(Some(alloc::sync::Arc::new(platform::WasmtimeCodeMemory {})));
112112
#[cfg(gdb)]
113113
config.debug_info(true);
114+
#[cfg(pulley)]
115+
config
116+
.target("pulley64")
117+
.map_err(|_| {
118+
HyperlightGuestError::new(
119+
ErrorCode::GuestError,
120+
"Failed to set wasmtime target: pulley64".to_string(),
121+
)
122+
})
123+
.unwrap();
114124
let engine = Engine::new(&config).unwrap();
115125
let linker = Linker::new(&engine);
116126
*CUR_ENGINE.lock() = Some(engine);

src/wasm_runtime/src/module.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,13 @@ fn init_wasm_runtime() -> Result<Vec<u8>> {
101101
config.with_custom_code_memory(Some(alloc::sync::Arc::new(platform::WasmtimeCodeMemory {})));
102102
#[cfg(gdb)]
103103
config.debug_info(true);
104+
#[cfg(pulley)]
105+
config.target("pulley64").map_err(|_| {
106+
HyperlightGuestError::new(
107+
ErrorCode::GuestError,
108+
"Failed to set wasmtime target: pulley64".to_string(),
109+
)
110+
})?;
104111
let engine = Engine::new(&config)?;
105112
let mut linker = Linker::new(&engine);
106113
wasip1::register_handlers(&mut linker)?;

0 commit comments

Comments
 (0)