Commit e061d75cdf
Changed files (3)
lib
std
src
link
lib/std/wasm.zig
@@ -406,6 +406,22 @@ pub fn externalKind(val: ExternalKind) u8 {
return @enumToInt(val);
}
+/// Defines the enum values for each subsection id for the "Names" custom section
+/// as described by:
+/// https://webassembly.github.io/spec/core/appendix/custom.html?highlight=name#name-section
+pub const NameSubsection = enum(u8) {
+ module,
+ function,
+ local,
+ label,
+ type,
+ table,
+ memory,
+ global,
+ elem_segment,
+ data_segment,
+};
+
// type constants
pub const element_type: u8 = 0x70;
pub const function_type: u8 = 0x60;
src/link/Wasm/Symbol.zig
@@ -25,6 +25,9 @@ pub const Tag = enum {
section,
event,
table,
+ /// synthetic kind used by the wasm linker during incremental compilation
+ /// to notate a symbol has been freed, but still lives in the symbol list.
+ dead,
/// From a given symbol tag, returns the `ExternalType`
/// Asserts the given tag can be represented as an external type.
src/link/Wasm.zig
@@ -328,6 +328,7 @@ pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void {
self.symbols_free_list.append(self.base.allocator, atom.sym_index) catch {};
atom.deinit(self.base.allocator);
_ = self.decls.remove(decl);
+ self.symbols.items[atom.sym_index].tag = .dead; // to ensure it does not end in the names section
if (decl.isExtern()) {
const import = self.imports.fetchRemove(decl.link.wasm.sym_index).?.value;
@@ -336,11 +337,6 @@ pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void {
else => unreachable,
}
}
-
- // maybe remove from function table if needed
- if (decl.ty.zigTypeTag() == .Fn) {
- _ = self.function_table.remove(atom.sym_index);
- }
}
/// Appends a new entry to the indirect function table
@@ -915,6 +911,75 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
@intCast(u32, segment_count),
);
}
+
+ // Custom section "name" which contains symbol names
+ {
+ const Name = struct {
+ index: u32,
+ name: []const u8,
+
+ fn lessThan(context: void, lhs: @This(), rhs: @This()) bool {
+ _ = context;
+ return lhs.index < rhs.index;
+ }
+ };
+
+ var funcs = try std.ArrayList(Name).initCapacity(self.base.allocator, self.functions.items.len + self.imported_functions_count);
+ defer funcs.deinit();
+ var globals = try std.ArrayList(Name).initCapacity(self.base.allocator, self.globals.items.len);
+ defer globals.deinit();
+ var segments = try std.ArrayList(Name).initCapacity(self.base.allocator, self.data_segments.count());
+ defer segments.deinit();
+
+ for (self.symbols.items) |symbol| {
+ switch (symbol.tag) {
+ .function => funcs.appendAssumeCapacity(.{ .index = symbol.index, .name = std.mem.sliceTo(symbol.name, 0) }),
+ .global => globals.appendAssumeCapacity(.{ .index = symbol.index, .name = std.mem.sliceTo(symbol.name, 0) }),
+ else => {},
+ }
+ }
+ // data segments are already 'ordered'
+ for (self.data_segments.keys()) |key, index| {
+ segments.appendAssumeCapacity(.{ .index = @intCast(u32, index), .name = key });
+ }
+
+ std.sort.sort(Name, funcs.items, {}, Name.lessThan);
+ std.sort.sort(Name, globals.items, {}, Name.lessThan);
+
+ const header_offset = try reserveCustomSectionHeader(file);
+ const writer = file.writer();
+ try leb.writeULEB128(writer, @intCast(u32, "name".len));
+ try writer.writeAll("name");
+
+ try self.emitNameSubsection(.function, funcs.items, writer);
+ try self.emitNameSubsection(.global, globals.items, writer);
+ try self.emitNameSubsection(.data_segment, segments.items, writer);
+
+ try writeCustomSectionHeader(
+ file,
+ header_offset,
+ @intCast(u32, (try file.getPos()) - header_offset - header_size),
+ );
+ }
+}
+
+fn emitNameSubsection(self: *Wasm, section_id: std.wasm.NameSubsection, names: anytype, writer: anytype) !void {
+ // We must emit subsection size, so first write to a temporary list
+ var section_list = std.ArrayList(u8).init(self.base.allocator);
+ defer section_list.deinit();
+ const sub_writer = section_list.writer();
+
+ try leb.writeULEB128(sub_writer, @intCast(u32, names.len));
+ for (names) |name| {
+ try leb.writeULEB128(sub_writer, name.index);
+ try leb.writeULEB128(sub_writer, @intCast(u32, name.name.len));
+ try sub_writer.writeAll(name.name);
+ }
+
+ // From now, write to the actual writer
+ try leb.writeULEB128(writer, @enumToInt(section_id));
+ try leb.writeULEB128(writer, @intCast(u32, section_list.items.len));
+ try writer.writeAll(section_list.items);
}
fn emitLimits(writer: anytype, limits: wasm.Limits) !void {
@@ -1335,6 +1400,15 @@ fn reserveVecSectionHeader(file: fs.File) !u64 {
return (try file.getPos()) - header_size;
}
+fn reserveCustomSectionHeader(file: fs.File) !u64 {
+ // unlike regular section, we don't emit the count
+ const header_size = 1 + 5;
+ // TODO: this should be a single lseek(2) call, but fs.File does not
+ // currently provide a way to do this.
+ try file.seekBy(header_size);
+ return (try file.getPos()) - header_size;
+}
+
fn writeVecSectionHeader(file: fs.File, offset: u64, section: wasm.Section, size: u32, items: u32) !void {
var buf: [1 + 5 + 5]u8 = undefined;
buf[0] = @enumToInt(section);
@@ -1343,6 +1417,13 @@ fn writeVecSectionHeader(file: fs.File, offset: u64, section: wasm.Section, size
try file.pwriteAll(&buf, offset);
}
+fn writeCustomSectionHeader(file: fs.File, offset: u64, size: u32) !void {
+ var buf: [1 + 5]u8 = undefined;
+ buf[0] = 0; // 0 = 'custom' section
+ leb.writeUnsignedFixed(5, buf[1..6], size);
+ try file.pwriteAll(&buf, offset);
+}
+
/// Searches for an a matching function signature, when not found
/// a new entry will be made. The index of the existing/new signature will be returned.
pub fn putOrGetFuncType(self: *Wasm, func_type: wasm.Type) !u32 {