authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-11-14 22:09:15+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-11-14 22:09:15+01:00
log6fd1c64f23807993246515b93eb14978cff0e5da
treeb6e218e5eae869258f5b51e1a105f52df60d62be
parentf8b38a174f0c4a843688fe8adac09dc4f66cd585
parentea3f1d27e0145a279f1920ccd8dc9b81c02f15bc
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #17978 from ziglang/elf-x86-tls

x86_64+elf: TLS support

11 files changed, 349 insertions(+), 132 deletions(-)

lib/std/math/big/int_test.zig+3
...@@ -1443,6 +1443,9 @@ test "big.int divFloor #11166" {...@@ -1443,6 +1443,9 @@ test "big.int divFloor #11166" {
1443}1443}
14441444
1445test "big.int gcd #10932" {1445test "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
1446 var a = try Managed.init(testing.allocator);1449 var a = try Managed.init(testing.allocator);
1447 defer a.deinit();1450 defer a.deinit();
14481451
src/arch/x86_64/CodeGen.zig+46-34
...@@ -1107,6 +1107,7 @@ fn formatWipMir(...@@ -1107,6 +1107,7 @@ fn formatWipMir(
1107 .cc = .Unspecified,1107 .cc = .Unspecified,
1108 .src_loc = data.self.src_loc,1108 .src_loc = data.self.src_loc,
1109 };1109 };
1110 var first = true;
1110 for ((lower.lowerMir(data.inst) catch |err| switch (err) {1111 for ((lower.lowerMir(data.inst) catch |err| switch (err) {
1111 error.LowerFail => {1112 error.LowerFail => {
1112 defer {1113 defer {
...@@ -1125,7 +1126,11 @@ fn formatWipMir(...@@ -1125,7 +1126,11 @@ fn formatWipMir(
1125 return;1126 return;
1126 },1127 },
1127 else => |e| return e,1128 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 }
1129}1134}
1130fn fmtWipMir(self: *Self, inst: Mir.Inst.Index) std.fmt.Formatter(formatWipMir) {1135fn fmtWipMir(self: *Self, inst: Mir.Inst.Index) std.fmt.Formatter(formatWipMir) {
1131 return .{ .data = .{ .self = self, .inst = inst } };1136 return .{ .data = .{ .self = self, .inst = inst } };
...@@ -10802,7 +10807,6 @@ fn genCall(self: *Self, info: union(enum) {...@@ -10802,7 +10807,6 @@ fn genCall(self: *Self, info: union(enum) {
10802 if (self.bin_file.cast(link.File.Elf)) |elf_file| {10807 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
10803 const sym_index = try elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, func.owner_decl);10808 const sym_index = try elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, func.owner_decl);
10804 const sym = elf_file.symbol(sym_index);10809 const sym = elf_file.symbol(sym_index);
10805 sym.flags.needs_zig_got = true;
10806 if (self.bin_file.options.pic) {10810 if (self.bin_file.options.pic) {
10807 const callee_reg: Register = switch (resolved_cc) {10811 const callee_reg: Register = switch (resolved_cc) {
10808 .SysV => callee: {10812 .SysV => callee: {
...@@ -13382,35 +13386,25 @@ fn genSetReg(self: *Self, dst_reg: Register, ty: Type, src_mcv: MCValue) InnerEr...@@ -13382,35 +13386,25 @@ fn genSetReg(self: *Self, dst_reg: Register, ty: Type, src_mcv: MCValue) InnerEr
13382 },13386 },
13383 .lea_direct, .lea_got => |sym_index| {13387 .lea_direct, .lea_got => |sym_index| {
13384 const atom_index = try self.owner.getSymbolIndex(self);13388 const atom_index = try self.owner.getSymbolIndex(self);
13385 if (self.bin_file.cast(link.File.Elf)) |_| {13389 _ = try self.addInst(.{
13386 try self.asmRegisterMemory(.{ ._, .lea }, dst_reg.to64(), .{13390 .tag = switch (src_mcv) {
13387 .base = .{ .reloc = .{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{
13388 .atom_index = atom_index,13403 .atom_index = atom_index,
13389 .sym_index = sym_index,13404 .sym_index = sym_index,
13390 } },13405 }),
13391 .mod = .{ .rm = .{ .size = .qword } },13406 } },
13392 });13407 });
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 }
13414 },13408 },
13415 .lea_tlv => |sym_index| {13409 .lea_tlv => |sym_index| {
13416 const atom_index = try self.owner.getSymbolIndex(self);13410 const atom_index = try self.owner.getSymbolIndex(self);
...@@ -13690,7 +13684,6 @@ fn genLazySymbolRef(...@@ -13690,7 +13684,6 @@ fn genLazySymbolRef(
13690 const sym_index = elf_file.zigObjectPtr().?.getOrCreateMetadataForLazySymbol(elf_file, lazy_sym) catch |err|13684 const sym_index = elf_file.zigObjectPtr().?.getOrCreateMetadataForLazySymbol(elf_file, lazy_sym) catch |err|
13691 return self.fail("{s} creating lazy symbol", .{@errorName(err)});13685 return self.fail("{s} creating lazy symbol", .{@errorName(err)});
13692 const sym = elf_file.symbol(sym_index);13686 const sym = elf_file.symbol(sym_index);
13693 sym.flags.needs_zig_got = true;
13694 if (self.bin_file.options.pic) {13687 if (self.bin_file.options.pic) {
13695 switch (tag) {13688 switch (tag) {
13696 .lea, .call => try self.genSetReg(reg, Type.usize, .{13689 .lea, .call => try self.genSetReg(reg, Type.usize, .{
...@@ -15810,11 +15803,30 @@ fn resolveInst(self: *Self, ref: Air.Inst.Ref) InnerError!MCValue {...@@ -15810,11 +15803,30 @@ fn resolveInst(self: *Self, ref: Air.Inst.Ref) InnerError!MCValue {
15810 } else mcv: {15803 } else mcv: {
15811 const ip_index = Air.refToInterned(ref).?;15804 const ip_index = Air.refToInterned(ref).?;
15812 const gop = try self.const_tracking.getOrPut(self.gpa, ip_index);15805 const gop = try self.const_tracking.getOrPut(self.gpa, ip_index);
15813 const mcv = try self.genTypedValue(.{15806 if (!gop.found_existing) gop.value_ptr.* = InstTracking.init(init: {
15814 .ty = ty,15807 const const_mcv = try self.genTypedValue(.{ .ty = ty, .val = ip_index.toValue() });
15815 .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 }
15816 });15829 });
15817 if (!gop.found_existing) gop.value_ptr.* = InstTracking.init(mcv);
15818 break :mcv gop.value_ptr.short;15830 break :mcv gop.value_ptr.short;
15819 };15831 };
1582015832
src/arch/x86_64/Emit.zig+20
...@@ -84,6 +84,24 @@ pub fn emitMir(emit: *Emit) Error!void {...@@ -84,6 +84,24 @@ pub fn emitMir(emit: *Emit) Error!void {
84 } else return emit.fail("TODO implement extern reloc for {s}", .{84 } else return emit.fail("TODO implement extern reloc for {s}", .{
85 @tagName(emit.lower.bin_file.tag),85 @tagName(emit.lower.bin_file.tag),
86 }),86 }),
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 },
87 .linker_reloc => |data| if (emit.lower.bin_file.cast(link.File.Elf)) |elf_file| {105 .linker_reloc => |data| if (emit.lower.bin_file.cast(link.File.Elf)) |elf_file| {
88 const is_obj_or_static_lib = switch (emit.lower.bin_file.options.output_mode) {106 const is_obj_or_static_lib = switch (emit.lower.bin_file.options.output_mode) {
89 .Exe => false,107 .Exe => false,
...@@ -120,6 +138,8 @@ pub fn emitMir(emit: *Emit) Error!void {...@@ -120,6 +138,8 @@ pub fn emitMir(emit: *Emit) Error!void {
120 link.File.Elf.R_X86_64_ZIG_GOT32138 link.File.Elf.R_X86_64_ZIG_GOT32
121 else if (sym.flags.needs_got)139 else if (sym.flags.needs_got)
122 std.elf.R_X86_64_GOT32140 std.elf.R_X86_64_GOT32
141 else if (sym.flags.is_tls)
142 std.elf.R_X86_64_TPOFF32
123 else143 else
124 std.elf.R_X86_64_32;144 std.elf.R_X86_64_32;
125 try atom.addReloc(elf_file, .{145 try atom.addReloc(elf_file, .{
src/arch/x86_64/Lower.zig+64-8
...@@ -11,6 +11,7 @@ result_relocs_len: u8 = undefined,...@@ -11,6 +11,7 @@ result_relocs_len: u8 = undefined,
11result_insts: [11result_insts: [
12 std.mem.max(usize, &.{12 std.mem.max(usize, &.{
13 1, // non-pseudo instructions13 1, // non-pseudo instructions
14 3, // TLS local dynamic (LD) sequence in PIC mode
14 2, // cmovcc: cmovcc \ cmovcc15 2, // cmovcc: cmovcc \ cmovcc
15 3, // setcc: setcc \ setcc \ logicop16 3, // setcc: setcc \ setcc \ logicop
16 2, // jcc: jcc \ jcc17 2, // jcc: jcc \ jcc
...@@ -28,6 +29,7 @@ result_relocs: [...@@ -28,6 +29,7 @@ result_relocs: [
28 2, // jcc: jcc \ jcc29 2, // jcc: jcc \ jcc
29 2, // test \ jcc \ probe \ sub \ jmp30 2, // test \ jcc \ probe \ sub \ jmp
30 1, // probe \ sub \ jcc31 1, // probe \ sub \ jcc
32 3, // TLS local dynamic (LD) sequence in PIC mode
31 })33 })
32]Reloc = undefined,34]Reloc = undefined,
3335
...@@ -51,6 +53,8 @@ pub const Reloc = struct {...@@ -51,6 +53,8 @@ pub const Reloc = struct {
51 const Target = union(enum) {53 const Target = union(enum) {
52 inst: Mir.Inst.Index,54 inst: Mir.Inst.Index,
53 linker_reloc: bits.Symbol,55 linker_reloc: bits.Symbol,
56 linker_tlsld: bits.Symbol,
57 linker_dtpoff: bits.Symbol,
54 linker_extern_fn: bits.Symbol,58 linker_extern_fn: bits.Symbol,
55 linker_got: bits.Symbol,59 linker_got: bits.Symbol,
56 linker_direct: bits.Symbol,60 linker_direct: bits.Symbol,
...@@ -319,20 +323,25 @@ fn reloc(lower: *Lower, target: Reloc.Target) Immediate {...@@ -319,20 +323,25 @@ fn reloc(lower: *Lower, target: Reloc.Target) Immediate {
319 return Immediate.s(0);323 return Immediate.s(0);
320}324}
321325
322fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) Error!void {326fn needsZigGot(sym: bits.Symbol, ctx: *link.File) bool {
323 const needsZigGot = struct {327 const elf_file = ctx.cast(link.File.Elf).?;
324 fn needsZigGot(sym: bits.Symbol, ctx: *link.File) bool {328 const sym_index = elf_file.zigObjectPtr().?.symbol(sym.sym_index);
325 const elf_file = ctx.cast(link.File.Elf).?;329 return elf_file.symbol(sym_index).flags.needs_zig_got;
326 const sym_index = elf_file.zigObjectPtr().?.symbol(sym.sym_index);330}
327 return elf_file.symbol(sym_index).flags.needs_zig_got;331
328 }332fn isTls(sym: bits.Symbol, ctx: *link.File) bool {
329 }.needsZigGot;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
338fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) Error!void {
331 const is_obj_or_static_lib = switch (lower.bin_file.options.output_mode) {339 const is_obj_or_static_lib = switch (lower.bin_file.options.output_mode) {
332 .Exe => false,340 .Exe => false,
333 .Obj => true,341 .Obj => true,
334 .Lib => lower.bin_file.options.link_mode == .Static,342 .Lib => lower.bin_file.options.link_mode == .Static,
335 };343 };
344
336 var emit_prefix = prefix;345 var emit_prefix = prefix;
337 var emit_mnemonic = mnemonic;346 var emit_mnemonic = mnemonic;
338 var emit_ops_storage: [4]Operand = undefined;347 var emit_ops_storage: [4]Operand = undefined;
...@@ -346,6 +355,53 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand)...@@ -346,6 +355,53 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand)
346 assert(prefix == .none);355 assert(prefix == .none);
347 assert(mem_op.sib.disp == 0);356 assert(mem_op.sib.disp == 0);
348 assert(mem_op.sib.scale_index.scale == 0);357 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
349 _ = lower.reloc(.{ .linker_reloc = sym });405 _ = lower.reloc(.{ .linker_reloc = sym });
350 break :op if (lower.bin_file.options.pic) switch (mnemonic) {406 break :op if (lower.bin_file.options.pic) switch (mnemonic) {
351 .lea => {407 .lea => {
src/codegen.zig+3-1
...@@ -912,7 +912,9 @@ fn genDeclRef(...@@ -912,7 +912,9 @@ fn genDeclRef(
912 }912 }
913 const sym_index = try elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, decl_index);913 const sym_index = try elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, decl_index);
914 const sym = elf_file.symbol(sym_index);914 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 }
916 return GenResult.mcv(.{ .load_symbol = sym.esym_index });918 return GenResult.mcv(.{ .load_symbol = sym.esym_index });
917 } else if (bin_file.cast(link.File.MachO)) |macho_file| {919 } else if (bin_file.cast(link.File.MachO)) |macho_file| {
918 if (is_extern) {920 if (is_extern) {
src/link/Coff.zig+4-4
...@@ -388,7 +388,6 @@ fn populateMissingMetadata(self: *Coff) !void {...@@ -388,7 +388,6 @@ fn populateMissingMetadata(self: *Coff) !void {
388 self.rdata_section_index = try self.allocateSection(".rdata", file_size, .{388 self.rdata_section_index = try self.allocateSection(".rdata", file_size, .{
389 .CNT_INITIALIZED_DATA = 1,389 .CNT_INITIALIZED_DATA = 1,
390 .MEM_READ = 1,390 .MEM_READ = 1,
391 .MEM_WRITE = 1,
392 });391 });
393 }392 }
394393
...@@ -920,7 +919,7 @@ fn markRelocsDirtyByTarget(self: *Coff, target: SymbolWithLoc) void {...@@ -920,7 +919,7 @@ fn markRelocsDirtyByTarget(self: *Coff, target: SymbolWithLoc) void {
920fn markRelocsDirtyByAddress(self: *Coff, addr: u32) void {919fn markRelocsDirtyByAddress(self: *Coff, addr: u32) void {
921 const got_moved = blk: {920 const got_moved = blk: {
922 const sect_id = self.got_section_index orelse break :blk false;921 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;
924 };923 };
925924
926 // TODO: dirty relocations targeting import table if that got moved in memory925 // TODO: dirty relocations targeting import table if that got moved in memory
...@@ -931,7 +930,7 @@ fn markRelocsDirtyByAddress(self: *Coff, addr: u32) void {...@@ -931,7 +930,7 @@ fn markRelocsDirtyByAddress(self: *Coff, addr: u32) void {
931 reloc.dirty = reloc.dirty or got_moved;930 reloc.dirty = reloc.dirty or got_moved;
932 } else {931 } else {
933 const target_vaddr = reloc.getTargetAddress(self) orelse continue;932 const target_vaddr = reloc.getTargetAddress(self) orelse continue;
934 if (target_vaddr > addr) reloc.dirty = true;933 if (target_vaddr >= addr) reloc.dirty = true;
935 }934 }
936 }935 }
937 }936 }
...@@ -939,7 +938,7 @@ fn markRelocsDirtyByAddress(self: *Coff, addr: u32) void {...@@ -939,7 +938,7 @@ fn markRelocsDirtyByAddress(self: *Coff, addr: u32) void {
939 // TODO: dirty only really affected GOT cells938 // TODO: dirty only really affected GOT cells
940 for (self.got_table.entries.items) |entry| {939 for (self.got_table.entries.items) |entry| {
941 const target_addr = self.getSymbol(entry).value;940 const target_addr = self.getSymbol(entry).value;
942 if (target_addr > addr) {941 if (target_addr >= addr) {
943 self.got_table_contents_dirty = true;942 self.got_table_contents_dirty = true;
944 break;943 break;
945 }944 }
...@@ -1722,6 +1721,7 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod...@@ -1722,6 +1721,7 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
1722 var code = std.ArrayList(u8).init(gpa);1721 var code = std.ArrayList(u8).init(gpa);
1723 defer code.deinit();1722 defer code.deinit();
1724 try code.resize(math.cast(usize, atom.size) orelse return error.Overflow);1723 try code.resize(math.cast(usize, atom.size) orelse return error.Overflow);
1724 assert(atom.size > 0);
17251725
1726 const amt = try self.base.file.?.preadAll(code.items, file_offset);1726 const amt = try self.base.file.?.preadAll(code.items, file_offset);
1727 if (amt != code.items.len) return error.InputOutput;1727 if (amt != code.items.len) return error.InputOutput;
src/link/Elf.zig+23-7
...@@ -4512,7 +4512,10 @@ fn allocateAllocSections(self: *Elf) error{OutOfMemory}!void {...@@ -4512,7 +4512,10 @@ fn allocateAllocSections(self: *Elf) error{OutOfMemory}!void {
45124512
4513 for (cover.items) |shndx| {4513 for (cover.items) |shndx| {
4514 const shdr = &self.shdrs.items[shndx];4514 const shdr = &self.shdrs.items[shndx];
4515 if (shdr.sh_type == elf.SHT_NOBITS) continue;4515 if (shdr.sh_type == elf.SHT_NOBITS) {
4516 shdr.sh_offset = 0;
4517 continue;
4518 }
4516 off = alignment.@"align"(shndx, shdr.sh_addralign, off);4519 off = alignment.@"align"(shndx, shdr.sh_addralign, off);
4517 shdr.sh_offset = off;4520 shdr.sh_offset = off;
4518 off += shdr.sh_size;4521 off += shdr.sh_size;
...@@ -4528,7 +4531,10 @@ fn allocateAllocSectionsObject(self: *Elf) !void {...@@ -4528,7 +4531,10 @@ fn allocateAllocSectionsObject(self: *Elf) !void {
4528 for (self.shdrs.items) |*shdr| {4531 for (self.shdrs.items) |*shdr| {
4529 if (shdr.sh_type == elf.SHT_NULL) continue;4532 if (shdr.sh_type == elf.SHT_NULL) continue;
4530 if (shdr.sh_flags & elf.SHF_ALLOC == 0) continue;4533 if (shdr.sh_flags & elf.SHF_ALLOC == 0) continue;
4531 if (shdr.sh_type == elf.SHT_NOBITS) continue;4534 if (shdr.sh_type == elf.SHT_NOBITS) {
4535 shdr.sh_offset = 0;
4536 continue;
4537 }
4532 const needed_size = shdr.sh_size;4538 const needed_size = shdr.sh_size;
4533 if (needed_size > self.allocatedSize(shdr.sh_offset)) {4539 if (needed_size > self.allocatedSize(shdr.sh_offset)) {
4534 shdr.sh_size = 0;4540 shdr.sh_size = 0;
...@@ -4640,6 +4646,9 @@ fn allocateSpecialPhdrs(self: *Elf) void {...@@ -4640,6 +4646,9 @@ fn allocateSpecialPhdrs(self: *Elf) void {
4640}4646}
46414647
4642fn allocateAtoms(self: *Elf) void {4648fn allocateAtoms(self: *Elf) void {
4649 if (self.zigObjectPtr()) |zig_object| {
4650 zig_object.allocateTlvAtoms(self);
4651 }
4643 for (self.objects.items) |index| {4652 for (self.objects.items) |index| {
4644 self.file(index).?.object.allocateAtoms(self);4653 self.file(index).?.object.allocateAtoms(self);
4645 }4654 }
...@@ -4698,7 +4707,6 @@ fn writeAtoms(self: *Elf) !void {...@@ -4698,7 +4707,6 @@ fn writeAtoms(self: *Elf) !void {
4698 const atom_ptr = self.atom(atom_index).?;4707 const atom_ptr = self.atom(atom_index).?;
4699 assert(atom_ptr.flags.alive);4708 assert(atom_ptr.flags.alive);
47004709
4701 const object = atom_ptr.file(self).?.object;
4702 const offset = math.cast(usize, atom_ptr.value - shdr.sh_addr - base_offset) orelse4710 const offset = math.cast(usize, atom_ptr.value - shdr.sh_addr - base_offset) orelse
4703 return error.Overflow;4711 return error.Overflow;
4704 const size = math.cast(usize, atom_ptr.size) orelse return error.Overflow;4712 const size = math.cast(usize, atom_ptr.size) orelse return error.Overflow;
...@@ -4707,7 +4715,11 @@ fn writeAtoms(self: *Elf) !void {...@@ -4707,7 +4715,11 @@ fn writeAtoms(self: *Elf) !void {
47074715
4708 // TODO decompress directly into provided buffer4716 // TODO decompress directly into provided buffer
4709 const out_code = buffer[offset..][0..size];4717 const out_code = buffer[offset..][0..size];
4710 const in_code = try object.codeDecompressAlloc(self, atom_index);4718 const in_code = switch (atom_ptr.file(self).?) {
4719 .object => |x| try x.codeDecompressAlloc(self, atom_index),
4720 .zig_object => |x| try x.codeAlloc(self, atom_index),
4721 else => unreachable,
4722 };
4711 defer gpa.free(in_code);4723 defer gpa.free(in_code);
4712 @memcpy(out_code, in_code);4724 @memcpy(out_code, in_code);
47134725
...@@ -4774,7 +4786,6 @@ fn writeAtomsObject(self: *Elf) !void {...@@ -4774,7 +4786,6 @@ fn writeAtomsObject(self: *Elf) !void {
4774 const atom_ptr = self.atom(atom_index).?;4786 const atom_ptr = self.atom(atom_index).?;
4775 assert(atom_ptr.flags.alive);4787 assert(atom_ptr.flags.alive);
47764788
4777 const object = atom_ptr.file(self).?.object;
4778 const offset = math.cast(usize, atom_ptr.value - shdr.sh_addr - base_offset) orelse4789 const offset = math.cast(usize, atom_ptr.value - shdr.sh_addr - base_offset) orelse
4779 return error.Overflow;4790 return error.Overflow;
4780 const size = math.cast(usize, atom_ptr.size) orelse return error.Overflow;4791 const size = math.cast(usize, atom_ptr.size) orelse return error.Overflow;
...@@ -4787,7 +4798,11 @@ fn writeAtomsObject(self: *Elf) !void {...@@ -4787,7 +4798,11 @@ fn writeAtomsObject(self: *Elf) !void {
47874798
4788 // TODO decompress directly into provided buffer4799 // TODO decompress directly into provided buffer
4789 const out_code = buffer[offset..][0..size];4800 const out_code = buffer[offset..][0..size];
4790 const in_code = try object.codeDecompressAlloc(self, atom_index);4801 const in_code = switch (atom_ptr.file(self).?) {
4802 .object => |x| try x.codeDecompressAlloc(self, atom_index),
4803 .zig_object => |x| try x.codeAlloc(self, atom_index),
4804 else => unreachable,
4805 };
4791 defer gpa.free(in_code);4806 defer gpa.free(in_code);
4792 @memcpy(out_code, in_code);4807 @memcpy(out_code, in_code);
4793 }4808 }
...@@ -6250,8 +6265,9 @@ fn fmtDumpState(...@@ -6250,8 +6265,9 @@ fn fmtDumpState(
6250 try writer.print("linker_defined({d}) : (linker defined)\n", .{index});6265 try writer.print("linker_defined({d}) : (linker defined)\n", .{index});
6251 try writer.print("{}\n", .{linker_defined.fmtSymtab(self)});6266 try writer.print("{}\n", .{linker_defined.fmtSymtab(self)});
6252 }6267 }
6253 try writer.print("{}\n", .{self.got.fmt(self)});
6254 try writer.print("{}\n", .{self.zig_got.fmt(self)});6268 try writer.print("{}\n", .{self.zig_got.fmt(self)});
6269 try writer.print("{}\n", .{self.got.fmt(self)});
6270 try writer.print("{}\n", .{self.plt.fmt(self)});
62556271
6256 try writer.writeAll("Output COMDAT groups\n");6272 try writer.writeAll("Output COMDAT groups\n");
6257 for (self.comdat_group_sections.items) |cg| {6273 for (self.comdat_group_sections.items) |cg| {
src/link/Elf/Symbol.zig+5
...@@ -399,6 +399,11 @@ pub const Flags = packed struct {...@@ -399,6 +399,11 @@ pub const Flags = packed struct {
399 /// Whether the symbol contains .zig.got indirection.399 /// Whether the symbol contains .zig.got indirection.
400 needs_zig_got: bool = false,400 needs_zig_got: bool = false,
401 has_zig_got: bool = false,401 has_zig_got: bool = false,
402
403 /// Whether the symbol is a TLS variable.
404 /// TODO this is really not needed if only we operated on esyms between
405 /// codegen and ZigObject.
406 is_tls: bool = false,
402};407};
403408
404pub const Extra = struct {409pub const Extra = struct {
src/link/Elf/ZigObject.zig+150-77
...@@ -29,6 +29,9 @@ lazy_syms: LazySymbolTable = .{},...@@ -29,6 +29,9 @@ lazy_syms: LazySymbolTable = .{},
29/// Table of tracked Decls.29/// Table of tracked Decls.
30decls: DeclTable = .{},30decls: DeclTable = .{},
3131
32/// TLS variables indexed by Atom.Index.
33tls_variables: TlsTable = .{},
34
32/// Table of unnamed constants associated with a parent `Decl`.35/// Table of unnamed constants associated with a parent `Decl`.
33/// We store them here so that we can free the constants whenever the `Decl`36/// We store them here so that we can free the constants whenever the `Decl`
34/// needs updating or is freed.37/// needs updating or is freed.
...@@ -137,6 +140,11 @@ pub fn deinit(self: *ZigObject, allocator: Allocator) void {...@@ -137,6 +140,11 @@ pub fn deinit(self: *ZigObject, allocator: Allocator) void {
137 self.anon_decls.deinit(allocator);140 self.anon_decls.deinit(allocator);
138 }141 }
139142
143 for (self.tls_variables.values()) |*tlv| {
144 tlv.deinit(allocator);
145 }
146 self.tls_variables.deinit(allocator);
147
140 if (self.dwarf) |*dw| {148 if (self.dwarf) |*dw| {
141 dw.deinit();149 dw.deinit();
142 }150 }
...@@ -212,8 +220,6 @@ pub fn flushModule(self: *ZigObject, elf_file: *Elf) !void {...@@ -212,8 +220,6 @@ pub fn flushModule(self: *ZigObject, elf_file: *Elf) !void {
212 self.saveDebugSectionsSizes(elf_file);220 self.saveDebugSectionsSizes(elf_file);
213 }221 }
214222
215 try self.sortSymbols(elf_file);
216
217 // The point of flushModule() is to commit changes, so in theory, nothing should223 // The point of flushModule() is to commit changes, so in theory, nothing should
218 // be dirty after this. However, it is possible for some things to remain224 // be dirty after this. However, it is possible for some things to remain
219 // dirty because they fail to be written in the event of compile errors,225 // dirty because they fail to be written in the event of compile errors,
...@@ -280,6 +286,7 @@ pub fn addAtom(self: *ZigObject, elf_file: *Elf) !Symbol.Index {...@@ -280,6 +286,7 @@ pub fn addAtom(self: *ZigObject, elf_file: *Elf) !Symbol.Index {
280 self.local_esyms.items(.elf_sym)[esym_index].st_shndx = SHN_ATOM;286 self.local_esyms.items(.elf_sym)[esym_index].st_shndx = SHN_ATOM;
281 symbol_ptr.esym_index = esym_index;287 symbol_ptr.esym_index = esym_index;
282288
289 // TODO I'm thinking that maybe we shouldn' set this value unless it's actually needed?
283 const relocs_index = @as(u32, @intCast(self.relocs.items.len));290 const relocs_index = @as(u32, @intCast(self.relocs.items.len));
284 const relocs = try self.relocs.addOne(gpa);291 const relocs = try self.relocs.addOne(gpa);
285 relocs.* = .{};292 relocs.* = .{};
...@@ -388,6 +395,19 @@ pub fn claimUnresolvedObject(self: ZigObject, elf_file: *Elf) void {...@@ -388,6 +395,19 @@ pub fn claimUnresolvedObject(self: ZigObject, elf_file: *Elf) void {
388 }395 }
389}396}
390397
398pub fn allocateTlvAtoms(self: ZigObject, elf_file: *Elf) void {
399 for (self.tls_variables.keys(), self.tls_variables.values()) |atom_index, tlv| {
400 const atom = elf_file.atom(atom_index) orelse continue;
401 if (!atom.flags.alive) continue;
402 const local = elf_file.symbol(tlv.symbol_index);
403 const shdr = elf_file.shdrs.items[atom.output_section_index];
404 atom.value += shdr.sh_addr;
405 local.value = atom.value;
406
407 // TODO exported TLS vars
408 }
409}
410
391pub fn scanRelocs(self: *ZigObject, elf_file: *Elf, undefs: anytype) !void {411pub fn scanRelocs(self: *ZigObject, elf_file: *Elf, undefs: anytype) !void {
392 for (self.atoms.items) |atom_index| {412 for (self.atoms.items) |atom_index| {
393 const atom = elf_file.atom(atom_index) orelse continue;413 const atom = elf_file.atom(atom_index) orelse continue;
...@@ -421,72 +441,6 @@ pub fn markLive(self: *ZigObject, elf_file: *Elf) void {...@@ -421,72 +441,6 @@ pub fn markLive(self: *ZigObject, elf_file: *Elf) void {
421 }441 }
422}442}
423443
424fn sortSymbols(self: *ZigObject, elf_file: *Elf) error{OutOfMemory}!void {
425 _ = self;
426 _ = elf_file;
427 // const Entry = struct {
428 // index: Symbol.Index,
429
430 // const Ctx = struct {
431 // zobj: ZigObject,
432 // efile: *Elf,
433 // };
434
435 // pub fn lessThan(ctx: Ctx, lhs: @This(), rhs: @This()) bool {
436 // const lhs_sym = ctx.efile.symbol(zobj.symbol(lhs.index));
437 // const rhs_sym = ctx.efile.symbol(zobj.symbol(rhs.index));
438 // if (lhs_sym.outputShndx() != null and rhs_sym.outputShndx() != null) {
439 // if (lhs_sym.output_section_index == rhs_sym.output_section_index) {
440 // if (lhs_sym.value == rhs_sym.value) {
441 // return lhs_sym.name_offset < rhs_sym.name_offset;
442 // }
443 // return lhs_sym.value < rhs_sym.value;
444 // }
445 // return lhs_sym.output_section_index < rhs_sym.output_section_index;
446 // }
447 // if (lhs_sym.outputShndx() != null) {
448 // if (rhs_sym.isAbs(ctx.efile)) return false;
449 // return true;
450 // }
451 // return false;
452 // }
453 // };
454
455 // const gpa = elf_file.base.allocator;
456
457 // {
458 // const sorted = try gpa.alloc(Entry, self.local_symbols.items.len);
459 // defer gpa.free(sorted);
460 // for (0..self.local_symbols.items.len) |index| {
461 // sorted[i] = .{ .index = @as(Symbol.Index, @intCast(index)) };
462 // }
463 // mem.sort(Entry, sorted, .{ .zobj = self, .efile = elf_file }, Entry.lessThan);
464
465 // const backlinks = try gpa.alloc(Symbol.Index, sorted.len);
466 // defer gpa.free(backlinks);
467 // for (sorted, 0..) |entry, i| {
468 // backlinks[entry.index] = @as(Symbol.Index, @intCast(i));
469 // }
470
471 // const local_symbols = try self.local_symbols.toOwnedSlice(gpa);
472 // defer gpa.free(local_symbols);
473
474 // try self.local_symbols.ensureTotalCapacityPrecise(gpa, local_symbols.len);
475 // for (sorted) |entry| {
476 // self.local_symbols.appendAssumeCapacity(local_symbols[entry.index]);
477 // }
478
479 // for (self.)
480 // }
481
482 // const sorted_globals = try gpa.alloc(Entry, self.global_symbols.items.len);
483 // defer gpa.free(sorted_globals);
484 // for (self.global_symbols.items, 0..) |index, i| {
485 // sorted_globals[i] = .{ .index = index };
486 // }
487 // mem.sort(Entry, sorted_globals, elf_file, Entry.lessThan);
488}
489
490pub fn updateArSymtab(self: ZigObject, ar_symtab: *Archive.ArSymtab, elf_file: *Elf) error{OutOfMemory}!void {444pub fn updateArSymtab(self: ZigObject, ar_symtab: *Archive.ArSymtab, elf_file: *Elf) error{OutOfMemory}!void {
491 const gpa = elf_file.base.allocator;445 const gpa = elf_file.base.allocator;
492446
...@@ -537,7 +491,9 @@ pub fn addAtomsToRelaSections(self: ZigObject, elf_file: *Elf) !void {...@@ -537,7 +491,9 @@ pub fn addAtomsToRelaSections(self: ZigObject, elf_file: *Elf) !void {
537 for (self.atoms.items) |atom_index| {491 for (self.atoms.items) |atom_index| {
538 const atom = elf_file.atom(atom_index) orelse continue;492 const atom = elf_file.atom(atom_index) orelse continue;
539 if (!atom.flags.alive) continue;493 if (!atom.flags.alive) continue;
540 _ = atom.relocsShndx() orelse continue;494 const rela_shndx = atom.relocsShndx() orelse continue;
495 // TODO this check will become obsolete when we rework our relocs mechanism at the ZigObject level
496 if (self.relocs.items[rela_shndx].items.len == 0) continue;
541 const out_shndx = atom.outputShndx().?;497 const out_shndx = atom.outputShndx().?;
542 const out_shdr = elf_file.shdrs.items[out_shndx];498 const out_shdr = elf_file.shdrs.items[out_shndx];
543 if (out_shdr.sh_type == elf.SHT_NOBITS) continue;499 if (out_shdr.sh_type == elf.SHT_NOBITS) continue;
...@@ -583,6 +539,13 @@ pub fn codeAlloc(self: ZigObject, elf_file: *Elf, atom_index: Atom.Index) ![]u8...@@ -583,6 +539,13 @@ pub fn codeAlloc(self: ZigObject, elf_file: *Elf, atom_index: Atom.Index) ![]u8
583 const atom = elf_file.atom(atom_index).?;539 const atom = elf_file.atom(atom_index).?;
584 assert(atom.file_index == self.index);540 assert(atom.file_index == self.index);
585 const shdr = &elf_file.shdrs.items[atom.outputShndx().?];541 const shdr = &elf_file.shdrs.items[atom.outputShndx().?];
542
543 if (shdr.sh_flags & elf.SHF_TLS != 0) {
544 const tlv = self.tls_variables.get(atom_index).?;
545 const code = try gpa.dupe(u8, tlv.code);
546 return code;
547 }
548
586 const file_offset = shdr.sh_offset + atom.value - shdr.sh_addr;549 const file_offset = shdr.sh_offset + atom.value - shdr.sh_addr;
587 const size = std.math.cast(usize, atom.size) orelse return error.Overflow;550 const size = std.math.cast(usize, atom.size) orelse return error.Overflow;
588 const code = try gpa.alloc(u8, size);551 const code = try gpa.alloc(u8, size);
...@@ -705,7 +668,12 @@ pub fn getOrCreateMetadataForLazySymbol(...@@ -705,7 +668,12 @@ pub fn getOrCreateMetadataForLazySymbol(
705 },668 },
706 };669 };
707 switch (metadata.state.*) {670 switch (metadata.state.*) {
708 .unused => metadata.symbol_index.* = try self.addAtom(elf_file),671 .unused => {
672 const symbol_index = try self.addAtom(elf_file);
673 const sym = elf_file.symbol(symbol_index);
674 sym.flags.needs_zig_got = true;
675 metadata.symbol_index.* = symbol_index;
676 },
709 .pending_flush => return metadata.symbol_index.*,677 .pending_flush => return metadata.symbol_index.*,
710 .flushed => {},678 .flushed => {},
711 }679 }
...@@ -760,20 +728,55 @@ pub fn getOrCreateMetadataForDecl(...@@ -760,20 +728,55 @@ pub fn getOrCreateMetadataForDecl(
760) !Symbol.Index {728) !Symbol.Index {
761 const gop = try self.decls.getOrPut(elf_file.base.allocator, decl_index);729 const gop = try self.decls.getOrPut(elf_file.base.allocator, decl_index);
762 if (!gop.found_existing) {730 if (!gop.found_existing) {
763 gop.value_ptr.* = .{ .symbol_index = try self.addAtom(elf_file) };731 const single_threaded = elf_file.base.options.single_threaded;
732 const symbol_index = try self.addAtom(elf_file);
733 const mod = elf_file.base.options.module.?;
734 const decl = mod.declPtr(decl_index);
735 const sym = elf_file.symbol(symbol_index);
736 if (decl.getOwnedVariable(mod)) |variable| {
737 if (variable.is_threadlocal and !single_threaded) {
738 sym.flags.is_tls = true;
739 }
740 }
741 if (!sym.flags.is_tls) {
742 sym.flags.needs_zig_got = true;
743 }
744 gop.value_ptr.* = .{ .symbol_index = symbol_index };
764 }745 }
765 return gop.value_ptr.symbol_index;746 return gop.value_ptr.symbol_index;
766}747}
767748
768fn getDeclShdrIndex(self: *ZigObject, elf_file: *Elf, decl_index: Module.Decl.Index, code: []const u8) u16 {749fn getDeclShdrIndex(
750 self: *ZigObject,
751 elf_file: *Elf,
752 decl: *const Module.Decl,
753 code: []const u8,
754) error{OutOfMemory}!u16 {
769 _ = self;755 _ = self;
770 const mod = elf_file.base.options.module.?;756 const mod = elf_file.base.options.module.?;
771 const decl = mod.declPtr(decl_index);757 const single_threaded = elf_file.base.options.single_threaded;
772 const shdr_index = switch (decl.ty.zigTypeTag(mod)) {758 const shdr_index = switch (decl.ty.zigTypeTag(mod)) {
773 // TODO: what if this is a function pointer?
774 .Fn => elf_file.zig_text_section_index.?,759 .Fn => elf_file.zig_text_section_index.?,
775 else => blk: {760 else => blk: {
776 if (decl.getOwnedVariable(mod)) |variable| {761 if (decl.getOwnedVariable(mod)) |variable| {
762 if (variable.is_threadlocal and !single_threaded) {
763 const is_all_zeroes = for (code) |byte| {
764 if (byte != 0) break false;
765 } else true;
766 if (is_all_zeroes) break :blk elf_file.sectionByName(".tbss") orelse try elf_file.addSection(.{
767 .type = elf.SHT_NOBITS,
768 .flags = elf.SHF_ALLOC | elf.SHF_WRITE | elf.SHF_TLS,
769 .name = ".tbss",
770 .offset = std.math.maxInt(u64),
771 });
772
773 break :blk elf_file.sectionByName(".tdata") orelse try elf_file.addSection(.{
774 .type = elf.SHT_PROGBITS,
775 .flags = elf.SHF_ALLOC | elf.SHF_WRITE | elf.SHF_TLS,
776 .name = ".tdata",
777 .offset = std.math.maxInt(u64),
778 });
779 }
777 if (variable.is_const) break :blk elf_file.zig_data_rel_ro_section_index.?;780 if (variable.is_const) break :blk elf_file.zig_data_rel_ro_section_index.?;
778 if (variable.init.toValue().isUndefDeep(mod)) {781 if (variable.init.toValue().isUndefDeep(mod)) {
779 const mode = elf_file.base.options.optimize_mode;782 const mode = elf_file.base.options.optimize_mode;
...@@ -799,6 +802,7 @@ fn updateDeclCode(...@@ -799,6 +802,7 @@ fn updateDeclCode(
799 elf_file: *Elf,802 elf_file: *Elf,
800 decl_index: Module.Decl.Index,803 decl_index: Module.Decl.Index,
801 sym_index: Symbol.Index,804 sym_index: Symbol.Index,
805 shdr_index: u16,
802 code: []const u8,806 code: []const u8,
803 stt_bits: u8,807 stt_bits: u8,
804) !void {808) !void {
...@@ -815,7 +819,6 @@ fn updateDeclCode(...@@ -815,7 +819,6 @@ fn updateDeclCode(
815 const esym = &self.local_esyms.items(.elf_sym)[sym.esym_index];819 const esym = &self.local_esyms.items(.elf_sym)[sym.esym_index];
816 const atom_ptr = sym.atom(elf_file).?;820 const atom_ptr = sym.atom(elf_file).?;
817821
818 const shdr_index = self.getDeclShdrIndex(elf_file, decl_index, code);
819 sym.output_section_index = shdr_index;822 sym.output_section_index = shdr_index;
820 atom_ptr.output_section_index = shdr_index;823 atom_ptr.output_section_index = shdr_index;
821824
...@@ -893,6 +896,58 @@ fn updateDeclCode(...@@ -893,6 +896,58 @@ fn updateDeclCode(
893 }896 }
894}897}
895898
899fn updateTlv(
900 self: *ZigObject,
901 elf_file: *Elf,
902 decl_index: Module.Decl.Index,
903 sym_index: Symbol.Index,
904 shndx: u16,
905 code: []const u8,
906) !void {
907 const gpa = elf_file.base.allocator;
908 const mod = elf_file.base.options.module.?;
909 const decl = mod.declPtr(decl_index);
910 const decl_name = mod.intern_pool.stringToSlice(try decl.getFullyQualifiedName(mod));
911
912 log.debug("updateTlv {s} ({*})", .{ decl_name, decl });
913
914 const required_alignment = decl.getAlignment(mod);
915
916 const sym = elf_file.symbol(sym_index);
917 const esym = &self.local_esyms.items(.elf_sym)[sym.esym_index];
918 const atom_ptr = sym.atom(elf_file).?;
919
920 sym.output_section_index = shndx;
921 atom_ptr.output_section_index = shndx;
922
923 sym.name_offset = try self.strtab.insert(gpa, decl_name);
924 atom_ptr.flags.alive = true;
925 atom_ptr.name_offset = sym.name_offset;
926 esym.st_name = sym.name_offset;
927 esym.st_info = elf.STT_TLS;
928 esym.st_size = code.len;
929
930 atom_ptr.alignment = required_alignment;
931 atom_ptr.size = code.len;
932
933 {
934 const gop = try self.tls_variables.getOrPut(gpa, atom_ptr.atom_index);
935 assert(!gop.found_existing); // TODO incremental updates
936 gop.value_ptr.* = .{ .symbol_index = sym_index };
937
938 // We only store the data for the TLV if it's non-zerofill.
939 if (elf_file.shdrs.items[shndx].sh_type != elf.SHT_NOBITS) {
940 gop.value_ptr.code = try gpa.dupe(u8, code);
941 }
942 }
943
944 {
945 const gop = try elf_file.output_sections.getOrPut(gpa, atom_ptr.output_section_index);
946 if (!gop.found_existing) gop.value_ptr.* = .{};
947 try gop.value_ptr.append(gpa, atom_ptr.atom_index);
948 }
949}
950
896pub fn updateFunc(951pub fn updateFunc(
897 self: *ZigObject,952 self: *ZigObject,
898 elf_file: *Elf,953 elf_file: *Elf,
...@@ -947,7 +1002,10 @@ pub fn updateFunc(...@@ -947,7 +1002,10 @@ pub fn updateFunc(
947 return;1002 return;
948 },1003 },
949 };1004 };
950 try self.updateDeclCode(elf_file, decl_index, sym_index, code, elf.STT_FUNC);1005
1006 const shndx = try self.getDeclShdrIndex(elf_file, decl, code);
1007 try self.updateDeclCode(elf_file, decl_index, sym_index, shndx, code, elf.STT_FUNC);
1008
951 if (decl_state) |*ds| {1009 if (decl_state) |*ds| {
952 const sym = elf_file.symbol(sym_index);1010 const sym = elf_file.symbol(sym_index);
953 try self.dwarf.?.commitDeclState(1011 try self.dwarf.?.commitDeclState(
...@@ -1026,7 +1084,12 @@ pub fn updateDecl(...@@ -1026,7 +1084,12 @@ pub fn updateDecl(
1026 },1084 },
1027 };1085 };
10281086
1029 try self.updateDeclCode(elf_file, decl_index, sym_index, code, elf.STT_OBJECT);1087 const shndx = try self.getDeclShdrIndex(elf_file, decl, code);
1088 if (elf_file.shdrs.items[shndx].sh_flags & elf.SHF_TLS != 0)
1089 try self.updateTlv(elf_file, decl_index, sym_index, shndx, code)
1090 else
1091 try self.updateDeclCode(elf_file, decl_index, sym_index, shndx, code, elf.STT_OBJECT);
1092
1030 if (decl_state) |*ds| {1093 if (decl_state) |*ds| {
1031 const sym = elf_file.symbol(sym_index);1094 const sym = elf_file.symbol(sym_index);
1032 try self.dwarf.?.commitDeclState(1095 try self.dwarf.?.commitDeclState(
...@@ -1454,11 +1517,21 @@ const DeclMetadata = struct {...@@ -1454,11 +1517,21 @@ const DeclMetadata = struct {
1454 }1517 }
1455};1518};
14561519
1520const TlsVariable = struct {
1521 symbol_index: Symbol.Index,
1522 code: []const u8 = &[0]u8{},
1523
1524 fn deinit(tlv: *TlsVariable, allocator: Allocator) void {
1525 allocator.free(tlv.code);
1526 }
1527};
1528
1457const AtomList = std.ArrayListUnmanaged(Atom.Index);1529const AtomList = std.ArrayListUnmanaged(Atom.Index);
1458const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(Symbol.Index));1530const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(Symbol.Index));
1459const DeclTable = std.AutoHashMapUnmanaged(Module.Decl.Index, DeclMetadata);1531const DeclTable = std.AutoHashMapUnmanaged(Module.Decl.Index, DeclMetadata);
1460const AnonDeclTable = std.AutoHashMapUnmanaged(InternPool.Index, DeclMetadata);1532const AnonDeclTable = std.AutoHashMapUnmanaged(InternPool.Index, DeclMetadata);
1461const LazySymbolTable = std.AutoArrayHashMapUnmanaged(Module.Decl.OptionalIndex, LazySymbolMetadata);1533const LazySymbolTable = std.AutoArrayHashMapUnmanaged(Module.Decl.OptionalIndex, LazySymbolMetadata);
1534const TlsTable = std.AutoArrayHashMapUnmanaged(Atom.Index, TlsVariable);
14621535
1463const assert = std.debug.assert;1536const assert = std.debug.assert;
1464const builtin = @import("builtin");1537const builtin = @import("builtin");
src/link/Elf/synthetic_sections.zig+30
...@@ -935,6 +935,36 @@ pub const PltSection = struct {...@@ -935,6 +935,36 @@ pub const PltSection = struct {
935 ilocal += 1;935 ilocal += 1;
936 }936 }
937 }937 }
938
939 const FormatCtx = struct {
940 plt: PltSection,
941 elf_file: *Elf,
942 };
943
944 pub fn fmt(plt: PltSection, elf_file: *Elf) std.fmt.Formatter(format2) {
945 return .{ .data = .{ .plt = plt, .elf_file = elf_file } };
946 }
947
948 pub fn format2(
949 ctx: FormatCtx,
950 comptime unused_fmt_string: []const u8,
951 options: std.fmt.FormatOptions,
952 writer: anytype,
953 ) !void {
954 _ = options;
955 _ = unused_fmt_string;
956 try writer.writeAll("PLT\n");
957 for (ctx.plt.symbols.items, 0..) |symbol_index, i| {
958 const symbol = ctx.elf_file.symbol(symbol_index);
959 try writer.print(" {d}@0x{x} => {d}@0x{x} ({s})\n", .{
960 i,
961 symbol.pltAddress(ctx.elf_file),
962 symbol_index,
963 symbol.address(.{}, ctx.elf_file),
964 symbol.name(ctx.elf_file),
965 });
966 }
967 }
938};968};
939969
940pub const GotPltSection = struct {970pub const GotPltSection = struct {
src/target.zig+1-1
...@@ -654,7 +654,7 @@ pub fn supportsTailCall(target: std.Target, backend: std.builtin.CompilerBackend...@@ -654,7 +654,7 @@ pub fn supportsTailCall(target: std.Target, backend: std.builtin.CompilerBackend
654654
655pub fn supportsThreads(target: std.Target, backend: std.builtin.CompilerBackend) bool {655pub fn supportsThreads(target: std.Target, backend: std.builtin.CompilerBackend) bool {
656 return switch (backend) {656 return switch (backend) {
657 .stage2_x86_64 => target.ofmt == .macho,657 .stage2_x86_64 => target.ofmt == .macho or target.ofmt == .elf,
658 else => true,658 else => true,
659 };659 };
660}660}