| ... | ... | @@ -300,7 +300,7 @@ pub fn free(self: *Atom, elf_file: *Elf) void { |
| 300 | 300 | self.* = .{}; |
| 301 | 301 | } |
| 302 | 302 | |
| 303 | | pub fn relocs(self: Atom, elf_file: *Elf) []align(1) const elf.Elf64_Rela { |
| 303 | pub fn relocs(self: Atom, elf_file: *Elf) []const elf.Elf64_Rela { |
| 304 | 304 | const shndx = self.relocsShndx() orelse return &[0]elf.Elf64_Rela{}; |
| 305 | 305 | return switch (self.file(elf_file).?) { |
| 306 | 306 | .zig_object => |x| x.relocs.items[shndx].items, |
| ... | ... | @@ -394,11 +394,54 @@ pub fn scanRelocsRequiresCode(self: Atom, elf_file: *Elf) bool { |
| 394 | 394 | return false; |
| 395 | 395 | } |
| 396 | 396 | |
| 397 | | pub 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, |
| 397 | pub 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 | } |
| 401 | 443 | } |
| 444 | if (has_reloc_errors) return error.RelocFailure; |
| 402 | 445 | } |
| 403 | 446 | |
| 404 | 447 | fn scanReloc( |
| ... | ... | @@ -407,7 +450,7 @@ fn scanReloc( |
| 407 | 450 | rel: elf.Elf64_Rela, |
| 408 | 451 | action: RelocAction, |
| 409 | 452 | elf_file: *Elf, |
| 410 | | ) error{OutOfMemory}!void { |
| 453 | ) RelocError!void { |
| 411 | 454 | const is_writeable = self.inputShdr(elf_file).sh_flags & elf.SHF_WRITE != 0; |
| 412 | 455 | const num_dynrelocs = switch (self.file(elf_file).?) { |
| 413 | 456 | .linker_defined => unreachable, |
| ... | ... | @@ -554,7 +597,7 @@ fn dataType(symbol: *const Symbol, elf_file: *Elf) u2 { |
| 554 | 597 | return 3; |
| 555 | 598 | } |
| 556 | 599 | |
| 557 | | fn reportUnhandledRelocError(self: Atom, rel: elf.Elf64_Rela, elf_file: *Elf) error{OutOfMemory}!void { |
| 600 | fn reportUnhandledRelocError(self: Atom, rel: elf.Elf64_Rela, elf_file: *Elf) RelocError!void { |
| 558 | 601 | var err = try elf_file.addErrorWithNotes(1); |
| 559 | 602 | try err.addMsg(elf_file, "fatal linker error: unhandled relocation type {} at offset 0x{x}", .{ |
| 560 | 603 | 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 |
| 564 | 607 | self.file(elf_file).?.fmtPath(), |
| 565 | 608 | self.name(elf_file), |
| 566 | 609 | }); |
| 610 | return error.RelocFailure; |
| 567 | 611 | } |
| 568 | 612 | |
| 569 | 613 | fn reportTextRelocError( |
| ... | ... | @@ -571,7 +615,7 @@ fn reportTextRelocError( |
| 571 | 615 | symbol: *const Symbol, |
| 572 | 616 | rel: elf.Elf64_Rela, |
| 573 | 617 | elf_file: *Elf, |
| 574 | | ) error{OutOfMemory}!void { |
| 618 | ) RelocError!void { |
| 575 | 619 | var err = try elf_file.addErrorWithNotes(1); |
| 576 | 620 | try err.addMsg(elf_file, "relocation at offset 0x{x} against symbol '{s}' cannot be used", .{ |
| 577 | 621 | rel.r_offset, |
| ... | ... | @@ -581,6 +625,7 @@ fn reportTextRelocError( |
| 581 | 625 | self.file(elf_file).?.fmtPath(), |
| 582 | 626 | self.name(elf_file), |
| 583 | 627 | }); |
| 628 | return error.RelocFailure; |
| 584 | 629 | } |
| 585 | 630 | |
| 586 | 631 | fn reportPicError( |
| ... | ... | @@ -588,7 +633,7 @@ fn reportPicError( |
| 588 | 633 | symbol: *const Symbol, |
| 589 | 634 | rel: elf.Elf64_Rela, |
| 590 | 635 | elf_file: *Elf, |
| 591 | | ) error{OutOfMemory}!void { |
| 636 | ) RelocError!void { |
| 592 | 637 | var err = try elf_file.addErrorWithNotes(2); |
| 593 | 638 | try err.addMsg(elf_file, "relocation at offset 0x{x} against symbol '{s}' cannot be used", .{ |
| 594 | 639 | rel.r_offset, |
| ... | ... | @@ -599,6 +644,7 @@ fn reportPicError( |
| 599 | 644 | self.name(elf_file), |
| 600 | 645 | }); |
| 601 | 646 | try err.addNote(elf_file, "recompile with -fPIC", .{}); |
| 647 | return error.RelocFailure; |
| 602 | 648 | } |
| 603 | 649 | |
| 604 | 650 | fn reportNoPicError( |
| ... | ... | @@ -606,7 +652,7 @@ fn reportNoPicError( |
| 606 | 652 | symbol: *const Symbol, |
| 607 | 653 | rel: elf.Elf64_Rela, |
| 608 | 654 | elf_file: *Elf, |
| 609 | | ) error{OutOfMemory}!void { |
| 655 | ) RelocError!void { |
| 610 | 656 | var err = try elf_file.addErrorWithNotes(2); |
| 611 | 657 | try err.addMsg(elf_file, "relocation at offset 0x{x} against symbol '{s}' cannot be used", .{ |
| 612 | 658 | rel.r_offset, |
| ... | ... | @@ -617,6 +663,7 @@ fn reportNoPicError( |
| 617 | 663 | self.name(elf_file), |
| 618 | 664 | }); |
| 619 | 665 | try err.addNote(elf_file, "recompile with -fno-PIC", .{}); |
| 666 | return error.RelocFailure; |
| 620 | 667 | } |
| 621 | 668 | |
| 622 | 669 | // This function will report any undefined non-weak symbols that are not imports. |
| ... | ... | @@ -627,7 +674,7 @@ fn reportUndefined( |
| 627 | 674 | sym_index: Symbol.Index, |
| 628 | 675 | rel: elf.Elf64_Rela, |
| 629 | 676 | undefs: anytype, |
| 630 | | ) !void { |
| 677 | ) !bool { |
| 631 | 678 | const comp = elf_file.base.comp; |
| 632 | 679 | const gpa = comp.gpa; |
| 633 | 680 | const rel_esym = switch (self.file(elf_file).?) { |
| ... | ... | @@ -647,7 +694,10 @@ fn reportUndefined( |
| 647 | 694 | gop.value_ptr.* = std.ArrayList(Atom.Index).init(gpa); |
| 648 | 695 | } |
| 649 | 696 | try gop.value_ptr.append(self.atom_index); |
| 697 | return true; |
| 650 | 698 | } |
| 699 | |
| 700 | return false; |
| 651 | 701 | } |
| 652 | 702 | |
| 653 | 703 | pub fn resolveRelocsAlloc(self: Atom, elf_file: *Elf, code: []u8) !void { |
| ... | ... | @@ -831,152 +881,123 @@ pub const Flags = packed struct { |
| 831 | 881 | }; |
| 832 | 882 | |
| 833 | 883 | const 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 { |
| 835 | 892 | const is_static = elf_file.base.isStatic(); |
| 836 | 893 | 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; |
| 847 | 894 | |
| 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; |
| 854 | 897 | |
| 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 | }, |
| 865 | 902 | |
| 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 | }, |
| 868 | 908 | |
| 869 | | if (symbol.isIFunc(elf_file)) { |
| 909 | .GOT32, |
| 910 | .GOTPC32, |
| 911 | .GOTPC64, |
| 912 | .GOTPCREL, |
| 913 | .GOTPCREL64, |
| 914 | .GOTPCRELX, |
| 915 | .REX_GOTPCRELX, |
| 916 | => { |
| 870 | 917 | 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 | }, |
| 886 | 919 | |
| 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 | }, |
| 897 | 927 | |
| 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 | }, |
| 905 | 931 | |
| 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 |
| 909 | 934 | |
| 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 | }, |
| 912 | 946 | |
| 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 |
| 924 | 949 | |
| 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 | }, |
| 927 | 958 | |
| 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 | }, |
| 936 | 969 | |
| 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 | }, |
| 947 | 976 | |
| 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 | }, |
| 954 | 982 | |
| 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, |
| 957 | 995 | => { |
| 958 | | if (is_dyn_lib) try atom.reportPicError(symbol, rel, elf_file); |
| 996 | assert(symbol.flags.has_zig_got); |
| 959 | 997 | }, |
| 960 | 998 | |
| 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 | }, |
| 980 | 1001 | } |
| 981 | 1002 | } |
| 982 | 1003 | |
| ... | ... | @@ -1195,7 +1216,7 @@ const x86_64 = struct { |
| 1195 | 1216 | } |
| 1196 | 1217 | |
| 1197 | 1218 | // 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; |
| 1199 | 1220 | |
| 1200 | 1221 | // We will use equation format to resolve relocations: |
| 1201 | 1222 | // https://intezer.com/blog/malware-analysis/executable-and-linkable-format-101-part-3-relocations/ |
| ... | ... | @@ -1485,6 +1506,36 @@ const x86_64 = struct { |
| 1485 | 1506 | const Instruction = encoder.Instruction; |
| 1486 | 1507 | }; |
| 1487 | 1508 | |
| 1509 | const RelocError = error{ |
| 1510 | Overflow, |
| 1511 | OutOfMemory, |
| 1512 | RelocFailure, |
| 1513 | UnsupportedCpuArch, |
| 1514 | }; |
| 1515 | |
| 1516 | const 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 | |
| 1488 | 1539 | const std = @import("std"); |
| 1489 | 1540 | const assert = std.debug.assert; |
| 1490 | 1541 | const elf = std.elf; |