diff options
| author | Jakub Konka <kubkon@jakubkonka.com> | 2023-11-10 19:14:42 +0100 |
|---|---|---|
| committer | Jacob Young <jacobly0@users.noreply.github.com> | 2023-11-12 18:12:40 -0500 |
| commit | f34247c4bc3a400d23b99c8921c9b358bf5a3250 (patch) | |
| tree | dc4eca3ed433b40ef8160ee18137dca8dde6de77 /src | |
| parent | 70d8baaec11ca370b73fce72d7f3dfce2277455b (diff) | |
| download | zig-f34247c4bc3a400d23b99c8921c9b358bf5a3250.tar.gz zig-f34247c4bc3a400d23b99c8921c9b358bf5a3250.zip | |
elf: lower TLS data into appropriate TLS section
Diffstat (limited to 'src')
| -rw-r--r-- | src/link/Elf.zig | 15 | ||||
| -rw-r--r-- | src/link/Elf/Object.zig | 1 | ||||
| -rw-r--r-- | src/link/Elf/ZigObject.zig | 195 | ||||
| -rw-r--r-- | src/target.zig | 2 |
4 files changed, 135 insertions, 78 deletions
diff --git a/src/link/Elf.zig b/src/link/Elf.zig index b21a7254ff..90aff333db 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -4512,7 +4512,10 @@ fn allocateAllocSections(self: *Elf) error{OutOfMemory}!void { for (cover.items) |shndx| { const shdr = &self.shdrs.items[shndx]; - if (shdr.sh_type == elf.SHT_NOBITS) continue; + if (shdr.sh_type == elf.SHT_NOBITS) { + shdr.sh_offset = 0; + continue; + } off = alignment.@"align"(shndx, shdr.sh_addralign, off); shdr.sh_offset = off; off += shdr.sh_size; @@ -4640,6 +4643,9 @@ fn allocateSpecialPhdrs(self: *Elf) void { } fn allocateAtoms(self: *Elf) void { + if (self.zigObjectPtr()) |zig_object| { + zig_object.allocateTlvAtoms(self); + } for (self.objects.items) |index| { self.file(index).?.object.allocateAtoms(self); } @@ -4698,7 +4704,6 @@ fn writeAtoms(self: *Elf) !void { const atom_ptr = self.atom(atom_index).?; assert(atom_ptr.flags.alive); - const object = atom_ptr.file(self).?.object; const offset = math.cast(usize, atom_ptr.value - shdr.sh_addr - base_offset) orelse return error.Overflow; const size = math.cast(usize, atom_ptr.size) orelse return error.Overflow; @@ -4707,7 +4712,11 @@ fn writeAtoms(self: *Elf) !void { // TODO decompress directly into provided buffer const out_code = buffer[offset..][0..size]; - const in_code = try object.codeDecompressAlloc(self, atom_index); + const in_code = switch (atom_ptr.file(self).?) { + .object => |x| try x.codeDecompressAlloc(self, atom_index), + .zig_object => |x| try x.codeAlloc(self, atom_index), + else => unreachable, + }; defer gpa.free(in_code); @memcpy(out_code, in_code); diff --git a/src/link/Elf/Object.zig b/src/link/Elf/Object.zig index f595988e82..3e1e49407b 100644 --- a/src/link/Elf/Object.zig +++ b/src/link/Elf/Object.zig @@ -251,6 +251,7 @@ fn initOutputSection(self: Object, elf_file: *Elf, shdr: ElfShdr) error{OutOfMem .type = @"type", .flags = flags, .name = name, + .offset = std.math.maxInt(u32), }); return out_shndx; } diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index f08e5bacbb..d7f7bbc174 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -29,6 +29,9 @@ lazy_syms: LazySymbolTable = .{}, /// Table of tracked Decls. decls: DeclTable = .{}, +/// TLS variables indexed by Atom.Index. +tls_variables: TlsTable = .{}, + /// Table of unnamed constants associated with a parent `Decl`. /// We store them here so that we can free the constants whenever the `Decl` /// needs updating or is freed. @@ -137,6 +140,11 @@ pub fn deinit(self: *ZigObject, allocator: Allocator) void { self.anon_decls.deinit(allocator); } + for (self.tls_variables.values()) |*tlv| { + tlv.deinit(allocator); + } + self.tls_variables.deinit(allocator); + if (self.dwarf) |*dw| { dw.deinit(); } @@ -212,8 +220,6 @@ pub fn flushModule(self: *ZigObject, elf_file: *Elf) !void { self.saveDebugSectionsSizes(elf_file); } - try self.sortSymbols(elf_file); - // The point of flushModule() is to commit changes, so in theory, nothing should // be dirty after this. However, it is possible for some things to remain // dirty because they fail to be written in the event of compile errors, @@ -388,6 +394,19 @@ pub fn claimUnresolvedObject(self: ZigObject, elf_file: *Elf) void { } } +pub fn allocateTlvAtoms(self: ZigObject, elf_file: *Elf) void { + for (self.tls_variables.keys(), self.tls_variables.values()) |atom_index, tlv| { + const atom = elf_file.atom(atom_index) orelse continue; + if (!atom.flags.alive) continue; + const local = elf_file.symbol(tlv.symbol_index); + const shdr = elf_file.shdrs.items[atom.output_section_index]; + atom.value += shdr.sh_addr; + local.value += shdr.sh_addr; + + // TODO exported TLS vars + } +} + pub fn scanRelocs(self: *ZigObject, elf_file: *Elf, undefs: anytype) !void { for (self.atoms.items) |atom_index| { const atom = elf_file.atom(atom_index) orelse continue; @@ -421,72 +440,6 @@ pub fn markLive(self: *ZigObject, elf_file: *Elf) void { } } -fn sortSymbols(self: *ZigObject, elf_file: *Elf) error{OutOfMemory}!void { - _ = self; - _ = elf_file; - // const Entry = struct { - // index: Symbol.Index, - - // const Ctx = struct { - // zobj: ZigObject, - // efile: *Elf, - // }; - - // pub fn lessThan(ctx: Ctx, lhs: @This(), rhs: @This()) bool { - // const lhs_sym = ctx.efile.symbol(zobj.symbol(lhs.index)); - // const rhs_sym = ctx.efile.symbol(zobj.symbol(rhs.index)); - // if (lhs_sym.outputShndx() != null and rhs_sym.outputShndx() != null) { - // if (lhs_sym.output_section_index == rhs_sym.output_section_index) { - // if (lhs_sym.value == rhs_sym.value) { - // return lhs_sym.name_offset < rhs_sym.name_offset; - // } - // return lhs_sym.value < rhs_sym.value; - // } - // return lhs_sym.output_section_index < rhs_sym.output_section_index; - // } - // if (lhs_sym.outputShndx() != null) { - // if (rhs_sym.isAbs(ctx.efile)) return false; - // return true; - // } - // return false; - // } - // }; - - // const gpa = elf_file.base.allocator; - - // { - // const sorted = try gpa.alloc(Entry, self.local_symbols.items.len); - // defer gpa.free(sorted); - // for (0..self.local_symbols.items.len) |index| { - // sorted[i] = .{ .index = @as(Symbol.Index, @intCast(index)) }; - // } - // mem.sort(Entry, sorted, .{ .zobj = self, .efile = elf_file }, Entry.lessThan); - - // const backlinks = try gpa.alloc(Symbol.Index, sorted.len); - // defer gpa.free(backlinks); - // for (sorted, 0..) |entry, i| { - // backlinks[entry.index] = @as(Symbol.Index, @intCast(i)); - // } - - // const local_symbols = try self.local_symbols.toOwnedSlice(gpa); - // defer gpa.free(local_symbols); - - // try self.local_symbols.ensureTotalCapacityPrecise(gpa, local_symbols.len); - // for (sorted) |entry| { - // self.local_symbols.appendAssumeCapacity(local_symbols[entry.index]); - // } - - // for (self.) - // } - - // const sorted_globals = try gpa.alloc(Entry, self.global_symbols.items.len); - // defer gpa.free(sorted_globals); - // for (self.global_symbols.items, 0..) |index, i| { - // sorted_globals[i] = .{ .index = index }; - // } - // mem.sort(Entry, sorted_globals, elf_file, Entry.lessThan); -} - pub fn updateArSymtab(self: ZigObject, ar_symtab: *Archive.ArSymtab, elf_file: *Elf) error{OutOfMemory}!void { const gpa = elf_file.base.allocator; @@ -583,6 +536,13 @@ pub fn codeAlloc(self: ZigObject, elf_file: *Elf, atom_index: Atom.Index) ![]u8 const atom = elf_file.atom(atom_index).?; assert(atom.file_index == self.index); const shdr = &elf_file.shdrs.items[atom.outputShndx().?]; + + if (shdr.sh_flags & elf.SHF_TLS != 0) { + const tlv = self.tls_variables.get(atom_index).?; + const code = try gpa.dupe(u8, tlv.code); + return code; + } + const file_offset = shdr.sh_offset + atom.value - shdr.sh_addr; const size = std.math.cast(usize, atom.size) orelse return error.Overflow; const code = try gpa.alloc(u8, size); @@ -765,15 +725,37 @@ pub fn getOrCreateMetadataForDecl( return gop.value_ptr.symbol_index; } -fn getDeclShdrIndex(self: *ZigObject, elf_file: *Elf, decl_index: Module.Decl.Index, code: []const u8) u16 { +fn getDeclShdrIndex( + self: *ZigObject, + elf_file: *Elf, + decl: *const Module.Decl, + code: []const u8, +) error{OutOfMemory}!u16 { _ = self; const mod = elf_file.base.options.module.?; - const decl = mod.declPtr(decl_index); + const single_threaded = elf_file.base.options.single_threaded; const shdr_index = switch (decl.ty.zigTypeTag(mod)) { - // TODO: what if this is a function pointer? .Fn => elf_file.zig_text_section_index.?, else => blk: { if (decl.getOwnedVariable(mod)) |variable| { + if (variable.is_threadlocal and !single_threaded) { + const is_all_zeroes = for (code) |byte| { + if (byte != 0) break false; + } else true; + if (is_all_zeroes) break :blk elf_file.sectionByName(".tbss") orelse try elf_file.addSection(.{ + .type = elf.SHT_NOBITS, + .flags = elf.SHF_ALLOC | elf.SHF_WRITE | elf.SHF_TLS, + .name = ".tbss", + .offset = std.math.maxInt(u32), + }); + + break :blk elf_file.sectionByName(".tdata") orelse try elf_file.addSection(.{ + .type = elf.SHT_PROGBITS, + .flags = elf.SHF_ALLOC | elf.SHF_WRITE | elf.SHF_TLS, + .name = ".tdata", + .offset = std.math.maxInt(u32), + }); + } if (variable.is_const) break :blk elf_file.zig_data_rel_ro_section_index.?; if (variable.init.toValue().isUndefDeep(mod)) { const mode = elf_file.base.options.optimize_mode; @@ -799,6 +781,7 @@ fn updateDeclCode( elf_file: *Elf, decl_index: Module.Decl.Index, sym_index: Symbol.Index, + shdr_index: u16, code: []const u8, stt_bits: u8, ) !void { @@ -815,7 +798,6 @@ fn updateDeclCode( const esym = &self.local_esyms.items(.elf_sym)[sym.esym_index]; const atom_ptr = sym.atom(elf_file).?; - const shdr_index = self.getDeclShdrIndex(elf_file, decl_index, code); sym.output_section_index = shdr_index; atom_ptr.output_section_index = shdr_index; @@ -893,6 +875,53 @@ fn updateDeclCode( } } +fn updateTlv( + self: *ZigObject, + elf_file: *Elf, + decl_index: Module.Decl.Index, + sym_index: Symbol.Index, + shndx: u16, + code: []const u8, +) !void { + const gpa = elf_file.base.allocator; + const mod = elf_file.base.options.module.?; + const decl = mod.declPtr(decl_index); + const decl_name = mod.intern_pool.stringToSlice(try decl.getFullyQualifiedName(mod)); + + log.debug("updateTlv {s}{*}", .{ decl_name, decl }); + + const required_alignment = decl.getAlignment(mod); + + const sym = elf_file.symbol(sym_index); + const esym = &self.local_esyms.items(.elf_sym)[sym.esym_index]; + const atom_ptr = sym.atom(elf_file).?; + + sym.output_section_index = shndx; + atom_ptr.output_section_index = shndx; + + sym.name_offset = try self.strtab.insert(gpa, decl_name); + atom_ptr.flags.alive = true; + atom_ptr.name_offset = sym.name_offset; + esym.st_name = sym.name_offset; + esym.st_info |= elf.STT_TLS; + esym.st_size = code.len; + + atom_ptr.alignment = required_alignment; + atom_ptr.size = code.len; + + { + const gop = try self.tls_variables.getOrPut(gpa, atom_ptr.atom_index); + assert(!gop.found_existing); // TODO incremental updates + gop.value_ptr.* = .{ .symbol_index = sym_index, .code = try gpa.dupe(u8, code) }; + } + + { + const gop = try elf_file.output_sections.getOrPut(gpa, atom_ptr.output_section_index); + if (!gop.found_existing) gop.value_ptr.* = .{}; + try gop.value_ptr.append(gpa, atom_ptr.atom_index); + } +} + pub fn updateFunc( self: *ZigObject, elf_file: *Elf, @@ -947,7 +976,10 @@ pub fn updateFunc( return; }, }; - try self.updateDeclCode(elf_file, decl_index, sym_index, code, elf.STT_FUNC); + + const shndx = try self.getDeclShdrIndex(elf_file, decl, code); + try self.updateDeclCode(elf_file, decl_index, sym_index, shndx, code, elf.STT_FUNC); + if (decl_state) |*ds| { const sym = elf_file.symbol(sym_index); try self.dwarf.?.commitDeclState( @@ -1026,7 +1058,12 @@ pub fn updateDecl( }, }; - try self.updateDeclCode(elf_file, decl_index, sym_index, code, elf.STT_OBJECT); + const shndx = try self.getDeclShdrIndex(elf_file, decl, code); + if (elf_file.shdrs.items[shndx].sh_flags & elf.SHF_TLS != 0) + try self.updateTlv(elf_file, decl_index, sym_index, shndx, code) + else + try self.updateDeclCode(elf_file, decl_index, sym_index, shndx, code, elf.STT_OBJECT); + if (decl_state) |*ds| { const sym = elf_file.symbol(sym_index); try self.dwarf.?.commitDeclState( @@ -1454,11 +1491,21 @@ const DeclMetadata = struct { } }; +const TlsVariable = struct { + symbol_index: Symbol.Index, + code: []const u8, + + fn deinit(tlv: *TlsVariable, allocator: Allocator) void { + allocator.free(tlv.code); + } +}; + const AtomList = std.ArrayListUnmanaged(Atom.Index); const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(Symbol.Index)); const DeclTable = std.AutoHashMapUnmanaged(Module.Decl.Index, DeclMetadata); const AnonDeclTable = std.AutoHashMapUnmanaged(InternPool.Index, DeclMetadata); const LazySymbolTable = std.AutoArrayHashMapUnmanaged(Module.Decl.OptionalIndex, LazySymbolMetadata); +const TlsTable = std.AutoArrayHashMapUnmanaged(Atom.Index, TlsVariable); const assert = std.debug.assert; const builtin = @import("builtin"); diff --git a/src/target.zig b/src/target.zig index 59528ed139..cac9aa11b0 100644 --- a/src/target.zig +++ b/src/target.zig @@ -654,7 +654,7 @@ pub fn supportsTailCall(target: std.Target, backend: std.builtin.CompilerBackend pub fn supportsThreads(target: std.Target, backend: std.builtin.CompilerBackend) bool { return switch (backend) { - .stage2_x86_64 => target.ofmt == .macho, + .stage2_x86_64 => target.ofmt == .macho or target.ofmt == .elf, else => true, }; } |
