authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-01-05 20:47:04+01:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-01-06 20:19:47+01:00
log6d951aff7e32b1b0252d341e66517a9a9ee98a2d
treed1e775e65de8a2fa91419c8c177a33db181861b2
parent0772fb0518902e06d1217b24fd11fe9ce40d295d
signaturelock-open Commit is signed but in an unrecognized format.

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.

6 files changed, 65 insertions(+), 13 deletions(-)

src/Compilation.zig+9
...@@ -165,6 +165,10 @@ emit_docs: ?EmitLoc,...@@ -165,6 +165,10 @@ emit_docs: ?EmitLoc,
165work_queue_wait_group: WaitGroup,165work_queue_wait_group: WaitGroup,
166astgen_wait_group: WaitGroup,166astgen_wait_group: WaitGroup,
167167
168/// Exported symbol names. This is only for when the target is wasm.
169/// TODO: Remove this when Stage2 becomes the default compiler as it will already have this information.
170export_symbol_names: std.ArrayListUnmanaged([]const u8) = .{},
171
168pub const SemaError = Module.SemaError;172pub const SemaError = Module.SemaError;
169173
170pub const CRTFile = struct {174pub const CRTFile = struct {
...@@ -1877,6 +1881,11 @@ pub fn destroy(self: *Compilation) void {...@@ -1877,6 +1881,11 @@ pub fn destroy(self: *Compilation) void {
1877 self.work_queue_wait_group.deinit();1881 self.work_queue_wait_group.deinit();
1878 self.astgen_wait_group.deinit();1882 self.astgen_wait_group.deinit();
18791883
1884 for (self.export_symbol_names.items) |symbol_name| {
1885 self.gpa.free(symbol_name);
1886 }
1887 self.export_symbol_names.deinit(self.gpa);
1888
1880 // This destroys `self`.1889 // This destroys `self`.
1881 self.arena_state.promote(gpa).deinit();1890 self.arena_state.promote(gpa).deinit();
1882}1891}
src/link/Wasm.zig+25-13
...@@ -1163,7 +1163,7 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {...@@ -1163,7 +1163,7 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
1163 };1163 };
1164 }1164 }
11651165
1166 if (self.base.options.output_mode == .Obj) {1166 if (is_obj) {
1167 // LLD's WASM driver does not support the equivalent of `-r` so we do a simple file copy1167 // LLD's WASM driver does not support the equivalent of `-r` so we do a simple file copy
1168 // here. TODO: think carefully about how we can avoid this redundant operation when doing1168 // here. TODO: think carefully about how we can avoid this redundant operation when doing
1169 // build-obj. See also the corresponding TODO in linkAsArchive.1169 // build-obj. See also the corresponding TODO in linkAsArchive.
...@@ -1233,14 +1233,38 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {...@@ -1233,14 +1233,38 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
1233 try argv.append(arg);1233 try argv.append(arg);
1234 }1234 }
12351235
1236 var auto_export_symbols = true;
1236 // Users are allowed to specify which symbols they want to export to the wasm host.1237 // Users are allowed to specify which symbols they want to export to the wasm host.
1237 for (self.base.options.export_symbol_names) |symbol_name| {1238 for (self.base.options.export_symbol_names) |symbol_name| {
1238 const arg = try std.fmt.allocPrint(arena, "--export={s}", .{symbol_name});1239 const arg = try std.fmt.allocPrint(arena, "--export={s}", .{symbol_name});
1239 try argv.append(arg);1240 try argv.append(arg);
1241 auto_export_symbols = false;
1240 }1242 }
12411243
1242 if (self.base.options.rdynamic) {1244 if (self.base.options.rdynamic) {
1243 try argv.append("--export-dynamic");1245 try argv.append("--export-dynamic");
1246 auto_export_symbols = false;
1247 }
1248
1249 if (auto_export_symbols) {
1250 if (self.base.options.module) |module| {
1251 // when we use stage1, we use the exports that stage1 provided us.
1252 // For stage2, we can directly retrieve them from the module.
1253 const use_stage1 = build_options.is_stage1 and self.base.options.use_stage1;
1254 if (use_stage1) {
1255 for (comp.export_symbol_names.items) |symbol_name| {
1256 try argv.append(try std.fmt.allocPrint(arena, "--export={s}", .{symbol_name}));
1257 }
1258 } else {
1259 for (module.decl_exports.values()) |exports| {
1260 for (exports) |exprt| {
1261 const symbol_name = exprt.exported_decl.name;
1262 const arg = try std.fmt.allocPrint(arena, "--export={s}", .{symbol_name});
1263 try argv.append(arg);
1264 }
1265 }
1266 }
1267 }
1244 }1268 }
12451269
1246 if (self.base.options.output_mode == .Exe) {1270 if (self.base.options.output_mode == .Exe) {
...@@ -1258,12 +1282,6 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {...@@ -1258,12 +1282,6 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
1258 if (self.base.options.wasi_exec_model == .reactor) {1282 if (self.base.options.wasi_exec_model == .reactor) {
1259 // Reactor execution model does not have _start so lld doesn't look for it.1283 // Reactor execution model does not have _start so lld doesn't look for it.
1260 try argv.append("--no-entry");1284 try argv.append("--no-entry");
1261 // Make sure "_initialize" and other used-defined functions are exported if this is WASI reactor.
1262 // If rdynamic is true, it will already be appended, so only verify if the user did not specify
1263 // the flag in which case, we ensure `--export-dynamic` is called.
1264 if (!self.base.options.rdynamic) {
1265 try argv.append("--export-dynamic");
1266 }
1267 }1285 }
1268 } else {1286 } else {
1269 if (self.base.options.stack_size_override) |stack_size| {1287 if (self.base.options.stack_size_override) |stack_size| {
...@@ -1271,12 +1289,6 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {...@@ -1271,12 +1289,6 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
1271 const arg = try std.fmt.allocPrint(arena, "stack-size={d}", .{stack_size});1289 const arg = try std.fmt.allocPrint(arena, "stack-size={d}", .{stack_size});
1272 try argv.append(arg);1290 try argv.append(arg);
1273 }1291 }
1274
1275 // Only when the user has not specified how they want to export the symbols, do we want
1276 // to export all symbols.
1277 if (self.base.options.export_symbol_names.len == 0 and !self.base.options.rdynamic) {
1278 try argv.append("--export-all");
1279 }
1280 try argv.append("--no-entry"); // So lld doesn't look for _start.1292 try argv.append("--no-entry"); // So lld doesn't look for _start.
1281 }1293 }
1282 try argv.appendSlice(&[_][]const u8{1294 try argv.appendSlice(&[_][]const u8{
src/stage1.zig+11
...@@ -467,3 +467,14 @@ export fn stage2_fetch_file(...@@ -467,3 +467,14 @@ export fn stage2_fetch_file(
467 if (contents.len == 0) return @intToPtr(?[*]const u8, 0x1);467 if (contents.len == 0) return @intToPtr(?[*]const u8, 0x1);
468 return contents.ptr;468 return contents.ptr;
469}469}
470
471export fn stage2_append_symbol(stage1: *Module, name_ptr: ?[*:0]const u8) Error {
472 const comp = @intToPtr(*Compilation, stage1.userdata);
473
474 if (name_ptr) |unwrapped_name| {
475 const symbol_name = std.mem.sliceTo(unwrapped_name, 0);
476 if (symbol_name.len == 0) return Error.None;
477 comp.export_symbol_names.append(comp.gpa, symbol_name) catch return Error.OutOfMemory;
478 }
479 return Error.None;
480}
src/stage1/codegen.cpp+12
...@@ -9905,6 +9905,18 @@ void codegen_build_object(CodeGen *g) {...@@ -9905,6 +9905,18 @@ void codegen_build_object(CodeGen *g) {
99059905
9906 codegen_add_time_event(g, "Done");9906 codegen_add_time_event(g, "Done");
9907 codegen_switch_sub_prog_node(g, nullptr);9907 codegen_switch_sub_prog_node(g, nullptr);
9908
9909 // append all export symbols to stage2 so we can provide them to the linker
9910 if (target_is_wasm(g->zig_target)){
9911 Error err;
9912 auto export_it = g->exported_symbol_names.entry_iterator();
9913 decltype(g->exported_symbol_names)::Entry *curr_entry = nullptr;
9914 while ((curr_entry = export_it.next()) != nullptr) {
9915 if ((err = stage2_append_symbol(&g->stage1, buf_ptr(curr_entry->key)))) {
9916 fprintf(stderr, "Unable to export symbol '%s': %s\n", buf_ptr(curr_entry->key), err_str(err));
9917 }
9918 }
9919 }
9908}9920}
99099921
9910ZigPackage *codegen_create_package(CodeGen *g, const char *root_src_dir, const char *root_src_path,9922ZigPackage *codegen_create_package(CodeGen *g, const char *root_src_dir, const char *root_src_path,
src/stage1/stage2.h+3
...@@ -182,4 +182,7 @@ ZIG_EXTERN_C const char *stage2_add_link_lib(struct ZigStage1 *stage1,...@@ -182,4 +182,7 @@ ZIG_EXTERN_C const char *stage2_add_link_lib(struct ZigStage1 *stage1,
182 const char *lib_name_ptr, size_t lib_name_len,182 const char *lib_name_ptr, size_t lib_name_len,
183 const char *symbol_name_ptr, size_t symbol_name_len);183 const char *symbol_name_ptr, size_t symbol_name_len);
184184
185// ABI warning
186ZIG_EXTERN_C enum Error stage2_append_symbol(struct ZigStage1 *stage1, const char *name_ptr);
187
185#endif188#endif
src/stage1/zig0.cpp+5
...@@ -554,3 +554,8 @@ const char *stage2_version_string(void) {...@@ -554,3 +554,8 @@ const char *stage2_version_string(void) {
554struct Stage2SemVer stage2_version(void) {554struct Stage2SemVer stage2_version(void) {
555 return {ZIG_VERSION_MAJOR, ZIG_VERSION_MINOR, ZIG_VERSION_PATCH};555 return {ZIG_VERSION_MAJOR, ZIG_VERSION_MINOR, ZIG_VERSION_PATCH};
556}556}
557
558Error stage2_append_symbol(struct ZigStage1 *stage1, const char *name_ptr)
559{
560 return ErrorNone;
561}