| ... | ... | @@ -312,6 +312,7 @@ pub fn relocs(self: Atom, elf_file: *Elf) []align(1) const elf.Elf64_Rela { |
| 312 | 312 | pub fn writeRelocs(self: Atom, elf_file: *Elf, out_relocs: *std.ArrayList(elf.Elf64_Rela)) !void { |
| 313 | 313 | relocs_log.debug("0x{x}: {s}", .{ self.address(elf_file), self.name(elf_file) }); |
| 314 | 314 | |
| 315 | const cpu_arch = elf_file.getTarget().cpu.arch; |
| 315 | 316 | const file_ptr = self.file(elf_file).?; |
| 316 | 317 | for (self.relocs(elf_file)) |rel| { |
| 317 | 318 | const target_index = switch (file_ptr) { |
| ... | ... | @@ -320,12 +321,7 @@ pub fn writeRelocs(self: Atom, elf_file: *Elf, out_relocs: *std.ArrayList(elf.El |
| 320 | 321 | else => unreachable, |
| 321 | 322 | }; |
| 322 | 323 | const target = elf_file.symbol(target_index); |
| 323 | | const r_type = switch (rel.r_type()) { |
| 324 | | Elf.R_X86_64_ZIG_GOT32, |
| 325 | | Elf.R_X86_64_ZIG_GOTPCREL, |
| 326 | | => unreachable, // Sanity check if we accidentally emitted those. |
| 327 | | else => |r_type| r_type, |
| 328 | | }; |
| 324 | const r_type = rel.r_type(); |
| 329 | 325 | const r_offset = self.value + rel.r_offset; |
| 330 | 326 | var r_addend = rel.r_addend; |
| 331 | 327 | var r_sym: u32 = 0; |
| ... | ... | @@ -340,7 +336,7 @@ pub fn writeRelocs(self: Atom, elf_file: *Elf, out_relocs: *std.ArrayList(elf.El |
| 340 | 336 | } |
| 341 | 337 | |
| 342 | 338 | relocs_log.debug(" {s}: [{x} => {d}({s})] + {x}", .{ |
| 343 | | fmtRelocType(r_type), |
| 339 | relocation.fmtRelocType(r_type, cpu_arch), |
| 344 | 340 | r_offset, |
| 345 | 341 | r_sym, |
| 346 | 342 | target.name(elf_file), |
| ... | ... | @@ -385,155 +381,20 @@ pub fn freeRelocs(self: Atom, elf_file: *Elf) void { |
| 385 | 381 | } |
| 386 | 382 | |
| 387 | 383 | pub fn scanRelocsRequiresCode(self: Atom, elf_file: *Elf) bool { |
| 384 | const cpu_arch = elf_file.getTarget().cpu.arch; |
| 388 | 385 | for (self.relocs(elf_file)) |rel| { |
| 389 | | if (rel.r_type() == elf.R_X86_64_GOTTPOFF) return true; |
| 386 | switch (cpu_arch) { |
| 387 | .x86_64 => if (rel.r_type() == elf.R_X86_64_GOTTPOFF) return true, |
| 388 | else => {}, |
| 389 | } |
| 390 | 390 | } |
| 391 | 391 | return false; |
| 392 | 392 | } |
| 393 | 393 | |
| 394 | 394 | pub fn scanRelocs(self: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype) !void { |
| 395 | | const is_static = elf_file.base.isStatic(); |
| 396 | | const is_dyn_lib = elf_file.base.isDynLib(); |
| 397 | | const file_ptr = self.file(elf_file).?; |
| 398 | | const rels = self.relocs(elf_file); |
| 399 | | var i: usize = 0; |
| 400 | | while (i < rels.len) : (i += 1) { |
| 401 | | const rel = rels[i]; |
| 402 | | |
| 403 | | if (rel.r_type() == elf.R_X86_64_NONE) continue; |
| 404 | | |
| 405 | | const r_offset = std.math.cast(usize, rel.r_offset) orelse return error.Overflow; |
| 406 | | |
| 407 | | const symbol_index = switch (file_ptr) { |
| 408 | | .zig_object => |x| x.symbol(rel.r_sym()), |
| 409 | | .object => |x| x.symbols.items[rel.r_sym()], |
| 410 | | else => unreachable, |
| 411 | | }; |
| 412 | | const symbol = elf_file.symbol(symbol_index); |
| 413 | | |
| 414 | | // Check for violation of One Definition Rule for COMDATs. |
| 415 | | if (symbol.file(elf_file) == null) { |
| 416 | | // TODO convert into an error |
| 417 | | log.debug("{}: {s}: {s} refers to a discarded COMDAT section", .{ |
| 418 | | file_ptr.fmtPath(), |
| 419 | | self.name(elf_file), |
| 420 | | symbol.name(elf_file), |
| 421 | | }); |
| 422 | | continue; |
| 423 | | } |
| 424 | | |
| 425 | | // Report an undefined symbol. |
| 426 | | try self.reportUndefined(elf_file, symbol, symbol_index, rel, undefs); |
| 427 | | |
| 428 | | if (symbol.isIFunc(elf_file)) { |
| 429 | | symbol.flags.needs_got = true; |
| 430 | | symbol.flags.needs_plt = true; |
| 431 | | } |
| 432 | | |
| 433 | | // While traversing relocations, mark symbols that require special handling such as |
| 434 | | // pointer indirection via GOT, or a stub trampoline via PLT. |
| 435 | | switch (rel.r_type()) { |
| 436 | | elf.R_X86_64_64 => { |
| 437 | | try self.scanReloc(symbol, rel, dynAbsRelocAction(symbol, elf_file), elf_file); |
| 438 | | }, |
| 439 | | |
| 440 | | elf.R_X86_64_32, |
| 441 | | elf.R_X86_64_32S, |
| 442 | | => { |
| 443 | | try self.scanReloc(symbol, rel, dynAbsRelocAction(symbol, elf_file), elf_file); |
| 444 | | }, |
| 445 | | |
| 446 | | elf.R_X86_64_GOT32, |
| 447 | | elf.R_X86_64_GOTPC32, |
| 448 | | elf.R_X86_64_GOTPC64, |
| 449 | | elf.R_X86_64_GOTPCREL, |
| 450 | | elf.R_X86_64_GOTPCREL64, |
| 451 | | elf.R_X86_64_GOTPCRELX, |
| 452 | | elf.R_X86_64_REX_GOTPCRELX, |
| 453 | | => { |
| 454 | | symbol.flags.needs_got = true; |
| 455 | | }, |
| 456 | | |
| 457 | | elf.R_X86_64_PLT32, |
| 458 | | elf.R_X86_64_PLTOFF64, |
| 459 | | => { |
| 460 | | if (symbol.flags.import) { |
| 461 | | symbol.flags.needs_plt = true; |
| 462 | | } |
| 463 | | }, |
| 464 | | |
| 465 | | elf.R_X86_64_PC32 => { |
| 466 | | try self.scanReloc(symbol, rel, pcRelocAction(symbol, elf_file), elf_file); |
| 467 | | }, |
| 468 | | |
| 469 | | elf.R_X86_64_TLSGD => { |
| 470 | | // TODO verify followed by appropriate relocation such as PLT32 __tls_get_addr |
| 471 | | |
| 472 | | if (is_static or (!symbol.flags.import and !is_dyn_lib)) { |
| 473 | | // Relax if building with -static flag as __tls_get_addr() will not be present in libc.a |
| 474 | | // We skip the next relocation. |
| 475 | | i += 1; |
| 476 | | } else if (!symbol.flags.import and is_dyn_lib) { |
| 477 | | symbol.flags.needs_gottp = true; |
| 478 | | i += 1; |
| 479 | | } else { |
| 480 | | symbol.flags.needs_tlsgd = true; |
| 481 | | } |
| 482 | | }, |
| 483 | | |
| 484 | | elf.R_X86_64_TLSLD => { |
| 485 | | // TODO verify followed by appropriate relocation such as PLT32 __tls_get_addr |
| 486 | | |
| 487 | | if (is_static or !is_dyn_lib) { |
| 488 | | // Relax if building with -static flag as __tls_get_addr() will not be present in libc.a |
| 489 | | // We skip the next relocation. |
| 490 | | i += 1; |
| 491 | | } else { |
| 492 | | elf_file.got.flags.needs_tlsld = true; |
| 493 | | } |
| 494 | | }, |
| 495 | | |
| 496 | | elf.R_X86_64_GOTTPOFF => { |
| 497 | | const should_relax = blk: { |
| 498 | | if (is_dyn_lib or symbol.flags.import) break :blk false; |
| 499 | | if (!x86_64.canRelaxGotTpOff(code.?[r_offset - 3 ..])) break :blk false; |
| 500 | | break :blk true; |
| 501 | | }; |
| 502 | | if (!should_relax) { |
| 503 | | symbol.flags.needs_gottp = true; |
| 504 | | } |
| 505 | | }, |
| 506 | | |
| 507 | | elf.R_X86_64_GOTPC32_TLSDESC => { |
| 508 | | const should_relax = is_static or (!is_dyn_lib and !symbol.flags.import); |
| 509 | | if (!should_relax) { |
| 510 | | symbol.flags.needs_tlsdesc = true; |
| 511 | | } |
| 512 | | }, |
| 513 | | |
| 514 | | elf.R_X86_64_TPOFF32, |
| 515 | | elf.R_X86_64_TPOFF64, |
| 516 | | => { |
| 517 | | if (is_dyn_lib) try self.reportPicError(symbol, rel, elf_file); |
| 518 | | }, |
| 519 | | |
| 520 | | elf.R_X86_64_GOTOFF64, |
| 521 | | elf.R_X86_64_DTPOFF32, |
| 522 | | elf.R_X86_64_DTPOFF64, |
| 523 | | elf.R_X86_64_SIZE32, |
| 524 | | elf.R_X86_64_SIZE64, |
| 525 | | elf.R_X86_64_TLSDESC_CALL, |
| 526 | | => {}, |
| 527 | | |
| 528 | | // Zig custom relocations |
| 529 | | Elf.R_X86_64_ZIG_GOT32, |
| 530 | | Elf.R_X86_64_ZIG_GOTPCREL, |
| 531 | | => { |
| 532 | | assert(symbol.flags.has_zig_got); |
| 533 | | }, |
| 534 | | |
| 535 | | else => try self.reportUnhandledRelocError(rel, elf_file), |
| 536 | | } |
| 395 | switch (elf_file.getTarget().cpu.arch) { |
| 396 | .x86_64 => try x86_64.scanRelocs(self, elf_file, code, undefs), |
| 397 | else => return error.UnsupportedCpuArch, |
| 537 | 398 | } |
| 538 | 399 | } |
| 539 | 400 | |
| ... | ... | @@ -693,7 +554,7 @@ fn dataType(symbol: *const Symbol, elf_file: *Elf) u2 { |
| 693 | 554 | fn reportUnhandledRelocError(self: Atom, rel: elf.Elf64_Rela, elf_file: *Elf) error{OutOfMemory}!void { |
| 694 | 555 | var err = try elf_file.addErrorWithNotes(1); |
| 695 | 556 | try err.addMsg(elf_file, "fatal linker error: unhandled relocation type {} at offset 0x{x}", .{ |
| 696 | | fmtRelocType(rel.r_type()), |
| 557 | relocation.fmtRelocType(rel.r_type(), elf_file.getTarget().cpu.arch), |
| 697 | 558 | rel.r_offset, |
| 698 | 559 | }); |
| 699 | 560 | try err.addNote(elf_file, "in {}:{s}", .{ |
| ... | ... | @@ -789,182 +650,9 @@ fn reportUndefined( |
| 789 | 650 | pub fn resolveRelocsAlloc(self: Atom, elf_file: *Elf, code: []u8) !void { |
| 790 | 651 | relocs_log.debug("0x{x}: {s}", .{ self.address(elf_file), self.name(elf_file) }); |
| 791 | 652 | |
| 792 | | const file_ptr = self.file(elf_file).?; |
| 793 | | var stream = std.io.fixedBufferStream(code); |
| 794 | | const cwriter = stream.writer(); |
| 795 | | |
| 796 | | const rels = self.relocs(elf_file); |
| 797 | | var i: usize = 0; |
| 798 | | while (i < rels.len) : (i += 1) { |
| 799 | | const rel = rels[i]; |
| 800 | | const r_type = rel.r_type(); |
| 801 | | if (r_type == elf.R_X86_64_NONE) continue; |
| 802 | | |
| 803 | | const target = switch (file_ptr) { |
| 804 | | .zig_object => |x| elf_file.symbol(x.symbol(rel.r_sym())), |
| 805 | | .object => |x| elf_file.symbol(x.symbols.items[rel.r_sym()]), |
| 806 | | else => unreachable, |
| 807 | | }; |
| 808 | | const r_offset = std.math.cast(usize, rel.r_offset) orelse return error.Overflow; |
| 809 | | |
| 810 | | // We will use equation format to resolve relocations: |
| 811 | | // https://intezer.com/blog/malware-analysis/executable-and-linkable-format-101-part-3-relocations/ |
| 812 | | // |
| 813 | | // Address of the source atom. |
| 814 | | const P = @as(i64, @intCast(self.address(elf_file) + rel.r_offset)); |
| 815 | | // Addend from the relocation. |
| 816 | | const A = rel.r_addend; |
| 817 | | // Address of the target symbol - can be address of the symbol within an atom or address of PLT stub. |
| 818 | | const S = @as(i64, @intCast(target.address(.{}, elf_file))); |
| 819 | | // Address of the global offset table. |
| 820 | | const GOT = blk: { |
| 821 | | const shndx = if (elf_file.got_plt_section_index) |shndx| |
| 822 | | shndx |
| 823 | | else if (elf_file.got_section_index) |shndx| |
| 824 | | shndx |
| 825 | | else |
| 826 | | null; |
| 827 | | break :blk if (shndx) |index| @as(i64, @intCast(elf_file.shdrs.items[index].sh_addr)) else 0; |
| 828 | | }; |
| 829 | | // Address of the .zig.got table entry if any. |
| 830 | | const ZIG_GOT = @as(i64, @intCast(target.zigGotAddress(elf_file))); |
| 831 | | // Relative offset to the start of the global offset table. |
| 832 | | const G = @as(i64, @intCast(target.gotAddress(elf_file))) - GOT; |
| 833 | | // // Address of the thread pointer. |
| 834 | | const TP = @as(i64, @intCast(elf_file.tpAddress())); |
| 835 | | // Address of the dynamic thread pointer. |
| 836 | | const DTP = @as(i64, @intCast(elf_file.dtpAddress())); |
| 837 | | |
| 838 | | relocs_log.debug(" {s}: {x}: [{x} => {x}] G({x}) ZG({x}) ({s})", .{ |
| 839 | | fmtRelocType(r_type), |
| 840 | | r_offset, |
| 841 | | P, |
| 842 | | S + A, |
| 843 | | G + GOT + A, |
| 844 | | ZIG_GOT + A, |
| 845 | | target.name(elf_file), |
| 846 | | }); |
| 847 | | |
| 848 | | try stream.seekTo(r_offset); |
| 849 | | |
| 850 | | switch (rel.r_type()) { |
| 851 | | elf.R_X86_64_NONE => unreachable, |
| 852 | | |
| 853 | | elf.R_X86_64_64 => { |
| 854 | | try self.resolveDynAbsReloc( |
| 855 | | target, |
| 856 | | rel, |
| 857 | | dynAbsRelocAction(target, elf_file), |
| 858 | | elf_file, |
| 859 | | cwriter, |
| 860 | | ); |
| 861 | | }, |
| 862 | | |
| 863 | | elf.R_X86_64_PLT32, |
| 864 | | elf.R_X86_64_PC32, |
| 865 | | => try cwriter.writeInt(i32, @as(i32, @intCast(S + A - P)), .little), |
| 866 | | |
| 867 | | elf.R_X86_64_GOTPCREL => try cwriter.writeInt(i32, @as(i32, @intCast(G + GOT + A - P)), .little), |
| 868 | | elf.R_X86_64_GOTPC32 => try cwriter.writeInt(i32, @as(i32, @intCast(GOT + A - P)), .little), |
| 869 | | elf.R_X86_64_GOTPC64 => try cwriter.writeInt(i64, GOT + A - P, .little), |
| 870 | | |
| 871 | | elf.R_X86_64_GOTPCRELX => { |
| 872 | | if (!target.flags.import and !target.isIFunc(elf_file) and !target.isAbs(elf_file)) blk: { |
| 873 | | x86_64.relaxGotpcrelx(code[r_offset - 2 ..]) catch break :blk; |
| 874 | | try cwriter.writeInt(i32, @as(i32, @intCast(S + A - P)), .little); |
| 875 | | continue; |
| 876 | | } |
| 877 | | try cwriter.writeInt(i32, @as(i32, @intCast(G + GOT + A - P)), .little); |
| 878 | | }, |
| 879 | | |
| 880 | | elf.R_X86_64_REX_GOTPCRELX => { |
| 881 | | if (!target.flags.import and !target.isIFunc(elf_file) and !target.isAbs(elf_file)) blk: { |
| 882 | | x86_64.relaxRexGotpcrelx(code[r_offset - 3 ..]) catch break :blk; |
| 883 | | try cwriter.writeInt(i32, @as(i32, @intCast(S + A - P)), .little); |
| 884 | | continue; |
| 885 | | } |
| 886 | | try cwriter.writeInt(i32, @as(i32, @intCast(G + GOT + A - P)), .little); |
| 887 | | }, |
| 888 | | |
| 889 | | elf.R_X86_64_32 => try cwriter.writeInt(u32, @as(u32, @truncate(@as(u64, @intCast(S + A)))), .little), |
| 890 | | elf.R_X86_64_32S => try cwriter.writeInt(i32, @as(i32, @truncate(S + A)), .little), |
| 891 | | |
| 892 | | elf.R_X86_64_TPOFF32 => try cwriter.writeInt(i32, @as(i32, @truncate(S + A - TP)), .little), |
| 893 | | elf.R_X86_64_TPOFF64 => try cwriter.writeInt(i64, S + A - TP, .little), |
| 894 | | |
| 895 | | elf.R_X86_64_DTPOFF32 => try cwriter.writeInt(i32, @as(i32, @truncate(S + A - DTP)), .little), |
| 896 | | elf.R_X86_64_DTPOFF64 => try cwriter.writeInt(i64, S + A - DTP, .little), |
| 897 | | |
| 898 | | elf.R_X86_64_TLSGD => { |
| 899 | | if (target.flags.has_tlsgd) { |
| 900 | | const S_ = @as(i64, @intCast(target.tlsGdAddress(elf_file))); |
| 901 | | try cwriter.writeInt(i32, @as(i32, @intCast(S_ + A - P)), .little); |
| 902 | | } else if (target.flags.has_gottp) { |
| 903 | | const S_ = @as(i64, @intCast(target.gotTpAddress(elf_file))); |
| 904 | | try x86_64.relaxTlsGdToIe(self, rels[i .. i + 2], @intCast(S_ - P), elf_file, &stream); |
| 905 | | i += 1; |
| 906 | | } else { |
| 907 | | try x86_64.relaxTlsGdToLe( |
| 908 | | self, |
| 909 | | rels[i .. i + 2], |
| 910 | | @as(i32, @intCast(S - TP)), |
| 911 | | elf_file, |
| 912 | | &stream, |
| 913 | | ); |
| 914 | | i += 1; |
| 915 | | } |
| 916 | | }, |
| 917 | | |
| 918 | | elf.R_X86_64_TLSLD => { |
| 919 | | if (elf_file.got.tlsld_index) |entry_index| { |
| 920 | | const tlsld_entry = elf_file.got.entries.items[entry_index]; |
| 921 | | const S_ = @as(i64, @intCast(tlsld_entry.address(elf_file))); |
| 922 | | try cwriter.writeInt(i32, @as(i32, @intCast(S_ + A - P)), .little); |
| 923 | | } else { |
| 924 | | try x86_64.relaxTlsLdToLe( |
| 925 | | self, |
| 926 | | rels[i .. i + 2], |
| 927 | | @as(i32, @intCast(TP - @as(i64, @intCast(elf_file.tlsAddress())))), |
| 928 | | elf_file, |
| 929 | | &stream, |
| 930 | | ); |
| 931 | | i += 1; |
| 932 | | } |
| 933 | | }, |
| 934 | | |
| 935 | | elf.R_X86_64_GOTPC32_TLSDESC => { |
| 936 | | if (target.flags.has_tlsdesc) { |
| 937 | | const S_ = @as(i64, @intCast(target.tlsDescAddress(elf_file))); |
| 938 | | try cwriter.writeInt(i32, @as(i32, @intCast(S_ + A - P)), .little); |
| 939 | | } else { |
| 940 | | try x86_64.relaxGotPcTlsDesc(code[r_offset - 3 ..]); |
| 941 | | try cwriter.writeInt(i32, @as(i32, @intCast(S - TP)), .little); |
| 942 | | } |
| 943 | | }, |
| 944 | | |
| 945 | | elf.R_X86_64_TLSDESC_CALL => if (!target.flags.has_tlsdesc) { |
| 946 | | // call -> nop |
| 947 | | try cwriter.writeAll(&.{ 0x66, 0x90 }); |
| 948 | | }, |
| 949 | | |
| 950 | | elf.R_X86_64_GOTTPOFF => { |
| 951 | | if (target.flags.has_gottp) { |
| 952 | | const S_ = @as(i64, @intCast(target.gotTpAddress(elf_file))); |
| 953 | | try cwriter.writeInt(i32, @as(i32, @intCast(S_ + A - P)), .little); |
| 954 | | } else { |
| 955 | | x86_64.relaxGotTpOff(code[r_offset - 3 ..]) catch unreachable; |
| 956 | | try cwriter.writeInt(i32, @as(i32, @intCast(S - TP)), .little); |
| 957 | | } |
| 958 | | }, |
| 959 | | |
| 960 | | elf.R_X86_64_GOT32 => try cwriter.writeInt(i32, @as(i32, @intCast(G + GOT + A)), .little), |
| 961 | | |
| 962 | | // Zig custom relocations |
| 963 | | Elf.R_X86_64_ZIG_GOT32 => try cwriter.writeInt(u32, @as(u32, @intCast(ZIG_GOT + A)), .little), |
| 964 | | Elf.R_X86_64_ZIG_GOTPCREL => try cwriter.writeInt(i32, @as(i32, @intCast(ZIG_GOT + A - P)), .little), |
| 965 | | |
| 966 | | else => {}, |
| 967 | | } |
| 653 | switch (elf_file.getTarget().cpu.arch) { |
| 654 | .x86_64 => try x86_64.resolveRelocsAlloc(self, elf_file, code), |
| 655 | else => return error.UnsupportedCpuArch, |
| 968 | 656 | } |
| 969 | 657 | } |
| 970 | 658 | |
| ... | ... | @@ -978,6 +666,7 @@ fn resolveDynAbsReloc( |
| 978 | 666 | ) !void { |
| 979 | 667 | const comp = elf_file.base.comp; |
| 980 | 668 | const gpa = comp.gpa; |
| 669 | const cpu_arch = elf_file.getTarget().cpu.arch; |
| 981 | 670 | const P = self.address(elf_file) + rel.r_offset; |
| 982 | 671 | const A = rel.r_addend; |
| 983 | 672 | const S = @as(i64, @intCast(target.address(.{}, elf_file))); |
| ... | ... | @@ -1005,7 +694,7 @@ fn resolveDynAbsReloc( |
| 1005 | 694 | elf_file.addRelaDynAssumeCapacity(.{ |
| 1006 | 695 | .offset = P, |
| 1007 | 696 | .sym = target.extra(elf_file).?.dynamic, |
| 1008 | | .type = elf.R_X86_64_64, |
| 697 | .type = relocation.encode(.abs, cpu_arch), |
| 1009 | 698 | .addend = A, |
| 1010 | 699 | }); |
| 1011 | 700 | try applyDynamicReloc(A, elf_file, writer); |
| ... | ... | @@ -1019,7 +708,7 @@ fn resolveDynAbsReloc( |
| 1019 | 708 | elf_file.addRelaDynAssumeCapacity(.{ |
| 1020 | 709 | .offset = P, |
| 1021 | 710 | .sym = target.extra(elf_file).?.dynamic, |
| 1022 | | .type = elf.R_X86_64_64, |
| 711 | .type = relocation.encode(.abs, cpu_arch), |
| 1023 | 712 | .addend = A, |
| 1024 | 713 | }); |
| 1025 | 714 | try applyDynamicReloc(A, elf_file, writer); |
| ... | ... | @@ -1032,7 +721,7 @@ fn resolveDynAbsReloc( |
| 1032 | 721 | elf_file.addRelaDynAssumeCapacity(.{ |
| 1033 | 722 | .offset = P, |
| 1034 | 723 | .sym = target.extra(elf_file).?.dynamic, |
| 1035 | | .type = elf.R_X86_64_64, |
| 724 | .type = relocation.encode(.abs, cpu_arch), |
| 1036 | 725 | .addend = A, |
| 1037 | 726 | }); |
| 1038 | 727 | try applyDynamicReloc(A, elf_file, writer); |
| ... | ... | @@ -1041,7 +730,7 @@ fn resolveDynAbsReloc( |
| 1041 | 730 | .baserel => { |
| 1042 | 731 | elf_file.addRelaDynAssumeCapacity(.{ |
| 1043 | 732 | .offset = P, |
| 1044 | | .type = elf.R_X86_64_RELATIVE, |
| 733 | .type = relocation.encode(.rel, cpu_arch), |
| 1045 | 734 | .addend = S + A, |
| 1046 | 735 | }); |
| 1047 | 736 | try applyDynamicReloc(S + A, elf_file, writer); |
| ... | ... | @@ -1051,7 +740,7 @@ fn resolveDynAbsReloc( |
| 1051 | 740 | const S_ = @as(i64, @intCast(target.address(.{ .plt = false }, elf_file))); |
| 1052 | 741 | elf_file.addRelaDynAssumeCapacity(.{ |
| 1053 | 742 | .offset = P, |
| 1054 | | .type = elf.R_X86_64_IRELATIVE, |
| 743 | .type = relocation.encode(.irel, cpu_arch), |
| 1055 | 744 | .addend = S_ + A, |
| 1056 | 745 | }); |
| 1057 | 746 | try applyDynamicReloc(S_ + A, elf_file, writer); |
| ... | ... | @@ -1069,158 +758,12 @@ fn applyDynamicReloc(value: i64, elf_file: *Elf, writer: anytype) !void { |
| 1069 | 758 | pub fn resolveRelocsNonAlloc(self: Atom, elf_file: *Elf, code: []u8, undefs: anytype) !void { |
| 1070 | 759 | relocs_log.debug("0x{x}: {s}", .{ self.address(elf_file), self.name(elf_file) }); |
| 1071 | 760 | |
| 1072 | | const file_ptr = self.file(elf_file).?; |
| 1073 | | var stream = std.io.fixedBufferStream(code); |
| 1074 | | const cwriter = stream.writer(); |
| 1075 | | |
| 1076 | | const rels = self.relocs(elf_file); |
| 1077 | | var i: usize = 0; |
| 1078 | | while (i < rels.len) : (i += 1) { |
| 1079 | | const rel = rels[i]; |
| 1080 | | const r_type = rel.r_type(); |
| 1081 | | if (r_type == elf.R_X86_64_NONE) continue; |
| 1082 | | |
| 1083 | | const r_offset = std.math.cast(usize, rel.r_offset) orelse return error.Overflow; |
| 1084 | | |
| 1085 | | const target_index = switch (file_ptr) { |
| 1086 | | .zig_object => |x| x.symbol(rel.r_sym()), |
| 1087 | | .object => |x| x.symbols.items[rel.r_sym()], |
| 1088 | | else => unreachable, |
| 1089 | | }; |
| 1090 | | const target = elf_file.symbol(target_index); |
| 1091 | | |
| 1092 | | // Check for violation of One Definition Rule for COMDATs. |
| 1093 | | if (target.file(elf_file) == null) { |
| 1094 | | // TODO convert into an error |
| 1095 | | log.debug("{}: {s}: {s} refers to a discarded COMDAT section", .{ |
| 1096 | | file_ptr.fmtPath(), |
| 1097 | | self.name(elf_file), |
| 1098 | | target.name(elf_file), |
| 1099 | | }); |
| 1100 | | continue; |
| 1101 | | } |
| 1102 | | |
| 1103 | | // Report an undefined symbol. |
| 1104 | | try self.reportUndefined(elf_file, target, target_index, rel, undefs); |
| 1105 | | |
| 1106 | | // We will use equation format to resolve relocations: |
| 1107 | | // https://intezer.com/blog/malware-analysis/executable-and-linkable-format-101-part-3-relocations/ |
| 1108 | | // |
| 1109 | | const P = @as(i64, @intCast(self.address(elf_file) + rel.r_offset)); |
| 1110 | | // Addend from the relocation. |
| 1111 | | const A = rel.r_addend; |
| 1112 | | // Address of the target symbol - can be address of the symbol within an atom or address of PLT stub. |
| 1113 | | const S = @as(i64, @intCast(target.address(.{}, elf_file))); |
| 1114 | | // Address of the global offset table. |
| 1115 | | const GOT = blk: { |
| 1116 | | const shndx = if (elf_file.got_plt_section_index) |shndx| |
| 1117 | | shndx |
| 1118 | | else if (elf_file.got_section_index) |shndx| |
| 1119 | | shndx |
| 1120 | | else |
| 1121 | | null; |
| 1122 | | break :blk if (shndx) |index| @as(i64, @intCast(elf_file.shdrs.items[index].sh_addr)) else 0; |
| 1123 | | }; |
| 1124 | | // Address of the dynamic thread pointer. |
| 1125 | | const DTP = @as(i64, @intCast(elf_file.dtpAddress())); |
| 1126 | | |
| 1127 | | relocs_log.debug(" {s}: {x}: [{x} => {x}] ({s})", .{ |
| 1128 | | fmtRelocType(r_type), |
| 1129 | | rel.r_offset, |
| 1130 | | P, |
| 1131 | | S + A, |
| 1132 | | target.name(elf_file), |
| 1133 | | }); |
| 1134 | | |
| 1135 | | try stream.seekTo(r_offset); |
| 1136 | | |
| 1137 | | switch (r_type) { |
| 1138 | | elf.R_X86_64_NONE => unreachable, |
| 1139 | | elf.R_X86_64_8 => try cwriter.writeInt(u8, @as(u8, @bitCast(@as(i8, @intCast(S + A)))), .little), |
| 1140 | | elf.R_X86_64_16 => try cwriter.writeInt(u16, @as(u16, @bitCast(@as(i16, @intCast(S + A)))), .little), |
| 1141 | | elf.R_X86_64_32 => try cwriter.writeInt(u32, @as(u32, @bitCast(@as(i32, @intCast(S + A)))), .little), |
| 1142 | | elf.R_X86_64_32S => try cwriter.writeInt(i32, @as(i32, @intCast(S + A)), .little), |
| 1143 | | elf.R_X86_64_64 => try cwriter.writeInt(i64, S + A, .little), |
| 1144 | | elf.R_X86_64_DTPOFF32 => try cwriter.writeInt(i32, @as(i32, @intCast(S + A - DTP)), .little), |
| 1145 | | elf.R_X86_64_DTPOFF64 => try cwriter.writeInt(i64, S + A - DTP, .little), |
| 1146 | | elf.R_X86_64_GOTOFF64 => try cwriter.writeInt(i64, S + A - GOT, .little), |
| 1147 | | elf.R_X86_64_GOTPC64 => try cwriter.writeInt(i64, GOT + A, .little), |
| 1148 | | elf.R_X86_64_SIZE32 => { |
| 1149 | | const size = @as(i64, @intCast(target.elfSym(elf_file).st_size)); |
| 1150 | | try cwriter.writeInt(u32, @as(u32, @bitCast(@as(i32, @intCast(size + A)))), .little); |
| 1151 | | }, |
| 1152 | | elf.R_X86_64_SIZE64 => { |
| 1153 | | const size = @as(i64, @intCast(target.elfSym(elf_file).st_size)); |
| 1154 | | try cwriter.writeInt(i64, @as(i64, @intCast(size + A)), .little); |
| 1155 | | }, |
| 1156 | | else => try self.reportUnhandledRelocError(rel, elf_file), |
| 1157 | | } |
| 761 | switch (elf_file.getTarget().cpu.arch) { |
| 762 | .x86_64 => try x86_64.resolveRelocsNonAlloc(self, elf_file, code, undefs), |
| 763 | else => return error.UnsupportedCpuArch, |
| 1158 | 764 | } |
| 1159 | 765 | } |
| 1160 | 766 | |
| 1161 | | pub fn fmtRelocType(r_type: u32) std.fmt.Formatter(formatRelocType) { |
| 1162 | | return .{ .data = r_type }; |
| 1163 | | } |
| 1164 | | |
| 1165 | | fn formatRelocType( |
| 1166 | | r_type: u32, |
| 1167 | | comptime unused_fmt_string: []const u8, |
| 1168 | | options: std.fmt.FormatOptions, |
| 1169 | | writer: anytype, |
| 1170 | | ) !void { |
| 1171 | | _ = unused_fmt_string; |
| 1172 | | _ = options; |
| 1173 | | const str = switch (r_type) { |
| 1174 | | elf.R_X86_64_NONE => "R_X86_64_NONE", |
| 1175 | | elf.R_X86_64_64 => "R_X86_64_64", |
| 1176 | | elf.R_X86_64_PC32 => "R_X86_64_PC32", |
| 1177 | | elf.R_X86_64_GOT32 => "R_X86_64_GOT32", |
| 1178 | | elf.R_X86_64_PLT32 => "R_X86_64_PLT32", |
| 1179 | | elf.R_X86_64_COPY => "R_X86_64_COPY", |
| 1180 | | elf.R_X86_64_GLOB_DAT => "R_X86_64_GLOB_DAT", |
| 1181 | | elf.R_X86_64_JUMP_SLOT => "R_X86_64_JUMP_SLOT", |
| 1182 | | elf.R_X86_64_RELATIVE => "R_X86_64_RELATIVE", |
| 1183 | | elf.R_X86_64_GOTPCREL => "R_X86_64_GOTPCREL", |
| 1184 | | elf.R_X86_64_32 => "R_X86_64_32", |
| 1185 | | elf.R_X86_64_32S => "R_X86_64_32S", |
| 1186 | | elf.R_X86_64_16 => "R_X86_64_16", |
| 1187 | | elf.R_X86_64_PC16 => "R_X86_64_PC16", |
| 1188 | | elf.R_X86_64_8 => "R_X86_64_8", |
| 1189 | | elf.R_X86_64_PC8 => "R_X86_64_PC8", |
| 1190 | | elf.R_X86_64_DTPMOD64 => "R_X86_64_DTPMOD64", |
| 1191 | | elf.R_X86_64_DTPOFF64 => "R_X86_64_DTPOFF64", |
| 1192 | | elf.R_X86_64_TPOFF64 => "R_X86_64_TPOFF64", |
| 1193 | | elf.R_X86_64_TLSGD => "R_X86_64_TLSGD", |
| 1194 | | elf.R_X86_64_TLSLD => "R_X86_64_TLSLD", |
| 1195 | | elf.R_X86_64_DTPOFF32 => "R_X86_64_DTPOFF32", |
| 1196 | | elf.R_X86_64_GOTTPOFF => "R_X86_64_GOTTPOFF", |
| 1197 | | elf.R_X86_64_TPOFF32 => "R_X86_64_TPOFF32", |
| 1198 | | elf.R_X86_64_PC64 => "R_X86_64_PC64", |
| 1199 | | elf.R_X86_64_GOTOFF64 => "R_X86_64_GOTOFF64", |
| 1200 | | elf.R_X86_64_GOTPC32 => "R_X86_64_GOTPC32", |
| 1201 | | elf.R_X86_64_GOT64 => "R_X86_64_GOT64", |
| 1202 | | elf.R_X86_64_GOTPCREL64 => "R_X86_64_GOTPCREL64", |
| 1203 | | elf.R_X86_64_GOTPC64 => "R_X86_64_GOTPC64", |
| 1204 | | elf.R_X86_64_GOTPLT64 => "R_X86_64_GOTPLT64", |
| 1205 | | elf.R_X86_64_PLTOFF64 => "R_X86_64_PLTOFF64", |
| 1206 | | elf.R_X86_64_SIZE32 => "R_X86_64_SIZE32", |
| 1207 | | elf.R_X86_64_SIZE64 => "R_X86_64_SIZE64", |
| 1208 | | elf.R_X86_64_GOTPC32_TLSDESC => "R_X86_64_GOTPC32_TLSDESC", |
| 1209 | | elf.R_X86_64_TLSDESC_CALL => "R_X86_64_TLSDESC_CALL", |
| 1210 | | elf.R_X86_64_TLSDESC => "R_X86_64_TLSDESC", |
| 1211 | | elf.R_X86_64_IRELATIVE => "R_X86_64_IRELATIVE", |
| 1212 | | elf.R_X86_64_RELATIVE64 => "R_X86_64_RELATIVE64", |
| 1213 | | elf.R_X86_64_GOTPCRELX => "R_X86_64_GOTPCRELX", |
| 1214 | | elf.R_X86_64_REX_GOTPCRELX => "R_X86_64_REX_GOTPCRELX", |
| 1215 | | elf.R_X86_64_NUM => "R_X86_64_NUM", |
| 1216 | | // Zig custom relocations |
| 1217 | | Elf.R_X86_64_ZIG_GOT32 => "R_X86_64_ZIG_GOT32", |
| 1218 | | Elf.R_X86_64_ZIG_GOTPCREL => "R_X86_64_ZIG_GOTPCREL", |
| 1219 | | else => "R_X86_64_UNKNOWN", |
| 1220 | | }; |
| 1221 | | try writer.print("{s}", .{str}); |
| 1222 | | } |
| 1223 | | |
| 1224 | 767 | pub fn format( |
| 1225 | 768 | atom: Atom, |
| 1226 | 769 | comptime unused_fmt_string: []const u8, |
| ... | ... | @@ -1285,7 +828,423 @@ pub const Flags = packed struct { |
| 1285 | 828 | }; |
| 1286 | 829 | |
| 1287 | 830 | const x86_64 = struct { |
| 1288 | | pub fn relaxGotpcrelx(code: []u8) !void { |
| 831 | fn scanRelocs(atom: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype) !void { |
| 832 | const is_static = elf_file.base.isStatic(); |
| 833 | const is_dyn_lib = elf_file.base.isDynLib(); |
| 834 | const file_ptr = atom.file(elf_file).?; |
| 835 | const rels = atom.relocs(elf_file); |
| 836 | var i: usize = 0; |
| 837 | while (i < rels.len) : (i += 1) { |
| 838 | const rel = rels[i]; |
| 839 | |
| 840 | if (rel.r_type() == elf.R_X86_64_NONE) continue; |
| 841 | |
| 842 | const r_offset = std.math.cast(usize, rel.r_offset) orelse return error.Overflow; |
| 843 | |
| 844 | const symbol_index = switch (file_ptr) { |
| 845 | .zig_object => |x| x.symbol(rel.r_sym()), |
| 846 | .object => |x| x.symbols.items[rel.r_sym()], |
| 847 | else => unreachable, |
| 848 | }; |
| 849 | const symbol = elf_file.symbol(symbol_index); |
| 850 | |
| 851 | // Check for violation of One Definition Rule for COMDATs. |
| 852 | if (symbol.file(elf_file) == null) { |
| 853 | // TODO convert into an error |
| 854 | log.debug("{}: {s}: {s} refers to a discarded COMDAT section", .{ |
| 855 | file_ptr.fmtPath(), |
| 856 | atom.name(elf_file), |
| 857 | symbol.name(elf_file), |
| 858 | }); |
| 859 | continue; |
| 860 | } |
| 861 | |
| 862 | // Report an undefined symbol. |
| 863 | try atom.reportUndefined(elf_file, symbol, symbol_index, rel, undefs); |
| 864 | |
| 865 | if (symbol.isIFunc(elf_file)) { |
| 866 | symbol.flags.needs_got = true; |
| 867 | symbol.flags.needs_plt = true; |
| 868 | } |
| 869 | |
| 870 | // While traversing relocations, mark symbols that require special handling such as |
| 871 | // pointer indirection via GOT, or a stub trampoline via PLT. |
| 872 | switch (rel.r_type()) { |
| 873 | elf.R_X86_64_64 => { |
| 874 | try atom.scanReloc(symbol, rel, dynAbsRelocAction(symbol, elf_file), elf_file); |
| 875 | }, |
| 876 | |
| 877 | elf.R_X86_64_32, |
| 878 | elf.R_X86_64_32S, |
| 879 | => { |
| 880 | try atom.scanReloc(symbol, rel, dynAbsRelocAction(symbol, elf_file), elf_file); |
| 881 | }, |
| 882 | |
| 883 | elf.R_X86_64_GOT32, |
| 884 | elf.R_X86_64_GOTPC32, |
| 885 | elf.R_X86_64_GOTPC64, |
| 886 | elf.R_X86_64_GOTPCREL, |
| 887 | elf.R_X86_64_GOTPCREL64, |
| 888 | elf.R_X86_64_GOTPCRELX, |
| 889 | elf.R_X86_64_REX_GOTPCRELX, |
| 890 | => { |
| 891 | symbol.flags.needs_got = true; |
| 892 | }, |
| 893 | |
| 894 | elf.R_X86_64_PLT32, |
| 895 | elf.R_X86_64_PLTOFF64, |
| 896 | => { |
| 897 | if (symbol.flags.import) { |
| 898 | symbol.flags.needs_plt = true; |
| 899 | } |
| 900 | }, |
| 901 | |
| 902 | elf.R_X86_64_PC32 => { |
| 903 | try atom.scanReloc(symbol, rel, pcRelocAction(symbol, elf_file), elf_file); |
| 904 | }, |
| 905 | |
| 906 | elf.R_X86_64_TLSGD => { |
| 907 | // TODO verify followed by appropriate relocation such as PLT32 __tls_get_addr |
| 908 | |
| 909 | if (is_static or (!symbol.flags.import and !is_dyn_lib)) { |
| 910 | // Relax if building with -static flag as __tls_get_addr() will not be present in libc.a |
| 911 | // We skip the next relocation. |
| 912 | i += 1; |
| 913 | } else if (!symbol.flags.import and is_dyn_lib) { |
| 914 | symbol.flags.needs_gottp = true; |
| 915 | i += 1; |
| 916 | } else { |
| 917 | symbol.flags.needs_tlsgd = true; |
| 918 | } |
| 919 | }, |
| 920 | |
| 921 | elf.R_X86_64_TLSLD => { |
| 922 | // TODO verify followed by appropriate relocation such as PLT32 __tls_get_addr |
| 923 | |
| 924 | if (is_static or !is_dyn_lib) { |
| 925 | // Relax if building with -static flag as __tls_get_addr() will not be present in libc.a |
| 926 | // We skip the next relocation. |
| 927 | i += 1; |
| 928 | } else { |
| 929 | elf_file.got.flags.needs_tlsld = true; |
| 930 | } |
| 931 | }, |
| 932 | |
| 933 | elf.R_X86_64_GOTTPOFF => { |
| 934 | const should_relax = blk: { |
| 935 | if (is_dyn_lib or symbol.flags.import) break :blk false; |
| 936 | if (!x86_64.canRelaxGotTpOff(code.?[r_offset - 3 ..])) break :blk false; |
| 937 | break :blk true; |
| 938 | }; |
| 939 | if (!should_relax) { |
| 940 | symbol.flags.needs_gottp = true; |
| 941 | } |
| 942 | }, |
| 943 | |
| 944 | elf.R_X86_64_GOTPC32_TLSDESC => { |
| 945 | const should_relax = is_static or (!is_dyn_lib and !symbol.flags.import); |
| 946 | if (!should_relax) { |
| 947 | symbol.flags.needs_tlsdesc = true; |
| 948 | } |
| 949 | }, |
| 950 | |
| 951 | elf.R_X86_64_TPOFF32, |
| 952 | elf.R_X86_64_TPOFF64, |
| 953 | => { |
| 954 | if (is_dyn_lib) try atom.reportPicError(symbol, rel, elf_file); |
| 955 | }, |
| 956 | |
| 957 | elf.R_X86_64_GOTOFF64, |
| 958 | elf.R_X86_64_DTPOFF32, |
| 959 | elf.R_X86_64_DTPOFF64, |
| 960 | elf.R_X86_64_SIZE32, |
| 961 | elf.R_X86_64_SIZE64, |
| 962 | elf.R_X86_64_TLSDESC_CALL, |
| 963 | => {}, |
| 964 | |
| 965 | // Zig custom relocations |
| 966 | Elf.R_X86_64_ZIG_GOT32, |
| 967 | Elf.R_X86_64_ZIG_GOTPCREL, |
| 968 | => { |
| 969 | assert(symbol.flags.has_zig_got); |
| 970 | }, |
| 971 | |
| 972 | else => try atom.reportUnhandledRelocError(rel, elf_file), |
| 973 | } |
| 974 | } |
| 975 | } |
| 976 | |
| 977 | fn resolveRelocsAlloc(atom: Atom, elf_file: *Elf, code: []u8) !void { |
| 978 | const file_ptr = atom.file(elf_file).?; |
| 979 | var stream = std.io.fixedBufferStream(code); |
| 980 | const cwriter = stream.writer(); |
| 981 | |
| 982 | const rels = atom.relocs(elf_file); |
| 983 | var i: usize = 0; |
| 984 | while (i < rels.len) : (i += 1) { |
| 985 | const rel = rels[i]; |
| 986 | const r_type = rel.r_type(); |
| 987 | if (r_type == elf.R_X86_64_NONE) continue; |
| 988 | |
| 989 | const target = switch (file_ptr) { |
| 990 | .zig_object => |x| elf_file.symbol(x.symbol(rel.r_sym())), |
| 991 | .object => |x| elf_file.symbol(x.symbols.items[rel.r_sym()]), |
| 992 | else => unreachable, |
| 993 | }; |
| 994 | const r_offset = std.math.cast(usize, rel.r_offset) orelse return error.Overflow; |
| 995 | |
| 996 | // We will use equation format to resolve relocations: |
| 997 | // https://intezer.com/blog/malware-analysis/executable-and-linkable-format-101-part-3-relocations/ |
| 998 | // |
| 999 | // Address of the source atom. |
| 1000 | const P = @as(i64, @intCast(atom.address(elf_file) + rel.r_offset)); |
| 1001 | // Addend from the relocation. |
| 1002 | const A = rel.r_addend; |
| 1003 | // Address of the target symbol - can be address of the symbol within an atom or address of PLT stub. |
| 1004 | const S = @as(i64, @intCast(target.address(.{}, elf_file))); |
| 1005 | // Address of the global offset table. |
| 1006 | const GOT = blk: { |
| 1007 | const shndx = if (elf_file.got_plt_section_index) |shndx| |
| 1008 | shndx |
| 1009 | else if (elf_file.got_section_index) |shndx| |
| 1010 | shndx |
| 1011 | else |
| 1012 | null; |
| 1013 | break :blk if (shndx) |index| @as(i64, @intCast(elf_file.shdrs.items[index].sh_addr)) else 0; |
| 1014 | }; |
| 1015 | // Address of the .zig.got table entry if any. |
| 1016 | const ZIG_GOT = @as(i64, @intCast(target.zigGotAddress(elf_file))); |
| 1017 | // Relative offset to the start of the global offset table. |
| 1018 | const G = @as(i64, @intCast(target.gotAddress(elf_file))) - GOT; |
| 1019 | // // Address of the thread pointer. |
| 1020 | const TP = @as(i64, @intCast(elf_file.tpAddress())); |
| 1021 | // Address of the dynamic thread pointer. |
| 1022 | const DTP = @as(i64, @intCast(elf_file.dtpAddress())); |
| 1023 | |
| 1024 | relocs_log.debug(" {s}: {x}: [{x} => {x}] G({x}) ZG({x}) ({s})", .{ |
| 1025 | relocation.fmtRelocType(r_type, .x86_64), |
| 1026 | r_offset, |
| 1027 | P, |
| 1028 | S + A, |
| 1029 | G + GOT + A, |
| 1030 | ZIG_GOT + A, |
| 1031 | target.name(elf_file), |
| 1032 | }); |
| 1033 | |
| 1034 | try stream.seekTo(r_offset); |
| 1035 | |
| 1036 | switch (rel.r_type()) { |
| 1037 | elf.R_X86_64_NONE => unreachable, |
| 1038 | |
| 1039 | elf.R_X86_64_64 => { |
| 1040 | try atom.resolveDynAbsReloc( |
| 1041 | target, |
| 1042 | rel, |
| 1043 | dynAbsRelocAction(target, elf_file), |
| 1044 | elf_file, |
| 1045 | cwriter, |
| 1046 | ); |
| 1047 | }, |
| 1048 | |
| 1049 | elf.R_X86_64_PLT32, |
| 1050 | elf.R_X86_64_PC32, |
| 1051 | => try cwriter.writeInt(i32, @as(i32, @intCast(S + A - P)), .little), |
| 1052 | |
| 1053 | elf.R_X86_64_GOTPCREL => try cwriter.writeInt(i32, @as(i32, @intCast(G + GOT + A - P)), .little), |
| 1054 | elf.R_X86_64_GOTPC32 => try cwriter.writeInt(i32, @as(i32, @intCast(GOT + A - P)), .little), |
| 1055 | elf.R_X86_64_GOTPC64 => try cwriter.writeInt(i64, GOT + A - P, .little), |
| 1056 | |
| 1057 | elf.R_X86_64_GOTPCRELX => { |
| 1058 | if (!target.flags.import and !target.isIFunc(elf_file) and !target.isAbs(elf_file)) blk: { |
| 1059 | x86_64.relaxGotpcrelx(code[r_offset - 2 ..]) catch break :blk; |
| 1060 | try cwriter.writeInt(i32, @as(i32, @intCast(S + A - P)), .little); |
| 1061 | continue; |
| 1062 | } |
| 1063 | try cwriter.writeInt(i32, @as(i32, @intCast(G + GOT + A - P)), .little); |
| 1064 | }, |
| 1065 | |
| 1066 | elf.R_X86_64_REX_GOTPCRELX => { |
| 1067 | if (!target.flags.import and !target.isIFunc(elf_file) and !target.isAbs(elf_file)) blk: { |
| 1068 | x86_64.relaxRexGotpcrelx(code[r_offset - 3 ..]) catch break :blk; |
| 1069 | try cwriter.writeInt(i32, @as(i32, @intCast(S + A - P)), .little); |
| 1070 | continue; |
| 1071 | } |
| 1072 | try cwriter.writeInt(i32, @as(i32, @intCast(G + GOT + A - P)), .little); |
| 1073 | }, |
| 1074 | |
| 1075 | elf.R_X86_64_32 => try cwriter.writeInt(u32, @as(u32, @truncate(@as(u64, @intCast(S + A)))), .little), |
| 1076 | elf.R_X86_64_32S => try cwriter.writeInt(i32, @as(i32, @truncate(S + A)), .little), |
| 1077 | |
| 1078 | elf.R_X86_64_TPOFF32 => try cwriter.writeInt(i32, @as(i32, @truncate(S + A - TP)), .little), |
| 1079 | elf.R_X86_64_TPOFF64 => try cwriter.writeInt(i64, S + A - TP, .little), |
| 1080 | |
| 1081 | elf.R_X86_64_DTPOFF32 => try cwriter.writeInt(i32, @as(i32, @truncate(S + A - DTP)), .little), |
| 1082 | elf.R_X86_64_DTPOFF64 => try cwriter.writeInt(i64, S + A - DTP, .little), |
| 1083 | |
| 1084 | elf.R_X86_64_TLSGD => { |
| 1085 | if (target.flags.has_tlsgd) { |
| 1086 | const S_ = @as(i64, @intCast(target.tlsGdAddress(elf_file))); |
| 1087 | try cwriter.writeInt(i32, @as(i32, @intCast(S_ + A - P)), .little); |
| 1088 | } else if (target.flags.has_gottp) { |
| 1089 | const S_ = @as(i64, @intCast(target.gotTpAddress(elf_file))); |
| 1090 | try x86_64.relaxTlsGdToIe(atom, rels[i .. i + 2], @intCast(S_ - P), elf_file, &stream); |
| 1091 | i += 1; |
| 1092 | } else { |
| 1093 | try x86_64.relaxTlsGdToLe( |
| 1094 | atom, |
| 1095 | rels[i .. i + 2], |
| 1096 | @as(i32, @intCast(S - TP)), |
| 1097 | elf_file, |
| 1098 | &stream, |
| 1099 | ); |
| 1100 | i += 1; |
| 1101 | } |
| 1102 | }, |
| 1103 | |
| 1104 | elf.R_X86_64_TLSLD => { |
| 1105 | if (elf_file.got.tlsld_index) |entry_index| { |
| 1106 | const tlsld_entry = elf_file.got.entries.items[entry_index]; |
| 1107 | const S_ = @as(i64, @intCast(tlsld_entry.address(elf_file))); |
| 1108 | try cwriter.writeInt(i32, @as(i32, @intCast(S_ + A - P)), .little); |
| 1109 | } else { |
| 1110 | try x86_64.relaxTlsLdToLe( |
| 1111 | atom, |
| 1112 | rels[i .. i + 2], |
| 1113 | @as(i32, @intCast(TP - @as(i64, @intCast(elf_file.tlsAddress())))), |
| 1114 | elf_file, |
| 1115 | &stream, |
| 1116 | ); |
| 1117 | i += 1; |
| 1118 | } |
| 1119 | }, |
| 1120 | |
| 1121 | elf.R_X86_64_GOTPC32_TLSDESC => { |
| 1122 | if (target.flags.has_tlsdesc) { |
| 1123 | const S_ = @as(i64, @intCast(target.tlsDescAddress(elf_file))); |
| 1124 | try cwriter.writeInt(i32, @as(i32, @intCast(S_ + A - P)), .little); |
| 1125 | } else { |
| 1126 | try x86_64.relaxGotPcTlsDesc(code[r_offset - 3 ..]); |
| 1127 | try cwriter.writeInt(i32, @as(i32, @intCast(S - TP)), .little); |
| 1128 | } |
| 1129 | }, |
| 1130 | |
| 1131 | elf.R_X86_64_TLSDESC_CALL => if (!target.flags.has_tlsdesc) { |
| 1132 | // call -> nop |
| 1133 | try cwriter.writeAll(&.{ 0x66, 0x90 }); |
| 1134 | }, |
| 1135 | |
| 1136 | elf.R_X86_64_GOTTPOFF => { |
| 1137 | if (target.flags.has_gottp) { |
| 1138 | const S_ = @as(i64, @intCast(target.gotTpAddress(elf_file))); |
| 1139 | try cwriter.writeInt(i32, @as(i32, @intCast(S_ + A - P)), .little); |
| 1140 | } else { |
| 1141 | x86_64.relaxGotTpOff(code[r_offset - 3 ..]) catch unreachable; |
| 1142 | try cwriter.writeInt(i32, @as(i32, @intCast(S - TP)), .little); |
| 1143 | } |
| 1144 | }, |
| 1145 | |
| 1146 | elf.R_X86_64_GOT32 => try cwriter.writeInt(i32, @as(i32, @intCast(G + GOT + A)), .little), |
| 1147 | |
| 1148 | // Zig custom relocations |
| 1149 | Elf.R_X86_64_ZIG_GOT32 => try cwriter.writeInt(u32, @as(u32, @intCast(ZIG_GOT + A)), .little), |
| 1150 | Elf.R_X86_64_ZIG_GOTPCREL => try cwriter.writeInt(i32, @as(i32, @intCast(ZIG_GOT + A - P)), .little), |
| 1151 | |
| 1152 | else => {}, |
| 1153 | } |
| 1154 | } |
| 1155 | } |
| 1156 | |
| 1157 | fn resolveRelocsNonAlloc(atom: Atom, elf_file: *Elf, code: []u8, undefs: anytype) !void { |
| 1158 | const file_ptr = atom.file(elf_file).?; |
| 1159 | var stream = std.io.fixedBufferStream(code); |
| 1160 | const cwriter = stream.writer(); |
| 1161 | |
| 1162 | const rels = atom.relocs(elf_file); |
| 1163 | var i: usize = 0; |
| 1164 | while (i < rels.len) : (i += 1) { |
| 1165 | const rel = rels[i]; |
| 1166 | const r_type = rel.r_type(); |
| 1167 | if (r_type == elf.R_X86_64_NONE) continue; |
| 1168 | |
| 1169 | const r_offset = std.math.cast(usize, rel.r_offset) orelse return error.Overflow; |
| 1170 | |
| 1171 | const target_index = switch (file_ptr) { |
| 1172 | .zig_object => |x| x.symbol(rel.r_sym()), |
| 1173 | .object => |x| x.symbols.items[rel.r_sym()], |
| 1174 | else => unreachable, |
| 1175 | }; |
| 1176 | const target = elf_file.symbol(target_index); |
| 1177 | |
| 1178 | // Check for violation of One Definition Rule for COMDATs. |
| 1179 | if (target.file(elf_file) == null) { |
| 1180 | // TODO convert into an error |
| 1181 | log.debug("{}: {s}: {s} refers to a discarded COMDAT section", .{ |
| 1182 | file_ptr.fmtPath(), |
| 1183 | atom.name(elf_file), |
| 1184 | target.name(elf_file), |
| 1185 | }); |
| 1186 | continue; |
| 1187 | } |
| 1188 | |
| 1189 | // Report an undefined symbol. |
| 1190 | try atom.reportUndefined(elf_file, target, target_index, rel, undefs); |
| 1191 | |
| 1192 | // We will use equation format to resolve relocations: |
| 1193 | // https://intezer.com/blog/malware-analysis/executable-and-linkable-format-101-part-3-relocations/ |
| 1194 | // |
| 1195 | const P = @as(i64, @intCast(atom.address(elf_file) + rel.r_offset)); |
| 1196 | // Addend from the relocation. |
| 1197 | const A = rel.r_addend; |
| 1198 | // Address of the target symbol - can be address of the symbol within an atom or address of PLT stub. |
| 1199 | const S = @as(i64, @intCast(target.address(.{}, elf_file))); |
| 1200 | // Address of the global offset table. |
| 1201 | const GOT = blk: { |
| 1202 | const shndx = if (elf_file.got_plt_section_index) |shndx| |
| 1203 | shndx |
| 1204 | else if (elf_file.got_section_index) |shndx| |
| 1205 | shndx |
| 1206 | else |
| 1207 | null; |
| 1208 | break :blk if (shndx) |index| @as(i64, @intCast(elf_file.shdrs.items[index].sh_addr)) else 0; |
| 1209 | }; |
| 1210 | // Address of the dynamic thread pointer. |
| 1211 | const DTP = @as(i64, @intCast(elf_file.dtpAddress())); |
| 1212 | |
| 1213 | relocs_log.debug(" {s}: {x}: [{x} => {x}] ({s})", .{ |
| 1214 | relocation.fmtRelocType(r_type, .x86_64), |
| 1215 | rel.r_offset, |
| 1216 | P, |
| 1217 | S + A, |
| 1218 | target.name(elf_file), |
| 1219 | }); |
| 1220 | |
| 1221 | try stream.seekTo(r_offset); |
| 1222 | |
| 1223 | switch (r_type) { |
| 1224 | elf.R_X86_64_NONE => unreachable, |
| 1225 | elf.R_X86_64_8 => try cwriter.writeInt(u8, @as(u8, @bitCast(@as(i8, @intCast(S + A)))), .little), |
| 1226 | elf.R_X86_64_16 => try cwriter.writeInt(u16, @as(u16, @bitCast(@as(i16, @intCast(S + A)))), .little), |
| 1227 | elf.R_X86_64_32 => try cwriter.writeInt(u32, @as(u32, @bitCast(@as(i32, @intCast(S + A)))), .little), |
| 1228 | elf.R_X86_64_32S => try cwriter.writeInt(i32, @as(i32, @intCast(S + A)), .little), |
| 1229 | elf.R_X86_64_64 => try cwriter.writeInt(i64, S + A, .little), |
| 1230 | elf.R_X86_64_DTPOFF32 => try cwriter.writeInt(i32, @as(i32, @intCast(S + A - DTP)), .little), |
| 1231 | elf.R_X86_64_DTPOFF64 => try cwriter.writeInt(i64, S + A - DTP, .little), |
| 1232 | elf.R_X86_64_GOTOFF64 => try cwriter.writeInt(i64, S + A - GOT, .little), |
| 1233 | elf.R_X86_64_GOTPC64 => try cwriter.writeInt(i64, GOT + A, .little), |
| 1234 | elf.R_X86_64_SIZE32 => { |
| 1235 | const size = @as(i64, @intCast(target.elfSym(elf_file).st_size)); |
| 1236 | try cwriter.writeInt(u32, @as(u32, @bitCast(@as(i32, @intCast(size + A)))), .little); |
| 1237 | }, |
| 1238 | elf.R_X86_64_SIZE64 => { |
| 1239 | const size = @as(i64, @intCast(target.elfSym(elf_file).st_size)); |
| 1240 | try cwriter.writeInt(i64, @as(i64, @intCast(size + A)), .little); |
| 1241 | }, |
| 1242 | else => try atom.reportUnhandledRelocError(rel, elf_file), |
| 1243 | } |
| 1244 | } |
| 1245 | } |
| 1246 | |
| 1247 | fn relaxGotpcrelx(code: []u8) !void { |
| 1289 | 1248 | const old_inst = disassemble(code) orelse return error.RelaxFail; |
| 1290 | 1249 | const inst = switch (old_inst.encoding.mnemonic) { |
| 1291 | 1250 | .call => try Instruction.new(old_inst.prefix, .call, &.{ |
| ... | ... | @@ -1303,7 +1262,7 @@ const x86_64 = struct { |
| 1303 | 1262 | encode(&.{ nop, inst }, code) catch return error.RelaxFail; |
| 1304 | 1263 | } |
| 1305 | 1264 | |
| 1306 | | pub fn relaxRexGotpcrelx(code: []u8) !void { |
| 1265 | fn relaxRexGotpcrelx(code: []u8) !void { |
| 1307 | 1266 | const old_inst = disassemble(code) orelse return error.RelaxFail; |
| 1308 | 1267 | switch (old_inst.encoding.mnemonic) { |
| 1309 | 1268 | .mov => { |
| ... | ... | @@ -1315,7 +1274,7 @@ const x86_64 = struct { |
| 1315 | 1274 | } |
| 1316 | 1275 | } |
| 1317 | 1276 | |
| 1318 | | pub fn relaxTlsGdToIe( |
| 1277 | fn relaxTlsGdToIe( |
| 1319 | 1278 | self: Atom, |
| 1320 | 1279 | rels: []align(1) const elf.Elf64_Rela, |
| 1321 | 1280 | value: i32, |
| ... | ... | @@ -1340,8 +1299,8 @@ const x86_64 = struct { |
| 1340 | 1299 | else => { |
| 1341 | 1300 | var err = try elf_file.addErrorWithNotes(1); |
| 1342 | 1301 | try err.addMsg(elf_file, "fatal linker error: rewrite {} when followed by {}", .{ |
| 1343 | | fmtRelocType(rels[0].r_type()), |
| 1344 | | fmtRelocType(rels[1].r_type()), |
| 1302 | relocation.fmtRelocType(rels[0].r_type(), .x86_64), |
| 1303 | relocation.fmtRelocType(rels[1].r_type(), .x86_64), |
| 1345 | 1304 | }); |
| 1346 | 1305 | try err.addNote(elf_file, "in {}:{s} at offset 0x{x}", .{ |
| 1347 | 1306 | self.file(elf_file).?.fmtPath(), |
| ... | ... | @@ -1352,7 +1311,7 @@ const x86_64 = struct { |
| 1352 | 1311 | } |
| 1353 | 1312 | } |
| 1354 | 1313 | |
| 1355 | | pub fn relaxTlsLdToLe( |
| 1314 | fn relaxTlsLdToLe( |
| 1356 | 1315 | self: Atom, |
| 1357 | 1316 | rels: []align(1) const elf.Elf64_Rela, |
| 1358 | 1317 | value: i32, |
| ... | ... | @@ -1392,8 +1351,8 @@ const x86_64 = struct { |
| 1392 | 1351 | else => { |
| 1393 | 1352 | var err = try elf_file.addErrorWithNotes(1); |
| 1394 | 1353 | try err.addMsg(elf_file, "fatal linker error: rewrite {} when followed by {}", .{ |
| 1395 | | fmtRelocType(rels[0].r_type()), |
| 1396 | | fmtRelocType(rels[1].r_type()), |
| 1354 | relocation.fmtRelocType(rels[0].r_type(), .x86_64), |
| 1355 | relocation.fmtRelocType(rels[1].r_type(), .x86_64), |
| 1397 | 1356 | }); |
| 1398 | 1357 | try err.addNote(elf_file, "in {}:{s} at offset 0x{x}", .{ |
| 1399 | 1358 | self.file(elf_file).?.fmtPath(), |
| ... | ... | @@ -1404,7 +1363,7 @@ const x86_64 = struct { |
| 1404 | 1363 | } |
| 1405 | 1364 | } |
| 1406 | 1365 | |
| 1407 | | pub fn canRelaxGotTpOff(code: []const u8) bool { |
| 1366 | fn canRelaxGotTpOff(code: []const u8) bool { |
| 1408 | 1367 | const old_inst = disassemble(code) orelse return false; |
| 1409 | 1368 | switch (old_inst.encoding.mnemonic) { |
| 1410 | 1369 | .mov => if (Instruction.new(old_inst.prefix, .mov, &.{ |
| ... | ... | @@ -1419,7 +1378,7 @@ const x86_64 = struct { |
| 1419 | 1378 | } |
| 1420 | 1379 | } |
| 1421 | 1380 | |
| 1422 | | pub fn relaxGotTpOff(code: []u8) !void { |
| 1381 | fn relaxGotTpOff(code: []u8) !void { |
| 1423 | 1382 | const old_inst = disassemble(code) orelse return error.RelaxFail; |
| 1424 | 1383 | switch (old_inst.encoding.mnemonic) { |
| 1425 | 1384 | .mov => { |
| ... | ... | @@ -1435,7 +1394,7 @@ const x86_64 = struct { |
| 1435 | 1394 | } |
| 1436 | 1395 | } |
| 1437 | 1396 | |
| 1438 | | pub fn relaxGotPcTlsDesc(code: []u8) !void { |
| 1397 | fn relaxGotPcTlsDesc(code: []u8) !void { |
| 1439 | 1398 | const old_inst = disassemble(code) orelse return error.RelaxFail; |
| 1440 | 1399 | switch (old_inst.encoding.mnemonic) { |
| 1441 | 1400 | .lea => { |
| ... | ... | @@ -1451,7 +1410,7 @@ const x86_64 = struct { |
| 1451 | 1410 | } |
| 1452 | 1411 | } |
| 1453 | 1412 | |
| 1454 | | pub fn relaxTlsGdToLe( |
| 1413 | fn relaxTlsGdToLe( |
| 1455 | 1414 | self: Atom, |
| 1456 | 1415 | rels: []align(1) const elf.Elf64_Rela, |
| 1457 | 1416 | value: i32, |
| ... | ... | @@ -1474,16 +1433,16 @@ const x86_64 = struct { |
| 1474 | 1433 | try stream.seekBy(-4); |
| 1475 | 1434 | try writer.writeAll(&insts); |
| 1476 | 1435 | relocs_log.debug(" relaxing {} and {}", .{ |
| 1477 | | fmtRelocType(rels[0].r_type()), |
| 1478 | | fmtRelocType(rels[1].r_type()), |
| 1436 | relocation.fmtRelocType(rels[0].r_type(), .x86_64), |
| 1437 | relocation.fmtRelocType(rels[1].r_type(), .x86_64), |
| 1479 | 1438 | }); |
| 1480 | 1439 | }, |
| 1481 | 1440 | |
| 1482 | 1441 | else => { |
| 1483 | 1442 | var err = try elf_file.addErrorWithNotes(1); |
| 1484 | 1443 | try err.addMsg(elf_file, "fatal linker error: rewrite {} when followed by {}", .{ |
| 1485 | | fmtRelocType(rels[0].r_type()), |
| 1486 | | fmtRelocType(rels[1].r_type()), |
| 1444 | relocation.fmtRelocType(rels[0].r_type(), .x86_64), |
| 1445 | relocation.fmtRelocType(rels[1].r_type(), .x86_64), |
| 1487 | 1446 | }); |
| 1488 | 1447 | try err.addNote(elf_file, "in {}:{s} at offset 0x{x}", .{ |
| 1489 | 1448 | self.file(elf_file).?.fmtPath(), |
| ... | ... | @@ -1521,6 +1480,7 @@ const elf = std.elf; |
| 1521 | 1480 | const eh_frame = @import("eh_frame.zig"); |
| 1522 | 1481 | const log = std.log.scoped(.link); |
| 1523 | 1482 | const relocs_log = std.log.scoped(.link_relocs); |
| 1483 | const relocation = @import("relocation.zig"); |
| 1524 | 1484 | |
| 1525 | 1485 | const Allocator = std.mem.Allocator; |
| 1526 | 1486 | const Atom = @This(); |