authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2023-03-12 15:23:58+01:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2023-03-18 20:13:25+01:00
logb0024c48841b78a962918cd4ab20459ba6451050
tree55a213e4ee740d77306ae1c4ac769bb6f1a586c9
parent49d37e2d179948f526f500043c6ea9ae324e9476
signaturelock-open Commit is signed but in an unrecognized format.

wasm-linker: basic TLS support

Linker now parses segments with regards to TLS segments. If the name represents a TLS segment but does not contain the TLS flag, we set it manually as the object file is created using an older compiler (LLVM). For now we panic when we find a TLS relocation and implement those later.

5 files changed, 57 insertions(+), 8 deletions(-)

src/link/Wasm.zig+19
...@@ -894,6 +894,13 @@ fn resolveLazySymbols(wasm: *Wasm) !void {...@@ -894,6 +894,13 @@ fn resolveLazySymbols(wasm: *Wasm) !void {
894 try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc);894 try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc);
895 _ = wasm.resolved_symbols.swapRemove(loc);895 _ = wasm.resolved_symbols.swapRemove(loc);
896 }896 }
897
898 if (!wasm.base.options.shared_memory) {
899 if (wasm.undefs.fetchSwapRemove("__tls_base")) |kv| {
900 const loc = try wasm.createSyntheticSymbol("__tls_base", .global);
901 try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc);
902 }
903 }
897}904}
898905
899// Tries to find a global symbol by its name. Returns null when not found,906// Tries to find a global symbol by its name. Returns null when not found,
...@@ -2224,6 +2231,18 @@ fn setupMemory(wasm: *Wasm) !void {...@@ -2224,6 +2231,18 @@ fn setupMemory(wasm: *Wasm) !void {
2224 while (data_seg_it.next()) |entry| {2231 while (data_seg_it.next()) |entry| {
2225 const segment = &wasm.segments.items[entry.value_ptr.*];2232 const segment = &wasm.segments.items[entry.value_ptr.*];
2226 memory_ptr = std.mem.alignForwardGeneric(u64, memory_ptr, segment.alignment);2233 memory_ptr = std.mem.alignForwardGeneric(u64, memory_ptr, segment.alignment);
2234
2235 // set TLS-related symbols
2236 if (mem.eql(u8, entry.key_ptr.*, ".tdata")) {
2237 if (wasm.findGlobalSymbol("__tls_base")) |loc| {
2238 const sym = loc.getSymbol(wasm);
2239 sym.index = try wasm.globals.append(wasm.base.allocator, wasm.imports.globalCount, .{
2240 .global_type = .{ .valtype = .i32_const, .mutable = false },
2241 .init = .{ .i32_const = @intCast(i32, memory_ptr) },
2242 });
2243 }
2244 }
2245
2227 memory_ptr += segment.size;2246 memory_ptr += segment.size;
2228 segment.offset = offset;2247 segment.offset = offset;
2229 offset += segment.size;2248 offset += segment.size;
src/link/Wasm/Atom.zig+7
...@@ -126,10 +126,12 @@ pub fn resolveRelocs(atom: *Atom, wasm_bin: *const Wasm) void {...@@ -126,10 +126,12 @@ pub fn resolveRelocs(atom: *Atom, wasm_bin: *const Wasm) void {
126 .R_WASM_TABLE_INDEX_SLEB,126 .R_WASM_TABLE_INDEX_SLEB,
127 .R_WASM_TABLE_NUMBER_LEB,127 .R_WASM_TABLE_NUMBER_LEB,
128 .R_WASM_TYPE_INDEX_LEB,128 .R_WASM_TYPE_INDEX_LEB,
129 .R_WASM_MEMORY_ADDR_TLS_SLEB,
129 => leb.writeUnsignedFixed(5, atom.code.items[reloc.offset..][0..5], @intCast(u32, value)),130 => leb.writeUnsignedFixed(5, atom.code.items[reloc.offset..][0..5], @intCast(u32, value)),
130 .R_WASM_MEMORY_ADDR_LEB64,131 .R_WASM_MEMORY_ADDR_LEB64,
131 .R_WASM_MEMORY_ADDR_SLEB64,132 .R_WASM_MEMORY_ADDR_SLEB64,
132 .R_WASM_TABLE_INDEX_SLEB64,133 .R_WASM_TABLE_INDEX_SLEB64,
134 .R_WASM_MEMORY_ADDR_TLS_SLEB64,
133 => leb.writeUnsignedFixed(10, atom.code.items[reloc.offset..][0..10], value),135 => leb.writeUnsignedFixed(10, atom.code.items[reloc.offset..][0..10], value),
134 }136 }
135 }137 }
...@@ -190,5 +192,10 @@ fn relocationValue(atom: Atom, relocation: types.Relocation, wasm_bin: *const Wa...@@ -190,5 +192,10 @@ fn relocationValue(atom: Atom, relocation: types.Relocation, wasm_bin: *const Wa
190 const rel_value = @intCast(i32, target_atom.offset + offset) + relocation.addend;192 const rel_value = @intCast(i32, target_atom.offset + offset) + relocation.addend;
191 return @intCast(u32, rel_value);193 return @intCast(u32, rel_value);
192 },194 },
195 .R_WASM_MEMORY_ADDR_TLS_SLEB,
196 .R_WASM_MEMORY_ADDR_TLS_SLEB64,
197 => {
198 @panic("TODO: Implement TLS relocations");
199 },
193 }200 }
194}201}
src/link/Wasm/Object.zig+6
...@@ -674,6 +674,12 @@ fn Parser(comptime ReaderType: type) type {...@@ -674,6 +674,12 @@ fn Parser(comptime ReaderType: type) type {
674 segment.alignment,674 segment.alignment,
675 segment.flags,675 segment.flags,
676 });676 });
677
678 // support legacy object files that specified being TLS by the name instead of the TLS flag.
679 if (!segment.isTLS() and (std.mem.startsWith(u8, segment.name, ".tdata") or std.mem.startsWith(u8, segment.name, ".tbss"))) {
680 // set the flag so we can simply check for the flag in the rest of the linker.
681 segment.flags |= @enumToInt(types.Segment.Flags.WASM_SEG_FLAG_TLS);
682 }
677 }683 }
678 parser.object.segment_info = segments;684 parser.object.segment_info = segments;
679 },685 },
src/link/Wasm/Symbol.zig+4
...@@ -90,6 +90,10 @@ pub fn requiresImport(symbol: Symbol) bool {...@@ -90,6 +90,10 @@ pub fn requiresImport(symbol: Symbol) bool {
90 return true;90 return true;
91}91}
9292
93pub fn isTLS(symbol: Symbol) bool {
94 return symbol.flags & @enumToInt(Flag.WASM_SYM_TLS) != 0;
95}
96
93pub fn hasFlag(symbol: Symbol, flag: Flag) bool {97pub fn hasFlag(symbol: Symbol, flag: Flag) bool {
94 return symbol.flags & @enumToInt(flag) != 0;98 return symbol.flags & @enumToInt(flag) != 0;
95}99}
src/link/Wasm/types.zig+21-8
...@@ -38,6 +38,8 @@ pub const Relocation = struct {...@@ -38,6 +38,8 @@ pub const Relocation = struct {
38 R_WASM_TABLE_INDEX_SLEB64 = 18,38 R_WASM_TABLE_INDEX_SLEB64 = 18,
39 R_WASM_TABLE_INDEX_I64 = 19,39 R_WASM_TABLE_INDEX_I64 = 19,
40 R_WASM_TABLE_NUMBER_LEB = 20,40 R_WASM_TABLE_NUMBER_LEB = 20,
41 R_WASM_MEMORY_ADDR_TLS_SLEB = 21,
42 R_WASM_MEMORY_ADDR_TLS_SLEB64 = 25,
4143
42 /// Returns true for relocation types where the `addend` field is present.44 /// Returns true for relocation types where the `addend` field is present.
43 pub fn addendIsPresent(self: RelocationType) bool {45 pub fn addendIsPresent(self: RelocationType) bool {
...@@ -125,23 +127,34 @@ pub const Segment = struct {...@@ -125,23 +127,34 @@ pub const Segment = struct {
125 /// Bitfield containing flags for a segment127 /// Bitfield containing flags for a segment
126 flags: u32,128 flags: u32,
127129
130 pub fn isTLS(segment: Segment) bool {
131 return segment.flags & @enumToInt(Flags.WASM_SEG_FLAG_TLS) != 0;
132 }
133
128 /// Returns the name as how it will be output into the final object134 /// Returns the name as how it will be output into the final object
129 /// file or binary. When `merge_segments` is true, this will return the135 /// file or binary. When `merge_segments` is true, this will return the
130 /// short name. i.e. ".rodata". When false, it returns the entire name instead.136 /// short name. i.e. ".rodata". When false, it returns the entire name instead.
131 pub fn outputName(self: Segment, merge_segments: bool) []const u8 {137 pub fn outputName(segment: Segment, merge_segments: bool) []const u8 {
132 if (std.mem.startsWith(u8, self.name, ".synthetic")) return ".synthetic"; // always merge138 if (segment.isTLS()) {
133 if (!merge_segments) return self.name;139 return ".tdata";
134 if (std.mem.startsWith(u8, self.name, ".rodata.")) {140 } else if (!merge_segments) {
141 return segment.name;
142 } else if (std.mem.startsWith(u8, segment.name, ".rodata.")) {
135 return ".rodata";143 return ".rodata";
136 } else if (std.mem.startsWith(u8, self.name, ".text.")) {144 } else if (std.mem.startsWith(u8, segment.name, ".text.")) {
137 return ".text";145 return ".text";
138 } else if (std.mem.startsWith(u8, self.name, ".data.")) {146 } else if (std.mem.startsWith(u8, segment.name, ".data.")) {
139 return ".data";147 return ".data";
140 } else if (std.mem.startsWith(u8, self.name, ".bss.")) {148 } else if (std.mem.startsWith(u8, segment.name, ".bss.")) {
141 return ".bss";149 return ".bss";
142 }150 }
143 return self.name;151 return segment.name;
144 }152 }
153
154 pub const Flags = enum(u32) {
155 WASM_SEG_FLAG_STRINGS = 0x1,
156 WASM_SEG_FLAG_TLS = 0x2,
157 };
145};158};
146159
147pub const InitFunc = struct {160pub const InitFunc = struct {