authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2023-03-17 06:32:37+01:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2023-03-18 20:13:30+01:00
log9fce1df4cdab57951137c0da2b44fbe6da2442f2
tree126a0e54a9e07ae9bf5b791d3faeec09c76594fa
parent9d13c2257dcae11d9bc69035e55c33a7dda14a2b
signaturelock-open Commit is signed but in an unrecognized format.

wasm-linker: implement runtime TLS relocations


3 files changed, 86 insertions(+), 17 deletions(-)

src/link/Wasm.zig+63
......@@ -139,6 +139,8 @@ archives: std.ArrayListUnmanaged(Archive) = .{},
139139
140140/// A map of global names (read: offset into string table) to their symbol location
141141globals: std.AutoHashMapUnmanaged(u32, SymbolLoc) = .{},
142/// The list of GOT symbols and their location
143got_symbols: std.ArrayListUnmanaged(SymbolLoc) = .{},
142144/// Maps discarded symbols and their positions to the location of the symbol
143145/// it was resolved to
144146discarded: std.AutoHashMapUnmanaged(SymbolLoc, SymbolLoc) = .{},
......@@ -635,6 +637,15 @@ fn parseArchive(wasm: *Wasm, path: []const u8, force_load: bool) !bool {
635637 return true;
636638}
637639
640fn requiresTLSReloc(wasm: *const Wasm) bool {
641 for (wasm.got_symbols.items) |loc| {
642 if (loc.getSymbol(wasm).isTLS()) {
643 return true;
644 }
645 }
646 return false;
647}
648
638649fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void {
639650 const object: Object = wasm.objects.items[object_index];
640651 log.debug("Resolving symbols in object: '{s}'", .{object.name});
......@@ -813,6 +824,48 @@ fn resolveSymbolsInArchives(wasm: *Wasm) !void {
813824 }
814825}
815826
827fn setupTLSRelocationsFunction(wasm: *Wasm) !void {
828 // When we have TLS GOT entries and shared memory is enabled,
829 // we must perform runtime relocations or else we don't create the function.
830 if (!wasm.base.options.shared_memory or !wasm.requiresTLSReloc()) {
831 return;
832 }
833
834 // const loc = try wasm.createSyntheticSymbol("__wasm_apply_global_tls_relocs");
835 var function_body = std.ArrayList(u8).init(wasm.base.allocator);
836 defer function_body.deinit();
837 const writer = function_body.writer();
838
839 // locals (we have none)
840 try writer.writeByte(0);
841 for (wasm.got_symbols.items, 0..) |got_loc, got_index| {
842 const sym: *Symbol = got_loc.getSymbol(wasm);
843 if (!sym.isTLS()) continue; // only relocate TLS symbols
844 if (sym.tag == .data and sym.isDefined()) {
845 // get __tls_base
846 try writer.writeByte(std.wasm.opcode(.global_get));
847 try leb.writeULEB128(writer, wasm.findGlobalSymbol("__tls_base").?.getSymbol(wasm).index);
848
849 // add the virtual address of the symbol
850 try writer.writeByte(std.wasm.opcode(.i32_const));
851 try leb.writeULEB128(writer, sym.virtual_address);
852 } else if (sym.tag == .function) {
853 @panic("TODO: relocate GOT entry of function");
854 } else continue;
855
856 try writer.writeByte(std.wasm.opcode(.i32_add));
857 try writer.writeByte(std.wasm.opcode(.global_set));
858 try leb.writeULEB128(writer, wasm.imported_globals_count + @intCast(u32, wasm.wasm_globals.items.len + got_index));
859 }
860 try writer.writeByte(std.wasm.opcode(.end));
861
862 try wasm.createSyntheticFunction(
863 "__wasm_apply_global_tls_relocs",
864 std.wasm.Type{ .params = &.{}, .returns = &.{} },
865 &function_body,
866 );
867}
868
816869fn validateFeatures(
817870 wasm: *const Wasm,
818871 to_emit: *[@typeInfo(types.Feature.Tag).Enum.fields.len]bool,
......@@ -2083,6 +2136,14 @@ fn initializeTLSFunction(wasm: *Wasm) !void {
20832136 try leb.writeULEB128(writer, @as(u32, 0));
20842137 }
20852138
2139 // If we have to perform any TLS relocations, call the corresponding function
2140 // which performs all runtime TLS relocations. This is a synthetic function,
2141 // generated by the linker.
2142 if (wasm.findGlobalSymbol("__wasm_apply_global_tls_relocs")) |loc| {
2143 try writer.writeByte(std.wasm.opcode(.call));
2144 try leb.writeULEB128(writer, loc.getSymbol(wasm).index);
2145 }
2146
20862147 try writer.writeByte(std.wasm.opcode(.end));
20872148
20882149 try wasm.createSyntheticFunction(
......@@ -2939,6 +3000,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l
29393000 try wasm.mergeSections();
29403001 try wasm.mergeTypes();
29413002 try wasm.initializeCallCtorsFunction();
3003 try wasm.setupTLSRelocationsFunction();
29423004 try wasm.initializeTLSFunction();
29433005 try wasm.setupExports();
29443006 try wasm.writeToFile(enabled_features, emit_features_count, arena);
......@@ -3059,6 +3121,7 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
30593121 try wasm.mergeSections();
30603122 try wasm.mergeTypes();
30613123 try wasm.initializeCallCtorsFunction();
3124 try wasm.setupTLSRelocationsFunction();
30623125 try wasm.initializeTLSFunction();
30633126 try wasm.setupExports();
30643127 try wasm.writeToFile(enabled_features, emit_features_count, arena);
src/link/Wasm/Object.zig+23-5
......@@ -930,11 +930,29 @@ pub fn parseIntoAtoms(object: *Object, gpa: Allocator, object_index: u16, wasm_b
930930 reloc.offset -= relocatable_data.offset;
931931 try atom.relocs.append(gpa, reloc);
932932
933 if (relocation.isTableIndex()) {
934 try wasm_bin.function_table.put(gpa, .{
935 .file = object_index,
936 .index = relocation.index,
937 }, 0);
933 switch (relocation.relocation_type) {
934 .R_WASM_TABLE_INDEX_I32,
935 .R_WASM_TABLE_INDEX_I64,
936 .R_WASM_TABLE_INDEX_SLEB,
937 .R_WASM_TABLE_INDEX_SLEB64,
938 => {
939 try wasm_bin.function_table.put(gpa, .{
940 .file = object_index,
941 .index = relocation.index,
942 }, 0);
943 },
944 .R_WASM_GLOBAL_INDEX_I32,
945 .R_WASM_GLOBAL_INDEX_LEB,
946 => {
947 const sym = object.symtable[relocation.index];
948 if (sym.tag != .global) {
949 try wasm_bin.got_symbols.append(
950 wasm_bin.base.allocator,
951 .{ .file = object_index, .index = relocation.index },
952 );
953 }
954 },
955 else => {},
938956 }
939957 }
940958 }
src/link/Wasm/types.zig-12
......@@ -71,18 +71,6 @@ pub const Relocation = struct {
7171 };
7272 }
7373
74 /// Returns true when the relocation represents a table index relocatable
75 pub fn isTableIndex(self: Relocation) bool {
76 return switch (self.relocation_type) {
77 .R_WASM_TABLE_INDEX_I32,
78 .R_WASM_TABLE_INDEX_I64,
79 .R_WASM_TABLE_INDEX_SLEB,
80 .R_WASM_TABLE_INDEX_SLEB64,
81 => true,
82 else => false,
83 };
84 }
85
8674 pub fn format(self: Relocation, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
8775 _ = fmt;
8876 _ = options;