diff options
| author | Luuk de Gram <luuk@degram.dev> | 2023-03-12 15:23:58 +0100 |
|---|---|---|
| committer | Luuk de Gram <luuk@degram.dev> | 2023-03-18 20:13:25 +0100 |
| commit | b0024c48841b78a962918cd4ab20459ba6451050 (patch) | |
| tree | 55a213e4ee740d77306ae1c4ac769bb6f1a586c9 | |
| parent | 49d37e2d179948f526f500043c6ea9ae324e9476 (diff) | |
| download | zig-b0024c48841b78a962918cd4ab20459ba6451050.tar.gz zig-b0024c48841b78a962918cd4ab20459ba6451050.zip | |
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.
| -rw-r--r-- | src/link/Wasm.zig | 19 | ||||
| -rw-r--r-- | src/link/Wasm/Atom.zig | 7 | ||||
| -rw-r--r-- | src/link/Wasm/Object.zig | 6 | ||||
| -rw-r--r-- | src/link/Wasm/Symbol.zig | 4 | ||||
| -rw-r--r-- | 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 e998a8d50e..287f880d92 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 0c9d761f05..7d2f5a6696 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 45c9464ec8..4b918064c1 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 156b507a32..8a1c4c5fdb 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 964ba04ba0..a65e352c46 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 { |
