From 6d951aff7e32b1b0252d341e66517a9a9ee98a2d Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Wed, 5 Jan 2022 20:47:04 +0100 Subject: wasm-linker: Only export symbols notated as such This exposes a function from stage2 to stage1 to append symbols to automatically export them. This happends under the following conditions: - Target is wasm - User has not provided --export/--rdynamic flags themselves. --- src/Compilation.zig | 9 +++++++++ src/link/Wasm.zig | 38 +++++++++++++++++++++++++------------- src/stage1.zig | 11 +++++++++++ src/stage1/codegen.cpp | 12 ++++++++++++ src/stage1/stage2.h | 3 +++ src/stage1/zig0.cpp | 5 +++++ 6 files changed, 65 insertions(+), 13 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index 40aabcae36..cca07c2a34 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -165,6 +165,10 @@ emit_docs: ?EmitLoc, work_queue_wait_group: WaitGroup, astgen_wait_group: WaitGroup, +/// Exported symbol names. This is only for when the target is wasm. +/// TODO: Remove this when Stage2 becomes the default compiler as it will already have this information. +export_symbol_names: std.ArrayListUnmanaged([]const u8) = .{}, + pub const SemaError = Module.SemaError; pub const CRTFile = struct { @@ -1877,6 +1881,11 @@ pub fn destroy(self: *Compilation) void { self.work_queue_wait_group.deinit(); self.astgen_wait_group.deinit(); + for (self.export_symbol_names.items) |symbol_name| { + self.gpa.free(symbol_name); + } + self.export_symbol_names.deinit(self.gpa); + // This destroys `self`. self.arena_state.promote(gpa).deinit(); } diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 401bf0b410..2a00b35bd9 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -1163,7 +1163,7 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void { }; } - if (self.base.options.output_mode == .Obj) { + if (is_obj) { // LLD's WASM driver does not support the equivalent of `-r` so we do a simple file copy // here. TODO: think carefully about how we can avoid this redundant operation when doing // build-obj. See also the corresponding TODO in linkAsArchive. @@ -1233,14 +1233,38 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void { try argv.append(arg); } + var auto_export_symbols = true; // Users are allowed to specify which symbols they want to export to the wasm host. for (self.base.options.export_symbol_names) |symbol_name| { const arg = try std.fmt.allocPrint(arena, "--export={s}", .{symbol_name}); try argv.append(arg); + auto_export_symbols = false; } if (self.base.options.rdynamic) { try argv.append("--export-dynamic"); + auto_export_symbols = false; + } + + if (auto_export_symbols) { + if (self.base.options.module) |module| { + // when we use stage1, we use the exports that stage1 provided us. + // For stage2, we can directly retrieve them from the module. + const use_stage1 = build_options.is_stage1 and self.base.options.use_stage1; + if (use_stage1) { + for (comp.export_symbol_names.items) |symbol_name| { + try argv.append(try std.fmt.allocPrint(arena, "--export={s}", .{symbol_name})); + } + } else { + for (module.decl_exports.values()) |exports| { + for (exports) |exprt| { + const symbol_name = exprt.exported_decl.name; + const arg = try std.fmt.allocPrint(arena, "--export={s}", .{symbol_name}); + try argv.append(arg); + } + } + } + } } if (self.base.options.output_mode == .Exe) { @@ -1258,12 +1282,6 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void { if (self.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 (!self.base.options.rdynamic) { - try argv.append("--export-dynamic"); - } } } else { if (self.base.options.stack_size_override) |stack_size| { @@ -1271,12 +1289,6 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void { const arg = try std.fmt.allocPrint(arena, "stack-size={d}", .{stack_size}); try argv.append(arg); } - - // Only when the user has not specified how they want to export the symbols, do we want - // to export all symbols. - if (self.base.options.export_symbol_names.len == 0 and !self.base.options.rdynamic) { - try argv.append("--export-all"); - } try argv.append("--no-entry"); // So lld doesn't look for _start. } try argv.appendSlice(&[_][]const u8{ diff --git a/src/stage1.zig b/src/stage1.zig index 384988dc07..c7967ae32d 100644 --- a/src/stage1.zig +++ b/src/stage1.zig @@ -467,3 +467,14 @@ export fn stage2_fetch_file( if (contents.len == 0) return @intToPtr(?[*]const u8, 0x1); return contents.ptr; } + +export fn stage2_append_symbol(stage1: *Module, name_ptr: ?[*:0]const u8) Error { + const comp = @intToPtr(*Compilation, stage1.userdata); + + if (name_ptr) |unwrapped_name| { + const symbol_name = std.mem.sliceTo(unwrapped_name, 0); + if (symbol_name.len == 0) return Error.None; + comp.export_symbol_names.append(comp.gpa, symbol_name) catch return Error.OutOfMemory; + } + return Error.None; +} diff --git a/src/stage1/codegen.cpp b/src/stage1/codegen.cpp index 0cf8024995..7b3640dae6 100644 --- a/src/stage1/codegen.cpp +++ b/src/stage1/codegen.cpp @@ -9905,6 +9905,18 @@ void codegen_build_object(CodeGen *g) { codegen_add_time_event(g, "Done"); codegen_switch_sub_prog_node(g, nullptr); + + // append all export symbols to stage2 so we can provide them to the linker + if (target_is_wasm(g->zig_target)){ + Error err; + auto export_it = g->exported_symbol_names.entry_iterator(); + decltype(g->exported_symbol_names)::Entry *curr_entry = nullptr; + while ((curr_entry = export_it.next()) != nullptr) { + if ((err = stage2_append_symbol(&g->stage1, buf_ptr(curr_entry->key)))) { + fprintf(stderr, "Unable to export symbol '%s': %s\n", buf_ptr(curr_entry->key), err_str(err)); + } + } + } } ZigPackage *codegen_create_package(CodeGen *g, const char *root_src_dir, const char *root_src_path, diff --git a/src/stage1/stage2.h b/src/stage1/stage2.h index b7c8616ec0..40f142d66e 100644 --- a/src/stage1/stage2.h +++ b/src/stage1/stage2.h @@ -182,4 +182,7 @@ ZIG_EXTERN_C const char *stage2_add_link_lib(struct ZigStage1 *stage1, const char *lib_name_ptr, size_t lib_name_len, const char *symbol_name_ptr, size_t symbol_name_len); +// ABI warning +ZIG_EXTERN_C enum Error stage2_append_symbol(struct ZigStage1 *stage1, const char *name_ptr); + #endif diff --git a/src/stage1/zig0.cpp b/src/stage1/zig0.cpp index 9e15bc6e2e..d3b7560a4f 100644 --- a/src/stage1/zig0.cpp +++ b/src/stage1/zig0.cpp @@ -554,3 +554,8 @@ const char *stage2_version_string(void) { struct Stage2SemVer stage2_version(void) { return {ZIG_VERSION_MAJOR, ZIG_VERSION_MINOR, ZIG_VERSION_PATCH}; } + +Error stage2_append_symbol(struct ZigStage1 *stage1, const char *name_ptr) +{ + return ErrorNone; +} -- cgit v1.2.3 From 247b638ccf8bfd5e0c4729935d230022726f97aa Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Thu, 6 Jan 2022 20:18:24 +0100 Subject: Pass `--allow-unknown-exports` to wasmtime Also skip exporting non-function symbols when we're building a WASI command using the stage2 llvm backend. --- lib/std/build.zig | 2 ++ src/Compilation.zig | 4 ++-- src/link/Wasm.zig | 7 +++++++ src/stage1.zig | 10 +++------- src/stage1/codegen.cpp | 2 +- src/stage1/stage2.h | 2 +- src/stage1/zig0.cpp | 2 +- 7 files changed, 17 insertions(+), 12 deletions(-) diff --git a/lib/std/build.zig b/lib/std/build.zig index 631ca43f38..ecc09bfc13 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -2659,6 +2659,8 @@ pub const LibExeObjStep = struct { try zig_args.append(bin_name); try zig_args.append("--test-cmd"); try zig_args.append("--dir=."); + try zig_args.append("--test-cmd"); + try zig_args.append("--allow-unknown-exports"); // TODO: Remove when stage2 is default compiler try zig_args.append("--test-cmd-bin"); } else { try zig_args.append("--test-no-exec"); diff --git a/src/Compilation.zig b/src/Compilation.zig index cca07c2a34..7d648d4649 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -1882,9 +1882,9 @@ pub fn destroy(self: *Compilation) void { self.astgen_wait_group.deinit(); for (self.export_symbol_names.items) |symbol_name| { - self.gpa.free(symbol_name); + gpa.free(symbol_name); } - self.export_symbol_names.deinit(self.gpa); + self.export_symbol_names.deinit(gpa); // This destroys `self`. self.arena_state.promote(gpa).deinit(); diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 2a00b35bd9..f1dca0afe0 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -1256,8 +1256,15 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void { try argv.append(try std.fmt.allocPrint(arena, "--export={s}", .{symbol_name})); } } else { + const skip_export_non_fn = target.os.tag == .wasi and + self.base.options.wasi_exec_model == .command; for (module.decl_exports.values()) |exports| { for (exports) |exprt| { + if (skip_export_non_fn and exprt.exported_decl.ty.zigTypeTag() != .Fn) { + // skip exporting symbols when we're building a WASI command + // and the symbol is not a function + continue; + } const symbol_name = exprt.exported_decl.name; const arg = try std.fmt.allocPrint(arena, "--export={s}", .{symbol_name}); try argv.append(arg); diff --git a/src/stage1.zig b/src/stage1.zig index c7967ae32d..5447c7c4b2 100644 --- a/src/stage1.zig +++ b/src/stage1.zig @@ -468,13 +468,9 @@ export fn stage2_fetch_file( return contents.ptr; } -export fn stage2_append_symbol(stage1: *Module, name_ptr: ?[*:0]const u8) Error { +export fn stage2_append_symbol(stage1: *Module, name_ptr: [*c]const u8, name_len: usize) Error { + if (name_len == 0) return Error.None; const comp = @intToPtr(*Compilation, stage1.userdata); - - if (name_ptr) |unwrapped_name| { - const symbol_name = std.mem.sliceTo(unwrapped_name, 0); - if (symbol_name.len == 0) return Error.None; - comp.export_symbol_names.append(comp.gpa, symbol_name) catch return Error.OutOfMemory; - } + comp.export_symbol_names.append(comp.gpa, name_ptr[0..name_len]) catch return Error.OutOfMemory; return Error.None; } diff --git a/src/stage1/codegen.cpp b/src/stage1/codegen.cpp index 7b3640dae6..cfd455e815 100644 --- a/src/stage1/codegen.cpp +++ b/src/stage1/codegen.cpp @@ -9912,7 +9912,7 @@ void codegen_build_object(CodeGen *g) { auto export_it = g->exported_symbol_names.entry_iterator(); decltype(g->exported_symbol_names)::Entry *curr_entry = nullptr; while ((curr_entry = export_it.next()) != nullptr) { - if ((err = stage2_append_symbol(&g->stage1, buf_ptr(curr_entry->key)))) { + if ((err = stage2_append_symbol(&g->stage1, buf_ptr(curr_entry->key), buf_len(curr_entry->key)))) { fprintf(stderr, "Unable to export symbol '%s': %s\n", buf_ptr(curr_entry->key), err_str(err)); } } diff --git a/src/stage1/stage2.h b/src/stage1/stage2.h index 40f142d66e..58b3a7687f 100644 --- a/src/stage1/stage2.h +++ b/src/stage1/stage2.h @@ -183,6 +183,6 @@ ZIG_EXTERN_C const char *stage2_add_link_lib(struct ZigStage1 *stage1, const char *symbol_name_ptr, size_t symbol_name_len); // ABI warning -ZIG_EXTERN_C enum Error stage2_append_symbol(struct ZigStage1 *stage1, const char *name_ptr); +ZIG_EXTERN_C enum Error stage2_append_symbol(struct ZigStage1 *stage1, const char *name_ptr, size_t name_len); #endif diff --git a/src/stage1/zig0.cpp b/src/stage1/zig0.cpp index d3b7560a4f..439f2bd1d4 100644 --- a/src/stage1/zig0.cpp +++ b/src/stage1/zig0.cpp @@ -555,7 +555,7 @@ struct Stage2SemVer stage2_version(void) { return {ZIG_VERSION_MAJOR, ZIG_VERSION_MINOR, ZIG_VERSION_PATCH}; } -Error stage2_append_symbol(struct ZigStage1 *stage1, const char *name_ptr) +Error stage2_append_symbol(struct ZigStage1 *stage1, const char *name_ptr, size_t name_len) { return ErrorNone; } -- cgit v1.2.3