diff --git a/doc/langref.html.in b/doc/langref.html.in index b3ae609a7542..7e4d25a2fa12 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -11336,12 +11336,12 @@ all your base are belong to us{#end_shell_samp#} {#header_open|WebAssembly#}

Zig supports building for WebAssembly out of the box.

{#header_open|Freestanding#} -

For host environments like the web browser and nodejs, build as a dynamic library using the freestanding +

For host environments like the web browser and nodejs, build as an executable using the freestanding OS target. Here's an example of running Zig code compiled to WebAssembly with nodejs.

- {#code_begin|lib|math#} + {#code_begin|exe|math#} {#target_wasm#} - {#link_mode_dynamic#} - {#additonal_option|-rdynamic#} + {#additonal_option|-fno-entry#} + {#additonal_option|--export=add#} extern fn print(i32) void; export fn add(a: i32, b: i32) void { diff --git a/lib/std/Build/Step/Compile.zig b/lib/std/Build/Step/Compile.zig index 9ede31c9172b..2d08541545b6 100644 --- a/lib/std/Build/Step/Compile.zig +++ b/lib/std/Build/Step/Compile.zig @@ -189,7 +189,8 @@ dll_export_fns: ?bool = null, subsystem: ?std.Target.SubSystem = null, -entry_symbol_name: ?[]const u8 = null, +/// How the linker must handle the entry point of the executable. +entry: Entry = .default, /// List of symbols forced as undefined in the symbol table /// thus forcing their resolution by the linker. @@ -304,6 +305,18 @@ const FrameworkLinkInfo = struct { weak: bool = false, }; +const Entry = union(enum) { + /// Let the compiler decide whether to make an entry point and what to name + /// it. + default, + /// The executable will have no entry point. + disabled, + /// The executable will have an entry point with the default symbol name. + enabled, + /// The executable will have an entry point with the specified symbol name. + symbol_name: []const u8, +}; + pub const IncludeDir = union(enum) { path: LazyPath, path_system: LazyPath, @@ -1418,9 +1431,13 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { try zig_args.append(try std.fmt.allocPrint(b.allocator, "-ofmt={s}", .{@tagName(ofmt)})); } - if (self.entry_symbol_name) |entry| { - try zig_args.append("--entry"); - try zig_args.append(entry); + switch (self.entry) { + .default => {}, + .disabled => try zig_args.append("-fno-entry"), + .enabled => try zig_args.append("-fentry"), + .symbol_name => |entry_name| { + try zig_args.append(try std.fmt.allocPrint(b.allocator, "-fentry={s}", .{entry_name})); + }, } { diff --git a/lib/std/start.zig b/lib/std/start.zig index 7ed59a467590..29fdb3b03126 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/link/Wasm.zig b/src/link/Wasm.zig index fdac89f8373e..baa10be60394 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -2817,15 +2817,11 @@ fn setupExports(wasm: *Wasm) !void { } fn setupStart(wasm: *Wasm) !void { - const entry_name = wasm.base.options.entry orelse "_start"; + // do not export entry point if user set none or no default was set. + const entry_name = wasm.base.options.entry orelse return; 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}); + log.err("Entry symbol '{s}' missing, use '-fno-entry' to suppress", .{entry_name}); return error.MissingSymbol; }; @@ -4535,6 +4531,8 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! if (wasm.base.options.entry) |entry| { try argv.append("--entry"); try argv.append(entry); + } else { + try argv.append("--no-entry"); } // Increase the default stack size to a more reasonable value of 1MB instead of @@ -4544,24 +4542,17 @@ 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.output_mode == .Lib and wasm.base.options.link_mode == .Dynamic) { + try argv.append("--shared"); + } + if (wasm.base.options.pie) { + try argv.append("--pie"); + } + // 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 cbc7283eefb9..51c5397c1017 100644 --- a/src/main.zig +++ b/src/main.zig @@ -509,7 +509,9 @@ const usage_build_generic = \\ --dynamic-linker [path] Set the dynamic interpreter path (usually ld.so) \\ --sysroot [path] Set the system root directory (usually /) \\ --version [ver] Dynamic library semver - \\ --entry [name] Set the entrypoint symbol name + \\ -fentry Enable entry point with default symbol name + \\ -fentry=[name] Override the entry point symbol name + \\ -fno-entry Do not output any entry point \\ --force_undefined [name] Specify the symbol must be defined for the link to succeed \\ -fsoname[=name] Override the default SONAME value \\ -fno-soname Disable emitting a SONAME @@ -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_force_entry: ?bool = null; var linker_initial_memory: ?u64 = null; var linker_max_memory: ?u64 = null; var linker_shared_memory: bool = false; @@ -1071,8 +1074,8 @@ fn buildOutputType( subsystem = try parseSubSystem(args_iter.nextOrFatal()); } else if (mem.eql(u8, arg, "-O")) { optimize_mode_string = args_iter.nextOrFatal(); - } else if (mem.eql(u8, arg, "--entry")) { - entry = args_iter.nextOrFatal(); + } else if (mem.startsWith(u8, arg, "-fentry=")) { + entry = arg["-fentry=".len..]; } else if (mem.eql(u8, arg, "--force_undefined")) { try force_undefined_symbols.put(gpa, args_iter.nextOrFatal(), {}); } else if (mem.eql(u8, arg, "--stack")) { @@ -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_force_entry = true; + } else if (mem.eql(u8, arg, "-fno-entry")) { + linker_force_entry = false; } 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_force_entry = false; } 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| { @@ -2596,6 +2605,23 @@ fn buildOutputType( link_libcpp = true; } + if (linker_force_entry) |force| { + if (!force) { + entry = null; + } else if (entry == null and output_mode == .Exe) { + entry = switch (target_info.target.ofmt) { + .coff => "wWinMainCRTStartup", + .macho => "_main", + .elf, .plan9 => "_start", + .wasm => defaultWasmEntryName(wasi_exec_model), + else => |tag| fatal("No default entry point available for output format {s}", .{@tagName(tag)}), + }; + } + } else if (entry == null and target_info.target.isWasm() and output_mode == .Exe) { + // For WebAssembly the compiler defaults to setting the entry name when no flags are set. + entry = defaultWasmEntryName(wasi_exec_model); + } + if (target_info.target.ofmt == .coff) { // Now that we know the target supports resources, // we can add the res files as link objects. @@ -2618,6 +2644,23 @@ fn buildOutputType( if (single_threaded == null) { single_threaded = true; } + if (link_mode) |mode| { + if (mode == .Dynamic) { + if (linker_export_memory != null and linker_export_memory.?) { + fatal("flags '-dynamic' and '--export-memory' are incompatible", .{}); + } + // User did not supply `--export-memory` which is incompatible with -dynamic, therefore + // set the flag to false to ensure it does not get enabled by default. + linker_export_memory = false; + } + } + if (wasi_exec_model != null and wasi_exec_model.? == .reactor) { + 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}); + } + } + } if (linker_shared_memory) { if (output_mode == .Obj) { fatal("shared memory is not allowed in object files", .{}); @@ -7214,3 +7257,11 @@ fn createDependenciesModule( try main_mod.deps.put(arena, "@dependencies", deps_mod); return deps_mod; } + +fn defaultWasmEntryName(exec_model: ?std.builtin.WasiExecModel) []const u8 { + const model = exec_model orelse .command; + if (model == .reactor) { + return "_initialize"; + } + return "_start"; +} diff --git a/test/link/elf.zig b/test/link/elf.zig index dad3604c31ae..d7fe7562763c 100644 --- a/test/link/elf.zig +++ b/test/link/elf.zig @@ -651,7 +651,7 @@ fn testEntryPoint(b: *Build, opts: Options) *Step { const exe = addExecutable(b, "main", opts); exe.addObject(a_o); exe.addObject(b_o); - exe.entry_symbol_name = "foo"; + exe.entry = .{ .symbol_name = "foo" }; const check = exe.checkObject(); check.checkStart(); @@ -667,7 +667,7 @@ fn testEntryPoint(b: *Build, opts: Options) *Step { const exe = addExecutable(b, "other", opts); exe.addObject(a_o); exe.addObject(b_o); - exe.entry_symbol_name = "bar"; + exe.entry = .{ .symbol_name = "bar" }; const check = exe.checkObject(); check.checkStart(); diff --git a/test/link/macho/entry/build.zig b/test/link/macho/entry/build.zig index fcba02cd9a88..9f493d2715d2 100644 --- a/test/link/macho/entry/build.zig +++ b/test/link/macho/entry/build.zig @@ -20,7 +20,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize }); exe.addCSourceFile(.{ .file = .{ .path = "main.c" }, .flags = &.{} }); exe.linkLibC(); - exe.entry_symbol_name = "_non_main"; + exe.entry = .{ .symbol_name = "_non_main" }; const check_exe = exe.checkObject(); diff --git a/test/link/macho/entry_in_dylib/build.zig b/test/link/macho/entry_in_dylib/build.zig index 97ffa917b451..eb036abe6a5f 100644 --- a/test/link/macho/entry_in_dylib/build.zig +++ b/test/link/macho/entry_in_dylib/build.zig @@ -30,7 +30,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize exe.addCSourceFile(.{ .file = .{ .path = "main.c" }, .flags = &.{} }); exe.linkLibrary(lib); exe.linkLibC(); - exe.entry_symbol_name = "_bootstrap"; + exe.entry = .{ .symbol_name = "_bootstrap" }; exe.forceUndefinedSymbol("_my_main"); const check_exe = exe.checkObject(); diff --git a/test/link/wasm/archive/build.zig b/test/link/wasm/archive/build.zig index d87b8e973e7f..3da284ac8ff5 100644 --- a/test/link/wasm/archive/build.zig +++ b/test/link/wasm/archive/build.zig @@ -15,12 +15,13 @@ pub fn build(b: *std.Build) void { fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { // The code in question will pull-in compiler-rt, // and therefore link with its archive file. - const lib = b.addSharedLibrary(.{ + const lib = b.addExecutable(.{ .name = "main", .root_source_file = .{ .path = "main.zig" }, .optimize = optimize, .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, }); + lib.entry = .disabled; lib.use_llvm = false; lib.use_lld = false; lib.strip = false; diff --git a/test/link/wasm/basic-features/build.zig b/test/link/wasm/basic-features/build.zig index 703bd13febca..0566fbe2c177 100644 --- a/test/link/wasm/basic-features/build.zig +++ b/test/link/wasm/basic-features/build.zig @@ -4,7 +4,7 @@ pub const requires_stage2 = true; pub fn build(b: *std.Build) void { // Library with explicitly set cpu features - const lib = b.addSharedLibrary(.{ + const lib = b.addExecutable(.{ .name = "lib", .root_source_file = .{ .path = "main.zig" }, .optimize = .Debug, @@ -15,6 +15,7 @@ pub fn build(b: *std.Build) void { .os_tag = .freestanding, }, }); + lib.entry = .disabled; lib.use_llvm = false; lib.use_lld = false; diff --git a/test/link/wasm/bss/build.zig b/test/link/wasm/bss/build.zig index e6cb9d4f3dcb..1bc059acdeca 100644 --- a/test/link/wasm/bss/build.zig +++ b/test/link/wasm/bss/build.zig @@ -14,12 +14,13 @@ pub fn build(b: *std.Build) void { fn add(b: *std.Build, test_step: *std.Build.Step, optimize_mode: std.builtin.OptimizeMode, is_safe: bool) void { { - const lib = b.addSharedLibrary(.{ + const lib = b.addExecutable(.{ .name = "lib", .root_source_file = .{ .path = "lib.zig" }, .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, .optimize = optimize_mode, }); + lib.entry = .disabled; lib.use_llvm = false; lib.use_lld = false; lib.strip = false; @@ -60,12 +61,13 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize_mode: std.builtin.Opt // verify zero'd declaration is stored in bss for all optimization modes. { - const lib = b.addSharedLibrary(.{ + const lib = b.addExecutable(.{ .name = "lib", .root_source_file = .{ .path = "lib2.zig" }, .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, .optimize = optimize_mode, }); + lib.entry = .disabled; lib.use_llvm = false; lib.use_lld = false; lib.strip = false; diff --git a/test/link/wasm/export-data/build.zig b/test/link/wasm/export-data/build.zig index 7e3128aa7692..58a8795390e7 100644 --- a/test/link/wasm/export-data/build.zig +++ b/test/link/wasm/export-data/build.zig @@ -9,12 +9,13 @@ pub fn build(b: *std.Build) void { return; } - const lib = b.addSharedLibrary(.{ + const lib = b.addExecutable(.{ .name = "lib", .root_source_file = .{ .path = "lib.zig" }, .optimize = .ReleaseSafe, // to make the output deterministic in address positions .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, }); + lib.entry = .disabled; lib.use_lld = false; lib.export_symbol_names = &.{ "foo", "bar" }; lib.global_base = 0; // put data section at address 0 to make data symbols easier to parse diff --git a/test/link/wasm/export/build.zig b/test/link/wasm/export/build.zig index 5afe2df768e4..5c0306335dd5 100644 --- a/test/link/wasm/export/build.zig +++ b/test/link/wasm/export/build.zig @@ -13,31 +13,34 @@ pub fn build(b: *std.Build) void { } fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { - const no_export = b.addSharedLibrary(.{ + const no_export = b.addExecutable(.{ .name = "no-export", .root_source_file = .{ .path = "main.zig" }, .optimize = optimize, .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, }); + no_export.entry = .disabled; no_export.use_llvm = false; no_export.use_lld = false; - const dynamic_export = b.addSharedLibrary(.{ + const dynamic_export = b.addExecutable(.{ .name = "dynamic", .root_source_file = .{ .path = "main.zig" }, .optimize = optimize, .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, }); + dynamic_export.entry = .disabled; dynamic_export.rdynamic = true; dynamic_export.use_llvm = false; dynamic_export.use_lld = false; - const force_export = b.addSharedLibrary(.{ + const force_export = b.addExecutable(.{ .name = "force", .root_source_file = .{ .path = "main.zig" }, .optimize = optimize, .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, }); + force_export.entry = .disabled; force_export.export_symbol_names = &.{"foo"}; force_export.use_llvm = false; force_export.use_lld = false; diff --git a/test/link/wasm/extern-mangle/build.zig b/test/link/wasm/extern-mangle/build.zig index 841d118efdc9..9f450c2dcc0e 100644 --- a/test/link/wasm/extern-mangle/build.zig +++ b/test/link/wasm/extern-mangle/build.zig @@ -11,12 +11,13 @@ pub fn build(b: *std.Build) void { } fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { - const lib = b.addSharedLibrary(.{ + const lib = b.addExecutable(.{ .name = "lib", .root_source_file = .{ .path = "lib.zig" }, .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, .optimize = optimize, }); + lib.entry = .disabled; lib.import_symbols = true; // import `a` and `b` lib.rdynamic = true; // export `foo` diff --git a/test/link/wasm/function-table/build.zig b/test/link/wasm/function-table/build.zig index 796ba670ad7a..906a2556423b 100644 --- a/test/link/wasm/function-table/build.zig +++ b/test/link/wasm/function-table/build.zig @@ -13,32 +13,35 @@ pub fn build(b: *std.Build) void { } fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { - const import_table = b.addSharedLibrary(.{ + const import_table = b.addExecutable(.{ .name = "import_table", .root_source_file = .{ .path = "lib.zig" }, .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, .optimize = optimize, }); + import_table.entry = .disabled; import_table.use_llvm = false; import_table.use_lld = false; import_table.import_table = true; - const export_table = b.addSharedLibrary(.{ + const export_table = b.addExecutable(.{ .name = "export_table", .root_source_file = .{ .path = "lib.zig" }, .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, .optimize = optimize, }); + export_table.entry = .disabled; export_table.use_llvm = false; export_table.use_lld = false; export_table.export_table = true; - const regular_table = b.addSharedLibrary(.{ + const regular_table = b.addExecutable(.{ .name = "regular_table", .root_source_file = .{ .path = "lib.zig" }, .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, .optimize = optimize, }); + regular_table.entry = .disabled; regular_table.use_llvm = false; regular_table.use_lld = false; diff --git a/test/link/wasm/infer-features/build.zig b/test/link/wasm/infer-features/build.zig index 6264edfba912..5c7fa574472a 100644 --- a/test/link/wasm/infer-features/build.zig +++ b/test/link/wasm/infer-features/build.zig @@ -17,7 +17,7 @@ pub fn build(b: *std.Build) void { // Wasm library that doesn't have any features specified. This will // infer its featureset from other linked object files. - const lib = b.addSharedLibrary(.{ + const lib = b.addExecutable(.{ .name = "lib", .root_source_file = .{ .path = "main.zig" }, .optimize = .Debug, @@ -27,6 +27,7 @@ pub fn build(b: *std.Build) void { .os_tag = .freestanding, }, }); + lib.entry = .disabled; lib.use_llvm = false; lib.use_lld = false; lib.addObject(c_obj); diff --git a/test/link/wasm/producers/build.zig b/test/link/wasm/producers/build.zig index f541a1c8ec14..e2bd95f45065 100644 --- a/test/link/wasm/producers/build.zig +++ b/test/link/wasm/producers/build.zig @@ -14,12 +14,13 @@ pub fn build(b: *std.Build) void { } fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { - const lib = b.addSharedLibrary(.{ + const lib = b.addExecutable(.{ .name = "lib", .root_source_file = .{ .path = "lib.zig" }, .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, .optimize = optimize, }); + lib.entry = .disabled; lib.use_llvm = false; lib.use_lld = false; lib.strip = false; diff --git a/test/link/wasm/segments/build.zig b/test/link/wasm/segments/build.zig index d01c34f90d39..21b954a9022a 100644 --- a/test/link/wasm/segments/build.zig +++ b/test/link/wasm/segments/build.zig @@ -13,12 +13,13 @@ pub fn build(b: *std.Build) void { } fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { - const lib = b.addSharedLibrary(.{ + const lib = b.addExecutable(.{ .name = "lib", .root_source_file = .{ .path = "lib.zig" }, .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, .optimize = optimize, }); + lib.entry = .disabled; lib.use_llvm = false; lib.use_lld = false; lib.strip = false; diff --git a/test/link/wasm/shared-memory/build.zig b/test/link/wasm/shared-memory/build.zig index cf84ad7528c2..a1d660d13582 100644 --- a/test/link/wasm/shared-memory/build.zig +++ b/test/link/wasm/shared-memory/build.zig @@ -11,88 +11,87 @@ pub fn build(b: *std.Build) void { } fn add(b: *std.Build, test_step: *std.Build.Step, optimize_mode: std.builtin.OptimizeMode) void { - { - const lib = b.addSharedLibrary(.{ - .name = "lib", - .root_source_file = .{ .path = "lib.zig" }, - .target = .{ - .cpu_arch = .wasm32, - .cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp }, - .cpu_features_add = std.Target.wasm.featureSet(&.{ .atomics, .bulk_memory }), - .os_tag = .freestanding, - }, - .optimize = optimize_mode, - }); - lib.use_lld = false; - lib.strip = false; - lib.import_memory = true; - lib.export_memory = true; - lib.shared_memory = true; - lib.max_memory = 67108864; - lib.single_threaded = false; - lib.export_symbol_names = &.{"foo"}; + const lib = b.addExecutable(.{ + .name = "lib", + .root_source_file = .{ .path = "lib.zig" }, + .target = .{ + .cpu_arch = .wasm32, + .cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp }, + .cpu_features_add = std.Target.wasm.featureSet(&.{ .atomics, .bulk_memory }), + .os_tag = .freestanding, + }, + .optimize = optimize_mode, + }); + lib.entry = .disabled; + lib.use_lld = false; + lib.strip = false; + lib.import_memory = true; + lib.export_memory = true; + lib.shared_memory = true; + lib.max_memory = 67108864; + lib.single_threaded = false; + lib.export_symbol_names = &.{"foo"}; - const check_lib = lib.checkObject(); + const check_lib = lib.checkObject(); - check_lib.checkStart("Section import"); - check_lib.checkNext("entries 1"); - check_lib.checkNext("module env"); - check_lib.checkNext("name memory"); // ensure we are importing memory + check_lib.checkStart("Section import"); + check_lib.checkNext("entries 1"); + check_lib.checkNext("module env"); + check_lib.checkNext("name memory"); // ensure we are importing memory - check_lib.checkStart("Section export"); - check_lib.checkNext("entries 2"); - check_lib.checkNext("name memory"); // ensure we also export memory again + check_lib.checkStart("Section export"); + check_lib.checkNext("entries 2"); + check_lib.checkNext("name memory"); // ensure we also export memory again - // This section *must* be emit as the start function is set to the index - // of __wasm_init_memory - // release modes will have the TLS segment optimized out in our test-case. - // This means we won't have __wasm_init_memory in such case, and therefore - // should also not have a section "start" - if (optimize_mode == .Debug) { - check_lib.checkStart("Section start"); - } - - // This section is only and *must* be emit when shared-memory is enabled - // release modes will have the TLS segment optimized out in our test-case. - if (optimize_mode == .Debug) { - check_lib.checkStart("Section data_count"); - check_lib.checkNext("count 3"); - } + // This section *must* be emit as the start function is set to the index + // of __wasm_init_memory + // release modes will have the TLS segment optimized out in our test-case. + // This means we won't have __wasm_init_memory in such case, and therefore + // should also not have a section "start" + if (optimize_mode == .Debug) { + check_lib.checkStart("Section start"); + } - check_lib.checkStart("Section custom"); - check_lib.checkNext("name name"); - check_lib.checkNext("type function"); - if (optimize_mode == .Debug) { - check_lib.checkNext("name __wasm_init_memory"); - } - check_lib.checkNext("name __wasm_init_tls"); - check_lib.checkNext("type global"); + // This section is only and *must* be emit when shared-memory is enabled + // release modes will have the TLS segment optimized out in our test-case. + if (optimize_mode == .Debug) { + check_lib.checkStart("Section data_count"); + check_lib.checkNext("count 3"); + } - // In debug mode the symbol __tls_base is resolved to an undefined symbol - // from the object file, hence its placement differs than in release modes - // where the entire tls segment is optimized away, and tls_base will have - // its original position. - if (optimize_mode == .Debug) { - check_lib.checkNext("name __tls_size"); - check_lib.checkNext("name __tls_align"); - check_lib.checkNext("name __tls_base"); - } else { - check_lib.checkNext("name __tls_base"); - check_lib.checkNext("name __tls_size"); - check_lib.checkNext("name __tls_align"); - } + check_lib.checkStart("Section custom"); + check_lib.checkNext("name name"); + check_lib.checkNext("type function"); + if (optimize_mode == .Debug) { + check_lib.checkNext("name __wasm_init_memory"); + } + check_lib.checkNext("name __wasm_init_tls"); + check_lib.checkNext("type global"); - check_lib.checkNext("type data_segment"); - if (optimize_mode == .Debug) { - check_lib.checkNext("names 3"); - check_lib.checkNext("index 0"); - check_lib.checkNext("name .rodata"); - check_lib.checkNext("index 1"); - check_lib.checkNext("name .bss"); - check_lib.checkNext("index 2"); - check_lib.checkNext("name .tdata"); - } + // In debug mode the symbol __tls_base is resolved to an undefined symbol + // from the object file, hence its placement differs than in release modes + // where the entire tls segment is optimized away, and tls_base will have + // its original position. + if (optimize_mode == .Debug) { + check_lib.checkNext("name __tls_size"); + check_lib.checkNext("name __tls_align"); + check_lib.checkNext("name __tls_base"); + } else { + check_lib.checkNext("name __tls_base"); + check_lib.checkNext("name __tls_size"); + check_lib.checkNext("name __tls_align"); + } - test_step.dependOn(&check_lib.step); + check_lib.checkNext("type data_segment"); + if (optimize_mode == .Debug) { + check_lib.checkNext("names 3"); + check_lib.checkNext("index 0"); + check_lib.checkNext("name .rodata"); + check_lib.checkNext("index 1"); + check_lib.checkNext("name .bss"); + check_lib.checkNext("index 2"); + check_lib.checkNext("name .tdata"); } + + test_step.dependOn(&check_lib.step); } diff --git a/test/link/wasm/stack_pointer/build.zig b/test/link/wasm/stack_pointer/build.zig index ce724e573639..00ef54c0528e 100644 --- a/test/link/wasm/stack_pointer/build.zig +++ b/test/link/wasm/stack_pointer/build.zig @@ -13,12 +13,13 @@ pub fn build(b: *std.Build) void { } fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { - const lib = b.addSharedLibrary(.{ + const lib = b.addExecutable(.{ .name = "lib", .root_source_file = .{ .path = "lib.zig" }, .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, .optimize = optimize, }); + lib.entry = .disabled; lib.use_llvm = false; lib.use_lld = false; lib.strip = false; diff --git a/test/link/wasm/type/build.zig b/test/link/wasm/type/build.zig index 7110f465f480..de574e36e4cb 100644 --- a/test/link/wasm/type/build.zig +++ b/test/link/wasm/type/build.zig @@ -13,12 +13,13 @@ pub fn build(b: *std.Build) void { } fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { - const lib = b.addSharedLibrary(.{ + const lib = b.addExecutable(.{ .name = "lib", .root_source_file = .{ .path = "lib.zig" }, .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, .optimize = optimize, }); + lib.entry = .disabled; lib.use_llvm = false; lib.use_lld = false; lib.strip = false;