Commit 60bc2e7616
Changed files (3)
src
link
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;
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 },
src/link/Elf.zig
@@ -2049,18 +2049,22 @@ fn scanRelocs(self: *Elf) !void {
if (self.zigObjectPtr()) |zo| objects.appendAssumeCapacity(zo.index);
objects.appendSliceAssumeCapacity(self.objects.items);
+ var has_reloc_errors = false;
for (objects.items) |index| {
self.file(index).?.scanRelocs(self, &undefs) catch |err| switch (err) {
error.UnsupportedCpuArch => {
try self.reportUnsupportedCpuArch();
return error.FlushFailure;
},
+ error.RelocFailure => has_reloc_errors = true,
else => |e| return e,
};
}
try self.reportUndefinedSymbols(&undefs);
+ if (has_reloc_errors) return error.FlushFailure;
+
for (self.symbols.items, 0..) |*sym, i| {
const index = @as(u32, @intCast(i));
if (!sym.isLocal(self) and !sym.flags.has_dynamic) {
@@ -4449,6 +4453,8 @@ fn writeAtoms(self: *Elf) !void {
undefs.deinit();
}
+ var has_reloc_errors = false;
+
// TODO iterate over `output_sections` directly
for (self.shdrs.items, 0..) |shdr, shndx| {
if (shdr.sh_type == elf.SHT_NULL) continue;
@@ -4519,6 +4525,7 @@ fn writeAtoms(self: *Elf) !void {
try self.reportUnsupportedCpuArch();
return error.FlushFailure;
},
+ error.RelocFailure => has_reloc_errors = true,
else => |e| return e,
};
}
@@ -4527,6 +4534,8 @@ fn writeAtoms(self: *Elf) !void {
}
try self.reportUndefinedSymbols(&undefs);
+
+ if (has_reloc_errors) return error.FlushFailure;
}
pub fn updateSymtabSize(self: *Elf) !void {