elf: correctly allocate TLS segment

This commit is contained in:
Jakub Konka
2023-09-28 14:59:09 +02:00
parent af00ac53b5
commit 785bd270ed
3 changed files with 70 additions and 23 deletions

View File

@@ -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,

View File

@@ -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 => {},
}
}

View File

@@ -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.* = .{