authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2021-12-22 17:39:44+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-12-23 01:47:45+01:00
loge061d75cdf6a7994dd50f2d28c9f1ed3ed5ec205
tree42c63508ee7ddd3969f343fac3bcc74789ca4960
parent3bd0575cfe685d52be79e1757c883c5f7e26ac0d

wasm-linker: Implement symbol names emitting

The linker will now emit names for all function, global and data segment symbols. This increases the ability to debug wasm modules tremendously as tools like wasm2wat can use this information to generate named functions, globals etc, rather than placeholders such as $f1.

3 files changed, 105 insertions(+), 5 deletions(-)

lib/std/wasm.zig+16
......@@ -406,6 +406,22 @@ pub fn externalKind(val: ExternalKind) u8 {
406406 return @enumToInt(val);
407407}
408408
409/// Defines the enum values for each subsection id for the "Names" custom section
410/// as described by:
411/// https://webassembly.github.io/spec/core/appendix/custom.html?highlight=name#name-section
412pub const NameSubsection = enum(u8) {
413 module,
414 function,
415 local,
416 label,
417 type,
418 table,
419 memory,
420 global,
421 elem_segment,
422 data_segment,
423};
424
409425// type constants
410426pub const element_type: u8 = 0x70;
411427pub const function_type: u8 = 0x60;
src/link/Wasm.zig+86-5
......@@ -328,6 +328,7 @@ pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void {
328328 self.symbols_free_list.append(self.base.allocator, atom.sym_index) catch {};
329329 atom.deinit(self.base.allocator);
330330 _ = self.decls.remove(decl);
331 self.symbols.items[atom.sym_index].tag = .dead; // to ensure it does not end in the names section
331332
332333 if (decl.isExtern()) {
333334 const import = self.imports.fetchRemove(decl.link.wasm.sym_index).?.value;
......@@ -336,11 +337,6 @@ pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void {
336337 else => unreachable,
337338 }
338339 }
339
340 // maybe remove from function table if needed
341 if (decl.ty.zigTypeTag() == .Fn) {
342 _ = self.function_table.remove(atom.sym_index);
343 }
344340}
345341
346342/// Appends a new entry to the indirect function table
......@@ -915,6 +911,75 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
915911 @intCast(u32, segment_count),
916912 );
917913 }
914
915 // Custom section "name" which contains symbol names
916 {
917 const Name = struct {
918 index: u32,
919 name: []const u8,
920
921 fn lessThan(context: void, lhs: @This(), rhs: @This()) bool {
922 _ = context;
923 return lhs.index < rhs.index;
924 }
925 };
926
927 var funcs = try std.ArrayList(Name).initCapacity(self.base.allocator, self.functions.items.len + self.imported_functions_count);
928 defer funcs.deinit();
929 var globals = try std.ArrayList(Name).initCapacity(self.base.allocator, self.globals.items.len);
930 defer globals.deinit();
931 var segments = try std.ArrayList(Name).initCapacity(self.base.allocator, self.data_segments.count());
932 defer segments.deinit();
933
934 for (self.symbols.items) |symbol| {
935 switch (symbol.tag) {
936 .function => funcs.appendAssumeCapacity(.{ .index = symbol.index, .name = std.mem.sliceTo(symbol.name, 0) }),
937 .global => globals.appendAssumeCapacity(.{ .index = symbol.index, .name = std.mem.sliceTo(symbol.name, 0) }),
938 else => {},
939 }
940 }
941 // data segments are already 'ordered'
942 for (self.data_segments.keys()) |key, index| {
943 segments.appendAssumeCapacity(.{ .index = @intCast(u32, index), .name = key });
944 }
945
946 std.sort.sort(Name, funcs.items, {}, Name.lessThan);
947 std.sort.sort(Name, globals.items, {}, Name.lessThan);
948
949 const header_offset = try reserveCustomSectionHeader(file);
950 const writer = file.writer();
951 try leb.writeULEB128(writer, @intCast(u32, "name".len));
952 try writer.writeAll("name");
953
954 try self.emitNameSubsection(.function, funcs.items, writer);
955 try self.emitNameSubsection(.global, globals.items, writer);
956 try self.emitNameSubsection(.data_segment, segments.items, writer);
957
958 try writeCustomSectionHeader(
959 file,
960 header_offset,
961 @intCast(u32, (try file.getPos()) - header_offset - header_size),
962 );
963 }
964}
965
966fn emitNameSubsection(self: *Wasm, section_id: std.wasm.NameSubsection, names: anytype, writer: anytype) !void {
967 // We must emit subsection size, so first write to a temporary list
968 var section_list = std.ArrayList(u8).init(self.base.allocator);
969 defer section_list.deinit();
970 const sub_writer = section_list.writer();
971
972 try leb.writeULEB128(sub_writer, @intCast(u32, names.len));
973 for (names) |name| {
974 try leb.writeULEB128(sub_writer, name.index);
975 try leb.writeULEB128(sub_writer, @intCast(u32, name.name.len));
976 try sub_writer.writeAll(name.name);
977 }
978
979 // From now, write to the actual writer
980 try leb.writeULEB128(writer, @enumToInt(section_id));
981 try leb.writeULEB128(writer, @intCast(u32, section_list.items.len));
982 try writer.writeAll(section_list.items);
918983}
919984
920985fn emitLimits(writer: anytype, limits: wasm.Limits) !void {
......@@ -1335,6 +1400,15 @@ fn reserveVecSectionHeader(file: fs.File) !u64 {
13351400 return (try file.getPos()) - header_size;
13361401}
13371402
1403fn reserveCustomSectionHeader(file: fs.File) !u64 {
1404 // unlike regular section, we don't emit the count
1405 const header_size = 1 + 5;
1406 // TODO: this should be a single lseek(2) call, but fs.File does not
1407 // currently provide a way to do this.
1408 try file.seekBy(header_size);
1409 return (try file.getPos()) - header_size;
1410}
1411
13381412fn writeVecSectionHeader(file: fs.File, offset: u64, section: wasm.Section, size: u32, items: u32) !void {
13391413 var buf: [1 + 5 + 5]u8 = undefined;
13401414 buf[0] = @enumToInt(section);
......@@ -1343,6 +1417,13 @@ fn writeVecSectionHeader(file: fs.File, offset: u64, section: wasm.Section, size
13431417 try file.pwriteAll(&buf, offset);
13441418}
13451419
1420fn writeCustomSectionHeader(file: fs.File, offset: u64, size: u32) !void {
1421 var buf: [1 + 5]u8 = undefined;
1422 buf[0] = 0; // 0 = 'custom' section
1423 leb.writeUnsignedFixed(5, buf[1..6], size);
1424 try file.pwriteAll(&buf, offset);
1425}
1426
13461427/// Searches for an a matching function signature, when not found
13471428/// a new entry will be made. The index of the existing/new signature will be returned.
13481429pub fn putOrGetFuncType(self: *Wasm, func_type: wasm.Type) !u32 {
src/link/Wasm/Symbol.zig+3
......@@ -25,6 +25,9 @@ pub const Tag = enum {
2525 section,
2626 event,
2727 table,
28 /// synthetic kind used by the wasm linker during incremental compilation
29 /// to notate a symbol has been freed, but still lives in the symbol list.
30 dead,
2831
2932 /// From a given symbol tag, returns the `ExternalType`
3033 /// Asserts the given tag can be represented as an external type.