(module $platform_wasi (memory 256) (export "memory" (memory 0)) (global $WASI_RIGHT_FD_READ i64 (i64.const 2)) (global $WASI_ESUCCESS i32 0) (global $WASI_EBADF i32 8) (global $WASI_PREOPENTYPE_DIR i32 0) (import "wasi_unstable" "args_get" (func $args_get (param i32 i32) (result i32))) (import "wasi_unstable" "args_sizes_get" (func $args_sizes_get (param i32 i32) (result i32))) (import "wasi_unstable" "clock_time_get" (func $clock_time_get (param i32 i64 i32) (result i32))) (import "wasi_unstable" "fd_prestat_get" (func $fd_prestat_get (param i32 i32) (result i32))) (import "wasi_unstable" "fd_prestat_dir_name" (func $fd_prestat_dir_name (param i32 i32 i32) (result i32))) (import "wasi_unstable" "fd_read" (func $fd_read (param i32 i32 i32 i32) (result i32))) (import "wasi_unstable" "fd_write" (func $fd_write (param i32 i32 i32 i32) (result i32))) (import "wasi_unstable" "path_open" (func $path_open (param i32 i32 i32 i32 i32 i64 i64 i32 i32) (result i32))) (import "wasi_unstable" "proc_exit" (func $proc_exit (param i32))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (func $fatal (param $code i32 $msg i32) ($print $msg) ($proc_exit $code) ) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (func $print (param $addr i32) (LET $ret 0 $nwritten_ptr (STATIC_ARRAY 4 4) $iovec (STATIC_ARRAY 8 8)) (i32.store $iovec $addr) (i32.store offset=4 $iovec ($strlen $addr)) (local.set $ret ($fd_write 1 $iovec 1 $nwritten_ptr)) ) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (func $readline (param $prompt i32 $buf i32) (result i32) (LET $ret 0 $nread_ptr (STATIC_ARRAY 4 4) $iovec (STATIC_ARRAY 8 8)) ($print $prompt) (i32.store $iovec $buf) (i32.store offset=4 $iovec 200) ;; TODO: not hardcoded length (local.set $ret ($fd_read 0 $iovec 1 $nread_ptr)) (if (i32.le_s (i32.load $nread_ptr) 0) (return 0)) ;; Replace ending newline with NULL (i32.store8 (i32.add $buf (i32.sub (i32.load $nread_ptr) 1)) 0) 1 ) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (func $read_file (param $path i32 $buf i32) (result i32) (LET $orig_path $path $ret 0 $prestat_ptr (STATIC_ARRAY 8 4) $pr_type 0 $pr_name_len 0 $prepath (STATIC_ARRAY 1024) $dirfd -1 $fd 3 $fd_ptr (STATIC_ARRAY 4 4) $nread_ptr (STATIC_ARRAY 4 4) $iovec (STATIC_ARRAY 8 8)) ;; Find the pre-opened dir fd with the same prefix as the our path ;; following the algorithm at: ;; https://github.com/CraneStation/wasi-sysroot/blob/1cc98f27f5ab8afdc033e16eac8799ee606eb769/libc-bottom-half/crt/crt1.c#L71 ;; The matching dir fd is then used to open and read the path. (block $loop_done (loop $loop ;; prestat the fd from 3 onward until EBADF is returned (local.set $ret ($fd_prestat_get $fd $prestat_ptr)) (if (i32.eq (global.get $WASI_EBADF) $ret) (br $loop_done)) (if (i32.ne (global.get $WASI_ESUCCESS) $ret) (then (local.set $fd (i32.add 1 $fd)) (br $loop))) ;;(br $loop_done)) (local.set $pr_type (i32.load $prestat_ptr)) (local.set $pr_name_len (i32.load offset=4 $prestat_ptr)) ;; Read the pre-opened path name (local.set $ret ($fd_prestat_dir_name $fd $prepath $pr_name_len)) (if (i32.ne (global.get $WASI_ESUCCESS) $ret) (br $loop_done)) ;; if pr_name_len includes a null, exclude it from the compare ;;($printf_2 "here1 pr_name_len: %d, char is %d\n" $pr_name_len (i32.load8_u (i32.add $prepath (i32.sub $pr_name_len 1)))) (if (i32.eqz (i32.load8_u (i32.add $prepath (i32.sub $pr_name_len 1)))) (then (local.set $pr_name_len (i32.sub $pr_name_len 1)))) ;; if it is a dir and the path prefix matches, use it ;;($printf_5 "fd: %d, ret: %d, pr_type: %d, pr_name_len: %d, prepath: %s\n" ;; $fd $ret $pr_type $pr_name_len $prepath) (if (AND (i32.eq $pr_type (global.get $WASI_PREOPENTYPE_DIR)) (i32.eqz ($strncmp $prepath $path $pr_name_len))) (then (local.set $path (i32.add $pr_name_len $path)) (local.set $dirfd $fd) (br $loop_done))) (local.set $fd (i32.add 1 $fd)) (br $loop) ) ) ;;($printf_3 "final dirfd: %d, adjusted path: %s (%d)\n" $dirfd $path ($strlen $path)) (if (i32.eq $dirfd -1) (then ($printf_1 "ERROR: could not find permission for '%s'\n" $orig_path) (return 0))) (local.set $ret ($path_open $dirfd 1 ;; dirflags (symlink follow) $path ($strlen $path) 0 ;; o_flags (global.get $WASI_RIGHT_FD_READ) (global.get $WASI_RIGHT_FD_READ) 0 ;; fs_flags $fd_ptr)) (if (i32.ne (global.get $WASI_ESUCCESS) $ret) (then ($printf_2 "ERROR: failed to open '%s', error %d\n" $orig_path $ret) (return 0))) (i32.store $iovec $buf) ;; TODO: use stat result instead of not hardcoded length (i32.store offset=4 $iovec 16384) (local.set $ret ($fd_read (i32.load $fd_ptr) $iovec 1 $nread_ptr)) (if (i32.ne (global.get $WASI_ESUCCESS) $ret) (then ($printf_2 "ERROR: failed to read '%s', error %d\n" $orig_path $ret) (return 0))) ;; Add null to string (i32.store8 (i32.add $buf (i32.load $nread_ptr)) 0) (i32.add 1 (i32.load $nread_ptr)) ) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (func $get_time_ms (result i32) (LET $tv (STATIC_ARRAY 8 8)) (drop (call $clock_time_get 0 (i64.const 0) $tv)) (i32.wrap_i64 ;; convert nanoseconds to milliseconds (i64.div_u (i64.load $tv) (i64.const 1000000))) ) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Returns an i64 with argc in high 32 and argv in low 32. ;; String memory is: argv + (argc * 4) (func $get_argc_argv (result i64) (LET $argc_ptr (STATIC_ARRAY 4 4) $argv_size_ptr (STATIC_ARRAY 4 4) $argc 0 $argv (STATIC_ARRAY 1024 4)) (drop ($args_sizes_get $argc_ptr $argv_size_ptr)) (local.set $argc (i32.load $argc_ptr)) (if (i32.gt_u (i32.add (i32.mul 4 $argc) (i32.load $argv_size_ptr)) 1024) ($fatal 2 "Command line arguments memory exceeds 1024 bytes")) (drop ($args_get $argv (i32.add $argv (i32.mul 4 $argc)))) (i64.or (i64.shl (i64.extend_i32_u $argc) (i64.const 32)) (i64.extend_i32_u $argv)) ) (func $entry (local $argc_argv i64) ($init_memory) (local.set $argc_argv ($get_argc_argv)) ($proc_exit ($main (i32.wrap_i64 (i64.shr_u $argc_argv (i64.const 32))) (i32.wrap_i64 $argc_argv))) ) ;;(start $entry) (export "_start" (func $entry)) )