authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-07-07 23:12:47+02:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-07-07 23:12:47+02:00
log2a1d559d67a8ca47e8c0a70e83272514480c9f7c
tree59bf02e828a15133c1717a7fce8081bcc232f961
parent5b2082c6c96015adac68ff535a2ca2f05076b7e1
parent6c28d6cce8ada3317cfdec39640d001a3ae35c98

Merge pull request 'Elf2: various enhancements' (#36069) from mlugg/elf2-enhancements into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/36069

3 files changed, 885 insertions(+), 428 deletions(-)

lib/std/os/linux/tls.zig+138-8
......@@ -14,7 +14,8 @@ const mem = std.mem;
1414const elf = std.elf;
1515const math = std.math;
1616const assert = std.debug.assert;
17const native_arch = @import("builtin").cpu.arch;
17const builtin = @import("builtin");
18const native_arch = builtin.cpu.arch;
1819const linux = std.os.linux;
1920const page_size_min = std.heap.page_size_min;
2021
......@@ -41,13 +42,13 @@ const Variant = enum {
4142 I_original,
4243 /// The modified Variant I:
4344 ///
44 /// ---------------------------------------------------
45 /// | DTV | Zig TCB | ABI TCB | [Offset] | TLS Blocks |
46 /// -------------------------------------^-------------
47 /// `-- The TP register points here.
45 /// --------------------------------------------
46 /// | DTV | Zig TCB | ABI TCB | TLS Blocks |
47 /// ------------------------------^-------------
48 /// `-- The TP register points here (*inside* the TLS blocks).
4849 ///
49 /// The offset (which can be zero) is applied to the TP only; there is never a physical gap
50 /// between the ABI TCB and the TLS blocks. This implies that we only need to align the TP.
50 /// The offset from the start of the TLS blocks to the TP register is `current_tp_offset`. It
51 /// may be zero, in which case the TP register points to the start of the TLS blocks.
5152 ///
5253 /// The first (and only) word in the ABI TCB points to the DTV.
5354 I_modified,
......@@ -106,7 +107,7 @@ const current_variant: Variant = switch (native_arch) {
106107 else => @compileError("undefined TLS variant for this architecture"),
107108};
108109
109/// The Offset value for the modified Variant I.
110/// The offset value for the modified Variant I.
110111const current_tp_offset = switch (native_arch) {
111112 .m68k,
112113 .mips,
......@@ -379,6 +380,106 @@ pub fn setThreadPointer(addr: usize) void {
379380 }
380381}
381382
383pub fn getThreadPointer() usize {
384 @setRuntimeSafety(false);
385 @disableInstrumentation();
386
387 return switch (native_arch) {
388 .aarch64, .aarch64_be => asm (
389 \\ mrs %[ret], tpidr_el0
390 : [ret] "=r" (-> usize),
391 ),
392 .alpha => asm (
393 \\ rduniq
394 : [ret] "={$0}" (-> usize),
395 ),
396 .arc, .arceb => asm (
397 \\ mov %[ret], r25
398 : [ret] "=r" (-> usize),
399 ),
400 .arm, .armeb, .thumb, .thumbeb => asm (
401 \\ mrc p15, 0, %[ret], c13, c0, 3
402 : [ret] "=r" (-> usize),
403 ),
404 .csky => asm (
405 \\ mov %[ret], r31
406 : [ret] "=r" (-> usize),
407 ),
408 .hexagon => asm (
409 \\ %[ret] = ugp
410 : [ret] "=r" (-> usize),
411 ),
412 .hppa => asm (
413 \\ mfctl %%cr27, %[ret]
414 : [ret] "=r" (-> usize),
415 ),
416 .loongarch32, .loongarch64 => asm (
417 \\ move %[ret], $tp
418 : [ret] "=r" (-> usize),
419 ),
420 .m68k => linux.syscall1(.get_thread_area),
421 .mips, .mipsel, .mips64, .mips64el => asm (
422 \\ rdhwr %[ret], $29
423 : [ret] "=r" (-> usize),
424 ),
425 .microblaze, .microblazeel => asm (
426 \\ ori %[ret], r21, 0
427 : [ret] "=r" (-> usize),
428 ),
429 .or1k => asm (
430 \\ l.ori %[ret], r10, 0
431 : [ret] "=r" (-> usize),
432 ),
433 .riscv32, .riscv64 => asm (
434 \\ mv %[ret], tp
435 : [ret] "=r" (-> usize),
436 ),
437 .powerpc, .powerpcle => asm (
438 \\ mr %[ret], 2
439 : [ret] "=r" (-> usize),
440 ),
441 .powerpc64, .powerpc64le => asm (
442 \\ mr %[ret], 13
443 : [ret] "=r" (-> usize),
444 ),
445 .s390x => asm (
446 \\ ear %[ret], %%a0
447 \\ sllg %[ret], %[ret], 32
448 \\ ear %[ret], %%a1
449 : [ret] "=r" (-> usize),
450 ),
451 .sh, .sheb => asm (
452 \\ stc %[ret], gbr
453 : [ret] "=r" (-> usize),
454 ),
455 .sparc, .sparc64 => asm (
456 \\ mov %%g7, %[ret]
457 : [ret] "=r" (-> usize),
458 ),
459 .x86 => asm (
460 \\ movl %%gs:0, %[ret]
461 : [ret] "=r" (-> usize),
462 ),
463 .x86_64 => switch (@sizeOf(usize)) {
464 8 => asm (
465 \\ movq %%fs:0, %[ret]
466 : [ret] "=r" (-> usize),
467 ),
468 // On x32, usize is 32 bits.
469 4 => asm (
470 \\ movl %%fs:0, %[ret]
471 : [ret] "=r" (-> usize),
472 ),
473 else => comptime unreachable,
474 },
475 .xtensa, .xtensaeb => asm (
476 \\ rur %[ret], threadptr
477 : [ret] "=r" (-> usize),
478 ),
479 else => @compileError("Unsupported architecture"),
480 };
481}
482
382483fn computeAreaDesc(phdrs: []elf.Phdr) void {
383484 @setRuntimeSafety(false);
384485 @disableInstrumentation();
......@@ -616,3 +717,32 @@ inline fn mmap_tls(length: usize) usize {
616717 });
617718 }
618719}
720
721comptime {
722 assert(!builtin.link_libc); // otherwise libc should control TLS
723
724 if (builtin.output_mode == .Exe and builtin.link_mode == .static) {
725 // This is a static executable without libc, so it is our job to provide the TLS accessor
726 // function for the GD and LD models. This function is unlikely to actually be used, since
727 // the linker should be able to relax every TLS access to the LE model and therefore
728 // eliminate all calls to this function, but that isn't guaranteed.
729 _ = struct {
730 const TlsIndex = switch (native_arch) {
731 .x86_64 => extern struct { module: u64, offset: u64 }, // Even for x32...
732 else => extern struct { module: usize, offset: usize }, // ...but not MIPS N32!
733 };
734 export fn __tls_get_addr(ti: *const TlsIndex) *anyopaque {
735 assert(ti.module == 1); // The executable's module ID is always 1
736 const tp = getThreadPointer();
737 const block: [*]u8 = switch (current_variant) {
738 .I_original => @ptrFromInt(tp -% area_desc.abi_tcb.offset +% area_desc.block.offset),
739 .I_modified => @ptrFromInt(tp -% current_tp_offset),
740 // The `.I_original` approach would also work for `.II`, but there is an
741 // alternative strategy which is one less operation:
742 .II => @ptrFromInt(tp -% area_desc.block.size),
743 };
744 return block[@intCast(ti.offset)..];
745 }
746 };
747 }
748}
src/link/Elf2.zig+736-414
......@@ -100,13 +100,15 @@ dynstr: StringTable,
100100///
101101/// Value is the output relocation in `.rela.dyn` for the GOT entry.
102102got: std.array_hash_map.Auto(GotKey, Section.RelaIndex.Optional),
103/// Key is the name of a global.
104///
103105/// Indices map 1--1 to indices into the actual `.got.plt` section. These also equal indices into
104106/// the relocations in `.rela.plt`, because every PLT entry has one output relocation (if a runtime
105107/// relocation is no longer necessary, then neither is the corresponding PLT entry!).
106108///
107109/// PLT entries in this map may be "dead", meaning the PLT entry has been deemed unnecessary so is
108110/// available for reuse---see `Elf.pltEntryIsDead`. Such entries must not be targeted by relocs.
109plt: std.array_hash_map.Auto(Symbol.Id, void),
111plt: std.array_hash_map.Auto(String(.strtab), void),
110112/// The `.plt` section contains zero or more symbol relocations starting at this index.
111113plt_first_symbol_reloc: SymbolReloc.Index,
112114/// The `.dynamic` section contains zero or more symbol relocations starting at this index.
......@@ -152,6 +154,9 @@ tls_size_symbol_relocs: std.array_hash_map.Auto(SymbolReloc.Index, void),
152154section_by_name: std.array_hash_map.Auto(String(.shstrtab), void),
153155/// Key is the name of a global symbol which has been moved to a new symtab index. Any relocation
154156/// entries which target that symbol must be updated to reference the correct symbol index.
157///
158/// When emitting a relocatable (`ET_REL`), this refers to the index in `.symtab`. Otherwise, it
159/// refers to the index in `.dynsym`.
155160changed_symtab_index: std.array_hash_map.Auto(String(.strtab), void),
156161/// Counts how many relocations are currently in `.rela.dyn` which would require a `DT_TEXTREL`
157162/// entry in the `.dynamic` section. This allows adding `DT_TEXTREL` to the output `.dynamic`
......@@ -474,9 +479,14 @@ const Section = struct {
474479 }
475480
476481 fn vaddr(s: Index, elf: *Elf) u64 {
477 return switch (s.get(elf).lsi) {
478 .null => 0,
479 else => |lsi| Symbol.Id.local(lsi).value(elf),
482 return switch (elf.shdrPtr(s)) {
483 inline else => |shdr| elf.targetLoad(&shdr.addr),
484 };
485 }
486
487 fn flags(s: Index, elf: *Elf) std.elf.SHF {
488 return switch (elf.shdrPtr(s)) {
489 inline else => |shdr| elf.targetLoad(&shdr.flags).shf,
480490 };
481491 }
482492
......@@ -487,8 +497,8 @@ const Section = struct {
487497 }
488498 }
489499
490 /// Asserts that `shndx` is a `SHT_RELA` section and ensures that its node has enough unused
491 /// 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.
492502 fn relaEnsureAdditionalCapacity(rela_shndx: Index, elf: *Elf, n: usize) Error!void {
493503 const node = rela_shndx.get(elf).ni;
494504 const need_size: u64 = switch (elf.shdrPtr(rela_shndx)) {
......@@ -514,9 +524,9 @@ const Section = struct {
514524 try elf.ensureNodeSize(node, need_size);
515525 }
516526
517 /// Asserts that `shndx` is a `SHT_RELA` section and deletes the `ElfN.Rela` entry at the
518 /// given `index` in it. The entry is added to the free-list for reuse later. Asserts that
519 /// 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.
520530 fn relaDeleteOne(rela_shndx: Index, elf: *Elf, index: RelaIndex) void {
521531 switch (elf.shdrPtr(rela_shndx)) {
522532 inline else => |shdr, class| {
......@@ -553,9 +563,9 @@ const Section = struct {
553563 rela_shndx.get(elf).rela.free_head = index.toOptional();
554564 }
555565
556 /// Asserts that `shndx` is a `SHT_RELA` section and adds a new `ElfN.Rela` entry to it with
557 /// the given field values. Returns the index of the populated entry. Asserts that capacity
558 /// 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`.
559569 fn relaAddOneAssumeCapacity(rela_shndx: Index, elf: *Elf, opts: struct {
560570 type: MachineRelocType,
561571 offset: u64,
......@@ -617,8 +627,8 @@ const Section = struct {
617627 }
618628 }
619629
620 /// Asserts that `shndx` is a `SHT_RELA` section and updates the `info.sym` field of the
621 /// `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
622632 /// index is a raw `u32`, because it may be an index into `.symtab` or an index into
623633 /// `.dynsym`. Asserts that `index` is not in the free-list (i.e. is not deleted).
624634 fn relaUpdateSym(rela_shndx: Index, elf: *Elf, index: RelaIndex, raw_sym_index: u32) void {
......@@ -642,7 +652,7 @@ const Section = struct {
642652 }
643653 }
644654
645 /// Asserts that `shndx` is a `SHT_RELA` section and updates the `offset` field of the
655 /// Asserts that `rela_shndx` is a `SHT_RELA` section and updates the `offset` field of the
646656 /// `ElfN.Rela` entry at the given index. Asserts that `index` is not in the free-list (i.e.
647657 /// it is not deleted).
648658 fn relaSetOffset(rela_shndx: Index, elf: *Elf, index: RelaIndex, new_offset: u64) void {
......@@ -663,7 +673,7 @@ const Section = struct {
663673 }
664674 }
665675
666 /// Asserts that `shndx` is a `SHT_RELA` section and updates the `offset` field of the
676 /// Asserts that `rela_shndx` is a `SHT_RELA` section and updates the `offset` field of the
667677 /// `ElfN.Rela` entry at the given index, by subtracting `old_base` and adding `new_base`.
668678 /// Asserts that `index` is not in the free-list (i.e. it is not deleted).
669679 fn relaAdjustOffset(rela_shndx: Index, elf: *Elf, index: RelaIndex, old_base: u64, new_base: u64) void {
......@@ -686,6 +696,28 @@ const Section = struct {
686696 },
687697 }
688698 }
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 }
689721 };
690722};
691723
......@@ -891,6 +923,16 @@ pub const MachineRelocType = union {
891923 .X86_64 => .{ .X86_64 = .COPY },
892924 };
893925 }
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 }
894936 pub fn jumpSlot(elf: *Elf) MachineRelocType {
895937 return switch (elf.ehdrField(.machine)) {
896938 else => unreachable,
......@@ -1022,6 +1064,15 @@ const SymbolReloc = struct {
10221064 /// do not apply any relocations ourselves). Otherwise, no symbol relocs use this type.
10231065 write_rela,
10241066
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
10251076 abs64,
10261077 abs32,
10271078 abs32s,
......@@ -1056,21 +1107,39 @@ const SymbolReloc = struct {
10561107 else => false,
10571108 };
10581109 }
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 }
10591118 };
10601119
10611120 fn apply(reloc: *const SymbolReloc, elf: *Elf) void {
10621121 assert(elf.ehdrField(.type) != .REL);
10631122 assert(reloc.node != .none);
1123
10641124 if (reloc.node.hasMoved(&elf.mf) or reloc.target.hasMoved(elf)) {
10651125 // There's no point applying the relocation now, because it will be re-applied by
10661126 // `flushMoved` at some point anyway.
10671127 return;
10681128 }
1069 if (reloc.rela_index != .none) {
1070 // This relocation has been lowered to a runtime relocation. Until that changes, it is
1071 // not our job to apply it.
1072 return;
1073 }
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 };
10741143 const node_vaddr: u64 = switch (elf.getNode(reloc.node)) {
10751144 .file => unreachable,
10761145 .ehdr => unreachable,
......@@ -1095,13 +1164,13 @@ const SymbolReloc = struct {
10951164 const target_value = sym_value +% @as(u64, @bitCast(reloc.addend));
10961165 type: switch (reloc.type) {
10971166 .write_rela => unreachable,
1098 .abs64 => std.mem.writeInt(
1167 .abs64, .dsorel64 => std.mem.writeInt(
10991168 u64,
11001169 dest_slice[0..8],
11011170 target_value,
11021171 target_endian,
11031172 ),
1104 .abs32 => std.mem.writeInt(
1173 .abs32, .dsorel32 => std.mem.writeInt(
11051174 u32,
11061175 dest_slice[0..4],
11071176 @intCast(target_value),
......@@ -1126,7 +1195,10 @@ const SymbolReloc = struct {
11261195 target_endian,
11271196 ),
11281197 .pltrel64 => {
1129 const plt_index = elf.plt.getIndex(reloc.target) orelse continue :type .rel64;
1198 const plt_index = switch (reloc.target.unwrap()) {
1199 .local => continue :type .rel64,
1200 .global => |name| elf.plt.getIndex(name) orelse continue :type .rel64,
1201 };
11301202 if (elf.pltEntryIsDead(plt_index)) continue :type .rel64;
11311203 const plt_shndx: Section.Index, const plt_entry_size: u64 = switch (elf.ehdrField(.machine)) {
11321204 else => |machine| @panic(@tagName(machine)),
......@@ -1141,7 +1213,10 @@ const SymbolReloc = struct {
11411213 );
11421214 },
11431215 .pltrel32 => {
1144 const plt_index = elf.plt.getIndex(reloc.target) orelse continue :type .rel32;
1216 const plt_index = switch (reloc.target.unwrap()) {
1217 .local => continue :type .rel32,
1218 .global => |name| elf.plt.getIndex(name) orelse continue :type .rel32,
1219 };
11451220 if (elf.pltEntryIsDead(plt_index)) continue :type .rel32;
11461221 const plt_shndx: Section.Index, const plt_entry_size: u64 = switch (elf.ehdrField(.machine)) {
11471222 else => |machine| @panic(@tagName(machine)),
......@@ -1323,12 +1398,9 @@ fn ensureUnusedSymbolCapacity(elf: *Elf, len: u32, kind: enum { all_local, maybe
13231398 try elf.symtab.ensureUnusedCapacity(gpa, len);
13241399
13251400 // If adding locals, we may need to move one global out of the way for each local. If adding
1326 // globals, they could all get demoted to STB_LOCAL, which would mean we move those N globals
1327 // *and* we move up to N other globals out of their way.
1328 try elf.changed_symtab_index.ensureUnusedCapacity(gpa, switch (kind) {
1329 .all_local => len,
1330 .maybe_global => len * 2,
1331 });
1401 // globals, they could all get demoted to STB_LOCAL, meaning we have to move N other globals
1402 // around to keep `.dynsym` compact. Either way, the maximum is N.
1403 try elf.changed_symtab_index.ensureUnusedCapacity(gpa, len);
13321404
13331405 {
13341406 // Ensure the symtab section's node is big enough
......@@ -1460,7 +1532,7 @@ fn addLocalSymbolAssumeCapacity(elf: *Elf, opts: AddLocalSymbolOptions) Symbol.L
14601532 const global_name: String(.strtab) = @enumFromInt(elf.targetLoad(&new_sym.name));
14611533 elf.globalByName(global_name).?.symtab_index = new_index;
14621534
1463 if (target_index.ptr(elf).first_target_reloc != .none) {
1535 if (elf.ehdrField(.type) == .REL and target_index.ptr(elf).first_target_reloc != .none) {
14641536 // This symbol's index is changing, so queue an update of relocs targeting it.
14651537 elf.changed_symtab_index.putAssumeCapacity(global_name, {});
14661538 }
......@@ -1722,16 +1794,14 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{
17221794 elf.moveDemotedGlobal(new_global_ptr);
17231795 }
17241796
1725 if (new_global_ptr.dynsym_index != 0 and
1726 opts.visibility == .DEFAULT and
1727 opts.shndx == .UNDEF and
1728 (@"type" == .FUNC or @"type" == std.elf.STT.GNU_IFUNC))
1729 {
1730 // We're adding an undefined global STT_FUNC symbol which could be resolved by another DSO.
1731 // We therefore might need a PLT entry, so let's add one now.
1732 elf.addPltEntry(opts.name.strtab, new_global_ptr.dynsym_index);
1733 // TODO: we also need to emit a PLT entry if the symbol could be preempted/interposed! By
1734 // not doing that we're basically implementing the behavior of `-Bsymbolic-functions`.
1797 switch (@"type") {
1798 .FUNC, .GNU_IFUNC => if (elf.ehdrField(.type) != .REL and
1799 elf.classifySymbolValue(.global(opts.name.strtab)) == .dynamic)
1800 {
1801 // This STT_FUNC symbol might be defined externally, so it needs a PLT entry.
1802 elf.addPltEntry(opts.name.strtab, new_global_ptr.dynsym_index);
1803 },
1804 else => {},
17351805 }
17361806
17371807 return .global(opts.name.strtab);
......@@ -1843,10 +1913,10 @@ fn setGlobalSymbolValue(
18431913 // If this symbol was previously undefined, it may have had a PLT entry. If so, we now need to
18441914 // delete its newly-unnecessary runtime relocation to avoid a runtime dynamic linker error.
18451915 // This also allows the PLT entry to be reused---see `pltEntryIsDead`.
1846 if (elf.plt.getIndex(.global(global_name))) |plt_index| {
1847 // TODO: we might still need the PLT entry if the symbol could be preempted/interposed! See
1848 // matching comment at the end of `addGlobalSymbolAssumeCapacity`.
1849 if (!elf.pltEntryIsDead(plt_index)) {
1916 if (elf.plt.getIndex(global_name)) |plt_index| {
1917 if (!elf.pltEntryIsDead(plt_index) and
1918 elf.classifySymbolValue(.global(global_name)) != .dynamic)
1919 {
18501920 elf.shndx.rela_plt.relaDeleteOne(elf, @enumFromInt(plt_index));
18511921 assert(elf.pltEntryIsDead(plt_index));
18521922 }
......@@ -1942,61 +2012,68 @@ fn moveDemotedGlobal(elf: *Elf, global_ptr: *Symbol.Global) void {
19422012
19432013 elf.targetStore(&shdr.info, @intFromEnum(dest_index) + 1);
19442014
1945 if (src_index == dest_index) {
1946 // The demoted global was already the first global, so we don't need to do any swap.
1947 return;
1948 }
2015 if (src_index != dest_index) {
2016 // The demoted global was not the first global in the symtab, so we need to swap it
2017 // to its new location.
19492018
1950 const src_sym_ptr = @field(elf.symPtr(src_index), @tagName(class));
1951 const dest_sym_ptr = @field(elf.symPtr(dest_index), @tagName(class));
2019 const src_sym_ptr = @field(elf.symPtr(src_index), @tagName(class));
2020 const dest_sym_ptr = @field(elf.symPtr(dest_index), @tagName(class));
19522021
1953 const this_name: String(.strtab) = @enumFromInt(elf.targetLoad(&src_sym_ptr.name));
1954 assert(elf.globalByName(this_name).? == global_ptr);
1955 if (global_ptr.symtab_index.ptr(elf).first_target_reloc != .none) {
1956 // This symbol's index is changing, so queue an update of relocs targeting it.
1957 elf.changed_symtab_index.putAssumeCapacity(this_name, {});
1958 }
2022 const this_name: String(.strtab) = @enumFromInt(elf.targetLoad(&src_sym_ptr.name));
2023 assert(elf.globalByName(this_name).? == global_ptr);
19592024
1960 const other_name: String(.strtab) = @enumFromInt(elf.targetLoad(&dest_sym_ptr.name));
1961 const other_global_ptr = elf.globalByName(other_name).?;
1962 assert(other_global_ptr.symtab_index == dest_index);
1963 if (other_global_ptr.symtab_index.ptr(elf).first_target_reloc != .none) {
1964 // This other symbol's index is changing, so queue an update of relocs targeting it.
1965 elf.changed_symtab_index.putAssumeCapacity(other_name, {});
2025 const other_name: String(.strtab) = @enumFromInt(elf.targetLoad(&dest_sym_ptr.name));
2026 const other_global_ptr = elf.globalByName(other_name).?;
2027 assert(other_global_ptr.symtab_index == dest_index);
2028
2029 // First swap the symtab entries...
2030 std.mem.swap(class.ElfN().Sym, src_sym_ptr, dest_sym_ptr);
2031 // ...then the `elf.symtab` metadata...
2032 std.mem.swap(Symbol, src_index.ptr(elf), dest_index.ptr(elf));
2033 // ...then update the `elf.globals` tracking.
2034 global_ptr.symtab_index = dest_index;
2035 other_global_ptr.symtab_index = src_index;
19662036 }
19672037
1968 // First swap the symtab entries...
1969 std.mem.swap(class.ElfN().Sym, src_sym_ptr, dest_sym_ptr);
1970 // ...then the `elf.symtab` metadata...
1971 std.mem.swap(Symbol, src_index.ptr(elf), dest_index.ptr(elf));
1972 // ...then update the `elf.globals` tracking.
1973 global_ptr.symtab_index = dest_index;
1974 other_global_ptr.symtab_index = src_index;
1975
1976 // We also need to get rid of the dynsym entry if there is one. For simplicity, just
1977 // replace it with a dummy entry which will never be used and will not cause problems.
1978 // TODO: we should have a free-list of dynsym slots so that other symbols can go here.
1979 // TODO: it would also be best to just avoid having gaps in the dynsym altogether.
2038 // We also need to get rid of the dynsym entry if there is one. To keep dynsym compact,
2039 // we'll move another symbol into its place just like we did above.
19802040 if (global_ptr.dynsym_index != 0) {
1981 const dynsym = @field(elf.dynsymPtr(global_ptr.dynsym_index), @tagName(class));
1982 dynsym.* = .{
1983 .name = @intFromEnum(String(.dynstr).empty),
1984 .value = 0,
1985 .size = 0,
1986 .info = .{
1987 .type = .NOTYPE,
1988 // STB_WEAK is important: we mustn't cause a dynamic linker error if the
1989 // symbol can't be resolved.
1990 .bind = .WEAK,
1991 },
1992 // SHN_UNDEF is important: we mustn't define this symbol for other DSOs.
1993 .shndx = std.elf.SHN_UNDEF,
1994 .other = .{ .visibility = .DEFAULT },
1995 };
1996 if (elf.targetEndian() != native_endian) {
1997 std.mem.byteSwapAllFields(class.ElfN().Sym, dynsym);
1998 }
2041 const dynsym_shdr = @field(elf.shdrPtr(elf.shndx.dynsym), @tagName(class));
2042
2043 const ent_size = @sizeOf(class.ElfN().Sym);
2044 assert(elf.targetLoad(&dynsym_shdr.entsize) == ent_size);
2045
2046 // We're going to decrease the size of `.dynsym`, thereby removing its last index.
2047 const old_size = elf.targetLoad(&dynsym_shdr.size);
2048 const new_size = old_size - ent_size;
2049 const remove_dynsym_index: u32 = @intCast(@divExact(new_size, ent_size));
2050
2051 const free_dynsym_index = global_ptr.dynsym_index;
19992052 global_ptr.dynsym_index = 0;
2053
2054 if (free_dynsym_index != remove_dynsym_index) {
2055 // The demoted global wasn't the last entry, so move whatever entry we just
2056 // truncated out of dynsym into its place.
2057
2058 const src_dynsym_ptr = @field(elf.dynsymPtr(remove_dynsym_index), @tagName(class));
2059 const dest_dynsym_ptr = @field(elf.dynsymPtr(free_dynsym_index), @tagName(class));
2060
2061 const moved_name_dynstr: String(.dynstr) = @enumFromInt(elf.targetLoad(&src_dynsym_ptr.name));
2062 const moved_name = elf.stringExisting(.strtab, moved_name_dynstr.slice(elf));
2063 const moved_global_ptr = elf.globalByName(moved_name).?;
2064
2065 dest_dynsym_ptr.* = src_dynsym_ptr.*;
2066
2067 assert(moved_global_ptr.dynsym_index == remove_dynsym_index);
2068 moved_global_ptr.dynsym_index = free_dynsym_index;
2069
2070 // Since that symbol's dynsym index has changed, we'll have to update any
2071 // relocation entries targeting it.
2072 elf.changed_symtab_index.putAssumeCapacity(moved_name, {});
2073 }
2074
2075 // Now that we've given that symbol a new home, actually decrease the section size.
2076 elf.targetStore(&dynsym_shdr.size, new_size);
20002077 }
20012078 },
20022079 }
......@@ -2033,13 +2110,13 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void
20332110
20342111 if (plt_index < elf.plt.count()) {
20352112 // We reused a free entry, so we're already done!
2036 elf.plt.setKey(plt_index, .global(global_name));
2113 elf.plt.setKey(plt_index, global_name);
20372114 return;
20382115 }
20392116
20402117 // We added a new entry, so we now need to extend the PLT sections.
20412118 assert(plt_index == elf.plt.count());
2042 elf.plt.putAssumeCapacityNoClobber(.global(global_name), {});
2119 elf.plt.putAssumeCapacityNoClobber(global_name, {});
20432120
20442121 switch (elf.ehdrField(.machine)) {
20452122 else => |machine| @panic(@tagName(machine)),
......@@ -2315,8 +2392,8 @@ const Symbol = struct {
23152392 }
23162393 }
23172394
2318 /// Scans through all relocations targeting `sym_id` and deletes each one's dynamic
2319 /// 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.
23202397 ///
23212398 /// Asserts we are creating a DSO.
23222399 fn deleteDynamicTargetRelocs(sym_id: Symbol.Id, elf: *Elf) void {
......@@ -2329,6 +2406,45 @@ const Symbol = struct {
23292406 reloc.deleteOutputRel(elf);
23302407 ri = reloc.next;
23312408 }
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 }
23322448 }
23332449
23342450 /// Returns `true` if the target of `s` has moved, meaning the symbol's value will change at
......@@ -2357,6 +2473,78 @@ fn globalByName(elf: *const Elf, name: String(.strtab)) ?*Symbol.Global {
23572473 return null;
23582474}
23592475
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,
2493 };
2494
2495 if (elf.shndx.dynamic == .UNDEF) {
2496 // This is a static non-PIE executable---every symbol has a statically known value.
2497 return .static;
2498 }
2499
2500 const shndx: Section.Index, const visibility: std.elf.STV = switch (elf.symPtr(sym.index(elf))) {
2501 inline else => |sym_ptr| .{
2502 .fromSection(elf.targetLoad(&sym_ptr.shndx)),
2503 elf.targetLoad(&sym_ptr.other).visibility,
2504 },
2505 };
2506
2507 switch (sym.unwrap()) {
2508 .local => {
2509 assert(shndx != .UNDEF);
2510 assert(visibility == .DEFAULT);
2511 },
2512 .global => |name| if (visibility == .DEFAULT and comp.config.output_mode != .Exe) {
2513 // An unprotected symbol in a DSO which is not an executable is subject to runtime
2514 // preemption, so a dynamic relocation is required for it even if we have a definition.
2515 return .dynamic;
2516 } else if (elf.copied_globals.contains(name)) {
2517 // This becomes a locally-defined symbol in `.data`.
2518 return if (runtime_load_addr) .static_relative else .static;
2519 },
2520 }
2521
2522 return switch (shndx) {
2523 .UNDEF => switch (visibility) {
2524 .DEFAULT => if (comp.config.link_mode == .static and comp.config.output_mode == .Exe) {
2525 assert(comp.config.pie); // non-PIE static exe should not have a `.dynamic` section
2526 // This is a static PIE---the only dynamic relocations are `R_*_RELATIVE`.
2527 return .static;
2528 } else .dynamic, // external symbol
2529
2530 // If the symbol *cannot* be external, then there's no point making a dynamic relocation
2531 // now---if linking succeeds we won't need anything more than perhaps an `R_*_RELATIVE`.
2532 .INTERNAL, .HIDDEN, .PROTECTED => .static,
2533 },
2534
2535 .ABS => .static,
2536
2537 else => if (runtime_load_addr and
2538 shndx.flags(elf).ALLOC and
2539 !shndx.flags(elf).TLS)
2540 {
2541 return .static_relative;
2542 } else {
2543 return .static;
2544 },
2545 };
2546}
2547
23602548pub fn symbolForAtom(elf: *Elf, atom: link.File.AtomId) link.File.SymbolId {
23612549 const lsi: Symbol.LocalIndex = switch (elf.getNode(Node.fromAtom(atom))) {
23622550 .file,
......@@ -2588,6 +2776,11 @@ fn string(elf: *Elf, comptime section: StringSection, key: []const u8) Error!Str
25882776 const st: *StringTable = &@field(elf, @tagName(section));
25892777 return @enumFromInt(try st.get(elf, section.shndx(elf), key));
25902778}
2779/// Like `string`, but asserts that the string is already in `section`.
2780fn stringExisting(elf: *Elf, comptime section: StringSection, key: []const u8) String(section) {
2781 const st: *StringTable = &@field(elf, @tagName(section));
2782 return @enumFromInt(st.getExisting(elf, section.shndx(elf), key));
2783}
25912784
25922785const StringTable = struct {
25932786 map: std.HashMapUnmanaged(u32, void, StringTable.Context, std.hash_map.default_max_load_percentage),
......@@ -2618,7 +2811,14 @@ const StringTable = struct {
26182811 }
26192812 };
26202813
2621 pub fn get(st: *StringTable, elf: *Elf, shndx: Section.Index, key: []const u8) Error!u32 {
2814 fn getExisting(st: *StringTable, elf: *Elf, shndx: Section.Index, key: []const u8) u32 {
2815 if (key.len == 0) return 0;
2816 const slice_const = shndx.get(elf).ni.sliceConst(&elf.mf);
2817 const adapter: StringTable.Adapter = .{ .slice = slice_const };
2818 return st.map.getKeyAdapted(key, adapter).?;
2819 }
2820
2821 fn get(st: *StringTable, elf: *Elf, shndx: Section.Index, key: []const u8) Error!u32 {
26222822 // If we are in `initHeaders` the strtab might not be initalized yet, so we need to special
26232823 // case the empty string.
26242824 if (key.len == 0) return 0;
......@@ -2884,51 +3084,104 @@ fn initHeaders(
28843084 .@"64" => .@"8",
28853085 };
28863086
2887 const shnum: u32 = 1;
2888 var phnum: u32 = 0;
2889 const phdr_phndx = phnum;
2890 phnum += 1;
2891 const interp_phndx = if (maybe_interp) |_| phndx: {
2892 defer phnum += 1;
2893 break :phndx phnum;
2894 } else undefined;
2895 const rodata_phndx = phnum;
2896 phnum += 1;
2897 const text_phndx = phnum;
2898 phnum += 1;
2899 const data_phndx = phnum;
2900 phnum += 1;
2901 const tls_phndx = if (comp.config.any_non_single_threaded) phndx: {
2902 defer phnum += 1;
2903 break :phndx phnum;
2904 } else undefined;
2905 const dynamic_phndx = if (have_dynamic_section) phndx: {
2906 defer phnum += 1;
2907 break :phndx phnum;
2908 } else undefined;
2909 const relro_phndx = phnum;
2910 phnum += 1;
2911
29123087 const init_plt_size: std.elf.Xword, const plt_align: std.mem.Alignment, const plt_sec =
29133088 switch (machine) {
29143089 else => @panic(@tagName(machine)),
29153090 .X86_64 => .{ 16, .@"16", true },
29163091 .LOONGARCH => .{ 32, .@"4", false },
29173092 };
2918 const expected_nodes_len = expected_nodes_len: switch (@"type") {
2919 .NONE, .CORE, _ => unreachable,
2920 .REL => {
2921 // Each phdr is actually going to be an shdr.
2922 defer phnum = 0;
2923 break :expected_nodes_len 5 + phnum;
2924 },
2925 .EXEC, .DYN => break :expected_nodes_len 9 +
2926 phnum * 2 - 1 + // each phdr also has a matching shdr, except for the PT_PHDR phdr
2927 @as(usize, 4) * @intFromBool(have_dynamic_section) + // .dynstr, .dynsym, .rela.dyn, .rela.plt
2928 @intFromBool(plt_sec),
3093
3094 const shnum: u32 = shnum: {
3095 var shnum: u32 = 1; // reserved ("null") shdr
3096 shnum += 1; // .symtab
3097 shnum += 1; // .shstrtab
3098 shnum += 1; // .strtab
3099 shnum += @intFromBool(maybe_interp != null); // .interp
3100 shnum += 1; // .rodata
3101 shnum += 1; // .text
3102 shnum += 1; // .data
3103 shnum += @intFromBool(comp.config.any_non_single_threaded); // .tdata
3104 shnum += 1; // .data.rel.ro
3105 if (have_dynamic_section) {
3106 shnum += 1; // .dynamic
3107 shnum += 1; // .dynstr
3108 shnum += 1; // .dynsym
3109 shnum += 1; // .rela.dyn
3110 shnum += 1; // .rela.plt
3111 }
3112 if (@"type" != .REL) {
3113 shnum += 1; // .got
3114 shnum += 1; // .got.plt
3115 shnum += 1; // .plt
3116 shnum += @intFromBool(plt_sec); // .plt_sec
3117 }
3118 break :shnum shnum;
3119 };
3120
3121 const phndx: struct {
3122 phdr: u32,
3123 interp: u32,
3124 rodata: u32,
3125 text: u32,
3126 data: u32,
3127 tls: u32,
3128 dynamic: u32,
3129 relro: u32,
3130 gnu_stack: u32,
3131 }, const phnum: u32 = ph: {
3132 switch (@"type") {
3133 .NONE, .CORE, _ => unreachable,
3134 .REL => break :ph .{ undefined, 0 },
3135 .EXEC, .DYN => {},
3136 }
3137 var phnum: u32 = 0;
3138 break :ph .{ .{
3139 .phdr = phndx: {
3140 defer phnum += 1;
3141 break :phndx phnum;
3142 },
3143 .interp = if (maybe_interp) |_| phndx: {
3144 defer phnum += 1;
3145 break :phndx phnum;
3146 } else undefined,
3147 .rodata = phndx: {
3148 defer phnum += 1;
3149 break :phndx phnum;
3150 },
3151 .text = phndx: {
3152 defer phnum += 1;
3153 break :phndx phnum;
3154 },
3155 .data = phndx: {
3156 defer phnum += 1;
3157 break :phndx phnum;
3158 },
3159 .tls = if (comp.config.any_non_single_threaded) phndx: {
3160 defer phnum += 1;
3161 break :phndx phnum;
3162 } else undefined,
3163 .dynamic = if (have_dynamic_section) phndx: {
3164 defer phnum += 1;
3165 break :phndx phnum;
3166 } else undefined,
3167 .relro = phndx: {
3168 defer phnum += 1;
3169 break :phndx phnum;
3170 },
3171 .gnu_stack = phndx: {
3172 defer phnum += 1;
3173 break :phndx phnum;
3174 },
3175 }, phnum };
29293176 };
3177
3178 const expected_nodes_len = 3 + // `.file`, `.ehdr`, and `.shdr` nodes
3179 (shnum - 1) + // -1 because the null shdr does not have a `.section` node
3180 (phnum -| 1); // -1 because the GNU_STACK phdr does not have a `.segment` node
3181
29303182 try elf.nodes.ensureTotalCapacity(gpa, expected_nodes_len);
29313183 try elf.shdrs.ensureTotalCapacity(gpa, shnum);
3184 try elf.section_by_name.ensureUnusedCapacity(gpa, shnum);
29323185 try elf.phdrs.resize(gpa, phnum);
29333186 try elf.symtab.ensureTotalCapacity(gpa, 1);
29343187 elf.nodes.appendAssumeCapacity(.file);
......@@ -2980,7 +3233,7 @@ fn initHeaders(
29803233 ehdr.phentsize = @sizeOf(ElfN.Phdr);
29813234 ehdr.phnum = @min(phnum, std.elf.PN_XNUM);
29823235 ehdr.shentsize = @sizeOf(ElfN.Shdr);
2983 ehdr.shnum = if (shnum < std.elf.SHN_LORESERVE) shnum else 0;
3236 ehdr.shnum = 1; // Only the null shdr initially---will be incremented by `addSection`
29843237 ehdr.shstrndx = std.elf.SHN_UNDEF;
29853238 if (elf.targetEndian() != native_endian) std.mem.byteSwapAllFields(ElfN.Ehdr, ehdr);
29863239 },
......@@ -3000,8 +3253,8 @@ fn initHeaders(
30003253 .moved = true,
30013254 .bubbles_moved = false,
30023255 }));
3003 elf.nodes.appendAssumeCapacity(.{ .segment = rodata_phndx });
3004 elf.phdrs.items[rodata_phndx] = elf.ni.rodata;
3256 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.rodata });
3257 elf.phdrs.items[phndx.rodata] = elf.ni.rodata;
30053258
30063259 assert(elf.ni.phdr == try elf.mf.addOnlyChildNode(gpa, elf.ni.rodata, .{
30073260 .size = elf.ehdrField(.phentsize) * elf.ehdrField(.phnum),
......@@ -3010,32 +3263,34 @@ fn initHeaders(
30103263 .resized = true,
30113264 .bubbles_moved = false,
30123265 }));
3013 elf.nodes.appendAssumeCapacity(.{ .segment = phdr_phndx });
3014 elf.phdrs.items[phdr_phndx] = elf.ni.phdr;
3266 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.phdr });
3267 elf.phdrs.items[phndx.phdr] = elf.ni.phdr;
30153268
30163269 assert(elf.ni.text == try elf.mf.addLastChildNode(gpa, elf.ni.file, .{
30173270 .alignment = elf.mf.flags.block_size,
30183271 .moved = true,
30193272 .bubbles_moved = false,
30203273 }));
3021 elf.nodes.appendAssumeCapacity(.{ .segment = text_phndx });
3022 elf.phdrs.items[text_phndx] = elf.ni.text;
3274 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.text });
3275 elf.phdrs.items[phndx.text] = elf.ni.text;
30233276
30243277 assert(elf.ni.data == try elf.mf.addLastChildNode(gpa, elf.ni.file, .{
30253278 .alignment = elf.mf.flags.block_size,
30263279 .moved = true,
30273280 .bubbles_moved = false,
30283281 }));
3029 elf.nodes.appendAssumeCapacity(.{ .segment = data_phndx });
3030 elf.phdrs.items[data_phndx] = elf.ni.data;
3282 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.data });
3283 elf.phdrs.items[phndx.data] = elf.ni.data;
30313284
30323285 assert(elf.ni.data_rel_ro == try elf.mf.addOnlyChildNode(gpa, elf.ni.data, .{
30333286 .alignment = elf.mf.flags.block_size,
30343287 .moved = true,
30353288 .bubbles_moved = false,
30363289 }));
3037 elf.nodes.appendAssumeCapacity(.{ .segment = relro_phndx });
3038 elf.phdrs.items[relro_phndx] = elf.ni.data_rel_ro;
3290 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.relro });
3291 elf.phdrs.items[phndx.relro] = elf.ni.data_rel_ro;
3292
3293 elf.phdrs.items[phndx.gnu_stack] = .none;
30393294
30403295 break :ph_vaddr switch (elf.ehdrField(.type)) {
30413296 .NONE, .CORE, _ => unreachable,
......@@ -3058,7 +3313,7 @@ fn initHeaders(
30583313
30593314 if (@"type" != .REL) {
30603315 const phdr: []ElfN.Phdr = @ptrCast(@alignCast(elf.ni.phdr.slice(&elf.mf)));
3061 const ph_phdr = &phdr[phdr_phndx];
3316 const ph_phdr = &phdr[phndx.phdr];
30623317 ph_phdr.* = .{
30633318 .type = .PHDR,
30643319 .offset = 0,
......@@ -3072,7 +3327,7 @@ fn initHeaders(
30723327 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_phdr);
30733328
30743329 if (maybe_interp) |_| {
3075 const ph_interp = &phdr[interp_phndx];
3330 const ph_interp = &phdr[phndx.interp];
30763331 ph_interp.* = .{
30773332 .type = .INTERP,
30783333 .offset = 0,
......@@ -3087,7 +3342,7 @@ fn initHeaders(
30873342 }
30883343
30893344 _, const rodata_size = elf.ni.rodata.location(&elf.mf).resolve(&elf.mf);
3090 const ph_rodata = &phdr[rodata_phndx];
3345 const ph_rodata = &phdr[phndx.rodata];
30913346 ph_rodata.* = .{
30923347 .type = if (rodata_size == 0) .NULL else .LOAD,
30933348 .offset = 0,
......@@ -3102,7 +3357,7 @@ fn initHeaders(
31023357 ph_vaddr += @intCast(rodata_size);
31033358
31043359 _, const text_size = elf.ni.text.location(&elf.mf).resolve(&elf.mf);
3105 const ph_text = &phdr[text_phndx];
3360 const ph_text = &phdr[phndx.text];
31063361 ph_text.* = .{
31073362 .type = if (text_size == 0) .NULL else .LOAD,
31083363 .offset = 0,
......@@ -3117,7 +3372,7 @@ fn initHeaders(
31173372 ph_vaddr += @intCast(text_size);
31183373
31193374 _, const data_size = elf.ni.data.location(&elf.mf).resolve(&elf.mf);
3120 const ph_data = &phdr[data_phndx];
3375 const ph_data = &phdr[phndx.data];
31213376 ph_data.* = .{
31223377 .type = if (data_size == 0) .NULL else .LOAD,
31233378 .offset = 0,
......@@ -3132,7 +3387,7 @@ fn initHeaders(
31323387 ph_vaddr += @intCast(data_size);
31333388
31343389 if (comp.config.any_non_single_threaded) {
3135 const ph_tls = &phdr[tls_phndx];
3390 const ph_tls = &phdr[phndx.tls];
31363391 ph_tls.* = .{
31373392 .type = .TLS,
31383393 .offset = 0,
......@@ -3147,7 +3402,7 @@ fn initHeaders(
31473402 }
31483403
31493404 if (have_dynamic_section) {
3150 const ph_dynamic = &phdr[dynamic_phndx];
3405 const ph_dynamic = &phdr[phndx.dynamic];
31513406 ph_dynamic.* = .{
31523407 .type = .DYNAMIC,
31533408 .offset = 0,
......@@ -3161,7 +3416,7 @@ fn initHeaders(
31613416 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_dynamic);
31623417 }
31633418
3164 const ph_relro = &phdr[relro_phndx];
3419 const ph_relro = &phdr[phndx.relro];
31653420 ph_relro.* = .{
31663421 .type = .GNU_RELRO,
31673422 .offset = 0,
......@@ -3173,6 +3428,19 @@ fn initHeaders(
31733428 .@"align" = @intCast(elf.mf.flags.block_size.toByteUnits()),
31743429 };
31753430 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_relro);
3431
3432 const ph_gnu_stack = &phdr[phndx.gnu_stack];
3433 ph_gnu_stack.* = .{
3434 .type = .GNU_STACK,
3435 .offset = 0,
3436 .vaddr = 0,
3437 .paddr = 0,
3438 .filesz = 0,
3439 .memsz = @intCast(elf.options.stack_size orelse 0),
3440 .flags = .{ .R = true, .W = true },
3441 .@"align" = 1,
3442 };
3443 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_gnu_stack);
31763444 }
31773445
31783446 const sh_undef: *ElfN.Shdr = @ptrCast(@alignCast(elf.ni.shdr.slice(&elf.mf)));
......@@ -3312,8 +3580,8 @@ fn initHeaders(
33123580 .resized = true,
33133581 .bubbles_moved = false,
33143582 });
3315 elf.nodes.appendAssumeCapacity(.{ .segment = interp_phndx });
3316 elf.phdrs.items[interp_phndx] = interp_ni;
3583 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.interp });
3584 elf.phdrs.items[phndx.interp] = interp_ni;
33173585
33183586 const sec_interp_shndx = try elf.addSection(interp_ni, .{
33193587 .name = ".interp",
......@@ -3331,8 +3599,8 @@ fn initHeaders(
33313599 .moved = true,
33323600 .bubbles_moved = false,
33333601 });
3334 elf.nodes.appendAssumeCapacity(.{ .segment = dynamic_phndx });
3335 elf.phdrs.items[dynamic_phndx] = dynamic_ni;
3602 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.dynamic });
3603 elf.phdrs.items[phndx.dynamic] = dynamic_ni;
33363604
33373605 const dynstr_shndx = try elf.addSection(elf.ni.rodata, .{
33383606 .name = ".dynstr",
......@@ -3418,19 +3686,19 @@ fn initHeaders(
34183686 });
34193687 elf.plt_first_symbol_reloc = @enumFromInt(elf.symbol_relocs.items.len);
34203688 try elf.ensureUnusedRelocCapacity(plt_ni, 2);
3421 try elf.addRelocAssumeCapacity(
3689 try elf.addSymbolRelocAssumeCapacity(
34223690 plt_ni,
34233691 2,
34243692 got_plt_sym,
34253693 8 * 1 - 4,
3426 .{ .X86_64 = .PC32 },
3694 .rel32,
34273695 );
3428 try elf.addRelocAssumeCapacity(
3696 try elf.addSymbolRelocAssumeCapacity(
34293697 plt_ni,
34303698 8,
34313699 got_plt_sym,
34323700 8 * 2 - 4,
3433 .{ .X86_64 = .PC32 },
3701 .rel32,
34343702 );
34353703 },
34363704 .LOONGARCH => {
......@@ -3461,9 +3729,9 @@ fn initHeaders(
34613729 });
34623730 elf.plt_first_symbol_reloc = @enumFromInt(elf.symbol_relocs.items.len);
34633731 try elf.ensureUnusedRelocCapacity(plt_ni, 3);
3464 try elf.addRelocAssumeCapacity(plt_ni, 0, got_plt_sym, 0, .{ .LOONGARCH = .PCALA_HI20 });
3465 try elf.addRelocAssumeCapacity(plt_ni, 8, got_plt_sym, 0, .{ .LOONGARCH = .PCALA_LO12 });
3466 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);
34673735 },
34683736 }
34693737 }
......@@ -3473,8 +3741,8 @@ fn initHeaders(
34733741 .moved = true,
34743742 .bubbles_moved = false,
34753743 });
3476 elf.nodes.appendAssumeCapacity(.{ .segment = tls_phndx });
3477 elf.phdrs.items[tls_phndx] = elf.ni.tls;
3744 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.tls });
3745 elf.phdrs.items[phndx.tls] = elf.ni.tls;
34783746 }
34793747
34803748 // Populate reserved GOT words.
......@@ -3641,10 +3909,11 @@ fn initHeaders(
36413909 .flags = .{ .WRITE = true, .ALLOC = true, .TLS = true },
36423910 .addralign = elf.mf.flags.block_size,
36433911 });
3912
36443913 assert(elf.nodes.len == expected_nodes_len);
3914 assert(elf.shdrs.items.len == shnum);
36453915
3646 try elf.section_by_name.ensureUnusedCapacity(gpa, elf.shdrs.items.len);
3647 for (0..elf.shdrs.items.len) |shndx_raw| {
3916 for (0..shnum) |shndx_raw| {
36483917 const shndx: Section.Index = @enumFromInt(shndx_raw);
36493918 elf.section_by_name.putAssumeCapacityNoClobber(shndx.name(elf), {});
36503919 }
......@@ -3780,13 +4049,11 @@ fn flushMovedNodeRelocs(
37804049 for (elf.symbol_relocs.items[@intFromEnum(first_symbol_reloc)..]) |*reloc| {
37814050 if (reloc.node != node) break;
37824051 if (reloc.rela_index.unwrap()) |rela_index| {
3783 // Update the offsets of any `ElfN.Rela` entry we've emitted, since the node they're
3784 // 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.
37854054 reloc.relaSection(elf).relaSetOffset(elf, rela_index, node_vaddr + reloc.offset);
3786 } else {
3787 // We've applied this relocation ourselves! Just re-apply it now.
3788 reloc.apply(elf);
37894055 }
4056 reloc.apply(elf);
37904057 }
37914058 }
37924059
......@@ -4108,7 +4375,7 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) Error!Node
41084375 } else if (ip.isFunctionType(nav.resolved.?.type)) {
41094376 break :section .text;
41104377 } else {
4111 break :section .rodata;
4378 break :section .data_rel_ro; // TODO: it would be better to use `.rodata` if the NAV value doesn't have relocs
41124379 }
41134380 };
41144381 const alignment: InternPool.Alignment = switch (Type.fromInterned(nav.resolved.?.type).zigTypeTag(zcu)) {
......@@ -4174,7 +4441,7 @@ fn uavMapIndex(
41744441 const uav_gop = elf.uavs.getOrPutAssumeCapacity(uav_val);
41754442 const umi: Node.UavMapIndex = @enumFromInt(uav_gop.index);
41764443 if (!uav_gop.found_existing) {
4177 const shndx: Section.Index = .data;
4444 const shndx: Section.Index = .data_rel_ro; // TODO: it would be better to use `.rodata` if the UAV value doesn't have relocs
41784445 const node = try elf.mf.addLastChildNode(gpa, shndx.get(elf).ni, .{
41794446 .moved = true, // see assert at end of `flushUav`
41804447 .alignment = resolved_align.toStdMem(),
......@@ -5124,7 +5391,7 @@ fn updateInitFiniArraySectionSize(
51245391 const end_vaddr: u64 = switch (elf.shdrPtr(shndx)) {
51255392 inline else => |shdr| shndx.vaddr(elf) + elf.targetLoad(&shdr.size),
51265393 };
5127 const end_sym_name = elf.string(.strtab, "__" ++ name ++ "_end") catch unreachable; // string definitely already exists
5394 const end_sym_name = elf.stringExisting(.strtab, "__" ++ name ++ "_end");
51285395 Symbol.Id.global(end_sym_name).flushMoved(elf, end_vaddr);
51295396}
51305397
......@@ -5178,6 +5445,10 @@ fn prelinkInner(elf: *Elf) Error!void {
51785445 }
51795446 break :rpath try elf.string(.dynstr, buf.items);
51805447 };
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);
51815452 const soname: ?String(.dynstr) = if (elf.options.soname) |soname_slice| str: {
51825453 break :str try elf.string(.dynstr, soname_slice);
51835454 } else null;
......@@ -5188,7 +5459,8 @@ fn prelinkInner(elf: *Elf) Error!void {
51885459 @as(usize, @intFromBool(elf.shndx.init_array != .UNDEF)) * 2 +
51895460 @as(usize, @intFromBool(elf.shndx.fini_array != .UNDEF)) * 2 +
51905461 @as(usize, @intFromBool(elf.shndx.preinit_array != .UNDEF)) * 2 +
5191 @intFromBool(comp.config.output_mode == .Exe) + 12;
5462 @as(usize, @intFromBool(use_plt)) * 4 +
5463 @intFromBool(comp.config.output_mode == .Exe) + 8;
51925464 const dynamic_size: u32 = @intCast(@sizeOf(ElfN.Addr) * 2 * dynamic_len);
51935465 const dynamic_ni = elf.shndx.dynamic.get(elf).ni;
51945466 try dynamic_ni.resize(&elf.mf, gpa, dynamic_size);
......@@ -5200,6 +5472,8 @@ fn prelinkInner(elf: *Elf) Error!void {
52005472 init_array: ?usize,
52015473 fini_array: ?usize,
52025474 preinit_array: ?usize,
5475 jmprel: ?usize,
5476 pltgot: ?usize,
52035477 } = indices: {
52045478 const sec_dynamic = dynamic_ni.slice(&elf.mf);
52055479 const dynamic_entries: [][2]ElfN.Addr = @ptrCast(@alignCast(sec_dynamic));
......@@ -5232,7 +5506,7 @@ fn prelinkInner(elf: *Elf) Error!void {
52325506 }
52335507 const init_array_index: ?usize = if (elf.shndx.init_array != .UNDEF) i: {
52345508 dynamic_entries[dynamic_index..][0..2].* = .{
5235 .{ std.elf.DT_INIT_ARRAY, @intCast(elf.shndx.init_array.vaddr(elf)) },
5509 .{ std.elf.DT_INIT_ARRAY, 0 }, // reloc added below
52365510 .{ std.elf.DT_INIT_ARRAYSZ, elf.targetLoad(
52375511 &@field(elf.shdrPtr(elf.shndx.init_array), @tagName(ct_class)).size,
52385512 ) },
......@@ -5242,7 +5516,7 @@ fn prelinkInner(elf: *Elf) Error!void {
52425516 } else null;
52435517 const fini_array_index: ?usize = if (elf.shndx.fini_array != .UNDEF) i: {
52445518 dynamic_entries[dynamic_index..][0..2].* = .{
5245 .{ std.elf.DT_FINI_ARRAY, @intCast(elf.shndx.fini_array.vaddr(elf)) },
5519 .{ std.elf.DT_FINI_ARRAY, 0 }, // reloc added below
52465520 .{ std.elf.DT_FINI_ARRAYSZ, elf.targetLoad(
52475521 &@field(elf.shdrPtr(elf.shndx.fini_array), @tagName(ct_class)).size,
52485522 ) },
......@@ -5252,7 +5526,7 @@ fn prelinkInner(elf: *Elf) Error!void {
52525526 } else null;
52535527 const preinit_array_index: ?usize = if (elf.shndx.preinit_array != .UNDEF) i: {
52545528 dynamic_entries[dynamic_index..][0..2].* = .{
5255 .{ std.elf.DT_PREINIT_ARRAY, @intCast(elf.shndx.preinit_array.vaddr(elf)) },
5529 .{ std.elf.DT_PREINIT_ARRAY, 0 }, // reloc added below
52565530 .{ std.elf.DT_PREINIT_ARRAYSZ, elf.targetLoad(
52575531 &@field(elf.shdrPtr(elf.shndx.preinit_array), @tagName(ct_class)).size,
52585532 ) },
......@@ -5260,27 +5534,33 @@ fn prelinkInner(elf: *Elf) Error!void {
52605534 defer dynamic_index += 2;
52615535 break :i dynamic_index;
52625536 } else null;
5263 dynamic_entries[dynamic_index..][0..12].* = .{
5264 .{ 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
52655551 .{ std.elf.DT_RELASZ, elf.targetLoad(
52665552 &@field(elf.shdrPtr(elf.shndx.rela_dyn), @tagName(ct_class)).size,
52675553 ) },
52685554 .{ std.elf.DT_RELAENT, @sizeOf(ElfN.Rela) },
5269 .{ std.elf.DT_JMPREL, @intCast(elf.shndx.rela_plt.vaddr(elf)) },
5270 .{ std.elf.DT_PLTRELSZ, elf.targetLoad(
5271 &@field(elf.shdrPtr(elf.shndx.rela_plt), @tagName(ct_class)).size,
5272 ) },
5273 .{ std.elf.DT_PLTGOT, @intCast(elf.shndx.got_plt.vaddr(elf)) },
5274 .{ std.elf.DT_PLTREL, std.elf.DT_RELA },
5275 .{ std.elf.DT_SYMTAB, @intCast(elf.shndx.dynsym.vaddr(elf)) },
5555 .{ std.elf.DT_SYMTAB, 0 }, // reloc added below
52765556 .{ std.elf.DT_SYMENT, @sizeOf(ElfN.Sym) },
5277 .{ std.elf.DT_STRTAB, @intCast(elf.shndx.dynstr.vaddr(elf)) },
5557 .{ std.elf.DT_STRTAB, 0 }, // reloc added below
52785558 .{ std.elf.DT_STRSZ, elf.targetLoad(
52795559 &@field(elf.shdrPtr(elf.shndx.dynstr), @tagName(ct_class)).size,
52805560 ) },
52815561 .{ std.elf.DT_NULL, 0 },
52825562 };
5283 dynamic_index += 12;
5563 dynamic_index += 8;
52845564 assert(dynamic_index == dynamic_len);
52855565 if (elf.targetEndian() != native_endian) for (dynamic_entries) |*dynamic_entry|
52865566 std.mem.byteSwapAllFields(@TypeOf(dynamic_entry.*), dynamic_entry);
......@@ -5289,66 +5569,74 @@ fn prelinkInner(elf: *Elf) Error!void {
52895569 .init_array = init_array_index,
52905570 .fini_array = fini_array_index,
52915571 .preinit_array = preinit_array_index,
5572 .jmprel = jmprel_index,
5573 .pltgot = pltgot_index,
52925574 };
52935575 };
52945576
5577 const dsorel: SymbolReloc.Type = switch (ct_class) {
5578 .NONE, _ => comptime unreachable,
5579 .@"32" => .dsorel32,
5580 .@"64" => .dsorel64,
5581 };
5582
52955583 elf.dynamic_first_symbol_reloc = @enumFromInt(elf.symbol_relocs.items.len);
52965584 try elf.ensureUnusedRelocCapacity(dynamic_ni, 8);
5297 if (dynamic_indices.init_array) |index| try elf.addRelocAssumeCapacity(
5585 if (dynamic_indices.init_array) |index| try elf.addSymbolRelocAssumeCapacity(
52985586 dynamic_ni,
52995587 @sizeOf(ElfN.Addr) * (2 * index + 1),
53005588 .local(elf.shndx.init_array.get(elf).lsi),
53015589 0,
5302 .absAddr(elf),
5590 dsorel,
53035591 );
5304 if (dynamic_indices.fini_array) |index| try elf.addRelocAssumeCapacity(
5592 if (dynamic_indices.fini_array) |index| try elf.addSymbolRelocAssumeCapacity(
53055593 dynamic_ni,
53065594 @sizeOf(ElfN.Addr) * (2 * index + 1),
53075595 .local(elf.shndx.fini_array.get(elf).lsi),
53085596 0,
5309 .absAddr(elf),
5597 dsorel,
53105598 );
5311 if (dynamic_indices.preinit_array) |index| try elf.addRelocAssumeCapacity(
5599 if (dynamic_indices.preinit_array) |index| try elf.addSymbolRelocAssumeCapacity(
53125600 dynamic_ni,
53135601 @sizeOf(ElfN.Addr) * (2 * index + 1),
53145602 .local(elf.shndx.preinit_array.get(elf).lsi),
53155603 0,
5316 .absAddr(elf),
5604 dsorel,
53175605 );
5318 try elf.addRelocAssumeCapacity(
5606 if (dynamic_indices.jmprel) |index| try elf.addSymbolRelocAssumeCapacity(
53195607 dynamic_ni,
5320 @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 12) + 1),
5321 .local(elf.shndx.rela_dyn.get(elf).lsi),
5608 @sizeOf(ElfN.Addr) * (2 * index + 1),
5609 .local(elf.shndx.rela_plt.get(elf).lsi),
53225610 0,
5323 .absAddr(elf),
5611 dsorel,
53245612 );
5325 try elf.addRelocAssumeCapacity(
5613 if (dynamic_indices.pltgot) |index| try elf.addSymbolRelocAssumeCapacity(
53265614 dynamic_ni,
5327 @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 9) + 1),
5328 .local(elf.shndx.rela_plt.get(elf).lsi),
5615 @sizeOf(ElfN.Addr) * (2 * index + 1),
5616 .local(elf.shndx.got_plt.get(elf).lsi),
53295617 0,
5330 .absAddr(elf),
5618 dsorel,
53315619 );
5332 try elf.addRelocAssumeCapacity(
5620 try elf.addSymbolRelocAssumeCapacity(
53335621 dynamic_ni,
5334 @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 7) + 1),
5335 .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),
53365624 0,
5337 .absAddr(elf),
5625 dsorel,
53385626 );
5339 try elf.addRelocAssumeCapacity(
5627 try elf.addSymbolRelocAssumeCapacity(
53405628 dynamic_ni,
53415629 @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 5) + 1),
53425630 .local(elf.shndx.dynsym.get(elf).lsi),
53435631 0,
5344 .absAddr(elf),
5632 dsorel,
53455633 );
5346 try elf.addRelocAssumeCapacity(
5634 try elf.addSymbolRelocAssumeCapacity(
53475635 dynamic_ni,
53485636 @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 3) + 1),
53495637 .local(elf.shndx.dynstr.get(elf).lsi),
53505638 0,
5351 .absAddr(elf),
5639 dsorel,
53525640 );
53535641 },
53545642 };
......@@ -5687,18 +5975,34 @@ fn addSymbolRelocAssumeCapacity(
56875975 @"type": SymbolReloc.Type,
56885976) Error!void {
56895977 assert(elf.ehdrField(.type) != .REL);
5978 assert(node != .none);
56905979
56915980 const rela_index: Section.RelaIndex.Optional = r: {
5692 if (elf.shndx.dynamic == .UNDEF) break :r .none;
5693 const global_name = switch (target.unwrap()) {
5694 .local => break :r .none,
5695 .global => |name| name,
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),
56965996 };
56975997
56985998 const rela_type: MachineRelocType = switch (elf.ehdrField(.machine)) {
56995999 else => |machine| @panic(@tagName(machine)),
57006000 .X86_64 => .{ .X86_64 = switch (@"type") {
57016001 .write_rela => unreachable,
6002 .dsorel64, .dsorel32 => {
6003 assert(target.unwrap() == .local);
6004 break :r .none;
6005 },
57026006 .abs64 => .@"64",
57036007 .abs32 => .@"32",
57046008 .abs32s => .@"32S",
......@@ -5726,72 +6030,79 @@ fn addSymbolRelocAssumeCapacity(
57266030 .tpoff64_hi12,
57276031 => unreachable,
57286032 } },
5729 .LOONGARCH => .{
5730 .LOONGARCH = switch (@"type") {
5731 .write_rela => unreachable,
5732 .abs64 => .@"64",
5733 .abs32 => .@"32",
5734 .abs32s, .size64, .size32 => unreachable,
5735 .rel64 => .@"64_PCREL",
5736 .rel32 => .@"32_PCREL",
5737 .pltrel64, .pltrel32 => break :r .none,
5738 .dtpoff64 => .TLS_DTPREL64,
5739 .dtpoff32 => .TLS_DTPREL32,
5740 .tpoff64 => .TLS_TPREL64,
5741 .tpoff32 => .TLS_TPREL32,
5742 .abs32_lo12 => .PCALA_LO12,
5743 .rel32_hi20 => .PCALA_HI20,
5744 .rel64_lo20 => .PCALA64_LO20,
5745 .rel64_hi12 => .PCALA64_HI12,
5746 .branch_rel18 => .B16,
5747 .branch_rel23 => .B21,
5748 .branch_rel28 => .B26,
5749 .call_rel38 => .CALL36,
5750 .tpoff32_lo12 => .TLS_LE_LO12,
5751 .tpoff32_hi20 => .TLS_LE_HI20,
5752 .tpoff64_lo20 => .TLS_LE64_LO20,
5753 .tpoff64_hi12 => .TLS_LE64_HI12,
6033 .LOONGARCH => .{ .LOONGARCH = switch (@"type") {
6034 .write_rela => unreachable,
6035 .dsorel64, .dsorel32 => {
6036 assert(target.unwrap() == .local);
6037 break :r .none;
57546038 },
5755 },
6039 .abs64 => .@"64",
6040 .abs32 => .@"32",
6041 .abs32s, .size64, .size32 => unreachable,
6042 .rel64 => .@"64_PCREL",
6043 .rel32 => .@"32_PCREL",
6044 .pltrel64, .pltrel32 => break :r .none,
6045 .dtpoff64 => .TLS_DTPREL64,
6046 .dtpoff32 => .TLS_DTPREL32,
6047 .tpoff64 => .TLS_TPREL64,
6048 .tpoff32 => .TLS_TPREL32,
6049 .abs32_lo12 => .PCALA_LO12,
6050 .rel32_hi20 => .PCALA_HI20,
6051 .rel64_lo20 => .PCALA64_LO20,
6052 .rel64_hi12 => .PCALA64_HI12,
6053 .branch_rel18 => .B16,
6054 .branch_rel23 => .B21,
6055 .branch_rel28 => .B26,
6056 .call_rel38 => .CALL36,
6057 .tpoff32_lo12 => .TLS_LE_LO12,
6058 .tpoff32_hi20 => .TLS_LE_HI20,
6059 .tpoff64_lo20 => .TLS_LE64_LO20,
6060 .tpoff64_hi12 => .TLS_LE64_HI12,
6061 } },
57566062 };
5757 // TODO: even if the symbol is locally defined, preemption/interposition is a
5758 // possibility, which this condition does not currently consider!
5759 if (elf.globals.strong_def.contains(global_name) or
5760 elf.globals.weak_def.contains(global_name))
5761 {
5762 break :r .none;
5763 }
57646063
5765 const dynsym_index = elf.globalByName(global_name).?.dynsym_index;
5766 if (dynsym_index == 0) break :r .none;
5767
5768 switch (elf.nodeWantsDsoRelocation(node)) {
5769 .no => break :r .none,
5770 .yes => {},
5771 .yes_textrel => if (try elf.maybeAddCopyRelocation(global_name)) {
5772 // We were able to use a copy relocation on this symbol to avoid a text relocation,
5773 // which is apparently considered a good thing despite copy relocations being an
5774 // abomination. (This is necessary for correctness in some cases, because e.g. a
5775 // 32-bit runtime relocation on a 64-bit target will often cause rtld errors due to
5776 // the DSOs being loaded too far apart.)
5777 break :r .none;
5778 } else {
5779 // At least for now, our only choice is a text relocation.
5780 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(),
57816104 },
57826105 }
5783
5784 // It currently looks like we need a runtime relocation for this.
5785 break :r elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{
5786 .type = rela_type,
5787 // This field needs to equal the offset into the section, which is *not* necessarily
5788 // the same thing as our `offset`, which is the offset into `node`. We could compute
5789 // the section offset now, but there's no point, because `flushMovedNodeRelocs` will
5790 // eventually do it for us anyway, so just init to 0.
5791 .offset = 0,
5792 .raw_sym_index = dynsym_index,
5793 .addend = addend,
5794 }).toOptional();
57956106 };
57966107
57976108 const ri: SymbolReloc.Index = @enumFromInt(elf.symbol_relocs.items.len);
......@@ -5814,6 +6125,9 @@ fn addSymbolRelocAssumeCapacity(
58146125 if (@"type".dependsOnTlsSize()) {
58156126 elf.tls_size_symbol_relocs.putAssumeCapacityNoClobber(ri, {});
58166127 }
6128
6129 // Actually apply the new relocation!
6130 ri.get(elf).apply(elf);
58176131}
58186132fn addGotRelocAssumeCapacity(
58196133 elf: *Elf,
......@@ -5885,29 +6199,13 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
58856199 reloc: struct {
58866200 type: MachineRelocType,
58876201 dynsym_index: u32,
6202 addend: i64,
58886203 },
58896204 } = switch (elf.got.keys()[got_index]) {
58906205 .reserved => .{ .unsigned = 0 },
58916206 .tpoff => |sym_id| val: {
5892 // We will break from this block if we require a relocation.
5893 known: {
5894 if (elf.base.comp.config.output_mode != .Exe) {
5895 // Only the executable's per-module TLS block is at a known offset from the
5896 // general TLS pointer.
5897 break :known;
5898 }
5899 switch (sym_id.unwrap()) {
5900 .local => {},
5901 .global => |name| if (elf.globals.strong_undef.contains(name) or
5902 elf.globals.weak_undef.contains(name))
5903 {
5904 // This is an external TLS symbol, so we don't know its offset.
5905 break :known;
5906 },
5907 }
5908 // It's a symbol which we define, the symbol is not interposable because we're the
5909 // executable, and we know our per-module TLS block's offset because we're the
5910 // executable. We therefore know this value!
6207 // Only the executable's per-module TLS block is at a known offset from the TLS pointer.
6208 if (elf.base.comp.config.output_mode == .Exe and elf.classifySymbolValue(sym_id) != .dynamic) {
59116209 const tls_phndx = elf.getNode(elf.ni.tls).segment;
59126210 const tls_size: u64 = switch (elf.phdrSlice()) {
59136211 inline else => |phdr| tls_size: {
......@@ -5918,110 +6216,80 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
59186216 const sym_value = sym_id.value(elf);
59196217 break :val .{ .signed = @bitCast(sym_value -% tls_size) };
59206218 }
5921 break :val .{
5922 .reloc = .{
5923 .type = switch (elf.ehdrField(.machine)) {
5924 else => |machine| @panic(@tagName(machine)),
5925 .X86_64 => .{ .X86_64 = .TPOFF64 },
5926 .LOONGARCH => .{ .LOONGARCH = if (elf.identClass() == .@"64") .TLS_TPREL64 else .TLS_TPREL32 },
5927 },
5928 .dynsym_index = switch (sym_id.unwrap()) {
5929 .global => |name| elf.globalByName(name).?.dynsym_index,
5930 // TODO: I have no idea if compilers are even allowed to emit this, but if they
5931 // are then I guess we need to add this local symbol to `.dynsym`?
5932 .local => @panic("TODO(Elf2): GOT tpoff entry referencing local symbol"),
5933 },
5934 },
5935 };
5936 },
5937 .symbol, .tlsgd1 => |sym_id, tag| val: {
5938 const name = switch (sym_id.unwrap()) {
5939 .local => break :val .{ .unsigned = sym_id.value(elf) },
5940 .global => |name| name,
6219 const reloc_type: MachineRelocType = switch (elf.ehdrField(.machine)) {
6220 else => |machine| @panic(@tagName(machine)),
6221 .X86_64 => .{ .X86_64 = .TPOFF64 },
6222 .LOONGARCH => .{ .LOONGARCH = if (elf.identClass() == .@"64") .TLS_TPREL64 else .TLS_TPREL32 },
59416223 };
5942 // If the symbol is *defined* in this module, we might be able to avoid the relocation.
5943 const need_reloc: bool = need_reloc: {
5944 const global = g: {
5945 if (elf.globals.strong_def.getPtr(name)) |g| break :g g;
5946 if (elf.globals.weak_def.getPtr(name)) |g| break :g g;
5947 // The global is undefined, which probably means we need a relocation---unless
5948 // we have created a copy relocation for it, in which case we own the canonical
5949 // address of this symbol in this DSO!
5950 break :need_reloc !elf.copied_globals.contains(name);
5951 };
5952
5953 // We have a definition, but it might be interposable (aka preemptible). There
5954 // are two cases where it is not and so we can (and, in fact, must) elide the
5955 // runtime relocation:
5956 // * We are the executable. Symbols from executables cannot be interposed.
5957 // * The symbol's visibility disallows interposition.
5958 if (elf.base.comp.config.output_mode == .Exe) {
5959 break :need_reloc false;
5960 }
5961 const visibility: std.elf.STV = switch (elf.symPtr(global.symtab_index)) {
5962 inline else => |sym| elf.targetLoad(&sym.other).visibility,
5963 };
5964 break :need_reloc switch (visibility) {
5965 .DEFAULT => true,
5966 .INTERNAL, .HIDDEN, .PROTECTED => false,
5967 };
6224 break :val switch (sym_id.unwrap()) {
6225 // For global symbols, just target the right dynsym with no addend.
6226 .global => |name| .{ .reloc = .{
6227 .type = reloc_type,
6228 .dynsym_index = elf.globalByName(name).?.dynsym_index,
6229 .addend = 0,
6230 } },
6231 // For local symbols, target the null symbol (index 0) so we get the offset to the
6232 // base of our TLS block, and then use `addend` to offset to the right symbol.
6233 .local => .{ .reloc = .{
6234 .type = reloc_type,
6235 .dynsym_index = 0,
6236 .addend = @intCast(sym_id.value(elf)),
6237 } },
59686238 };
5969
5970 if (!need_reloc) {
5971 break :val .{ .unsigned = sym_id.value(elf) };
5972 }
5973
5974 break :val .{ .reloc = .{
5975 .type = if (tag == .symbol) .globDat(elf) else .dtpOffAddr(elf),
5976 .dynsym_index = elf.globalByName(name).?.dynsym_index,
5977 } };
59786239 },
5979 .tlsgd0 => |sym| switch (elf.shndx.dynamic) {
5980 .UNDEF => .{ .unsigned = 1 }, // TLS module ID for exexcutable
5981 else => .{
5982 .reloc = .{
5983 .type = switch (elf.ehdrField(.machine)) {
5984 else => |machine| @panic(@tagName(machine)),
5985 .X86_64 => .{ .X86_64 = .DTPMOD64 },
5986 .LOONGARCH => .{ .LOONGARCH = if (elf.identClass() == .@"64") .TLS_DTPMOD64 else .TLS_DTPMOD32 },
5987 },
5988 .dynsym_index = switch (sym.unwrap()) {
5989 .local => 0,
5990 .global => |name| dsi: {
5991 // Like in the `.tlsgd1` case, we need to check for a non-interposable definition.
5992 if (elf.globals.strong_def.getPtr(name) orelse
5993 elf.globals.weak_def.getPtr(name)) |global|
5994 {
5995 if (elf.base.comp.config.output_mode == .Exe) {
5996 break :dsi 0; // non-interposable definition
5997 }
5998 const visibility: std.elf.STV = switch (elf.symPtr(global.symtab_index)) {
5999 inline else => |sym_ptr| elf.targetLoad(&sym_ptr.other).visibility,
6000 };
6001 switch (visibility) {
6002 .DEFAULT => {},
6003 .INTERNAL, .HIDDEN, .PROTECTED => {
6004 break :dsi 0; // non-interposable definition
6005 },
6006 }
6007 }
6008 // `sym` is either undefined or an interposable definition, so use its
6009 // actual dynsym index.
6010 break :dsi elf.globalByName(name).?.dynsym_index;
6011 },
6012 },
6013 },
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),
6249 .dynsym_index = elf.globalByName(sym.unwrap().global).?.dynsym_index,
6250 .addend = 0,
6251 } },
6252 },
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
60146266 },
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 } },
60156279 },
6016 .tlsld0 => switch (elf.shndx.dynamic) {
6017 .UNDEF => .{ .unsigned = 1 }, // TLS module ID for exexcutable
6018 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 = .{
60196286 .type = switch (elf.ehdrField(.machine)) {
60206287 else => |machine| @panic(@tagName(machine)),
60216288 .X86_64 => .{ .X86_64 = .DTPMOD64 },
60226289 .LOONGARCH => .{ .LOONGARCH = if (elf.identClass() == .@"64") .TLS_DTPMOD64 else .TLS_DTPMOD32 },
60236290 },
60246291 .dynsym_index = 0,
6292 .addend = 0,
60256293 } },
60266294 },
60276295 .tlsld1 => .{ .unsigned = 0 },
......@@ -6065,7 +6333,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
60656333 .type = reloc.type,
60666334 .offset = got_entry_addr,
60676335 .raw_sym_index = reloc.dynsym_index,
6068 .addend = 0,
6336 .addend = reloc.addend,
60696337 }).toOptional(),
60706338 };
60716339}
......@@ -6405,26 +6673,80 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool {
64056673 };
64066674 break :task;
64076675 }
6408 while (elf.changed_symtab_index.pop()) |kv| {
6409 // We only need to do work in relocatables, because in ELF modules (non-relocatables)
6410 // our `ElfN.Rela` entries use `.dynsym` indices rather than `.symtab` indices, and
6411 // `.dynsym` indices are (at the time of writing) always immutable.
6412 if (elf.ehdrField(.type) == .REL) {
6413 const sub_prog_node = elf.mf.update_prog_node.start(kv.key.slice(elf), 0);
6414 defer sub_prog_node.end();
6415 const sym = elf.globalByName(kv.key).?.symtab_index.ptr(elf);
6416 var ri = sym.first_target_reloc;
6417 while (ri != .none) {
6418 const reloc = ri.get(elf);
6419 reloc.relaSection(elf).relaUpdateSym(
6420 elf,
6421 reloc.rela_index.unwrap().?,
6422 @intFromEnum(reloc.target.index(elf)),
6423 );
6424 ri = reloc.next;
6425 }
6426 break :task;
6676 if (elf.changed_symtab_index.pop()) |kv| {
6677 const sub_prog_node = elf.mf.update_prog_node.start(kv.key.slice(elf), 0);
6678 defer sub_prog_node.end();
6679
6680 const global_name = kv.key;
6681 const global = elf.globalByName(global_name).?;
6682 const sym_id: Symbol.Id = .global(global_name);
6683 const sym = global.symtab_index.ptr(elf);
6684
6685 switch (elf.ehdrField(.type)) {
6686 .REL => {
6687 // Index in `.symtab` has changed. Relocatables are easy, we just need to update
6688 // all of the output relocations.
6689 const symtab_index = @intFromEnum(global.symtab_index);
6690 var ri = sym.first_target_reloc;
6691 while (ri != .none) {
6692 const reloc = ri.get(elf);
6693 assert(reloc.target == sym_id);
6694 // In relocatables, every symbol relocation has an output relocation.
6695 const rela_index = reloc.rela_index.unwrap().?;
6696 reloc.relaSection(elf).relaUpdateSym(elf, rela_index, symtab_index);
6697 ri = reloc.next;
6698 }
6699 },
6700 // For other `ET_*` values, the index in `.dynsym` has changed. There are a few
6701 // places we might have emitted output relocations, depending on whether or not the
6702 // symbol's value is statically known.
6703 else => switch (elf.classifySymbolValue(sym_id)) {
6704 .static, .static_relative => {
6705 // Since the symbol value is statically known, we definitely aren't emitting
6706 // any relocation targeting it (we might have `R_*_RELATIVE` relocs but they
6707 // don't care about the dynsym index). The only exception is a copy reloc
6708 // could exist (and be the *reason* the symbol value is statically known).
6709 if (elf.copied_globals.get(global_name)) |copied| {
6710 elf.shndx.rela_dyn.relaUpdateSym(elf, copied.rela_index, global.dynsym_index);
6711 }
6712 },
6713 .dynamic => {
6714 assert(!elf.copied_globals.contains(global_name)); // value would be statically known
6715
6716 // Update symbol relocs:
6717 var ri = sym.first_target_reloc;
6718 while (ri != .none) {
6719 const reloc = ri.get(elf);
6720 assert(reloc.target == sym_id);
6721 // There may or may not be a runtime relocation for this symbol reloc.
6722 if (reloc.rela_index.unwrap()) |rela_index| {
6723 elf.shndx.rela_dyn.relaUpdateSym(elf, rela_index, global.dynsym_index);
6724 }
6725 ri = reloc.next;
6726 }
6727
6728 // Update the PLT entry's reloc if there is one:
6729 if (elf.plt.getIndex(global_name)) |plt_index| {
6730 // PLT indices exactly match `.rela.plt` relocation indices.
6731 elf.shndx.rela_plt.relaUpdateSym(elf, @enumFromInt(plt_index), global.dynsym_index);
6732 }
6733
6734 // Update relocs for any relevant GOT entries:
6735 if (elf.got.getIndex(.{ .symbol = sym_id })) |got_index| {
6736 elf.updateGotEntry(got_index);
6737 }
6738 if (elf.got.getIndex(.{ .tpoff = sym_id })) |got_index| {
6739 elf.updateGotEntry(got_index);
6740 }
6741 if (elf.got.getIndex(.{ .tlsgd0 = sym_id })) |got_index| {
6742 elf.updateGotEntry(got_index);
6743 elf.updateGotEntry(got_index + 1); // tlsgd1
6744 }
6745 },
6746 },
64276747 }
6748
6749 break :task;
64286750 }
64296751 while (elf.mf.updates.pop()) |ni| {
64306752 const clean_moved = ni.cleanMoved(&elf.mf);
......@@ -6838,7 +7160,7 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo
68387160 switch (elf.targetLoad(&next_ph.type)) {
68397161 else => unreachable,
68407162 .NULL, .LOAD => {},
6841 .DYNAMIC, .INTERP, .PHDR, .TLS, std.elf.PT.GNU_RELRO => break,
7163 .DYNAMIC, .INTERP, .PHDR, .TLS, .GNU_RELRO, .GNU_STACK => break,
68427164 }
68437165 const next_vaddr = elf.targetLoad(&next_ph.vaddr);
68447166 if (vaddr + memsz <= next_vaddr) break;
......@@ -6917,8 +7239,8 @@ fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_ad
69177239 // its relocations are probably going through the PLT, so we don't bother with
69187240 // specific tracking for PLT relocations---instead just re-apply all relocations
69197241 // targeting symbols with PLT entries.
6920 for (elf.plt.keys()) |sym| {
6921 sym.applyTargetRelocs(elf);
7242 for (elf.plt.keys()) |name| {
7243 Symbol.Id.global(name).applyTargetRelocs(elf);
69227244 }
69237245 // We also need to update all of the references from `.plt.sec` to `.got.plt`.
69247246 // However, if there's also a flush pending for `.got.plt`, don't bother doing
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);