From b0024c48841b78a962918cd4ab20459ba6451050 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Sun, 12 Mar 2023 15:23:58 +0100 Subject: [PATCH] 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. --- src/link/Wasm.zig | 19 +++++++++++++++++++ src/link/Wasm/Atom.zig | 7 +++++++ src/link/Wasm/Object.zig | 6 ++++++ src/link/Wasm/Symbol.zig | 4 ++++ src/link/Wasm/types.zig | 29 +++++++++++++++++++++-------- 5 files changed, 57 insertions(+), 8 deletions(-) diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index e998a8d50ed74b18e3b524fbae70911fec2f92ab..287f880d9299c3e7947a9cfbeb7c79b7c86ff776 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -894,6 +894,13 @@ fn resolveLazySymbols(wasm: *Wasm) !void { try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc); _ = wasm.resolved_symbols.swapRemove(loc); } + + if (!wasm.base.options.shared_memory) { + if (wasm.undefs.fetchSwapRemove("__tls_base")) |kv| { + const loc = try wasm.createSyntheticSymbol("__tls_base", .global); + try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc); + } + } } // Tries to find a global symbol by its name. Returns null when not found, @@ -2224,6 +2231,18 @@ fn setupMemory(wasm: *Wasm) !void { while (data_seg_it.next()) |entry| { const segment = &wasm.segments.items[entry.value_ptr.*]; memory_ptr = std.mem.alignForwardGeneric(u64, memory_ptr, segment.alignment); + + // set TLS-related symbols + if (mem.eql(u8, entry.key_ptr.*, ".tdata")) { + if (wasm.findGlobalSymbol("__tls_base")) |loc| { + const sym = loc.getSymbol(wasm); + sym.index = try wasm.globals.append(wasm.base.allocator, wasm.imports.globalCount, .{ + .global_type = .{ .valtype = .i32_const, .mutable = false }, + .init = .{ .i32_const = @intCast(i32, memory_ptr) }, + }); + } + } + memory_ptr += segment.size; segment.offset = offset; offset += segment.size; diff --git a/src/link/Wasm/Atom.zig b/src/link/Wasm/Atom.zig index 0c9d761f05ab3a67078e6d1b71129663065222e6..7d2f5a66965248eb1a094d3731eda9840a536321 100644 --- a/src/link/Wasm/Atom.zig +++ b/src/link/Wasm/Atom.zig @@ -126,10 +126,12 @@ pub fn resolveRelocs(atom: *Atom, wasm_bin: *const Wasm) void { .R_WASM_TABLE_INDEX_SLEB, .R_WASM_TABLE_NUMBER_LEB, .R_WASM_TYPE_INDEX_LEB, + .R_WASM_MEMORY_ADDR_TLS_SLEB, => leb.writeUnsignedFixed(5, atom.code.items[reloc.offset..][0..5], @intCast(u32, value)), .R_WASM_MEMORY_ADDR_LEB64, .R_WASM_MEMORY_ADDR_SLEB64, .R_WASM_TABLE_INDEX_SLEB64, + .R_WASM_MEMORY_ADDR_TLS_SLEB64, => leb.writeUnsignedFixed(10, atom.code.items[reloc.offset..][0..10], value), } } @@ -190,5 +192,10 @@ fn relocationValue(atom: Atom, relocation: types.Relocation, wasm_bin: *const Wa const rel_value = @intCast(i32, target_atom.offset + offset) + relocation.addend; return @intCast(u32, rel_value); }, + .R_WASM_MEMORY_ADDR_TLS_SLEB, + .R_WASM_MEMORY_ADDR_TLS_SLEB64, + => { + @panic("TODO: Implement TLS relocations"); + }, } } diff --git a/src/link/Wasm/Object.zig b/src/link/Wasm/Object.zig index 45c9464ec8ea295f9f23641313739a49b4c7010d..4b918064c1cb425b122d8325da82b07a2c963625 100644 --- a/src/link/Wasm/Object.zig +++ b/src/link/Wasm/Object.zig @@ -674,6 +674,12 @@ fn Parser(comptime ReaderType: type) type { segment.alignment, segment.flags, }); + + // support legacy object files that specified being TLS by the name instead of the TLS flag. + if (!segment.isTLS() and (std.mem.startsWith(u8, segment.name, ".tdata") or std.mem.startsWith(u8, segment.name, ".tbss"))) { + // set the flag so we can simply check for the flag in the rest of the linker. + segment.flags |= @enumToInt(types.Segment.Flags.WASM_SEG_FLAG_TLS); + } } parser.object.segment_info = segments; }, diff --git a/src/link/Wasm/Symbol.zig b/src/link/Wasm/Symbol.zig index 156b507a322d40345d11c828e2c35f94365505ef..8a1c4c5fdbb141f5e890350de6f7952e6df1614b 100644 --- a/src/link/Wasm/Symbol.zig +++ b/src/link/Wasm/Symbol.zig @@ -90,6 +90,10 @@ pub fn requiresImport(symbol: Symbol) bool { return true; } +pub fn isTLS(symbol: Symbol) bool { + return symbol.flags & @enumToInt(Flag.WASM_SYM_TLS) != 0; +} + pub fn hasFlag(symbol: Symbol, flag: Flag) bool { return symbol.flags & @enumToInt(flag) != 0; } diff --git a/src/link/Wasm/types.zig b/src/link/Wasm/types.zig index 964ba04ba06ce4482c8dd04d61901e9a575d4146..a65e352c4617e9a05508d95d026d324495c5d9aa 100644 --- a/src/link/Wasm/types.zig +++ b/src/link/Wasm/types.zig @@ -38,6 +38,8 @@ pub const Relocation = struct { R_WASM_TABLE_INDEX_SLEB64 = 18, R_WASM_TABLE_INDEX_I64 = 19, R_WASM_TABLE_NUMBER_LEB = 20, + R_WASM_MEMORY_ADDR_TLS_SLEB = 21, + R_WASM_MEMORY_ADDR_TLS_SLEB64 = 25, /// Returns true for relocation types where the `addend` field is present. pub fn addendIsPresent(self: RelocationType) bool { @@ -125,23 +127,34 @@ pub const Segment = struct { /// Bitfield containing flags for a segment flags: u32, + pub fn isTLS(segment: Segment) bool { + return segment.flags & @enumToInt(Flags.WASM_SEG_FLAG_TLS) != 0; + } + /// Returns the name as how it will be output into the final object /// file or binary. When `merge_segments` is true, this will return the /// short name. i.e. ".rodata". When false, it returns the entire name instead. - pub fn outputName(self: Segment, merge_segments: bool) []const u8 { - if (std.mem.startsWith(u8, self.name, ".synthetic")) return ".synthetic"; // always merge - if (!merge_segments) return self.name; - if (std.mem.startsWith(u8, self.name, ".rodata.")) { + pub fn outputName(segment: Segment, merge_segments: bool) []const u8 { + if (segment.isTLS()) { + return ".tdata"; + } else if (!merge_segments) { + return segment.name; + } else if (std.mem.startsWith(u8, segment.name, ".rodata.")) { return ".rodata"; - } else if (std.mem.startsWith(u8, self.name, ".text.")) { + } else if (std.mem.startsWith(u8, segment.name, ".text.")) { return ".text"; - } else if (std.mem.startsWith(u8, self.name, ".data.")) { + } else if (std.mem.startsWith(u8, segment.name, ".data.")) { return ".data"; - } else if (std.mem.startsWith(u8, self.name, ".bss.")) { + } else if (std.mem.startsWith(u8, segment.name, ".bss.")) { return ".bss"; } - return self.name; + return segment.name; } + + pub const Flags = enum(u32) { + WASM_SEG_FLAG_STRINGS = 0x1, + WASM_SEG_FLAG_TLS = 0x2, + }; }; pub const InitFunc = struct { -- 2.54.0