authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-02-21 19:06:10+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-02-21 19:06:10+01:00
log60bc2e7616b0fd42c98ba8b9e0b212439b6ea1a0
treea7b067cbb12e047718cc324aa912ca613a22700a
parent1ca004176f8f76b1d54d878e002ebcc4362f9b92

elf: simplify logic for handling scanning relocs on different arches


3 files changed, 209 insertions(+), 144 deletions(-)

src/link/Elf.zig+9
......@@ -2049,18 +2049,22 @@ fn scanRelocs(self: *Elf) !void {
20492049 if (self.zigObjectPtr()) |zo| objects.appendAssumeCapacity(zo.index);
20502050 objects.appendSliceAssumeCapacity(self.objects.items);
20512051
2052 var has_reloc_errors = false;
20522053 for (objects.items) |index| {
20532054 self.file(index).?.scanRelocs(self, &undefs) catch |err| switch (err) {
20542055 error.UnsupportedCpuArch => {
20552056 try self.reportUnsupportedCpuArch();
20562057 return error.FlushFailure;
20572058 },
2059 error.RelocFailure => has_reloc_errors = true,
20582060 else => |e| return e,
20592061 };
20602062 }
20612063
20622064 try self.reportUndefinedSymbols(&undefs);
20632065
2066 if (has_reloc_errors) return error.FlushFailure;
2067
20642068 for (self.symbols.items, 0..) |*sym, i| {
20652069 const index = @as(u32, @intCast(i));
20662070 if (!sym.isLocal(self) and !sym.flags.has_dynamic) {
......@@ -4449,6 +4453,8 @@ fn writeAtoms(self: *Elf) !void {
44494453 undefs.deinit();
44504454 }
44514455
4456 var has_reloc_errors = false;
4457
44524458 // TODO iterate over `output_sections` directly
44534459 for (self.shdrs.items, 0..) |shdr, shndx| {
44544460 if (shdr.sh_type == elf.SHT_NULL) continue;
......@@ -4519,6 +4525,7 @@ fn writeAtoms(self: *Elf) !void {
45194525 try self.reportUnsupportedCpuArch();
45204526 return error.FlushFailure;
45214527 },
4528 error.RelocFailure => has_reloc_errors = true,
45224529 else => |e| return e,
45234530 };
45244531 }
......@@ -4527,6 +4534,8 @@ fn writeAtoms(self: *Elf) !void {
45274534 }
45284535
45294536 try self.reportUndefinedSymbols(&undefs);
4537
4538 if (has_reloc_errors) return error.FlushFailure;
45304539}
45314540
45324541pub fn updateSymtabSize(self: *Elf) !void {
src/link/Elf/Atom.zig+189-138
......@@ -300,7 +300,7 @@ pub fn free(self: *Atom, elf_file: *Elf) void {
300300 self.* = .{};
301301}
302302
303pub fn relocs(self: Atom, elf_file: *Elf) []align(1) const elf.Elf64_Rela {
303pub fn relocs(self: Atom, elf_file: *Elf) []const elf.Elf64_Rela {
304304 const shndx = self.relocsShndx() orelse return &[0]elf.Elf64_Rela{};
305305 return switch (self.file(elf_file).?) {
306306 .zig_object => |x| x.relocs.items[shndx].items,
......@@ -394,11 +394,54 @@ pub fn scanRelocsRequiresCode(self: Atom, elf_file: *Elf) bool {
394394 return false;
395395}
396396
397pub fn scanRelocs(self: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype) !void {
398 switch (elf_file.getTarget().cpu.arch) {
399 .x86_64 => try x86_64.scanRelocs(self, elf_file, code, undefs),
400 else => return error.UnsupportedCpuArch,
397pub fn scanRelocs(self: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype) RelocError!void {
398 const cpu_arch = elf_file.getTarget().cpu.arch;
399 const file_ptr = self.file(elf_file).?;
400 const rels = self.relocs(elf_file);
401
402 var has_reloc_errors = false;
403 var it = RelocsIterator{ .relocs = rels };
404 while (it.next()) |rel| {
405 const r_kind = relocation.decode(rel.r_type(), cpu_arch);
406 if (r_kind == .none) continue;
407
408 const symbol_index = switch (file_ptr) {
409 .zig_object => |x| x.symbol(rel.r_sym()),
410 .object => |x| x.symbols.items[rel.r_sym()],
411 else => unreachable,
412 };
413 const symbol = elf_file.symbol(symbol_index);
414
415 // Check for violation of One Definition Rule for COMDATs.
416 if (symbol.file(elf_file) == null) {
417 // TODO convert into an error
418 log.debug("{}: {s}: {s} refers to a discarded COMDAT section", .{
419 file_ptr.fmtPath(),
420 self.name(elf_file),
421 symbol.name(elf_file),
422 });
423 continue;
424 }
425
426 // Report an undefined symbol.
427 if (try self.reportUndefined(elf_file, symbol, symbol_index, rel, undefs)) continue;
428
429 if (symbol.isIFunc(elf_file)) {
430 symbol.flags.needs_got = true;
431 symbol.flags.needs_plt = true;
432 }
433
434 // While traversing relocations, mark symbols that require special handling such as
435 // pointer indirection via GOT, or a stub trampoline via PLT.
436 switch (elf_file.getTarget().cpu.arch) {
437 .x86_64 => x86_64.scanReloc(self, elf_file, rel, symbol, code, &it) catch |err| switch (err) {
438 error.RelocFailure => has_reloc_errors = true,
439 else => |e| return e,
440 },
441 else => return error.UnsupportedCpuArch,
442 }
401443 }
444 if (has_reloc_errors) return error.RelocFailure;
402445}
403446
404447fn scanReloc(
......@@ -407,7 +450,7 @@ fn scanReloc(
407450 rel: elf.Elf64_Rela,
408451 action: RelocAction,
409452 elf_file: *Elf,
410) error{OutOfMemory}!void {
453) RelocError!void {
411454 const is_writeable = self.inputShdr(elf_file).sh_flags & elf.SHF_WRITE != 0;
412455 const num_dynrelocs = switch (self.file(elf_file).?) {
413456 .linker_defined => unreachable,
......@@ -554,7 +597,7 @@ fn dataType(symbol: *const Symbol, elf_file: *Elf) u2 {
554597 return 3;
555598}
556599
557fn reportUnhandledRelocError(self: Atom, rel: elf.Elf64_Rela, elf_file: *Elf) error{OutOfMemory}!void {
600fn reportUnhandledRelocError(self: Atom, rel: elf.Elf64_Rela, elf_file: *Elf) RelocError!void {
558601 var err = try elf_file.addErrorWithNotes(1);
559602 try err.addMsg(elf_file, "fatal linker error: unhandled relocation type {} at offset 0x{x}", .{
560603 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
564607 self.file(elf_file).?.fmtPath(),
565608 self.name(elf_file),
566609 });
610 return error.RelocFailure;
567611}
568612
569613fn reportTextRelocError(
......@@ -571,7 +615,7 @@ fn reportTextRelocError(
571615 symbol: *const Symbol,
572616 rel: elf.Elf64_Rela,
573617 elf_file: *Elf,
574) error{OutOfMemory}!void {
618) RelocError!void {
575619 var err = try elf_file.addErrorWithNotes(1);
576620 try err.addMsg(elf_file, "relocation at offset 0x{x} against symbol '{s}' cannot be used", .{
577621 rel.r_offset,
......@@ -581,6 +625,7 @@ fn reportTextRelocError(
581625 self.file(elf_file).?.fmtPath(),
582626 self.name(elf_file),
583627 });
628 return error.RelocFailure;
584629}
585630
586631fn reportPicError(
......@@ -588,7 +633,7 @@ fn reportPicError(
588633 symbol: *const Symbol,
589634 rel: elf.Elf64_Rela,
590635 elf_file: *Elf,
591) error{OutOfMemory}!void {
636) RelocError!void {
592637 var err = try elf_file.addErrorWithNotes(2);
593638 try err.addMsg(elf_file, "relocation at offset 0x{x} against symbol '{s}' cannot be used", .{
594639 rel.r_offset,
......@@ -599,6 +644,7 @@ fn reportPicError(
599644 self.name(elf_file),
600645 });
601646 try err.addNote(elf_file, "recompile with -fPIC", .{});
647 return error.RelocFailure;
602648}
603649
604650fn reportNoPicError(
......@@ -606,7 +652,7 @@ fn reportNoPicError(
606652 symbol: *const Symbol,
607653 rel: elf.Elf64_Rela,
608654 elf_file: *Elf,
609) error{OutOfMemory}!void {
655) RelocError!void {
610656 var err = try elf_file.addErrorWithNotes(2);
611657 try err.addMsg(elf_file, "relocation at offset 0x{x} against symbol '{s}' cannot be used", .{
612658 rel.r_offset,
......@@ -617,6 +663,7 @@ fn reportNoPicError(
617663 self.name(elf_file),
618664 });
619665 try err.addNote(elf_file, "recompile with -fno-PIC", .{});
666 return error.RelocFailure;
620667}
621668
622669// This function will report any undefined non-weak symbols that are not imports.
......@@ -627,7 +674,7 @@ fn reportUndefined(
627674 sym_index: Symbol.Index,
628675 rel: elf.Elf64_Rela,
629676 undefs: anytype,
630) !void {
677) !bool {
631678 const comp = elf_file.base.comp;
632679 const gpa = comp.gpa;
633680 const rel_esym = switch (self.file(elf_file).?) {
......@@ -647,7 +694,10 @@ fn reportUndefined(
647694 gop.value_ptr.* = std.ArrayList(Atom.Index).init(gpa);
648695 }
649696 try gop.value_ptr.append(self.atom_index);
697 return true;
650698 }
699
700 return false;
651701}
652702
653703pub fn resolveRelocsAlloc(self: Atom, elf_file: *Elf, code: []u8) !void {
......@@ -831,152 +881,123 @@ pub const Flags = packed struct {
831881};
832882
833883const x86_64 = struct {
834 fn scanRelocs(atom: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype) !void {
884 fn scanReloc(
885 atom: Atom,
886 elf_file: *Elf,
887 rel: elf.Elf64_Rela,
888 symbol: *Symbol,
889 code: ?[]const u8,
890 it: *RelocsIterator,
891 ) !void {
835892 const is_static = elf_file.base.isStatic();
836893 const is_dyn_lib = elf_file.base.isDynLib();
837 const file_ptr = atom.file(elf_file).?;
838 const rels = atom.relocs(elf_file);
839 var i: usize = 0;
840 while (i < rels.len) : (i += 1) {
841 const rel = rels[i];
842 const r_type: elf.R_X86_64 = @enumFromInt(rel.r_type());
843
844 if (r_type == .NONE) continue;
845
846 const r_offset = std.math.cast(usize, rel.r_offset) orelse return error.Overflow;
847894
848 const symbol_index = switch (file_ptr) {
849 .zig_object => |x| x.symbol(rel.r_sym()),
850 .object => |x| x.symbols.items[rel.r_sym()],
851 else => unreachable,
852 };
853 const symbol = elf_file.symbol(symbol_index);
895 const r_type: elf.R_X86_64 = @enumFromInt(rel.r_type());
896 const r_offset = std.math.cast(usize, rel.r_offset) orelse return error.Overflow;
854897
855 // Check for violation of One Definition Rule for COMDATs.
856 if (symbol.file(elf_file) == null) {
857 // TODO convert into an error
858 log.debug("{}: {s}: {s} refers to a discarded COMDAT section", .{
859 file_ptr.fmtPath(),
860 atom.name(elf_file),
861 symbol.name(elf_file),
862 });
863 continue;
864 }
898 switch (r_type) {
899 .@"64" => {
900 try atom.scanReloc(symbol, rel, dynAbsRelocAction(symbol, elf_file), elf_file);
901 },
865902
866 // Report an undefined symbol.
867 try atom.reportUndefined(elf_file, symbol, symbol_index, rel, undefs);
903 .@"32",
904 .@"32S",
905 => {
906 try atom.scanReloc(symbol, rel, absRelocAction(symbol, elf_file), elf_file);
907 },
868908
869 if (symbol.isIFunc(elf_file)) {
909 .GOT32,
910 .GOTPC32,
911 .GOTPC64,
912 .GOTPCREL,
913 .GOTPCREL64,
914 .GOTPCRELX,
915 .REX_GOTPCRELX,
916 => {
870917 symbol.flags.needs_got = true;
871 symbol.flags.needs_plt = true;
872 }
873
874 // While traversing relocations, mark symbols that require special handling such as
875 // pointer indirection via GOT, or a stub trampoline via PLT.
876 switch (r_type) {
877 .@"64" => {
878 try atom.scanReloc(symbol, rel, dynAbsRelocAction(symbol, elf_file), elf_file);
879 },
880
881 .@"32",
882 .@"32S",
883 => {
884 try atom.scanReloc(symbol, rel, absRelocAction(symbol, elf_file), elf_file);
885 },
918 },
886919
887 .GOT32,
888 .GOTPC32,
889 .GOTPC64,
890 .GOTPCREL,
891 .GOTPCREL64,
892 .GOTPCRELX,
893 .REX_GOTPCRELX,
894 => {
895 symbol.flags.needs_got = true;
896 },
920 .PLT32,
921 .PLTOFF64,
922 => {
923 if (symbol.flags.import) {
924 symbol.flags.needs_plt = true;
925 }
926 },
897927
898 .PLT32,
899 .PLTOFF64,
900 => {
901 if (symbol.flags.import) {
902 symbol.flags.needs_plt = true;
903 }
904 },
928 .PC32 => {
929 try atom.scanReloc(symbol, rel, pcRelocAction(symbol, elf_file), elf_file);
930 },
905931
906 .PC32 => {
907 try atom.scanReloc(symbol, rel, pcRelocAction(symbol, elf_file), elf_file);
908 },
932 .TLSGD => {
933 // TODO verify followed by appropriate relocation such as PLT32 __tls_get_addr
909934
910 .TLSGD => {
911 // TODO verify followed by appropriate relocation such as PLT32 __tls_get_addr
935 if (is_static or (!symbol.flags.import and !is_dyn_lib)) {
936 // Relax if building with -static flag as __tls_get_addr() will not be present in libc.a
937 // We skip the next relocation.
938 it.skip(1);
939 } else if (!symbol.flags.import and is_dyn_lib) {
940 symbol.flags.needs_gottp = true;
941 it.skip(1);
942 } else {
943 symbol.flags.needs_tlsgd = true;
944 }
945 },
912946
913 if (is_static or (!symbol.flags.import and !is_dyn_lib)) {
914 // Relax if building with -static flag as __tls_get_addr() will not be present in libc.a
915 // We skip the next relocation.
916 i += 1;
917 } else if (!symbol.flags.import and is_dyn_lib) {
918 symbol.flags.needs_gottp = true;
919 i += 1;
920 } else {
921 symbol.flags.needs_tlsgd = true;
922 }
923 },
947 .TLSLD => {
948 // TODO verify followed by appropriate relocation such as PLT32 __tls_get_addr
924949
925 .TLSLD => {
926 // TODO verify followed by appropriate relocation such as PLT32 __tls_get_addr
950 if (is_static or !is_dyn_lib) {
951 // Relax if building with -static flag as __tls_get_addr() will not be present in libc.a
952 // We skip the next relocation.
953 it.skip(1);
954 } else {
955 elf_file.got.flags.needs_tlsld = true;
956 }
957 },
927958
928 if (is_static or !is_dyn_lib) {
929 // Relax if building with -static flag as __tls_get_addr() will not be present in libc.a
930 // We skip the next relocation.
931 i += 1;
932 } else {
933 elf_file.got.flags.needs_tlsld = true;
934 }
935 },
959 .GOTTPOFF => {
960 const should_relax = blk: {
961 if (is_dyn_lib or symbol.flags.import) break :blk false;
962 if (!x86_64.canRelaxGotTpOff(code.?[r_offset - 3 ..])) break :blk false;
963 break :blk true;
964 };
965 if (!should_relax) {
966 symbol.flags.needs_gottp = true;
967 }
968 },
936969
937 .GOTTPOFF => {
938 const should_relax = blk: {
939 if (is_dyn_lib or symbol.flags.import) break :blk false;
940 if (!x86_64.canRelaxGotTpOff(code.?[r_offset - 3 ..])) break :blk false;
941 break :blk true;
942 };
943 if (!should_relax) {
944 symbol.flags.needs_gottp = true;
945 }
946 },
970 .GOTPC32_TLSDESC => {
971 const should_relax = is_static or (!is_dyn_lib and !symbol.flags.import);
972 if (!should_relax) {
973 symbol.flags.needs_tlsdesc = true;
974 }
975 },
947976
948 .GOTPC32_TLSDESC => {
949 const should_relax = is_static or (!is_dyn_lib and !symbol.flags.import);
950 if (!should_relax) {
951 symbol.flags.needs_tlsdesc = true;
952 }
953 },
977 .TPOFF32,
978 .TPOFF64,
979 => {
980 if (is_dyn_lib) try atom.reportPicError(symbol, rel, elf_file);
981 },
954982
955 .TPOFF32,
956 .TPOFF64,
983 .GOTOFF64,
984 .DTPOFF32,
985 .DTPOFF64,
986 .SIZE32,
987 .SIZE64,
988 .TLSDESC_CALL,
989 => {},
990
991 else => |x| switch (@intFromEnum(x)) {
992 // Zig custom relocations
993 Elf.R_ZIG_GOT32,
994 Elf.R_ZIG_GOTPCREL,
957995 => {
958 if (is_dyn_lib) try atom.reportPicError(symbol, rel, elf_file);
996 assert(symbol.flags.has_zig_got);
959997 },
960998
961 .GOTOFF64,
962 .DTPOFF32,
963 .DTPOFF64,
964 .SIZE32,
965 .SIZE64,
966 .TLSDESC_CALL,
967 => {},
968
969 else => |x| switch (@intFromEnum(x)) {
970 // Zig custom relocations
971 Elf.R_ZIG_GOT32,
972 Elf.R_ZIG_GOTPCREL,
973 => {
974 assert(symbol.flags.has_zig_got);
975 },
976
977 else => try atom.reportUnhandledRelocError(rel, elf_file),
978 },
979 }
999 else => try atom.reportUnhandledRelocError(rel, elf_file),
1000 },
9801001 }
9811002 }
9821003
......@@ -1195,7 +1216,7 @@ const x86_64 = struct {
11951216 }
11961217
11971218 // Report an undefined symbol.
1198 try atom.reportUndefined(elf_file, target, target_index, rel, undefs);
1219 if (try atom.reportUndefined(elf_file, target, target_index, rel, undefs)) continue;
11991220
12001221 // We will use equation format to resolve relocations:
12011222 // https://intezer.com/blog/malware-analysis/executable-and-linkable-format-101-part-3-relocations/
......@@ -1485,6 +1506,36 @@ const x86_64 = struct {
14851506 const Instruction = encoder.Instruction;
14861507};
14871508
1509const RelocError = error{
1510 Overflow,
1511 OutOfMemory,
1512 RelocFailure,
1513 UnsupportedCpuArch,
1514};
1515
1516const RelocsIterator = struct {
1517 relocs: []const elf.Elf64_Rela,
1518 pos: i64 = -1,
1519
1520 fn next(it: *RelocsIterator) ?elf.Elf64_Rela {
1521 it.pos += 1;
1522 if (it.pos >= it.relocs.len) return null;
1523 return it.relocs[@intCast(it.pos)];
1524 }
1525
1526 fn prev(it: *RelocsIterator) ?elf.Elf64_Rela {
1527 if (it.pos == -1) return null;
1528 const rel = it.relocs[@intCast(it.pos)];
1529 it.pos -= 1;
1530 return rel;
1531 }
1532
1533 fn skip(it: *RelocsIterator, num: usize) void {
1534 assert(num > 0);
1535 it.pos += @intCast(num);
1536 }
1537};
1538
14881539const std = @import("std");
14891540const assert = std.debug.assert;
14901541const elf = std.elf;
src/link/Elf/relocation.zig+11-6
......@@ -1,4 +1,6 @@
11pub const Kind = enum {
2 none,
3 other,
24 abs,
35 copy,
46 rel,
......@@ -13,23 +15,24 @@ pub const Kind = enum {
1315
1416fn Table(comptime len: comptime_int, comptime RelType: type, comptime mapping: [len]struct { Kind, RelType }) type {
1517 return struct {
16 fn decode(r_type: u32) ?Kind {
18 fn decode(r_type: u32) Kind {
1719 inline for (mapping) |entry| {
1820 if (@intFromEnum(entry[1]) == r_type) return entry[0];
1921 }
20 return null;
22 return .other;
2123 }
2224
2325 fn encode(comptime kind: Kind) u32 {
2426 inline for (mapping) |entry| {
2527 if (entry[0] == kind) return @intFromEnum(entry[1]);
2628 }
27 unreachable;
29 @panic("encoding .other is ambiguous");
2830 }
2931 };
3032}
3133
32const x86_64_relocs = Table(10, elf.R_X86_64, .{
34const x86_64_relocs = Table(11, elf.R_X86_64, .{
35 .{ .none, .NONE },
3336 .{ .abs, .@"64" },
3437 .{ .copy, .COPY },
3538 .{ .rel, .RELATIVE },
......@@ -42,7 +45,8 @@ const x86_64_relocs = Table(10, elf.R_X86_64, .{
4245 .{ .tlsdesc, .TLSDESC },
4346});
4447
45const aarch64_relocs = Table(10, elf.R_AARCH64, .{
48const aarch64_relocs = Table(11, elf.R_AARCH64, .{
49 .{ .none, .NONE },
4650 .{ .abs, .ABS64 },
4751 .{ .copy, .COPY },
4852 .{ .rel, .RELATIVE },
......@@ -55,7 +59,8 @@ const aarch64_relocs = Table(10, elf.R_AARCH64, .{
5559 .{ .tlsdesc, .TLSDESC },
5660});
5761
58const riscv64_relocs = Table(10, elf.R_RISCV, .{
62const riscv64_relocs = Table(11, elf.R_RISCV, .{
63 .{ .none, .NONE },
5964 .{ .abs, .@"64" },
6065 .{ .copy, .COPY },
6166 .{ .rel, .RELATIVE },