Commit 984c598590

Jakub Konka <kubkon@jakubkonka.com>
2023-11-12 10:28:45
x86_64: emit TLS local dynamic model when PIC
1 parent 51efee2
Changed files (2)
src
arch
src/arch/x86_64/Emit.zig
@@ -84,6 +84,24 @@ pub fn emitMir(emit: *Emit) Error!void {
                 } else return emit.fail("TODO implement extern reloc for {s}", .{
                     @tagName(emit.lower.bin_file.tag),
                 }),
+                .linker_tlsld => |data| {
+                    const elf_file = emit.lower.bin_file.cast(link.File.Elf).?;
+                    const atom = elf_file.symbol(data.atom_index).atom(elf_file).?;
+                    try atom.addReloc(elf_file, .{
+                        .r_offset = end_offset - 4,
+                        .r_info = (@as(u64, @intCast(data.sym_index)) << 32) | std.elf.R_X86_64_TLSLD,
+                        .r_addend = -4,
+                    });
+                },
+                .linker_dtpoff => |data| {
+                    const elf_file = emit.lower.bin_file.cast(link.File.Elf).?;
+                    const atom = elf_file.symbol(data.atom_index).atom(elf_file).?;
+                    try atom.addReloc(elf_file, .{
+                        .r_offset = end_offset - 4,
+                        .r_info = (@as(u64, @intCast(data.sym_index)) << 32) | std.elf.R_X86_64_DTPOFF32,
+                        .r_addend = 0,
+                    });
+                },
                 .linker_reloc => |data| if (emit.lower.bin_file.cast(link.File.Elf)) |elf_file| {
                     const is_obj_or_static_lib = switch (emit.lower.bin_file.options.output_mode) {
                         .Exe => false,
src/arch/x86_64/Lower.zig
@@ -11,6 +11,7 @@ result_relocs_len: u8 = undefined,
 result_insts: [
     std.mem.max(usize, &.{
         1, // non-pseudo instructions
+        3, // TLS local dynamic (LD) sequence in PIC mode
         2, // cmovcc: cmovcc \ cmovcc
         3, // setcc: setcc \ setcc \ logicop
         2, // jcc: jcc \ jcc
@@ -28,6 +29,7 @@ result_relocs: [
         2, // jcc: jcc \ jcc
         2, // test \ jcc \ probe \ sub \ jmp
         1, // probe \ sub \ jcc
+        3, // TLS local dynamic (LD) sequence in PIC mode
     })
 ]Reloc = undefined,
 
@@ -51,6 +53,8 @@ pub const Reloc = struct {
     const Target = union(enum) {
         inst: Mir.Inst.Index,
         linker_reloc: bits.Symbol,
+        linker_tlsld: bits.Symbol,
+        linker_dtpoff: bits.Symbol,
         linker_extern_fn: bits.Symbol,
         linker_got: bits.Symbol,
         linker_direct: bits.Symbol,
@@ -353,24 +357,56 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand)
                     assert(mem_op.sib.scale_index.scale == 0);
 
                     if (isTls(sym, lower.bin_file)) {
-                        lower.result_insts[lower.result_insts_len] =
-                            try Instruction.new(.none, .mov, &[_]Operand{
-                            .{ .reg = ops[0].reg.to64() },
-                            .{ .mem = Memory.sib(.qword, .{ .base = .{ .reg = .fs } }) },
-                        });
-                        lower.result_insts_len += 1;
-                        _ = lower.reloc(.{ .linker_reloc = sym });
-                        if (lower.bin_file.cast(link.File.Elf)) |elf_file| {
-                            const sym_index = elf_file.zigObjectPtr().?.symbol(sym.sym_index);
-                            elf_file.symbol(sym_index).flags.needs_zig_got = false;
-                        }
-                        emit_mnemonic = .lea;
-                        switch (mnemonic) {
-                            .lea, .mov => break :op .{ .mem = Memory.sib(mem_op.sib.ptr_size, .{
+                        // TODO handle extern TLS vars, i.e., emit GD model
+                        if (lower.bin_file.options.pic) {
+                            // Here, we currently assume local dynamic TLS vars, and so
+                            // we emit LD model.
+                            _ = lower.reloc(.{ .linker_tlsld = sym });
+                            lower.result_insts[lower.result_insts_len] =
+                                try Instruction.new(.none, .lea, &[_]Operand{
+                                .{ .reg = ops[0].reg.to64() },
+                                .{ .mem = Memory.rip(mem_op.sib.ptr_size, 0) },
+                            });
+                            lower.result_insts_len += 1;
+                            if (lower.bin_file.cast(link.File.Elf)) |elf_file| {
+                                _ = lower.reloc(.{ .linker_extern_fn = .{
+                                    .atom_index = sym.atom_index,
+                                    .sym_index = try elf_file.getGlobalSymbol("__tls_get_address", null),
+                                } });
+                            }
+                            lower.result_insts[lower.result_insts_len] =
+                                try Instruction.new(.none, .call, &[_]Operand{
+                                .{ .imm = Immediate.s(0) },
+                            });
+                            lower.result_insts_len += 1;
+                            _ = lower.reloc(.{ .linker_dtpoff = sym });
+                            if (lower.bin_file.cast(link.File.Elf)) |elf_file| {
+                                const sym_index = elf_file.zigObjectPtr().?.symbol(sym.sym_index);
+                                elf_file.symbol(sym_index).flags.needs_zig_got = false;
+                            }
+                            emit_mnemonic = .lea;
+                            break :op .{ .mem = Memory.sib(mem_op.sib.ptr_size, .{
                                 .base = .{ .reg = ops[0].reg.to64() },
                                 .disp = undefined,
-                            }) },
-                            else => unreachable,
+                            }) };
+                        } else {
+                            // Since we are linking statically, we emit LE model directly.
+                            lower.result_insts[lower.result_insts_len] =
+                                try Instruction.new(.none, .mov, &[_]Operand{
+                                .{ .reg = ops[0].reg.to64() },
+                                .{ .mem = Memory.sib(.qword, .{ .base = .{ .reg = .fs } }) },
+                            });
+                            lower.result_insts_len += 1;
+                            _ = lower.reloc(.{ .linker_reloc = sym });
+                            if (lower.bin_file.cast(link.File.Elf)) |elf_file| {
+                                const sym_index = elf_file.zigObjectPtr().?.symbol(sym.sym_index);
+                                elf_file.symbol(sym_index).flags.needs_zig_got = false;
+                            }
+                            emit_mnemonic = .lea;
+                            break :op .{ .mem = Memory.sib(mem_op.sib.ptr_size, .{
+                                .base = .{ .reg = ops[0].reg.to64() },
+                                .disp = undefined,
+                            }) };
                         }
                     }