diff --git a/lib/std/Build/Step/Compile.zig b/lib/std/Build/Step/Compile.zig index 9ede31c917..18977ac472 100644 --- a/lib/std/Build/Step/Compile.zig +++ b/lib/std/Build/Step/Compile.zig @@ -64,6 +64,8 @@ initial_memory: ?u64 = null, max_memory: ?u64 = null, shared_memory: bool = false, global_base: ?u64 = null, +/// For WebAssembly only. Tells the linker to not output an entry point. +no_entry: ?bool = null, c_std: std.Build.CStd, /// Set via options; intended to be read-only after that. zig_lib_dir: ?LazyPath, @@ -1851,6 +1853,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { if (self.global_base) |global_base| { try zig_args.append(b.fmt("--global-base={d}", .{global_base})); } + try addFlag(&zig_args, "entry", self.no_entry); if (self.code_model != .default) { try zig_args.append("-mcmodel"); diff --git a/lib/std/start.zig b/lib/std/start.zig index 7ed59a4675..29fdb3b031 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -82,11 +82,15 @@ comptime { .reactor => "_initialize", .command => "_start", }; - if (!@hasDecl(root, wasm_start_sym)) { + if (!@hasDecl(root, wasm_start_sym) and @hasDecl(root, "main")) { + // Only call main when defined. For WebAssembly it's allowed to pass `-fno-entry` in which + // case it's not required to provide an entrypoint such as main. @export(wasi_start, .{ .name = wasm_start_sym }); } } else if (native_arch.isWasm() and native_os == .freestanding) { - if (!@hasDecl(root, start_sym_name)) @export(wasm_freestanding_start, .{ .name = start_sym_name }); + // Only call main when defined. For WebAssembly it's allowed to pass `-fno-entry` in which + // case it's not required to provide an entrypoint such as main. + if (!@hasDecl(root, start_sym_name) and @hasDecl(root, "main")) @export(wasm_freestanding_start, .{ .name = start_sym_name }); } else if (native_os != .other and native_os != .freestanding) { if (!@hasDecl(root, start_sym_name)) @export(_start, .{ .name = start_sym_name }); } diff --git a/src/Compilation.zig b/src/Compilation.zig index 954acd44b7..1d60be20a5 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -643,6 +643,7 @@ pub const InitOptions = struct { linker_import_symbols: bool = false, linker_import_table: bool = false, linker_export_table: bool = false, + linker_no_entry: bool = false, linker_initial_memory: ?u64 = null, linker_max_memory: ?u64 = null, linker_shared_memory: bool = false, @@ -1614,6 +1615,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { .import_symbols = options.linker_import_symbols, .import_table = options.linker_import_table, .export_table = options.linker_export_table, + .no_entry = options.linker_no_entry, .initial_memory = options.linker_initial_memory, .max_memory = options.linker_max_memory, .shared_memory = options.linker_shared_memory, @@ -2577,6 +2579,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes man.hash.addOptional(comp.bin_file.options.max_memory); man.hash.add(comp.bin_file.options.shared_memory); man.hash.addOptional(comp.bin_file.options.global_base); + man.hash.add(comp.bin_file.options.no_entry); // Mach-O specific stuff man.hash.addListOfBytes(comp.bin_file.options.framework_dirs); diff --git a/src/link.zig b/src/link.zig index 1648d6a63e..a49582aac9 100644 --- a/src/link.zig +++ b/src/link.zig @@ -166,6 +166,7 @@ pub const Options = struct { export_table: bool, initial_memory: ?u64, max_memory: ?u64, + no_entry: bool, shared_memory: bool, export_symbol_names: []const []const u8, global_base: ?u64, diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index fdac89f837..135803007a 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -2817,14 +2817,10 @@ fn setupExports(wasm: *Wasm) !void { } fn setupStart(wasm: *Wasm) !void { + if (wasm.base.options.no_entry) return; const entry_name = wasm.base.options.entry orelse "_start"; const symbol_loc = wasm.findGlobalSymbol(entry_name) orelse { - if (wasm.base.options.output_mode == .Exe) { - if (wasm.base.options.wasi_exec_model == .reactor) return; // Not required for reactors - } else { - return; // No entry point needed for non-executable wasm files - } log.err("Entry symbol '{s}' missing", .{entry_name}); return error.MissingSymbol; }; @@ -4544,24 +4540,14 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! const arg = try std.fmt.allocPrint(arena, "stack-size={d}", .{stack_size}); try argv.append(arg); - if (wasm.base.options.output_mode == .Exe) { - if (wasm.base.options.wasi_exec_model == .reactor) { - // Reactor execution model does not have _start so lld doesn't look for it. - try argv.append("--no-entry"); - // Make sure "_initialize" and other used-defined functions are exported if this is WASI reactor. - // If rdynamic is true, it will already be appended, so only verify if the user did not specify - // the flag in which case, we ensure `--export-dynamic` is called. - if (!wasm.base.options.rdynamic) { - try argv.append("--export-dynamic"); - } - } - } else if (wasm.base.options.entry == null) { - try argv.append("--no-entry"); // So lld doesn't look for _start. - } if (wasm.base.options.import_symbols) { try argv.append("--allow-undefined"); } + if (wasm.base.options.no_entry) { + try argv.append("--no-entry"); + } + // XXX - TODO: add when wasm-ld supports --build-id. // if (wasm.base.options.build_id) { // try argv.append("--build-id=tree"); diff --git a/src/main.zig b/src/main.zig index cbc7283eef..fc7e43b16f 100644 --- a/src/main.zig +++ b/src/main.zig @@ -577,6 +577,8 @@ const usage_build_generic = \\ --shared-memory (WebAssembly) use shared linear memory \\ --global-base=[addr] (WebAssembly) where to start to place global data \\ --export=[value] (WebAssembly) Force a symbol to be exported + \\ -fentry (WebAssembly) Force output an entry point + \\ -fno-entry (WebAssembly) Do not output any entry point \\ \\Test Options: \\ --test-filter [text] Skip tests that do not match filter @@ -835,6 +837,7 @@ fn buildOutputType( var linker_import_symbols: bool = false; var linker_import_table: bool = false; var linker_export_table: bool = false; + var linker_no_entry: ?bool = null; var linker_initial_memory: ?u64 = null; var linker_max_memory: ?u64 = null; var linker_shared_memory: bool = false; @@ -1503,6 +1506,10 @@ fn buildOutputType( } } else if (mem.eql(u8, arg, "--import-memory")) { linker_import_memory = true; + } else if (mem.eql(u8, arg, "-fentry")) { + linker_no_entry = false; + } else if (mem.eql(u8, arg, "-fno-entry")) { + linker_no_entry = true; } else if (mem.eql(u8, arg, "--export-memory")) { linker_export_memory = true; } else if (mem.eql(u8, arg, "--import-symbols")) { @@ -2134,6 +2141,8 @@ fn buildOutputType( linker_import_table = true; } else if (mem.eql(u8, arg, "--export-table")) { linker_export_table = true; + } else if (mem.eql(u8, arg, "--no-entry")) { + linker_no_entry = true; } else if (mem.eql(u8, arg, "--initial-memory")) { const next_arg = linker_args_it.nextOrFatal(); linker_initial_memory = std.fmt.parseUnsigned(u32, eatIntPrefix(next_arg, 16), 16) catch |err| { @@ -2618,6 +2627,25 @@ fn buildOutputType( if (single_threaded == null) { single_threaded = true; } + if (wasi_exec_model) |model| { + if (model == .reactor) { + if (linker_no_entry != null and !linker_no_entry.?) { + fatal("WASI exucution model 'reactor' incompatible with flag '-fentry'. Reactor execution model has no entry point", .{}); + } + if (entry) |entry_name| { + if (!mem.eql(u8, "_initialize", entry_name)) { + fatal("the entry symbol of the reactor model must be '_initialize', but found '{s}'", .{entry_name}); + } + } else { + entry = "_initialize"; + } + } + } + if (linker_no_entry) |no_entry| { + if (no_entry and entry != null) { + fatal("combination of '--entry' and `-fno-entry` are incompatible", .{}); + } + } if (linker_shared_memory) { if (output_mode == .Obj) { fatal("shared memory is not allowed in object files", .{}); @@ -3465,6 +3493,7 @@ fn buildOutputType( .linker_import_symbols = linker_import_symbols, .linker_import_table = linker_import_table, .linker_export_table = linker_export_table, + .linker_no_entry = linker_no_entry orelse false, .linker_initial_memory = linker_initial_memory, .linker_max_memory = linker_max_memory, .linker_shared_memory = linker_shared_memory,