Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

elf: sync with Zig's ELF linker #156

Merged
merged 19 commits into from
Sep 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading