Skip to content

Commit

Permalink
Merge pull request #67 from kubkon/elf-tls
Browse files Browse the repository at this point in the history
elf: let's finish off TLS implementation
  • Loading branch information
kubkon authored Jul 18, 2023
2 parents 82439c7 + ae99743 commit be80fd6
Show file tree
Hide file tree
Showing 10 changed files with 1,092 additions and 200 deletions.
138 changes: 72 additions & 66 deletions src/Elf.zig
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,9 @@ fn createEmpty(gpa: Allocator, options: Options, thread_pool: *ThreadPool) !*Elf
.arena = std.heap.ArenaAllocator.init(gpa).state,
.options = options,
.default_sym_version = if (options.output_mode == .lib or options.export_dynamic)
VER_NDX_GLOBAL
elf.VER_NDX_GLOBAL
else
VER_NDX_LOCAL,
elf.VER_NDX_LOCAL,
};

return self;
Expand Down Expand Up @@ -181,24 +181,50 @@ fn resolveFile(
obj: LinkObject,
search_dirs: []const []const u8,
) !LinkObject {
const GetFullPath = struct {
const Tag = enum {
dso,
ar,
none,
};

pub fn getFullPath(alloc: Allocator, dir: []const u8, path: []const u8, comptime tag: Tag) !?[]const u8 {
const suffix_str = switch (tag) {
.dso => "lib{s}.so",
.ar => "lib{s}.a",
.none => "{s}",
};
const full_path = try std.fmt.allocPrint(alloc, "{s}" ++ std.fs.path.sep_str ++ suffix_str, .{
dir,
path,
});
const tmp = fs.cwd().openFile(full_path, .{}) catch return null;
defer tmp.close();
return full_path;
}
};
const getFullPath = GetFullPath.getFullPath;

const full_path = full_path: {
if (mem.startsWith(u8, obj.path, "-l")) {
const path = obj.path["-l".len..];
if (!obj.static) {
const search_name = try std.fmt.allocPrint(arena, "lib{s}.so", .{path});
if (try resolveFileInDir(arena, search_dirs, search_name)) |full_path| break :full_path full_path;
for (search_dirs) |search_dir| {
if (!obj.static) {
if (try getFullPath(arena, search_dir, path, .dso)) |full_path| break :full_path full_path;
}
if (try getFullPath(arena, search_dir, path, .ar)) |full_path| break :full_path full_path;
}
const search_name = try std.fmt.allocPrint(arena, "lib{s}.a", .{path});
if (try resolveFileInDir(arena, search_dirs, search_name)) |full_path| break :full_path full_path;
self.base.fatal("{s}: library not found", .{path});
self.base.fatal("library not found '{s}'", .{path});
return error.ResolveFail;
}

const path = path: {
var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
const path = std.fs.realpath(obj.path, &buffer) catch |err| switch (err) {
error.FileNotFound => {
if (try resolveFileInDir(arena, search_dirs, obj.path)) |path| break :path path;
for (search_dirs) |search_dir| {
if (try getFullPath(arena, search_dir, obj.path, .none)) |path| break :path path;
}
self.base.fatal("file not found '{s}'", .{obj.path});
return error.ResolveFail;
},
Expand All @@ -215,20 +241,6 @@ fn resolveFile(
};
}

fn resolveFileInDir(arena: Allocator, search_dirs: []const []const u8, search_name: []const u8) !?[]const u8 {
for (search_dirs) |dir| {
const full_path = try fs.path.join(arena, &[_][]const u8{ dir, search_name });
if (checkFileExists(full_path)) return full_path;
}
return null;
}

fn checkFileExists(path: []const u8) bool {
const tmp = fs.cwd().openFile(path, .{}) catch return false;
defer tmp.close();
return true;
}

fn hasSharedLibraryExt(filename: []const u8) bool {
if (mem.endsWith(u8, filename, ".so")) return true;
// Look for .so.X, .so.X.Y, .so.X.Y.Z
Expand Down Expand Up @@ -599,25 +611,26 @@ fn initSections(self: *Elf) !void {
self.gnu_hash_sect_index = try self.addSection(.{
.name = ".gnu.hash",
.flags = elf.SHF_ALLOC,
.type = SHT_GNU_HASH,
.type = elf.SHT_GNU_HASH,
.addralign = 8,
});

const needs_versions = for (self.shared_objects.items) |index| {
if (self.getFile(index).?.shared.versym_sect_index != null) break true;
const needs_versions = for (self.dynsym.symbols.items) |dynsym| {
const symbol = self.getSymbol(dynsym.index);
if (symbol.flags.import and symbol.ver_idx & elf.VERSYM_VERSION > elf.VER_NDX_GLOBAL) break true;
} else false;
if (needs_versions) {
self.versym_sect_index = try self.addSection(.{
.name = ".gnu.version",
.flags = elf.SHF_ALLOC,
.type = SHT_GNU_versym,
.type = elf.SHT_GNU_VERSYM,
.addralign = @alignOf(elf.Elf64_Versym),
.entsize = @sizeOf(elf.Elf64_Versym),
});
self.verneed_sect_index = try self.addSection(.{
.name = ".gnu.version_r",
.flags = elf.SHF_ALLOC,
.type = SHT_GNU_verneed,
.type = elf.SHT_GNU_VERNEED,
.addralign = @alignOf(elf.Elf64_Verneed),
});
}
Expand Down Expand Up @@ -1120,10 +1133,10 @@ fn getSectionRank(self: *Elf, shndx: u16) u8 {
elf.SHT_NULL => return 0,
elf.SHT_DYNSYM => return 2,
elf.SHT_HASH => return 3,
SHT_GNU_HASH => return 3,
SHT_GNU_versym => return 4,
SHT_GNU_verdef => return 4,
SHT_GNU_verneed => return 4,
elf.SHT_GNU_HASH => return 3,
elf.SHT_GNU_VERSYM => return 4,
elf.SHT_GNU_VERDEF => return 4,
elf.SHT_GNU_VERNEED => return 4,

elf.SHT_PREINIT_ARRAY,
elf.SHT_INIT_ARRAY,
Expand Down Expand Up @@ -1742,7 +1755,7 @@ fn markImportsAndExports(self: *Elf) !void {
for (self.objects.items) |index| {
for (self.getFile(index).?.object.getGlobals()) |global_index| {
const global = self.getSymbol(global_index);
if (global.ver_idx == VER_NDX_LOCAL) continue;
if (global.ver_idx == elf.VER_NDX_LOCAL) continue;
const file = global.getFile(self) orelse continue;
const vis = @as(elf.STV, @enumFromInt(global.getSourceSymbol(self).st_other));
if (vis == .HIDDEN) continue;
Expand Down Expand Up @@ -1840,7 +1853,7 @@ fn claimUnresolved(self: *Elf) void {
.atom = 0,
.sym_idx = sym_idx,
.file = object.index,
.ver_idx = if (is_import) VER_NDX_LOCAL else self.default_sym_version,
.ver_idx = if (is_import) elf.VER_NDX_LOCAL else self.default_sym_version,
};
global.flags.import = is_import;
}
Expand Down Expand Up @@ -1900,7 +1913,7 @@ fn scanRelocs(self: *Elf) !void {
}
if (symbol.flags.got) {
log.debug("'{s}' needs GOT", .{symbol.getName(self)});
try self.got.addSymbol(index, self);
try self.got.addGotSymbol(index, self);
if (symbol.flags.import or symbol.isIFunc(self)) self.got.needs_rela = true;
}
if (symbol.flags.plt) {
Expand All @@ -1921,12 +1934,22 @@ fn scanRelocs(self: *Elf) !void {
try self.copy_rel.addSymbol(index, self);
}
if (symbol.flags.tlsgd) {
log.warn("'{s}' needs TLSGD", .{symbol.getName(self)});
log.debug("'{s}' needs TLSGD", .{symbol.getName(self)});
try self.got.addTlsGdSymbol(index, self);
}
if (symbol.flags.gottp) {
log.debug("'{s}' needs GOTTP", .{symbol.getName(self)});
try self.got.addGotTpSymbol(index, self);
}
if (symbol.flags.tlsdesc) {
log.debug("'{s}' needs TLSDESC", .{symbol.getName(self)});
try self.dynsym.addSymbol(index, self);
try self.got.addTlsDescSymbol(index, self);
}
}

if (self.needs_tlsld) {
log.warn("needs TLSLD", .{});
log.debug("needs TLSLD", .{});
self.got.emit_tlsld = true;
}
}
Expand All @@ -1951,7 +1974,7 @@ fn setDynsym(self: *Elf) void {
fn setVerSymtab(self: *Elf) !void {
if (self.versym_sect_index == null) return;
try self.versym.resize(self.base.allocator, self.dynsym.count());
self.versym.items[0] = VER_NDX_LOCAL;
self.versym.items[0] = elf.VER_NDX_LOCAL;
for (self.dynsym.symbols.items, 1..) |dynsym, i| {
const sym = self.getSymbol(dynsym.index);
self.versym.items[i] = sym.ver_idx;
Expand Down Expand Up @@ -2447,9 +2470,10 @@ fn sortRelaDyn(self: *Elf) void {
fn getNumIRelativeRelocs(self: *Elf) usize {
var count: usize = self.num_ifunc_dynrelocs;

for (self.got.symbols.items) |sym_index| {
const sym = self.getSymbol(sym_index);
if (sym.isIFunc(self)) count += 1;
for (self.got.symbols.items) |sym| {
if (sym != .got) continue;
const symbol = self.getSymbol(sym.getIndex());
if (symbol.isIFunc(self)) count += 1;
}

return count;
Expand Down Expand Up @@ -2495,7 +2519,7 @@ pub inline fn getPltGotEntryAddress(self: *Elf, index: u32) u64 {
}

pub inline fn getTlsLdAddress(self: *Elf) u64 {
return self.getGotEntryAddress(@as(u32, @intCast(self.got.symbols.items.len)));
return self.getGotEntryAddress(self.got.getTlsLdIndex());
}

pub fn getTpAddress(self: *Elf) u64 {
Expand Down Expand Up @@ -2600,8 +2624,12 @@ fn fmtDumpState(
try writer.print("{}\n", .{internal.fmtSymtab(self)});
}
try writer.writeAll("GOT\n");
for (self.got.symbols.items, 0..) |sym_index, i| {
try writer.print(" {d} => {d} '{s}'\n", .{ i, sym_index, self.getSymbol(sym_index).getName(self) });
for (self.got.symbols.items) |sym| {
try writer.print(" ({s}) {d} '{s}'\n", .{
@tagName(sym),
sym.getIndex(),
self.getSymbol(sym.getIndex()).getName(self),
});
}
try writer.writeByte('\n');
try writer.writeAll("PLT\n");
Expand Down Expand Up @@ -2679,28 +2707,6 @@ pub const null_sym = elf.Elf64_Sym{

pub const base_tag = Zld.Tag.elf;

pub const VERSYM_HIDDEN = 0x8000;
pub const VERSYM_VERSION = 0x7fff;

/// Symbol is local
pub const VER_NDX_LOCAL = 0;
/// Symbol is global
pub const VER_NDX_GLOBAL = 1;

/// Version definition of the file itself
pub const VER_FLG_BASE = 1;
/// Weak version identifier
pub const VER_FLG_WEAK = 2;

// VERDEF
pub const SHT_GNU_verdef = 0x6ffffffd;
// VERNEED
pub const SHT_GNU_verneed = 0x6ffffffe;
// VERSYM
pub const SHT_GNU_versym = 0x6fffffff;
// GNU_HASH
pub const SHT_GNU_HASH = 0x6ffffff6;

const std = @import("std");
const build_options = @import("build_options");
const builtin = @import("builtin");
Expand Down
Loading

0 comments on commit be80fd6

Please sign in to comment.