aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuuk de Gram <luuk@degram.dev>2023-03-12 15:23:58 +0100
committerLuuk de Gram <luuk@degram.dev>2023-03-18 20:13:25 +0100
commitb0024c48841b78a962918cd4ab20459ba6451050 (patch)
tree55a213e4ee740d77306ae1c4ac769bb6f1a586c9
parent49d37e2d179948f526f500043c6ea9ae324e9476 (diff)
downloadzig-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.zig19
-rw-r--r--src/link/Wasm/Atom.zig7
-rw-r--r--src/link/Wasm/Object.zig6
-rw-r--r--src/link/Wasm/Symbol.zig4
-rw-r--r--src/link/Wasm/types.zig29
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 {