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.
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
},
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user