@@ -14,13 +14,31 @@ See the License for the specific language governing permissions and
1414limitations under the License.
1515*/
1616
17+ use std:: fmt:: Display ;
1718use std:: path:: Path ;
1819
1920use cargo_metadata:: { MetadataCommand , Package } ;
2021use cargo_util_schemas:: manifest:: PackageName ;
2122use clap:: { Parser , Subcommand } ;
23+ use object:: read:: elf:: ElfFile64 ;
24+ use object:: { Architecture , Endianness , FileFlags , Object } ;
2225use 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+ }
0 commit comments