authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-02-27 21:15:33+01:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-03-01 08:35:20+01:00
logb1159ab7aecfe704adcb4cf44d6fedafd572a720
tree7ddafb8b9c72922f22cc6c03987b72e9006df300
parent49f01c0a0cd510437f6f2d13d5de3e722f48cc3d

wasm-linker: Intern all symbol names

For all symbols read from object files as well as generated from Zig code will now be interned and have their offset into the string table saved on the `Symbol` instead. Besides interning, local symbols now also use a decl's fully qualified name. When a decl/symbol is extern/to-be-imported, the name of the decl itself will be used for symbol resolving. Similarly for symbols that will be exported, will have their 'export name' set.

4 files changed, 144 insertions(+), 62 deletions(-)

src/link/Wasm.zig+113-36
...@@ -79,6 +79,8 @@ data_segments: std.StringArrayHashMapUnmanaged(u32) = .{},...@@ -79,6 +79,8 @@ data_segments: std.StringArrayHashMapUnmanaged(u32) = .{},
79/// A list of `types.Segment` which provide meta data79/// A list of `types.Segment` which provide meta data
80/// about a data symbol such as its name80/// about a data symbol such as its name
81segment_info: std.ArrayListUnmanaged(types.Segment) = .{},81segment_info: std.ArrayListUnmanaged(types.Segment) = .{},
82/// Deduplicated string table for strings used by symbols, imports and exports.
83string_table: StringTable = .{},
8284
83// Output sections85// Output sections
84/// Output type section86/// Output type section
...@@ -155,6 +157,79 @@ pub const SymbolLoc = struct {...@@ -155,6 +157,79 @@ pub const SymbolLoc = struct {
155 }157 }
156 return &wasm_bin.symbols.items[self.index];158 return &wasm_bin.symbols.items[self.index];
157 }159 }
160
161 /// From a given location, returns the name of the symbol.
162 pub fn getName(self: SymbolLoc, wasm_bin: *const Wasm) []const u8 {
163 if (wasm_bin.discarded.get(self)) |new_loc| {
164 return new_loc.getName(wasm_bin);
165 }
166 if (self.file) |object_index| {
167 const object = wasm_bin.objects.items[object_index];
168 return object.string_table.get(object.symtable[self.index].name);
169 }
170 return wasm_bin.string_table.get(wasm_bin.symbols.items[self.index].name);
171 }
172};
173
174/// Generic string table that duplicates strings
175/// and converts them into offsets instead.
176pub const StringTable = struct {
177 /// Table that maps string offsets, which is used to de-duplicate strings.
178 /// Rather than having the offset map to the data, the `StringContext` holds all bytes of the string.
179 /// The strings are stored as a contigious array where each string is zero-terminated.
180 string_table: std.HashMapUnmanaged(
181 u32,
182 void,
183 std.hash_map.StringIndexContext,
184 std.hash_map.default_max_load_percentage,
185 ) = .{},
186 /// Holds the actual data of the string table.
187 string_data: std.ArrayListUnmanaged(u8) = .{},
188
189 /// Accepts a string and searches for a corresponding string.
190 /// When found, de-duplicates the string and returns the existing offset instead.
191 /// When the string is not found in the `string_table`, a new entry will be inserted
192 /// and the new offset to its data will be returned.
193 pub fn put(self: *StringTable, allocator: Allocator, string: []const u8) !u32 {
194 const gop = try self.string_table.getOrPutContextAdapted(
195 allocator,
196 string,
197 std.hash_map.StringIndexAdapter{ .bytes = &self.string_data },
198 .{ .bytes = &self.string_data },
199 );
200 if (gop.found_existing) {
201 const off = gop.key_ptr.*;
202 log.debug("reusing string '{s}' at offset 0x{x}", .{ string, off });
203 return off;
204 }
205
206 try self.string_data.ensureUnusedCapacity(allocator, string.len + 1);
207 const offset = @intCast(u32, self.string_data.items.len);
208
209 log.debug("writing new string '{s}' at offset 0x{x}", .{ string, offset });
210
211 self.string_data.appendSliceAssumeCapacity(string);
212 self.string_data.appendAssumeCapacity(0);
213
214 gop.key_ptr.* = offset;
215
216 return offset;
217 }
218
219 /// From a given offset, returns its corresponding string value.
220 /// Asserts offset does not exceed bounds.
221 pub fn get(self: StringTable, off: u32) []const u8 {
222 assert(off < self.string_data.items.len);
223 return mem.sliceTo(@ptrCast([*:0]const u8, self.string_data.items.ptr + off), 0);
224 }
225
226 /// Frees all resources of the string table. Any references pointing
227 /// to the strings will be invalid.
228 pub fn deinit(self: *StringTable, allocator: Allocator) void {
229 self.string_data.deinit(allocator);
230 self.string_table.deinit(allocator);
231 self.* = undefined;
232 }
158};233};
159234
160pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Options) !*Wasm {235pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Options) !*Wasm {
...@@ -177,7 +252,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option...@@ -177,7 +252,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
177 // As sym_index '0' is reserved, we use it for our stack pointer symbol252 // As sym_index '0' is reserved, we use it for our stack pointer symbol
178 const symbol = try wasm_bin.symbols.addOne(allocator);253 const symbol = try wasm_bin.symbols.addOne(allocator);
179 symbol.* = .{254 symbol.* = .{
180 .name = "__stack_pointer",255 .name = try wasm_bin.string_table.put(allocator, "__stack_pointer"),
181 .tag = .global,256 .tag = .global,
182 .flags = 0,257 .flags = 0,
183 .index = 0,258 .index = 0,
...@@ -268,12 +343,12 @@ fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void {...@@ -268,12 +343,12 @@ fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void {
268 .file = object_index,343 .file = object_index,
269 .index = sym_index,344 .index = sym_index,
270 };345 };
271 const sym_name = std.mem.sliceTo(symbol.name, 0);346 const sym_name = object.string_table.get(symbol.name);
272347
273 if (symbol.isLocal()) {348 if (symbol.isLocal()) {
274 if (symbol.isUndefined()) {349 if (symbol.isUndefined()) {
275 log.err("Local symbols are not allowed to reference imports", .{});350 log.err("Local symbols are not allowed to reference imports", .{});
276 log.err(" symbol '{s}' defined in '{s}'", .{ symbol.name, object.name });351 log.err(" symbol '{s}' defined in '{s}'", .{ sym_name, object.name });
277 return error.undefinedLocal;352 return error.undefinedLocal;
278 }353 }
279 try self.resolved_symbols.putNoClobber(self.base.allocator, location, {});354 try self.resolved_symbols.putNoClobber(self.base.allocator, location, {});
...@@ -299,7 +374,7 @@ fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void {...@@ -299,7 +374,7 @@ fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void {
299374
300 if (!existing_sym.isUndefined()) {375 if (!existing_sym.isUndefined()) {
301 if (!symbol.isUndefined()) {376 if (!symbol.isUndefined()) {
302 log.err("symbol '{s}' defined multiple times", .{existing_sym.name});377 log.err("symbol '{s}' defined multiple times", .{sym_name});
303 log.err(" first definition in '{s}'", .{existing_file_path});378 log.err(" first definition in '{s}'", .{existing_file_path});
304 log.err(" next definition in '{s}'", .{object.name});379 log.err(" next definition in '{s}'", .{object.name});
305 return error.SymbolCollision;380 return error.SymbolCollision;
...@@ -309,7 +384,7 @@ fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void {...@@ -309,7 +384,7 @@ fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void {
309 }384 }
310385
311 // simply overwrite with the new symbol386 // simply overwrite with the new symbol
312 log.debug("Overwriting symbol '{s}'", .{symbol.name});387 log.debug("Overwriting symbol '{s}'", .{sym_name});
313 log.debug(" old definition in '{s}'", .{existing_file_path});388 log.debug(" old definition in '{s}'", .{existing_file_path});
314 log.debug(" new definition in '{s}'", .{object.name});389 log.debug(" new definition in '{s}'", .{object.name});
315 try self.discarded.putNoClobber(self.base.allocator, maybe_existing.value_ptr.*, location);390 try self.discarded.putNoClobber(self.base.allocator, maybe_existing.value_ptr.*, location);
...@@ -328,12 +403,7 @@ pub fn deinit(self: *Wasm) void {...@@ -328,12 +403,7 @@ pub fn deinit(self: *Wasm) void {
328403
329 var decl_it = self.decls.keyIterator();404 var decl_it = self.decls.keyIterator();
330 while (decl_it.next()) |decl_ptr| {405 while (decl_it.next()) |decl_ptr| {
331 const decl = decl_ptr.*;406 decl_ptr.*.link.wasm.deinit(gpa);
332 const atom: *Atom = &decl.link.wasm;
333 for (atom.locals.items) |local| {
334 gpa.free(mem.sliceTo(self.symbols.items[local.sym_index].name, 0));
335 }
336 decl.link.wasm.deinit(gpa);
337 }407 }
338408
339 for (self.func_types.items) |*func_type| {409 for (self.func_types.items) |*func_type| {
...@@ -374,6 +444,8 @@ pub fn deinit(self: *Wasm) void {...@@ -374,6 +444,8 @@ pub fn deinit(self: *Wasm) void {
374 self.function_table.deinit(gpa);444 self.function_table.deinit(gpa);
375 self.tables.deinit(gpa);445 self.tables.deinit(gpa);
376 self.exports.deinit(gpa);446 self.exports.deinit(gpa);
447
448 self.string_table.deinit(gpa);
377}449}
378450
379pub fn allocateDeclIndexes(self: *Wasm, decl: *Module.Decl) !void {451pub fn allocateDeclIndexes(self: *Wasm, decl: *Module.Decl) !void {
...@@ -498,7 +570,10 @@ fn finishUpdateDecl(self: *Wasm, decl: *Module.Decl, code: []const u8) !void {...@@ -498,7 +570,10 @@ fn finishUpdateDecl(self: *Wasm, decl: *Module.Decl, code: []const u8) !void {
498 atom.size = @intCast(u32, code.len);570 atom.size = @intCast(u32, code.len);
499 atom.alignment = decl.ty.abiAlignment(self.base.options.target);571 atom.alignment = decl.ty.abiAlignment(self.base.options.target);
500 const symbol = &self.symbols.items[atom.sym_index];572 const symbol = &self.symbols.items[atom.sym_index];
501 symbol.name = decl.name;573
574 const full_name = try decl.getFullyQualifiedName(self.base.allocator);
575 defer self.base.allocator.free(full_name);
576 symbol.name = try self.string_table.put(self.base.allocator, full_name);
502 try atom.code.appendSlice(self.base.allocator, code);577 try atom.code.appendSlice(self.base.allocator, code);
503}578}
504579
...@@ -511,8 +586,9 @@ pub fn lowerUnnamedConst(self: *Wasm, decl: *Module.Decl, tv: TypedValue) !u32 {...@@ -511,8 +586,9 @@ pub fn lowerUnnamedConst(self: *Wasm, decl: *Module.Decl, tv: TypedValue) !u32 {
511 // Create and initialize a new local symbol and atom586 // Create and initialize a new local symbol and atom
512 const local_index = decl.link.wasm.locals.items.len;587 const local_index = decl.link.wasm.locals.items.len;
513 const name = try std.fmt.allocPrintZ(self.base.allocator, "__unnamed_{s}_{d}", .{ decl.name, local_index });588 const name = try std.fmt.allocPrintZ(self.base.allocator, "__unnamed_{s}_{d}", .{ decl.name, local_index });
589 defer self.base.allocator.free(name);
514 var symbol: Symbol = .{590 var symbol: Symbol = .{
515 .name = name,591 .name = try self.string_table.put(self.base.allocator, name),
516 .flags = 0,592 .flags = 0,
517 .tag = .data,593 .tag = .data,
518 .index = undefined,594 .index = undefined,
...@@ -615,7 +691,7 @@ pub fn deleteExport(self: *Wasm, exp: Export) void {...@@ -615,7 +691,7 @@ pub fn deleteExport(self: *Wasm, exp: Export) void {
615 const sym_index = exp.sym_index orelse return;691 const sym_index = exp.sym_index orelse return;
616 const loc: SymbolLoc = .{ .file = null, .index = sym_index };692 const loc: SymbolLoc = .{ .file = null, .index = sym_index };
617 const symbol = loc.getSymbol(self);693 const symbol = loc.getSymbol(self);
618 const symbol_name = mem.sliceTo(symbol.name, 0);694 const symbol_name = self.string_table.get(symbol.name);
619 log.debug("Deleting export for decl '{s}'", .{symbol_name});695 log.debug("Deleting export for decl '{s}'", .{symbol_name});
620 if (self.export_names.fetchRemove(loc)) |kv| {696 if (self.export_names.fetchRemove(loc)) |kv| {
621 assert(self.globals.remove(kv.value));697 assert(self.globals.remove(kv.value));
...@@ -656,7 +732,7 @@ pub fn updateDeclExports(...@@ -656,7 +732,7 @@ pub fn updateDeclExports(
656 // are strong symbols, we have a linker error.732 // are strong symbols, we have a linker error.
657 // In the other case we replace one with the other.733 // In the other case we replace one with the other.
658 if (!exp_is_weak and !existing_sym.isWeak()) {734 if (!exp_is_weak and !existing_sym.isWeak()) {
659 try module.failed_exports.putNoClobber(module.gpa, exp, try Module.ErrorMsg.create(735 try module.failed_exports.put(module.gpa, exp, try Module.ErrorMsg.create(
660 module.gpa,736 module.gpa,
661 decl.srcLoc(),737 decl.srcLoc(),
662 \\LinkError: symbol '{s}' defined multiple times738 \\LinkError: symbol '{s}' defined multiple times
...@@ -665,6 +741,7 @@ pub fn updateDeclExports(...@@ -665,6 +741,7 @@ pub fn updateDeclExports(
665 ,741 ,
666 .{ exp.options.name, self.name, self.name },742 .{ exp.options.name, self.name, self.name },
667 ));743 ));
744 continue;
668 } else if (exp_is_weak) {745 } else if (exp_is_weak) {
669 continue; // to-be-exported symbol is weak, so we keep the existing symbol746 continue; // to-be-exported symbol is weak, so we keep the existing symbol
670 } else {747 } else {
...@@ -697,7 +774,7 @@ pub fn updateDeclExports(...@@ -697,7 +774,7 @@ pub fn updateDeclExports(
697 },774 },
698 }775 }
699 // Ensure the symbol will be exported using the given name776 // Ensure the symbol will be exported using the given name
700 if (!mem.eql(u8, exp.options.name, mem.sliceTo(exp.exported_decl.name, 0))) {777 if (!mem.eql(u8, exp.options.name, sym_loc.getName(self))) {
701 try self.export_names.put(self.base.allocator, sym_loc, exp.options.name);778 try self.export_names.put(self.base.allocator, sym_loc, exp.options.name);
702 }779 }
703780
...@@ -725,7 +802,6 @@ pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void {...@@ -725,7 +802,6 @@ pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void {
725 for (atom.locals.items) |local_atom| {802 for (atom.locals.items) |local_atom| {
726 const local_symbol = &self.symbols.items[local_atom.sym_index];803 const local_symbol = &self.symbols.items[local_atom.sym_index];
727 local_symbol.tag = .dead; // also for any local symbol804 local_symbol.tag = .dead; // also for any local symbol
728 self.base.allocator.free(mem.sliceTo(local_symbol.name, 0));
729 self.symbols_free_list.append(self.base.allocator, local_atom.sym_index) catch {};805 self.symbols_free_list.append(self.base.allocator, local_atom.sym_index) catch {};
730 assert(self.resolved_symbols.swapRemove(local_atom.symbolLoc()));806 assert(self.resolved_symbols.swapRemove(local_atom.symbolLoc()));
731 }807 }
...@@ -755,14 +831,15 @@ fn mapFunctionTable(self: *Wasm) void {...@@ -755,14 +831,15 @@ fn mapFunctionTable(self: *Wasm) void {
755}831}
756832
757fn addOrUpdateImport(self: *Wasm, decl: *Module.Decl) !void {833fn addOrUpdateImport(self: *Wasm, decl: *Module.Decl) !void {
834 // For the import name itself, we use the decl's name, rather than the fully qualified name
835 const decl_name = mem.sliceTo(decl.name, 0);
758 const symbol_index = decl.link.wasm.sym_index;836 const symbol_index = decl.link.wasm.sym_index;
759 const symbol: *Symbol = &self.symbols.items[symbol_index];837 const symbol: *Symbol = &self.symbols.items[symbol_index];
760 symbol.name = decl.name;
761 symbol.setUndefined(true);838 symbol.setUndefined(true);
762 symbol.setGlobal(true);839 symbol.setGlobal(true);
763 try self.globals.putNoClobber(840 try self.globals.putNoClobber(
764 self.base.allocator,841 self.base.allocator,
765 mem.sliceTo(symbol.name, 0),842 decl_name,
766 .{ .file = null, .index = symbol_index },843 .{ .file = null, .index = symbol_index },
767 );844 );
768 try self.resolved_symbols.put(self.base.allocator, .{ .file = null, .index = symbol_index }, {});845 try self.resolved_symbols.put(self.base.allocator, .{ .file = null, .index = symbol_index }, {});
...@@ -776,7 +853,7 @@ fn addOrUpdateImport(self: *Wasm, decl: *Module.Decl) !void {...@@ -776,7 +853,7 @@ fn addOrUpdateImport(self: *Wasm, decl: *Module.Decl) !void {
776 if (!gop.found_existing) {853 if (!gop.found_existing) {
777 gop.value_ptr.* = .{854 gop.value_ptr.* = .{
778 .module_name = module_name,855 .module_name = module_name,
779 .name = mem.sliceTo(symbol.name, 0),856 .name = decl_name,
780 .kind = .{ .function = decl.fn_link.wasm.type_index },857 .kind = .{ .function = decl.fn_link.wasm.type_index },
781 };858 };
782 }859 }
...@@ -815,7 +892,7 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void {...@@ -815,7 +892,7 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void {
815 // TODO: Add mutables global decls to .bss section instead892 // TODO: Add mutables global decls to .bss section instead
816 const segment_name = try std.mem.concat(self.base.allocator, u8, &.{893 const segment_name = try std.mem.concat(self.base.allocator, u8, &.{
817 ".rodata.",894 ".rodata.",
818 std.mem.span(symbol.name),895 self.string_table.get(symbol.name),
819 });896 });
820 errdefer self.base.allocator.free(segment_name);897 errdefer self.base.allocator.free(segment_name);
821 const segment_info: types.Segment = .{898 const segment_info: types.Segment = .{
...@@ -886,7 +963,7 @@ fn allocateAtoms(self: *Wasm) !void {...@@ -886,7 +963,7 @@ fn allocateAtoms(self: *Wasm) !void {
886 atom.offset = offset;963 atom.offset = offset;
887 const symbol_loc = atom.symbolLoc();964 const symbol_loc = atom.symbolLoc();
888 log.debug("Atom '{s}' allocated from 0x{x:0>8} to 0x{x:0>8} size={d}", .{965 log.debug("Atom '{s}' allocated from 0x{x:0>8} to 0x{x:0>8} size={d}", .{
889 symbol_loc.getSymbol(self).name,966 symbol_loc.getName(self),
890 offset,967 offset,
891 offset + atom.size,968 offset + atom.size,
892 atom.size,969 atom.size,
...@@ -906,7 +983,7 @@ fn setupImports(self: *Wasm) !void {...@@ -906,7 +983,7 @@ fn setupImports(self: *Wasm) !void {
906 // remove an import if it was resolved983 // remove an import if it was resolved
907 if (self.imports.remove(discarded.*)) {984 if (self.imports.remove(discarded.*)) {
908 log.debug("Removed symbol '{s}' as an import", .{985 log.debug("Removed symbol '{s}' as an import", .{
909 discarded.getSymbol(self).name,986 discarded.getName(self),
910 });987 });
911 }988 }
912 }989 }
...@@ -923,7 +1000,7 @@ fn setupImports(self: *Wasm) !void {...@@ -923,7 +1000,7 @@ fn setupImports(self: *Wasm) !void {
923 continue;1000 continue;
924 }1001 }
9251002
926 log.debug("Symbol '{s}' will be imported from the host", .{symbol.name});1003 log.debug("Symbol '{s}' will be imported from the host", .{symbol_loc.getName(self)});
927 const import = self.objects.items[symbol_loc.file.?].findImport(symbol.tag.externalType(), symbol.index);1004 const import = self.objects.items[symbol_loc.file.?].findImport(symbol.tag.externalType(), symbol.index);
928 // TODO: De-duplicate imports1005 // TODO: De-duplicate imports
929 try self.imports.putNoClobber(self.base.allocator, symbol_loc, import);1006 try self.imports.putNoClobber(self.base.allocator, symbol_loc, import);
...@@ -1036,12 +1113,12 @@ fn mergeTypes(self: *Wasm) !void {...@@ -1036,12 +1113,12 @@ fn mergeTypes(self: *Wasm) !void {
1036 }1113 }
10371114
1038 if (symbol.isUndefined()) {1115 if (symbol.isUndefined()) {
1039 log.debug("Adding type from extern function '{s}'", .{symbol.name});1116 log.debug("Adding type from extern function '{s}'", .{sym_loc.getName(self)});
1040 const import: *wasm.Import = self.imports.getPtr(sym_loc).?;1117 const import: *wasm.Import = self.imports.getPtr(sym_loc).?;
1041 const original_type = object.func_types[import.kind.function];1118 const original_type = object.func_types[import.kind.function];
1042 import.kind.function = try self.putOrGetFuncType(original_type);1119 import.kind.function = try self.putOrGetFuncType(original_type);
1043 } else {1120 } else {
1044 log.debug("Adding type from function '{s}'", .{symbol.name});1121 log.debug("Adding type from function '{s}'", .{sym_loc.getName(self)});
1045 const func = &self.functions.items[symbol.index - self.imported_functions_count];1122 const func = &self.functions.items[symbol.index - self.imported_functions_count];
1046 func.type_index = try self.putOrGetFuncType(object.func_types[func.type_index]);1123 func.type_index = try self.putOrGetFuncType(object.func_types[func.type_index]);
1047 }1124 }
...@@ -1057,13 +1134,14 @@ fn setupExports(self: *Wasm) !void {...@@ -1057,13 +1134,14 @@ fn setupExports(self: *Wasm) !void {
1057 const symbol = sym_loc.getSymbol(self);1134 const symbol = sym_loc.getSymbol(self);
1058 if (!symbol.isExported()) continue;1135 if (!symbol.isExported()) continue;
10591136
1060 const export_name = if (self.export_names.get(sym_loc)) |name| name else mem.sliceTo(symbol.name, 0);1137 const sym_name = sym_loc.getName(self);
1138 const export_name = if (self.export_names.get(sym_loc)) |name| name else sym_name;
1061 const exp: wasm.Export = .{1139 const exp: wasm.Export = .{
1062 .name = export_name,1140 .name = export_name,
1063 .kind = symbol.tag.externalType(),1141 .kind = symbol.tag.externalType(),
1064 .index = symbol.index,1142 .index = symbol.index,
1065 };1143 };
1066 log.debug("Exporting symbol '{s}' as '{s}' at index: ({d})", .{ symbol.name, exp.name, exp.index });1144 log.debug("Exporting symbol '{s}' as '{s}' at index: ({d})", .{ sym_name, exp.name, exp.index });
1067 try self.exports.append(self.base.allocator, exp);1145 try self.exports.append(self.base.allocator, exp);
1068 }1146 }
10691147
...@@ -1670,8 +1748,8 @@ fn emitNameSection(self: *Wasm, file: fs.File, arena: Allocator) !void {...@@ -1670,8 +1748,8 @@ fn emitNameSection(self: *Wasm, file: fs.File, arena: Allocator) !void {
1670 for (self.resolved_symbols.keys()) |sym_loc| {1748 for (self.resolved_symbols.keys()) |sym_loc| {
1671 const symbol = sym_loc.getSymbol(self).*;1749 const symbol = sym_loc.getSymbol(self).*;
1672 switch (symbol.tag) {1750 switch (symbol.tag) {
1673 .function => funcs.appendAssumeCapacity(.{ .index = symbol.index, .name = mem.sliceTo(symbol.name, 0) }),1751 .function => funcs.appendAssumeCapacity(.{ .index = symbol.index, .name = sym_loc.getName(self) }),
1674 .global => globals.appendAssumeCapacity(.{ .index = symbol.index, .name = mem.sliceTo(symbol.name, 0) }),1752 .global => globals.appendAssumeCapacity(.{ .index = symbol.index, .name = sym_loc.getName(self) }),
1675 else => {},1753 else => {},
1676 }1754 }
1677 }1755 }
...@@ -2275,11 +2353,11 @@ fn emitSymbolTable(self: *Wasm, file: fs.File, arena: Allocator, symbol_table: *...@@ -2275,11 +2353,11 @@ fn emitSymbolTable(self: *Wasm, file: fs.File, arena: Allocator, symbol_table: *
2275 try leb.writeULEB128(writer, @enumToInt(symbol.tag));2353 try leb.writeULEB128(writer, @enumToInt(symbol.tag));
2276 try leb.writeULEB128(writer, symbol.flags);2354 try leb.writeULEB128(writer, symbol.flags);
22772355
2356 const sym_name = if (self.export_names.get(sym_loc)) |exp_name| exp_name else sym_loc.getName(self);
2278 switch (symbol.tag) {2357 switch (symbol.tag) {
2279 .data => {2358 .data => {
2280 const name = mem.sliceTo(symbol.name, 0);2359 try leb.writeULEB128(writer, @intCast(u32, sym_name.len));
2281 try leb.writeULEB128(writer, @intCast(u32, name.len));2360 try writer.writeAll(sym_name);
2282 try writer.writeAll(name);
22832361
2284 if (symbol.isDefined()) {2362 if (symbol.isDefined()) {
2285 try leb.writeULEB128(writer, symbol.index);2363 try leb.writeULEB128(writer, symbol.index);
...@@ -2294,9 +2372,8 @@ fn emitSymbolTable(self: *Wasm, file: fs.File, arena: Allocator, symbol_table: *...@@ -2294,9 +2372,8 @@ fn emitSymbolTable(self: *Wasm, file: fs.File, arena: Allocator, symbol_table: *
2294 else => {2372 else => {
2295 try leb.writeULEB128(writer, symbol.index);2373 try leb.writeULEB128(writer, symbol.index);
2296 if (symbol.isDefined()) {2374 if (symbol.isDefined()) {
2297 const name = mem.sliceTo(symbol.name, 0);2375 try leb.writeULEB128(writer, @intCast(u32, sym_name.len));
2298 try leb.writeULEB128(writer, @intCast(u32, name.len));2376 try writer.writeAll(sym_name);
2299 try writer.writeAll(name);
2300 }2377 }
2301 },2378 },
2302 }2379 }
src/link/Wasm/Atom.zig+4-4
...@@ -103,17 +103,17 @@ pub fn symbolLoc(self: Atom) Wasm.SymbolLoc {...@@ -103,17 +103,17 @@ pub fn symbolLoc(self: Atom) Wasm.SymbolLoc {
103/// at the calculated offset.103/// at the calculated offset.
104pub fn resolveRelocs(self: *Atom, wasm_bin: *const Wasm) !void {104pub fn resolveRelocs(self: *Atom, wasm_bin: *const Wasm) !void {
105 if (self.relocs.items.len == 0) return;105 if (self.relocs.items.len == 0) return;
106 const symbol = self.symbolLoc().getSymbol(wasm_bin).*;106 const symbol_name = self.symbolLoc().getName(wasm_bin);
107 log.debug("Resolving relocs in atom '{s}' count({d})", .{107 log.debug("Resolving relocs in atom '{s}' count({d})", .{
108 symbol.name,108 symbol_name,
109 self.relocs.items.len,109 self.relocs.items.len,
110 });110 });
111111
112 for (self.relocs.items) |reloc| {112 for (self.relocs.items) |reloc| {
113 const value = try self.relocationValue(reloc, wasm_bin);113 const value = try self.relocationValue(reloc, wasm_bin);
114 log.debug("Relocating '{s}' referenced in '{s}' offset=0x{x:0>8} value={d}", .{114 log.debug("Relocating '{s}' referenced in '{s}' offset=0x{x:0>8} value={d}", .{
115 (Wasm.SymbolLoc{ .file = self.file, .index = reloc.index }).getSymbol(wasm_bin).name,115 (Wasm.SymbolLoc{ .file = self.file, .index = reloc.index }).getName(wasm_bin),
116 symbol.name,116 symbol_name,
117 reloc.offset,117 reloc.offset,
118 value,118 value,
119 });119 });
src/link/Wasm/Object.zig+16-14
...@@ -59,6 +59,10 @@ comdat_info: []const types.Comdat = &.{},...@@ -59,6 +59,10 @@ comdat_info: []const types.Comdat = &.{},
59/// Represents non-synthetic sections that can essentially be mem-cpy'd into place59/// Represents non-synthetic sections that can essentially be mem-cpy'd into place
60/// after performing relocations.60/// after performing relocations.
61relocatable_data: []const RelocatableData = &.{},61relocatable_data: []const RelocatableData = &.{},
62/// String table for all strings required by the object file, such as symbol names,
63/// import name, module name and export names. Each string will be deduplicated
64/// and returns an offset into the table.
65string_table: Wasm.StringTable = .{},
6266
63/// Represents a single item within a section (depending on its `type`)67/// Represents a single item within a section (depending on its `type`)
64const RelocatableData = struct {68const RelocatableData = struct {
...@@ -142,9 +146,6 @@ pub fn deinit(self: *Object, gpa: Allocator) void {...@@ -142,9 +146,6 @@ pub fn deinit(self: *Object, gpa: Allocator) void {
142 gpa.free(val);146 gpa.free(val);
143 }147 }
144 self.relocations.deinit(gpa);148 self.relocations.deinit(gpa);
145 for (self.symtable) |symbol| {
146 gpa.free(std.mem.sliceTo(symbol.name, 0));
147 }
148 gpa.free(self.symtable);149 gpa.free(self.symtable);
149 gpa.free(self.comdat_info);150 gpa.free(self.comdat_info);
150 gpa.free(self.init_funcs);151 gpa.free(self.init_funcs);
...@@ -156,6 +157,7 @@ pub fn deinit(self: *Object, gpa: Allocator) void {...@@ -156,6 +157,7 @@ pub fn deinit(self: *Object, gpa: Allocator) void {
156 gpa.free(rel_data.data[0..rel_data.size]);157 gpa.free(rel_data.data[0..rel_data.size]);
157 }158 }
158 gpa.free(self.relocatable_data);159 gpa.free(self.relocatable_data);
160 self.string_table.deinit(gpa);
159 self.* = undefined;161 self.* = undefined;
160}162}
161163
...@@ -228,7 +230,7 @@ fn checkLegacyIndirectFunctionTable(self: *Object, gpa: Allocator) !?Symbol {...@@ -228,7 +230,7 @@ fn checkLegacyIndirectFunctionTable(self: *Object, gpa: Allocator) !?Symbol {
228230
229 var table_symbol: Symbol = .{231 var table_symbol: Symbol = .{
230 .flags = 0,232 .flags = 0,
231 .name = try gpa.dupeZ(u8, table_import.name),233 .name = try self.string_table.put(gpa, table_import.name),
232 .tag = .table,234 .tag = .table,
233 .index = 0,235 .index = 0,
234 };236 };
...@@ -666,7 +668,7 @@ fn Parser(comptime ReaderType: type) type {...@@ -666,7 +668,7 @@ fn Parser(comptime ReaderType: type) type {
666 symbol.* = try self.parseSymbol(gpa, reader);668 symbol.* = try self.parseSymbol(gpa, reader);
667 log.debug("Found symbol: type({s}) name({s}) flags(0b{b:0>8})", .{669 log.debug("Found symbol: type({s}) name({s}) flags(0b{b:0>8})", .{
668 @tagName(symbol.tag),670 @tagName(symbol.tag),
669 symbol.name,671 self.object.string_table.get(symbol.name),
670 symbol.flags,672 symbol.flags,
671 });673 });
672 }674 }
...@@ -699,10 +701,10 @@ fn Parser(comptime ReaderType: type) type {...@@ -699,10 +701,10 @@ fn Parser(comptime ReaderType: type) type {
699 switch (tag) {701 switch (tag) {
700 .data => {702 .data => {
701 const name_len = try leb.readULEB128(u32, reader);703 const name_len = try leb.readULEB128(u32, reader);
702 const name = try gpa.allocSentinel(u8, name_len, 0);704 const name = try gpa.alloc(u8, name_len);
703 errdefer gpa.free(name);705 defer gpa.free(name);
704 try reader.readNoEof(name);706 try reader.readNoEof(name);
705 symbol.name = name;707 symbol.name = try self.object.string_table.put(gpa, name);
706708
707 // Data symbols only have the following fields if the symbol is defined709 // Data symbols only have the following fields if the symbol is defined
708 if (symbol.isDefined()) {710 if (symbol.isDefined()) {
...@@ -714,7 +716,7 @@ fn Parser(comptime ReaderType: type) type {...@@ -714,7 +716,7 @@ fn Parser(comptime ReaderType: type) type {
714 },716 },
715 .section => {717 .section => {
716 symbol.index = try leb.readULEB128(u32, reader);718 symbol.index = try leb.readULEB128(u32, reader);
717 symbol.name = @tagName(symbol.tag);719 symbol.name = try self.object.string_table.put(gpa, @tagName(symbol.tag));
718 },720 },
719 else => {721 else => {
720 symbol.index = try leb.readULEB128(u32, reader);722 symbol.index = try leb.readULEB128(u32, reader);
...@@ -727,12 +729,12 @@ fn Parser(comptime ReaderType: type) type {...@@ -727,12 +729,12 @@ fn Parser(comptime ReaderType: type) type {
727 const explicit_name = symbol.hasFlag(.WASM_SYM_EXPLICIT_NAME);729 const explicit_name = symbol.hasFlag(.WASM_SYM_EXPLICIT_NAME);
728 if (!(is_undefined and !explicit_name)) {730 if (!(is_undefined and !explicit_name)) {
729 const name_len = try leb.readULEB128(u32, reader);731 const name_len = try leb.readULEB128(u32, reader);
730 const name = try gpa.allocSentinel(u8, name_len, 0);732 const name = try gpa.alloc(u8, name_len);
731 errdefer gpa.free(name);733 defer gpa.free(name);
732 try reader.readNoEof(name);734 try reader.readNoEof(name);
733 symbol.name = name;735 symbol.name = try self.object.string_table.put(gpa, name);
734 } else {736 } else {
735 symbol.name = try gpa.dupeZ(u8, maybe_import.?.name);737 symbol.name = try self.object.string_table.put(gpa, maybe_import.?.name);
736 }738 }
737 },739 },
738 }740 }
...@@ -882,7 +884,7 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin...@@ -882,7 +884,7 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin
882 } else {884 } else {
883 try wasm_bin.atoms.putNoClobber(gpa, final_index, atom);885 try wasm_bin.atoms.putNoClobber(gpa, final_index, atom);
884 }886 }
885 log.debug("Parsed into atom: '{s}'", .{self.symtable[atom.sym_index].name});887 log.debug("Parsed into atom: '{s}'", .{self.string_table.get(self.symtable[atom.sym_index].name)});
886 }888 }
887}889}
888890
src/link/Wasm/Symbol.zig+11-8
...@@ -1,5 +1,8 @@...@@ -1,5 +1,8 @@
1//! Wasm symbols describing its kind,1//! Represents a wasm symbol. Containing all of its properties,
2//! name and its properties.2//! as well as providing helper methods to determine its functionality
3//! and how it will/must be linked.
4//! The name of the symbol can be found by providing the offset, found
5//! on the `name` field, to a string table in the wasm binary or object file.
3const Symbol = @This();6const Symbol = @This();
47
5const std = @import("std");8const std = @import("std");
...@@ -8,15 +11,15 @@ const types = @import("types.zig");...@@ -8,15 +11,15 @@ const types = @import("types.zig");
8/// Bitfield containings flags for a symbol11/// Bitfield containings flags for a symbol
9/// Can contain any of the flags defined in `Flag`12/// Can contain any of the flags defined in `Flag`
10flags: u32,13flags: u32,
11/// Symbol name, when undefined this will be taken from the import.14/// Symbol name, when the symbol is undefined the name will be taken from the import.
12name: [*:0]const u8,15/// Note: This is an index into the string table.
13/// An union that represents both the type of symbol16name: u32,
14/// as well as the data it holds.
15tag: Tag,
16/// Index into the list of objects based on set `tag`17/// Index into the list of objects based on set `tag`
17/// NOTE: This will be set to `undefined` when `tag` is `data`18/// NOTE: This will be set to `undefined` when `tag` is `data`
18/// and the symbol is undefined.19/// and the symbol is undefined.
19index: u32,20index: u32,
21/// Represents the kind of the symbol, such as a function or global.
22tag: Tag,
2023
21pub const Tag = enum {24pub const Tag = enum {
22 function,25 function,
...@@ -164,7 +167,7 @@ pub fn format(self: Symbol, comptime fmt: []const u8, options: std.fmt.FormatOpt...@@ -164,7 +167,7 @@ pub fn format(self: Symbol, comptime fmt: []const u8, options: std.fmt.FormatOpt
164 const binding: []const u8 = if (self.isLocal()) "local" else "global";167 const binding: []const u8 = if (self.isLocal()) "local" else "global";
165168
166 try writer.print(169 try writer.print(
167 "{c} binding={s} visible={s} id={d} name={s}",170 "{c} binding={s} visible={s} id={d} name_offset={d}",
168 .{ kind_fmt, binding, visible, self.index, self.name },171 .{ kind_fmt, binding, visible, self.index, self.name },
169 );172 );
170}173}