diff options
| author | Jakub Konka <kubkon@jakubkonka.com> | 2024-02-21 19:06:10 +0100 |
|---|---|---|
| committer | Jakub Konka <kubkon@jakubkonka.com> | 2024-02-21 19:06:10 +0100 |
| commit | 60bc2e7616b0fd42c98ba8b9e0b212439b6ea1a0 (patch) | |
| tree | a7b067cbb12e047718cc324aa912ca613a22700a /src/link/Elf | |
| parent | 1ca004176f8f76b1d54d878e002ebcc4362f9b92 (diff) | |
| download | zig-60bc2e7616b0fd42c98ba8b9e0b212439b6ea1a0.tar.gz zig-60bc2e7616b0fd42c98ba8b9e0b212439b6ea1a0.zip | |
elf: simplify logic for handling scanning relocs on different arches
Diffstat (limited to 'src/link/Elf')
| -rw-r--r-- | src/link/Elf/Atom.zig | 327 | ||||
| -rw-r--r-- | src/link/Elf/relocation.zig | 17 |
2 files changed, 200 insertions, 144 deletions
diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index 0de33aaf28..5293822315 100644 --- a/src/link/Elf/Atom.zig +++ b/src/link/Elf/Atom.zig @@ -300,7 +300,7 @@ pub fn free(self: *Atom, elf_file: *Elf) void { self.* = .{}; } -pub fn relocs(self: Atom, elf_file: *Elf) []align(1) const elf.Elf64_Rela { +pub fn relocs(self: Atom, elf_file: *Elf) []const elf.Elf64_Rela { const shndx = self.relocsShndx() orelse return &[0]elf.Elf64_Rela{}; return switch (self.file(elf_file).?) { .zig_object => |x| x.relocs.items[shndx].items, @@ -394,11 +394,54 @@ pub fn scanRelocsRequiresCode(self: Atom, elf_file: *Elf) bool { return false; } -pub fn scanRelocs(self: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype) !void { - switch (elf_file.getTarget().cpu.arch) { - .x86_64 => try x86_64.scanRelocs(self, elf_file, code, undefs), - else => return error.UnsupportedCpuArch, +pub fn scanRelocs(self: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype) RelocError!void { + const cpu_arch = elf_file.getTarget().cpu.arch; + const file_ptr = self.file(elf_file).?; + const rels = self.relocs(elf_file); + + var has_reloc_errors = false; + var it = RelocsIterator{ .relocs = rels }; + while (it.next()) |rel| { + const r_kind = relocation.decode(rel.r_type(), cpu_arch); + if (r_kind == .none) continue; + + const symbol_index = switch (file_ptr) { + .zig_object => |x| x.symbol(rel.r_sym()), + .object => |x| x.symbols.items[rel.r_sym()], + else => unreachable, + }; + const symbol = elf_file.symbol(symbol_index); + + // Check for violation of One Definition Rule for COMDATs. + if (symbol.file(elf_file) == null) { + // TODO convert into an error + log.debug("{}: {s}: {s} refers to a discarded COMDAT section", .{ + file_ptr.fmtPath(), + self.name(elf_file), + symbol.name(elf_file), + }); + continue; + } + + // Report an undefined symbol. + if (try self.reportUndefined(elf_file, symbol, symbol_index, rel, undefs)) continue; + + if (symbol.isIFunc(elf_file)) { + symbol.flags.needs_got = true; + symbol.flags.needs_plt = true; + } + + // While traversing relocations, mark symbols that require special handling such as + // pointer indirection via GOT, or a stub trampoline via PLT. + switch (elf_file.getTarget().cpu.arch) { + .x86_64 => x86_64.scanReloc(self, elf_file, rel, symbol, code, &it) catch |err| switch (err) { + error.RelocFailure => has_reloc_errors = true, + else => |e| return e, + }, + else => return error.UnsupportedCpuArch, + } } + if (has_reloc_errors) return error.RelocFailure; } fn scanReloc( @@ -407,7 +450,7 @@ fn scanReloc( rel: elf.Elf64_Rela, action: RelocAction, elf_file: *Elf, -) error{OutOfMemory}!void { +) RelocError!void { const is_writeable = self.inputShdr(elf_file).sh_flags & elf.SHF_WRITE != 0; const num_dynrelocs = switch (self.file(elf_file).?) { .linker_defined => unreachable, @@ -554,7 +597,7 @@ fn dataType(symbol: *const Symbol, elf_file: *Elf) u2 { return 3; } -fn reportUnhandledRelocError(self: Atom, rel: elf.Elf64_Rela, elf_file: *Elf) error{OutOfMemory}!void { +fn reportUnhandledRelocError(self: Atom, rel: elf.Elf64_Rela, elf_file: *Elf) RelocError!void { var err = try elf_file.addErrorWithNotes(1); try err.addMsg(elf_file, "fatal linker error: unhandled relocation type {} at offset 0x{x}", .{ relocation.fmtRelocType(rel.r_type(), elf_file.getTarget().cpu.arch), @@ -564,6 +607,7 @@ fn reportUnhandledRelocError(self: Atom, rel: elf.Elf64_Rela, elf_file: *Elf) er self.file(elf_file).?.fmtPath(), self.name(elf_file), }); + return error.RelocFailure; } fn reportTextRelocError( @@ -571,7 +615,7 @@ fn reportTextRelocError( symbol: *const Symbol, rel: elf.Elf64_Rela, elf_file: *Elf, -) error{OutOfMemory}!void { +) RelocError!void { var err = try elf_file.addErrorWithNotes(1); try err.addMsg(elf_file, "relocation at offset 0x{x} against symbol '{s}' cannot be used", .{ rel.r_offset, @@ -581,6 +625,7 @@ fn reportTextRelocError( self.file(elf_file).?.fmtPath(), self.name(elf_file), }); + return error.RelocFailure; } fn reportPicError( @@ -588,7 +633,7 @@ fn reportPicError( symbol: *const Symbol, rel: elf.Elf64_Rela, elf_file: *Elf, -) error{OutOfMemory}!void { +) RelocError!void { var err = try elf_file.addErrorWithNotes(2); try err.addMsg(elf_file, "relocation at offset 0x{x} against symbol '{s}' cannot be used", .{ rel.r_offset, @@ -599,6 +644,7 @@ fn reportPicError( self.name(elf_file), }); try err.addNote(elf_file, "recompile with -fPIC", .{}); + return error.RelocFailure; } fn reportNoPicError( @@ -606,7 +652,7 @@ fn reportNoPicError( symbol: *const Symbol, rel: elf.Elf64_Rela, elf_file: *Elf, -) error{OutOfMemory}!void { +) RelocError!void { var err = try elf_file.addErrorWithNotes(2); try err.addMsg(elf_file, "relocation at offset 0x{x} against symbol '{s}' cannot be used", .{ rel.r_offset, @@ -617,6 +663,7 @@ fn reportNoPicError( self.name(elf_file), }); try err.addNote(elf_file, "recompile with -fno-PIC", .{}); + return error.RelocFailure; } // This function will report any undefined non-weak symbols that are not imports. @@ -627,7 +674,7 @@ fn reportUndefined( sym_index: Symbol.Index, rel: elf.Elf64_Rela, undefs: anytype, -) !void { +) !bool { const comp = elf_file.base.comp; const gpa = comp.gpa; const rel_esym = switch (self.file(elf_file).?) { @@ -647,7 +694,10 @@ fn reportUndefined( gop.value_ptr.* = std.ArrayList(Atom.Index).init(gpa); } try gop.value_ptr.append(self.atom_index); + return true; } + + return false; } pub fn resolveRelocsAlloc(self: Atom, elf_file: *Elf, code: []u8) !void { @@ -831,152 +881,123 @@ pub const Flags = packed struct { }; const x86_64 = struct { - fn scanRelocs(atom: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype) !void { + fn scanReloc( + atom: Atom, + elf_file: *Elf, + rel: elf.Elf64_Rela, + symbol: *Symbol, + code: ?[]const u8, + it: *RelocsIterator, + ) !void { const is_static = elf_file.base.isStatic(); const is_dyn_lib = elf_file.base.isDynLib(); - const file_ptr = atom.file(elf_file).?; - const rels = atom.relocs(elf_file); - var i: usize = 0; - while (i < rels.len) : (i += 1) { - const rel = rels[i]; - const r_type: elf.R_X86_64 = @enumFromInt(rel.r_type()); - - if (r_type == .NONE) continue; - - const r_offset = std.math.cast(usize, rel.r_offset) orelse return error.Overflow; - const symbol_index = switch (file_ptr) { - .zig_object => |x| x.symbol(rel.r_sym()), - .object => |x| x.symbols.items[rel.r_sym()], - else => unreachable, - }; - const symbol = elf_file.symbol(symbol_index); + const r_type: elf.R_X86_64 = @enumFromInt(rel.r_type()); + const r_offset = std.math.cast(usize, rel.r_offset) orelse return error.Overflow; - // Check for violation of One Definition Rule for COMDATs. - if (symbol.file(elf_file) == null) { - // TODO convert into an error - log.debug("{}: {s}: {s} refers to a discarded COMDAT section", .{ - file_ptr.fmtPath(), - atom.name(elf_file), - symbol.name(elf_file), - }); - continue; - } + switch (r_type) { + .@"64" => { + try atom.scanReloc(symbol, rel, dynAbsRelocAction(symbol, elf_file), elf_file); + }, - // Report an undefined symbol. - try atom.reportUndefined(elf_file, symbol, symbol_index, rel, undefs); + .@"32", + .@"32S", + => { + try atom.scanReloc(symbol, rel, absRelocAction(symbol, elf_file), elf_file); + }, - if (symbol.isIFunc(elf_file)) { + .GOT32, + .GOTPC32, + .GOTPC64, + .GOTPCREL, + .GOTPCREL64, + .GOTPCRELX, + .REX_GOTPCRELX, + => { symbol.flags.needs_got = true; - symbol.flags.needs_plt = true; - } - - // While traversing relocations, mark symbols that require special handling such as - // pointer indirection via GOT, or a stub trampoline via PLT. - switch (r_type) { - .@"64" => { - try atom.scanReloc(symbol, rel, dynAbsRelocAction(symbol, elf_file), elf_file); - }, - - .@"32", - .@"32S", - => { - try atom.scanReloc(symbol, rel, absRelocAction(symbol, elf_file), elf_file); - }, + }, - .GOT32, - .GOTPC32, - .GOTPC64, - .GOTPCREL, - .GOTPCREL64, - .GOTPCRELX, - .REX_GOTPCRELX, - => { - symbol.flags.needs_got = true; - }, + .PLT32, + .PLTOFF64, + => { + if (symbol.flags.import) { + symbol.flags.needs_plt = true; + } + }, - .PLT32, - .PLTOFF64, - => { - if (symbol.flags.import) { - symbol.flags.needs_plt = true; - } - }, + .PC32 => { + try atom.scanReloc(symbol, rel, pcRelocAction(symbol, elf_file), elf_file); + }, - .PC32 => { - try atom.scanReloc(symbol, rel, pcRelocAction(symbol, elf_file), elf_file); - }, + .TLSGD => { + // TODO verify followed by appropriate relocation such as PLT32 __tls_get_addr - .TLSGD => { - // TODO verify followed by appropriate relocation such as PLT32 __tls_get_addr + if (is_static or (!symbol.flags.import and !is_dyn_lib)) { + // Relax if building with -static flag as __tls_get_addr() will not be present in libc.a + // We skip the next relocation. + it.skip(1); + } else if (!symbol.flags.import and is_dyn_lib) { + symbol.flags.needs_gottp = true; + it.skip(1); + } else { + symbol.flags.needs_tlsgd = true; + } + }, - if (is_static or (!symbol.flags.import and !is_dyn_lib)) { - // Relax if building with -static flag as __tls_get_addr() will not be present in libc.a - // We skip the next relocation. - i += 1; - } else if (!symbol.flags.import and is_dyn_lib) { - symbol.flags.needs_gottp = true; - i += 1; - } else { - symbol.flags.needs_tlsgd = true; - } - }, + .TLSLD => { + // TODO verify followed by appropriate relocation such as PLT32 __tls_get_addr - .TLSLD => { - // TODO verify followed by appropriate relocation such as PLT32 __tls_get_addr + if (is_static or !is_dyn_lib) { + // Relax if building with -static flag as __tls_get_addr() will not be present in libc.a + // We skip the next relocation. + it.skip(1); + } else { + elf_file.got.flags.needs_tlsld = true; + } + }, - if (is_static or !is_dyn_lib) { - // Relax if building with -static flag as __tls_get_addr() will not be present in libc.a - // We skip the next relocation. - i += 1; - } else { - elf_file.got.flags.needs_tlsld = true; - } - }, + .GOTTPOFF => { + const should_relax = blk: { + if (is_dyn_lib or symbol.flags.import) break :blk false; + if (!x86_64.canRelaxGotTpOff(code.?[r_offset - 3 ..])) break :blk false; + break :blk true; + }; + if (!should_relax) { + symbol.flags.needs_gottp = true; + } + }, - .GOTTPOFF => { - const should_relax = blk: { - if (is_dyn_lib or symbol.flags.import) break :blk false; - if (!x86_64.canRelaxGotTpOff(code.?[r_offset - 3 ..])) break :blk false; - break :blk true; - }; - if (!should_relax) { - symbol.flags.needs_gottp = true; - } - }, + .GOTPC32_TLSDESC => { + const should_relax = is_static or (!is_dyn_lib and !symbol.flags.import); + if (!should_relax) { + symbol.flags.needs_tlsdesc = true; + } + }, - .GOTPC32_TLSDESC => { - const should_relax = is_static or (!is_dyn_lib and !symbol.flags.import); - if (!should_relax) { - symbol.flags.needs_tlsdesc = true; - } - }, + .TPOFF32, + .TPOFF64, + => { + if (is_dyn_lib) try atom.reportPicError(symbol, rel, elf_file); + }, - .TPOFF32, - .TPOFF64, + .GOTOFF64, + .DTPOFF32, + .DTPOFF64, + .SIZE32, + .SIZE64, + .TLSDESC_CALL, + => {}, + + else => |x| switch (@intFromEnum(x)) { + // Zig custom relocations + Elf.R_ZIG_GOT32, + Elf.R_ZIG_GOTPCREL, => { - if (is_dyn_lib) try atom.reportPicError(symbol, rel, elf_file); + assert(symbol.flags.has_zig_got); }, - .GOTOFF64, - .DTPOFF32, - .DTPOFF64, - .SIZE32, - .SIZE64, - .TLSDESC_CALL, - => {}, - - else => |x| switch (@intFromEnum(x)) { - // Zig custom relocations - Elf.R_ZIG_GOT32, - Elf.R_ZIG_GOTPCREL, - => { - assert(symbol.flags.has_zig_got); - }, - - else => try atom.reportUnhandledRelocError(rel, elf_file), - }, - } + else => try atom.reportUnhandledRelocError(rel, elf_file), + }, } } @@ -1195,7 +1216,7 @@ const x86_64 = struct { } // Report an undefined symbol. - try atom.reportUndefined(elf_file, target, target_index, rel, undefs); + if (try atom.reportUndefined(elf_file, target, target_index, rel, undefs)) continue; // We will use equation format to resolve relocations: // https://intezer.com/blog/malware-analysis/executable-and-linkable-format-101-part-3-relocations/ @@ -1485,6 +1506,36 @@ const x86_64 = struct { const Instruction = encoder.Instruction; }; +const RelocError = error{ + Overflow, + OutOfMemory, + RelocFailure, + UnsupportedCpuArch, +}; + +const RelocsIterator = struct { + relocs: []const elf.Elf64_Rela, + pos: i64 = -1, + + fn next(it: *RelocsIterator) ?elf.Elf64_Rela { + it.pos += 1; + if (it.pos >= it.relocs.len) return null; + return it.relocs[@intCast(it.pos)]; + } + + fn prev(it: *RelocsIterator) ?elf.Elf64_Rela { + if (it.pos == -1) return null; + const rel = it.relocs[@intCast(it.pos)]; + it.pos -= 1; + return rel; + } + + fn skip(it: *RelocsIterator, num: usize) void { + assert(num > 0); + it.pos += @intCast(num); + } +}; + const std = @import("std"); const assert = std.debug.assert; const elf = std.elf; diff --git a/src/link/Elf/relocation.zig b/src/link/Elf/relocation.zig index 7b0d42dc3d..3c8afa3c12 100644 --- a/src/link/Elf/relocation.zig +++ b/src/link/Elf/relocation.zig @@ -1,4 +1,6 @@ pub const Kind = enum { + none, + other, abs, copy, rel, @@ -13,23 +15,24 @@ pub const Kind = enum { fn Table(comptime len: comptime_int, comptime RelType: type, comptime mapping: [len]struct { Kind, RelType }) type { return struct { - fn decode(r_type: u32) ?Kind { + fn decode(r_type: u32) Kind { inline for (mapping) |entry| { if (@intFromEnum(entry[1]) == r_type) return entry[0]; } - return null; + return .other; } fn encode(comptime kind: Kind) u32 { inline for (mapping) |entry| { if (entry[0] == kind) return @intFromEnum(entry[1]); } - unreachable; + @panic("encoding .other is ambiguous"); } }; } -const x86_64_relocs = Table(10, elf.R_X86_64, .{ +const x86_64_relocs = Table(11, elf.R_X86_64, .{ + .{ .none, .NONE }, .{ .abs, .@"64" }, .{ .copy, .COPY }, .{ .rel, .RELATIVE }, @@ -42,7 +45,8 @@ const x86_64_relocs = Table(10, elf.R_X86_64, .{ .{ .tlsdesc, .TLSDESC }, }); -const aarch64_relocs = Table(10, elf.R_AARCH64, .{ +const aarch64_relocs = Table(11, elf.R_AARCH64, .{ + .{ .none, .NONE }, .{ .abs, .ABS64 }, .{ .copy, .COPY }, .{ .rel, .RELATIVE }, @@ -55,7 +59,8 @@ const aarch64_relocs = Table(10, elf.R_AARCH64, .{ .{ .tlsdesc, .TLSDESC }, }); -const riscv64_relocs = Table(10, elf.R_RISCV, .{ +const riscv64_relocs = Table(11, elf.R_RISCV, .{ + .{ .none, .NONE }, .{ .abs, .@"64" }, .{ .copy, .COPY }, .{ .rel, .RELATIVE }, |
