From d2cdfb949033cfd463cdd4ef1bbf93f94d4d190a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 9 Nov 2021 14:29:20 -0700 Subject: [PATCH] stage2: add 4 new linker flags for WebAssembly --import-memory import memory from the environment --initial-memory=[bytes] initial size of the linear memory --max-memory=[bytes] maximum size of the linear memory --global-base=[addr] where to start to place global data See #8633 --- lib/std/build.zig | 16 ++++++++++++++++ src/Compilation.zig | 8 ++++++++ src/link.zig | 4 ++++ src/link/Wasm.zig | 23 +++++++++++++++++++++++ src/main.zig | 36 +++++++++++++++++++++++++++++++++++- 5 files changed, 86 insertions(+), 1 deletion(-) diff --git a/lib/std/build.zig b/lib/std/build.zig index 3f56b5752c..94cab99fa4 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -1439,6 +1439,10 @@ pub const LibExeObjStep = struct { disable_sanitize_c: bool, sanitize_thread: bool, rdynamic: bool, + import_memory: bool = false, + initial_memory: ?u64 = null, + max_memory: ?u64 = null, + global_base: ?u64 = null, c_std: Builder.CStd, override_lib_dir: ?[]const u8, main_pkg_path: ?[]const u8, @@ -2431,6 +2435,18 @@ pub const LibExeObjStep = struct { if (self.rdynamic) { try zig_args.append("-rdynamic"); } + if (self.import_memory) { + try zig_args.append("--import-memory"); + } + if (self.initial_memory) |initial_memory| { + try zig_args.append(builder.fmt("--initial-memory={d}", .{initial_memory})); + } + if (self.max_memory) |max_memory| { + try zig_args.append(builder.fmt("--max-memory={d}", .{max_memory})); + } + if (self.global_base) |global_base| { + try zig_args.append(builder.fmt("--global-base={d}", .{global_base})); + } if (self.code_model != .default) { try zig_args.append("-mcmodel"); diff --git a/src/Compilation.zig b/src/Compilation.zig index 4a8d9aec02..e03efebeb4 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -716,6 +716,10 @@ pub const InitOptions = struct { linker_gc_sections: ?bool = null, linker_allow_shlib_undefined: ?bool = null, linker_bind_global_refs_locally: ?bool = null, + linker_import_memory: ?bool = null, + linker_initial_memory: ?u64 = null, + linker_max_memory: ?u64 = null, + linker_global_base: ?u64 = null, each_lib_rpath: ?bool = null, disable_c_depfile: bool = false, linker_z_nodelete: bool = false, @@ -1401,6 +1405,10 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { .function_sections = options.function_sections, .allow_shlib_undefined = options.linker_allow_shlib_undefined, .bind_global_refs_locally = options.linker_bind_global_refs_locally orelse false, + .import_memory = options.linker_import_memory orelse false, + .initial_memory = options.linker_initial_memory, + .max_memory = options.linker_max_memory, + .global_base = options.linker_global_base, .z_nodelete = options.linker_z_nodelete, .z_notext = options.linker_z_notext, .z_defs = options.linker_z_defs, diff --git a/src/link.zig b/src/link.zig index 762c6f9212..e023453284 100644 --- a/src/link.zig +++ b/src/link.zig @@ -82,6 +82,10 @@ pub const Options = struct { nxcompat: bool, dynamicbase: bool, bind_global_refs_locally: bool, + import_memory: bool, + initial_memory: ?u64, + max_memory: ?u64, + global_base: ?u64, is_native_os: bool, is_native_abi: bool, pic: bool, diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index a75ad1b2f7..80c529ff94 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -681,6 +681,10 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void { try man.addOptionalFile(compiler_rt_path); man.hash.addOptional(self.base.options.stack_size_override); man.hash.addListOfBytes(self.base.options.extra_lld_args); + man.hash.add(self.base.options.import_memory); + man.hash.addOptional(self.base.options.initial_memory); + man.hash.addOptional(self.base.options.max_memory); + man.hash.addOptional(self.base.options.global_base); // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock. _ = try man.hit(); @@ -754,6 +758,25 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void { } } + if (self.base.options.import_memory) { + try argv.append("--import-memory"); + } + + if (self.base.options.initial_memory) |initial_memory| { + const arg = try std.fmt.allocPrint(arena, "--initial-memory={d}", .{initial_memory}); + try argv.append(arg); + } + + if (self.base.options.max_memory) |max_memory| { + const arg = try std.fmt.allocPrint(arena, "--max-memory={d}", .{max_memory}); + try argv.append(arg); + } + + if (self.base.options.global_base) |global_base| { + const arg = try std.fmt.allocPrint(arena, "--global-base={d}", .{global_base}); + try argv.append(arg); + } + if (self.base.options.output_mode == .Exe) { // Increase the default stack size to a more reasonable value of 1MB instead of // the default of 1 Wasm page being 64KB, unless overridden by the user. diff --git a/src/main.zig b/src/main.zig index f2d9b7e098..f5ab7f7368 100644 --- a/src/main.zig +++ b/src/main.zig @@ -332,7 +332,7 @@ const usage_build_generic = \\ -mno-red-zone Force-disable the "red-zone" \\ -fomit-frame-pointer Omit the stack frame pointer \\ -fno-omit-frame-pointer Store the stack frame pointer - \\ -mexec-model=[value] Execution model (WASI only) + \\ -mexec-model=[value] (WASI) Execution model \\ --name [name] Override root name (not a file path) \\ -O [mode] Choose what to optimize for \\ Debug (default) Optimizations off, safety on @@ -424,6 +424,10 @@ const usage_build_generic = \\ --image-base [addr] Set base address for executable image \\ -framework [name] (Darwin) link against framework \\ -F[dir] (Darwin) add search path for frameworks + \\ --import-memory (WebAssembly) import memory from the environment + \\ --initial-memory=[bytes] (WebAssembly) initial size of the linear memory + \\ --max-memory=[bytes] (WebAssembly) maximum size of the linear memory + \\ --global-base=[addr] (WebAssembly) where to start to place global data \\ \\Test Options: \\ --test-filter [text] Skip tests that do not match filter @@ -609,6 +613,10 @@ fn buildOutputType( var linker_gc_sections: ?bool = null; var linker_allow_shlib_undefined: ?bool = null; var linker_bind_global_refs_locally: ?bool = null; + var linker_import_memory: ?bool = null; + var linker_initial_memory: ?u64 = null; + var linker_max_memory: ?u64 = null; + var linker_global_base: ?u64 = null; var linker_z_nodelete = false; var linker_z_notext = false; var linker_z_defs = false; @@ -1125,6 +1133,14 @@ fn buildOutputType( } else { warn("unsupported linker extension flag: -z {s}", .{z_arg}); } + } else if (mem.eql(u8, arg, "--import-memory")) { + linker_import_memory = true; + } else if (mem.startsWith(u8, arg, "--initial-memory=")) { + linker_initial_memory = parseIntSuffix(arg, "--initial-memory=".len); + } else if (mem.startsWith(u8, arg, "--max-memory=")) { + linker_max_memory = parseIntSuffix(arg, "--max-memory=".len); + } else if (mem.startsWith(u8, arg, "--global-base=")) { + linker_global_base = parseIntSuffix(arg, "--global-base=".len); } else if (mem.eql(u8, arg, "-Bsymbolic")) { linker_bind_global_refs_locally = true; } else if (mem.eql(u8, arg, "--debug-compile-errors")) { @@ -1453,6 +1469,14 @@ fn buildOutputType( linker_allow_shlib_undefined = false; } else if (mem.eql(u8, arg, "-Bsymbolic")) { linker_bind_global_refs_locally = true; + } else if (mem.eql(u8, arg, "--import-memory")) { + linker_import_memory = true; + } else if (mem.startsWith(u8, arg, "--initial-memory=")) { + linker_initial_memory = parseIntSuffix(arg, "--initial-memory=".len); + } else if (mem.startsWith(u8, arg, "--max-memory=")) { + linker_max_memory = parseIntSuffix(arg, "--max-memory=".len); + } else if (mem.startsWith(u8, arg, "--global-base=")) { + linker_global_base = parseIntSuffix(arg, "--global-base=".len); } else if (mem.eql(u8, arg, "-z")) { i += 1; if (i >= linker_args.items.len) { @@ -2148,6 +2172,10 @@ fn buildOutputType( .linker_gc_sections = linker_gc_sections, .linker_allow_shlib_undefined = linker_allow_shlib_undefined, .linker_bind_global_refs_locally = linker_bind_global_refs_locally, + .linker_import_memory = linker_import_memory, + .linker_initial_memory = linker_initial_memory, + .linker_max_memory = linker_max_memory, + .linker_global_base = linker_global_base, .linker_z_nodelete = linker_z_nodelete, .linker_z_notext = linker_z_notext, .linker_z_defs = linker_z_defs, @@ -4371,3 +4399,9 @@ pub fn cmdChangelist( } try bw.flush(); } + +fn parseIntSuffix(arg: []const u8, prefix_len: usize) u64 { + return std.fmt.parseUnsigned(u64, arg[prefix_len..], 0) catch |err| { + fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) }); + }; +}