@@ -23,17 +23,23 @@ mod bindings {
23
23
}
24
24
25
25
#[ export_name = "command" ]
26
- unsafe extern "C" fn command_entrypoint ( stdin : i32 , stdout : i32 , _args_ptr : i32 , _args_len : i32 ) {
26
+ unsafe extern "C" fn command_entrypoint (
27
+ stdin : u32 ,
28
+ stdout : u32 ,
29
+ args_ptr : * const WasmStr ,
30
+ args_len : usize ,
31
+ ) {
27
32
State :: with_mut ( |state| {
28
33
state. push_desc ( Descriptor :: File ( File {
29
- fd : stdin as u32 ,
34
+ fd : stdin,
30
35
position : Cell :: new ( 0 ) ,
31
36
} ) ) ?;
32
37
state. push_desc ( Descriptor :: File ( File {
33
- fd : stdout as u32 ,
38
+ fd : stdout,
34
39
position : Cell :: new ( 0 ) ,
35
40
} ) ) ?;
36
41
state. push_desc ( Descriptor :: Log ) ?;
42
+ state. args = Some ( slice:: from_raw_parts ( args_ptr, args_len) ) ;
37
43
Ok ( ( ) )
38
44
} ) ;
39
45
@@ -84,49 +90,83 @@ pub unsafe extern "C" fn cabi_import_realloc(
84
90
ptr
85
91
}
86
92
93
+ /// This allocator is only used for the `command` entrypoint.
94
+ ///
95
+ /// The implementation here is a bump allocator into `State::command_data` which
96
+ /// traps when it runs out of data. This means that the total size of
97
+ /// arguments/env/etc coming into a component is bounded by the current 64k
98
+ /// (ish) limit. That's just an implementation limit though which can be lifted
99
+ /// by dynamically calling `memory.grow` as necessary for more data.
87
100
#[ no_mangle]
88
101
pub unsafe extern "C" fn cabi_export_realloc (
89
102
old_ptr : * mut u8 ,
90
103
old_size : usize ,
91
104
align : usize ,
92
105
new_size : usize ,
93
106
) -> * mut u8 {
94
- if !old_ptr. is_null ( ) {
95
- unreachable ( ) ;
96
- }
97
- if new_size > PAGE_SIZE {
98
- unreachable ( ) ;
99
- }
100
- let grew = core:: arch:: wasm32:: memory_grow ( 0 , 1 ) ;
101
- if grew == usize:: MAX {
107
+ if !old_ptr. is_null ( ) || old_size != 0 {
102
108
unreachable ( ) ;
103
109
}
104
- ( grew * PAGE_SIZE ) as * mut u8
110
+ let mut ret = null_mut :: < u8 > ( ) ;
111
+ State :: with_mut ( |state| {
112
+ let data = state. command_data . as_mut_ptr ( ) ;
113
+ let ptr = usize:: try_from ( state. command_data_next ) . unwrap ( ) ;
114
+
115
+ // "oom" as too much argument data tried to flow into the component.
116
+ // Ideally this would have a better error message?
117
+ if ptr + new_size > ( * data) . len ( ) {
118
+ unreachable ( ) ;
119
+ }
120
+ state. command_data_next += new_size as u16 ;
121
+ ret = ( * data) . as_mut_ptr ( ) . add ( ptr) ;
122
+ Ok ( ( ) )
123
+ } ) ;
124
+ ret
105
125
}
106
126
107
127
/// Read command-line argument data.
108
128
/// The size of the array should match that returned by `args_sizes_get`
109
129
#[ no_mangle]
110
- pub unsafe extern "C" fn args_get ( argv : * mut * mut u8 , argv_buf : * mut u8 ) -> Errno {
111
- // TODO: Use real arguments.
112
- // Store bytes one at a time to avoid needing a static init.
113
- argv_buf. add ( 0 ) . write ( b'w' ) ;
114
- argv_buf. add ( 1 ) . write ( b'a' ) ;
115
- argv_buf. add ( 2 ) . write ( b's' ) ;
116
- argv_buf. add ( 3 ) . write ( b'm' ) ;
117
- argv_buf. add ( 4 ) . write ( b'\0' ) ;
118
- argv. add ( 0 ) . write ( argv_buf) ;
119
- argv. add ( 1 ) . write ( null_mut ( ) ) ;
120
- ERRNO_SUCCESS
130
+ pub unsafe extern "C" fn args_get ( mut argv : * mut * mut u8 , mut argv_buf : * mut u8 ) -> Errno {
131
+ State :: with ( |state| {
132
+ if let Some ( args) = state. args {
133
+ for arg in args {
134
+ // Copy the argument into `argv_buf` which must be sized
135
+ // appropriately by the caller.
136
+ ptr:: copy_nonoverlapping ( arg. ptr , argv_buf, arg. len ) ;
137
+ * argv_buf. add ( arg. len ) = 0 ;
138
+
139
+ // Copy the argument pointer into the `argv` buf
140
+ * argv = argv_buf;
141
+
142
+ // Update our pointers past what's written to prepare for the
143
+ // next argument.
144
+ argv = argv. add ( 1 ) ;
145
+ argv_buf = argv_buf. add ( arg. len + 1 ) ;
146
+ }
147
+ }
148
+ Ok ( ( ) )
149
+ } )
121
150
}
122
151
123
152
/// Return command-line argument data sizes.
124
153
#[ no_mangle]
125
154
pub unsafe extern "C" fn args_sizes_get ( argc : * mut Size , argv_buf_size : * mut Size ) -> Errno {
126
- // TODO: Use real arguments.
127
- * argc = 1 ;
128
- * argv_buf_size = 5 ;
129
- ERRNO_SUCCESS
155
+ State :: with ( |state| {
156
+ match state. args {
157
+ Some ( args) => {
158
+ * argc = args. len ( ) ;
159
+ // Add one to each length for the terminating nul byte added by
160
+ // the `args_get` function.
161
+ * argv_buf_size = args. iter ( ) . map ( |s| s. len + 1 ) . sum ( ) ;
162
+ }
163
+ None => {
164
+ * argc = 0 ;
165
+ * argv_buf_size = 0 ;
166
+ }
167
+ }
168
+ Ok ( ( ) )
169
+ } )
130
170
}
131
171
132
172
/// Read environment variable data.
@@ -1153,14 +1193,61 @@ const PAGE_SIZE: usize = 65536;
1153
1193
/// polyfill.
1154
1194
const PATH_MAX : usize = 4096 ;
1155
1195
1196
+ const MAX_DESCRIPTORS : usize = 128 ;
1197
+
1156
1198
struct State {
1199
+ /// Used by `register_buffer` to coordinate allocations with
1200
+ /// `cabi_import_realloc`.
1157
1201
buffer_ptr : Cell < * mut u8 > ,
1158
1202
buffer_len : Cell < usize > ,
1159
- ndescriptors : usize ,
1160
- descriptors : MaybeUninit < [ Descriptor ; 128 ] > ,
1203
+
1204
+ /// Storage of mapping from preview1 file descriptors to preview2 file
1205
+ /// descriptors.
1206
+ ndescriptors : u16 ,
1207
+ descriptors : MaybeUninit < [ Descriptor ; MAX_DESCRIPTORS ] > ,
1208
+
1209
+ /// Auxiliary storage to handle the `path_readlink` function.
1161
1210
path_buf : UnsafeCell < MaybeUninit < [ u8 ; PATH_MAX ] > > ,
1211
+
1212
+ /// Storage area for data passed to the `command` entrypoint. The
1213
+ /// `command_data` is a block of memory which is dynamically allocated from
1214
+ /// in `cabi_export_realloc`. The `command_data_next` is the
1215
+ /// bump-allocated-pointer of where to allocate from next.
1216
+ command_data : MaybeUninit < [ u8 ; command_data_size ( ) ] > ,
1217
+ command_data_next : u16 ,
1218
+
1219
+ /// Arguments passed to the `command` entrypoint
1220
+ args : Option < & ' static [ WasmStr ] > ,
1162
1221
}
1163
1222
1223
+ struct WasmStr {
1224
+ ptr : * const u8 ,
1225
+ len : usize ,
1226
+ }
1227
+
1228
+ const fn command_data_size ( ) -> usize {
1229
+ // The total size of the struct should be a page, so start there
1230
+ let mut start = PAGE_SIZE ;
1231
+
1232
+ // Remove the big chunks of the struct, the `path_buf` and `descriptors`
1233
+ // fields.
1234
+ start -= PATH_MAX ;
1235
+ start -= size_of :: < Descriptor > ( ) * MAX_DESCRIPTORS ;
1236
+
1237
+ // Remove miscellaneous metadata also stored in state.
1238
+ start -= 5 * size_of :: < usize > ( ) ;
1239
+
1240
+ // Everything else is the `command_data` allocation.
1241
+ start
1242
+ }
1243
+
1244
+ // Statically assert that the `State` structure is the size of a wasm page. This
1245
+ // mostly guarantees that it's not larger than one page which is relied upon
1246
+ // below.
1247
+ const _: ( ) = {
1248
+ let _size_assert: [ ( ) ; PAGE_SIZE ] = [ ( ) ; size_of :: < State > ( ) ] ;
1249
+ } ;
1250
+
1164
1251
#[ allow( improper_ctypes) ]
1165
1252
extern "C" {
1166
1253
fn get_global_ptr ( ) -> * const RefCell < State > ;
@@ -1189,7 +1276,6 @@ impl State {
1189
1276
}
1190
1277
1191
1278
fn ptr ( ) -> & ' static RefCell < State > {
1192
- assert ! ( size_of:: <State >( ) <= PAGE_SIZE ) ;
1193
1279
unsafe {
1194
1280
let mut ptr = get_global_ptr ( ) ;
1195
1281
if ptr. is_null ( ) {
@@ -1214,6 +1300,9 @@ impl State {
1214
1300
ndescriptors : 0 ,
1215
1301
descriptors : MaybeUninit :: uninit ( ) ,
1216
1302
path_buf : UnsafeCell :: new ( MaybeUninit :: uninit ( ) ) ,
1303
+ command_data : MaybeUninit :: uninit ( ) ,
1304
+ command_data_next : 0 ,
1305
+ args : None ,
1217
1306
} ) ) ;
1218
1307
& * ret
1219
1308
}
@@ -1222,18 +1311,19 @@ impl State {
1222
1311
fn push_desc ( & mut self , desc : Descriptor ) -> Result < Fd , Errno > {
1223
1312
unsafe {
1224
1313
let descriptors = self . descriptors . as_mut_ptr ( ) ;
1225
- if self . ndescriptors >= ( * descriptors) . len ( ) {
1314
+ let ndescriptors = usize:: try_from ( self . ndescriptors ) . unwrap ( ) ;
1315
+ if ndescriptors >= ( * descriptors) . len ( ) {
1226
1316
return Err ( ERRNO_INVAL ) ;
1227
1317
}
1228
- ptr:: addr_of_mut!( ( * descriptors) [ self . ndescriptors] ) . write ( desc) ;
1318
+ ptr:: addr_of_mut!( ( * descriptors) [ ndescriptors] ) . write ( desc) ;
1229
1319
self . ndescriptors += 1 ;
1230
- Ok ( Fd :: try_from ( self . ndescriptors - 1 ) . unwrap ( ) )
1320
+ Ok ( Fd :: from ( self . ndescriptors - 1 ) )
1231
1321
}
1232
1322
}
1233
1323
1234
1324
fn get ( & self , fd : Fd ) -> Result < & Descriptor , Errno > {
1235
1325
let index = usize:: try_from ( fd) . unwrap ( ) ;
1236
- if index < self . ndescriptors {
1326
+ if index < usize :: try_from ( self . ndescriptors ) . unwrap ( ) {
1237
1327
unsafe { ( * self . descriptors . as_ptr ( ) ) . get ( index) . ok_or ( ERRNO_BADF ) }
1238
1328
} else {
1239
1329
Err ( ERRNO_BADF )
0 commit comments