Skip to content

Commit 6fd1c64

Browse files
authored
Merge pull request #17978 from ziglang/elf-x86-tls
x86_64+elf: TLS support
2 parents f8b38a1 + ea3f1d2 commit 6fd1c64

File tree

11 files changed

+349
-132
lines changed

11 files changed

+349
-132
lines changed

lib/std/math/big/int_test.zig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1443,6 +1443,9 @@ test "big.int divFloor #11166" {
14431443
}
14441444

14451445
test "big.int gcd #10932" {
1446+
// TODO https://github.com/ziglang/zig/issues/17998
1447+
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
1448+
14461449
var a = try Managed.init(testing.allocator);
14471450
defer a.deinit();
14481451

src/arch/x86_64/CodeGen.zig

Lines changed: 46 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1107,6 +1107,7 @@ fn formatWipMir(
11071107
.cc = .Unspecified,
11081108
.src_loc = data.self.src_loc,
11091109
};
1110+
var first = true;
11101111
for ((lower.lowerMir(data.inst) catch |err| switch (err) {
11111112
error.LowerFail => {
11121113
defer {
@@ -1125,7 +1126,11 @@ fn formatWipMir(
11251126
return;
11261127
},
11271128
else => |e| return e,
1128-
}).insts) |lowered_inst| try writer.print(" | {}", .{lowered_inst});
1129+
}).insts) |lowered_inst| {
1130+
if (!first) try writer.writeAll("\ndebug(wip_mir): ");
1131+
try writer.print(" | {}", .{lowered_inst});
1132+
first = false;
1133+
}
11291134
}
11301135
fn fmtWipMir(self: *Self, inst: Mir.Inst.Index) std.fmt.Formatter(formatWipMir) {
11311136
return .{ .data = .{ .self = self, .inst = inst } };
@@ -10802,7 +10807,6 @@ fn genCall(self: *Self, info: union(enum) {
1080210807
if (self.bin_file.cast(link.File.Elf)) |elf_file| {
1080310808
const sym_index = try elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, func.owner_decl);
1080410809
const sym = elf_file.symbol(sym_index);
10805-
sym.flags.needs_zig_got = true;
1080610810
if (self.bin_file.options.pic) {
1080710811
const callee_reg: Register = switch (resolved_cc) {
1080810812
.SysV => callee: {
@@ -13382,35 +13386,25 @@ fn genSetReg(self: *Self, dst_reg: Register, ty: Type, src_mcv: MCValue) InnerEr
1338213386
},
1338313387
.lea_direct, .lea_got => |sym_index| {
1338413388
const atom_index = try self.owner.getSymbolIndex(self);
13385-
if (self.bin_file.cast(link.File.Elf)) |_| {
13386-
try self.asmRegisterMemory(.{ ._, .lea }, dst_reg.to64(), .{
13387-
.base = .{ .reloc = .{
13389+
_ = try self.addInst(.{
13390+
.tag = switch (src_mcv) {
13391+
.lea_direct => .lea,
13392+
.lea_got => .mov,
13393+
else => unreachable,
13394+
},
13395+
.ops = switch (src_mcv) {
13396+
.lea_direct => .direct_reloc,
13397+
.lea_got => .got_reloc,
13398+
else => unreachable,
13399+
},
13400+
.data = .{ .rx = .{
13401+
.r1 = dst_reg.to64(),
13402+
.payload = try self.addExtra(bits.Symbol{
1338813403
.atom_index = atom_index,
1338913404
.sym_index = sym_index,
13390-
} },
13391-
.mod = .{ .rm = .{ .size = .qword } },
13392-
});
13393-
} else {
13394-
_ = try self.addInst(.{
13395-
.tag = switch (src_mcv) {
13396-
.lea_direct => .lea,
13397-
.lea_got => .mov,
13398-
else => unreachable,
13399-
},
13400-
.ops = switch (src_mcv) {
13401-
.lea_direct => .direct_reloc,
13402-
.lea_got => .got_reloc,
13403-
else => unreachable,
13404-
},
13405-
.data = .{ .rx = .{
13406-
.r1 = dst_reg.to64(),
13407-
.payload = try self.addExtra(bits.Symbol{
13408-
.atom_index = atom_index,
13409-
.sym_index = sym_index,
13410-
}),
13411-
} },
13412-
});
13413-
}
13405+
}),
13406+
} },
13407+
});
1341413408
},
1341513409
.lea_tlv => |sym_index| {
1341613410
const atom_index = try self.owner.getSymbolIndex(self);
@@ -13690,7 +13684,6 @@ fn genLazySymbolRef(
1369013684
const sym_index = elf_file.zigObjectPtr().?.getOrCreateMetadataForLazySymbol(elf_file, lazy_sym) catch |err|
1369113685
return self.fail("{s} creating lazy symbol", .{@errorName(err)});
1369213686
const sym = elf_file.symbol(sym_index);
13693-
sym.flags.needs_zig_got = true;
1369413687
if (self.bin_file.options.pic) {
1369513688
switch (tag) {
1369613689
.lea, .call => try self.genSetReg(reg, Type.usize, .{
@@ -15810,11 +15803,30 @@ fn resolveInst(self: *Self, ref: Air.Inst.Ref) InnerError!MCValue {
1581015803
} else mcv: {
1581115804
const ip_index = Air.refToInterned(ref).?;
1581215805
const gop = try self.const_tracking.getOrPut(self.gpa, ip_index);
15813-
const mcv = try self.genTypedValue(.{
15814-
.ty = ty,
15815-
.val = ip_index.toValue(),
15806+
if (!gop.found_existing) gop.value_ptr.* = InstTracking.init(init: {
15807+
const const_mcv = try self.genTypedValue(.{ .ty = ty, .val = ip_index.toValue() });
15808+
switch (const_mcv) {
15809+
.lea_tlv => |tlv_sym| if (self.bin_file.cast(link.File.Elf)) |_| {
15810+
if (self.bin_file.options.pic) {
15811+
try self.spillRegisters(&.{ .rdi, .rax });
15812+
} else {
15813+
try self.spillRegisters(&.{.rax});
15814+
}
15815+
const frame_index = try self.allocFrameIndex(FrameAlloc.init(.{
15816+
.size = 8,
15817+
.alignment = .@"8",
15818+
}));
15819+
try self.genSetMem(
15820+
.{ .frame = frame_index },
15821+
0,
15822+
Type.usize,
15823+
.{ .lea_symbol = .{ .sym = tlv_sym } },
15824+
);
15825+
break :init .{ .load_frame = .{ .index = frame_index } };
15826+
} else break :init const_mcv,
15827+
else => break :init const_mcv,
15828+
}
1581615829
});
15817-
if (!gop.found_existing) gop.value_ptr.* = InstTracking.init(mcv);
1581815830
break :mcv gop.value_ptr.short;
1581915831
};
1582015832

src/arch/x86_64/Emit.zig

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,24 @@ pub fn emitMir(emit: *Emit) Error!void {
8484
} else return emit.fail("TODO implement extern reloc for {s}", .{
8585
@tagName(emit.lower.bin_file.tag),
8686
}),
87+
.linker_tlsld => |data| {
88+
const elf_file = emit.lower.bin_file.cast(link.File.Elf).?;
89+
const atom = elf_file.symbol(data.atom_index).atom(elf_file).?;
90+
try atom.addReloc(elf_file, .{
91+
.r_offset = end_offset - 4,
92+
.r_info = (@as(u64, @intCast(data.sym_index)) << 32) | std.elf.R_X86_64_TLSLD,
93+
.r_addend = -4,
94+
});
95+
},
96+
.linker_dtpoff => |data| {
97+
const elf_file = emit.lower.bin_file.cast(link.File.Elf).?;
98+
const atom = elf_file.symbol(data.atom_index).atom(elf_file).?;
99+
try atom.addReloc(elf_file, .{
100+
.r_offset = end_offset - 4,
101+
.r_info = (@as(u64, @intCast(data.sym_index)) << 32) | std.elf.R_X86_64_DTPOFF32,
102+
.r_addend = 0,
103+
});
104+
},
87105
.linker_reloc => |data| if (emit.lower.bin_file.cast(link.File.Elf)) |elf_file| {
88106
const is_obj_or_static_lib = switch (emit.lower.bin_file.options.output_mode) {
89107
.Exe => false,
@@ -120,6 +138,8 @@ pub fn emitMir(emit: *Emit) Error!void {
120138
link.File.Elf.R_X86_64_ZIG_GOT32
121139
else if (sym.flags.needs_got)
122140
std.elf.R_X86_64_GOT32
141+
else if (sym.flags.is_tls)
142+
std.elf.R_X86_64_TPOFF32
123143
else
124144
std.elf.R_X86_64_32;
125145
try atom.addReloc(elf_file, .{

src/arch/x86_64/Lower.zig

Lines changed: 64 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ result_relocs_len: u8 = undefined,
1111
result_insts: [
1212
std.mem.max(usize, &.{
1313
1, // non-pseudo instructions
14+
3, // TLS local dynamic (LD) sequence in PIC mode
1415
2, // cmovcc: cmovcc \ cmovcc
1516
3, // setcc: setcc \ setcc \ logicop
1617
2, // jcc: jcc \ jcc
@@ -28,6 +29,7 @@ result_relocs: [
2829
2, // jcc: jcc \ jcc
2930
2, // test \ jcc \ probe \ sub \ jmp
3031
1, // probe \ sub \ jcc
32+
3, // TLS local dynamic (LD) sequence in PIC mode
3133
})
3234
]Reloc = undefined,
3335

@@ -51,6 +53,8 @@ pub const Reloc = struct {
5153
const Target = union(enum) {
5254
inst: Mir.Inst.Index,
5355
linker_reloc: bits.Symbol,
56+
linker_tlsld: bits.Symbol,
57+
linker_dtpoff: bits.Symbol,
5458
linker_extern_fn: bits.Symbol,
5559
linker_got: bits.Symbol,
5660
linker_direct: bits.Symbol,
@@ -319,20 +323,25 @@ fn reloc(lower: *Lower, target: Reloc.Target) Immediate {
319323
return Immediate.s(0);
320324
}
321325

322-
fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) Error!void {
323-
const needsZigGot = struct {
324-
fn needsZigGot(sym: bits.Symbol, ctx: *link.File) bool {
325-
const elf_file = ctx.cast(link.File.Elf).?;
326-
const sym_index = elf_file.zigObjectPtr().?.symbol(sym.sym_index);
327-
return elf_file.symbol(sym_index).flags.needs_zig_got;
328-
}
329-
}.needsZigGot;
326+
fn needsZigGot(sym: bits.Symbol, ctx: *link.File) bool {
327+
const elf_file = ctx.cast(link.File.Elf).?;
328+
const sym_index = elf_file.zigObjectPtr().?.symbol(sym.sym_index);
329+
return elf_file.symbol(sym_index).flags.needs_zig_got;
330+
}
331+
332+
fn isTls(sym: bits.Symbol, ctx: *link.File) bool {
333+
const elf_file = ctx.cast(link.File.Elf).?;
334+
const sym_index = elf_file.zigObjectPtr().?.symbol(sym.sym_index);
335+
return elf_file.symbol(sym_index).flags.is_tls;
336+
}
330337

338+
fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) Error!void {
331339
const is_obj_or_static_lib = switch (lower.bin_file.options.output_mode) {
332340
.Exe => false,
333341
.Obj => true,
334342
.Lib => lower.bin_file.options.link_mode == .Static,
335343
};
344+
336345
var emit_prefix = prefix;
337346
var emit_mnemonic = mnemonic;
338347
var emit_ops_storage: [4]Operand = undefined;
@@ -346,6 +355,53 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand)
346355
assert(prefix == .none);
347356
assert(mem_op.sib.disp == 0);
348357
assert(mem_op.sib.scale_index.scale == 0);
358+
359+
if (isTls(sym, lower.bin_file)) {
360+
// TODO handle extern TLS vars, i.e., emit GD model
361+
if (lower.bin_file.options.pic) {
362+
// Here, we currently assume local dynamic TLS vars, and so
363+
// we emit LD model.
364+
_ = lower.reloc(.{ .linker_tlsld = sym });
365+
lower.result_insts[lower.result_insts_len] =
366+
try Instruction.new(.none, .lea, &[_]Operand{
367+
.{ .reg = .rdi },
368+
.{ .mem = Memory.rip(mem_op.sib.ptr_size, 0) },
369+
});
370+
lower.result_insts_len += 1;
371+
if (lower.bin_file.cast(link.File.Elf)) |elf_file| {
372+
_ = lower.reloc(.{ .linker_extern_fn = .{
373+
.atom_index = sym.atom_index,
374+
.sym_index = try elf_file.getGlobalSymbol("__tls_get_addr", null),
375+
} });
376+
}
377+
lower.result_insts[lower.result_insts_len] =
378+
try Instruction.new(.none, .call, &[_]Operand{
379+
.{ .imm = Immediate.s(0) },
380+
});
381+
lower.result_insts_len += 1;
382+
_ = lower.reloc(.{ .linker_dtpoff = sym });
383+
emit_mnemonic = .lea;
384+
break :op .{ .mem = Memory.sib(mem_op.sib.ptr_size, .{
385+
.base = .{ .reg = .rax },
386+
.disp = std.math.minInt(i32),
387+
}) };
388+
} else {
389+
// Since we are linking statically, we emit LE model directly.
390+
lower.result_insts[lower.result_insts_len] =
391+
try Instruction.new(.none, .mov, &[_]Operand{
392+
.{ .reg = .rax },
393+
.{ .mem = Memory.sib(.qword, .{ .base = .{ .reg = .fs } }) },
394+
});
395+
lower.result_insts_len += 1;
396+
_ = lower.reloc(.{ .linker_reloc = sym });
397+
emit_mnemonic = .lea;
398+
break :op .{ .mem = Memory.sib(mem_op.sib.ptr_size, .{
399+
.base = .{ .reg = .rax },
400+
.disp = std.math.minInt(i32),
401+
}) };
402+
}
403+
}
404+
349405
_ = lower.reloc(.{ .linker_reloc = sym });
350406
break :op if (lower.bin_file.options.pic) switch (mnemonic) {
351407
.lea => {

src/codegen.zig

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -912,7 +912,9 @@ fn genDeclRef(
912912
}
913913
const sym_index = try elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, decl_index);
914914
const sym = elf_file.symbol(sym_index);
915-
sym.flags.needs_zig_got = true;
915+
if (is_threadlocal) {
916+
return GenResult.mcv(.{ .load_tlv = sym.esym_index });
917+
}
916918
return GenResult.mcv(.{ .load_symbol = sym.esym_index });
917919
} else if (bin_file.cast(link.File.MachO)) |macho_file| {
918920
if (is_extern) {

src/link/Coff.zig

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -388,7 +388,6 @@ fn populateMissingMetadata(self: *Coff) !void {
388388
self.rdata_section_index = try self.allocateSection(".rdata", file_size, .{
389389
.CNT_INITIALIZED_DATA = 1,
390390
.MEM_READ = 1,
391-
.MEM_WRITE = 1,
392391
});
393392
}
394393

@@ -920,7 +919,7 @@ fn markRelocsDirtyByTarget(self: *Coff, target: SymbolWithLoc) void {
920919
fn markRelocsDirtyByAddress(self: *Coff, addr: u32) void {
921920
const got_moved = blk: {
922921
const sect_id = self.got_section_index orelse break :blk false;
923-
break :blk self.sections.items(.header)[sect_id].virtual_address > addr;
922+
break :blk self.sections.items(.header)[sect_id].virtual_address >= addr;
924923
};
925924

926925
// TODO: dirty relocations targeting import table if that got moved in memory
@@ -931,15 +930,15 @@ fn markRelocsDirtyByAddress(self: *Coff, addr: u32) void {
931930
reloc.dirty = reloc.dirty or got_moved;
932931
} else {
933932
const target_vaddr = reloc.getTargetAddress(self) orelse continue;
934-
if (target_vaddr > addr) reloc.dirty = true;
933+
if (target_vaddr >= addr) reloc.dirty = true;
935934
}
936935
}
937936
}
938937

939938
// TODO: dirty only really affected GOT cells
940939
for (self.got_table.entries.items) |entry| {
941940
const target_addr = self.getSymbol(entry).value;
942-
if (target_addr > addr) {
941+
if (target_addr >= addr) {
943942
self.got_table_contents_dirty = true;
944943
break;
945944
}
@@ -1722,6 +1721,7 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
17221721
var code = std.ArrayList(u8).init(gpa);
17231722
defer code.deinit();
17241723
try code.resize(math.cast(usize, atom.size) orelse return error.Overflow);
1724+
assert(atom.size > 0);
17251725

17261726
const amt = try self.base.file.?.preadAll(code.items, file_offset);
17271727
if (amt != code.items.len) return error.InputOutput;

0 commit comments

Comments
 (0)