| author | |
| committer | |
| log | 702b809ea3e9b9dbdc1fd6efe9306442487e7103 |
| tree | bd639378ad2931013ff49789aadfc2104093261c |
| parent | bec36aa7c028f2eaec94a2358f3e1326fcb9a30c |
| parent | c893f837151d4764fd34911376836a01192b4d75 |
| signature |
wasm-linker: implement `-fno-entry` and correctly pass `--shared` and `--pie` when given21 files changed, 209 insertions(+), 130 deletions(-)
doc/langref.html.in+4-4| ... | ... | @@ -11336,12 +11336,12 @@ all your base are belong to us{#end_shell_samp#} |
| 11336 | 11336 | {#header_open|WebAssembly#} |
| 11337 | 11337 | <p>Zig supports building for WebAssembly out of the box.</p> |
| 11338 | 11338 | {#header_open|Freestanding#} |
| 11339 | <p>For host environments like the web browser and nodejs, build as a dynamic library using the freestanding | |
| 11339 | <p>For host environments like the web browser and nodejs, build as an executable using the freestanding | |
| 11340 | 11340 | OS target. Here's an example of running Zig code compiled to WebAssembly with nodejs.</p> |
| 11341 | {#code_begin|lib|math#} | |
| 11341 | {#code_begin|exe|math#} | |
| 11342 | 11342 | {#target_wasm#} |
| 11343 | {#link_mode_dynamic#} | |
| 11344 | {#additonal_option|-rdynamic#} | |
| 11343 | {#additonal_option|-fno-entry#} | |
| 11344 | {#additonal_option|--export=add#} | |
| 11345 | 11345 | extern fn print(i32) void; |
| 11346 | 11346 | |
| 11347 | 11347 | export fn add(a: i32, b: i32) void { |
lib/std/Build/Step/Compile.zig+21-4| ... | ... | @@ -189,7 +189,8 @@ dll_export_fns: ?bool = null, |
| 189 | 189 | |
| 190 | 190 | subsystem: ?std.Target.SubSystem = null, |
| 191 | 191 | |
| 192 | entry_symbol_name: ?[]const u8 = null, | |
| 192 | /// How the linker must handle the entry point of the executable. | |
| 193 | entry: Entry = .default, | |
| 193 | 194 | |
| 194 | 195 | /// List of symbols forced as undefined in the symbol table |
| 195 | 196 | /// thus forcing their resolution by the linker. |
| ... | ... | @@ -304,6 +305,18 @@ const FrameworkLinkInfo = struct { |
| 304 | 305 | weak: bool = false, |
| 305 | 306 | }; |
| 306 | 307 | |
| 308 | const Entry = union(enum) { | |
| 309 | /// Let the compiler decide whether to make an entry point and what to name | |
| 310 | /// it. | |
| 311 | default, | |
| 312 | /// The executable will have no entry point. | |
| 313 | disabled, | |
| 314 | /// The executable will have an entry point with the default symbol name. | |
| 315 | enabled, | |
| 316 | /// The executable will have an entry point with the specified symbol name. | |
| 317 | symbol_name: []const u8, | |
| 318 | }; | |
| 319 | ||
| 307 | 320 | pub const IncludeDir = union(enum) { |
| 308 | 321 | path: LazyPath, |
| 309 | 322 | path_system: LazyPath, |
| ... | ... | @@ -1418,9 +1431,13 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 1418 | 1431 | try zig_args.append(try std.fmt.allocPrint(b.allocator, "-ofmt={s}", .{@tagName(ofmt)})); |
| 1419 | 1432 | } |
| 1420 | 1433 | |
| 1421 | if (self.entry_symbol_name) |entry| { | |
| 1422 | try zig_args.append("--entry"); | |
| 1423 | try zig_args.append(entry); | |
| 1434 | switch (self.entry) { | |
| 1435 | .default => {}, | |
| 1436 | .disabled => try zig_args.append("-fno-entry"), | |
| 1437 | .enabled => try zig_args.append("-fentry"), | |
| 1438 | .symbol_name => |entry_name| { | |
| 1439 | try zig_args.append(try std.fmt.allocPrint(b.allocator, "-fentry={s}", .{entry_name})); | |
| 1440 | }, | |
| 1424 | 1441 | } |
| 1425 | 1442 | |
| 1426 | 1443 | { |
lib/std/start.zig+6-2| ... | ... | @@ -82,11 +82,15 @@ comptime { |
| 82 | 82 | .reactor => "_initialize", |
| 83 | 83 | .command => "_start", |
| 84 | 84 | }; |
| 85 | if (!@hasDecl(root, wasm_start_sym)) { | |
| 85 | if (!@hasDecl(root, wasm_start_sym) and @hasDecl(root, "main")) { | |
| 86 | // Only call main when defined. For WebAssembly it's allowed to pass `-fno-entry` in which | |
| 87 | // case it's not required to provide an entrypoint such as main. | |
| 86 | 88 | @export(wasi_start, .{ .name = wasm_start_sym }); |
| 87 | 89 | } |
| 88 | 90 | } else if (native_arch.isWasm() and native_os == .freestanding) { |
| 89 | if (!@hasDecl(root, start_sym_name)) @export(wasm_freestanding_start, .{ .name = start_sym_name }); | |
| 91 | // Only call main when defined. For WebAssembly it's allowed to pass `-fno-entry` in which | |
| 92 | // case it's not required to provide an entrypoint such as main. | |
| 93 | if (!@hasDecl(root, start_sym_name) and @hasDecl(root, "main")) @export(wasm_freestanding_start, .{ .name = start_sym_name }); | |
| 90 | 94 | } else if (native_os != .other and native_os != .freestanding) { |
| 91 | 95 | if (!@hasDecl(root, start_sym_name)) @export(_start, .{ .name = start_sym_name }); |
| 92 | 96 | } |
src/link/Wasm.zig+12-21| ... | ... | @@ -2817,15 +2817,11 @@ fn setupExports(wasm: *Wasm) !void { |
| 2817 | 2817 | } |
| 2818 | 2818 | |
| 2819 | 2819 | fn setupStart(wasm: *Wasm) !void { |
| 2820 | const entry_name = wasm.base.options.entry orelse "_start"; | |
| 2820 | // do not export entry point if user set none or no default was set. | |
| 2821 | const entry_name = wasm.base.options.entry orelse return; | |
| 2821 | 2822 | |
| 2822 | 2823 | const symbol_loc = wasm.findGlobalSymbol(entry_name) orelse { |
| 2823 | if (wasm.base.options.output_mode == .Exe) { | |
| 2824 | if (wasm.base.options.wasi_exec_model == .reactor) return; // Not required for reactors | |
| 2825 | } else { | |
| 2826 | return; // No entry point needed for non-executable wasm files | |
| 2827 | } | |
| 2828 | log.err("Entry symbol '{s}' missing", .{entry_name}); | |
| 2824 | log.err("Entry symbol '{s}' missing, use '-fno-entry' to suppress", .{entry_name}); | |
| 2829 | 2825 | return error.MissingSymbol; |
| 2830 | 2826 | }; |
| 2831 | 2827 | |
| ... | ... | @@ -4535,6 +4531,8 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! |
| 4535 | 4531 | if (wasm.base.options.entry) |entry| { |
| 4536 | 4532 | try argv.append("--entry"); |
| 4537 | 4533 | try argv.append(entry); |
| 4534 | } else { | |
| 4535 | try argv.append("--no-entry"); | |
| 4538 | 4536 | } |
| 4539 | 4537 | |
| 4540 | 4538 | // 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) ! |
| 4544 | 4542 | const arg = try std.fmt.allocPrint(arena, "stack-size={d}", .{stack_size}); |
| 4545 | 4543 | try argv.append(arg); |
| 4546 | 4544 | |
| 4547 | if (wasm.base.options.output_mode == .Exe) { | |
| 4548 | if (wasm.base.options.wasi_exec_model == .reactor) { | |
| 4549 | // Reactor execution model does not have _start so lld doesn't look for it. | |
| 4550 | try argv.append("--no-entry"); | |
| 4551 | // Make sure "_initialize" and other used-defined functions are exported if this is WASI reactor. | |
| 4552 | // If rdynamic is true, it will already be appended, so only verify if the user did not specify | |
| 4553 | // the flag in which case, we ensure `--export-dynamic` is called. | |
| 4554 | if (!wasm.base.options.rdynamic) { | |
| 4555 | try argv.append("--export-dynamic"); | |
| 4556 | } | |
| 4557 | } | |
| 4558 | } else if (wasm.base.options.entry == null) { | |
| 4559 | try argv.append("--no-entry"); // So lld doesn't look for _start. | |
| 4560 | } | |
| 4561 | 4545 | if (wasm.base.options.import_symbols) { |
| 4562 | 4546 | try argv.append("--allow-undefined"); |
| 4563 | 4547 | } |
| 4564 | 4548 | |
| 4549 | if (wasm.base.options.output_mode == .Lib and wasm.base.options.link_mode == .Dynamic) { | |
| 4550 | try argv.append("--shared"); | |
| 4551 | } | |
| 4552 | if (wasm.base.options.pie) { | |
| 4553 | try argv.append("--pie"); | |
| 4554 | } | |
| 4555 | ||
| 4565 | 4556 | // XXX - TODO: add when wasm-ld supports --build-id. |
| 4566 | 4557 | // if (wasm.base.options.build_id) { |
| 4567 | 4558 | // try argv.append("--build-id=tree"); |
src/main.zig+54-3| ... | ... | @@ -519,7 +519,9 @@ const usage_build_generic = |
| 519 | 519 | \\ --dynamic-linker [path] Set the dynamic interpreter path (usually ld.so) |
| 520 | 520 | \\ --sysroot [path] Set the system root directory (usually /) |
| 521 | 521 | \\ --version [ver] Dynamic library semver |
| 522 | \\ --entry [name] Set the entrypoint symbol name | |
| 522 | \\ -fentry Enable entry point with default symbol name | |
| 523 | \\ -fentry=[name] Override the entry point symbol name | |
| 524 | \\ -fno-entry Do not output any entry point | |
| 523 | 525 | \\ --force_undefined [name] Specify the symbol must be defined for the link to succeed |
| 524 | 526 | \\ -fsoname[=name] Override the default SONAME value |
| 525 | 527 | \\ -fno-soname Disable emitting a SONAME |
| ... | ... | @@ -845,6 +847,7 @@ fn buildOutputType( |
| 845 | 847 | var linker_import_symbols: bool = false; |
| 846 | 848 | var linker_import_table: bool = false; |
| 847 | 849 | var linker_export_table: bool = false; |
| 850 | var linker_force_entry: ?bool = null; | |
| 848 | 851 | var linker_initial_memory: ?u64 = null; |
| 849 | 852 | var linker_max_memory: ?u64 = null; |
| 850 | 853 | var linker_shared_memory: bool = false; |
| ... | ... | @@ -1081,8 +1084,8 @@ fn buildOutputType( |
| 1081 | 1084 | subsystem = try parseSubSystem(args_iter.nextOrFatal()); |
| 1082 | 1085 | } else if (mem.eql(u8, arg, "-O")) { |
| 1083 | 1086 | optimize_mode_string = args_iter.nextOrFatal(); |
| 1084 | } else if (mem.eql(u8, arg, "--entry")) { | |
| 1085 | entry = args_iter.nextOrFatal(); | |
| 1087 | } else if (mem.startsWith(u8, arg, "-fentry=")) { | |
| 1088 | entry = arg["-fentry=".len..]; | |
| 1086 | 1089 | } else if (mem.eql(u8, arg, "--force_undefined")) { |
| 1087 | 1090 | try force_undefined_symbols.put(gpa, args_iter.nextOrFatal(), {}); |
| 1088 | 1091 | } else if (mem.eql(u8, arg, "--stack")) { |
| ... | ... | @@ -1513,6 +1516,10 @@ fn buildOutputType( |
| 1513 | 1516 | } |
| 1514 | 1517 | } else if (mem.eql(u8, arg, "--import-memory")) { |
| 1515 | 1518 | linker_import_memory = true; |
| 1519 | } else if (mem.eql(u8, arg, "-fentry")) { | |
| 1520 | linker_force_entry = true; | |
| 1521 | } else if (mem.eql(u8, arg, "-fno-entry")) { | |
| 1522 | linker_force_entry = false; | |
| 1516 | 1523 | } else if (mem.eql(u8, arg, "--export-memory")) { |
| 1517 | 1524 | linker_export_memory = true; |
| 1518 | 1525 | } else if (mem.eql(u8, arg, "--import-symbols")) { |
| ... | ... | @@ -2144,6 +2151,8 @@ fn buildOutputType( |
| 2144 | 2151 | linker_import_table = true; |
| 2145 | 2152 | } else if (mem.eql(u8, arg, "--export-table")) { |
| 2146 | 2153 | linker_export_table = true; |
| 2154 | } else if (mem.eql(u8, arg, "--no-entry")) { | |
| 2155 | linker_force_entry = false; | |
| 2147 | 2156 | } else if (mem.eql(u8, arg, "--initial-memory")) { |
| 2148 | 2157 | const next_arg = linker_args_it.nextOrFatal(); |
| 2149 | 2158 | linker_initial_memory = std.fmt.parseUnsigned(u32, eatIntPrefix(next_arg, 16), 16) catch |err| { |
| ... | ... | @@ -2606,6 +2615,23 @@ fn buildOutputType( |
| 2606 | 2615 | link_libcpp = true; |
| 2607 | 2616 | } |
| 2608 | 2617 | |
| 2618 | if (linker_force_entry) |force| { | |
| 2619 | if (!force) { | |
| 2620 | entry = null; | |
| 2621 | } else if (entry == null and output_mode == .Exe) { | |
| 2622 | entry = switch (target_info.target.ofmt) { | |
| 2623 | .coff => "wWinMainCRTStartup", | |
| 2624 | .macho => "_main", | |
| 2625 | .elf, .plan9 => "_start", | |
| 2626 | .wasm => defaultWasmEntryName(wasi_exec_model), | |
| 2627 | else => |tag| fatal("No default entry point available for output format {s}", .{@tagName(tag)}), | |
| 2628 | }; | |
| 2629 | } | |
| 2630 | } else if (entry == null and target_info.target.isWasm() and output_mode == .Exe) { | |
| 2631 | // For WebAssembly the compiler defaults to setting the entry name when no flags are set. | |
| 2632 | entry = defaultWasmEntryName(wasi_exec_model); | |
| 2633 | } | |
| 2634 | ||
| 2609 | 2635 | if (target_info.target.ofmt == .coff) { |
| 2610 | 2636 | // Now that we know the target supports resources, |
| 2611 | 2637 | // we can add the res files as link objects. |
| ... | ... | @@ -2628,6 +2654,23 @@ fn buildOutputType( |
| 2628 | 2654 | if (single_threaded == null) { |
| 2629 | 2655 | single_threaded = true; |
| 2630 | 2656 | } |
| 2657 | if (link_mode) |mode| { | |
| 2658 | if (mode == .Dynamic) { | |
| 2659 | if (linker_export_memory != null and linker_export_memory.?) { | |
| 2660 | fatal("flags '-dynamic' and '--export-memory' are incompatible", .{}); | |
| 2661 | } | |
| 2662 | // User did not supply `--export-memory` which is incompatible with -dynamic, therefore | |
| 2663 | // set the flag to false to ensure it does not get enabled by default. | |
| 2664 | linker_export_memory = false; | |
| 2665 | } | |
| 2666 | } | |
| 2667 | if (wasi_exec_model != null and wasi_exec_model.? == .reactor) { | |
| 2668 | if (entry) |entry_name| { | |
| 2669 | if (!mem.eql(u8, "_initialize", entry_name)) { | |
| 2670 | fatal("the entry symbol of the reactor model must be '_initialize', but found '{s}'", .{entry_name}); | |
| 2671 | } | |
| 2672 | } | |
| 2673 | } | |
| 2631 | 2674 | if (linker_shared_memory) { |
| 2632 | 2675 | if (output_mode == .Obj) { |
| 2633 | 2676 | fatal("shared memory is not allowed in object files", .{}); |
| ... | ... | @@ -7225,3 +7268,11 @@ fn createDependenciesModule( |
| 7225 | 7268 | try main_mod.deps.put(arena, "@dependencies", deps_mod); |
| 7226 | 7269 | return deps_mod; |
| 7227 | 7270 | } |
| 7271 | ||
| 7272 | fn defaultWasmEntryName(exec_model: ?std.builtin.WasiExecModel) []const u8 { | |
| 7273 | const model = exec_model orelse .command; | |
| 7274 | if (model == .reactor) { | |
| 7275 | return "_initialize"; | |
| 7276 | } | |
| 7277 | return "_start"; | |
| 7278 | } |
test/link/elf.zig+2-2| ... | ... | @@ -658,7 +658,7 @@ fn testEntryPoint(b: *Build, opts: Options) *Step { |
| 658 | 658 | const exe = addExecutable(b, "main", opts); |
| 659 | 659 | exe.addObject(a_o); |
| 660 | 660 | exe.addObject(b_o); |
| 661 | exe.entry_symbol_name = "foo"; | |
| 661 | exe.entry = .{ .symbol_name = "foo" }; | |
| 662 | 662 | |
| 663 | 663 | const check = exe.checkObject(); |
| 664 | 664 | check.checkStart(); |
| ... | ... | @@ -674,7 +674,7 @@ fn testEntryPoint(b: *Build, opts: Options) *Step { |
| 674 | 674 | const exe = addExecutable(b, "other", opts); |
| 675 | 675 | exe.addObject(a_o); |
| 676 | 676 | exe.addObject(b_o); |
| 677 | exe.entry_symbol_name = "bar"; | |
| 677 | exe.entry = .{ .symbol_name = "bar" }; | |
| 678 | 678 | |
| 679 | 679 | const check = exe.checkObject(); |
| 680 | 680 | check.checkStart(); |
test/link/macho/entry/build.zig+1-1| ... | ... | @@ -20,7 +20,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize |
| 20 | 20 | }); |
| 21 | 21 | exe.addCSourceFile(.{ .file = .{ .path = "main.c" }, .flags = &.{} }); |
| 22 | 22 | exe.linkLibC(); |
| 23 | exe.entry_symbol_name = "_non_main"; | |
| 23 | exe.entry = .{ .symbol_name = "_non_main" }; | |
| 24 | 24 | |
| 25 | 25 | const check_exe = exe.checkObject(); |
| 26 | 26 |
test/link/macho/entry_in_dylib/build.zig+1-1| ... | ... | @@ -30,7 +30,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize |
| 30 | 30 | exe.addCSourceFile(.{ .file = .{ .path = "main.c" }, .flags = &.{} }); |
| 31 | 31 | exe.linkLibrary(lib); |
| 32 | 32 | exe.linkLibC(); |
| 33 | exe.entry_symbol_name = "_bootstrap"; | |
| 33 | exe.entry = .{ .symbol_name = "_bootstrap" }; | |
| 34 | 34 | exe.forceUndefinedSymbol("_my_main"); |
| 35 | 35 | |
| 36 | 36 | const check_exe = exe.checkObject(); |
test/link/wasm/archive/build.zig+2-1| ... | ... | @@ -15,12 +15,13 @@ pub fn build(b: *std.Build) void { |
| 15 | 15 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { |
| 16 | 16 | // The code in question will pull-in compiler-rt, |
| 17 | 17 | // and therefore link with its archive file. |
| 18 | const lib = b.addSharedLibrary(.{ | |
| 18 | const lib = b.addExecutable(.{ | |
| 19 | 19 | .name = "main", |
| 20 | 20 | .root_source_file = .{ .path = "main.zig" }, |
| 21 | 21 | .optimize = optimize, |
| 22 | 22 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, |
| 23 | 23 | }); |
| 24 | lib.entry = .disabled; | |
| 24 | 25 | lib.use_llvm = false; |
| 25 | 26 | lib.use_lld = false; |
| 26 | 27 | lib.strip = false; |
test/link/wasm/basic-features/build.zig+2-1| ... | ... | @@ -4,7 +4,7 @@ pub const requires_stage2 = true; |
| 4 | 4 | |
| 5 | 5 | pub fn build(b: *std.Build) void { |
| 6 | 6 | // Library with explicitly set cpu features |
| 7 | const lib = b.addSharedLibrary(.{ | |
| 7 | const lib = b.addExecutable(.{ | |
| 8 | 8 | .name = "lib", |
| 9 | 9 | .root_source_file = .{ .path = "main.zig" }, |
| 10 | 10 | .optimize = .Debug, |
| ... | ... | @@ -15,6 +15,7 @@ pub fn build(b: *std.Build) void { |
| 15 | 15 | .os_tag = .freestanding, |
| 16 | 16 | }, |
| 17 | 17 | }); |
| 18 | lib.entry = .disabled; | |
| 18 | 19 | lib.use_llvm = false; |
| 19 | 20 | lib.use_lld = false; |
| 20 | 21 |
test/link/wasm/bss/build.zig+4-2| ... | ... | @@ -14,12 +14,13 @@ pub fn build(b: *std.Build) void { |
| 14 | 14 | |
| 15 | 15 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize_mode: std.builtin.OptimizeMode, is_safe: bool) void { |
| 16 | 16 | { |
| 17 | const lib = b.addSharedLibrary(.{ | |
| 17 | const lib = b.addExecutable(.{ | |
| 18 | 18 | .name = "lib", |
| 19 | 19 | .root_source_file = .{ .path = "lib.zig" }, |
| 20 | 20 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, |
| 21 | 21 | .optimize = optimize_mode, |
| 22 | 22 | }); |
| 23 | lib.entry = .disabled; | |
| 23 | 24 | lib.use_llvm = false; |
| 24 | 25 | lib.use_lld = false; |
| 25 | 26 | lib.strip = false; |
| ... | ... | @@ -60,12 +61,13 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize_mode: std.builtin.Opt |
| 60 | 61 | |
| 61 | 62 | // verify zero'd declaration is stored in bss for all optimization modes. |
| 62 | 63 | { |
| 63 | const lib = b.addSharedLibrary(.{ | |
| 64 | const lib = b.addExecutable(.{ | |
| 64 | 65 | .name = "lib", |
| 65 | 66 | .root_source_file = .{ .path = "lib2.zig" }, |
| 66 | 67 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, |
| 67 | 68 | .optimize = optimize_mode, |
| 68 | 69 | }); |
| 70 | lib.entry = .disabled; | |
| 69 | 71 | lib.use_llvm = false; |
| 70 | 72 | lib.use_lld = false; |
| 71 | 73 | lib.strip = false; |
test/link/wasm/export-data/build.zig+2-1| ... | ... | @@ -9,12 +9,13 @@ pub fn build(b: *std.Build) void { |
| 9 | 9 | return; |
| 10 | 10 | } |
| 11 | 11 | |
| 12 | const lib = b.addSharedLibrary(.{ | |
| 12 | const lib = b.addExecutable(.{ | |
| 13 | 13 | .name = "lib", |
| 14 | 14 | .root_source_file = .{ .path = "lib.zig" }, |
| 15 | 15 | .optimize = .ReleaseSafe, // to make the output deterministic in address positions |
| 16 | 16 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, |
| 17 | 17 | }); |
| 18 | lib.entry = .disabled; | |
| 18 | 19 | lib.use_lld = false; |
| 19 | 20 | lib.export_symbol_names = &.{ "foo", "bar" }; |
| 20 | 21 | lib.global_base = 0; // put data section at address 0 to make data symbols easier to parse |
test/link/wasm/export/build.zig+6-3| ... | ... | @@ -13,31 +13,34 @@ pub fn build(b: *std.Build) void { |
| 13 | 13 | } |
| 14 | 14 | |
| 15 | 15 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { |
| 16 | const no_export = b.addSharedLibrary(.{ | |
| 16 | const no_export = b.addExecutable(.{ | |
| 17 | 17 | .name = "no-export", |
| 18 | 18 | .root_source_file = .{ .path = "main.zig" }, |
| 19 | 19 | .optimize = optimize, |
| 20 | 20 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, |
| 21 | 21 | }); |
| 22 | no_export.entry = .disabled; | |
| 22 | 23 | no_export.use_llvm = false; |
| 23 | 24 | no_export.use_lld = false; |
| 24 | 25 | |
| 25 | const dynamic_export = b.addSharedLibrary(.{ | |
| 26 | const dynamic_export = b.addExecutable(.{ | |
| 26 | 27 | .name = "dynamic", |
| 27 | 28 | .root_source_file = .{ .path = "main.zig" }, |
| 28 | 29 | .optimize = optimize, |
| 29 | 30 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, |
| 30 | 31 | }); |
| 32 | dynamic_export.entry = .disabled; | |
| 31 | 33 | dynamic_export.rdynamic = true; |
| 32 | 34 | dynamic_export.use_llvm = false; |
| 33 | 35 | dynamic_export.use_lld = false; |
| 34 | 36 | |
| 35 | const force_export = b.addSharedLibrary(.{ | |
| 37 | const force_export = b.addExecutable(.{ | |
| 36 | 38 | .name = "force", |
| 37 | 39 | .root_source_file = .{ .path = "main.zig" }, |
| 38 | 40 | .optimize = optimize, |
| 39 | 41 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, |
| 40 | 42 | }); |
| 43 | force_export.entry = .disabled; | |
| 41 | 44 | force_export.export_symbol_names = &.{"foo"}; |
| 42 | 45 | force_export.use_llvm = false; |
| 43 | 46 | force_export.use_lld = false; |
test/link/wasm/extern-mangle/build.zig+2-1| ... | ... | @@ -11,12 +11,13 @@ pub fn build(b: *std.Build) void { |
| 11 | 11 | } |
| 12 | 12 | |
| 13 | 13 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { |
| 14 | const lib = b.addSharedLibrary(.{ | |
| 14 | const lib = b.addExecutable(.{ | |
| 15 | 15 | .name = "lib", |
| 16 | 16 | .root_source_file = .{ .path = "lib.zig" }, |
| 17 | 17 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, |
| 18 | 18 | .optimize = optimize, |
| 19 | 19 | }); |
| 20 | lib.entry = .disabled; | |
| 20 | 21 | lib.import_symbols = true; // import `a` and `b` |
| 21 | 22 | lib.rdynamic = true; // export `foo` |
| 22 | 23 |
test/link/wasm/function-table/build.zig+6-3| ... | ... | @@ -13,32 +13,35 @@ pub fn build(b: *std.Build) void { |
| 13 | 13 | } |
| 14 | 14 | |
| 15 | 15 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { |
| 16 | const import_table = b.addSharedLibrary(.{ | |
| 16 | const import_table = b.addExecutable(.{ | |
| 17 | 17 | .name = "import_table", |
| 18 | 18 | .root_source_file = .{ .path = "lib.zig" }, |
| 19 | 19 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, |
| 20 | 20 | .optimize = optimize, |
| 21 | 21 | }); |
| 22 | import_table.entry = .disabled; | |
| 22 | 23 | import_table.use_llvm = false; |
| 23 | 24 | import_table.use_lld = false; |
| 24 | 25 | import_table.import_table = true; |
| 25 | 26 | |
| 26 | const export_table = b.addSharedLibrary(.{ | |
| 27 | const export_table = b.addExecutable(.{ | |
| 27 | 28 | .name = "export_table", |
| 28 | 29 | .root_source_file = .{ .path = "lib.zig" }, |
| 29 | 30 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, |
| 30 | 31 | .optimize = optimize, |
| 31 | 32 | }); |
| 33 | export_table.entry = .disabled; | |
| 32 | 34 | export_table.use_llvm = false; |
| 33 | 35 | export_table.use_lld = false; |
| 34 | 36 | export_table.export_table = true; |
| 35 | 37 | |
| 36 | const regular_table = b.addSharedLibrary(.{ | |
| 38 | const regular_table = b.addExecutable(.{ | |
| 37 | 39 | .name = "regular_table", |
| 38 | 40 | .root_source_file = .{ .path = "lib.zig" }, |
| 39 | 41 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, |
| 40 | 42 | .optimize = optimize, |
| 41 | 43 | }); |
| 44 | regular_table.entry = .disabled; | |
| 42 | 45 | regular_table.use_llvm = false; |
| 43 | 46 | regular_table.use_lld = false; |
| 44 | 47 |
test/link/wasm/infer-features/build.zig+2-1| ... | ... | @@ -17,7 +17,7 @@ pub fn build(b: *std.Build) void { |
| 17 | 17 | |
| 18 | 18 | // Wasm library that doesn't have any features specified. This will |
| 19 | 19 | // infer its featureset from other linked object files. |
| 20 | const lib = b.addSharedLibrary(.{ | |
| 20 | const lib = b.addExecutable(.{ | |
| 21 | 21 | .name = "lib", |
| 22 | 22 | .root_source_file = .{ .path = "main.zig" }, |
| 23 | 23 | .optimize = .Debug, |
| ... | ... | @@ -27,6 +27,7 @@ pub fn build(b: *std.Build) void { |
| 27 | 27 | .os_tag = .freestanding, |
| 28 | 28 | }, |
| 29 | 29 | }); |
| 30 | lib.entry = .disabled; | |
| 30 | 31 | lib.use_llvm = false; |
| 31 | 32 | lib.use_lld = false; |
| 32 | 33 | lib.addObject(c_obj); |
test/link/wasm/producers/build.zig+2-1| ... | ... | @@ -14,12 +14,13 @@ pub fn build(b: *std.Build) void { |
| 14 | 14 | } |
| 15 | 15 | |
| 16 | 16 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { |
| 17 | const lib = b.addSharedLibrary(.{ | |
| 17 | const lib = b.addExecutable(.{ | |
| 18 | 18 | .name = "lib", |
| 19 | 19 | .root_source_file = .{ .path = "lib.zig" }, |
| 20 | 20 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, |
| 21 | 21 | .optimize = optimize, |
| 22 | 22 | }); |
| 23 | lib.entry = .disabled; | |
| 23 | 24 | lib.use_llvm = false; |
| 24 | 25 | lib.use_lld = false; |
| 25 | 26 | lib.strip = false; |
test/link/wasm/segments/build.zig+2-1| ... | ... | @@ -13,12 +13,13 @@ pub fn build(b: *std.Build) void { |
| 13 | 13 | } |
| 14 | 14 | |
| 15 | 15 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { |
| 16 | const lib = b.addSharedLibrary(.{ | |
| 16 | const lib = b.addExecutable(.{ | |
| 17 | 17 | .name = "lib", |
| 18 | 18 | .root_source_file = .{ .path = "lib.zig" }, |
| 19 | 19 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, |
| 20 | 20 | .optimize = optimize, |
| 21 | 21 | }); |
| 22 | lib.entry = .disabled; | |
| 22 | 23 | lib.use_llvm = false; |
| 23 | 24 | lib.use_lld = false; |
| 24 | 25 | lib.strip = false; |
test/link/wasm/shared-memory/build.zig+74-75| ... | ... | @@ -11,88 +11,87 @@ pub fn build(b: *std.Build) void { |
| 11 | 11 | } |
| 12 | 12 | |
| 13 | 13 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize_mode: std.builtin.OptimizeMode) void { |
| 14 | { | |
| 15 | const lib = b.addSharedLibrary(.{ | |
| 16 | .name = "lib", | |
| 17 | .root_source_file = .{ .path = "lib.zig" }, | |
| 18 | .target = .{ | |
| 19 | .cpu_arch = .wasm32, | |
| 20 | .cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp }, | |
| 21 | .cpu_features_add = std.Target.wasm.featureSet(&.{ .atomics, .bulk_memory }), | |
| 22 | .os_tag = .freestanding, | |
| 23 | }, | |
| 24 | .optimize = optimize_mode, | |
| 25 | }); | |
| 26 | lib.use_lld = false; | |
| 27 | lib.strip = false; | |
| 28 | lib.import_memory = true; | |
| 29 | lib.export_memory = true; | |
| 30 | lib.shared_memory = true; | |
| 31 | lib.max_memory = 67108864; | |
| 32 | lib.single_threaded = false; | |
| 33 | lib.export_symbol_names = &.{"foo"}; | |
| 14 | const lib = b.addExecutable(.{ | |
| 15 | .name = "lib", | |
| 16 | .root_source_file = .{ .path = "lib.zig" }, | |
| 17 | .target = .{ | |
| 18 | .cpu_arch = .wasm32, | |
| 19 | .cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp }, | |
| 20 | .cpu_features_add = std.Target.wasm.featureSet(&.{ .atomics, .bulk_memory }), | |
| 21 | .os_tag = .freestanding, | |
| 22 | }, | |
| 23 | .optimize = optimize_mode, | |
| 24 | }); | |
| 25 | lib.entry = .disabled; | |
| 26 | lib.use_lld = false; | |
| 27 | lib.strip = false; | |
| 28 | lib.import_memory = true; | |
| 29 | lib.export_memory = true; | |
| 30 | lib.shared_memory = true; | |
| 31 | lib.max_memory = 67108864; | |
| 32 | lib.single_threaded = false; | |
| 33 | lib.export_symbol_names = &.{"foo"}; | |
| 34 | 34 | |
| 35 | const check_lib = lib.checkObject(); | |
| 35 | const check_lib = lib.checkObject(); | |
| 36 | 36 | |
| 37 | check_lib.checkStart("Section import"); | |
| 38 | check_lib.checkNext("entries 1"); | |
| 39 | check_lib.checkNext("module env"); | |
| 40 | check_lib.checkNext("name memory"); // ensure we are importing memory | |
| 37 | check_lib.checkStart("Section import"); | |
| 38 | check_lib.checkNext("entries 1"); | |
| 39 | check_lib.checkNext("module env"); | |
| 40 | check_lib.checkNext("name memory"); // ensure we are importing memory | |
| 41 | 41 | |
| 42 | check_lib.checkStart("Section export"); | |
| 43 | check_lib.checkNext("entries 2"); | |
| 44 | check_lib.checkNext("name memory"); // ensure we also export memory again | |
| 42 | check_lib.checkStart("Section export"); | |
| 43 | check_lib.checkNext("entries 2"); | |
| 44 | check_lib.checkNext("name memory"); // ensure we also export memory again | |
| 45 | 45 | |
| 46 | // This section *must* be emit as the start function is set to the index | |
| 47 | // of __wasm_init_memory | |
| 48 | // release modes will have the TLS segment optimized out in our test-case. | |
| 49 | // This means we won't have __wasm_init_memory in such case, and therefore | |
| 50 | // should also not have a section "start" | |
| 51 | if (optimize_mode == .Debug) { | |
| 52 | check_lib.checkStart("Section start"); | |
| 53 | } | |
| 54 | ||
| 55 | // This section is only and *must* be emit when shared-memory is enabled | |
| 56 | // release modes will have the TLS segment optimized out in our test-case. | |
| 57 | if (optimize_mode == .Debug) { | |
| 58 | check_lib.checkStart("Section data_count"); | |
| 59 | check_lib.checkNext("count 3"); | |
| 60 | } | |
| 46 | // This section *must* be emit as the start function is set to the index | |
| 47 | // of __wasm_init_memory | |
| 48 | // release modes will have the TLS segment optimized out in our test-case. | |
| 49 | // This means we won't have __wasm_init_memory in such case, and therefore | |
| 50 | // should also not have a section "start" | |
| 51 | if (optimize_mode == .Debug) { | |
| 52 | check_lib.checkStart("Section start"); | |
| 53 | } | |
| 61 | 54 | |
| 62 | check_lib.checkStart("Section custom"); | |
| 63 | check_lib.checkNext("name name"); | |
| 64 | check_lib.checkNext("type function"); | |
| 65 | if (optimize_mode == .Debug) { | |
| 66 | check_lib.checkNext("name __wasm_init_memory"); | |
| 67 | } | |
| 68 | check_lib.checkNext("name __wasm_init_tls"); | |
| 69 | check_lib.checkNext("type global"); | |
| 55 | // This section is only and *must* be emit when shared-memory is enabled | |
| 56 | // release modes will have the TLS segment optimized out in our test-case. | |
| 57 | if (optimize_mode == .Debug) { | |
| 58 | check_lib.checkStart("Section data_count"); | |
| 59 | check_lib.checkNext("count 3"); | |
| 60 | } | |
| 70 | 61 | |
| 71 | // In debug mode the symbol __tls_base is resolved to an undefined symbol | |
| 72 | // from the object file, hence its placement differs than in release modes | |
| 73 | // where the entire tls segment is optimized away, and tls_base will have | |
| 74 | // its original position. | |
| 75 | if (optimize_mode == .Debug) { | |
| 76 | check_lib.checkNext("name __tls_size"); | |
| 77 | check_lib.checkNext("name __tls_align"); | |
| 78 | check_lib.checkNext("name __tls_base"); | |
| 79 | } else { | |
| 80 | check_lib.checkNext("name __tls_base"); | |
| 81 | check_lib.checkNext("name __tls_size"); | |
| 82 | check_lib.checkNext("name __tls_align"); | |
| 83 | } | |
| 62 | check_lib.checkStart("Section custom"); | |
| 63 | check_lib.checkNext("name name"); | |
| 64 | check_lib.checkNext("type function"); | |
| 65 | if (optimize_mode == .Debug) { | |
| 66 | check_lib.checkNext("name __wasm_init_memory"); | |
| 67 | } | |
| 68 | check_lib.checkNext("name __wasm_init_tls"); | |
| 69 | check_lib.checkNext("type global"); | |
| 84 | 70 | |
| 85 | check_lib.checkNext("type data_segment"); | |
| 86 | if (optimize_mode == .Debug) { | |
| 87 | check_lib.checkNext("names 3"); | |
| 88 | check_lib.checkNext("index 0"); | |
| 89 | check_lib.checkNext("name .rodata"); | |
| 90 | check_lib.checkNext("index 1"); | |
| 91 | check_lib.checkNext("name .bss"); | |
| 92 | check_lib.checkNext("index 2"); | |
| 93 | check_lib.checkNext("name .tdata"); | |
| 94 | } | |
| 71 | // In debug mode the symbol __tls_base is resolved to an undefined symbol | |
| 72 | // from the object file, hence its placement differs than in release modes | |
| 73 | // where the entire tls segment is optimized away, and tls_base will have | |
| 74 | // its original position. | |
| 75 | if (optimize_mode == .Debug) { | |
| 76 | check_lib.checkNext("name __tls_size"); | |
| 77 | check_lib.checkNext("name __tls_align"); | |
| 78 | check_lib.checkNext("name __tls_base"); | |
| 79 | } else { | |
| 80 | check_lib.checkNext("name __tls_base"); | |
| 81 | check_lib.checkNext("name __tls_size"); | |
| 82 | check_lib.checkNext("name __tls_align"); | |
| 83 | } | |
| 95 | 84 | |
| 96 | test_step.dependOn(&check_lib.step); | |
| 85 | check_lib.checkNext("type data_segment"); | |
| 86 | if (optimize_mode == .Debug) { | |
| 87 | check_lib.checkNext("names 3"); | |
| 88 | check_lib.checkNext("index 0"); | |
| 89 | check_lib.checkNext("name .rodata"); | |
| 90 | check_lib.checkNext("index 1"); | |
| 91 | check_lib.checkNext("name .bss"); | |
| 92 | check_lib.checkNext("index 2"); | |
| 93 | check_lib.checkNext("name .tdata"); | |
| 97 | 94 | } |
| 95 | ||
| 96 | test_step.dependOn(&check_lib.step); | |
| 98 | 97 | } |
test/link/wasm/stack_pointer/build.zig+2-1| ... | ... | @@ -13,12 +13,13 @@ pub fn build(b: *std.Build) void { |
| 13 | 13 | } |
| 14 | 14 | |
| 15 | 15 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { |
| 16 | const lib = b.addSharedLibrary(.{ | |
| 16 | const lib = b.addExecutable(.{ | |
| 17 | 17 | .name = "lib", |
| 18 | 18 | .root_source_file = .{ .path = "lib.zig" }, |
| 19 | 19 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, |
| 20 | 20 | .optimize = optimize, |
| 21 | 21 | }); |
| 22 | lib.entry = .disabled; | |
| 22 | 23 | lib.use_llvm = false; |
| 23 | 24 | lib.use_lld = false; |
| 24 | 25 | lib.strip = false; |
test/link/wasm/type/build.zig+2-1| ... | ... | @@ -13,12 +13,13 @@ pub fn build(b: *std.Build) void { |
| 13 | 13 | } |
| 14 | 14 | |
| 15 | 15 | fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { |
| 16 | const lib = b.addSharedLibrary(.{ | |
| 16 | const lib = b.addExecutable(.{ | |
| 17 | 17 | .name = "lib", |
| 18 | 18 | .root_source_file = .{ .path = "lib.zig" }, |
| 19 | 19 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, |
| 20 | 20 | .optimize = optimize, |
| 21 | 21 | }); |
| 22 | lib.entry = .disabled; | |
| 22 | 23 | lib.use_llvm = false; |
| 23 | 24 | lib.use_lld = false; |
| 24 | 25 | lib.strip = false; |