| ... | @@ -484,6 +484,12 @@ const Section = struct { | ... | @@ -484,6 +484,12 @@ const Section = struct { |
| 484 | }; | 484 | }; |
| 485 | } | 485 | } |
| 486 | | 486 | |
| | 487 | fn flags(s: Index, elf: *Elf) std.elf.SHF { |
| | 488 | return switch (elf.shdrPtr(s)) { |
| | 489 | inline else => |shdr| elf.targetLoad(&shdr.flags).shf, |
| | 490 | }; |
| | 491 | } |
| | 492 | |
| 487 | fn rename(shndx: Index, elf: *Elf, new_name: []const u8) Error!void { | 493 | fn rename(shndx: Index, elf: *Elf, new_name: []const u8) Error!void { |
| 488 | const shstrtab_entry = try elf.string(.shstrtab, new_name); | 494 | const shstrtab_entry = try elf.string(.shstrtab, new_name); |
| 489 | switch (elf.shdrPtr(shndx)) { | 495 | switch (elf.shdrPtr(shndx)) { |
| ... | @@ -491,8 +497,8 @@ const Section = struct { | ... | @@ -491,8 +497,8 @@ const Section = struct { |
| 491 | } | 497 | } |
| 492 | } | 498 | } |
| 493 | | 499 | |
| 494 | /// Asserts that `shndx` is a `SHT_RELA` section and ensures that its node has enough unused | 500 | /// Asserts that `rela_shndx` is a `SHT_RELA` section and ensures that its node has enough |
| 495 | /// space to hold `n` additional `ElfN.Rela` entries. | 501 | /// unused space to hold `n` additional `ElfN.Rela` entries. |
| 496 | fn relaEnsureAdditionalCapacity(rela_shndx: Index, elf: *Elf, n: usize) Error!void { | 502 | fn relaEnsureAdditionalCapacity(rela_shndx: Index, elf: *Elf, n: usize) Error!void { |
| 497 | const node = rela_shndx.get(elf).ni; | 503 | const node = rela_shndx.get(elf).ni; |
| 498 | const need_size: u64 = switch (elf.shdrPtr(rela_shndx)) { | 504 | const need_size: u64 = switch (elf.shdrPtr(rela_shndx)) { |
| ... | @@ -518,9 +524,9 @@ const Section = struct { | ... | @@ -518,9 +524,9 @@ const Section = struct { |
| 518 | try elf.ensureNodeSize(node, need_size); | 524 | try elf.ensureNodeSize(node, need_size); |
| 519 | } | 525 | } |
| 520 | | 526 | |
| 521 | /// Asserts that `shndx` is a `SHT_RELA` section and deletes the `ElfN.Rela` entry at the | 527 | /// Asserts that `rela_shndx` is a `SHT_RELA` section and deletes the `ElfN.Rela` entry at |
| 522 | /// given `index` in it. The entry is added to the free-list for reuse later. Asserts that | 528 | /// the given `index` in it. The entry is added to the free-list for reuse later. Asserts |
| 523 | /// the relocation entry at `index` is not already free. | 529 | /// that the relocation entry at `index` is not already free. |
| 524 | fn relaDeleteOne(rela_shndx: Index, elf: *Elf, index: RelaIndex) void { | 530 | fn relaDeleteOne(rela_shndx: Index, elf: *Elf, index: RelaIndex) void { |
| 525 | switch (elf.shdrPtr(rela_shndx)) { | 531 | switch (elf.shdrPtr(rela_shndx)) { |
| 526 | inline else => |shdr, class| { | 532 | inline else => |shdr, class| { |
| ... | @@ -557,9 +563,9 @@ const Section = struct { | ... | @@ -557,9 +563,9 @@ const Section = struct { |
| 557 | rela_shndx.get(elf).rela.free_head = index.toOptional(); | 563 | rela_shndx.get(elf).rela.free_head = index.toOptional(); |
| 558 | } | 564 | } |
| 559 | | 565 | |
| 560 | /// Asserts that `shndx` is a `SHT_RELA` section and adds a new `ElfN.Rela` entry to it with | 566 | /// Asserts that `rela_shndx` is a `SHT_RELA` section and adds a new `ElfN.Rela` entry to it |
| 561 | /// the given field values. Returns the index of the populated entry. Asserts that capacity | 567 | /// with the given field values. Returns the index of the populated entry. Asserts that |
| 562 | /// for this operation was already guaranteed using `relaEnsureAdditionalCapacity`. | 568 | /// capacity for this operation was already guaranteed using `relaEnsureAdditionalCapacity`. |
| 563 | fn relaAddOneAssumeCapacity(rela_shndx: Index, elf: *Elf, opts: struct { | 569 | fn relaAddOneAssumeCapacity(rela_shndx: Index, elf: *Elf, opts: struct { |
| 564 | type: MachineRelocType, | 570 | type: MachineRelocType, |
| 565 | offset: u64, | 571 | offset: u64, |
| ... | @@ -621,8 +627,8 @@ const Section = struct { | ... | @@ -621,8 +627,8 @@ const Section = struct { |
| 621 | } | 627 | } |
| 622 | } | 628 | } |
| 623 | | 629 | |
| 624 | /// Asserts that `shndx` is a `SHT_RELA` section and updates the `info.sym` field of the | 630 | /// Asserts that `rela_shndx` is a `SHT_RELA` section and updates the `info.sym` field of |
| 625 | /// `ElfN.Rela` entry at the given index. As with `relaAddOneAssumeCapacity`, the symbol | 631 | /// the `ElfN.Rela` entry at the given index. As with `relaAddOneAssumeCapacity`, the symbol |
| 626 | /// index is a raw `u32`, because it may be an index into `.symtab` or an index into | 632 | /// index is a raw `u32`, because it may be an index into `.symtab` or an index into |
| 627 | /// `.dynsym`. Asserts that `index` is not in the free-list (i.e. is not deleted). | 633 | /// `.dynsym`. Asserts that `index` is not in the free-list (i.e. is not deleted). |
| 628 | fn relaUpdateSym(rela_shndx: Index, elf: *Elf, index: RelaIndex, raw_sym_index: u32) void { | 634 | fn relaUpdateSym(rela_shndx: Index, elf: *Elf, index: RelaIndex, raw_sym_index: u32) void { |
| ... | @@ -646,7 +652,7 @@ const Section = struct { | ... | @@ -646,7 +652,7 @@ const Section = struct { |
| 646 | } | 652 | } |
| 647 | } | 653 | } |
| 648 | | 654 | |
| 649 | /// Asserts that `shndx` is a `SHT_RELA` section and updates the `offset` field of the | 655 | /// Asserts that `rela_shndx` is a `SHT_RELA` section and updates the `offset` field of the |
| 650 | /// `ElfN.Rela` entry at the given index. Asserts that `index` is not in the free-list (i.e. | 656 | /// `ElfN.Rela` entry at the given index. Asserts that `index` is not in the free-list (i.e. |
| 651 | /// it is not deleted). | 657 | /// it is not deleted). |
| 652 | fn relaSetOffset(rela_shndx: Index, elf: *Elf, index: RelaIndex, new_offset: u64) void { | 658 | fn relaSetOffset(rela_shndx: Index, elf: *Elf, index: RelaIndex, new_offset: u64) void { |
| ... | @@ -667,7 +673,7 @@ const Section = struct { | ... | @@ -667,7 +673,7 @@ const Section = struct { |
| 667 | } | 673 | } |
| 668 | } | 674 | } |
| 669 | | 675 | |
| 670 | /// Asserts that `shndx` is a `SHT_RELA` section and updates the `offset` field of the | 676 | /// Asserts that `rela_shndx` is a `SHT_RELA` section and updates the `offset` field of the |
| 671 | /// `ElfN.Rela` entry at the given index, by subtracting `old_base` and adding `new_base`. | 677 | /// `ElfN.Rela` entry at the given index, by subtracting `old_base` and adding `new_base`. |
| 672 | /// Asserts that `index` is not in the free-list (i.e. it is not deleted). | 678 | /// Asserts that `index` is not in the free-list (i.e. it is not deleted). |
| 673 | fn relaAdjustOffset(rela_shndx: Index, elf: *Elf, index: RelaIndex, old_base: u64, new_base: u64) void { | 679 | fn relaAdjustOffset(rela_shndx: Index, elf: *Elf, index: RelaIndex, old_base: u64, new_base: u64) void { |
| ... | @@ -690,6 +696,28 @@ const Section = struct { | ... | @@ -690,6 +696,28 @@ const Section = struct { |
| 690 | }, | 696 | }, |
| 691 | } | 697 | } |
| 692 | } | 698 | } |
| | 699 | |
| | 700 | /// Asserts that `rela_shndx` is a `SHT_RELA` section, and asserts that `index` refers to an |
| | 701 | /// `R_*_RELATIVE` relocation inside of it; then, updates that relocation's addend (which is |
| | 702 | /// an address in this DSO without the runtime load offset applied) to the given value. |
| | 703 | fn relaSetRelativeOffset(rela_shndx: Index, elf: *Elf, index: RelaIndex, new_addend: u64) void { |
| | 704 | switch (elf.shdrPtr(rela_shndx)) { |
| | 705 | inline else => |shdr, class| { |
| | 706 | assert(elf.targetLoad(&shdr.type) == .RELA); |
| | 707 | assert(elf.targetLoad(&shdr.entsize) == @sizeOf(class.ElfN().Rela)); |
| | 708 | const relas: []class.ElfN().Rela = @ptrCast(@alignCast( |
| | 709 | rela_shndx.get(elf).ni.slice(&elf.mf)[0..@intCast(elf.targetLoad(&shdr.size))], |
| | 710 | )); |
| | 711 | { |
| | 712 | const rela_info = elf.targetLoad(&relas[@intFromEnum(index)].info); |
| | 713 | const none_reloc_type = MachineRelocType.none(elf).unwrap(elf); |
| | 714 | assert(rela_info.type != none_reloc_type); // bug: `index` is in the free-list |
| | 715 | } |
| | 716 | const unsigned: class.ElfN().Addr = @intCast(new_addend); |
| | 717 | elf.targetStore(&relas[@intFromEnum(index)].addend, @bitCast(unsigned)); |
| | 718 | }, |
| | 719 | } |
| | 720 | } |
| 693 | }; | 721 | }; |
| 694 | }; | 722 | }; |
| 695 | | 723 | |
| ... | @@ -895,6 +923,16 @@ pub const MachineRelocType = union { | ... | @@ -895,6 +923,16 @@ pub const MachineRelocType = union { |
| 895 | .X86_64 => .{ .X86_64 = .COPY }, | 923 | .X86_64 => .{ .X86_64 = .COPY }, |
| 896 | }; | 924 | }; |
| 897 | } | 925 | } |
| | 926 | pub fn relative(elf: *Elf) MachineRelocType { |
| | 927 | return switch (elf.ehdrField(.machine)) { |
| | 928 | else => unreachable, |
| | 929 | .AARCH64 => .{ .AARCH64 = .RELATIVE }, |
| | 930 | .LOONGARCH => .{ .LOONGARCH = .RELATIVE }, |
| | 931 | .PPC64 => .{ .PPC64 = .RELATIVE }, |
| | 932 | .RISCV => .{ .RISCV = .RELATIVE }, |
| | 933 | .X86_64 => .{ .X86_64 = .RELATIVE }, |
| | 934 | }; |
| | 935 | } |
| 898 | pub fn jumpSlot(elf: *Elf) MachineRelocType { | 936 | pub fn jumpSlot(elf: *Elf) MachineRelocType { |
| 899 | return switch (elf.ehdrField(.machine)) { | 937 | return switch (elf.ehdrField(.machine)) { |
| 900 | else => unreachable, | 938 | else => unreachable, |
| ... | @@ -1026,6 +1064,15 @@ const SymbolReloc = struct { | ... | @@ -1026,6 +1064,15 @@ const SymbolReloc = struct { |
| 1026 | /// do not apply any relocations ourselves). Otherwise, no symbol relocs use this type. | 1064 | /// do not apply any relocations ourselves). Otherwise, no symbol relocs use this type. |
| 1027 | write_rela, | 1065 | write_rela, |
| 1028 | | 1066 | |
| | 1067 | /// Address relative to the DSO base. Like `.abs64` but does not emit `R_*_RELATIVE` relocs. |
| | 1068 | /// |
| | 1069 | /// This is only used targeting local symbols so can always be statically resolved. |
| | 1070 | dsorel64, |
| | 1071 | /// Address relative to the DSO base. Like `.abs32` but does not emit `R_*_RELATIVE` relocs. |
| | 1072 | /// |
| | 1073 | /// This is only used targeting local symbols so can always be statically resolved. |
| | 1074 | dsorel32, |
| | 1075 | |
| 1029 | abs64, | 1076 | abs64, |
| 1030 | abs32, | 1077 | abs32, |
| 1031 | abs32s, | 1078 | abs32s, |
| ... | @@ -1060,21 +1107,39 @@ const SymbolReloc = struct { | ... | @@ -1060,21 +1107,39 @@ const SymbolReloc = struct { |
| 1060 | else => false, | 1107 | else => false, |
| 1061 | }; | 1108 | }; |
| 1062 | } | 1109 | } |
| | 1110 | |
| | 1111 | fn isAbsAddr(t: SymbolReloc.Type, elf: *const Elf) bool { |
| | 1112 | return switch (elf.identClass()) { |
| | 1113 | .NONE, _ => unreachable, |
| | 1114 | .@"32" => t == .abs32, |
| | 1115 | .@"64" => t == .abs64, |
| | 1116 | }; |
| | 1117 | } |
| 1063 | }; | 1118 | }; |
| 1064 | | 1119 | |
| 1065 | fn apply(reloc: *const SymbolReloc, elf: *Elf) void { | 1120 | fn apply(reloc: *const SymbolReloc, elf: *Elf) void { |
| 1066 | assert(elf.ehdrField(.type) != .REL); | 1121 | assert(elf.ehdrField(.type) != .REL); |
| 1067 | assert(reloc.node != .none); | 1122 | assert(reloc.node != .none); |
| | 1123 | |
| 1068 | if (reloc.node.hasMoved(&elf.mf) or reloc.target.hasMoved(elf)) { | 1124 | if (reloc.node.hasMoved(&elf.mf) or reloc.target.hasMoved(elf)) { |
| 1069 | // There's no point applying the relocation now, because it will be re-applied by | 1125 | // There's no point applying the relocation now, because it will be re-applied by |
| 1070 | // `flushMoved` at some point anyway. | 1126 | // `flushMoved` at some point anyway. |
| 1071 | return; | 1127 | return; |
| 1072 | } | 1128 | } |
| 1073 | if (reloc.rela_index != .none) { | 1129 | |
| 1074 | // This relocation has been lowered to a runtime relocation. Until that changes, it is | 1130 | if (reloc.rela_index.unwrap()) |rela_index| switch (elf.classifySymbolValue(reloc.target)) { |
| 1075 | // not our job to apply it. | 1131 | .static => unreachable, |
| 1076 | return; | 1132 | .dynamic => return, // the relocation happens at runtime |
| 1077 | } | 1133 | .static_relative => { |
| | 1134 | assert(reloc.type.isAbsAddr(elf)); |
| | 1135 | // We have emitted an R_*_RELATIVE relocation to help lower an abs32/abs64 reloc. |
| | 1136 | // This is a simplified version of the general relocation handling logic, where we |
| | 1137 | // know we're using '.abs64' or '.abs32' (matching the ELF ident class). |
| | 1138 | const value = reloc.target.value(elf) +% @as(u64, @bitCast(reloc.addend)); |
| | 1139 | elf.shndx.rela_dyn.relaSetRelativeOffset(elf, rela_index, value); |
| | 1140 | return; |
| | 1141 | }, |
| | 1142 | }; |
| 1078 | const node_vaddr: u64 = switch (elf.getNode(reloc.node)) { | 1143 | const node_vaddr: u64 = switch (elf.getNode(reloc.node)) { |
| 1079 | .file => unreachable, | 1144 | .file => unreachable, |
| 1080 | .ehdr => unreachable, | 1145 | .ehdr => unreachable, |
| ... | @@ -1099,13 +1164,13 @@ const SymbolReloc = struct { | ... | @@ -1099,13 +1164,13 @@ const SymbolReloc = struct { |
| 1099 | const target_value = sym_value +% @as(u64, @bitCast(reloc.addend)); | 1164 | const target_value = sym_value +% @as(u64, @bitCast(reloc.addend)); |
| 1100 | type: switch (reloc.type) { | 1165 | type: switch (reloc.type) { |
| 1101 | .write_rela => unreachable, | 1166 | .write_rela => unreachable, |
| 1102 | .abs64 => std.mem.writeInt( | 1167 | .abs64, .dsorel64 => std.mem.writeInt( |
| 1103 | u64, | 1168 | u64, |
| 1104 | dest_slice[0..8], | 1169 | dest_slice[0..8], |
| 1105 | target_value, | 1170 | target_value, |
| 1106 | target_endian, | 1171 | target_endian, |
| 1107 | ), | 1172 | ), |
| 1108 | .abs32 => std.mem.writeInt( | 1173 | .abs32, .dsorel32 => std.mem.writeInt( |
| 1109 | u32, | 1174 | u32, |
| 1110 | dest_slice[0..4], | 1175 | dest_slice[0..4], |
| 1111 | @intCast(target_value), | 1176 | @intCast(target_value), |
| ... | @@ -1730,7 +1795,9 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{ | ... | @@ -1730,7 +1795,9 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{ |
| 1730 | } | 1795 | } |
| 1731 | | 1796 | |
| 1732 | switch (@"type") { | 1797 | switch (@"type") { |
| 1733 | .FUNC, .GNU_IFUNC => if (!elf.haveCanonicalSymbolDefinition(.global(opts.name.strtab))) { | 1798 | .FUNC, .GNU_IFUNC => if (elf.ehdrField(.type) != .REL and |
| | 1799 | elf.classifySymbolValue(.global(opts.name.strtab)) == .dynamic) |
| | 1800 | { |
| 1734 | // This STT_FUNC symbol might be defined externally, so it needs a PLT entry. | 1801 | // This STT_FUNC symbol might be defined externally, so it needs a PLT entry. |
| 1735 | elf.addPltEntry(opts.name.strtab, new_global_ptr.dynsym_index); | 1802 | elf.addPltEntry(opts.name.strtab, new_global_ptr.dynsym_index); |
| 1736 | }, | 1803 | }, |
| ... | @@ -1847,7 +1914,9 @@ fn setGlobalSymbolValue( | ... | @@ -1847,7 +1914,9 @@ fn setGlobalSymbolValue( |
| 1847 | // delete its newly-unnecessary runtime relocation to avoid a runtime dynamic linker error. | 1914 | // delete its newly-unnecessary runtime relocation to avoid a runtime dynamic linker error. |
| 1848 | // This also allows the PLT entry to be reused---see `pltEntryIsDead`. | 1915 | // This also allows the PLT entry to be reused---see `pltEntryIsDead`. |
| 1849 | if (elf.plt.getIndex(global_name)) |plt_index| { | 1916 | if (elf.plt.getIndex(global_name)) |plt_index| { |
| 1850 | if (elf.haveCanonicalSymbolDefinition(.global(global_name)) and !elf.pltEntryIsDead(plt_index)) { | 1917 | if (!elf.pltEntryIsDead(plt_index) and |
| | 1918 | elf.classifySymbolValue(.global(global_name)) != .dynamic) |
| | 1919 | { |
| 1851 | elf.shndx.rela_plt.relaDeleteOne(elf, @enumFromInt(plt_index)); | 1920 | elf.shndx.rela_plt.relaDeleteOne(elf, @enumFromInt(plt_index)); |
| 1852 | assert(elf.pltEntryIsDead(plt_index)); | 1921 | assert(elf.pltEntryIsDead(plt_index)); |
| 1853 | } | 1922 | } |
| ... | @@ -2323,8 +2392,8 @@ const Symbol = struct { | ... | @@ -2323,8 +2392,8 @@ const Symbol = struct { |
| 2323 | } | 2392 | } |
| 2324 | } | 2393 | } |
| 2325 | | 2394 | |
| 2326 | /// Scans through all relocations targeting `sym_id` and deletes each one's dynamic | 2395 | /// Scans through all relocations targeting `sym_id` and, for each one with a dynamic |
| 2327 | /// relocation entry, if it has one. | 2396 | /// relocation entry, either deletes it or converts it to R_*_RELATIVE as required. |
| 2328 | /// | 2397 | /// |
| 2329 | /// Asserts we are creating a DSO. | 2398 | /// Asserts we are creating a DSO. |
| 2330 | fn deleteDynamicTargetRelocs(sym_id: Symbol.Id, elf: *Elf) void { | 2399 | fn deleteDynamicTargetRelocs(sym_id: Symbol.Id, elf: *Elf) void { |
| ... | @@ -2337,6 +2406,45 @@ const Symbol = struct { | ... | @@ -2337,6 +2406,45 @@ const Symbol = struct { |
| 2337 | reloc.deleteOutputRel(elf); | 2406 | reloc.deleteOutputRel(elf); |
| 2338 | ri = reloc.next; | 2407 | ri = reloc.next; |
| 2339 | } | 2408 | } |
| | 2409 | switch (elf.classifySymbolValue(sym_id)) { |
| | 2410 | .static => return, |
| | 2411 | .static_relative => {}, |
| | 2412 | .dynamic => unreachable, |
| | 2413 | } |
| | 2414 | // We removed the symbol relocations, now add R_*_RELATIVE relocations where needed. |
| | 2415 | ri = sym_id.index(elf).ptr(elf).first_target_reloc; |
| | 2416 | while (ri != .none) { |
| | 2417 | const reloc = ri.get(elf); |
| | 2418 | ri = reloc.next; |
| | 2419 | assert(reloc.target == sym_id); |
| | 2420 | if (!reloc.type.isAbsAddr(elf)) continue; |
| | 2421 | switch (elf.nodeWantsDsoRelocation(reloc.node)) { |
| | 2422 | .no => continue, |
| | 2423 | .yes_textrel => elf.textrel_count += 1, |
| | 2424 | .yes => {}, |
| | 2425 | } |
| | 2426 | const node_vaddr: u64 = switch (elf.getNode(reloc.node)) { |
| | 2427 | .file => unreachable, |
| | 2428 | .ehdr => unreachable, |
| | 2429 | .shdr => unreachable, |
| | 2430 | .segment => unreachable, |
| | 2431 | .copied_global => unreachable, |
| | 2432 | .section => |shndx| shndx.vaddr(elf), |
| | 2433 | .input_section => |isi| isi.ptrConst(elf).vaddr, |
| | 2434 | inline .nav, |
| | 2435 | .uav, |
| | 2436 | .lazy_code, |
| | 2437 | .lazy_const_data, |
| | 2438 | => |i| Symbol.Id.local(i.symbol(elf)).value(elf), |
| | 2439 | }; |
| | 2440 | // There is capacity for a relocation because we just deleted one earlier. |
| | 2441 | reloc.rela_index = elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{ |
| | 2442 | .type = .relative(elf), |
| | 2443 | .offset = node_vaddr + reloc.offset, |
| | 2444 | .raw_sym_index = 0, |
| | 2445 | .addend = 0, |
| | 2446 | }).toOptional(); |
| | 2447 | } |
| 2340 | } | 2448 | } |
| 2341 | | 2449 | |
| 2342 | /// Returns `true` if the target of `s` has moved, meaning the symbol's value will change at | 2450 | /// Returns `true` if the target of `s` has moved, meaning the symbol's value will change at |
| ... | @@ -2365,29 +2473,75 @@ fn globalByName(elf: *const Elf, name: String(.strtab)) ?*Symbol.Global { | ... | @@ -2365,29 +2473,75 @@ fn globalByName(elf: *const Elf, name: String(.strtab)) ?*Symbol.Global { |
| 2365 | return null; | 2473 | return null; |
| 2366 | } | 2474 | } |
| 2367 | | 2475 | |
| 2368 | fn haveCanonicalSymbolDefinition(elf: *Elf, sym: Symbol.Id) bool { | 2476 | fn classifySymbolValue(elf: *Elf, sym: Symbol.Id) enum { |
| 2369 | const global_name = switch (sym.unwrap()) { | 2477 | /// This symbol's value is guaranteed to equal `sym.value(elf)`. |
| 2370 | .local => return true, | 2478 | static, |
| 2371 | .global => |name| name, | 2479 | /// This symbol's value is an offset of `sym.value(elf)` from the runtime-known load address of |
| | 2480 | /// this DSO (which is position-independent). |
| | 2481 | static_relative, |
| | 2482 | /// This symbol's definition does not necessarily come from this DSO, so is not known until RTLD |
| | 2483 | /// runs. Therefore, a dynamic (runtime) relocation is necessary. |
| | 2484 | dynamic, |
| | 2485 | } { |
| | 2486 | const comp = elf.base.comp; |
| | 2487 | |
| | 2488 | const runtime_load_addr = switch (elf.ehdrField(.type)) { |
| | 2489 | .NONE, .CORE, _ => unreachable, |
| | 2490 | .REL => unreachable, |
| | 2491 | .DYN => true, |
| | 2492 | .EXEC => false, |
| 2372 | }; | 2493 | }; |
| 2373 | | 2494 | |
| 2374 | if (elf.shndx.dynamic == .UNDEF) return true; | 2495 | if (elf.shndx.dynamic == .UNDEF) { |
| | 2496 | // This is a static non-PIE executable---every symbol has a statically known value. |
| | 2497 | return .static; |
| | 2498 | } |
| 2375 | | 2499 | |
| 2376 | const global_ptr = elf.globals.strong_def.getPtr(global_name) orelse | 2500 | const shndx: Section.Index, const visibility: std.elf.STV = switch (elf.symPtr(sym.index(elf))) { |
| 2377 | elf.globals.weak_def.getPtr(global_name) orelse | 2501 | inline else => |sym_ptr| .{ |
| 2378 | return false; // no definition at all | 2502 | .fromSection(elf.targetLoad(&sym_ptr.shndx)), |
| | 2503 | elf.targetLoad(&sym_ptr.other).visibility, |
| | 2504 | }, |
| | 2505 | }; |
| 2379 | | 2506 | |
| 2380 | if (elf.base.comp.config.output_mode == .Exe) { | 2507 | switch (sym.unwrap()) { |
| 2381 | // Symbols defined in executables cannot be preempted | 2508 | .local => { |
| 2382 | return true; | 2509 | assert(shndx != .UNDEF); |
| | 2510 | assert(visibility == .DEFAULT); |
| | 2511 | }, |
| | 2512 | .global => |name| if (visibility == .DEFAULT and comp.config.output_mode != .Exe) { |
| | 2513 | // An unprotected symbol in a DSO which is not an executable is subject to runtime |
| | 2514 | // preemption, so a dynamic relocation is required for it even if we have a definition. |
| | 2515 | return .dynamic; |
| | 2516 | } else if (elf.copied_globals.contains(name)) { |
| | 2517 | // This becomes a locally-defined symbol in `.data`. |
| | 2518 | return if (runtime_load_addr) .static_relative else .static; |
| | 2519 | }, |
| 2383 | } | 2520 | } |
| 2384 | | 2521 | |
| 2385 | const visibility: std.elf.STV = switch (elf.symPtr(global_ptr.symtab_index)) { | 2522 | return switch (shndx) { |
| 2386 | inline else => |sym_ptr| elf.targetLoad(&sym_ptr.other).visibility, | 2523 | .UNDEF => switch (visibility) { |
| 2387 | }; | 2524 | .DEFAULT => if (comp.config.link_mode == .static and comp.config.output_mode == .Exe) { |
| 2388 | return switch (visibility) { | 2525 | assert(comp.config.pie); // non-PIE static exe should not have a `.dynamic` section |
| 2389 | .INTERNAL, .HIDDEN, .PROTECTED => true, // protection prevents preemption | 2526 | // This is a static PIE---the only dynamic relocations are `R_*_RELATIVE`. |
| 2390 | .DEFAULT => false, | 2527 | return .static; |
| | 2528 | } else .dynamic, // external symbol |
| | 2529 | |
| | 2530 | // If the symbol *cannot* be external, then there's no point making a dynamic relocation |
| | 2531 | // now---if linking succeeds we won't need anything more than perhaps an `R_*_RELATIVE`. |
| | 2532 | .INTERNAL, .HIDDEN, .PROTECTED => .static, |
| | 2533 | }, |
| | 2534 | |
| | 2535 | .ABS => .static, |
| | 2536 | |
| | 2537 | else => if (runtime_load_addr and |
| | 2538 | shndx.flags(elf).ALLOC and |
| | 2539 | !shndx.flags(elf).TLS) |
| | 2540 | { |
| | 2541 | return .static_relative; |
| | 2542 | } else { |
| | 2543 | return .static; |
| | 2544 | }, |
| 2391 | }; | 2545 | }; |
| 2392 | } | 2546 | } |
| 2393 | | 2547 | |
| ... | @@ -3532,19 +3686,19 @@ fn initHeaders( | ... | @@ -3532,19 +3686,19 @@ fn initHeaders( |
| 3532 | }); | 3686 | }); |
| 3533 | elf.plt_first_symbol_reloc = @enumFromInt(elf.symbol_relocs.items.len); | 3687 | elf.plt_first_symbol_reloc = @enumFromInt(elf.symbol_relocs.items.len); |
| 3534 | try elf.ensureUnusedRelocCapacity(plt_ni, 2); | 3688 | try elf.ensureUnusedRelocCapacity(plt_ni, 2); |
| 3535 | try elf.addRelocAssumeCapacity( | 3689 | try elf.addSymbolRelocAssumeCapacity( |
| 3536 | plt_ni, | 3690 | plt_ni, |
| 3537 | 2, | 3691 | 2, |
| 3538 | got_plt_sym, | 3692 | got_plt_sym, |
| 3539 | 8 * 1 - 4, | 3693 | 8 * 1 - 4, |
| 3540 | .{ .X86_64 = .PC32 }, | 3694 | .rel32, |
| 3541 | ); | 3695 | ); |
| 3542 | try elf.addRelocAssumeCapacity( | 3696 | try elf.addSymbolRelocAssumeCapacity( |
| 3543 | plt_ni, | 3697 | plt_ni, |
| 3544 | 8, | 3698 | 8, |
| 3545 | got_plt_sym, | 3699 | got_plt_sym, |
| 3546 | 8 * 2 - 4, | 3700 | 8 * 2 - 4, |
| 3547 | .{ .X86_64 = .PC32 }, | 3701 | .rel32, |
| 3548 | ); | 3702 | ); |
| 3549 | }, | 3703 | }, |
| 3550 | .LOONGARCH => { | 3704 | .LOONGARCH => { |
| ... | @@ -3575,9 +3729,9 @@ fn initHeaders( | ... | @@ -3575,9 +3729,9 @@ fn initHeaders( |
| 3575 | }); | 3729 | }); |
| 3576 | elf.plt_first_symbol_reloc = @enumFromInt(elf.symbol_relocs.items.len); | 3730 | elf.plt_first_symbol_reloc = @enumFromInt(elf.symbol_relocs.items.len); |
| 3577 | try elf.ensureUnusedRelocCapacity(plt_ni, 3); | 3731 | try elf.ensureUnusedRelocCapacity(plt_ni, 3); |
| 3578 | try elf.addRelocAssumeCapacity(plt_ni, 0, got_plt_sym, 0, .{ .LOONGARCH = .PCALA_HI20 }); | 3732 | try elf.addSymbolRelocAssumeCapacity(plt_ni, 0, got_plt_sym, 0, .rel32_hi20); |
| 3579 | try elf.addRelocAssumeCapacity(plt_ni, 8, got_plt_sym, 0, .{ .LOONGARCH = .PCALA_LO12 }); | 3733 | try elf.addSymbolRelocAssumeCapacity(plt_ni, 8, got_plt_sym, 0, .abs32_lo12); |
| 3580 | try elf.addRelocAssumeCapacity(plt_ni, 16, got_plt_sym, 0, .{ .LOONGARCH = .PCALA_LO12 }); | 3734 | try elf.addSymbolRelocAssumeCapacity(plt_ni, 16, got_plt_sym, 0, .abs32_lo12); |
| 3581 | }, | 3735 | }, |
| 3582 | } | 3736 | } |
| 3583 | } | 3737 | } |
| ... | @@ -3895,13 +4049,11 @@ fn flushMovedNodeRelocs( | ... | @@ -3895,13 +4049,11 @@ fn flushMovedNodeRelocs( |
| 3895 | for (elf.symbol_relocs.items[@intFromEnum(first_symbol_reloc)..]) |*reloc| { | 4049 | for (elf.symbol_relocs.items[@intFromEnum(first_symbol_reloc)..]) |*reloc| { |
| 3896 | if (reloc.node != node) break; | 4050 | if (reloc.node != node) break; |
| 3897 | if (reloc.rela_index.unwrap()) |rela_index| { | 4051 | if (reloc.rela_index.unwrap()) |rela_index| { |
| 3898 | // Update the offsets of any `ElfN.Rela` entry we've emitted, since the node they're | 4052 | // The node has moved, so the offset of the relocation within the section might have |
| 3899 | // in has moved, so their offset within the section might also have moved. | 4053 | // changed, so update the `offset` field of the `ElfN.Rela` entry. |
| 3900 | reloc.relaSection(elf).relaSetOffset(elf, rela_index, node_vaddr + reloc.offset); | 4054 | reloc.relaSection(elf).relaSetOffset(elf, rela_index, node_vaddr + reloc.offset); |
| 3901 | } else { | | |
| 3902 | // We've applied this relocation ourselves! Just re-apply it now. | | |
| 3903 | reloc.apply(elf); | | |
| 3904 | } | 4055 | } |
| | 4056 | reloc.apply(elf); |
| 3905 | } | 4057 | } |
| 3906 | } | 4058 | } |
| 3907 | | 4059 | |
| ... | @@ -4223,7 +4375,7 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) Error!Node | ... | @@ -4223,7 +4375,7 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) Error!Node |
| 4223 | } else if (ip.isFunctionType(nav.resolved.?.type)) { | 4375 | } else if (ip.isFunctionType(nav.resolved.?.type)) { |
| 4224 | break :section .text; | 4376 | break :section .text; |
| 4225 | } else { | 4377 | } else { |
| 4226 | break :section .rodata; | 4378 | break :section .data_rel_ro; // TODO: it would be better to use `.rodata` if the NAV value doesn't have relocs |
| 4227 | } | 4379 | } |
| 4228 | }; | 4380 | }; |
| 4229 | const alignment: InternPool.Alignment = switch (Type.fromInterned(nav.resolved.?.type).zigTypeTag(zcu)) { | 4381 | const alignment: InternPool.Alignment = switch (Type.fromInterned(nav.resolved.?.type).zigTypeTag(zcu)) { |
| ... | @@ -4289,7 +4441,7 @@ fn uavMapIndex( | ... | @@ -4289,7 +4441,7 @@ fn uavMapIndex( |
| 4289 | const uav_gop = elf.uavs.getOrPutAssumeCapacity(uav_val); | 4441 | const uav_gop = elf.uavs.getOrPutAssumeCapacity(uav_val); |
| 4290 | const umi: Node.UavMapIndex = @enumFromInt(uav_gop.index); | 4442 | const umi: Node.UavMapIndex = @enumFromInt(uav_gop.index); |
| 4291 | if (!uav_gop.found_existing) { | 4443 | if (!uav_gop.found_existing) { |
| 4292 | const shndx: Section.Index = .data; | 4444 | const shndx: Section.Index = .data_rel_ro; // TODO: it would be better to use `.rodata` if the UAV value doesn't have relocs |
| 4293 | const node = try elf.mf.addLastChildNode(gpa, shndx.get(elf).ni, .{ | 4445 | const node = try elf.mf.addLastChildNode(gpa, shndx.get(elf).ni, .{ |
| 4294 | .moved = true, // see assert at end of `flushUav` | 4446 | .moved = true, // see assert at end of `flushUav` |
| 4295 | .alignment = resolved_align.toStdMem(), | 4447 | .alignment = resolved_align.toStdMem(), |
| ... | @@ -5293,6 +5445,10 @@ fn prelinkInner(elf: *Elf) Error!void { | ... | @@ -5293,6 +5445,10 @@ fn prelinkInner(elf: *Elf) Error!void { |
| 5293 | } | 5445 | } |
| 5294 | break :rpath try elf.string(.dynstr, buf.items); | 5446 | break :rpath try elf.string(.dynstr, buf.items); |
| 5295 | }; | 5447 | }; |
| | 5448 | // Static PIEs don't need a PLT, so we shouldn't emit the associated dynamic entries. |
| | 5449 | const use_plt = !(comp.config.output_mode == .Exe and |
| | 5450 | comp.config.link_mode == .static and |
| | 5451 | comp.config.pie); |
| 5296 | const soname: ?String(.dynstr) = if (elf.options.soname) |soname_slice| str: { | 5452 | const soname: ?String(.dynstr) = if (elf.options.soname) |soname_slice| str: { |
| 5297 | break :str try elf.string(.dynstr, soname_slice); | 5453 | break :str try elf.string(.dynstr, soname_slice); |
| 5298 | } else null; | 5454 | } else null; |
| ... | @@ -5303,7 +5459,8 @@ fn prelinkInner(elf: *Elf) Error!void { | ... | @@ -5303,7 +5459,8 @@ fn prelinkInner(elf: *Elf) Error!void { |
| 5303 | @as(usize, @intFromBool(elf.shndx.init_array != .UNDEF)) * 2 + | 5459 | @as(usize, @intFromBool(elf.shndx.init_array != .UNDEF)) * 2 + |
| 5304 | @as(usize, @intFromBool(elf.shndx.fini_array != .UNDEF)) * 2 + | 5460 | @as(usize, @intFromBool(elf.shndx.fini_array != .UNDEF)) * 2 + |
| 5305 | @as(usize, @intFromBool(elf.shndx.preinit_array != .UNDEF)) * 2 + | 5461 | @as(usize, @intFromBool(elf.shndx.preinit_array != .UNDEF)) * 2 + |
| 5306 | @intFromBool(comp.config.output_mode == .Exe) + 12; | 5462 | @as(usize, @intFromBool(use_plt)) * 4 + |
| | 5463 | @intFromBool(comp.config.output_mode == .Exe) + 8; |
| 5307 | const dynamic_size: u32 = @intCast(@sizeOf(ElfN.Addr) * 2 * dynamic_len); | 5464 | const dynamic_size: u32 = @intCast(@sizeOf(ElfN.Addr) * 2 * dynamic_len); |
| 5308 | const dynamic_ni = elf.shndx.dynamic.get(elf).ni; | 5465 | const dynamic_ni = elf.shndx.dynamic.get(elf).ni; |
| 5309 | try dynamic_ni.resize(&elf.mf, gpa, dynamic_size); | 5466 | try dynamic_ni.resize(&elf.mf, gpa, dynamic_size); |
| ... | @@ -5315,6 +5472,8 @@ fn prelinkInner(elf: *Elf) Error!void { | ... | @@ -5315,6 +5472,8 @@ fn prelinkInner(elf: *Elf) Error!void { |
| 5315 | init_array: ?usize, | 5472 | init_array: ?usize, |
| 5316 | fini_array: ?usize, | 5473 | fini_array: ?usize, |
| 5317 | preinit_array: ?usize, | 5474 | preinit_array: ?usize, |
| | 5475 | jmprel: ?usize, |
| | 5476 | pltgot: ?usize, |
| 5318 | } = indices: { | 5477 | } = indices: { |
| 5319 | const sec_dynamic = dynamic_ni.slice(&elf.mf); | 5478 | const sec_dynamic = dynamic_ni.slice(&elf.mf); |
| 5320 | const dynamic_entries: [][2]ElfN.Addr = @ptrCast(@alignCast(sec_dynamic)); | 5479 | const dynamic_entries: [][2]ElfN.Addr = @ptrCast(@alignCast(sec_dynamic)); |
| ... | @@ -5347,7 +5506,7 @@ fn prelinkInner(elf: *Elf) Error!void { | ... | @@ -5347,7 +5506,7 @@ fn prelinkInner(elf: *Elf) Error!void { |
| 5347 | } | 5506 | } |
| 5348 | const init_array_index: ?usize = if (elf.shndx.init_array != .UNDEF) i: { | 5507 | const init_array_index: ?usize = if (elf.shndx.init_array != .UNDEF) i: { |
| 5349 | dynamic_entries[dynamic_index..][0..2].* = .{ | 5508 | dynamic_entries[dynamic_index..][0..2].* = .{ |
| 5350 | .{ std.elf.DT_INIT_ARRAY, @intCast(elf.shndx.init_array.vaddr(elf)) }, | 5509 | .{ std.elf.DT_INIT_ARRAY, 0 }, // reloc added below |
| 5351 | .{ std.elf.DT_INIT_ARRAYSZ, elf.targetLoad( | 5510 | .{ std.elf.DT_INIT_ARRAYSZ, elf.targetLoad( |
| 5352 | &@field(elf.shdrPtr(elf.shndx.init_array), @tagName(ct_class)).size, | 5511 | &@field(elf.shdrPtr(elf.shndx.init_array), @tagName(ct_class)).size, |
| 5353 | ) }, | 5512 | ) }, |
| ... | @@ -5357,7 +5516,7 @@ fn prelinkInner(elf: *Elf) Error!void { | ... | @@ -5357,7 +5516,7 @@ fn prelinkInner(elf: *Elf) Error!void { |
| 5357 | } else null; | 5516 | } else null; |
| 5358 | const fini_array_index: ?usize = if (elf.shndx.fini_array != .UNDEF) i: { | 5517 | const fini_array_index: ?usize = if (elf.shndx.fini_array != .UNDEF) i: { |
| 5359 | dynamic_entries[dynamic_index..][0..2].* = .{ | 5518 | dynamic_entries[dynamic_index..][0..2].* = .{ |
| 5360 | .{ std.elf.DT_FINI_ARRAY, @intCast(elf.shndx.fini_array.vaddr(elf)) }, | 5519 | .{ std.elf.DT_FINI_ARRAY, 0 }, // reloc added below |
| 5361 | .{ std.elf.DT_FINI_ARRAYSZ, elf.targetLoad( | 5520 | .{ std.elf.DT_FINI_ARRAYSZ, elf.targetLoad( |
| 5362 | &@field(elf.shdrPtr(elf.shndx.fini_array), @tagName(ct_class)).size, | 5521 | &@field(elf.shdrPtr(elf.shndx.fini_array), @tagName(ct_class)).size, |
| 5363 | ) }, | 5522 | ) }, |
| ... | @@ -5367,7 +5526,7 @@ fn prelinkInner(elf: *Elf) Error!void { | ... | @@ -5367,7 +5526,7 @@ fn prelinkInner(elf: *Elf) Error!void { |
| 5367 | } else null; | 5526 | } else null; |
| 5368 | const preinit_array_index: ?usize = if (elf.shndx.preinit_array != .UNDEF) i: { | 5527 | const preinit_array_index: ?usize = if (elf.shndx.preinit_array != .UNDEF) i: { |
| 5369 | dynamic_entries[dynamic_index..][0..2].* = .{ | 5528 | dynamic_entries[dynamic_index..][0..2].* = .{ |
| 5370 | .{ std.elf.DT_PREINIT_ARRAY, @intCast(elf.shndx.preinit_array.vaddr(elf)) }, | 5529 | .{ std.elf.DT_PREINIT_ARRAY, 0 }, // reloc added below |
| 5371 | .{ std.elf.DT_PREINIT_ARRAYSZ, elf.targetLoad( | 5530 | .{ std.elf.DT_PREINIT_ARRAYSZ, elf.targetLoad( |
| 5372 | &@field(elf.shdrPtr(elf.shndx.preinit_array), @tagName(ct_class)).size, | 5531 | &@field(elf.shdrPtr(elf.shndx.preinit_array), @tagName(ct_class)).size, |
| 5373 | ) }, | 5532 | ) }, |
| ... | @@ -5375,27 +5534,33 @@ fn prelinkInner(elf: *Elf) Error!void { | ... | @@ -5375,27 +5534,33 @@ fn prelinkInner(elf: *Elf) Error!void { |
| 5375 | defer dynamic_index += 2; | 5534 | defer dynamic_index += 2; |
| 5376 | break :i dynamic_index; | 5535 | break :i dynamic_index; |
| 5377 | } else null; | 5536 | } else null; |
| 5378 | dynamic_entries[dynamic_index..][0..12].* = .{ | 5537 | const jmprel_index: ?usize, const pltgot_index: ?usize = if (use_plt) i: { |
| 5379 | .{ std.elf.DT_RELA, @intCast(elf.shndx.rela_dyn.vaddr(elf)) }, | 5538 | dynamic_entries[dynamic_index..][0..4].* = .{ |
| | 5539 | .{ std.elf.DT_JMPREL, 0 }, // reloc added below |
| | 5540 | .{ std.elf.DT_PLTGOT, 0 }, // reloc added below |
| | 5541 | .{ std.elf.DT_PLTRELSZ, elf.targetLoad( |
| | 5542 | &@field(elf.shdrPtr(elf.shndx.rela_plt), @tagName(ct_class)).size, |
| | 5543 | ) }, |
| | 5544 | .{ std.elf.DT_PLTREL, std.elf.DT_RELA }, |
| | 5545 | }; |
| | 5546 | defer dynamic_index += 4; |
| | 5547 | break :i .{ dynamic_index, dynamic_index + 1 }; |
| | 5548 | } else .{ null, null }; |
| | 5549 | dynamic_entries[dynamic_index..][0..8].* = .{ |
| | 5550 | .{ std.elf.DT_RELA, 0 }, // reloc added below |
| 5380 | .{ std.elf.DT_RELASZ, elf.targetLoad( | 5551 | .{ std.elf.DT_RELASZ, elf.targetLoad( |
| 5381 | &@field(elf.shdrPtr(elf.shndx.rela_dyn), @tagName(ct_class)).size, | 5552 | &@field(elf.shdrPtr(elf.shndx.rela_dyn), @tagName(ct_class)).size, |
| 5382 | ) }, | 5553 | ) }, |
| 5383 | .{ std.elf.DT_RELAENT, @sizeOf(ElfN.Rela) }, | 5554 | .{ std.elf.DT_RELAENT, @sizeOf(ElfN.Rela) }, |
| 5384 | .{ std.elf.DT_JMPREL, @intCast(elf.shndx.rela_plt.vaddr(elf)) }, | 5555 | .{ std.elf.DT_SYMTAB, 0 }, // reloc added below |
| 5385 | .{ std.elf.DT_PLTRELSZ, elf.targetLoad( | | |
| 5386 | &@field(elf.shdrPtr(elf.shndx.rela_plt), @tagName(ct_class)).size, | | |
| 5387 | ) }, | | |
| 5388 | .{ std.elf.DT_PLTGOT, @intCast(elf.shndx.got_plt.vaddr(elf)) }, | | |
| 5389 | .{ std.elf.DT_PLTREL, std.elf.DT_RELA }, | | |
| 5390 | .{ std.elf.DT_SYMTAB, @intCast(elf.shndx.dynsym.vaddr(elf)) }, | | |
| 5391 | .{ std.elf.DT_SYMENT, @sizeOf(ElfN.Sym) }, | 5556 | .{ std.elf.DT_SYMENT, @sizeOf(ElfN.Sym) }, |
| 5392 | .{ std.elf.DT_STRTAB, @intCast(elf.shndx.dynstr.vaddr(elf)) }, | 5557 | .{ std.elf.DT_STRTAB, 0 }, // reloc added below |
| 5393 | .{ std.elf.DT_STRSZ, elf.targetLoad( | 5558 | .{ std.elf.DT_STRSZ, elf.targetLoad( |
| 5394 | &@field(elf.shdrPtr(elf.shndx.dynstr), @tagName(ct_class)).size, | 5559 | &@field(elf.shdrPtr(elf.shndx.dynstr), @tagName(ct_class)).size, |
| 5395 | ) }, | 5560 | ) }, |
| 5396 | .{ std.elf.DT_NULL, 0 }, | 5561 | .{ std.elf.DT_NULL, 0 }, |
| 5397 | }; | 5562 | }; |
| 5398 | dynamic_index += 12; | 5563 | dynamic_index += 8; |
| 5399 | assert(dynamic_index == dynamic_len); | 5564 | assert(dynamic_index == dynamic_len); |
| 5400 | if (elf.targetEndian() != native_endian) for (dynamic_entries) |*dynamic_entry| | 5565 | if (elf.targetEndian() != native_endian) for (dynamic_entries) |*dynamic_entry| |
| 5401 | std.mem.byteSwapAllFields(@TypeOf(dynamic_entry.*), dynamic_entry); | 5566 | std.mem.byteSwapAllFields(@TypeOf(dynamic_entry.*), dynamic_entry); |
| ... | @@ -5404,66 +5569,74 @@ fn prelinkInner(elf: *Elf) Error!void { | ... | @@ -5404,66 +5569,74 @@ fn prelinkInner(elf: *Elf) Error!void { |
| 5404 | .init_array = init_array_index, | 5569 | .init_array = init_array_index, |
| 5405 | .fini_array = fini_array_index, | 5570 | .fini_array = fini_array_index, |
| 5406 | .preinit_array = preinit_array_index, | 5571 | .preinit_array = preinit_array_index, |
| | 5572 | .jmprel = jmprel_index, |
| | 5573 | .pltgot = pltgot_index, |
| 5407 | }; | 5574 | }; |
| 5408 | }; | 5575 | }; |
| 5409 | | 5576 | |
| | 5577 | const dsorel: SymbolReloc.Type = switch (ct_class) { |
| | 5578 | .NONE, _ => comptime unreachable, |
| | 5579 | .@"32" => .dsorel32, |
| | 5580 | .@"64" => .dsorel64, |
| | 5581 | }; |
| | 5582 | |
| 5410 | elf.dynamic_first_symbol_reloc = @enumFromInt(elf.symbol_relocs.items.len); | 5583 | elf.dynamic_first_symbol_reloc = @enumFromInt(elf.symbol_relocs.items.len); |
| 5411 | try elf.ensureUnusedRelocCapacity(dynamic_ni, 8); | 5584 | try elf.ensureUnusedRelocCapacity(dynamic_ni, 8); |
| 5412 | if (dynamic_indices.init_array) |index| try elf.addRelocAssumeCapacity( | 5585 | if (dynamic_indices.init_array) |index| try elf.addSymbolRelocAssumeCapacity( |
| 5413 | dynamic_ni, | 5586 | dynamic_ni, |
| 5414 | @sizeOf(ElfN.Addr) * (2 * index + 1), | 5587 | @sizeOf(ElfN.Addr) * (2 * index + 1), |
| 5415 | .local(elf.shndx.init_array.get(elf).lsi), | 5588 | .local(elf.shndx.init_array.get(elf).lsi), |
| 5416 | 0, | 5589 | 0, |
| 5417 | .absAddr(elf), | 5590 | dsorel, |
| 5418 | ); | 5591 | ); |
| 5419 | if (dynamic_indices.fini_array) |index| try elf.addRelocAssumeCapacity( | 5592 | if (dynamic_indices.fini_array) |index| try elf.addSymbolRelocAssumeCapacity( |
| 5420 | dynamic_ni, | 5593 | dynamic_ni, |
| 5421 | @sizeOf(ElfN.Addr) * (2 * index + 1), | 5594 | @sizeOf(ElfN.Addr) * (2 * index + 1), |
| 5422 | .local(elf.shndx.fini_array.get(elf).lsi), | 5595 | .local(elf.shndx.fini_array.get(elf).lsi), |
| 5423 | 0, | 5596 | 0, |
| 5424 | .absAddr(elf), | 5597 | dsorel, |
| 5425 | ); | 5598 | ); |
| 5426 | if (dynamic_indices.preinit_array) |index| try elf.addRelocAssumeCapacity( | 5599 | if (dynamic_indices.preinit_array) |index| try elf.addSymbolRelocAssumeCapacity( |
| 5427 | dynamic_ni, | 5600 | dynamic_ni, |
| 5428 | @sizeOf(ElfN.Addr) * (2 * index + 1), | 5601 | @sizeOf(ElfN.Addr) * (2 * index + 1), |
| 5429 | .local(elf.shndx.preinit_array.get(elf).lsi), | 5602 | .local(elf.shndx.preinit_array.get(elf).lsi), |
| 5430 | 0, | 5603 | 0, |
| 5431 | .absAddr(elf), | 5604 | dsorel, |
| 5432 | ); | 5605 | ); |
| 5433 | try elf.addRelocAssumeCapacity( | 5606 | if (dynamic_indices.jmprel) |index| try elf.addSymbolRelocAssumeCapacity( |
| 5434 | dynamic_ni, | 5607 | dynamic_ni, |
| 5435 | @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 12) + 1), | 5608 | @sizeOf(ElfN.Addr) * (2 * index + 1), |
| 5436 | .local(elf.shndx.rela_dyn.get(elf).lsi), | 5609 | .local(elf.shndx.rela_plt.get(elf).lsi), |
| 5437 | 0, | 5610 | 0, |
| 5438 | .absAddr(elf), | 5611 | dsorel, |
| 5439 | ); | 5612 | ); |
| 5440 | try elf.addRelocAssumeCapacity( | 5613 | if (dynamic_indices.pltgot) |index| try elf.addSymbolRelocAssumeCapacity( |
| 5441 | dynamic_ni, | 5614 | dynamic_ni, |
| 5442 | @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 9) + 1), | 5615 | @sizeOf(ElfN.Addr) * (2 * index + 1), |
| 5443 | .local(elf.shndx.rela_plt.get(elf).lsi), | 5616 | .local(elf.shndx.got_plt.get(elf).lsi), |
| 5444 | 0, | 5617 | 0, |
| 5445 | .absAddr(elf), | 5618 | dsorel, |
| 5446 | ); | 5619 | ); |
| 5447 | try elf.addRelocAssumeCapacity( | 5620 | try elf.addSymbolRelocAssumeCapacity( |
| 5448 | dynamic_ni, | 5621 | dynamic_ni, |
| 5449 | @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 7) + 1), | 5622 | @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 8) + 1), |
| 5450 | .local(elf.shndx.got_plt.get(elf).lsi), | 5623 | .local(elf.shndx.rela_dyn.get(elf).lsi), |
| 5451 | 0, | 5624 | 0, |
| 5452 | .absAddr(elf), | 5625 | dsorel, |
| 5453 | ); | 5626 | ); |
| 5454 | try elf.addRelocAssumeCapacity( | 5627 | try elf.addSymbolRelocAssumeCapacity( |
| 5455 | dynamic_ni, | 5628 | dynamic_ni, |
| 5456 | @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 5) + 1), | 5629 | @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 5) + 1), |
| 5457 | .local(elf.shndx.dynsym.get(elf).lsi), | 5630 | .local(elf.shndx.dynsym.get(elf).lsi), |
| 5458 | 0, | 5631 | 0, |
| 5459 | .absAddr(elf), | 5632 | dsorel, |
| 5460 | ); | 5633 | ); |
| 5461 | try elf.addRelocAssumeCapacity( | 5634 | try elf.addSymbolRelocAssumeCapacity( |
| 5462 | dynamic_ni, | 5635 | dynamic_ni, |
| 5463 | @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 3) + 1), | 5636 | @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 3) + 1), |
| 5464 | .local(elf.shndx.dynstr.get(elf).lsi), | 5637 | .local(elf.shndx.dynstr.get(elf).lsi), |
| 5465 | 0, | 5638 | 0, |
| 5466 | .absAddr(elf), | 5639 | dsorel, |
| 5467 | ); | 5640 | ); |
| 5468 | }, | 5641 | }, |
| 5469 | }; | 5642 | }; |
| ... | @@ -5802,16 +5975,34 @@ fn addSymbolRelocAssumeCapacity( | ... | @@ -5802,16 +5975,34 @@ fn addSymbolRelocAssumeCapacity( |
| 5802 | @"type": SymbolReloc.Type, | 5975 | @"type": SymbolReloc.Type, |
| 5803 | ) Error!void { | 5976 | ) Error!void { |
| 5804 | assert(elf.ehdrField(.type) != .REL); | 5977 | assert(elf.ehdrField(.type) != .REL); |
| | 5978 | assert(node != .none); |
| 5805 | | 5979 | |
| 5806 | const rela_index: Section.RelaIndex.Optional = r: { | 5980 | const rela_index: Section.RelaIndex.Optional = r: { |
| 5807 | if (elf.haveCanonicalSymbolDefinition(target)) break :r .none; | 5981 | // If we emit a runtime relocation entry, its `offset` is a virtual address, so we need to |
| 5808 | // If the definition is (potentially) external, `target` must be global. | 5982 | // determine the vaddr of `node`. |
| 5809 | const global_name = target.unwrap().global; | 5983 | const node_vaddr: u64 = switch (elf.getNode(node)) { |
| | 5984 | .file => unreachable, |
| | 5985 | .ehdr => unreachable, |
| | 5986 | .shdr => unreachable, |
| | 5987 | .segment => unreachable, |
| | 5988 | .copied_global => unreachable, |
| | 5989 | .section => |shndx| shndx.vaddr(elf), |
| | 5990 | .input_section => |isi| isi.ptrConst(elf).vaddr, |
| | 5991 | inline .nav, |
| | 5992 | .uav, |
| | 5993 | .lazy_code, |
| | 5994 | .lazy_const_data, |
| | 5995 | => |i| Symbol.Id.local(i.symbol(elf)).value(elf), |
| | 5996 | }; |
| 5810 | | 5997 | |
| 5811 | const rela_type: MachineRelocType = switch (elf.ehdrField(.machine)) { | 5998 | const rela_type: MachineRelocType = switch (elf.ehdrField(.machine)) { |
| 5812 | else => |machine| @panic(@tagName(machine)), | 5999 | else => |machine| @panic(@tagName(machine)), |
| 5813 | .X86_64 => .{ .X86_64 = switch (@"type") { | 6000 | .X86_64 => .{ .X86_64 = switch (@"type") { |
| 5814 | .write_rela => unreachable, | 6001 | .write_rela => unreachable, |
| | 6002 | .dsorel64, .dsorel32 => { |
| | 6003 | assert(target.unwrap() == .local); |
| | 6004 | break :r .none; |
| | 6005 | }, |
| 5815 | .abs64 => .@"64", | 6006 | .abs64 => .@"64", |
| 5816 | .abs32 => .@"32", | 6007 | .abs32 => .@"32", |
| 5817 | .abs32s => .@"32S", | 6008 | .abs32s => .@"32S", |
| ... | @@ -5839,65 +6030,79 @@ fn addSymbolRelocAssumeCapacity( | ... | @@ -5839,65 +6030,79 @@ fn addSymbolRelocAssumeCapacity( |
| 5839 | .tpoff64_hi12, | 6030 | .tpoff64_hi12, |
| 5840 | => unreachable, | 6031 | => unreachable, |
| 5841 | } }, | 6032 | } }, |
| 5842 | .LOONGARCH => .{ | 6033 | .LOONGARCH => .{ .LOONGARCH = switch (@"type") { |
| 5843 | .LOONGARCH = switch (@"type") { | 6034 | .write_rela => unreachable, |
| 5844 | .write_rela => unreachable, | 6035 | .dsorel64, .dsorel32 => { |
| 5845 | .abs64 => .@"64", | 6036 | assert(target.unwrap() == .local); |
| 5846 | .abs32 => .@"32", | 6037 | break :r .none; |
| 5847 | .abs32s, .size64, .size32 => unreachable, | | |
| 5848 | .rel64 => .@"64_PCREL", | | |
| 5849 | .rel32 => .@"32_PCREL", | | |
| 5850 | .pltrel64, .pltrel32 => break :r .none, | | |
| 5851 | .dtpoff64 => .TLS_DTPREL64, | | |
| 5852 | .dtpoff32 => .TLS_DTPREL32, | | |
| 5853 | .tpoff64 => .TLS_TPREL64, | | |
| 5854 | .tpoff32 => .TLS_TPREL32, | | |
| 5855 | .abs32_lo12 => .PCALA_LO12, | | |
| 5856 | .rel32_hi20 => .PCALA_HI20, | | |
| 5857 | .rel64_lo20 => .PCALA64_LO20, | | |
| 5858 | .rel64_hi12 => .PCALA64_HI12, | | |
| 5859 | .branch_rel18 => .B16, | | |
| 5860 | .branch_rel23 => .B21, | | |
| 5861 | .branch_rel28 => .B26, | | |
| 5862 | .call_rel38 => .CALL36, | | |
| 5863 | .tpoff32_lo12 => .TLS_LE_LO12, | | |
| 5864 | .tpoff32_hi20 => .TLS_LE_HI20, | | |
| 5865 | .tpoff64_lo20 => .TLS_LE64_LO20, | | |
| 5866 | .tpoff64_hi12 => .TLS_LE64_HI12, | | |
| 5867 | }, | 6038 | }, |
| 5868 | }, | 6039 | .abs64 => .@"64", |
| | 6040 | .abs32 => .@"32", |
| | 6041 | .abs32s, .size64, .size32 => unreachable, |
| | 6042 | .rel64 => .@"64_PCREL", |
| | 6043 | .rel32 => .@"32_PCREL", |
| | 6044 | .pltrel64, .pltrel32 => break :r .none, |
| | 6045 | .dtpoff64 => .TLS_DTPREL64, |
| | 6046 | .dtpoff32 => .TLS_DTPREL32, |
| | 6047 | .tpoff64 => .TLS_TPREL64, |
| | 6048 | .tpoff32 => .TLS_TPREL32, |
| | 6049 | .abs32_lo12 => .PCALA_LO12, |
| | 6050 | .rel32_hi20 => .PCALA_HI20, |
| | 6051 | .rel64_lo20 => .PCALA64_LO20, |
| | 6052 | .rel64_hi12 => .PCALA64_HI12, |
| | 6053 | .branch_rel18 => .B16, |
| | 6054 | .branch_rel23 => .B21, |
| | 6055 | .branch_rel28 => .B26, |
| | 6056 | .call_rel38 => .CALL36, |
| | 6057 | .tpoff32_lo12 => .TLS_LE_LO12, |
| | 6058 | .tpoff32_hi20 => .TLS_LE_HI20, |
| | 6059 | .tpoff64_lo20 => .TLS_LE64_LO20, |
| | 6060 | .tpoff64_hi12 => .TLS_LE64_HI12, |
| | 6061 | } }, |
| 5869 | }; | 6062 | }; |
| 5870 | | 6063 | |
| 5871 | const dynsym_index = elf.globalByName(global_name).?.dynsym_index; | 6064 | class: switch (elf.classifySymbolValue(target)) { |
| 5872 | assert(dynsym_index != 0); | 6065 | .static => break :r .none, |
| 5873 | | 6066 | .static_relative => { |
| 5874 | switch (elf.nodeWantsDsoRelocation(node)) { | 6067 | if (!@"type".isAbsAddr(elf)) break :r .none; |
| 5875 | .no => break :r .none, | 6068 | switch (elf.nodeWantsDsoRelocation(node)) { |
| 5876 | .yes => {}, | 6069 | .no => break :r .none, |
| 5877 | .yes_textrel => if (try elf.maybeAddCopyRelocation(global_name)) { | 6070 | .yes => {}, |
| 5878 | // We were able to use a copy relocation on this symbol to avoid a text relocation, | 6071 | .yes_textrel => elf.textrel_count += 1, |
| 5879 | // which is apparently considered a good thing despite copy relocations being an | 6072 | } |
| 5880 | // abomination. (This is necessary for correctness in some cases, because e.g. a | 6073 | break :r elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{ |
| 5881 | // 32-bit runtime relocation on a 64-bit target will often cause rtld errors due to | 6074 | .type = .relative(elf), |
| 5882 | // the DSOs being loaded too far apart.) | 6075 | .offset = node_vaddr + offset, |
| 5883 | break :r .none; | 6076 | .raw_sym_index = 0, |
| 5884 | } else { | 6077 | .addend = 0, |
| 5885 | // At least for now, our only choice is a text relocation. | 6078 | }).toOptional(); |
| 5886 | elf.textrel_count += 1; | 6079 | }, |
| | 6080 | .dynamic => dso_reloc: switch (elf.nodeWantsDsoRelocation(node)) { |
| | 6081 | .no => break :r .none, |
| | 6082 | .yes_textrel => if (try elf.maybeAddCopyRelocation(target.unwrap().global)) { |
| | 6083 | // We were able to use a copy relocation on this symbol to avoid a text relocation, |
| | 6084 | // which is apparently considered a good thing despite copy relocations being an |
| | 6085 | // abomination. (This is necessary for correctness in some cases, because e.g. a |
| | 6086 | // 32-bit runtime relocation on a 64-bit target will often cause rtld errors due to |
| | 6087 | // the DSOs being loaded too far apart.) |
| | 6088 | switch (elf.classifySymbolValue(target)) { |
| | 6089 | .dynamic => unreachable, // we just added a copy relocation |
| | 6090 | .static => continue :class .static, |
| | 6091 | .static_relative => continue :class .static_relative, |
| | 6092 | } |
| | 6093 | } else { |
| | 6094 | // At least for now, our only choice is a text relocation. |
| | 6095 | elf.textrel_count += 1; |
| | 6096 | continue :dso_reloc .yes; |
| | 6097 | }, |
| | 6098 | .yes => break :r elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{ |
| | 6099 | .type = rela_type, |
| | 6100 | .offset = node_vaddr + offset, |
| | 6101 | .raw_sym_index = elf.globalByName(target.unwrap().global).?.dynsym_index, |
| | 6102 | .addend = addend, |
| | 6103 | }).toOptional(), |
| 5887 | }, | 6104 | }, |
| 5888 | } | 6105 | } |
| 5889 | | | |
| 5890 | // It currently looks like we need a runtime relocation for this. | | |
| 5891 | break :r elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{ | | |
| 5892 | .type = rela_type, | | |
| 5893 | // This field needs to equal the offset into the section, which is *not* necessarily | | |
| 5894 | // the same thing as our `offset`, which is the offset into `node`. We could compute | | |
| 5895 | // the section offset now, but there's no point, because `flushMovedNodeRelocs` will | | |
| 5896 | // eventually do it for us anyway, so just init to 0. | | |
| 5897 | .offset = 0, | | |
| 5898 | .raw_sym_index = dynsym_index, | | |
| 5899 | .addend = addend, | | |
| 5900 | }).toOptional(); | | |
| 5901 | }; | 6106 | }; |
| 5902 | | 6107 | |
| 5903 | const ri: SymbolReloc.Index = @enumFromInt(elf.symbol_relocs.items.len); | 6108 | const ri: SymbolReloc.Index = @enumFromInt(elf.symbol_relocs.items.len); |
| ... | @@ -5920,6 +6125,9 @@ fn addSymbolRelocAssumeCapacity( | ... | @@ -5920,6 +6125,9 @@ fn addSymbolRelocAssumeCapacity( |
| 5920 | if (@"type".dependsOnTlsSize()) { | 6125 | if (@"type".dependsOnTlsSize()) { |
| 5921 | elf.tls_size_symbol_relocs.putAssumeCapacityNoClobber(ri, {}); | 6126 | elf.tls_size_symbol_relocs.putAssumeCapacityNoClobber(ri, {}); |
| 5922 | } | 6127 | } |
| | 6128 | |
| | 6129 | // Actually apply the new relocation! |
| | 6130 | ri.get(elf).apply(elf); |
| 5923 | } | 6131 | } |
| 5924 | fn addGotRelocAssumeCapacity( | 6132 | fn addGotRelocAssumeCapacity( |
| 5925 | elf: *Elf, | 6133 | elf: *Elf, |
| ... | @@ -5997,7 +6205,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void { | ... | @@ -5997,7 +6205,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void { |
| 5997 | .reserved => .{ .unsigned = 0 }, | 6205 | .reserved => .{ .unsigned = 0 }, |
| 5998 | .tpoff => |sym_id| val: { | 6206 | .tpoff => |sym_id| val: { |
| 5999 | // Only the executable's per-module TLS block is at a known offset from the TLS pointer. | 6207 | // Only the executable's per-module TLS block is at a known offset from the TLS pointer. |
| 6000 | if (elf.base.comp.config.output_mode == .Exe and elf.haveCanonicalSymbolDefinition(sym_id)) { | 6208 | if (elf.base.comp.config.output_mode == .Exe and elf.classifySymbolValue(sym_id) != .dynamic) { |
| 6001 | const tls_phndx = elf.getNode(elf.ni.tls).segment; | 6209 | const tls_phndx = elf.getNode(elf.ni.tls).segment; |
| 6002 | const tls_size: u64 = switch (elf.phdrSlice()) { | 6210 | const tls_size: u64 = switch (elf.phdrSlice()) { |
| 6003 | inline else => |phdr| tls_size: { | 6211 | inline else => |phdr| tls_size: { |
| ... | @@ -6029,33 +6237,52 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void { | ... | @@ -6029,33 +6237,52 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void { |
| 6029 | } }, | 6237 | } }, |
| 6030 | }; | 6238 | }; |
| 6031 | }, | 6239 | }, |
| 6032 | .symbol, .tlsgd1 => |sym, tag| val: { | 6240 | .symbol => |sym| switch (elf.classifySymbolValue(sym)) { |
| 6033 | if (elf.haveCanonicalSymbolDefinition(sym)) { | 6241 | .static => .{ .unsigned = sym.value(elf) }, |
| 6034 | break :val .{ .unsigned = sym.value(elf) }; | 6242 | .static_relative => .{ .reloc = .{ |
| 6035 | } | 6243 | .type = .relative(elf), |
| 6036 | break :val .{ .reloc = .{ | 6244 | .dynsym_index = 0, |
| 6037 | .type = if (tag == .symbol) .globDat(elf) else .dtpOffAddr(elf), | 6245 | .addend = @bitCast(sym.value(elf)), |
| | 6246 | } }, |
| | 6247 | .dynamic => .{ .reloc = .{ |
| | 6248 | .type = .globDat(elf), |
| 6038 | .dynsym_index = elf.globalByName(sym.unwrap().global).?.dynsym_index, | 6249 | .dynsym_index = elf.globalByName(sym.unwrap().global).?.dynsym_index, |
| 6039 | .addend = 0, | 6250 | .addend = 0, |
| 6040 | } }; | 6251 | } }, |
| 6041 | }, | 6252 | }, |
| 6042 | .tlsgd0 => |sym| switch (elf.shndx.dynamic) { | 6253 | .tlsgd1 => |sym| switch (elf.classifySymbolValue(sym)) { |
| 6043 | .UNDEF => .{ .unsigned = 1 }, // TLS module ID for exexcutable | 6254 | .static => .{ .unsigned = sym.value(elf) }, |
| 6044 | else => .{ | 6255 | .static_relative => unreachable, // TLS variables should be in TLS sections, which do not return `.static_relative` |
| 6045 | .reloc = .{ | 6256 | .dynamic => .{ .reloc = .{ |
| 6046 | .type = switch (elf.ehdrField(.machine)) { | 6257 | .type = .dtpOffAddr(elf), |
| 6047 | else => |machine| @panic(@tagName(machine)), | 6258 | .dynsym_index = elf.globalByName(sym.unwrap().global).?.dynsym_index, |
| 6048 | .X86_64 => .{ .X86_64 = .DTPMOD64 }, | 6259 | .addend = 0, |
| 6049 | .LOONGARCH => .{ .LOONGARCH = if (elf.identClass() == .@"64") .TLS_DTPMOD64 else .TLS_DTPMOD32 }, | 6260 | } }, |
| 6050 | }, | 6261 | }, |
| 6051 | .dynsym_index = if (elf.haveCanonicalSymbolDefinition(sym)) 0 else elf.globalByName(sym.unwrap().global).?.dynsym_index, | 6262 | .tlsgd0 => |sym| switch (elf.base.comp.config.link_mode) { |
| 6052 | .addend = 0, | 6263 | .static => val: { |
| 6053 | }, | 6264 | assert(elf.base.comp.config.output_mode == .Exe); // static libraries don't have GOTs |
| | 6265 | break :val .{ .unsigned = 1 }; // TLS module ID for executable |
| 6054 | }, | 6266 | }, |
| | 6267 | .dynamic => .{ .reloc = .{ |
| | 6268 | .type = switch (elf.ehdrField(.machine)) { |
| | 6269 | else => |machine| @panic(@tagName(machine)), |
| | 6270 | .X86_64 => .{ .X86_64 = .DTPMOD64 }, |
| | 6271 | .LOONGARCH => .{ .LOONGARCH = if (elf.identClass() == .@"64") .TLS_DTPMOD64 else .TLS_DTPMOD32 }, |
| | 6272 | }, |
| | 6273 | .dynsym_index = switch (elf.classifySymbolValue(sym)) { |
| | 6274 | .static, .static_relative => 0, |
| | 6275 | .dynamic => elf.globalByName(sym.unwrap().global).?.dynsym_index, |
| | 6276 | }, |
| | 6277 | .addend = 0, |
| | 6278 | } }, |
| 6055 | }, | 6279 | }, |
| 6056 | .tlsld0 => switch (elf.shndx.dynamic) { | 6280 | .tlsld0 => switch (elf.base.comp.config.link_mode) { |
| 6057 | .UNDEF => .{ .unsigned = 1 }, // TLS module ID for exexcutable | 6281 | .static => val: { |
| 6058 | else => .{ .reloc = .{ | 6282 | assert(elf.base.comp.config.output_mode == .Exe); // static libraries don't have GOTs |
| | 6283 | break :val .{ .unsigned = 1 }; // TLS module ID for executable |
| | 6284 | }, |
| | 6285 | .dynamic => .{ .reloc = .{ |
| 6059 | .type = switch (elf.ehdrField(.machine)) { | 6286 | .type = switch (elf.ehdrField(.machine)) { |
| 6060 | else => |machine| @panic(@tagName(machine)), | 6287 | else => |machine| @panic(@tagName(machine)), |
| 6061 | .X86_64 => .{ .X86_64 = .DTPMOD64 }, | 6288 | .X86_64 => .{ .X86_64 = .DTPMOD64 }, |
| ... | @@ -6470,44 +6697,52 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool { | ... | @@ -6470,44 +6697,52 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool { |
| 6470 | ri = reloc.next; | 6697 | ri = reloc.next; |
| 6471 | } | 6698 | } |
| 6472 | }, | 6699 | }, |
| 6473 | else => { | 6700 | // For other `ET_*` values, the index in `.dynsym` has changed. There are a few |
| 6474 | // Index in `.dynsym` has changed. This case is slightly trickier because there | 6701 | // places we might have emitted output relocations, depending on whether or not the |
| 6475 | // are a few things which may have emitted runtime relocations, including symbol | 6702 | // symbol's value is statically known. |
| 6476 | // relocs... | 6703 | else => switch (elf.classifySymbolValue(sym_id)) { |
| 6477 | const dynsym_index = global.dynsym_index; | 6704 | .static, .static_relative => { |
| 6478 | var ri = sym.first_target_reloc; | 6705 | // Since the symbol value is statically known, we definitely aren't emitting |
| 6479 | while (ri != .none) { | 6706 | // any relocation targeting it (we might have `R_*_RELATIVE` relocs but they |
| 6480 | const reloc = ri.get(elf); | 6707 | // don't care about the dynsym index). The only exception is a copy reloc |
| 6481 | assert(reloc.target == sym_id); | 6708 | // could exist (and be the *reason* the symbol value is statically known). |
| 6482 | // There may or may not be a runtime relocation for this symbol reloc. | 6709 | if (elf.copied_globals.get(global_name)) |copied| { |
| 6483 | if (reloc.rela_index.unwrap()) |rela_index| { | 6710 | elf.shndx.rela_dyn.relaUpdateSym(elf, copied.rela_index, global.dynsym_index); |
| 6484 | reloc.relaSection(elf).relaUpdateSym(elf, rela_index, dynsym_index); | 6711 | } |
| | 6712 | }, |
| | 6713 | .dynamic => { |
| | 6714 | assert(!elf.copied_globals.contains(global_name)); // value would be statically known |
| | 6715 | |
| | 6716 | // Update symbol relocs: |
| | 6717 | var ri = sym.first_target_reloc; |
| | 6718 | while (ri != .none) { |
| | 6719 | const reloc = ri.get(elf); |
| | 6720 | assert(reloc.target == sym_id); |
| | 6721 | // There may or may not be a runtime relocation for this symbol reloc. |
| | 6722 | if (reloc.rela_index.unwrap()) |rela_index| { |
| | 6723 | elf.shndx.rela_dyn.relaUpdateSym(elf, rela_index, global.dynsym_index); |
| | 6724 | } |
| | 6725 | ri = reloc.next; |
| 6485 | } | 6726 | } |
| 6486 | ri = reloc.next; | | |
| 6487 | } | | |
| 6488 | | | |
| 6489 | // ...a copy relocation... | | |
| 6490 | if (elf.copied_globals.get(global_name)) |copied| { | | |
| 6491 | elf.shndx.rela_dyn.relaUpdateSym(elf, copied.rela_index, dynsym_index); | | |
| 6492 | } | | |
| 6493 | | 6727 | |
| 6494 | // ...a PLT entry... | 6728 | // Update the PLT entry's reloc if there is one: |
| 6495 | if (elf.plt.getIndex(global_name)) |plt_index| { | 6729 | if (elf.plt.getIndex(global_name)) |plt_index| { |
| 6496 | // PLT indices exactly match `.rela.plt` relocation indices. | 6730 | // PLT indices exactly match `.rela.plt` relocation indices. |
| 6497 | elf.shndx.rela_plt.relaUpdateSym(elf, @enumFromInt(plt_index), dynsym_index); | 6731 | elf.shndx.rela_plt.relaUpdateSym(elf, @enumFromInt(plt_index), global.dynsym_index); |
| 6498 | } | 6732 | } |
| 6499 | | 6733 | |
| 6500 | // ...and any relevant GOT entries. | 6734 | // Update relocs for any relevant GOT entries: |
| 6501 | if (elf.got.getIndex(.{ .symbol = sym_id })) |got_index| { | 6735 | if (elf.got.getIndex(.{ .symbol = sym_id })) |got_index| { |
| 6502 | elf.updateGotEntry(got_index); | 6736 | elf.updateGotEntry(got_index); |
| 6503 | } | 6737 | } |
| 6504 | if (elf.got.getIndex(.{ .tpoff = sym_id })) |got_index| { | 6738 | if (elf.got.getIndex(.{ .tpoff = sym_id })) |got_index| { |
| 6505 | elf.updateGotEntry(got_index); | 6739 | elf.updateGotEntry(got_index); |
| 6506 | } | 6740 | } |
| 6507 | if (elf.got.getIndex(.{ .tlsgd0 = sym_id })) |got_index| { | 6741 | if (elf.got.getIndex(.{ .tlsgd0 = sym_id })) |got_index| { |
| 6508 | elf.updateGotEntry(got_index); | 6742 | elf.updateGotEntry(got_index); |
| 6509 | elf.updateGotEntry(got_index + 1); // tlsgd1 | 6743 | elf.updateGotEntry(got_index + 1); // tlsgd1 |
| 6510 | } | 6744 | } |
| | 6745 | }, |
| 6511 | }, | 6746 | }, |
| 6512 | } | 6747 | } |
| 6513 | | 6748 | |