Skip to content

Commit 1b1f6ed

Browse files
early alloc stack and refactor brk (#1066)
* early init stack and refactor brk * cleanup: remove unused vars, fix stale comments, and apply formatting * quick fix * address review comments: use u64::MAX for fd=-1 and add mmap flag comments
1 parent ea80b1c commit 1b1f6ed

File tree

8 files changed

+98
-61
lines changed

8 files changed

+98
-61
lines changed

src/cage/src/memory/memory.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -142,16 +142,16 @@ pub fn fork_vmmap(parent_cageid: u64, child_cageid: u64) {
142142
// update program break for child
143143
drop(child_vmmap);
144144
let mut child_vmmap = child_cage.vmmap.write();
145-
child_vmmap.set_program_break(parent_vmmap.program_break);
145+
child_vmmap.set_heap_start(parent_vmmap.heap_start);
146146
}
147147

148148
// set the wasm linear memory base address to vmmap
149-
pub fn init_vmmap(cageid: u64, base_address: usize, program_break: Option<u32>) {
149+
pub fn init_vmmap(cageid: u64, base_address: usize, heap_start: Option<u32>) {
150150
let cage = get_cage(cageid).unwrap();
151151
let mut vmmap = cage.vmmap.write();
152152
vmmap.set_base_address(base_address);
153-
if program_break.is_some() {
154-
vmmap.set_program_break(program_break.unwrap());
153+
if heap_start.is_some() {
154+
vmmap.set_heap_start(heap_start.unwrap());
155155
}
156156
}
157157

src/cage/src/memory/vmmap.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ pub struct Vmmap {
255255

256256
pub start_address: u32, // start address of valid vmmap address range
257257
pub end_address: u32, // end address of valid vmmap address range
258-
pub program_break: u32, // program break (i.e. heap bottom) of the memory
258+
pub heap_start: u32, // start of heap memory
259259
}
260260

261261
#[allow(dead_code)]
@@ -269,7 +269,7 @@ impl Vmmap {
269269
base_address: None,
270270
start_address: 0,
271271
end_address: DEFAULT_VMMAP_SIZE,
272-
program_break: 0,
272+
heap_start: 0,
273273
}
274274
}
275275

@@ -284,7 +284,7 @@ impl Vmmap {
284284
self.base_address = None;
285285
self.start_address = 0;
286286
self.end_address = DEFAULT_VMMAP_SIZE;
287-
self.program_break = 0;
287+
self.heap_start = 0;
288288
}
289289

290290
/// Rounds up a page number to the nearest multiple of pages_per_map
@@ -320,12 +320,12 @@ impl Vmmap {
320320
self.base_address = Some(base_address);
321321
}
322322

323-
/// Sets the program break for the memory
323+
/// Sets the heap start page number for the memory
324324
///
325325
/// Arguments:
326-
/// - program_break: The program break to set
327-
pub fn set_program_break(&mut self, program_break: u32) {
328-
self.program_break = program_break;
326+
/// - heap_start: The page number at which the heap begins
327+
pub fn set_heap_start(&mut self, heap_start: u32) {
328+
self.heap_start = heap_start;
329329
}
330330

331331
/// Converts a user address to a system address

src/glibc/csu/libc-tls.c

Lines changed: 1 addition & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -169,36 +169,7 @@ __libc_setup_tls (void)
169169
tlsblock = (void *) (((uintptr_t) tlsblock + max_align - 1)
170170
& ~(max_align - 1));
171171

172-
/* Initialize the dtv. [0] is the length, [1] the generation counter. */
173-
_dl_static_dtv[0].counter = (sizeof (_dl_static_dtv) / sizeof (_dl_static_dtv[0])) - 2;
174-
// _dl_static_dtv[1].counter = 0; would be needed if not already done
175-
176-
/* Initialize the TLS block. */
177-
#if TLS_TCB_AT_TP
178-
_dl_static_dtv[2].pointer.val = ((char *) tlsblock + tcb_offset
179-
- roundup (memsz, align ?: 1));
180-
main_map->l_tls_offset = roundup (memsz, align ?: 1);
181-
#elif TLS_DTV_AT_TP
182-
_dl_static_dtv[2].pointer.val = (char *) tlsblock + tcb_offset;
183-
main_map->l_tls_offset = tcb_offset;
184-
#else
185-
# error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined"
186-
#endif
187-
_dl_static_dtv[2].pointer.to_free = NULL;
188-
/* sbrk gives us zero'd memory, so we don't need to clear the remainder. */
189-
memcpy (_dl_static_dtv[2].pointer.val, initimage, filesz);
190-
191-
/* Install the pointer to the dtv. */
192-
193-
/* Initialize the thread pointer. */
194-
#if TLS_TCB_AT_TP
195-
INSTALL_DTV ((char *) tlsblock + tcb_offset, _dl_static_dtv);
196-
197-
// call_tls_init_tp ((char *) tlsblock + tcb_offset);
198-
#elif TLS_DTV_AT_TP
199-
INSTALL_DTV (tlsblock, _dl_static_dtv);
200-
// call_tls_init_tp (tlsblock);
201-
#endif
172+
// lind-wasm: remove DTV related logic since lind do not use DTV
202173

203174
/* Update the executable's link map with enough information to make
204175
the TLS routines happy. */

src/lind-boot/src/lind_wasmtime/execute.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ use wasmtime_lind_3i::{VmCtxWrapper, init_vmctx_pool, rm_vmctx, set_vmctx, set_v
2222
use wasmtime_lind_common::LindEnviron;
2323
use wasmtime_lind_dylink::DynamicLoader;
2424
use wasmtime_lind_multi_process::{
25-
CAGE_START_ID, LindCtx, THREAD_START_ID, attach_shared_memory, get_memory_base,
25+
CAGE_START_ID, LindCtx, THREAD_START_ID, attach_shared_memory, early_init_stack,
26+
get_memory_base,
2627
};
2728
use wasmtime_lind_utils::symbol_table::SymbolMap;
2829
use wasmtime_lind_utils::{LindCageManager, LindGOT};
@@ -243,6 +244,13 @@ pub fn execute_with_lind(
243244
if dylink_metadata.dylink_enabled {
244245
let lind_got = dylink_metadata.got.as_ref().unwrap();
245246

247+
early_init_stack(
248+
cageid,
249+
GUARD_SIZE as i32,
250+
(GUARD_SIZE + DEFAULT_STACKSIZE) as i32,
251+
)
252+
.unwrap();
253+
246254
// For each module (including the main module),
247255
// register its GOT imports with the shared LindGOT instance.
248256
//

src/rawposix/src/fs_calls.rs

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1199,19 +1199,42 @@ pub extern "C" fn brk_syscall(
11991199
let cage = get_cage(cageid).unwrap();
12001200

12011201
let mut vmmap = cage.vmmap.write();
1202-
let heap = vmmap.find_page(HEAP_ENTRY_INDEX).unwrap().clone();
1202+
let heap_opt = vmmap.find_page(vmmap.heap_start);
12031203

1204-
assert!(heap.npages == vmmap.program_break);
1204+
let heap = if heap_opt.is_none() {
1205+
// if heap page is not found, create an empty heap entry with 0 size
1206+
cage::VmmapEntry::new(
1207+
vmmap.heap_start,
1208+
0,
1209+
(PROT_READ | PROT_WRITE),
1210+
(PROT_READ | PROT_WRITE),
1211+
(MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED) as i32,
1212+
false,
1213+
0,
1214+
0,
1215+
cageid,
1216+
MemoryBackingType::Anonymous,
1217+
)
1218+
} else {
1219+
heap_opt.unwrap().clone()
1220+
};
1221+
1222+
assert!(heap.page_num == vmmap.heap_start);
1223+
1224+
let old_brk_page = heap.page_num + heap.npages;
12051225

12061226
// passing 0 to brk will always return the current brk
12071227
if brk == 0 {
1208-
return (PAGESIZE * heap.npages) as i32;
1228+
return (PAGESIZE * old_brk_page) as i32;
12091229
}
1210-
1211-
let old_brk_page = heap.npages;
12121230
// round up the break to multiple of pages
12131231
let brk_page = (round_up_page(brk as u64) >> PAGESHIFT) as u32;
12141232

1233+
// shrink heap below heap start is not allowed
1234+
if brk_page < vmmap.heap_start {
1235+
return syscall_error(Errno::ENOMEM, "brk", "no memory");
1236+
}
1237+
12151238
// if we are incrementing program break, we need to check if we have enough space
12161239
if brk_page > old_brk_page {
12171240
if vmmap.check_existing_mapping(old_brk_page, brk_page - old_brk_page, 0) {
@@ -1220,12 +1243,12 @@ pub extern "C" fn brk_syscall(
12201243
}
12211244

12221245
// remove the old entries since new entry is overlapping with it.
1223-
vmmap.remove_entry(0, old_brk_page);
1246+
vmmap.remove_entry(heap.page_num, heap.npages);
12241247

12251248
// update vmmap entry
12261249
vmmap.add_entry_with_overwrite(
1227-
0,
1228-
brk_page,
1250+
heap.page_num,
1251+
brk_page - heap.page_num,
12291252
heap.prot,
12301253
heap.maxprot,
12311254
heap.flags,
@@ -1241,8 +1264,6 @@ pub extern "C" fn brk_syscall(
12411264
let new_heap_end_usr = (brk_page * PAGESIZE) as u32;
12421265
let new_heap_end_sys = vmmap.user_to_sys(new_heap_end_usr) as *mut u8;
12431266

1244-
vmmap.set_program_break(brk_page);
1245-
12461267
drop(vmmap);
12471268

12481269
// if new brk is larger than old brk

src/sysdefs/src/constants/lind_platform_const.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ pub const THREEI_CAGEID: u64 = 999999;
8686
/// Default stack size assigned to each cage
8787
pub const DEFAULT_STACKSIZE: u32 = 8388608; // 8 MB
8888
/// Size of guard pages
89-
pub const GUARD_SIZE: u32 = 1024; // 1 KB
89+
pub const GUARD_SIZE: u32 = 4096; // 4 KB
9090

9191
/// The starting index for function tables of wasm modules in Lind.
9292
/// function index of 1 must be reserved for SIG_IGN constant for signal handling

src/wasmtime/crates/lind-multi-process/src/lib.rs

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use std::ffi::c_void;
77
use std::ptr::NonNull;
88
use sysdefs::constants::lind_platform_const::{UNUSED_ARG, UNUSED_ID, UNUSED_NAME};
99
use sysdefs::constants::syscall_const::{EXEC_SYSCALL, EXIT_SYSCALL, FORK_SYSCALL};
10-
use sysdefs::constants::{Errno, MAX_SHEBANG_DEPTH};
10+
use sysdefs::constants::{Errno, MAX_SHEBANG_DEPTH, MMAP_SYSCALL};
1111
use sysdefs::logging::lind_debug_panic;
1212
use sysdefs::{constants::sys_const, data::sys_struct};
1313
use threei::{threei::make_syscall, threei_const};
@@ -2212,6 +2212,40 @@ pub fn attach_shared_memory<
22122212
Err(anyhow!("Main Module does not contain a shared memory"))
22132213
}
22142214

2215+
pub fn early_init_stack(cageid: u64, stack_start: i32, stack_end: i32) -> Result<()> {
2216+
// TODO: currently we explicitly allocate first guard page (0-stack_start)
2217+
// due to a known issue. This should be fixed in the future and only allocate
2218+
// the actual stack space (stack_start-stack_end)
2219+
2220+
let ret = make_syscall(
2221+
cageid, // self cageid
2222+
(MMAP_SYSCALL) as u64, // syscall num
2223+
0, // since wasmtime operates with lower level memory, it always interacts with underlying os
2224+
cageid, // target cageid (should be same)
2225+
0, // map from address 0 (includes the leading guard page per the TODO above)
2226+
cageid,
2227+
stack_end as u64, // length: guard page + stack
2228+
cageid,
2229+
// PROT_READ | PROT_WRITE: stack needs both read and write access
2230+
(typemap::PROT_READ | typemap::PROT_WRITE) as u64,
2231+
cageid,
2232+
// MAP_PRIVATE: changes are not shared; MAP_ANONYMOUS: not file-backed;
2233+
// MAP_FIXED: must map at the exact address (address 0 for guard page + stack region)
2234+
(typemap::MAP_PRIVATE | typemap::MAP_ANONYMOUS | typemap::MAP_FIXED) as u64,
2235+
cageid,
2236+
u64::MAX, // fd: -1 (required for MAP_ANONYMOUS mappings)
2237+
cageid,
2238+
0,
2239+
cageid,
2240+
);
2241+
2242+
if ret < 0 {
2243+
return Err(anyhow!("failed to allocate stack"));
2244+
}
2245+
2246+
Ok(())
2247+
}
2248+
22152249
// check if the module has the necessary exported Asyncify functions
22162250
fn support_asyncify(module: &Module) -> bool {
22172251
module.get_export(ASYNCIFY_START_UNWIND).is_some()

src/wasmtime/crates/wasmtime/src/runtime/instance.rs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ impl Instance {
340340
drop(memory_iter);
341341
let memory_base = memory.data_ptr(&mut *store) as usize;
342342

343-
let required_memory_size = if dylink_enabled {
343+
let (start_addr, required_memory_size) = if dylink_enabled {
344344
// for dynamic builds, we manually calcuate the initial memory region
345345
// which is stack size + data region size
346346
let module_meminfo = module.dylink_meminfo().unwrap();
@@ -359,7 +359,7 @@ impl Instance {
359359
rounded_stack_size, rounded_data_size
360360
);
361361

362-
rounded_stack_size + rounded_data_size
362+
(rounded_stack_size, rounded_data_size)
363363
} else {
364364
// retrieve the initial memory size for static module
365365
let plans = module.compiled_module().module().memory_plans.clone();
@@ -377,12 +377,15 @@ impl Instance {
377377

378378
let minimal_size = minimal_pages << PAGESHIFT;
379379

380-
minimal_size
381-
.try_into()
382-
.expect("allocated memory is larger than 4GB")
380+
(
381+
0,
382+
minimal_size
383+
.try_into()
384+
.expect("allocated memory is larger than 4GB"),
385+
)
383386
};
384387

385-
let required_memory_page = required_memory_size >> PAGESHIFT;
388+
let required_memory_page = (start_addr + required_memory_size) >> PAGESHIFT;
386389

387390
init_vmmap(cageid, memory_base, Some(required_memory_page));
388391
// Allocated memory should include stack AND constant data region
@@ -394,7 +397,7 @@ impl Instance {
394397
(MMAP_SYSCALL) as u64, // syscall num
395398
0, // since wasmtime operates with lower level memory, it always interacts with underlying os
396399
cageid, // target cageid (should be same)
397-
0, // the first memory region starts from 0
400+
start_addr as u64,
398401
cageid,
399402
required_memory_size as u64,
400403
cageid,

0 commit comments

Comments
 (0)