Commit 785bd270ed

Jakub Konka <kubkon@jakubkonka.com>
2023-09-28 14:59:09
elf: correctly allocate TLS segment
1 parent af00ac5
Changed files (3)
src/link/Elf/Atom.zig
@@ -388,6 +388,12 @@ pub fn scanRelocs(self: Atom, elf_file: *Elf, undefs: anytype) !void {
 
             elf.R_X86_64_PC32 => {},
 
+            elf.R_X86_64_TPOFF32,
+            elf.R_X86_64_TPOFF64,
+            => {
+                // if (is_shared) self.picError(symbol, rel, elf_file);
+            },
+
             else => {
                 var err = try elf_file.addErrorWithNotes(1);
                 try err.addMsg(elf_file, "fatal linker error: unhandled relocation type {}", .{
@@ -473,9 +479,9 @@ pub fn resolveRelocs(self: Atom, elf_file: *Elf, code: []u8) !void {
         // Relative offset to the start of the global offset table.
         const G = @as(i64, @intCast(target.gotAddress(elf_file))) - GOT;
         // // Address of the thread pointer.
-        // const TP = @as(i64, @intCast(elf_file.getTpAddress()));
+        const TP = @as(i64, @intCast(elf_file.tpAddress()));
         // // Address of the dynamic thread pointer.
-        // const DTP = @as(i64, @intCast(elf_file.getDtpAddress()));
+        // const DTP = @as(i64, @intCast(elf_file.dtpAddress()));
 
         relocs_log.debug("  {s}: {x}: [{x} => {x}] G({x}) ({s})", .{
             fmtRelocType(r_type),
@@ -522,6 +528,9 @@ pub fn resolveRelocs(self: Atom, elf_file: *Elf, code: []u8) !void {
                 try cwriter.writeIntLittle(i32, @as(i32, @intCast(G + GOT + A - P)));
             },
 
+            elf.R_X86_64_TPOFF32 => try cwriter.writeIntLittle(i32, @as(i32, @truncate(S + A - TP))),
+            elf.R_X86_64_TPOFF64 => try cwriter.writeIntLittle(i64, S + A - TP),
+
             else => {},
         }
     }
src/link/Elf/Symbol.zig
@@ -196,9 +196,9 @@ pub fn setOutputSym(symbol: Symbol, elf_file: *Elf, out: *elf.Elf64_Sym) void {
         //     if (symbol.flags.is_canonical) break :blk symbol.address(.{}, elf_file);
         //     break :blk 0;
         // }
-        // if (st_shndx == elf.SHN_ABS) break :blk symbol.value;
-        // const shdr = &elf_file.sections.items(.shdr)[st_shndx];
-        // if (Elf.shdrIsTls(shdr)) break :blk symbol.value - elf_file.getTlsAddress();
+        if (st_shndx == elf.SHN_ABS) break :blk symbol.value;
+        const shdr = &elf_file.shdrs.items[st_shndx];
+        if (shdr.sh_flags & elf.SHF_TLS != 0) break :blk symbol.value - elf_file.tlsAddress();
         break :blk symbol.value;
     };
     out.* = .{
src/link/Elf.zig
@@ -247,7 +247,8 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf {
     else
         elf.VER_NDX_LOCAL;
 
-    var dwarf: ?Dwarf = if (!options.strip and options.module != null)
+    const use_llvm = options.use_llvm;
+    var dwarf: ?Dwarf = if (!options.strip and options.module != null and !use_llvm)
         Dwarf.init(gpa, &self.base, options.target)
     else
         null;
@@ -264,7 +265,6 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf {
         .page_size = page_size,
         .default_sym_version = default_sym_version,
     };
-    const use_llvm = options.use_llvm;
     if (use_llvm and options.module != null) {
         self.llvm_object = try LlvmObject.create(gpa, options);
     }
@@ -643,6 +643,13 @@ pub fn populateMissingMetadata(self: *Elf) !void {
         }
 
         if (self.phdr_load_tls_zerofill_index == null) {
+            // TODO .tbss doesn't need any physical or memory representation (aka a loadable segment)
+            // since the loader only cares about the PT_TLS to work out TLS size. However, when
+            // relocating we need to have .tdata and .tbss contiguously laid out so that we can
+            // work out correct offsets to the start/end of the TLS segment. I am thinking that
+            // perhaps it's possible to completely spoof it by having an abstracted mechanism
+            // for this that wouldn't require us to explicitly track .tbss. Anyhow, for now,
+            // we go the savage route of treating .tbss like .bss.
             const alignment = if (is_linux) self.page_size else @as(u16, ptr_size);
             self.phdr_load_tls_zerofill_index = try self.allocateSegment(.{
                 .size = 0,
@@ -655,6 +662,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {
         }
 
         if (self.phdr_tls_index == null) {
+            self.phdr_tls_index = @intCast(self.phdrs.items.len);
             const phdr_tdata = &self.phdrs.items[self.phdr_load_tls_data_index.?];
             const phdr_tbss = &self.phdrs.items[self.phdr_load_tls_zerofill_index.?];
             try self.phdrs.append(gpa, .{
@@ -1280,6 +1288,36 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
     try self.allocateObjects();
     self.allocateLinkerDefinedSymbols();
 
+    // .bss always overlaps .data in file offset, but is zero-sized in file so it doesn't
+    // get mapped by the loader
+    if (self.data_section_index) |data_shndx| blk: {
+        const bss_shndx = self.bss_section_index orelse break :blk;
+        const data_phndx = self.phdr_to_shdr_table.get(data_shndx).?;
+        const bss_phndx = self.phdr_to_shdr_table.get(bss_shndx).?;
+        self.shdrs.items[bss_shndx].sh_offset = self.shdrs.items[data_shndx].sh_offset;
+        self.phdrs.items[bss_phndx].p_offset = self.phdrs.items[data_phndx].p_offset;
+    }
+
+    // Same treatment for .tbss section.
+    if (self.tdata_section_index) |tdata_shndx| blk: {
+        const tbss_shndx = self.tbss_section_index orelse break :blk;
+        const tdata_phndx = self.phdr_to_shdr_table.get(tdata_shndx).?;
+        const tbss_phndx = self.phdr_to_shdr_table.get(tbss_shndx).?;
+        self.shdrs.items[tbss_shndx].sh_offset = self.shdrs.items[tdata_shndx].sh_offset;
+        self.phdrs.items[tbss_phndx].p_offset = self.phdrs.items[tdata_phndx].p_offset;
+    }
+
+    if (self.phdr_tls_index) |tls_index| {
+        const tdata_phdr = &self.phdrs.items[self.phdr_load_tls_data_index.?];
+        const tbss_phdr = &self.phdrs.items[self.phdr_load_tls_zerofill_index.?];
+        const phdr = &self.phdrs.items[tls_index];
+        phdr.p_offset = tdata_phdr.p_offset;
+        phdr.p_filesz = tdata_phdr.p_filesz;
+        phdr.p_vaddr = tdata_phdr.p_vaddr;
+        phdr.p_paddr = tdata_phdr.p_vaddr;
+        phdr.p_memsz = tbss_phdr.p_vaddr + tbss_phdr.p_memsz - tdata_phdr.p_vaddr;
+    }
+
     // Beyond this point, everything has been allocated a virtual address and we can resolve
     // the relocations, and commit objects to file.
     if (self.zig_module_index) |index| {
@@ -1325,22 +1363,6 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
     try self.updateSymtabSize();
     try self.writeSymtab();
 
-    // .bss always overlaps .data in file offset, but is zero-sized in file so it doesn't
-    // get mapped by the loader
-    if (self.data_section_index) |data_shndx| blk: {
-        const bss_shndx = self.bss_section_index orelse break :blk;
-        const data_phndx = self.phdr_to_shdr_table.get(data_shndx).?;
-        const bss_phndx = self.phdr_to_shdr_table.get(bss_shndx).?;
-        self.shdrs.items[bss_shndx].sh_offset = self.shdrs.items[data_shndx].sh_offset;
-        self.phdrs.items[bss_phndx].p_offset = self.phdrs.items[data_phndx].p_offset;
-    }
-
-    // Same treatment for .tbss section.
-    if (self.tdata_section_index) |tdata_shndx| blk: {
-        const tbss_shndx = self.tbss_section_index orelse break :blk;
-        self.shdrs.items[tbss_shndx].sh_offset = self.shdrs.items[tdata_shndx].sh_offset;
-    }
-
     // Dump the state for easy debugging.
     // State can be dumped via `--debug-log link_state`.
     if (build_options.enable_logging) {
@@ -4066,6 +4088,22 @@ pub fn comdatGroupOwner(self: *Elf, index: ComdatGroupOwner.Index) *ComdatGroupO
     return &self.comdat_groups_owners.items[index];
 }
 
+pub fn tpAddress(self: *Elf) u64 {
+    const index = self.phdr_tls_index orelse return 0;
+    const phdr = self.phdrs.items[index];
+    return mem.alignForward(u64, phdr.p_vaddr + phdr.p_memsz, phdr.p_align);
+}
+
+pub fn dtpAddress(self: *Elf) u64 {
+    return self.tlsAddress();
+}
+
+pub fn tlsAddress(self: *Elf) u64 {
+    const index = self.phdr_tls_index orelse return 0;
+    const phdr = self.phdrs.items[index];
+    return phdr.p_vaddr;
+}
+
 const ErrorWithNotes = struct {
     /// Allocated index in misc_errors array.
     index: usize,