authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-07-06 13:09:15+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-07-07 13:05:31+01:00
logbd0a3f0a0c8058cbad1d8e110fe900ec877c1909
treee8f8516f749b8b0f7890dd4f1d95f1f0eb49d653
parent1d6305631e134696a57d93575f97ab3d8c1fe5cf
signaturelock-open Commit is signed but in an unrecognized format.

Elf2: implement `R_*_RELATIVE` relocations

This makes it possible to build position-independent ELF modules---that is, shared libraries and PIEs (shared and static). Previously, such compilations would quickly segfault at runtime due to the module's load address not being applied to absolute addresses in the file. This includes a handful of other fixes necessary to get PIEs working: * Don't include `.dynamic` entries relating to the PLT in static PIEs * Don't emit runtime relocations targeting `.dynamic` entries (addresses in `.dynamic` entries are module-relative rather than absolute) * Put constant data which may contain pointers into `.data.rel.ro` instead of `.data` or `.rodata`

2 files changed, 452 insertions(+), 212 deletions(-)

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