Commit d2cdfb9490

Andrew Kelley <andrew@ziglang.org>
2021-11-09 22:29:20
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
1 parent 9bb7ff6
Changed files (5)
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");
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.
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,
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,
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) });
+    };
+}