authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-02-21 21:43:38+01:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-02-23 16:07:36+01:00
logacec06cfaf9a82ec8037a23993ff36fa72eb6e82
tree6fbabbcf1bedaf2b46ee42c170f187c2a34648f5
parent0a48a763fd3548a7b8609555cecbafc21ebe1fc3

wasm-linker: Implement `updateDeclExports`

We now correctly implement exporting decls. This means it is possible to export a decl with a different name than the decl that is doing the export. This also sets the symbols with the correct flags, so when we emit a relocatable object file, a linker can correctly resolve symbols and/or export the symbol to the host environment. This commit also includes fixes to ensure relocations have the correct offset to how other linkers will expect the offset, rather than what we use internally. Other linkers accept the offset, relative to the section. Internally we use an offset relative to the atom.

8 files changed, 235 insertions(+), 84 deletions(-)

src/Module.zig+3
......@@ -4516,6 +4516,9 @@ fn deleteDeclExports(mod: *Module, decl: *Decl) void {
45164516 if (mod.comp.bin_file.cast(link.File.MachO)) |macho| {
45174517 macho.deleteExport(exp.link.macho);
45184518 }
4519 if (mod.comp.bin_file.cast(link.File.Wasm)) |wasm| {
4520 wasm.deleteExport(exp.link.wasm);
4521 }
45194522 if (mod.failed_exports.fetchSwapRemove(exp)) |failed_kv| {
45204523 failed_kv.value.destroy(mod.gpa);
45214524 }
src/Sema.zig+1-1
......@@ -3919,7 +3919,7 @@ pub fn analyzeExport(
39193919 .macho => .{ .macho = .{} },
39203920 .plan9 => .{ .plan9 = null },
39213921 .c => .{ .c = {} },
3922 .wasm => .{ .wasm = {} },
3922 .wasm => .{ .wasm = .{} },
39233923 .spirv => .{ .spirv = {} },
39243924 .nvptx => .{ .nvptx = {} },
39253925 },
src/arch/wasm/CodeGen.zig+12-8
......@@ -1320,14 +1320,18 @@ pub const DeclGen = struct {
13201320 }
13211321
13221322 decl.markAlive();
1323 try writer.writeIntLittle(u32, try self.bin_file.getDeclVAddr(
1324 self.decl, // The decl containing the source symbol index
1325 decl.ty, // type we generate the address of
1326 self.symbol_index, // source symbol index
1327 decl.link.wasm.sym_index, // target symbol index
1328 @intCast(u32, self.code.items.len), // offset
1329 @intCast(u32, offset), // addend
1330 ));
1323 if (decl.link.wasm.sym_index == 0) {
1324 try writer.writeIntLittle(u32, 0);
1325 } else {
1326 try writer.writeIntLittle(u32, try self.bin_file.getDeclVAddr(
1327 self.decl, // The decl containing the source symbol index
1328 decl.ty, // type we generate the address of
1329 self.symbol_index, // source symbol index
1330 decl.link.wasm.sym_index, // target symbol index
1331 @intCast(u32, self.code.items.len), // offset
1332 @intCast(u32, offset), // addend
1333 ));
1334 }
13311335 return Result{ .appended = {} };
13321336 }
13331337};
src/arch/wasm/Emit.zig+23-16
......@@ -242,6 +242,7 @@ fn emitGlobal(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) !void {
242242 const global_offset = emit.offset();
243243 try emit.code.appendSlice(&buf);
244244
245 // globals can have index 0 as it represents the stack pointer
245246 try emit.decl.link.wasm.relocs.append(emit.bin_file.allocator, .{
246247 .index = label,
247248 .offset = global_offset,
......@@ -294,11 +295,13 @@ fn emitCall(emit: *Emit, inst: Mir.Inst.Index) !void {
294295 leb128.writeUnsignedFixed(5, &buf, label);
295296 try emit.code.appendSlice(&buf);
296297
297 try emit.decl.link.wasm.relocs.append(emit.bin_file.allocator, .{
298 .offset = call_offset,
299 .index = label,
300 .relocation_type = .R_WASM_FUNCTION_INDEX_LEB,
301 });
298 if (label != 0) {
299 try emit.decl.link.wasm.relocs.append(emit.bin_file.allocator, .{
300 .offset = call_offset,
301 .index = label,
302 .relocation_type = .R_WASM_FUNCTION_INDEX_LEB,
303 });
304 }
302305}
303306
304307fn emitCallIndirect(emit: *Emit, inst: Mir.Inst.Index) !void {
......@@ -318,11 +321,13 @@ fn emitFunctionIndex(emit: *Emit, inst: Mir.Inst.Index) !void {
318321 leb128.writeUnsignedFixed(5, &buf, symbol_index);
319322 try emit.code.appendSlice(&buf);
320323
321 try emit.decl.link.wasm.relocs.append(emit.bin_file.allocator, .{
322 .offset = index_offset,
323 .index = symbol_index,
324 .relocation_type = .R_WASM_TABLE_INDEX_SLEB,
325 });
324 if (symbol_index != 0) {
325 try emit.decl.link.wasm.relocs.append(emit.bin_file.allocator, .{
326 .offset = index_offset,
327 .index = symbol_index,
328 .relocation_type = .R_WASM_TABLE_INDEX_SLEB,
329 });
330 }
326331}
327332
328333fn emitMemAddress(emit: *Emit, inst: Mir.Inst.Index) !void {
......@@ -342,12 +347,14 @@ fn emitMemAddress(emit: *Emit, inst: Mir.Inst.Index) !void {
342347 try emit.code.appendSlice(&buf);
343348 }
344349
345 try emit.decl.link.wasm.relocs.append(emit.bin_file.allocator, .{
346 .offset = mem_offset,
347 .index = mem.pointer,
348 .relocation_type = if (is_wasm32) .R_WASM_MEMORY_ADDR_LEB else .R_WASM_MEMORY_ADDR_LEB64,
349 .addend = mem.offset,
350 });
350 if (mem.pointer != 0) {
351 try emit.decl.link.wasm.relocs.append(emit.bin_file.allocator, .{
352 .offset = mem_offset,
353 .index = mem.pointer,
354 .relocation_type = if (is_wasm32) .R_WASM_MEMORY_ADDR_LEB else .R_WASM_MEMORY_ADDR_LEB64,
355 .addend = mem.offset,
356 });
357 }
351358}
352359
353360fn emitExtended(emit: *Emit, inst: Mir.Inst.Index) !void {
src/link.zig+1-1
......@@ -237,7 +237,7 @@ pub const File = struct {
237237 macho: MachO.Export,
238238 plan9: Plan9.Export,
239239 c: void,
240 wasm: void,
240 wasm: Wasm.Export,
241241 spirv: void,
242242 nvptx: void,
243243 };
src/link/Wasm.zig+187-57
......@@ -114,6 +114,9 @@ resolved_symbols: std.AutoArrayHashMapUnmanaged(SymbolLoc, void) = .{},
114114/// data of a symbol, such as its size, or its offset to perform a relocation.
115115/// Undefined (and synthetic) symbols do not have an Atom and therefore cannot be mapped.
116116symbol_atom: std.AutoHashMapUnmanaged(SymbolLoc, *Atom) = .{},
117/// Maps a symbol's location to its export name, which may differ from the decl's name
118/// which does the exporting.
119export_names: std.AutoHashMapUnmanaged(SymbolLoc, []const u8) = .{},
117120
118121pub const Segment = struct {
119122 alignment: u32,
......@@ -129,6 +132,10 @@ pub const FnData = struct {
129132 };
130133};
131134
135pub const Export = struct {
136 sym_index: ?u32 = null,
137};
138
132139pub const SymbolLoc = struct {
133140 /// The index of the symbol within the specified file
134141 index: u32,
......@@ -191,6 +198,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
191198 },
192199 );
193200 } else {
201 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
194202 const global = try wasm_bin.wasm_globals.addOne(allocator);
195203 global.* = .{
196204 .global_type = .{
......@@ -345,6 +353,7 @@ pub fn deinit(self: *Wasm) void {
345353 self.resolved_symbols.deinit(gpa);
346354 self.discarded.deinit(gpa);
347355 self.symbol_atom.deinit(gpa);
356 self.export_names.deinit(gpa);
348357 self.atoms.deinit(gpa);
349358 self.managed_atoms.deinit(gpa);
350359 self.segments.deinit(gpa);
......@@ -372,7 +381,7 @@ pub fn allocateDeclIndexes(self: *Wasm, decl: *Module.Decl) !void {
372381
373382 var symbol: Symbol = .{
374383 .name = undefined, // will be set after updateDecl
375 .flags = 0,
384 .flags = @enumToInt(Symbol.Flag.WASM_SYM_BINDING_LOCAL),
376385 .tag = undefined, // will be set after updateDecl
377386 .index = undefined, // will be set after updateDecl
378387 };
......@@ -485,7 +494,6 @@ fn finishUpdateDecl(self: *Wasm, decl: *Module.Decl, code: []const u8) !void {
485494 atom.alignment = decl.ty.abiAlignment(self.base.options.target);
486495 const symbol = &self.symbols.items[atom.sym_index];
487496 symbol.name = decl.name;
488 symbol.setFlag(.WASM_SYM_BINDING_LOCAL);
489497 try atom.code.appendSlice(self.base.allocator, code);
490498}
491499
......@@ -541,6 +549,7 @@ pub fn getDeclVAddr(
541549 offset: u32,
542550 addend: u32,
543551) !u32 {
552 assert(target_symbol_index != 0);
544553 const atom = decl.link.wasm.symbolAtom(symbol_index);
545554 const is_wasm32 = self.base.options.target.cpu.arch == .wasm32;
546555 if (ty.zigTypeTag() == .Fn) {
......@@ -569,6 +578,20 @@ pub fn getDeclVAddr(
569578 return target_symbol_index;
570579}
571580
581pub fn deleteExport(self: *Wasm, exp: Export) void {
582 if (self.llvm_object) |_| return;
583 const sym_index = exp.sym_index orelse return;
584 const loc: SymbolLoc = .{ .file = null, .index = sym_index };
585 const symbol = loc.getSymbol(self);
586 const symbol_name = mem.sliceTo(symbol.name, 0);
587 log.debug("Deleting export for decl '{s}'", .{symbol_name});
588 if (self.export_names.fetchRemove(loc)) |kv| {
589 assert(self.globals.remove(kv.value));
590 } else {
591 assert(self.globals.remove(symbol_name));
592 }
593}
594
572595pub fn updateDeclExports(
573596 self: *Wasm,
574597 module: *Module,
......@@ -581,6 +604,82 @@ pub fn updateDeclExports(
581604 if (build_options.have_llvm) {
582605 if (self.llvm_object) |llvm_object| return llvm_object.updateDeclExports(module, decl, exports);
583606 }
607
608 for (exports) |exp| {
609 if (exp.options.section) |section| {
610 try module.failed_exports.putNoClobber(module.gpa, exp, try Module.ErrorMsg.create(
611 module.gpa,
612 decl.srcLoc(),
613 "Unimplemented: ExportOptions.section '{s}'",
614 .{section},
615 ));
616 continue;
617 }
618 if (self.globals.getPtr(exp.options.name)) |existing_loc| {
619 if (existing_loc.index == decl.link.wasm.sym_index) continue;
620 const existing_sym: Symbol = existing_loc.getSymbol(self).*;
621
622 const exp_is_weak = exp.options.linkage == .Internal or exp.options.linkage == .Weak;
623 // When both the to-bo-exported symbol and the already existing symbol
624 // are strong symbols, we have a linker error.
625 // In the other case we replace one with the other.
626 if (!exp_is_weak and !existing_sym.isWeak()) {
627 try module.failed_exports.putNoClobber(module.gpa, exp, try Module.ErrorMsg.create(
628 module.gpa,
629 decl.srcLoc(),
630 \\LinkError: symbol '{s}' defined multiple times
631 \\ first definition in '{s}'
632 \\ next definition in '{s}'
633 ,
634 .{ exp.options.name, self.name, self.name },
635 ));
636 } else if (exp_is_weak) {
637 continue; // to-be-exported symbol is weak, so we keep the existing symbol
638 } else {
639 existing_loc.index = decl.link.wasm.sym_index;
640 existing_loc.file = null;
641 exp.link.wasm.sym_index = existing_loc.index;
642 }
643 }
644
645 const sym_index = exp.exported_decl.link.wasm.sym_index;
646 const sym_loc = exp.exported_decl.link.wasm.symbolLoc();
647 const symbol = sym_loc.getSymbol(self);
648 switch (exp.options.linkage) {
649 .Internal => {
650 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
651 symbol.setFlag(.WASM_SYM_BINDING_WEAK);
652 },
653 .Weak => {
654 symbol.setFlag(.WASM_SYM_BINDING_WEAK);
655 },
656 .Strong => {}, // symbols are strong by default
657 .LinkOnce => {
658 try module.failed_exports.putNoClobber(module.gpa, exp, try Module.ErrorMsg.create(
659 module.gpa,
660 decl.srcLoc(),
661 "Unimplemented: LinkOnce",
662 .{},
663 ));
664 continue;
665 },
666 }
667 // Ensure the symbol will be exported using the given name
668 if (!mem.eql(u8, exp.options.name, mem.sliceTo(exp.exported_decl.name, 0))) {
669 try self.export_names.put(self.base.allocator, sym_loc, exp.options.name);
670 }
671
672 symbol.setGlobal(true);
673 try self.globals.put(
674 self.base.allocator,
675 exp.options.name,
676 sym_loc,
677 );
678
679 // if the symbol was previously undefined, remove it as an import
680 _ = self.imports.remove(sym_loc);
681 exp.link.wasm.sym_index = sym_index;
682 }
584683}
585684
586685pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void {
......@@ -596,12 +695,13 @@ pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void {
596695 local_symbol.tag = .dead; // also for any local symbol
597696 self.base.allocator.free(mem.sliceTo(local_symbol.name, 0));
598697 self.symbols_free_list.append(self.base.allocator, local_atom.sym_index) catch {};
698 assert(self.resolved_symbols.swapRemove(local_atom.symbolLoc()));
599699 }
600700
601701 if (decl.isExtern()) {
602 assert(self.imports.remove(.{ .file = null, .index = atom.sym_index }));
702 assert(self.imports.remove(atom.symbolLoc()));
603703 }
604 assert(self.resolved_symbols.swapRemove(.{ .index = atom.sym_index, .file = null }));
704 assert(self.resolved_symbols.swapRemove(atom.symbolLoc()));
605705 atom.deinit(self.base.allocator);
606706}
607707
......@@ -627,6 +727,7 @@ fn addOrUpdateImport(self: *Wasm, decl: *Module.Decl) !void {
627727 const symbol: *Symbol = &self.symbols.items[symbol_index];
628728 symbol.name = decl.name;
629729 symbol.setUndefined(true);
730 symbol.setGlobal(true);
630731 try self.globals.putNoClobber(
631732 self.base.allocator,
632733 mem.sliceTo(symbol.name, 0),
......@@ -778,6 +879,7 @@ fn setupImports(self: *Wasm) !void {
778879 }
779880 }
780881 }
882
781883 for (self.resolved_symbols.keys()) |symbol_loc| {
782884 if (symbol_loc.file == null) {
783885 // imports generated by Zig code are already in the `import` section
......@@ -916,23 +1018,20 @@ fn mergeTypes(self: *Wasm) !void {
9161018}
9171019
9181020fn setupExports(self: *Wasm) !void {
1021 if (self.base.options.output_mode == .Obj) return;
9191022 log.debug("Building exports from symbols", .{});
9201023
921 // When importing memory option if false, we export it instead
922 if (!self.base.options.import_memory) {
923 try self.exports.append(self.base.allocator, .{ .name = "memory", .kind = .memory, .index = 0 });
924 }
925
9261024 for (self.resolved_symbols.keys()) |sym_loc| {
9271025 const symbol = sym_loc.getSymbol(self);
9281026 if (!symbol.isExported()) continue;
9291027
1028 const export_name = if (self.export_names.get(sym_loc)) |name| name else mem.sliceTo(symbol.name, 0);
9301029 const exp: wasm.Export = .{
931 .name = mem.sliceTo(symbol.name, 0),
1030 .name = export_name,
9321031 .kind = symbol.tag.externalType(),
9331032 .index = symbol.index,
9341033 };
935 log.debug("Appending export for symbol '{s}' at index: ({d})", .{ exp.name, exp.index });
1034 log.debug("Exporting symbol '{s}' as '{s}' at index: ({d})", .{ symbol.name, exp.name, exp.index });
9361035 try self.exports.append(self.base.allocator, exp);
9371036 }
9381037
......@@ -959,7 +1058,9 @@ fn setupStart(self: *Wasm) !void {
9591058 }
9601059
9611060 // Ensure the symbol is exported so host environment can access it
962 symbol.setFlag(.WASM_SYM_EXPORTED);
1061 if (self.base.options.output_mode != .Obj) {
1062 symbol.setFlag(.WASM_SYM_EXPORTED);
1063 }
9631064}
9641065
9651066/// Sets up the memory section of the wasm module, as well as the stack.
......@@ -1093,10 +1194,12 @@ fn resetState(self: *Wasm) void {
10931194 atom.prev = null;
10941195 }
10951196 self.functions.clearRetainingCapacity();
1197 self.exports.clearRetainingCapacity();
10961198 self.segments.clearRetainingCapacity();
10971199 self.segment_info.clearRetainingCapacity();
10981200 self.data_segments.clearRetainingCapacity();
10991201 self.atoms.clearRetainingCapacity();
1202 self.symbol_atom.clearRetainingCapacity();
11001203 self.code_section_index = null;
11011204}
11021205
......@@ -1121,6 +1224,13 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
11211224 const tracy = trace(@src());
11221225 defer tracy.end();
11231226
1227 // The amount of sections that will be written
1228 var section_count: u32 = 0;
1229 // Index of the code section. Used to tell relocation table where the section lives.
1230 var code_section_index: ?u32 = null;
1231 // Index of the data section. Used to tell relocation table where the section lives.
1232 var data_section_index: ?u32 = null;
1233
11241234 // Used for all temporary memory allocated during flushin
11251235 var arena_instance = std.heap.ArenaAllocator.init(self.base.allocator);
11261236 defer arena_instance.deinit();
......@@ -1148,6 +1258,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
11481258 // When we finish/error we reset the state of the linker
11491259 // So we can rebuild the binary file on each incremental update
11501260 defer self.resetState();
1261 try self.setupStart();
11511262 try self.setupImports();
11521263 var decl_it = self.decls.keyIterator();
11531264 while (decl_it.next()) |decl| {
......@@ -1186,7 +1297,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
11861297 try file.seekTo(@sizeOf(@TypeOf(wasm.magic ++ wasm.version)));
11871298
11881299 // Type section
1189 {
1300 if (self.func_types.items.len != 0) {
11901301 const header_offset = try reserveVecSectionHeader(file);
11911302 const writer = file.writer();
11921303 log.debug("Writing type section. Count: ({d})", .{self.func_types.items.len});
......@@ -1205,11 +1316,12 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
12051316 @intCast(u32, (try file.getPos()) - header_offset - header_size),
12061317 @intCast(u32, self.func_types.items.len),
12071318 );
1319 section_count += 1;
12081320 }
12091321
12101322 // Import section
1211 const import_memory = self.base.options.import_memory;
1212 const import_table = self.base.options.import_table;
1323 const import_memory = self.base.options.import_memory or is_obj;
1324 const import_table = self.base.options.import_table or is_obj;
12131325 if (self.imports.count() != 0 or import_memory or import_table) {
12141326 const header_offset = try reserveVecSectionHeader(file);
12151327 const writer = file.writer();
......@@ -1242,7 +1354,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
12421354 if (import_memory) {
12431355 const mem_imp: wasm.Import = .{
12441356 .module_name = self.host_name,
1245 .name = "memory",
1357 .name = "__linear_memory",
12461358 .kind = .{ .memory = self.memories.limits },
12471359 };
12481360 try emitImport(writer, mem_imp);
......@@ -1255,10 +1367,11 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
12551367 @intCast(u32, (try file.getPos()) - header_offset - header_size),
12561368 @intCast(u32, self.imports.count() + @boolToInt(import_memory) + @boolToInt(import_table)),
12571369 );
1370 section_count += 1;
12581371 }
12591372
12601373 // Function section
1261 {
1374 if (self.functions.items.len != 0) {
12621375 const header_offset = try reserveVecSectionHeader(file);
12631376 const writer = file.writer();
12641377 for (self.functions.items) |function| {
......@@ -1272,11 +1385,12 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
12721385 @intCast(u32, (try file.getPos()) - header_offset - header_size),
12731386 @intCast(u32, self.functions.items.len),
12741387 );
1388 section_count += 1;
12751389 }
12761390
12771391 // Table section
12781392 const export_table = self.base.options.export_table;
1279 if (!import_table) {
1393 if (!import_table and self.function_table.count() != 0) {
12801394 const header_offset = try reserveVecSectionHeader(file);
12811395 const writer = file.writer();
12821396
......@@ -1293,10 +1407,11 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
12931407 @intCast(u32, (try file.getPos()) - header_offset - header_size),
12941408 @as(u32, 1),
12951409 );
1410 section_count += 1;
12961411 }
12971412
12981413 // Memory section
1299 if (!self.base.options.import_memory) {
1414 if (!import_memory) {
13001415 const header_offset = try reserveVecSectionHeader(file);
13011416 const writer = file.writer();
13021417
......@@ -1308,6 +1423,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
13081423 @intCast(u32, (try file.getPos()) - header_offset - header_size),
13091424 @as(u32, 1), // wasm currently only supports 1 linear memory segment
13101425 );
1426 section_count += 1;
13111427 }
13121428
13131429 // Global section (used to emit stack pointer)
......@@ -1328,43 +1444,18 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
13281444 @intCast(u32, (try file.getPos()) - header_offset - header_size),
13291445 @intCast(u32, self.wasm_globals.items.len),
13301446 );
1447 section_count += 1;
13311448 }
13321449
13331450 // Export section
1334 if (self.base.options.module) |module| {
1451 if (self.exports.items.len != 0 or export_table or !import_memory) {
13351452 const header_offset = try reserveVecSectionHeader(file);
13361453 const writer = file.writer();
1337 var count: u32 = 0;
1338 for (module.decl_exports.values()) |exports| {
1339 for (exports) |exprt| {
1340 // Export name length + name
1341 try leb.writeULEB128(writer, @intCast(u32, exprt.options.name.len));
1342 try writer.writeAll(exprt.options.name);
1343
1344 switch (exprt.exported_decl.ty.zigTypeTag()) {
1345 .Fn => {
1346 const target = exprt.exported_decl.link.wasm.sym_index;
1347 const target_symbol = self.symbols.items[target];
1348 assert(target_symbol.tag == .function);
1349 // Type of the export
1350 try writer.writeByte(wasm.externalKind(.function));
1351 // Exported function index
1352 try leb.writeULEB128(writer, target_symbol.index);
1353 },
1354 else => return error.TODOImplementNonFnDeclsForWasm,
1355 }
1356
1357 count += 1;
1358 }
1359 }
1360
1361 // export memory if size is not 0
1362 if (!import_memory) {
1363 try leb.writeULEB128(writer, @intCast(u32, "memory".len));
1364 try writer.writeAll("memory");
1365 try writer.writeByte(wasm.externalKind(.memory));
1366 try leb.writeULEB128(writer, @as(u32, 0)); // only 1 memory 'object' can exist
1367 count += 1;
1454 for (self.exports.items) |exp| {
1455 try leb.writeULEB128(writer, @intCast(u32, exp.name.len));
1456 try writer.writeAll(exp.name);
1457 try leb.writeULEB128(writer, @enumToInt(exp.kind));
1458 try leb.writeULEB128(writer, exp.index);
13681459 }
13691460
13701461 if (export_table) {
......@@ -1372,7 +1463,13 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
13721463 try writer.writeAll("__indirect_function_table");
13731464 try writer.writeByte(wasm.externalKind(.table));
13741465 try leb.writeULEB128(writer, @as(u32, 0)); // function table is always the first table
1375 count += 1;
1466 }
1467
1468 if (!import_memory) {
1469 try leb.writeULEB128(writer, @intCast(u32, "memory".len));
1470 try writer.writeAll("memory");
1471 try writer.writeByte(wasm.externalKind(.memory));
1472 try leb.writeULEB128(writer, @as(u32, 0));
13761473 }
13771474
13781475 try writeVecSectionHeader(
......@@ -1380,8 +1477,9 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
13801477 header_offset,
13811478 .@"export",
13821479 @intCast(u32, (try file.getPos()) - header_offset - header_size),
1383 count,
1480 @intCast(u32, self.exports.items.len) + @boolToInt(export_table) + @boolToInt(!import_memory),
13841481 );
1482 section_count += 1;
13851483 }
13861484
13871485 // element section (function table)
......@@ -1407,6 +1505,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
14071505 @intCast(u32, (try file.getPos()) - header_offset - header_size),
14081506 @as(u32, 1),
14091507 );
1508 section_count += 1;
14101509 }
14111510
14121511 // Code section
......@@ -1429,6 +1528,8 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
14291528 @intCast(u32, (try file.getPos()) - header_offset - header_size),
14301529 @intCast(u32, self.functions.items.len),
14311530 );
1531 code_section_index = section_count;
1532 section_count += 1;
14321533 }
14331534
14341535 // Data section
......@@ -1493,6 +1594,8 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
14931594 @intCast(u32, (try file.getPos()) - header_offset - header_size),
14941595 @intCast(u32, segment_count),
14951596 );
1597 data_section_index = section_count;
1598 section_count += 1;
14961599 }
14971600
14981601 if (is_obj) {
......@@ -1501,8 +1604,12 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
15011604 // This means that for a relocatable object file, we need to generate one and provide it to the relocation sections.
15021605 var symbol_table = std.AutoArrayHashMap(SymbolLoc, u32).init(arena);
15031606 try self.emitLinkSection(file, arena, &symbol_table);
1504 try self.emitCodeRelocations(file, arena, 6, symbol_table);
1505 try self.emitDataRelocations(file, arena, 7, symbol_table);
1607 if (code_section_index) |code_index| {
1608 try self.emitCodeRelocations(file, arena, code_index, symbol_table);
1609 }
1610 if (data_section_index) |data_index| {
1611 try self.emitDataRelocations(file, arena, data_index, symbol_table);
1612 }
15061613 } else {
15071614 try self.emitNameSection(file, arena);
15081615 }
......@@ -1563,6 +1670,7 @@ fn emitNameSubsection(self: *Wasm, section_id: std.wasm.NameSubsection, names: a
15631670
15641671 try leb.writeULEB128(sub_writer, @intCast(u32, names.len));
15651672 for (names) |name| {
1673 log.debug("Emit symbol '{s}' type({s})", .{ name.name, @tagName(section_id) });
15661674 try leb.writeULEB128(sub_writer, name.index);
15671675 try leb.writeULEB128(sub_writer, @intCast(u32, name.name.len));
15681676 try sub_writer.writeAll(name.name);
......@@ -2194,6 +2302,18 @@ fn emitSegmentInfo(self: *Wasm, file: fs.File, arena: Allocator) !void {
21942302 try file.writevAll(&.{iovec});
21952303}
21962304
2305fn getULEB128Size(uint_value: anytype) u32 {
2306 const T = @TypeOf(uint_value);
2307 const U = if (@typeInfo(T).Int.bits < 8) u8 else T;
2308 var value = @intCast(U, uint_value);
2309
2310 var size: u32 = 0;
2311 while (value != 0) : (size += 1) {
2312 value >>= 7;
2313 }
2314 return size;
2315}
2316
21972317/// For each relocatable section, emits a custom "relocation.<section_name>" section
21982318fn emitCodeRelocations(
21992319 self: *Wasm,
......@@ -2215,17 +2335,22 @@ fn emitCodeRelocations(
22152335
22162336 var count: u32 = 0;
22172337 var atom: *Atom = self.atoms.get(code_index).?.getFirst();
2338 // for each atom, we calculate the uleb size and append that
2339 var size_offset: u32 = 5; // account for code section size leb128
22182340 while (true) {
2341 size_offset += getULEB128Size(atom.size);
22192342 for (atom.relocs.items) |relocation| {
22202343 count += 1;
22212344 const sym_loc: SymbolLoc = .{ .file = atom.file, .index = relocation.index };
22222345 const symbol_index = symbol_table.get(sym_loc).?;
22232346 try leb.writeULEB128(writer, @enumToInt(relocation.relocation_type));
2224 try leb.writeULEB128(writer, atom.offset + relocation.offset);
2347 const offset = atom.offset + relocation.offset + size_offset;
2348 try leb.writeULEB128(writer, offset);
22252349 try leb.writeULEB128(writer, symbol_index);
22262350 if (relocation.relocation_type.addendIsPresent()) {
22272351 try leb.writeULEB128(writer, relocation.addend orelse 0);
22282352 }
2353 log.debug("Emit relocation: {}", .{relocation});
22292354 }
22302355 atom = atom.next orelse break;
22312356 }
......@@ -2262,9 +2387,12 @@ fn emitDataRelocations(
22622387 const reloc_start = payload.items.len;
22632388
22642389 var count: u32 = 0;
2390 // for each atom, we calculate the uleb size and append that
2391 var size_offset: u32 = 5; // account for code section size leb128
22652392 for (self.data_segments.values()) |segment_index| {
22662393 var atom: *Atom = self.atoms.get(segment_index).?.getFirst();
22672394 while (true) {
2395 size_offset += getULEB128Size(atom.size);
22682396 for (atom.relocs.items) |relocation| {
22692397 count += 1;
22702398 const sym_loc: SymbolLoc = .{
......@@ -2273,11 +2401,13 @@ fn emitDataRelocations(
22732401 };
22742402 const symbol_index = symbol_table.get(sym_loc).?;
22752403 try leb.writeULEB128(writer, @enumToInt(relocation.relocation_type));
2276 try leb.writeULEB128(writer, atom.offset + relocation.offset);
2404 const offset = atom.offset + relocation.offset + size_offset;
2405 try leb.writeULEB128(writer, offset);
22772406 try leb.writeULEB128(writer, symbol_index);
22782407 if (relocation.relocation_type.addendIsPresent()) {
22792408 try leb.writeULEB128(writer, relocation.addend orelse 0);
22802409 }
2410 log.debug("Emit relocation: {}", .{relocation});
22812411 }
22822412 atom = atom.next orelse break;
22832413 }
src/link/Wasm/Atom.zig-1
......@@ -173,7 +173,6 @@ fn relocationValue(self: Atom, relocation: types.Relocation, wasm_bin: *const Wa
173173 if (symbol.isUndefined() and (symbol.tag == .data or symbol.isWeak())) {
174174 return 0;
175175 }
176
177176 const merge_segment = wasm_bin.base.options.output_mode != .Obj;
178177 const segment_name = wasm_bin.segment_info.items[symbol.index].outputName(merge_segment);
179178 const atom_index = wasm_bin.data_segments.get(segment_name).?;
src/link/Wasm/Symbol.zig+8
......@@ -104,6 +104,14 @@ pub fn setUndefined(self: *Symbol, is_undefined: bool) void {
104104 }
105105}
106106
107pub fn setGlobal(self: *Symbol, is_global: bool) void {
108 if (is_global) {
109 self.flags &= ~@enumToInt(Flag.WASM_SYM_BINDING_LOCAL);
110 } else {
111 self.setFlag(.WASM_SYM_BINDING_LOCAL);
112 }
113}
114
107115pub fn isDefined(self: Symbol) bool {
108116 return !self.isUndefined();
109117}