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 {
484484 };
485485 }
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
487493 fn rename(shndx: Index, elf: *Elf, new_name: []const u8) Error!void {
488494 const shstrtab_entry = try elf.string(.shstrtab, new_name);
489495 switch (elf.shdrPtr(shndx)) {
......@@ -491,8 +497,8 @@ const Section = struct {
491497 }
492498 }
493499
494 /// Asserts that `shndx` is a `SHT_RELA` section and ensures that its node has enough unused
495 /// space to hold `n` additional `ElfN.Rela` entries.
500 /// Asserts that `rela_shndx` is a `SHT_RELA` section and ensures that its node has enough
501 /// unused space to hold `n` additional `ElfN.Rela` entries.
496502 fn relaEnsureAdditionalCapacity(rela_shndx: Index, elf: *Elf, n: usize) Error!void {
497503 const node = rela_shndx.get(elf).ni;
498504 const need_size: u64 = switch (elf.shdrPtr(rela_shndx)) {
......@@ -518,9 +524,9 @@ const Section = struct {
518524 try elf.ensureNodeSize(node, need_size);
519525 }
520526
521 /// Asserts that `shndx` is a `SHT_RELA` section and deletes the `ElfN.Rela` entry at the
522 /// given `index` in it. The entry is added to the free-list for reuse later. Asserts that
523 /// the relocation entry at `index` is not already free.
527 /// Asserts that `rela_shndx` is a `SHT_RELA` section and deletes the `ElfN.Rela` entry at
528 /// the given `index` in it. The entry is added to the free-list for reuse later. Asserts
529 /// that the relocation entry at `index` is not already free.
524530 fn relaDeleteOne(rela_shndx: Index, elf: *Elf, index: RelaIndex) void {
525531 switch (elf.shdrPtr(rela_shndx)) {
526532 inline else => |shdr, class| {
......@@ -557,9 +563,9 @@ const Section = struct {
557563 rela_shndx.get(elf).rela.free_head = index.toOptional();
558564 }
559565
560 /// Asserts that `shndx` is a `SHT_RELA` section and adds a new `ElfN.Rela` entry to it with
561 /// the given field values. Returns the index of the populated entry. Asserts that capacity
562 /// for this operation was already guaranteed using `relaEnsureAdditionalCapacity`.
566 /// Asserts that `rela_shndx` is a `SHT_RELA` section and adds a new `ElfN.Rela` entry to it
567 /// with the given field values. Returns the index of the populated entry. Asserts that
568 /// capacity for this operation was already guaranteed using `relaEnsureAdditionalCapacity`.
563569 fn relaAddOneAssumeCapacity(rela_shndx: Index, elf: *Elf, opts: struct {
564570 type: MachineRelocType,
565571 offset: u64,
......@@ -621,8 +627,8 @@ const Section = struct {
621627 }
622628 }
623629
624 /// Asserts that `shndx` is a `SHT_RELA` section and updates the `info.sym` field of the
625 /// `ElfN.Rela` entry at the given index. As with `relaAddOneAssumeCapacity`, the symbol
630 /// Asserts that `rela_shndx` is a `SHT_RELA` section and updates the `info.sym` field of
631 /// the `ElfN.Rela` entry at the given index. As with `relaAddOneAssumeCapacity`, the symbol
626632 /// index is a raw `u32`, because it may be an index into `.symtab` or an index into
627633 /// `.dynsym`. Asserts that `index` is not in the free-list (i.e. is not deleted).
628634 fn relaUpdateSym(rela_shndx: Index, elf: *Elf, index: RelaIndex, raw_sym_index: u32) void {
......@@ -646,7 +652,7 @@ const Section = struct {
646652 }
647653 }
648654
649 /// Asserts that `shndx` is a `SHT_RELA` section and updates the `offset` field of the
655 /// Asserts that `rela_shndx` is a `SHT_RELA` section and updates the `offset` field of the
650656 /// `ElfN.Rela` entry at the given index. Asserts that `index` is not in the free-list (i.e.
651657 /// it is not deleted).
652658 fn relaSetOffset(rela_shndx: Index, elf: *Elf, index: RelaIndex, new_offset: u64) void {
......@@ -667,7 +673,7 @@ const Section = struct {
667673 }
668674 }
669675
670 /// Asserts that `shndx` is a `SHT_RELA` section and updates the `offset` field of the
676 /// Asserts that `rela_shndx` is a `SHT_RELA` section and updates the `offset` field of the
671677 /// `ElfN.Rela` entry at the given index, by subtracting `old_base` and adding `new_base`.
672678 /// Asserts that `index` is not in the free-list (i.e. it is not deleted).
673679 fn relaAdjustOffset(rela_shndx: Index, elf: *Elf, index: RelaIndex, old_base: u64, new_base: u64) void {
......@@ -690,6 +696,28 @@ const Section = struct {
690696 },
691697 }
692698 }
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 }
693721 };
694722};
695723
......@@ -895,6 +923,16 @@ pub const MachineRelocType = union {
895923 .X86_64 => .{ .X86_64 = .COPY },
896924 };
897925 }
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 }
898936 pub fn jumpSlot(elf: *Elf) MachineRelocType {
899937 return switch (elf.ehdrField(.machine)) {
900938 else => unreachable,
......@@ -1026,6 +1064,15 @@ const SymbolReloc = struct {
10261064 /// do not apply any relocations ourselves). Otherwise, no symbol relocs use this type.
10271065 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
10291076 abs64,
10301077 abs32,
10311078 abs32s,
......@@ -1060,21 +1107,39 @@ const SymbolReloc = struct {
10601107 else => false,
10611108 };
10621109 }
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 }
10631118 };
10641119
10651120 fn apply(reloc: *const SymbolReloc, elf: *Elf) void {
10661121 assert(elf.ehdrField(.type) != .REL);
10671122 assert(reloc.node != .none);
1123
10681124 if (reloc.node.hasMoved(&elf.mf) or reloc.target.hasMoved(elf)) {
10691125 // There's no point applying the relocation now, because it will be re-applied by
10701126 // `flushMoved` at some point anyway.
10711127 return;
10721128 }
1073 if (reloc.rela_index != .none) {
1074 // This relocation has been lowered to a runtime relocation. Until that changes, it is
1075 // not our job to apply it.
1076 return;
1077 }
1129
1130 if (reloc.rela_index.unwrap()) |rela_index| switch (elf.classifySymbolValue(reloc.target)) {
1131 .static => unreachable,
1132 .dynamic => return, // the relocation happens at runtime
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 };
10781143 const node_vaddr: u64 = switch (elf.getNode(reloc.node)) {
10791144 .file => unreachable,
10801145 .ehdr => unreachable,
......@@ -1099,13 +1164,13 @@ const SymbolReloc = struct {
10991164 const target_value = sym_value +% @as(u64, @bitCast(reloc.addend));
11001165 type: switch (reloc.type) {
11011166 .write_rela => unreachable,
1102 .abs64 => std.mem.writeInt(
1167 .abs64, .dsorel64 => std.mem.writeInt(
11031168 u64,
11041169 dest_slice[0..8],
11051170 target_value,
11061171 target_endian,
11071172 ),
1108 .abs32 => std.mem.writeInt(
1173 .abs32, .dsorel32 => std.mem.writeInt(
11091174 u32,
11101175 dest_slice[0..4],
11111176 @intCast(target_value),
......@@ -1730,7 +1795,9 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{
17301795 }
17311796
17321797 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 {
17341801 // This STT_FUNC symbol might be defined externally, so it needs a PLT entry.
17351802 elf.addPltEntry(opts.name.strtab, new_global_ptr.dynsym_index);
17361803 },
......@@ -1847,7 +1914,9 @@ fn setGlobalSymbolValue(
18471914 // delete its newly-unnecessary runtime relocation to avoid a runtime dynamic linker error.
18481915 // This also allows the PLT entry to be reused---see `pltEntryIsDead`.
18491916 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 {
18511920 elf.shndx.rela_plt.relaDeleteOne(elf, @enumFromInt(plt_index));
18521921 assert(elf.pltEntryIsDead(plt_index));
18531922 }
......@@ -2323,8 +2392,8 @@ const Symbol = struct {
23232392 }
23242393 }
23252394
2326 /// Scans through all relocations targeting `sym_id` and deletes each one's dynamic
2327 /// relocation entry, if it has one.
2395 /// Scans through all relocations targeting `sym_id` and, for each one with a dynamic
2396 /// relocation entry, either deletes it or converts it to R_*_RELATIVE as required.
23282397 ///
23292398 /// Asserts we are creating a DSO.
23302399 fn deleteDynamicTargetRelocs(sym_id: Symbol.Id, elf: *Elf) void {
......@@ -2337,6 +2406,45 @@ const Symbol = struct {
23372406 reloc.deleteOutputRel(elf);
23382407 ri = reloc.next;
23392408 }
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 }
23402448 }
23412449
23422450 /// 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 {
23652473 return null;
23662474}
23672475
2368fn haveCanonicalSymbolDefinition(elf: *Elf, sym: Symbol.Id) bool {
2369 const global_name = switch (sym.unwrap()) {
2370 .local => return true,
2371 .global => |name| name,
2476fn 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,
23722493 };
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) orelse
2377 elf.globals.weak_def.getPtr(global_name) orelse
2378 return false; // no definition at all
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 };
23792506
2380 if (elf.base.comp.config.output_mode == .Exe) {
2381 // Symbols defined in executables cannot be preempted
2382 return true;
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 },
23832520 }
23842521
2385 const visibility: std.elf.STV = switch (elf.symPtr(global_ptr.symtab_index)) {
2386 inline else => |sym_ptr| elf.targetLoad(&sym_ptr.other).visibility,
2387 };
2388 return switch (visibility) {
2389 .INTERNAL, .HIDDEN, .PROTECTED => true, // protection prevents preemption
2390 .DEFAULT => false,
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 },
23912545 };
23922546}
23932547
......@@ -3532,19 +3686,19 @@ fn initHeaders(
35323686 });
35333687 elf.plt_first_symbol_reloc = @enumFromInt(elf.symbol_relocs.items.len);
35343688 try elf.ensureUnusedRelocCapacity(plt_ni, 2);
3535 try elf.addRelocAssumeCapacity(
3689 try elf.addSymbolRelocAssumeCapacity(
35363690 plt_ni,
35373691 2,
35383692 got_plt_sym,
35393693 8 * 1 - 4,
3540 .{ .X86_64 = .PC32 },
3694 .rel32,
35413695 );
3542 try elf.addRelocAssumeCapacity(
3696 try elf.addSymbolRelocAssumeCapacity(
35433697 plt_ni,
35443698 8,
35453699 got_plt_sym,
35463700 8 * 2 - 4,
3547 .{ .X86_64 = .PC32 },
3701 .rel32,
35483702 );
35493703 },
35503704 .LOONGARCH => {
......@@ -3575,9 +3729,9 @@ fn initHeaders(
35753729 });
35763730 elf.plt_first_symbol_reloc = @enumFromInt(elf.symbol_relocs.items.len);
35773731 try elf.ensureUnusedRelocCapacity(plt_ni, 3);
3578 try elf.addRelocAssumeCapacity(plt_ni, 0, got_plt_sym, 0, .{ .LOONGARCH = .PCALA_HI20 });
3579 try elf.addRelocAssumeCapacity(plt_ni, 8, got_plt_sym, 0, .{ .LOONGARCH = .PCALA_LO12 });
3580 try elf.addRelocAssumeCapacity(plt_ni, 16, got_plt_sym, 0, .{ .LOONGARCH = .PCALA_LO12 });
3732 try elf.addSymbolRelocAssumeCapacity(plt_ni, 0, got_plt_sym, 0, .rel32_hi20);
3733 try elf.addSymbolRelocAssumeCapacity(plt_ni, 8, got_plt_sym, 0, .abs32_lo12);
3734 try elf.addSymbolRelocAssumeCapacity(plt_ni, 16, got_plt_sym, 0, .abs32_lo12);
35813735 },
35823736 }
35833737 }
......@@ -3895,13 +4049,11 @@ fn flushMovedNodeRelocs(
38954049 for (elf.symbol_relocs.items[@intFromEnum(first_symbol_reloc)..]) |*reloc| {
38964050 if (reloc.node != node) break;
38974051 if (reloc.rela_index.unwrap()) |rela_index| {
3898 // Update the offsets of any `ElfN.Rela` entry we've emitted, since the node they're
3899 // in has moved, so their offset within the section might also have moved.
4052 // The node has moved, so the offset of the relocation within the section might have
4053 // changed, so update the `offset` field of the `ElfN.Rela` entry.
39004054 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);
39044055 }
4056 reloc.apply(elf);
39054057 }
39064058 }
39074059
......@@ -4223,7 +4375,7 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) Error!Node
42234375 } else if (ip.isFunctionType(nav.resolved.?.type)) {
42244376 break :section .text;
42254377 } 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
42274379 }
42284380 };
42294381 const alignment: InternPool.Alignment = switch (Type.fromInterned(nav.resolved.?.type).zigTypeTag(zcu)) {
......@@ -4289,7 +4441,7 @@ fn uavMapIndex(
42894441 const uav_gop = elf.uavs.getOrPutAssumeCapacity(uav_val);
42904442 const umi: Node.UavMapIndex = @enumFromInt(uav_gop.index);
42914443 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
42934445 const node = try elf.mf.addLastChildNode(gpa, shndx.get(elf).ni, .{
42944446 .moved = true, // see assert at end of `flushUav`
42954447 .alignment = resolved_align.toStdMem(),
......@@ -5293,6 +5445,10 @@ fn prelinkInner(elf: *Elf) Error!void {
52935445 }
52945446 break :rpath try elf.string(.dynstr, buf.items);
52955447 };
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);
52965452 const soname: ?String(.dynstr) = if (elf.options.soname) |soname_slice| str: {
52975453 break :str try elf.string(.dynstr, soname_slice);
52985454 } else null;
......@@ -5303,7 +5459,8 @@ fn prelinkInner(elf: *Elf) Error!void {
53035459 @as(usize, @intFromBool(elf.shndx.init_array != .UNDEF)) * 2 +
53045460 @as(usize, @intFromBool(elf.shndx.fini_array != .UNDEF)) * 2 +
53055461 @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;
53075464 const dynamic_size: u32 = @intCast(@sizeOf(ElfN.Addr) * 2 * dynamic_len);
53085465 const dynamic_ni = elf.shndx.dynamic.get(elf).ni;
53095466 try dynamic_ni.resize(&elf.mf, gpa, dynamic_size);
......@@ -5315,6 +5472,8 @@ fn prelinkInner(elf: *Elf) Error!void {
53155472 init_array: ?usize,
53165473 fini_array: ?usize,
53175474 preinit_array: ?usize,
5475 jmprel: ?usize,
5476 pltgot: ?usize,
53185477 } = indices: {
53195478 const sec_dynamic = dynamic_ni.slice(&elf.mf);
53205479 const dynamic_entries: [][2]ElfN.Addr = @ptrCast(@alignCast(sec_dynamic));
......@@ -5347,7 +5506,7 @@ fn prelinkInner(elf: *Elf) Error!void {
53475506 }
53485507 const init_array_index: ?usize = if (elf.shndx.init_array != .UNDEF) i: {
53495508 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
53515510 .{ std.elf.DT_INIT_ARRAYSZ, elf.targetLoad(
53525511 &@field(elf.shdrPtr(elf.shndx.init_array), @tagName(ct_class)).size,
53535512 ) },
......@@ -5357,7 +5516,7 @@ fn prelinkInner(elf: *Elf) Error!void {
53575516 } else null;
53585517 const fini_array_index: ?usize = if (elf.shndx.fini_array != .UNDEF) i: {
53595518 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
53615520 .{ std.elf.DT_FINI_ARRAYSZ, elf.targetLoad(
53625521 &@field(elf.shdrPtr(elf.shndx.fini_array), @tagName(ct_class)).size,
53635522 ) },
......@@ -5367,7 +5526,7 @@ fn prelinkInner(elf: *Elf) Error!void {
53675526 } else null;
53685527 const preinit_array_index: ?usize = if (elf.shndx.preinit_array != .UNDEF) i: {
53695528 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
53715530 .{ std.elf.DT_PREINIT_ARRAYSZ, elf.targetLoad(
53725531 &@field(elf.shdrPtr(elf.shndx.preinit_array), @tagName(ct_class)).size,
53735532 ) },
......@@ -5375,27 +5534,33 @@ fn prelinkInner(elf: *Elf) Error!void {
53755534 defer dynamic_index += 2;
53765535 break :i dynamic_index;
53775536 } else null;
5378 dynamic_entries[dynamic_index..][0..12].* = .{
5379 .{ std.elf.DT_RELA, @intCast(elf.shndx.rela_dyn.vaddr(elf)) },
5537 const jmprel_index: ?usize, const pltgot_index: ?usize = if (use_plt) i: {
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
53805551 .{ std.elf.DT_RELASZ, elf.targetLoad(
53815552 &@field(elf.shdrPtr(elf.shndx.rela_dyn), @tagName(ct_class)).size,
53825553 ) },
53835554 .{ std.elf.DT_RELAENT, @sizeOf(ElfN.Rela) },
5384 .{ std.elf.DT_JMPREL, @intCast(elf.shndx.rela_plt.vaddr(elf)) },
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)) },
5555 .{ std.elf.DT_SYMTAB, 0 }, // reloc added below
53915556 .{ 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
53935558 .{ std.elf.DT_STRSZ, elf.targetLoad(
53945559 &@field(elf.shdrPtr(elf.shndx.dynstr), @tagName(ct_class)).size,
53955560 ) },
53965561 .{ std.elf.DT_NULL, 0 },
53975562 };
5398 dynamic_index += 12;
5563 dynamic_index += 8;
53995564 assert(dynamic_index == dynamic_len);
54005565 if (elf.targetEndian() != native_endian) for (dynamic_entries) |*dynamic_entry|
54015566 std.mem.byteSwapAllFields(@TypeOf(dynamic_entry.*), dynamic_entry);
......@@ -5404,66 +5569,74 @@ fn prelinkInner(elf: *Elf) Error!void {
54045569 .init_array = init_array_index,
54055570 .fini_array = fini_array_index,
54065571 .preinit_array = preinit_array_index,
5572 .jmprel = jmprel_index,
5573 .pltgot = pltgot_index,
54075574 };
54085575 };
54095576
5577 const dsorel: SymbolReloc.Type = switch (ct_class) {
5578 .NONE, _ => comptime unreachable,
5579 .@"32" => .dsorel32,
5580 .@"64" => .dsorel64,
5581 };
5582
54105583 elf.dynamic_first_symbol_reloc = @enumFromInt(elf.symbol_relocs.items.len);
54115584 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(
54135586 dynamic_ni,
54145587 @sizeOf(ElfN.Addr) * (2 * index + 1),
54155588 .local(elf.shndx.init_array.get(elf).lsi),
54165589 0,
5417 .absAddr(elf),
5590 dsorel,
54185591 );
5419 if (dynamic_indices.fini_array) |index| try elf.addRelocAssumeCapacity(
5592 if (dynamic_indices.fini_array) |index| try elf.addSymbolRelocAssumeCapacity(
54205593 dynamic_ni,
54215594 @sizeOf(ElfN.Addr) * (2 * index + 1),
54225595 .local(elf.shndx.fini_array.get(elf).lsi),
54235596 0,
5424 .absAddr(elf),
5597 dsorel,
54255598 );
5426 if (dynamic_indices.preinit_array) |index| try elf.addRelocAssumeCapacity(
5599 if (dynamic_indices.preinit_array) |index| try elf.addSymbolRelocAssumeCapacity(
54275600 dynamic_ni,
54285601 @sizeOf(ElfN.Addr) * (2 * index + 1),
54295602 .local(elf.shndx.preinit_array.get(elf).lsi),
54305603 0,
5431 .absAddr(elf),
5604 dsorel,
54325605 );
5433 try elf.addRelocAssumeCapacity(
5606 if (dynamic_indices.jmprel) |index| try elf.addSymbolRelocAssumeCapacity(
54345607 dynamic_ni,
5435 @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 12) + 1),
5436 .local(elf.shndx.rela_dyn.get(elf).lsi),
5608 @sizeOf(ElfN.Addr) * (2 * index + 1),
5609 .local(elf.shndx.rela_plt.get(elf).lsi),
54375610 0,
5438 .absAddr(elf),
5611 dsorel,
54395612 );
5440 try elf.addRelocAssumeCapacity(
5613 if (dynamic_indices.pltgot) |index| try elf.addSymbolRelocAssumeCapacity(
54415614 dynamic_ni,
5442 @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 9) + 1),
5443 .local(elf.shndx.rela_plt.get(elf).lsi),
5615 @sizeOf(ElfN.Addr) * (2 * index + 1),
5616 .local(elf.shndx.got_plt.get(elf).lsi),
54445617 0,
5445 .absAddr(elf),
5618 dsorel,
54465619 );
5447 try elf.addRelocAssumeCapacity(
5620 try elf.addSymbolRelocAssumeCapacity(
54485621 dynamic_ni,
5449 @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 7) + 1),
5450 .local(elf.shndx.got_plt.get(elf).lsi),
5622 @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 8) + 1),
5623 .local(elf.shndx.rela_dyn.get(elf).lsi),
54515624 0,
5452 .absAddr(elf),
5625 dsorel,
54535626 );
5454 try elf.addRelocAssumeCapacity(
5627 try elf.addSymbolRelocAssumeCapacity(
54555628 dynamic_ni,
54565629 @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 5) + 1),
54575630 .local(elf.shndx.dynsym.get(elf).lsi),
54585631 0,
5459 .absAddr(elf),
5632 dsorel,
54605633 );
5461 try elf.addRelocAssumeCapacity(
5634 try elf.addSymbolRelocAssumeCapacity(
54625635 dynamic_ni,
54635636 @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 3) + 1),
54645637 .local(elf.shndx.dynstr.get(elf).lsi),
54655638 0,
5466 .absAddr(elf),
5639 dsorel,
54675640 );
54685641 },
54695642 };
......@@ -5802,16 +5975,34 @@ fn addSymbolRelocAssumeCapacity(
58025975 @"type": SymbolReloc.Type,
58035976) Error!void {
58045977 assert(elf.ehdrField(.type) != .REL);
5978 assert(node != .none);
58055979
58065980 const rela_index: Section.RelaIndex.Optional = r: {
5807 if (elf.haveCanonicalSymbolDefinition(target)) break :r .none;
5808 // If the definition is (potentially) external, `target` must be global.
5809 const global_name = target.unwrap().global;
5981 // If we emit a runtime relocation entry, its `offset` is a virtual address, so we need to
5982 // determine the vaddr of `node`.
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
58115998 const rela_type: MachineRelocType = switch (elf.ehdrField(.machine)) {
58125999 else => |machine| @panic(@tagName(machine)),
58136000 .X86_64 => .{ .X86_64 = switch (@"type") {
58146001 .write_rela => unreachable,
6002 .dsorel64, .dsorel32 => {
6003 assert(target.unwrap() == .local);
6004 break :r .none;
6005 },
58156006 .abs64 => .@"64",
58166007 .abs32 => .@"32",
58176008 .abs32s => .@"32S",
......@@ -5839,65 +6030,79 @@ fn addSymbolRelocAssumeCapacity(
58396030 .tpoff64_hi12,
58406031 => unreachable,
58416032 } },
5842 .LOONGARCH => .{
5843 .LOONGARCH = switch (@"type") {
5844 .write_rela => unreachable,
5845 .abs64 => .@"64",
5846 .abs32 => .@"32",
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,
6033 .LOONGARCH => .{ .LOONGARCH = switch (@"type") {
6034 .write_rela => unreachable,
6035 .dsorel64, .dsorel32 => {
6036 assert(target.unwrap() == .local);
6037 break :r .none;
58676038 },
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 } },
58696062 };
58706063
5871 const dynsym_index = elf.globalByName(global_name).?.dynsym_index;
5872 assert(dynsym_index != 0);
5873
5874 switch (elf.nodeWantsDsoRelocation(node)) {
5875 .no => break :r .none,
5876 .yes => {},
5877 .yes_textrel => if (try elf.maybeAddCopyRelocation(global_name)) {
5878 // We were able to use a copy relocation on this symbol to avoid a text relocation,
5879 // which is apparently considered a good thing despite copy relocations being an
5880 // abomination. (This is necessary for correctness in some cases, because e.g. a
5881 // 32-bit runtime relocation on a 64-bit target will often cause rtld errors due to
5882 // the DSOs being loaded too far apart.)
5883 break :r .none;
5884 } else {
5885 // At least for now, our only choice is a text relocation.
5886 elf.textrel_count += 1;
6064 class: switch (elf.classifySymbolValue(target)) {
6065 .static => break :r .none,
6066 .static_relative => {
6067 if (!@"type".isAbsAddr(elf)) break :r .none;
6068 switch (elf.nodeWantsDsoRelocation(node)) {
6069 .no => break :r .none,
6070 .yes => {},
6071 .yes_textrel => elf.textrel_count += 1,
6072 }
6073 break :r elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{
6074 .type = .relative(elf),
6075 .offset = node_vaddr + offset,
6076 .raw_sym_index = 0,
6077 .addend = 0,
6078 }).toOptional();
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(),
58876104 },
58886105 }
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();
59016106 };
59026107
59036108 const ri: SymbolReloc.Index = @enumFromInt(elf.symbol_relocs.items.len);
......@@ -5920,6 +6125,9 @@ fn addSymbolRelocAssumeCapacity(
59206125 if (@"type".dependsOnTlsSize()) {
59216126 elf.tls_size_symbol_relocs.putAssumeCapacityNoClobber(ri, {});
59226127 }
6128
6129 // Actually apply the new relocation!
6130 ri.get(elf).apply(elf);
59236131}
59246132fn addGotRelocAssumeCapacity(
59256133 elf: *Elf,
......@@ -5997,7 +6205,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
59976205 .reserved => .{ .unsigned = 0 },
59986206 .tpoff => |sym_id| val: {
59996207 // 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) {
60016209 const tls_phndx = elf.getNode(elf.ni.tls).segment;
60026210 const tls_size: u64 = switch (elf.phdrSlice()) {
60036211 inline else => |phdr| tls_size: {
......@@ -6029,33 +6237,52 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
60296237 } },
60306238 };
60316239 },
6032 .symbol, .tlsgd1 => |sym, tag| val: {
6033 if (elf.haveCanonicalSymbolDefinition(sym)) {
6034 break :val .{ .unsigned = sym.value(elf) };
6035 }
6036 break :val .{ .reloc = .{
6037 .type = if (tag == .symbol) .globDat(elf) else .dtpOffAddr(elf),
6240 .symbol => |sym| switch (elf.classifySymbolValue(sym)) {
6241 .static => .{ .unsigned = sym.value(elf) },
6242 .static_relative => .{ .reloc = .{
6243 .type = .relative(elf),
6244 .dynsym_index = 0,
6245 .addend = @bitCast(sym.value(elf)),
6246 } },
6247 .dynamic => .{ .reloc = .{
6248 .type = .globDat(elf),
60386249 .dynsym_index = elf.globalByName(sym.unwrap().global).?.dynsym_index,
60396250 .addend = 0,
6040 } };
6251 } },
60416252 },
6042 .tlsgd0 => |sym| switch (elf.shndx.dynamic) {
6043 .UNDEF => .{ .unsigned = 1 }, // TLS module ID for exexcutable
6044 else => .{
6045 .reloc = .{
6046 .type = switch (elf.ehdrField(.machine)) {
6047 else => |machine| @panic(@tagName(machine)),
6048 .X86_64 => .{ .X86_64 = .DTPMOD64 },
6049 .LOONGARCH => .{ .LOONGARCH = if (elf.identClass() == .@"64") .TLS_DTPMOD64 else .TLS_DTPMOD32 },
6050 },
6051 .dynsym_index = if (elf.haveCanonicalSymbolDefinition(sym)) 0 else elf.globalByName(sym.unwrap().global).?.dynsym_index,
6052 .addend = 0,
6053 },
6253 .tlsgd1 => |sym| switch (elf.classifySymbolValue(sym)) {
6254 .static => .{ .unsigned = sym.value(elf) },
6255 .static_relative => unreachable, // TLS variables should be in TLS sections, which do not return `.static_relative`
6256 .dynamic => .{ .reloc = .{
6257 .type = .dtpOffAddr(elf),
6258 .dynsym_index = elf.globalByName(sym.unwrap().global).?.dynsym_index,
6259 .addend = 0,
6260 } },
6261 },
6262 .tlsgd0 => |sym| switch (elf.base.comp.config.link_mode) {
6263 .static => val: {
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
60546266 },
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 } },
60556279 },
6056 .tlsld0 => switch (elf.shndx.dynamic) {
6057 .UNDEF => .{ .unsigned = 1 }, // TLS module ID for exexcutable
6058 else => .{ .reloc = .{
6280 .tlsld0 => switch (elf.base.comp.config.link_mode) {
6281 .static => val: {
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 = .{
60596286 .type = switch (elf.ehdrField(.machine)) {
60606287 else => |machine| @panic(@tagName(machine)),
60616288 .X86_64 => .{ .X86_64 = .DTPMOD64 },
......@@ -6470,44 +6697,52 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool {
64706697 ri = reloc.next;
64716698 }
64726699 },
6473 else => {
6474 // Index in `.dynsym` has changed. This case is slightly trickier because there
6475 // are a few things which may have emitted runtime relocations, including symbol
6476 // relocs...
6477 const dynsym_index = global.dynsym_index;
6478 var ri = sym.first_target_reloc;
6479 while (ri != .none) {
6480 const reloc = ri.get(elf);
6481 assert(reloc.target == sym_id);
6482 // There may or may not be a runtime relocation for this symbol reloc.
6483 if (reloc.rela_index.unwrap()) |rela_index| {
6484 reloc.relaSection(elf).relaUpdateSym(elf, rela_index, dynsym_index);
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;
64856726 }
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...
6495 if (elf.plt.getIndex(global_name)) |plt_index| {
6496 // PLT indices exactly match `.rela.plt` relocation indices.
6497 elf.shndx.rela_plt.relaUpdateSym(elf, @enumFromInt(plt_index), dynsym_index);
6498 }
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 }
64996733
6500 // ...and any relevant GOT entries.
6501 if (elf.got.getIndex(.{ .symbol = sym_id })) |got_index| {
6502 elf.updateGotEntry(got_index);
6503 }
6504 if (elf.got.getIndex(.{ .tpoff = sym_id })) |got_index| {
6505 elf.updateGotEntry(got_index);
6506 }
6507 if (elf.got.getIndex(.{ .tlsgd0 = sym_id })) |got_index| {
6508 elf.updateGotEntry(got_index);
6509 elf.updateGotEntry(got_index + 1); // tlsgd1
6510 }
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 },
65116746 },
65126747 }
65136748
test/standalone/elf2/build.zig+11-6
......@@ -3,18 +3,21 @@ pub fn build(b: *Build) void {
33 b.default_step = test_step;
44
55 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");
7 addOne(b, test_step, b.graph.host, false, .dynamic, "elf2-hello-native-selfhosted-dynamic");
8 addOne(b, test_step, b.graph.host, true, .static, "elf2-hello-native-llvm-static");
9 addOne(b, test_step, b.graph.host, true, .dynamic, "elf2-hello-native-llvm-dynamic");
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, false, "elf2-hello-native-selfhosted-dynamic");
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, 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");
1012 }
1113
1214 const x86_64_linux_target: Build.ResolvedTarget = b.resolveTargetQuery(.{
1315 .cpu_arch = .x86_64,
1416 .os_tag = .linux,
1517 });
16 addOne(b, test_step, x86_64_linux_target, false, .static, "elf2-hello-selfhosted-static");
17 addOne(b, test_step, x86_64_linux_target, true, .static, "elf2-hello-llvm-static");
18 addOne(b, test_step, x86_64_linux_target, false, .static, false, "elf2-hello-selfhosted-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");
1821}
1922
2023fn addOne(
......@@ -23,6 +26,7 @@ fn addOne(
2326 target: Build.ResolvedTarget,
2427 use_llvm: bool,
2528 link_mode: std.lang.LinkMode,
29 pie: bool,
2630 name: []const u8,
2731) void {
2832 const mod = b.createModule(.{
......@@ -38,6 +42,7 @@ fn addOne(
3842 });
3943 exe.use_new_linker = true;
4044 exe.use_llvm = use_llvm;
45 if (pie) exe.pie = true;
4146
4247 const run = b.addRunArtifact(exe);
4348 run.expectExitCode(0);