Skip to content

Commit

Permalink
Merge pull request #156 from kubkon/elf-ownership
Browse files Browse the repository at this point in the history
elf: sync with Zig's ELF linker
  • Loading branch information
kubkon authored Sep 20, 2024
2 parents 2401d66 + 63c0a76 commit 63b47ae
Show file tree
Hide file tree
Showing 13 changed files with 2,166 additions and 1,678 deletions.
1,208 changes: 420 additions & 788 deletions src/Elf.zig

Large diffs are not rendered by default.

202 changes: 112 additions & 90 deletions src/Elf/Atom.zig

Large diffs are not rendered by default.

445 changes: 401 additions & 44 deletions src/Elf/InternalObject.zig

Large diffs are not rendered by default.

874 changes: 568 additions & 306 deletions src/Elf/Object.zig

Large diffs are not rendered by default.

330 changes: 227 additions & 103 deletions src/Elf/SharedObject.zig

Large diffs are not rendered by default.

104 changes: 57 additions & 47 deletions src/Elf/Symbol.zig
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,15 @@ name: u32 = 0,
file: File.Index = 0,

/// Atom containing this symbol if any.
/// Index of 0 means there is no associated atom with this symbol.
/// Use `getAtom` to get the pointer to the atom.
atom: Atom.Index = 0,
ref: Elf.Ref = .{},

/// Assigned output section index for this symbol.
shndx: u32 = 0,

/// Index of the source symbol this symbol references.
/// Use `getSourceSymbol` to pull the source symbol from the relevant file.
sym_idx: Index = 0,
/// Use `getElfSym` to pull the source symbol from the relevant file.
esym_idx: Index = 0,

/// Index of the source version symbol this symbol references if any.
/// If the symbol is unversioned it will have either VER_NDX_LOCAL or VER_NDX_GLOBAL.
Expand All @@ -32,54 +31,66 @@ extra: u32 = 0,

pub fn isAbs(symbol: Symbol, elf_file: *Elf) bool {
const file = symbol.getFile(elf_file).?;
if (file == .shared) return symbol.getSourceSymbol(elf_file).st_shndx == elf.SHN_ABS;
return !symbol.flags.import and symbol.getAtom(elf_file) == null and symbol.getMergeSubsection(elf_file) == null and symbol.shndx == 0 and file != .internal;
if (file == .shared) return symbol.getElfSym(elf_file).st_shndx == elf.SHN_ABS;
return !symbol.flags.import and symbol.getAtom(elf_file) == null and symbol.getMergeSubsection(elf_file) == null and symbol.getShndx(elf_file) == null and file != .internal;
}

pub fn isLocal(symbol: Symbol, elf_file: *Elf) bool {
if (elf_file.options.relocatable) return symbol.getSourceSymbol(elf_file).st_bind() == elf.STB_LOCAL;
if (elf_file.options.relocatable) return symbol.getElfSym(elf_file).st_bind() == elf.STB_LOCAL;
return !(symbol.flags.import or symbol.flags.@"export");
}

pub inline fn isIFunc(symbol: Symbol, elf_file: *Elf) bool {
pub fn isIFunc(symbol: Symbol, elf_file: *Elf) bool {
return symbol.getType(elf_file) == elf.STT_GNU_IFUNC;
}

pub fn getType(symbol: Symbol, elf_file: *Elf) u4 {
const file = symbol.getFile(elf_file).?;
const s_sym = symbol.getSourceSymbol(elf_file);
const s_sym = symbol.getElfSym(elf_file);
if (s_sym.st_type() == elf.STT_GNU_IFUNC and file == .shared) return elf.STT_FUNC;
return s_sym.st_type();
}

pub fn getName(symbol: Symbol, elf_file: *Elf) [:0]const u8 {
return elf_file.string_intern.getAssumeExists(symbol.name);
return switch (symbol.getFile(elf_file).?) {
inline else => |x| x.getString(symbol.name),
};
}

pub fn getShndx(symbol: Symbol, elf_file: *Elf) ?u32 {
if (symbol.getMergeSubsection(elf_file)) |msub|
return if (msub.alive) msub.getMergeSection(elf_file).out_shndx else null;
if (symbol.getAtom(elf_file)) |atom_ptr|
return if (atom_ptr.flags.alive) atom_ptr.out_shndx else null;
if (symbol.shndx == 0) return null;
return symbol.shndx;
}

pub fn getAtom(symbol: Symbol, elf_file: *Elf) ?*Atom {
return elf_file.getAtom(symbol.atom);
if (symbol.flags.merge_subsection) return null;
return elf_file.getAtom(symbol.ref);
}

pub fn getMergeSubsection(symbol: Symbol, elf_file: *Elf) ?*MergeSubsection {
if (!symbol.flags.merge_subsection) return null;
const extra = symbol.getExtra(elf_file).?;
return elf_file.getMergeSubsection(extra.subsection);
const msec = elf_file.getMergeSection(symbol.ref.file);
return msec.getMergeSubsection(symbol.ref.index);
}

pub inline fn getFile(symbol: Symbol, elf_file: *Elf) ?File {
pub fn getFile(symbol: Symbol, elf_file: *Elf) ?File {
return elf_file.getFile(symbol.file);
}

pub fn getSourceSymbol(symbol: Symbol, elf_file: *Elf) elf.Elf64_Sym {
pub fn getElfSym(symbol: Symbol, elf_file: *Elf) elf.Elf64_Sym {
const file = symbol.getFile(elf_file).?;
return switch (file) {
inline else => |x| x.symtab.items[symbol.sym_idx],
inline else => |x| x.symtab.items[symbol.esym_idx],
};
}

pub fn getSymbolRank(symbol: Symbol, elf_file: *Elf) u32 {
const file = symbol.getFile(elf_file) orelse return std.math.maxInt(u32);
const sym = symbol.getSourceSymbol(elf_file);
const sym = symbol.getElfSym(elf_file);
const in_archive = switch (file) {
.object => |x| !x.alive,
else => false,
Expand Down Expand Up @@ -117,7 +128,7 @@ pub fn getAddress(symbol: Symbol, opts: struct {
if (mem.startsWith(u8, sym_name, "__EH_FRAME_BEGIN__") or
mem.startsWith(u8, sym_name, "__EH_FRAME_LIST__") or
mem.startsWith(u8, sym_name, ".eh_frame_seg") or
symbol.getSourceSymbol(elf_file).st_type() == elf.STT_SECTION)
symbol.getElfSym(elf_file).st_type() == elf.STT_SECTION)
{
return @intCast(sh_addr);
}
Expand All @@ -144,36 +155,36 @@ pub fn getOutputSymtabIndex(symbol: Symbol, elf_file: *Elf) ?u32 {
const symtab_ctx = switch (file) {
inline else => |x| x.output_symtab_ctx,
};
const idx = symbol.getExtra(elf_file).?.symtab;
const idx = symbol.getExtra(elf_file).symtab;
return if (symbol.isLocal(elf_file)) idx + symtab_ctx.ilocal else idx + symtab_ctx.iglobal;
}

pub fn getGotAddress(symbol: Symbol, elf_file: *Elf) i64 {
if (!symbol.flags.got) return 0;
const extra = symbol.getExtra(elf_file).?;
const extra = symbol.getExtra(elf_file);
const entry = elf_file.got.entries.items[extra.got];
return entry.getAddress(elf_file);
}

pub fn getPltGotAddress(symbol: Symbol, elf_file: *Elf) i64 {
if (!(symbol.flags.plt and symbol.flags.got)) return 0;
const extra = symbol.getExtra(elf_file).?;
const extra = symbol.getExtra(elf_file);
const shdr = elf_file.sections.items(.shdr)[elf_file.plt_got_sect_index.?];
const cpu_arch = elf_file.options.cpu_arch.?;
return @intCast(shdr.sh_addr + extra.plt_got * PltGotSection.entrySize(cpu_arch));
}

pub fn getPltAddress(symbol: Symbol, elf_file: *Elf) i64 {
if (!symbol.flags.plt) return 0;
const extra = symbol.getExtra(elf_file).?;
const extra = symbol.getExtra(elf_file);
const shdr = elf_file.sections.items(.shdr)[elf_file.plt_sect_index.?];
const cpu_arch = elf_file.options.cpu_arch.?;
return @intCast(shdr.sh_addr + extra.plt * PltSection.entrySize(cpu_arch) + PltSection.preambleSize(cpu_arch));
}

pub fn getGotPltAddress(symbol: Symbol, elf_file: *Elf) i64 {
if (!symbol.flags.plt) return 0;
const extra = symbol.getExtra(elf_file).?;
const extra = symbol.getExtra(elf_file);
const shdr = elf_file.sections.items(.shdr)[elf_file.got_plt_sect_index.?];
return @intCast(shdr.sh_addr + extra.plt * 8 + GotPltSection.preamble_size);
}
Expand All @@ -186,29 +197,29 @@ pub fn getCopyRelAddress(symbol: Symbol, elf_file: *Elf) i64 {

pub fn getTlsGdAddress(symbol: Symbol, elf_file: *Elf) i64 {
if (!symbol.flags.tlsgd) return 0;
const extra = symbol.getExtra(elf_file).?;
const extra = symbol.getExtra(elf_file);
const entry = elf_file.got.entries.items[extra.tlsgd];
return entry.getAddress(elf_file);
}

pub fn getGotTpAddress(symbol: Symbol, elf_file: *Elf) i64 {
if (!symbol.flags.gottp) return 0;
const extra = symbol.getExtra(elf_file).?;
const extra = symbol.getExtra(elf_file);
const entry = elf_file.got.entries.items[extra.gottp];
return entry.getAddress(elf_file);
}

pub fn getTlsDescAddress(symbol: Symbol, elf_file: *Elf) i64 {
if (!symbol.flags.tlsdesc) return 0;
const extra = symbol.getExtra(elf_file).?;
const extra = symbol.getExtra(elf_file);
const entry = elf_file.got.entries.items[extra.tlsdesc];
return entry.getAddress(elf_file);
}

pub fn getAlignment(symbol: Symbol, elf_file: *Elf) !u64 {
const file = symbol.getFile(elf_file) orelse return 0;
const shared = file.shared;
const s_sym = symbol.getSourceSymbol(elf_file);
const s_sym = symbol.getElfSym(elf_file);
const shdr = shared.shdrs.items[s_sym.st_shndx];
const alignment = @max(1, shdr.sh_addralign);
return if (s_sym.st_value == 0)
Expand All @@ -227,14 +238,10 @@ const AddExtraOpts = struct {
tlsgd: ?u32 = null,
gottp: ?u32 = null,
tlsdesc: ?u32 = null,
subsection: ?u32 = null,
};

pub fn addExtra(symbol: *Symbol, opts: AddExtraOpts, elf_file: *Elf) !void {
if (symbol.getExtra(elf_file) == null) {
symbol.extra = try elf_file.addSymbolExtra(.{});
}
var extra = symbol.getExtra(elf_file).?;
pub fn addExtra(symbol: *Symbol, opts: AddExtraOpts, elf_file: *Elf) void {
var extra = symbol.getExtra(elf_file);
inline for (@typeInfo(@TypeOf(opts)).@"struct".fields) |field| {
if (@field(opts, field.name)) |x| {
@field(extra, field.name) = x;
Expand All @@ -243,31 +250,35 @@ pub fn addExtra(symbol: *Symbol, opts: AddExtraOpts, elf_file: *Elf) !void {
symbol.setExtra(extra, elf_file);
}

pub inline fn getExtra(symbol: Symbol, elf_file: *Elf) ?Extra {
return elf_file.getSymbolExtra(symbol.extra);
pub fn getExtra(symbol: Symbol, elf_file: *Elf) Extra {
return switch (symbol.getFile(elf_file).?) {
inline else => |x| x.getSymbolExtra(symbol.extra),
};
}

pub inline fn setExtra(symbol: Symbol, extra: Extra, elf_file: *Elf) void {
elf_file.setSymbolExtra(symbol.extra, extra);
pub fn setExtra(symbol: Symbol, extra: Extra, elf_file: *Elf) void {
return switch (symbol.getFile(elf_file).?) {
inline else => |x| x.setSymbolExtra(symbol.extra, extra),
};
}

pub fn setOutputSym(symbol: Symbol, elf_file: *Elf, out: *elf.Elf64_Sym) void {
const file = symbol.getFile(elf_file).?;
const s_sym = symbol.getSourceSymbol(elf_file);
const s_sym = symbol.getElfSym(elf_file);
const st_type = symbol.getType(elf_file);
const st_bind: u8 = blk: {
if (symbol.isLocal(elf_file)) break :blk 0;
if (symbol.flags.weak) break :blk elf.STB_WEAK;
if (file == .shared) break :blk elf.STB_GLOBAL;
break :blk s_sym.st_bind();
};
const st_shndx = blk: {
if (symbol.flags.copy_rel) break :blk elf_file.copy_rel_sect_index.?;
const st_shndx: u16 = blk: {
if (symbol.flags.copy_rel) break :blk @intCast(elf_file.copy_rel_sect_index.?);
if (file == .shared or s_sym.st_shndx == elf.SHN_UNDEF) break :blk elf.SHN_UNDEF;
if (elf_file.options.relocatable and s_sym.st_shndx == elf.SHN_COMMON) break :blk elf.SHN_COMMON;
if (symbol.getMergeSubsection(elf_file)) |msub| break :blk msub.getMergeSection(elf_file).out_shndx;
if (symbol.getMergeSubsection(elf_file)) |msub| break :blk @intCast(msub.getMergeSection(elf_file).out_shndx);
if (symbol.getAtom(elf_file) == null and file != .internal) break :blk elf.SHN_ABS;
break :blk symbol.shndx;
break :blk @intCast(symbol.getShndx(elf_file) orelse elf.SHN_UNDEF);
};
const st_value = blk: {
if (symbol.flags.copy_rel) break :blk symbol.getAddress(.{}, elf_file);
Expand Down Expand Up @@ -348,16 +359,16 @@ fn format2(
_ = options;
_ = unused_fmt_string;
const symbol = ctx.symbol;
try writer.print("%{d} : {s} : @{x}", .{ symbol.sym_idx, symbol.fmtName(ctx.elf_file), symbol.getAddress(.{}, ctx.elf_file) });
try writer.print("%{d} : {s} : @{x}", .{ symbol.esym_idx, symbol.fmtName(ctx.elf_file), symbol.getAddress(.{}, ctx.elf_file) });
if (symbol.getFile(ctx.elf_file)) |file| {
if (symbol.isAbs(ctx.elf_file)) {
if (symbol.getSourceSymbol(ctx.elf_file).st_shndx == elf.SHN_UNDEF) {
if (symbol.getElfSym(ctx.elf_file).st_shndx == elf.SHN_UNDEF) {
try writer.writeAll(" : undef");
} else {
try writer.writeAll(" : absolute");
}
} else if (symbol.shndx != 0) {
try writer.print(" : sect({d})", .{symbol.shndx});
} else if (symbol.getShndx(ctx.elf_file)) |shndx| {
try writer.print(" : sect({d})", .{shndx});
}
if (symbol.getAtom(ctx.elf_file)) |atom| {
try writer.print(" : atom({d})", .{atom.atom_index});
Expand Down Expand Up @@ -424,7 +435,6 @@ pub const Extra = struct {
tlsgd: u32 = 0,
gottp: u32 = 0,
tlsdesc: u32 = 0,
subsection: u32 = 0,
};

pub const Index = u32;
Expand Down
29 changes: 15 additions & 14 deletions src/Elf/Thunk.zig
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
value: i64 = 0,
out_shndx: u32 = 0,
symbols: std.AutoArrayHashMapUnmanaged(Symbol.Index, void) = .{},
symbols: std.AutoArrayHashMapUnmanaged(Elf.Ref, void) = .empty,
output_symtab_ctx: Elf.SymtabCtx = .{},

pub fn deinit(thunk: *Thunk, allocator: Allocator) void {
Expand All @@ -17,9 +17,9 @@ pub fn getAddress(thunk: Thunk, elf_file: *Elf) i64 {
return @as(i64, @intCast(shdr.sh_addr)) + thunk.value;
}

pub fn getTargetAddress(thunk: Thunk, sym_index: Symbol.Index, elf_file: *Elf) i64 {
pub fn getTargetAddress(thunk: Thunk, ref: Elf.Ref, elf_file: *Elf) i64 {
const cpu_arch = elf_file.options.cpu_arch.?;
return thunk.getAddress(elf_file) + @as(i64, @intCast(thunk.symbols.getIndex(sym_index).? * trampolineSize(cpu_arch)));
return thunk.getAddress(elf_file) + @as(i64, @intCast(thunk.symbols.getIndex(ref).? * trampolineSize(cpu_arch)));
}

pub fn isReachable(atom: *const Atom, rel: elf.Elf64_Rela, elf_file: *Elf) bool {
Expand Down Expand Up @@ -52,8 +52,8 @@ pub fn calcSymtabSize(thunk: *Thunk, elf_file: *Elf) void {
if (elf_file.options.strip_all) return;

thunk.output_symtab_ctx.nlocals = @as(u32, @intCast(thunk.symbols.keys().len));
for (thunk.symbols.keys()) |sym_index| {
const sym = elf_file.getSymbol(sym_index);
for (thunk.symbols.keys()) |ref| {
const sym = elf_file.getSymbol(ref).?;
thunk.output_symtab_ctx.strsize += @as(u32, @intCast(sym.getName(elf_file).len + "$thunk".len + 1));
}
}
Expand All @@ -62,8 +62,8 @@ pub fn writeSymtab(thunk: Thunk, elf_file: *Elf) void {
if (elf_file.options.strip_all) return;
const cpu_arch = elf_file.options.cpu_arch.?;

for (thunk.symbols.keys(), thunk.output_symtab_ctx.ilocal..) |sym_index, ilocal| {
const sym = elf_file.getSymbol(sym_index);
for (thunk.symbols.keys(), thunk.output_symtab_ctx.ilocal..) |ref, ilocal| {
const sym = elf_file.getSymbol(ref).?;
const st_name = @as(u32, @intCast(elf_file.strtab.items.len));
elf_file.strtab.appendSliceAssumeCapacity(sym.getName(elf_file));
elf_file.strtab.appendSliceAssumeCapacity("$thunk");
Expand All @@ -73,7 +73,7 @@ pub fn writeSymtab(thunk: Thunk, elf_file: *Elf) void {
.st_info = elf.STT_FUNC,
.st_other = 0,
.st_shndx = @intCast(thunk.out_shndx),
.st_value = @intCast(thunk.getTargetAddress(sym_index, elf_file)),
.st_value = @intCast(thunk.getTargetAddress(ref, elf_file)),
.st_size = trampolineSize(cpu_arch),
};
}
Expand Down Expand Up @@ -123,9 +123,9 @@ fn format2(
const thunk = ctx.thunk;
const elf_file = ctx.elf_file;
try writer.print("@{x} : size({x})\n", .{ thunk.value, thunk.size(elf_file) });
for (thunk.symbols.keys()) |index| {
const sym = elf_file.getSymbol(index);
try writer.print(" %{d} : {s} : @{x}\n", .{ index, sym.getName(elf_file), sym.value });
for (thunk.symbols.keys()) |ref| {
const sym = elf_file.getSymbol(ref).?;
try writer.print(" {} : {s} : @{x}\n", .{ ref, sym.getName(elf_file), sym.value });
}
}

Expand All @@ -136,7 +136,8 @@ const aarch64 = struct {
const r_type: elf.R_AARCH64 = @enumFromInt(rel.r_type());
if (r_type != .CALL26 and r_type != .JUMP26) return true;
const object = atom.getObject(elf_file);
const target = object.getSymbol(rel.r_sym(), elf_file);
const target_ref = object.resolveSymbol(rel.r_sym(), elf_file);
const target = elf_file.getSymbol(target_ref).?;
if (target.flags.plt) return false;
if (atom.out_shndx != target.shndx) return false;
const target_atom = target.getAtom(elf_file).?;
Expand All @@ -148,8 +149,8 @@ const aarch64 = struct {
}

fn write(thunk: Thunk, elf_file: *Elf, writer: anytype) !void {
for (thunk.symbols.keys(), 0..) |sym_index, i| {
const sym = elf_file.getSymbol(sym_index);
for (thunk.symbols.keys(), 0..) |ref, i| {
const sym = elf_file.getSymbol(ref).?;
const saddr = thunk.getAddress(elf_file) + @as(i64, @intCast(i * trampoline_size));
const taddr = sym.getAddress(.{}, elf_file);
const pages = try util.calcNumberOfPages(saddr, taddr);
Expand Down
Loading

0 comments on commit 63b47ae

Please sign in to comment.