| ... | @@ -100,13 +100,15 @@ dynstr: StringTable, | ... | @@ -100,13 +100,15 @@ dynstr: StringTable, |
| 100 | /// | 100 | /// |
| 101 | /// Value is the output relocation in `.rela.dyn` for the GOT entry. | 101 | /// Value is the output relocation in `.rela.dyn` for the GOT entry. |
| 102 | got: std.array_hash_map.Auto(GotKey, Section.RelaIndex.Optional), | 102 | got: std.array_hash_map.Auto(GotKey, Section.RelaIndex.Optional), |
| | 103 | /// Key is the name of a global. |
| | 104 | /// |
| 103 | /// Indices map 1--1 to indices into the actual `.got.plt` section. These also equal indices into | 105 | /// Indices map 1--1 to indices into the actual `.got.plt` section. These also equal indices into |
| 104 | /// the relocations in `.rela.plt`, because every PLT entry has one output relocation (if a runtime | 106 | /// the relocations in `.rela.plt`, because every PLT entry has one output relocation (if a runtime |
| 105 | /// relocation is no longer necessary, then neither is the corresponding PLT entry!). | 107 | /// relocation is no longer necessary, then neither is the corresponding PLT entry!). |
| 106 | /// | 108 | /// |
| 107 | /// PLT entries in this map may be "dead", meaning the PLT entry has been deemed unnecessary so is | 109 | /// PLT entries in this map may be "dead", meaning the PLT entry has been deemed unnecessary so is |
| 108 | /// available for reuse---see `Elf.pltEntryIsDead`. Such entries must not be targeted by relocs. | 110 | /// available for reuse---see `Elf.pltEntryIsDead`. Such entries must not be targeted by relocs. |
| 109 | plt: std.array_hash_map.Auto(Symbol.Id, void), | 111 | plt: std.array_hash_map.Auto(String(.strtab), void), |
| 110 | /// The `.plt` section contains zero or more symbol relocations starting at this index. | 112 | /// The `.plt` section contains zero or more symbol relocations starting at this index. |
| 111 | plt_first_symbol_reloc: SymbolReloc.Index, | 113 | plt_first_symbol_reloc: SymbolReloc.Index, |
| 112 | /// The `.dynamic` section contains zero or more symbol relocations starting at this index. | 114 | /// The `.dynamic` section contains zero or more symbol relocations starting at this index. |
| ... | @@ -152,6 +154,9 @@ tls_size_symbol_relocs: std.array_hash_map.Auto(SymbolReloc.Index, void), | ... | @@ -152,6 +154,9 @@ tls_size_symbol_relocs: std.array_hash_map.Auto(SymbolReloc.Index, void), |
| 152 | section_by_name: std.array_hash_map.Auto(String(.shstrtab), void), | 154 | section_by_name: std.array_hash_map.Auto(String(.shstrtab), void), |
| 153 | /// Key is the name of a global symbol which has been moved to a new symtab index. Any relocation | 155 | /// Key is the name of a global symbol which has been moved to a new symtab index. Any relocation |
| 154 | /// entries which target that symbol must be updated to reference the correct symbol index. | 156 | /// entries which target that symbol must be updated to reference the correct symbol index. |
| | 157 | /// |
| | 158 | /// When emitting a relocatable (`ET_REL`), this refers to the index in `.symtab`. Otherwise, it |
| | 159 | /// refers to the index in `.dynsym`. |
| 155 | changed_symtab_index: std.array_hash_map.Auto(String(.strtab), void), | 160 | changed_symtab_index: std.array_hash_map.Auto(String(.strtab), void), |
| 156 | /// Counts how many relocations are currently in `.rela.dyn` which would require a `DT_TEXTREL` | 161 | /// Counts how many relocations are currently in `.rela.dyn` which would require a `DT_TEXTREL` |
| 157 | /// entry in the `.dynamic` section. This allows adding `DT_TEXTREL` to the output `.dynamic` | 162 | /// entry in the `.dynamic` section. This allows adding `DT_TEXTREL` to the output `.dynamic` |
| ... | @@ -474,9 +479,14 @@ const Section = struct { | ... | @@ -474,9 +479,14 @@ const Section = struct { |
| 474 | } | 479 | } |
| 475 | | 480 | |
| 476 | fn vaddr(s: Index, elf: *Elf) u64 { | 481 | fn vaddr(s: Index, elf: *Elf) u64 { |
| 477 | return switch (s.get(elf).lsi) { | 482 | return switch (elf.shdrPtr(s)) { |
| 478 | .null => 0, | 483 | inline else => |shdr| elf.targetLoad(&shdr.addr), |
| 479 | else => |lsi| Symbol.Id.local(lsi).value(elf), | 484 | }; |
| | 485 | } |
| | 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, |
| 480 | }; | 490 | }; |
| 481 | } | 491 | } |
| 482 | | 492 | |
| ... | @@ -487,8 +497,8 @@ const Section = struct { | ... | @@ -487,8 +497,8 @@ const Section = struct { |
| 487 | } | 497 | } |
| 488 | } | 498 | } |
| 489 | | 499 | |
| 490 | /// 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 |
| 491 | /// space to hold `n` additional `ElfN.Rela` entries. | 501 | /// unused space to hold `n` additional `ElfN.Rela` entries. |
| 492 | fn relaEnsureAdditionalCapacity(rela_shndx: Index, elf: *Elf, n: usize) Error!void { | 502 | fn relaEnsureAdditionalCapacity(rela_shndx: Index, elf: *Elf, n: usize) Error!void { |
| 493 | const node = rela_shndx.get(elf).ni; | 503 | const node = rela_shndx.get(elf).ni; |
| 494 | const need_size: u64 = switch (elf.shdrPtr(rela_shndx)) { | 504 | const need_size: u64 = switch (elf.shdrPtr(rela_shndx)) { |
| ... | @@ -514,9 +524,9 @@ const Section = struct { | ... | @@ -514,9 +524,9 @@ const Section = struct { |
| 514 | try elf.ensureNodeSize(node, need_size); | 524 | try elf.ensureNodeSize(node, need_size); |
| 515 | } | 525 | } |
| 516 | | 526 | |
| 517 | /// 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 |
| 518 | /// 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 |
| 519 | /// the relocation entry at `index` is not already free. | 529 | /// that the relocation entry at `index` is not already free. |
| 520 | fn relaDeleteOne(rela_shndx: Index, elf: *Elf, index: RelaIndex) void { | 530 | fn relaDeleteOne(rela_shndx: Index, elf: *Elf, index: RelaIndex) void { |
| 521 | switch (elf.shdrPtr(rela_shndx)) { | 531 | switch (elf.shdrPtr(rela_shndx)) { |
| 522 | inline else => |shdr, class| { | 532 | inline else => |shdr, class| { |
| ... | @@ -553,9 +563,9 @@ const Section = struct { | ... | @@ -553,9 +563,9 @@ const Section = struct { |
| 553 | rela_shndx.get(elf).rela.free_head = index.toOptional(); | 563 | rela_shndx.get(elf).rela.free_head = index.toOptional(); |
| 554 | } | 564 | } |
| 555 | | 565 | |
| 556 | /// 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 |
| 557 | /// 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 |
| 558 | /// for this operation was already guaranteed using `relaEnsureAdditionalCapacity`. | 568 | /// capacity for this operation was already guaranteed using `relaEnsureAdditionalCapacity`. |
| 559 | fn relaAddOneAssumeCapacity(rela_shndx: Index, elf: *Elf, opts: struct { | 569 | fn relaAddOneAssumeCapacity(rela_shndx: Index, elf: *Elf, opts: struct { |
| 560 | type: MachineRelocType, | 570 | type: MachineRelocType, |
| 561 | offset: u64, | 571 | offset: u64, |
| ... | @@ -617,8 +627,8 @@ const Section = struct { | ... | @@ -617,8 +627,8 @@ const Section = struct { |
| 617 | } | 627 | } |
| 618 | } | 628 | } |
| 619 | | 629 | |
| 620 | /// 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 |
| 621 | /// `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 |
| 622 | /// 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 |
| 623 | /// `.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). |
| 624 | 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 { |
| ... | @@ -642,7 +652,7 @@ const Section = struct { | ... | @@ -642,7 +652,7 @@ const Section = struct { |
| 642 | } | 652 | } |
| 643 | } | 653 | } |
| 644 | | 654 | |
| 645 | /// 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 |
| 646 | /// `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. |
| 647 | /// it is not deleted). | 657 | /// it is not deleted). |
| 648 | 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 { |
| ... | @@ -663,7 +673,7 @@ const Section = struct { | ... | @@ -663,7 +673,7 @@ const Section = struct { |
| 663 | } | 673 | } |
| 664 | } | 674 | } |
| 665 | | 675 | |
| 666 | /// 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 |
| 667 | /// `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`. |
| 668 | /// 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). |
| 669 | 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 { |
| ... | @@ -686,6 +696,28 @@ const Section = struct { | ... | @@ -686,6 +696,28 @@ const Section = struct { |
| 686 | }, | 696 | }, |
| 687 | } | 697 | } |
| 688 | } | 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 | } |
| 689 | }; | 721 | }; |
| 690 | }; | 722 | }; |
| 691 | | 723 | |
| ... | @@ -891,6 +923,16 @@ pub const MachineRelocType = union { | ... | @@ -891,6 +923,16 @@ pub const MachineRelocType = union { |
| 891 | .X86_64 => .{ .X86_64 = .COPY }, | 923 | .X86_64 => .{ .X86_64 = .COPY }, |
| 892 | }; | 924 | }; |
| 893 | } | 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 | } |
| 894 | pub fn jumpSlot(elf: *Elf) MachineRelocType { | 936 | pub fn jumpSlot(elf: *Elf) MachineRelocType { |
| 895 | return switch (elf.ehdrField(.machine)) { | 937 | return switch (elf.ehdrField(.machine)) { |
| 896 | else => unreachable, | 938 | else => unreachable, |
| ... | @@ -1022,6 +1064,15 @@ const SymbolReloc = struct { | ... | @@ -1022,6 +1064,15 @@ const SymbolReloc = struct { |
| 1022 | /// 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. |
| 1023 | write_rela, | 1065 | write_rela, |
| 1024 | | 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 | |
| 1025 | abs64, | 1076 | abs64, |
| 1026 | abs32, | 1077 | abs32, |
| 1027 | abs32s, | 1078 | abs32s, |
| ... | @@ -1056,21 +1107,39 @@ const SymbolReloc = struct { | ... | @@ -1056,21 +1107,39 @@ const SymbolReloc = struct { |
| 1056 | else => false, | 1107 | else => false, |
| 1057 | }; | 1108 | }; |
| 1058 | } | 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 | } |
| 1059 | }; | 1118 | }; |
| 1060 | | 1119 | |
| 1061 | fn apply(reloc: *const SymbolReloc, elf: *Elf) void { | 1120 | fn apply(reloc: *const SymbolReloc, elf: *Elf) void { |
| 1062 | assert(elf.ehdrField(.type) != .REL); | 1121 | assert(elf.ehdrField(.type) != .REL); |
| 1063 | assert(reloc.node != .none); | 1122 | assert(reloc.node != .none); |
| | 1123 | |
| 1064 | if (reloc.node.hasMoved(&elf.mf) or reloc.target.hasMoved(elf)) { | 1124 | if (reloc.node.hasMoved(&elf.mf) or reloc.target.hasMoved(elf)) { |
| 1065 | // 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 |
| 1066 | // `flushMoved` at some point anyway. | 1126 | // `flushMoved` at some point anyway. |
| 1067 | return; | 1127 | return; |
| 1068 | } | 1128 | } |
| 1069 | if (reloc.rela_index != .none) { | 1129 | |
| 1070 | // 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)) { |
| 1071 | // not our job to apply it. | 1131 | .static => unreachable, |
| 1072 | return; | 1132 | .dynamic => return, // the relocation happens at runtime |
| 1073 | } | 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 | }; |
| 1074 | const node_vaddr: u64 = switch (elf.getNode(reloc.node)) { | 1143 | const node_vaddr: u64 = switch (elf.getNode(reloc.node)) { |
| 1075 | .file => unreachable, | 1144 | .file => unreachable, |
| 1076 | .ehdr => unreachable, | 1145 | .ehdr => unreachable, |
| ... | @@ -1095,13 +1164,13 @@ const SymbolReloc = struct { | ... | @@ -1095,13 +1164,13 @@ const SymbolReloc = struct { |
| 1095 | const target_value = sym_value +% @as(u64, @bitCast(reloc.addend)); | 1164 | const target_value = sym_value +% @as(u64, @bitCast(reloc.addend)); |
| 1096 | type: switch (reloc.type) { | 1165 | type: switch (reloc.type) { |
| 1097 | .write_rela => unreachable, | 1166 | .write_rela => unreachable, |
| 1098 | .abs64 => std.mem.writeInt( | 1167 | .abs64, .dsorel64 => std.mem.writeInt( |
| 1099 | u64, | 1168 | u64, |
| 1100 | dest_slice[0..8], | 1169 | dest_slice[0..8], |
| 1101 | target_value, | 1170 | target_value, |
| 1102 | target_endian, | 1171 | target_endian, |
| 1103 | ), | 1172 | ), |
| 1104 | .abs32 => std.mem.writeInt( | 1173 | .abs32, .dsorel32 => std.mem.writeInt( |
| 1105 | u32, | 1174 | u32, |
| 1106 | dest_slice[0..4], | 1175 | dest_slice[0..4], |
| 1107 | @intCast(target_value), | 1176 | @intCast(target_value), |
| ... | @@ -1126,7 +1195,10 @@ const SymbolReloc = struct { | ... | @@ -1126,7 +1195,10 @@ const SymbolReloc = struct { |
| 1126 | target_endian, | 1195 | target_endian, |
| 1127 | ), | 1196 | ), |
| 1128 | .pltrel64 => { | 1197 | .pltrel64 => { |
| 1129 | const plt_index = elf.plt.getIndex(reloc.target) orelse continue :type .rel64; | 1198 | const plt_index = switch (reloc.target.unwrap()) { |
| | 1199 | .local => continue :type .rel64, |
| | 1200 | .global => |name| elf.plt.getIndex(name) orelse continue :type .rel64, |
| | 1201 | }; |
| 1130 | if (elf.pltEntryIsDead(plt_index)) continue :type .rel64; | 1202 | if (elf.pltEntryIsDead(plt_index)) continue :type .rel64; |
| 1131 | const plt_shndx: Section.Index, const plt_entry_size: u64 = switch (elf.ehdrField(.machine)) { | 1203 | const plt_shndx: Section.Index, const plt_entry_size: u64 = switch (elf.ehdrField(.machine)) { |
| 1132 | else => |machine| @panic(@tagName(machine)), | 1204 | else => |machine| @panic(@tagName(machine)), |
| ... | @@ -1141,7 +1213,10 @@ const SymbolReloc = struct { | ... | @@ -1141,7 +1213,10 @@ const SymbolReloc = struct { |
| 1141 | ); | 1213 | ); |
| 1142 | }, | 1214 | }, |
| 1143 | .pltrel32 => { | 1215 | .pltrel32 => { |
| 1144 | const plt_index = elf.plt.getIndex(reloc.target) orelse continue :type .rel32; | 1216 | const plt_index = switch (reloc.target.unwrap()) { |
| | 1217 | .local => continue :type .rel32, |
| | 1218 | .global => |name| elf.plt.getIndex(name) orelse continue :type .rel32, |
| | 1219 | }; |
| 1145 | if (elf.pltEntryIsDead(plt_index)) continue :type .rel32; | 1220 | if (elf.pltEntryIsDead(plt_index)) continue :type .rel32; |
| 1146 | const plt_shndx: Section.Index, const plt_entry_size: u64 = switch (elf.ehdrField(.machine)) { | 1221 | const plt_shndx: Section.Index, const plt_entry_size: u64 = switch (elf.ehdrField(.machine)) { |
| 1147 | else => |machine| @panic(@tagName(machine)), | 1222 | else => |machine| @panic(@tagName(machine)), |
| ... | @@ -1323,12 +1398,9 @@ fn ensureUnusedSymbolCapacity(elf: *Elf, len: u32, kind: enum { all_local, maybe | ... | @@ -1323,12 +1398,9 @@ fn ensureUnusedSymbolCapacity(elf: *Elf, len: u32, kind: enum { all_local, maybe |
| 1323 | try elf.symtab.ensureUnusedCapacity(gpa, len); | 1398 | try elf.symtab.ensureUnusedCapacity(gpa, len); |
| 1324 | | 1399 | |
| 1325 | // If adding locals, we may need to move one global out of the way for each local. If adding | 1400 | // If adding locals, we may need to move one global out of the way for each local. If adding |
| 1326 | // globals, they could all get demoted to STB_LOCAL, which would mean we move those N globals | 1401 | // globals, they could all get demoted to STB_LOCAL, meaning we have to move N other globals |
| 1327 | // *and* we move up to N other globals out of their way. | 1402 | // around to keep `.dynsym` compact. Either way, the maximum is N. |
| 1328 | try elf.changed_symtab_index.ensureUnusedCapacity(gpa, switch (kind) { | 1403 | try elf.changed_symtab_index.ensureUnusedCapacity(gpa, len); |
| 1329 | .all_local => len, | | |
| 1330 | .maybe_global => len * 2, | | |
| 1331 | }); | | |
| 1332 | | 1404 | |
| 1333 | { | 1405 | { |
| 1334 | // Ensure the symtab section's node is big enough | 1406 | // Ensure the symtab section's node is big enough |
| ... | @@ -1460,7 +1532,7 @@ fn addLocalSymbolAssumeCapacity(elf: *Elf, opts: AddLocalSymbolOptions) Symbol.L | ... | @@ -1460,7 +1532,7 @@ fn addLocalSymbolAssumeCapacity(elf: *Elf, opts: AddLocalSymbolOptions) Symbol.L |
| 1460 | const global_name: String(.strtab) = @enumFromInt(elf.targetLoad(&new_sym.name)); | 1532 | const global_name: String(.strtab) = @enumFromInt(elf.targetLoad(&new_sym.name)); |
| 1461 | elf.globalByName(global_name).?.symtab_index = new_index; | 1533 | elf.globalByName(global_name).?.symtab_index = new_index; |
| 1462 | | 1534 | |
| 1463 | if (target_index.ptr(elf).first_target_reloc != .none) { | 1535 | if (elf.ehdrField(.type) == .REL and target_index.ptr(elf).first_target_reloc != .none) { |
| 1464 | // This symbol's index is changing, so queue an update of relocs targeting it. | 1536 | // This symbol's index is changing, so queue an update of relocs targeting it. |
| 1465 | elf.changed_symtab_index.putAssumeCapacity(global_name, {}); | 1537 | elf.changed_symtab_index.putAssumeCapacity(global_name, {}); |
| 1466 | } | 1538 | } |
| ... | @@ -1722,16 +1794,14 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{ | ... | @@ -1722,16 +1794,14 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{ |
| 1722 | elf.moveDemotedGlobal(new_global_ptr); | 1794 | elf.moveDemotedGlobal(new_global_ptr); |
| 1723 | } | 1795 | } |
| 1724 | | 1796 | |
| 1725 | if (new_global_ptr.dynsym_index != 0 and | 1797 | switch (@"type") { |
| 1726 | opts.visibility == .DEFAULT and | 1798 | .FUNC, .GNU_IFUNC => if (elf.ehdrField(.type) != .REL and |
| 1727 | opts.shndx == .UNDEF and | 1799 | elf.classifySymbolValue(.global(opts.name.strtab)) == .dynamic) |
| 1728 | (@"type" == .FUNC or @"type" == std.elf.STT.GNU_IFUNC)) | 1800 | { |
| 1729 | { | 1801 | // This STT_FUNC symbol might be defined externally, so it needs a PLT entry. |
| 1730 | // We're adding an undefined global STT_FUNC symbol which could be resolved by another DSO. | 1802 | elf.addPltEntry(opts.name.strtab, new_global_ptr.dynsym_index); |
| 1731 | // We therefore might need a PLT entry, so let's add one now. | 1803 | }, |
| 1732 | elf.addPltEntry(opts.name.strtab, new_global_ptr.dynsym_index); | 1804 | else => {}, |
| 1733 | // TODO: we also need to emit a PLT entry if the symbol could be preempted/interposed! By | | |
| 1734 | // not doing that we're basically implementing the behavior of `-Bsymbolic-functions`. | | |
| 1735 | } | 1805 | } |
| 1736 | | 1806 | |
| 1737 | return .global(opts.name.strtab); | 1807 | return .global(opts.name.strtab); |
| ... | @@ -1843,10 +1913,10 @@ fn setGlobalSymbolValue( | ... | @@ -1843,10 +1913,10 @@ fn setGlobalSymbolValue( |
| 1843 | // If this symbol was previously undefined, it may have had a PLT entry. If so, we now need to | 1913 | // If this symbol was previously undefined, it may have had a PLT entry. If so, we now need to |
| 1844 | // 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. |
| 1845 | // This also allows the PLT entry to be reused---see `pltEntryIsDead`. | 1915 | // This also allows the PLT entry to be reused---see `pltEntryIsDead`. |
| 1846 | if (elf.plt.getIndex(.global(global_name))) |plt_index| { | 1916 | if (elf.plt.getIndex(global_name)) |plt_index| { |
| 1847 | // TODO: we might still need the PLT entry if the symbol could be preempted/interposed! See | 1917 | if (!elf.pltEntryIsDead(plt_index) and |
| 1848 | // matching comment at the end of `addGlobalSymbolAssumeCapacity`. | 1918 | elf.classifySymbolValue(.global(global_name)) != .dynamic) |
| 1849 | if (!elf.pltEntryIsDead(plt_index)) { | 1919 | { |
| 1850 | elf.shndx.rela_plt.relaDeleteOne(elf, @enumFromInt(plt_index)); | 1920 | elf.shndx.rela_plt.relaDeleteOne(elf, @enumFromInt(plt_index)); |
| 1851 | assert(elf.pltEntryIsDead(plt_index)); | 1921 | assert(elf.pltEntryIsDead(plt_index)); |
| 1852 | } | 1922 | } |
| ... | @@ -1942,61 +2012,68 @@ fn moveDemotedGlobal(elf: *Elf, global_ptr: *Symbol.Global) void { | ... | @@ -1942,61 +2012,68 @@ fn moveDemotedGlobal(elf: *Elf, global_ptr: *Symbol.Global) void { |
| 1942 | | 2012 | |
| 1943 | elf.targetStore(&shdr.info, @intFromEnum(dest_index) + 1); | 2013 | elf.targetStore(&shdr.info, @intFromEnum(dest_index) + 1); |
| 1944 | | 2014 | |
| 1945 | if (src_index == dest_index) { | 2015 | if (src_index != dest_index) { |
| 1946 | // The demoted global was already the first global, so we don't need to do any swap. | 2016 | // The demoted global was not the first global in the symtab, so we need to swap it |
| 1947 | return; | 2017 | // to its new location. |
| 1948 | } | | |
| 1949 | | 2018 | |
| 1950 | const src_sym_ptr = @field(elf.symPtr(src_index), @tagName(class)); | 2019 | const src_sym_ptr = @field(elf.symPtr(src_index), @tagName(class)); |
| 1951 | const dest_sym_ptr = @field(elf.symPtr(dest_index), @tagName(class)); | 2020 | const dest_sym_ptr = @field(elf.symPtr(dest_index), @tagName(class)); |
| 1952 | | 2021 | |
| 1953 | const this_name: String(.strtab) = @enumFromInt(elf.targetLoad(&src_sym_ptr.name)); | 2022 | const this_name: String(.strtab) = @enumFromInt(elf.targetLoad(&src_sym_ptr.name)); |
| 1954 | assert(elf.globalByName(this_name).? == global_ptr); | 2023 | assert(elf.globalByName(this_name).? == global_ptr); |
| 1955 | if (global_ptr.symtab_index.ptr(elf).first_target_reloc != .none) { | | |
| 1956 | // This symbol's index is changing, so queue an update of relocs targeting it. | | |
| 1957 | elf.changed_symtab_index.putAssumeCapacity(this_name, {}); | | |
| 1958 | } | | |
| 1959 | | 2024 | |
| 1960 | const other_name: String(.strtab) = @enumFromInt(elf.targetLoad(&dest_sym_ptr.name)); | 2025 | const other_name: String(.strtab) = @enumFromInt(elf.targetLoad(&dest_sym_ptr.name)); |
| 1961 | const other_global_ptr = elf.globalByName(other_name).?; | 2026 | const other_global_ptr = elf.globalByName(other_name).?; |
| 1962 | assert(other_global_ptr.symtab_index == dest_index); | 2027 | assert(other_global_ptr.symtab_index == dest_index); |
| 1963 | if (other_global_ptr.symtab_index.ptr(elf).first_target_reloc != .none) { | 2028 | |
| 1964 | // This other symbol's index is changing, so queue an update of relocs targeting it. | 2029 | // First swap the symtab entries... |
| 1965 | elf.changed_symtab_index.putAssumeCapacity(other_name, {}); | 2030 | std.mem.swap(class.ElfN().Sym, src_sym_ptr, dest_sym_ptr); |
| | 2031 | // ...then the `elf.symtab` metadata... |
| | 2032 | std.mem.swap(Symbol, src_index.ptr(elf), dest_index.ptr(elf)); |
| | 2033 | // ...then update the `elf.globals` tracking. |
| | 2034 | global_ptr.symtab_index = dest_index; |
| | 2035 | other_global_ptr.symtab_index = src_index; |
| 1966 | } | 2036 | } |
| 1967 | | 2037 | |
| 1968 | // First swap the symtab entries... | 2038 | // We also need to get rid of the dynsym entry if there is one. To keep dynsym compact, |
| 1969 | std.mem.swap(class.ElfN().Sym, src_sym_ptr, dest_sym_ptr); | 2039 | // we'll move another symbol into its place just like we did above. |
| 1970 | // ...then the `elf.symtab` metadata... | | |
| 1971 | std.mem.swap(Symbol, src_index.ptr(elf), dest_index.ptr(elf)); | | |
| 1972 | // ...then update the `elf.globals` tracking. | | |
| 1973 | global_ptr.symtab_index = dest_index; | | |
| 1974 | other_global_ptr.symtab_index = src_index; | | |
| 1975 | | | |
| 1976 | // We also need to get rid of the dynsym entry if there is one. For simplicity, just | | |
| 1977 | // replace it with a dummy entry which will never be used and will not cause problems. | | |
| 1978 | // TODO: we should have a free-list of dynsym slots so that other symbols can go here. | | |
| 1979 | // TODO: it would also be best to just avoid having gaps in the dynsym altogether. | | |
| 1980 | if (global_ptr.dynsym_index != 0) { | 2040 | if (global_ptr.dynsym_index != 0) { |
| 1981 | const dynsym = @field(elf.dynsymPtr(global_ptr.dynsym_index), @tagName(class)); | 2041 | const dynsym_shdr = @field(elf.shdrPtr(elf.shndx.dynsym), @tagName(class)); |
| 1982 | dynsym.* = .{ | 2042 | |
| 1983 | .name = @intFromEnum(String(.dynstr).empty), | 2043 | const ent_size = @sizeOf(class.ElfN().Sym); |
| 1984 | .value = 0, | 2044 | assert(elf.targetLoad(&dynsym_shdr.entsize) == ent_size); |
| 1985 | .size = 0, | 2045 | |
| 1986 | .info = .{ | 2046 | // We're going to decrease the size of `.dynsym`, thereby removing its last index. |
| 1987 | .type = .NOTYPE, | 2047 | const old_size = elf.targetLoad(&dynsym_shdr.size); |
| 1988 | // STB_WEAK is important: we mustn't cause a dynamic linker error if the | 2048 | const new_size = old_size - ent_size; |
| 1989 | // symbol can't be resolved. | 2049 | const remove_dynsym_index: u32 = @intCast(@divExact(new_size, ent_size)); |
| 1990 | .bind = .WEAK, | 2050 | |
| 1991 | }, | 2051 | const free_dynsym_index = global_ptr.dynsym_index; |
| 1992 | // SHN_UNDEF is important: we mustn't define this symbol for other DSOs. | | |
| 1993 | .shndx = std.elf.SHN_UNDEF, | | |
| 1994 | .other = .{ .visibility = .DEFAULT }, | | |
| 1995 | }; | | |
| 1996 | if (elf.targetEndian() != native_endian) { | | |
| 1997 | std.mem.byteSwapAllFields(class.ElfN().Sym, dynsym); | | |
| 1998 | } | | |
| 1999 | global_ptr.dynsym_index = 0; | 2052 | global_ptr.dynsym_index = 0; |
| | 2053 | |
| | 2054 | if (free_dynsym_index != remove_dynsym_index) { |
| | 2055 | // The demoted global wasn't the last entry, so move whatever entry we just |
| | 2056 | // truncated out of dynsym into its place. |
| | 2057 | |
| | 2058 | const src_dynsym_ptr = @field(elf.dynsymPtr(remove_dynsym_index), @tagName(class)); |
| | 2059 | const dest_dynsym_ptr = @field(elf.dynsymPtr(free_dynsym_index), @tagName(class)); |
| | 2060 | |
| | 2061 | const moved_name_dynstr: String(.dynstr) = @enumFromInt(elf.targetLoad(&src_dynsym_ptr.name)); |
| | 2062 | const moved_name = elf.stringExisting(.strtab, moved_name_dynstr.slice(elf)); |
| | 2063 | const moved_global_ptr = elf.globalByName(moved_name).?; |
| | 2064 | |
| | 2065 | dest_dynsym_ptr.* = src_dynsym_ptr.*; |
| | 2066 | |
| | 2067 | assert(moved_global_ptr.dynsym_index == remove_dynsym_index); |
| | 2068 | moved_global_ptr.dynsym_index = free_dynsym_index; |
| | 2069 | |
| | 2070 | // Since that symbol's dynsym index has changed, we'll have to update any |
| | 2071 | // relocation entries targeting it. |
| | 2072 | elf.changed_symtab_index.putAssumeCapacity(moved_name, {}); |
| | 2073 | } |
| | 2074 | |
| | 2075 | // Now that we've given that symbol a new home, actually decrease the section size. |
| | 2076 | elf.targetStore(&dynsym_shdr.size, new_size); |
| 2000 | } | 2077 | } |
| 2001 | }, | 2078 | }, |
| 2002 | } | 2079 | } |
| ... | @@ -2033,13 +2110,13 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void | ... | @@ -2033,13 +2110,13 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void |
| 2033 | | 2110 | |
| 2034 | if (plt_index < elf.plt.count()) { | 2111 | if (plt_index < elf.plt.count()) { |
| 2035 | // We reused a free entry, so we're already done! | 2112 | // We reused a free entry, so we're already done! |
| 2036 | elf.plt.setKey(plt_index, .global(global_name)); | 2113 | elf.plt.setKey(plt_index, global_name); |
| 2037 | return; | 2114 | return; |
| 2038 | } | 2115 | } |
| 2039 | | 2116 | |
| 2040 | // We added a new entry, so we now need to extend the PLT sections. | 2117 | // We added a new entry, so we now need to extend the PLT sections. |
| 2041 | assert(plt_index == elf.plt.count()); | 2118 | assert(plt_index == elf.plt.count()); |
| 2042 | elf.plt.putAssumeCapacityNoClobber(.global(global_name), {}); | 2119 | elf.plt.putAssumeCapacityNoClobber(global_name, {}); |
| 2043 | | 2120 | |
| 2044 | switch (elf.ehdrField(.machine)) { | 2121 | switch (elf.ehdrField(.machine)) { |
| 2045 | else => |machine| @panic(@tagName(machine)), | 2122 | else => |machine| @panic(@tagName(machine)), |
| ... | @@ -2315,8 +2392,8 @@ const Symbol = struct { | ... | @@ -2315,8 +2392,8 @@ const Symbol = struct { |
| 2315 | } | 2392 | } |
| 2316 | } | 2393 | } |
| 2317 | | 2394 | |
| 2318 | /// 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 |
| 2319 | /// relocation entry, if it has one. | 2396 | /// relocation entry, either deletes it or converts it to R_*_RELATIVE as required. |
| 2320 | /// | 2397 | /// |
| 2321 | /// Asserts we are creating a DSO. | 2398 | /// Asserts we are creating a DSO. |
| 2322 | fn deleteDynamicTargetRelocs(sym_id: Symbol.Id, elf: *Elf) void { | 2399 | fn deleteDynamicTargetRelocs(sym_id: Symbol.Id, elf: *Elf) void { |
| ... | @@ -2329,6 +2406,45 @@ const Symbol = struct { | ... | @@ -2329,6 +2406,45 @@ const Symbol = struct { |
| 2329 | reloc.deleteOutputRel(elf); | 2406 | reloc.deleteOutputRel(elf); |
| 2330 | ri = reloc.next; | 2407 | ri = reloc.next; |
| 2331 | } | 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 | } |
| 2332 | } | 2448 | } |
| 2333 | | 2449 | |
| 2334 | /// 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 |
| ... | @@ -2357,6 +2473,78 @@ fn globalByName(elf: *const Elf, name: String(.strtab)) ?*Symbol.Global { | ... | @@ -2357,6 +2473,78 @@ fn globalByName(elf: *const Elf, name: String(.strtab)) ?*Symbol.Global { |
| 2357 | return null; | 2473 | return null; |
| 2358 | } | 2474 | } |
| 2359 | | 2475 | |
| | 2476 | fn classifySymbolValue(elf: *Elf, sym: Symbol.Id) enum { |
| | 2477 | /// This symbol's value is guaranteed to equal `sym.value(elf)`. |
| | 2478 | static, |
| | 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, |
| | 2493 | }; |
| | 2494 | |
| | 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 | } |
| | 2499 | |
| | 2500 | const shndx: Section.Index, const visibility: std.elf.STV = switch (elf.symPtr(sym.index(elf))) { |
| | 2501 | inline else => |sym_ptr| .{ |
| | 2502 | .fromSection(elf.targetLoad(&sym_ptr.shndx)), |
| | 2503 | elf.targetLoad(&sym_ptr.other).visibility, |
| | 2504 | }, |
| | 2505 | }; |
| | 2506 | |
| | 2507 | switch (sym.unwrap()) { |
| | 2508 | .local => { |
| | 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 | }, |
| | 2520 | } |
| | 2521 | |
| | 2522 | return switch (shndx) { |
| | 2523 | .UNDEF => switch (visibility) { |
| | 2524 | .DEFAULT => if (comp.config.link_mode == .static and comp.config.output_mode == .Exe) { |
| | 2525 | assert(comp.config.pie); // non-PIE static exe should not have a `.dynamic` section |
| | 2526 | // This is a static PIE---the only dynamic relocations are `R_*_RELATIVE`. |
| | 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 | }, |
| | 2545 | }; |
| | 2546 | } |
| | 2547 | |
| 2360 | pub fn symbolForAtom(elf: *Elf, atom: link.File.AtomId) link.File.SymbolId { | 2548 | pub fn symbolForAtom(elf: *Elf, atom: link.File.AtomId) link.File.SymbolId { |
| 2361 | const lsi: Symbol.LocalIndex = switch (elf.getNode(Node.fromAtom(atom))) { | 2549 | const lsi: Symbol.LocalIndex = switch (elf.getNode(Node.fromAtom(atom))) { |
| 2362 | .file, | 2550 | .file, |
| ... | @@ -2588,6 +2776,11 @@ fn string(elf: *Elf, comptime section: StringSection, key: []const u8) Error!Str | ... | @@ -2588,6 +2776,11 @@ fn string(elf: *Elf, comptime section: StringSection, key: []const u8) Error!Str |
| 2588 | const st: *StringTable = &@field(elf, @tagName(section)); | 2776 | const st: *StringTable = &@field(elf, @tagName(section)); |
| 2589 | return @enumFromInt(try st.get(elf, section.shndx(elf), key)); | 2777 | return @enumFromInt(try st.get(elf, section.shndx(elf), key)); |
| 2590 | } | 2778 | } |
| | 2779 | /// Like `string`, but asserts that the string is already in `section`. |
| | 2780 | fn stringExisting(elf: *Elf, comptime section: StringSection, key: []const u8) String(section) { |
| | 2781 | const st: *StringTable = &@field(elf, @tagName(section)); |
| | 2782 | return @enumFromInt(st.getExisting(elf, section.shndx(elf), key)); |
| | 2783 | } |
| 2591 | | 2784 | |
| 2592 | const StringTable = struct { | 2785 | const StringTable = struct { |
| 2593 | map: std.HashMapUnmanaged(u32, void, StringTable.Context, std.hash_map.default_max_load_percentage), | 2786 | map: std.HashMapUnmanaged(u32, void, StringTable.Context, std.hash_map.default_max_load_percentage), |
| ... | @@ -2618,7 +2811,14 @@ const StringTable = struct { | ... | @@ -2618,7 +2811,14 @@ const StringTable = struct { |
| 2618 | } | 2811 | } |
| 2619 | }; | 2812 | }; |
| 2620 | | 2813 | |
| 2621 | pub fn get(st: *StringTable, elf: *Elf, shndx: Section.Index, key: []const u8) Error!u32 { | 2814 | fn getExisting(st: *StringTable, elf: *Elf, shndx: Section.Index, key: []const u8) u32 { |
| | 2815 | if (key.len == 0) return 0; |
| | 2816 | const slice_const = shndx.get(elf).ni.sliceConst(&elf.mf); |
| | 2817 | const adapter: StringTable.Adapter = .{ .slice = slice_const }; |
| | 2818 | return st.map.getKeyAdapted(key, adapter).?; |
| | 2819 | } |
| | 2820 | |
| | 2821 | fn get(st: *StringTable, elf: *Elf, shndx: Section.Index, key: []const u8) Error!u32 { |
| 2622 | // If we are in `initHeaders` the strtab might not be initalized yet, so we need to special | 2822 | // If we are in `initHeaders` the strtab might not be initalized yet, so we need to special |
| 2623 | // case the empty string. | 2823 | // case the empty string. |
| 2624 | if (key.len == 0) return 0; | 2824 | if (key.len == 0) return 0; |
| ... | @@ -2884,51 +3084,104 @@ fn initHeaders( | ... | @@ -2884,51 +3084,104 @@ fn initHeaders( |
| 2884 | .@"64" => .@"8", | 3084 | .@"64" => .@"8", |
| 2885 | }; | 3085 | }; |
| 2886 | | 3086 | |
| 2887 | const shnum: u32 = 1; | | |
| 2888 | var phnum: u32 = 0; | | |
| 2889 | const phdr_phndx = phnum; | | |
| 2890 | phnum += 1; | | |
| 2891 | const interp_phndx = if (maybe_interp) |_| phndx: { | | |
| 2892 | defer phnum += 1; | | |
| 2893 | break :phndx phnum; | | |
| 2894 | } else undefined; | | |
| 2895 | const rodata_phndx = phnum; | | |
| 2896 | phnum += 1; | | |
| 2897 | const text_phndx = phnum; | | |
| 2898 | phnum += 1; | | |
| 2899 | const data_phndx = phnum; | | |
| 2900 | phnum += 1; | | |
| 2901 | const tls_phndx = if (comp.config.any_non_single_threaded) phndx: { | | |
| 2902 | defer phnum += 1; | | |
| 2903 | break :phndx phnum; | | |
| 2904 | } else undefined; | | |
| 2905 | const dynamic_phndx = if (have_dynamic_section) phndx: { | | |
| 2906 | defer phnum += 1; | | |
| 2907 | break :phndx phnum; | | |
| 2908 | } else undefined; | | |
| 2909 | const relro_phndx = phnum; | | |
| 2910 | phnum += 1; | | |
| 2911 | | | |
| 2912 | const init_plt_size: std.elf.Xword, const plt_align: std.mem.Alignment, const plt_sec = | 3087 | const init_plt_size: std.elf.Xword, const plt_align: std.mem.Alignment, const plt_sec = |
| 2913 | switch (machine) { | 3088 | switch (machine) { |
| 2914 | else => @panic(@tagName(machine)), | 3089 | else => @panic(@tagName(machine)), |
| 2915 | .X86_64 => .{ 16, .@"16", true }, | 3090 | .X86_64 => .{ 16, .@"16", true }, |
| 2916 | .LOONGARCH => .{ 32, .@"4", false }, | 3091 | .LOONGARCH => .{ 32, .@"4", false }, |
| 2917 | }; | 3092 | }; |
| 2918 | const expected_nodes_len = expected_nodes_len: switch (@"type") { | 3093 | |
| 2919 | .NONE, .CORE, _ => unreachable, | 3094 | const shnum: u32 = shnum: { |
| 2920 | .REL => { | 3095 | var shnum: u32 = 1; // reserved ("null") shdr |
| 2921 | // Each phdr is actually going to be an shdr. | 3096 | shnum += 1; // .symtab |
| 2922 | defer phnum = 0; | 3097 | shnum += 1; // .shstrtab |
| 2923 | break :expected_nodes_len 5 + phnum; | 3098 | shnum += 1; // .strtab |
| 2924 | }, | 3099 | shnum += @intFromBool(maybe_interp != null); // .interp |
| 2925 | .EXEC, .DYN => break :expected_nodes_len 9 + | 3100 | shnum += 1; // .rodata |
| 2926 | phnum * 2 - 1 + // each phdr also has a matching shdr, except for the PT_PHDR phdr | 3101 | shnum += 1; // .text |
| 2927 | @as(usize, 4) * @intFromBool(have_dynamic_section) + // .dynstr, .dynsym, .rela.dyn, .rela.plt | 3102 | shnum += 1; // .data |
| 2928 | @intFromBool(plt_sec), | 3103 | shnum += @intFromBool(comp.config.any_non_single_threaded); // .tdata |
| | 3104 | shnum += 1; // .data.rel.ro |
| | 3105 | if (have_dynamic_section) { |
| | 3106 | shnum += 1; // .dynamic |
| | 3107 | shnum += 1; // .dynstr |
| | 3108 | shnum += 1; // .dynsym |
| | 3109 | shnum += 1; // .rela.dyn |
| | 3110 | shnum += 1; // .rela.plt |
| | 3111 | } |
| | 3112 | if (@"type" != .REL) { |
| | 3113 | shnum += 1; // .got |
| | 3114 | shnum += 1; // .got.plt |
| | 3115 | shnum += 1; // .plt |
| | 3116 | shnum += @intFromBool(plt_sec); // .plt_sec |
| | 3117 | } |
| | 3118 | break :shnum shnum; |
| | 3119 | }; |
| | 3120 | |
| | 3121 | const phndx: struct { |
| | 3122 | phdr: u32, |
| | 3123 | interp: u32, |
| | 3124 | rodata: u32, |
| | 3125 | text: u32, |
| | 3126 | data: u32, |
| | 3127 | tls: u32, |
| | 3128 | dynamic: u32, |
| | 3129 | relro: u32, |
| | 3130 | gnu_stack: u32, |
| | 3131 | }, const phnum: u32 = ph: { |
| | 3132 | switch (@"type") { |
| | 3133 | .NONE, .CORE, _ => unreachable, |
| | 3134 | .REL => break :ph .{ undefined, 0 }, |
| | 3135 | .EXEC, .DYN => {}, |
| | 3136 | } |
| | 3137 | var phnum: u32 = 0; |
| | 3138 | break :ph .{ .{ |
| | 3139 | .phdr = phndx: { |
| | 3140 | defer phnum += 1; |
| | 3141 | break :phndx phnum; |
| | 3142 | }, |
| | 3143 | .interp = if (maybe_interp) |_| phndx: { |
| | 3144 | defer phnum += 1; |
| | 3145 | break :phndx phnum; |
| | 3146 | } else undefined, |
| | 3147 | .rodata = phndx: { |
| | 3148 | defer phnum += 1; |
| | 3149 | break :phndx phnum; |
| | 3150 | }, |
| | 3151 | .text = phndx: { |
| | 3152 | defer phnum += 1; |
| | 3153 | break :phndx phnum; |
| | 3154 | }, |
| | 3155 | .data = phndx: { |
| | 3156 | defer phnum += 1; |
| | 3157 | break :phndx phnum; |
| | 3158 | }, |
| | 3159 | .tls = if (comp.config.any_non_single_threaded) phndx: { |
| | 3160 | defer phnum += 1; |
| | 3161 | break :phndx phnum; |
| | 3162 | } else undefined, |
| | 3163 | .dynamic = if (have_dynamic_section) phndx: { |
| | 3164 | defer phnum += 1; |
| | 3165 | break :phndx phnum; |
| | 3166 | } else undefined, |
| | 3167 | .relro = phndx: { |
| | 3168 | defer phnum += 1; |
| | 3169 | break :phndx phnum; |
| | 3170 | }, |
| | 3171 | .gnu_stack = phndx: { |
| | 3172 | defer phnum += 1; |
| | 3173 | break :phndx phnum; |
| | 3174 | }, |
| | 3175 | }, phnum }; |
| 2929 | }; | 3176 | }; |
| | 3177 | |
| | 3178 | const expected_nodes_len = 3 + // `.file`, `.ehdr`, and `.shdr` nodes |
| | 3179 | (shnum - 1) + // -1 because the null shdr does not have a `.section` node |
| | 3180 | (phnum -| 1); // -1 because the GNU_STACK phdr does not have a `.segment` node |
| | 3181 | |
| 2930 | try elf.nodes.ensureTotalCapacity(gpa, expected_nodes_len); | 3182 | try elf.nodes.ensureTotalCapacity(gpa, expected_nodes_len); |
| 2931 | try elf.shdrs.ensureTotalCapacity(gpa, shnum); | 3183 | try elf.shdrs.ensureTotalCapacity(gpa, shnum); |
| | 3184 | try elf.section_by_name.ensureUnusedCapacity(gpa, shnum); |
| 2932 | try elf.phdrs.resize(gpa, phnum); | 3185 | try elf.phdrs.resize(gpa, phnum); |
| 2933 | try elf.symtab.ensureTotalCapacity(gpa, 1); | 3186 | try elf.symtab.ensureTotalCapacity(gpa, 1); |
| 2934 | elf.nodes.appendAssumeCapacity(.file); | 3187 | elf.nodes.appendAssumeCapacity(.file); |
| ... | @@ -2980,7 +3233,7 @@ fn initHeaders( | ... | @@ -2980,7 +3233,7 @@ fn initHeaders( |
| 2980 | ehdr.phentsize = @sizeOf(ElfN.Phdr); | 3233 | ehdr.phentsize = @sizeOf(ElfN.Phdr); |
| 2981 | ehdr.phnum = @min(phnum, std.elf.PN_XNUM); | 3234 | ehdr.phnum = @min(phnum, std.elf.PN_XNUM); |
| 2982 | ehdr.shentsize = @sizeOf(ElfN.Shdr); | 3235 | ehdr.shentsize = @sizeOf(ElfN.Shdr); |
| 2983 | ehdr.shnum = if (shnum < std.elf.SHN_LORESERVE) shnum else 0; | 3236 | ehdr.shnum = 1; // Only the null shdr initially---will be incremented by `addSection` |
| 2984 | ehdr.shstrndx = std.elf.SHN_UNDEF; | 3237 | ehdr.shstrndx = std.elf.SHN_UNDEF; |
| 2985 | if (elf.targetEndian() != native_endian) std.mem.byteSwapAllFields(ElfN.Ehdr, ehdr); | 3238 | if (elf.targetEndian() != native_endian) std.mem.byteSwapAllFields(ElfN.Ehdr, ehdr); |
| 2986 | }, | 3239 | }, |
| ... | @@ -3000,8 +3253,8 @@ fn initHeaders( | ... | @@ -3000,8 +3253,8 @@ fn initHeaders( |
| 3000 | .moved = true, | 3253 | .moved = true, |
| 3001 | .bubbles_moved = false, | 3254 | .bubbles_moved = false, |
| 3002 | })); | 3255 | })); |
| 3003 | elf.nodes.appendAssumeCapacity(.{ .segment = rodata_phndx }); | 3256 | elf.nodes.appendAssumeCapacity(.{ .segment = phndx.rodata }); |
| 3004 | elf.phdrs.items[rodata_phndx] = elf.ni.rodata; | 3257 | elf.phdrs.items[phndx.rodata] = elf.ni.rodata; |
| 3005 | | 3258 | |
| 3006 | assert(elf.ni.phdr == try elf.mf.addOnlyChildNode(gpa, elf.ni.rodata, .{ | 3259 | assert(elf.ni.phdr == try elf.mf.addOnlyChildNode(gpa, elf.ni.rodata, .{ |
| 3007 | .size = elf.ehdrField(.phentsize) * elf.ehdrField(.phnum), | 3260 | .size = elf.ehdrField(.phentsize) * elf.ehdrField(.phnum), |
| ... | @@ -3010,32 +3263,34 @@ fn initHeaders( | ... | @@ -3010,32 +3263,34 @@ fn initHeaders( |
| 3010 | .resized = true, | 3263 | .resized = true, |
| 3011 | .bubbles_moved = false, | 3264 | .bubbles_moved = false, |
| 3012 | })); | 3265 | })); |
| 3013 | elf.nodes.appendAssumeCapacity(.{ .segment = phdr_phndx }); | 3266 | elf.nodes.appendAssumeCapacity(.{ .segment = phndx.phdr }); |
| 3014 | elf.phdrs.items[phdr_phndx] = elf.ni.phdr; | 3267 | elf.phdrs.items[phndx.phdr] = elf.ni.phdr; |
| 3015 | | 3268 | |
| 3016 | assert(elf.ni.text == try elf.mf.addLastChildNode(gpa, elf.ni.file, .{ | 3269 | assert(elf.ni.text == try elf.mf.addLastChildNode(gpa, elf.ni.file, .{ |
| 3017 | .alignment = elf.mf.flags.block_size, | 3270 | .alignment = elf.mf.flags.block_size, |
| 3018 | .moved = true, | 3271 | .moved = true, |
| 3019 | .bubbles_moved = false, | 3272 | .bubbles_moved = false, |
| 3020 | })); | 3273 | })); |
| 3021 | elf.nodes.appendAssumeCapacity(.{ .segment = text_phndx }); | 3274 | elf.nodes.appendAssumeCapacity(.{ .segment = phndx.text }); |
| 3022 | elf.phdrs.items[text_phndx] = elf.ni.text; | 3275 | elf.phdrs.items[phndx.text] = elf.ni.text; |
| 3023 | | 3276 | |
| 3024 | assert(elf.ni.data == try elf.mf.addLastChildNode(gpa, elf.ni.file, .{ | 3277 | assert(elf.ni.data == try elf.mf.addLastChildNode(gpa, elf.ni.file, .{ |
| 3025 | .alignment = elf.mf.flags.block_size, | 3278 | .alignment = elf.mf.flags.block_size, |
| 3026 | .moved = true, | 3279 | .moved = true, |
| 3027 | .bubbles_moved = false, | 3280 | .bubbles_moved = false, |
| 3028 | })); | 3281 | })); |
| 3029 | elf.nodes.appendAssumeCapacity(.{ .segment = data_phndx }); | 3282 | elf.nodes.appendAssumeCapacity(.{ .segment = phndx.data }); |
| 3030 | elf.phdrs.items[data_phndx] = elf.ni.data; | 3283 | elf.phdrs.items[phndx.data] = elf.ni.data; |
| 3031 | | 3284 | |
| 3032 | assert(elf.ni.data_rel_ro == try elf.mf.addOnlyChildNode(gpa, elf.ni.data, .{ | 3285 | assert(elf.ni.data_rel_ro == try elf.mf.addOnlyChildNode(gpa, elf.ni.data, .{ |
| 3033 | .alignment = elf.mf.flags.block_size, | 3286 | .alignment = elf.mf.flags.block_size, |
| 3034 | .moved = true, | 3287 | .moved = true, |
| 3035 | .bubbles_moved = false, | 3288 | .bubbles_moved = false, |
| 3036 | })); | 3289 | })); |
| 3037 | elf.nodes.appendAssumeCapacity(.{ .segment = relro_phndx }); | 3290 | elf.nodes.appendAssumeCapacity(.{ .segment = phndx.relro }); |
| 3038 | elf.phdrs.items[relro_phndx] = elf.ni.data_rel_ro; | 3291 | elf.phdrs.items[phndx.relro] = elf.ni.data_rel_ro; |
| | 3292 | |
| | 3293 | elf.phdrs.items[phndx.gnu_stack] = .none; |
| 3039 | | 3294 | |
| 3040 | break :ph_vaddr switch (elf.ehdrField(.type)) { | 3295 | break :ph_vaddr switch (elf.ehdrField(.type)) { |
| 3041 | .NONE, .CORE, _ => unreachable, | 3296 | .NONE, .CORE, _ => unreachable, |
| ... | @@ -3058,7 +3313,7 @@ fn initHeaders( | ... | @@ -3058,7 +3313,7 @@ fn initHeaders( |
| 3058 | | 3313 | |
| 3059 | if (@"type" != .REL) { | 3314 | if (@"type" != .REL) { |
| 3060 | const phdr: []ElfN.Phdr = @ptrCast(@alignCast(elf.ni.phdr.slice(&elf.mf))); | 3315 | const phdr: []ElfN.Phdr = @ptrCast(@alignCast(elf.ni.phdr.slice(&elf.mf))); |
| 3061 | const ph_phdr = &phdr[phdr_phndx]; | 3316 | const ph_phdr = &phdr[phndx.phdr]; |
| 3062 | ph_phdr.* = .{ | 3317 | ph_phdr.* = .{ |
| 3063 | .type = .PHDR, | 3318 | .type = .PHDR, |
| 3064 | .offset = 0, | 3319 | .offset = 0, |
| ... | @@ -3072,7 +3327,7 @@ fn initHeaders( | ... | @@ -3072,7 +3327,7 @@ fn initHeaders( |
| 3072 | if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_phdr); | 3327 | if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_phdr); |
| 3073 | | 3328 | |
| 3074 | if (maybe_interp) |_| { | 3329 | if (maybe_interp) |_| { |
| 3075 | const ph_interp = &phdr[interp_phndx]; | 3330 | const ph_interp = &phdr[phndx.interp]; |
| 3076 | ph_interp.* = .{ | 3331 | ph_interp.* = .{ |
| 3077 | .type = .INTERP, | 3332 | .type = .INTERP, |
| 3078 | .offset = 0, | 3333 | .offset = 0, |
| ... | @@ -3087,7 +3342,7 @@ fn initHeaders( | ... | @@ -3087,7 +3342,7 @@ fn initHeaders( |
| 3087 | } | 3342 | } |
| 3088 | | 3343 | |
| 3089 | _, const rodata_size = elf.ni.rodata.location(&elf.mf).resolve(&elf.mf); | 3344 | _, const rodata_size = elf.ni.rodata.location(&elf.mf).resolve(&elf.mf); |
| 3090 | const ph_rodata = &phdr[rodata_phndx]; | 3345 | const ph_rodata = &phdr[phndx.rodata]; |
| 3091 | ph_rodata.* = .{ | 3346 | ph_rodata.* = .{ |
| 3092 | .type = if (rodata_size == 0) .NULL else .LOAD, | 3347 | .type = if (rodata_size == 0) .NULL else .LOAD, |
| 3093 | .offset = 0, | 3348 | .offset = 0, |
| ... | @@ -3102,7 +3357,7 @@ fn initHeaders( | ... | @@ -3102,7 +3357,7 @@ fn initHeaders( |
| 3102 | ph_vaddr += @intCast(rodata_size); | 3357 | ph_vaddr += @intCast(rodata_size); |
| 3103 | | 3358 | |
| 3104 | _, const text_size = elf.ni.text.location(&elf.mf).resolve(&elf.mf); | 3359 | _, const text_size = elf.ni.text.location(&elf.mf).resolve(&elf.mf); |
| 3105 | const ph_text = &phdr[text_phndx]; | 3360 | const ph_text = &phdr[phndx.text]; |
| 3106 | ph_text.* = .{ | 3361 | ph_text.* = .{ |
| 3107 | .type = if (text_size == 0) .NULL else .LOAD, | 3362 | .type = if (text_size == 0) .NULL else .LOAD, |
| 3108 | .offset = 0, | 3363 | .offset = 0, |
| ... | @@ -3117,7 +3372,7 @@ fn initHeaders( | ... | @@ -3117,7 +3372,7 @@ fn initHeaders( |
| 3117 | ph_vaddr += @intCast(text_size); | 3372 | ph_vaddr += @intCast(text_size); |
| 3118 | | 3373 | |
| 3119 | _, const data_size = elf.ni.data.location(&elf.mf).resolve(&elf.mf); | 3374 | _, const data_size = elf.ni.data.location(&elf.mf).resolve(&elf.mf); |
| 3120 | const ph_data = &phdr[data_phndx]; | 3375 | const ph_data = &phdr[phndx.data]; |
| 3121 | ph_data.* = .{ | 3376 | ph_data.* = .{ |
| 3122 | .type = if (data_size == 0) .NULL else .LOAD, | 3377 | .type = if (data_size == 0) .NULL else .LOAD, |
| 3123 | .offset = 0, | 3378 | .offset = 0, |
| ... | @@ -3132,7 +3387,7 @@ fn initHeaders( | ... | @@ -3132,7 +3387,7 @@ fn initHeaders( |
| 3132 | ph_vaddr += @intCast(data_size); | 3387 | ph_vaddr += @intCast(data_size); |
| 3133 | | 3388 | |
| 3134 | if (comp.config.any_non_single_threaded) { | 3389 | if (comp.config.any_non_single_threaded) { |
| 3135 | const ph_tls = &phdr[tls_phndx]; | 3390 | const ph_tls = &phdr[phndx.tls]; |
| 3136 | ph_tls.* = .{ | 3391 | ph_tls.* = .{ |
| 3137 | .type = .TLS, | 3392 | .type = .TLS, |
| 3138 | .offset = 0, | 3393 | .offset = 0, |
| ... | @@ -3147,7 +3402,7 @@ fn initHeaders( | ... | @@ -3147,7 +3402,7 @@ fn initHeaders( |
| 3147 | } | 3402 | } |
| 3148 | | 3403 | |
| 3149 | if (have_dynamic_section) { | 3404 | if (have_dynamic_section) { |
| 3150 | const ph_dynamic = &phdr[dynamic_phndx]; | 3405 | const ph_dynamic = &phdr[phndx.dynamic]; |
| 3151 | ph_dynamic.* = .{ | 3406 | ph_dynamic.* = .{ |
| 3152 | .type = .DYNAMIC, | 3407 | .type = .DYNAMIC, |
| 3153 | .offset = 0, | 3408 | .offset = 0, |
| ... | @@ -3161,7 +3416,7 @@ fn initHeaders( | ... | @@ -3161,7 +3416,7 @@ fn initHeaders( |
| 3161 | if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_dynamic); | 3416 | if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_dynamic); |
| 3162 | } | 3417 | } |
| 3163 | | 3418 | |
| 3164 | const ph_relro = &phdr[relro_phndx]; | 3419 | const ph_relro = &phdr[phndx.relro]; |
| 3165 | ph_relro.* = .{ | 3420 | ph_relro.* = .{ |
| 3166 | .type = .GNU_RELRO, | 3421 | .type = .GNU_RELRO, |
| 3167 | .offset = 0, | 3422 | .offset = 0, |
| ... | @@ -3173,6 +3428,19 @@ fn initHeaders( | ... | @@ -3173,6 +3428,19 @@ fn initHeaders( |
| 3173 | .@"align" = @intCast(elf.mf.flags.block_size.toByteUnits()), | 3428 | .@"align" = @intCast(elf.mf.flags.block_size.toByteUnits()), |
| 3174 | }; | 3429 | }; |
| 3175 | if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_relro); | 3430 | if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_relro); |
| | 3431 | |
| | 3432 | const ph_gnu_stack = &phdr[phndx.gnu_stack]; |
| | 3433 | ph_gnu_stack.* = .{ |
| | 3434 | .type = .GNU_STACK, |
| | 3435 | .offset = 0, |
| | 3436 | .vaddr = 0, |
| | 3437 | .paddr = 0, |
| | 3438 | .filesz = 0, |
| | 3439 | .memsz = @intCast(elf.options.stack_size orelse 0), |
| | 3440 | .flags = .{ .R = true, .W = true }, |
| | 3441 | .@"align" = 1, |
| | 3442 | }; |
| | 3443 | if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_gnu_stack); |
| 3176 | } | 3444 | } |
| 3177 | | 3445 | |
| 3178 | const sh_undef: *ElfN.Shdr = @ptrCast(@alignCast(elf.ni.shdr.slice(&elf.mf))); | 3446 | const sh_undef: *ElfN.Shdr = @ptrCast(@alignCast(elf.ni.shdr.slice(&elf.mf))); |
| ... | @@ -3312,8 +3580,8 @@ fn initHeaders( | ... | @@ -3312,8 +3580,8 @@ fn initHeaders( |
| 3312 | .resized = true, | 3580 | .resized = true, |
| 3313 | .bubbles_moved = false, | 3581 | .bubbles_moved = false, |
| 3314 | }); | 3582 | }); |
| 3315 | elf.nodes.appendAssumeCapacity(.{ .segment = interp_phndx }); | 3583 | elf.nodes.appendAssumeCapacity(.{ .segment = phndx.interp }); |
| 3316 | elf.phdrs.items[interp_phndx] = interp_ni; | 3584 | elf.phdrs.items[phndx.interp] = interp_ni; |
| 3317 | | 3585 | |
| 3318 | const sec_interp_shndx = try elf.addSection(interp_ni, .{ | 3586 | const sec_interp_shndx = try elf.addSection(interp_ni, .{ |
| 3319 | .name = ".interp", | 3587 | .name = ".interp", |
| ... | @@ -3331,8 +3599,8 @@ fn initHeaders( | ... | @@ -3331,8 +3599,8 @@ fn initHeaders( |
| 3331 | .moved = true, | 3599 | .moved = true, |
| 3332 | .bubbles_moved = false, | 3600 | .bubbles_moved = false, |
| 3333 | }); | 3601 | }); |
| 3334 | elf.nodes.appendAssumeCapacity(.{ .segment = dynamic_phndx }); | 3602 | elf.nodes.appendAssumeCapacity(.{ .segment = phndx.dynamic }); |
| 3335 | elf.phdrs.items[dynamic_phndx] = dynamic_ni; | 3603 | elf.phdrs.items[phndx.dynamic] = dynamic_ni; |
| 3336 | | 3604 | |
| 3337 | const dynstr_shndx = try elf.addSection(elf.ni.rodata, .{ | 3605 | const dynstr_shndx = try elf.addSection(elf.ni.rodata, .{ |
| 3338 | .name = ".dynstr", | 3606 | .name = ".dynstr", |
| ... | @@ -3418,19 +3686,19 @@ fn initHeaders( | ... | @@ -3418,19 +3686,19 @@ fn initHeaders( |
| 3418 | }); | 3686 | }); |
| 3419 | elf.plt_first_symbol_reloc = @enumFromInt(elf.symbol_relocs.items.len); | 3687 | elf.plt_first_symbol_reloc = @enumFromInt(elf.symbol_relocs.items.len); |
| 3420 | try elf.ensureUnusedRelocCapacity(plt_ni, 2); | 3688 | try elf.ensureUnusedRelocCapacity(plt_ni, 2); |
| 3421 | try elf.addRelocAssumeCapacity( | 3689 | try elf.addSymbolRelocAssumeCapacity( |
| 3422 | plt_ni, | 3690 | plt_ni, |
| 3423 | 2, | 3691 | 2, |
| 3424 | got_plt_sym, | 3692 | got_plt_sym, |
| 3425 | 8 * 1 - 4, | 3693 | 8 * 1 - 4, |
| 3426 | .{ .X86_64 = .PC32 }, | 3694 | .rel32, |
| 3427 | ); | 3695 | ); |
| 3428 | try elf.addRelocAssumeCapacity( | 3696 | try elf.addSymbolRelocAssumeCapacity( |
| 3429 | plt_ni, | 3697 | plt_ni, |
| 3430 | 8, | 3698 | 8, |
| 3431 | got_plt_sym, | 3699 | got_plt_sym, |
| 3432 | 8 * 2 - 4, | 3700 | 8 * 2 - 4, |
| 3433 | .{ .X86_64 = .PC32 }, | 3701 | .rel32, |
| 3434 | ); | 3702 | ); |
| 3435 | }, | 3703 | }, |
| 3436 | .LOONGARCH => { | 3704 | .LOONGARCH => { |
| ... | @@ -3461,9 +3729,9 @@ fn initHeaders( | ... | @@ -3461,9 +3729,9 @@ fn initHeaders( |
| 3461 | }); | 3729 | }); |
| 3462 | elf.plt_first_symbol_reloc = @enumFromInt(elf.symbol_relocs.items.len); | 3730 | elf.plt_first_symbol_reloc = @enumFromInt(elf.symbol_relocs.items.len); |
| 3463 | try elf.ensureUnusedRelocCapacity(plt_ni, 3); | 3731 | try elf.ensureUnusedRelocCapacity(plt_ni, 3); |
| 3464 | 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); |
| 3465 | 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); |
| 3466 | 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); |
| 3467 | }, | 3735 | }, |
| 3468 | } | 3736 | } |
| 3469 | } | 3737 | } |
| ... | @@ -3473,8 +3741,8 @@ fn initHeaders( | ... | @@ -3473,8 +3741,8 @@ fn initHeaders( |
| 3473 | .moved = true, | 3741 | .moved = true, |
| 3474 | .bubbles_moved = false, | 3742 | .bubbles_moved = false, |
| 3475 | }); | 3743 | }); |
| 3476 | elf.nodes.appendAssumeCapacity(.{ .segment = tls_phndx }); | 3744 | elf.nodes.appendAssumeCapacity(.{ .segment = phndx.tls }); |
| 3477 | elf.phdrs.items[tls_phndx] = elf.ni.tls; | 3745 | elf.phdrs.items[phndx.tls] = elf.ni.tls; |
| 3478 | } | 3746 | } |
| 3479 | | 3747 | |
| 3480 | // Populate reserved GOT words. | 3748 | // Populate reserved GOT words. |
| ... | @@ -3641,10 +3909,11 @@ fn initHeaders( | ... | @@ -3641,10 +3909,11 @@ fn initHeaders( |
| 3641 | .flags = .{ .WRITE = true, .ALLOC = true, .TLS = true }, | 3909 | .flags = .{ .WRITE = true, .ALLOC = true, .TLS = true }, |
| 3642 | .addralign = elf.mf.flags.block_size, | 3910 | .addralign = elf.mf.flags.block_size, |
| 3643 | }); | 3911 | }); |
| | 3912 | |
| 3644 | assert(elf.nodes.len == expected_nodes_len); | 3913 | assert(elf.nodes.len == expected_nodes_len); |
| | 3914 | assert(elf.shdrs.items.len == shnum); |
| 3645 | | 3915 | |
| 3646 | try elf.section_by_name.ensureUnusedCapacity(gpa, elf.shdrs.items.len); | 3916 | for (0..shnum) |shndx_raw| { |
| 3647 | for (0..elf.shdrs.items.len) |shndx_raw| { | | |
| 3648 | const shndx: Section.Index = @enumFromInt(shndx_raw); | 3917 | const shndx: Section.Index = @enumFromInt(shndx_raw); |
| 3649 | elf.section_by_name.putAssumeCapacityNoClobber(shndx.name(elf), {}); | 3918 | elf.section_by_name.putAssumeCapacityNoClobber(shndx.name(elf), {}); |
| 3650 | } | 3919 | } |
| ... | @@ -3780,13 +4049,11 @@ fn flushMovedNodeRelocs( | ... | @@ -3780,13 +4049,11 @@ fn flushMovedNodeRelocs( |
| 3780 | for (elf.symbol_relocs.items[@intFromEnum(first_symbol_reloc)..]) |*reloc| { | 4049 | for (elf.symbol_relocs.items[@intFromEnum(first_symbol_reloc)..]) |*reloc| { |
| 3781 | if (reloc.node != node) break; | 4050 | if (reloc.node != node) break; |
| 3782 | if (reloc.rela_index.unwrap()) |rela_index| { | 4051 | if (reloc.rela_index.unwrap()) |rela_index| { |
| 3783 | // 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 |
| 3784 | // 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. |
| 3785 | reloc.relaSection(elf).relaSetOffset(elf, rela_index, node_vaddr + reloc.offset); | 4054 | reloc.relaSection(elf).relaSetOffset(elf, rela_index, node_vaddr + reloc.offset); |
| 3786 | } else { | | |
| 3787 | // We've applied this relocation ourselves! Just re-apply it now. | | |
| 3788 | reloc.apply(elf); | | |
| 3789 | } | 4055 | } |
| | 4056 | reloc.apply(elf); |
| 3790 | } | 4057 | } |
| 3791 | } | 4058 | } |
| 3792 | | 4059 | |
| ... | @@ -4108,7 +4375,7 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) Error!Node | ... | @@ -4108,7 +4375,7 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) Error!Node |
| 4108 | } else if (ip.isFunctionType(nav.resolved.?.type)) { | 4375 | } else if (ip.isFunctionType(nav.resolved.?.type)) { |
| 4109 | break :section .text; | 4376 | break :section .text; |
| 4110 | } else { | 4377 | } else { |
| 4111 | 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 |
| 4112 | } | 4379 | } |
| 4113 | }; | 4380 | }; |
| 4114 | 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)) { |
| ... | @@ -4174,7 +4441,7 @@ fn uavMapIndex( | ... | @@ -4174,7 +4441,7 @@ fn uavMapIndex( |
| 4174 | const uav_gop = elf.uavs.getOrPutAssumeCapacity(uav_val); | 4441 | const uav_gop = elf.uavs.getOrPutAssumeCapacity(uav_val); |
| 4175 | const umi: Node.UavMapIndex = @enumFromInt(uav_gop.index); | 4442 | const umi: Node.UavMapIndex = @enumFromInt(uav_gop.index); |
| 4176 | if (!uav_gop.found_existing) { | 4443 | if (!uav_gop.found_existing) { |
| 4177 | 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 |
| 4178 | const node = try elf.mf.addLastChildNode(gpa, shndx.get(elf).ni, .{ | 4445 | const node = try elf.mf.addLastChildNode(gpa, shndx.get(elf).ni, .{ |
| 4179 | .moved = true, // see assert at end of `flushUav` | 4446 | .moved = true, // see assert at end of `flushUav` |
| 4180 | .alignment = resolved_align.toStdMem(), | 4447 | .alignment = resolved_align.toStdMem(), |
| ... | @@ -5124,7 +5391,7 @@ fn updateInitFiniArraySectionSize( | ... | @@ -5124,7 +5391,7 @@ fn updateInitFiniArraySectionSize( |
| 5124 | const end_vaddr: u64 = switch (elf.shdrPtr(shndx)) { | 5391 | const end_vaddr: u64 = switch (elf.shdrPtr(shndx)) { |
| 5125 | inline else => |shdr| shndx.vaddr(elf) + elf.targetLoad(&shdr.size), | 5392 | inline else => |shdr| shndx.vaddr(elf) + elf.targetLoad(&shdr.size), |
| 5126 | }; | 5393 | }; |
| 5127 | const end_sym_name = elf.string(.strtab, "__" ++ name ++ "_end") catch unreachable; // string definitely already exists | 5394 | const end_sym_name = elf.stringExisting(.strtab, "__" ++ name ++ "_end"); |
| 5128 | Symbol.Id.global(end_sym_name).flushMoved(elf, end_vaddr); | 5395 | Symbol.Id.global(end_sym_name).flushMoved(elf, end_vaddr); |
| 5129 | } | 5396 | } |
| 5130 | | 5397 | |
| ... | @@ -5178,6 +5445,10 @@ fn prelinkInner(elf: *Elf) Error!void { | ... | @@ -5178,6 +5445,10 @@ fn prelinkInner(elf: *Elf) Error!void { |
| 5178 | } | 5445 | } |
| 5179 | break :rpath try elf.string(.dynstr, buf.items); | 5446 | break :rpath try elf.string(.dynstr, buf.items); |
| 5180 | }; | 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); |
| 5181 | const soname: ?String(.dynstr) = if (elf.options.soname) |soname_slice| str: { | 5452 | const soname: ?String(.dynstr) = if (elf.options.soname) |soname_slice| str: { |
| 5182 | break :str try elf.string(.dynstr, soname_slice); | 5453 | break :str try elf.string(.dynstr, soname_slice); |
| 5183 | } else null; | 5454 | } else null; |
| ... | @@ -5188,7 +5459,8 @@ fn prelinkInner(elf: *Elf) Error!void { | ... | @@ -5188,7 +5459,8 @@ fn prelinkInner(elf: *Elf) Error!void { |
| 5188 | @as(usize, @intFromBool(elf.shndx.init_array != .UNDEF)) * 2 + | 5459 | @as(usize, @intFromBool(elf.shndx.init_array != .UNDEF)) * 2 + |
| 5189 | @as(usize, @intFromBool(elf.shndx.fini_array != .UNDEF)) * 2 + | 5460 | @as(usize, @intFromBool(elf.shndx.fini_array != .UNDEF)) * 2 + |
| 5190 | @as(usize, @intFromBool(elf.shndx.preinit_array != .UNDEF)) * 2 + | 5461 | @as(usize, @intFromBool(elf.shndx.preinit_array != .UNDEF)) * 2 + |
| 5191 | @intFromBool(comp.config.output_mode == .Exe) + 12; | 5462 | @as(usize, @intFromBool(use_plt)) * 4 + |
| | 5463 | @intFromBool(comp.config.output_mode == .Exe) + 8; |
| 5192 | const dynamic_size: u32 = @intCast(@sizeOf(ElfN.Addr) * 2 * dynamic_len); | 5464 | const dynamic_size: u32 = @intCast(@sizeOf(ElfN.Addr) * 2 * dynamic_len); |
| 5193 | const dynamic_ni = elf.shndx.dynamic.get(elf).ni; | 5465 | const dynamic_ni = elf.shndx.dynamic.get(elf).ni; |
| 5194 | try dynamic_ni.resize(&elf.mf, gpa, dynamic_size); | 5466 | try dynamic_ni.resize(&elf.mf, gpa, dynamic_size); |
| ... | @@ -5200,6 +5472,8 @@ fn prelinkInner(elf: *Elf) Error!void { | ... | @@ -5200,6 +5472,8 @@ fn prelinkInner(elf: *Elf) Error!void { |
| 5200 | init_array: ?usize, | 5472 | init_array: ?usize, |
| 5201 | fini_array: ?usize, | 5473 | fini_array: ?usize, |
| 5202 | preinit_array: ?usize, | 5474 | preinit_array: ?usize, |
| | 5475 | jmprel: ?usize, |
| | 5476 | pltgot: ?usize, |
| 5203 | } = indices: { | 5477 | } = indices: { |
| 5204 | const sec_dynamic = dynamic_ni.slice(&elf.mf); | 5478 | const sec_dynamic = dynamic_ni.slice(&elf.mf); |
| 5205 | const dynamic_entries: [][2]ElfN.Addr = @ptrCast(@alignCast(sec_dynamic)); | 5479 | const dynamic_entries: [][2]ElfN.Addr = @ptrCast(@alignCast(sec_dynamic)); |
| ... | @@ -5232,7 +5506,7 @@ fn prelinkInner(elf: *Elf) Error!void { | ... | @@ -5232,7 +5506,7 @@ fn prelinkInner(elf: *Elf) Error!void { |
| 5232 | } | 5506 | } |
| 5233 | 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: { |
| 5234 | dynamic_entries[dynamic_index..][0..2].* = .{ | 5508 | dynamic_entries[dynamic_index..][0..2].* = .{ |
| 5235 | .{ std.elf.DT_INIT_ARRAY, @intCast(elf.shndx.init_array.vaddr(elf)) }, | 5509 | .{ std.elf.DT_INIT_ARRAY, 0 }, // reloc added below |
| 5236 | .{ std.elf.DT_INIT_ARRAYSZ, elf.targetLoad( | 5510 | .{ std.elf.DT_INIT_ARRAYSZ, elf.targetLoad( |
| 5237 | &@field(elf.shdrPtr(elf.shndx.init_array), @tagName(ct_class)).size, | 5511 | &@field(elf.shdrPtr(elf.shndx.init_array), @tagName(ct_class)).size, |
| 5238 | ) }, | 5512 | ) }, |
| ... | @@ -5242,7 +5516,7 @@ fn prelinkInner(elf: *Elf) Error!void { | ... | @@ -5242,7 +5516,7 @@ fn prelinkInner(elf: *Elf) Error!void { |
| 5242 | } else null; | 5516 | } else null; |
| 5243 | 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: { |
| 5244 | dynamic_entries[dynamic_index..][0..2].* = .{ | 5518 | dynamic_entries[dynamic_index..][0..2].* = .{ |
| 5245 | .{ std.elf.DT_FINI_ARRAY, @intCast(elf.shndx.fini_array.vaddr(elf)) }, | 5519 | .{ std.elf.DT_FINI_ARRAY, 0 }, // reloc added below |
| 5246 | .{ std.elf.DT_FINI_ARRAYSZ, elf.targetLoad( | 5520 | .{ std.elf.DT_FINI_ARRAYSZ, elf.targetLoad( |
| 5247 | &@field(elf.shdrPtr(elf.shndx.fini_array), @tagName(ct_class)).size, | 5521 | &@field(elf.shdrPtr(elf.shndx.fini_array), @tagName(ct_class)).size, |
| 5248 | ) }, | 5522 | ) }, |
| ... | @@ -5252,7 +5526,7 @@ fn prelinkInner(elf: *Elf) Error!void { | ... | @@ -5252,7 +5526,7 @@ fn prelinkInner(elf: *Elf) Error!void { |
| 5252 | } else null; | 5526 | } else null; |
| 5253 | 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: { |
| 5254 | dynamic_entries[dynamic_index..][0..2].* = .{ | 5528 | dynamic_entries[dynamic_index..][0..2].* = .{ |
| 5255 | .{ std.elf.DT_PREINIT_ARRAY, @intCast(elf.shndx.preinit_array.vaddr(elf)) }, | 5529 | .{ std.elf.DT_PREINIT_ARRAY, 0 }, // reloc added below |
| 5256 | .{ std.elf.DT_PREINIT_ARRAYSZ, elf.targetLoad( | 5530 | .{ std.elf.DT_PREINIT_ARRAYSZ, elf.targetLoad( |
| 5257 | &@field(elf.shdrPtr(elf.shndx.preinit_array), @tagName(ct_class)).size, | 5531 | &@field(elf.shdrPtr(elf.shndx.preinit_array), @tagName(ct_class)).size, |
| 5258 | ) }, | 5532 | ) }, |
| ... | @@ -5260,27 +5534,33 @@ fn prelinkInner(elf: *Elf) Error!void { | ... | @@ -5260,27 +5534,33 @@ fn prelinkInner(elf: *Elf) Error!void { |
| 5260 | defer dynamic_index += 2; | 5534 | defer dynamic_index += 2; |
| 5261 | break :i dynamic_index; | 5535 | break :i dynamic_index; |
| 5262 | } else null; | 5536 | } else null; |
| 5263 | dynamic_entries[dynamic_index..][0..12].* = .{ | 5537 | const jmprel_index: ?usize, const pltgot_index: ?usize = if (use_plt) i: { |
| 5264 | .{ 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 |
| 5265 | .{ std.elf.DT_RELASZ, elf.targetLoad( | 5551 | .{ std.elf.DT_RELASZ, elf.targetLoad( |
| 5266 | &@field(elf.shdrPtr(elf.shndx.rela_dyn), @tagName(ct_class)).size, | 5552 | &@field(elf.shdrPtr(elf.shndx.rela_dyn), @tagName(ct_class)).size, |
| 5267 | ) }, | 5553 | ) }, |
| 5268 | .{ std.elf.DT_RELAENT, @sizeOf(ElfN.Rela) }, | 5554 | .{ std.elf.DT_RELAENT, @sizeOf(ElfN.Rela) }, |
| 5269 | .{ std.elf.DT_JMPREL, @intCast(elf.shndx.rela_plt.vaddr(elf)) }, | 5555 | .{ std.elf.DT_SYMTAB, 0 }, // reloc added below |
| 5270 | .{ std.elf.DT_PLTRELSZ, elf.targetLoad( | | |
| 5271 | &@field(elf.shdrPtr(elf.shndx.rela_plt), @tagName(ct_class)).size, | | |
| 5272 | ) }, | | |
| 5273 | .{ std.elf.DT_PLTGOT, @intCast(elf.shndx.got_plt.vaddr(elf)) }, | | |
| 5274 | .{ std.elf.DT_PLTREL, std.elf.DT_RELA }, | | |
| 5275 | .{ std.elf.DT_SYMTAB, @intCast(elf.shndx.dynsym.vaddr(elf)) }, | | |
| 5276 | .{ std.elf.DT_SYMENT, @sizeOf(ElfN.Sym) }, | 5556 | .{ std.elf.DT_SYMENT, @sizeOf(ElfN.Sym) }, |
| 5277 | .{ std.elf.DT_STRTAB, @intCast(elf.shndx.dynstr.vaddr(elf)) }, | 5557 | .{ std.elf.DT_STRTAB, 0 }, // reloc added below |
| 5278 | .{ std.elf.DT_STRSZ, elf.targetLoad( | 5558 | .{ std.elf.DT_STRSZ, elf.targetLoad( |
| 5279 | &@field(elf.shdrPtr(elf.shndx.dynstr), @tagName(ct_class)).size, | 5559 | &@field(elf.shdrPtr(elf.shndx.dynstr), @tagName(ct_class)).size, |
| 5280 | ) }, | 5560 | ) }, |
| 5281 | .{ std.elf.DT_NULL, 0 }, | 5561 | .{ std.elf.DT_NULL, 0 }, |
| 5282 | }; | 5562 | }; |
| 5283 | dynamic_index += 12; | 5563 | dynamic_index += 8; |
| 5284 | assert(dynamic_index == dynamic_len); | 5564 | assert(dynamic_index == dynamic_len); |
| 5285 | if (elf.targetEndian() != native_endian) for (dynamic_entries) |*dynamic_entry| | 5565 | if (elf.targetEndian() != native_endian) for (dynamic_entries) |*dynamic_entry| |
| 5286 | std.mem.byteSwapAllFields(@TypeOf(dynamic_entry.*), dynamic_entry); | 5566 | std.mem.byteSwapAllFields(@TypeOf(dynamic_entry.*), dynamic_entry); |
| ... | @@ -5289,66 +5569,74 @@ fn prelinkInner(elf: *Elf) Error!void { | ... | @@ -5289,66 +5569,74 @@ fn prelinkInner(elf: *Elf) Error!void { |
| 5289 | .init_array = init_array_index, | 5569 | .init_array = init_array_index, |
| 5290 | .fini_array = fini_array_index, | 5570 | .fini_array = fini_array_index, |
| 5291 | .preinit_array = preinit_array_index, | 5571 | .preinit_array = preinit_array_index, |
| | 5572 | .jmprel = jmprel_index, |
| | 5573 | .pltgot = pltgot_index, |
| 5292 | }; | 5574 | }; |
| 5293 | }; | 5575 | }; |
| 5294 | | 5576 | |
| | 5577 | const dsorel: SymbolReloc.Type = switch (ct_class) { |
| | 5578 | .NONE, _ => comptime unreachable, |
| | 5579 | .@"32" => .dsorel32, |
| | 5580 | .@"64" => .dsorel64, |
| | 5581 | }; |
| | 5582 | |
| 5295 | elf.dynamic_first_symbol_reloc = @enumFromInt(elf.symbol_relocs.items.len); | 5583 | elf.dynamic_first_symbol_reloc = @enumFromInt(elf.symbol_relocs.items.len); |
| 5296 | try elf.ensureUnusedRelocCapacity(dynamic_ni, 8); | 5584 | try elf.ensureUnusedRelocCapacity(dynamic_ni, 8); |
| 5297 | if (dynamic_indices.init_array) |index| try elf.addRelocAssumeCapacity( | 5585 | if (dynamic_indices.init_array) |index| try elf.addSymbolRelocAssumeCapacity( |
| 5298 | dynamic_ni, | 5586 | dynamic_ni, |
| 5299 | @sizeOf(ElfN.Addr) * (2 * index + 1), | 5587 | @sizeOf(ElfN.Addr) * (2 * index + 1), |
| 5300 | .local(elf.shndx.init_array.get(elf).lsi), | 5588 | .local(elf.shndx.init_array.get(elf).lsi), |
| 5301 | 0, | 5589 | 0, |
| 5302 | .absAddr(elf), | 5590 | dsorel, |
| 5303 | ); | 5591 | ); |
| 5304 | if (dynamic_indices.fini_array) |index| try elf.addRelocAssumeCapacity( | 5592 | if (dynamic_indices.fini_array) |index| try elf.addSymbolRelocAssumeCapacity( |
| 5305 | dynamic_ni, | 5593 | dynamic_ni, |
| 5306 | @sizeOf(ElfN.Addr) * (2 * index + 1), | 5594 | @sizeOf(ElfN.Addr) * (2 * index + 1), |
| 5307 | .local(elf.shndx.fini_array.get(elf).lsi), | 5595 | .local(elf.shndx.fini_array.get(elf).lsi), |
| 5308 | 0, | 5596 | 0, |
| 5309 | .absAddr(elf), | 5597 | dsorel, |
| 5310 | ); | 5598 | ); |
| 5311 | if (dynamic_indices.preinit_array) |index| try elf.addRelocAssumeCapacity( | 5599 | if (dynamic_indices.preinit_array) |index| try elf.addSymbolRelocAssumeCapacity( |
| 5312 | dynamic_ni, | 5600 | dynamic_ni, |
| 5313 | @sizeOf(ElfN.Addr) * (2 * index + 1), | 5601 | @sizeOf(ElfN.Addr) * (2 * index + 1), |
| 5314 | .local(elf.shndx.preinit_array.get(elf).lsi), | 5602 | .local(elf.shndx.preinit_array.get(elf).lsi), |
| 5315 | 0, | 5603 | 0, |
| 5316 | .absAddr(elf), | 5604 | dsorel, |
| 5317 | ); | 5605 | ); |
| 5318 | try elf.addRelocAssumeCapacity( | 5606 | if (dynamic_indices.jmprel) |index| try elf.addSymbolRelocAssumeCapacity( |
| 5319 | dynamic_ni, | 5607 | dynamic_ni, |
| 5320 | @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 12) + 1), | 5608 | @sizeOf(ElfN.Addr) * (2 * index + 1), |
| 5321 | .local(elf.shndx.rela_dyn.get(elf).lsi), | 5609 | .local(elf.shndx.rela_plt.get(elf).lsi), |
| 5322 | 0, | 5610 | 0, |
| 5323 | .absAddr(elf), | 5611 | dsorel, |
| 5324 | ); | 5612 | ); |
| 5325 | try elf.addRelocAssumeCapacity( | 5613 | if (dynamic_indices.pltgot) |index| try elf.addSymbolRelocAssumeCapacity( |
| 5326 | dynamic_ni, | 5614 | dynamic_ni, |
| 5327 | @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 9) + 1), | 5615 | @sizeOf(ElfN.Addr) * (2 * index + 1), |
| 5328 | .local(elf.shndx.rela_plt.get(elf).lsi), | 5616 | .local(elf.shndx.got_plt.get(elf).lsi), |
| 5329 | 0, | 5617 | 0, |
| 5330 | .absAddr(elf), | 5618 | dsorel, |
| 5331 | ); | 5619 | ); |
| 5332 | try elf.addRelocAssumeCapacity( | 5620 | try elf.addSymbolRelocAssumeCapacity( |
| 5333 | dynamic_ni, | 5621 | dynamic_ni, |
| 5334 | @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 7) + 1), | 5622 | @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 8) + 1), |
| 5335 | .local(elf.shndx.got_plt.get(elf).lsi), | 5623 | .local(elf.shndx.rela_dyn.get(elf).lsi), |
| 5336 | 0, | 5624 | 0, |
| 5337 | .absAddr(elf), | 5625 | dsorel, |
| 5338 | ); | 5626 | ); |
| 5339 | try elf.addRelocAssumeCapacity( | 5627 | try elf.addSymbolRelocAssumeCapacity( |
| 5340 | dynamic_ni, | 5628 | dynamic_ni, |
| 5341 | @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 5) + 1), | 5629 | @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 5) + 1), |
| 5342 | .local(elf.shndx.dynsym.get(elf).lsi), | 5630 | .local(elf.shndx.dynsym.get(elf).lsi), |
| 5343 | 0, | 5631 | 0, |
| 5344 | .absAddr(elf), | 5632 | dsorel, |
| 5345 | ); | 5633 | ); |
| 5346 | try elf.addRelocAssumeCapacity( | 5634 | try elf.addSymbolRelocAssumeCapacity( |
| 5347 | dynamic_ni, | 5635 | dynamic_ni, |
| 5348 | @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 3) + 1), | 5636 | @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 3) + 1), |
| 5349 | .local(elf.shndx.dynstr.get(elf).lsi), | 5637 | .local(elf.shndx.dynstr.get(elf).lsi), |
| 5350 | 0, | 5638 | 0, |
| 5351 | .absAddr(elf), | 5639 | dsorel, |
| 5352 | ); | 5640 | ); |
| 5353 | }, | 5641 | }, |
| 5354 | }; | 5642 | }; |
| ... | @@ -5687,18 +5975,34 @@ fn addSymbolRelocAssumeCapacity( | ... | @@ -5687,18 +5975,34 @@ fn addSymbolRelocAssumeCapacity( |
| 5687 | @"type": SymbolReloc.Type, | 5975 | @"type": SymbolReloc.Type, |
| 5688 | ) Error!void { | 5976 | ) Error!void { |
| 5689 | assert(elf.ehdrField(.type) != .REL); | 5977 | assert(elf.ehdrField(.type) != .REL); |
| | 5978 | assert(node != .none); |
| 5690 | | 5979 | |
| 5691 | const rela_index: Section.RelaIndex.Optional = r: { | 5980 | const rela_index: Section.RelaIndex.Optional = r: { |
| 5692 | if (elf.shndx.dynamic == .UNDEF) break :r .none; | 5981 | // If we emit a runtime relocation entry, its `offset` is a virtual address, so we need to |
| 5693 | const global_name = switch (target.unwrap()) { | 5982 | // determine the vaddr of `node`. |
| 5694 | .local => break :r .none, | 5983 | const node_vaddr: u64 = switch (elf.getNode(node)) { |
| 5695 | .global => |name| name, | 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), |
| 5696 | }; | 5996 | }; |
| 5697 | | 5997 | |
| 5698 | const rela_type: MachineRelocType = switch (elf.ehdrField(.machine)) { | 5998 | const rela_type: MachineRelocType = switch (elf.ehdrField(.machine)) { |
| 5699 | else => |machine| @panic(@tagName(machine)), | 5999 | else => |machine| @panic(@tagName(machine)), |
| 5700 | .X86_64 => .{ .X86_64 = switch (@"type") { | 6000 | .X86_64 => .{ .X86_64 = switch (@"type") { |
| 5701 | .write_rela => unreachable, | 6001 | .write_rela => unreachable, |
| | 6002 | .dsorel64, .dsorel32 => { |
| | 6003 | assert(target.unwrap() == .local); |
| | 6004 | break :r .none; |
| | 6005 | }, |
| 5702 | .abs64 => .@"64", | 6006 | .abs64 => .@"64", |
| 5703 | .abs32 => .@"32", | 6007 | .abs32 => .@"32", |
| 5704 | .abs32s => .@"32S", | 6008 | .abs32s => .@"32S", |
| ... | @@ -5726,72 +6030,79 @@ fn addSymbolRelocAssumeCapacity( | ... | @@ -5726,72 +6030,79 @@ fn addSymbolRelocAssumeCapacity( |
| 5726 | .tpoff64_hi12, | 6030 | .tpoff64_hi12, |
| 5727 | => unreachable, | 6031 | => unreachable, |
| 5728 | } }, | 6032 | } }, |
| 5729 | .LOONGARCH => .{ | 6033 | .LOONGARCH => .{ .LOONGARCH = switch (@"type") { |
| 5730 | .LOONGARCH = switch (@"type") { | 6034 | .write_rela => unreachable, |
| 5731 | .write_rela => unreachable, | 6035 | .dsorel64, .dsorel32 => { |
| 5732 | .abs64 => .@"64", | 6036 | assert(target.unwrap() == .local); |
| 5733 | .abs32 => .@"32", | 6037 | break :r .none; |
| 5734 | .abs32s, .size64, .size32 => unreachable, | | |
| 5735 | .rel64 => .@"64_PCREL", | | |
| 5736 | .rel32 => .@"32_PCREL", | | |
| 5737 | .pltrel64, .pltrel32 => break :r .none, | | |
| 5738 | .dtpoff64 => .TLS_DTPREL64, | | |
| 5739 | .dtpoff32 => .TLS_DTPREL32, | | |
| 5740 | .tpoff64 => .TLS_TPREL64, | | |
| 5741 | .tpoff32 => .TLS_TPREL32, | | |
| 5742 | .abs32_lo12 => .PCALA_LO12, | | |
| 5743 | .rel32_hi20 => .PCALA_HI20, | | |
| 5744 | .rel64_lo20 => .PCALA64_LO20, | | |
| 5745 | .rel64_hi12 => .PCALA64_HI12, | | |
| 5746 | .branch_rel18 => .B16, | | |
| 5747 | .branch_rel23 => .B21, | | |
| 5748 | .branch_rel28 => .B26, | | |
| 5749 | .call_rel38 => .CALL36, | | |
| 5750 | .tpoff32_lo12 => .TLS_LE_LO12, | | |
| 5751 | .tpoff32_hi20 => .TLS_LE_HI20, | | |
| 5752 | .tpoff64_lo20 => .TLS_LE64_LO20, | | |
| 5753 | .tpoff64_hi12 => .TLS_LE64_HI12, | | |
| 5754 | }, | 6038 | }, |
| 5755 | }, | 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 | } }, |
| 5756 | }; | 6062 | }; |
| 5757 | // TODO: even if the symbol is locally defined, preemption/interposition is a | | |
| 5758 | // possibility, which this condition does not currently consider! | | |
| 5759 | if (elf.globals.strong_def.contains(global_name) or | | |
| 5760 | elf.globals.weak_def.contains(global_name)) | | |
| 5761 | { | | |
| 5762 | break :r .none; | | |
| 5763 | } | | |
| 5764 | | 6063 | |
| 5765 | const dynsym_index = elf.globalByName(global_name).?.dynsym_index; | 6064 | class: switch (elf.classifySymbolValue(target)) { |
| 5766 | if (dynsym_index == 0) break :r .none; | 6065 | .static => break :r .none, |
| 5767 | | 6066 | .static_relative => { |
| 5768 | switch (elf.nodeWantsDsoRelocation(node)) { | 6067 | if (!@"type".isAbsAddr(elf)) break :r .none; |
| 5769 | .no => break :r .none, | 6068 | switch (elf.nodeWantsDsoRelocation(node)) { |
| 5770 | .yes => {}, | 6069 | .no => break :r .none, |
| 5771 | .yes_textrel => if (try elf.maybeAddCopyRelocation(global_name)) { | 6070 | .yes => {}, |
| 5772 | // We were able to use a copy relocation on this symbol to avoid a text relocation, | 6071 | .yes_textrel => elf.textrel_count += 1, |
| 5773 | // which is apparently considered a good thing despite copy relocations being an | 6072 | } |
| 5774 | // abomination. (This is necessary for correctness in some cases, because e.g. a | 6073 | break :r elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{ |
| 5775 | // 32-bit runtime relocation on a 64-bit target will often cause rtld errors due to | 6074 | .type = .relative(elf), |
| 5776 | // the DSOs being loaded too far apart.) | 6075 | .offset = node_vaddr + offset, |
| 5777 | break :r .none; | 6076 | .raw_sym_index = 0, |
| 5778 | } else { | 6077 | .addend = 0, |
| 5779 | // At least for now, our only choice is a text relocation. | 6078 | }).toOptional(); |
| 5780 | 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(), |
| 5781 | }, | 6104 | }, |
| 5782 | } | 6105 | } |
| 5783 | | | |
| 5784 | // It currently looks like we need a runtime relocation for this. | | |
| 5785 | break :r elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{ | | |
| 5786 | .type = rela_type, | | |
| 5787 | // This field needs to equal the offset into the section, which is *not* necessarily | | |
| 5788 | // the same thing as our `offset`, which is the offset into `node`. We could compute | | |
| 5789 | // the section offset now, but there's no point, because `flushMovedNodeRelocs` will | | |
| 5790 | // eventually do it for us anyway, so just init to 0. | | |
| 5791 | .offset = 0, | | |
| 5792 | .raw_sym_index = dynsym_index, | | |
| 5793 | .addend = addend, | | |
| 5794 | }).toOptional(); | | |
| 5795 | }; | 6106 | }; |
| 5796 | | 6107 | |
| 5797 | const ri: SymbolReloc.Index = @enumFromInt(elf.symbol_relocs.items.len); | 6108 | const ri: SymbolReloc.Index = @enumFromInt(elf.symbol_relocs.items.len); |
| ... | @@ -5814,6 +6125,9 @@ fn addSymbolRelocAssumeCapacity( | ... | @@ -5814,6 +6125,9 @@ fn addSymbolRelocAssumeCapacity( |
| 5814 | if (@"type".dependsOnTlsSize()) { | 6125 | if (@"type".dependsOnTlsSize()) { |
| 5815 | elf.tls_size_symbol_relocs.putAssumeCapacityNoClobber(ri, {}); | 6126 | elf.tls_size_symbol_relocs.putAssumeCapacityNoClobber(ri, {}); |
| 5816 | } | 6127 | } |
| | 6128 | |
| | 6129 | // Actually apply the new relocation! |
| | 6130 | ri.get(elf).apply(elf); |
| 5817 | } | 6131 | } |
| 5818 | fn addGotRelocAssumeCapacity( | 6132 | fn addGotRelocAssumeCapacity( |
| 5819 | elf: *Elf, | 6133 | elf: *Elf, |
| ... | @@ -5885,29 +6199,13 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void { | ... | @@ -5885,29 +6199,13 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void { |
| 5885 | reloc: struct { | 6199 | reloc: struct { |
| 5886 | type: MachineRelocType, | 6200 | type: MachineRelocType, |
| 5887 | dynsym_index: u32, | 6201 | dynsym_index: u32, |
| | 6202 | addend: i64, |
| 5888 | }, | 6203 | }, |
| 5889 | } = switch (elf.got.keys()[got_index]) { | 6204 | } = switch (elf.got.keys()[got_index]) { |
| 5890 | .reserved => .{ .unsigned = 0 }, | 6205 | .reserved => .{ .unsigned = 0 }, |
| 5891 | .tpoff => |sym_id| val: { | 6206 | .tpoff => |sym_id| val: { |
| 5892 | // We will break from this block if we require a relocation. | 6207 | // Only the executable's per-module TLS block is at a known offset from the TLS pointer. |
| 5893 | known: { | 6208 | if (elf.base.comp.config.output_mode == .Exe and elf.classifySymbolValue(sym_id) != .dynamic) { |
| 5894 | if (elf.base.comp.config.output_mode != .Exe) { | | |
| 5895 | // Only the executable's per-module TLS block is at a known offset from the | | |
| 5896 | // general TLS pointer. | | |
| 5897 | break :known; | | |
| 5898 | } | | |
| 5899 | switch (sym_id.unwrap()) { | | |
| 5900 | .local => {}, | | |
| 5901 | .global => |name| if (elf.globals.strong_undef.contains(name) or | | |
| 5902 | elf.globals.weak_undef.contains(name)) | | |
| 5903 | { | | |
| 5904 | // This is an external TLS symbol, so we don't know its offset. | | |
| 5905 | break :known; | | |
| 5906 | }, | | |
| 5907 | } | | |
| 5908 | // It's a symbol which we define, the symbol is not interposable because we're the | | |
| 5909 | // executable, and we know our per-module TLS block's offset because we're the | | |
| 5910 | // executable. We therefore know this value! | | |
| 5911 | const tls_phndx = elf.getNode(elf.ni.tls).segment; | 6209 | const tls_phndx = elf.getNode(elf.ni.tls).segment; |
| 5912 | const tls_size: u64 = switch (elf.phdrSlice()) { | 6210 | const tls_size: u64 = switch (elf.phdrSlice()) { |
| 5913 | inline else => |phdr| tls_size: { | 6211 | inline else => |phdr| tls_size: { |
| ... | @@ -5918,110 +6216,80 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void { | ... | @@ -5918,110 +6216,80 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void { |
| 5918 | const sym_value = sym_id.value(elf); | 6216 | const sym_value = sym_id.value(elf); |
| 5919 | break :val .{ .signed = @bitCast(sym_value -% tls_size) }; | 6217 | break :val .{ .signed = @bitCast(sym_value -% tls_size) }; |
| 5920 | } | 6218 | } |
| 5921 | break :val .{ | 6219 | const reloc_type: MachineRelocType = switch (elf.ehdrField(.machine)) { |
| 5922 | .reloc = .{ | 6220 | else => |machine| @panic(@tagName(machine)), |
| 5923 | .type = switch (elf.ehdrField(.machine)) { | 6221 | .X86_64 => .{ .X86_64 = .TPOFF64 }, |
| 5924 | else => |machine| @panic(@tagName(machine)), | 6222 | .LOONGARCH => .{ .LOONGARCH = if (elf.identClass() == .@"64") .TLS_TPREL64 else .TLS_TPREL32 }, |
| 5925 | .X86_64 => .{ .X86_64 = .TPOFF64 }, | | |
| 5926 | .LOONGARCH => .{ .LOONGARCH = if (elf.identClass() == .@"64") .TLS_TPREL64 else .TLS_TPREL32 }, | | |
| 5927 | }, | | |
| 5928 | .dynsym_index = switch (sym_id.unwrap()) { | | |
| 5929 | .global => |name| elf.globalByName(name).?.dynsym_index, | | |
| 5930 | // TODO: I have no idea if compilers are even allowed to emit this, but if they | | |
| 5931 | // are then I guess we need to add this local symbol to `.dynsym`? | | |
| 5932 | .local => @panic("TODO(Elf2): GOT tpoff entry referencing local symbol"), | | |
| 5933 | }, | | |
| 5934 | }, | | |
| 5935 | }; | | |
| 5936 | }, | | |
| 5937 | .symbol, .tlsgd1 => |sym_id, tag| val: { | | |
| 5938 | const name = switch (sym_id.unwrap()) { | | |
| 5939 | .local => break :val .{ .unsigned = sym_id.value(elf) }, | | |
| 5940 | .global => |name| name, | | |
| 5941 | }; | 6223 | }; |
| 5942 | // If the symbol is *defined* in this module, we might be able to avoid the relocation. | 6224 | break :val switch (sym_id.unwrap()) { |
| 5943 | const need_reloc: bool = need_reloc: { | 6225 | // For global symbols, just target the right dynsym with no addend. |
| 5944 | const global = g: { | 6226 | .global => |name| .{ .reloc = .{ |
| 5945 | if (elf.globals.strong_def.getPtr(name)) |g| break :g g; | 6227 | .type = reloc_type, |
| 5946 | if (elf.globals.weak_def.getPtr(name)) |g| break :g g; | 6228 | .dynsym_index = elf.globalByName(name).?.dynsym_index, |
| 5947 | // The global is undefined, which probably means we need a relocation---unless | 6229 | .addend = 0, |
| 5948 | // we have created a copy relocation for it, in which case we own the canonical | 6230 | } }, |
| 5949 | // address of this symbol in this DSO! | 6231 | // For local symbols, target the null symbol (index 0) so we get the offset to the |
| 5950 | break :need_reloc !elf.copied_globals.contains(name); | 6232 | // base of our TLS block, and then use `addend` to offset to the right symbol. |
| 5951 | }; | 6233 | .local => .{ .reloc = .{ |
| 5952 | | 6234 | .type = reloc_type, |
| 5953 | // We have a definition, but it might be interposable (aka preemptible). There | 6235 | .dynsym_index = 0, |
| 5954 | // are two cases where it is not and so we can (and, in fact, must) elide the | 6236 | .addend = @intCast(sym_id.value(elf)), |
| 5955 | // runtime relocation: | 6237 | } }, |
| 5956 | // * We are the executable. Symbols from executables cannot be interposed. | | |
| 5957 | // * The symbol's visibility disallows interposition. | | |
| 5958 | if (elf.base.comp.config.output_mode == .Exe) { | | |
| 5959 | break :need_reloc false; | | |
| 5960 | } | | |
| 5961 | const visibility: std.elf.STV = switch (elf.symPtr(global.symtab_index)) { | | |
| 5962 | inline else => |sym| elf.targetLoad(&sym.other).visibility, | | |
| 5963 | }; | | |
| 5964 | break :need_reloc switch (visibility) { | | |
| 5965 | .DEFAULT => true, | | |
| 5966 | .INTERNAL, .HIDDEN, .PROTECTED => false, | | |
| 5967 | }; | | |
| 5968 | }; | 6238 | }; |
| 5969 | | | |
| 5970 | if (!need_reloc) { | | |
| 5971 | break :val .{ .unsigned = sym_id.value(elf) }; | | |
| 5972 | } | | |
| 5973 | | | |
| 5974 | break :val .{ .reloc = .{ | | |
| 5975 | .type = if (tag == .symbol) .globDat(elf) else .dtpOffAddr(elf), | | |
| 5976 | .dynsym_index = elf.globalByName(name).?.dynsym_index, | | |
| 5977 | } }; | | |
| 5978 | }, | 6239 | }, |
| 5979 | .tlsgd0 => |sym| switch (elf.shndx.dynamic) { | 6240 | .symbol => |sym| switch (elf.classifySymbolValue(sym)) { |
| 5980 | .UNDEF => .{ .unsigned = 1 }, // TLS module ID for exexcutable | 6241 | .static => .{ .unsigned = sym.value(elf) }, |
| 5981 | else => .{ | 6242 | .static_relative => .{ .reloc = .{ |
| 5982 | .reloc = .{ | 6243 | .type = .relative(elf), |
| 5983 | .type = switch (elf.ehdrField(.machine)) { | 6244 | .dynsym_index = 0, |
| 5984 | else => |machine| @panic(@tagName(machine)), | 6245 | .addend = @bitCast(sym.value(elf)), |
| 5985 | .X86_64 => .{ .X86_64 = .DTPMOD64 }, | 6246 | } }, |
| 5986 | .LOONGARCH => .{ .LOONGARCH = if (elf.identClass() == .@"64") .TLS_DTPMOD64 else .TLS_DTPMOD32 }, | 6247 | .dynamic => .{ .reloc = .{ |
| 5987 | }, | 6248 | .type = .globDat(elf), |
| 5988 | .dynsym_index = switch (sym.unwrap()) { | 6249 | .dynsym_index = elf.globalByName(sym.unwrap().global).?.dynsym_index, |
| 5989 | .local => 0, | 6250 | .addend = 0, |
| 5990 | .global => |name| dsi: { | 6251 | } }, |
| 5991 | // Like in the `.tlsgd1` case, we need to check for a non-interposable definition. | 6252 | }, |
| 5992 | if (elf.globals.strong_def.getPtr(name) orelse | 6253 | .tlsgd1 => |sym| switch (elf.classifySymbolValue(sym)) { |
| 5993 | elf.globals.weak_def.getPtr(name)) |global| | 6254 | .static => .{ .unsigned = sym.value(elf) }, |
| 5994 | { | 6255 | .static_relative => unreachable, // TLS variables should be in TLS sections, which do not return `.static_relative` |
| 5995 | if (elf.base.comp.config.output_mode == .Exe) { | 6256 | .dynamic => .{ .reloc = .{ |
| 5996 | break :dsi 0; // non-interposable definition | 6257 | .type = .dtpOffAddr(elf), |
| 5997 | } | 6258 | .dynsym_index = elf.globalByName(sym.unwrap().global).?.dynsym_index, |
| 5998 | const visibility: std.elf.STV = switch (elf.symPtr(global.symtab_index)) { | 6259 | .addend = 0, |
| 5999 | inline else => |sym_ptr| elf.targetLoad(&sym_ptr.other).visibility, | 6260 | } }, |
| 6000 | }; | 6261 | }, |
| 6001 | switch (visibility) { | 6262 | .tlsgd0 => |sym| switch (elf.base.comp.config.link_mode) { |
| 6002 | .DEFAULT => {}, | 6263 | .static => val: { |
| 6003 | .INTERNAL, .HIDDEN, .PROTECTED => { | 6264 | assert(elf.base.comp.config.output_mode == .Exe); // static libraries don't have GOTs |
| 6004 | break :dsi 0; // non-interposable definition | 6265 | break :val .{ .unsigned = 1 }; // TLS module ID for executable |
| 6005 | }, | | |
| 6006 | } | | |
| 6007 | } | | |
| 6008 | // `sym` is either undefined or an interposable definition, so use its | | |
| 6009 | // actual dynsym index. | | |
| 6010 | break :dsi elf.globalByName(name).?.dynsym_index; | | |
| 6011 | }, | | |
| 6012 | }, | | |
| 6013 | }, | | |
| 6014 | }, | 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 | } }, |
| 6015 | }, | 6279 | }, |
| 6016 | .tlsld0 => switch (elf.shndx.dynamic) { | 6280 | .tlsld0 => switch (elf.base.comp.config.link_mode) { |
| 6017 | .UNDEF => .{ .unsigned = 1 }, // TLS module ID for exexcutable | 6281 | .static => val: { |
| 6018 | 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 = .{ |
| 6019 | .type = switch (elf.ehdrField(.machine)) { | 6286 | .type = switch (elf.ehdrField(.machine)) { |
| 6020 | else => |machine| @panic(@tagName(machine)), | 6287 | else => |machine| @panic(@tagName(machine)), |
| 6021 | .X86_64 => .{ .X86_64 = .DTPMOD64 }, | 6288 | .X86_64 => .{ .X86_64 = .DTPMOD64 }, |
| 6022 | .LOONGARCH => .{ .LOONGARCH = if (elf.identClass() == .@"64") .TLS_DTPMOD64 else .TLS_DTPMOD32 }, | 6289 | .LOONGARCH => .{ .LOONGARCH = if (elf.identClass() == .@"64") .TLS_DTPMOD64 else .TLS_DTPMOD32 }, |
| 6023 | }, | 6290 | }, |
| 6024 | .dynsym_index = 0, | 6291 | .dynsym_index = 0, |
| | 6292 | .addend = 0, |
| 6025 | } }, | 6293 | } }, |
| 6026 | }, | 6294 | }, |
| 6027 | .tlsld1 => .{ .unsigned = 0 }, | 6295 | .tlsld1 => .{ .unsigned = 0 }, |
| ... | @@ -6065,7 +6333,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void { | ... | @@ -6065,7 +6333,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void { |
| 6065 | .type = reloc.type, | 6333 | .type = reloc.type, |
| 6066 | .offset = got_entry_addr, | 6334 | .offset = got_entry_addr, |
| 6067 | .raw_sym_index = reloc.dynsym_index, | 6335 | .raw_sym_index = reloc.dynsym_index, |
| 6068 | .addend = 0, | 6336 | .addend = reloc.addend, |
| 6069 | }).toOptional(), | 6337 | }).toOptional(), |
| 6070 | }; | 6338 | }; |
| 6071 | } | 6339 | } |
| ... | @@ -6405,26 +6673,80 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool { | ... | @@ -6405,26 +6673,80 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool { |
| 6405 | }; | 6673 | }; |
| 6406 | break :task; | 6674 | break :task; |
| 6407 | } | 6675 | } |
| 6408 | while (elf.changed_symtab_index.pop()) |kv| { | 6676 | if (elf.changed_symtab_index.pop()) |kv| { |
| 6409 | // We only need to do work in relocatables, because in ELF modules (non-relocatables) | 6677 | const sub_prog_node = elf.mf.update_prog_node.start(kv.key.slice(elf), 0); |
| 6410 | // our `ElfN.Rela` entries use `.dynsym` indices rather than `.symtab` indices, and | 6678 | defer sub_prog_node.end(); |
| 6411 | // `.dynsym` indices are (at the time of writing) always immutable. | 6679 | |
| 6412 | if (elf.ehdrField(.type) == .REL) { | 6680 | const global_name = kv.key; |
| 6413 | const sub_prog_node = elf.mf.update_prog_node.start(kv.key.slice(elf), 0); | 6681 | const global = elf.globalByName(global_name).?; |
| 6414 | defer sub_prog_node.end(); | 6682 | const sym_id: Symbol.Id = .global(global_name); |
| 6415 | const sym = elf.globalByName(kv.key).?.symtab_index.ptr(elf); | 6683 | const sym = global.symtab_index.ptr(elf); |
| 6416 | var ri = sym.first_target_reloc; | 6684 | |
| 6417 | while (ri != .none) { | 6685 | switch (elf.ehdrField(.type)) { |
| 6418 | const reloc = ri.get(elf); | 6686 | .REL => { |
| 6419 | reloc.relaSection(elf).relaUpdateSym( | 6687 | // Index in `.symtab` has changed. Relocatables are easy, we just need to update |
| 6420 | elf, | 6688 | // all of the output relocations. |
| 6421 | reloc.rela_index.unwrap().?, | 6689 | const symtab_index = @intFromEnum(global.symtab_index); |
| 6422 | @intFromEnum(reloc.target.index(elf)), | 6690 | var ri = sym.first_target_reloc; |
| 6423 | ); | 6691 | while (ri != .none) { |
| 6424 | ri = reloc.next; | 6692 | const reloc = ri.get(elf); |
| 6425 | } | 6693 | assert(reloc.target == sym_id); |
| 6426 | break :task; | 6694 | // In relocatables, every symbol relocation has an output relocation. |
| | 6695 | const rela_index = reloc.rela_index.unwrap().?; |
| | 6696 | reloc.relaSection(elf).relaUpdateSym(elf, rela_index, symtab_index); |
| | 6697 | ri = reloc.next; |
| | 6698 | } |
| | 6699 | }, |
| | 6700 | // For other `ET_*` values, the index in `.dynsym` has changed. There are a few |
| | 6701 | // places we might have emitted output relocations, depending on whether or not the |
| | 6702 | // symbol's value is statically known. |
| | 6703 | else => switch (elf.classifySymbolValue(sym_id)) { |
| | 6704 | .static, .static_relative => { |
| | 6705 | // Since the symbol value is statically known, we definitely aren't emitting |
| | 6706 | // any relocation targeting it (we might have `R_*_RELATIVE` relocs but they |
| | 6707 | // don't care about the dynsym index). The only exception is a copy reloc |
| | 6708 | // could exist (and be the *reason* the symbol value is statically known). |
| | 6709 | if (elf.copied_globals.get(global_name)) |copied| { |
| | 6710 | elf.shndx.rela_dyn.relaUpdateSym(elf, copied.rela_index, global.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; |
| | 6726 | } |
| | 6727 | |
| | 6728 | // Update the PLT entry's reloc if there is one: |
| | 6729 | if (elf.plt.getIndex(global_name)) |plt_index| { |
| | 6730 | // PLT indices exactly match `.rela.plt` relocation indices. |
| | 6731 | elf.shndx.rela_plt.relaUpdateSym(elf, @enumFromInt(plt_index), global.dynsym_index); |
| | 6732 | } |
| | 6733 | |
| | 6734 | // Update relocs for any relevant GOT entries: |
| | 6735 | if (elf.got.getIndex(.{ .symbol = sym_id })) |got_index| { |
| | 6736 | elf.updateGotEntry(got_index); |
| | 6737 | } |
| | 6738 | if (elf.got.getIndex(.{ .tpoff = sym_id })) |got_index| { |
| | 6739 | elf.updateGotEntry(got_index); |
| | 6740 | } |
| | 6741 | if (elf.got.getIndex(.{ .tlsgd0 = sym_id })) |got_index| { |
| | 6742 | elf.updateGotEntry(got_index); |
| | 6743 | elf.updateGotEntry(got_index + 1); // tlsgd1 |
| | 6744 | } |
| | 6745 | }, |
| | 6746 | }, |
| 6427 | } | 6747 | } |
| | 6748 | |
| | 6749 | break :task; |
| 6428 | } | 6750 | } |
| 6429 | while (elf.mf.updates.pop()) |ni| { | 6751 | while (elf.mf.updates.pop()) |ni| { |
| 6430 | const clean_moved = ni.cleanMoved(&elf.mf); | 6752 | const clean_moved = ni.cleanMoved(&elf.mf); |
| ... | @@ -6838,7 +7160,7 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo | ... | @@ -6838,7 +7160,7 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo |
| 6838 | switch (elf.targetLoad(&next_ph.type)) { | 7160 | switch (elf.targetLoad(&next_ph.type)) { |
| 6839 | else => unreachable, | 7161 | else => unreachable, |
| 6840 | .NULL, .LOAD => {}, | 7162 | .NULL, .LOAD => {}, |
| 6841 | .DYNAMIC, .INTERP, .PHDR, .TLS, std.elf.PT.GNU_RELRO => break, | 7163 | .DYNAMIC, .INTERP, .PHDR, .TLS, .GNU_RELRO, .GNU_STACK => break, |
| 6842 | } | 7164 | } |
| 6843 | const next_vaddr = elf.targetLoad(&next_ph.vaddr); | 7165 | const next_vaddr = elf.targetLoad(&next_ph.vaddr); |
| 6844 | if (vaddr + memsz <= next_vaddr) break; | 7166 | if (vaddr + memsz <= next_vaddr) break; |
| ... | @@ -6917,8 +7239,8 @@ fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_ad | ... | @@ -6917,8 +7239,8 @@ fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_ad |
| 6917 | // its relocations are probably going through the PLT, so we don't bother with | 7239 | // its relocations are probably going through the PLT, so we don't bother with |
| 6918 | // specific tracking for PLT relocations---instead just re-apply all relocations | 7240 | // specific tracking for PLT relocations---instead just re-apply all relocations |
| 6919 | // targeting symbols with PLT entries. | 7241 | // targeting symbols with PLT entries. |
| 6920 | for (elf.plt.keys()) |sym| { | 7242 | for (elf.plt.keys()) |name| { |
| 6921 | sym.applyTargetRelocs(elf); | 7243 | Symbol.Id.global(name).applyTargetRelocs(elf); |
| 6922 | } | 7244 | } |
| 6923 | // We also need to update all of the references from `.plt.sec` to `.got.plt`. | 7245 | // We also need to update all of the references from `.plt.sec` to `.got.plt`. |
| 6924 | // However, if there's also a flush pending for `.got.plt`, don't bother doing | 7246 | // However, if there's also a flush pending for `.got.plt`, don't bother doing |