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 {
894894 try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc);
895895 _ = wasm.resolved_symbols.swapRemove(loc);
896896 }
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 }
897904}
898905
899906// Tries to find a global symbol by its name. Returns null when not found,
......@@ -2224,6 +2231,18 @@ fn setupMemory(wasm: *Wasm) !void {
22242231 while (data_seg_it.next()) |entry| {
22252232 const segment = &wasm.segments.items[entry.value_ptr.*];
22262233 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
22272246 memory_ptr += segment.size;
22282247 segment.offset = offset;
22292248 offset += segment.size;
src/link/Wasm/Atom.zig+7
......@@ -126,10 +126,12 @@ pub fn resolveRelocs(atom: *Atom, wasm_bin: *const Wasm) void {
126126 .R_WASM_TABLE_INDEX_SLEB,
127127 .R_WASM_TABLE_NUMBER_LEB,
128128 .R_WASM_TYPE_INDEX_LEB,
129 .R_WASM_MEMORY_ADDR_TLS_SLEB,
129130 => leb.writeUnsignedFixed(5, atom.code.items[reloc.offset..][0..5], @intCast(u32, value)),
130131 .R_WASM_MEMORY_ADDR_LEB64,
131132 .R_WASM_MEMORY_ADDR_SLEB64,
132133 .R_WASM_TABLE_INDEX_SLEB64,
134 .R_WASM_MEMORY_ADDR_TLS_SLEB64,
133135 => leb.writeUnsignedFixed(10, atom.code.items[reloc.offset..][0..10], value),
134136 }
135137 }
......@@ -190,5 +192,10 @@ fn relocationValue(atom: Atom, relocation: types.Relocation, wasm_bin: *const Wa
190192 const rel_value = @intCast(i32, target_atom.offset + offset) + relocation.addend;
191193 return @intCast(u32, rel_value);
192194 },
195 .R_WASM_MEMORY_ADDR_TLS_SLEB,
196 .R_WASM_MEMORY_ADDR_TLS_SLEB64,
197 => {
198 @panic("TODO: Implement TLS relocations");
199 },
193200 }
194201}
src/link/Wasm/Object.zig+6
......@@ -674,6 +674,12 @@ fn Parser(comptime ReaderType: type) type {
674674 segment.alignment,
675675 segment.flags,
676676 });
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 }
677683 }
678684 parser.object.segment_info = segments;
679685 },
src/link/Wasm/Symbol.zig+4
......@@ -90,6 +90,10 @@ pub fn requiresImport(symbol: Symbol) bool {
9090 return true;
9191}
9292
93pub fn isTLS(symbol: Symbol) bool {
94 return symbol.flags & @enumToInt(Flag.WASM_SYM_TLS) != 0;
95}
96
9397pub fn hasFlag(symbol: Symbol, flag: Flag) bool {
9498 return symbol.flags & @enumToInt(flag) != 0;
9599}
src/link/Wasm/types.zig+21-8
......@@ -38,6 +38,8 @@ pub const Relocation = struct {
3838 R_WASM_TABLE_INDEX_SLEB64 = 18,
3939 R_WASM_TABLE_INDEX_I64 = 19,
4040 R_WASM_TABLE_NUMBER_LEB = 20,
41 R_WASM_MEMORY_ADDR_TLS_SLEB = 21,
42 R_WASM_MEMORY_ADDR_TLS_SLEB64 = 25,
4143
4244 /// Returns true for relocation types where the `addend` field is present.
4345 pub fn addendIsPresent(self: RelocationType) bool {
......@@ -125,23 +127,34 @@ pub const Segment = struct {
125127 /// Bitfield containing flags for a segment
126128 flags: u32,
127129
130 pub fn isTLS(segment: Segment) bool {
131 return segment.flags & @enumToInt(Flags.WASM_SEG_FLAG_TLS) != 0;
132 }
133
128134 /// Returns the name as how it will be output into the final object
129135 /// file or binary. When `merge_segments` is true, this will return the
130136 /// short name. i.e. ".rodata". When false, it returns the entire name instead.
131 pub fn outputName(self: Segment, merge_segments: bool) []const u8 {
132 if (std.mem.startsWith(u8, self.name, ".synthetic")) return ".synthetic"; // always merge
133 if (!merge_segments) return self.name;
134 if (std.mem.startsWith(u8, self.name, ".rodata.")) {
137 pub fn outputName(segment: Segment, merge_segments: bool) []const u8 {
138 if (segment.isTLS()) {
139 return ".tdata";
140 } else if (!merge_segments) {
141 return segment.name;
142 } else if (std.mem.startsWith(u8, segment.name, ".rodata.")) {
135143 return ".rodata";
136 } else if (std.mem.startsWith(u8, self.name, ".text.")) {
144 } else if (std.mem.startsWith(u8, segment.name, ".text.")) {
137145 return ".text";
138 } else if (std.mem.startsWith(u8, self.name, ".data.")) {
146 } else if (std.mem.startsWith(u8, segment.name, ".data.")) {
139147 return ".data";
140 } else if (std.mem.startsWith(u8, self.name, ".bss.")) {
148 } else if (std.mem.startsWith(u8, segment.name, ".bss.")) {
141149 return ".bss";
142150 }
143 return self.name;
151 return segment.name;
144152 }
153
154 pub const Flags = enum(u32) {
155 WASM_SEG_FLAG_STRINGS = 0x1,
156 WASM_SEG_FLAG_TLS = 0x2,
157 };
145158};
146159
147160pub const InitFunc = struct {